[
  {
    "path": ".gitignore",
    "content": "*.iml\n*.pyc\n*.swp\nblueprint/\nkati/\nsoong/\n"
  },
  {
    "path": "Android.bp",
    "content": "// Copyright 2024 Google Inc. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\n// Package the minimal files required to run envsetup.sh in the test\n// environment.\ngenrule {\n    name: \"envsetup_minimum.zip\",\n    visibility: [\n        \"//build/make/tests:__subpackages__\",\n    ],\n    tools: [\n        \"soong_zip\",\n    ],\n    srcs: [\n        \"envsetup.sh\",\n        \"shell_utils.sh\",\n        \"core/envsetup.mk\",\n    ],\n    out: [\"envsetup.zip\"],\n    cmd: \"$(location soong_zip) -o $(out) -D build/make\",\n}\n"
  },
  {
    "path": "Changes.md",
    "content": "# Build System Changes for Android.mk/Android.bp Writers\n\n## Soong genrules are now sandboxed\n\nPreviously, soong genrules could access any files in the source tree, without specifying them as\ninputs. This makes them incorrect in incremental builds, and incompatible with RBE and Bazel.\n\nNow, genrules are sandboxed so they can only access their listed srcs. Modules denylisted in\ngenrule/allowlists.go are exempt from this. You can also set `BUILD_BROKEN_GENRULE_SANDBOXING`\nin board config to disable this behavior.\n\n## Partitions are no longer affected by previous builds\n\nPartition builds used to include everything in their staging directories, and building an\nindividual module will install it to the staging directory. Thus, previously, `m mymodule` followed\nby `m` would cause `mymodule` to be presinstalled on the device, even if it wasn't listed in\n`PRODUCT_PACKAGES`.\n\nThis behavior has been changed, and now the partition images only include what they'd have if you\ndid a clean build. This behavior can be disabled by setting the\n`BUILD_BROKEN_INCORRECT_PARTITION_IMAGES` environment variable or board config variable.\n\nManually adding make rules that build to the staging directories without going through the make\nmodule system will not be compatible with this change. This includes many usages of\n`LOCAL_POST_INSTALL_CMD`.\n\n## Perform validation of Soong plugins\n\nEach Soong plugin will require manual work to migrate to Bazel. In order to\nminimize the manual work outside of build/soong, we are restricting plugins to\nthose that exist today and those in vendor or hardware directories.\n\nIf you need to extend the build system via a plugin, please reach out to the\nbuild team via email android-building@googlegroups.com (external) for any\nquestions, or see [go/soong](http://go/soong) (internal).\n\nTo omit the validation, `BUILD_BROKEN_PLUGIN_VALIDATION` expects a\nspace-separated list of plugins to omit from the validation. This must be set\nwithin a product configuration .mk file, board config .mk file, or buildspec.mk.\n\n## Python 2 to 3 migration\n\nPython 2 has been completely removed from the build. Please migrate any remaining usages to\nPython 3, and remove any version-specific properties from bp files.\n\n## Stop referencing sysprop_library directly from cc modules\n\nFor the migration to Bazel, we are no longer mapping sysprop_library targets\nto their generated `cc_library` counterparts when dependning on them from a\ncc module. Instead, directly depend on the generated module by prefixing the\nmodule name with `lib`. For example, depending on the following module:\n\n```\nsysprop_library {\n    name: \"foo\",\n    srcs: [\"foo.sysprop\"],\n}\n```\n\nfrom a module named `bar` can be done like so:\n\n```\ncc_library {\n    name: \"bar\",\n    srcs: [\"bar.cc\"],\n    deps: [\"libfoo\"],\n}\n```\n\nFailure to do this will result in an error about a missing variant.\n\n## Gensrcs starts disallowing depfile property\n\nTo migrate all gensrcs to Bazel, we are restricting the use of depfile property\nbecause Bazel requires specifying the dependencies directly.\n\nTo fix existing uses, remove depfile and directly specify all the dependencies\nin .bp files. For example:\n\n```\ngensrcs {\n    name: \"framework-cppstream-protos\",\n    tools: [\n        \"aprotoc\",\n        \"protoc-gen-cppstream\",\n    ],\n    cmd: \"mkdir -p $(genDir)/$(in) \" +\n        \"&& $(location aprotoc) \" +\n        \"  --plugin=$(location protoc-gen-cppstream) \" +\n        \"  -I . \" +\n        \"  $(in) \",\n    srcs: [\n        \"bar.proto\",\n    ],\n    output_extension: \"srcjar\",\n}\n```\nwhere `bar.proto` imports `external.proto` would become\n\n```\ngensrcs {\n    name: \"framework-cppstream-protos\",\n    tools: [\n        \"aprotoc\",\n        \"protoc-gen-cpptream\",\n    ],\n    tool_files: [\n        \"external.proto\",\n    ],\n    cmd: \"mkdir -p $(genDir)/$(in) \" +\n        \"&& $(location aprotoc) \" +\n        \"  --plugin=$(location protoc-gen-cppstream) \" +\n        \"  $(in) \",\n    srcs: [\n        \"bar.proto\",\n    ],\n    output_extension: \"srcjar\",\n}\n```\nas in https://android-review.googlesource.com/c/platform/frameworks/base/+/2125692/.\n\n`BUILD_BROKEN_DEPFILE` can be used to allowlist usage of depfile in `gensrcs`.\n\nIf `depfile` is needed for generating javastream proto, `java_library` with `proto.type`\nset `stream` is the alternative solution. Sees\nhttps://android-review.googlesource.com/c/platform/packages/modules/Permission/+/2118004/\nfor an example.\n\n## Genrule starts disallowing directory inputs\n\nTo better specify the inputs to the build, we are restricting use of directories\nas inputs to genrules.\n\nTo fix existing uses, change inputs to specify the inputs and update the command\naccordingly. For example:\n\n```\ngenrule: {\n    name: \"foo\",\n    srcs: [\"bar\"],\n    cmd: \"cp $(location bar)/*.xml $(gendir)\",\n    ...\n}\n```\n\nwould become\n\n```\ngenrule: {\n    name: \"foo\",\n    srcs: [\"bar/*.xml\"],\n    cmd: \"cp $(in) $(gendir)\",\n    ...\n}\n```\n\n`BUILD_BROKEN_INPUT_DIR_MODULES` can be used to allowlist specific directories\nwith genrules that have input directories.\n\n## Dexpreopt starts enforcing `<uses-library>` checks (for Java modules)\n\nIn order to construct correct class loader context for dexpreopt, build system\nneeds to know about the shared library dependencies of Java modules listed in\nthe `<uses-library>` tags in the manifest. Since the build system does not have\naccess to the manifest contents, that information must be present in the build\nfiles. In simple cases Soong is able to infer it from its knowledge of Java SDK\nlibraries and the `libs` property in Android.bp, but in more complex cases it is\nnecessary to add the missing information in Android.bp/Android.mk manually.\n\nTo specify a list of libraries for a given modules, use:\n\n* Android.bp properties: `uses_libs`, `optional_uses_libs`\n* Android.mk variables: `LOCAL_USES_LIBRARIES`, `LOCAL_OPTIONAL_USES_LIBRARIES`\n\nIf a library is in `libs`, it usually should *not* be added to the above\nproperties, and Soong should be able to infer the `<uses-library>` tag. But\nsometimes a library also needs additional information in its\nAndroid.bp/Android.mk file (e.g. when it is a `java_library` rather than a\n`java_sdk_library`, or when the library name is different from its module name,\nor when the module is defined in Android.mk rather than Android.bp). In such\ncases it is possible to tell the build system that the library provides a\n`<uses-library>` with a given name (however, this is discouraged and will be\ndeprecated in the future, and it is recommended to fix the underlying problem):\n\n* Android.bp property: `provides_uses_lib`\n* Android.mk variable: `LOCAL_PROVIDES_USES_LIBRARY`\n\nIt is possible to disable the check on a per-module basis. When doing that it is\nalso recommended to disable dexpreopt, as disabling a failed check will result\nin incorrect class loader context recorded in the .odex file, which will cause\nclass loader context mismatch and dexopt at first boot.\n\n* Android.bp property: `enforce_uses_lib`\n* Android.mk variable: `LOCAL_ENFORCE_USES_LIBRARIES`\n\nFinally, it is possible to globally disable the check:\n\n* For a given product: `PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true`\n* On the command line: `RELAX_USES_LIBRARY_CHECK=true`\n\nThe environment variable overrides the product variable, so it is possible to\ndisable the check for a product, but quickly re-enable it for a local build.\n\n## `LOCAL_REQUIRED_MODULES` requires listed modules to exist {#BUILD_BROKEN_MISSING_REQUIRED_MODULES}\n\nModules listed in `LOCAL_REQUIRED_MODULES`, `LOCAL_HOST_REQUIRED_MODULES` and\n`LOCAL_TARGET_REQUIRED_MODULES` need to exist unless `ALLOW_MISSING_DEPENDENCIES`\nis set.\n\nTo temporarily relax missing required modules check, use:\n\n`BUILD_BROKEN_MISSING_REQUIRED_MODULES := true`\n\n## Changes in system properties settings\n\n### Product variables\n\nSystem properties for each of the partition is supposed to be set via following\nproduct config variables.\n\nFor system partition,\n\n* `PRODUCT_SYSTEM_PROPERTIES`\n* `PRODUCT_SYSTEM_DEFAULT_PROPERTIES` is highly discouraged. Will be deprecated.\n\nFor vendor partition,\n\n* `PRODUCT_VENDOR_PROPERTIES`\n* `PRODUCT_PROPERTY_OVERRIDES` is highly discouraged. Will be deprecated.\n* `PRODUCT_DEFAULT_PROPERTY_OVERRIDES` is also discouraged. Will be deprecated.\n\nFor odm partition,\n\n* `PRODUCT_ODM_PROPERTIES`\n\nFor system_ext partition,\n\n* `PRODUCT_SYSTEM_EXT_PROPERTIES`\n\nFor product partition,\n\n* `PRODUCT_PRODUCT_PROPERTIES`\n\n### Duplication is not allowed within a partition\n\nFor each partition, having multiple sysprop assignments for the same name is\nprohibited. For example, the following will now trigger an error:\n\n`PRODUCT_VENDOR_PROPERTIES += foo=true foo=false`\n\nHaving duplication across partitions are still allowed. So, the following is\nnot an error:\n\n`PRODUCT_VENDOR_PROPERTIES += foo=true`\n`PRODUCT_SYSTEM_PROPERTIES += foo=false`\n\nIn that case, the final value is determined at runtime. The precedence is\n\n* product\n* odm\n* vendor\n* system_ext\n* system\n\nSo, `foo` becomes `true` because vendor has higher priority than system.\n\nTo temporarily turn the build-time restriction off, use\n\n`BUILD_BROKEN_DUP_SYSPROP := true`\n\n### Optional assignments\n\nSystem properties can now be set as optional using the new syntax:\n\n`name ?= value`\n\nThen the system property named `name` gets the value `value` only when there\nis no other non-optional assignments having the same name. For example, the\nfollowing is allowed and `foo` gets `true`\n\n`PRODUCT_VENDOR_PROPERTIES += foo=true foo?=false`\n\nNote that the order between the optional and the non-optional assignments\ndoesn't matter. The following gives the same result as above.\n\n`PRODUCT_VENDOR_PROPERTIES += foo?=false foo=true`\n\nOptional assignments can be duplicated and in that case their order matters.\nSpecifically, the last one eclipses others.\n\n`PRODUCT_VENDOR_PROPERTIES += foo?=apple foo?=banana foo?=mango`\n\nWith above, `foo` becomes `mango` since its the last one.\n\nNote that this behavior is different from the previous behavior of preferring\nthe first one. To go back to the original behavior for compatability reason,\nuse:\n\n`BUILD_BROKEN_DUP_SYSPROP := true`\n\n## ELF prebuilts in `PRODUCT_COPY_FILES` {#BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES}\n\nELF prebuilts in `PRODUCT_COPY_FILES` that are installed into these paths are an\nerror:\n\n* `<partition>/bin/*`\n* `<partition>/lib/*`\n* `<partition>/lib64/*`\n\nDefine prebuilt modules and add them to `PRODUCT_PACKAGES` instead.\nTo temporarily relax this check and restore the behavior prior to this change,\nset `BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES := true` in `BoardConfig.mk`.\n\n## COPY_HEADERS usage now produces warnings {#copy_headers}\n\nWe've considered `BUILD_COPY_HEADERS`/`LOCAL_COPY_HEADERS` to be deprecated for\na long time, and the places where it's been able to be used have shrinked over\nthe last several releases. Equivalent functionality is not available in Soong.\n\nSee the [build/soong/docs/best_practices.md#headers] for more information about\nhow best to handle headers in Android.\n\n## `m4` is not available on `$PATH`\n\nThere is a prebuilt of it available in prebuilts/build-tools, and a make\nvariable `M4` that contains the path.\n\nBeyond the direct usage, whenever you use bison or flex directly, they call m4\nbehind the scene, so you must set the M4 environment variable (and depend upon\nit for incremental build correctness):\n\n```\n$(intermediates)/foo.c: .KATI_IMPLICIT_OUTPUTS := $(intermediates)/foo.h\n$(intermediates)/foo.c: $(LOCAL_PATH)/foo.y $(M4) $(BISON) $(BISON_DATA)\n\tM4=$(M4) $(BISON) ...\n```\n\n## Rules executed within limited environment\n\nWith `ALLOW_NINJA_ENV=false` (soon to be the default), ninja, and all the\nrules/actions executed within it will only have access to a limited number of\nenvironment variables. Ninja does not track when environment variables change\nin order to trigger rebuilds, so changing behavior based on arbitrary variables\nis not safe with incremental builds.\n\nKati and Soong can safely use environment variables, so the expectation is that\nyou'd embed any environment variables that you need to use within the command\nline generated by those tools. See the [export section](#export_keyword) below\nfor examples.\n\nFor a temporary workaround, you can set `ALLOW_NINJA_ENV=true` in your\nenvironment to restore the previous behavior, or set\n`BUILD_BROKEN_NINJA_USES_ENV_VAR := <var> <var2> ...` in your `BoardConfig.mk`\nto allow specific variables to be passed through until you've fixed the rules.\n\n## LOCAL_C_INCLUDES outside the source/output trees are an error {#BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS}\n\nInclude directories are expected to be within the source tree (or in the output\ndirectory, generated during the build). This has been checked in some form\nsince Oreo, but now has better checks.\n\nThere's now a `BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS` variable, that when set, will\nturn these errors into warnings temporarily. I don't expect this to last more\nthan a release, since they're fairly easy to clean up.\n\nNeither of these cases are supported by Soong, and will produce errors when\nconverting your module.\n\n### Absolute paths\n\nThis has been checked since Oreo. The common reason to hit this is because a\nmakefile is calculating a path, and ran abspath/realpath/etc. This is a problem\nbecause it makes your build non-reproducible. It's very unlikely that your\nsource path is the same on every machine.\n\n### Using `../` to leave the source/output directories\n\nThis is the new check that has been added. In every case I've found, this has\nbeen a mistake in the Android.mk -- assuming that `LOCAL_C_INCLUDES` (which is\nrelative to the top of the source tree) acts like `LOCAL_SRC_FILES` (which is\nrelative to `LOCAL_PATH`).\n\nSince this usually isn't a valid path, you can almost always just remove the\noffending line.\n\n\n## `BOARD_HAL_STATIC_LIBRARIES` and `LOCAL_HAL_STATIC_LIBRARIES` are obsolete {#BOARD_HAL_STATIC_LIBRARIES}\n\nDefine proper HIDL / Stable AIDL HAL instead.\n\n* For libhealthd, use health HAL. See instructions for implementing\n  health HAL:\n\n  * [hardware/interfaces/health/2.1/README.md] for health 2.1 HAL (recommended)\n  * [hardware/interfaces/health/1.0/README.md] for health 1.0 HAL\n\n* For libdumpstate, use at least Dumpstate HAL 1.0.\n\n## PRODUCT_STATIC_BOOT_CONTROL_HAL is obsolete {#PRODUCT_STATIC_BOOT_CONTROL_HAL}\n\n`PRODUCT_STATIC_BOOT_CONTROL_HAL` was the workaround to allow sideloading with\nstatically linked boot control HAL, before shared library HALs were supported\nunder recovery. Android Q has added such support (HALs will be loaded in\npassthrough mode), and the workarounds are being removed. Targets should build\nand install the recovery variant of boot control HAL modules into recovery\nimage, similar to the ones installed for normal boot. See the change to\ncrosshatch for example of this:\n\n* [device/google/crosshatch/bootctrl/Android.bp] for `bootctrl.sdm845` building\n  rules\n* [device/google/crosshatch/device.mk] for installing `bootctrl.sdm845.recovery`\n  and `android.hardware.boot@1.0-impl.recovery` into recovery image\n\n[device/google/crosshatch/bootctrl/Android.bp]: https://android.googlesource.com/device/google/crosshatch/+/master/bootctrl/Android.bp\n[device/google/crosshatch/device.mk]: https://android.googlesource.com/device/google/crosshatch/+/master/device.mk\n\n## Deprecation of `BUILD_*` module types\n\nSee [build/make/Deprecation.md](Deprecation.md) for the current status.\n\n## `PRODUCT_HOST_PACKAGES` split from `PRODUCT_PACKAGES` {#PRODUCT_HOST_PACKAGES}\n\nPreviously, adding a module to `PRODUCT_PACKAGES` that supported both the host\nand the target (`host_supported` in Android.bp; two modules with the same name\nin Android.mk) would cause both to be built and installed. In many cases you\nonly want either the host or target versions to be built/installed by default,\nand would be over-building with both. So `PRODUCT_PACKAGES` will be changing to\njust affect target modules, while `PRODUCT_HOST_PACKAGES` is being added for\nhost modules.\n\nFunctional differences between `PRODUCT_PACKAGES` and `PRODUCT_HOST_PACKAGES`:\n\n* `PRODUCT_HOST_PACKAGES` does not have `_ENG`/`_DEBUG` variants, as that's a\n  property of the target, not the host.\n* `PRODUCT_HOST_PACKAGES` does not support `LOCAL_MODULE_OVERRIDES`.\n* `PRODUCT_HOST_PACKAGES` requires listed modules to exist, and be host\n  modules. (Unless `ALLOW_MISSING_DEPENDENCIES` is set)\n\nThis is still an active migration, so currently it still uses\n`PRODUCT_PACKAGES` to make installation decisions, but verifies that if we used\n`PRODUCT_HOST_PACKAGES`, it would trigger installation for all of the same host\npackages. This check ignores shared libraries, as those are not normally\nnecessary in `PRODUCT_*PACKAGES`, and tended to be over-built (especially the\n32-bit variants).\n\nFuture changes will switch installation decisions to `PRODUCT_HOST_PACKAGES`\nfor host modules, error when there's a host-only module in `PRODUCT_PACKAGES`,\nand do some further cleanup where `LOCAL_REQUIRED_MODULES` are still merged\nbetween host and target modules with the same name.\n\n## `*.c.arm` / `*.cpp.arm` deprecation  {#file_arm}\n\nIn Android.mk files, you used to be able to change LOCAL_ARM_MODE for each\nsource file by appending `.arm` to the end of the filename in\n`LOCAL_SRC_FILES`.\n\nSoong does not support this uncommonly used behavior, instead expecting those\nfiles to be split out into a separate static library that chooses `arm` over\n`thumb` for the entire library. This must now also be done in Android.mk files.\n\n## Windows cross-compiles no longer supported in Android.mk\n\nModules that build for Windows (our only `HOST_CROSS` OS currently) must now be\ndefined in `Android.bp` files.\n\n## `LOCAL_MODULE_TAGS := eng debug` are obsolete {#LOCAL_MODULE_TAGS}\n\n`LOCAL_MODULE_TAGS` value `eng` and `debug` are now obsolete. They allowed\nmodules to specify that they should always be installed on `-eng`, or `-eng`\nand `-userdebug` builds. This conflicted with the ability for products to\nspecify which modules should be installed, effectively making it impossible to\nbuild a stripped down product configuration that did not include those modules.\n\nFor the equivalent functionality, specify the modules in `PRODUCT_PACKAGES_ENG`\nor `PRODUCT_PACKAGES_DEBUG` in the appropriate product makefiles.\n\nCore android packages like `su` got added to the list in\n`build/make/target/product/base_system.mk`, but for device-specific modules\nthere are often better base product makefiles to use instead.\n\n## `USER` deprecation  {#USER}\n\n`USER` will soon be `nobody` in many cases due to the addition of a sandbox\naround the Android build. Most of the time you shouldn't need to know the\nidentity of the user running the build, but if you do, it's available in the\nmake variable `BUILD_USERNAME` for now.\n\nSimilarly, the `hostname` tool will also be returning a more consistent value\nof `android-build`. The real value is available as `BUILD_HOSTNAME`.\n\n## `BUILD_NUMBER` removal from Android.mk  {#BUILD_NUMBER}\n\n`BUILD_NUMBER` should not be used directly in Android.mk files, as it would\ntrigger them to be re-read every time the `BUILD_NUMBER` changes (which it does\non every build server build). If possible, just remove the use so that your\nbuilds are more reproducible. If you do need it, use `BUILD_NUMBER_FROM_FILE`:\n\n``` make\n$(LOCAL_BUILT_MODULE):\n\tmytool --build_number $(BUILD_NUMBER_FROM_FILE) -o $@\n```\n\nThat will expand out to a subshell that will read the current `BUILD_NUMBER`\nwhenever it's run. It will not re-run your command if the build number has\nchanged, so incremental builds will have the build number from the last time\nthe particular output was rebuilt.\n\n## `DIST_DIR`, `dist_goal`, and `dist-for-goals`  {#dist}\n\n`DIST_DIR` and `dist_goal` are no longer available when reading Android.mk\nfiles (or other build tasks). Always use `dist-for-goals` instead, which takes\na PHONY goal, and a list of files to copy to `$DIST_DIR`. Whenever `dist` is\nspecified, and the goal would be built (either explicitly on the command line,\nor as a dependency of something on the command line), that file will be copied\ninto `$DIST_DIR`. For example,\n\n``` make\n$(call dist-for-goals,foo,bar/baz)\n```\n\nwill copy `bar/baz` into `$DIST_DIR/baz` when `m foo dist` is run.\n\n#### FILE_NAME_TAG  {#FILE_NAME_TAG}\n\nTo embed the `BUILD_NUMBER` (or for local builds, `eng.${USER}`), include\n`FILE_NAME_TAG_PLACEHOLDER` in the destination:\n\n``` make\n# you can use dist-for-goals-with-filenametag function\n$(call dist-for-goals-with-filenametag,foo,bar.zip)\n# or use FILE_NAME_TAG_PLACEHOLDER manually\n$(call dist-for-goals,foo,bar.zip:baz-FILE_NAME_TAG_PLACEHOLDER.zip)\n```\n\nWhich will produce `$DIST_DIR/baz-1234567.zip` on build servers which set\n`BUILD_NUMBER=1234567`, or `$DIST_DIR/baz-eng.builder.zip` for local builds.\n\nIf you just want to append `BUILD_NUMBER` at the end of basename, use\n`dist-for-goals-with-filenametag` instead of `dist-for-goals`.\n\n#### Renames during copy\n\nInstead of specifying just a file, a destination name can be specified,\nincluding subdirectories:\n\n``` make\n$(call dist-for-goals,foo,bar/baz:logs/foo.log)\n```\n\nwill copy `bar/baz` into `$DIST_DIR/logs/foo.log` when `m foo dist` is run.\n\n## `.PHONY` rule enforcement  {#phony_targets}\n\nThere are several new warnings/errors meant to ensure the proper use of\n`.PHONY` targets in order to improve the speed and reliability of incremental\nbuilds.\n\n`.PHONY`-marked targets are often used as shortcuts to provide \"friendly\" names\nfor real files to be built, but any target marked with `.PHONY` is also always\nconsidered dirty, needing to be rebuilt every build. This isn't a problem for\naliases or one-off user-requested operations, but if real builds steps depend\non a `.PHONY` target, it can get quite expensive for what should be a tiny\nbuild.\n\n``` make\n...mk:42: warning: PHONY target \"out/.../foo\" looks like a real file (contains a \"/\")\n```\n\nBetween this warning and the next, we're requiring that `.PHONY` targets do not\nhave \"/\" in them, and real file targets do have a \"/\". This makes it more\nobvious when reading makefiles what is happening, and will help the build\nsystem differentiate these in the future too.\n\n``` make\n...mk:42: warning: writing to readonly directory: \"kernel-modules\"\n```\n\nThis warning will show up for one of two reasons:\n\n1. The target isn't intended to be a real file, and should be marked with\n   `.PHONY`. This would be the case for this example.\n2. The target is a real file, but it's outside the output directories. All\n   outputs from the build system should be within the output directory,\n   otherwise `m clean` is unable to clean the build, and future builds may not\n   work properly.\n\n``` make\n...mk:42: warning: real file \"out/.../foo\" depends on PHONY target \"buildbins\"\n```\n\nIf the first target isn't intended to be a real file, then it should be marked\nwith `.PHONY`, which will satisfy this warning. This isn't the case for this\nexample, as we require `.PHONY` targets not to have '/' in them.\n\nIf the second (PHONY) target is a real file, it may unnecessarily be marked\nwith `.PHONY`.\n\n### `.PHONY` and calling other build systems\n\nOne common pattern (mostly outside AOSP) that we've seen hit these warning is\nwhen building with external build systems (firmware, bootloader, kernel, etc).\nThose are often marked as `.PHONY` because the Android build system doesn't\nhave enough dependencies to know when to run the other build system again\nduring an incremental build.\n\nWe recommend to build these outside of Android, and deliver prebuilts into the\nAndroid tree instead of decreasing the speed and reliability of the incremental\nAndroid build.\n\nIn cases where that's not desired, to preserve the speed of Android\nincrementals, over-specifying dependencies is likely a better option than\nmarking it with `.PHONY`:\n\n``` make\nout/target/.../zImage: $(sort $(shell find -L $(KERNEL_SRCDIR)))\n\t...\n```\n\nFor reliability, many of these other build systems do not guarantee the same\nlevel of incremental build assurances as the Android Build is attempting to do\n-- without custom checks, Make doesn't rebuild objects when CFLAGS change, etc.\nIn order to fix this, our recommendation is to do clean builds for each of\nthese external build systems every time anything they rely on changes. For\nrelatively smaller builds (like the kernel), this may be reasonable as long as\nyou're not trying to actively debug the kernel.\n\n## `export` and `unexport` deprecation  {#export_keyword}\n\nThe `export` and `unexport` keywords are obsolete, and will throw errors when\nused.\n\nDevice specific configuration should not be able to affect common core build\nsteps -- we're looking at triggering build steps to be invalidated if the set\nof environment variables they can access changes. If device specific\nconfiguration is allowed to change those, switching devices with the same\noutput directory could become significantly more expensive than it already can\nbe.\n\nIf used during Android.mk files, and later tasks, it is increasingly likely\nthat they are being used incorrectly. Attempting to change the environment for\na single build step, and instead setting it for hundreds of thousands.\n\nIt is not recommended to just move the environment variable setting outside of\nthe build (in vendorsetup.sh, or some other configuration script or wrapper).\nWe expect to limit the environment variables that the build respects in the\nfuture, others will be cleared. (There will be methods to get custom variables\ninto the build, just not to every build step)\n\nInstead, write the export commands into the rule command lines themselves:\n\n``` make\n$(intermediates)/generated_output.img:\n\trm -rf $@\n\texport MY_ENV_A=\"$(MY_A)\"; make ...\n```\n\nIf you want to set many environment variables, and/or use them many times,\nwrite them out to a script and source the script:\n\n``` make\nenvsh := $(intermediates)/env.sh\n$(envsh):\n\trm -rf $@\n\techo 'export MY_ENV_A=\"$(MY_A)\"' >$@\n\techo 'export MY_ENV_B=\"$(MY_B)\"' >>$@\n\n$(intermediates)/generated_output.img: PRIVATE_ENV := $(envsh)\n$(intermediates)/generated_output.img: $(envsh) a/b/c/package.sh\n\trm -rf $@\n\tsource $(PRIVATE_ENV); make ...\n\tsource $(PRIVATE_ENV); a/b/c/package.sh ...\n```\n\n## Implicit make rules are obsolete {#implicit_rules}\n\nImplicit rules look something like the following:\n\n``` make\n$(TARGET_OUT_SHARED_LIBRARIES)/%_vendor.so: $(TARGET_OUT_SHARED_LIBRARIES)/%.so\n\t...\n\n%.o : %.foo\n\t...\n```\n\nThese can have wide ranging effects across unrelated modules, so they're now obsolete. Instead, use static pattern rules, which are similar, but explicitly match the specified outputs:\n\n``` make\nlibs := $(foreach lib,libfoo libbar,$(TARGET_OUT_SHARED_LIBRARIES)/$(lib)_vendor.so)\n$(libs): %_vendor.so: %.so\n\t...\n\nfiles := $(wildcard $(LOCAL_PATH)/*.foo)\ngen := $(patsubst $(LOCAL_PATH)/%.foo,$(intermediates)/%.o,$(files))\n$(gen): %.o : %.foo\n\t...\n```\n\n## Removing '/' from Valid Module Names {#name_slash}\n\nThe build system uses module names in path names in many places. Having an\nextra '/' or '../' being inserted can cause problems -- and not just build\nbreaks, but stranger invalid behavior.\n\nIn every case we've seen, the fix is relatively simple: move the directory into\n`LOCAL_MODULE_RELATIVE_PATH` (or `LOCAL_MODULE_PATH` if you're still using it).\nIf this causes multiple modules to be named the same, use unique module names\nand `LOCAL_MODULE_STEM` to change the installed file name:\n\n``` make\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := ver1/code.bin\nLOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/firmware\n...\ninclude $(BUILD_PREBUILT)\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := ver2/code.bin\nLOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/firmware\n...\ninclude $(BUILD_PREBUILT)\n```\n\nCan be rewritten as:\n\n```\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := ver1_code.bin\nLOCAL_MODULE_STEM := code.bin\nLOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/firmware/ver1\n...\ninclude $(BUILD_PREBUILT)\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := ver2_code.bin\nLOCAL_MODULE_STEM := code.bin\nLOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/firmware/ver2\n...\ninclude $(BUILD_PREBUILT)\n```\n\nYou just need to make sure that any other references (`PRODUCT_PACKAGES`,\n`LOCAL_REQUIRED_MODULES`, etc) are converted to the new names.\n\n## Valid Module Names {#name}\n\nWe've adopted lexical requirements very similar to [Bazel's\nrequirements](https://docs.bazel.build/versions/master/build-ref.html#name) for\ntarget names. Valid characters are `a-z`, `A-Z`, `0-9`, and the special\ncharacters `_.+-=,@~`. This currently applies to `LOCAL_PACKAGE_NAME`,\n`LOCAL_MODULE`, and `LOCAL_MODULE_SUFFIX`, and `LOCAL_MODULE_STEM*`.\n\nMany other characters already caused problems if you used them, so we don't\nexpect this to have a large effect.\n\n## PATH Tools {#PATH_Tools}\n\nThe build has started restricting the external host tools usable inside the\nbuild. This will help ensure that build results are reproducible across\ndifferent machines, and catch mistakes before they become larger issues.\n\nTo start with, this includes replacing the $PATH with our own directory of\ntools, mirroring that of the host PATH.  The only difference so far is the\nremoval of the host GCC tools. Anything that is not explicitly in the\nconfiguration as allowed will continue functioning, but will generate a log\nmessage. This is expected to become more restrictive over time.\n\nThe configuration is located in build/soong/ui/build/paths/config.go, and\ncontains all the common tools in use in many builds. Anything not in that list\nwill currently print a warning in the `$OUT_DIR/soong.log` file, including the\ncommand and arguments used, and the process tree in order to help locate the\nusage.\n\nIn order to fix any issues brought up by these checks, the best way to fix them\nis to use tools checked into the tree -- either as prebuilts, or building them\nas host tools during the build.\n\nAs a temporary measure, you can set `TEMPORARY_DISABLE_PATH_RESTRICTIONS=true`\nin your environment to temporarily turn off the error checks and allow any tool\nto be used (with logging). Beware that GCC didn't work well with the interposer\nused for logging, so this may not help in all cases.\n\n## Deprecating / obsoleting envsetup.sh variables in Makefiles\n\nIt is not required to source envsetup.sh before running a build. Many scripts,\nincluding a majority of our automated build systems, do not do so. Make will\ntransparently make every environment variable available as a make variable.\nThis means that relying on environment variables only set up in envsetup.sh will\nproduce different output for local users and scripted users.\n\nMany of these variables also include absolute path names, which we'd like to\nkeep out of the generated files, so that you don't need to do a full rebuild if\nyou move the source tree.\n\nTo fix this, we're marking the variables that are set in envsetup.sh as\ndeprecated in the makefiles. This will trigger a warning every time one is read\n(or written) inside Kati. Once all the warnings have been removed for a\nparticular variable, we'll switch it to obsolete, and any references will become\nerrors.\n\n### envsetup.sh variables with make equivalents\n\n| instead of                                                   | use                  |\n|--------------------------------------------------------------|----------------------|\n| OUT {#OUT}                                                   | PRODUCT_OUT          |\n| ANDROID_HOST_OUT {#ANDROID_HOST_OUT}                         | HOST_OUT             |\n| ANDROID_PRODUCT_OUT {#ANDROID_PRODUCT_OUT}                   | PRODUCT_OUT          |\n| ANDROID_HOST_OUT_TESTCASES {#ANDROID_HOST_OUT_TESTCASES}     | HOST_OUT_TESTCASES   |\n| ANDROID_TARGET_OUT_TESTCASES {#ANDROID_TARGET_OUT_TESTCASES} | TARGET_OUT_TESTCASES |\n\nAll of the make variables may be relative paths from the current directory, or\nabsolute paths if the output directory was specified as an absolute path. If you\nneed an absolute variable, convert it to absolute during a rule, so that it's\nnot expanded into the generated ninja file:\n\n``` make\n$(PRODUCT_OUT)/gen.img: my/src/path/gen.sh\n\texport PRODUCT_OUT=$$(cd $(PRODUCT_OUT); pwd); cd my/src/path; ./gen.sh -o $${PRODUCT_OUT}/gen.img\n```\n\n### ANDROID_BUILD_TOP  {#ANDROID_BUILD_TOP}\n\nIn Android.mk files, you can always assume that the current directory is the\nroot of the source tree, so this can just be replaced with '.' (which is what\n$TOP is hardcoded to), or removed entirely. If you need an absolute path, see\nthe instructions above.\n\n### Stop using PATH directly  {#PATH}\n\nThis isn't only set by envsetup.sh, but it is modified by it. Due to that it's\nrather easy for this to change between different shells, and it's not ideal to\nreread the makefiles every time this changes.\n\nIn most cases, you shouldn't need to touch PATH at all. When you need to have a\nrule reference a particular binary that's part of the source tree or outputs,\nit's preferrable to just use the path to the file itself (since you should\nalready be adding that as a dependency).\n\nDepending on the rule, passing the file path itself may not be feasible due to\nlayers of unchangable scripts/binaries. In that case, be sure to add the\ndependency, but modify the PATH within the rule itself:\n\n``` make\n$(TARGET): myscript my/path/binary\n\tPATH=my/path:$$PATH myscript -o $@\n```\n\n### Stop using PYTHONPATH directly  {#PYTHONPATH}\n\nLike PATH, this isn't only set by envsetup.sh, but it is modified by it. Due to\nthat it's rather easy for this to change between different shells, and it's not\nideal to reread the makefiles every time.\n\nThe best solution here is to start switching to Soong's python building support,\nwhich packages the python interpreter, libraries, and script all into one file\nthat no longer needs PYTHONPATH. See fontchain_lint for examples of this:\n\n* [external/fonttools/Lib/fontTools/Android.bp] for python_library_host\n* [frameworks/base/Android.bp] for python_binary_host\n* [frameworks/base/data/fonts/Android.mk] to execute the python binary\n\nIf you still need to use PYTHONPATH, do so within the rule itself, just like\npath:\n\n``` make\n$(TARGET): myscript.py $(sort $(shell find my/python/lib -name '*.py'))\n\tPYTHONPATH=my/python/lib:$$PYTHONPATH myscript.py -o $@\n```\n### Stop using PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE directly {#PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE}\n\nSpecify Framework Compatibility Matrix Version in device manifest by adding a `target-level`\nattribute to the root element `<manifest>`. If `PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE`\nis 26 or 27, you can add `\"target-level\"=\"1\"` to your device manifest instead.\n\n### Stop using USE_CLANG_PLATFORM_BUILD {#USE_CLANG_PLATFORM_BUILD}\n\nClang is the default and only supported Android compiler, so there is no reason\nfor this option to exist.\n\n### Stop using clang property\n\nThe clang property has been deleted from Soong. To fix any build errors, remove the clang\nproperty from affected Android.bp files using bpmodify.\n\n\n``` make\ngo run bpmodify.go -w -m=module_name -remove-property=true -property=clang filepath\n```\n\n`BUILD_BROKEN_CLANG_PROPERTY` can be used as temporarily workaround\n\n\n### Stop using clang_cflags and clang_asflags\n\nclang_cflags and clang_asflags are deprecated.\nTo fix any build errors, use bpmodify to either\n    - move the contents of clang_asflags/clang_cflags into asflags/cflags or\n    - delete clang_cflags/as_flags as necessary\n\nTo Move the contents:\n``` make\ngo run bpmodify.go -w -m=module_name -move-property=true -property=clang_cflags -new-location=cflags filepath\n```\n\nTo Delete:\n``` make\ngo run bpmodify.go -w -m=module_name -remove-property=true -property=clang_cflags filepath\n```\n\n`BUILD_BROKEN_CLANG_ASFLAGS` and `BUILD_BROKEN_CLANG_CFLAGS` can be used as temporarily workarounds\n\n### Other envsetup.sh variables  {#other_envsetup_variables}\n\n* ANDROID_TOOLCHAIN\n* ANDROID_TOOLCHAIN_2ND_ARCH\n* ANDROID_DEV_SCRIPTS\n* ANDROID_EMULATOR_PREBUILTS\n* ANDROID_PRE_BUILD_PATHS\n\nThese are all exported from envsetup.sh, but don't have clear equivalents within\nthe makefile system. If you need one of them, you'll have to set up your own\nversion.\n\n## Soong config variables\n\n### Soong config string variables must list all values they can be set to\n\nIn order to facilitate the transition to bazel, all soong_config_string_variables\nmust only be set to a value listed in their `values` property, or an empty string.\nIt is a build error otherwise.\n\nExample Android.bp:\n```\nsoong_config_string_variable {\n    name: \"my_string_variable\",\n    values: [\n        \"foo\",\n        \"bar\",\n    ],\n}\n\nsoong_config_module_type {\n    name: \"my_cc_defaults\",\n    module_type: \"cc_defaults\",\n    config_namespace: \"my_namespace\",\n    variables: [\"my_string_variable\"],\n    properties: [\n        \"shared_libs\",\n        \"static_libs\",\n    ],\n}\n```\nProduct config:\n```\n$(call soong_config_set,my_namespace,my_string_variable,baz) # Will be an error as baz is not listed in my_string_variable's values.\n```\n\n[build/soong/Changes.md]: https://android.googlesource.com/platform/build/soong/+/master/Changes.md\n[build/soong/docs/best_practices.md#headers]: https://android.googlesource.com/platform/build/soong/+/master/docs/best_practices.md#headers\n[external/fonttools/Lib/fontTools/Android.bp]: https://android.googlesource.com/platform/external/fonttools/+/master/Lib/fontTools/Android.bp\n[frameworks/base/Android.bp]: https://android.googlesource.com/platform/frameworks/base/+/master/Android.bp\n[frameworks/base/data/fonts/Android.mk]: https://android.googlesource.com/platform/frameworks/base/+/master/data/fonts/Android.mk\n[hardware/interfaces/health/1.0/README.md]: https://android.googlesource.com/platform/hardware/interfaces/+/master/health/1.0/README.md\n[hardware/interfaces/health/2.1/README.md]: https://android.googlesource.com/platform/hardware/interfaces/+/master/health/2.1/README.md\n"
  },
  {
    "path": "CleanSpec.mk",
    "content": "# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# If you don't need to do a full clean build but would like to touch\n# a file or delete some intermediate files, add a clean step to the end\n# of the list.  These steps will only be run once, if they haven't been\n# run before.\n#\n# E.g.:\n#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)\n#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)\n#\n# Always use \"touch -c\" and \"rm -f\" or \"rm -rf\" to gracefully deal with\n# files that are missing or have been moved.\n#\n# Use $(PRODUCT_OUT) to get to the \"out/target/product/blah/\" directory.\n# Use $(OUT_DIR) to refer to the \"out\" directory.\n#\n# If you need to re-do something that's already mentioned, just copy\n# the command and add it to the bottom of the list.  E.g., if a change\n# that you made last week required touching a file and a change you\n# made today requires touching the same file, just copy the old\n# touch step and add it to the end of the list.\n#\n# ************************************************\n# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST\n# ************************************************\n\n# For example:\n#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)\n#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)\n#$(call add-clean-step, find $(OUT_DIR) -type f -name \"IGTalkSession*\" -print0 | xargs -0 rm -f)\n#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)\n\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libmediaplayerservice_intermediates)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libmedia_jni_intermediates)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libstagefright_omx_intermediates)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/android-info.txt)\n$(call add-clean-step, find $(PRODUCT_OUT) -name \"*.apk\" | xargs rm)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/*/LINKED)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/*.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/*.so)\n$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/iself)\n$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/lsd)\n$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/apriori)\n$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/isprelinked)\n$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/soslim)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/*.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/*.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/YouTube*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libstagefright_intermediates)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libstagefright_omx_intermediates)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/librtp_jni_intermediates)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/android-info.txt)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/JAVA_LIBRARIES/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libbcinfo_intermediates)\n\n# ICS MR2!!!!!!!!!!!!\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libbcinfo_intermediates)\n\n# WAIT, I MEAN JELLY BEAN!!!!!!!!!!!!\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Changing where ro.carrier value is instantiated for system/build.prop\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Now we switched to build against Mac OS X SDK 10.6\n$(call add-clean-step, rm -rf $(OUT_DIR)/host/darwin-x86/obj)\n\n$(call add-clean-step, rm -f $(OUT_DIR)/versions_checked.mk)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o)\n\n# JB MR2!!!!!!!  AND *NO*, THIS WILL NOT BE K-WHATEVER.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Start of \"K\" development!\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# GCC 4.7\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o)\n\n# Wait, back to some JB development!\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# And on to KLP...\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# KLP now based off API 18.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# Clean up around the /system/app -> /system/priv-app migration\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n\n# Clean up old location of generated Java files from aidl\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src)\n\n# Clean up ApplicationsProvider which is being removed.\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/ApplicationsProvider_intermediates)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/ApplicationsProvider.apk)\n\n# Clean up Moto OMA DM client which isn't ready yet.\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.plugin.dev_intermediates)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.plugin.diagmon_intermediates)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.pluginhelper_intermediates)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.plugin_intermediates)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.service.api_intermediates)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/DMService_intermediates)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/SprintDM_intermediates)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DMService.apk)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/SprintDM.apk)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/omadm)\n\n# GCC 4.8\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o)\n\n# KLP I mean KitKat now API 19.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# 4.4.1\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# 4.4.2\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# \"L\" and beyond.\n# Make libart the default runtime\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Rename persist.sys.dalvik.vm.lib to allow new default\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# KKWT development\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# L development\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# L development\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# Add ro.product.cpu.abilist{32,64} to build.prop.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Unset TARGET_PREFER_32_BIT_APPS for 64 bit targets.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Adding dalvik.vm.dex2oat-flags to eng builds\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Unset TARGET_PREFER_32_BIT_APPS for 64 bit targets.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Switching the x86 emulator over to a 64 bit primary zygote.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n\n# Rename persist.sys.dalvik.vm.lib.1 to allow new default\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Switching PRODUCT_RUNTIMES default for some devices\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Switching to 32-bit-by-default host multilib build\n$(call add-clean-step, rm -rf $(HOST_OUT_INTERMEDIATES))\n\n# KKWT has become API 20\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# ims-common.jar added to BOOTCLASSPATH\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/init.environ.rc_intermediates)\n\n# Change ro.zygote for core_64_bit.mk from zygote32_64 to zygote64_32\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)\n\n# Adding dalvik.vm.dex2oat-Xms, dalvik.vm.dex2oat-Xmx\n# dalvik.vm.image-dex2oat-Xms, and dalvik.vm.image-dex2oat-Xmx\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system)\n\n# Switch host builds to Clang by default\n$(call add-clean-step, rm -rf $(OUT_DIR)/host)\n\n# Adding dalvik.vm.dex2oat-filter\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)\n\n# API 21?\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# API 21!\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# API 22!\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# Move to libc++ as the default STL.\n$(call add-clean-step, rm -rf $(OUT_DIR))\n\n# dex2oat instruction-set changes\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)\n\n# Make GNU++11 the default standard version. This requires a cleanspec because\n# char16_t/char32_t will be real types now instead of typedefs, which means\n# an ABI change since the names will mangle differently.\n$(call add-clean-step, rm -rf $(OUT_DIR))\n\n# 5.1!\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# Remove ro.product.locale.language/country and add ro.product.locale\n# instead.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# On to MNC\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# Adding dalvik.vm.usejit\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)\n\n# Rename dalvik.vm.usejit to debug.dalvik.vm.usejit\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)\n\n# Revert rename dalvik.vm.usejit to debug.dalvik.vm.usejit\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)\n\n# Change from interpret-only to verify-at-runtime.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)\n\n# New York, New York!\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# 23 is becoming alive!!!\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n\n# Change PLATFORM_VERSION from NYC to N\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)\n\n# $(PRODUCT_OUT)/recovery/root/sdcard goes from symlink to folder.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sdcard)\n\n# Add BOARD_USES_SYSTEM_OTHER_ODEX\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/*)\n\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/previous_overlays.txt)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/current_packages.txt)\n\n$(call add-clean-step, rm -rf $(HOST_OUT_INTERMEDIATES)/include)\n\n$(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src)\n$(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/previous_gen_java_config.mk)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/current_gen_java_config.mk)\n\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*/package-res.apk)\n$(call add-clean-step, rm -rf $(TARGET_OUT_INTERMEDIATES)/APPS/*/package-res.apk)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src)\n\n$(call add-clean-step, rm -rf $(HOST_OUT_TESTCASES))\n$(call add-clean-step, rm -rf $(TARGET_OUT_TESTCASES))\n\n$(call add-clean-step, rm -rf $(TARGET_OUT_ETC)/init)\n\n# Libraries are moved from {system|vendor}/lib to ./lib/framework, ./lib/vndk, etc.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/lib*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/lib*)\n\n# Revert that move\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/lib*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/lib*)\n\n# Sanitized libraries now live in a different location.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/lib*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/vendor/lib*)\n\n# Soong module variant change, remove obsolete intermediates\n$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates)\n\n# Version checking moving to Soong\n$(call add-clean-step, rm -rf $(OUT_DIR)/versions_checked.mk)\n\n# Vendor tests were being installed into /vendor/bin accidentally\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/nativetest*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/nativetest*)\n\n# Jack is no longer the default compiler, remove the intermediates\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/*/*/classes*.jack)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/*/*/jack*)\n\n# Move adbd from $(PRODUCT_OUT)/root/sbin to $(PRODUCT_OUT)/system/bin\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin/adbd)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/sbin/adbd)\n\n# Soong linux -> linux_glibc rename\n$(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -name 'linux_x86*' | xargs rm -rf)\n$(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -name 'linux_common*' | xargs rm -rf)\n\n# Remove old aidl/logtags files that may be in the generated source directory\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/*/*_intermediates/src)\n$(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/*/*_intermediates/java-source-list)\n$(call add-clean-step, rm -rf $(OUT_DIR)/host/common/obj/*/*_intermediates/src)\n$(call add-clean-step, rm -f $(OUT_DIR)/host/common/obj/*/*_intermediates/java-source-list)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*/flat-res)\n\n# Remove old VNDK directories without version\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk-sp)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/vndk)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/vndk-sp)\n\n# Remove old dex output directories\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/with-local/)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/no-local/)\n$(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/with-local/)\n$(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/no-local/)\n\n# Remove legacy VINTF metadata files\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/manifest.xml)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/manifest.xml)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/manifest.xml)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/compatibility_matrix.xml)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/compatibility_matrix.xml)\n\n# Remove DisplayCutoutEmulation overlays\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutoutEmulationWide)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutoutEmulationNarrow)\n\n# Remove obsolete intermedates src files\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/src/RenderScript.stamp*)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/java-source-list)\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/java-source-list)\n$(call add-clean-step, rm -rf $(OUT_DOCS)/*-timestamp)\n\n$(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/APPS/*_intermediates/src)\n$(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/JAVA_LIBRARIES/*_intermediates/src)\n$(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/APPS/*_intermediates/java-source-list)\n$(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/JAVA_LIBRARIES/*_intermediates/java-source-list)\n\n# Remove stale init.noenforce.rc\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/gsi/init.noenforce.rc)\n\n# Clean up Launcher3 which has been replaced with Launcher3QuickStep\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/Launcher3)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/Launcher3)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Launcher3_intermediates)\n\n# Remove old merged AndroidManifest.xml location\n$(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/AndroidManifest.xml)\n\n$(call add-clean-step, find $(PRODUCT_OUT) -type f -name \"vr_hwc*\" -print0 | xargs -0 rm -f)\n\n$(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/.intermediates/system/vold)\n\n# Remove product-services related files / images\n$(call add-clean-step, find $(PRODUCT_OUT) -type f -name \"*product-services*\" -print0 | xargs -0 rm -rf)\n$(call add-clean-step, find $(PRODUCT_OUT) -type d -name \"*product-services*\" -print0 | xargs -0 rm -rf)\n$(call add-clean-step, find $(PRODUCT_OUT) -type l -name \"*product-services*\" -print0 | xargs -0 rm -rf)\n\n# Remove obsolete recovery etc files\n$(call add-clean-step, rm -rf $(TARGET_RECOVERY_ROOT_OUT)/etc)\n\n# Remove *_OUT_INTERMEDIATE_LIBRARIES\n$(call add-clean-step, rm -rf $(addsuffix /lib,\\\n$(HOST_OUT_INTERMEDIATES) $(2ND_HOST_OUT_INTERMEDIATES) \\\n$(HOST_CROSS_OUT_INTERMEDIATES) $(2ND_HOST_CROSS_OUT_INTERMEDIATES) \\\n$(TARGET_OUT_INTERMEDIATES) $(2ND_TARGET_OUT_INTERMEDIATES)))\n\n# Remove strip.sh intermediates to save space\n$(call add-clean-step, find $(OUT_DIR) \\( -name \"*.so.debug\" -o -name \"*.so.dynsyms\" -o -name \"*.so.funcsyms\" -o -name \"*.so.keep_symbols\" -o -name \"*.so.mini_debuginfo.xz\" \\) -print0 | xargs -0 rm -f)\n\n# Clean up old ninja files\n$(call add-clean-step, rm -f $(OUT_DIR)/build-*-dist*.ninja)\n\n$(call add-clean-step, rm -f $(HOST_OUT)/*ts/host-libprotobuf-java-*.jar)\n\n$(call add-clean-step, find $(OUT_DIR)/target/product/mainline_arm64/system -type f -name \"*.*dex\" -print0 | xargs -0 rm -f)\n\n# Clean up aidegen\n$(call add-clean-step, rm -f $(HOST_OUT)/bin/aidegen)\n\n# Remove perfprofd\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/perfprofd)\n\n# Remove incorrectly created directories in the source tree\n$(call add-clean-step, find system/app system/priv-app system/framework system_other -depth -type d -print0 | xargs -0 rmdir)\n$(call add-clean-step, rm -f .d)\n\n# Remove obsolete apps\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)\n\n# Remove corrupt generated rule due to using toybox's sed\n$(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/.intermediates/system/core/init/generated_stub_builtin_function_map)\n\n# Clean up core JNI libraries moved to runtime apex\n$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libjavacore.so)\n$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libopenjdk.so)\n$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libexpat.so)\n\n# Merge product_services into product\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product_services)\n\n# Clean up old location of hiddenapi files\n$(call add-clean-step, rm -f $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi*)\n\n# Clean up previous default location of RROs\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay)\n\n# Remove ART artifacts installed only by modules `art-runtime` and\n# `art-tools` in /system on target.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dalvikvm)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dalvikvm32)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dalvikvm64)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dex2oat)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dex2oatd)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexdiag)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexdump)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexlist)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexoptanalyzer)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexoptanalyzerd)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/oatdump)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/profman)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/profmand)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libadbconnection.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libadbconnectiond.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart-compiler.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartd-compiler.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart-dexlayout.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartd-dexlayout.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart-disassembler.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartd.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartbase.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartbased.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfile.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfiled.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfile_external.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfile_support.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdt_fd_forward.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdt_socket.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libjdwp.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libnpt.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkd.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvm.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvmd.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvmti.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvmtid.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libprofile.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libprofiled.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libtombstoned_client.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libvixl.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libvixld.so)\n\n# Clean up old location of dexpreopted boot jars\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/dex_bootjars)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/dex_bootjars_input)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libnpt.so)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*)\n\n# Clean up old testcase files\n$(call add-clean-step, rm -rf $(TARGET_OUT_TESTCASES)/*)\n$(call add-clean-step, rm -rf $(HOST_OUT_TESTCASES)/*)\n$(call add-clean-step, rm -rf $(HOST_CROSS_OUT_TESTCASES)/*)\n$(call add-clean-step, rm -rf $(TARGET_OUT_DATA)/*)\n$(call add-clean-step, rm -rf $(HOST_OUT)/vts/*)\n$(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts-tradefed.jar)\n\n# Clean up old location of system_other.avbpubkey\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/security/avb/)\n\n# Clean up bufferhub files\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/hw/android.frameworks.bufferhub@1.0-service)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/android.frameworks.bufferhub@1.0-service.rc)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/super.img)\n\n$(call add-clean-step, find $(PRODUCT_OUT) -type f -name \"generated_*_image_info.txt\" -print0 | xargs -0 rm -f)\n\n# Clean up libicuuc.so and libicui18n.so\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libicu*)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/target/common/obj/framework.aidl)\n\n# Clean up adb_debug.propr\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/adb_debug.prop)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libjavacrypto.so)\n\n# Clean up old verity tools.\n$(call add-clean-step, rm -rf $(HOST_OUT_JAVA_LIBRARIES)/BootSignature.jar)\n$(call add-clean-step, rm -rf $(HOST_OUT_JAVA_LIBRARIES)/VeritySigner.jar)\n$(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/build_verity_metadata.py)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libc_malloc*)\n\n# Clean up old location of soft OMX plugins\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libstagefright_soft*)\n\n# Move odm build.prop to /odm/etc/.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/odm/build.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/odm/build.prop)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex)\n\n# Remove libcameraservice and libcamera_client from base_system\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libcameraservice.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libcamera_client.so)\n\n# Move product and system_ext to root for emulators\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/product)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/system_ext)\n\n# link_type and jni_link_type files are no longer needed\n$(call add-clean-step, find $(OUT_DIR) -type f -name \"*link_type\" -print0 | xargs -0 rm -f)\n\n# import_includes and export_includes files are no longer needed\n$(call add-clean-step, find $(OUT_DIR) -type f -name \"import_includes\" -o -name \"export_includes\" -print0 | xargs -0 rm -f)\n\n# Recreate product and system_ext partitions for emulator\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*product*)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*system_ext*)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/product)\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/system_ext)\n\n# Move GSI-specific files from /system to /system/system_ext\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/init.gsi.rc)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/config/)\n\n# Move fuzz targets from /data/fuzz/* to /data/fuzz/<arch>/* for device, and\n# /fuzz/* to /fuzz/<arch>/* on host.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/fuzz/*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/data/fuzz/*)\n$(call add-clean-step, rm -rf $(HOST_OUT)/fuzz/*)\n$(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/host/*/fuzz/*)\n\n# Change file layout of system_other\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex)\n\n# Migrate preopt files to system_other for some devices\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/*/*app/*/oat)\n\n# Migrate preopt files from system_other for some devices\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other)\n\n# Remove Android Core Library artifacts from the system partition, now\n# that they live in the ART APEX (b/142944799).\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*.jar)\n\n# Remove symlinks for VNDK apexes\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/vndk-*)\n\n# Switch to symlinks for VNDK libs\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/vndk-*)\n\n# Remove Android Core Library artifacts from the system partition\n# again, as the original change removing them was reverted.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*.jar)\n\n# Remove cas@1.1 from the vendor partition\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/bin/hw/android.hardware.cas@1.1*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/init/android.hardware.cas@1.1*)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/vintf/manifest/android.hardware.cas@1.1*)\n\n# Remove com.android.cellbroadcast apex for Go devices\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.cellbroadcast.apex)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/apex/com.android.cellbroadcast)\n\n# Remove CellBroadcastLegacyApp for Go devices\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/CellBroadcastLegacyApp)\n\n# Remove MediaProvider after moving into APEX\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/MediaProvider)\n\n# The core image variant has been renamed to \"\"\n$(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -type d -name \"android_*_core*\" -print0 | xargs -0 rm -rf)\n\n# Remove 'media' command\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/media)\n\n# Remove CtsShim apks from system partition, since the have been moved inside\n# the cts shim apex. Also remove the cts shim apex prebuilt since it has been\n# removed in flattened apexs configurations.\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/CtsShimPrivPrebuilt)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/CtsShimPrebuilt)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.apex.cts.shim.apex)\n\n# Remove vendor and recovery variants, the directory name has changed.\n$(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -type d -name \"android_*_recovery*\" -print0 | xargs -0 rm -rf)\n$(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -type d -name \"android_*_vendor*\" -print0 | xargs -0 rm -rf)\n\n# Remove PermissionController after moving into APEX\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/*PermissionController)\n\n# Clean up VTS-Core and VTS10 related artifacts.\n$(call add-clean-step, rm -rf $(HOST_OUT)/vts-core/*)\n$(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts-core-tradefed.jar)\n$(call add-clean-step, rm -rf $(HOST_OUT)/vts10/*)\n$(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts10-tradefed.jar)\n# Clean up VTS again as VTS-Core will be renamed to VTS \n$(call add-clean-step, rm -rf $(HOST_OUT)/vts/*)\n$(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts-tradefed.jar)\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/prop.default)\n\n# Workaround for Soong not being able to rebuild the host binary if its\n# JNI dependencies change: b/170389375\n$(call add-clean-step, rm -rf $(OUT_DIR)/soong/host/*/lib*/libconscrypt_openjdk_jni.so)\n# vendor-ramdisk renamed to vendor_ramdisk\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor-ramdisk)\n\n# Common R directory has been removed.\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R)\n\n# Most of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES\n$(call add-clean-step, rm -rf $(SOONG_HOST_OUT))\n\n# More of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES\n$(call add-clean-step, rm -rf $(SOONG_HOST_OUT))\n\n# More of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES\n$(call add-clean-step, rm -rf $(SOONG_HOST_OUT))\n\n# Last of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES\n# Don't use SOONG_HOST_OUT, it is now an alias for HOST_OUT.\n$(call add-clean-step, rm -rf $(OUT_DIR)/soong/host)\n\n# Clear out tools/metalava Bazel output dir\n$(call add-clean-step, rm -rf $(OUT_DIR)/bazel/output/execroot/__main__/bazel-out/mixed_builds_product-*/bin/tools/metalava)\n\n# Clear out rustc compiler intermediates after reverting rust compiler/linker split.\n$(call add-clean-step, find $(OUT_DIR) -name \"*.rsp.whole.a\" -print0 | xargs -0 /bin/bash -c 'rm -f $$$${@}; rm -f $$$${@/.rsp.whole.a/.rsp.a}; rm -f $$$${@/.rsp.whole.a/.rsp}')\n\n# Remove obsolete java compilation artifacts\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/)\n$(call add-clean-step, find $(OUT_DIR) -type f -name \"*.jar\" -print0 | xargs -0 rm -f)\n\n# Remove obsolete java compilation artifacts\n$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/)\n$(call add-clean-step, find $(OUT_DIR) -type f -name \"*.jar\" -print0 | xargs -0 rm -f)\n\n# Remove obsolete dexpreopt_config artifacts\n$(call add-clean-step, rm -f $(PRODUCT_OUT)/dexpreopt_config/dexpreopt.config)\n$(call add-clean-step, rm -f $(PRODUCT_OUT)/dexpreopt_config/dexpreopt_soong.config)\n\n# Clear out Soong .intermediates directory regarding removal of hashed subdir\n$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates)\n\n# ************************************************\n# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST\n# ************************************************\n"
  },
  {
    "path": "Deprecation.md",
    "content": "# Deprecation of Make\n\nWe've made significant progress converting AOSP from Make to Soong (Android.mk\nto Android.bp), and we're ready to start turning off pieces of Make. If you\nhave any problems converting, please contact us via:\n\n* The [android-building@googlegroups.com] group.\n* Our [public bug tracker](https://issuetracker.google.com/issues/new?component=381517).\n* Or privately through your existing contacts at Google.\n\n## Status\n\n[build/make/core/deprecation.mk] is the source of truth, but for easy browsing:\n\n| Module type                      | State     |\n| -------------------------------- | --------- |\n| `BUILD_AUX_EXECUTABLE`           | Obsolete  |\n| `BUILD_AUX_STATIC_LIBRARY`       | Obsolete  |\n| `BUILD_COPY_HEADERS`             | Error     |\n| `BUILD_HOST_EXECUTABLE`          | Error     |\n| `BUILD_HOST_FUZZ_TEST`           | Obsolete  |\n| `BUILD_HOST_NATIVE_TEST`         | Obsolete  |\n| `BUILD_HOST_SHARED_LIBRARY`      | Error     |\n| `BUILD_HOST_SHARED_TEST_LIBRARY` | Obsolete  |\n| `BUILD_HOST_STATIC_LIBRARY`      | Error     |\n| `BUILD_HOST_STATIC_TEST_LIBRARY` | Obsolete  |\n| `BUILD_HOST_TEST_CONFIG`         | Obsolete  |\n| `BUILD_NATIVE_BENCHMARK`         | Obsolete  |\n| `BUILD_SHARED_TEST_LIBRARY`      | Obsolete  |\n| `BUILD_STATIC_TEST_LIBRARY`      | Obsolete  |\n| `BUILD_TARGET_TEST_CONFIG`       | Obsolete  |\n| `BUILD_*`                        | Available |\n\n## Module Type Deprecation Process\n\nWe'll be turning off `BUILD_*` module types as all of the users are removed\nfrom AOSP (and Google's internal trees). The process will go something like\nthis, using `BUILD_PACKAGE` as an example:\n\n* Prerequisite: all common users of `BUILD_PACKAGE` have been removed (some\n  device-specific ones may remain).\n* `BUILD_PACKAGE` will be moved from `AVAILABLE_BUILD_MODULE_TYPES` to\n  `DEFAULT_WARNING_BUILD_MODULE_TYPES` in [build/make/core/deprecation.mk]. This\n  will make referring to `BUILD_PACKAGE` a warning.\n* Any devices that still have warnings will have\n  `BUILD_BROKEN_USES_BUILD_PACKAGE := true` added to their `BoardConfig.mk`.\n* `BUILD_PACKAGE` will be switched from `DEFAULT_WARNING_BUILD_MODULE_TYPES` to\n  `DEFAULT_ERROR_BUILD_MODULE_TYPES`, which will turn referring to\n  `BUILD_PACKAGE` into an error unless the device has overridden it.\n* At some later point, after all devices in AOSP no longer set\n  `BUILD_BROKEN_USES_BUILD_PACKAGE`, `BUILD_PACKAGE` will be moved from\n  `DEFAULT_ERROR_BUILD_MODULE_TYPES` to `OBSOLETE_BUILD_MODULE_TYPES` and the\n  code will be removed. It will no longer be possible to use `BUILD_PACKAGE`.\n\nIn most cases, we expect module types to stay in the default warning state for\nabout two weeks before becoming an error by default. Then it will spend some\namount of time in the default error state before moving to obsolete -- we'll\ntry and keep that around for a while, but other development may cause those to\nbreak, and the fix may to be to obsolete them. There is no expectation that the\n`BUILD_BROKEN_USES_BUILD_*` workarounds will work in a future release, it's a\nshort-term workaround.\n\nJust to be clear, the above process will happen on the AOSP master branch. So\nif you're following Android releases, none of the deprecation steps will be in\nAndroid Q, and the 2020 release will have jumped directly to the end for many\nmodule types.\n\n[android-building@googlegroups.com]: https://groups.google.com/forum/#!forum/android-building\n[build/make/core/deprecation.mk]: /core/deprecation.mk\n"
  },
  {
    "path": "OWNERS",
    "content": "include platform/build/soong:/OWNERS\n\n# Since this file affects all Android developers, lock it down. There is still\n# round the world timzeone coverage.\nper-file envsetup.sh = joeo@google.com, jingwen@google.com\nper-file shell_utils.sh = joeo@google.com, jingwen@google.com\n\n"
  },
  {
    "path": "PREUPLOAD.cfg",
    "content": "[Hook Scripts]\ndo_not_use_DO_NOT_MERGE = ${REPO_ROOT}/build/soong/scripts/check_do_not_merge.sh ${PREUPLOAD_COMMIT}\n\n[Builtin Hooks]\nktfmt = true\n"
  },
  {
    "path": "README.md",
    "content": "# Android Make Build System\n\nThis is the Makefile-based portion of the Android Build System.\n\nFor documentation on how to run a build, see [Usage.txt](Usage.txt)\n\nFor a list of behavioral changes useful for Android.mk writers see\n[Changes.md](Changes.md)\n\nFor an outdated reference on Android.mk files, see\n[build-system.html](/core/build-system.html). Our Android.mk files look similar,\nbut are entirely different from the Android.mk files used by the NDK build\nsystem. When searching for documentation elsewhere, ensure that it is for the\nplatform build system -- most are not.\n\nThis Makefile-based system is in the process of being replaced with [Soong], a\nnew build system written in Go. During the transition, all of these makefiles\nare read by [Kati], and generate a ninja file instead of being executed\ndirectly. That's combined with a ninja file read by Soong so that the build\ngraph of the two systems can be combined and run as one.\n\n[Kati]: https://github.com/google/kati\n[Soong]: https://android.googlesource.com/platform/build/soong/+/master\n"
  },
  {
    "path": "Usage.txt",
    "content": "Android build system usage:\n\nm [-j] [<targets>] [<variable>=<value>...]\n\n\nWays to specify what to build:\n  The common way to specify what to build is to set that information in the\n  environment via:\n\n    # Set up the shell environment.\n    source build/envsetup.sh # Run \"hmm\" after sourcing for more info\n    # Select the device and variant to target. If no argument is given, it\n    # will list choices and prompt.\n    lunch [<product>-<variant>] # Selects the device and variant to target.\n    # Invoke the configured build.\n    m [<options>] [<targets>] [<variable>=<value>...]\n\n      <product> is the device that the created image is intended to be run on.\n        This is saved in the shell environment as $TARGET_PRODUCT by `lunch`.\n      <variant> is one of \"user\", \"userdebug\", or \"eng\", and controls the\n        amount of debugging to be added into the generated image.\n        This gets saved in the shell environment as $TARGET_BUILD_VARIANT by\n          `lunch`.\n\n    Each of <options>, <targets>, and <variable>=<value> is optional.\n      If no targets are specified, the build system will build the images\n      for the configured product and variant.\n\n  A target may be a file path. For example, out/host/linux-x86/bin/adb .\n    Note that when giving a relative file path as a target, that path is\n    interpreted relative to the root of the source tree (rather than relative\n    to the current working directory).\n\n  A target may also be any other target defined within a Makefile. Run\n    `m help` to view the names of some common targets.\n\n  To view the modules and targets defined in a particular directory, look for:\n    files named *.mk (most commonly Android.mk)\n      these files are defined in Make syntax\n    files named Android.bp\n      these files are defined in Blueprint syntax\n\n  During a build, a few log files are generated in ${OUT} (or ${DIST_DIR}/logs\n    for dist builds):\n\n    verbose.log.gz\n      every command run, along with its outputs. This is similar to the\n      previous `m showcommands` option.\n    error.log\n      list of actions that failed during the build, and their outputs.\n    soong.log\n      verbose debug information from soong_ui\n\n  For now, the full (extremely large) compiled list of targets can be found\n    (after running the build once), split among these two files:\n\n    ${OUT}/build-<product>*.ninja\n    ${OUT}/soong/build.ninja\n\n    If you find yourself interacting with these files, you are encouraged to\n    provide a more convenient tool for browsing targets, and to mention the\n    tool here.\n\nTargets that adjust an existing build:\n  dist                      Copy into ${DIST_DIR} the portion of the build\n                            that must be distributed\n\nFlags\n  -j <N>                    Run <N> processes at once\n  -j                        Autodetect the number of processes to run at once,\n                            and run that many\n\nVariables\n  Variables can either be set in the surrounding shell environment or can be\n    passed as command-line arguments. For example:\n      export I_AM_A_SHELL_VAR=1\n      I_AM_ANOTHER_SHELL_VAR=2 m droid I_AM_A_MAKE_VAR=3\n  Here are some common variables and their meanings:\n    TARGET_PRODUCT          The <product> to build # as described above\n    TARGET_BUILD_VARIANT    The <variant> to build # as described above\n    DIST_DIR                The directory in which to place the distribution\n                            artifacts.\n    OUT_DIR                 The directory in which to place non-distribution\n                            artifacts.\n\n  There is not yet known a convenient method by which to discover the full\n  list of supported variables. Please mention it here when there is.\n\n"
  },
  {
    "path": "backported_fixes/Android.bp",
    "content": "// Copyright 2024 Google Inc. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n    default_team: \"trendy_team_android_media_reliability\",\n}\n\ngenrule {\n    name: \"applied_backported_fixes\",\n    tools: [\"applied_backported_fixes_property_writer\"],\n    srcs: [\":applied_backported_fix_binpbs\"],\n    out: [\"applied_backported_fixes.prop\"],\n    cmd: \"$(location applied_backported_fixes_property_writer)\" +\n        \" -p $(location applied_backported_fixes.prop)\" +\n        \" $(in)\",\n}\n\nfilegroup {\n    name: \"backported_fixes_proto_file\",\n    srcs: [\n        \"backported_fixes.proto\",\n    ],\n}\n\njava_library {\n    name: \"backported_fixes_proto\",\n    srcs: [\"backported_fixes.proto\"],\n    host_supported: true,\n    sdk_version: \"current\",\n}\n\njava_library {\n    name: \"backported_fixes_common\",\n    srcs: [\"src/java/com/android/build/backportedfixes/common/*.java\"],\n    static_libs: [\n        \"backported_fixes_proto\",\n        \"guava\",\n    ],\n    host_supported: true,\n}\n\njava_test_host {\n    name: \"backported_fixes_common_test\",\n    srcs: [\"tests/java/com/android/build/backportedfixes/common/*.java\"],\n    static_libs: [\n        \"backported_fixes_common\",\n        \"backported_fixes_proto\",\n        \"junit\",\n        \"truth\",\n        \"truth-liteproto-extension\",\n        \"truth-proto-extension\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n    test_suites: [\"general-tests\"],\n}\n\njava_library {\n    name: \"backported_fixes_main_lib\",\n    srcs: [\"src/java/com/android/build/backportedfixes/*.java\"],\n    static_libs: [\n        \"backported_fixes_common\",\n        \"backported_fixes_proto\",\n        \"jcommander\",\n        \"guava\",\n    ],\n    host_supported: true,\n}\n\njava_binary_host {\n    name: \"applied_backported_fixes_property_writer\",\n    main_class: \"com.android.build.backportedfixes.WriteBackportedFixesPropFile\",\n    static_libs: [\n        \"backported_fixes_main_lib\",\n    ],\n}\n\njava_binary_host {\n    name: \"backported_fixes_combiner\",\n    main_class: \"com.android.build.backportedfixes.CombineBackportedFixes\",\n    static_libs: [\n        \"backported_fixes_main_lib\",\n    ],\n}\n\n// Combines BackportedFix binary proto files into a single BackportedFixes binary proto file.\ngenrule_defaults {\n    name: \"default_backported_fixes_combiner\",\n    tools: [\"backported_fixes_combiner\"],\n    cmd: \"$(location backported_fixes_combiner)\" +\n        \" -o $(out)\" +\n        \" $(in)\",\n}\n\njava_test_host {\n    name: \"backported_fixes_main_lib_test\",\n    srcs: [\"tests/java/com/android/build/backportedfixes/*.java\"],\n    static_libs: [\n        \"backported_fixes_main_lib\",\n        \"backported_fixes_proto\",\n        \"junit\",\n        \"truth\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n    test_suites: [\"general-tests\"],\n}\n\n// Converts BackprotedFix text protos to binary protos\ngenrule_defaults {\n    name: \"default_backported_fix_binpbs\",\n    tools: [\"aprotoc\"],\n    tool_files: [\n        \":backported_fixes_proto_file\",\n    ],\n    cmd: \"$(location aprotoc)  \" +\n        \" --encode=com.android.build.backportedfixes.BackportedFix\" +\n        \"  $(location :backported_fixes_proto_file)\" +\n        \" < $(in)\" +\n        \" > $(out); echo $(out)\",\n}\n\ngensrcs {\n    name: \"applied_backported_fix_binpbs\",\n    defaults: [\"default_backported_fix_binpbs\"],\n    output_extension: \"binpb\",\n    srcs: [\n        \"applied_fixes/*.txtpb\",\n    ],\n}\n"
  },
  {
    "path": "backported_fixes/OWNERS",
    "content": "essick@google.com\nnchalko@google.com\nportmannc@google.com\n"
  },
  {
    "path": "backported_fixes/applied_fixes/ki350037023.txtpb",
    "content": "# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# proto-file: ../backported_fixes.proto\n# proto-message: BackportedFix\n\nknown_issue: 350037023\nalias: 1\n"
  },
  {
    "path": "backported_fixes/backported_fixes.proto",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage com.android.build.backportedfixes;\n\noption java_multiple_files = true;\n\n// A list of backported fixes.\nmessage BackportedFixes {\n  repeated BackportedFix fixes = 1;\n}\n\n// A known issue approved for reporting Build.getBackportedFixStatus\nmessage BackportedFix {\n\n  // The issue id from the public bug tracker\n  // https://issuetracker.google.com/issues/{known_issue}\n  optional int64 known_issue = 1;\n  // The alias for the known issue.\n  // 1 - 1023 are valid aliases\n  // Must be unique across all backported fixes.\n  optional int32 alias = 2;\n}\n\n"
  },
  {
    "path": "backported_fixes/src/java/com/android/build/backportedfixes/CombineBackportedFixes.java",
    "content": "\n/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.build.backportedfixes;\n\nimport com.android.build.backportedfixes.common.Parser;\n\nimport com.beust.jcommander.JCommander;\nimport com.beust.jcommander.Parameter;\nimport com.beust.jcommander.converters.FileConverter;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.List;\n\n\n/** Creates a BackportedFixes binary proto file from a list of BackportedFix proto binary files. */\npublic final class CombineBackportedFixes {\n\n    @Parameter(description = \"BackportedFix proto binary files\",\n            converter = FileConverter.class,\n            required = true)\n    List<File> fixFiles;\n    @Parameter(description = \"Write the BackportedFixes proto binary to this file\",\n            names = {\"--out\",\"-o\"},\n            converter = FileConverter.class,\n            required = true)\n    File outFile;\n\n    public static void main(String... argv) throws Exception {\n        CombineBackportedFixes main = new CombineBackportedFixes();\n        JCommander.newBuilder().addObject(main).build().parse(argv);\n        main.run();\n    }\n\n    CombineBackportedFixes() {\n    }\n\n    private void run() throws Exception {\n        try (var out = new FileOutputStream(outFile)) {\n            var fixes = Parser.parseBackportedFixFiles(fixFiles);\n            writeBackportedFixes(fixes, out);\n        }\n    }\n\n    static void writeBackportedFixes(BackportedFixes fixes, OutputStream out)\n            throws IOException {\n        fixes.writeTo(out);\n    }\n}\n"
  },
  {
    "path": "backported_fixes/src/java/com/android/build/backportedfixes/WriteBackportedFixesPropFile.java",
    "content": "\n/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.build.backportedfixes;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\nimport com.android.build.backportedfixes.common.Parser;\n\nimport com.beust.jcommander.JCommander;\nimport com.beust.jcommander.Parameter;\nimport com.beust.jcommander.converters.FileConverter;\nimport com.google.common.io.Files;\n\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.io.Writer;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n\n/**\n * Creates backported fix properties file.\n *\n * <p>Writes BitSet of backported fix aliases from a list of BackportedFix proto binary files and\n * writes the property {@value PROPERTY_NAME} to a file.\n */\npublic final class WriteBackportedFixesPropFile {\n\n    private static final String PROPERTY_NAME = \"ro.build.backported_fixes.alias_bitset.long_list\";\n    @Parameter(description = \"BackportedFix proto binary files\",\n            converter = FileConverter.class,\n            required = true)\n    List<File> fixFiles;\n    @Parameter(description = \"The file to write the property value to.\",\n            names = {\"--property_file\", \"-p\"},\n            converter = FileConverter.class,\n            required = true)\n    File propertyFile;\n\n    public static void main(String... argv) throws Exception {\n        WriteBackportedFixesPropFile main = new WriteBackportedFixesPropFile();\n        JCommander.newBuilder().addObject(main).build().parse(argv);\n        main.run();\n    }\n\n    WriteBackportedFixesPropFile() {\n    }\n\n    private void run() throws Exception {\n        try (var out = Files.newWriter(propertyFile, UTF_8)) {\n            var fixes = Parser.parseBackportedFixFiles(fixFiles);\n            writeFixesAsAliasBitSet(fixes, out);\n        }\n    }\n\n    static void writeFixesAsAliasBitSet(BackportedFixes fixes, Writer out) {\n        PrintWriter printWriter = new PrintWriter(out);\n        printWriter.println(\"# The following backported fixes have been applied\");\n        for (var f : fixes.getFixesList()) {\n            printWriter.printf(\"# https://issuetracker.google.com/issues/%d with alias %d\",\n                    f.getKnownIssue(), f.getAlias());\n            printWriter.println();\n        }\n        var bsArray = Parser.getBitSetArray(\n                fixes.getFixesList().stream().mapToInt(BackportedFix::getAlias).toArray());\n        String bsString = Arrays.stream(bsArray).mapToObj(Long::toString).collect(\n                Collectors.joining(\",\"));\n        printWriter.printf(\"%s=%s\", PROPERTY_NAME, bsString);\n        printWriter.println();\n        if (printWriter.checkError()) {\n            throw new RuntimeException(\"There was an error writing to \" + out.toString());\n        }\n    }\n}\n"
  },
  {
    "path": "backported_fixes/src/java/com/android/build/backportedfixes/common/Parser.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.build.backportedfixes.common;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport com.android.build.backportedfixes.BackportedFix;\nimport com.android.build.backportedfixes.BackportedFixes;\n\nimport com.google.common.base.Throwables;\nimport com.google.common.collect.ImmutableList;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.BitSet;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collector;\nimport java.util.stream.Collectors;\n\n\n/** Static utilities for working with {@link BackportedFixes}. */\npublic final class Parser {\n\n    /** Creates list of FileInputStreams for a list of files. */\n    public static ImmutableList<FileInputStream> getFileInputStreams(List<File> fixFiles) throws\n            FileNotFoundException {\n        var streams = ImmutableList.<FileInputStream>builder();\n        for (var f : fixFiles) {\n            streams.add(new FileInputStream(f));\n        }\n        return streams.build();\n    }\n\n    /** Converts a list of backported fix aliases into a long array representing a {@link BitSet} */\n    public static long[] getBitSetArray(int[] aliases) {\n        BitSet bs = new BitSet();\n        for (int a : aliases) {\n            bs.set(a);\n        }\n        return bs.toLongArray();\n    }\n\n    /**\n     * Creates a {@link BackportedFixes} from a list of {@link BackportedFix} binary proto streams.\n     */\n    public static BackportedFixes parseBackportedFixFiles(List<File> fixFiles)\n            throws IOException {\n        try {\n            return fixFiles.stream().map(Parser::tunelFileInputStream)\n                    .map(Parser::tunnelParse)\n                    .sorted(Comparator.comparing(BackportedFix::getKnownIssue))\n                    .collect(fixCollector());\n\n        } catch (TunnelException e) {\n            throw e.rethrow(FileNotFoundException.class, IOException.class);\n        }\n    }\n\n\n    private static Collector<BackportedFix, ?, BackportedFixes> fixCollector() {\n        return Collectors.collectingAndThen(Collectors.toList(), fixList -> {\n            var result = BackportedFixes.newBuilder();\n            result.addAllFixes(fixList);\n            return result.build();\n        });\n    }\n\n    private static FileInputStream tunelFileInputStream(File file) throws TunnelException {\n        try {\n            return new FileInputStream(file);\n        } catch (FileNotFoundException e) {\n            throw new TunnelException(e);\n        }\n    }\n\n    private static BackportedFix tunnelParse(InputStream s) throws TunnelException {\n        try {\n            var fix = BackportedFix.parseFrom(s);\n            s.close();\n            return fix;\n        } catch (IOException e) {\n            throw new TunnelException(e);\n        }\n    }\n\n    private static class TunnelException extends RuntimeException {\n        TunnelException(Exception cause) {\n            super(\"If you see this TunnelException something went wrong.  It should always be rethrown as the cause.\", cause);\n        }\n\n        <X extends Exception> RuntimeException rethrow(Class<X> exceptionClazz) throws X {\n            checkNotNull(exceptionClazz);\n            Throwables.throwIfInstanceOf(getCause(), exceptionClazz);\n            throw exception(\n                    getCause(),\n                    \"rethrow(%s) doesn't match underlying exception\", exceptionClazz);\n        }\n\n        public <X1 extends Exception, X2 extends Exception> RuntimeException rethrow(\n                Class<X1> exceptionClazz1, Class<X2> exceptionClazz2) throws X1, X2 {\n            checkNotNull(exceptionClazz1);\n            checkNotNull(exceptionClazz2);\n            Throwables.throwIfInstanceOf(getCause(), exceptionClazz1);\n            Throwables.throwIfInstanceOf(getCause(), exceptionClazz2);\n            throw exception(\n                    getCause(),\n                    \"rethrow(%s, %s) doesn't match underlying exception\",\n                    exceptionClazz1,\n                    exceptionClazz2);\n        }\n\n        private static ClassCastException exception(\n                Throwable cause, String message, Object... formatArgs) {\n            ClassCastException result = new ClassCastException(String.format(message, formatArgs));\n            result.initCause(cause);\n            return result;\n        }\n\n    }\n\n    private Parser() {\n    }\n}\n"
  },
  {
    "path": "backported_fixes/tests/java/com/android/build/backportedfixes/CombineBackportedFixesTest.java",
    "content": "\n/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.build.backportedfixes;\n\nimport com.google.common.truth.Truth;\n\nimport org.junit.Test;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\n\n/** Tests for {@link CombineBackportedFixes}. */\npublic class CombineBackportedFixesTest {\n\n\n    @Test\n    public void writeBackportedFixes_default() throws IOException {\n        // Not much of a test, but there is not much to test.\n        BackportedFixes fixes = BackportedFixes.newBuilder()\n                .addFixes(BackportedFix.newBuilder().setKnownIssue(123).build())\n                .build();\n        var result = new ByteArrayOutputStream();\n        CombineBackportedFixes.writeBackportedFixes(fixes, result);\n        Truth.assertThat(BackportedFixes.parseFrom(result.toByteArray()))\n                .isEqualTo(fixes);\n    }\n}\n"
  },
  {
    "path": "backported_fixes/tests/java/com/android/build/backportedfixes/WriteBackportedFixesPropFileTest.java",
    "content": "\n/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.build.backportedfixes;\n\nimport com.google.common.truth.Truth;\n\nimport org.junit.Test;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\n\n/** Tests for {@link WriteBackportedFixesPropFile}. */\npublic class WriteBackportedFixesPropFileTest {\n\n\n    @Test\n    public void writeFixesAsAliasBitSet_default() {\n        BackportedFixes fixes = BackportedFixes.newBuilder().build();\n        var result = new StringWriter();\n\n        WriteBackportedFixesPropFile.writeFixesAsAliasBitSet(fixes, new PrintWriter(result));\n\n        Truth.assertThat(result.toString())\n                .isEqualTo(\"\"\"\n                        # The following backported fixes have been applied\n                        ro.build.backported_fixes.alias_bitset.long_list=\n                        \"\"\");\n    }\n\n    @Test\n    public void writeFixesAsAliasBitSet_some() {\n        BackportedFixes fixes = BackportedFixes.newBuilder()\n                .addFixes(BackportedFix.newBuilder().setKnownIssue(1234L).setAlias(1))\n                .addFixes(BackportedFix.newBuilder().setKnownIssue(3L).setAlias(65))\n                .addFixes(BackportedFix.newBuilder().setKnownIssue(4L).setAlias(67))\n                .build();\n        var result = new StringWriter();\n\n        WriteBackportedFixesPropFile.writeFixesAsAliasBitSet(fixes, new PrintWriter(result));\n\n        Truth.assertThat(result.toString())\n                .isEqualTo(\"\"\"\n                        # The following backported fixes have been applied\n                        # https://issuetracker.google.com/issues/1234 with alias 1\n                        # https://issuetracker.google.com/issues/3 with alias 65\n                        # https://issuetracker.google.com/issues/4 with alias 67\n                        ro.build.backported_fixes.alias_bitset.long_list=2,10\n                        \"\"\");\n    }\n}\n"
  },
  {
    "path": "backported_fixes/tests/java/com/android/build/backportedfixes/common/ParserTest.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.build.backportedfixes.common;\n\nimport static com.google.common.truth.Truth.assertThat;\nimport static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;\n\nimport com.android.build.backportedfixes.BackportedFix;\nimport com.android.build.backportedfixes.BackportedFixes;\n\nimport com.google.common.collect.ImmutableList;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\n\n/** Tests for {@link Parser}.*/\npublic class ParserTest {\n\n    @Rule\n    public TemporaryFolder mTempFolder = new TemporaryFolder();\n\n    @Test\n    public void getFileInputStreams() throws IOException {\n        var results = Parser.getFileInputStreams(\n                ImmutableList.of(Files.createTempFile(\"test\", null).toFile()));\n        assertThat(results).isNotEmpty();\n    }\n\n\n    @Test\n    public void getBitSetArray_empty() {\n        var results = Parser.getBitSetArray(new int[]{});\n        assertThat(results).isEmpty();\n    }\n\n    @Test\n    public void getBitSetArray_2_3_64() {\n        var results = Parser.getBitSetArray(new int[]{2,3,64});\n        assertThat(results).asList().containsExactly(12L,1L).inOrder();\n    }\n\n    @Test\n    public void parseBackportedFixFiles_empty() throws IOException {\n        var result = Parser.parseBackportedFixFiles(ImmutableList.of());\n        assertThat(result).isEqualTo(BackportedFixes.getDefaultInstance());\n    }\n\n\n    @Test\n    public void parseBackportedFixFiles_oneBlank() throws IOException {\n        var result = Parser.parseBackportedFixFiles(ImmutableList.of(mTempFolder.newFile()));\n\n        assertThat(result).isEqualTo(\n                BackportedFixes.newBuilder()\n                        .addFixes(BackportedFix.getDefaultInstance())\n                        .build());\n    }\n\n    @Test\n    public void parseBackportedFixFiles_two() throws IOException {\n        BackportedFix ki123 = BackportedFix.newBuilder()\n                .setKnownIssue(123)\n                .setAlias(1)\n                .build();\n        BackportedFix ki456 = BackportedFix.newBuilder()\n                .setKnownIssue(456)\n                .setAlias(2)\n                .build();\n        var result = Parser.parseBackportedFixFiles(\n                ImmutableList.of(tempFile(ki456), tempFile(ki123)));\n        assertThat(result).isEqualTo(\n                BackportedFixes.newBuilder()\n                        .addFixes(ki123)\n                        .addFixes(ki456)\n                        .build());\n    }\n\n    private File tempFile(BackportedFix fix) throws IOException {\n        File f = mTempFolder.newFile();\n        try (FileOutputStream out = new FileOutputStream(f)) {\n            fix.writeTo(out);\n            return f;\n        }\n    }\n}\n"
  },
  {
    "path": "banchanHelp.sh",
    "content": "#!/bin/bash\n\n# locate some directories\ncd \"$(dirname $0)\"\nSCRIPT_DIR=\"${PWD}\"\ncd ../..\nTOP=\"${PWD}\"\n\nmessage='usage: banchan <module> ... [<product>|arm|x86|arm64|x86_64] [eng|userdebug|user]\n\nbanchan selects individual APEX modules to be built by the Android build system.\nLike \"tapas\", \"banchan\" does not request the building of images for a device but\ninstead configures it for an unbundled build of the given modules, suitable for\ninstalling on any api-compatible device.\n\nThe difference from \"tapas\" is that \"banchan\" sets the appropriate products etc\nfor building APEX modules rather than apps (APKs).\n\nThe module names should match apex{} modules in Android.bp files, typically\nstarting with \"com.android.\".\n\nThe product argument should be a product name ending in \"_<arch>\", where <arch>\nis one of arm, x86, arm64, x86_64. It can also be just an arch, in which case\nthe standard product for building modules with that architecture is used, i.e.\nmodule_<arch>.\n\nThe usage of the other arguments matches that of the rest of the platform\nbuild system and can be found by running `m help`'\n\necho \"$message\"\n"
  },
  {
    "path": "buildspec.mk.default",
    "content": "#\n# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n######################################################################\n# This is a do-nothing template file.  To use it, copy it to a file\n# named \"buildspec.mk\" in the root directory, and uncomment or change\n# the variables necessary for your desired configuration.  The file\n# \"buildspec.mk\" should never be checked in to source control.\n######################################################################\n\n# Choose a product to build for.  Look in the products directory for ones\n# that work.\nifndef TARGET_PRODUCT\n#TARGET_PRODUCT:=generic\nendif\n\n# Choose a variant to build.  If you don't pick one, the default is eng.\n# User is what we ship.  Userdebug is that, with a few flags turned on\n# for debugging.  Eng has lots of extra tools for development.\nifndef TARGET_BUILD_VARIANT\n#TARGET_BUILD_VARIANT:=user\n#TARGET_BUILD_VARIANT:=userdebug\n#TARGET_BUILD_VARIANT:=eng\nendif\n\n# Choose a targeted release.  If you don't pick one, the default is the\n# soonest future release.\nifndef TARGET_PLATFORM_RELEASE\n#TARGET_PLATFORM_RELEASE:=OPR1\nendif\n\n# Choose additional targets to always install, even when building\n# minimal targets like \"make droid\".  This takes simple target names\n# like \"Browser\" or \"MyApp\", the names used by LOCAL_MODULE or\n# LOCAL_PACKAGE_NAME.  Modules listed here will always be installed in\n# /system, even if they'd usually go in /data.\nifndef CUSTOM_MODULES\n#CUSTOM_MODULES:=\nendif\n\n# Set this to debug or release if you care.  Otherwise, it defaults to release.\nifndef TARGET_BUILD_TYPE\n#TARGET_BUILD_TYPE:=release\nendif\n\n# Uncomment this if you want the host tools built in debug mode.  Otherwise\n# it defaults to release.\nifndef HOST_BUILD_TYPE\n#HOST_BUILD_TYPE:=debug\nendif\n\n# Turn on debugging for selected modules.  If DEBUG_MODULE_<module-name> is set\n# to a non-empty value, the appropriate HOST_/TARGET_CUSTOM_DEBUG_CFLAGS\n# will be added to LOCAL_CFLAGS when building the module.\n#DEBUG_MODULE_ModuleName:=true\n\n# Specify the extra CFLAGS to use when building a module whose\n# DEBUG_MODULE_ variable is set.  Host and device flags are handled\n# separately.\n#HOST_CUSTOM_DEBUG_CFLAGS:=\n#TARGET_CUSTOM_DEBUG_CFLAGS:=\n\n# Choose additional locales, like \"en_US\" or \"it_IT\", to add to any\n# built product.  Any locales that appear in CUSTOM_LOCALES but not in\n# the locale list for the selected product will be added to the end\n# of PRODUCT_LOCALES.\nifndef CUSTOM_LOCALES\n#CUSTOM_LOCALES:=\nendif\n\n# If you have a special place to put your ouput files, set this, otherwise\n# it goes to <build-root>/out\n#OUT_DIR:=/tmp/stuff\n\n# If you want to always set certain system properties, add them to this list.\n# E.g., \"ADDITIONAL_BUILD_PROPERTIES += ro.prop1=5 prop2=value\"\n# This mechanism does not currently support values containing spaces.\n#ADDITIONAL_BUILD_PROPERTIES +=\n\n# If you want to reduce the system.img size by several meg, and are willing to\n# lose access to CJK (and other) character sets, define NO_FALLBACK_FONT:=true\nifndef NO_FALLBACK_FONT\n#NO_FALLBACK_FONT:=true\nendif\n\n# OVERRIDE_RUNTIMES allows you to locally override PRODUCT_RUNTIMES.\n#\n# To only build ART, use \"runtime_libart_default\"\n# To use Dalvik but also include ART, use \"runtime_libdvm_default runtime_libart\"\n# To use ART but also include Dalvik, use \"runtime_libart_default runtime_libdvm\"\nifndef OVERRIDE_RUNTIMES\n#OVERRIDE_RUNTIMES:=runtime_libart_default\n#OVERRIDE_RUNTIMES:=runtime_libdvm_default runtime_libart\n#OVERRIDE_RUNTIMES:=runtime_libart_default runtime_libdvm\nendif\n\n# when the build system changes such that this file must be updated, this\n# variable will be changed.  After you have modified this file with the new\n# changes (see buildspec.mk.default), update this to the new value from\n# buildspec.mk.default.\nBUILD_ENV_SEQUENCE_NUMBER := 13\n"
  },
  {
    "path": "ci/Android.bp",
    "content": "// Copyright 2024 Google Inc. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n    default_team: \"trendy_team_adte\",\n}\n\npython_test_host {\n    name: \"build_test_suites_test\",\n    main: \"build_test_suites_test.py\",\n    pkg_path: \"testdata\",\n    srcs: [\n        \"build_test_suites_test.py\",\n    ],\n    libs: [\n        \"build_test_suites_lib\",\n        \"pyfakefs\",\n        \"ci_test_lib\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n    data: [\n        \":py3-cmd\",\n    ],\n}\n\n// This test is only intended to be run locally since it's slow, not hermetic,\n// and requires a lot of system state. It is therefore not marked as `unit_test`\n// and is not part of any test suite. Note that we also don't want to run this\n// test with Bazel since that would require disabling sandboxing and explicitly\n// passing in all the env vars we depend on via the command-line. The test\n// target could be configured to do so but it's not worth doing seeing that\n// we're moving away from Bazel.\npython_test_host {\n    name: \"build_test_suites_local_test\",\n    main: \"build_test_suites_local_test.py\",\n    srcs: [\n        \"build_test_suites_local_test.py\",\n    ],\n    libs: [\n        \"build_test_suites_lib\",\n        \"pyfakefs\",\n        \"ci_test_lib\",\n    ],\n    test_config_template: \"AndroidTest.xml.template\",\n    test_options: {\n        unit_test: false,\n    },\n}\n\npython_test_host {\n    name: \"optimized_targets_test\",\n    main: \"optimized_targets_test.py\",\n    pkg_path: \"testdata\",\n    srcs: [\n        \"optimized_targets_test.py\",\n    ],\n    libs: [\n        \"build_test_suites_lib\",\n        \"pyfakefs\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n    data: [\n        \":py3-cmd\",\n    ],\n}\n\npython_binary_host {\n    name: \"build_test_suites\",\n    srcs: [\n        \"build_test_suites.py\",\n        \"optimized_targets.py\",\n        \"test_mapping_module_retriever.py\",\n        \"build_context.py\",\n        \"test_discovery_agent.py\",\n        \"metrics_agent.py\",\n        \"buildbot.py\",\n    ],\n    main: \"build_test_suites.py\",\n    libs: [\n        \"soong-metrics-proto-py\",\n    ],\n}\n\npython_library_host {\n    name: \"build_test_suites_lib\",\n    srcs: [\n        \"build_test_suites.py\",\n        \"optimized_targets.py\",\n        \"test_mapping_module_retriever.py\",\n        \"build_context.py\",\n        \"test_discovery_agent.py\",\n        \"metrics_agent.py\",\n        \"buildbot.py\",\n    ],\n    libs: [\n        \"soong-metrics-proto-py\",\n    ],\n}\n\npython_library_host {\n    name: \"ci_test_lib\",\n    srcs: [\n        \"ci_test_lib.py\",\n    ],\n}\n"
  },
  {
    "path": "ci/AndroidTest.xml.template",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2020 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration>\n  <test class=\"com.android.tradefed.testtype.python.PythonBinaryHostTest\">\n    <option name=\"par-file-name\" value=\"{MODULE}\"/>\n    <option name=\"use-test-output-file\" value=\"false\"/>\n    <option name=\"test-timeout\" value=\"5m\"/>\n  </test>\n</configuration>\n"
  },
  {
    "path": "ci/build_context.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Container class for build context with utility functions.\"\"\"\n\nimport re\n\n\nclass BuildContext:\n\n  def __init__(self, build_context_dict: dict[str, any]):\n    self.enabled_build_features = set()\n    for opt in build_context_dict.get('enabledBuildFeatures', []):\n      self.enabled_build_features.add(opt.get('name'))\n    self.test_infos = set()\n    for test_info_dict in build_context_dict.get('testContext', dict()).get(\n        'testInfos', []\n    ):\n      self.test_infos.add(self.TestInfo(test_info_dict))\n\n  def build_target_used(self, target: str) -> bool:\n    return any(test.build_target_used(target) for test in self.test_infos)\n\n  class TestInfo:\n\n    _DOWNLOAD_OPTS = {\n        'test-config-only-zip',\n        'test-zip-file-filter',\n        'extra-host-shared-lib-zip',\n        'sandbox-tests-zips',\n        'additional-files-filter',\n        'cts-package-name',\n    }\n\n    def __init__(self, test_info_dict: dict[str, any]):\n      self.is_test_mapping = False\n      self.test_mapping_test_groups = set()\n      self.file_download_options = set()\n      self.name = test_info_dict.get('name')\n      self.command = test_info_dict.get('command')\n      self.extra_options = test_info_dict.get('extraOptions')\n      for opt in test_info_dict.get('extraOptions', []):\n        key = opt.get('key')\n        if key == 'test-mapping-test-group':\n          self.is_test_mapping = True\n          self.test_mapping_test_groups.update(opt.get('values', set()))\n\n        if key in self._DOWNLOAD_OPTS:\n          self.file_download_options.update(opt.get('values', set()))\n\n    def build_target_used(self, target: str) -> bool:\n      # For all of a targets' outputs, check if any of the regexes used by tests\n      # to download artifacts would match it. If any of them do then this target\n      # is necessary.\n      regex = r'\\b(%s)\\b' % re.escape(target)\n      return any(re.search(regex, opt) for opt in self.file_download_options)\n"
  },
  {
    "path": "ci/build_device_and_tests",
    "content": "#!/usr/bin/env bash\n#\n# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nset -euo pipefail\n\nbuild/soong/soong_ui.bash --make-mode build_test_suites\n$(build/soong/soong_ui.bash --dumpvar-mode HOST_OUT)/bin/build_test_suites --device-build $@\n"
  },
  {
    "path": "ci/build_metadata",
    "content": "#/bin/bash\n\n# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -x\n\nsource build/make/shell_utils.sh\n\nexport TARGET_PRODUCT=aosp_arm64\nexport TARGET_RELEASE=trunk_staging\nexport TARGET_BUILD_VARIANT=eng\n\nimport_build_vars \\\n        OUT_DIR \\\n        DIST_DIR \\\n        HOST_OUT_EXECUTABLES \\\n    || exit $?\n\nTARGETS=(\n    all_teams\n    source_tree_size\n    release_config_metadata\n)\n\n# Build modules\nbuild/soong/bin/m dist ${TARGETS[@]} || exit $?\n\n# List all source files in the tree\n( \\\n    $HOST_OUT_EXECUTABLES/source_tree_size -o $DIST_DIR/all_source_tree_files.pb \\\n        && gzip -fn $DIST_DIR/all_source_tree_files.pb \\\n) || exit $?\n"
  },
  {
    "path": "ci/build_test_suites",
    "content": "#!/usr/bin/env bash\n#\n# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nset -euo pipefail\n\nbuild/soong/soong_ui.bash --make-mode build_test_suites\n$(build/soong/soong_ui.bash --dumpvar-mode HOST_OUT)/bin/build_test_suites $@\n"
  },
  {
    "path": "ci/build_test_suites.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Build script for the CI `test_suites` target.\"\"\"\n\nimport argparse\nfrom dataclasses import dataclass\nimport json\nimport logging\nimport os\nimport pathlib\nimport re\nimport subprocess\nimport sys\nfrom typing import Callable\nfrom build_context import BuildContext\nimport optimized_targets\nimport metrics_agent\nimport test_discovery_agent\n\n\nREQUIRED_ENV_VARS = frozenset(['TARGET_PRODUCT', 'TARGET_RELEASE', 'TOP', 'DIST_DIR'])\nSOONG_UI_EXE_REL_PATH = 'build/soong/soong_ui.bash'\nLOG_PATH = 'logs/build_test_suites.log'\n# Currently, this prevents the removal of those tags when they exist. In the future we likely\n# want the script to supply 'dist directly\nREQUIRED_BUILD_TARGETS = frozenset(['dist', 'droid', 'checkbuild'])\n\n\nclass Error(Exception):\n\n  def __init__(self, message):\n    super().__init__(message)\n\n\nclass BuildFailureError(Error):\n\n  def __init__(self, return_code):\n    super().__init__(f'Build command failed with return code: f{return_code}')\n    self.return_code = return_code\n\n\nclass BuildPlanner:\n  \"\"\"Class in charge of determining how to optimize build targets.\n\n  Given the build context and targets to build it will determine a final list of\n  targets to build along with getting a set of packaging functions to package up\n  any output zip files needed by the build.\n  \"\"\"\n\n  def __init__(\n      self,\n      build_context: BuildContext,\n      args: argparse.Namespace,\n      target_optimizations: dict[str, optimized_targets.OptimizedBuildTarget],\n  ):\n    self.build_context = build_context\n    self.args = args\n    self.target_optimizations = target_optimizations\n\n  def create_build_plan(self):\n\n    if 'optimized_build' not in self.build_context.enabled_build_features:\n      return BuildPlan(set(self.args.extra_targets), set())\n\n    if not self.build_context.test_infos:\n      logging.warning('Build context has no test infos, skipping optimizations.')\n      for target in self.args.extra_targets:\n        get_metrics_agent().report_unoptimized_target(target, 'BUILD_CONTEXT has no test infos.')\n      return BuildPlan(set(self.args.extra_targets), set())\n\n    build_targets = set()\n    packaging_commands_getters = []\n    # In order to roll optimizations out differently between test suites and\n    # device builds, we have separate flags.\n    enable_discovery = (('test_suites_zip_test_discovery'\n        in self.build_context.enabled_build_features\n        and not self.args.device_build\n    ) or (\n        'device_zip_test_discovery'\n        in self.build_context.enabled_build_features\n        and self.args.device_build\n    )) and not self.args.test_discovery_info_mode\n    logging.info(f'Discovery mode is enabled= {enable_discovery}')\n    preliminary_build_targets = self._collect_preliminary_build_targets(enable_discovery)\n\n    for target in preliminary_build_targets:\n      target_optimizer_getter = self.target_optimizations.get(target, None)\n      if not target_optimizer_getter:\n        build_targets.add(target)\n        continue\n\n      target_optimizer = target_optimizer_getter(\n          target, self.build_context, self.args\n      )\n      build_targets.update(target_optimizer.get_build_targets())\n      packaging_commands_getters.append(\n          target_optimizer.get_package_outputs_commands\n      )\n\n    return BuildPlan(build_targets, packaging_commands_getters)\n\n  def _collect_preliminary_build_targets(self, enable_discovery: bool):\n    build_targets = set()\n    try:\n      test_discovery_zip_regexes = self._get_test_discovery_zip_regexes()\n      logging.info(f'Discovered test discovery regexes: {test_discovery_zip_regexes}')\n    except test_discovery_agent.TestDiscoveryError as e:\n      optimization_rationale = e.message\n      logging.warning(f'Unable to perform test discovery: {optimization_rationale}')\n\n      for target in self.args.extra_targets:\n        get_metrics_agent().report_unoptimized_target(target, optimization_rationale)\n      return self._legacy_collect_preliminary_build_targets()\n\n    for target in self.args.extra_targets:\n      if target in REQUIRED_BUILD_TARGETS:\n        build_targets.add(target)\n        get_metrics_agent().report_unoptimized_target(target, 'Required build target.')\n        continue\n      # If nothing is discovered without error, that means nothing is needed.\n      if not test_discovery_zip_regexes:\n        get_metrics_agent().report_optimized_target(target)\n        continue\n\n      regex = r'\\b(%s.*)\\b' % re.escape(target)\n      for opt in test_discovery_zip_regexes:\n        try:\n          if re.search(regex, opt):\n            get_metrics_agent().report_unoptimized_target(target, 'Test artifact used.')\n            build_targets.add(target)\n            # proceed to next target evaluation\n            break\n          get_metrics_agent().report_optimized_target(target)\n        except Exception as e:\n          # In case of exception report as unoptimized\n          build_targets.add(target)\n          get_metrics_agent().report_unoptimized_target(target, f'Error in parsing test discovery output for {target}: {repr(e)}')\n          logging.error(f'unable to parse test discovery output: {repr(e)}')\n          break\n    # If discovery is not enabled, return the original list\n    if not enable_discovery:\n      return self._legacy_collect_preliminary_build_targets()\n\n    return build_targets\n\n  def _legacy_collect_preliminary_build_targets(self):\n    build_targets = set()\n    for target in self.args.extra_targets:\n      if self._unused_target_exclusion_enabled(\n          target\n      ) and not self.build_context.build_target_used(target):\n        continue\n\n      build_targets.add(target)\n    return build_targets\n\n  def _unused_target_exclusion_enabled(self, target: str) -> bool:\n    return (\n        f'{target}_unused_exclusion'\n        in self.build_context.enabled_build_features\n    )\n\n  def _get_test_discovery_zip_regexes(self) -> set[str]:\n    build_target_regexes = set()\n    for test_info in self.build_context.test_infos:\n      tf_command = self._build_tf_command(test_info)\n      discovery_agent = test_discovery_agent.TestDiscoveryAgent(tradefed_args=tf_command)\n      for regex in discovery_agent.discover_test_zip_regexes():\n        build_target_regexes.add(regex)\n    return build_target_regexes\n\n\n  def _build_tf_command(self, test_info) -> list[str]:\n    command = [test_info.command]\n    for extra_option in test_info.extra_options:\n      if not extra_option.get('key'):\n        continue\n      arg_key = '--' + extra_option.get('key')\n      if arg_key == '--build-id':\n        command.append(arg_key)\n        command.append(os.environ.get('BUILD_NUMBER'))\n        continue\n      if extra_option.get('values'):\n        for value in extra_option.get('values'):\n          command.append(arg_key)\n          command.append(value)\n      else:\n        command.append(arg_key)\n\n    return command\n\n@dataclass(frozen=True)\nclass BuildPlan:\n  build_targets: set[str]\n  packaging_commands_getters: list[Callable[[], list[list[str]]]]\n\n\ndef build_test_suites(argv: list[str]) -> int:\n  \"\"\"Builds all test suites passed in, optimizing based on the build_context content.\n\n  Args:\n    argv: The command line arguments passed in.\n\n  Returns:\n    The exit code of the build.\n  \"\"\"\n  get_metrics_agent().analysis_start()\n  try:\n    args = parse_args(argv)\n    check_required_env()\n    build_context = BuildContext(load_build_context())\n    build_planner = BuildPlanner(\n        build_context, args, optimized_targets.OPTIMIZED_BUILD_TARGETS\n    )\n    build_plan = build_planner.create_build_plan()\n  except:\n    raise\n  finally:\n    get_metrics_agent().analysis_end()\n\n  try:\n    execute_build_plan(build_plan)\n  except BuildFailureError as e:\n    logging.error('Build command failed! Check build_log for details.')\n    return e.return_code\n  finally:\n    get_metrics_agent().end_reporting()\n\n  return 0\n\n\ndef parse_args(argv: list[str]) -> argparse.Namespace:\n  argparser = argparse.ArgumentParser()\n\n  argparser.add_argument(\n      'extra_targets', nargs='*', help='Extra test suites to build.'\n  )\n  argparser.add_argument(\n      '--device-build',\n      action='store_true',\n      help='Flag to indicate running a device build.',\n  )\n  argparser.add_argument(\n      '--test_discovery_info_mode',\n      action='store_true',\n      help='Flag to enable running test discovery in info only mode.',\n  )\n\n  return argparser.parse_args(argv)\n\n\ndef check_required_env():\n  \"\"\"Check for required env vars.\n\n  Raises:\n    RuntimeError: If any required env vars are not found.\n  \"\"\"\n  missing_env_vars = sorted(v for v in REQUIRED_ENV_VARS if v not in os.environ)\n\n  if not missing_env_vars:\n    return\n\n  t = ','.join(missing_env_vars)\n  raise Error(f'Missing required environment variables: {t}')\n\n\ndef load_build_context():\n  build_context_path = pathlib.Path(os.environ.get('BUILD_CONTEXT', ''))\n  if build_context_path.is_file():\n    try:\n      with open(build_context_path, 'r') as f:\n        return json.load(f)\n    except json.decoder.JSONDecodeError as e:\n      raise Error(f'Failed to load JSON file: {build_context_path}')\n\n  logging.info('No BUILD_CONTEXT found, skipping optimizations.')\n  return empty_build_context()\n\n\ndef empty_build_context():\n  return {'enabledBuildFeatures': []}\n\n\ndef execute_build_plan(build_plan: BuildPlan):\n  build_command = []\n  build_command.append(get_top().joinpath(SOONG_UI_EXE_REL_PATH))\n  build_command.append('--make-mode')\n  build_command.extend(build_plan.build_targets)\n  logging.info(f'Running build command: {build_command}')\n  try:\n    run_command(build_command)\n  except subprocess.CalledProcessError as e:\n    raise BuildFailureError(e.returncode) from e\n\n  get_metrics_agent().packaging_start()\n  try:\n    for packaging_commands_getter in build_plan.packaging_commands_getters:\n      for packaging_command in packaging_commands_getter():\n        run_command(packaging_command)\n  except subprocess.CalledProcessError as e:\n    raise BuildFailureError(e.returncode) from e\n  finally:\n    get_metrics_agent().packaging_end()\n\n\ndef get_top() -> pathlib.Path:\n  return pathlib.Path(os.environ['TOP'])\n\n\ndef run_command(args: list[str], stdout=None):\n  subprocess.run(args=args, check=True, stdout=stdout)\n\n\ndef get_metrics_agent():\n  return metrics_agent.MetricsAgent.instance()\n\n\ndef main(argv):\n  dist_dir = os.environ.get('DIST_DIR')\n  if dist_dir:\n    log_file = pathlib.Path(dist_dir) / LOG_PATH\n    logging.basicConfig(\n        level=logging.DEBUG,\n        format='%(asctime)s %(levelname)s %(message)s',\n        filename=log_file,\n    )\n  sys.exit(build_test_suites(argv))\n\n\nif __name__ == '__main__':\n  main(sys.argv[1:])\n"
  },
  {
    "path": "ci/build_test_suites_local_test.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Integration tests for build_test_suites that require a local build env.\"\"\"\n\nimport os\nimport pathlib\nimport shutil\nimport signal\nimport subprocess\nimport tempfile\nimport time\nimport ci_test_lib\n\n\nclass BuildTestSuitesLocalTest(ci_test_lib.TestCase):\n\n  def setUp(self):\n    self.top_dir = pathlib.Path(os.environ['ANDROID_BUILD_TOP']).resolve()\n    self.executable = self.top_dir.joinpath('build/make/ci/build_test_suites')\n    self.process_session = ci_test_lib.TemporaryProcessSession(self)\n    self.temp_dir = ci_test_lib.TestTemporaryDirectory.create(self)\n\n  def build_subprocess_args(self, build_args: list[str]):\n    env = os.environ.copy()\n    env['TOP'] = str(self.top_dir)\n    env['OUT_DIR'] = self.temp_dir\n\n    args = ([self.executable] + build_args,)\n    kwargs = {\n        'cwd': self.top_dir,\n        'env': env,\n        'text': True,\n    }\n\n    return (args, kwargs)\n\n  def run_build(self, build_args: list[str]) -> subprocess.CompletedProcess:\n    args, kwargs = self.build_subprocess_args(build_args)\n\n    return subprocess.run(\n        *args,\n        **kwargs,\n        check=True,\n        capture_output=True,\n        timeout=5 * 60,\n    )\n\n  def assert_children_alive(self, children: list[int]):\n    for c in children:\n      self.assertTrue(ci_test_lib.process_alive(c))\n\n  def assert_children_dead(self, children: list[int]):\n    for c in children:\n      self.assertFalse(ci_test_lib.process_alive(c))\n\n  def test_fails_for_invalid_arg(self):\n    invalid_arg = '--invalid-arg'\n\n    with self.assertRaises(subprocess.CalledProcessError) as cm:\n      self.run_build([invalid_arg])\n\n    self.assertIn(invalid_arg, cm.exception.stderr)\n\n  def test_builds_successfully(self):\n    self.run_build(['nothing'])\n\n  def test_can_interrupt_build(self):\n    args, kwargs = self.build_subprocess_args(['general-tests'])\n    p = self.process_session.create(args, kwargs)\n\n    # TODO(lucafarsi): Replace this (and other instances) with a condition.\n    time.sleep(5)  # Wait for the build to get going.\n    self.assertIsNone(p.poll())  # Check that the process is still alive.\n    children = query_child_pids(p.pid)\n    self.assert_children_alive(children)\n\n    p.send_signal(signal.SIGINT)\n    p.wait()\n\n    time.sleep(5)  # Wait for things to die out.\n    self.assert_children_dead(children)\n\n  def test_can_kill_build_process_group(self):\n    args, kwargs = self.build_subprocess_args(['general-tests'])\n    p = self.process_session.create(args, kwargs)\n\n    time.sleep(5)  # Wait for the build to get going.\n    self.assertIsNone(p.poll())  # Check that the process is still alive.\n    children = query_child_pids(p.pid)\n    self.assert_children_alive(children)\n\n    os.killpg(os.getpgid(p.pid), signal.SIGKILL)\n    p.wait()\n\n    time.sleep(5)  # Wait for things to die out.\n    self.assert_children_dead(children)\n\n\n# TODO(hzalek): Replace this with `psutils` once available in the tree.\ndef query_child_pids(parent_pid: int) -> set[int]:\n  p = subprocess.run(\n      ['pgrep', '-P', str(parent_pid)],\n      check=True,\n      capture_output=True,\n      text=True,\n  )\n  return {int(pid) for pid in p.stdout.splitlines()}\n\n\nif __name__ == '__main__':\n  ci_test_lib.main()\n"
  },
  {
    "path": "ci/build_test_suites_test.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Tests for build_test_suites.py\"\"\"\n\nimport argparse\nimport functools\nfrom importlib import resources\nimport json\nimport multiprocessing\nimport os\nimport pathlib\nimport shutil\nimport signal\nimport stat\nimport subprocess\nimport sys\nimport tempfile\nimport textwrap\nimport time\nfrom typing import Callable\nimport unittest\nfrom unittest import mock\nfrom build_context import BuildContext\nimport build_test_suites\nimport ci_test_lib\nimport optimized_targets\nfrom pyfakefs import fake_filesystem_unittest\nimport metrics_agent\nimport test_discovery_agent\n\n\nclass BuildTestSuitesTest(fake_filesystem_unittest.TestCase):\n\n  def setUp(self):\n    self.setUpPyfakefs()\n\n    os_environ_patcher = mock.patch.dict('os.environ', {})\n    self.addCleanup(os_environ_patcher.stop)\n    self.mock_os_environ = os_environ_patcher.start()\n\n    subprocess_run_patcher = mock.patch('subprocess.run')\n    self.addCleanup(subprocess_run_patcher.stop)\n    self.mock_subprocess_run = subprocess_run_patcher.start()\n\n    metrics_agent_finalize_patcher = mock.patch('metrics_agent.MetricsAgent.end_reporting')\n    self.addCleanup(metrics_agent_finalize_patcher.stop)\n    self.mock_metrics_agent_end = metrics_agent_finalize_patcher.start()\n\n    self._setup_working_build_env()\n\n  def test_missing_target_release_env_var_raises(self):\n    del os.environ['TARGET_RELEASE']\n\n    with self.assert_raises_word(build_test_suites.Error, 'TARGET_RELEASE'):\n      build_test_suites.main([])\n\n  def test_missing_target_product_env_var_raises(self):\n    del os.environ['TARGET_PRODUCT']\n\n    with self.assert_raises_word(build_test_suites.Error, 'TARGET_PRODUCT'):\n      build_test_suites.main([])\n\n  def test_missing_top_env_var_raises(self):\n    del os.environ['TOP']\n\n    with self.assert_raises_word(build_test_suites.Error, 'TOP'):\n      build_test_suites.main([])\n\n  def test_missing_dist_dir_env_var_raises(self):\n    del os.environ['DIST_DIR']\n\n    with self.assert_raises_word(build_test_suites.Error, 'DIST_DIR'):\n      build_test_suites.main([])\n\n  def test_invalid_arg_raises(self):\n    invalid_args = ['--invalid_arg']\n\n    with self.assertRaisesRegex(SystemExit, '2'):\n      build_test_suites.main(invalid_args)\n\n  def test_build_failure_returns(self):\n    self.mock_subprocess_run.side_effect = subprocess.CalledProcessError(\n        42, None\n    )\n\n    with self.assertRaisesRegex(SystemExit, '42'):\n      build_test_suites.main([])\n\n  def test_incorrectly_formatted_build_context_raises(self):\n    build_context = self.fake_top.joinpath('build_context')\n    build_context.touch()\n    os.environ['BUILD_CONTEXT'] = str(build_context)\n\n    with self.assert_raises_word(build_test_suites.Error, 'JSON'):\n      build_test_suites.main([])\n\n  def test_build_success_returns(self):\n    with self.assertRaisesRegex(SystemExit, '0'):\n      build_test_suites.main([])\n\n  def assert_raises_word(self, cls, word):\n    return self.assertRaisesRegex(cls, rf'\\b{word}\\b')\n\n  def _setup_working_build_env(self):\n    self.fake_top = pathlib.Path('/fake/top')\n    self.fake_top.mkdir(parents=True)\n\n    self.soong_ui_dir = self.fake_top.joinpath('build/soong')\n    self.soong_ui_dir.mkdir(parents=True, exist_ok=True)\n\n    self.logs_dir = self.fake_top.joinpath('dist/logs')\n    self.logs_dir.mkdir(parents=True, exist_ok=True)\n\n    self.soong_ui = self.soong_ui_dir.joinpath('soong_ui.bash')\n    self.soong_ui.touch()\n\n    self.mock_os_environ.update({\n        'TARGET_RELEASE': 'release',\n        'TARGET_PRODUCT': 'product',\n        'TOP': str(self.fake_top),\n        'DIST_DIR': str(self.fake_top.joinpath('dist')),\n    })\n\n    self.mock_subprocess_run.return_value = 0\n\n\nclass RunCommandIntegrationTest(ci_test_lib.TestCase):\n\n  def setUp(self):\n    self.temp_dir = ci_test_lib.TestTemporaryDirectory.create(self)\n\n    # Copy the Python executable from 'non-code' resources and make it\n    # executable for use by tests that launch a subprocess. Note that we don't\n    # use Python's native `sys.executable` property since that is not set when\n    # running via the embedded launcher.\n    base_name = 'py3-cmd'\n    dest_file = self.temp_dir.joinpath(base_name)\n    with resources.as_file(\n        resources.files('testdata').joinpath(base_name)\n    ) as p:\n      shutil.copy(p, dest_file)\n    dest_file.chmod(dest_file.stat().st_mode | stat.S_IEXEC)\n    self.python_executable = dest_file\n\n    self._managed_processes = []\n\n  def tearDown(self):\n    self._terminate_managed_processes()\n\n  def test_raises_on_nonzero_exit(self):\n    with self.assertRaises(Exception):\n      build_test_suites.run_command([\n          self.python_executable,\n          '-c',\n          textwrap.dedent(f\"\"\"\\\n              import sys\n              sys.exit(1)\n              \"\"\"),\n      ])\n\n  def test_streams_stdout(self):\n\n    def run_slow_command(stdout_file, marker):\n      with open(stdout_file, 'w') as f:\n        build_test_suites.run_command(\n            [\n                self.python_executable,\n                '-c',\n                textwrap.dedent(f\"\"\"\\\n                  import time\n\n                  print('{marker}', end='', flush=True)\n\n                  # Keep process alive until we check stdout.\n                  time.sleep(10)\n                  \"\"\"),\n            ],\n            stdout=f,\n        )\n\n    marker = 'Spinach'\n    stdout_file = self.temp_dir.joinpath('stdout.txt')\n\n    p = self.start_process(target=run_slow_command, args=[stdout_file, marker])\n\n    self.assert_file_eventually_contains(stdout_file, marker)\n\n  def test_propagates_interruptions(self):\n\n    def run(pid_file):\n      build_test_suites.run_command([\n          self.python_executable,\n          '-c',\n          textwrap.dedent(f\"\"\"\\\n              import os\n              import pathlib\n              import time\n\n              pathlib.Path('{pid_file}').write_text(str(os.getpid()))\n\n              # Keep the process alive for us to explicitly interrupt it.\n              time.sleep(10)\n              \"\"\"),\n      ])\n\n    pid_file = self.temp_dir.joinpath('pid.txt')\n    p = self.start_process(target=run, args=[pid_file])\n    subprocess_pid = int(read_eventual_file_contents(pid_file))\n\n    os.kill(p.pid, signal.SIGINT)\n    p.join()\n\n    self.assert_process_eventually_dies(p.pid)\n    self.assert_process_eventually_dies(subprocess_pid)\n\n  def start_process(self, *args, **kwargs) -> multiprocessing.Process:\n    p = multiprocessing.Process(*args, **kwargs)\n    self._managed_processes.append(p)\n    p.start()\n    return p\n\n  def assert_process_eventually_dies(self, pid: int):\n    try:\n      wait_until(lambda: not ci_test_lib.process_alive(pid))\n    except TimeoutError as e:\n      self.fail(f'Process {pid} did not die after a while: {e}')\n\n  def assert_file_eventually_contains(self, file: pathlib.Path, substring: str):\n    wait_until(lambda: file.is_file() and file.stat().st_size > 0)\n    self.assertIn(substring, read_file_contents(file))\n\n  def _terminate_managed_processes(self):\n    for p in self._managed_processes:\n      if not p.is_alive():\n        continue\n\n      # We terminate the process with `SIGINT` since using `terminate` or\n      # `SIGKILL` doesn't kill any grandchild processes and we don't have\n      # `psutil` available to easily query all children.\n      os.kill(p.pid, signal.SIGINT)\n\n\nclass BuildPlannerTest(unittest.TestCase):\n\n  class TestOptimizedBuildTarget(optimized_targets.OptimizedBuildTarget):\n\n    def __init__(\n        self, target, build_context, args, output_targets, packaging_commands\n    ):\n      super().__init__(target, build_context, args)\n      self.output_targets = output_targets\n      self.packaging_commands = packaging_commands\n\n    def get_build_targets_impl(self):\n      return self.output_targets\n\n    def get_package_outputs_commands_impl(self):\n      return self.packaging_commands\n\n    def get_enabled_flag(self):\n      return f'{self.target}_enabled'\n\n  def setUp(self):\n    test_discovery_agent_patcher = mock.patch('test_discovery_agent.TestDiscoveryAgent.discover_test_zip_regexes')\n    self.addCleanup(test_discovery_agent_patcher.stop)\n    self.mock_test_discovery_agent_end = test_discovery_agent_patcher.start()\n\n\n  def test_build_optimization_off_builds_everything(self):\n    build_targets = {'target_1', 'target_2'}\n    build_planner = self.create_build_planner(\n        build_context=self.create_build_context(optimized_build_enabled=False),\n        build_targets=build_targets,\n    )\n\n    build_plan = build_planner.create_build_plan()\n\n    self.assertSetEqual(build_targets, build_plan.build_targets)\n\n  def test_build_optimization_off_doesnt_package(self):\n    build_targets = {'target_1', 'target_2'}\n    build_planner = self.create_build_planner(\n        build_context=self.create_build_context(optimized_build_enabled=False),\n        build_targets=build_targets,\n    )\n\n    build_plan = build_planner.create_build_plan()\n\n    for packaging_command in self.run_packaging_commands(build_plan):\n      self.assertEqual(len(packaging_command), 0)\n\n  def test_build_optimization_on_optimizes_target(self):\n    build_targets = {'target_1', 'target_2'}\n    build_planner = self.create_build_planner(\n        build_targets=build_targets,\n        build_context=self.create_build_context(\n            enabled_build_features=[{'name': self.get_target_flag('target_1')}],\n            test_context=self.get_test_context('target_1'),\n        ),\n    )\n\n    build_plan = build_planner.create_build_plan()\n\n    expected_targets = {self.get_optimized_target_name('target_1'), 'target_2'}\n    self.assertSetEqual(expected_targets, build_plan.build_targets)\n\n  def test_build_optimization_on_packages_target(self):\n    build_targets = {'target_1', 'target_2'}\n    optimized_target_name = self.get_optimized_target_name('target_1')\n    packaging_commands = [[f'packaging {optimized_target_name}']]\n    build_planner = self.create_build_planner(\n        build_targets=build_targets,\n        build_context=self.create_build_context(\n            enabled_build_features=[{'name': self.get_target_flag('target_1')}],\n            test_context=self.get_test_context('target_1'),\n        ),\n        packaging_commands=packaging_commands,\n    )\n\n    build_plan = build_planner.create_build_plan()\n\n    self.assertIn(packaging_commands, self.run_packaging_commands(build_plan))\n\n  def test_individual_build_optimization_off_doesnt_optimize(self):\n    build_targets = {'target_1', 'target_2'}\n    build_planner = self.create_build_planner(\n        build_targets=build_targets,\n    )\n\n    build_plan = build_planner.create_build_plan()\n\n    self.assertSetEqual(build_targets, build_plan.build_targets)\n\n  def test_individual_build_optimization_off_doesnt_package(self):\n    build_targets = {'target_1', 'target_2'}\n    packaging_commands = [['packaging command']]\n    build_planner = self.create_build_planner(\n        build_targets=build_targets,\n        packaging_commands=packaging_commands,\n    )\n\n    build_plan = build_planner.create_build_plan()\n\n    for packaging_command in self.run_packaging_commands(build_plan):\n      self.assertEqual(len(packaging_command), 0)\n\n  def test_target_output_used_target_built(self):\n    build_target = 'test_target'\n    build_planner = self.create_build_planner(\n        build_targets={build_target},\n        build_context=self.create_build_context(\n            test_context=self.get_test_context(build_target),\n            enabled_build_features=[{'name': 'test_target_unused_exclusion'}],\n        ),\n    )\n\n    build_plan = build_planner.create_build_plan()\n\n    self.assertSetEqual(build_plan.build_targets, {build_target})\n\n  def test_target_regex_used_target_built(self):\n    build_target = 'test_target'\n    test_context = self.get_test_context(build_target)\n    test_context['testInfos'][0]['extraOptions'] = [{\n        'key': 'additional-files-filter',\n        'values': [f'.*{build_target}.*\\.zip'],\n    }]\n    build_planner = self.create_build_planner(\n        build_targets={build_target},\n        build_context=self.create_build_context(\n            test_context=test_context,\n            enabled_build_features=[{'name': 'test_target_unused_exclusion'}],\n        ),\n    )\n\n    build_plan = build_planner.create_build_plan()\n\n    self.assertSetEqual(build_plan.build_targets, {build_target})\n\n  def test_target_output_not_used_target_not_built(self):\n    build_target = 'test_target'\n    test_context = self.get_test_context(build_target)\n    test_context['testInfos'][0]['extraOptions'] = []\n    build_planner = self.create_build_planner(\n        build_targets={build_target},\n        build_context=self.create_build_context(\n            test_context=test_context,\n            enabled_build_features=[{'name': 'test_target_unused_exclusion'}],\n        ),\n    )\n\n    build_plan = build_planner.create_build_plan()\n\n    self.assertSetEqual(build_plan.build_targets, set())\n\n  def test_target_regex_matching_not_too_broad(self):\n    build_target = 'test_target'\n    test_context = self.get_test_context(build_target)\n    test_context['testInfos'][0]['extraOptions'] = [{\n        'key': 'additional-files-filter',\n        'values': [f'.*a{build_target}.*\\.zip'],\n    }]\n    build_planner = self.create_build_planner(\n        build_targets={build_target},\n        build_context=self.create_build_context(\n            test_context=test_context,\n            enabled_build_features=[{'name': 'test_target_unused_exclusion'}],\n        ),\n    )\n\n    build_plan = build_planner.create_build_plan()\n\n    self.assertSetEqual(build_plan.build_targets, set())\n\n  def create_build_planner(\n      self,\n      build_targets: set[str],\n      build_context: BuildContext = None,\n      args: argparse.Namespace = None,\n      target_optimizations: dict[\n          str, optimized_targets.OptimizedBuildTarget\n      ] = None,\n      packaging_commands: list[list[str]] = [],\n  ) -> build_test_suites.BuildPlanner:\n    if not build_context:\n      build_context = self.create_build_context()\n    if not args:\n      args = self.create_args(extra_build_targets=build_targets)\n    if not target_optimizations:\n      target_optimizations = self.create_target_optimizations(\n          build_context,\n          build_targets,\n          packaging_commands,\n      )\n    return build_test_suites.BuildPlanner(\n        build_context, args, target_optimizations\n    )\n\n  def create_build_context(\n      self,\n      optimized_build_enabled: bool = True,\n      enabled_build_features: list[dict[str, str]] = [],\n      test_context: dict[str, any] = {},\n  ) -> BuildContext:\n    build_context_dict = {}\n    build_context_dict['enabledBuildFeatures'] = enabled_build_features\n    if optimized_build_enabled:\n      build_context_dict['enabledBuildFeatures'].append(\n          {'name': 'optimized_build'}\n      )\n    build_context_dict['testContext'] = test_context\n    return BuildContext(build_context_dict)\n\n  def create_args(\n      self, extra_build_targets: set[str] = set()\n  ) -> argparse.Namespace:\n    parser = argparse.ArgumentParser()\n    parser.add_argument('extra_targets', nargs='*')\n    return parser.parse_args(extra_build_targets)\n\n  def create_target_optimizations(\n      self,\n      build_context: BuildContext,\n      build_targets: set[str],\n      packaging_commands: list[list[str]] = [],\n  ):\n    target_optimizations = dict()\n    for target in build_targets:\n      target_optimizations[target] = functools.partial(\n          self.TestOptimizedBuildTarget,\n          output_targets={self.get_optimized_target_name(target)},\n          packaging_commands=packaging_commands,\n      )\n\n    return target_optimizations\n\n  def get_target_flag(self, target: str):\n    return f'{target}_enabled'\n\n  def get_optimized_target_name(self, target: str):\n    return f'{target}_optimized'\n\n  def get_test_context(self, target: str):\n    return {\n        'testInfos': [\n            {\n                'name': 'atp_test',\n                'target': 'test_target',\n                'branch': 'branch',\n                'extraOptions': [{\n                    'key': 'additional-files-filter',\n                    'values': [f'{target}.zip'],\n                }],\n                'command': '/tf/command',\n                'extraBuildTargets': [\n                    'extra_build_target',\n                ],\n            },\n        ],\n    }\n\n  def run_packaging_commands(self, build_plan: build_test_suites.BuildPlan):\n    return [\n        packaging_command_getter()\n        for packaging_command_getter in build_plan.packaging_commands_getters\n    ]\n\n\ndef wait_until(\n    condition_function: Callable[[], bool],\n    timeout_secs: float = 3.0,\n    polling_interval_secs: float = 0.1,\n):\n  \"\"\"Waits until a condition function returns True.\"\"\"\n\n  start_time_secs = time.time()\n\n  while not condition_function():\n    if time.time() - start_time_secs > timeout_secs:\n      raise TimeoutError(\n          f'Condition not met within timeout: {timeout_secs} seconds'\n      )\n\n    time.sleep(polling_interval_secs)\n\n\ndef read_file_contents(file: pathlib.Path) -> str:\n  with open(file, 'r') as f:\n    return f.read()\n\n\ndef read_eventual_file_contents(file: pathlib.Path) -> str:\n  wait_until(lambda: file.is_file() and file.stat().st_size > 0)\n  return read_file_contents(file)\n\n\nif __name__ == '__main__':\n  ci_test_lib.main()\n"
  },
  {
    "path": "ci/buildbot.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Utilities for interacting with buildbot, with a simulation in a local environment\"\"\"\n\nimport os\nimport sys\n\n# Check that the script is running from the root of the tree. Prevents subtle\n# errors later, and CI always runs from the root of the tree.\nif not os.path.exists(\"build/make/ci/buildbot.py\"):\n    raise Exception(\"CI script must be run from the root of the tree instead of: \"\n                    + os.getcwd())\n\n# Check that we are using the hermetic interpreter\nif \"prebuilts/build-tools/\" not in sys.executable:\n    raise Exception(\"CI script must be run using the hermetic interpreter from \"\n                    + \"prebuilts/build-tools instead of: \" + sys.executable)\n\n\ndef OutDir():\n    \"Get the out directory. Will create it if needed.\"\n    result = os.environ.get(\"OUT_DIR\", \"out\")\n    os.makedirs(result, exist_ok=True)\n    return result\n\ndef DistDir():\n    \"Get the dist directory. Will create it if needed.\"\n    result = os.environ.get(\"DIST_DIR\", os.path.join(OutDir(), \"dist\"))\n    os.makedirs(result, exist_ok=True)\n    return result\n\n"
  },
  {
    "path": "ci/ci_test_lib.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Testing utilities for tests in the CI package.\"\"\"\n\nimport logging\nimport os\nimport unittest\nimport subprocess\nimport pathlib\nimport shutil\nimport tempfile\n\n\n# Export the TestCase class to reduce the number of imports tests have to list.\nTestCase = unittest.TestCase\n\n\ndef process_alive(pid):\n  \"\"\"Check For the existence of a pid.\"\"\"\n\n  try:\n    os.kill(pid, 0)\n  except OSError:\n    return False\n\n  return True\n\n\nclass TemporaryProcessSession:\n\n  def __init__(self, test_case: TestCase):\n    self._created_processes = []\n    test_case.addCleanup(self.cleanup)\n\n  def create(self, args, kwargs):\n    p = subprocess.Popen(*args, **kwargs, start_new_session=True)\n    self._created_processes.append(p)\n    return p\n\n  def cleanup(self):\n    for p in self._created_processes:\n      if not process_alive(p.pid):\n        return\n      os.killpg(os.getpgid(p.pid), signal.SIGKILL)\n\n\nclass TestTemporaryDirectory:\n\n  def __init__(self, delete: bool, ):\n    self._delete = delete\n\n  @classmethod\n  def create(cls, test_case: TestCase, delete: bool = True):\n    temp_dir = TestTemporaryDirectory(delete)\n    temp_dir._dir = pathlib.Path(tempfile.mkdtemp())\n    test_case.addCleanup(temp_dir.cleanup)\n    return temp_dir._dir\n\n  def get_dir(self):\n    return self._dir\n\n  def cleanup(self):\n    if not self._delete:\n      return\n    shutil.rmtree(self._dir, ignore_errors=True)\n\n\ndef main():\n\n  # Disable logging since it breaks the TF Python test output parser.\n  # TODO(hzalek): Use TF's `test-output-file` option to re-enable logging.\n  logging.getLogger().disabled = True\n\n  unittest.main()\n"
  },
  {
    "path": "ci/dump_product_config",
    "content": "#!prebuilts/build-tools/linux-x86/bin/py3-cmd -B\n\n# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Script to collect all of the make variables from all product config combos.\n\nThis script must be run from the root of the source tree.\n\nSee GetArgs() below or run dump_product_config for more information.\n\"\"\"\n\nimport argparse\nimport asyncio\nimport contextlib\nimport csv\nimport dataclasses\nimport json\nimport multiprocessing\nimport os\nimport subprocess\nimport sys\nimport time\nfrom typing import List, Dict, Tuple, Optional\n\nimport buildbot\n\n# We have some BIG variables\ncsv.field_size_limit(sys.maxsize)\n\n\nclass DataclassJSONEncoder(json.JSONEncoder):\n    \"\"\"JSONEncoder for our custom types.\"\"\"\n    def default(self, o):\n        if dataclasses.is_dataclass(o):\n            return dataclasses.asdict(o)\n        return super().default(o)\n\n\ndef GetProducts():\n    \"\"\"Get the all of the available TARGET_PRODUCT values.\"\"\"\n    try:\n        stdout = subprocess.check_output([\"build/soong/bin/list_products\"], text=True)\n    except subprocess.CalledProcessError:\n        sys.exit(1)\n    return [s.strip() for s in stdout.splitlines() if s.strip()]\n\n\ndef GetReleases(product):\n    \"\"\"For a given product, get the release configs available to it.\"\"\"\n    if True:\n        # Hard code the list\n        mainline_products = [\n            \"module_arm\",\n            \"module_x86\",\n            \"module_arm64\",\n            \"module_riscv64\",\n            \"module_x86_64\",\n            \"module_arm64only\",\n            \"module_x86_64only\",\n        ]\n        if product in mainline_products:\n            return [\"trunk_staging\", \"trunk\", \"mainline\"]\n        else:\n            return [\"trunk_staging\", \"trunk\", \"next\"]\n    else:\n        # Get it from the build system\n        try:\n            stdout = subprocess.check_output([\"build/soong/bin/list_releases\", product], text=True)\n        except subprocess.CalledProcessError:\n            sys.exit(1)\n        return [s.strip() for s in stdout.splitlines() if s.strip()]\n\n\ndef GenerateAllLunchTargets():\n    \"\"\"Generate the full list of lunch targets.\"\"\"\n    for product in GetProducts():\n        for release in GetReleases(product):\n            for variant in [\"user\", \"userdebug\", \"eng\"]:\n                yield (product, release, variant)\n\n\nasync def ParallelExec(parallelism, tasks):\n    '''\n    ParallelExec takes a parallelism number, and an iterator of tasks to run.\n    Then it will run all the tasks, but a maximum of parallelism will be run at\n    any given time. The tasks must be async functions that accept one argument,\n    which will be an integer id of the worker that they're running on.\n    '''\n    tasks = iter(tasks)\n\n    overall_start = time.monotonic()\n    # lists so they can be modified from the inner function\n    total_duration = [0]\n    count = [0]\n    async def dispatch(worker):\n        while True:\n            try:\n                task = next(tasks)\n                item_start = time.monotonic()\n                await task(worker)\n                now = time.monotonic()\n                item_duration = now - item_start\n                count[0] += 1\n                total_duration[0] += item_duration\n                sys.stderr.write(f\"Timing: Items processed: {count[0]}, Wall time: {now-overall_start:0.1f} sec, Throughput: {(now-overall_start)/count[0]:0.3f} sec per item, Average duration: {total_duration[0]/count[0]:0.1f} sec\\n\")\n            except StopIteration:\n                return\n\n    await asyncio.gather(*[dispatch(worker) for worker in range(parallelism)])\n\n\nasync def DumpProductConfigs(out, generator, out_dir):\n    \"\"\"Collects all of the product config data and store it in file.\"\"\"\n    # Write the outer json list by hand so we can stream it\n    out.write(\"[\")\n    try:\n        first_result = [True] # a list so it can be modified from the inner function\n        def run(lunch):\n            async def curried(worker):\n                sys.stderr.write(f\"running: {'-'.join(lunch)}\\n\")\n                result = await DumpOneProductConfig(lunch, os.path.join(out_dir, f\"lunchable_{worker}\"))\n                if first_result[0]:\n                    out.write(\"\\n\")\n                    first_result[0] = False\n                else:\n                    out.write(\",\\n\")\n                result.dumpToFile(out)\n                sys.stderr.write(f\"finished: {'-'.join(lunch)}\\n\")\n            return curried\n\n        await ParallelExec(multiprocessing.cpu_count(), (run(lunch) for lunch in generator))\n    finally:\n        # Close the json regardless of how we exit\n        out.write(\"\\n]\\n\")\n\n\n@dataclasses.dataclass(frozen=True)\nclass Variable:\n    \"\"\"A variable name, value and where it was set.\"\"\"\n    name: str\n    value: str\n    location: str\n\n\n@dataclasses.dataclass(frozen=True)\nclass ProductResult:\n    product: str\n    release: str\n    variant: str\n    board_includes: List[str]\n    product_includes: Dict[str, List[str]]\n    product_graph: List[Tuple[str, str]]\n    board_vars: List[Variable]\n    product_vars: List[Variable]\n\n    def dumpToFile(self, f):\n        json.dump(self, f, sort_keys=True, indent=2, cls=DataclassJSONEncoder)\n\n\n@dataclasses.dataclass(frozen=True)\nclass ProductError:\n    product: str\n    release: str\n    variant: str\n    error: str\n\n    def dumpToFile(self, f):\n        json.dump(self, f, sort_keys=True, indent=2, cls=DataclassJSONEncoder)\n\n\ndef NormalizeInheritGraph(lists):\n    \"\"\"Flatten the inheritance graph to a simple list for easier querying.\"\"\"\n    result = set()\n    for item in lists:\n        for i in range(len(item)):\n            result.add((item[i+1] if i < len(item)-1 else \"\", item[i]))\n    return sorted(list(result))\n\n\ndef ParseDump(lunch, filename) -> ProductResult:\n    \"\"\"Parses the csv and returns a tuple of the data.\"\"\"\n    def diff(initial, final):\n        return [after for after in final.values() if\n                initial.get(after.name, Variable(after.name, \"\", \"<unset>\")).value != after.value]\n    product_initial = {}\n    product_final = {}\n    board_initial = {}\n    board_final = {}\n    inherit_product = [] # The stack of inherit-product calls\n    product_includes = {} # Other files included by each of the properly imported files\n    board_includes = [] # Files included by boardconfig\n    with open(filename) as f:\n        phase = \"\"\n        for line in csv.reader(f):\n            if line[0] == \"phase\":\n                phase = line[1]\n            elif line[0] == \"val\":\n                # TOOD: We should skip these somewhere else.\n                if line[3].startswith(\"_ALL_RELEASE_FLAGS\"):\n                    continue\n                if line[3].startswith(\"PRODUCTS.\"):\n                    continue\n                if phase == \"PRODUCTS\":\n                    if line[2] == \"initial\":\n                        product_initial[line[3]] = Variable(line[3], line[4], line[5])\n                if phase == \"PRODUCT-EXPAND\":\n                    if line[2] == \"final\":\n                        product_final[line[3]] = Variable(line[3], line[4], line[5])\n                if phase == \"BOARD\":\n                    if line[2] == \"initial\":\n                        board_initial[line[3]] = Variable(line[3], line[4], line[5])\n                    if line[2] == \"final\":\n                        board_final[line[3]] = Variable(line[3], line[4], line[5])\n            elif line[0] == \"imported\":\n                imports = [s.strip() for s in line[1].split()]\n                if imports:\n                    inherit_product.append(imports)\n                    inc = [s.strip() for s in line[2].split()]\n                    for f in inc:\n                        product_includes.setdefault(imports[0], []).append(f)\n            elif line[0] == \"board_config_files\":\n                board_includes += [s.strip() for s in line[1].split()]\n    return ProductResult(\n        product = lunch[0],\n        release = lunch[1],\n        variant = lunch[2],\n        product_vars = diff(product_initial, product_final),\n        board_vars = diff(board_initial, board_final),\n        product_graph = NormalizeInheritGraph(inherit_product),\n        product_includes = product_includes,\n        board_includes = board_includes\n    )\n\n\nasync def DumpOneProductConfig(lunch, out_dir) -> ProductResult | ProductError:\n    \"\"\"Print a single config's lunch info to stdout.\"\"\"\n    product, release, variant = lunch\n\n    dumpconfig_file = os.path.join(out_dir, f\"{product}-{release}-{variant}.csv\")\n\n    # Run get_build_var to bootstrap soong_ui for this target\n    env = dict(os.environ)\n    env[\"TARGET_PRODUCT\"] = product\n    env[\"TARGET_RELEASE\"] = release\n    env[\"TARGET_BUILD_VARIANT\"] = variant\n    env[\"OUT_DIR\"] = out_dir\n    process = await asyncio.create_subprocess_exec(\n        \"build/soong/bin/get_build_var\",\n        \"TARGET_PRODUCT\",\n        stdout=subprocess.PIPE,\n        stderr=subprocess.STDOUT,\n        env=env\n    )\n    stdout, _ = await process.communicate()\n    stdout = stdout.decode()\n\n    if process.returncode != 0:\n        return ProductError(\n            product = product,\n            release = release,\n            variant = variant,\n            error = stdout\n        )\n    else:\n        # Run kati to extract the data\n        process = await asyncio.create_subprocess_exec(\n            \"prebuilts/build-tools/linux-x86/bin/ckati\",\n            \"-f\",\n            \"build/make/core/dumpconfig.mk\",\n            f\"TARGET_PRODUCT={product}\",\n            f\"TARGET_RELEASE={release}\",\n            f\"TARGET_BUILD_VARIANT={variant}\",\n            f\"DUMPCONFIG_FILE={dumpconfig_file}\",\n            stdout=subprocess.PIPE,\n            stderr=subprocess.STDOUT,\n            env=env\n        )\n        stdout, _ = await process.communicate()\n        if process.returncode != 0:\n            stdout = stdout.decode()\n            return ProductError(\n                product = product,\n                release = release,\n                variant = variant,\n                error = stdout\n            )\n        else:\n            # Parse and record the output\n            return ParseDump(lunch, dumpconfig_file)\n\n\ndef GetArgs():\n    \"\"\"Parse command line arguments.\"\"\"\n    parser = argparse.ArgumentParser(\n            description=\"Collect all of the make variables from product config.\",\n            epilog=\"NOTE: This script must be run from the root of the source tree.\")\n    parser.add_argument(\"--lunch\", nargs=\"*\")\n    parser.add_argument(\"--dist\", action=\"store_true\")\n\n    return parser.parse_args()\n\n\nasync def main():\n    args = GetArgs()\n\n    out_dir = buildbot.OutDir()\n\n    if args.dist:\n        cm = open(os.path.join(buildbot.DistDir(), \"all_product_config.json\"), \"w\")\n    else:\n        cm = contextlib.nullcontext(sys.stdout)\n\n\n    with cm as out:\n        if args.lunch:\n            lunches = [lunch.split(\"-\") for lunch in args.lunch]\n            fail = False\n            for i in range(len(lunches)):\n                if len(lunches[i]) != 3:\n                    sys.stderr.write(f\"Malformed lunch targets: {args.lunch[i]}\\n\")\n                    fail = True\n            if fail:\n                sys.exit(1)\n            if len(lunches) == 1:\n                result = await DumpOneProductConfig(lunches[0], out_dir)\n                result.dumpToFile(out)\n                out.write(\"\\n\")\n            else:\n                await DumpProductConfigs(out, lunches, out_dir)\n        else:\n            # All configs mode. This will exec single config mode in parallel\n            # for each lunch combo. Write output to $DIST_DIR.\n            await DumpProductConfigs(out, GenerateAllLunchTargets(), out_dir)\n\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n\n\n# vim: set syntax=python ts=4 sw=4 sts=4:\n\n"
  },
  {
    "path": "ci/metrics_agent.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"MetricsAgent is a singleton class that collects metrics for optimized build.\"\"\"\n\nfrom enum import Enum\nimport time\nimport metrics_pb2\nimport os\nimport logging\n\n\nclass MetricsAgent:\n  _SOONG_METRICS_PATH = 'logs/soong_metrics'\n  _DIST_DIR = 'DIST_DIR'\n  _instance = None\n\n  def __init__(self):\n    raise RuntimeError(\n        'MetricsAgent cannot be instantialized, use instance() instead'\n    )\n\n  @classmethod\n  def instance(cls):\n    if not cls._instance:\n      cls._instance = cls.__new__(cls)\n      cls._instance._proto = metrics_pb2.OptimizedBuildMetrics()\n      cls._instance._init_proto()\n      cls._instance._target_results = dict()\n\n    return cls._instance\n\n  def _init_proto(self):\n    self._proto.analysis_perf.name = 'Optimized build analysis time.'\n    self._proto.packaging_perf.name = 'Optimized build total packaging time.'\n\n  def analysis_start(self):\n    self._proto.analysis_perf.start_time = time.time_ns()\n\n  def analysis_end(self):\n    self._proto.analysis_perf.real_time = (\n        time.time_ns() - self._proto.analysis_perf.start_time\n    )\n\n  def packaging_start(self):\n    self._proto.packaging_perf.start_time = time.time_ns()\n\n  def packaging_end(self):\n    self._proto.packaging_perf.real_time = (\n        time.time_ns() - self._proto.packaging_perf.start_time\n    )\n\n  def report_optimized_target(self, name: str):\n    target_result = metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult()\n    target_result.name = name\n    target_result.optimized = True\n    self._target_results[name] = target_result\n\n  def report_unoptimized_target(self, name: str, optimization_rationale: str):\n    target_result = metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult()\n    target_result.name = name\n    target_result.optimization_rationale = optimization_rationale\n    target_result.optimized = False\n    self._target_results[name] = target_result\n\n  def target_packaging_start(self, name: str):\n    target_result = self._target_results.get(name)\n    target_result.packaging_perf.start_time = time.time_ns()\n    self._target_results[name] = target_result\n\n  def target_packaging_end(self, name: str):\n    target_result = self._target_results.get(name)\n    target_result.packaging_perf.real_time = (\n        time.time_ns() - target_result.packaging_perf.start_time\n    )\n\n  def add_target_artifact(\n      self,\n      target_name: str,\n      artifact_name: str,\n      size: int,\n      included_modules: set[str],\n  ):\n    target_result = self.target_results.get(target_name)\n    artifact = (\n        metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult.OutputArtifact()\n    )\n    artifact.name = artifact_name\n    artifact.size = size\n    for module in included_modules:\n      artifact.included_modules.add(module)\n    target_result.output_artifacts.add(artifact)\n\n  def end_reporting(self):\n    for target_result in self._target_results.values():\n      self._proto.target_result.append(target_result)\n    soong_metrics_proto = metrics_pb2.MetricsBase()\n    # Read in existing metrics that should have been written out by the soong\n    # build command so that we don't overwrite them.\n    with open(os.path.join(os.environ[self._DIST_DIR], self._SOONG_METRICS_PATH), 'rb') as f:\n      soong_metrics_proto.ParseFromString(f.read())\n    soong_metrics_proto.optimized_build_metrics.CopyFrom(self._proto)\n    logging.info(soong_metrics_proto)\n    with open(os.path.join(os.environ[self._DIST_DIR], self._SOONG_METRICS_PATH), 'wb') as f:\n      f.write(soong_metrics_proto.SerializeToString())\n"
  },
  {
    "path": "ci/optimized_targets.py",
    "content": "#\n# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom abc import ABC\nimport argparse\nimport functools\nimport json\nimport logging\nimport os\nimport pathlib\nimport subprocess\n\nfrom build_context import BuildContext\nimport test_mapping_module_retriever\n\n\nclass OptimizedBuildTarget(ABC):\n  \"\"\"A representation of an optimized build target.\n\n  This class will determine what targets to build given a given build_cotext and\n  will have a packaging function to generate any necessary output zips for the\n  build.\n  \"\"\"\n\n  _SOONG_UI_BASH_PATH = 'build/soong/soong_ui.bash'\n  _PREBUILT_SOONG_ZIP_PATH = 'prebuilts/build-tools/linux-x86/bin/soong_zip'\n\n  def __init__(\n      self,\n      target: str,\n      build_context: BuildContext,\n      args: argparse.Namespace,\n  ):\n    self.target = target\n    self.build_context = build_context\n    self.args = args\n\n  def get_build_targets(self) -> set[str]:\n    features = self.build_context.enabled_build_features\n    if self.get_enabled_flag() in features:\n      self.modules_to_build = self.get_build_targets_impl()\n      return self.modules_to_build\n\n    self.modules_to_build = {self.target}\n    return {self.target}\n\n  def get_package_outputs_commands(self) -> list[list[str]]:\n    features = self.build_context.enabled_build_features\n    if self.get_enabled_flag() in features:\n      return self.get_package_outputs_commands_impl()\n\n    return []\n\n  def get_package_outputs_commands_impl(self) -> list[list[str]]:\n    raise NotImplementedError(\n        'get_package_outputs_commands_impl not implemented in'\n        f' {type(self).__name__}'\n    )\n\n  def get_enabled_flag(self):\n    raise NotImplementedError(\n        f'get_enabled_flag not implemented in {type(self).__name__}'\n    )\n\n  def get_build_targets_impl(self) -> set[str]:\n    raise NotImplementedError(\n        f'get_build_targets_impl not implemented in {type(self).__name__}'\n    )\n\n  def _generate_zip_options_for_items(\n      self,\n      prefix: str = '',\n      relative_root: str = '',\n      list_files: list[str] | None = None,\n      files: list[str] | None = None,\n      directories: list[str] | None = None,\n  ) -> list[str]:\n    if not list_files and not files and not directories:\n      raise RuntimeError(\n          f'No items specified to be added to zip! Prefix: {prefix}, Relative'\n          f' root: {relative_root}'\n      )\n    command_segment = []\n    # These are all soong_zip options so consult soong_zip --help for specifics.\n    if prefix:\n      command_segment.append('-P')\n      command_segment.append(prefix)\n    if relative_root:\n      command_segment.append('-C')\n      command_segment.append(relative_root)\n    if list_files:\n      for list_file in list_files:\n        command_segment.append('-l')\n        command_segment.append(list_file)\n    if files:\n      for file in files:\n        command_segment.append('-f')\n        command_segment.append(file)\n    if directories:\n      for directory in directories:\n        command_segment.append('-D')\n        command_segment.append(directory)\n\n    return command_segment\n\n  def _query_soong_vars(\n      self, src_top: pathlib.Path, soong_vars: list[str]\n  ) -> dict[str, str]:\n    process_result = subprocess.run(\n        args=[\n            f'{src_top / self._SOONG_UI_BASH_PATH}',\n            '--dumpvars-mode',\n            f'--abs-vars={\" \".join(soong_vars)}',\n        ],\n        env=os.environ,\n        check=False,\n        capture_output=True,\n        text=True,\n    )\n    if not process_result.returncode == 0:\n      logging.error('soong dumpvars command failed! stderr:')\n      logging.error(process_result.stderr)\n      raise RuntimeError('Soong dumpvars failed! See log for stderr.')\n\n    if not process_result.stdout:\n      raise RuntimeError(\n          'Necessary soong variables ' + soong_vars + ' not found.'\n      )\n\n    try:\n      return {\n          line.split('=')[0]: line.split('=')[1].strip(\"'\")\n          for line in process_result.stdout.strip().split('\\n')\n      }\n    except IndexError as e:\n      raise RuntimeError(\n          'Error parsing soong dumpvars output! See output here:'\n          f' {process_result.stdout}',\n          e,\n      )\n\n  def _base_zip_command(\n      self, src_top: pathlib.Path, dist_dir: pathlib.Path, name: str\n  ) -> list[str]:\n    return [\n        f'{src_top / self._PREBUILT_SOONG_ZIP_PATH }',\n        '-d',\n        '-o',\n        f'{dist_dir / name}',\n    ]\n\n\nclass NullOptimizer(OptimizedBuildTarget):\n  \"\"\"No-op target optimizer.\n\n  This will simply build the same target it was given and do nothing for the\n  packaging step.\n  \"\"\"\n\n  def __init__(self, target):\n    self.target = target\n\n  def get_build_targets(self):\n    return {self.target}\n\n  def get_package_outputs_commands(self):\n    return []\n\n\nclass ChangeInfo:\n\n  def __init__(self, change_info_file_path):\n    try:\n      with open(change_info_file_path) as change_info_file:\n        change_info_contents = json.load(change_info_file)\n    except json.decoder.JSONDecodeError:\n      logging.error(f'Failed to load CHANGE_INFO: {change_info_file_path}')\n      raise\n\n    self._change_info_contents = change_info_contents\n\n  def find_changed_files(self) -> set[str]:\n    changed_files = set()\n\n    for change in self._change_info_contents['changes']:\n      project_path = change.get('projectPath') + '/'\n\n      for revision in change.get('revisions'):\n        for file_info in revision.get('fileInfos'):\n          changed_files.add(project_path + file_info.get('path'))\n\n    return changed_files\n\n\nclass GeneralTestsOptimizer(OptimizedBuildTarget):\n  \"\"\"general-tests optimizer\n\n  This optimizer reads in the list of changed files from the file located in\n  env[CHANGE_INFO] and uses this list alongside the normal TEST MAPPING logic to\n  determine what test mapping modules will run for the given changes. It then\n  builds those modules and packages them in the same way general-tests.zip is\n  normally built.\n  \"\"\"\n\n  # List of modules that are built alongside general-tests as dependencies.\n  _REQUIRED_MODULES = frozenset([\n      'cts-tradefed',\n      'vts-tradefed',\n      'compatibility-host-util',\n      'general-tests-shared-libs',\n  ])\n\n  def get_build_targets_impl(self) -> set[str]:\n    change_info_file_path = os.environ.get('CHANGE_INFO')\n    if not change_info_file_path:\n      logging.info(\n          'No CHANGE_INFO env var found, general-tests optimization disabled.'\n      )\n      return {'general-tests'}\n\n    test_infos = self.build_context.test_infos\n    test_mapping_test_groups = set()\n    for test_info in test_infos:\n      is_test_mapping = test_info.is_test_mapping\n      current_test_mapping_test_groups = test_info.test_mapping_test_groups\n      uses_general_tests = test_info.build_target_used('general-tests')\n\n      if uses_general_tests and not is_test_mapping:\n        logging.info(\n            'Test uses general-tests.zip but is not test-mapping, general-tests'\n            ' optimization disabled.'\n        )\n        return {'general-tests'}\n\n      if is_test_mapping:\n        test_mapping_test_groups.update(current_test_mapping_test_groups)\n\n    change_info = ChangeInfo(change_info_file_path)\n    changed_files = change_info.find_changed_files()\n\n    test_mappings = test_mapping_module_retriever.GetTestMappings(\n        changed_files, set()\n    )\n\n    modules_to_build = set(self._REQUIRED_MODULES)\n\n    modules_to_build.update(\n        test_mapping_module_retriever.FindAffectedModules(\n            test_mappings, changed_files, test_mapping_test_groups\n        )\n    )\n\n    return modules_to_build\n\n  def get_package_outputs_commands_impl(self):\n    src_top = pathlib.Path(os.environ.get('TOP', os.getcwd()))\n    dist_dir = pathlib.Path(os.environ.get('DIST_DIR'))\n\n    soong_vars = self._query_soong_vars(\n        src_top,\n        [\n            'HOST_OUT_TESTCASES',\n            'TARGET_OUT_TESTCASES',\n            'PRODUCT_OUT',\n            'SOONG_HOST_OUT',\n            'HOST_OUT',\n        ],\n    )\n    host_out_testcases = pathlib.Path(soong_vars.get('HOST_OUT_TESTCASES'))\n    target_out_testcases = pathlib.Path(soong_vars.get('TARGET_OUT_TESTCASES'))\n    product_out = pathlib.Path(soong_vars.get('PRODUCT_OUT'))\n    soong_host_out = pathlib.Path(soong_vars.get('SOONG_HOST_OUT'))\n    host_out = pathlib.Path(soong_vars.get('HOST_OUT'))\n\n    host_paths = []\n    target_paths = []\n    host_config_files = []\n    target_config_files = []\n    for module in self.modules_to_build:\n      # The required modules are handled separately, no need to package.\n      if module in self._REQUIRED_MODULES:\n        continue\n\n      host_path = host_out_testcases / module\n      if os.path.exists(host_path):\n        host_paths.append(host_path)\n        self._collect_config_files(src_top, host_path, host_config_files)\n\n      target_path = target_out_testcases / module\n      if os.path.exists(target_path):\n        target_paths.append(target_path)\n        self._collect_config_files(src_top, target_path, target_config_files)\n\n      if not os.path.exists(host_path) and not os.path.exists(target_path):\n        logging.info(f'No host or target build outputs found for {module}.')\n\n    zip_commands = []\n\n    zip_commands.extend(\n        self._get_zip_test_configs_zips_commands(\n            src_top,\n            dist_dir,\n            host_out,\n            product_out,\n            host_config_files,\n            target_config_files,\n        )\n    )\n\n    zip_command = self._base_zip_command(src_top, dist_dir, 'general-tests.zip')\n\n    # Add host testcases.\n    if host_paths:\n      zip_command.extend(\n          self._generate_zip_options_for_items(\n              prefix='host',\n              relative_root=f'{src_top / soong_host_out}',\n              directories=host_paths,\n          )\n      )\n\n    # Add target testcases.\n    if target_paths:\n      zip_command.extend(\n          self._generate_zip_options_for_items(\n              prefix='target',\n              relative_root=f'{src_top / product_out}',\n              directories=target_paths,\n          )\n      )\n\n    # TODO(lucafarsi): Push this logic into a general-tests-minimal build command\n    # Add necessary tools. These are also hardcoded in general-tests.mk.\n    framework_path = soong_host_out / 'framework'\n\n    zip_command.extend(\n        self._generate_zip_options_for_items(\n            prefix='host/tools',\n            relative_root=str(framework_path),\n            files=[\n                f\"{framework_path / 'cts-tradefed.jar'}\",\n                f\"{framework_path / 'compatibility-host-util.jar'}\",\n                f\"{framework_path / 'vts-tradefed.jar'}\",\n            ],\n        )\n    )\n\n    zip_commands.append(zip_command)\n    return zip_commands\n\n  def _collect_config_files(\n      self,\n      src_top: pathlib.Path,\n      root_dir: pathlib.Path,\n      config_files: list[str],\n  ):\n    for root, dirs, files in os.walk(src_top / root_dir):\n      for file in files:\n        if file.endswith('.config'):\n          config_files.append(root_dir / file)\n\n  def _get_zip_test_configs_zips_commands(\n      self,\n      src_top: pathlib.Path,\n      dist_dir: pathlib.Path,\n      host_out: pathlib.Path,\n      product_out: pathlib.Path,\n      host_config_files: list[str],\n      target_config_files: list[str],\n  ) -> tuple[list[str], list[str]]:\n    \"\"\"Generate general-tests_configs.zip and general-tests_list.zip.\n\n    general-tests_configs.zip contains all of the .config files that were\n    built and general-tests_list.zip contains a text file which lists\n    all of the .config files that are in general-tests_configs.zip.\n\n    general-tests_configs.zip is organized as follows:\n    /\n      host/\n        testcases/\n          test_1.config\n          test_2.config\n          ...\n      target/\n        testcases/\n          test_1.config\n          test_2.config\n          ...\n\n    So the process is we write out the paths to all the host config files into\n    one\n    file and all the paths to the target config files in another. We also write\n    the paths to all the config files into a third file to use for\n    general-tests_list.zip.\n\n    Args:\n      dist_dir: dist directory.\n      host_out: host out directory.\n      product_out: product out directory.\n      host_config_files: list of all host config files.\n      target_config_files: list of all target config files.\n\n    Returns:\n      The commands to generate general-tests_configs.zip and\n      general-tests_list.zip\n    \"\"\"\n    with open(\n        f\"{host_out / 'host_general-tests_list'}\", 'w'\n    ) as host_list_file, open(\n        f\"{product_out / 'target_general-tests_list'}\", 'w'\n    ) as target_list_file, open(\n        f\"{host_out / 'general-tests_list'}\", 'w'\n    ) as list_file:\n\n      for config_file in host_config_files:\n        host_list_file.write(f'{config_file}' + '\\n')\n        list_file.write('host/' + os.path.relpath(config_file, host_out) + '\\n')\n\n      for config_file in target_config_files:\n        target_list_file.write(f'{config_file}' + '\\n')\n        list_file.write(\n            'target/' + os.path.relpath(config_file, product_out) + '\\n'\n        )\n\n    zip_commands = []\n\n    tests_config_zip_command = self._base_zip_command(\n        src_top, dist_dir, 'general-tests_configs.zip'\n    )\n    tests_config_zip_command.extend(\n        self._generate_zip_options_for_items(\n            prefix='host',\n            relative_root=str(host_out),\n            list_files=[f\"{host_out / 'host_general-tests_list'}\"],\n        )\n    )\n\n    tests_config_zip_command.extend(\n        self._generate_zip_options_for_items(\n            prefix='target',\n            relative_root=str(product_out),\n            list_files=[f\"{product_out / 'target_general-tests_list'}\"],\n        ),\n    )\n\n    zip_commands.append(tests_config_zip_command)\n\n    tests_list_zip_command = self._base_zip_command(\n        src_top, dist_dir, 'general-tests_list.zip'\n    )\n    tests_list_zip_command.extend(\n        self._generate_zip_options_for_items(\n            relative_root=str(host_out),\n            files=[f\"{host_out / 'general-tests_list'}\"],\n        )\n    )\n    zip_commands.append(tests_list_zip_command)\n\n    return zip_commands\n\n  def get_enabled_flag(self):\n    return 'general_tests_optimized'\n\n  @classmethod\n  def get_optimized_targets(cls) -> dict[str, OptimizedBuildTarget]:\n    return {'general-tests': functools.partial(cls)}\n\n\nOPTIMIZED_BUILD_TARGETS = {}\nOPTIMIZED_BUILD_TARGETS.update(GeneralTestsOptimizer.get_optimized_targets())\n"
  },
  {
    "path": "ci/optimized_targets_test.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Tests for optimized_targets.py\"\"\"\n\nimport json\nimport logging\nimport os\nimport pathlib\nimport re\nimport subprocess\nimport textwrap\nimport unittest\nfrom unittest import mock\nfrom build_context import BuildContext\nimport optimized_targets\nfrom pyfakefs import fake_filesystem_unittest\n\n\nclass GeneralTestsOptimizerTest(fake_filesystem_unittest.TestCase):\n\n  def setUp(self):\n    self.setUpPyfakefs()\n\n    os_environ_patcher = mock.patch.dict('os.environ', {})\n    self.addCleanup(os_environ_patcher.stop)\n    self.mock_os_environ = os_environ_patcher.start()\n\n    self._setup_working_build_env()\n    self._write_change_info_file()\n    test_mapping_dir = pathlib.Path('/project/path/file/path')\n    test_mapping_dir.mkdir(parents=True)\n    self._write_test_mapping_file()\n\n  def _setup_working_build_env(self):\n    self.change_info_file = pathlib.Path('/tmp/change_info')\n    self._write_soong_ui_file()\n    self._host_out_testcases = pathlib.Path('/tmp/top/host_out_testcases')\n    self._host_out_testcases.mkdir(parents=True)\n    self._target_out_testcases = pathlib.Path('/tmp/top/target_out_testcases')\n    self._target_out_testcases.mkdir(parents=True)\n    self._product_out = pathlib.Path('/tmp/top/product_out')\n    self._product_out.mkdir(parents=True)\n    self._soong_host_out = pathlib.Path('/tmp/top/soong_host_out')\n    self._soong_host_out.mkdir(parents=True)\n    self._host_out = pathlib.Path('/tmp/top/host_out')\n    self._host_out.mkdir(parents=True)\n\n    self._dist_dir = pathlib.Path('/tmp/top/out/dist')\n    self._dist_dir.mkdir(parents=True)\n\n    self.mock_os_environ.update({\n        'CHANGE_INFO': str(self.change_info_file),\n        'TOP': '/tmp/top',\n        'DIST_DIR': '/tmp/top/out/dist',\n    })\n\n  def _write_soong_ui_file(self):\n    soong_path = pathlib.Path('/tmp/top/build/soong')\n    soong_path.mkdir(parents=True)\n    with open(os.path.join(soong_path, 'soong_ui.bash'), 'w') as f:\n      f.write(\"\"\"\n              #/bin/bash\n              echo HOST_OUT_TESTCASES='/tmp/top/host_out_testcases'\n              echo TARGET_OUT_TESTCASES='/tmp/top/target_out_testcases'\n              echo PRODUCT_OUT='/tmp/top/product_out'\n              echo SOONG_HOST_OUT='/tmp/top/soong_host_out'\n              echo HOST_OUT='/tmp/top/host_out'\n              \"\"\")\n    os.chmod(os.path.join(soong_path, 'soong_ui.bash'), 0o666)\n\n  def _write_change_info_file(self):\n    change_info_contents = {\n        'changes': [{\n            'projectPath': '/project/path',\n            'revisions': [{\n                'fileInfos': [{\n                    'path': 'file/path/file_name',\n                }],\n            }],\n        }]\n    }\n\n    with open(self.change_info_file, 'w') as f:\n      json.dump(change_info_contents, f)\n\n  def _write_test_mapping_file(self):\n    test_mapping_contents = {\n        'test-mapping-group': [\n            {\n                'name': 'test_mapping_module',\n            },\n        ],\n    }\n\n    with open('/project/path/file/path/TEST_MAPPING', 'w') as f:\n      json.dump(test_mapping_contents, f)\n\n  def test_general_tests_optimized(self):\n    optimizer = self._create_general_tests_optimizer()\n\n    build_targets = optimizer.get_build_targets()\n\n    expected_build_targets = set(\n        optimized_targets.GeneralTestsOptimizer._REQUIRED_MODULES\n    )\n    expected_build_targets.add('test_mapping_module')\n\n    self.assertSetEqual(build_targets, expected_build_targets)\n\n  def test_no_change_info_no_optimization(self):\n    del os.environ['CHANGE_INFO']\n\n    optimizer = self._create_general_tests_optimizer()\n\n    build_targets = optimizer.get_build_targets()\n\n    self.assertSetEqual(build_targets, {'general-tests'})\n\n  def test_mapping_groups_unused_module_not_built(self):\n    test_context = self._create_test_context()\n    test_context['testInfos'][0]['extraOptions'] = [\n        {\n            'key': 'additional-files-filter',\n            'values': ['general-tests.zip'],\n        },\n        {\n            'key': 'test-mapping-test-group',\n            'values': ['unused-test-mapping-group'],\n        },\n    ]\n    optimizer = self._create_general_tests_optimizer(\n        build_context=self._create_build_context(test_context=test_context)\n    )\n\n    build_targets = optimizer.get_build_targets()\n\n    expected_build_targets = set(\n        optimized_targets.GeneralTestsOptimizer._REQUIRED_MODULES\n    )\n    self.assertSetEqual(build_targets, expected_build_targets)\n\n  def test_general_tests_used_by_non_test_mapping_test_no_optimization(self):\n    test_context = self._create_test_context()\n    test_context['testInfos'][0]['extraOptions'] = [{\n        'key': 'additional-files-filter',\n        'values': ['general-tests.zip'],\n    }]\n    optimizer = self._create_general_tests_optimizer(\n        build_context=self._create_build_context(test_context=test_context)\n    )\n\n    build_targets = optimizer.get_build_targets()\n\n    self.assertSetEqual(build_targets, {'general-tests'})\n\n  def test_malformed_change_info_raises(self):\n    with open(self.change_info_file, 'w') as f:\n      f.write('not change info')\n\n    optimizer = self._create_general_tests_optimizer()\n\n    with self.assertRaises(json.decoder.JSONDecodeError):\n      build_targets = optimizer.get_build_targets()\n\n  def test_malformed_test_mapping_raises(self):\n    with open('/project/path/file/path/TEST_MAPPING', 'w') as f:\n      f.write('not test mapping')\n\n    optimizer = self._create_general_tests_optimizer()\n\n    with self.assertRaises(json.decoder.JSONDecodeError):\n      build_targets = optimizer.get_build_targets()\n\n  @mock.patch('subprocess.run')\n  def test_packaging_outputs_success(self, subprocess_run):\n    subprocess_run.return_value = self._get_soong_vars_output()\n    optimizer = self._create_general_tests_optimizer()\n    self._set_up_build_outputs(['test_mapping_module'])\n\n    targets = optimizer.get_build_targets()\n    package_commands = optimizer.get_package_outputs_commands()\n\n    self._verify_soong_zip_commands(package_commands, ['test_mapping_module'])\n\n  @mock.patch('subprocess.run')\n  def test_get_soong_dumpvars_fails_raises(self, subprocess_run):\n    subprocess_run.return_value = self._get_soong_vars_output(return_code=-1)\n    optimizer = self._create_general_tests_optimizer()\n    self._set_up_build_outputs(['test_mapping_module'])\n\n    targets = optimizer.get_build_targets()\n\n    with self.assertRaisesRegex(RuntimeError, 'Soong dumpvars failed!'):\n      package_commands = optimizer.get_package_outputs_commands()\n\n  @mock.patch('subprocess.run')\n  def test_get_soong_dumpvars_bad_output_raises(self, subprocess_run):\n    subprocess_run.return_value = self._get_soong_vars_output(\n        stdout='This output is bad'\n    )\n    optimizer = self._create_general_tests_optimizer()\n    self._set_up_build_outputs(['test_mapping_module'])\n\n    targets = optimizer.get_build_targets()\n\n    with self.assertRaisesRegex(\n        RuntimeError, 'Error parsing soong dumpvars output'\n    ):\n      package_commands = optimizer.get_package_outputs_commands()\n\n  def _create_general_tests_optimizer(self, build_context: BuildContext = None):\n    if not build_context:\n      build_context = self._create_build_context()\n    return optimized_targets.GeneralTestsOptimizer(\n        'general-tests', build_context, None\n    )\n\n  def _create_build_context(\n      self,\n      general_tests_optimized: bool = True,\n      test_context: dict[str, any] = None,\n  ) -> BuildContext:\n    if not test_context:\n      test_context = self._create_test_context()\n    build_context_dict = {}\n    build_context_dict['enabledBuildFeatures'] = [{'name': 'optimized_build'}]\n    if general_tests_optimized:\n      build_context_dict['enabledBuildFeatures'].append(\n          {'name': 'general_tests_optimized'}\n      )\n    build_context_dict['testContext'] = test_context\n    return BuildContext(build_context_dict)\n\n  def _create_test_context(self):\n    return {\n        'testInfos': [\n            {\n                'name': 'atp_test',\n                'target': 'test_target',\n                'branch': 'branch',\n                'extraOptions': [\n                    {\n                        'key': 'additional-files-filter',\n                        'values': ['general-tests.zip'],\n                    },\n                    {\n                        'key': 'test-mapping-test-group',\n                        'values': ['test-mapping-group'],\n                    },\n                ],\n                'command': '/tf/command',\n                'extraBuildTargets': [\n                    'extra_build_target',\n                ],\n            },\n        ],\n    }\n\n  def _get_soong_vars_output(\n      self, return_code: int = 0, stdout: str = ''\n  ) -> subprocess.CompletedProcess:\n    return_value = subprocess.CompletedProcess(args=[], returncode=return_code)\n    if not stdout:\n      stdout = textwrap.dedent(f\"\"\"\\\n                               HOST_OUT_TESTCASES='{self._host_out_testcases}'\n                               TARGET_OUT_TESTCASES='{self._target_out_testcases}'\n                               PRODUCT_OUT='{self._product_out}'\n                               SOONG_HOST_OUT='{self._soong_host_out}'\n                               HOST_OUT='{self._host_out}'\"\"\")\n\n    return_value.stdout = stdout\n    return return_value\n\n  def _set_up_build_outputs(self, targets: list[str]):\n    for target in targets:\n      host_dir = self._host_out_testcases / target\n      host_dir.mkdir()\n      (host_dir / f'{target}.config').touch()\n      (host_dir / f'test_file').touch()\n\n      target_dir = self._target_out_testcases / target\n      target_dir.mkdir()\n      (target_dir / f'{target}.config').touch()\n      (target_dir / f'test_file').touch()\n\n  def _verify_soong_zip_commands(self, commands: list[str], targets: list[str]):\n    \"\"\"Verify the structure of the zip commands.\n\n    Zip commands have to start with the soong_zip binary path, then are followed\n    by a couple of options and the name of the file being zipped. Depending on\n    which zip we are creating look for a few essential items being added in\n    those zips.\n\n    Args:\n      commands: list of command lists\n      targets: list of targets expected to be in general-tests.zip\n    \"\"\"\n    for command in commands:\n      self.assertEqual(\n          '/tmp/top/prebuilts/build-tools/linux-x86/bin/soong_zip',\n          command[0],\n      )\n      self.assertEqual('-d', command[1])\n      self.assertEqual('-o', command[2])\n      match (command[3]):\n        case '/tmp/top/out/dist/general-tests_configs.zip':\n          self.assertIn(f'{self._host_out}/host_general-tests_list', command)\n          self.assertIn(\n              f'{self._product_out}/target_general-tests_list', command\n          )\n          return\n        case '/tmp/top/out/dist/general-tests_list.zip':\n          self.assertIn('-f', command)\n          self.assertIn(f'{self._host_out}/general-tests_list', command)\n          return\n        case '/tmp/top/out/dist/general-tests.zip':\n          for target in targets:\n            self.assertIn(f'{self._host_out_testcases}/{target}', command)\n            self.assertIn(f'{self._target_out_testcases}/{target}', command)\n          self.assertIn(\n              f'{self._soong_host_out}/framework/cts-tradefed.jar', command\n          )\n          self.assertIn(\n              f'{self._soong_host_out}/framework/compatibility-host-util.jar',\n              command,\n          )\n          self.assertIn(\n              f'{self._soong_host_out}/framework/vts-tradefed.jar', command\n          )\n          return\n        case _:\n          self.fail(f'malformed command: {command}')\n\n\nif __name__ == '__main__':\n  # Setup logging to be silent so unit tests can pass through TF.\n  logging.disable(logging.ERROR)\n  unittest.main()\n"
  },
  {
    "path": "ci/test_discovery_agent.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Test discovery agent that uses TradeFed to discover test artifacts.\"\"\"\nimport glob\nimport json\nimport logging\nimport os\nimport subprocess\n\n\nclass TestDiscoveryAgent:\n  \"\"\"Test discovery agent.\"\"\"\n\n  _TRADEFED_PREBUILT_JAR_RELATIVE_PATH = (\n      \"vendor/google_tradefederation/prebuilts/filegroups/google-tradefed/\"\n  )\n\n  _TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY = \"NoPossibleTestDiscovery\"\n\n  _TRADEFED_TEST_ZIP_REGEXES_LIST_KEY = \"TestZipRegexes\"\n\n  _TRADEFED_DISCOVERY_OUTPUT_FILE_NAME = \"test_discovery_agent.txt\"\n\n  def __init__(\n      self,\n      tradefed_args: list[str],\n      test_mapping_zip_path: str = \"\",\n      tradefed_jar_revelant_files_path: str = _TRADEFED_PREBUILT_JAR_RELATIVE_PATH,\n  ):\n    self.tradefed_args = tradefed_args\n    self.test_mapping_zip_path = test_mapping_zip_path\n    self.tradefed_jar_relevant_files_path = tradefed_jar_revelant_files_path\n\n  def discover_test_zip_regexes(self) -> list[str]:\n    \"\"\"Discover test zip regexes from TradeFed.\n\n    Returns:\n      A list of test zip regexes that TF is going to try to pull files from.\n    \"\"\"\n    test_discovery_output_file_name = os.path.join(\n        os.environ.get('TOP'), 'out', self._TRADEFED_DISCOVERY_OUTPUT_FILE_NAME\n    )\n    with open(\n        test_discovery_output_file_name, mode=\"w+t\"\n    ) as test_discovery_output_file:\n      java_args = []\n      java_args.append(\"prebuilts/jdk/jdk21/linux-x86/bin/java\")\n      java_args.append(\"-cp\")\n      java_args.append(\n          self.create_classpath(self.tradefed_jar_relevant_files_path)\n      )\n      java_args.append(\n          \"com.android.tradefed.observatory.TestZipDiscoveryExecutor\"\n      )\n      java_args.extend(self.tradefed_args)\n      env = os.environ.copy()\n      env.update({\"DISCOVERY_OUTPUT_FILE\": test_discovery_output_file.name})\n      logging.info(f\"Calling test discovery with args: {java_args}\")\n      try:\n        result = subprocess.run(args=java_args, env=env, text=True, check=True)\n        logging.info(f\"Test zip discovery output: {result.stdout}\")\n      except subprocess.CalledProcessError as e:\n        raise TestDiscoveryError(\n            f\"Failed to run test discovery, strout: {e.stdout}, strerr:\"\n            f\" {e.stderr}, returncode: {e.returncode}\"\n        )\n      data = json.loads(test_discovery_output_file.read())\n      logging.info(f\"Test discovery result file content: {data}\")\n      if (\n          self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY in data\n          and data[self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY]\n      ):\n        raise TestDiscoveryError(\"No possible test discovery\")\n      if (\n          data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is None\n          or data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is []\n      ):\n        raise TestDiscoveryError(\"No test zip regexes returned\")\n      return data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY]\n\n  def discover_test_modules(self) -> list[str]:\n    \"\"\"Discover test modules from TradeFed.\n\n    Returns:\n      A list of test modules that TradeFed is going to execute based on the\n      TradeFed test args.\n    \"\"\"\n    return []\n\n  def create_classpath(self, directory):\n    \"\"\"Creates a classpath string from all .jar files in the given directory.\n\n    Args:\n      directory: The directory to search for .jar files.\n\n    Returns:\n      A string representing the classpath, with jar files separated by the\n      OS-specific path separator (e.g., ':' on Linux/macOS, ';' on Windows).\n    \"\"\"\n    jar_files = glob.glob(os.path.join(directory, \"*.jar\"))\n    return os.pathsep.join(jar_files)\n\n\nclass TestDiscoveryError(Exception):\n  \"\"\"A TestDiscoveryErrorclass.\"\"\"\n\n  def __init__(self, message):\n    super().__init__(message)\n    self.message = message\n"
  },
  {
    "path": "ci/test_mapping_module_retriever.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nSimple parsing code to scan test_mapping files and determine which\nmodules are needed to build for the given list of changed files.\nTODO(lucafarsi): Deduplicate from artifact_helper.py\n\"\"\"\n# TODO(lucafarsi): Share this logic with the original logic in\n# test_mapping_test_retriever.py\n\nimport json\nimport os\nimport re\nfrom typing import Any\n\n# Regex to extra test name from the path of test config file.\nTEST_NAME_REGEX = r'(?:^|.*/)([^/]+)\\.config'\n\n# Key name for TEST_MAPPING imports\nKEY_IMPORTS = 'imports'\nKEY_IMPORT_PATH = 'path'\n\n# Name of TEST_MAPPING file.\nTEST_MAPPING = 'TEST_MAPPING'\n\n# Pattern used to identify double-quoted strings and '//'-format comments in\n# TEST_MAPPING file, but only double-quoted strings are included within the\n# matching group.\n_COMMENTS_RE = re.compile(r'(\\\"(?:[^\\\"\\\\]|\\\\.)*\\\"|(?=//))(?://.*)?')\n\n\ndef FilterComments(test_mapping_file: str) -> str:\n  \"\"\"Remove comments in TEST_MAPPING file to valid format.\n\n  Only '//' is regarded as comments.\n\n  Args:\n    test_mapping_file: Path to a TEST_MAPPING file.\n\n  Returns:\n    Valid json string without comments.\n  \"\"\"\n  return re.sub(_COMMENTS_RE, r'\\1', test_mapping_file)\n\ndef GetTestMappings(paths: set[str],\n                    checked_paths: set[str]) -> dict[str, dict[str, Any]]:\n  \"\"\"Get the affected TEST_MAPPING files.\n\n  TEST_MAPPING files in source code are packaged into a build artifact\n  `test_mappings.zip`. Inside the zip file, the path of each TEST_MAPPING file\n  is preserved. From all TEST_MAPPING files in the source code, this method\n  locates the affected TEST_MAPPING files based on the given paths list.\n\n  A TEST_MAPPING file may also contain `imports` that import TEST_MAPPING files\n  from a different location, e.g.,\n    \"imports\": [\n      {\n        \"path\": \"../folder2\"\n      }\n    ]\n  In that example, TEST_MAPPING files inside ../folder2 (relative to the\n  TEST_MAPPING file containing that imports section) and its parent directories\n  will also be included.\n\n  Args:\n    paths: A set of paths with related TEST_MAPPING files for given changes.\n    checked_paths: A set of paths that have been checked for TEST_MAPPING file\n      already. The set is updated after processing each TEST_MAPPING file. It's\n      used to prevent infinite loop when the method is called recursively.\n\n  Returns:\n    A dictionary of Test Mapping containing the content of the affected\n      TEST_MAPPING files, indexed by the path containing the TEST_MAPPING file.\n  \"\"\"\n  test_mappings = {}\n\n  # Search for TEST_MAPPING files in each modified path and its parent\n  # directories.\n  all_paths = set()\n  for path in paths:\n    dir_names = path.split(os.path.sep)\n    all_paths |= set(\n        [os.path.sep.join(dir_names[:i + 1]) for i in range(len(dir_names))])\n  # Add root directory to the paths to search for TEST_MAPPING file.\n  all_paths.add('')\n\n  all_paths.difference_update(checked_paths)\n  checked_paths |= all_paths\n  # Try to load TEST_MAPPING file in each possible path.\n  for path in all_paths:\n    try:\n      test_mapping_file = os.path.join(os.path.join(os.getcwd(), path), 'TEST_MAPPING')\n      # Read content of TEST_MAPPING file.\n      content = FilterComments(open(test_mapping_file, \"r\").read())\n      test_mapping = json.loads(content)\n      test_mappings[path] = test_mapping\n\n      import_paths = set()\n      for import_detail in test_mapping.get(KEY_IMPORTS, []):\n        import_path = import_detail[KEY_IMPORT_PATH]\n        # Try the import path as absolute path.\n        import_paths.add(import_path)\n        # Try the import path as relative path based on the test mapping file\n        # containing the import.\n        norm_import_path = os.path.normpath(os.path.join(path, import_path))\n        import_paths.add(norm_import_path)\n      import_paths.difference_update(checked_paths)\n      if import_paths:\n        import_test_mappings = GetTestMappings(import_paths, checked_paths)\n        test_mappings.update(import_test_mappings)\n    except (KeyError, FileNotFoundError, NotADirectoryError):\n      # TEST_MAPPING file doesn't exist in path\n      pass\n\n  return test_mappings\n\n\ndef FindAffectedModules(\n    test_mappings: dict[str, Any],\n    changed_files: set[str],\n    test_mapping_test_groups: set[str],\n) -> set[str]:\n  \"\"\"Find affected test modules.\n\n  Find the affected set of test modules that would run in a test mapping run based on the given test mappings, changed files, and test mapping test group.\n\n  Args:\n    test_mappings: A set of test mappings returned by GetTestMappings in the following format:\n      {\n        'test_mapping_file_path': {\n          'group_name' : [\n            'name': 'module_name',\n          ],\n        }\n      }\n    changed_files: A set of files changed for the given run.\n    test_mapping_test_groups: A set of test mapping test groups that are being considered for the given run.\n\n  Returns:\n    A set of test module names which would run for a test mapping test run with the given parameters.\n  \"\"\"\n\n  modules = set()\n\n  for test_mapping in test_mappings.values():\n    for group_name, group in test_mapping.items():\n      # If a module is not in any of the test mapping groups being tested skip\n      # it.\n      if group_name not in test_mapping_test_groups:\n        continue\n\n      for entry in group:\n        module_name = entry.get('name')\n\n        if not module_name:\n          continue\n\n        file_patterns = entry.get('file_patterns')\n        if not file_patterns:\n          modules.add(module_name)\n          continue\n\n        if matches_file_patterns(file_patterns, changed_files):\n          modules.add(module_name)\n\n  return modules\n\ndef MatchesFilePatterns(\n    file_patterns: list[set], changed_files: set[str]\n) -> bool:\n  \"\"\"Checks if any of the changed files match any of the file patterns.\n\n  Args:\n    file_patterns: A list of file patterns to match against.\n    changed_files: A set of files to check against the file patterns.\n\n  Returns:\n    True if any of the changed files match any of the file patterns.\n  \"\"\"\n  return any(re.search(pattern, \"|\".join(changed_files)) for pattern in file_patterns)\n"
  },
  {
    "path": "common/core.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Only use ANDROID_BUILD_SHELL to wrap around bash.\n# DO NOT use other shells such as zsh.\nifdef ANDROID_BUILD_SHELL\nSHELL := $(ANDROID_BUILD_SHELL)\nelse\n# Use bash, not whatever shell somebody has installed as /bin/sh\n# This is repeated from main.mk, since envsetup.sh runs this file\n# directly.\nSHELL := /bin/bash\nendif\n\n# Utility variables.\nempty :=\nspace := $(empty) $(empty)\ncomma := ,\n# Note that make will eat the newline just before endef.\ndefine newline\n\n\nendef\n# The pound character \"#\"\ndefine pound\n#\nendef\n# Unfortunately you can't simply define backslash as \\ or \\\\.\nbackslash := \\a\nbackslash := $(patsubst %a,%,$(backslash))\n\nTOP :=$= .\nTOPDIR :=$=\n\n# Prevent accidentally changing these variables\n.KATI_READONLY := SHELL empty space comma newline pound backslash\n\n# Basic warning/error wrappers. These will be redefined to include the local\n# module information when reading Android.mk files.\ndefine pretty-warning\n$(warning $(1))\nendef\n\ndefine pretty-error\n$(error $(1))\nendef\n"
  },
  {
    "path": "common/json.mk",
    "content": "4space :=$= $(space)$(space)$(space)$(space)\ninvert_bool =$= $(if $(strip $(1)),,true)\n\n# Converts a list to a JSON list.\n# $1: List separator.\n# $2: List.\n_json_list =$= [$(if $(2),\"$(subst $(1),\"$(comma)\",$(2))\")]\n\n# Converts a space-separated list to a JSON list.\njson_list =$= $(call _json_list,$(space),$(1))\n\n# Converts a comma-separated list to a JSON list.\ncsv_to_json_list =$= $(call _json_list,$(comma),$(1))\n\n# Adds or removes 4 spaces from _json_indent\njson_increase_indent =$= $(eval _json_indent := $$(_json_indent)$$(4space))\njson_decrease_indent =$= $(eval _json_indent := $$(subst _,$$(space),$$(patsubst %____,%,$$(subst $$(space),_,$$(_json_indent)))))\n\n# 1: Key name\n# 2: Value\nadd_json_val =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent)\"$$(strip $$(1))\": $$(strip $$(2))$$(comma)$$(newline))\nadd_json_str =$= $(call add_json_val,$(1),\"$(strip $(2))\")\nadd_json_list =$= $(call add_json_val,$(1),$(call json_list,$(patsubst %,%,$(2))))\nadd_json_csv =$= $(call add_json_val,$(1),$(call csv_to_json_list,$(strip $(2))))\nadd_json_bool =$= $(call add_json_val,$(1),$(if $(strip $(2)),true,false))\nadd_json_map =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent)\"$$(strip $$(1))\": {$$(newline))$(json_increase_indent)\nadd_json_map_anon =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent){$$(newline))$(json_increase_indent)\nend_json_map =$= $(json_decrease_indent)$(eval _json_contents := $$(_json_contents)$$(if $$(filter %$$(comma),$$(lastword $$(_json_contents))),__SV_END)$$(_json_indent)},$$(newline))\nadd_json_array =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent)\"$$(strip $$(1))\": [$$(newline))$(json_increase_indent)\nend_json_array =$= $(json_decrease_indent)$(eval _json_contents := $$(_json_contents)$$(if $$(filter %$$(comma),$$(lastword $$(_json_contents))),__SV_END)$$(_json_indent)],$$(newline))\n\n# Clears _json_contents to start a new json file\njson_start =$= $(eval _json_contents := {$$(newline))$(eval _json_indent := $$(4space))\n\n# Adds the trailing close brace to _json_contents, and removes any trailing commas if necessary\njson_end =$= $(eval _json_contents := $$(subst $$(comma)$$(newline)__SV_END,$$(newline),$$(_json_contents)__SV_END}$$(newline)))\n\njson_contents =$= $(_json_contents)\n"
  },
  {
    "path": "common/math.mk",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n###########################################################\n# Basic math functions for non-negative integers <= 100\n#\n# (SDK versions for example)\n###########################################################\n__MATH_POS_NUMBERS :=  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 \\\n                      21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \\\n                      41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \\\n                      61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \\\n                      81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100\n__MATH_NUMBERS := 0 $(__MATH_POS_NUMBERS)\n__MATH_ONE_NUMBERS := 0 1 2 3 4 5 6 7 8 9\n\nmath-error = $(call pretty-error,$(1))\nmath-expect :=\nmath-expect-true :=\nmath-expect :=\nmath-expect-error :=\n\n# Run the math tests with:\n#  make -f ${ANDROID_BUILD_TOP}/build/make/common/math.mk RUN_MATH_TESTS=true\n#  $(get_build_var CKATI) -f ${ANDROID_BUILD_TOP}//build/make/common/math.mk RUN_MATH_TESTS=true\nifdef RUN_MATH_TESTS\n  ifndef empty\n    empty :=\n    space := $(empty) $(empty)\n  endif\n  MATH_TEST_FAILURE :=\n  MATH_TEST_ERROR :=\n  math-error = $(if $(MATH_TEST_ERROR),,$(eval MATH_TEST_ERROR:=$(1)))\n  define math-expect\n    $(eval got:=$$$1) \\\n    $(if $(subst $(got),,$(2))$(subst $(2),,$(got))$(MATH_TEST_ERROR), \\\n      $(if $(MATH_TEST_ERROR),$(warning $(MATH_TEST_ERROR)),$(warning $$$1 '$(got)' != '$(2)')) \\\n      $(eval MATH_TEST_FAILURE := true)) \\\n    $(eval MATH_TEST_ERROR :=) \\\n    $(eval got:=)\n  endef\n  math-expect-true = $(call math-expect,$(1),true)\n  math-expect-false = $(call math-expect,$(1),)\n\n  define math-expect-error\n    $(eval got:=$$$1) \\\n    $(if $(subst $(MATH_TEST_ERROR),,$(2))$(subst $(2),,$(MATH_TEST_ERROR)), \\\n      $(warning '$(MATH_TEST_ERROR)' != '$(2)') \\\n      $(eval MATH_TEST_FAILURE := true)) \\\n    $(eval MATH_TEST_ERROR :=) \\\n    $(eval got:=)\n  endef\nendif\n\n# Returns true if $(1) is a non-negative integer <= 100, otherwise returns nothing.\ndefine math_is_number_in_100\n$(strip \\\n  $(if $(1),,$(call math-error,Argument missing)) \\\n  $(if $(word 2,$(1)),$(call math-error,Multiple words in a single argument: $(1))) \\\n  $(if $(filter $(1),$(__MATH_NUMBERS)),true))\nendef\n\n# Same with math_is_number_in_100, but no limit.\ndefine _math_ext_is_number\n$(strip \\\n  $(if $(1),,$(call math-error,Argument missing)) \\\n  $(if $(word 2,$(1)),$(call math-error,Multiple words in a single argument: $(1))) \\\n  $(eval should_empty:=$(1)) \\\n  $(foreach num,$(__MATH_ONE_NUMBERS),\\\n    $(eval should_empty:=$(subst $(num),$(empty),$(should_empty)))) \\\n  $(if $(should_empty),,true))\nendef\n\n# Returns true if $(1) is a non-negative integer.\ndefine math_is_number\n$(strip $(if $(call math_is_number_in_100,$(1)),true,$(call _math_ext_is_number,$(1))))\nendef\n\n# Returns true if $(1) is a positive or negative integer.\ndefine math_is_int\n$(call math_is_number,$(patsubst -%,%,$(1)))\nendef\n\ndefine math_is_zero\n$(strip \\\n  $(if $(word 2,$(1)),$(call math-error,Multiple words in a single argument: $(1))) \\\n  $(if $(filter 0,$(1)),true))\nendef\n\n$(call math-expect-true,(call math_is_number,0))\n$(call math-expect-true,(call math_is_number,2))\n$(call math-expect-true,(call math_is_number,202412))\n$(call math-expect-false,(call math_is_number,foo))\n$(call math-expect-false,(call math_is_number,-1))\n$(call math-expect-true,(call math_is_int,50))\n$(call math-expect-true,(call math_is_int,-1))\n$(call math-expect-true,(call math_is_int,-528))\n$(call math-expect-true,(call math_is_int,-0))\n$(call math-expect-false,(call math_is_int,--1))\n$(call math-expect-false,(call math_is_int,-))\n$(call math-expect-error,(call math_is_number,1 2),Multiple words in a single argument: 1 2)\n$(call math-expect-error,(call math_is_number,no 2),Multiple words in a single argument: no 2)\n\n$(call math-expect-true,(call math_is_zero,0))\n$(call math-expect-false,(call math_is_zero,1))\n$(call math-expect-false,(call math_is_zero,foo))\n$(call math-expect-error,(call math_is_zero,1 2),Multiple words in a single argument: 1 2)\n$(call math-expect-error,(call math_is_zero,no 2),Multiple words in a single argument: no 2)\n\ndefine _math_check_valid\n$(if $(call math_is_number_in_100,$(1)),,$(call math-error,Only non-negative integers <= 100 are supported (not $(1))))\nendef\n\n$(call math-expect,(call _math_check_valid,0))\n$(call math-expect,(call _math_check_valid,1))\n$(call math-expect,(call _math_check_valid,100))\n$(call math-expect-error,(call _math_check_valid,-1),Only non-negative integers <= 100 are supported (not -1))\n$(call math-expect-error,(call _math_check_valid,101),Only non-negative integers <= 100 are supported (not 101))\n$(call math-expect-error,(call _math_check_valid,),Argument missing)\n$(call math-expect-error,(call _math_check_valid,1 2),Multiple words in a single argument: 1 2)\n\n# return a list containing integers ranging from [$(1),$(2)]\ndefine int_range_list\n$(strip \\\n  $(call _math_check_valid,$(1))$(call _math_check_valid,$(2)) \\\n  $(if $(call math_is_zero,$(1)),0)\\\n  $(wordlist $(if $(call math_is_zero,$(1)),1,$(1)),$(2),$(__MATH_POS_NUMBERS)))\nendef\n\n$(call math-expect,(call int_range_list,0,1),0 1)\n$(call math-expect,(call int_range_list,1,1),1)\n$(call math-expect,(call int_range_list,1,2),1 2)\n$(call math-expect,(call int_range_list,2,1),)\n$(call math-expect-error,(call int_range_list,1,101),Only non-negative integers <= 100 are supported (not 101))\n\n# Split an integer into a list of digits\ndefine _math_number_to_list\n$(strip \\\n  $(if $(call _math_ext_is_number,$(1)),,\\\n    $(call math-error,Only non-negative integers are supported (not $(1)))) \\\n  $(eval num_list:=$(1)) \\\n  $(foreach num,$(__MATH_ONE_NUMBERS),\\\n    $(eval num_list:=$(subst $(num),$(space)$(num),$(num_list)))) \\\n  $(if $(filter $(words $(num_list)),$(__MATH_ONE_NUMBERS)),,\\\n    $(call math-error,Only non-negative integers with less than 9 digits are supported (not $(1)))) \\\n  $(if $(filter 0,$(word 1,$(num_list))),\\\n    $(call math-error,Only non-negative integers without leading zeros are supported (not $(1)))) \\\n  $(num_list))\nendef\n\n$(call math-expect,(call _math_number_to_list,123),1 2 3)\n$(call math-expect-error,(call _math_number_to_list,123 456),Multiple words in a single argument: 123 456)\n$(call math-expect-error,(call _math_number_to_list,-123),Only non-negative integers are supported (not -123))\n$(call math-expect-error,(call _math_number_to_list,002),Only non-negative integers without leading zeros are supported (not 002))\n$(call math-expect-error,(call _math_number_to_list,1234567890),Only non-negative integers with less than 9 digits are supported (not 1234567890))\n\n# Compare 1-digit integer $(1) and $(2).\n# Returns 1 if $(1) > $(2), -1 if $(1) < $(2), nothing if equals.\ndefine _math_1digit_comp\n$(strip \\\n  $(if $(filter $(1),$(2)),,\\\n    $(if $(filter $(1),$(firstword $(filter $(1) $(2),$(__MATH_ONE_NUMBERS)))),-1,1)))\nendef\n\n$(call math-expect,(call _math_1digit_comp,1,1))\n$(call math-expect,(call _math_1digit_comp,0,9),-1)\n$(call math-expect,(call _math_1digit_comp,3,1),1)\n\n# Compare the same $(3)-digit-length integers $(1) and $(2) that are split into a list of digits.\n# Returns 1 if $(1) > $(2), -1 if $(1) < $(2), nothing if equals.\ndefine _math_list_comp\n$(strip \\\n  $(eval ans:=) \\\n  $(foreach num,$(call int_range_list,1,$(3)),\\\n    $(if $(ans),,$(eval ans:=$(call _math_1digit_comp,$(word $(num),$(1)),$(word $(num),$(2)))))) \\\n  $(ans))\nendef\n\n# Compare any two non-negative integers $(1) and $(2).\n# Returns 1 if $(1) > $(2), -1 if $(1) < $(2), nothing if equals.\ndefine _math_ext_comp\n$(strip \\\n  $(eval num_list1:=$(call _math_number_to_list,$(1))) \\\n  $(eval len1:=$(words $(num_list1))) \\\n  $(eval num_list2:=$(call _math_number_to_list,$(2))) \\\n  $(eval len2:=$(words $(num_list2))) \\\n  $(eval comp:=$(call _math_1digit_comp,$(len1),$(len2))) \\\n  $(if $(comp),$(comp),$(call _math_list_comp,$(num_list1),$(num_list2),$(len1))))\nendef\n\n$(call math-expect,(call _math_ext_comp,5,10),-1)\n$(call math-expect,(call _math_ext_comp,12345,12345))\n$(call math-expect,(call _math_ext_comp,500,5),1)\n$(call math-expect,(call _math_ext_comp,202404,202504),-1)\n\n# Returns the greater of $1 or $2.\n# If $1 or $2 is not a positive integer, then an error is generated.\ndefine math_max\n$(strip \\\n  $(if $(filter truetrue,$(call math_is_number_in_100,$(1))$(call math_is_number_in_100,$(2))),\\\n    $(lastword $(filter $(1) $(2),$(__MATH_NUMBERS))),\\\n    $(if $(filter 1,$(call _math_ext_comp,$(1),$(2))),$(1),$(2))))\nendef\n\n# Returns the lesser of $1 or $2.\ndefine math_min\n$(strip \\\n  $(if $(filter truetrue,$(call math_is_number_in_100,$(1))$(call math_is_number_in_100,$(2))),\\\n    $(firstword $(filter $(1) $(2),$(__MATH_NUMBERS))),\\\n    $(if $(filter -1,$(call _math_ext_comp,$(1),$(2))),$(1),$(2))))\nendef\n\n$(call math-expect-error,(call math_max),Argument missing)\n$(call math-expect-error,(call math_max,1),Argument missing)\n$(call math-expect-error,(call math_max,1 2,3),Multiple words in a single argument: 1 2)\n$(call math-expect-error,(call math_min,1,2 3),Multiple words in a single argument: 2 3)\n$(call math-expect,(call math_max,0,1),1)\n$(call math-expect,(call math_max,1,0),1)\n$(call math-expect,(call math_max,1,1),1)\n$(call math-expect,(call math_max,5,42),42)\n$(call math-expect,(call math_max,42,5),42)\n$(call math-expect,(call math_min,0,1),0)\n$(call math-expect,(call math_min,1,0),0)\n$(call math-expect,(call math_min,1,1),1)\n$(call math-expect,(call math_min,7,32),7)\n$(call math-expect,(call math_min,32,7),7)\n\n$(call math-expect,(call math_max,32759,7),32759)\n$(call math-expect,(call math_max,7,32759),32759)\n$(call math-expect,(call math_max,202404,202505),202505)\n$(call math-expect,(call math_max,202404,202404),202404)\n$(call math-expect,(call math_min,8908527,32),32)\n$(call math-expect,(call math_min,32,8908527),32)\n$(call math-expect,(call math_min,202404,202505),202404)\n$(call math-expect,(call math_min,202404,202404),202404)\n\ndefine math_gt_or_eq\n$(if $(filter $(1),$(call math_max,$(1),$(2))),true)\nendef\n\ndefine math_gt\n$(if $(call math_gt_or_eq,$(2),$(1)),,true)\nendef\n\ndefine math_lt_or_eq\n$(if $(call math_gt_or_eq,$(2),$(1)),true)\nendef\n\ndefine math_lt\n$(if $(call math_gt_or_eq,$(1),$(2)),,true)\nendef\n\n$(call math-expect-true,(call math_gt_or_eq, 2, 1))\n$(call math-expect-true,(call math_gt_or_eq, 1, 1))\n$(call math-expect-false,(call math_gt_or_eq, 1, 2))\n$(call math-expect-true,(call math_gt, 4, 3))\n$(call math-expect-false,(call math_gt, 5, 5))\n$(call math-expect-false,(call math_gt, 6, 7))\n$(call math-expect-true,(call math_lt_or_eq, 11, 11))\n$(call math-expect-false,(call math_lt_or_eq, 25, 15))\n$(call math-expect-true,(call math_lt_or_eq, 9, 16))\n$(call math-expect-false,(call math_lt, 1, 0))\n$(call math-expect-false,(call math_lt, 8, 8))\n$(call math-expect-true,(call math_lt, 10, 11))\n\n$(call math-expect-true,(call math_gt_or_eq, 2573904, 2573900))\n$(call math-expect-true,(call math_gt_or_eq, 12345, 12345))\n$(call math-expect-false,(call math_gt_or_eq, 56, 2780))\n\n# $1 is the variable name to increment\ndefine inc_and_print\n$(strip $(eval $(1) := $($(1)) .)$(words $($(1))))\nendef\n\nifdef RUN_MATH_TESTS\na :=\n$(call math-expect,(call inc_and_print,a),1)\n$(call math-expect,(call inc_and_print,a),2)\n$(call math-expect,(call inc_and_print,a),3)\n$(call math-expect,(call inc_and_print,a),4)\nendif\n\n# Returns the words in $2 that are numbers and are less than $1\ndefine numbers_less_than\n$(strip \\\n  $(foreach n,$2, \\\n    $(if $(call math_is_number,$(n)), \\\n      $(if $(call math_lt,$(n),$(1)), \\\n        $(n)))))\nendef\n\n$(call math-expect,(call numbers_less_than,0,0 1 2 3),)\n$(call math-expect,(call numbers_less_than,1,0 2 1 3),0)\n$(call math-expect,(call numbers_less_than,2,0 2 1 3),0 1)\n$(call math-expect,(call numbers_less_than,3,0 2 1 3),0 2 1)\n$(call math-expect,(call numbers_less_than,4,0 2 1 3),0 2 1 3)\n$(call math-expect,(call numbers_less_than,3,0 2 1 3 2),0 2 1 2)\n$(call math-expect,(call numbers_less_than,100,0 1000 50 101 100),0 50)\n\n# Returns the words in $2 that are numbers and are greater or equal to $1\ndefine numbers_greater_or_equal_to\n$(strip \\\n  $(foreach n,$2, \\\n    $(if $(call math_is_number,$(n)), \\\n      $(if $(call math_gt_or_eq,$(n),$(1)), \\\n        $(n)))))\nendef\n\n$(call math-expect,(call numbers_greater_or_equal_to,4,0 1 2 3),)\n$(call math-expect,(call numbers_greater_or_equal_to,3,0 2 1 3),3)\n$(call math-expect,(call numbers_greater_or_equal_to,2,0 2 1 3),2 3)\n$(call math-expect,(call numbers_greater_or_equal_to,1,0 2 1 3),2 1 3)\n$(call math-expect,(call numbers_greater_or_equal_to,0,0 2 1 3),0 2 1 3)\n$(call math-expect,(call numbers_greater_or_equal_to,1,0 2 1 3 2),2 1 3 2)\n\n# 10,001 = 10 ** 4 + 1, contains 10,001 x's, so 1 more than 10,000 (future) API level\n_INT_LIMIT_WORDS := x $(foreach a,0 1 2 3 4 5 6 7 8 9,$(foreach b,0 1 2 3 4 5 6 7 8 9,\\\n  $(foreach c,0 1 2 3 4 5 6 7 8 9,x x x x x x x x x x)))\n\ndefine _int_encode\n$(if $(filter $(words x $(_INT_LIMIT_WORDS)),$(words $(wordlist 1,$(1),x $(_INT_LIMIT_WORDS)))),\\\n  $(call math-error,integer greater than $(words $(_INT_LIMIT_WORDS)) is not supported!),\\\n    $(wordlist 1,$(1),$(_INT_LIMIT_WORDS)))\nendef\n\n# _int_max returns the maximum of the two arguments\n# input: two (x) lists; output: one (x) list\n# integer cannot be passed in directly. It has to be converted using _int_encode.\ndefine _int_max\n$(subst xx,x,$(join $(1),$(2)))\nendef\n\n# first argument is greater than second argument\n# output: non-empty if true\n# integer cannot be passed in directly. It has to be converted using _int_encode.\ndefine _int_greater-than\n$(filter-out $(words $(2)),$(words $(call _int_max,$(1),$(2))))\nendef\n\n# first argument equals to second argument\n# output: non-empty if true\n# integer cannot be passed in directly. It has to be converted using _int_encode.\ndefine _int_equal\n$(filter $(words $(1)),$(words $(2)))\nendef\n\n# first argument is greater than or equal to second argument\n# output: non-empty if true\n# integer cannot be passed in directly. It has to be converted using _int_encode.\ndefine _int_greater-or-equal\n$(call _int_greater-than,$(1),$(2))$(call _int_equal,$(1),$(2))\nendef\n\ndefine int_plus\n$(words $(call _int_encode,$(1)) $(call _int_encode,$(2)))\nendef\n\n$(call math-expect,(call int_plus,0,0),0)\n$(call math-expect,(call int_plus,0,1),1)\n$(call math-expect,(call int_plus,1,0),1)\n$(call math-expect,(call int_plus,1,100),101)\n$(call math-expect,(call int_plus,100,100),200)\n\ndefine int_subtract\n$(strip \\\n  $(if $(call _int_greater-or-equal,$(call _int_encode,$(1)),$(call _int_encode,$(2))),\\\n  $(words $(filter-out xx,$(join $(call _int_encode,$(1)),$(call _int_encode,$(2))))),\\\n    $(call math-error,subtract underflow $(1) - $(2))))\nendef\n\n$(call math-expect,(call int_subtract,0,0),0)\n$(call math-expect,(call int_subtract,1,0),1)\n$(call math-expect,(call int_subtract,1,1),0)\n$(call math-expect,(call int_subtract,100,1),99)\n$(call math-expect,(call int_subtract,200,100),100)\n$(call math-expect-error,(call int_subtract,0,1),subtract underflow 0 - 1)\n\ndefine int_multiply\n$(words $(foreach a,$(call _int_encode,$(1)),$(call _int_encode,$(2))))\nendef\n\n$(call math-expect,(call int_multiply,0,0),0)\n$(call math-expect,(call int_multiply,1,0),0)\n$(call math-expect,(call int_multiply,1,1),1)\n$(call math-expect,(call int_multiply,100,1),100)\n$(call math-expect,(call int_multiply,1,100),100)\n$(call math-expect,(call int_multiply,4,100),400)\n$(call math-expect,(call int_multiply,100,4),400)\n\ndefine int_divide\n$(if $(filter 0,$(2)),$(call math-error,division by zero is not allowed!),$(strip \\\n  $(if $(call _int_greater-or-equal,$(call _int_encode,$(1)),$(call _int_encode,$(2))), \\\n    $(call int_plus,$(call int_divide,$(call int_subtract,$(1),$(2)),$(2)),1),0)))\nendef\n\n$(call math-expect,(call int_divide,1,1),1)\n$(call math-expect,(call int_divide,200,1),200)\n$(call math-expect,(call int_divide,200,3),66)\n$(call math-expect,(call int_divide,1,2),0)\n$(call math-expect-error,(call int_divide,0,0),division by zero is not allowed!)\n$(call math-expect-error,(call int_divide,1,0),division by zero is not allowed!)\n\nifdef RUN_MATH_TESTS\n  ifdef MATH_TEST_FAILURE\n    math-tests:\n\t@echo FAIL\n\t@false\n  else\n    math-tests:\n\t@echo PASS\n  endif\n  .PHONY: math-tests\nendif\n"
  },
  {
    "path": "common/strings.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n###########################################################\n## Convert to lower case without requiring a shell, which isn't cacheable.\n##\n## $(1): string\n###########################################################\nto-lower=$(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))\n\n###########################################################\n## Convert to upper case without requiring a shell, which isn't cacheable.\n##\n## $(1): string\n###########################################################\nto-upper=$(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$1))))))))))))))))))))))))))\n\n# Test to-lower and to-upper\nlower := abcdefghijklmnopqrstuvwxyz-_\nupper := ABCDEFGHIJKLMNOPQRSTUVWXYZ-_\n\nifneq ($(lower),$(call to-lower,$(upper)))\n  $(error to-lower sanity check failure)\nendif\n\nifneq ($(upper),$(call to-upper,$(lower)))\n  $(error to-upper sanity check failure)\nendif\n\nlower :=\nupper :=\n\n###########################################################\n## Returns true if $(1) and $(2) are equal.  Returns\n## the empty string if they are not equal.\n###########################################################\ndefine streq\n$(strip $(if $(strip $(1)),\\\n  $(if $(strip $(2)),\\\n    $(if $(filter-out __,_$(subst $(strip $(1)),,$(strip $(2)))$(subst $(strip $(2)),,$(strip $(1)))_),,true), \\\n    ),\\\n  $(if $(strip $(2)),\\\n    ,\\\n    true)\\\n ))\nendef\n\n###########################################################\n## Convert \"a b c\" into \"a:b:c\"\n###########################################################\ndefine normalize-path-list\n$(subst $(space),:,$(strip $(1)))\nendef\n\n###########################################################\n## Convert \"a b c\" into \"a,b,c\"\n###########################################################\ndefine normalize-comma-list\n$(subst $(space),$(comma),$(strip $(1)))\nendef\n\n###########################################################\n## Read the word out of a colon-separated list of words.\n## This has the same behavior as the built-in function\n## $(word n,str).\n##\n## The individual words may not contain spaces.\n##\n## $(1): 1 based index\n## $(2): value of the form a:b:c...\n###########################################################\n\ndefine word-colon\n$(word $(1),$(subst :,$(space),$(2)))\nendef\n\n###########################################################\n## Read a colon-separated sublist out of a colon-separated\n## list of words.\n## This has similar behavior to the built-in function\n## $(wordlist s,e,str) except both the input and output\n## word lists are colon-separated.\n##\n## The individual words may not contain spaces.\n##\n## $(1): 1 based index start\n## $(2): 1 based index end (can be 0)\n## $(3): value of the form a:b:c...\n###########################################################\n\ndefine wordlist-colon\n$(subst $(space),:,$(wordlist $(1),$(2),$(subst :,$(space),$(3))))\nendef\n\n###########################################################\n## Convert \"a=b c= d e = f = g h=\" into \"a=b c=d e= f=g h=\"\n##\n## $(1): list to collapse\n## $(2): if set, separator word; usually \"=\", \":\", or \":=\"\n##       Defaults to \"=\" if not set.\n###########################################################\n\ndefine collapse-pairs\n$(strip \\\n$(eval _cpSEP := $(strip $(if $(2),$(2),=)))\\\n$(eval _cpLHS :=)\\\n$(eval _cpRET :=)\\\n$(foreach w,$(subst $(space)$(_cpSEP),$(_cpSEP),$(strip \\\n            $(subst $(_cpSEP),$(space)$(_cpSEP)$(space),$(1)))),\\\n  $(if $(findstring $(_cpSEP),$(w)),\\\n    $(eval _cpRET += $(_cpLHS))$(eval _cpLHS := $(w)),\\\n    $(eval _cpRET += $(_cpLHS)$(w))$(eval _cpLHS :=)))\\\n$(if $(_cpLHS),$(_cpRET)$(space)$(_cpLHS),$(_cpRET))\\\n$(eval _cpSEP :=)\\\n$(eval _cpLHS :=)\\\n$(eval _cpRET :=))\nendef\n\n# Sanity check for collapse-pairs.\nifneq (a=b c=d e= f=g h=,$(call collapse-pairs,a=b c= d e = f = g h=))\n  $(error collapse-pairs sanity check failure)\nendif\nifneq (a:=b c:=d e:=f g:=h,$(call collapse-pairs,a:=b c:= d e :=f g := h,:=))\n  $(error collapse-pairs sanity check failure)\nendif\n\n###########################################################\n## Given a list of pairs, if multiple pairs have the same\n## first components, keep only the first pair.\n##\n## $(1): list of pairs\n## $(2): the separator word, such as \":\", \"=\", etc.\ndefine uniq-pairs-by-first-component\n$(eval _upbfc_fc_set :=)\\\n$(strip $(foreach w,$(1), $(eval _first := $(word 1,$(subst $(2),$(space),$(w))))\\\n    $(if $(filter $(_upbfc_fc_set),$(_first)),,$(w)\\\n        $(eval _upbfc_fc_set += $(_first)))))\\\n$(eval _upbfc_fc_set :=)\\\n$(eval _first:=)\nendef\n"
  },
  {
    "path": "core/LINUX_KERNEL_COPYING",
    "content": "\n   NOTE! This copyright does *not* cover user programs that use kernel\n services by normal system calls - this is merely considered normal use\n of the kernel, and does *not* fall under the heading of \"derived work\".\n Also note that the GPL below is copyrighted by the Free Software\n Foundation, but the instance of code that it refers to (the Linux\n kernel) is copyrighted by me and others who actually wrote it.\n\n Also note that the only valid version of the GPL as far as the kernel\n is concerned is _this_ particular version of the license (ie v2, not\n v2.2 or v3.x or whatever), unless explicitly otherwise stated.\n\n\t\t\tLinus Torvalds\n\n----------------------------------------\n\n\t\t    GNU GENERAL PUBLIC LICENSE\n\t\t       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Library General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\f\n\t\t    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\f\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\f\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\f\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n\f\n\t    How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program; if not, write to the Free Software\n    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Library General\nPublic License instead of this License.\n"
  },
  {
    "path": "core/Makefile",
    "content": "# Put some miscellaneous rules here\n\n# HACK: clear LOCAL_PATH from including last build target before calling\n# intermedites-dir-for\nLOCAL_PATH := $(BUILD_SYSTEM)\n\nSYSTEM_NOTICE_DEPS :=\nVENDOR_NOTICE_DEPS :=\nUNMOUNTED_NOTICE_DEPS :=\nUNMOUNTED_NOTICE_VENDOR_DEPS :=\nODM_NOTICE_DEPS :=\nOEM_NOTICE_DEPS :=\nPRODUCT_NOTICE_DEPS :=\nSYSTEM_EXT_NOTICE_DEPS :=\nVENDOR_DLKM_NOTICE_DEPS :=\nODM_DLKM_NOTICE_DEPS :=\nSYSTEM_DLKM_NOTICE_DEPS :=\n\n\n# IMAGES_TO_BUILD is a list of the partition .img files that will be created.\nIMAGES_TO_BUILD:=\nifneq ($(BUILDING_BOOT_IMAGE),)\n  IMAGES_TO_BUILD += boot\nendif\nifneq ($(BUILDING_CACHE_IMAGE),)\n  IMAGES_TO_BUILD += cache\nendif\nifneq ($(BUILDING_DEBUG_BOOT_IMAGE),)\n  IMAGES_TO_BUILD += debug_boot\nendif\nifneq ($(BUILDING_DEBUG_VENDOR_BOOT_IMAGE),)\n  IMAGES_TO_BUILD += debug_vendor_boot\nendif\nifneq ($(BUILDING_INIT_BOOT_IMAGE),)\n  IMAGES_TO_BUILD += init_boot\nendif\nifneq ($(BUILDING_ODM_DLKM_IMAGE),)\n  IMAGES_TO_BUILD += odm_dlkm\nendif\nifneq ($(BUILDING_ODM_IMAGE),)\n  IMAGES_TO_BUILD += odm\nendif\nifneq ($(BUILDING_PRODUCT_IMAGE),)\n  IMAGES_TO_BUILD += product\nendif\nifneq ($(BUILDING_RAMDISK_IMAGE),)\n  IMAGES_TO_BUILD += ramdisk\nendif\nifneq ($(BUILDING_RECOVERY_IMAGE),)\n  IMAGES_TO_BUILD += recovery\nendif\nifneq ($(BUILDING_SUPER_EMPTY_IMAGE),)\n  IMAGES_TO_BUILD += super_empty\nendif\nifneq ($(BUILDING_SYSTEM_DLKM_IMAGE),)\n  IMAGES_TO_BUILD += system_dlkm\nendif\nifneq ($(BUILDING_SYSTEM_EXT_IMAGE),)\n  IMAGES_TO_BUILD += system_ext\nendif\nifneq ($(BUILDING_SYSTEM_IMAGE),)\n  IMAGES_TO_BUILD += system\nendif\nifneq ($(BUILDING_SYSTEM_OTHER_IMAGE),)\n  IMAGES_TO_BUILD += system_other\nendif\nifneq ($(BUILDING_USERDATA_IMAGE),)\n  IMAGES_TO_BUILD += userdata\nendif\nifneq ($(BUILDING_VBMETA_IMAGE),)\n  IMAGES_TO_BUILD += vbmeta\nendif\nifneq ($(BUILDING_VENDOR_BOOT_IMAGE),)\n  IMAGES_TO_BUILD += vendor_boot\nendif\nifneq ($(BUILDING_VENDOR_DLKM_IMAGE),)\n  IMAGES_TO_BUILD += vendor_dlkm\nendif\nifneq ($(BUILDING_VENDOR_IMAGE),)\n  IMAGES_TO_BUILD += vendor\nendif\nifneq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),)\n  IMAGES_TO_BUILD += vendor_kernel_boot\nendif\n\n\n# Release & Aconfig Flags\n# -----------------------------------------------------------------\ninclude $(BUILD_SYSTEM)/packaging/flags.mk\n\n\n# -----------------------------------------------------------------\n# Define rules to copy PRODUCT_COPY_FILES defined by the product.\n# PRODUCT_COPY_FILES contains words like <source file>:<dest file>[:<owner>].\n# <dest file> is relative to $(PRODUCT_OUT), so it should look like,\n# e.g., \"system/etc/file.xml\".\n# The filter part means \"only eval the copy-one-file rule if this\n# src:dest pair is the first one to match the same dest\"\n#$(1): the src:dest pair\n#$(2): the dest\ndefine check-product-copy-files\n$(if $(filter-out $(TARGET_COPY_OUT_SYSTEM_OTHER)/%,$(2)), \\\n  $(if $(filter %.apk, $(2)),$(error \\\n     Prebuilt apk found in PRODUCT_COPY_FILES: $(1), use BUILD_PREBUILT instead!))) \\\n$(if $(filter true,$(BUILD_BROKEN_VINTF_PRODUCT_COPY_FILES)),, \\\n  $(if $(filter $(TARGET_COPY_OUT_SYSTEM)/etc/vintf/% \\\n                $(TARGET_COPY_OUT_SYSTEM)/manifest.xml \\\n                $(TARGET_COPY_OUT_SYSTEM)/compatibility_matrix.xml,$(2)), \\\n    $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), use vintf_fragments instead!)) \\\n  $(if $(filter $(TARGET_COPY_OUT_PRODUCT)/etc/vintf/%,$(2)), \\\n    $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \\\n      use PRODUCT_MANIFEST_FILES / DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE / vintf_compatibility_matrix / vintf_fragments instead!)) \\\n  $(if $(filter $(TARGET_COPY_OUT_SYSTEM_EXT)/etc/vintf/%,$(2)), \\\n    $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \\\n      use vintf_compatibility_matrix / vintf_fragments instead!)) \\\n  $(if $(filter $(TARGET_COPY_OUT_VENDOR)/etc/vintf/% \\\n                $(TARGET_COPY_OUT_VENDOR)/manifest.xml \\\n                $(TARGET_COPY_OUT_VENDOR)/compatibility_matrix.xml,$(2)), \\\n    $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \\\n      use DEVICE_MANIFEST_FILE / DEVICE_MATRIX_FILE / vintf_compatibility_matrix / vintf_fragments instead!)) \\\n  $(if $(filter $(TARGET_COPY_OUT_ODM)/etc/vintf/% \\\n                $(TARGET_COPY_OUT_ODM)/etc/manifest%,$(2)), \\\n    $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \\\n      use ODM_MANIFEST_FILES / vintf_fragments instead!)) \\\n)\nendef\n\n# Phony target to check PRODUCT_COPY_FILES copy pairs don't contain ELF files\n.PHONY: check-elf-prebuilt-product-copy-files\ncheck-elf-prebuilt-product-copy-files:\n\ncheck_elf_prebuilt_product_copy_files := true\nifneq (,$(filter true,$(BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES)))\ncheck_elf_prebuilt_product_copy_files :=\nendif\ncheck_elf_prebuilt_product_copy_files_hint := \\\n    found ELF prebuilt in PRODUCT_COPY_FILES, use cc_prebuilt_binary / cc_prebuilt_library_shared instead.\n\n# filter out the duplicate <source file>:<dest file> pairs.\nunique_product_copy_files_pairs :=\n$(foreach cf,$(PRODUCT_COPY_FILES), \\\n    $(if $(filter $(unique_product_copy_files_pairs),$(cf)),,\\\n        $(eval unique_product_copy_files_pairs += $(cf))))\nunique_product_copy_files_destinations :=\nproduct_copy_files_ignored :=\n$(foreach cf,$(unique_product_copy_files_pairs), \\\n    $(eval _src := $(call word-colon,1,$(cf))) \\\n    $(eval _dest := $(call word-colon,2,$(cf))) \\\n    $(call check-product-copy-files,$(cf),$(_dest)) \\\n    $(if $(filter $(unique_product_copy_files_destinations),$(_dest)), \\\n        $(eval product_copy_files_ignored += $(cf)), \\\n        $(eval _fulldest := $(call append-path,$(PRODUCT_OUT),$(_dest))) \\\n        $(if $(filter %.xml,$(_dest)),\\\n            $(eval $(call copy-xml-file-checked,$(_src),$(_fulldest))),\\\n            $(if $(and $(filter %.jar,$(_dest)),$(filter $(basename $(notdir $(_dest))),$(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))),\\\n                $(eval $(call copy-and-uncompress-dexs,$(_src),$(_fulldest))), \\\n                $(if $(filter init%rc,$(notdir $(_dest)))$(filter %/etc/init/,$(dir $(_dest))),\\\n                    $(eval $(call copy-init-script-file-checked,$(_src),$(_fulldest))),\\\n                    $(if $(and $(filter true,$(check_elf_prebuilt_product_copy_files)), \\\n                               $(filter bin lib lib64,$(subst /,$(space),$(_dest)))), \\\n                        $(eval $(call copy-non-elf-file-checked,$(_src),$(_fulldest),$(check_elf_prebuilt_product_copy_files_hint))), \\\n                        $(eval $(call copy-one-file,$(_src),$(_fulldest))))))) \\\n        $(eval unique_product_copy_files_destinations += $(_dest))))\n\n# Dump a list of overriden (and ignored PRODUCT_COPY_FILES entries)\npcf_ignored_file := $(PRODUCT_OUT)/product_copy_files_ignored.txt\n$(pcf_ignored_file): PRIVATE_IGNORED := $(sort $(product_copy_files_ignored))\n$(pcf_ignored_file):\n\techo \"$(PRIVATE_IGNORED)\" | tr \" \" \"\\n\" >$@\n\n$(call declare-0p-target,$(pcf_ignored_file))\n\n$(call dist-for-goals,droidcore-unbundled,$(pcf_ignored_file):logs/$(notdir $(pcf_ignored_file)))\n\npcf_ignored_file :=\nproduct_copy_files_ignored :=\nunique_product_copy_files_pairs :=\nunique_product_copy_files_destinations :=\n\n\n# Returns a list of EXTRA_INSTALL_ZIPS trios whose primary file is contained within $(1)\n# The trios will contain the primary installed file : the directory to unzip the zip to : the zip\ndefine relevant-extra-install-zips\n$(strip $(foreach p,$(EXTRA_INSTALL_ZIPS), \\\n  $(if $(filter $(call word-colon,1,$(p)),$(1)), \\\n    $(p))))\nendef\n\n# Writes a text file that contains all of the files that will be inside a partition.\n# All the file paths will be relative to the partition's staging directory.\n# It will also take into account files inside zips listed in EXTRA_INSTALL_ZIPS.\n#\n# Arguments:\n#   $(1): Output file\n#   $(2): The partition's staging directory\n#   $(3): Files to include in the partition\ndefine write-partition-file-list\n$(1): PRIVATE_FILES := $(subst $(2)/,,$(filter $(2)/%,$(3)))\n$(1): PRIVATE_EXTRA_INSTALL_ZIPS := $(call relevant-extra-install-zips,$(filter $(2)/%,$(3)))\n$(1): $$(HOST_OUT_EXECUTABLES)/extra_install_zips_file_list $(foreach p,$(call relevant-extra-install-zips,$(filter $(2)/%,$(3))),$(call word-colon,3,$(p)))\n\t@echo Writing $$@\n\trm -f $$@\n\techo -n > $$@\n\t$$(foreach f,$$(PRIVATE_FILES),echo \"$$(f)\" >> $$@$$(newline))\n\t$$(HOST_OUT_EXECUTABLES)/extra_install_zips_file_list $(2) $$(PRIVATE_EXTRA_INSTALL_ZIPS) >> $$@\nendef\n\n# -----------------------------------------------------------------\n# Returns the max allowed size for an image suitable for hash verification\n# (e.g., boot.img, recovery.img, etc).\n# The value 69632 derives from MAX_VBMETA_SIZE + MAX_FOOTER_SIZE in $(AVBTOOL).\n# $(1): partition size to flash the image\ndefine get-hash-image-max-size\n$(if $(1), \\\n  $(if $(filter true,$(BOARD_AVB_ENABLE)), \\\n    $(eval _hash_meta_size := 69632), \\\n    $(eval _hash_meta_size := 0)) \\\n  $(1)-$(_hash_meta_size))\nendef\n\n# -----------------------------------------------------------------\n# Define rules to copy headers defined in copy_headers.mk\n# If more than one makefile declared a header, print a warning,\n# then copy the last one defined. This matches the previous make\n# behavior.\nhas_dup_copy_headers :=\n$(foreach dest,$(ALL_COPIED_HEADERS), \\\n    $(eval _srcs := $(ALL_COPIED_HEADERS.$(dest).SRC)) \\\n    $(eval _src := $(lastword $(_srcs))) \\\n    $(if $(call streq,$(_src),$(_srcs)),, \\\n        $(warning Duplicate header copy: $(dest)) \\\n        $(warning _ Using $(_src)) \\\n        $(warning __ from $(lastword $(ALL_COPIED_HEADERS.$(dest).MAKEFILE))) \\\n        $(eval _makefiles := $$(wordlist 1,$(call int_subtract,$(words $(ALL_COPIED_HEADERS.$(dest).MAKEFILE)),1),$$(ALL_COPIED_HEADERS.$$(dest).MAKEFILE))) \\\n        $(foreach src,$(wordlist 1,$(call int_subtract,$(words $(_srcs)),1),$(_srcs)), \\\n            $(warning _ Ignoring $(src)) \\\n            $(warning __ from $(firstword $(_makefiles))) \\\n            $(eval _makefiles := $$(wordlist 2,9999,$$(_makefiles)))) \\\n        $(eval has_dup_copy_headers := true)) \\\n    $(eval $(call copy-one-header,$(_src),$(dest))))\nall_copied_headers: $(ALL_COPIED_HEADERS)\n\nifdef has_dup_copy_headers\n  has_dup_copy_headers :=\n  $(error duplicate header copies are no longer allowed. For more information about headers, see: https://android.googlesource.com/platform/build/soong/+/master/docs/best_practices.md#headers)\nendif\n\n$(file >$(PRODUCT_OUT)/.copied_headers_list,$(TARGET_OUT_HEADERS) $(ALL_COPIED_HEADERS))\n\n# -----------------------------------------------------------------\n# docs/index.html\nifeq (,$(TARGET_BUILD_UNBUNDLED))\ngen := $(OUT_DOCS)/index.html\nALL_DOCS += $(gen)\n$(gen): frameworks/base/docs/docs-redirect-index.html\n\t@mkdir -p $(dir $@)\n\t@cp -f $< $@\nendif\n\nndk_doxygen_out := $(OUT_NDK_DOCS)\nndk_headers := $(SOONG_OUT_DIR)/ndk/sysroot/usr/include\nndk_docs_src_dir := frameworks/native/docs\nndk_doxyfile := $(ndk_docs_src_dir)/Doxyfile\nifneq ($(wildcard $(ndk_docs_src_dir)),)\nndk_docs_srcs := $(addprefix $(ndk_docs_src_dir)/,\\\n    $(call find-files-in-subdirs,$(ndk_docs_src_dir),\"*\",.))\n$(ndk_doxygen_out)/index.html: $(ndk_docs_srcs) $(SOONG_OUT_DIR)/ndk.timestamp\n\t@mkdir -p $(ndk_doxygen_out)\n\t@echo \"Generating NDK docs to $(ndk_doxygen_out)\"\n\t@( cat $(ndk_doxyfile); \\\n\t    echo \"INPUT=$(ndk_headers)\"; \\\n\t    echo \"HTML_OUTPUT=$(ndk_doxygen_out)\" \\\n\t) | doxygen -\n\n$(call declare-1p-target,$(ndk_doxygen_out)/index.html,)\n\n# Note: Not a part of the docs target because we don't have doxygen available.\n# You can run this target locally if you have doxygen installed.\nndk-docs: $(ndk_doxygen_out)/index.html\n.PHONY: ndk-docs\nendif\n\nINSTALLED_RECOVERYIMAGE_TARGET :=\n# Build recovery image if\n# BUILDING_RECOVERY_IMAGE && !BOARD_USES_RECOVERY_AS_BOOT && !BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT.\n# If BOARD_USES_RECOVERY_AS_BOOT is true, leave empty because INSTALLED_BOOTIMAGE_TARGET is built\n#   with recovery resources.\n# If BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT is true, leave empty to build recovery resources\n#   but not the final recovery image.\nifdef BUILDING_RECOVERY_IMAGE\nifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\nifneq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true)\nINSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img\nendif\nendif\nendif\n\n# Do this early because sysprop.mk depends on BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC.\nifeq (default,$(ENABLE_UFFD_GC))\nBUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC := $(OUT_DIR)/soong/dexpreopt/kernel_version_for_uffd_gc.txt\nendif # ENABLE_UFFD_GC\n\ninclude $(BUILD_SYSTEM)/sysprop.mk\n\n# ----------------------------------------------------------------\n\n# -----------------------------------------------------------------\n# sdk-build.prop\n#\n# There are certain things in build.prop that we don't want to\n# ship with the sdk; remove them.\n\n# This must be a list of entire property keys followed by\n# \"=\" characters, without any internal spaces.\nsdk_build_prop_remove := \\\n\tro.build.user= \\\n\tro.build.host= \\\n\tro.product.brand= \\\n\tro.product.manufacturer= \\\n\tro.product.device=\n# TODO: Remove this soon-to-be obsolete property\nsdk_build_prop_remove += ro.build.product=\nINSTALLED_SDK_BUILD_PROP_TARGET := $(PRODUCT_OUT)/sdk/sdk-build.prop\n$(INSTALLED_SDK_BUILD_PROP_TARGET): $(INSTALLED_BUILD_PROP_TARGET)\n\t@echo SDK buildinfo: $@\n\t@mkdir -p $(dir $@)\n\t$(hide) grep -v \"$(subst $(space),\\|,$(strip \\\n\t            $(sdk_build_prop_remove)))\" $< > $@.tmp\n\t$(hide) for x in $(strip $(sdk_build_prop_remove)); do \\\n\t            echo \"$$x\"generic >> $@.tmp; done\n\t$(hide) mv $@.tmp $@\n\n$(call declare-0p-target,$(INSTALLED_SDK_BUILD_PROP_TARGET))\n\n# -----------------------------------------------------------------\n# declare recovery ramdisk files\nifeq ($(BUILDING_RECOVERY_IMAGE),true)\nINTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP := $(call intermediates-dir-for,PACKAGING,recovery)/ramdisk_files-timestamp\nendif\n\n# -----------------------------------------------------------------\n# Declare vendor ramdisk fragments\nINTERNAL_VENDOR_RAMDISK_FRAGMENTS :=\n\nifeq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))\n  ifneq (,$(filter recovery,$(BOARD_VENDOR_RAMDISK_FRAGMENTS)))\n    $(error BOARD_VENDOR_RAMDISK_FRAGMENTS must not contain \"recovery\" if \\\n      BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT is set)\n  endif\n  INTERNAL_VENDOR_RAMDISK_FRAGMENTS += recovery\n  VENDOR_RAMDISK_FRAGMENT.recovery.STAGING_DIR := $(TARGET_RECOVERY_ROOT_OUT)\n  VENDOR_RAMDISK_FRAGMENT.recovery.FILES := $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)\n  BOARD_VENDOR_RAMDISK_FRAGMENT.recovery.MKBOOTIMG_ARGS += --ramdisk_type RECOVERY\n  .KATI_READONLY := VENDOR_RAMDISK_FRAGMENT.recovery.STAGING_DIR\nendif\n\n# Validation check and assign default --ramdisk_type.\n$(foreach vendor_ramdisk_fragment,$(BOARD_VENDOR_RAMDISK_FRAGMENTS), \\\n  $(if $(and $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS), \\\n             $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).PREBUILT)), \\\n    $(error Must not specify KERNEL_MODULE_DIRS for prebuilt vendor ramdisk fragment \"$(vendor_ramdisk_fragment)\": $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS))) \\\n  $(eval VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR := $(call intermediates-dir-for,PACKAGING,vendor_ramdisk_fragment-stage-$(vendor_ramdisk_fragment))) \\\n  $(eval VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).FILES :=) \\\n  $(if $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS), \\\n    $(if $(filter --ramdisk_type,$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS)),, \\\n      $(eval BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS += --ramdisk_type DLKM))) \\\n)\n\n# Create the \"kernel module directory\" to \"vendor ramdisk fragment\" inverse mapping.\n$(foreach vendor_ramdisk_fragment,$(BOARD_VENDOR_RAMDISK_FRAGMENTS), \\\n  $(foreach kmd,$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS), \\\n    $(eval kmd_vrf := KERNEL_MODULE_DIR_VENDOR_RAMDISK_FRAGMENT_$(kmd)) \\\n    $(if $($(kmd_vrf)),$(error Kernel module directory \"$(kmd)\" belongs to multiple vendor ramdisk fragments: \"$($(kmd_vrf))\" \"$(vendor_ramdisk_fragment)\", each kernel module directory should belong to exactly one or none vendor ramdisk fragment)) \\\n    $(eval $(kmd_vrf) := $(vendor_ramdisk_fragment)) \\\n  ) \\\n)\nINTERNAL_VENDOR_RAMDISK_FRAGMENTS += $(BOARD_VENDOR_RAMDISK_FRAGMENTS)\n\nifneq ($(BOARD_KERNEL_MODULES_16K),)\nINTERNAL_VENDOR_RAMDISK_FRAGMENTS += 16K\nendif\n\n# Strip the list in case of any whitespace.\nINTERNAL_VENDOR_RAMDISK_FRAGMENTS := \\\n  $(strip $(INTERNAL_VENDOR_RAMDISK_FRAGMENTS))\n\n# Assign --ramdisk_name for each vendor ramdisk fragment.\n$(foreach vendor_ramdisk_fragment,$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS), \\\n  $(if $(filter --ramdisk_name,$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS)), \\\n    $(error Must not specify --ramdisk_name for vendor ramdisk fragment: $(vendor_ramdisk_fragment))) \\\n  $(eval BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS += --ramdisk_name $(vendor_ramdisk_fragment)) \\\n  $(eval .KATI_READONLY := BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS) \\\n)\n\n# -----------------------------------------------------------------\n# kernel modules\n\n# Depmod requires a well-formed kernel version so 0.0 is used as a placeholder.\nDEPMOD_STAGING_SUBDIR :=$= lib/modules/0.0\n\ndefine copy-and-strip-kernel-module\n$(2): $(1)\n\t$(LLVM_STRIP) -o $(2) --strip-debug $(1)\nendef\n\n# $(1): modules list\n# $(2): output dir\n# $(3): mount point\n# $(4): staging dir\n# $(5): module load list\n# $(6): module load list filename\n# $(7): module archive\n# $(8): staging dir for stripped modules\n# $(9): module directory name\n# $(10): extra modules that might be dependency of modules in this partition, but should not be copied to output dir\n# $(11): mount point for extra modules\n# Returns a list of src:dest pairs to install the modules using copy-many-files.\ndefine build-image-kernel-modules\n  $(if $(9), \\\n    $(eval _dir := $(9)/), \\\n    $(eval _dir :=)) \\\n  $(foreach module,$(1), \\\n    $(eval _src := $(module)) \\\n    $(if $(8), \\\n      $(eval _src := $(8)/$(notdir $(module))) \\\n      $(eval $(call copy-and-strip-kernel-module,$(module),$(_src)))) \\\n    $(_src):$(2)/lib/modules/$(_dir)$(notdir $(module))) \\\n  $(eval $(call build-image-kernel-modules-depmod,$(1),$(3),$(4),$(5),$(6),$(7),$(2),$(9),$(10),$(11))) \\\n  $(4)/$(DEPMOD_STAGING_SUBDIR)/modules.dep:$(2)/lib/modules/$(_dir)modules.dep \\\n  $(4)/$(DEPMOD_STAGING_SUBDIR)/modules.alias:$(2)/lib/modules/$(_dir)modules.alias \\\n  $(4)/$(DEPMOD_STAGING_SUBDIR)/modules.softdep:$(2)/lib/modules/$(_dir)modules.softdep \\\n  $(4)/$(DEPMOD_STAGING_SUBDIR)/$(6):$(2)/lib/modules/$(_dir)$(6)\nendef\n\n# $(1): modules list\n# $(2): mount point\n# $(3): staging dir\n# $(4): module load list\n# $(5): module load list filename\n# $(6): module archive\n# $(7): output dir\n# $(8): module directory name\n# $(9): extra modules which should not be copied to output dir, but might be dependency of modules in this partition\n# $(10): mount point for extra modules\n# TODO(b/144844424): If a module archive is being used, this step (which\n# generates obj/PACKAGING/.../modules.dep) also unzips the module archive into\n# the output directory. This should be moved to a module with a\n# LOCAL_POST_INSTALL_CMD so that if modules.dep is removed from the output dir,\n# the archive modules are restored along with modules.dep.\ndefine build-image-kernel-modules-depmod\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: .KATI_IMPLICIT_OUTPUTS := $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.alias $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.softdep $(3)/$(DEPMOD_STAGING_SUBDIR)/$(5)\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: $(DEPMOD)\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULES := $(strip $(1))\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_EXTRA_MODULES := $(strip $(9))\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MOUNT_POINT := $(2)\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_EXTRA_MOUNT_POINT := $(10)\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULE_DIR := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(2)/lib/modules/$(8)\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_EXTRA_MODULE_DIR := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(10)/lib/modules/$(8)\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_STAGING_DIR := $(3)\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_MODULES := $(strip $(4))\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_FILE := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(5)\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULE_ARCHIVE := $(6)\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_OUTPUT_DIR := $(7)\n$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: $(1) $(6)\n\t@echo depmod $$(PRIVATE_STAGING_DIR)\n\trm -rf $$(PRIVATE_STAGING_DIR)\n\tmkdir -p $$(PRIVATE_MODULE_DIR)\n\t$(if $(6),\\\n\t  unzip -qoDD -d $$(PRIVATE_MODULE_DIR) $$(PRIVATE_MODULE_ARCHIVE); \\\n\t  mkdir -p $$(PRIVATE_OUTPUT_DIR)/lib; \\\n\t  cp -r  $(3)/$(DEPMOD_STAGING_SUBDIR)/$(2)/lib/modules $$(PRIVATE_OUTPUT_DIR)/lib/; \\\n\t  find $$(PRIVATE_MODULE_DIR) -type f -name '*.ko' | xargs basename -a > $$(PRIVATE_LOAD_FILE); \\\n\t)\n\t$(if $(1),\\\n\t  cp $$(PRIVATE_MODULES) $$(PRIVATE_MODULE_DIR)/; \\\n\t  if [ -n \"$$(PRIVATE_LOAD_MODULES)\" ]; then basename -a $$(PRIVATE_LOAD_MODULES); fi > $$(PRIVATE_LOAD_FILE); \\\n\t)\n\t# The ln -sf + find -delete sequence is to remove any modules in\n\t# PRIVATE_EXTRA_MODULES which have same basename as MODULES in PRIVATE_MODULES\n\t# Basically, it computes a set difference. When there is a duplicate module\n\t# present in both directories, we want modules in PRIVATE_MODULES to take\n\t# precedence. Since depmod does not provide any guarantee about ordering of\n\t# dependency resolution, we achieve this by maually removing any duplicate\n\t# modules with lower priority.\n\t$(if $(9),\\\n\t  mkdir -p $$(PRIVATE_EXTRA_MODULE_DIR); \\\n\t  find $$(PRIVATE_EXTRA_MODULE_DIR) -maxdepth 1 -type f -name \"*.ko\" -delete; \\\n\t  cp $$(PRIVATE_EXTRA_MODULES) $$(PRIVATE_EXTRA_MODULE_DIR); \\\n\t  ln -sf $$(PRIVATE_MODULE_DIR)/*.ko $$(PRIVATE_EXTRA_MODULE_DIR); \\\n\t  find $$(PRIVATE_EXTRA_MODULE_DIR) -type l -delete; \\\n\t)\n\t$(DEPMOD) -b $$(PRIVATE_STAGING_DIR) 0.0\n\t# Turn paths in modules.dep into absolute paths\n\tsed -i.tmp -e 's|\\([^: ]*lib/modules/[^: ]*\\)|/\\1|g' $$(PRIVATE_STAGING_DIR)/$$(DEPMOD_STAGING_SUBDIR)/modules.dep\n\ttouch $$(PRIVATE_LOAD_FILE)\nendef\n\n# $(1): staging dir\n# $(2): modules list\n# $(3): module load list\n# $(4): module load list filename\n# $(5): output dir\ndefine module-load-list-copy-paths\n  $(eval $(call build-image-module-load-list,$(1),$(2),$(3),$(4))) \\\n  $(1)/$(DEPMOD_STAGING_SUBDIR)/$(4):$(5)/lib/modules/$(4)\nendef\n\n# $(1): staging dir\n# $(2): modules list\n# $(3): module load list\n# $(4): module load list filename\ndefine build-image-module-load-list\n$(1)/$(DEPMOD_STAGING_SUBDIR)/$(4): PRIVATE_LOAD_MODULES := $(3)\n$(1)/$(DEPMOD_STAGING_SUBDIR)/$(4): $(2)\n\t@echo load-list $$(@)\n\t@echo '$$(strip $$(notdir $$(PRIVATE_LOAD_MODULES)))' | tr ' ' '\\n' > $$(@)\nendef\n\n# $(1): source options file\n# $(2): destination pathname\n# Returns a build rule that checks the syntax of and installs a kernel modules\n# options file. Strip and squeeze any extra space and blank lines.\n# For use via $(eval).\ndefine build-image-kernel-modules-options-file\n$(2): $(1)\n\t@echo \"libmodprobe options $$(@)\"\n\t$(hide) mkdir -p \"$$(dir $$@)\"\n\t$(hide) rm -f \"$$@\"\n\t$(hide) awk <\"$$<\" >\"$$@\" \\\n\t  '/^#/ { print; next } \\\n\t   NF == 0 { next } \\\n\t   NF < 2 || $$$$1 != \"options\" \\\n\t     { print \"Invalid options line \" FNR \": \" $$$$0 >\"/dev/stderr\"; \\\n\t       exit_status = 1; next } \\\n\t   { $$$$1 = $$$$1; print } \\\n\t   END { exit exit_status }'\nendef\n\n# $(1): source blocklist file\n# $(2): destination pathname\n# Returns a build rule that checks the syntax of and installs a kernel modules\n# blocklist file. Strip and squeeze any extra space and blank lines.\n# For use via $(eval).\ndefine build-image-kernel-modules-blocklist-file\n$(2): $(1)\n\t@echo \"libmodprobe blocklist $$(@)\"\n\t$(hide) mkdir -p \"$$(dir $$@)\"\n\t$(hide) rm -f \"$$@\"\n\t$(hide) awk <\"$$<\" >\"$$@\" \\\n\t  '/^#/ { print; next } \\\n\t   NF == 0 { next } \\\n\t   NF != 2 || $$$$1 != \"blocklist\" \\\n\t     { print \"Invalid blocklist line \" FNR \": \" $$$$0 >\"/dev/stderr\"; \\\n\t       exit_status = 1; next } \\\n\t   { $$$$1 = $$$$1; print } \\\n\t   END { exit exit_status }'\nendef\n\n# $(1): image name\n# $(2): build output directory (TARGET_OUT_VENDOR, TARGET_RECOVERY_ROOT_OUT, etc)\n# $(3): mount point\n# $(4): module load filename\n# $(5): stripped staging directory\n# $(6): kernel module directory name (top is an out of band value for no directory)\n# $(7): list of extra modules that might be dependency of modules in this partition\n# $(8): mount point for extra modules. e.g. system\n\ndefine build-image-kernel-modules-dir\n$(if $(filter top,$(6)),\\\n  $(eval _kver :=)$(eval _sep :=),\\\n  $(eval _kver := $(6))$(eval _sep :=_))\\\n$(if $(5),\\\n  $(eval _stripped_staging_dir := $(5)$(_sep)$(_kver)),\\\n  $(eval _stripped_staging_dir :=))\\\n$(if $(strip $(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver))$(BOARD_$(1)_KERNEL_MODULES_ARCHIVE$(_sep)$(_kver))),\\\n  $(if $(BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver)),,\\\n    $(eval BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver) := $(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver)))) \\\n  $(if $(filter false,$(BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver))),\\\n    $(eval BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver) :=),) \\\n  $(eval _files := $(call build-image-kernel-modules,$(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver)),$(2),$(3),$(call intermediates-dir-for,PACKAGING,depmod_$(1)$(_sep)$(_kver)),$(BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver)),$(4),$(BOARD_$(1)_KERNEL_MODULES_ARCHIVE$(_sep)$(_kver)),$(_stripped_staging_dir),$(_kver),$(7),$(8))) \\\n  $(call copy-many-files,$(_files)) \\\n  $(eval _modules := $(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver)) ANDROID-GEN ANDROID-GEN ANDROID-GEN ANDROID-GEN) \\\n  $(eval KERNEL_MODULE_COPY_FILES += $(join $(addsuffix :,$(_modules)),$(_files)))) \\\n$(if $(_kver), \\\n  $(eval _dir := $(_kver)/), \\\n  $(eval _dir :=)) \\\n$(if $(BOARD_$(1)_KERNEL_MODULES_OPTIONS_FILE$(_sep)$(_kver)), \\\n  $(eval $(call build-image-kernel-modules-options-file, \\\n    $(BOARD_$(1)_KERNEL_MODULES_OPTIONS_FILE$(_sep)$(_kver)), \\\n    $(2)/lib/modules/$(_dir)modules.options)) \\\n  $(2)/lib/modules/$(_dir)modules.options) \\\n$(if $(BOARD_$(1)_KERNEL_MODULES_BLOCKLIST_FILE$(_sep)$(_kver)), \\\n  $(eval $(call build-image-kernel-modules-blocklist-file, \\\n    $(BOARD_$(1)_KERNEL_MODULES_BLOCKLIST_FILE$(_sep)$(_kver)), \\\n    $(2)/lib/modules/$(_dir)modules.blocklist)) \\\n  $(eval ALL_KERNEL_MODULES_BLOCKLIST += $(2)/lib/modules/$(_dir)modules.blocklist) \\\n  $(2)/lib/modules/$(_dir)modules.blocklist)\nendef\n\n# $(1): kernel module directory name (top is an out of band value for no directory)\ndefine build-recovery-as-boot-load\n$(if $(filter top,$(1)),\\\n  $(eval _kver :=)$(eval _sep :=),\\\n  $(eval _kver := $(1))$(eval _sep :=_))\\\n  $(if $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\\\n    $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,ramdisk_module_list$(_sep)$(_kver)),$(BOARD_GENERIC_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load,$(TARGET_RECOVERY_ROOT_OUT))))\nendef\n\n# $(1): kernel module directory name (top is an out of band value for no directory)\ndefine build-vendor-ramdisk-recovery-load\n$(if $(filter top,$(1)),\\\n  $(eval _kver :=)$(eval _sep :=),\\\n  $(eval _kver := $(1))$(eval _sep :=_))\\\n  $(if $(BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\\\n    $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_ramdisk_recovery_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.recovery,$(TARGET_VENDOR_RAMDISK_OUT))))\nendef\n\n# $(1): kernel module directory name (top is an out of band value for no directory)\ndefine build-vendor-kernel-ramdisk-recovery-load\n$(if $(filter top,$(1)),\\\n  $(eval _kver :=)$(eval _sep :=),\\\n  $(eval _kver := $(1))$(eval _sep :=_))\\\n  $(if $(BOARD_VENDOR_KERNEL_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\\\n    $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_kernel_ramdisk_recovery_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_KERNEL_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_KERNEL_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.recovery,$(TARGET_VENDOR_KERNEL_RAMDISK_OUT))))\nendef\n\n# $(1): kernel module directory name (top is an out of band value for no directory)\ndefine build-vendor-charger-load\n$(if $(filter top,$(1)),\\\n  $(eval _kver :=)$(eval _sep :=),\\\n  $(eval _kver := $(1))$(eval _sep :=_))\\\n  $(if $(BOARD_VENDOR_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\\\n    $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_charger_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_CHARGER_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.charger,$(TARGET_OUT_VENDOR))))\nendef\n\n# $(1): kernel module directory name (top is an out of band value for no directory)\ndefine build-vendor-ramdisk-charger-load\n$(if $(filter top,$(1)),\\\n  $(eval _kver :=)$(eval _sep :=),\\\n  $(eval _kver := $(1))$(eval _sep :=_))\\\n  $(if $(BOARD_VENDOR_RAMDISK_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\\\n    $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_ramdisk_charger_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.charger,$(TARGET_VENDOR_RAMDISK_OUT))))\nendef\n\n# $(1): kernel module directory name (top is an out of band value for no directory)\ndefine build-vendor-kernel-ramdisk-charger-load\n$(if $(filter top,$(1)),\\\n  $(eval _kver :=)$(eval _sep :=),\\\n  $(eval _kver := $(1))$(eval _sep :=_))\\\n  $(if $(BOARD_VENDOR_KERNEL_RAMDISK_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\\\n    $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_kernel_ramdisk_charger_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_KERNEL_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_KERNEL_RAMDISK_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.charger,$(TARGET_VENDOR_KERNEL_RAMDISK_OUT))))\nendef\n\nifneq ($(BUILDING_VENDOR_BOOT_IMAGE),true)\n  # If there is no vendor boot partition, store vendor ramdisk kernel modules in the\n  # boot ramdisk.\n  BOARD_GENERIC_RAMDISK_KERNEL_MODULES += $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES)\n  BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD += $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD)\nendif\n\nifeq ($(BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD),)\n  BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD := $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES)\nendif\n\nifneq ($(strip $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES)),)\n  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT), true)\n    BOARD_RECOVERY_KERNEL_MODULES += $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES)\n  endif\nendif\n\nifneq ($(BOARD_DO_NOT_STRIP_GENERIC_RAMDISK_MODULES),true)\n  GENERIC_RAMDISK_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_generic_ramdisk_kernel_stripped)\nelse\n  GENERIC_RAMDISK_STRIPPED_MODULE_STAGING_DIR :=\nendif\n\nifneq ($(BOARD_DO_NOT_STRIP_RECOVERY_MODULES),true)\n\tRECOVERY_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_recovery_stripped)\nelse\n\tRECOVERY_STRIPPED_MODULE_STAGING_DIR :=\nendif\n\nifneq ($(BOARD_DO_NOT_STRIP_VENDOR_MODULES),true)\n\tVENDOR_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_stripped)\nelse\n\tVENDOR_STRIPPED_MODULE_STAGING_DIR :=\nendif\n\nifneq ($(BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES),true)\n  VENDOR_RAMDISK_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_ramdisk_stripped)\nelse\n  VENDOR_RAMDISK_STRIPPED_MODULE_STAGING_DIR :=\nendif\n\nifneq ($(BOARD_DO_NOT_STRIP_VENDOR_KERNEL_RAMDISK_MODULES),true)\n  VENDOR_KERNEL_RAMDISK_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_kernel_ramdisk_stripped)\nelse\n  VENDOR_KERNEL_RAMDISK_STRIPPED_MODULE_STAGING_DIR :=\nendif\n\nBOARD_KERNEL_MODULE_DIRS += top\n\n# Default to not generating modules.load for kernel modules on system\n# side. We should only load these modules if they are depended by vendor\n# side modules.\nifeq ($(BOARD_SYSTEM_KERNEL_MODULES_LOAD),)\n  BOARD_SYSTEM_KERNEL_MODULES_LOAD := false\nendif\n\n$(foreach kmd,$(BOARD_KERNEL_MODULE_DIRS), \\\n  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,RECOVERY,$(TARGET_RECOVERY_ROOT_OUT),,modules.load.recovery,$(RECOVERY_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \\\n  $(eval vendor_ramdisk_fragment := $(KERNEL_MODULE_DIR_VENDOR_RAMDISK_FRAGMENT_$(kmd))) \\\n  $(if $(vendor_ramdisk_fragment), \\\n    $(eval output_dir := $(VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR)) \\\n    $(eval result_var := VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).FILES) \\\n    $(eval ### else ###), \\\n    $(eval output_dir := $(TARGET_VENDOR_RAMDISK_OUT)) \\\n    $(eval result_var := ALL_DEFAULT_INSTALLED_MODULES)) \\\n  $(eval $(result_var) += $(call build-image-kernel-modules-dir,VENDOR_RAMDISK,$(output_dir),,modules.load,$(VENDOR_RAMDISK_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \\\n  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,VENDOR_KERNEL_RAMDISK,$(TARGET_VENDOR_KERNEL_RAMDISK_OUT),,modules.load,$(VENDOR_KERNEL_RAMDISK_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \\\n  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-ramdisk-recovery-load,$(kmd))) \\\n  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-kernel-ramdisk-recovery-load,$(kmd))) \\\n  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,VENDOR,$(if $(filter true,$(BOARD_USES_VENDOR_DLKMIMAGE)),$(TARGET_OUT_VENDOR_DLKM),$(TARGET_OUT_VENDOR)),vendor,modules.load,$(VENDOR_STRIPPED_MODULE_STAGING_DIR),$(kmd),$(BOARD_SYSTEM_KERNEL_MODULES),system)) \\\n  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-charger-load,$(kmd))) \\\n  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-ramdisk-charger-load,$(kmd))) \\\n  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-kernel-ramdisk-charger-load,$(kmd))) \\\n  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,ODM,$(if $(filter true,$(BOARD_USES_ODM_DLKMIMAGE)),$(TARGET_OUT_ODM_DLKM),$(TARGET_OUT_ODM)),odm,modules.load,,$(kmd))) \\\n  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,SYSTEM,$(if $(filter true,$(BOARD_USES_SYSTEM_DLKMIMAGE)),$(TARGET_OUT_SYSTEM_DLKM),$(TARGET_OUT)),system,modules.load,,$(kmd))) \\\n  $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)),\\\n    $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-recovery-as-boot-load,$(kmd))),\\\n    $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,GENERIC_RAMDISK,$(TARGET_RAMDISK_OUT),,modules.load,$(GENERIC_RAMDISK_STRIPPED_MODULE_STAGING_DIR),$(kmd)))))\n\nifeq ($(BOARD_SYSTEM_KERNEL_MODULES),)\nifneq ($(BOARD_SYSTEM_DLKM_SRC),)\nifneq ($(wildcard $(BOARD_SYSTEM_DLKM_SRC)/*),)\n  SYSTEM_KERNEL_MODULES := $(shell find $(BOARD_SYSTEM_DLKM_SRC) -type f)\n  SRC_SYSTEM_KERNEL_MODULES := $(SYSTEM_KERNEL_MODULES)\n  DST_SYSTEM_KERNEL_MODULES := $(patsubst $(BOARD_SYSTEM_DLKM_SRC)/%,:$(TARGET_OUT_SYSTEM_DLKM)/%,$(SRC_SYSTEM_KERNEL_MODULES))\n  SYSTEM_KERNEL_MODULE_COPY_PAIRS := $(join $(SRC_SYSTEM_KERNEL_MODULES),$(DST_SYSTEM_KERNEL_MODULES))\n  ALL_DEFAULT_INSTALLED_MODULES += $(call copy-many-files,$(SYSTEM_KERNEL_MODULE_COPY_PAIRS))\nendif\nendif\nendif\n\n\n# -----------------------------------------------------------------\n# Cert-to-package mapping.  Used by the post-build signing tools.\n# Use a macro to add newline to each echo command\n# $1 stem name of the package\n# $2 certificate\n# $3 private key\n# $4 compressed\n# $5 partition tag\n# $6 output file\ndefine _apkcerts_write_line\n$(hide) echo 'name=\"$(1).apk\" certificate=\"$2\" private_key=\"$3\"$(if $(4), compressed=\"$4\")$(if $(5), partition=\"$5\")' >> $6\n\nendef\n\n# -----------------------------------------------------------------\n# Merge an individual apkcerts output into the final apkcerts.txt output.\n# Use a macro to make it compatible with _apkcerts_write_line\n# $1 apkcerts file to be merged\n# $2 output file\ndefine _apkcerts_merge\n$(hide) cat $1 >> $2\n\nendef\n\nname := $(TARGET_PRODUCT)\nifeq ($(TARGET_BUILD_TYPE),debug)\n  name := $(name)_debug\nendif\nname := $(name)-apkcerts\nintermediates := \\\n\t$(call intermediates-dir-for,PACKAGING,apkcerts)\nAPKCERTS_FILE := $(intermediates)/$(name).txt\nifeq ($(RELEASE_APKCERTS_INSTALL_ONLY), true)\n  all_apkcerts_packages := $(filter $(call product-installed-modules,$(INTERNAL_PRODUCT)),$(PACKAGES))\nelse\n  all_apkcerts_packages := $(PACKAGES)\nendif\nall_apkcerts_files := $(sort $(foreach p,$(all_apkcerts_packages),$(PACKAGES.$(p).APKCERTS_FILE)))\n\n$(APKCERTS_FILE): $(all_apkcerts_files)\n# We don't need to really build all the modules.\n# TODO: rebuild APKCERTS_FILE if any app change its cert.\n$(APKCERTS_FILE):\n\t@echo APK certs list: $@\n\t@mkdir -p $(dir $@)\n\t@rm -f $@\n\t$(foreach p,$(sort $(all_apkcerts_packages)),\\\n\t  $(if $(PACKAGES.$(p).APKCERTS_FILE),\\\n\t    $(call _apkcerts_merge,$(PACKAGES.$(p).APKCERTS_FILE), $@),\\\n\t    $(if $(PACKAGES.$(p).EXTERNAL_KEY),\\\n\t      $(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),EXTERNAL,,$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@),\\\n\t      $(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),$(PACKAGES.$(p).CERTIFICATE),$(PACKAGES.$(p).PRIVATE_KEY),$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@))))\n\t$(if $(filter true,$(PRODUCT_FSVERITY_GENERATE_METADATA)),\\\n\t  $(call _apkcerts_write_line,BuildManifest,$(FSVERITY_APK_KEY_PATH).x509.pem,$(FSVERITY_APK_KEY_PATH).pk8,,system,$@) \\\n\t  $(if $(filter true,$(BUILDING_SYSTEM_EXT_IMAGE)),\\\n            $(call _apkcerts_write_line,BuildManifestSystemExt,$(FSVERITY_APK_KEY_PATH).x509.pem,$(FSVERITY_APK_KEY_PATH).pk8,,system_ext,$@)))\n\t# In case value of PACKAGES is empty.\n\t$(hide) touch $@ && sort -u -o $@ $@\n\n$(call declare-0p-target,$(APKCERTS_FILE))\n\n.PHONY: apkcerts-list\napkcerts-list: $(APKCERTS_FILE)\n\nintermediates := $(call intermediates-dir-for,PACKAGING,apexkeys)\nAPEX_KEYS_FILE := $(intermediates)/apexkeys.txt\n\nall_apex_keys_files := $(sort $(foreach m,$(call product-installed-modules,$(INTERNAL_PRODUCT)),$(ALL_MODULES.$(m).APEX_KEYS_FILE)))\n$(APEX_KEYS_FILE): $(all_apex_keys_files)\n\t@mkdir -p $(dir $@)\n\t@rm -f $@\n\t$(hide) touch $@\n\t$(hide) $(foreach file,$^,cat $(file) >> $@ $(newline))\nall_apex_keys_files :=\n\n$(call declare-0p-target,$(APEX_KEYS_FILE))\n\n.PHONY: apexkeys.txt\napexkeys.txt: $(APEX_KEYS_FILE)\n\nifneq (,$(TARGET_BUILD_APPS))\n  $(call dist-for-goals, apps_only, $(APKCERTS_FILE):apkcerts.txt)\n  $(call dist-for-goals, apps_only, $(APEX_KEYS_FILE):apexkeys.txt)\nendif\n\n\n# -----------------------------------------------------------------\n# build /product/etc/security/avb/system_other.avbpubkey if needed\nifdef BUILDING_SYSTEM_OTHER_IMAGE\nifeq ($(BOARD_AVB_ENABLE),true)\nINSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET := $(TARGET_OUT_PRODUCT_ETC)/security/avb/system_other.avbpubkey\nALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET)\nendif # BOARD_AVB_ENABLE\nendif # BUILDING_SYSTEM_OTHER_IMAGE\n\n# -----------------------------------------------------------------\n# Modules ready to be converted to Soong, ordered by how many\n# modules depend on them.\nSOONG_CONV := $(sort $(SOONG_CONV))\nSOONG_CONV_DATA := $(call intermediates-dir-for,PACKAGING,soong_conversion)/soong_conv_data\n$(SOONG_CONV_DATA):\n\t@rm -f $@\n\t@touch $@ # This file must be present even if SOONG_CONV is empty.\n\t@$(foreach s,$(SOONG_CONV),echo \"$(s),$(SOONG_CONV.$(s).TYPE),$(sort $(SOONG_CONV.$(s).PROBLEMS)),$(sort $(filter-out $(SOONG_ALREADY_CONV),$(SOONG_CONV.$(s).DEPS))),$(sort $(SOONG_CONV.$(s).MAKEFILES)),$(sort $(SOONG_CONV.$(s).INSTALLED))\" >>$@;)\n\n$(call declare-1p-target,$(SOONG_CONV_DATA),build)\n\nSOONG_TO_CONVERT_SCRIPT := build/make/tools/soong_to_convert.py\nSOONG_TO_CONVERT := $(PRODUCT_OUT)/soong_to_convert.txt\n$(SOONG_TO_CONVERT): $(SOONG_CONV_DATA) $(SOONG_TO_CONVERT_SCRIPT)\n\t@rm -f $@\n\t$(hide) $(SOONG_TO_CONVERT_SCRIPT) $< >$@\n$(call declare-1p-target,$(SOONG_TO_CONVERT),build)\n$(call dist-for-goals,droidcore-unbundled,$(SOONG_TO_CONVERT))\n\nMK2BP_CATALOG_SCRIPT := build/make/tools/mk2bp_catalog.py\nPRODUCT_PACKAGES_TXT := $(PRODUCT_OUT)/product_packages.txt\nMK2BP_REMAINING_HTML := $(PRODUCT_OUT)/mk2bp_remaining.html\n$(MK2BP_REMAINING_HTML): PRIVATE_CODE_SEARCH_BASE_URL := \"https://cs.android.com/android/platform/superproject/+/master:\"\n$(MK2BP_REMAINING_HTML): $(SOONG_CONV_DATA) $(MK2BP_CATALOG_SCRIPT) $(PRODUCT_PACKAGES_TXT)\n\t@rm -f $@\n\t$(hide) $(MK2BP_CATALOG_SCRIPT) \\\n\t\t--device=$(TARGET_DEVICE) \\\n\t\t--product-packages=$(PRODUCT_PACKAGES_TXT) \\\n\t\t--title=\"Remaining Android.mk files for $(TARGET_DEVICE)-$(TARGET_BUILD_VARIANT)\" \\\n\t\t--codesearch=$(PRIVATE_CODE_SEARCH_BASE_URL) \\\n\t\t--out-dir=\"$(OUT_DIR)\" \\\n\t\t--mode=html \\\n\t\t> $@\n$(call declare-1p-target,$(MK2BP_REMAINING_HTML),build)\n$(call dist-for-goals,droidcore-unbundled,$(MK2BP_REMAINING_HTML))\n\nMK2BP_REMAINING_CSV := $(PRODUCT_OUT)/mk2bp_remaining.csv\n$(MK2BP_REMAINING_CSV): $(SOONG_CONV_DATA) $(MK2BP_CATALOG_SCRIPT) $(PRODUCT_PACKAGES_TXT)\n\t@rm -f $@\n\t$(hide) $(MK2BP_CATALOG_SCRIPT) \\\n\t\t--device=$(TARGET_DEVICE) \\\n\t\t--product-packages=$(PRODUCT_PACKAGES_TXT) \\\n\t\t--out-dir=\"$(OUT_DIR)\" \\\n\t\t--mode=csv \\\n\t\t> $@\n$(call declare-1p-target,$(MK2BP_REMAINING_CSV))\n$(call dist-for-goals,droidcore-unbundled,$(MK2BP_REMAINING_CSV))\n\n.PHONY: mk2bp_remaining\nmk2bp_remaining: $(MK2BP_REMAINING_HTML) $(MK2BP_REMAINING_CSV)\n\n# -----------------------------------------------------------------\n# Modules use -Wno-error, or added default -Wall -Werror\nWALL_WERROR := $(PRODUCT_OUT)/wall_werror.txt\n$(WALL_WERROR):\n\t@rm -f $@\n\techo \"# Modules using -Wno-error\" >> $@\n\tfor m in $(sort $(SOONG_MODULES_USING_WNO_ERROR) $(MODULES_USING_WNO_ERROR)); do echo $$m >> $@; done\n\techo \"# Modules that allow warnings\" >> $@\n\tfor m in $(sort $(SOONG_MODULES_WARNINGS_ALLOWED) $(MODULES_WARNINGS_ALLOWED)); do echo $$m >> $@; done\n\n$(call declare-0p-target,$(WALL_WERROR))\n\n$(call dist-for-goals,droidcore-unbundled,$(WALL_WERROR))\n\nCERTIFICATE_VIOLATION_MODULES_FILENAME := $(PRODUCT_OUT)/certificate_violation_modules.txt\n$(CERTIFICATE_VIOLATION_MODULES_FILENAME):\n\trm -f $@\n\t$(foreach m,$(sort $(CERTIFICATE_VIOLATION_MODULES)), echo $(m) >> $@;)\n$(call declare-0p-target,$(CERTIFICATE_VIOLATION_MODULES_FILENAME))\n$(call dist-for-goals,droidcore,$(CERTIFICATE_VIOLATION_MODULES_FILENAME))\n\n# -----------------------------------------------------------------\n# The dev key is used to sign this package, and as the key required\n# for future OTA packages installed by this system.  Actual product\n# deliverables will be re-signed by hand.  We expect this file to\n# exist with the suffixes \".x509.pem\" and \".pk8\".\nDEFAULT_KEY_CERT_PAIR := $(strip $(DEFAULT_SYSTEM_DEV_CERTIFICATE))\n\n\n# Rules that need to be present for the all targets, even\n# if they don't do anything.\n.PHONY: systemimage\nsystemimage:\n\n# -----------------------------------------------------------------\n\nevent_log_tags_file := $(TARGET_OUT)/etc/event-log-tags\n\n# Include tags from all packages that we know about\nall_event_log_tags_src := \\\n    $(sort $(foreach m, $(ALL_MODULES), $(ALL_MODULES.$(m).EVENT_LOG_TAGS)))\n\n# Include tags from all packages included in this product, plus all\n# tags that are part of the system (ie, not in a vendor/ or device/\n# directory).\nevent_log_tags_src := \\\n    $(sort $(foreach m,\\\n      $(call resolve-bitness-for-modules,TARGET,$(PRODUCT_PACKAGES)) \\\n      $(call module-names-for-tag-list,user), \\\n      $(ALL_MODULES.$(m).EVENT_LOG_TAGS)) \\\n      $(filter-out vendor/% device/% out/%,$(all_event_log_tags_src)))\n\n$(event_log_tags_file): PRIVATE_SRC_FILES := $(event_log_tags_src)\n$(event_log_tags_file): $(event_log_tags_src) $(MERGETAGS)\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) $(MERGETAGS) -o $@ $(PRIVATE_SRC_FILES)\n\n$(eval $(call declare-0p-target,$(event_log_tags_file)))\n\n.PHONY: event-log-tags\nevent-log-tags: $(event_log_tags_file)\n\nALL_DEFAULT_INSTALLED_MODULES += $(event_log_tags_file)\n\n# Initialize INSTALLED_FILES_OUTSIDE_IMAGES with the list of all device files,\n# files installed in images will be filtered out later.\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out \\\n  $(PRODUCT_OUT)/apex/% \\\n  $(PRODUCT_OUT)/fake_packages/% \\\n  $(PRODUCT_OUT)/testcases/%, \\\n  $(filter $(PRODUCT_OUT)/%,$(ALL_DEFAULT_INSTALLED_MODULES)))\n\n# #################################################################\n# Targets for boot/OS images\n# #################################################################\nifneq ($(strip $(TARGET_NO_BOOTLOADER)),true)\n  INSTALLED_BOOTLOADER_MODULE := $(PRODUCT_OUT)/bootloader\n  ifdef BOARD_PREBUILT_BOOTLOADER\n    $(eval $(call copy-one-file,$(BOARD_PREBUILT_BOOTLOADER),$(INSTALLED_BOOTLOADER_MODULE)))\n    $(call dist-for-goals,dist_files,$(INSTALLED_BOOTLOADER_MODULE))\n  endif # BOARD_PREBUILT_BOOTLOADER\n  ifeq ($(strip $(TARGET_BOOTLOADER_IS_2ND)),true)\n    INSTALLED_2NDBOOTLOADER_TARGET := $(PRODUCT_OUT)/2ndbootloader\n  else\n    INSTALLED_2NDBOOTLOADER_TARGET :=\n  endif\nelse\n  INSTALLED_BOOTLOADER_MODULE :=\n  INSTALLED_2NDBOOTLOADER_TARGET :=\nendif # TARGET_NO_BOOTLOADER\nifneq ($(strip $(TARGET_NO_KERNEL)),true)\n  ifneq ($(strip $(BOARD_KERNEL_BINARIES)),)\n    INSTALLED_KERNEL_TARGET := $(foreach k,$(BOARD_KERNEL_BINARIES), \\\n      $(PRODUCT_OUT)/$(k))\n  else\n    INSTALLED_KERNEL_TARGET := $(PRODUCT_OUT)/kernel\n  endif\nelse\n  INSTALLED_KERNEL_TARGET :=\nendif\n\n# -----------------------------------------------------------------\n# the root dir\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_ROOT_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nINTERNAL_ROOT_FILES := $(filter $(TARGET_ROOT_OUT)/%, \\\n\t$(ALL_DEFAULT_INSTALLED_MODULES))\n\n\nINSTALLED_FILES_FILE_ROOT := $(PRODUCT_OUT)/installed-files-root.txt\nINSTALLED_FILES_JSON_ROOT := $(INSTALLED_FILES_FILE_ROOT:.txt=.json)\n$(INSTALLED_FILES_FILE_ROOT): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_ROOT)\n$(INSTALLED_FILES_FILE_ROOT) : $(INTERNAL_ROOT_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(TARGET_ROOT_OUT)\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_ROOT_OUT) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(call declare-0p-target,$(INSTALLED_FILES_FILE_ROOT))\n$(call declare-0p-target,$(INSTALLED_FILES_JSON_ROOT))\n\n#------------------------------------------------------------------\n# dtb\nifdef BOARD_INCLUDE_DTB_IN_BOOTIMG\nINSTALLED_DTBIMAGE_TARGET := $(PRODUCT_OUT)/dtb.img\nifdef BOARD_PREBUILT_DTBIMAGE_DIR\n$(INSTALLED_DTBIMAGE_TARGET) : $(sort $(wildcard $(BOARD_PREBUILT_DTBIMAGE_DIR)/*.dtb))\n\tcat $^ > $@\nendif\nendif\n\n\n# -----------------------------------------------------------------\n# dtbo image\nifdef BOARD_PREBUILT_DTBOIMAGE\nINSTALLED_DTBOIMAGE_TARGET := $(PRODUCT_OUT)/dtbo.img\n\nifeq ($(BOARD_AVB_ENABLE),true)\n$(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE) $(AVBTOOL) $(BOARD_AVB_DTBO_KEY_PATH)\n\tcp $(BOARD_PREBUILT_DTBOIMAGE) $@\n\tchmod +w $@\n\t$(AVBTOOL) add_hash_footer \\\n\t    --image $@ \\\n\t    $(call get-partition-size-argument,$(BOARD_DTBOIMG_PARTITION_SIZE)) \\\n\t    --partition_name dtbo $(INTERNAL_AVB_DTBO_SIGNING_ARGS) \\\n\t    $(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS)\n\n$(call declare-1p-container,$(INSTALLED_DTBOIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_DTBOIMAGE_TARGET),$(BOARD_PREBUILT_DTBOIMAGE),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_DTBOIMAGE_TARGET)\nelse\n$(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE)\n\tcp $(BOARD_PREBUILT_DTBOIMAGE) $@\nendif\n\nendif # BOARD_PREBUILT_DTBOIMAGE\n\n# -----------------------------------------------------------------\n\n# -----------------------------------------------------------------\n# the ramdisk\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_RAMDISK_IMAGE\nINTERNAL_RAMDISK_FILES := $(filter $(TARGET_RAMDISK_OUT)/%, \\\n\t$(ALL_DEFAULT_INSTALLED_MODULES))\n\nINSTALLED_FILES_FILE_RAMDISK := $(PRODUCT_OUT)/installed-files-ramdisk.txt\nINSTALLED_FILES_JSON_RAMDISK := $(INSTALLED_FILES_FILE_RAMDISK:.txt=.json)\n$(INSTALLED_FILES_FILE_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_RAMDISK)\n$(INSTALLED_FILES_FILE_RAMDISK) : $(INTERNAL_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(TARGET_RAMDISK_OUT)\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_RAMDISK_OUT) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_RAMDISK)))\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_RAMDISK)))\n\nBUILT_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img\n\nifeq ($(BOARD_RAMDISK_USE_LZ4),true)\n# -l enables the legacy format used by the Linux kernel\nCOMPRESSION_COMMAND_DEPS := $(LZ4)\nCOMPRESSION_COMMAND := $(LZ4) -l -12 --favor-decSpeed\nRAMDISK_EXT := .lz4\nelse\nCOMPRESSION_COMMAND_DEPS := $(GZIP)\nCOMPRESSION_COMMAND := $(GZIP)\nRAMDISK_EXT := .gz\nendif\n\nifneq ($(BOARD_KERNEL_MODULES_16K),)\n\nTARGET_OUT_RAMDISK_16K := $(PRODUCT_OUT)/ramdisk_16k\nBUILT_RAMDISK_16K_TARGET := $(PRODUCT_OUT)/ramdisk_16k.img\nRAMDISK_16K_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_ramdisk_16k)\n\nifneq ($(BOARD_SYSTEM_KERNEL_MODULES),)\nSYSTEM_DLKM_MODULE_PATTERNS := $(foreach path,$(BOARD_SYSTEM_KERNEL_MODULES),%/$(notdir $(path)))\n\nendif\n\n# BOARD_KERNEL_MODULES_16K might contain duplicate modules under different path.\n# for example, foo/bar/wifi.ko and foo/wifi.ko . To avoid build issues, de-dup\n# module list on basename first.\nBOARD_KERNEL_MODULES_16K := $(foreach \\\n  pattern,\\\n  $(sort $(foreach \\\n    path,\\\n    $(BOARD_KERNEL_MODULES_16K),\\\n    %/$(notdir $(path)))\\\n  ),\\\n  $(firstword $(filter $(pattern),$(BOARD_KERNEL_MODULES_16K))) \\\n)\n# For non-GKI modules, strip them before install. As debug symbols take up\n# significant space.\n$(foreach \\\n  file,\\\n  $(filter-out $(SYSTEM_DLKM_MODULE_PATTERNS),$(BOARD_KERNEL_MODULES_16K)),\\\n  $(eval \\\n    $(call copy-and-strip-kernel-module,\\\n      $(file),\\\n      $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/$(notdir $(file)) \\\n    ) \\\n  ) \\\n)\n\n# For GKI modules, copy as-is without stripping, because stripping would\n# remove the signature of kernel modules, and GKI modules must be signed\n# for kernel to load them.\n$(foreach \\\n  file,\\\n  $(filter $(SYSTEM_DLKM_MODULE_PATTERNS),$(BOARD_KERNEL_MODULES_16K)),\\\n  $(eval \\\n    $(call copy-one-file,\\\n      $(file),\\\n      $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/$(notdir $(file)) \\\n    ) \\\n  ) \\\n)\n\nBOARD_VENDOR_RAMDISK_FRAGMENT.16K.PREBUILT := $(BUILT_RAMDISK_16K_TARGET)\n\nifndef BOARD_KERNEL_MODULES_LOAD_16K\n  BOARD_KERNEL_MODULES_LOAD_16K := $(BOARD_KERNEL_MODULES_16K)\nendif\n\n$(BUILT_RAMDISK_16K_TARGET): $(DEPMOD) $(MKBOOTFS) $(EXTRACT_KERNEL) $(COMPRESSION_COMMAND_DEPS)\n$(BUILT_RAMDISK_16K_TARGET): $(foreach file,$(BOARD_KERNEL_MODULES_16K),$(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/$(notdir $(file)))\n\t$(DEPMOD) -b $(RAMDISK_16K_STAGING_DIR) 0.0\n\trm -f $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/modules.load\n\tfor MODULE in $(BOARD_KERNEL_MODULES_LOAD_16K); do \\\n\t\tbasename $$MODULE >> $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/modules.load ; \\\n\tdone;\n\trm -rf $(TARGET_OUT_RAMDISK_16K)/lib/modules\n\tmkdir -p $(TARGET_OUT_RAMDISK_16K)/lib/modules\n\tKERNEL_RELEASE=`$(EXTRACT_KERNEL) --tools lz4:$(LZ4) --input $(BOARD_KERNEL_PATH_16K) --output-release` ;\\\n\tIS_16K_KERNEL=`$(EXTRACT_KERNEL) --tools lz4:$(LZ4) --input $(BOARD_KERNEL_PATH_16K) --output-config` ;\\\n\tif [[ \"$$IS_16K_KERNEL\" == *\"CONFIG_ARM64_16K_PAGES=y\"* ]]; then SUFFIX=_16k; fi ;\\\n\tcp -r $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0 $(TARGET_OUT_RAMDISK_16K)/lib/modules/$$KERNEL_RELEASE$$SUFFIX\n\t$(MKBOOTFS) $(TARGET_OUT_RAMDISK_16K) | $(COMPRESSION_COMMAND) > $@\n\n# Builds a ramdisk using modules defined in BOARD_KERNEL_MODULES_16K\nramdisk_16k: $(BUILT_RAMDISK_16K_TARGET)\n.PHONY: ramdisk_16k\n\nendif\n\n# -----------------------------------------------------------------\n# 16KB dtbo image\nifdef BOARD_PREBUILT_DTBOIMAGE_16KB\nINSTALLED_DTBOIMAGE_16KB_TARGET := $(PRODUCT_OUT)/dtbo_16k.img\n\nifeq ($(BOARD_AVB_ENABLE),true)\n$(INSTALLED_DTBOIMAGE_16KB_TARGET): $(BOARD_PREBUILT_DTBOIMAGE_16KB) $(AVBTOOL) $(BOARD_AVB_DTBO_KEY_PATH)\n\tcp $(BOARD_PREBUILT_DTBOIMAGE_16KB) $@\n\tchmod +w $@\n\t$(AVBTOOL) add_hash_footer \\\n\t    --image $@ \\\n\t    $(call get-partition-size-argument,$(BOARD_DTBOIMG_PARTITION_SIZE)) \\\n\t    --partition_name dtbo $(INTERNAL_AVB_DTBO_SIGNING_ARGS) \\\n\t    $(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS)\n\n$(call declare-1p-container,$(INSTALLED_DTBOIMAGE_16KB_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_DTBOIMAGE_16KB_TARGET),$(BOARD_PREBUILT_DTBOIMAGE_16KB),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_DTBOIMAGE_16KB_TARGET)\nelse\n$(INSTALLED_DTBOIMAGE_16KB_TARGET): $(BOARD_PREBUILT_DTBOIMAGE_16KB)\n\tcp $(BOARD_PREBUILT_DTBOIMAGE_16KB) $@\nendif\n\nendif # BOARD_PREBUILT_DTBOIMAGE_16KB\n\n\nramdisk_intermediates :=$= $(call intermediates-dir-for,PACKAGING,ramdisk)\n$(eval $(call write-partition-file-list,$(ramdisk_intermediates)/file_list.txt,$(TARGET_RAMDISK_OUT),$(INTERNAL_RAMDISK_FILES)))\n\n# The value of RAMDISK_NODE_LIST is defined in system/core/rootdir/Android.bp.\n# This file contains /dev nodes description added to the generic ramdisk\n\n# We just build this directly to the install location.\nINSTALLED_RAMDISK_TARGET := $(BUILT_RAMDISK_TARGET)\n$(INSTALLED_RAMDISK_TARGET): PRIVATE_DIRS := debug_ramdisk dev metadata mnt proc second_stage_resources sys\n$(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(RAMDISK_NODE_LIST) $(INTERNAL_RAMDISK_FILES) $(INSTALLED_FILES_FILE_RAMDISK) | $(COMPRESSION_COMMAND_DEPS)\n\t$(call pretty,\"Target ramdisk: $@\")\n\t$(hide) mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/,$(PRIVATE_DIRS))\nifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))\n\t$(hide) mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/first_stage_ramdisk/,$(PRIVATE_DIRS))\nendif\n\t$(hide) $(MKBOOTFS) -n $(RAMDISK_NODE_LIST) -d $(TARGET_OUT) $(TARGET_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $@\n\n$(call declare-1p-container,$(INSTALLED_RAMDISK_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_RAMDISK_TARGET),$(INTERNAL_RAMDISK_FILE),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_RAMDISK_TARGET)\n\n.PHONY: ramdisk-nodeps\nramdisk-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)\n\t@echo \"make $@: ignoring dependencies\"\n\t$(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $(INSTALLED_RAMDISK_TARGET)\n\nendif # BUILDING_RAMDISK_IMAGE\n\n# -----------------------------------------------------------------\n# the boot image, which is a collection of other images.\n\n# This is defined here since we may be building recovery as boot\n# below and only want to define this once\nifneq ($(strip $(BOARD_KERNEL_BINARIES)),)\n  BUILT_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot,$(BOARD_KERNEL_BINARIES)), $(PRODUCT_OUT)/$(k).img)\nelse\n  BUILT_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img\nendif\n\nINTERNAL_PREBUILT_BOOTIMAGE :=\n\nmy_installed_prebuilt_gki_apex := $(strip $(foreach package,$(PRODUCT_PACKAGES),$(if $(ALL_MODULES.$(package).EXTRACTED_BOOT_IMAGE),$(package))))\nifdef my_installed_prebuilt_gki_apex\n  ifneq (1,$(words $(my_installed_prebuilt_gki_apex))) # len(my_installed_prebuilt_gki_apex) > 1\n    $(error More than one prebuilt GKI APEXes are installed: $(my_installed_prebuilt_gki_apex))\n  endif # len(my_installed_prebuilt_gki_apex) > 1\n\n  ifdef BOARD_PREBUILT_BOOTIMAGE\n    $(error Must not define BOARD_PREBUILT_BOOTIMAGE because a prebuilt GKI APEX is installed: $(my_installed_prebuilt_gki_apex))\n  endif # BOARD_PREBUILT_BOOTIMAGE defined\n\n  my_apex_extracted_boot_image := $(ALL_MODULES.$(my_installed_prebuilt_gki_apex).EXTRACTED_BOOT_IMAGE)\n  INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img\n  $(eval $(call copy-one-file,$(my_apex_extracted_boot_image),$(INSTALLED_BOOTIMAGE_TARGET)))\n  $(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,\"Boot Image\",boot)\n\n  INTERNAL_PREBUILT_BOOTIMAGE := $(my_apex_extracted_boot_image)\n\nelse # my_installed_prebuilt_gki_apex not defined\n\n# $1: boot image target\n# returns the kernel used to make the bootimage\ndefine bootimage-to-kernel\n  $(if $(BOARD_KERNEL_BINARIES),\\\n    $(PRODUCT_OUT)/$(subst .img,,$(subst boot,kernel,$(notdir $(1)))),\\\n    $(INSTALLED_KERNEL_TARGET))\nendef\n\nifdef BOARD_BOOTIMAGE_PARTITION_SIZE\n  BOARD_KERNEL_BOOTIMAGE_PARTITION_SIZE := $(BOARD_BOOTIMAGE_PARTITION_SIZE)\nendif\n\n# $1: boot image file name\n# $2: boot image variant (boot, boot-debug, boot-test-harness)\ndefine get-bootimage-partition-size\n$(BOARD_$(call to-upper,$(subst .img,,$(subst $(2),kernel,$(notdir $(1)))))_BOOTIMAGE_PARTITION_SIZE)\nendef\n\n# $1: partition size\ndefine get-partition-size-argument\n  $(if $(1),--partition_size $(1),--dynamic_partition_size)\nendef\n\n# $1: output boot image target\n# $2: input path to kernel binary\ndefine build_boot_from_kernel_avb_enabled\n  $(eval kernel := $(2))\n  $(MKBOOTIMG) --kernel $(kernel) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1)\n  $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot)))\n  $(AVBTOOL) add_hash_footer \\\n          --image $(1) \\\n          $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),boot)) \\\n          --salt `sha256sum \"$(kernel)\" | cut -d \" \" -f 1` \\\n          --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) \\\n          $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)\nendef\n\n\nifndef BOARD_PREBUILT_BOOTIMAGE\n\nifneq ($(strip $(TARGET_NO_KERNEL)),true)\nINTERNAL_BOOTIMAGE_ARGS := \\\n\t$(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET))\n\nifneq ($(BUILDING_INIT_BOOT_IMAGE),true)\n  INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET)\nendif\n\nifndef BUILDING_VENDOR_BOOT_IMAGE\nifdef BOARD_INCLUDE_DTB_IN_BOOTIMG\n  INTERNAL_BOOTIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET)\nendif\nendif\n\nINTERNAL_BOOTIMAGE_FILES := $(filter-out --%,$(INTERNAL_BOOTIMAGE_ARGS))\n\n# kernel cmdline/base/pagesize in boot.\n# - If using GKI, use GENERIC_KERNEL_CMDLINE. Remove kernel base and pagesize because they are\n#   device-specific.\n# - If not using GKI:\n#   - If building vendor_boot, INTERNAL_KERNEL_CMDLINE, base and pagesize goes in vendor_boot.\n#   - Otherwise, put them in boot.\nifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))\n  ifdef GENERIC_KERNEL_CMDLINE\n    INTERNAL_BOOTIMAGE_ARGS += --cmdline \"$(GENERIC_KERNEL_CMDLINE)\"\n  endif\nelse ifndef BUILDING_VENDOR_BOOT_IMAGE # && BOARD_USES_GENERIC_KERNEL_IMAGE != true\n  ifdef INTERNAL_KERNEL_CMDLINE\n    INTERNAL_BOOTIMAGE_ARGS += --cmdline \"$(INTERNAL_KERNEL_CMDLINE)\"\n  endif\n  ifdef BOARD_KERNEL_BASE\n    INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)\n  endif\n  ifdef BOARD_KERNEL_PAGESIZE\n    INTERNAL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)\n  endif\nendif # BUILDING_VENDOR_BOOT_IMAGE == \"\" && BOARD_USES_GENERIC_KERNEL_IMAGE != true\n\nINTERNAL_MKBOOTIMG_VERSION_ARGS := \\\n  --os_version $(PLATFORM_VERSION_LAST_STABLE) \\\n  --os_patch_level $(PLATFORM_SECURITY_PATCH)\n\n# Define these only if we are building boot\nifdef BUILDING_BOOT_IMAGE\nINSTALLED_BOOTIMAGE_TARGET := $(BUILT_BOOTIMAGE_TARGET)\n\nifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true)\n$(error TARGET_BOOTIMAGE_USE_EXT2 is not supported anymore)\nendif # TARGET_BOOTIMAGE_USE_EXT2\n\n$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET), $(eval $(call add-dependency,$(b),$(call bootimage-to-kernel,$(b)))))\n\nifeq (true,$(BOARD_AVB_ENABLE))\n\n# $1: boot image target\ndefine build_boot_board_avb_enabled\n  $(eval kernel := $(call bootimage-to-kernel,$(1)))\n  $(call build_boot_from_kernel_avb_enabled,$(1),$(kernel))\nendef\n\n$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH)\n\t$(call pretty,\"Target boot image: $@\")\n\t$(call build_boot_board_avb_enabled,$@)\n\n$(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,\"Boot Image\",boot)\n$(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_BOOTIMAGE_FILES),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_BOOTIMAGE_TARGET)\n\n.PHONY: bootimage-nodeps\nbootimage-nodeps: $(MKBOOTIMG) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH)\n\t@echo \"make $@: ignoring dependencies\"\n\t$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(call build_boot_board_avb_enabled,$(b)))\n\nelse # BOARD_AVB_ENABLE != true\n\n# $1: boot image target\ndefine build_boot_novboot\n  $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1)\n  $(call assert-max-image-size,$1,$(call get-bootimage-partition-size,$(1),boot))\nendef\n\n$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)\n\t$(call pretty,\"Target boot image: $@\")\n\t$(call build_boot_novboot,$@)\n\n$(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,\"Boot Image\",boot)\n$(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_BOOTIMAGE_FILES),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_BOOTIMAGE_TARGET)\n\n.PHONY: bootimage-nodeps\nbootimage-nodeps: $(MKBOOTIMG)\n\t@echo \"make $@: ignoring dependencies\"\n\t$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(call build_boot_novboot,$(b)))\n\nendif # BOARD_AVB_ENABLE\nendif # BUILDING_BOOT_IMAGE\n\nelse # TARGET_NO_KERNEL == \"true\"\nINSTALLED_BOOTIMAGE_TARGET :=\nendif # TARGET_NO_KERNEL\n\nelse # BOARD_PREBUILT_BOOTIMAGE defined\nINTERNAL_PREBUILT_BOOTIMAGE := $(BOARD_PREBUILT_BOOTIMAGE)\nINSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img\n\nifeq ($(BOARD_AVB_ENABLE),true)\n$(INSTALLED_BOOTIMAGE_TARGET): PRIVATE_WORKING_DIR := $(call intermediates-dir-for,PACKAGING,prebuilt_bootimg)\n$(INSTALLED_BOOTIMAGE_TARGET): $(INTERNAL_PREBUILT_BOOTIMAGE) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) $(UNPACK_BOOTIMG)\n\tcp $(INTERNAL_PREBUILT_BOOTIMAGE) $@\n\t$(UNPACK_BOOTIMG) --boot_img $(INTERNAL_PREBUILT_BOOTIMAGE) --out $(PRIVATE_WORKING_DIR)\n\tchmod +w $@\n\t$(AVBTOOL) add_hash_footer \\\n\t    --image $@ \\\n\t    --salt `sha256sum $(PRIVATE_WORKING_DIR)/kernel | cut -d \" \" -f 1` \\\n\t    $(call get-partition-size-argument,$(BOARD_BOOTIMAGE_PARTITION_SIZE)) \\\n\t    --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) \\\n\t    $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)\n\n\n$(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,\"Boot Image\",bool)\n$(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_PREBUILT_BOOTIMAGE),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_BOOTIMAGE_TARGET)\nelse\n$(INSTALLED_BOOTIMAGE_TARGET): $(INTERNAL_PREBUILT_BOOTIMAGE)\n\tcp $(INTERNAL_PREBUILT_BOOTIMAGE) $@\nendif # BOARD_AVB_ENABLE\n\nendif # BOARD_PREBUILT_BOOTIMAGE\n\nendif # my_installed_prebuilt_gki_apex not defined\n\nifneq ($(BOARD_KERNEL_PATH_16K),)\n\nBUILT_KERNEL_16K_TARGET := $(PRODUCT_OUT)/kernel_16k\n\n$(eval $(call copy-one-file,$(BOARD_KERNEL_PATH_16K),$(BUILT_KERNEL_16K_TARGET)))\n\n# Copies BOARD_KERNEL_PATH_16K to output directory as is\nkernel_16k: $(BUILT_KERNEL_16K_TARGET)\n.PHONY: kernel_16k\n\nBUILT_BOOTIMAGE_16K_TARGET := $(PRODUCT_OUT)/boot_16k.img\n\nBOARD_KERNEL_16K_BOOTIMAGE_PARTITION_SIZE := $(BOARD_BOOTIMAGE_PARTITION_SIZE)\n\n$(BUILT_BOOTIMAGE_16K_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH) $(BUILT_KERNEL_16K_TARGET)\n\t$(call pretty,\"Target boot 16k image: $@\")\n\t$(call build_boot_from_kernel_avb_enabled,$@,$(BUILT_KERNEL_16K_TARGET))\n\n\nbootimage_16k: $(BUILT_BOOTIMAGE_16K_TARGET)\n.PHONY: bootimage_16k\n\nBUILT_BOOT_OTA_PACKAGE_16K := $(PRODUCT_OUT)/boot_ota_16k.zip\n$(BUILT_BOOT_OTA_PACKAGE_16K): PRIVATE_BOOTIMAGE_TARGET := $(INSTALLED_BOOTIMAGE_TARGET)\n$(BUILT_BOOT_OTA_PACKAGE_16K): PRIVATE_BOOTIMAGE_16KB_TARGET := $(BUILT_BOOTIMAGE_16K_TARGET)\n$(BUILT_BOOT_OTA_PACKAGE_16K):  $(OTA_FROM_RAW_IMG) \\\n                                $(DEFAULT_SYSTEM_DEV_CERTIFICATE).pk8 \\\n                                $(INSTALLED_BOOTIMAGE_TARGET) \\\n                                $(BUILT_BOOTIMAGE_16K_TARGET) \\\n                                $(INSTALLED_DTBOIMAGE_16KB_TARGET) \\\n                                $(INSTALLED_DTBOIMAGE_TARGET)\n\t$(OTA_FROM_RAW_IMG) --package_key $(DEFAULT_SYSTEM_DEV_CERTIFICATE) \\\n                      --max_timestamp `cat $(BUILD_DATETIME_FILE)` \\\n                      --path $(HOST_OUT) \\\n                      --partition_name $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),\\\n                          $(INSTALLED_DTBOIMAGE_16KB_TARGET)),\\\n                        boot$(comma)dtbo,\\\n                        boot) \\\n                      --output $@ \\\n                      $(if $(BOARD_16K_OTA_USE_INCREMENTAL),\\\n                        $(PRIVATE_BOOTIMAGE_TARGET):$(PRIVATE_BOOTIMAGE_16KB_TARGET),\\\n                        $(PRIVATE_BOOTIMAGE_16KB_TARGET)\\\n                      )\\\n                      $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),$(INSTALLED_DTBOIMAGE_16KB_TARGET)),\\\n                        $(INSTALLED_DTBOIMAGE_16KB_TARGET))\n\nboototapackage_16k: $(BUILT_BOOT_OTA_PACKAGE_16K)\n.PHONY: boototapackage_16k\n\n\nBUILT_BOOT_OTA_PACKAGE_4K := $(PRODUCT_OUT)/boot_ota_4k.zip\n$(BUILT_BOOT_OTA_PACKAGE_4K): $(OTA_FROM_RAW_IMG) \\\n                              $(INSTALLED_BOOTIMAGE_TARGET) \\\n                              $(BUILT_BOOTIMAGE_16K_TARGET) \\\n                              $(DEFAULT_SYSTEM_DEV_CERTIFICATE).pk8 \\\n                              $(INSTALLED_DTBOIMAGE_TARGET) \\\n                              $(INSTALLED_DTBOIMAGE_16KB_TARGET)\n\t$(OTA_FROM_RAW_IMG) --package_key $(DEFAULT_SYSTEM_DEV_CERTIFICATE) \\\n                      --max_timestamp `cat $(BUILD_DATETIME_FILE)` \\\n                      --path $(HOST_OUT) \\\n                      --partition_name $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),\\\n                          $(INSTALLED_DTBOIMAGE_16KB_TARGET)),\\\n                        boot$(comma)dtbo,\\\n                        boot) \\\n                      --output $@ \\\n                      $(if $(BOARD_16K_OTA_USE_INCREMENTAL),\\\n                        $(BUILT_BOOTIMAGE_16K_TARGET):$(INSTALLED_BOOTIMAGE_TARGET),\\\n                        $(INSTALLED_BOOTIMAGE_TARGET)\\\n                      )\\\n                      $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),$(INSTALLED_DTBOIMAGE_16KB_TARGET)),\\\n                        $(INSTALLED_DTBOIMAGE_TARGET))\n\nboototapackage_4k: $(BUILT_BOOT_OTA_PACKAGE_4K)\n.PHONY: boototapackage_4k\n\nifeq ($(BOARD_16K_OTA_MOVE_VENDOR),true)\n$(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_4K),$(TARGET_OUT_VENDOR)/boot_otas/boot_ota_4k.zip))\n$(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_16K),$(TARGET_OUT_VENDOR)/boot_otas/boot_ota_16k.zip))\n\nALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_VENDOR)/boot_otas/boot_ota_4k.zip\nALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_VENDOR)/boot_otas/boot_ota_16k.zip\n\nifneq ($(BOARD_VENDOR_KERNEL_MODULES_2ND_STAGE_16KB_MODE),)\n# Add the modules that need to be loaded in the Second Boot Stage\n# to /vendor_dlkm/lib/modules/16k-mode\nVENDOR_DLKM_16K_MODE_DIR := lib/modules/16k-mode\n$(foreach module,$(BOARD_VENDOR_KERNEL_MODULES_2ND_STAGE_16KB_MODE), \\\n    $(eval $(call copy-one-file,$(TARGET_KERNEL_DIR_16K)/$(module),\\\n                                $(TARGET_OUT_VENDOR_DLKM)/$(VENDOR_DLKM_16K_MODE_DIR)/$(module))))\n\nALL_DEFAULT_INSTALLED_MODULES += $(foreach module,$(BOARD_VENDOR_KERNEL_MODULES_2ND_STAGE_16KB_MODE),\\\n    $(TARGET_OUT_VENDOR_DLKM)/$(VENDOR_DLKM_16K_MODE_DIR)/$(module))\nendif # BOARD_VENDOR_KERNEL_MODULES_2ND_STAGE_16KB_MODE not empty\n\nelse\n$(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_4K),$(TARGET_OUT)/boot_otas/boot_ota_4k.zip))\n$(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_16K),$(TARGET_OUT)/boot_otas/boot_ota_16k.zip))\n\nALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT)/boot_otas/boot_ota_4k.zip\nALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT)/boot_otas/boot_ota_16k.zip\nendif # BOARD_16K_OTA_MOVE_VENDOR == true\n\n\nendif\n\nmy_apex_extracted_boot_image :=\nmy_installed_prebuilt_gki_apex :=\n\n# -----------------------------------------------------------------\n#  init boot image\nifeq ($(BUILDING_INIT_BOOT_IMAGE),true)\n\nINSTALLED_INIT_BOOT_IMAGE_TARGET := $(PRODUCT_OUT)/init_boot.img\n$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_RAMDISK_TARGET)\n\nINTERNAL_INIT_BOOT_IMAGE_ARGS := --ramdisk $(INSTALLED_RAMDISK_TARGET)\n\nifdef BOARD_KERNEL_PAGESIZE\n  INTERNAL_INIT_BOOT_IMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)\nendif\n\nifeq ($(BOARD_AVB_ENABLE),true)\n$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_INIT_BOOT_KEY_PATH)\n\t$(call pretty,\"Target init_boot image: $@\")\n\t$(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_INIT_ARGS) --output \"$@\"\n\t$(call assert-max-image-size,$@,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE))\n\t$(AVBTOOL) add_hash_footer \\\n           --image $@ \\\n\t   $(call get-partition-size-argument,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) \\\n\t   --salt $$(sha256sum $(BUILD_NUMBER_FILE) $(BUILD_DATETIME_FILE) | cut -d \" \" -f 1 | tr -d '\\n') \\\n\t   --partition_name init_boot $(INTERNAL_AVB_INIT_BOOT_SIGNING_ARGS) \\\n\t   $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)\n\n$(call declare-1p-container,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),$(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE),$(PRODUCT_OUT)/:/)\nelse\n$(INSTALLED_INIT_BOOT_IMAGE_TARGET):\n\t$(call pretty,\"Target init_boot image: $@\")\n\t$(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_INIT_ARGS) --output $@\n\t$(call assert-max-image-size,$@,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE))\n\n$(call declare-1p-target,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),)\nendif\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_INIT_BOOT_IMAGE_TARGET)\n\nelse # BUILDING_INIT_BOOT_IMAGE is not true\n\nifdef BOARD_PREBUILT_INIT_BOOT_IMAGE\nINTERNAL_PREBUILT_INIT_BOOT_IMAGE := $(BOARD_PREBUILT_INIT_BOOT_IMAGE)\nINSTALLED_INIT_BOOT_IMAGE_TARGET := $(PRODUCT_OUT)/init_boot.img\n\nifeq ($(BOARD_AVB_ENABLE),true)\n$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $(AVBTOOL) $(BOARD_AVB_INIT_BOOT_KEY_PATH)\n\tcp $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $@\n\tchmod +w $@\n\t$(AVBTOOL) add_hash_footer \\\n\t    --image $@ \\\n\t    $(call get-partition-size-argument,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) \\\n\t    --partition_name init_boot $(INTERNAL_AVB_INIT_BOOT_SIGNING_ARGS) \\\n\t    $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)\n\n$(call declare-1p-container,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),$(INTERNAL_PREBUILT_INIT_BOOT_IMAGE),$(PRODUCT_OUT)/:/)\nelse\n$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE)\n\tcp $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $@\n\n$(call declare-1p-target,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),)\nendif # BOARD_AVB_ENABLE\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_INIT_BOOT_IMAGE_TARGET)\n\nelse # BOARD_PREBUILT_INIT_BOOT_IMAGE not defined\nINSTALLED_INIT_BOOT_IMAGE_TARGET :=\nendif # BOARD_PREBUILT_INIT_BOOT_IMAGE\n\nendif # BUILDING_INIT_BOOT_IMAGE is not true\n\n# -----------------------------------------------------------------\n# vendor boot image\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_VENDOR_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true)\n\nINTERNAL_VENDOR_RAMDISK_FILES := $(filter $(TARGET_VENDOR_RAMDISK_OUT)/%, \\\n    $(ALL_DEFAULT_INSTALLED_MODULES))\n\nINTERNAL_VENDOR_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot)/vendor_ramdisk.cpio$(RAMDISK_EXT)\nvendor_ramdisk_intermediates :=$= $(call intermediates-dir-for,PACKAGING,vendor_ramdisk)\n$(eval $(call write-partition-file-list,$(vendor_ramdisk_intermediates)/file_list.txt,$(TARGET_VENDOR_RAMDISK_OUT),$(INTERNAL_VENDOR_RAMDISK_FILES)))\n\n# Exclude recovery files in the default vendor ramdisk if including a standalone\n# recovery ramdisk in vendor_boot.\nifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))\nifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))\n$(INTERNAL_VENDOR_RAMDISK_TARGET): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)\n$(INTERNAL_VENDOR_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)\nendif\nendif\n\n$(INTERNAL_VENDOR_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)\n\t$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@\n\nINSTALLED_VENDOR_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk.img\n$(INSTALLED_VENDOR_RAMDISK_TARGET): $(INTERNAL_VENDOR_RAMDISK_TARGET)\n\t@echo \"Target vendor ramdisk: $@\"\n\t$(copy-file-to-target)\n\n$(call declare-1p-container,$(INSTALLED_VENDOR_RAMDISK_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_VENDOR_RAMDISK_TARGET),$(INTERNAL_VENDOR_RAMDISK_TARGET),$(PRODUCT_OUT)/:/)\n\nVENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_RAMDISK_TARGET)\n\nINSTALLED_FILES_FILE_VENDOR_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk.txt\nINSTALLED_FILES_JSON_VENDOR_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_RAMDISK:.txt=.json)\n$(INSTALLED_FILES_FILE_VENDOR_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_RAMDISK)\n$(INSTALLED_FILES_FILE_VENDOR_RAMDISK): $(INTERNAL_VENDOR_RAMDISK_TARGET)\n$(INSTALLED_FILES_FILE_VENDOR_RAMDISK): $(INTERNAL_VENDOR_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_VENDOR_RAMDISK_OUT) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_RAMDISK)))\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_RAMDISK)))\n\nifdef BOARD_INCLUDE_DTB_IN_BOOTIMG\n  ifneq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),true)\n    # If we have vendor_kernel_boot partition, we migrate dtb image to that image\n    # and allow dtb in vendor_boot to be empty.\n    INTERNAL_VENDOR_BOOTIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET)\n  endif\nendif\nifdef BOARD_KERNEL_BASE\n  INTERNAL_VENDOR_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)\nendif\nifdef BOARD_KERNEL_PAGESIZE\n  INTERNAL_VENDOR_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)\nendif\nifdef INTERNAL_KERNEL_CMDLINE\n  INTERNAL_VENDOR_BOOTIMAGE_ARGS += --vendor_cmdline \"$(INTERNAL_KERNEL_CMDLINE)\"\nendif\n\nifneq (, $(INTERNAL_BOOTCONFIG)$(INTERNAL_BOOTCONFIG_FILE))\n  INTERNAL_VENDOR_BOOTCONFIG_TARGET := $(PRODUCT_OUT)/vendor-bootconfig.img\n  $(INTERNAL_VENDOR_BOOTCONFIG_TARGET):\n\trm -f $@\n\t$(foreach param,$(INTERNAL_BOOTCONFIG), \\\n\t printf \"%s\\n\" $(param) >> $@;)\n\tcat $(INTERNAL_BOOTCONFIG_FILE) >> $@\n  INTERNAL_VENDOR_BOOTIMAGE_ARGS += --vendor_bootconfig $(INTERNAL_VENDOR_BOOTCONFIG_TARGET)\nendif\n\n# $(1): Build target name\n# $(2): Staging dir to be compressed\n# $(3): Build dependencies\ndefine build-vendor-ramdisk-fragment-target\n$(1): $(3) $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)\n\t$(MKBOOTFS) -d $(TARGET_OUT) $(2) | $(COMPRESSION_COMMAND) > $$@\nendef\n\n# $(1): Ramdisk name\ndefine build-vendor-ramdisk-fragment\n$(strip \\\n  $(eval build_target := $(call intermediates-dir-for,PACKAGING,vendor_ramdisk_fragments)/$(1).cpio$(RAMDISK_EXT)) \\\n  $(eval $(call build-vendor-ramdisk-fragment-target,$(build_target),$(VENDOR_RAMDISK_FRAGMENT.$(1).STAGING_DIR),$(VENDOR_RAMDISK_FRAGMENT.$(1).FILES))) \\\n  $(build_target) \\\n)\nendef\n\n# $(1): Ramdisk name\n# $(2): Prebuilt file path\ndefine build-prebuilt-vendor-ramdisk-fragment\n$(strip \\\n  $(eval build_target := $(call intermediates-dir-for,PACKAGING,prebuilt_vendor_ramdisk_fragments)/$(1)) \\\n  $(eval $(call copy-one-file,$(2),$(build_target))) \\\n  $(build_target) \\\n)\nendef\n\nINTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS :=\nINTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS :=\n\n$(foreach vendor_ramdisk_fragment,$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS), \\\n  $(eval prebuilt_vendor_ramdisk_fragment_file := $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).PREBUILT)) \\\n  $(if $(prebuilt_vendor_ramdisk_fragment_file), \\\n    $(eval vendor_ramdisk_fragment_target := $(call build-prebuilt-vendor-ramdisk-fragment,$(vendor_ramdisk_fragment),$(prebuilt_vendor_ramdisk_fragment_file))) \\\n    $(eval ### else ###), \\\n    $(eval vendor_ramdisk_fragment_target := $(call build-vendor-ramdisk-fragment,$(vendor_ramdisk_fragment)))) \\\n  $(eval INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS += $(vendor_ramdisk_fragment_target)) \\\n  $(eval INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS += $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS) --vendor_ramdisk_fragment $(vendor_ramdisk_fragment_target)) \\\n)\n\nINSTALLED_VENDOR_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot.img\n$(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_DTBIMAGE_TARGET)\n$(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) $(INTERNAL_VENDOR_BOOTCONFIG_TARGET)\nifeq ($(BOARD_AVB_ENABLE),true)\n$(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOTIMAGE_KEY_PATH)\n\t$(call pretty,\"Target vendor_boot image: $@\")\n\t$(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@\n\t$(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))\n\t$(AVBTOOL) add_hash_footer \\\n           --image $@ \\\n\t   $(call get-partition-size-argument,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) \\\n\t   --salt $$(sha256sum $(BUILD_NUMBER_FILE) $(BUILD_DATETIME_FILE) | cut -d \" \" -f 1 | tr -d '\\n') \\\n\t   --partition_name vendor_boot $(INTERNAL_AVB_VENDOR_BOOT_SIGNING_ARGS) \\\n\t   $(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS)\nelse\n$(INSTALLED_VENDOR_BOOTIMAGE_TARGET):\n\t$(call pretty,\"Target vendor_boot image: $@\")\n\t$(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@\n\t$(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))\nendif\n\n$(call declare-1p-container,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_DTB_IMAGE_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) $(INTERNAL_VENDOR_BOOTCONDIG_TARGET),$(PRODUCT_OUT)/:/)\nVENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_BOOTIMAGE_TARGET)\n\nelse # BUILDING_VENDOR_BOOT_IMAGE not defined, use prebuilt image\n\nifdef BOARD_PREBUILT_VENDOR_BOOTIMAGE\nINTERNAL_PREBUILT_VENDOR_BOOTIMAGE := $(BOARD_PREBUILT_VENDOR_BOOTIMAGE)\nINSTALLED_VENDOR_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot.img\n\nifeq ($(BOARD_AVB_ENABLE),true)\n$(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(INTERNAL_PREBUILT_VENDOR_BOOTIMAGE) $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOT_KEY_PATH)\n\tcp $(INTERNAL_PREBUILT_VENDOR_BOOTIMAGE) $@\n\tchmod +w $@\n\t$(AVBTOOL) add_hash_footer \\\n\t    --image $@ \\\n\t    $(call get-partition-size-argument,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) \\\n\t    --partition_name vendor_boot $(INTERNAL_AVB_VENDOR_BOOT_SIGNING_ARGS) \\\n\t    $(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS)\nelse\n$(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(INTERNAL_PREBUILT_VENDOR_BOOTIMAGE)\n\tcp $(INTERNAL_PREBUILT_VENDOR_BOOTIMAGE) $@\n\nendif # BOARD_AVB_ENABLE\n$(call declare-1p-container,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET),$(INTERNAL_PREBUILT_VENDOR_BOOTIMAGE),$(PRODUCT_OUT)/:/)\nendif # BOARD_PREBUILT_VENDOR_BOOTIMAGE\nendif # BUILDING_VENDOR_BOOT_IMAGE\n\n# -----------------------------------------------------------------\n# vendor kernel boot image\nifeq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),true)\n\nINTERNAL_VENDOR_KERNEL_RAMDISK_FILES := $(filter $(TARGET_VENDOR_KERNEL_RAMDISK_OUT)/%, \\\n    $(ALL_DEFAULT_INSTALLED_MODULES))\n\nINTERNAL_VENDOR_KERNEL_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_kernel_boot)/vendor_kernel_ramdisk.cpio$(RAMDISK_EXT)\n\n$(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)\n\t$(hide) : $(words $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES))\n\t$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_KERNEL_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $@\n\nINSTALLED_VENDOR_KERNEL_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_kernel_ramdisk.img\n$(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET): $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET)\n\t@echo \"Target vendor kernel ramdisk: $@\"\n\t$(copy-file-to-target)\n\nINSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-kernel-ramdisk.txt\nINSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK:.txt=.json)\n$(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK)\n$(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK): $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET)\n$(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK): $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_VENDOR_KERNEL_RAMDISK_OUT) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK))\n$(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK))\n\nINTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS := --vendor_ramdisk $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET)\nINSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_kernel_boot.img\n$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET)\n\nifdef BOARD_INCLUDE_DTB_IN_BOOTIMG\n  INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET)\n  $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): $(INSTALLED_DTBIMAGE_TARGET)\nendif\nifdef BOARD_KERNEL_PAGESIZE\n  INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)\nendif\n\n\nifeq ($(BOARD_AVB_ENABLE),true)\n$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_KERNEL_BOOTIMAGE_KEY_PATH)\n\t$(call pretty,\"Target vendor_kernel_boot image: $@\")\n\t$(MKBOOTIMG) $(INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_boot $@\n\t$(call assert-max-image-size,$@,$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE))\n\t$(AVBTOOL) add_hash_footer \\\n\t    --image $@ \\\n\t   $(call get-partition-size-argument,$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE)) \\\n\t   --partition_name vendor_kernel_boot $(INTERNAL_AVB_VENDOR_KERNEL_BOOT_SIGNING_ARGS) \\\n\t   $(BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS)\nelse\n$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET):\n\t$(call pretty,\"Target vendor_kernel_boot image: $@\")\n\t$(MKBOOTIMG) $(INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_boot $@\n\t$(call assert-max-image-size,$@,$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE))\nendif\n$(call declare-1p-container,$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),)\nifdef BOARD_INCLUDE_DTB_IN_BOOTIMG\n$(call declare-container-license-deps,$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),\\\n    $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) $(INSTALLED_DTBIMAGE_TARGET),\\\n    $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET):)\nelse\n$(call declare-container-license-deps,$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET),$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET):)\nendif\nendif # BUILDING_VENDOR_KERNEL_BOOT_IMAGE\n\n# -----------------------------------------------------------------\n# NOTICE files\n#\n# We are required to publish the licenses for all code under BSD, GPL and\n# Apache licenses (and possibly other more exotic ones as well). We err on the\n# side of caution, so the licenses for other third-party code are included here\n# too.\n#\n# This needs to be before the systemimage rules, because it adds to\n# ALL_DEFAULT_INSTALLED_MODULES, which those use to pick which files\n# go into the systemimage.\n\n.PHONY: notice_files\n\n# Convert license metadata into xml notice file.\n# $(1) - Output target notice filename\n# $(2) - Product name\n# $(3) - File title\n# $(4) - License metadata file roots\n# $(5) - Prefixes to strip\n#\ndefine xml-notice-rule\n$(1): PRIVATE_PRODUCT := $(2)\n$(1): PRIVATE_MESSAGE := $(3)\n$(1): PRIVATE_DEPS := $(call corresponding-license-metadata,$(4))\n$(1): $(call corresponding-license-metadata,$(4)) $(XMLNOTICE) $(BUILD_SYSTEM)/Makefile\n\tOUT_DIR=$(OUT_DIR) $(XMLNOTICE) -o $$@ -product=$$(PRIVATE_PRODUCT) -title=$$(PRIVATE_MESSAGE) $(foreach prefix, $(5), -strip_prefix=$(prefix)) $$(PRIVATE_DEPS)\n\nnotice_files: $(1)\nendef\n\n# Convert license metadata into text notice file.\n# $(1) - Output target notice filename\n# $(2) - Product name\n# $(3) - File title\n# $(4) - License metadata file roots\n# $(5) - Prefixes to strip\n#\ndefine text-notice-rule\n$(1): PRIVATE_PRODUCT := $(2)\n$(1): PRIVATE_MESSAGE := $(3)\n$(1): $(call corresponding-license-metadata,$(4)) $(TEXTNOTICE) $(BUILD_SYSTEM)/Makefile\n\tOUT_DIR=$(OUT_DIR) $(TEXTNOTICE) -o $$@ -product=$$(PRIVATE_PRODUCT) -title=$$(PRIVATE_MESSAGE) $(foreach prefix, $(5), -strip_prefix=$(prefix)) $(call corresponding-license-metadata,$(4))\n\nnotice_files: $(1)\nendef\n\n# Conversion license metadata into html notice file.\n# $(1) - Output target notice filename\n# $(2) - Product name\n# $(3) - File title\n# $(4) - License metadata file roots\n# $(5) - Prefixes to strip\n#\ndefine html-notice-rule\n$(1): PRIVATE_PRODUCT := $(2)\n$(1): PRIVATE_MESSAGE := $(3)\n$(1): $(call corresponding-license-metadata,$(4)) $(HTMLNOTICE) $(BUILD_SYSTEM)/Makefile\n\tOUT_DIR=$(OUT_DIR) $(HTMLNOTICE) -o $$@ -product=$$(PRIVATE_PRODUCT) -title=$$(PRIVATE_MESSAGE) $(foreach prefix, $(5), -strip_prefix=$(prefix)) $(call corresponding-license-metadata,$(4))\n\nnotice_files: $(1)\nendef\n\n$(KATI_obsolete_var combine-notice-files, To create notice files use xml-notice-rule, html-notice-rule, or text-notice-rule.)\n\n# Notice file logic isn't relevant for TARGET_BUILD_APPS\nifndef TARGET_BUILD_APPS\n\n# TODO These intermediate NOTICE.txt/NOTICE.html files should go into\n# TARGET_OUT_NOTICE_FILES now that the notice files are gathered from\n# the src subdirectory.\nkernel_notice_file := $(TARGET_OUT_NOTICE_FILES)/src/kernel.txt\n\n# Some targets get included under $(PRODUCT_OUT) for debug symbols or other\n# reasons--not to be flashed onto any device. Targets under these directories\n# need no associated notice file on the device UI.\nexclude_target_dirs := apex\n\n# target_notice_file_xml := $(TARGET_OUT_INTERMEDIATES)/NOTICE.xml\ntarget_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE.xml.gz\ninstalled_notice_html_or_xml_gz := $(TARGET_OUT)/etc/NOTICE.xml.gz\n\ntarget_vendor_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR.txt\ntarget_vendor_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR.xml.gz\ninstalled_vendor_notice_xml_gz := $(TARGET_OUT_VENDOR)/etc/NOTICE.xml.gz\n\ntarget_product_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_PRODUCT.txt\ntarget_product_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_PRODUCT.xml.gz\ninstalled_product_notice_xml_gz := $(TARGET_OUT_PRODUCT)/etc/NOTICE.xml.gz\n\ntarget_system_ext_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_EXT.txt\ntarget_system_ext_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_EXT.xml.gz\ninstalled_system_ext_notice_xml_gz := $(TARGET_OUT_SYSTEM_EXT)/etc/NOTICE.xml.gz\n\ntarget_odm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM.txt\ntarget_odm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM.xml.gz\ninstalled_odm_notice_xml_gz := $(TARGET_OUT_ODM)/etc/NOTICE.xml.gz\n\ntarget_vendor_dlkm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR_DLKM.txt\ntarget_vendor_dlkm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR_DLKM.xml.gz\ninstalled_vendor_dlkm_notice_xml_gz := $(TARGET_OUT_VENDOR_DLKM)/etc/NOTICE.xml.gz\n\ntarget_odm_dlkm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM_DLKM.txt\ntarget_odm_dlkm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM_DLKM.xml.gz\ninstalled_odm_dlkm_notice_xml_gz := $(TARGET_OUT_ODM_DLKM)/etc/NOTICE.xml.gz\n\ntarget_system_dlkm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_DLKM.txt\ntarget_system_dlkm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_DLKM.xml.gz\ninstalled_system_dlkm_notice_xml_gz := $(TARGET_OUT_SYSTEM_DLKM)/etc/NOTICE.xml.gz\n\nALL_INSTALLED_NOTICE_FILES := \\\n  $(installed_notice_html_or_xml_gz) \\\n  $(installed_vendor_notice_xml_gz) \\\n  $(installed_product_notice_xml_gz) \\\n  $(installed_system_ext_notice_xml_gz) \\\n  $(installed_odm_notice_xml_gz) \\\n  $(installed_vendor_dlkm_notice_xml_gz) \\\n  $(installed_odm_dlkm_notice_xml_gz) \\\n  $(installed_system_dlkm_notice_xml_gz) \\\n\n# $1 installed file path, e.g. out/target/product/vsoc_x86_64/system_ext/etc/NOTICE.xml.gz\ndefine is-notice-file\n$(if $(filter true,$(PRODUCT_USE_SOONG_NOTICE_XML)),, \\\n  $(if $(findstring $1,$(ALL_INSTALLED_NOTICE_FILES)),Y))\nendef\n\n# Notice files are copied to TARGET_OUT_NOTICE_FILES as a side-effect of their module\n# being built. A notice xml file must depend on all modules that could potentially\n# install a license file relevant to it.\nlicense_modules := $(ALL_DEFAULT_INSTALLED_MODULES) $(kernel_notice_file)\n# Only files copied to a system image need system image notices.\nlicense_modules := $(filter $(PRODUCT_OUT)/%,$(license_modules))\n# Phonys/fakes don't have notice files (though their deps might)\nlicense_modules := $(filter-out $(TARGET_OUT_FAKE)/%,$(license_modules))\n# testcases are not relevant to the system image.\nlicense_modules := $(filter-out $(TARGET_OUT_TESTCASES)/%,$(license_modules))\n# filesystem images: system, vendor, product, system_ext, odm, vendor_dlkm, and odm_dlkm\nlicense_modules_system := $(filter $(TARGET_OUT)/%,$(license_modules))\n# system_other is relevant to system partition.\nlicense_modules_system += $(filter $(TARGET_OUT_SYSTEM_OTHER)/%,$(license_modules))\nlicense_modules_vendor := $(filter $(TARGET_OUT_VENDOR)/%,$(license_modules))\nlicense_modules_product := $(filter $(TARGET_OUT_PRODUCT)/%,$(license_modules))\nlicense_modules_system_ext := $(filter $(TARGET_OUT_SYSTEM_EXT)/%,$(license_modules))\nlicense_modules_odm := $(filter $(TARGET_OUT_ODM)/%,$(license_modules))\nlicense_modules_vendor_dlkm := $(filter $(TARGET_OUT_VENDOR_DLKM)/%,$(license_modules))\nlicense_modules_odm_dlkm := $(filter $(TARGET_OUT_ODM_DLKM)/%,$(license_modules))\nlicense_modules_odm_dlkm := $(filter $(TARGET_OUT_SYSTEM_DLKM)/%,$(license_modules))\nlicense_modules_agg := $(license_modules_system) \\\n                       $(license_modules_vendor) \\\n                       $(license_modules_product) \\\n                       $(license_modules_system_ext) \\\n                       $(license_modules_odm) \\\n                       $(license_modules_vendor_dlkm) \\\n                       $(license_modules_odm_dlkm) \\\n                       $(license_modules_system_dlkm)\n# targets used for debug symbols only and do not get copied to the device\nlicense_modules_symbols_only := $(filter $(PRODUCT_OUT)/apex/%,$(license_modules))\n\nlicense_modules_rest := $(filter-out $(license_modules_agg),$(license_modules))\nlicense_modules_rest := $(filter-out $(license_modules_symbols_only),$(license_modules_rest))\n\n# Identify the other targets we expect to have notices for:\n# targets copied to the device but are not readable by the UI (e.g. must boot\n# into a different partition to read or don't have an associated /etc\n# directory) must have their notices built somewhere readable.\nlicense_modules_rehomed := $(filter-out $(PRODUCT_OUT)/%/%,$(license_modules_rest))  # files in root have no /etc\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/recovery/%,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/root/%,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/data/%,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/ramdisk/%,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/debug_ramdisk/%,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/vendor_ramdisk/%,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/persist/%,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/persist.img,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/system_other/%,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/kernel%,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/%.img,$(license_modules_rest))\nlicense_modules_rehomed += $(filter $(PRODUCT_OUT)/%.bin,$(license_modules_rest))\n\n# after removing targets in system images, targets reported in system images, and\n# targets used for debug symbols that do not need notices, nothing must remain.\nlicense_modules_rest := $(filter-out $(license_modules_rehomed),$(license_modules_rest))\n$(call maybe-print-list-and-error, $(license_modules_rest), \\\n        \"Targets added under $(PRODUCT_OUT)/ unaccounted for notice handling.\")\n\n# If we are building in a configuration that includes a prebuilt vendor.img, we can't\n# update its notice file, so include those notices in the system partition instead\nifdef BOARD_PREBUILT_VENDORIMAGE\nlicense_modules_system += $(license_modules_rehomed)\nsystem_xml_directories := xml_excluded_vendor_product_odm_vendor_dlkm_odm_dlkm\nsystem_notice_file_message := \"Notices for files contained in all filesystem images except vendor/system_ext/product/odm/vendor_dlkm/odm_dlkm in this directory:\"\nelse\nlicense_modules_vendor += $(license_modules_rehomed)\nsystem_xml_directories := xml_system\nsystem_notice_file_message := \"Notices for files contained in the system filesystem image in this directory:\"\nendif\n\nALL_DEFAULT_INSTALLED_MODULES += $(installed_notice_html_or_xml_gz)\n\nneed_vendor_notice:=false\nifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true)\n   need_vendor_notice:=true\nendif\n\nifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE\n   need_vendor_notice:=true\nendif\n\nifdef BUILDING_VENDOR_IMAGE\n   need_vendor_notice:=true\nendif\n\nifeq (true,$(need_vendor_notice))\nifneq (,$(installed_vendor_notice_xml_gz))\nALL_DEFAULT_INSTALLED_MODULES += $(installed_vendor_notice_xml_gz)\nendif\nendif\n\nneed_vendor_notice:=\n\nifdef BUILDING_ODM_IMAGE\nifneq (,$(installed_odm_notice_xml_gz))\nALL_DEFAULT_INSTALLED_MODULES += $(installed_odm_notice_xml_gz)\nendif\nendif\n\nifdef BUILDING_PRODUCT_IMAGE\nifneq (,$(installed_product_notice_xml_gz))\nALL_DEFAULT_INSTALLED_MODULES += $(installed_product_notice_xml_gz)\nendif\nendif\n\nifdef BUILDING_SYSTEM_EXT_IMAGE\nifneq (,$(installed_system_ext_notice_xml_gz))\nALL_DEFAULT_INSTALLED_MODULES += $(installed_system_ext_notice_xml_gz)\nendif\nendif\n\nifdef BUILDING_VENDOR_DLKM_IMAGE\nifneq (,$(installed_vendor_dlkm_notice_xml_gz)\nALL_DEFAULT_INSTALLED_MODULES += $(installed_vendor_dlkm_notice_xml_gz)\nendif\nendif\n\nifdef BUILDING_ODM_DLKM_IMAGE\nifneq (,$(installed_odm_dlkm_notice_xml_gz))\nALL_DEFAULT_INSTALLED_MODULES += $(installed_odm_dlkm_notice_xml_gz)\nendif\nendif\n\nifdef BUILDING_SYSTEM_DLKM_IMAGE\nifneq (,$(installed_system_dlkm_notice_xml_gz))\nALL_DEFAULT_INSTALLED_MODULES += $(installed_system_dlkm_notice_xml_gz)\nendif\nendif\n\nendif  # TARGET_BUILD_APPS\n\n# Presently none of the prebuilts etc. comply with policy to have a license text. Fake one here.\n$(eval $(call copy-one-file,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,$(kernel_notice_file)))\n\nifneq (,$(strip $(INSTALLED_KERNEL_TARGET)))\n$(call declare-license-metadata,$(INSTALLED_KERNEL_TARGET),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,\"Kernel\",kernel)\nendif\n\n# No matter where it gets copied from, a copied linux kernel is licensed under \"GPL 2.0 only\"\n$(eval $(call declare-copy-files-license-metadata,,:kernel,SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,kernel))\n\n\n# #################################################################\n# Targets for user images\n# #################################################################\n\n# These options tell the recovery updater/installer how to mount the partitions writebale.\n# <fstype>=<fstype_opts>[|<fstype_opts>]...\n# fstype_opts := <opt>[,<opt>]...\n#         opt := <name>[=<value>]\n# The following worked on Nexus devices with Kernel 3.1, 3.4, 3.10\nDEFAULT_TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS := ext4=max_batch_time=0,commit=1,data=ordered,barrier=1,errors=panic,nodelalloc\n\nINTERNAL_USERIMAGES_DEPS := \\\n    $(BUILD_IMAGE) \\\n    $(MKE2FS_CONF) \\\n    $(MKEXTUSERIMG)\n\n$(call declare-1p-target,$(MKE2FS_CONF),system/extras)\n\nifeq ($(TARGET_USERIMAGES_USE_F2FS),true)\nINTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG)\nendif\n\nifneq ($(filter \\\n    $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE) \\\n  ,erofs),)\nINTERNAL_USERIMAGES_DEPS += $(MKEROFS)\nifeq ($(BOARD_EROFS_USE_LEGACY_COMPRESSION),true)\nBOARD_EROFS_COMPRESSOR ?= \"lz4\"\nelse\nBOARD_EROFS_COMPRESSOR ?= \"lz4hc,9\"\nendif\nendif\n\nifneq ($(filter \\\n    $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE) \\\n    $(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE) \\\n  ,squashfs),)\nINTERNAL_USERIMAGES_DEPS += $(MKSQUASHFSUSERIMG)\nendif\n\nifeq ($(BOARD_AVB_ENABLE),true)\nINTERNAL_USERIMAGES_DEPS += $(AVBTOOL)\nendif\n\n# Get a colon-separated list of search paths.\nINTERNAL_USERIMAGES_BINARY_PATHS := $(subst $(space),:,$(sort $(dir $(INTERNAL_USERIMAGES_DEPS))))\n\nSELINUX_FC := $(call intermediates-dir-for,ETC,file_contexts.bin)/file_contexts.bin\n\nINTERNAL_USERIMAGES_DEPS += $(SELINUX_FC)\n\n# $(1) the partition name (eg system)\n# $(2) the image prop file\ndefine add-common-flags-to-image-props\n$(eval _var := $(call to-upper,$(1)))\n$(hide) echo \"$(1)_selinux_fc=$(SELINUX_FC)\" >> $(2)\n$(hide) echo \"building_$(1)_image=$(BUILDING_$(_var)_IMAGE)\" >> $(2)\nendef\n\n# $(1) the partition name (eg system)\n# $(2) the image prop file\ndefine add-common-ro-flags-to-image-props\n$(eval _var := $(call to-upper,$(1)))\n$(if $(BOARD_$(_var)IMAGE_EROFS_COMPRESSOR),$(hide) echo \"$(1)_erofs_compressor=$(BOARD_$(_var)IMAGE_EROFS_COMPRESSOR)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_EROFS_COMPRESS_HINTS),$(hide) echo \"$(1)_erofs_compress_hints=$(BOARD_$(_var)IMAGE_EROFS_COMPRESS_HINTS)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_EROFS_PCLUSTER_SIZE),$(hide) echo \"$(1)_erofs_pcluster_size=$(BOARD_$(_var)IMAGE_EROFS_PCLUSTER_SIZE)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_EROFS_BLOCKSIZE),$(hide) echo \"$(1)_erofs_blocksize=$(BOARD_$(_var)IMAGE_EROFS_BLOCKSIZE)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT),$(hide) echo \"$(1)_extfs_inode_count=$(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT),$(hide) echo \"$(1)_extfs_rsv_pct=$(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo \"$(1)_f2fs_sldc_flags=$(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_F2FS_BLOCKSIZE),$(hide) echo \"$(1)_f2fs_blocksize=$(BOARD_$(_var)IMAGE_F2FS_BLOCKSIZE)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo \"$(1)_f2fs_compress=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE),$(hide) echo \"$(1)_fs_type=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_JOURNAL_SIZE),$(hide) echo \"$(1)_journal_size=$(BOARD_$(_var)IMAGE_JOURNAL_SIZE)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE),$(hide) echo \"$(1)_reserved_size=$(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_PARTITION_SIZE),$(hide) echo \"$(1)_size=$(BOARD_$(_var)IMAGE_PARTITION_SIZE)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo \"$(1)_squashfs_block_size=$(BOARD_$(_var)IMAGE_SQUASHFS_BLOCK_SIZE)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR),$(hide) echo \"$(1)_squashfs_compressor=$(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo \"$(1)_squashfs_compressor_opt=$(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR_OPT)\" >> $(2))\n$(if $(BOARD_$(_var)IMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo \"$(1)_squashfs_disable_4k_align=$(BOARD_$(_var)IMAGE_SQUASHFS_DISABLE_4K_ALIGN)\" >> $(2))\n$(if $(PRODUCT_$(_var)_BASE_FS_PATH),$(hide) echo \"$(1)_base_fs_file=$(PRODUCT_$(_var)_BASE_FS_PATH)\" >> $(2))\n$(eval _size := $(BOARD_$(_var)IMAGE_PARTITION_SIZE))\n$(eval _reserved := $(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE))\n$(eval _headroom := $(PRODUCT_$(_var)_HEADROOM))\n$(if $(or $(_size), $(_reserved), $(_headroom)),,\n    $(hide) echo \"$(1)_disable_sparse=true\" >> $(2))\n$(call add-common-flags-to-image-props,$(1),$(2))\nendef\n\n# $(1): the path of the output dictionary file\n# $(2): a subset of \"system vendor cache userdata product system_ext oem odm vendor_dlkm odm_dlkm system_dlkm\"\n# $(3): additional \"key=value\" pairs to append to the dictionary file.\ndefine generate-image-prop-dictionary\n$(if $(filter $(2),system),\\\n    $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),$(hide) echo \"system_other_size=$(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE)\" >> $(1))\n    $(if $(PRODUCT_SYSTEM_HEADROOM),$(hide) echo \"system_headroom=$(PRODUCT_SYSTEM_HEADROOM)\" >> $(1))\n    $(call add-common-ro-flags-to-image-props,system,$(1))\n)\n$(if $(filter $(2),system_other),\\\n    $(hide) echo \"building_system_other_image=$(BUILDING_SYSTEM_OTHER_IMAGE)\" >> $(1)\n    $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),,\n        $(hide) echo \"system_other_disable_sparse=true\" >> $(1))\n)\n$(if $(filter $(2),userdata),\\\n    $(if $(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE),$(hide) echo \"userdata_fs_type=$(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE)\" >> $(1))\n    $(if $(BOARD_USERDATAIMAGE_PARTITION_SIZE),$(hide) echo \"userdata_size=$(BOARD_USERDATAIMAGE_PARTITION_SIZE)\" >> $(1))\n    $(if $(PRODUCT_FS_CASEFOLD),$(hide) echo \"needs_casefold=$(PRODUCT_FS_CASEFOLD)\" >> $(1))\n    $(if $(PRODUCT_QUOTA_PROJID),$(hide) echo \"needs_projid=$(PRODUCT_QUOTA_PROJID)\" >> $(1))\n    $(if $(PRODUCT_FS_COMPRESSION),$(hide) echo \"needs_compress=$(PRODUCT_FS_COMPRESSION)\" >> $(1))\n    $(call add-common-flags-to-image-props,userdata,$(1))\n)\n$(if $(filter $(2),cache),\\\n    $(if $(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE),$(hide) echo \"cache_fs_type=$(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE)\" >> $(1))\n    $(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),$(hide) echo \"cache_size=$(BOARD_CACHEIMAGE_PARTITION_SIZE)\" >> $(1))\n    $(call add-common-flags-to-image-props,cache,$(1))\n)\n$(if $(filter $(2),vendor),\\\n    $(call add-common-ro-flags-to-image-props,vendor,$(1))\n)\n$(if $(filter $(2),product),\\\n    $(call add-common-ro-flags-to-image-props,product,$(1))\n)\n$(if $(filter $(2),system_ext),\\\n    $(call add-common-ro-flags-to-image-props,system_ext,$(1))\n)\n$(if $(filter $(2),odm),\\\n    $(call add-common-ro-flags-to-image-props,odm,$(1))\n)\n$(if $(filter $(2),vendor_dlkm),\\\n    $(call add-common-ro-flags-to-image-props,vendor_dlkm,$(1))\n)\n$(if $(filter $(2),odm_dlkm),\\\n    $(call add-common-ro-flags-to-image-props,odm_dlkm,$(1))\n)\n$(if $(filter $(2),system_dlkm),\\\n    $(call add-common-ro-flags-to-image-props,system_dlkm,$(1))\n)\n$(if $(filter $(2),oem),\\\n    $(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo \"oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)\" >> $(1))\n    $(if $(BOARD_OEMIMAGE_JOURNAL_SIZE),$(hide) echo \"oem_journal_size=$(BOARD_OEMIMAGE_JOURNAL_SIZE)\" >> $(1))\n    $(if $(BOARD_OEMIMAGE_EXTFS_INODE_COUNT),$(hide) echo \"oem_extfs_inode_count=$(BOARD_OEMIMAGE_EXTFS_INODE_COUNT)\" >> $(1))\n    $(if $(BOARD_OEMIMAGE_EXTFS_RSV_PCT),$(hide) echo \"oem_extfs_rsv_pct=$(BOARD_OEMIMAGE_EXTFS_RSV_PCT)\" >> $(1))\n    $(call add-common-flags-to-image-props,oem,$(1))\n)\n$(hide) echo \"ext_mkuserimg=$(notdir $(MKEXTUSERIMG))\" >> $(1)\n\n$(if $(filter true,$(TARGET_USERIMAGES_USE_EXT2)),$(hide) echo \"fs_type=ext2\" >> $(1),\n  $(if $(filter true,$(TARGET_USERIMAGES_USE_EXT3)),$(hide) echo \"fs_type=ext3\" >> $(1),\n    $(if $(filter true,$(TARGET_USERIMAGES_USE_EXT4)),$(hide) echo \"fs_type=ext4\" >> $(1))))\n\n$(if $(filter true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED)),,$(hide) echo \"extfs_sparse_flag=-s\" >> $(1))\n$(if $(filter true,$(TARGET_USERIMAGES_SPARSE_EROFS_DISABLED)),,$(hide) echo \"erofs_sparse_flag=-s\" >> $(1))\n$(if $(filter true,$(TARGET_USERIMAGES_SPARSE_SQUASHFS_DISABLED)),,$(hide) echo \"squashfs_sparse_flag=-s\" >> $(1))\n$(if $(filter true,$(TARGET_USERIMAGES_SPARSE_F2FS_DISABLED)),,$(hide) echo \"f2fs_sparse_flag=-S\" >> $(1))\n$(if $(BOARD_EROFS_COMPRESSOR),$(hide) echo \"erofs_default_compressor=$(BOARD_EROFS_COMPRESSOR)\" >> $(1))\n$(if $(BOARD_EROFS_COMPRESS_HINTS),$(hide) echo \"erofs_default_compress_hints=$(BOARD_EROFS_COMPRESS_HINTS)\" >> $(1))\n$(if $(BOARD_EROFS_PCLUSTER_SIZE),$(hide) echo \"erofs_pcluster_size=$(BOARD_EROFS_PCLUSTER_SIZE)\" >> $(1))\n$(if $(BOARD_EROFS_BLOCKSIZE),$(hide) echo \"erofs_blocksize=$(BOARD_EROFS_BLOCKSIZE)\" >> $(1))\n$(if $(BOARD_EROFS_SHARE_DUP_BLOCKS),$(hide) echo \"erofs_share_dup_blocks=$(BOARD_EROFS_SHARE_DUP_BLOCKS)\" >> $(1))\n$(if $(BOARD_EROFS_USE_LEGACY_COMPRESSION),$(hide) echo \"erofs_use_legacy_compression=$(BOARD_EROFS_USE_LEGACY_COMPRESSION)\" >> $(1))\n$(if $(BOARD_EXT4_SHARE_DUP_BLOCKS),$(hide) echo \"ext4_share_dup_blocks=$(BOARD_EXT4_SHARE_DUP_BLOCKS)\" >> $(1))\n$(if $(BOARD_F2FS_BLOCKSIZE),$(hide) echo \"f2fs_blocksize=$(BOARD_F2FS_BLOCKSIZE)\" >> $(1))\n$(if $(BOARD_FLASH_LOGICAL_BLOCK_SIZE), $(hide) echo \"flash_logical_block_size=$(BOARD_FLASH_LOGICAL_BLOCK_SIZE)\" >> $(1))\n$(if $(BOARD_FLASH_ERASE_BLOCK_SIZE), $(hide) echo \"flash_erase_block_size=$(BOARD_FLASH_ERASE_BLOCK_SIZE)\" >> $(1))\n$(if $(filter eng, $(TARGET_BUILD_VARIANT)),$(hide) echo \"verity_disable=true\" >> $(1))\n$(if $(PRODUCT_SYSTEM_VERITY_PARTITION),$(hide) echo \"system_verity_block_device=$(PRODUCT_SYSTEM_VERITY_PARTITION)\" >> $(1))\n$(if $(PRODUCT_VENDOR_VERITY_PARTITION),$(hide) echo \"vendor_verity_block_device=$(PRODUCT_VENDOR_VERITY_PARTITION)\" >> $(1))\n$(if $(PRODUCT_PRODUCT_VERITY_PARTITION),$(hide) echo \"product_verity_block_device=$(PRODUCT_PRODUCT_VERITY_PARTITION)\" >> $(1))\n$(if $(PRODUCT_SYSTEM_EXT_VERITY_PARTITION),$(hide) echo \"system_ext_verity_block_device=$(PRODUCT_SYSTEM_EXT_VERITY_PARTITION)\" >> $(1))\n$(if $(PRODUCT_VENDOR_DLKM_VERITY_PARTITION),$(hide) echo \"vendor_dlkm_verity_block_device=$(PRODUCT_VENDOR_DLKM_VERITY_PARTITION)\" >> $(1))\n$(if $(PRODUCT_ODM_DLKM_VERITY_PARTITION),$(hide) echo \"odm_dlkm_verity_block_device=$(PRODUCT_ODM_DLKM_VERITY_PARTITION)\" >> $(1))\n$(if $(PRODUCT_SYSTEM_DLKM_VERITY_PARTITION),$(hide) echo \"system_dlkm_verity_block_device=$(PRODUCT_SYSTEM_DLKM_VERITY_PARTITION)\" >> $(1))\n$(if $(BOARD_AVB_ENABLE), \\\n  $(hide) echo \"avb_avbtool=$(notdir $(AVBTOOL))\" >> $(1)$(newline) \\\n  $(if $(filter $(2),system), \\\n    $(hide) echo \"avb_system_hashtree_enable=$(BOARD_AVB_ENABLE)\" >> $(1)$(newline) \\\n    $(hide) echo \"avb_system_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS)\" >> $(1)$(newline) \\\n    $(if $(BOARD_AVB_SYSTEM_KEY_PATH), \\\n      $(hide) echo \"avb_system_key_path=$(BOARD_AVB_SYSTEM_KEY_PATH)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_system_algorithm=$(BOARD_AVB_SYSTEM_ALGORITHM)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_system_rollback_index_location=$(BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION)\" >> $(1)$(newline))) \\\n  $(if $(filter $(2),system_other), \\\n    $(hide) echo \"avb_system_other_hashtree_enable=$(BOARD_AVB_ENABLE)\" >> $(1)$(newline) \\\n    $(hide) echo \"avb_system_other_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS)\" >> $(1)$(newline) \\\n    $(if $(BOARD_AVB_SYSTEM_OTHER_KEY_PATH),\\\n      $(hide) echo \"avb_system_other_key_path=$(BOARD_AVB_SYSTEM_OTHER_KEY_PATH)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_system_other_algorithm=$(BOARD_AVB_SYSTEM_OTHER_ALGORITHM)\" >> $(1)$(newline))) \\\n  $(if $(filter $(2),vendor), \\\n    $(hide) echo \"avb_vendor_hashtree_enable=$(BOARD_AVB_ENABLE)\" >> $(1)$(newline) \\\n    $(hide) echo \"avb_vendor_add_hashtree_footer_args=$(BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS)\" >> $(1)$(newline) \\\n    $(if $(BOARD_AVB_VENDOR_KEY_PATH),\\\n      $(hide) echo \"avb_vendor_key_path=$(BOARD_AVB_VENDOR_KEY_PATH)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_vendor_algorithm=$(BOARD_AVB_VENDOR_ALGORITHM)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_vendor_rollback_index_location=$(BOARD_AVB_VENDOR_ROLLBACK_INDEX_LOCATION)\" >> $(1)$(newline))) \\\n  $(if $(filter $(2),product), \\\n    $(hide) echo \"avb_product_hashtree_enable=$(BOARD_AVB_ENABLE)\" >> $(1)$(newline) \\\n    $(hide) echo \"avb_product_add_hashtree_footer_args=$(BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS)\" >> $(1)$(newline) \\\n    $(if $(BOARD_AVB_PRODUCT_KEY_PATH),\\\n      $(hide) echo \"avb_product_key_path=$(BOARD_AVB_PRODUCT_KEY_PATH)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_product_algorithm=$(BOARD_AVB_PRODUCT_ALGORITHM)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_product_rollback_index_location=$(BOARD_AVB_PRODUCT_ROLLBACK_INDEX_LOCATION)\" >> $(1)$(newline))) \\\n  $(if $(filter $(2),system_ext), \\\n    $(hide) echo \"avb_system_ext_hashtree_enable=$(BOARD_AVB_ENABLE)\" >> $(1)$(newline) \\\n    $(hide) echo \"avb_system_ext_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS)\" >> $(1)$(newline) \\\n    $(if $(BOARD_AVB_SYSTEM_EXT_KEY_PATH),\\\n      $(hide) echo \"avb_system_ext_key_path=$(BOARD_AVB_SYSTEM_EXT_KEY_PATH)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_system_ext_algorithm=$(BOARD_AVB_SYSTEM_EXT_ALGORITHM)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_system_ext_rollback_index_location=$(BOARD_AVB_SYSTEM_EXT_ROLLBACK_INDEX_LOCATION)\" >> $(1)$(newline))) \\\n  $(if $(filter $(2),odm), \\\n    $(hide) echo \"avb_odm_hashtree_enable=$(BOARD_AVB_ENABLE)\" >> $(1)$(newline) \\\n    $(hide) echo \"avb_odm_add_hashtree_footer_args=$(BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS)\" >> $(1)$(newline) \\\n    $(if $(BOARD_AVB_ODM_KEY_PATH),\\\n      $(hide) echo \"avb_odm_key_path=$(BOARD_AVB_ODM_KEY_PATH)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_odm_algorithm=$(BOARD_AVB_ODM_ALGORITHM)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_odm_rollback_index_location=$(BOARD_AVB_ODM_ROLLBACK_INDEX_LOCATION)\" >> $(1)$(newline))) \\\n  $(if $(filter $(2),vendor_dlkm), \\\n    $(hide) echo \"avb_vendor_dlkm_hashtree_enable=$(BOARD_AVB_ENABLE)\" >> $(1)$(newline) \\\n    $(hide) echo \"avb_vendor_dlkm_add_hashtree_footer_args=$(BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS)\" >> $(1)$(newline) \\\n    $(if $(BOARD_AVB_VENDOR_DLKM_KEY_PATH),\\\n      $(hide) echo \"avb_vendor_dlkm_key_path=$(BOARD_AVB_VENDOR_DLKM_KEY_PATH)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_vendor_dlkm_algorithm=$(BOARD_AVB_VENDOR_DLKM_ALGORITHM)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_vendor_dlkm_rollback_index_location=$(BOARD_AVB_VENDOR_DLKM_ROLLBACK_INDEX_LOCATION)\" >> $(1)$(newline))) \\\n  $(if $(filter $(2),odm_dlkm), \\\n    $(hide) echo \"avb_odm_dlkm_hashtree_enable=$(BOARD_AVB_ENABLE)\" >> $(1)$(newline) \\\n    $(hide) echo \"avb_odm_dlkm_add_hashtree_footer_args=$(BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS)\" >> $(1)$(newline) \\\n    $(if $(BOARD_AVB_ODM_DLKM_KEY_PATH),\\\n      $(hide) echo \"avb_odm_dlkm_key_path=$(BOARD_AVB_ODM_DLKM_KEY_PATH)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_odm_dlkm_algorithm=$(BOARD_AVB_ODM_DLKM_ALGORITHM)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_odm_dlkm_rollback_index_location=$(BOARD_AVB_ODM_DLKM_ROLLBACK_INDEX_LOCATION)\" >> $(1)$(newline))) \\\n  $(if $(filter $(2),system_dlkm), \\\n    $(hide) echo \"avb_system_dlkm_hashtree_enable=$(BOARD_AVB_ENABLE)\" >> $(1)$(newline) \\\n    $(hide) echo \"avb_system_dlkm_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS)\" >> $(1)$(newline) \\\n    $(if $(BOARD_AVB_SYSTEM_DLKM_KEY_PATH),\\\n      $(hide) echo \"avb_system_dlkm_key_path=$(BOARD_AVB_SYSTEM_DLKM_KEY_PATH)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_system_dlkm_algorithm=$(BOARD_AVB_SYSTEM_DLKM_ALGORITHM)\" >> $(1)$(newline) \\\n      $(hide) echo \"avb_system_dlkm_rollback_index_location=$(BOARD_SYSTEM_SYSTEM_DLKM_ROLLBACK_INDEX_LOCATION)\" >> $(1)$(newline))) \\\n)\n$(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)),\\\n    $(hide) echo \"recovery_as_boot=true\" >> $(1))\n$(hide) echo \"root_dir=$(TARGET_ROOT_OUT)\" >> $(1)\n$(if $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITION_SIZE)),\\\n    $(hide) echo \"use_dynamic_partition_size=true\" >> $(1))\n$(if $(USE_FIXED_TIMESTAMP_IMG_FILES)$(COPY_IMAGES_FOR_TARGET_FILES_ZIP),\\\n    $(hide) echo \"use_fixed_timestamp=true\" >> $(1))\n$(if $(3),$(hide) $(foreach kv,$(3),echo \"$(kv)\" >> $(1);))\n$(hide) sort -o $(1) $(1)\nendef\n\n# $(1): the path of the output dictionary file\n# $(2): additional \"key=value\" pairs to append to the dictionary file.\nPROP_DICTIONARY_IMAGES := oem\nifdef BUILDING_CACHE_IMAGE\n  PROP_DICTIONARY_IMAGES += cache\nendif\nifdef BUILDING_SYSTEM_IMAGE\n  PROP_DICTIONARY_IMAGES += system\nendif\nifdef BUILDING_SYSTEM_OTHER_IMAGE\n  PROP_DICTIONARY_IMAGES += system_other\nendif\nifdef BUILDING_USERDATA_IMAGE\n  PROP_DICTIONARY_IMAGES += userdata\nendif\nifdef BUILDING_VENDOR_IMAGE\n  PROP_DICTIONARY_IMAGES += vendor\nendif\nifdef BUILDING_PRODUCT_IMAGE\n  PROP_DICTIONARY_IMAGES += product\nendif\nifdef BUILDING_SYSTEM_EXT_IMAGE\n  PROP_DICTIONARY_IMAGES += system_ext\nendif\nifdef BUILDING_ODM_IMAGE\n  PROP_DICTIONARY_IMAGES += odm\nendif\nifdef BUILDING_VENDOR_DLKM_IMAGE\n  PROP_DICTIONARY_IMAGES += vendor_dlkm\nendif\nifdef BUILDING_ODM_DLKM_IMAGE\n  PROP_DICTIONARY_IMAGES += odm_dlkm\nendif\nifdef BUILDING_SYSTEM_DLKM_IMAGE\n  PROP_DICTIONARY_IMAGES += system_dlkm\nendif\ndefine generate-userimage-prop-dictionary\n  $(call generate-image-prop-dictionary,$(1),$(PROP_DICTIONARY_IMAGES),$(2))\nendef\n\n# $(1): the path of the input dictionary file, where each line has the format key=value\n# $(2): the key to look up\ndefine read-image-prop-dictionary\n$$(grep '$(2)=' $(1) | cut -f2- -d'=')\nendef\n\n# -----------------------------------------------------------------\n# Recovery image\n\n# Recovery image exists if we are building recovery, or building recovery as boot.\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_RECOVERY_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_RECOVERY_IMAGE\n\nINTERNAL_RECOVERYIMAGE_FILES := $(filter $(TARGET_RECOVERY_OUT)/%, \\\n    $(ALL_DEFAULT_INSTALLED_MODULES))\n\nINSTALLED_FILES_FILE_RECOVERY := $(PRODUCT_OUT)/installed-files-recovery.txt\nINSTALLED_FILES_JSON_RECOVERY := $(INSTALLED_FILES_FILE_RECOVERY:.txt=.json)\n\nifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\nINSTALLED_BOOTIMAGE_TARGET := $(BUILT_BOOTIMAGE_TARGET)\nendif\n\n# TODO(b/30414428): Can't depend on INTERNAL_RECOVERYIMAGE_FILES alone like other\n# INSTALLED_FILES_FILE_* rules. Because currently there're cp/rsync/rm commands in\n# build-recoveryimage-target, which would touch the files under TARGET_RECOVERY_OUT and race with\n# the call to FILELIST.\n$(INSTALLED_FILES_FILE_RECOVERY): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)\n\n$(INSTALLED_FILES_FILE_RECOVERY): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_RECOVERY)\n$(INSTALLED_FILES_FILE_RECOVERY): $(INTERNAL_RECOVERYIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_RECOVERY_ROOT_OUT) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_RECOVERY)))\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_RECOVERY)))\n\nrecovery_sepolicy := \\\n    $(TARGET_RECOVERY_ROOT_OUT)/sepolicy \\\n    $(TARGET_RECOVERY_ROOT_OUT)/plat_file_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/plat_service_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/plat_property_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/system_ext_file_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/system_ext_service_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/system_ext_property_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/vendor_file_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/vendor_service_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/vendor_property_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/odm_file_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/odm_property_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/product_file_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/product_service_contexts \\\n    $(TARGET_RECOVERY_ROOT_OUT)/product_property_contexts\n\n# Passed into rsync from non-recovery root to recovery root, to avoid overwriting recovery-specific\n# SELinux files\nIGNORE_RECOVERY_SEPOLICY := $(patsubst $(TARGET_RECOVERY_OUT)/%,--exclude=/%,$(recovery_sepolicy))\n\n# if building multiple boot images from multiple kernels, use the first kernel listed\n# for the recovery image\nrecovery_kernel := $(firstword $(INSTALLED_KERNEL_TARGET))\nrecovery_ramdisk := $(PRODUCT_OUT)/ramdisk-recovery.img\nrecovery_resources_common := bootable/recovery/res\n\n# Set recovery_density to a density bucket based on TARGET_SCREEN_DENSITY, PRODUCT_AAPT_PREF_CONFIG,\n# or mdpi, in order of preference. We support both specific buckets (e.g. xdpi) and numbers,\n# which get remapped to a bucket.\nrecovery_density := $(or $(TARGET_SCREEN_DENSITY),$(PRODUCT_AAPT_PREF_CONFIG),mdpi)\nifeq (,$(filter xxxhdpi xxhdpi xhdpi hdpi mdpi,$(recovery_density)))\nrecovery_density_value := $(patsubst %dpi,%,$(recovery_density))\n# We roughly use the medium point between the primary densities to split buckets.\n# ------160------240------320----------480------------640------\n#       mdpi     hdpi    xhdpi        xxhdpi        xxxhdpi\nrecovery_density := $(strip \\\n  $(or $(if $(filter $(shell echo $$(($(recovery_density_value) >= 560))),1),xxxhdpi),\\\n       $(if $(filter $(shell echo $$(($(recovery_density_value) >= 400))),1),xxhdpi),\\\n       $(if $(filter $(shell echo $$(($(recovery_density_value) >= 280))),1),xhdpi),\\\n       $(if $(filter $(shell echo $$(($(recovery_density_value) >= 200))),1),hdpi,mdpi)))\nendif\n\nifneq (,$(wildcard $(recovery_resources_common)-$(recovery_density)))\nrecovery_resources_common := $(recovery_resources_common)-$(recovery_density)\nelse\nrecovery_resources_common := $(recovery_resources_common)-xhdpi\nendif\n\n# Select the 18x32 font on high-density devices (xhdpi and up); and the 12x22 font on other devices.\n# Note that the font selected here can be overridden for a particular device by putting a font.png\n# in its private recovery resources.\nifneq (,$(filter xxxhdpi xxhdpi xhdpi,$(recovery_density)))\nrecovery_font := bootable/recovery/fonts/18x32.png\nelse\nrecovery_font := bootable/recovery/fonts/12x22.png\nendif\n\n\n# We will only generate the recovery background text images if the variable\n# TARGET_RECOVERY_UI_SCREEN_WIDTH is defined. For devices with xxxhdpi and xxhdpi, we set the\n# variable to the commonly used values here, if it hasn't been intialized elsewhere. While for\n# devices with lower density, they must have TARGET_RECOVERY_UI_SCREEN_WIDTH defined in their\n# BoardConfig in order to use this feature.\nifeq ($(recovery_density),xxxhdpi)\nTARGET_RECOVERY_UI_SCREEN_WIDTH ?= 1440\nelse ifeq ($(recovery_density),xxhdpi)\nTARGET_RECOVERY_UI_SCREEN_WIDTH ?= 1080\nendif\n\nifneq ($(TARGET_RECOVERY_UI_SCREEN_WIDTH),)\n# Subtracts the margin width and menu indent from the screen width; it's safe to be conservative.\nifeq ($(TARGET_RECOVERY_UI_MARGIN_WIDTH),)\n  recovery_image_width := $$(($(TARGET_RECOVERY_UI_SCREEN_WIDTH) - 10))\nelse\n  recovery_image_width := $$(($(TARGET_RECOVERY_UI_SCREEN_WIDTH) - $(TARGET_RECOVERY_UI_MARGIN_WIDTH) - 10))\nendif\n\n\nRECOVERY_INSTALLING_TEXT_FILE := $(call intermediates-dir-for,ETC,recovery_text_res)/installing_text.png\nRECOVERY_INSTALLING_SECURITY_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/installing_security_text.png\nRECOVERY_ERASING_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/erasing_text.png\nRECOVERY_ERROR_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/error_text.png\nRECOVERY_NO_COMMAND_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/no_command_text.png\n\nRECOVERY_CANCEL_WIPE_DATA_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/cancel_wipe_data_text.png\nRECOVERY_FACTORY_DATA_RESET_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/factory_data_reset_text.png\nRECOVERY_TRY_AGAIN_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/try_again_text.png\nRECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/wipe_data_confirmation_text.png\nRECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/wipe_data_menu_header_text.png\n\ngenerated_recovery_text_files := \\\n  $(RECOVERY_INSTALLING_TEXT_FILE) \\\n  $(RECOVERY_INSTALLING_SECURITY_TEXT_FILE) \\\n  $(RECOVERY_ERASING_TEXT_FILE) \\\n  $(RECOVERY_ERROR_TEXT_FILE) \\\n  $(RECOVERY_NO_COMMAND_TEXT_FILE) \\\n  $(RECOVERY_CANCEL_WIPE_DATA_TEXT_FILE) \\\n  $(RECOVERY_FACTORY_DATA_RESET_TEXT_FILE) \\\n  $(RECOVERY_TRY_AGAIN_TEXT_FILE) \\\n  $(RECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE) \\\n  $(RECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE)\n\nresource_dir := bootable/recovery/tools/recovery_l10n/res/\nresource_dir_deps := $(sort $(shell find $(resource_dir) -name *.xml -not -name .*))\nimage_generator_jar := $(HOST_OUT_JAVA_LIBRARIES)/RecoveryImageGenerator.jar\nzopflipng := $(HOST_OUT_EXECUTABLES)/zopflipng\n$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_SOURCE_FONTS := $(recovery_noto-fonts_dep) $(recovery_roboto-fonts_dep)\n$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_FONT_FILES_DIR := $(call intermediates-dir-for,ETC,recovery_font_files)\n$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RESOURCE_DIR := $(resource_dir)\n$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_IMAGE_GENERATOR_JAR := $(image_generator_jar)\n$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_ZOPFLIPNG := $(zopflipng)\n$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_IMAGE_WIDTH := $(recovery_image_width)\n$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST := \\\n  recovery_installing \\\n  recovery_installing_security \\\n  recovery_erasing \\\n  recovery_error \\\n  recovery_no_command\n$(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_WIPE_DATA_TEXT_LIST := \\\n  recovery_cancel_wipe_data \\\n  recovery_factory_data_reset \\\n  recovery_try_again \\\n  recovery_wipe_data_menu_header \\\n  recovery_wipe_data_confirmation\n$(RECOVERY_INSTALLING_TEXT_FILE): .KATI_IMPLICIT_OUTPUTS := $(filter-out $(RECOVERY_INSTALLING_TEXT_FILE),$(generated_recovery_text_files))\n$(RECOVERY_INSTALLING_TEXT_FILE): $(image_generator_jar) $(resource_dir_deps) $(recovery_noto-fonts_dep) $(recovery_roboto-fonts_dep) $(zopflipng)\n\t# Prepares the font directory.\n\t@rm -rf $(PRIVATE_RECOVERY_FONT_FILES_DIR)\n\t@mkdir -p $(PRIVATE_RECOVERY_FONT_FILES_DIR)\n\t$(foreach filename,$(PRIVATE_SOURCE_FONTS), cp $(filename) $(PRIVATE_RECOVERY_FONT_FILES_DIR) &&) true\n\t@rm -rf $(dir $@)\n\t@mkdir -p $(dir $@)\n\t$(foreach text_name,$(PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST) $(PRIVATE_RECOVERY_WIPE_DATA_TEXT_LIST), \\\n\t  $(eval output_file := $(dir $@)/$(patsubst recovery_%,%_text.png,$(text_name))) \\\n\t  $(eval center_alignment := $(if $(filter $(text_name),$(PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST)), --center_alignment)) \\\n\t  java -jar $(PRIVATE_IMAGE_GENERATOR_JAR) \\\n\t    --image_width $(PRIVATE_RECOVERY_IMAGE_WIDTH) \\\n\t    --text_name $(text_name) \\\n\t    --font_dir $(PRIVATE_RECOVERY_FONT_FILES_DIR) \\\n\t    --resource_dir $(PRIVATE_RESOURCE_DIR) \\\n\t    --output_file $(output_file) $(center_alignment) && \\\n\t  $(PRIVATE_ZOPFLIPNG) -y --iterations=1 --filters=0 $(output_file) $(output_file) > /dev/null &&) true\nelse\nRECOVERY_INSTALLING_TEXT_FILE :=\nRECOVERY_INSTALLING_SECURITY_TEXT_FILE :=\nRECOVERY_ERASING_TEXT_FILE :=\nRECOVERY_ERROR_TEXT_FILE :=\nRECOVERY_NO_COMMAND_TEXT_FILE :=\nRECOVERY_CANCEL_WIPE_DATA_TEXT_FILE :=\nRECOVERY_FACTORY_DATA_RESET_TEXT_FILE :=\nRECOVERY_TRY_AGAIN_TEXT_FILE :=\nRECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE :=\nRECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE :=\nendif # TARGET_RECOVERY_UI_SCREEN_WIDTH\n\nifndef TARGET_PRIVATE_RES_DIRS\nTARGET_PRIVATE_RES_DIRS := $(wildcard $(TARGET_DEVICE_DIR)/recovery/res)\nendif\nrecovery_resource_deps := $(shell find $(recovery_resources_common) \\\n  $(TARGET_PRIVATE_RES_DIRS) -type f -not -name \"*.bp\")\nrecovery_resource_deps += $(generated_recovery_text_files)\n\n\nifdef TARGET_RECOVERY_FSTAB\nrecovery_fstab := $(TARGET_RECOVERY_FSTAB)\nelse ifdef TARGET_RECOVERY_FSTAB_GENRULE\n# Specifies a soong genrule module that generates an fstab.\nrecovery_fstab := $(call intermediates-dir-for,ETC,$(TARGET_RECOVERY_FSTAB_GENRULE))/$(TARGET_RECOVERY_FSTAB_GENRULE)\nelse\nrecovery_fstab := $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery.fstab))\nendif\nifdef TARGET_RECOVERY_WIPE\nrecovery_wipe := $(TARGET_RECOVERY_WIPE)\nelse\nrecovery_wipe :=\nendif\n\n# Traditionally with non-A/B OTA we have:\n#   boot.img + recovery-from-boot.p + recovery-resource.dat = recovery.img.\n# recovery-resource.dat is needed only if we carry an imgdiff patch of the boot and recovery images\n# and invoke install-recovery.sh on the first boot post an OTA update.\n#\n# We no longer need that if one of the following conditions holds:\n#   a) We carry a full copy of the recovery image - no patching needed\n#      (BOARD_USES_FULL_RECOVERY_IMAGE = true);\n#   b) We build a single image that contains boot and recovery both - no recovery image to install\n#      (BOARD_USES_RECOVERY_AS_BOOT = true);\n#   c) We include the recovery DTBO image within recovery - not needing the resource file as we\n#      do bsdiff because boot and recovery will contain different number of entries\n#      (BOARD_INCLUDE_RECOVERY_DTBO = true).\n#   d) We include the recovery ACPIO image within recovery - not needing the resource file as we\n#      do bsdiff because boot and recovery will contain different number of entries\n#      (BOARD_INCLUDE_RECOVERY_ACPIO = true).\n#   e) We build a single image that contains vendor_boot and recovery both - no recovery image to\n#      install\n#      (BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT = true).\n\nifeq (,$(filter true, $(BOARD_USES_FULL_RECOVERY_IMAGE) $(BOARD_USES_RECOVERY_AS_BOOT) \\\n  $(BOARD_INCLUDE_RECOVERY_DTBO) $(BOARD_INCLUDE_RECOVERY_ACPIO) \\\n  $(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)))\n# Named '.dat' so we don't attempt to use imgdiff for patching it.\nRECOVERY_RESOURCE_ZIP := $(TARGET_OUT_VENDOR)/etc/recovery-resource.dat\nALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_RESOURCE_ZIP)\nelse\nRECOVERY_RESOURCE_ZIP :=\nendif\n\nINSTALLED_RECOVERY_BUILD_PROP_TARGET := $(TARGET_RECOVERY_ROOT_OUT)/prop.default\n\n$(INSTALLED_RECOVERY_BUILD_PROP_TARGET): PRIVATE_RECOVERY_UI_PROPERTIES := \\\n    TARGET_RECOVERY_UI_ANIMATION_FPS:animation_fps \\\n    TARGET_RECOVERY_UI_MARGIN_HEIGHT:margin_height \\\n    TARGET_RECOVERY_UI_MARGIN_WIDTH:margin_width \\\n    TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS:menu_unusable_rows \\\n    TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE:progress_bar_baseline \\\n    TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD:touch_low_threshold \\\n    TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD:touch_high_threshold \\\n    TARGET_RECOVERY_UI_VR_STEREO_OFFSET:vr_stereo_offset \\\n    TARGET_RECOVERY_UI_BRIGHTNESS_FILE:brightness_file \\\n    TARGET_RECOVERY_UI_MAX_BRIGHTNESS_FILE:max_brightness_file \\\n    TARGET_RECOVERY_UI_BRIGHTNESS_NORMAL:brightness_normal_percent \\\n    TARGET_RECOVERY_UI_BRIGHTNESS_DIMMED:brightness_dimmed_percent\n\n# Parses the given list of build variables and writes their values as build properties if defined.\n# For example, if a target defines `TARGET_RECOVERY_UI_MARGIN_HEIGHT := 100`,\n# `ro.recovery.ui.margin_height=100` will be appended to the given output file.\n# $(1): Map from the build variable names to property names\n# $(2): Output file\ndefine append-recovery-ui-properties\necho \"#\" >> $(2)\necho \"# RECOVERY UI BUILD PROPERTIES\" >> $(2)\necho \"#\" >> $(2)\n$(foreach prop,$(1), \\\n    $(eval _varname := $(call word-colon,1,$(prop))) \\\n    $(eval _propname := $(call word-colon,2,$(prop))) \\\n    $(eval _value := $($(_varname))) \\\n    $(if $(_value), \\\n        echo ro.recovery.ui.$(_propname)=$(_value) >> $(2) &&)) true\nendef\n\n$(INSTALLED_RECOVERY_BUILD_PROP_TARGET): \\\n\t    $(INSTALLED_BUILD_PROP_TARGET) \\\n\t    $(INSTALLED_VENDOR_BUILD_PROP_TARGET) \\\n\t    $(INSTALLED_ODM_BUILD_PROP_TARGET) \\\n\t    $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) \\\n\t    $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET)\n\t@echo \"Target recovery buildinfo: $@\"\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) rm -f $@\n\t$(hide) cat $(INSTALLED_BUILD_PROP_TARGET) >> $@\n\t$(hide) cat $(INSTALLED_VENDOR_BUILD_PROP_TARGET) >> $@\n\t$(hide) cat $(INSTALLED_ODM_BUILD_PROP_TARGET) >> $@\n\t$(hide) cat $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) >> $@\n\t$(hide) cat $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET) >> $@\n\t$(call append-recovery-ui-properties,$(PRIVATE_RECOVERY_UI_PROPERTIES),$@)\n\n$(call declare-1p-target,$(INSTALLED_RECOVERY_BUILD_PROP_TARGET),build)\n$(call declare-license-deps,$(INSTALLED_RECOVERY_BUILD_PROP_TARGET),\\\n    $(INSTALLED_BUILD_PROP_TARGET) $(INSTALLED_VENDOR_BUILD_PROP_TARGET) $(INSTALLED_ODM_BUILD_PROP_TARGET) \\\n    $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET))\n\n# Only install boot/etc/build.prop to recovery image on recovery_as_boot.\n# On device with dedicated recovery partition, the file should come from the boot\n# ramdisk.\nifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT))\nINSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET := $(TARGET_RECOVERY_ROOT_OUT)/$(RAMDISK_BUILD_PROP_REL_PATH)\n$(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET): $(INSTALLED_RAMDISK_BUILD_PROP_TARGET)\n\t$(copy-file-to-target)\n\n$(call declare-1p-target,$(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET),build)\n$(call declare-license-deps,$(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET),$(INSTALLED_RAMDISK_BUILD_PROP_TARGET))\nendif\n\nINTERNAL_RECOVERYIMAGE_ARGS := --ramdisk $(recovery_ramdisk)\n\nifneq (truetrue,$(strip $(BUILDING_VENDOR_BOOT_IMAGE))$(strip $(BOARD_USES_RECOVERY_AS_BOOT)))\nINTERNAL_RECOVERYIMAGE_ARGS += $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET))\n# Assumes this has already been stripped\nifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE))\nifdef INTERNAL_KERNEL_CMDLINE\n  INTERNAL_RECOVERYIMAGE_ARGS += --cmdline \"$(INTERNAL_KERNEL_CMDLINE)\"\nendif # INTERNAL_KERNEL_CMDLINE != \"\"\nendif # BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE != true\nifdef BOARD_KERNEL_BASE\n  INTERNAL_RECOVERYIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)\nendif\nifdef BOARD_KERNEL_PAGESIZE\n  INTERNAL_RECOVERYIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)\nendif\nifdef BOARD_INCLUDE_RECOVERY_DTBO\nifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE\n  INTERNAL_RECOVERYIMAGE_ARGS += --recovery_dtbo $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE)\nelse\n  INTERNAL_RECOVERYIMAGE_ARGS += --recovery_dtbo $(BOARD_PREBUILT_DTBOIMAGE)\nendif\nendif # BOARD_INCLUDE_RECOVERY_DTBO\nifdef BOARD_INCLUDE_RECOVERY_ACPIO\n  INTERNAL_RECOVERYIMAGE_ARGS += --recovery_acpio $(BOARD_RECOVERY_ACPIO)\nendif\nifdef BOARD_INCLUDE_DTB_IN_BOOTIMG\n  INTERNAL_RECOVERYIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET)\nendif\nendif # (BUILDING_VENDOR_BOOT_IMAGE and BOARD_USES_RECOVERY_AS_BOOT)\nifndef BOARD_RECOVERY_MKBOOTIMG_ARGS\n  BOARD_RECOVERY_MKBOOTIMG_ARGS := $(BOARD_MKBOOTIMG_ARGS)\nendif\n\n$(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP): $(MKBOOTFS) $(COMPRESSION_COMMAND_DEPS) \\\n\t    $(INTERNAL_ROOT_FILES) \\\n\t    $(INSTALLED_RAMDISK_TARGET) \\\n\t    $(INTERNAL_RECOVERYIMAGE_FILES) \\\n\t    $(recovery_sepolicy) \\\n\t    $(INSTALLED_2NDBOOTLOADER_TARGET) \\\n\t    $(INSTALLED_RECOVERY_BUILD_PROP_TARGET) \\\n\t    $(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET) \\\n\t    $(recovery_resource_deps) \\\n\t    $(recovery_fstab)\n\t# Making recovery image\n\tmkdir -p $(TARGET_RECOVERY_OUT)\n\tmkdir -p $(TARGET_RECOVERY_ROOT_OUT)/sdcard $(TARGET_RECOVERY_ROOT_OUT)/tmp\n\t# Copying baseline ramdisk...\n\t# Use rsync because \"cp -Rf\" fails to overwrite broken symlinks on Mac.\n\trsync -a --exclude=sdcard $(IGNORE_RECOVERY_SEPOLICY) $(IGNORE_CACHE_LINK) $(TARGET_ROOT_OUT) $(TARGET_RECOVERY_OUT)\n\t# Modifying ramdisk contents...\n\tln -sf /system/bin/init $(TARGET_RECOVERY_ROOT_OUT)/init\n\t# Removes $(TARGET_RECOVERY_ROOT_OUT)/init*.rc EXCEPT init.recovery*.rc.\n\tfind $(TARGET_RECOVERY_ROOT_OUT) -maxdepth 1 -name 'init*.rc' -type f -not -name \"init.recovery.*.rc\" | xargs rm -f\n\tcp $(TARGET_ROOT_OUT)/init.recovery.*.rc $(TARGET_RECOVERY_ROOT_OUT)/ 2> /dev/null || true # Ignore error when the src file doesn't exist.\n\tmkdir -p $(TARGET_RECOVERY_ROOT_OUT)/res\n\trm -rf $(TARGET_RECOVERY_ROOT_OUT)/res/*\n\tcp -rf $(recovery_resources_common)/* $(TARGET_RECOVERY_ROOT_OUT)/res\n\t$(foreach recovery_text_file,$(generated_recovery_text_files), \\\n\t  cp -rf $(recovery_text_file) $(TARGET_RECOVERY_ROOT_OUT)/res/images/ &&) true\n\tcp -f $(recovery_font) $(TARGET_RECOVERY_ROOT_OUT)/res/images/font.png\n\t$(foreach item,$(TARGET_PRIVATE_RES_DIRS), \\\n\t  cp -rf $(item) $(TARGET_RECOVERY_ROOT_OUT)/$(newline))\n\t$(foreach item,$(recovery_fstab), \\\n\t  cp -f $(item) $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.fstab)\n\t$(if $(strip $(recovery_wipe)), \\\n\t  cp -f $(recovery_wipe) $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.wipe)\n\tln -sf prop.default $(TARGET_RECOVERY_ROOT_OUT)/default.prop\n\t# Silence warnings in first_stage_console.\n\ttouch $(TARGET_RECOVERY_ROOT_OUT)/linkerconfig/ld.config.txt\n\t$(BOARD_RECOVERY_IMAGE_PREPARE)\n\t$(hide) touch $@\n\n$(recovery_ramdisk): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)\n\t$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RECOVERY_ROOT_OUT) | $(COMPRESSION_COMMAND) > $(recovery_ramdisk)\n\n# $(1): output file\n# $(2): optional kernel file\ndefine build-recoveryimage-target\n  $(MKBOOTIMG) $(if $(strip $(2)),--kernel $(strip $(2))) $(INTERNAL_RECOVERYIMAGE_ARGS) \\\n               $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \\\n               $(BOARD_RECOVERY_MKBOOTIMG_ARGS) --output $(1)\n  $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)), \\\n    $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot))), \\\n    $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE))))\n  $(if $(filter true,$(BOARD_AVB_ENABLE)), \\\n    $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)), \\\n      $(AVBTOOL) add_hash_footer --image $(1) $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),boot)) --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS),\\\n      $(AVBTOOL) add_hash_footer --image $(1) $(call get-partition-size-argument,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)) --partition_name recovery $(INTERNAL_AVB_RECOVERY_SIGNING_ARGS) $(BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS)))\nendef\n\nrecoveryimage-deps := $(MKBOOTIMG) $(recovery_ramdisk) $(recovery_kernel)\nifeq (true,$(BOARD_AVB_ENABLE))\n  recoveryimage-deps += $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH)\nendif\nifdef BOARD_INCLUDE_RECOVERY_DTBO\n  ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE\n    recoveryimage-deps += $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE)\n  else\n    recoveryimage-deps += $(BOARD_PREBUILT_DTBOIMAGE)\n  endif\nendif\nifdef BOARD_INCLUDE_RECOVERY_ACPIO\n  recoveryimage-deps += $(BOARD_RECOVERY_ACPIO)\nendif\nifdef BOARD_INCLUDE_DTB_IN_BOOTIMG\n  recoveryimage-deps += $(INSTALLED_DTBIMAGE_TARGET)\nendif\n\nifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET), $(eval $(call add-dependency,$(b),$(call bootimage-to-kernel,$(b)))))\n$(INSTALLED_BOOTIMAGE_TARGET): $(recoveryimage-deps)\n\t$(call pretty,\"Target boot image from recovery: $@\")\n\t$(call build-recoveryimage-target, $@, $(PRODUCT_OUT)/$(subst .img,,$(subst boot,kernel,$(notdir $@))))\n\n$(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,\"Boot Image\",bool)\n$(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(recoveryimage-deps),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_BOOTIMAGE_TARGET)\nendif # BOARD_USES_RECOVERY_AS_BOOT\n\n$(INSTALLED_RECOVERYIMAGE_TARGET): $(recoveryimage-deps)\n\t$(call build-recoveryimage-target, $@, \\\n\t  $(if $(filter true, $(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)),, $(recovery_kernel)))\n\nifdef RECOVERY_RESOURCE_ZIP\n$(RECOVERY_RESOURCE_ZIP): $(INSTALLED_RECOVERYIMAGE_TARGET) | $(ZIPTIME)\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) find $(TARGET_RECOVERY_ROOT_OUT)/res -type f | sort | zip -0qrjX $@ -@\n\t$(remove-timestamps-from-package)\nendif\n\n\n$(call declare-1p-container,$(INSTALLED_RECOVERYIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_RECOVERYIMAGE_TARGET),$(recoveryimage-deps),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_RECOVERYIMAGE_TARGET)\n\n.PHONY: recoveryimage-nodeps\nrecoveryimage-nodeps:\n\t@echo \"make $@: ignoring dependencies\"\n\t$(call build-recoveryimage-target, $(INSTALLED_RECOVERYIMAGE_TARGET), \\\n\t  $(if $(filter true, $(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)),, $(recovery_kernel)))\n\nelse # BUILDING_RECOVERY_IMAGE\nRECOVERY_RESOURCE_ZIP :=\nendif # BUILDING_RECOVERY_IMAGE\n\n.PHONY: recoveryimage\nrecoveryimage: $(INSTALLED_RECOVERYIMAGE_TARGET) $(RECOVERY_RESOURCE_ZIP)\n\nifneq ($(BOARD_NAND_PAGE_SIZE),)\n$(error MTD device is no longer supported and thus BOARD_NAND_PAGE_SIZE is deprecated.)\nendif\n\nifneq ($(BOARD_NAND_SPARE_SIZE),)\n$(error MTD device is no longer supported and thus BOARD_NAND_SPARE_SIZE is deprecated.)\nendif\n\nrecovery_intermediates := $(call intermediates-dir-for,PACKAGING,recovery)\n$(eval $(call write-partition-file-list,$(recovery_intermediates)/file_list.txt,$(TARGET_RECOVERY_OUT),$(INTERNAL_RECOVERYIMAGE_FILES)))\n\n\n# -----------------------------------------------------------------\n# Build debug ramdisk and debug boot image.\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_DEBUG_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifneq ($(BUILDING_DEBUG_BOOT_IMAGE)$(BUILDING_DEBUG_VENDOR_BOOT_IMAGE),)\n\nINTERNAL_DEBUG_RAMDISK_FILES := $(filter $(TARGET_DEBUG_RAMDISK_OUT)/%, \\\n    $(ALL_DEFAULT_INSTALLED_MODULES))\n\n# Directories to be picked into the debug ramdisk.\n# As these directories are all merged into one single cpio archive, the order\n# matters. If there are multiple files with the same pathname, then the last one\n# wins.\n#\n# ramdisk-debug.img will merge the content from either ramdisk.img or\n# ramdisk-recovery.img, depending on whether BOARD_USES_RECOVERY_AS_BOOT\n# is set or not.\nifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n  INTERNAL_DEBUG_RAMDISK_SRC_DIRS := $(TARGET_RECOVERY_ROOT_OUT)\n  INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET := $(recovery_ramdisk)\nelse  # BOARD_USES_RECOVERY_AS_BOOT == true\n  INTERNAL_DEBUG_RAMDISK_SRC_DIRS := $(TARGET_RAMDISK_OUT)\n  INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET := $(INSTALLED_RAMDISK_TARGET)\nendif # BOARD_USES_RECOVERY_AS_BOOT != true\n\nINTERNAL_DEBUG_RAMDISK_SRC_DIRS += $(TARGET_DEBUG_RAMDISK_OUT)\nINTERNAL_DEBUG_RAMDISK_SRC_DEPS := $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET) $(INTERNAL_DEBUG_RAMDISK_FILES)\n\n# INSTALLED_FILES_FILE_DEBUG_RAMDISK would ensure TARGET_DEBUG_RAMDISK_OUT is created.\nINSTALLED_FILES_FILE_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-ramdisk-debug.txt\nINSTALLED_FILES_JSON_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK:.txt=.json)\n$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_DEBUG_RAMDISK)\n$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(INTERNAL_DEBUG_RAMDISK_SRC_DEPS)\n$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo \"Installed file list: $@\"\n\t$(hide) rm -f $@\n\t$(hide) mkdir -p $(dir $@) $(TARGET_DEBUG_RAMDISK_OUT)\n\ttouch $(TARGET_DEBUG_RAMDISK_OUT)/force_debuggable\n\t$(FILESLIST) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_DEBUG_RAMDISK)))\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_DEBUG_RAMDISK)))\n\nifdef BUILDING_DEBUG_BOOT_IMAGE\n\n# -----------------------------------------------------------------\n# the debug ramdisk, which is the original ramdisk plus additional\n# files: force_debuggable, adb_debug.prop and userdebug sepolicy.\n# When /force_debuggable is present, /init will load userdebug sepolicy\n# and property files to allow adb root, if the device is unlocked.\nINSTALLED_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-debug.img\n\n$(INSTALLED_DEBUG_RAMDISK_TARGET): $(INSTALLED_FILES_FILE_DEBUG_RAMDISK)\n$(INSTALLED_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)\n\t@echo \"Target debug ramdisk: $@\"\n\t$(hide) rm -f $@\n\t$(hide) mkdir -p $(dir $@)\n\t$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@\n\n$(call declare-1p-container,$(INSTALLED_DEBUG_RAMDISK_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INSTALLED_RAMDISK_TARGET),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_DEBUG_RAMDISK_TARGET)\n\n.PHONY: ramdisk_debug-nodeps\nramdisk_debug-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)\n\t@echo \"make $@: ignoring dependencies\"\n\t$(hide) rm -f $(INSTALLED_DEBUG_RAMDISK_TARGET)\n\t$(hide) mkdir -p $(dir $(INSTALLED_DEBUG_RAMDISK_TARGET)) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS)\n\t$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $(INSTALLED_DEBUG_RAMDISK_TARGET)\n\n# -----------------------------------------------------------------\n# the boot-debug.img, which is the kernel plus ramdisk-debug.img\n#\n# Note: it's intentional to skip signing for boot-debug.img, because it\n# can only be used if the device is unlocked with verification error.\nifneq ($(strip $(BOARD_KERNEL_BINARIES)),)\n  INSTALLED_DEBUG_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot-debug,$(BOARD_KERNEL_BINARIES)), \\\n         $(PRODUCT_OUT)/$(k).img)\nelse\n  INSTALLED_DEBUG_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot-debug.img\nendif\n\n# Replace ramdisk.img in $(MKBOOTIMG) ARGS with ramdisk-debug.img to build boot-debug.img\n$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(INSTALLED_DEBUG_RAMDISK_TARGET)\nifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n  INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INTERNAL_RECOVERYIMAGE_ARGS))\nelse\n  INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INTERNAL_BOOTIMAGE_ARGS))\nendif\n\n# If boot.img is chained but boot-debug.img is not signed, libavb in bootloader\n# will fail to find valid AVB metadata from the end of /boot, thus stop booting.\n# Using a test key to sign boot-debug.img to continue booting with the mismatched\n# public key, if the device is unlocked.\nifneq ($(BOARD_AVB_BOOT_KEY_PATH),)\n$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_BOOT_TEST_KEY_PATH)\nendif\n\nBOARD_AVB_BOOT_TEST_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem\nINTERNAL_AVB_BOOT_TEST_SIGNING_ARGS := --algorithm SHA256_RSA2048 --key $(BOARD_AVB_BOOT_TEST_KEY_PATH)\n# $(1): the bootimage to sign\n# $(2): boot image variant (boot, boot-debug, boot-test-harness)\ndefine test-key-sign-bootimage\n$(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),$(2))))\n$(AVBTOOL) add_hash_footer \\\n  --image $(1) \\\n  $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),$(2)))\\\n  --partition_name boot $(INTERNAL_AVB_BOOT_TEST_SIGNING_ARGS) \\\n  $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)\n$(call assert-max-image-size,$(1),$(call get-bootimage-partition-size,$(1),$(2)))\nendef\n\n# $(1): output file\ndefine build-debug-bootimage-target\n  $(MKBOOTIMG) --kernel $(PRODUCT_OUT)/$(subst .img,,$(subst boot-debug,kernel,$(notdir $(1)))) \\\n    $(INTERNAL_DEBUG_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \\\n    $(BOARD_MKBOOTIMG_ARGS) --output $1\n  $(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$1,boot-debug))\nendef\n\n# Depends on original boot.img and ramdisk-debug.img, to build the new boot-debug.img\n$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(AVBTOOL)\n\t$(call pretty,\"Target boot debug image: $@\")\n\t$(call build-debug-bootimage-target, $@)\n\n$(call declare-container-license-metadata,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,\"Boot Image\",boot)\n$(call declare-container-license-deps,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(INSTALLED_BOOTIMAGE_TARGET),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)\n\n.PHONY: bootimage_debug-nodeps\nbootimage_debug-nodeps: $(MKBOOTIMG) $(AVBTOOL)\n\techo \"make $@: ignoring dependencies\"\n\t$(foreach b,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(call build-debug-bootimage-target,$b))\n\nendif # BUILDING_DEBUG_BOOT_IMAGE\n\n# -----------------------------------------------------------------\n# vendor debug ramdisk\n# Combines vendor ramdisk files and debug ramdisk files to build the vendor debug ramdisk.\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE\n\nINTERNAL_VENDOR_DEBUG_RAMDISK_FILES := $(filter $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)/%, \\\n    $(ALL_DEFAULT_INSTALLED_MODULES))\n\n# The debug vendor ramdisk combines vendor ramdisk and debug ramdisk.\nINTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS := $(TARGET_VENDOR_RAMDISK_OUT)\nINTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS := $(INTERNAL_VENDOR_RAMDISK_TARGET)\n\n# Exclude recovery files in the default vendor ramdisk if including a standalone\n# recovery ramdisk in vendor_boot.\nifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))\n  ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))\n    INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS += $(TARGET_RECOVERY_ROOT_OUT)\n    INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS += $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)\n  endif # BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT != true\nendif # BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT == true\n\nINTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS += $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) $(TARGET_DEBUG_RAMDISK_OUT)\nINTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS += $(INTERNAL_VENDOR_DEBUG_RAMDISK_FILES) $(INSTALLED_FILES_FILE_DEBUG_RAMDISK)\n\n# INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK would ensure TARGET_VENDOR_DEBUG_RAMDISK_OUT is created.\nINSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk-debug.txt\nINSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK:.txt=.json)\n$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK)\n$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS)\n$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo \"Installed file list: $@\"\n\t$(hide) rm -f $@\n\t$(hide) mkdir -p $(dir $@) $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)\n\t$(FILESLIST) $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK))\n$(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK))\n\nINTERNAL_VENDOR_DEBUG_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-debug)/vendor_ramdisk-debug.cpio$(RAMDISK_EXT)\n\n$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK)\n$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)\n\t$(hide) rm -f $@\n\t$(hide) mkdir -p $(dir $@)\n\t$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@\n\nINSTALLED_VENDOR_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk-debug.img\n$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET): $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET)\n\t@echo \"Target debug vendor ramdisk: $@\"\n\t$(copy-file-to-target)\n\n$(call declare-1p-container,$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET),$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET),$(PRODUCT_OUT)/:/)\n\nVENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET)\n\n# -----------------------------------------------------------------\n# vendor_boot-debug.img.\nINSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot-debug.img\n\n# The util to sign vendor_boot-debug.img with a test key.\nBOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem\nINTERNAL_AVB_VENDOR_BOOT_TEST_SIGNING_ARGS := --algorithm SHA256_RSA2048 --key $(BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH)\n# $(1): the vendor bootimage to sign\ndefine test-key-sign-vendor-bootimage\n$(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)))\n$(AVBTOOL) add_hash_footer \\\n  --image $(1) \\\n  $(call get-partition-size-argument,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) \\\n  --partition_name vendor_boot $(INTERNAL_AVB_VENDOR_BOOT_TEST_SIGNING_ARGS) \\\n  $(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS)\n$(call assert-max-image-size,$(1),$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))\nendef\n\nifneq ($(BOARD_AVB_VENDOR_BOOT_KEY_PATH),)\n$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH)\nendif\n\n# Depends on vendor_boot.img and vendor-ramdisk-debug.cpio.gz to build the new vendor_boot-debug.img\n$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET)\n$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS)\n\t$(call pretty,\"Target vendor_boot debug image: $@\")\n\t$(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@\n\t$(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))\n\t$(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@))\n\n$(call declare-1p-container,$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET),$(PRODUCT_OUT)/:/)\n\nVENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET)\n\nendif # BUILDING_DEBUG_VENDOR_BOOT_IMAGE\n\n# Appends a few test harness specific properties into the adb_debug.prop.\nADDITIONAL_TEST_HARNESS_PROPERTIES := ro.audio.silent=1\nADDITIONAL_TEST_HARNESS_PROPERTIES += ro.test_harness=1\n\nINTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET := $(strip $(filter $(TARGET_DEBUG_RAMDISK_OUT)/adb_debug.prop,$(INTERNAL_DEBUG_RAMDISK_FILES)))\nINTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET := $(TARGET_TEST_HARNESS_RAMDISK_OUT)/adb_debug.prop\n$(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET): $(INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET)\n\t$(hide) rm -f $@\n\t$(hide) mkdir -p $(dir $@)\nifdef INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET\n\t$(hide) cp $(INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET) $@\nendif\n\t$(hide) echo \"\" >> $@\n\t$(hide) echo \"#\" >> $@\n\t$(hide) echo \"# ADDITIONAL TEST HARNESS PROPERTIES\" >> $@\n\t$(hide) echo \"#\" >> $@\n\t$(hide) $(foreach line,$(ADDITIONAL_TEST_HARNESS_PROPERTIES), \\\n\t          echo \"$(line)\" >> $@;)\n\n$(call declare-1p-target,$(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET))\n\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_TEST_HARNESS_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nINTERNAL_TEST_HARNESS_RAMDISK_FILES := $(filter $(TARGET_TEST_HARNESS_RAMDISK_OUT)/%, \\\n    $(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET) \\\n    $(ALL_DEFAULT_INSTALLED_MODULES))\n\n# The order is important here. The test harness ramdisk staging directory has to\n# come last so that it can override the adb_debug.prop in the debug ramdisk\n# staging directory.\nINTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS := $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) $(TARGET_TEST_HARNESS_RAMDISK_OUT)\nINTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES)\n\nifdef BUILDING_DEBUG_BOOT_IMAGE\n\n# -----------------------------------------------------------------\n# The test harness ramdisk, which is based off debug_ramdisk, plus a\n# few additional test-harness-specific properties in adb_debug.prop.\nINSTALLED_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-test-harness.img\n\n$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS)\n$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)\n\t@echo \"Target test harness ramdisk: $@\"\n\t$(hide) rm -f $@\n\t$(hide) mkdir -p $(dir $@)\n\t$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@\n\n$(call declare-1p-container,$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)\n\n.PHONY: ramdisk_test_harness-nodeps\nramdisk_test_harness-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)\n\t@echo \"make $@: ignoring dependencies\"\n\t$(hide) rm -f $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)\n\t$(hide) mkdir -p $(dir $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS)\n\t$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)\n\n# -----------------------------------------------------------------\n# the boot-test-harness.img, which is the kernel plus ramdisk-test-harness.img\n#\n# Note: it's intentional to skip signing for boot-test-harness.img, because it\n# can only be used if the device is unlocked with verification error.\nifneq ($(strip $(BOARD_KERNEL_BINARIES)),)\n  INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot-test-harness,$(BOARD_KERNEL_BINARIES)), \\\n    $(PRODUCT_OUT)/$(k).img)\nelse\n  INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot-test-harness.img\nendif\n\n# Replace ramdisk-debug.img in $(MKBOOTIMG) ARGS with ramdisk-test-harness.img to build boot-test-harness.img\n$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)\nINTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS := $(subst $(INSTALLED_DEBUG_RAMDISK_TARGET),$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_DEBUG_BOOTIMAGE_ARGS))\n\n# If boot.img is chained but boot-test-harness.img is not signed, libavb in bootloader\n# will fail to find valid AVB metadata from the end of /boot, thus stop booting.\n# Using a test key to sign boot-test-harness.img to continue booting with the mismatched\n# public key, if the device is unlocked.\nifneq ($(BOARD_AVB_BOOT_KEY_PATH),)\n$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_BOOT_TEST_KEY_PATH)\nendif\n\n# $(1): output file\ndefine build-boot-test-harness-target\n  $(MKBOOTIMG) --kernel $(PRODUCT_OUT)/$(subst .img,,$(subst boot-test-harness,kernel,$(notdir $(1)))) \\\n    $(INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \\\n    $(BOARD_MKBOOTIMG_ARGS) --output $@\n  $(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$@,boot-test-harness))\nendef\n\n# Build the new boot-test-harness.img, based on boot-debug.img and ramdisk-test-harness.img.\n$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) $(AVBTOOL)\n\t$(call pretty,\"Target boot test harness image: $@\")\n\t$(call build-boot-test-harness-target,$@)\n\n$(call declare-1p-container,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET)\n\n.PHONY: bootimage_test_harness-nodeps\nbootimage_test_harness-nodeps: $(MKBOOTIMG) $(AVBTOOL)\n\techo \"make $@: ignoring dependencies\"\n\t$(foreach b,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),$(call build-boot-test-harness-target,$b))\n\nendif # BUILDING_DEBUG_BOOT_IMAGE\n\n# -----------------------------------------------------------------\n# vendor test harness ramdisk, which is a vendor ramdisk combined with\n# a test harness ramdisk.\nifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE\n\nINTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-test-harness)/vendor_ramdisk-test-harness.cpio$(RAMDISK_EXT)\n\n# The order is important here. The test harness ramdisk staging directory has to\n# come last so that it can override the adb_debug.prop in the debug ramdisk\n# staging directory.\nINTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DIRS := $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) $(TARGET_TEST_HARNESS_RAMDISK_OUT)\nINTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DEPS := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES)\n\n$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DEPS)\n$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)\n\t$(hide) rm -f $@\n\t$(hide) mkdir -p $(dir $@)\n\t$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@\n\nINSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk-test-harness.img\n$(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET)\n\t@echo \"Target test harness vendor ramdisk: $@\"\n\t$(copy-file-to-target)\n\n$(call declare-1p-container,$(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET),$(PRODUCT_OUT)/:/)\n\nVENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET)\n\n# -----------------------------------------------------------------\n# vendor_boot-test-harness.img.\nINSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot-test-harness.img\n\nifneq ($(BOARD_AVB_VENDOR_BOOT_KEY_PATH),)\n$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH)\nendif\n\n# Depends on vendor_boot.img and vendor_ramdisk-test-harness.cpio$(RAMDISK_EXT) to build the new vendor_boot-test-harness.img\n$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET)\n$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET)\n$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS)\n\t$(call pretty,\"Target vendor_boot test harness image: $@\")\n\t$(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@\n\t$(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))\n\t$(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@))\n\n$(call declare-1p-container,$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET),$(PRODUCT_OUT)/:/)\n\nVENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET)\n\nendif # BUILDING_DEBUG_VENDOR_BOOT_IMAGE\n\nendif # BUILDING_DEBUG_BOOT_IMAGE || BUILDING_DEBUG_VENDOR_BOOT_IMAGE\n\nPARTITION_COMPAT_SYMLINKS :=\n# Creates a compatibility symlink between two partitions, e.g. /system/vendor to /vendor\n# $1: from location (e.g $(TARGET_OUT)/vendor)\n# $2: destination location (e.g. /vendor)\n# $3: partition image name (e.g. vendor.img)\ndefine create-partition-compat-symlink\n$(eval \\\n$1:\n\t@echo Symlink $(patsubst $(PRODUCT_OUT)/%,%,$1) to $2\n\tmkdir -p $(dir $1)\n\tif [ -d $1 ] && [ ! -h $1 ]; then \\\n\t  echo 'Non-symlink $1 detected!' 1>&2; \\\n\t  echo 'You cannot install files to $1 while building a separate $3!' 1>&2; \\\n\t  exit 1; \\\n\tfi\n\tln -sfn $2 $1\n)\n$(eval PARTITION_COMPAT_SYMLINKS += $1)\n$1\nendef\n\n\n# FSVerity metadata generation\n# Generate fsverity metadata files (.fsv_meta) and build manifest\n# (<partition>/etc/security/fsverity/BuildManifest<suffix>.apk) BEFORE filtering systemimage,\n# vendorimage, odmimage, productimage files below.\nifeq ($(PRODUCT_FSVERITY_GENERATE_METADATA),true)\n\nfsverity-metadata-targets-patterns := \\\n  $(TARGET_OUT)/framework/% \\\n  $(TARGET_OUT)/etc/boot-image.prof \\\n  $(TARGET_OUT)/etc/dirty-image-objects \\\n  $(TARGET_OUT)/etc/preloaded-classes \\\n  $(TARGET_OUT)/etc/classpaths/%.pb \\\n\nifdef BUILDING_SYSTEM_EXT_IMAGE\nfsverity-metadata-targets-patterns += $(TARGET_OUT_SYSTEM_EXT)/framework/%\nendif\n\n# Generate fsv_meta\nfsverity-metadata-targets := $(sort $(filter \\\n  $(fsverity-metadata-targets-patterns), \\\n  $(ALL_DEFAULT_INSTALLED_MODULES)))\n\ndefine fsverity-generate-metadata\n$(call declare-0p-target,$(1).fsv_meta)\n\n$(1).fsv_meta: PRIVATE_SRC := $(1)\n$(1).fsv_meta: PRIVATE_FSVERITY := $(HOST_OUT_EXECUTABLES)/fsverity\n$(1).fsv_meta: $(HOST_OUT_EXECUTABLES)/fsverity_metadata_generator $(HOST_OUT_EXECUTABLES)/fsverity $(1)\n\t$$< --fsverity-path $$(PRIVATE_FSVERITY) --signature none \\\n\t    --hash-alg sha256 --output $$@ $$(PRIVATE_SRC)\nendef\n\n$(foreach f,$(fsverity-metadata-targets),$(eval $(call fsverity-generate-metadata,$(f))))\nALL_DEFAULT_INSTALLED_MODULES += $(addsuffix .fsv_meta,$(fsverity-metadata-targets))\n\nFSVERITY_APK_KEY_PATH := $(DEFAULT_SYSTEM_DEV_CERTIFICATE)\nFSVERITY_APK_MANIFEST_TEMPLATE_PATH := system/security/fsverity/AndroidManifest.xml\n\n# Generate and install BuildManifest<suffix>.apk for the given partition\n# $(1): path of the output APK\n# $(2): partition name\ndefine fsverity-generate-and-install-manifest-apk\nfsverity-metadata-targets-$(2) := $(filter $(PRODUCT_OUT)/$(2)/%,\\\n      $(fsverity-metadata-targets))\n$(1): PRIVATE_FSVERITY := $(HOST_OUT_EXECUTABLES)/fsverity\n$(1): PRIVATE_AAPT2 := $(HOST_OUT_EXECUTABLES)/aapt2\n$(1): PRIVATE_MIN_SDK_VERSION := $(DEFAULT_APP_TARGET_SDK)\n$(1): PRIVATE_VERSION_CODE := $(PLATFORM_SDK_VERSION)\n$(1): PRIVATE_VERSION_NAME := $(APPS_DEFAULT_VERSION_NAME)\n$(1): PRIVATE_APKSIGNER := $(HOST_OUT_EXECUTABLES)/apksigner\n$(1): PRIVATE_MANIFEST := $(FSVERITY_APK_MANIFEST_TEMPLATE_PATH)\n$(1): PRIVATE_FRAMEWORK_RES := $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk\n$(1): PRIVATE_KEY := $(FSVERITY_APK_KEY_PATH)\n$(1): PRIVATE_INPUTS := $$(fsverity-metadata-targets-$(2))\n$(1): PRIVATE_ASSETS := $(call intermediates-dir-for,ETC,build_manifest-$(2))/assets\n$(1): $(HOST_OUT_EXECUTABLES)/fsverity_manifest_generator \\\n    $(HOST_OUT_EXECUTABLES)/fsverity $(HOST_OUT_EXECUTABLES)/aapt2 \\\n    $(HOST_OUT_EXECUTABLES)/apksigner $(FSVERITY_APK_MANIFEST_TEMPLATE_PATH) \\\n    $(FSVERITY_APK_KEY_PATH).x509.pem $(FSVERITY_APK_KEY_PATH).pk8 \\\n    $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk \\\n    $$(fsverity-metadata-targets-$(2))\n\trm -rf $$(PRIVATE_ASSETS)\n\tmkdir -p $$(PRIVATE_ASSETS)\n\t$$< --fsverity-path $$(PRIVATE_FSVERITY) \\\n\t    --base-dir $$(PRODUCT_OUT) \\\n\t    --output $$(PRIVATE_ASSETS)/build_manifest.pb \\\n\t    $$(PRIVATE_INPUTS)\n\t$$(PRIVATE_AAPT2) link -o $$@ \\\n\t    -A $$(PRIVATE_ASSETS) \\\n\t    -I $$(PRIVATE_FRAMEWORK_RES) \\\n\t    --min-sdk-version $$(PRIVATE_MIN_SDK_VERSION) \\\n\t    --version-code $$(PRIVATE_VERSION_CODE) \\\n\t    --version-name $$(PRIVATE_VERSION_NAME) \\\n\t    --manifest $$(PRIVATE_MANIFEST) \\\n            --rename-manifest-package com.android.security.fsverity_metadata.$(2)\n\t$$(PRIVATE_APKSIGNER) sign --in $$@ \\\n\t    --cert $$(PRIVATE_KEY).x509.pem \\\n\t    --key $$(PRIVATE_KEY).pk8\n\n$(1).idsig: $(1)\n\nALL_DEFAULT_INSTALLED_MODULES += $(1) $(1).idsig\n\nendef  # fsverity-generate-and-install-manifest-apk\n\n$(eval $(call fsverity-generate-and-install-manifest-apk, \\\n  $(TARGET_OUT)/etc/security/fsverity/BuildManifest.apk,system))\nALL_FSVERITY_BUILD_MANIFEST_APK += $(TARGET_OUT)/etc/security/fsverity/BuildManifest.apk $(TARGET_OUT)/etc/security/fsverity/BuildManifest.apk.idsig\nifdef BUILDING_SYSTEM_EXT_IMAGE\n  $(eval $(call fsverity-generate-and-install-manifest-apk, \\\n    $(TARGET_OUT_SYSTEM_EXT)/etc/security/fsverity/BuildManifestSystemExt.apk,system_ext))\n  ALL_FSVERITY_BUILD_MANIFEST_APK += $(TARGET_OUT_SYSTEM_EXT)/etc/security/fsverity/BuildManifestSystemExt.apk $(TARGET_OUT_SYSTEM_EXT)/etc/security/fsverity/BuildManifestSystemExt.apk.idsig\nendif\n\nendif  # PRODUCT_FSVERITY_GENERATE_METADATA\n\n\n# -----------------------------------------------------------------\n# system image\n\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_SYSTEM_IMAGE\nINTERNAL_SYSTEMIMAGE_FILES := $(sort $(filter $(TARGET_OUT)/%, \\\n    $(ALL_DEFAULT_INSTALLED_MODULES)))\nendif\n\n# Create symlink /system/vendor to /vendor if necessary.\nifdef BOARD_USES_VENDORIMAGE\n  _vendor_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT)/vendor,/vendor,vendor.img)\n  INTERNAL_SYSTEMIMAGE_FILES += $(_vendor_symlink)\n  ALL_DEFAULT_INSTALLED_MODULES += $(_vendor_symlink)\nendif\n\n# Create symlink /system/product to /product if necessary.\nifdef BOARD_USES_PRODUCTIMAGE\n  _product_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT)/product,/product,product.img)\n  INTERNAL_SYSTEMIMAGE_FILES += $(_product_symlink)\n  ALL_DEFAULT_INSTALLED_MODULES += $(_product_symlink)\nendif\n\n# Create symlink /system/system_ext to /system_ext if necessary.\nifdef BOARD_USES_SYSTEM_EXTIMAGE\n  _systemext_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT)/system_ext,/system_ext,system_ext.img)\n  INTERNAL_SYSTEMIMAGE_FILES += $(_systemext_symlink)\n  ALL_DEFAULT_INSTALLED_MODULES += $(_systemext_symlink)\nendif\n\n# -----------------------------------------------------------------\n# system_dlkm partition image\n\n# Create symlinks for system_dlkm on devices with a system_dlkm partition:\n# /system/lib/modules -> /system_dlkm/lib/modules\n#\n# On devices with a system_dlkm partition,\n# - /system/lib/modules is a symlink to a directory that stores system DLKMs.\n# - The system_dlkm partition is mounted at /system_dlkm at runtime.\nifdef BOARD_USES_SYSTEM_DLKMIMAGE\n  _system_dlkm_lib_modules_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT)/lib/modules,/system_dlkm/lib/modules,system_dlkm.img)\n  INTERNAL_SYSTEMIMAGE_FILES += $(_system_dlkm_lib_modules_symlink)\n  ALL_DEFAULT_INSTALLED_MODULES += $(_system_dlkm_lib_modules_symlink)\nendif\n\nFULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)\n\n# ASAN libraries in the system image - add dependency.\nASAN_IN_SYSTEM_INSTALLED := $(TARGET_OUT)/asan.tar.bz2\nifneq (,$(filter address, $(SANITIZE_TARGET)))\n  ifeq (true,$(SANITIZE_TARGET_SYSTEM))\n    FULL_SYSTEMIMAGE_DEPS += $(ASAN_IN_SYSTEM_INSTALLED)\n  endif\nendif\n\nFULL_SYSTEMIMAGE_DEPS += $(INTERNAL_ROOT_FILES) $(INSTALLED_FILES_FILE_ROOT)\n\n# -----------------------------------------------------------------\nifdef BUILDING_SYSTEM_IMAGE\n\n# Install system linker configuration\n# Collect all available stub libraries installed in system and install with predefined linker configuration\n# Also append LLNDK libraries in the APEX as required libs\nSYSTEM_LINKER_CONFIG := $(TARGET_OUT)/etc/linker.config.pb\nSYSTEM_LINKER_CONFIG_SOURCE := system/core/rootdir/etc/linker.config.json\n$(SYSTEM_LINKER_CONFIG): PRIVATE_SYSTEM_LINKER_CONFIG_SOURCE := $(SYSTEM_LINKER_CONFIG_SOURCE)\n$(SYSTEM_LINKER_CONFIG): $(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SOURCE) | conv_linker_config\n\t@echo Creating linker config: $@\n\t@mkdir -p $(dir $@)\n\t@rm -f $@ $@.step1\n\t$(HOST_OUT_EXECUTABLES)/conv_linker_config proto --force -s $(PRIVATE_SYSTEM_LINKER_CONFIG_SOURCE) -o $@.step1\n\t$(HOST_OUT_EXECUTABLES)/conv_linker_config systemprovide --source $@.step1 \\\n\t\t--output $@ --value \"$(STUB_LIBRARIES)\" --system \"$(TARGET_OUT)\"\n\t$(HOST_OUT_EXECUTABLES)/conv_linker_config append --source $@ --output $@ --key requireLibs \\\n\t\t--value \"$(foreach lib,$(LLNDK_MOVED_TO_APEX_LIBRARIES), $(lib).so)\"\n\t$(HOST_OUT_EXECUTABLES)/conv_linker_config append --source $@ --output $@ --key provideLibs \\\n\t\t--value \"$(foreach lib,$(PRODUCT_EXTRA_STUB_LIBRARIES), $(lib).so)\"\n\trm -f $@.step1\n\n$(call declare-1p-target,$(SYSTEM_LINKER_CONFIG),)\n$(call declare-license-deps,$(SYSTEM_LINKER_CONFIG),$(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SOURCE))\n\nFULL_SYSTEMIMAGE_DEPS += $(SYSTEM_LINKER_CONFIG)\nALL_DEFAULT_INSTALLED_MODULES += $(SYSTEM_LINKER_CONFIG)\n\n# installed file list\n# Depending on anything that $(BUILT_SYSTEMIMAGE) depends on.\n# We put installed-files.txt ahead of image itself in the dependency graph\n# so that we can get the size stat even if the build fails due to too large\n# system image.\nINSTALLED_FILES_FILE := $(PRODUCT_OUT)/installed-files.txt\nINSTALLED_FILES_JSON := $(INSTALLED_FILES_FILE:.txt=.json)\n$(INSTALLED_FILES_FILE): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON)\n$(INSTALLED_FILES_FILE): $(FULL_SYSTEMIMAGE_DEPS) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_OUT) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE)))\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON)))\n\n.PHONY: installed-file-list\ninstalled-file-list: $(INSTALLED_FILES_FILE)\n\nsystemimage_intermediates :=$= $(call intermediates-dir-for,PACKAGING,system)\nBUILT_SYSTEMIMAGE :=$= $(systemimage_intermediates)/system.img\n\n# $(1): output file\ndefine build-systemimage-target\n  @echo \"Target system fs image: $(1)\"\n  @mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt\n  $(call generate-image-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt,system, \\\n      skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(systemimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \\\n          || ( mkdir -p $${DIST_DIR}; \\\n               cp $(INSTALLED_FILES_FILE) $${DIST_DIR}/installed-files-rescued.txt; \\\n               exit 1 )\nendef\n\n$(eval $(call write-partition-file-list,$(systemimage_intermediates)/file_list.txt,$(TARGET_OUT),$(FULL_SYSTEMIMAGE_DEPS)))\n\nifneq ($(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE),)\nfile_list_diff := $(HOST_OUT_EXECUTABLES)/file_list_diff$(HOST_EXECUTABLE_SUFFIX)\nsystem_file_diff_timestamp := $(systemimage_intermediates)/file_diff.timestamp\n\n# The build configuration to build the REL version may have more files to allow.\n# Use allowlist_next in addition to the allowlist in this case.\nsystem_file_diff_allowlist_next :=\nifeq (REL,$(PLATFORM_VERSION_CODENAME))\nsystem_file_diff_allowlist_next := $(ALL_MODULES.system_image_diff_allowlist_next.INSTALLED)\n$(system_file_diff_timestamp): PRIVATE_ALLOWLIST_NEXT := $(system_file_diff_allowlist_next)\nendif\n$(system_file_diff_timestamp): \\\n\t    $(systemimage_intermediates)/file_list.txt \\\n\t    $(ALL_MODULES.$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE).FILESYSTEM_FILELIST) \\\n\t    $(ALL_MODULES.system_image_diff_allowlist.INSTALLED) \\\n\t    $(system_file_diff_allowlist_next) \\\n\t    $(file_list_diff)\n\t$(file_list_diff) $(systemimage_intermediates)/file_list.txt \\\n\t  $(ALL_MODULES.$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE).FILESYSTEM_FILELIST) \\\n\t  $(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE) \\\n\t  --allowlists $(ALL_MODULES.system_image_diff_allowlist.INSTALLED) \\\n\t  $(PRIVATE_ALLOWLIST_NEXT)\n\ttouch $@\n\n$(BUILT_SYSTEMIMAGE): $(system_file_diff_timestamp)\nendif\n\n# Used by soong sandwich to request the staging dir be built\n$(systemimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT)/%,$(FULL_SYSTEMIMAGE_DEPS))\n\ttouch $@\n\nifeq ($(BOARD_AVB_ENABLE),true)\n$(BUILT_SYSTEMIMAGE): $(BOARD_AVB_SYSTEM_KEY_PATH)\nendif\n\nifeq ($(USE_SOONG_DEFINED_SYSTEM_IMAGE),true)\nifeq ($(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE),)\n$(error PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE must be set if USE_SOONG_DEFINED_SYSTEM_IMAGE is true)\nendif\nSOONG_DEFINED_SYSTEM_IMAGE_BASE := $(dir $(ALL_MODULES.$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE).FILESYSTEM_FILELIST))\n$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(systemimage_intermediates)/file_list.txt $(SOONG_DEFINED_SYSTEM_IMAGE_PATH)\n$(eval $(call copy-one-file, $(SOONG_DEFINED_SYSTEM_IMAGE_PATH), $(BUILT_SYSTEMIMAGE)))\nelse\n$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(systemimage_intermediates)/file_list.txt\n\t$(call build-systemimage-target,$@)\nendif\n\n$(call declare-1p-container,$(BUILT_SYSTEMIMAGE),system/extras)\n$(call declare-container-license-deps,$(BUILT_SYSTEMIMAGE),$(FULL_SYSTEMIMAGE_DEPS),$(PRODUCT_OUT)/:/)\n\nINSTALLED_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/system.img\nSYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)\n\n# INSTALLED_SYSTEMIMAGE_TARGET used to be named INSTALLED_SYSTEMIMAGE. Create an alias for backward\n# compatibility, in case device-specific Makefiles still refer to the old name.\nINSTALLED_SYSTEMIMAGE := $(INSTALLED_SYSTEMIMAGE_TARGET)\n\n# The system partition needs room for the recovery image as well.  We\n# now store the recovery image as a binary patch using the boot image\n# as the source (since they are very similar).  Generate the patch so\n# we can see how big it's going to be, and include that in the system\n# image size check calculation.\nifneq ($(INSTALLED_BOOTIMAGE_TARGET),)\nifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)\nifneq ($(BOARD_USES_FULL_RECOVERY_IMAGE),true)\nifneq (,$(filter true,$(BOARD_INCLUDE_RECOVERY_DTBO) $(BOARD_INCLUDE_RECOVERY_ACPIO)))\ndiff_tool := $(HOST_OUT_EXECUTABLES)/bsdiff\nelse\ndiff_tool := $(HOST_OUT_EXECUTABLES)/imgdiff\nendif\nintermediates := $(call intermediates-dir-for,PACKAGING,recovery_patch)\nRECOVERY_FROM_BOOT_PATCH := $(intermediates)/recovery_from_boot.p\n$(RECOVERY_FROM_BOOT_PATCH): PRIVATE_DIFF_TOOL := $(diff_tool)\n$(RECOVERY_FROM_BOOT_PATCH): \\\n\t    $(INSTALLED_RECOVERYIMAGE_TARGET) \\\n\t    $(firstword $(INSTALLED_BOOTIMAGE_TARGET)) \\\n\t    $(diff_tool)\n\t@echo \"Construct recovery from boot\"\n\tmkdir -p $(dir $@)\n\t$(PRIVATE_DIFF_TOOL) $(firstword $(INSTALLED_BOOTIMAGE_TARGET)) $(INSTALLED_RECOVERYIMAGE_TARGET) $@\nelse # $(BOARD_USES_FULL_RECOVERY_IMAGE) == true\nRECOVERY_FROM_BOOT_PATCH := $(INSTALLED_RECOVERYIMAGE_TARGET)\nendif # BOARD_USES_FULL_RECOVERY_IMAGE\nendif # INSTALLED_RECOVERYIMAGE_TARGET\nendif # INSTALLED_BOOTIMAGE_TARGET\n\n$(INSTALLED_SYSTEMIMAGE_TARGET): $(BUILT_SYSTEMIMAGE)\n\t@echo \"Install system fs image: $@\"\n\t$(copy-file-to-target)\n\t$(hide) $(call assert-max-image-size,$@,$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))\n\n$(call declare-1p-container,$(INSTALLED_SYSTEMIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BUILT_SYSTEMIMAGE),$(BUILT_SYSTEMIMAGE):/)\n\nsystemimage: $(INSTALLED_SYSTEMIMAGE_TARGET)\n\nSYSTEM_NOTICE_DEPS += $(INSTALLED_SYSTEMIMAGE_TARGET)\n\n.PHONY: systemimage-nodeps snod\nsystemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \\\n\t            | $(INTERNAL_USERIMAGES_DEPS) $(systemimage_intermediates)/file_list.txt\n\t@echo \"make $@: ignoring dependencies\"\n\t$(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE_TARGET))\n\t$(hide) $(call assert-max-image-size,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))\nifeq (true,$(WITH_DEXPREOPT))\n\t$(warning Warning: with dexpreopt enabled, you may need a full rebuild.)\nendif\n\nendif # BUILDING_SYSTEM_IMAGE\n\n.PHONY: sync syncsys sync_system\nsync syncsys sync_system: $(INTERNAL_SYSTEMIMAGE_FILES)\n\n# -----------------------------------------------------------------\n# Old PDK fusion targets\n.PHONY: platform\nplatform:\n\techo \"Warning: 'platform' is obsolete\"\n\n.PHONY: platform-java\nplatform-java:\n\techo \"Warning: 'platform-java' is obsolete\"\n\n# -----------------------------------------------------------------\n# data partition image\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_DATA)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_USERDATA_IMAGE\nINTERNAL_USERDATAIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_DATA)/%,$(ALL_DEFAULT_INSTALLED_MODULES))\n\nuserdataimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,userdata)\nBUILT_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/userdata.img\n\ndefine build-userdataimage-target\n  $(call pretty,\"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)\")\n  @mkdir -p $(TARGET_OUT_DATA)\n  @mkdir -p $(userdataimage_intermediates) && rm -rf $(userdataimage_intermediates)/userdata_image_info.txt\n  $(call generate-image-prop-dictionary, $(userdataimage_intermediates)/userdata_image_info.txt,userdata,skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(userdataimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT_DATA) $(userdataimage_intermediates)/userdata_image_info.txt \\\n          $(INSTALLED_USERDATAIMAGE_TARGET) $(TARGET_OUT)\n  $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE))\nendef\n\n# We just build this directly to the install location.\nINSTALLED_USERDATAIMAGE_TARGET := $(BUILT_USERDATAIMAGE_TARGET)\nINSTALLED_USERDATAIMAGE_TARGET_DEPS := \\\n    $(INTERNAL_USERIMAGES_DEPS) \\\n    $(INTERNAL_USERDATAIMAGE_FILES)\n\n$(eval $(call write-partition-file-list,$(userdataimage_intermediates)/file_list.txt,$(TARGET_OUT_DATA),$(INSTALLED_USERDATAIMAGE_TARGET_DEPS)))\n# Used by soong sandwich to request the staging dir be built\n$(userdataimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_DATA)/%,$(INSTALLED_USERDATAIMAGE_TARGET_DEPS))\n\ttouch $@\n\n$(INSTALLED_USERDATAIMAGE_TARGET): $(INSTALLED_USERDATAIMAGE_TARGET_DEPS) $(userdataimage_intermediates)/file_list.txt\n\t$(build-userdataimage-target)\n\n$(call declare-1p-container,$(INSTALLED_USERDATAIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_USERDATAIMAGE_TARGET),$(INSTALLED_USERDATAIMAGE_TARGET_DEPS),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_USERDATAIMAGE_TARGET)\n\n.PHONY: userdataimage-nodeps\nuserdataimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS) $(userdataimage_intermediates)/file_list.txt\n\t$(build-userdataimage-target)\n\nendif # BUILDING_USERDATA_IMAGE\n\n# ASAN libraries in the system image - build rule.\nASAN_OUT_DIRS_FOR_SYSTEM_INSTALL := $(sort $(patsubst $(PRODUCT_OUT)/%,%,\\\n  $(TARGET_OUT_SHARED_LIBRARIES) \\\n  $(2ND_TARGET_OUT_SHARED_LIBRARIES) \\\n  $(TARGET_OUT_VENDOR_SHARED_LIBRARIES) \\\n  $(2ND_TARGET_OUT_VENDOR_SHARED_LIBRARIES)))\n# Extra options: Enforce the system user for the files to avoid having to change ownership.\nASAN_SYSTEM_INSTALL_OPTIONS := --owner=1000 --group=1000\n# Note: experimentally, it seems not worth it to try to get \"best\" compression. We don't save\n#       enough space.\n$(ASAN_IN_SYSTEM_INSTALLED): $(INSTALLED_USERDATAIMAGE_TARGET_DEPS)\n\ttar cfj $(ASAN_IN_SYSTEM_INSTALLED) $(ASAN_SYSTEM_INSTALL_OPTIONS) -C $(TARGET_OUT_DATA)/.. $(ASAN_OUT_DIRS_FOR_SYSTEM_INSTALL) >/dev/null\n\n# -----------------------------------------------------------------\n# cache partition image\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_CACHE)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_CACHE_IMAGE\nINTERNAL_CACHEIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_CACHE)/%,$(ALL_DEFAULT_INSTALLED_MODULES))\n\ncacheimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,cache)\nBUILT_CACHEIMAGE_TARGET := $(PRODUCT_OUT)/cache.img\n\ndefine build-cacheimage-target\n  $(call pretty,\"Target cache fs image: $(INSTALLED_CACHEIMAGE_TARGET)\")\n  @mkdir -p $(TARGET_OUT_CACHE)\n  @mkdir -p $(cacheimage_intermediates) && rm -rf $(cacheimage_intermediates)/cache_image_info.txt\n  $(call generate-image-prop-dictionary, $(cacheimage_intermediates)/cache_image_info.txt,cache,skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(cacheimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT_CACHE) $(cacheimage_intermediates)/cache_image_info.txt \\\n          $(INSTALLED_CACHEIMAGE_TARGET) $(TARGET_OUT)\n  $(call assert-max-image-size,$(INSTALLED_CACHEIMAGE_TARGET),$(BOARD_CACHEIMAGE_PARTITION_SIZE))\nendef\n\n$(eval $(call write-partition-file-list,$(cacheimage_intermediates)/file_list.txt,$(TARGET_OUT_CACHE),$(INTERNAL_CACHEIMAGE_FILES)))\n# Used by soong sandwich to request the staging dir be built\n$(cacheimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_CACHE)/%,$(INTERNAL_CACHEIMAGE_FILES))\n\ttouch $@\n\n# We just build this directly to the install location.\nINSTALLED_CACHEIMAGE_TARGET := $(BUILT_CACHEIMAGE_TARGET)\n$(INSTALLED_CACHEIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_CACHEIMAGE_FILES) $(cacheimage_intermediates)/file_list.txt\n\t$(build-cacheimage-target)\n\n$(call declare-1p-container,$(INSTALLED_CACHEIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_CACHEIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_CACHEIMAGE_FILES),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_CACHEIMAGE_TARGET)\n\n.PHONY: cacheimage-nodeps\ncacheimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS) $(cacheimage_intermediates)/file_list.txt\n\t$(build-cacheimage-target)\n\nelse # BUILDING_CACHE_IMAGE\n# we need to ignore the broken cache link when doing the rsync\nIGNORE_CACHE_LINK := --exclude=cache\nendif # BUILDING_CACHE_IMAGE\n\n# -----------------------------------------------------------------\n# system_other partition image\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_SYSTEM_OTHER)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_SYSTEM_OTHER_IMAGE\nifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true)\n# Marker file to identify that odex files are installed\nINSTALLED_SYSTEM_OTHER_ODEX_MARKER := $(TARGET_OUT_SYSTEM_OTHER)/system-other-odex-marker\nALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_SYSTEM_OTHER_ODEX_MARKER)\n$(INSTALLED_SYSTEM_OTHER_ODEX_MARKER):\n\t$(hide) touch $@\n\n$(call declare-0p-target,$(INSTALLED_SYSTEM_OTHER_ODEX_MARKER))\nendif\n\nINTERNAL_SYSTEMOTHERIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_SYSTEM_OTHER)/%,\\\n      $(ALL_DEFAULT_INSTALLED_MODULES))\n\n# system_other dex files are installed as a side-effect of installing system image files\nINTERNAL_SYSTEMOTHERIMAGE_FILES += $(INTERNAL_SYSTEMIMAGE_FILES)\n\nINSTALLED_FILES_FILE_SYSTEMOTHER := $(PRODUCT_OUT)/installed-files-system-other.txt\nINSTALLED_FILES_JSON_SYSTEMOTHER := $(INSTALLED_FILES_FILE_SYSTEMOTHER:.txt=.json)\n$(INSTALLED_FILES_FILE_SYSTEMOTHER): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_SYSTEMOTHER)\n$(INSTALLED_FILES_FILE_SYSTEMOTHER) : $(INTERNAL_SYSTEMOTHERIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_OUT_SYSTEM_OTHER) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_SYSTEMOTHER)))\n$(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_SYSTEMOTHER)))\n\n# Determines partition size for system_other.img.\nifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true)\nifneq ($(filter system,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)),)\nINTERNAL_SYSTEM_OTHER_PARTITION_SIZE := $(BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE)\nendif\nendif\n\nifndef INTERNAL_SYSTEM_OTHER_PARTITION_SIZE\nINTERNAL_SYSTEM_OTHER_PARTITION_SIZE:= $(BOARD_SYSTEMIMAGE_PARTITION_SIZE)\nendif\n\nsystemotherimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,system_other)\nBUILT_SYSTEMOTHERIMAGE_TARGET := $(PRODUCT_OUT)/system_other.img\n\n# Note that we assert the size is SYSTEMIMAGE_PARTITION_SIZE since this is the 'b' system image.\ndefine build-systemotherimage-target\n  $(call pretty,\"Target system_other fs image: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET)\")\n  @mkdir -p $(TARGET_OUT_SYSTEM_OTHER)\n  @mkdir -p $(systemotherimage_intermediates) && rm -rf $(systemotherimage_intermediates)/system_other_image_info.txt\n  $(call generate-image-prop-dictionary, $(systemotherimage_intermediates)/system_other_image_info.txt,system,skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(systemotherimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT_SYSTEM_OTHER) $(systemotherimage_intermediates)/system_other_image_info.txt \\\n          $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) $(TARGET_OUT)\n  $(call assert-max-image-size,$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))\nendef\n\n$(eval $(call write-partition-file-list,$(systemotherimage_intermediates)/file_list.txt,$(TARGET_OUT_SYSTEM_OTHER),$(INTERNAL_SYSTEMOTHERIMAGE_FILES)))\n# Used by soong sandwich to request the staging dir be built\n$(systemotherimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_SYSTEM_OTHER)/%,$(INTERNAL_SYSTEMOTHERIMAGE_FILES))\n\ttouch $@\n\n# We just build this directly to the install location.\nINSTALLED_SYSTEMOTHERIMAGE_TARGET := $(BUILT_SYSTEMOTHERIMAGE_TARGET)\nifneq (true,$(SANITIZE_LITE))\n# Only create system_other when not building the second stage of a SANITIZE_LITE build.\n$(INSTALLED_SYSTEMOTHERIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEMOTHERIMAGE_FILES) $(INSTALLED_FILES_FILE_SYSTEMOTHER) $(systemotherimage_intermediates)/file_list.txt\n\t$(build-systemotherimage-target)\n\n$(call declare-1p-container,$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEMOTHERIMAGE_FILES),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_DEPS += $(INSTALLED_SYSTEMOTHERIMAGE_TARGET)\nendif\n\n.PHONY: systemotherimage-nodeps\nsystemotherimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS) $(systemotherimage_intermediates)/file_list.txt\n\t$(build-systemotherimage-target)\n\nendif # BUILDING_SYSTEM_OTHER_IMAGE\n\n\n# -----------------------------------------------------------------\n# vendor partition image\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_VENDOR)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_VENDOR_IMAGE\nINTERNAL_VENDORIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_VENDOR)/%,\\\n      $(ALL_DEFAULT_INSTALLED_MODULES))\n\n\n# Create symlink /vendor/odm to /odm if necessary.\nifdef BOARD_USES_ODMIMAGE\n  _odm_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT_VENDOR)/odm,/odm,odm.img)\n  INTERNAL_VENDORIMAGE_FILES += $(_odm_symlink)\n  ALL_DEFAULT_INSTALLED_MODULES += $(_odm_symlink)\nendif\n\n# Create symlinks for vendor_dlkm on devices with a vendor_dlkm partition:\n# /vendor/lib/modules -> /vendor_dlkm/lib/modules\n#\n# On devices with a vendor_dlkm partition,\n# - /vendor/lib/modules is a symlink to a directory that stores vendor DLKMs.\n# - /vendor_dlkm/{etc,...} store other vendor_dlkm files directly. The vendor_dlkm partition is\n#   mounted at /vendor_dlkm at runtime and the symlinks created in system/core/rootdir/Android.mk\n#   are hidden.\n# On devices without a vendor_dlkm partition,\n# - /vendor/lib/modules stores vendor DLKMs directly.\n# - /vendor_dlkm/{etc,...} are symlinks to directories that store other vendor_dlkm files.\n#   See system/core/rootdir/Android.mk for a list of created symlinks.\n# The vendor DLKMs and other vendor_dlkm files must not be accessed using other paths because they\n# are not guaranteed to exist on all devices.\nifdef BOARD_USES_VENDOR_DLKMIMAGE\n  _vendor_dlkm_lib_modules_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT_VENDOR)/lib/modules,/vendor_dlkm/lib/modules,vendor_dlkm.img)\n  INTERNAL_VENDORIMAGE_FILES += $(_vendor_dlkm_lib_modules_symlink)\n  ALL_DEFAULT_INSTALLED_MODULES += $(_vendor_dlkm_lib_modules_symlink)\nendif\n\n# Install vendor/etc/linker.config.pb with PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS and SOONG_STUB_VENDOR_LIBRARIES\nvendor_linker_config_file := $(TARGET_OUT_VENDOR)/etc/linker.config.pb\n$(vendor_linker_config_file): private_linker_config_fragments := $(PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS)\n$(vendor_linker_config_file): $(INTERNAL_VENDORIMAGE_FILES) $(PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS) | $(HOST_OUT_EXECUTABLES)/conv_linker_config\n\t@echo Creating linker config: $@\n\t@mkdir -p $(dir $@)\n\t@rm -f $@\n\t$(HOST_OUT_EXECUTABLES)/conv_linker_config proto \\\n\t\t--source $(call normalize-path-list,$(private_linker_config_fragments)) \\\n\t\t--output $@\n\t$(HOST_OUT_EXECUTABLES)/conv_linker_config systemprovide --source $@ \\\n\t\t--output $@ --value \"$(SOONG_STUB_VENDOR_LIBRARIES)\" --system \"$(TARGET_OUT_VENDOR)\"\n$(call define declare-0p-target,$(vendor_linker_config_file),)\nINTERNAL_VENDORIMAGE_FILES += $(vendor_linker_config_file)\nALL_DEFAULT_INSTALLED_MODULES += $(vendor_linker_config_file)\n\nINSTALLED_FILES_FILE_VENDOR := $(PRODUCT_OUT)/installed-files-vendor.txt\nINSTALLED_FILES_JSON_VENDOR := $(INSTALLED_FILES_FILE_VENDOR:.txt=.json)\n$(INSTALLED_FILES_FILE_VENDOR): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR)\n$(INSTALLED_FILES_FILE_VENDOR) : $(INTERNAL_VENDORIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_OUT_VENDOR) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR))\n$(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR))\n\nvendorimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,vendor)\nBUILT_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img\ndefine build-vendorimage-target\n  $(call pretty,\"Target vendor fs image: $(INSTALLED_VENDORIMAGE_TARGET)\")\n  @mkdir -p $(TARGET_OUT_VENDOR)\n  @mkdir -p $(vendorimage_intermediates) && rm -rf $(vendorimage_intermediates)/vendor_image_info.txt\n  $(call generate-image-prop-dictionary, $(vendorimage_intermediates)/vendor_image_info.txt,vendor,skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(vendorimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT_VENDOR) $(vendorimage_intermediates)/vendor_image_info.txt \\\n          $(INSTALLED_VENDORIMAGE_TARGET) $(TARGET_OUT)\n  $(call assert-max-image-size,$(INSTALLED_VENDORIMAGE_TARGET) $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_VENDORIMAGE_PARTITION_SIZE))\nendef\n\n$(eval $(call write-partition-file-list,$(vendorimage_intermediates)/file_list.txt,$(TARGET_OUT_VENDOR),$(INTERNAL_VENDORIMAGE_FILES)))\n# Used by soong sandwich to request the staging dir be built\n$(vendorimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_VENDOR)/%,$(INTERNAL_VENDORIMAGE_FILES))\n\ttouch $@\n\n# We just build this directly to the install location.\nINSTALLED_VENDORIMAGE_TARGET := $(BUILT_VENDORIMAGE_TARGET)\n$(INSTALLED_VENDORIMAGE_TARGET): \\\n    $(INTERNAL_USERIMAGES_DEPS) \\\n    $(INTERNAL_VENDORIMAGE_FILES) \\\n    $(INSTALLED_FILES_FILE_VENDOR) \\\n    $(RECOVERY_FROM_BOOT_PATCH) \\\n    $(vendorimage_intermediates)/file_list.txt\n\t$(build-vendorimage-target)\n\nVENDOR_NOTICE_DEPS += $(INSTALLED_VENDORIMAGE_TARGET)\n\n$(call declare-container-license-metadata,$(INSTALLED_VENDORIMAGE_TARGET),legacy_proprietary,proprietary,,\"Vendor Image\",vendor)\n$(call declare-container-license-deps,$(INSTALLED_VENDORIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_VENDORIMAGE_FILES) $(RECOVERY_FROM_BOOT_PATH),$(PRODUCT_OUT)/:/)\n\n.PHONY: vendorimage-nodeps vnod\nvendorimage-nodeps vnod: | $(INTERNAL_USERIMAGES_DEPS) $(vendorimage_intermediates)/file_list.txt\n\t$(build-vendorimage-target)\n\n.PHONY: sync_vendor\nsync sync_vendor: $(INTERNAL_VENDORIMAGE_FILES)\n\nelse ifdef BOARD_PREBUILT_VENDORIMAGE\nINSTALLED_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img\n$(eval $(call copy-one-file,$(BOARD_PREBUILT_VENDORIMAGE),$(INSTALLED_VENDORIMAGE_TARGET)))\n$(if $(strip $(ALL_TARGETS.$(INSTALLED_VENDORIMAGE_TARGET).META_LIC)),,\\\n    $(if $(strip $(ALL_TARGETS.$(BOARD_PREBUILT_VENDORIMAGE).META_LIC)),\\\n        $(call declare-copy-target-license-metadata,$(INSTALLED_VENDORIMAGE_TARGET),$(BOARD_PREBUILT_VENDORIMAGE)),\\\n        $(call declare-license-metadata,$(INSTALLED_VENDORIMAGE_TARGET),legacy_proprietary,proprietary,,\"Vendor Image\",vendor)))\nendif\n\n# -----------------------------------------------------------------\n# product partition image\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_PRODUCT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_PRODUCT_IMAGE\nINTERNAL_PRODUCTIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_PRODUCT)/%,\\\n      $(ALL_DEFAULT_INSTALLED_MODULES))\n\n# Install product/etc/linker.config.pb with PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS\nproduct_linker_config_file := $(TARGET_OUT_PRODUCT)/etc/linker.config.pb\n$(product_linker_config_file): private_linker_config_fragments := $(PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS)\n$(product_linker_config_file): $(INTERNAL_PRODUCTIMAGE_FILES) | $(HOST_OUT_EXECUTABLES)/conv_linker_config\n\t@echo Creating linker config: $@\n\t@mkdir -p $(dir $@)\n\t@rm -f $@\n\t$(HOST_OUT_EXECUTABLES)/conv_linker_config proto \\\n\t\t--source $(call normalize-path-list,$(private_linker_config_fragments)) \\\n\t\t--output $@\n$(call define declare-1p-target,$(product_linker_config_file),)\nINTERNAL_PRODUCTIMAGE_FILES += $(product_linker_config_file)\nALL_DEFAULT_INSTALLED_MODULES += $(product_linker_config_file)\n\n\nINSTALLED_FILES_FILE_PRODUCT := $(PRODUCT_OUT)/installed-files-product.txt\nINSTALLED_FILES_JSON_PRODUCT := $(INSTALLED_FILES_FILE_PRODUCT:.txt=.json)\n$(INSTALLED_FILES_FILE_PRODUCT): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_PRODUCT)\n$(INSTALLED_FILES_FILE_PRODUCT) : $(INTERNAL_PRODUCTIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_OUT_PRODUCT) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(call declare-0p-target,$(INSTALLED_FILES_FILE_PRODUCT))\n$(call declare-0p-target,$(INSTALLED_FILES_JSON_PRODUCT))\n\nproductimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,product)\nBUILT_PRODUCTIMAGE_TARGET := $(PRODUCT_OUT)/product.img\ndefine build-productimage-target\n  $(call pretty,\"Target product fs image: $(INSTALLED_PRODUCTIMAGE_TARGET)\")\n  @mkdir -p $(TARGET_OUT_PRODUCT)\n  @mkdir -p $(productimage_intermediates) && rm -rf $(productimage_intermediates)/product_image_info.txt\n  $(call generate-image-prop-dictionary, $(productimage_intermediates)/product_image_info.txt,product,skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(productimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT_PRODUCT) $(productimage_intermediates)/product_image_info.txt \\\n          $(INSTALLED_PRODUCTIMAGE_TARGET) $(TARGET_OUT)\n  $(call assert-max-image-size,$(INSTALLED_PRODUCTIMAGE_TARGET),$(BOARD_PRODUCTIMAGE_PARTITION_SIZE))\nendef\n\n$(eval $(call write-partition-file-list,$(productimage_intermediates)/file_list.txt,$(TARGET_OUT_PRODUCT),$(INTERNAL_PRODUCTIMAGE_FILES)))\n# Used by soong sandwich to request the staging dir be built\n$(productimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_PRODUCT)/%,$(INTERNAL_PRODUCTIMAGE_FILES))\n\ttouch $@\n\n# We just build this directly to the install location.\nINSTALLED_PRODUCTIMAGE_TARGET := $(BUILT_PRODUCTIMAGE_TARGET)\n$(INSTALLED_PRODUCTIMAGE_TARGET): \\\n    $(INTERNAL_USERIMAGES_DEPS) \\\n    $(INTERNAL_PRODUCTIMAGE_FILES) \\\n    $(INSTALLED_FILES_FILE_PRODUCT) \\\n    $(productimage_intermediates)/file_list.txt\n\t$(build-productimage-target)\n\nPRODUCT_NOTICE_DEPS += $(INSTALLED_PRODUCTIMAGE_TARGET)\n\n$(call declare-1p-container,$(INSTALLED_PRODUCTIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_PRODUCTIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_PRODUCTIMAGE_FILES) $(INSTALLED_FILES_FILE_PRODUCT),$(PRODUCT_OUT)/:/)\n\n.PHONY: productimage-nodeps pnod\nproductimage-nodeps pnod: | $(INTERNAL_USERIMAGES_DEPS) $(productimage_intermediates)/file_list.txt\n\t$(build-productimage-target)\n\n.PHONY: sync_product\nsync sync_product: $(INTERNAL_PRODUCTIMAGE_FILES)\n\nelse ifdef BOARD_PREBUILT_PRODUCTIMAGE\nINSTALLED_PRODUCTIMAGE_TARGET := $(PRODUCT_OUT)/product.img\n$(eval $(call copy-one-file,$(BOARD_PREBUILT_PRODUCTIMAGE),$(INSTALLED_PRODUCTIMAGE_TARGET)))\nendif\n\n# -----------------------------------------------------------------\n# system_ext partition image\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_SYSTEM_EXT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_SYSTEM_EXT_IMAGE\nINTERNAL_SYSTEM_EXTIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_SYSTEM_EXT)/%,\\\n      $(ALL_DEFAULT_INSTALLED_MODULES))\n\nINSTALLED_FILES_FILE_SYSTEM_EXT := $(PRODUCT_OUT)/installed-files-system_ext.txt\nINSTALLED_FILES_JSON_SYSTEM_EXT := $(INSTALLED_FILES_FILE_SYSTEM_EXT:.txt=.json)\n$(INSTALLED_FILES_FILE_SYSTEM_EXT): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_SYSTEM_EXT)\n$(INSTALLED_FILES_FILE_SYSTEM_EXT) : $(INTERNAL_SYSTEM_EXTIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_OUT_SYSTEM_EXT) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(call declare-0p-target,$(INSTALLED_FILES_FILE_SYSTEM_EXT))\n$(call declare-0p-target,$(INSTALLED_FILES_JSON_SYSTEM_EXT))\n\nsystem_extimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,system_ext)\nBUILT_SYSTEM_EXTIMAGE_TARGET := $(PRODUCT_OUT)/system_ext.img\ndefine build-system_extimage-target\n  $(call pretty,\"Target system_ext fs image: $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)\")\n  @mkdir -p $(TARGET_OUT_SYSTEM_EXT)\n  @mkdir -p $(system_extimage_intermediates) && rm -rf $(system_extimage_intermediates)/system_ext_image_info.txt\n  $(call generate-image-prop-dictionary, $(system_extimage_intermediates)/system_ext_image_info.txt,system_ext, skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(system_extimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT_SYSTEM_EXT) \\\n          $(system_extimage_intermediates)/system_ext_image_info.txt \\\n          $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \\\n          $(TARGET_OUT)\n  $(call assert-max-image-size,$(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET),$(BOARD_PRODUCT_SERVICESIMAGE_PARTITION_SIZE))\nendef\n\n$(eval $(call write-partition-file-list,$(system_extimage_intermediates)/file_list.txt,$(TARGET_OUT_SYSTEM_EXT),$(INTERNAL_SYSTEM_EXTIMAGE_FILES)))\n# Used by soong sandwich to request the staging dir be built\n$(system_extimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_SYSTEM_EXT)/%,$(INTERNAL_SYSTEM_EXTIMAGE_FILES))\n\ttouch $@\n\n# We just build this directly to the install location.\nINSTALLED_SYSTEM_EXTIMAGE_TARGET := $(BUILT_SYSTEM_EXTIMAGE_TARGET)\n$(INSTALLED_SYSTEM_EXTIMAGE_TARGET): \\\n    $(INTERNAL_USERIMAGES_DEPS) \\\n    $(INTERNAL_SYSTEM_EXTIMAGE_FILES) \\\n    $(INSTALLED_FILES_FILE_SYSTEM_EXT) \\\n    $(system_extimage_intermediates)/file_list.txt\n\t$(build-system_extimage-target)\n\nSYSTEM_EXT_NOTICE_DEPS += $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)\n\n$(call declare-1p-container,$(INSTALLED_SYSTEM_EXTIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_SYSTEM_EXTIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEM_EXTIMAGE_FILES) $(INSTALLED_FILES_FILE_SYSTEM_EXT),$(PRODUCT_OUT)/:/)\n\n.PHONY: systemextimage-nodeps senod\nsystemextimage-nodeps senod: | $(INTERNAL_USERIMAGES_DEPS) $(system_extimage_intermediates)/file_list.txt\n\t$(build-system_extimage-target)\n\n.PHONY: sync_system_ext\nsync sync_system_ext: $(INTERNAL_SYSTEM_EXTIMAGE_FILES)\n\nelse ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE\nINSTALLED_SYSTEM_EXTIMAGE_TARGET := $(PRODUCT_OUT)/system_ext.img\n$(eval $(call copy-one-file,$(BOARD_PREBUILT_SYSTEM_EXTIMAGE),$(INSTALLED_SYSTEM_EXTIMAGE_TARGET)))\nendif\n\n# -----------------------------------------------------------------\n# odm partition image\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_ODM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_ODM_IMAGE\nINTERNAL_ODMIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_ODM)/%,\\\n      $(ALL_DEFAULT_INSTALLED_MODULES))\n\n# Create symlinks for odm_dlkm on devices with a odm_dlkm partition:\n# /odm/lib/modules -> /odm_dlkm/lib/modules\n#\n# On devices with a odm_dlkm partition,\n# - /odm/lib/modules is a symlink to a directory that stores odm DLKMs.\n# - /odm_dlkm/{etc,...} store other odm_dlkm files directly. The odm_dlkm partition is\n#   mounted at /odm_dlkm at runtime and the symlinks created in system/core/rootdir/Android.mk\n#   are hidden.\n# On devices without a odm_dlkm partition,\n# - /odm/lib/modules stores odm DLKMs directly.\n# - /odm_dlkm/{etc,...} are symlinks to directories that store other odm_dlkm files.\n#   See system/core/rootdir/Android.mk for a list of created symlinks.\n# The odm DLKMs and other odm_dlkm files must not be accessed using other paths because they\n# are not guaranteed to exist on all devices.\nifdef BOARD_USES_ODM_DLKMIMAGE\n  _odm_dlkm_lib_modules_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT_ODM)/lib/modules,/odm_dlkm/lib/modules,odm_dlkm.img)\n  INTERNAL_ODMIMAGE_FILES += $(_odm_dlkm_lib_modules_symlink)\n  ALL_DEFAULT_INSTALLED_MODULES += $(_odm_dlkm_lib_modules_symlink)\nendif\n\nINSTALLED_FILES_FILE_ODM := $(PRODUCT_OUT)/installed-files-odm.txt\nINSTALLED_FILES_JSON_ODM := $(INSTALLED_FILES_FILE_ODM:.txt=.json)\n$(INSTALLED_FILES_FILE_ODM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_ODM)\n$(INSTALLED_FILES_FILE_ODM) : $(INTERNAL_ODMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_OUT_ODM) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(call declare-0p-target,$(INSTALLED_FILES_FILE_ODM))\n$(call declare-0p-target,$(INSTALLED_FILES_JSON_ODM))\n\nodmimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,odm)\nBUILT_ODMIMAGE_TARGET := $(PRODUCT_OUT)/odm.img\ndefine build-odmimage-target\n  $(call pretty,\"Target odm fs image: $(INSTALLED_ODMIMAGE_TARGET)\")\n  @mkdir -p $(TARGET_OUT_ODM)\n  @mkdir -p $(odmimage_intermediates) && rm -rf $(odmimage_intermediates)/odm_image_info.txt\n  $(call generate-image-prop-dictionary, $(odmimage_intermediates)/odm_image_info.txt, odm, \\\n\t  skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(odmimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT_ODM) $(odmimage_intermediates)/odm_image_info.txt \\\n          $(INSTALLED_ODMIMAGE_TARGET) $(TARGET_OUT)\n  $(call assert-max-image-size,$(INSTALLED_ODMIMAGE_TARGET),$(BOARD_ODMIMAGE_PARTITION_SIZE))\nendef\n\n$(eval $(call write-partition-file-list,$(odmimage_intermediates)/file_list.txt,$(TARGET_OUT_ODM),$(INTERNAL_ODMIMAGE_FILES)))\n# Used by soong sandwich to request the staging dir be built\n$(odmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_ODM)/%,$(INTERNAL_ODMIMAGE_FILES))\n\ttouch $@\n\n# We just build this directly to the install location.\nINSTALLED_ODMIMAGE_TARGET := $(BUILT_ODMIMAGE_TARGET)\n$(INSTALLED_ODMIMAGE_TARGET): \\\n    $(INTERNAL_USERIMAGES_DEPS) \\\n    $(INTERNAL_ODMIMAGE_FILES) \\\n    $(INSTALLED_FILES_FILE_ODM) \\\n    $(odmimage_intermediates)/file_list.txt\n\t$(build-odmimage-target)\n\nODM_NOTICE_DEPS += $(INSTALLED_ODMIMAGE_TARGET)\n\n$(call declare-1p-container,$(INSTALLED_ODMIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_ODMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_ODMIMAGE_FILES) $(INSTALLED_FILES_FILE_ODM),$(PRODUCT_OUT)/:/)\n\n.PHONY: odmimage-nodeps onod\nodmimage-nodeps onod: | $(INTERNAL_USERIMAGES_DEPS) $(odmimage_intermediates)/file_list.txt\n\t$(build-odmimage-target)\n\n.PHONY: sync_odm\nsync sync_odm: $(INTERNAL_ODMIMAGE_FILES)\n\nelse ifdef BOARD_PREBUILT_ODMIMAGE\nINSTALLED_ODMIMAGE_TARGET := $(PRODUCT_OUT)/odm.img\n$(eval $(call copy-one-file,$(BOARD_PREBUILT_ODMIMAGE),$(INSTALLED_ODMIMAGE_TARGET)))\nendif\n\n# -----------------------------------------------------------------\n# vendor_dlkm partition image\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_VENDOR_DLKM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_VENDOR_DLKM_IMAGE\nINTERNAL_VENDOR_DLKMIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_VENDOR_DLKM)/%,\\\n      $(ALL_DEFAULT_INSTALLED_MODULES))\n\nINSTALLED_FILES_FILE_VENDOR_DLKM := $(PRODUCT_OUT)/installed-files-vendor_dlkm.txt\nINSTALLED_FILES_JSON_VENDOR_DLKM := $(INSTALLED_FILES_FILE_VENDOR_DLKM:.txt=.json)\n$(INSTALLED_FILES_FILE_VENDOR_DLKM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_DLKM)\n$(INSTALLED_FILES_FILE_VENDOR_DLKM) : $(INTERNAL_VENDOR_DLKMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_OUT_VENDOR_DLKM) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_DLKM))\n$(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_DLKM))\n\nvendor_dlkmimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,vendor_dlkm)\nBUILT_VENDOR_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/vendor_dlkm.img\ndefine build-vendor_dlkmimage-target\n  $(call pretty,\"Target vendor_dlkm fs image: $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)\")\n  @mkdir -p $(TARGET_OUT_VENDOR_DLKM)\n  @mkdir -p $(vendor_dlkmimage_intermediates) && rm -rf $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt\n  $(call generate-image-prop-dictionary, $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt, \\\n\t  vendor_dlkm, skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(vendor_dlkmimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT_VENDOR_DLKM) $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt \\\n          $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(TARGET_OUT)\n  $(call assert-max-image-size,$(INSTALLED_VENDOR_DLKMIMAGE_TARGET),$(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE))\nendef\n\n$(eval $(call write-partition-file-list,$(vendor_dlkmimage_intermediates)/file_list.txt,$(TARGET_OUT_VENDOR_DLKM),$(INTERNAL_VENDOR_DLKMIMAGE_FILES)))\n# Used by soong sandwich to request the staging dir be built\n$(vendor_dlkmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_VENDOR_DLKM)/%,$(INTERNAL_VENDOR_DLKMIMAGE_FILES))\n\ttouch $@\n\n# We just build this directly to the install location.\nINSTALLED_VENDOR_DLKMIMAGE_TARGET := $(BUILT_VENDOR_DLKMIMAGE_TARGET)\n$(INSTALLED_VENDOR_DLKMIMAGE_TARGET): \\\n    $(INTERNAL_USERIMAGES_DEPS) \\\n    $(INTERNAL_VENDOR_DLKMIMAGE_FILES) \\\n    $(INSTALLED_FILES_FILE_VENDOR_DLKM) \\\n    $(vendor_dlkmimage_intermediates)/file_list.txt\n\t$(build-vendor_dlkmimage-target)\n\nVENDOR_DLKM_NOTICE_DEPS += $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)\n\n$(call declare-1p-container,$(INSTALLED_VENDOR_DLKMIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_VENDOR_DLKMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_VENDOR_DLKMIMAGE_FILES) $(INSTALLED_FILES_FILE_VENDOR_DLKM),$(PRODUCT_OUT)/:/)\n\n.PHONY: vendor_dlkmimage-nodeps vdnod\nvendor_dlkmimage-nodeps vdnod: | $(INTERNAL_USERIMAGES_DEPS) $(vendor_dlkmimage_intermediates)/file_list.txt\n\t$(build-vendor_dlkmimage-target)\n\n.PHONY: sync_vendor_dlkm\nsync sync_vendor_dlkm: $(INTERNAL_VENDOR_DLKMIMAGE_FILES)\n\nelse ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE\nINSTALLED_VENDOR_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/vendor_dlkm.img\n$(eval $(call copy-one-file,$(BOARD_PREBUILT_VENDOR_DLKMIMAGE),$(INSTALLED_VENDOR_DLKMIMAGE_TARGET)))\nendif\n\n# -----------------------------------------------------------------\n# odm_dlkm partition image\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_ODM_DLKM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_ODM_DLKM_IMAGE\nINTERNAL_ODM_DLKMIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_ODM_DLKM)/%,\\\n      $(ALL_DEFAULT_INSTALLED_MODULES))\n\nINSTALLED_FILES_FILE_ODM_DLKM := $(PRODUCT_OUT)/installed-files-odm_dlkm.txt\nINSTALLED_FILES_JSON_ODM_DLKM := $(INSTALLED_FILES_FILE_ODM_DLKM:.txt=.json)\n$(INSTALLED_FILES_FILE_ODM_DLKM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_ODM_DLKM)\n$(INSTALLED_FILES_FILE_ODM_DLKM) : $(INTERNAL_ODM_DLKMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_OUT_ODM_DLKM) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(call declare-0p-target,$(INSTALLED_FILES_FILE_ODM_DLKM))\n$(call declare-0p-target,$(INSTALLED_FILES_JSON_ODM_DLKM))\n\nodm_dlkmimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,odm_dlkm)\nBUILT_ODM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/odm_dlkm.img\ndefine build-odm_dlkmimage-target\n  $(call pretty,\"Target odm_dlkm fs image: $(INSTALLED_ODM_DLKMIMAGE_TARGET)\")\n  @mkdir -p $(TARGET_OUT_ODM_DLKM)\n  @mkdir -p $(odm_dlkmimage_intermediates) && rm -rf $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt\n  $(call generate-image-prop-dictionary, $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt, \\\n\t  odm_dlkm, skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(odm_dlkmimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT_ODM_DLKM) $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt \\\n          $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(TARGET_OUT)\n  $(call assert-max-image-size,$(INSTALLED_ODM_DLKMIMAGE_TARGET),$(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE))\nendef\n\n$(eval $(call write-partition-file-list,$(odm_dlkmimage_intermediates)/file_list.txt,$(TARGET_OUT_ODM_DLKM),$(INTERNAL_ODM_DLKMIMAGE_FILES)))\n# Used by soong sandwich to request the staging dir be built\n$(odm_dlkmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_ODM_DLKM)/%,$(INTERNAL_ODM_DLKMIMAGE_FILES))\n\ttouch $@\n\n# We just build this directly to the install location.\nINSTALLED_ODM_DLKMIMAGE_TARGET := $(BUILT_ODM_DLKMIMAGE_TARGET)\n$(INSTALLED_ODM_DLKMIMAGE_TARGET): \\\n    $(INTERNAL_USERIMAGES_DEPS) \\\n    $(INTERNAL_ODM_DLKMIMAGE_FILES) \\\n    $(INSTALLED_FILES_FILE_ODM_DLKM) \\\n    $(odm_dlkmimage_intermediates)/file_list.txt\n\t$(build-odm_dlkmimage-target)\n\nODM_DLKM_NOTICE_DEPS += $(INSTALLED_ODM_DLKMIMAGE_TARGET)\n\n$(call declare-1p-container,$(INSTALLED_ODM_DLKMIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_ODM_DLKMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_ODM_DLKMIMAGE_FILES) $(INSTALLED_FILES_FILE_ODM_DLKM),$(PRODUCT_OUT)/:/)\n\n.PHONY: odm_dlkmimage-nodeps odnod\nodm_dlkmimage-nodeps odnod: | $(INTERNAL_USERIMAGES_DEPS) $(odm_dlkmimage_intermediates)/file_list.txt\n\t$(build-odm_dlkmimage-target)\n\n.PHONY: sync_odm_dlkm\nsync sync_odm_dlkm: $(INTERNAL_ODM_DLKMIMAGE_FILES)\n\nelse ifdef BOARD_PREBUILT_ODM_DLKMIMAGE\nINSTALLED_ODM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/odm_dlkm.img\n$(eval $(call copy-one-file,$(BOARD_PREBUILT_ODM_DLKMIMAGE),$(INSTALLED_ODM_DLKMIMAGE_TARGET)))\nendif\n\n# -----------------------------------------------------------------\n# system_dlkm partition image\n\nINSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_SYSTEM_DLKM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))\nifdef BUILDING_SYSTEM_DLKM_IMAGE\n\nINTERNAL_SYSTEM_DLKMIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_SYSTEM_DLKM)/%,\\\n      $(ALL_DEFAULT_INSTALLED_MODULES))\n\nINSTALLED_FILES_FILE_SYSTEM_DLKM := $(PRODUCT_OUT)/installed-files-system_dlkm.txt\nINSTALLED_FILES_JSON_SYSTEM_DLKM := $(INSTALLED_FILES_FILE_SYSTEM_DLKM:.txt=.json)\n$(INSTALLED_FILES_FILE_SYSTEM_DLKM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_SYSTEM_DLKM)\n$(INSTALLED_FILES_FILE_SYSTEM_DLKM): $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)\n\t@echo Installed file list: $@\n\tmkdir -p $(dir $@)\n\trm -f $@\n\t$(FILESLIST) $(TARGET_OUT_SYSTEM_DLKM) > $(@:.txt=.json)\n\t$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@\n\n$(call declare-0p-target,$(INSTALLED_FILES_FILE_SYSTEM_DLKM))\n$(call declare-0p-target,$(INSTALLED_FILES_JSON_SYSTEM_DLKM))\n\nsystem_dlkmimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,system_dlkm)\nBUILT_SYSTEM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/system_dlkm.img\ndefine build-system_dlkmimage-target\n  $(call pretty,\"Target system_dlkm fs image: $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)\")\n  @mkdir -p $(TARGET_OUT_SYSTEM_DLKM)\n  @mkdir -p $(system_dlkmimage_intermediates) && rm -rf $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt\n  $(call generate-image-prop-dictionary, $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt, \\\n\t  system_dlkm, skip_fsck=true)\n  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n      $(BUILD_IMAGE) \\\n          $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(system_dlkmimage_intermediates)/file_list.txt) \\\n          $(TARGET_OUT_SYSTEM_DLKM) $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt \\\n          $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(TARGET_OUT)\n  $(call assert-max-image-size,$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),$(BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE))\nendef\n\n$(eval $(call write-partition-file-list,$(system_dlkmimage_intermediates)/file_list.txt,$(TARGET_OUT_SYSTEM_DLKM),$(INTERNAL_SYSTEM_DLKMIMAGE_FILES)))\n# Used by soong sandwich to request the staging dir be built\n$(system_dlkmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_SYSTEM_DLKM)/%,$(INTERNAL_SYSTEM_DLKMIMAGE_FILES))\n\ttouch $@\n\n# We just build this directly to the install location.\nINSTALLED_SYSTEM_DLKMIMAGE_TARGET := $(BUILT_SYSTEM_DLKMIMAGE_TARGET)\n$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET): \\\n    $(INTERNAL_USERIMAGES_DEPS) \\\n    $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) \\\n    $(INSTALLED_FILES_FILE_SYSTEM_DLKM) \\\n    $(system_dlkmimage_intermediates)/file_list.txt\n\t$(build-system_dlkmimage-target)\n\nSYSTEM_DLKM_NOTICE_DEPS += $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)\n\n$(call declare-1p-container,$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) $(INSTALLED_FILES_FILE_SYSTEM_DLKM),$(PRODUCT_OUT)/:/)\n\n.PHONY: system_dlkmimage-nodeps sdnod\nsystem_dlkmimage-nodeps sdnod: | $(INTERNAL_USERIMAGES_DEPS) $(system_dlkmimage_intermediates)/file_list.txt\n\t$(build-system_dlkmimage-target)\n\n.PHONY: sync_system_dlkm\nsync sync_system_dlkm: $(INTERNAL_SYSTEM_DLKMIMAGE_FILES)\n\nelse ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE\nINSTALLED_SYSTEM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/system_dlkm.img\n$(eval $(call copy-one-file,$(BOARD_PREBUILT_SYSTEM_DLKMIMAGE),$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)))\nendif\n\n# Protected VM firmware image\nifeq ($(BOARD_USES_PVMFWIMAGE),true)\n\n.PHONY: pvmfwimage\npvmfwimage: $(INSTALLED_PVMFWIMAGE_TARGET)\n\nINSTALLED_PVMFWIMAGE_TARGET := $(PRODUCT_OUT)/pvmfw.img\nINSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET := $(PRODUCT_OUT)/pvmfw_embedded.avbpubkey\nINSTALLED_PVMFW_BINARY_TARGET := $(call module-target-built-files,pvmfw_bin)\nINTERNAL_PVMFWIMAGE_FILES := $(call module-target-built-files,pvmfw_img)\nINTERNAL_PVMFW_EMBEDDED_AVBKEY := $(call module-target-built-files,pvmfw_embedded_key_pub_bin)\nINTERNAL_PVMFW_SYMBOL := $(TARGET_OUT_EXECUTABLES_UNSTRIPPED)/pvmfw\n\n# If pvmfw target is not available and there is a prebuilt available use prebuilt\n# NOTE: This is only a temporary feature for x86_64 and is not meant to be supported for long.\n# TODO(b/391333413): Don't allow use of pvmfw prebuilts as soon as it is possible\nifeq ($(INTERNAL_PVMFWIMAGE_FILES),)\nifneq ($(PRODUCT_PVMFW_IMAGE_PREBUILT),)\nINTERNAL_PVMFWIMAGE_FILES := $(call module-target-built-files,$(PRODUCT_PVMFW_IMAGE_PREBUILT))\nINTERNAL_PVMFW_SYMBOL :=\n\nifneq ($(PRODUCT_PVMFW_BIN_PREBUILT),)\nINSTALLED_PVMFW_BINARY_TARGET := $(call module-target-built-files,$(PRODUCT_PVMFW_BIN_PREBUILT))\nendif # PRODUCT_PVMFW_BIN_PREBUILT\n\nifneq ($(PRODUCT_PVMFW_EMBEDDED_AVBKEY_PREBUILT),)\nINTERNAL_PVMFW_EMBEDDED_AVBKEY := $(call module-target-built-files,$(PRODUCT_PVMFW_EMBEDDED_AVBKEY_PREBUILT))\nendif # PRODUCT_PVMFW_EMBEDDED_AVBKEY_PREBUILT\n\nendif # PRODUCT_PVMFW_IMAGE_PREBUILT\nendif # INTERNAL_PVMFWIMAGE_FILES\n\n$(call declare-1p-container,$(INSTALLED_PVMFWIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_PVMFWIMAGE_TARGET),$(INTERNAL_PVMFWIMAGE_FILES),$(PRODUCT_OUT)/:/)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_PVMFWIMAGE_TARGET)\n\n# Place the unstripped pvmfw image to the symbols directory\n$(INTERNAL_PVMFWIMAGE_FILES): |$(INTERNAL_PVMFW_SYMBOL)\n\n$(eval $(call copy-one-file,$(INTERNAL_PVMFWIMAGE_FILES),$(INSTALLED_PVMFWIMAGE_TARGET)))\n\n$(INSTALLED_PVMFWIMAGE_TARGET): $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET)\n\n$(eval $(call copy-one-file,$(INTERNAL_PVMFW_EMBEDDED_AVBKEY),$(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET)))\n\nendif # BOARD_USES_PVMFWIMAGE\n\n# Returns a list of image targets corresponding to the given list of partitions. For example, it\n# returns \"$(INSTALLED_PRODUCTIMAGE_TARGET)\" for \"product\", or \"$(INSTALLED_SYSTEMIMAGE_TARGET)\n# $(INSTALLED_VENDORIMAGE_TARGET)\" for \"system vendor\".\n# (1): list of partitions like \"system\", \"vendor\" or \"system product system_ext\".\ndefine images-for-partitions\n$(strip $(foreach item,$(1),\\\n  $(if $(filter $(item),system_other),$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),\\\n    $(if $(filter $(item),init_boot),$(INSTALLED_INIT_BOOT_IMAGE_TARGET),\\\n      $(INSTALLED_$(call to-upper,$(item))IMAGE_TARGET)))))\nendef\n\n# -----------------------------------------------------------------\n# custom images\nINSTALLED_CUSTOMIMAGES_TARGET :=\n\nifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),)\nINTERNAL_AVB_CUSTOMIMAGES_SIGNING_ARGS :=\nBOARD_AVB_CUSTOMIMAGES_PARTITION_LIST :=\n# If BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH is set, the image will be included in\n# BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST, otherwise the image won't be AVB signed.\n$(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \\\n\t$(if $(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH), \\\n\t$(eval BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST += $(partition)) \\\n\t$(eval BOARD_$(call to-upper,$(partition))_IMAGE_LIST := $(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST))))\n\n# Sign custom image.\n# $(1): the prebuilt custom image.\n# $(2): the mount point of the prebuilt custom image.\n# $(3): the signed custom image target.\ndefine sign_custom_image\n$(3): $(1) $(INTERNAL_USERIMAGES_DEPS)\n\t@echo Target custom image: $(3)\n\tmkdir -p $(dir $(3))\n\tcp $(1) $(3)\nifeq ($(BOARD_AVB_ENABLE),true)\n\tPATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$$$PATH \\\n          $(AVBTOOL) add_hashtree_footer \\\n          --image $(3) \\\n          --key $(BOARD_AVB_$(call to-upper,$(2))_KEY_PATH) \\\n          --algorithm $(BOARD_AVB_$(call to-upper,$(2))_ALGORITHM) \\\n          $(call get-partition-size-argument,$(BOARD_AVB_$(call to-upper,$(2))_PARTITION_SIZE)) \\\n          --partition_name $(2) \\\n          $(INTERNAL_AVB_CUSTOMIMAGES_SIGNING_ARGS) \\\n          $(BOARD_AVB_$(call to-upper,$(2))_ADD_HASHTREE_FOOTER_ARGS)\nendif\nINSTALLED_CUSTOMIMAGES_TARGET += $(3)\nendef\n\n# Copy unsigned custom image.\n# $(1): the prebuilt custom image.\n# $(2): the signed custom image target.\ndefine copy_custom_image\n$(2): $(1) $(INTERNAL_USERIMAGES_DEPS)\n\t@echo Target custom image: $(2)\n\tmkdir -p $(dir $(2))\n\tcp $(1) $(2)\nINSTALLED_CUSTOMIMAGES_TARGET += $(2)\nendef\n\n# Add AVB custom image to droid target\n$(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \\\n  $(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST), \\\n     $(eval $(call sign_custom_image,$(image),$(partition),$(PRODUCT_OUT)/$(notdir $(image))))))\n\n# Add unsigned custom image to droid target\n$(foreach partition,$(filter-out $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), $(BOARD_CUSTOMIMAGES_PARTITION_LIST)), \\\n  $(foreach image,$(BOARD_$(call to-upper,$(partition))_IMAGE_LIST), \\\n     $(eval $(call copy_custom_image,$(image),$(PRODUCT_OUT)/$(notdir $(image))))))\nendif\n\n# -----------------------------------------------------------------\n# vbmeta image\nifeq ($(BOARD_AVB_ENABLE),true)\n\nBUILT_VBMETAIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta.img\nAVB_CHAIN_KEY_DIR := $(TARGET_OUT_INTERMEDIATES)/avb_chain_keys\n\nifdef BOARD_AVB_KEY_PATH\n$(if $(BOARD_AVB_ALGORITHM),,$(error BOARD_AVB_ALGORITHM is not defined))\nelse\n# If key path isn't specified, use the 4096-bit test key.\nBOARD_AVB_ALGORITHM := SHA256_RSA4096\nBOARD_AVB_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem\nendif\n\n# AVB signing for system_other.img.\nifdef BUILDING_SYSTEM_OTHER_IMAGE\nifdef BOARD_AVB_SYSTEM_OTHER_KEY_PATH\n$(if $(BOARD_AVB_SYSTEM_OTHER_ALGORITHM),,$(error BOARD_AVB_SYSTEM_OTHER_ALGORITHM is not defined))\nelse\n# If key path isn't specified, use the same key as BOARD_AVB_KEY_PATH.\nBOARD_AVB_SYSTEM_OTHER_KEY_PATH := $(BOARD_AVB_KEY_PATH)\nBOARD_AVB_SYSTEM_OTHER_ALGORITHM := $(BOARD_AVB_ALGORITHM)\nendif\n\n$(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET): $(AVBTOOL) $(BOARD_AVB_SYSTEM_OTHER_KEY_PATH)\n\t@echo Extracting system_other avb key: $@\n\t@rm -f $@\n\t@mkdir -p $(dir $@)\n\t$(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_OTHER_KEY_PATH) --output $@\n\n$(eval $(call declare-0p-target,$(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET),))\n\nifndef BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX\nBOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)\nendif\n\nBOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS += --rollback_index $(BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX)\nendif # end of AVB for BUILDING_SYSTEM_OTHER_IMAGE\n\nINTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES := \\\n    $(BOARD_AVB_VBMETA_SYSTEM) \\\n    $(BOARD_AVB_VBMETA_VENDOR) \\\n    $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))))\n\n# Not allowing the same partition to appear in multiple groups.\nifneq ($(words $(sort $(INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES))),$(words $(INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES)))\n  $(error BOARD_AVB_VBMETA_SYSTEM and BOARD_AVB_VBMETA_VENDOR cannot have duplicates)\nendif\n\n# When building a standalone recovery image for non-A/B devices, recovery image must be self-signed\n# to be verified independently, and cannot be chained into vbmeta.img. See the link below for\n# details.\nifeq ($(TARGET_OTA_ALLOW_NON_AB),true)\nifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)\n$(if $(BOARD_AVB_RECOVERY_KEY_PATH),,\\\n    $(error BOARD_AVB_RECOVERY_KEY_PATH must be defined for if non-A/B is supported. \\\n            See https://android.googlesource.com/platform/external/avb/+/master/README.md#booting-into-recovery))\nendif\nendif\n\n# Appends os version as a AVB property descriptor.\nSYSTEM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE)\nBOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.system.os_version:$(SYSTEM_OS_VERSION)\n\nPRODUCT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE)\nBOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.product.os_version:$(PRODUCT_OS_VERSION)\n\nSYSTEM_EXT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE)\nBOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.system_ext.os_version:$(SYSTEM_EXT_OS_VERSION)\n\nINIT_BOOT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE)\nBOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.init_boot.os_version:$(INIT_BOOT_OS_VERSION)\n\nBOOT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE)\nBOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.boot.os_version:$(BOOT_OS_VERSION)\n\nVENDOR_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE)\nBOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.vendor.os_version:$(VENDOR_OS_VERSION)\n\nODM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE)\nBOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.odm.os_version:$(ODM_OS_VERSION)\n\nVENDOR_DLKM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE)\nBOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.vendor_dlkm.os_version:$(VENDOR_DLKM_OS_VERSION)\n\nODM_DLKM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE)\nBOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.odm_dlkm.os_version:$(ODM_DLKM_OS_VERSION)\n\nSYSTEM_DLKM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE)\nBOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.system_dlkm.os_version:$(SYSTEM_DLKM_OS_VERSION)\n\n# Appends fingerprint and security patch level as a AVB property descriptor.\nBOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.system.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \\\n    --prop com.android.build.system.security_patch:$(PLATFORM_SECURITY_PATCH)\n\nBOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.product.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \\\n    --prop com.android.build.product.security_patch:$(PLATFORM_SECURITY_PATCH)\n\nBOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.system_ext.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \\\n    --prop com.android.build.system_ext.security_patch:$(PLATFORM_SECURITY_PATCH)\n\nBOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)\n\nBOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.init_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)\n\nBOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.vendor_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \\\n\nBOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.vendor_kernel_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \\\n\nBOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.recovery.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)\n\nBOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.vendor.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)\n\nBOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.odm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)\n\nBOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.vendor_dlkm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)\n\nBOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.odm_dlkm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)\n\nBOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.system_dlkm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)\n\nBOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.dtbo.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)\n\nBOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.pvmfw.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)\n\n# The following vendor- and odm-specific images needs explicit SPL set per board.\n# TODO(b/210875415) Is this security_patch property used? Should it be removed from\n# boot.img when there is no platform ramdisk included in it?\nifdef BOOT_SECURITY_PATCH\nBOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.boot.security_patch:$(BOOT_SECURITY_PATCH)\nendif\n\nifdef INIT_BOOT_SECURITY_PATCH\nBOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.init_boot.security_patch:$(INIT_BOOT_SECURITY_PATCH)\nelse ifdef BOOT_SECURITY_PATCH\nBOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.init_boot.security_patch:$(BOOT_SECURITY_PATCH)\nendif\n\nifdef VENDOR_SECURITY_PATCH\nBOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.vendor.security_patch:$(VENDOR_SECURITY_PATCH)\nendif\n\nifdef ODM_SECURITY_PATCH\nBOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.odm.security_patch:$(ODM_SECURITY_PATCH)\nendif\n\nifdef VENDOR_DLKM_SECURITY_PATCH\nBOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.vendor_dlkm.security_patch:$(VENDOR_DLKM_SECURITY_PATCH)\nendif\n\nifdef ODM_DLKM_SECURITY_PATCH\nBOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.odm_dlkm.security_patch:$(ODM_DLKM_SECURITY_PATCH)\nendif\n\nifdef SYSTEM_DLKM_SECURITY_PATCH\nBOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop com.android.build.system_dlkm.security_patch:$(SYSTEM_DLKM_SECURITY_PATCH)\nendif\n\nifdef PVMFW_SECURITY_PATCH\nBOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS += \\\n    --prop com.android.build.pvmfw.security_patch:$(PVMFW_SECURITY_PATCH)\nendif\n\n# Append root digest of microdroid-vendor partition's hashtree descriptor into vendor partition.\nifdef MICRODROID_VENDOR_IMAGE_MODULE\nMICRODROID_VENDOR_IMAGE := \\\n    $(call intermediates-dir-for,ETC,$(MICRODROID_VENDOR_IMAGE_MODULE))/$(MICRODROID_VENDOR_IMAGE_MODULE)\nMICRODROID_VENDOR_ROOT_DIGEST := $(PRODUCT_OUT)/microdroid_vendor_root_digest\nBOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \\\n    --prop_from_file com.android.build.microdroid-vendor.root_digest:$(MICRODROID_VENDOR_ROOT_DIGEST)\n$(MICRODROID_VENDOR_ROOT_DIGEST): $(AVBTOOL) $(MICRODROID_VENDOR_IMAGE)\n\t$(AVBTOOL) print_partition_digests \\\n      --image $(MICRODROID_VENDOR_IMAGE) \\\n      | tr -d '\\n' | sed -E 's/.*: //g' > $@\n$(INSTALLED_VENDORIMAGE_TARGET): $(MICRODROID_VENDOR_ROOT_DIGEST)\nendif\n\nBOOT_FOOTER_ARGS := BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS\nINIT_BOOT_FOOTER_ARGS := BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS\nVENDOR_BOOT_FOOTER_ARGS := BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS\nVENDOR_KERNEL_BOOT_FOOTER_ARGS := BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS\nDTBO_FOOTER_ARGS := BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS\nPVMFW_FOOTER_ARGS := BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS\nSYSTEM_FOOTER_ARGS := BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS\nVENDOR_FOOTER_ARGS := BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS\nRECOVERY_FOOTER_ARGS := BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS\nPRODUCT_FOOTER_ARGS := BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS\nSYSTEM_EXT_FOOTER_ARGS := BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS\nODM_FOOTER_ARGS := BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS\nVENDOR_DLKM_FOOTER_ARGS := BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS\nODM_DLKM_FOOTER_ARGS := BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS\nSYSTEM_DLKM_FOOTER_ARGS := BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS\n\n# Helper function that checks and sets required build variables for an AVB chained partition.\n# $(1): the partition to enable AVB chain, e.g., boot or system or vbmeta_system.\ndefine _check-and-set-avb-chain-args\n$(eval part := $(1))\n$(eval PART=$(call to-upper,$(part)))\n\n$(eval _key_path := BOARD_AVB_$(PART)_KEY_PATH)\n$(eval _signing_algorithm := BOARD_AVB_$(PART)_ALGORITHM)\n$(eval _rollback_index := BOARD_AVB_$(PART)_ROLLBACK_INDEX)\n$(eval _rollback_index_location := BOARD_AVB_$(PART)_ROLLBACK_INDEX_LOCATION)\n$(if $($(_key_path)),,$(error $(_key_path) is not defined))\n$(if $($(_signing_algorithm)),,$(error $(_signing_algorithm) is not defined))\n$(if $($(_rollback_index)),,$(error $(_rollback_index) is not defined))\n$(if $($(_rollback_index_location)),,$(error $(_rollback_index_location) is not defined))\n\n# Set INTERNAL_AVB_(PART)_SIGNING_ARGS\n$(eval _signing_args := INTERNAL_AVB_$(PART)_SIGNING_ARGS)\n$(eval $(_signing_args) := \\\n    --algorithm $($(_signing_algorithm)) --key $($(_key_path)))\n\n# The recovery partition in non-A/B devices should be verified separately. Skip adding the chain\n# partition descriptor for recovery partition into vbmeta.img.\n$(if $(or $(filter-out true,$(TARGET_OTA_ALLOW_NON_AB)),$(filter-out recovery,$(part))),\\\n    $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \\\n        --chain_partition $(part):$($(_rollback_index_location)):$(AVB_CHAIN_KEY_DIR)/$(part).avbpubkey))\n\n# Set rollback_index via footer args for non-chained vbmeta image. Chained vbmeta image will pick up\n# the index via a separate flag (e.g. BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX).\n$(if $(filter $(part),$(part:vbmeta_%=%)),\\\n    $(eval _footer_args := $(PART)_FOOTER_ARGS) \\\n    $(eval $($(_footer_args)) += --rollback_index $($(_rollback_index))))\nendef\n\n# Checks and sets the required build variables for an AVB partition. The partition will be\n# configured as a chained partition, if BOARD_AVB_<partition>_KEY_PATH is defined. Otherwise the\n# image descriptor will be included into vbmeta.img, unless it has been already added to any chained\n# VBMeta image.\n# Multiple boot images can be generated based on BOARD_KERNEL_BINARIES\n# but vbmeta would capture the image descriptor of only the first boot\n# image specified in BUILT_BOOTIMAGE_TARGET.\n# $(1): Partition name, e.g. boot or system.\ndefine check-and-set-avb-args\n$(eval _in_chained_vbmeta := $(filter $(1),$(INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES)))\n$(if $(BOARD_AVB_$(call to-upper,$(1))_KEY_PATH),\\\n    $(if $(_in_chained_vbmeta),\\\n        $(error Chaining partition \"$(1)\" in chained VBMeta image is not supported)) \\\n    $(call _check-and-set-avb-chain-args,$(1)),\\\n    $(if $(_in_chained_vbmeta),,\\\n        $(if $(filter boot,$(1)),\\\n            $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \\\n                --include_descriptors_from_image $(firstword $(call images-for-partitions,$(1)))),\\\n            $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \\\n                --include_descriptors_from_image $(call images-for-partitions,$(1))))))\nendef\n\n# Checks and sets build variables for a custom chained partition to include it into vbmeta.img.\n# $(1): the custom partition to enable AVB chain.\ndefine check-and-set-custom-avb-chain-args\n$(eval part := $(1))\n$(eval PART=$(call to-upper,$(part)))\n$(eval _rollback_index_location := BOARD_AVB_$(PART)_ROLLBACK_INDEX_LOCATION)\n$(eval _key_path := BOARD_AVB_$(PART)_KEY_PATH)\n$(if $($(_rollback_index_location)),,$(error $(_rollback_index_location) is not defined))\n$(if $($(_key_path)),,$(error $(_key_path) is not defined))\n\nINTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \\\n    --chain_partition $(part):$($(_rollback_index_location)):$(AVB_CHAIN_KEY_DIR)/$(part).avbpubkey\nendef\n\nifdef INSTALLED_BOOTIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,boot))\nendif\n\nifdef INSTALLED_INIT_BOOT_IMAGE_TARGET\n$(eval $(call check-and-set-avb-args,init_boot))\nendif\n\nifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,vendor_boot))\nendif\n\nifdef INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,vendor_kernel_boot))\nendif\n\nifdef INSTALLED_SYSTEMIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,system))\nendif\n\nifdef INSTALLED_VENDORIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,vendor))\nendif\n\nifdef INSTALLED_PRODUCTIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,product))\nendif\n\nifdef INSTALLED_SYSTEM_EXTIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,system_ext))\nendif\n\nifdef INSTALLED_ODMIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,odm))\nendif\n\nifdef INSTALLED_VENDOR_DLKMIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,vendor_dlkm))\nendif\n\nifdef INSTALLED_ODM_DLKMIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,odm_dlkm))\nendif\n\nifdef INSTALLED_SYSTEM_DLKMIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,system_dlkm))\nendif\n\nifdef INSTALLED_DTBOIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,dtbo))\nendif\n\nifdef INSTALLED_PVMFWIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,pvmfw))\nendif\n\nifdef INSTALLED_RECOVERYIMAGE_TARGET\n$(eval $(call check-and-set-avb-args,recovery))\nendif\n\n# Not using INSTALLED_VBMETA_SYSTEMIMAGE_TARGET as it won't be set yet.\nifdef BOARD_AVB_VBMETA_SYSTEM\n$(eval $(call check-and-set-avb-args,vbmeta_system))\nendif\n\nifdef BOARD_AVB_VBMETA_VENDOR\n$(eval $(call check-and-set-avb-args,vbmeta_vendor))\nendif\n\nifdef BOARD_AVB_VBMETA_CUSTOM_PARTITIONS\n$(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),$(eval $(call check-and-set-avb-args,vbmeta_$(partition))))\n$(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),$(eval BOARD_AVB_MAKE_VBMETA_$(call to-upper,$(partition))_IMAGE_ARGS += --padding_size 4096))\nendif\n\nifneq ($(strip $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST)),)\n$(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \\\n    $(eval $(call check-and-set-custom-avb-chain-args,$(partition))))\nendif\n\nBOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --padding_size 4096\nBOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS += --padding_size 4096\nBOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS += --padding_size 4096\n\nifeq (eng,$(filter eng, $(TARGET_BUILD_VARIANT)))\n# We only need the flag in top-level vbmeta.img.\nBOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --set_hashtree_disabled_flag\nendif\n\nifdef BOARD_AVB_ROLLBACK_INDEX\nBOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --rollback_index $(BOARD_AVB_ROLLBACK_INDEX)\nendif\n\nifdef BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX\nBOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS += \\\n    --rollback_index $(BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX)\nendif\n\nifdef BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX\nBOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS += \\\n    --rollback_index $(BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX)\nendif\n\nifdef BOARD_AVB_VBMETA_CUSTOM_PARTITIONS\n  $(foreach partition,$(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)), \\\n      $(if $(BOARD_AVB_VBMETA_$(partition)_ROLLBACK_INDEX),$(eval \\\n        BOARD_AVB_MAKE_VBMETA_$(partition)_IMAGE_ARGS += \\\n          --rollback_index $(BOARD_AVB_VBMETA_$(partition)_ROLLBACK_INDEX))))\nendif\n\n# $(1): the directory to extract public keys to\ndefine extract-avb-chain-public-keys\n  $(if $(BOARD_AVB_BOOT_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_BOOT_KEY_PATH) \\\n      --output $(1)/boot.avbpubkey)\n  $(if $(BOARD_AVB_INIT_BOOT_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_INIT_BOOT_KEY_PATH) \\\n      --output $(1)/init_boot.avbpubkey)\n  $(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),\\\n    $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_BOOT_KEY_PATH) \\\n      --output $(1)/vendor_boot.avbpubkey)\n  $(if $(BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH),\\\n    $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH) \\\n      --output $(1)/vendor_kernel_boot.avbpubkey)\n  $(if $(BOARD_AVB_SYSTEM_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_KEY_PATH) \\\n      --output $(1)/system.avbpubkey)\n  $(if $(BOARD_AVB_VENDOR_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_KEY_PATH) \\\n      --output $(1)/vendor.avbpubkey)\n  $(if $(BOARD_AVB_PRODUCT_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_PRODUCT_KEY_PATH) \\\n      --output $(1)/product.avbpubkey)\n  $(if $(BOARD_AVB_SYSTEM_EXT_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_EXT_KEY_PATH) \\\n      --output $(1)/system_ext.avbpubkey)\n  $(if $(BOARD_AVB_ODM_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_ODM_KEY_PATH) \\\n      --output $(1)/odm.avbpubkey)\n  $(if $(BOARD_AVB_VENDOR_DLKM_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_DLKM_KEY_PATH) \\\n      --output $(1)/vendor_dlkm.avbpubkey)\n  $(if $(BOARD_AVB_ODM_DLKM_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_ODM_DLKM_KEY_PATH) \\\n      --output $(1)/odm_dlkm.avbpubkey)\n  $(if $(BOARD_AVB_SYSTEM_DLKM_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_DLKM_KEY_PATH) \\\n      --output $(1)/system_dlkm.avbpubkey)\n  $(if $(BOARD_AVB_DTBO_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_DTBO_KEY_PATH) \\\n      --output $(1)/dtbo.avbpubkey)\n  $(if $(BOARD_AVB_PVMFW_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_PVMFW_KEY_PATH) \\\n      --output $(1)/pvmfw.avbpubkey)\n  $(if $(BOARD_AVB_RECOVERY_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_RECOVERY_KEY_PATH) \\\n      --output $(1)/recovery.avbpubkey)\n  $(if $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH) \\\n        --output $(1)/vbmeta_system.avbpubkey)\n  $(if $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH),\\\n    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH) \\\n        --output $(1)/vbmeta_vendor.avbpubkey)\n  $(if $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST),\\\n    $(hide) $(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \\\n        $(AVBTOOL) extract_public_key --key $(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH) \\\n            --output $(1)/$(partition).avbpubkey;))\n  $(if $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),\\\n    $(hide) $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS), \\\n        $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_KEY_PATH) \\\n            --output $(1)/vbmeta_$(partition).avbpubkey;))\nendef\n\n# Builds a chained VBMeta image. This VBMeta image will contain the descriptors for the partitions\n# specified in BOARD_AVB_VBMETA_<NAME>. The built VBMeta image will be included into the top-level\n# vbmeta image as a chained partition. For example, if a target defines `BOARD_AVB_VBMETA_SYSTEM\n# := system system_ext`, `vbmeta_system.img` will be created that includes the descriptors for\n# `system.img` and `system_ext.img`. `vbmeta_system.img` itself will be included into\n# `vbmeta.img` as a chained partition.\n# $(1): VBMeta image name, such as \"vbmeta_system\", \"vbmeta_vendor\" etc.\n# $(2): Output filename.\ndefine build-chained-vbmeta-image\n\t$(call pretty,\"Target chained vbmeta image: $@\")\n\t$(hide) $(AVBTOOL) make_vbmeta_image \\\n\t    $(INTERNAL_AVB_$(call to-upper,$(1))_SIGNING_ARGS) \\\n\t    $(BOARD_AVB_MAKE_$(call to-upper,$(1))_IMAGE_ARGS) \\\n\t    $(foreach image,$(BOARD_AVB_$(call to-upper,$(1))), \\\n\t        --include_descriptors_from_image $(call images-for-partitions,$(image))) \\\n\t    --output $@\n      # libavb expects to be able to read the maximum vbmeta size, so we must provide a partition\n      # which matches this or the read will fail.\n      # See external/avb/libavb/avb_slot_verify.c#VBMETA_MAX_SIZE\n      truncate -s 65536 $@\nendef\n\nifdef BUILDING_SYSTEM_IMAGE\nifdef BOARD_AVB_VBMETA_SYSTEM\nINSTALLED_VBMETA_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta_system.img\n$(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET): \\\n\t    $(AVBTOOL) \\\n\t    $(call images-for-partitions,$(BOARD_AVB_VBMETA_SYSTEM)) \\\n\t    $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH)\n\t$(call build-chained-vbmeta-image,vbmeta_system)\n\n$(call declare-1p-container,$(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET),)\n\nSYSTEM_NOTICE_DEPS += $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET)\nendif\nendif # BUILDING_SYSTEM_IMAGE\n\nifdef BOARD_AVB_VBMETA_VENDOR\nINSTALLED_VBMETA_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta_vendor.img\n$(INSTALLED_VBMETA_VENDORIMAGE_TARGET): \\\n\t    $(AVBTOOL) \\\n\t    $(call images-for-partitions,$(BOARD_AVB_VBMETA_VENDOR)) \\\n\t    $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH)\n\t$(call build-chained-vbmeta-image,vbmeta_vendor)\n\n$(call declare-1p-container,$(INSTALLED_VBMETA_VENDORIMAGE_TARGET),)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_VBMETA_VENDORIMAGE_TARGET)\nendif\n\nifdef BOARD_AVB_VBMETA_CUSTOM_PARTITIONS\ndefine declare-custom-vbmeta-target\nINSTALLED_VBMETA_$(call to-upper,$(1))IMAGE_TARGET := $(PRODUCT_OUT)/vbmeta_$(call to-lower,$(1)).img\n$$(INSTALLED_VBMETA_$(call to-upper,$(1))IMAGE_TARGET): \\\n\t    $(AVBTOOL) \\\n\t    $(call images-for-partitions,$(BOARD_AVB_VBMETA_$(call to-upper,$(1)))) \\\n\t    $(BOARD_AVB_VBMETA_$(call to-upper,$(1))_KEY_PATH)\n\t$$(call build-chained-vbmeta-image,vbmeta_$(call to-lower,$(1)))\n\n$(call declare-1p-container,$(INSTALLED_VBMETA_$(call to-upper,$(1))IMAGE_TARGET),)\n\nUNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_VBMETA_$(call to-upper,$(1))IMAGE_TARGET)\nendef\n\n$(foreach partition,\\\n          $(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)),\\\n          $(eval $(call declare-custom-vbmeta-target,$(partition))))\nendif\n\ndefine build-vbmetaimage-target\n  $(call pretty,\"Target vbmeta image: $(INSTALLED_VBMETAIMAGE_TARGET)\")\n  $(hide) mkdir -p $(AVB_CHAIN_KEY_DIR)\n  $(call extract-avb-chain-public-keys, $(AVB_CHAIN_KEY_DIR))\n  $(hide) $(AVBTOOL) make_vbmeta_image \\\n    $(INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS) \\\n    $(PRIVATE_AVB_VBMETA_SIGNING_ARGS) \\\n    $(BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS) \\\n    --output $@\n    # libavb expects to be able to read the maximum vbmeta size, so we must provide a partition\n    # which matches this or the read will fail.\n    # See external/avb/libavb/avb_slot_verify.c#VBMETA_MAX_SIZE\n    truncate -s 65536 $@\n  $(hide) rm -rf $(AVB_CHAIN_KEY_DIR)\nendef\n\nifdef BUILDING_VBMETA_IMAGE\nINSTALLED_VBMETAIMAGE_TARGET := $(BUILT_VBMETAIMAGE_TARGET)\n$(INSTALLED_VBMETAIMAGE_TARGET): PRIVATE_AVB_VBMETA_SIGNING_ARGS := \\\n    --algorithm $(BOARD_AVB_ALGORITHM) --key $(BOARD_AVB_KEY_PATH)\n\n\n$(INSTALLED_VBMETAIMAGE_TARGET): \\\n\t    $(AVBTOOL) \\\n\t    $(INSTALLED_BOOTIMAGE_TARGET) \\\n\t    $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \\\n\t    $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \\\n\t    $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET) \\\n\t    $(INSTALLED_SYSTEMIMAGE_TARGET) \\\n\t    $(INSTALLED_VENDORIMAGE_TARGET) \\\n\t    $(INSTALLED_PRODUCTIMAGE_TARGET) \\\n\t    $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \\\n\t    $(INSTALLED_ODMIMAGE_TARGET) \\\n\t    $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \\\n\t    $(INSTALLED_ODM_DLKMIMAGE_TARGET) \\\n\t    $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \\\n\t    $(INSTALLED_DTBOIMAGE_TARGET) \\\n\t    $(INSTALLED_PVMFWIMAGE_TARGET) \\\n\t    $(INSTALLED_CUSTOMIMAGES_TARGET) \\\n\t    $(INSTALLED_RECOVERYIMAGE_TARGET) \\\n\t    $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) \\\n\t    $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) \\\n      $(foreach partition,$(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)),$(INSTALLED_VBMETA_$(partition)IMAGE_TARGET)) \\\n\t    $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH) \\\n\t    $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH) \\\n      $(foreach partition,$(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)),$(BOARD_AVB_VBMETA_$(partition)_KEY_PATH)) \\\n\t    $(BOARD_AVB_KEY_PATH)\n\t$(build-vbmetaimage-target)\n\n$(call declare-1p-container,$(INSTALLED_VBMETAIMAGE_TARGET),)\n\nUNMOUNTED_NOTICE_DEPS += $(INSTALLED_VBMETAIMAGE_TARGET)\n\n.PHONY: vbmetaimage-nodeps\nvbmetaimage-nodeps: PRIVATE_AVB_VBMETA_SIGNING_ARGS := \\\n    --algorithm $(BOARD_AVB_ALGORITHM) --key $(BOARD_AVB_KEY_PATH)\nvbmetaimage-nodeps:\n\t$(build-vbmetaimage-target)\nendif # BUILDING_VBMETA_IMAGE\n\nendif # BOARD_AVB_ENABLE\n\n# List of files from all images\nINTERNAL_ALLIMAGES_FILES := \\\n    $(FULL_SYSTEMIMAGE_DEPS) \\\n    $(INTERNAL_RAMDISK_FILES) \\\n    $(INTERNAL_USERDATAIMAGE_FILES) \\\n    $(INTERNAL_VENDORIMAGE_FILES) \\\n    $(INTERNAL_PRODUCTIMAGE_FILES) \\\n    $(INTERNAL_SYSTEM_EXTIMAGE_FILES) \\\n    $(INTERNAL_ODMIMAGE_FILES) \\\n    $(INTERNAL_VENDOR_DLKMIMAGE_FILES) \\\n    $(INTERNAL_ODM_DLKMIMAGE_FILES) \\\n    $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) \\\n    $(INTERNAL_PVMFWIMAGE_FILES) \\\n\n# -----------------------------------------------------------------\n# Run apex_sepolicy_tests for all installed APEXes\n\nifeq (,$(TARGET_BUILD_UNBUNDLED))\nifneq (,$(filter ext4 erofs,$(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE)))\nintermediate := $(call intermediates-dir-for,PACKAGING,apex_sepolicy_tests)\napex_dirs := \\\n  $(TARGET_OUT)/apex/% \\\n  $(TARGET_OUT_SYSTEM_EXT)/apex/% \\\n  $(TARGET_OUT_VENDOR)/apex/% \\\n  $(TARGET_OUT_ODM)/apex/% \\\n  $(TARGET_OUT_PRODUCT)/apex/% \\\n\napex_files := $(sort $(filter $(apex_dirs), $(INTERNAL_ALLIMAGES_FILES)))\napex_dirs :=\n\n# $1: apex file\n# $2: output file\ndefine _run_apex_sepolicy_tests\n$2: $1 \\\n    $(HOST_OUT_EXECUTABLES)/apex_sepolicy_tests \\\n    $(HOST_OUT_EXECUTABLES)/apex-ls\n\t@rm -rf $$@\n\t@mkdir -p $(dir $$@)\n\t$(HOST_OUT_EXECUTABLES)/apex_sepolicy_tests --all -f <($(HOST_OUT_EXECUTABLES)/apex-ls -Z $$<)\n\t@touch $$@\nendef\n\n# $1: apex file list\ndefine run_apex_sepolicy_tests\n$(foreach apex_file,$1, \\\n  $(eval passfile := $(patsubst $(PRODUCT_OUT)/%,$(intermediate)/%.pass,$(apex_file))) \\\n  $(eval $(call _run_apex_sepolicy_tests,$(apex_file),$(passfile))) \\\n  $(passfile))\nendef\n\n.PHONY: run_apex_sepolicy_tests\nrun_apex_sepolicy_tests: $(call run_apex_sepolicy_tests,$(apex_files))\n\ndroid_targets: run_apex_sepolicy_tests\n\napex_files :=\nintermediate :=\nendif # PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE\nendif # TARGET_BUILD_UNBUNDLED\n\n# -----------------------------------------------------------------\n# Check VINTF of build\n\n# Note: vendor_dlkm, odm_dlkm, and system_dlkm does not have VINTF files.\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n\nintermediates := $(call intermediates-dir-for,PACKAGING,check_vintf_all)\ncheck_vintf_all_deps :=\n\n# -----------------------------------------------------------------\n# Activate APEXes for checkvintf\n\napex_dirs := \\\n  $(TARGET_OUT)/apex/% \\\n  $(TARGET_OUT_PRODUCT)/apex/% \\\n  $(TARGET_OUT_SYSTEM_EXT)/apex/% \\\n  $(TARGET_OUT_VENDOR)/apex/% \\\n  $(TARGET_OUT_ODM)/apex/% \\\n\napex_files := $(sort $(filter $(apex_dirs), $(INTERNAL_ALLIMAGES_FILES)))\n\nAPEX_OUT := $(intermediates)/apex\nAPEX_INFO_FILE := $(APEX_OUT)/apex-info-list.xml\n\n# apexd_host scans/activates APEX files and writes /apex/apex-info-list.xml\n# Note that `@echo $(PRIVATE_APEX_FILES)` line is added to trigger the rule when the APEX list is changed.\n$(APEX_INFO_FILE): PRIVATE_APEX_FILES := $(apex_files)\n$(APEX_INFO_FILE): $(HOST_OUT_EXECUTABLES)/apexd_host \\\n    $(HOST_OUT_EXECUTABLES)/deapexer $(HOST_OUT_EXECUTABLES)/debugfs $(HOST_OUT_EXECUTABLES)/fsck.erofs \\\n    $(apex_files)\n\t@echo \"Extracting apexes...\"\n\t@echo $(PRIVATE_APEX_FILES) > /dev/null\n\t@rm -rf $(APEX_OUT)\n\t@mkdir -p $(APEX_OUT)\n\t$< --system_path $(TARGET_OUT) \\\n\t   --system_ext_path $(TARGET_OUT_SYSTEM_EXT) \\\n\t   --product_path $(TARGET_OUT_PRODUCT) \\\n\t   --vendor_path $(TARGET_OUT_VENDOR) \\\n\t   --odm_path $(TARGET_OUT_ODM) \\\n\t   --apex_path $(APEX_OUT)\n\napex_files :=\napex_dirs :=\n\n# The build system only writes VINTF metadata to */etc/vintf paths. Legacy paths aren't needed here\n# because they are only used for prebuilt images.\n# APEX files in /$partition/apex can have VINTF fragments as well.\ncheck_vintf_common_srcs_patterns := \\\n  $(TARGET_OUT)/etc/vintf/% \\\n  $(TARGET_OUT_VENDOR)/etc/vintf/% \\\n  $(TARGET_OUT_ODM)/etc/vintf/% \\\n  $(TARGET_OUT_PRODUCT)/etc/vintf/% \\\n  $(TARGET_OUT_SYSTEM_EXT)/etc/vintf/% \\\n  $(apex_dirs)\n\ncheck_vintf_common_srcs := $(sort $(filter $(check_vintf_common_srcs_patterns),$(INTERNAL_ALLIMAGES_FILES)))\ncheck_vintf_common_srcs_patterns :=\n\ncheck_vintf_has_system :=\ncheck_vintf_has_vendor :=\n\nifneq (,$(filter EMPTY_ODM_SKU_PLACEHOLDER,$(ODM_MANIFEST_SKUS)))\n$(error EMPTY_ODM_SKU_PLACEHOLDER is an internal variable and cannot be used for ODM_MANIFEST_SKUS)\nendif\nifneq (,$(filter EMPTY_VENDOR_SKU_PLACEHOLDER,$(DEVICE_MANIFEST_SKUS)))\n$(error EMPTY_VENDOR_SKU_PLACEHOLDER is an internal variable and cannot be used for DEIVCE_MANIFEST_SKUS)\nendif\n\n# -- Check system and system_ext manifests / matrices including fragments (excluding other framework manifests / matrices, e.g. product);\nifdef BUILDING_SYSTEM_IMAGE\ncheck_vintf_system_deps := $(filter $(TARGET_OUT)/etc/vintf/% \\\n                                    $(TARGET_OUT_SYSTEM_EXT)/etc/vintf/% \\\n                                    $(TARGET_OUT)/apex/% \\\n                                    $(TARGET_OUT_SYSTEM_EXT)/apex/%, \\\n                                    $(check_vintf_common_srcs))\nifneq ($(check_vintf_system_deps),)\ncheck_vintf_has_system := true\n\ncheck_vintf_system_log := $(intermediates)/check_vintf_system.log\ncheck_vintf_all_deps += $(check_vintf_system_log)\n$(check_vintf_system_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_system_deps) $(APEX_INFO_FILE)\n\t@( $< --check-one --dirmap /system:$(TARGET_OUT) --dirmap /apex:$(APEX_OUT) > $@ 2>&1 ) || ( cat $@ && exit 1 )\n$(call declare-1p-target,$(check_vintf_system_log))\ncheck_vintf_system_log :=\n\n# -- Check framework manifest against frozen manifests for GSI targets. They need to be compatible.\nifneq (true, $(BUILDING_VENDOR_IMAGE))\n    vintffm_log := $(intermediates)/vintffm.log\nendif\ncheck_vintf_all_deps += $(vintffm_log)\n$(vintffm_log): $(HOST_OUT_EXECUTABLES)/vintffm $(check_vintf_system_deps) $(APEX_INFO_FILE)\n\t@( $< --check --dirmap /system:$(TARGET_OUT) \\\n\t  --dirmap /system_ext:$(TARGET_OUT_SYSTEM_EXT) \\\n\t  --dirmap /product:$(TARGET_OUT_PRODUCT) \\\n\t  --dirmap /apex:$(APEX_OUT) \\\n\t  system/libhidl/vintfdata/frozen > $@ 2>&1 ) || ( cat $@ && exit 1 )\n\n$(call declare-1p-target,$(vintffm_log))\n\nendif # check_vintf_system_deps\ncheck_vintf_system_deps :=\n\nendif # BUILDING_SYSTEM_IMAGE\n\n# -- Check vendor manifest / matrix including fragments (excluding other device manifests / matrices)\ncheck_vintf_vendor_deps := $(filter $(TARGET_OUT_VENDOR)/etc/vintf/% \\\n                                    $(TARGET_OUT_VENDOR)/apex/%, \\\n                                    $(check_vintf_common_srcs))\nifneq ($(strip $(check_vintf_vendor_deps)),)\ncheck_vintf_has_vendor := true\ncheck_vintf_vendor_log := $(intermediates)/check_vintf_vendor.log\ncheck_vintf_all_deps += $(check_vintf_vendor_log)\n# Check vendor SKU=(empty) case when:\n# - DEVICE_MANIFEST_FILE is not empty; OR\n# - DEVICE_MANIFEST_FILE is empty AND DEVICE_MANIFEST_SKUS is empty (only vendor manifest fragments are used)\n$(check_vintf_vendor_log): PRIVATE_VENDOR_SKUS := \\\n  $(if $(DEVICE_MANIFEST_FILE),EMPTY_VENDOR_SKU_PLACEHOLDER,\\\n    $(if $(DEVICE_MANIFEST_SKUS),,EMPTY_VENDOR_SKU_PLACEHOLDER)) \\\n  $(DEVICE_MANIFEST_SKUS)\n$(check_vintf_vendor_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_vendor_deps) $(APEX_INFO_FILE)\n\t$(foreach vendor_sku,$(PRIVATE_VENDOR_SKUS), \\\n\t  ( $< --check-one --dirmap /vendor:$(TARGET_OUT_VENDOR) --dirmap /apex:$(APEX_OUT) \\\n\t       --property ro.boot.product.vendor.sku=$(filter-out EMPTY_VENDOR_SKU_PLACEHOLDER,$(vendor_sku)) \\\n\t       > $@ 2>&1 ) || ( cat $@ && exit 1 ); )\n$(call declare-1p-target,$(check_vintf_vendor_log))\ncheck_vintf_vendor_log :=\nendif # check_vintf_vendor_deps\ncheck_vintf_vendor_deps :=\n\n# -- Kernel version and configurations.\nifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),true)\n\nBUILT_KERNEL_CONFIGS_FILE := $(intermediates)/kernel_configs.txt\nBUILT_KERNEL_VERSION_FILE := $(intermediates)/kernel_version.txt\n\nmy_board_extracted_kernel :=\n\n# Tools for decompression that is not in PATH.\n# Check $(EXTRACT_KERNEL) for decompression algorithms supported by the script.\n# Algorithms that are in the script but not in this list will be found in PATH.\nmy_decompress_tools := \\\n    lz4:$(HOST_OUT_EXECUTABLES)/lz4 \\\n\n\n# BOARD_KERNEL_VERSION can be used to override the values extracted\n# from INSTALLED_KERNEL_TARGET.\nifdef BOARD_KERNEL_VERSION\n$(BUILT_KERNEL_VERSION_FILE): PRIVATE_DECOMPRESS_TOOLS := $(my_decompress_tools)\n$(BUILT_KERNEL_VERSION_FILE): $(foreach pair,$(my_decompress_tools),$(call word-colon,2,$(pair)))\n$(BUILT_KERNEL_VERSION_FILE): $(EXTRACT_KERNEL) $(firstword $(INSTALLED_KERNEL_TARGET))\n\tKERNEL_RELEASE=`$(EXTRACT_KERNEL) --tools $(PRIVATE_DECOMPRESS_TOOLS) --input $(firstword $(INSTALLED_KERNEL_TARGET)) \\\n\t  --output-release` ;\\\n  if [ \"$$KERNEL_RELEASE\" != '$(BOARD_KERNEL_VERSION)' ]; then \\\n    echo \"Specified kernel version '$(BOARD_KERNEL_VERSION)' does not match actual kernel version '$$KERNEL_RELEASE' \" ; exit 1; fi;\n\techo '$(BOARD_KERNEL_VERSION)' > $@\n\n$(call declare-license-metadata,$(BUILT_KERNEL_VERSION_FILE),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,\"Kernel\",kernel)\n\nendif # BOARD_KERNEL_VERSION\n\n\nifneq ($(my_board_extracted_kernel),true)\nifdef INSTALLED_KERNEL_TARGET\nifndef BOARD_KERNEL_VERSION\n$(BUILT_KERNEL_CONFIGS_FILE): .KATI_IMPLICIT_OUTPUTS := $(BUILT_KERNEL_VERSION_FILE)\nendif\n$(BUILT_KERNEL_CONFIGS_FILE): PRIVATE_DECOMPRESS_TOOLS := $(my_decompress_tools)\n$(BUILT_KERNEL_CONFIGS_FILE): $(foreach pair,$(my_decompress_tools),$(call word-colon,2,$(pair)))\n$(BUILT_KERNEL_CONFIGS_FILE): $(EXTRACT_KERNEL) $(firstword $(INSTALLED_KERNEL_TARGET))\n\t$< --tools $(PRIVATE_DECOMPRESS_TOOLS) --input $(firstword $(INSTALLED_KERNEL_TARGET)) \\\n\t  --output-configs $@ \\\n\t  $(if $(BOARD_KERNEL_VERSION),,--output-release $(BUILT_KERNEL_VERSION_FILE))\n\n$(call declare-license-metadata,$(BUILT_KERNEL_CONFIGS_FILE),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,\"Kernel\",kernel)\n\nmy_board_extracted_kernel := true\nendif # INSTALLED_KERNEL_TARGET\nendif # my_board_extracted_kernel\n\nifneq ($(my_board_extracted_kernel),true)\nifdef INSTALLED_BOOTIMAGE_TARGET\n$(BUILT_KERNEL_CONFIGS_FILE): .KATI_IMPLICIT_OUTPUTS := $(BUILT_KERNEL_VERSION_FILE)\n$(BUILT_KERNEL_CONFIGS_FILE): PRIVATE_DECOMPRESS_TOOLS := $(my_decompress_tools)\n$(BUILT_KERNEL_CONFIGS_FILE): $(foreach pair,$(my_decompress_tools),$(call word-colon,2,$(pair)))\n$(BUILT_KERNEL_CONFIGS_FILE): PRIVATE_UNPACKED_BOOTIMG := $(intermediates)/unpacked_bootimage\n$(BUILT_KERNEL_CONFIGS_FILE): \\\n        $(HOST_OUT_EXECUTABLES)/unpack_bootimg \\\n        $(EXTRACT_KERNEL) \\\n        $(INSTALLED_BOOTIMAGE_TARGET)\n\t$(HOST_OUT_EXECUTABLES)/unpack_bootimg --boot_img $(INSTALLED_BOOTIMAGE_TARGET) --out $(PRIVATE_UNPACKED_BOOTIMG)\n\t$(EXTRACT_KERNEL) --tools $(PRIVATE_DECOMPRESS_TOOLS) --input $(PRIVATE_UNPACKED_BOOTIMG)/kernel \\\n\t  --output-configs $@ \\\n\t  --output-release $(BUILT_KERNEL_VERSION_FILE)\n\n$(call declare-license-metadata,$(BUILT_KERNEL_CONFIGS_FILE),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,\"Kernel\",kernel)\n\nmy_board_extracted_kernel := true\nendif # INSTALLED_BOOTIMAGE_TARGET\nendif # my_board_extracted_kernel\n\nifeq ($(my_board_extracted_kernel),true)\n$(call dist-for-goals, droid_targets, $(BUILT_KERNEL_VERSION_FILE))\nelse\n$(warning Neither INSTALLED_KERNEL_TARGET nor INSTALLED_BOOTIMAGE_TARGET is defined when \\\n    PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS  is true. Information about the updated kernel \\\n    cannot be built into OTA update package. You can fix this by: \\\n    (1) setting TARGET_NO_KERNEL to false and installing the built kernel to $(PRODUCT_OUT)/kernel,\\\n        so that kernel information will be extracted from the built kernel; or \\\n    (2) Add a prebuilt boot image and specify it in BOARD_PREBUILT_BOOTIMAGE; or \\\n    (3) extracting kernel configuration and defining BOARD_KERNEL_CONFIG_FILE and \\\n        BOARD_KERNEL_VERSION manually; or \\\n    (4) unsetting PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS manually.)\n# Clear their values to indicate that these two files does not exist.\nBUILT_KERNEL_CONFIGS_FILE :=\nBUILT_KERNEL_VERSION_FILE :=\nendif\n\nmy_decompress_tools :=\nmy_board_extracted_kernel :=\n\nendif # PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS\n\nifeq (default,$(ENABLE_UFFD_GC))\n\nifneq (,$(BUILT_KERNEL_VERSION_FILE))\n$(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC): $(BUILT_KERNEL_VERSION_FILE)\n$(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC):\n\tif ! cmp -s $(BUILT_KERNEL_VERSION_FILE) $@ ; then cp $(BUILT_KERNEL_VERSION_FILE) $@; fi\n.KATI_RESTAT: $(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC)\nelse\n# We make this a warning rather than an error to avoid breaking too many builds. When it happens,\n# we use a placeholder as the kernel version, which is consumed by uffd_gc_utils.py.\n$(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC):\n\techo $$'\\\nUnable to determine UFFD GC flag because the kernel version is not available and\\n\\\nPRODUCT_ENABLE_UFFD_GC is \"default\".\\n\\\nYou can fix this by:\\n\\\n  1. [Recommended] Making the kernel version available.\\n\\\n    (1). Set PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS to \"true\".\\n\\\n    (2). If you are still getting this message after doing so, see the warning about\\n\\\n         PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS in the build logs.\\n\\\n  or\\n\\\n  2. Explicitly setting PRODUCT_ENABLE_UFFD_GC to \"true\" or \"false\" based on the kernel version.\\n\\\n    (1). Set PRODUCT_ENABLE_UFFD_GC to \"true\" if the kernel is a GKI kernel and is android12-5.4\\n\\\n         or above, or a non-GKI kernel that supports userfaultfd(2) and MREMAP_DONTUNMAP.\\n\\\n    (2). Set PRODUCT_ENABLE_UFFD_GC to \"false\" otherwise.'\\\n\t  && echo '<unknown-kernel>' > $@\nendif # BUILT_KERNEL_VERSION_FILE\n\nendif # ENABLE_UFFD_GC == \"default\"\n\n# -- Check VINTF compatibility of build.\n# Skip partial builds; only check full builds. Only check if:\n# - PRODUCT_ENFORCE_VINTF_MANIFEST is true\n# - system / vendor VINTF metadata exists\n# - Building product / system_ext / odm images if board has product / system_ext / odm images\nifeq ($(PRODUCT_ENFORCE_VINTF_MANIFEST),true)\nifeq ($(check_vintf_has_system),true)\nifeq ($(check_vintf_has_vendor),true)\nifeq ($(filter true,$(BUILDING_ODM_IMAGE)),$(filter true,$(BOARD_USES_ODMIMAGE)))\nifeq ($(filter true,$(BUILDING_PRODUCT_IMAGE)),$(filter true,$(BOARD_USES_PRODUCTIMAGE)))\nifeq ($(filter true,$(BUILDING_SYSTEM_EXT_IMAGE)),$(filter true,$(BOARD_USES_SYSTEM_EXTIMAGE)))\n\ncheck_vintf_compatible_log := $(intermediates)/check_vintf_compatible.log\ncheck_vintf_all_deps += $(check_vintf_compatible_log)\n\ncheck_vintf_compatible_args :=\ncheck_vintf_compatible_deps := $(check_vintf_common_srcs) $(APEX_INFO_FILE)\n\nifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),true)\nifneq (,$(BUILT_KERNEL_VERSION_FILE)$(BUILT_KERNEL_CONFIGS_FILE))\ncheck_vintf_compatible_args += --kernel $(BUILT_KERNEL_VERSION_FILE):$(BUILT_KERNEL_CONFIGS_FILE)\ncheck_vintf_compatible_deps += $(BUILT_KERNEL_CONFIGS_FILE) $(BUILT_KERNEL_VERSION_FILE)\nendif # BUILT_KERNEL_VERSION_FILE != \"\" || BUILT_KERNEL_CONFIGS_FILE != \"\"\nendif # PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS\n\ncheck_vintf_compatible_args += \\\n  --dirmap /system:$(TARGET_OUT) \\\n  --dirmap /vendor:$(TARGET_OUT_VENDOR) \\\n  --dirmap /odm:$(TARGET_OUT_ODM) \\\n  --dirmap /product:$(TARGET_OUT_PRODUCT) \\\n  --dirmap /system_ext:$(TARGET_OUT_SYSTEM_EXT) \\\n  --dirmap /apex:$(APEX_OUT) \\\n\nifdef PRODUCT_SHIPPING_API_LEVEL\ncheck_vintf_compatible_args += --property ro.product.first_api_level=$(PRODUCT_SHIPPING_API_LEVEL)\nendif # PRODUCT_SHIPPING_API_LEVEL\n\n$(check_vintf_compatible_log): PRIVATE_CHECK_VINTF_ARGS := $(check_vintf_compatible_args)\n$(check_vintf_compatible_log): PRIVATE_CHECK_VINTF_DEPS := $(check_vintf_compatible_deps)\n# Check ODM SKU=(empty) case when:\n# - ODM_MANIFEST_FILES is not empty; OR\n# - ODM_MANIFEST_FILES is empty AND ODM_MANIFEST_SKUS is empty (only ODM manifest fragments are used)\n$(check_vintf_compatible_log): PRIVATE_ODM_SKUS := \\\n  $(if $(ODM_MANIFEST_FILES),EMPTY_ODM_SKU_PLACEHOLDER,\\\n    $(if $(ODM_MANIFEST_SKUS),,EMPTY_ODM_SKU_PLACEHOLDER)) \\\n  $(ODM_MANIFEST_SKUS)\n# Check vendor SKU=(empty) case when:\n# - DEVICE_MANIFEST_FILE is not empty; OR\n# - DEVICE_MANIFEST_FILE is empty AND DEVICE_MANIFEST_SKUS is empty (only vendor manifest fragments are used)\n$(check_vintf_compatible_log): PRIVATE_VENDOR_SKUS := \\\n  $(if $(DEVICE_MANIFEST_FILE),EMPTY_VENDOR_SKU_PLACEHOLDER,\\\n    $(if $(DEVICE_MANIFEST_SKUS),,EMPTY_VENDOR_SKU_PLACEHOLDER)) \\\n  $(DEVICE_MANIFEST_SKUS)\n$(check_vintf_compatible_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_compatible_deps)\n\t@echo \"PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS=$(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS)\" > $@\n\t@echo -n -e 'Deps: \\n  ' >> $@\n\t@sed 's/ /\\n  /g' <<< \"$(PRIVATE_CHECK_VINTF_DEPS)\" >> $@\n\t@echo -n -e 'Args: \\n  ' >> $@\n\t@cat <<< \"$(PRIVATE_CHECK_VINTF_ARGS)\" >> $@\n\t$(foreach odm_sku,$(PRIVATE_ODM_SKUS), $(foreach vendor_sku,$(PRIVATE_VENDOR_SKUS), \\\n\t  echo \"For ODM SKU = $(odm_sku), vendor SKU = $(vendor_sku)\" >> $@; \\\n\t  ( $< --check-compat $(PRIVATE_CHECK_VINTF_ARGS) \\\n\t       --property ro.boot.product.hardware.sku=$(filter-out EMPTY_ODM_SKU_PLACEHOLDER,$(odm_sku)) \\\n\t       --property ro.boot.product.vendor.sku=$(filter-out EMPTY_VENDOR_SKU_PLACEHOLDER,$(vendor_sku)) \\\n\t       >> $@ 2>&1 ) || (cat $@ && exit 1); ))\n\n$(call declare-1p-target,$(check_vintf_compatible_log))\n\ncheck_vintf_compatible_log :=\ncheck_vintf_compatible_args :=\ncheck_vintf_compatible_deps :=\n\nendif # BUILDING_SYSTEM_EXT_IMAGE equals BOARD_USES_SYSTEM_EXTIMAGE\nendif # BUILDING_PRODUCT_IMAGE equals BOARD_USES_PRODUCTIMAGE\nendif # BUILDING_ODM_IMAGE equals BOARD_USES_ODMIMAGE\nendif # check_vintf_has_vendor\nendif # check_vintf_has_system\nendif # PRODUCT_ENFORCE_VINTF_MANIFEST\n\n# Add all logs of VINTF checks to dist builds\ndroid_targets: $(check_vintf_all_deps)\n$(call dist-for-goals, droid_targets, $(check_vintf_all_deps))\n\n# Helper alias to check all VINTF of current build.\n.PHONY: check-vintf-all\ncheck-vintf-all: $(check_vintf_all_deps)\n\t$(foreach file,$^,echo \"$(file)\"; cat \"$(file)\"; echo;)\n\ncheck_vintf_has_vendor :=\ncheck_vintf_has_system :=\ncheck_vintf_common_srcs :=\ncheck_vintf_all_deps :=\nintermediates :=\nendif # !TARGET_BUILD_UNBUNDLED\n\n# -----------------------------------------------------------------\n# Check image sizes <= size of super partition\n\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n\nifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION))\n\nPARTITIONS_AND_OTHER_IN_SUPER := $(BOARD_SUPER_PARTITION_PARTITION_LIST)\n\n# Add the system other image to the misc_info. Because factory ota may install system_other to the super partition.\nifdef BUILDING_SYSTEM_OTHER_IMAGE\nPARTITIONS_AND_OTHER_IN_SUPER += system_other\nendif # BUILDING_SYSTEM_OTHER_IMAGE\n\n# $(1): misc_info.txt\n# #(2): optional log file\ndefine check-all-partition-sizes-target\n  mkdir -p $(dir $(1))\n  rm -f $(1)\n  $(call dump-super-image-info, $(1))\n  $(foreach partition,$(PARTITIONS_AND_OTHER_IN_SUPER), \\\n    echo \"$(partition)_image=\"$(call images-for-partitions,$(partition)) >> $(1);)\n  $(CHECK_PARTITION_SIZES) $(if $(2),--logfile $(2),-v) $(1)\nendef\n\ncheck_all_partition_sizes_log := $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes)/check_all_partition_sizes.log\ndroid_targets: $(check_all_partition_sizes_log)\n$(call dist-for-goals, droid_targets, $(check_all_partition_sizes_log))\n\n$(check_all_partition_sizes_log): \\\n    $(CHECK_PARTITION_SIZES) \\\n    $(call images-for-partitions,$(PARTITIONS_AND_OTHER_IN_SUPER))\n\t$(call check-all-partition-sizes-target, \\\n\t  $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes)/misc_info.txt, \\\n\t  $@)\n\n$(call declare-1p-target,$(check_all_partition_sizes_log))\n\n.PHONY: check-all-partition-sizes\ncheck-all-partition-sizes: $(check_all_partition_sizes_log)\n\n.PHONY: check-all-partition-sizes-nodeps\ncheck-all-partition-sizes-nodeps:\n\t$(call check-all-partition-sizes-target, \\\n\t  $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes-nodeps)/misc_info.txt)\n\nendif # PRODUCT_BUILD_SUPER_PARTITION\n\nendif # !TARGET_BUILD_UNBUNDLED\n\n# -----------------------------------------------------------------\n# bring in the installer image generation defines if necessary\nifeq ($(TARGET_USE_DISKINSTALLER),true)\ninclude bootable/diskinstaller/config.mk\nendif\n\n# -----------------------------------------------------------------\n# host tools needed to build dist and OTA packages\n\nifeq ($(BUILD_OS),darwin)\n  build_ota_package := false\n  build_otatools_package := false\nelse\n  # Set build_ota_package, and allow opt-out below.\n  build_ota_package := true\n  ifeq ($(TARGET_SKIP_OTA_PACKAGE),true)\n    build_ota_package := false\n  endif\n  ifneq (,$(filter address, $(SANITIZE_TARGET)))\n    build_ota_package := false\n  endif\n  ifeq ($(TARGET_PRODUCT),sdk)\n    build_ota_package := false\n  endif\n  # A target without a kernel may be one of the following:\n  # - A generic target. In this case, the OTA package usually isn't built.\n  #   PRODUCT_BUILD_GENERIC_OTA_PACKAGE may be set to true to force OTA package\n  #   generation.\n  # - A real device target, with TARGET_NO_KERNEL set to true and\n  #   BOARD_PREBUILT_BOOTIMAGE set. In this case, it is valid to generate\n  #   an OTA package.\n  ifneq ($(PRODUCT_BUILD_GENERIC_OTA_PACKAGE),true)\n    ifneq ($(filter generic%,$(TARGET_DEVICE)),)\n      build_ota_package := false\n    endif\n    ifeq ($(INSTALLED_BOOTIMAGE_TARGET),)\n      ifeq ($(TARGET_NO_KERNEL),true)\n        build_ota_package := false\n      endif\n    endif # INSTALLED_BOOTIMAGE_TARGET == \"\"\n    ifeq ($(recovery_fstab),)\n      ifeq ($(filter $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.fstab,$(INTERNAL_RECOVERYIMAGE_FILES)),)\n        build_ota_package := false\n      endif\n    endif\n  endif # PRODUCT_BUILD_GENERIC_OTA_PACKAGE\n\n  # Set build_otatools_package, and allow opt-out below.\n  build_otatools_package := true\n  ifeq ($(TARGET_SKIP_OTATOOLS_PACKAGE),true)\n    build_otatools_package := false\n  endif\nendif\n\nifeq ($(build_otatools_package),true)\n\nINTERNAL_OTATOOLS_MODULES := \\\n  aapt2 \\\n  add_img_to_target_files \\\n  apksigner \\\n  append2simg \\\n  avbtool \\\n  blk_alloc_to_base_fs \\\n  boot_signer \\\n  brillo_update_payload \\\n  brotli \\\n  bsdiff \\\n  build_image \\\n  build_mixed_kernels_ramdisk_host \\\n  build_super_image \\\n  build_verity_metadata \\\n  build_verity_tree \\\n  care_map_generator \\\n  check_ota_package_signature \\\n  check_target_files_signatures \\\n  check_target_files_vintf \\\n  checkvintf \\\n  create_brick_ota \\\n  delta_generator \\\n  e2fsck \\\n  e2fsdroid \\\n  fc_sort \\\n  fec \\\n  fsck.erofs \\\n  fsck.f2fs \\\n  fs_config \\\n  generate_verity_key \\\n  host_init_verifier \\\n  img2simg \\\n  img_from_target_files \\\n  imgdiff \\\n  initrd_bootconfig \\\n  libconscrypt_openjdk_jni \\\n  lpmake \\\n  lpunpack \\\n  lz4 \\\n  make_f2fs \\\n  make_f2fs_casefold \\\n  merge_ota \\\n  merge_target_files \\\n  mk_combined_img \\\n  mkbootfs \\\n  mkbootimg \\\n  mke2fs \\\n  mke2fs.conf \\\n  mkfs.erofs \\\n  mkf2fsuserimg \\\n  mksquashfs \\\n  mksquashfsimage \\\n  mkuserimg_mke2fs \\\n  ota_extractor \\\n  ota_from_target_files \\\n  repack_bootimg \\\n  secilc \\\n  sefcontext_compile \\\n  sgdisk \\\n  shflags \\\n  sign_apex \\\n  sign_target_files_apks \\\n  sign_virt_apex \\\n  signapk \\\n  simg2img \\\n  sload_f2fs \\\n  toybox \\\n  tune2fs \\\n  unpack_bootimg \\\n  update_device \\\n  update_host_simulator \\\n  validate_target_files \\\n  verity_signer \\\n  verity_verifier \\\n  zipalign \\\n  zucchini \\\n  zip2zip \\\n\n\n# Additional tools to unpack and repack the apex file.\nINTERNAL_OTATOOLS_MODULES += \\\n  apexd_host \\\n  apexer \\\n  apex_compression_tool \\\n  deapexer \\\n  debugfs_static \\\n  fsck.erofs \\\n  make_erofs \\\n  merge_zips \\\n  resize2fs \\\n  soong_zip \\\n\nINTERNAL_OTATOOLS_FILES := \\\n  $(filter $(HOST_OUT)/%,$(call module-installed-files,$(INTERNAL_OTATOOLS_MODULES)))\n\n.PHONY: otatools\notatools: $(INTERNAL_OTATOOLS_FILES)\n\nendif # build_otatools_package\n\n# -----------------------------------------------------------------\n#  fastboot-info.txt\nFASTBOOT_INFO_VERSION = 1\n\nINSTALLED_FASTBOOT_INFO_TARGET := $(PRODUCT_OUT)/fastboot-info.txt\nifdef TARGET_BOARD_FASTBOOT_INFO_FILE\n$(INSTALLED_FASTBOOT_INFO_TARGET): $(TARGET_BOARD_FASTBOOT_INFO_FILE)\n\trm -f $@\n\t$(call pretty,\"Target fastboot-info.txt: $@\")\n\t$(hide) cp $< $@\nelse\n$(INSTALLED_FASTBOOT_INFO_TARGET):\n\trm -f $@\n\t$(call pretty,\"Target fastboot-info.txt: $@\")\n\t$(hide) echo \"# fastboot-info for $(TARGET_PRODUCT)\" >> $@\n\t$(hide) echo \"version $(FASTBOOT_INFO_VERSION)\" >> $@\nifneq ($(INSTALLED_BOOTIMAGE_TARGET),)\n\t$(hide) echo \"flash boot\" >> $@\nendif\nifneq ($(INSTALLED_INIT_BOOT_IMAGE_TARGET),)\n\t$(hide) echo \"flash init_boot\" >> $@\nendif\nifdef BOARD_PREBUILT_DTBOIMAGE\n\t$(hide) echo \"flash dtbo\" >> $@\nendif\nifneq ($(INSTALLED_DTIMAGE_TARGET),)\n\t$(hide) echo \"flash dts dt.img\" >> $@\nendif\nifneq ($(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),)\n\t$(hide) echo \"flash vendor_kernel_boot\" >> $@\nendif\nifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)\n\t$(hide) echo \"flash recovery\" >> $@\nendif\nifeq ($(BOARD_USES_PVMFWIMAGE),true)\n\t$(hide) echo \"flash pvmfw\" >> $@\nendif\nifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),)\n\t$(hide) echo \"flash vendor_boot\" >> $@\nendif\nifeq ($(BOARD_AVB_ENABLE),true)\nifeq ($(BUILDING_VBMETA_IMAGE),true)\n\t$(hide) echo \"flash --apply-vbmeta vbmeta\" >> $@\nendif\nifneq (,$(strip $(BOARD_AVB_VBMETA_SYSTEM)))\n\t$(hide) echo \"flash vbmeta_system\" >> $@\nendif\nifneq (,$(strip $(BOARD_AVB_VBMETA_VENDOR)))\n\t$(hide) echo \"flash vbmeta_vendor\" >> $@\nendif\nifneq (,$(strip $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)))\n\t$(hide) $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS), \\\n\t  echo \"flash vbmeta_$(partition)\" >> $@;)\nendif\nendif # BOARD_AVB_ENABLE\nifneq (,$(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)))\n\t$(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \\\n\t\t$(if $(BOARD_$(call to-upper,$(partition))_IMAGE_NO_FLASHALL),, \\\n\t      echo \"flash $(partition)\" >> $@; \\\n\t\t) \\\n\t)\nendif\n\t$(hide) echo \"reboot fastboot\" >> $@\n\t$(hide) echo \"update-super\" >> $@\n\t$(hide) $(foreach partition,$(BOARD_SUPER_PARTITION_PARTITION_LIST), \\\n\t  echo \"flash $(partition)\" >> $@;)\nifdef BUILDING_SYSTEM_OTHER_IMAGE\n\t$(hide) echo \"flash --slot-other system system_other.img\" >> $@\nendif\nifdef BUILDING_CACHE_IMAGE\n\t$(hide) echo \"if-wipe erase cache\" >> $@\nendif\n\t$(hide) echo \"if-wipe erase userdata\" >> $@\nifeq ($(BOARD_USES_METADATA_PARTITION),true)\n\t$(hide) echo \"if-wipe erase metadata\" >> $@\nendif\nendif\n\n# -----------------------------------------------------------------\n#  misc_info.txt\n\nINSTALLED_MISC_INFO_TARGET := $(PRODUCT_OUT)/misc_info.txt\n\nifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),)\n# default to common dir for device vendor\ntool_extensions := $(TARGET_DEVICE_DIR)/../common\nelse\ntool_extensions := $(TARGET_RELEASETOOLS_EXTENSIONS)\nendif\n.KATI_READONLY := tool_extensions\n\n# $1: boot image file name\ndefine misc_boot_size\n$(subst .img,_size,$(1))=$(BOARD_KERNEL$(call to-upper,$(subst boot,,$(subst .img,,$(1))))_BOOTIMAGE_PARTITION_SIZE)\nendef\n\n$(INSTALLED_MISC_INFO_TARGET):\n\trm -f $@\n\t$(call pretty,\"Target misc_info.txt: $@\")\n\t$(hide) echo \"recovery_api_version=$(RECOVERY_API_VERSION)\" >> $@\n\t$(hide) echo \"fstab_version=$(RECOVERY_FSTAB_VERSION)\" >> $@\nifdef BOARD_FLASH_BLOCK_SIZE\n\t$(hide) echo \"blocksize=$(BOARD_FLASH_BLOCK_SIZE)\" >> $@\nendif\nifneq ($(strip $(BOARD_BOOTIMAGE_PARTITION_SIZE))$(strip $(BOARD_KERNEL_BINARIES)),)\n\t$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),\\\n\t\techo \"$(call misc_boot_size,$(notdir $(b)))\" >> $@;)\nendif\nifeq ($(INSTALLED_BOOTIMAGE_TARGET),)\n\t$(hide) echo \"no_boot=true\" >> $@\nelse\n\techo \"boot_images=$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(notdir $(b)))\" >> $@\nendif\nifneq ($(INSTALLED_INIT_BOOT_IMAGE_TARGET),)\n\t$(hide) echo \"init_boot=true\" >> $@\n\t$(hide) echo \"init_boot_size=$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)\" >> $@\nendif\nifeq ($(BOARD_RAMDISK_USE_LZ4),true)\n\techo \"lz4_ramdisks=true\" >> $@\nendif\nifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),)\n\techo \"vendor_boot=true\" >> $@\n\techo \"vendor_boot_size=$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)\" >> $@\nendif\nifneq ($(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),)\n\techo \"vendor_kernel_boot=true\" >> $@\n\techo \"vendor_kernel_boot_size=$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE)\" >> $@\nendif\nifeq ($(INSTALLED_RECOVERYIMAGE_TARGET),)\n\t$(hide) echo \"no_recovery=true\" >> $@\nendif\nifdef BOARD_INCLUDE_RECOVERY_DTBO\n\t$(hide) echo \"include_recovery_dtbo=true\" >> $@\nendif\nifdef BOARD_INCLUDE_RECOVERY_ACPIO\n\t$(hide) echo \"include_recovery_acpio=true\" >> $@\nendif\nifdef BOARD_RECOVERYIMAGE_PARTITION_SIZE\n\t$(hide) echo \"recovery_size=$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)\" >> $@\nendif\nifdef TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS\n\t@# TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS can be empty to indicate that nothing but defaults should be used.\n\t$(hide) echo \"recovery_mount_options=$(TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS)\" >> $@\nelse\n\t$(hide) echo \"recovery_mount_options=$(DEFAULT_TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS)\" >> $@\nendif\n\t$(hide) echo \"tool_extensions=$(tool_extensions)\" >> $@\n\t$(hide) echo \"default_system_dev_certificate=$(DEFAULT_SYSTEM_DEV_CERTIFICATE)\" >> $@\nifdef PRODUCT_EXTRA_OTA_KEYS\n\t$(hide) echo \"extra_ota_keys=$(PRODUCT_EXTRA_OTA_KEYS)\" >> $@\nendif\nifdef PRODUCT_EXTRA_RECOVERY_KEYS\n\t$(hide) echo \"extra_recovery_keys=$(PRODUCT_EXTRA_RECOVERY_KEYS)\" >> $@\nendif\n\t$(hide) echo 'mkbootimg_args=$(BOARD_MKBOOTIMG_ARGS)' >> $@\n\t$(hide) echo 'recovery_mkbootimg_args=$(BOARD_RECOVERY_MKBOOTIMG_ARGS)' >> $@\n\t$(hide) echo 'mkbootimg_version_args=$(INTERNAL_MKBOOTIMG_VERSION_ARGS)' >> $@\n\t$(hide) echo 'mkbootimg_init_args=$(BOARD_MKBOOTIMG_INIT_ARGS)' >> $@\n\t$(hide) echo \"multistage_support=1\" >> $@\n\t$(hide) echo \"blockimgdiff_versions=3,4\" >> $@\nifeq ($(PRODUCT_BUILD_GENERIC_OTA_PACKAGE),true)\n\t$(hide) echo \"build_generic_ota_package=true\" >> $@\nendif\nifneq ($(OEM_THUMBPRINT_PROPERTIES),)\n\t# OTA scripts are only interested in fingerprint related properties\n\t$(hide) echo \"oem_fingerprint_properties=$(OEM_THUMBPRINT_PROPERTIES)\" >> $@\nendif\nifneq (,$(filter address, $(SANITIZE_TARGET)))\n\t# We need to create userdata.img with real data because the instrumented libraries are in userdata.img.\n\t$(hide) echo \"userdata_img_with_data=true\" >> $@\nendif\nifeq ($(BOARD_USES_FULL_RECOVERY_IMAGE),true)\n\t$(hide) echo \"full_recovery_image=true\" >> $@\nendif\nifdef BOARD_USES_VENDORIMAGE\n\t$(hide) echo \"board_uses_vendorimage=true\" >> $@\nendif\nifeq ($(BOARD_AVB_ENABLE),true)\nifeq ($(BUILDING_VBMETA_IMAGE),true)\n\t$(hide) echo \"avb_building_vbmeta_image=true\" >> $@\nendif # BUILDING_VBMETA_IMAGE\n\t$(hide) echo \"avb_enable=true\" >> $@\n\t$(hide) echo \"avb_vbmeta_key_path=$(BOARD_AVB_KEY_PATH)\" >> $@\n\t$(hide) echo \"avb_vbmeta_algorithm=$(BOARD_AVB_ALGORITHM)\" >> $@\n\t$(hide) echo \"avb_vbmeta_args=$(BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS)\" >> $@\n\t$(hide) echo \"avb_boot_add_hash_footer_args=$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)\" >> $@\nifdef BOARD_AVB_BOOT_KEY_PATH\n\t$(hide) echo \"avb_boot_key_path=$(BOARD_AVB_BOOT_KEY_PATH)\" >> $@\n\t$(hide) echo \"avb_boot_algorithm=$(BOARD_AVB_BOOT_ALGORITHM)\" >> $@\n\t$(hide) echo \"avb_boot_rollback_index_location=$(BOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION)\" >> $@\nendif # BOARD_AVB_BOOT_KEY_PATH\n\t$(hide) echo \"avb_init_boot_add_hash_footer_args=$(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)\" >> $@\nifdef BOARD_AVB_INIT_BOOT_KEY_PATH\n\t$(hide) echo \"avb_init_boot_key_path=$(BOARD_AVB_INIT_BOOT_KEY_PATH)\" >> $@\n\t$(hide) echo \"avb_init_boot_algorithm=$(BOARD_AVB_INIT_BOOT_ALGORITHM)\" >> $@\n\t$(hide) echo \"avb_init_boot_rollback_index_location=$(BOARD_AVB_INIT_BOOT_ROLLBACK_INDEX_LOCATION)\" >> $@\nendif # BOARD_AVB_INIT_BOOT_KEY_PATH\n\techo \"avb_vendor_boot_add_hash_footer_args=$(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS)\" >> $@\nifdef BOARD_AVB_VENDOR_BOOT_KEY_PATH\n\techo \"avb_vendor_boot_key_path=$(BOARD_AVB_VENDOR_BOOT_KEY_PATH)\" >> $@\n\techo \"avb_vendor_boot_algorithm=$(BOARD_AVB_VENDOR_BOOT_ALGORITHM)\" >> $@\n\techo \"avb_vendor_boot_rollback_index_location=$(BOARD_AVB_VENDOR_BOOT_ROLLBACK_INDEX_LOCATION)\" >> $@\nendif # BOARD_AVB_VENDOR_BOOT_KEY_PATH\n\techo \"avb_vendor_kernel_boot_add_hash_footer_args=$(BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS)\" >> $@\nifdef BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH\n\techo \"avb_vendor_kernel_boot_key_path=$(BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH)\" >> $@\n\techo \"avb_vendor_kernel_boot_algorithm=$(BOARD_AVB_VENDOR_KERNEL_BOOT_ALGORITHM)\" >> $@\n\techo \"avb_vendor_kernel_boot_rollback_index_location=$(BOARD_AVB_VENDOR_KERNEL_BOOT_ROLLBACK_INDEX_LOCATION)\" >> $@\nendif # BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH\n\t$(hide) echo \"avb_recovery_add_hash_footer_args=$(BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS)\" >> $@\nifdef BOARD_AVB_RECOVERY_KEY_PATH\n\t$(hide) echo \"avb_recovery_key_path=$(BOARD_AVB_RECOVERY_KEY_PATH)\" >> $@\n\t$(hide) echo \"avb_recovery_algorithm=$(BOARD_AVB_RECOVERY_ALGORITHM)\" >> $@\n\t$(hide) echo \"avb_recovery_rollback_index_location=$(BOARD_AVB_RECOVERY_ROLLBACK_INDEX_LOCATION)\" >> $@\nendif # BOARD_AVB_RECOVERY_KEY_PATH\nifneq (,$(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)))\n\t$(hide) echo \"custom_images_partition_list=$(filter-out $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), $(BOARD_CUSTOMIMAGES_PARTITION_LIST))\" >> $@\n\t$(hide) $(foreach partition,$(filter-out $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), $(BOARD_CUSTOMIMAGES_PARTITION_LIST)), \\\n\t    echo \"$(partition)_image_list=$(foreach image,$(BOARD_$(call to-upper,$(partition))_IMAGE_LIST),$(notdir $(image)))\" >> $@;)\nendif # BOARD_CUSTOMIMAGES_PARTITION_LIST\nifneq (,$(strip $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST)))\n\t$(hide) echo \"avb_custom_images_partition_list=$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST)\" >> $@\n\t$(hide) $(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \\\n\t    echo \"avb_$(partition)_key_path=$(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH)\"  >> $@; \\\n\t    echo \"avb_$(partition)_algorithm=$(BOARD_AVB_$(call to-upper,$(partition))_ALGORITHM)\"  >> $@; \\\n\t    echo \"avb_$(partition)_add_hashtree_footer_args=$(BOARD_AVB_$(call to-upper,$(partition))_ADD_HASHTREE_FOOTER_ARGS)\"  >> $@; \\\n\t    echo \"avb_$(partition)_rollback_index_location=$(BOARD_AVB_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)\"  >> $@; \\\n\t    echo \"avb_$(partition)_partition_size=$(BOARD_AVB_$(call to-upper,$(partition))_PARTITION_SIZE)\"  >> $@; \\\n\t    echo \"avb_$(partition)_image_list=$(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST),$(notdir $(image)))\" >> $@;)\nendif # BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST\nifneq (,$(strip $(BOARD_AVB_VBMETA_SYSTEM)))\n\t$(hide) echo \"avb_vbmeta_system=$(BOARD_AVB_VBMETA_SYSTEM)\" >> $@\n\t$(hide) echo \"avb_vbmeta_system_args=$(BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS)\" >> $@\n\t$(hide) echo \"avb_vbmeta_system_key_path=$(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH)\" >> $@\n\t$(hide) echo \"avb_vbmeta_system_algorithm=$(BOARD_AVB_VBMETA_SYSTEM_ALGORITHM)\" >> $@\n\t$(hide) echo \"avb_vbmeta_system_rollback_index_location=$(BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION)\" >> $@\nendif # BOARD_AVB_VBMETA_SYSTEM\nifneq (,$(strip $(BOARD_AVB_VBMETA_VENDOR)))\n\t$(hide) echo \"avb_vbmeta_vendor=$(BOARD_AVB_VBMETA_VENDOR)\" >> $@\n\t$(hide) echo \"avb_vbmeta_vendor_args=$(BOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS)\" >> $@\n\t$(hide) echo \"avb_vbmeta_vendor_key_path=$(BOARD_AVB_VBMETA_VENDOR_KEY_PATH)\" >> $@\n\t$(hide) echo \"avb_vbmeta_vendor_algorithm=$(BOARD_AVB_VBMETA_VENDOR_ALGORITHM)\" >> $@\n\t$(hide) echo \"avb_vbmeta_vendor_rollback_index_location=$(BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION)\" >> $@\nendif # BOARD_AVB_VBMETA_VENDOR_KEY_PATH\nifneq (,$(strip $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)))\n\t$(hide) echo \"avb_custom_vbmeta_images_partition_list=$(sort $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS))\" >> $@\n\t$(hide) $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),\\\n\techo \"avb_vbmeta_$(partition)=$(BOARD_AVB_VBMETA_$(call to-upper,$(partition)))\" >> $@ ;\\\n\techo \"avb_vbmeta_$(partition)_args=$(BOARD_AVB_MAKE_VBMETA_$(call to-upper,$(partition))_IMAGE_ARGS)\" >> $@ ;\\\n\techo \"avb_vbmeta_$(partition)_key_path=$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_KEY_PATH)\" >> $@ ;\\\n\techo \"avb_vbmeta_$(partition)_algorithm=$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ALGORITHM)\" >> $@ ;\\\n\techo \"avb_vbmeta_$(partition)_rollback_index_location=$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)\" >> $@ ;)\nendif # BOARD_AVB_VBMETA_CUSTOM_PARTITIONS\nendif # BOARD_AVB_ENABLE\n\t$(call generate-userimage-prop-dictionary, $@)\nifeq ($(AB_OTA_UPDATER),true)\n\t@# Include the build type in META/misc_info.txt so the server can easily differentiate production builds.\n\t$(hide) echo \"build_type=$(TARGET_BUILD_VARIANT)\" >> $@\n\t$(hide) echo \"ab_update=true\" >> $@\nendif\nifeq ($(TARGET_OTA_ALLOW_NON_AB),true)\n\t$(hide) echo \"allow_non_ab=true\" >> $@\nendif\nifeq ($(BOARD_NON_AB_OTA_DISABLE_COMPRESSION),true)\n\t$(hide) echo \"board_non_ab_ota_disable_compression=true\" >> $@\nendif\nifdef BOARD_PREBUILT_DTBOIMAGE\n\t$(hide) echo \"has_dtbo=true\" >> $@\nifeq ($(BOARD_AVB_ENABLE),true)\n\t$(hide) echo \"dtbo_size=$(BOARD_DTBOIMG_PARTITION_SIZE)\" >> $@\n\t$(hide) echo \"avb_dtbo_add_hash_footer_args=$(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS)\" >> $@\nifdef BOARD_AVB_DTBO_KEY_PATH\n\t$(hide) echo \"avb_dtbo_key_path=$(BOARD_AVB_DTBO_KEY_PATH)\" >> $@\n\t$(hide) echo \"avb_dtbo_algorithm=$(BOARD_AVB_DTBO_ALGORITHM)\" >> $@\n\t$(hide) echo \"avb_dtbo_rollback_index_location=$(BOARD_AVB_DTBO_ROLLBACK_INDEX_LOCATION)\" >> $@\nendif # BOARD_AVB_DTBO_KEY_PATH\nendif # BOARD_AVB_ENABLE\nendif # BOARD_PREBUILT_DTBOIMAGE\nifeq ($(BOARD_USES_PVMFWIMAGE),true)\n\t$(hide) echo \"has_pvmfw=true\" >> $@\nifeq ($(BOARD_AVB_ENABLE),true)\n\t$(hide) echo \"pvmfw_size=$(BOARD_PVMFWIMAGE_PARTITION_SIZE)\" >> $@\n\t$(hide) echo \"avb_pvmfw_add_hash_footer_args=$(BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS)\" >> $@\nifdef BOARD_AVB_PVMFW_KEY_PATH\n\t$(hide) echo \"avb_pvmfw_key_path=$(BOARD_AVB_PVMFW_KEY_PATH)\" >> $@\n\t$(hide) echo \"avb_pvmfw_algorithm=$(BOARD_AVB_PVMFW_ALGORITHM)\" >> $@\n\t$(hide) echo \"avb_pvmfw_rollback_index_location=$(BOARD_AVB_PVMFW_ROLLBACK_INDEX_LOCATION)\" >> $@\nendif # BOARD_AVB_PVMFW_KEY_PATH\nendif # BOARD_AVB_ENABLE\nendif # BOARD_USES_PVMFWIMAGE\n\t$(call dump-dynamic-partitions-info,$@)\n\t@# VINTF checks\nifeq ($(PRODUCT_ENFORCE_VINTF_MANIFEST),true)\n\t$(hide) echo \"vintf_enforce=true\" >> $@\nendif\nifdef ODM_MANIFEST_SKUS\n\t$(hide) echo \"vintf_odm_manifest_skus=$(ODM_MANIFEST_SKUS)\" >> $@\nendif\nifdef ODM_MANIFEST_FILES\n\t$(hide) echo \"vintf_include_empty_odm_sku=true\" >> $@\nendif\nifdef DEVICE_MANIFEST_SKUS\n\t$(hide) echo \"vintf_vendor_manifest_skus=$(DEVICE_MANIFEST_SKUS)\" >> $@\nendif\nifdef DEVICE_MANIFEST_FILE\n\t$(hide) echo \"vintf_include_empty_vendor_sku=true\" >> $@\nendif\nifeq ($(BOARD_BOOTLOADER_IN_UPDATE_PACKAGE),true)\n\t$(hide) echo \"bootloader_in_update_package=true\" >> $@\nendif\nifeq ($(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE),true)\n\t$(hide) echo \"exclude_kernel_from_recovery_image=true\" >> $@\nendif\nifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),)\n\t$(hide) echo \"partial_ota_update_partitions_list=$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)\" >> $@\nendif\n\t$(hide) sort -o $@ $@\n\n$(call declare-0p-target,$(INSTALLED_FASTBOOT_INFO_TARGET))\n\n.PHONY: fastboot_info\nfastboot_info: $(INSTALLED_FASTBOOT_INFO_TARGET)\n\ndroidcore-unbundled: $(INSTALLED_FASTBOOT_INFO_TARGET)\n\n$(call declare-0p-target,$(INSTALLED_MISC_INFO_TARGET))\n\n.PHONY: misc_info\nmisc_info: $(INSTALLED_MISC_INFO_TARGET)\n\ndroidcore-unbundled: $(INSTALLED_MISC_INFO_TARGET)\n\n# -----------------------------------------------------------------\n# A zip of the directories that map to the target filesystem.\n# This zip can be used to create an OTA package or filesystem image\n# as a post-build step.\n#\nname := $(TARGET_PRODUCT)\nifeq ($(TARGET_BUILD_TYPE),debug)\n  name := $(name)_debug\nendif\nname := $(name)-target_files\n\nintermediates := $(call intermediates-dir-for,PACKAGING,target_files)\nBUILT_TARGET_FILES_DIR := $(intermediates)/$(name).zip.list\nBUILT_TARGET_FILES_PACKAGE := $(intermediates)/$(name).zip\n$(BUILT_TARGET_FILES_PACKAGE): zip_root := $(intermediates)/$(name)\n$(BUILT_TARGET_FILES_DIR): zip_root := $(intermediates)/$(name)\n$(BUILT_TARGET_FILES_DIR): intermediates := $(intermediates)\n\nifneq ($(SOONG_DEFINED_SYSTEM_IMAGE_PATH),)\n  $(BUILT_TARGET_FILES_DIR): $(SOONG_DEFINED_SYSTEM_IMAGE_PATH)\nendif\n\n# $(1): Directory to copy\n# $(2): Location to copy it to\n# The \"ls -A\" is to skip if $(1) is empty.\ndefine package_files-copy-root\n  if [ -d \"$(strip $(1))\" -a \"$$(ls -A $(1))\" ]; then \\\n    mkdir -p $(2) && \\\n    $(ACP) -rd $(strip $(1))/. $(strip $(2))/; \\\n  fi\nendef\n\nbuilt_ota_tools :=\n\n\n# We can't build static executables when SANITIZE_TARGET=address\nifeq (,$(filter address, $(SANITIZE_TARGET)))\nifeq (false,$(AB_OTA_UPDATER))\nbuilt_ota_tools += \\\n    $(call intermediates-dir-for,EXECUTABLES,updater)/updater\nendif\nendif\n\n$(BUILT_TARGET_FILES_DIR): PRIVATE_OTA_TOOLS := $(built_ota_tools)\n\ntool_extension := $(wildcard $(tool_extensions)/releasetools.py)\n$(BUILT_TARGET_FILES_DIR): PRIVATE_TOOL_EXTENSION := $(tool_extension)\n\nupdater_dep :=\nifeq ($(AB_OTA_UPDATER),true)\nupdater_dep += system/update_engine/update_engine.conf\n$(call declare-1p-target,system/update_engine/update_engine.conf,system/update_engine)\nupdater_dep += external/zucchini/version_info.h\n$(call declare-license-metadata,external/zucchini/version_info.h,legacy_notice,notice,external/zucchini/LICENSE,external/zucchini)\nupdater_dep += $(HOST_OUT_SHARED_LIBRARIES)/liblz4.so\nendif\n\n# Build OTA tools if non-A/B is allowed\nifeq ($(TARGET_OTA_ALLOW_NON_AB),true)\nupdater_dep += $(built_ota_tools)\nendif\n\n$(BUILT_TARGET_FILES_DIR): $(updater_dep)\n\n# If we are using recovery as boot, output recovery files to BOOT/.\n# If we are moving recovery resources to vendor_boot, output recovery files to VENDOR_BOOT/.\nifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n$(BUILT_TARGET_FILES_DIR): PRIVATE_RECOVERY_OUT := BOOT\nelse ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true)\n$(BUILT_TARGET_FILES_DIR): PRIVATE_RECOVERY_OUT := VENDOR_BOOT\nelse\n$(BUILT_TARGET_FILES_DIR): PRIVATE_RECOVERY_OUT := RECOVERY\nendif\n\nifeq ($(AB_OTA_UPDATER),true)\n  ifdef OSRELEASED_DIRECTORY\n    $(BUILT_TARGET_FILES_DIR): $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_id\n    $(BUILT_TARGET_FILES_DIR): $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_version\n    $(BUILT_TARGET_FILES_DIR): $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/system_version\n  endif\nendif\n\nifneq ($(AB_OTA_PARTITIONS),)\n  ifneq ($(AB_OTA_UPDATER),true)\n    $(error AB_OTA_UPDATER must be true when defining AB_OTA_PARTITIONS)\n  endif\nendif\n\n# Run fs_config while creating the target files package\n# $1: root directory\n# $2: add prefix\ndefine fs_config\n(cd $(1); find . -type d | sed 's,$$,/,'; find . \\! -type d) | cut -c 3- | sort | sed 's,^,$(2),' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -R \"$(2)\"\nendef\n\ndefine filter-out-missing-vendor\n$(if $(INSTALLED_VENDORIMAGE_TARGET),$(1),$(filter-out vendor,$(1)))\nendef\ndefine filter-out-missing-vendor_dlkm\n$(if $(INSTALLED_VENDOR_DLKMIMAGE_TARGET),$(1),$(filter-out vendor_dlkm,$(1)))\nendef\ndefine filter-out-missing-odm\n$(if $(INSTALLED_ODMIMAGE_TARGET),$(1),$(filter-out odm,$(1)))\nendef\ndefine filter-out-missing-odm_dlkm\n$(if $(INSTALLED_ODM_DLKMIMAGE_TARGET),$(1),$(filter-out odm_dlkm,$(1)))\nendef\ndefine filter-out-missing-system_dlkm\n$(if $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),$(1),$(filter-out system_dlkm,$(1)))\nendef\n# Filter out vendor,vendor_dlkm,odm,odm_dlkm,system_dlkm from the list for AOSP targets.\n# $(1): list\ndefine filter-out-missing-partitions\n$(call filter-out-missing-vendor,\\\n  $(call filter-out-missing-vendor_dlkm,\\\n    $(call filter-out-missing-odm,\\\n      $(call filter-out-missing-odm_dlkm,\\\n        $(call filter-out-missing-system_dlkm,$(1))))))\nendef\n\n# Information related to dynamic partitions and virtual A/B. This information\n# is needed for building the super image (see dump-super-image-info) and\n# building OTA packages.\n# $(1): file\ndefine dump-dynamic-partitions-info\n  $(if $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)), \\\n    echo \"use_dynamic_partitions=true\" >> $(1))\n  $(if $(filter true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)), \\\n    echo \"dynamic_partition_retrofit=true\" >> $(1))\n  echo \"lpmake=$(notdir $(LPMAKE))\" >> $(1)\n  $(if $(filter true,$(PRODUCT_BUILD_SUPER_PARTITION)), $(if $(BOARD_SUPER_PARTITION_SIZE), \\\n    echo \"build_super_partition=true\" >> $(1)))\n  $(if $(BUILDING_SUPER_EMPTY_IMAGE), \\\n    echo \"build_super_empty_partition=true\" >> $(1))\n  $(if $(filter true,$(BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE)), \\\n    echo \"build_retrofit_dynamic_partitions_ota_package=true\" >> $(1))\n  echo \"super_metadata_device=$(BOARD_SUPER_PARTITION_METADATA_DEVICE)\" >> $(1)\n  $(if $(BOARD_SUPER_PARTITION_BLOCK_DEVICES), \\\n    echo \"super_block_devices=$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)\" >> $(1))\n  $(foreach device,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES), \\\n    echo \"super_$(device)_device_size=$(BOARD_SUPER_PARTITION_$(call to-upper,$(device))_DEVICE_SIZE)\" >> $(1);)\n  $(if $(BOARD_SUPER_PARTITION_PARTITION_LIST), \\\n    echo \"dynamic_partition_list=$(sort $(call filter-out-missing-partitions,$(BOARD_SUPER_PARTITION_PARTITION_LIST)))\" >> $(1))\n  $(if $(BOARD_SUPER_PARTITION_GROUPS),\n    echo \"super_partition_groups=$(BOARD_SUPER_PARTITION_GROUPS)\" >> $(1))\n  $(foreach group,$(BOARD_SUPER_PARTITION_GROUPS), \\\n    echo \"super_$(group)_group_size=$(BOARD_$(call to-upper,$(group))_SIZE)\" >> $(1); \\\n    $(if $(BOARD_$(call to-upper,$(group))_PARTITION_LIST), \\\n      echo \"super_$(group)_partition_list=$(strip $(call filter-out-missing-partitions,$(BOARD_$(call to-upper,$(group))_PARTITION_LIST)))\" >> $(1);))\n  $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED)), \\\n    echo \"build_non_sparse_super_partition=true\" >> $(1))\n  $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_F2FS_DISABLED)), \\\n    echo \"build_non_sparse_super_partition=true\" >> $(1))\n  $(if $(filter true,$(BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE)), \\\n    echo \"super_image_in_update_package=true\" >> $(1))\n  $(if $(BOARD_SUPER_PARTITION_SIZE), \\\n    echo \"super_partition_size=$(BOARD_SUPER_PARTITION_SIZE)\" >> $(1))\n  $(if $(BOARD_SUPER_PARTITION_ALIGNMENT), \\\n    echo \"super_partition_alignment=$(BOARD_SUPER_PARTITION_ALIGNMENT)\" >> $(1))\n  $(if $(BOARD_SUPER_PARTITION_WARN_LIMIT), \\\n    echo \"super_partition_warn_limit=$(BOARD_SUPER_PARTITION_WARN_LIMIT)\" >> $(1))\n  $(if $(BOARD_SUPER_PARTITION_ERROR_LIMIT), \\\n    echo \"super_partition_error_limit=$(BOARD_SUPER_PARTITION_ERROR_LIMIT)\" >> $(1))\n  $(if $(filter true,$(PRODUCT_VIRTUAL_AB_OTA)), \\\n    echo \"virtual_ab=true\" >> $(1))\n  $(if $(filter true,$(PRODUCT_VIRTUAL_AB_COMPRESSION)), \\\n    echo \"virtual_ab_compression=true\" >> $(1))\n# This value controls the compression algorithm used for VABC\n# valid options are defined in system/core/fs_mgr/libsnapshot/cow_writer.cpp\n# e.g. \"none\", \"gz\", \"brotli\"\n  $(if $(PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD), \\\n    echo \"virtual_ab_compression_method=$(PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD)\" >> $(1))\n  $(if $(filter true,$(PRODUCT_VIRTUAL_AB_OTA_RETROFIT)), \\\n    echo \"virtual_ab_retrofit=true\" >> $(1))\n  $(if $(PRODUCT_VIRTUAL_AB_COW_VERSION), \\\n    echo \"virtual_ab_cow_version=$(PRODUCT_VIRTUAL_AB_COW_VERSION)\" >> $(1))\n  $(if $(PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR), \\\n    echo \"virtual_ab_compression_factor=$(PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR)\" >> $(1))\nendef\n\n# Copy an image file to a directory and generate a block list map file from the image,\n# only if the map_file_generator supports the file system.\n# Otherwise, skip generating map files as well as copying images. The image will be\n# generated from the $(ADD_IMG_TO_TARGET_FILES) to generate the map file with it.\n# $(1): path of the image file\n# $(2): target out directory\n# $(3): image name to generate a map file. skip generating map file if empty\ndefine copy-image-and-generate-map\n  $(if $(COPY_IMAGES_FOR_TARGET_FILES_ZIP), \\\n    $(eval _supported_fs_for_map_file_generator := erofs ext%) \\\n    $(eval _img := $(call to-upper,$(3))) \\\n    $(if $(3),$(eval _map_fs_type := $(BOARD_$(_img)IMAGE_FILE_SYSTEM_TYPE)),\\\n      $(eval _no_map_file := \"true\")) \\\n    $(if $(filter $(_supported_fs_for_map_file_generator),$(_map_fs_type))$(_no_map_file),\\\n      mkdir -p $(2); \\\n      cp $(1) $(2); \\\n      $(if $(3),$(HOST_OUT_EXECUTABLES)/map_file_generator $(1) $(2)/$(3).map)) \\\n    $(eval _img :=) \\\n    $(eval _map_fs_type :=) \\\n    $(eval _no_map_file :=) \\\n  )\nendef\n\n# By conditionally including the dependency of the target files package on the\n# full system image deps, we speed up builds that do not build the system\n# image.\nifdef BUILDING_SYSTEM_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(FULL_SYSTEMIMAGE_DEPS)\n  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP\n    $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEMIMAGE)\n  endif\nelse\n  # releasetools may need the system build.prop even when building a\n  # system-image-less product.\n  $(BUILT_TARGET_FILES_DIR): $(INSTALLED_BUILD_PROP_TARGET)\nendif\n\nifdef BUILDING_USERDATA_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_USERDATAIMAGE_FILES)\nendif\n\nifdef BUILDING_SYSTEM_OTHER_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEMOTHERIMAGE_FILES)\n  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP\n    $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEMOTHERIMAGE_TARGET)\n  endif\nendif\n\nifdef BUILDING_VENDOR_BOOT_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_RAMDISK_FILES)\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS)\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_BOOTCONFIG_TARGET)\n  # The vendor ramdisk may be built from the recovery ramdisk.\n  ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))\n    $(BUILT_TARGET_FILES_DIR): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)\n  endif\nendif\n\nifdef BUILDING_VENDOR_KERNEL_BOOT_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES)\nendif\n\nifdef BUILDING_RECOVERY_IMAGE\n  # TODO(b/30414428): Can't depend on INTERNAL_RECOVERYIMAGE_FILES alone like other\n  # BUILT_TARGET_FILES_PACKAGE dependencies because currently there're cp/rsync/rm\n  # commands in build-recoveryimage-target, which would touch the files under\n  # TARGET_RECOVERY_OUT and race with packaging target-files.zip.\n  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n    $(BUILT_TARGET_FILES_DIR): $(INSTALLED_BOOTIMAGE_TARGET)\n  else\n    $(BUILT_TARGET_FILES_DIR): $(INSTALLED_RECOVERYIMAGE_TARGET)\n  endif\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_RECOVERYIMAGE_FILES)\nendif\n\n# Conditionally depend on the image files if the image is being built so the\n# target-files.zip rule doesn't wait on the image creation rule, or the image\n# if it is coming from a prebuilt.\n\nifdef BUILDING_VENDOR_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDORIMAGE_FILES)\n  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP\n    $(BUILT_TARGET_FILES_DIR): $(BUILT_VENDORIMAGE_TARGET)\n  endif\nelse ifdef BOARD_PREBUILT_VENDORIMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INSTALLED_VENDORIMAGE_TARGET)\nendif\n\nifdef BUILDING_PRODUCT_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_PRODUCTIMAGE_FILES)\n  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP\n    $(BUILT_TARGET_FILES_DIR): $(BUILT_PRODUCTIMAGE_TARGET)\n  endif\nelse ifdef BOARD_PREBUILT_PRODUCTIMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INSTALLED_PRODUCTIMAGE_TARGET)\nendif\n\nifdef BUILDING_SYSTEM_EXT_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEM_EXTIMAGE_FILES)\n  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP\n    $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEM_EXTIMAGE_TARGET)\n  endif\nelse ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)\nendif\n\nifneq (,$(BUILDING_BOOT_IMAGE)$(BUILDING_INIT_BOOT_IMAGE))\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_RAMDISK_FILES)\nendif  # BUILDING_BOOT_IMAGE != \"\" || BUILDING_INIT_BOOT_IMAGE != \"\"\n\nifneq (,$(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES)))\n  $(BUILT_TARGET_FILES_DIR): $(INSTALLED_BOOTIMAGE_TARGET)\nendif\n\nifdef BUILDING_ODM_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_ODMIMAGE_FILES)\n  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP\n    $(BUILT_TARGET_FILES_DIR): $(BUILT_ODMIMAGE_TARGET)\n  endif\nelse ifdef BOARD_PREBUILT_ODMIMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INSTALLED_ODMIMAGE_TARGET)\nendif\n\nifdef BUILDING_VENDOR_DLKM_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_DLKMIMAGE_FILES)\n  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP\n    $(BUILT_TARGET_FILES_DIR): $(BUILT_VENDOR_DLKMIMAGE_TARGET)\n  endif\nelse ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)\nendif\n\nifdef BUILDING_ODM_DLKM_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_ODM_DLKMIMAGE_FILES)\n  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP\n    $(BUILT_TARGET_FILES_DIR): $(BUILT_ODM_DLKMIMAGE_TARGET)\n  endif\nelse ifdef BOARD_PREBUILT_ODM_DLKMIMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INSTALLED_ODM_DLKMIMAGE_TARGET)\nendif\n\nifdef BUILDING_SYSTEM_DLKM_IMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEM_DLKMIMAGE_FILES)\n  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP\n    $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEM_DLKMIMAGE_TARGET)\n  endif\nelse ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE\n  $(BUILT_TARGET_FILES_DIR): $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)\nendif\n\nifeq ($(BUILD_QEMU_IMAGES),true)\n  MK_VBMETA_BOOT_KERNEL_CMDLINE_SH := device/generic/goldfish/tools/mk_vbmeta_boot_params.sh\n  $(BUILT_TARGET_FILES_DIR): $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH)\nendif\n\nifdef BOARD_PREBUILT_BOOTLOADER\n$(BUILT_TARGET_FILES_DIR): $(INSTALLED_BOOTLOADER_MODULE)\ndroidcore-unbundled: $(INSTALLED_BOOTLOADER_MODULE)\nendif\n\n# Depending on the various images guarantees that the underlying\n# directories are up-to-date.\n$(BUILT_TARGET_FILES_DIR): \\\n\t    $(INSTALLED_RADIOIMAGE_TARGET) \\\n\t    $(INSTALLED_RECOVERYIMAGE_TARGET) \\\n\t    $(INSTALLED_CACHEIMAGE_TARGET) \\\n\t    $(INSTALLED_DTBOIMAGE_TARGET) \\\n\t    $(INSTALLED_PVMFWIMAGE_TARGET) \\\n\t    $(INSTALLED_PVMFW_BINARY_TARGET) \\\n\t    $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) \\\n\t    $(INSTALLED_CUSTOMIMAGES_TARGET) \\\n\t    $(INSTALLED_ANDROID_INFO_TXT_TARGET) \\\n\t    $(INSTALLED_KERNEL_TARGET) \\\n\t    $(INSTALLED_RAMDISK_TARGET) \\\n\t    $(INSTALLED_DTBIMAGE_TARGET) \\\n\t    $(INSTALLED_2NDBOOTLOADER_TARGET) \\\n\t    $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) \\\n\t    $(BUILT_RAMDISK_16K_TARGET) \\\n\t    $(BUILT_KERNEL_16K_TARGET) \\\n\t    $(BUILT_BOOTIMAGE_16K_TARGET) \\\n\t    $(INSTALLED_DTBOIMAGE_16KB_TARGET) \\\n\t    $(BOARD_PREBUILT_DTBOIMAGE) \\\n\t    $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) \\\n\t    $(BOARD_RECOVERY_ACPIO) \\\n\t    $(PRODUCT_SYSTEM_BASE_FS_PATH) \\\n\t    $(PRODUCT_VENDOR_BASE_FS_PATH) \\\n\t    $(PRODUCT_PRODUCT_BASE_FS_PATH) \\\n\t    $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH) \\\n\t    $(PRODUCT_ODM_BASE_FS_PATH) \\\n\t    $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH) \\\n\t    $(PRODUCT_ODM_DLKM_BASE_FS_PATH) \\\n\t    $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH) \\\n\t    $(LPMAKE) \\\n\t    $(SELINUX_FC) \\\n\t    $(INSTALLED_MISC_INFO_TARGET) \\\n\t    $(INSTALLED_FASTBOOT_INFO_TARGET) \\\n\t    $(APKCERTS_FILE) \\\n\t    $(APEX_KEYS_FILE) \\\n\t    $(SOONG_ZIP) \\\n\t    $(HOST_OUT_EXECUTABLES)/fs_config \\\n\t    $(HOST_OUT_EXECUTABLES)/map_file_generator \\\n\t    $(ADD_IMG_TO_TARGET_FILES) \\\n\t    $(MAKE_RECOVERY_PATCH) \\\n\t    $(BUILT_KERNEL_CONFIGS_FILE) \\\n\t    $(BUILT_KERNEL_VERSION_FILE) \\\n\t    | $(ACP)\n\t@echo \"Building target files: $@\"\n\t$(hide) rm -rf $@ $@.list $(zip_root)\n\t$(hide) mkdir -p $(dir $@) $(zip_root)\nifneq (,$(INSTALLED_RECOVERYIMAGE_TARGET)$(filter true,$(BOARD_USES_RECOVERY_AS_BOOT))$(filter true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)))\n\t@# Components of the recovery image\n\t$(hide) mkdir -p $(zip_root)/$(PRIVATE_RECOVERY_OUT)\n# Exclude recovery files in the default vendor ramdisk if including a standalone\n# recovery ramdisk in vendor_boot.\nifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_RECOVERY_ROOT_OUT),$(zip_root)/$(PRIVATE_RECOVERY_OUT)/RAMDISK)\nendif\nifdef INSTALLED_KERNEL_TARGET\nifneq (,$(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)))\n\tcp $(INSTALLED_KERNEL_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/\nelse ifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE))\n\tcp $(firstword $(INSTALLED_KERNEL_TARGET)) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/kernel\nendif\nendif\nifneq (truetrue,$(strip $(BUILDING_VENDOR_BOOT_IMAGE))$(strip $(BOARD_USES_RECOVERY_AS_BOOT)))\nifdef INSTALLED_2NDBOOTLOADER_TARGET\n\tcp $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/second\nendif\nifdef BOARD_INCLUDE_RECOVERY_DTBO\nifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE\n\tcp $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/recovery_dtbo\nelse\n\tcp $(BOARD_PREBUILT_DTBOIMAGE) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/recovery_dtbo\nendif\nendif # BOARD_INCLUDE_RECOVERY_DTBO\nifdef BOARD_INCLUDE_RECOVERY_ACPIO\n\tcp $(BOARD_RECOVERY_ACPIO) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/recovery_acpio\nendif\nifdef INSTALLED_DTBIMAGE_TARGET\n\tcp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/dtb\nendif\nifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE))\nifdef INTERNAL_KERNEL_CMDLINE\n\techo \"$(INTERNAL_KERNEL_CMDLINE)\" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/cmdline\nendif # INTERNAL_KERNEL_CMDLINE != \"\"\nendif # BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE != true\nifdef BOARD_KERNEL_BASE\n\techo \"$(BOARD_KERNEL_BASE)\" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/base\nendif\nifdef BOARD_KERNEL_PAGESIZE\n\techo \"$(BOARD_KERNEL_PAGESIZE)\" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/pagesize\nendif\nendif # not (BUILDING_VENDOR_BOOT_IMAGE and BOARD_USES_RECOVERY_AS_BOOT)\nendif # INSTALLED_RECOVERYIMAGE_TARGET defined or BOARD_USES_RECOVERY_AS_BOOT is true\n\t@# Components of the boot image\n\t$(hide) mkdir -p $(zip_root)/BOOT\n\t$(hide) mkdir -p $(zip_root)/ROOT\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_ROOT_OUT),$(zip_root)/ROOT)\n\t@# If we are using recovery as boot, this is already done when processing recovery.\nifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_RAMDISK_OUT),$(zip_root)/BOOT/RAMDISK)\nifdef INSTALLED_KERNEL_TARGET\n\t$(hide) cp $(INSTALLED_KERNEL_TARGET) $(zip_root)/BOOT/\nendif\nifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))\n\techo \"$(GENERIC_KERNEL_CMDLINE)\" > $(zip_root)/BOOT/cmdline\nelse ifndef INSTALLED_VENDOR_BOOTIMAGE_TARGET # && BOARD_USES_GENERIC_KERNEL_IMAGE != true\n\techo \"$(INTERNAL_KERNEL_CMDLINE)\" > $(zip_root)/BOOT/cmdline\nifdef INSTALLED_2NDBOOTLOADER_TARGET\n\tcp $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/BOOT/second\nendif\nifdef INSTALLED_DTBIMAGE_TARGET\n\tcp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/BOOT/dtb\nendif\nifdef BOARD_KERNEL_BASE\n\techo \"$(BOARD_KERNEL_BASE)\" > $(zip_root)/BOOT/base\nendif\nifdef BOARD_KERNEL_PAGESIZE\n\techo \"$(BOARD_KERNEL_PAGESIZE)\" > $(zip_root)/BOOT/pagesize\nendif\nendif # INSTALLED_VENDOR_BOOTIMAGE_TARGET == \"\" && BOARD_USES_GENERIC_KERNEL_IMAGE != true\nendif # BOARD_USES_RECOVERY_AS_BOOT not true\n\t$(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\\\n\t            mkdir -p $(zip_root)/RADIO; \\\n\t            cp $(t) $(zip_root)/RADIO/$(notdir $(t));)\nifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET\n\tmkdir -p $(zip_root)/VENDOR_BOOT\n\t$(call package_files-copy-root, \\\n\t    $(TARGET_VENDOR_RAMDISK_OUT),$(zip_root)/VENDOR_BOOT/RAMDISK)\nifdef INSTALLED_DTBIMAGE_TARGET\nifneq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),true)\n\tcp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/VENDOR_BOOT/dtb\nendif\nendif # end of INSTALLED_DTBIMAGE_TARGET\nifdef INTERNAL_VENDOR_BOOTCONFIG_TARGET\n\tcp $(INTERNAL_VENDOR_BOOTCONFIG_TARGET) $(zip_root)/VENDOR_BOOT/vendor_bootconfig\nendif\nifdef BOARD_KERNEL_BASE\n\techo \"$(BOARD_KERNEL_BASE)\" > $(zip_root)/VENDOR_BOOT/base\nendif\nifdef BOARD_KERNEL_PAGESIZE\n\techo \"$(BOARD_KERNEL_PAGESIZE)\" > $(zip_root)/VENDOR_BOOT/pagesize\nendif\n\techo \"$(INTERNAL_KERNEL_CMDLINE)\" > $(zip_root)/VENDOR_BOOT/vendor_cmdline\nifdef INTERNAL_VENDOR_RAMDISK_FRAGMENTS\n\techo \"$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS)\" > \"$(zip_root)/VENDOR_BOOT/vendor_ramdisk_fragments\"\n\t$(foreach vendor_ramdisk_fragment,$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS), \\\n\t  mkdir -p $(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment); \\\n\t  echo \"$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS)\" > \"$(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment)/mkbootimg_args\"; \\\n\t  $(eval prebuilt_ramdisk := $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).PREBUILT)) \\\n\t  $(if $(prebuilt_ramdisk), \\\n\t    cp \"$(prebuilt_ramdisk)\" \"$(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment)/prebuilt_ramdisk\";, \\\n\t    $(call package_files-copy-root, \\\n\t      $(VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR), \\\n\t      $(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment)/RAMDISK); \\\n\t  ))\nendif # INTERNAL_VENDOR_RAMDISK_FRAGMENTS != \"\"\nendif # INSTALLED_VENDOR_BOOTIMAGE_TARGET\nifdef INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET\n\tmkdir -p $(zip_root)/VENDOR_KERNEL_BOOT\n\t$(call package_files-copy-root, \\\n\t    $(TARGET_VENDOR_KERNEL_RAMDISK_OUT),$(zip_root)/VENDOR_KERNEL_BOOT/RAMDISK)\nifdef INSTALLED_DTBIMAGE_TARGET\n\tcp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/VENDOR_KERNEL_BOOT/dtb\nendif\nifdef BOARD_KERNEL_PAGESIZE\n\techo \"$(BOARD_KERNEL_PAGESIZE)\" > $(zip_root)/VENDOR_KERNEL_BOOT/pagesize\nendif\nendif # INSTALLED_VENDOR_BOOTIMAGE_TARGET\nifdef BUILDING_SYSTEM_IMAGE\n\t@# Contents of the system image\nifneq ($(SOONG_DEFINED_SYSTEM_IMAGE_PATH),)\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(SOONG_DEFINED_SYSTEM_IMAGE_BASE)/system/system,$(zip_root)/SYSTEM)\nelse\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)\nendif\nelse ifdef INSTALLED_BUILD_PROP_TARGET\n\t@# Copy the system build.prop even if not building a system image\n\t@# because add_img_to_target_files may need it to build other partition\n\t@# images.\n\t$(hide) mkdir -p \"$(zip_root)/SYSTEM\"\n\t$(hide) cp \"$(INSTALLED_BUILD_PROP_TARGET)\" \"$(patsubst $(TARGET_OUT)/%,$(zip_root)/SYSTEM/%,$(INSTALLED_BUILD_PROP_TARGET))\"\nendif\nifdef BUILDING_USERDATA_IMAGE\n\t@# Contents of the data image\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_OUT_DATA),$(zip_root)/DATA)\nendif\nifdef BUILDING_VENDOR_IMAGE\n\t@# Contents of the vendor image\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_OUT_VENDOR),$(zip_root)/VENDOR)\nendif\nifdef BUILDING_PRODUCT_IMAGE\n\t@# Contents of the product image\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_OUT_PRODUCT),$(zip_root)/PRODUCT)\nendif\nifdef BUILDING_SYSTEM_EXT_IMAGE\n\t@# Contents of the system_ext image\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_OUT_SYSTEM_EXT),$(zip_root)/SYSTEM_EXT)\nendif\nifdef BUILDING_ODM_IMAGE\n\t@# Contents of the odm image\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_OUT_ODM),$(zip_root)/ODM)\nendif\nifdef BUILDING_VENDOR_DLKM_IMAGE\n\t@# Contents of the vendor_dlkm image\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_OUT_VENDOR_DLKM),$(zip_root)/VENDOR_DLKM)\nendif\nifdef BUILDING_ODM_DLKM_IMAGE\n\t@# Contents of the odm_dlkm image\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_OUT_ODM_DLKM),$(zip_root)/ODM_DLKM)\nendif\nifdef BUILDING_SYSTEM_DLKM_IMAGE\n\t@# Contents of the system_dlkm image\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_OUT_SYSTEM_DLKM),$(zip_root)/SYSTEM_DLKM)\nendif\nifdef BUILDING_SYSTEM_OTHER_IMAGE\n\t@# Contents of the system_other image\n\t$(hide) $(call package_files-copy-root, \\\n\t    $(TARGET_OUT_SYSTEM_OTHER),$(zip_root)/SYSTEM_OTHER)\nendif\n\t@# Extra contents of the OTA package\n\t$(hide) mkdir -p $(zip_root)/OTA\n\t$(hide) cp $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/\nifdef BUILDING_RAMDISK_IMAGE\nifeq (true,$(BOARD_IMG_USE_RAMDISK))\n\t@# Contents of the ramdisk image\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_RAMDISK_TARGET) $(zip_root)/IMAGES/\nendif\nendif\nifeq ($(TARGET_OTA_ALLOW_NON_AB),true)\nifneq ($(built_ota_tools),)\n\t$(hide) mkdir -p $(zip_root)/OTA/bin\n\t$(hide) cp $(PRIVATE_OTA_TOOLS) $(zip_root)/OTA/bin/\nendif\nendif\n\t@# Files that do not end up in any images, but are necessary to\n\t@# build them.\n\t$(hide) mkdir -p $(zip_root)/META\n\t$(hide) cp $(APKCERTS_FILE) $(zip_root)/META/apkcerts.txt\n\t$(hide) cp $(APEX_KEYS_FILE) $(zip_root)/META/apexkeys.txt\nifneq ($(tool_extension),)\n\t$(hide) cp $(PRIVATE_TOOL_EXTENSION) $(zip_root)/META/\nendif\n\t$(hide) echo \"$(PRODUCT_OTA_PUBLIC_KEYS)\" > $(zip_root)/META/otakeys.txt\n\t$(hide) cp $(SELINUX_FC) $(zip_root)/META/file_contexts.bin\n\t$(hide) cp $(INSTALLED_MISC_INFO_TARGET) $(zip_root)/META/misc_info.txt\nifneq ($(INSTALLED_FASTBOOT_INFO_TARGET),)\n\t$(hide) cp $(INSTALLED_FASTBOOT_INFO_TARGET) $(zip_root)/META/fastboot-info.txt\nendif\nifneq ($(PRODUCT_SYSTEM_BASE_FS_PATH),)\n\t$(hide) cp $(PRODUCT_SYSTEM_BASE_FS_PATH) \\\n\t  $(zip_root)/META/$(notdir $(PRODUCT_SYSTEM_BASE_FS_PATH))\nendif\nifneq ($(PRODUCT_VENDOR_BASE_FS_PATH),)\n\t$(hide) cp $(PRODUCT_VENDOR_BASE_FS_PATH) \\\n\t  $(zip_root)/META/$(notdir $(PRODUCT_VENDOR_BASE_FS_PATH))\nendif\nifneq ($(PRODUCT_PRODUCT_BASE_FS_PATH),)\n\t$(hide) cp $(PRODUCT_PRODUCT_BASE_FS_PATH) \\\n\t  $(zip_root)/META/$(notdir $(PRODUCT_PRODUCT_BASE_FS_PATH))\nendif\nifneq ($(PRODUCT_SYSTEM_EXT_BASE_FS_PATH),)\n\t$(hide) cp $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH) \\\n\t  $(zip_root)/META/$(notdir $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH))\nendif\nifneq ($(PRODUCT_ODM_BASE_FS_PATH),)\n\t$(hide) cp $(PRODUCT_ODM_BASE_FS_PATH) \\\n\t  $(zip_root)/META/$(notdir $(PRODUCT_ODM_BASE_FS_PATH))\nendif\nifneq ($(PRODUCT_VENDOR_DLKM_BASE_FS_PATH),)\n\t$(hide) cp $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH) \\\n\t  $(zip_root)/META/$(notdir $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH))\nendif\nifneq ($(PRODUCT_ODM_DLKM_BASE_FS_PATH),)\n\t$(hide) cp $(PRODUCT_ODM_DLKM_BASE_FS_PATH) \\\n\t  $(zip_root)/META/$(notdir $(PRODUCT_ODM_DLKM_BASE_FS_PATH))\nendif\nifneq ($(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH),)\n\t$(hide) cp $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH) \\\n\t  $(zip_root)/META/$(notdir $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH))\nendif\nifeq ($(TARGET_OTA_ALLOW_NON_AB),true)\nifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)\n\t$(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \\\n\t    $(MAKE_RECOVERY_PATCH) $(zip_root) $(zip_root)\nendif\nendif\nifeq ($(AB_OTA_UPDATER),true)\n\t@# When using the A/B updater, include the updater config files in the zip.\n\t$(hide) cp $(TOPDIR)system/update_engine/update_engine.conf $(zip_root)/META/update_engine_config.txt\n\t$(hide) cp $(TOPDIR)external/zucchini/version_info.h $(zip_root)/META/zucchini_config.txt\n\t$(hide) cp $(HOST_OUT_SHARED_LIBRARIES)/liblz4.so $(zip_root)/META/liblz4.so\n\t$(hide) for part in $(sort $(AB_OTA_PARTITIONS)); do \\\n\t  echo \"$${part}\" >> $(zip_root)/META/ab_partitions.txt; \\\n\tdone\n\t$(hide) for conf in $(strip $(AB_OTA_POSTINSTALL_CONFIG)); do \\\n\t  echo \"$${conf}\" >> $(zip_root)/META/postinstall_config.txt; \\\n\tdone\nifdef OSRELEASED_DIRECTORY\n\t$(hide) cp $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_id $(zip_root)/META/product_id.txt\n\t$(hide) cp $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_version $(zip_root)/META/product_version.txt\n\t$(hide) cp $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/system_version $(zip_root)/META/system_version.txt\nendif\nendif\nifeq ($(BREAKPAD_GENERATE_SYMBOLS),true)\n\t@# If breakpad symbols have been generated, add them to the zip.\n\t$(hide) cp -R $(TARGET_OUT_BREAKPAD) $(zip_root)/BREAKPAD\nendif\nifdef BOARD_PREBUILT_VENDOR_BOOTIMAGE\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) $(zip_root)/IMAGES/\nendif\nifdef BOARD_PREBUILT_VENDORIMAGE\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_VENDORIMAGE_TARGET) $(zip_root)/IMAGES/\nendif\nifdef BOARD_PREBUILT_PRODUCTIMAGE\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_PRODUCTIMAGE_TARGET) $(zip_root)/IMAGES/\nendif\nifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) $(zip_root)/IMAGES/\nendif\nifdef BOARD_PREBUILT_INIT_BOOT_IMAGE\n\t$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES\n\t$(hide) cp $(INSTALLED_INIT_BOOT_IMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/\nendif\n\nifndef BOARD_PREBUILT_BOOTIMAGE\nifneq (,$(strip $(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES))))\nifdef INSTALLED_BOOTIMAGE_TARGET\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_BOOTIMAGE_TARGET) $(zip_root)/IMAGES/\nendif # INSTALLED_BOOTIMAGE_TARGET\nendif # INTERNAL_PREBUILT_BOOTIMAGE != \"\" || BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES == true\nelse # BOARD_PREBUILT_BOOTIMAGE is defined\n\t$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES\n\t$(hide) cp $(INSTALLED_BOOTIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/\nendif # BOARD_PREBUILT_BOOTIMAGE\nifdef BOARD_PREBUILT_ODMIMAGE\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_ODMIMAGE_TARGET) $(zip_root)/IMAGES/\nendif\nifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(zip_root)/IMAGES/\nendif\nifdef BOARD_PREBUILT_ODM_DLKMIMAGE\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(zip_root)/IMAGES/\nendif\nifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(zip_root)/IMAGES/\nendif\nifdef BOARD_PREBUILT_DTBOIMAGE\n\t$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES\n\t$(hide) cp $(INSTALLED_DTBOIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/\nendif # BOARD_PREBUILT_DTBOIMAGE\nifdef BOARD_KERNEL_PATH_16K\n\t$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES\n\t$(hide) cp $(BUILT_KERNEL_16K_TARGET) $(zip_root)/PREBUILT_IMAGES/\nendif # BOARD_KERNEL_PATH_16K\nifdef BOARD_KERNEL_MODULES_16K\n\t$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES\n\t$(hide) cp $(BUILT_RAMDISK_16K_TARGET) $(zip_root)/PREBUILT_IMAGES/\nendif # BOARD_KERNEL_MODULES_16K\nifdef BUILT_BOOTIMAGE_16K_TARGET\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(BUILT_BOOTIMAGE_16K_TARGET) $(zip_root)/IMAGES/\nendif # BUILT_BOOTIMAGE_16K_TARGET\nifdef INSTALLED_DTBOIMAGE_16KB_TARGET\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_DTBOIMAGE_16KB_TARGET) $(zip_root)/IMAGES/\nendif # INSTALLED_DTBOIMAGE_16KB_TARGET\nifeq ($(BOARD_USES_PVMFWIMAGE),true)\n\t$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES\n\t$(hide) cp $(INSTALLED_PVMFWIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/\n\t$(hide) cp $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) $(zip_root)/PREBUILT_IMAGES/\n\t$(hide) mkdir -p $(zip_root)/PVMFW\n\t$(hide) cp $(INSTALLED_PVMFW_BINARY_TARGET) $(zip_root)/PVMFW/\nendif\nifdef BOARD_PREBUILT_BOOTLOADER\n\t$(hide) mkdir -p $(zip_root)/IMAGES\n\t$(hide) cp $(INSTALLED_BOOTLOADER_MODULE) $(zip_root)/IMAGES/\nendif\nifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),)\n\t$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES\n\t$(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \\\n\t    $(foreach image,$(BOARD_$(call to-upper,$(partition))_IMAGE_LIST),cp $(image) $(zip_root)/PREBUILT_IMAGES/;))\nendif # BOARD_CUSTOMIMAGES_PARTITION_LIST\n\t@# The radio images in BOARD_PACK_RADIOIMAGES will be additionally copied from RADIO/ into\n\t@# IMAGES/, which then will be added into <product>-img.zip. Such images must be listed in\n\t@# INSTALLED_RADIOIMAGE_TARGET.\n\t$(hide) $(foreach part,$(BOARD_PACK_RADIOIMAGES), \\\n\t    echo $(part) >> $(zip_root)/META/pack_radioimages.txt;)\n\t@# Run fs_config on all the system, vendor, boot ramdisk,\n\t@# and recovery ramdisk files in the zip, and save the output\nifdef BUILDING_SYSTEM_IMAGE\n\t$(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEMIMAGE),$(zip_root)/IMAGES,system)\n\t$(hide) $(call fs_config,$(zip_root)/SYSTEM,system/) > $(zip_root)/META/filesystem_config.txt\nendif\nifdef BUILDING_VENDOR_IMAGE\n\t$(hide) $(call copy-image-and-generate-map,$(BUILT_VENDORIMAGE_TARGET),$(zip_root)/IMAGES,vendor)\n\t$(hide) $(call fs_config,$(zip_root)/VENDOR,vendor/) > $(zip_root)/META/vendor_filesystem_config.txt\nendif\nifdef BUILDING_PRODUCT_IMAGE\n\t$(hide) $(call copy-image-and-generate-map,$(BUILT_PRODUCTIMAGE_TARGET),$(zip_root)/IMAGES,product)\n\t$(hide) $(call fs_config,$(zip_root)/PRODUCT,product/) > $(zip_root)/META/product_filesystem_config.txt\nendif\nifdef BUILDING_SYSTEM_EXT_IMAGE\n\t$(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEM_EXTIMAGE_TARGET),$(zip_root)/IMAGES,system_ext)\n\t$(hide) $(call fs_config,$(zip_root)/SYSTEM_EXT,system_ext/) > $(zip_root)/META/system_ext_filesystem_config.txt\nendif\nifdef BUILDING_ODM_IMAGE\n\t$(hide) $(call copy-image-and-generate-map,$(BUILT_ODMIMAGE_TARGET),$(zip_root)/IMAGES,odm)\n\t$(hide) $(call fs_config,$(zip_root)/ODM,odm/) > $(zip_root)/META/odm_filesystem_config.txt\nendif\nifdef BUILDING_VENDOR_DLKM_IMAGE\n\t$(hide)$(call copy-image-and-generate-map,$(BUILT_VENDOR_DLKMIMAGE_TARGET),$(zip_root)/IMAGES,vendor_dlkm)\n\t$(hide) $(call fs_config,$(zip_root)/VENDOR_DLKM,vendor_dlkm/) > $(zip_root)/META/vendor_dlkm_filesystem_config.txt\nendif\nifdef BUILDING_ODM_DLKM_IMAGE\n\t$(hide) $(call copy-image-and-generate-map,$(BUILT_ODM_DLKMIMAGE_TARGET),$(zip_root)/IMAGES,odm_dlkm)\n\t$(hide) $(call fs_config,$(zip_root)/ODM_DLKM,odm_dlkm/) > $(zip_root)/META/odm_dlkm_filesystem_config.txt\nendif\nifdef BUILDING_SYSTEM_DLKM_IMAGE\n\t$(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEM_DLKMIMAGE_TARGET),$(zip_root)/IMAGES,system_dlkm)\n\t$(hide) $(call fs_config,$(zip_root)/SYSTEM_DLKM,system_dlkm/) > $(zip_root)/META/system_dlkm_filesystem_config.txt\nendif\n\t@# ROOT always contains the files for the root under normal boot.\n\t$(hide) $(call fs_config,$(zip_root)/ROOT,) > $(zip_root)/META/root_filesystem_config.txt\n\t@# BOOT/RAMDISK contains the first stage and recovery ramdisk.\n\t$(hide) $(call fs_config,$(zip_root)/BOOT/RAMDISK,) > $(zip_root)/META/boot_filesystem_config.txt\nifdef BUILDING_INIT_BOOT_IMAGE\n\t$(hide) $(call package_files-copy-root, $(TARGET_RAMDISK_OUT),$(zip_root)/INIT_BOOT/RAMDISK)\n\t$(hide) $(call fs_config,$(zip_root)/INIT_BOOT/RAMDISK,) > $(zip_root)/META/init_boot_filesystem_config.txt\n\t$(hide) cp $(RAMDISK_NODE_LIST) $(zip_root)/META/ramdisk_node_list\nifdef BOARD_KERNEL_PAGESIZE\n\t$(hide) echo \"$(BOARD_KERNEL_PAGESIZE)\" > $(zip_root)/INIT_BOOT/pagesize\nendif # BOARD_KERNEL_PAGESIZE\nendif # BUILDING_INIT_BOOT_IMAGE\nifdef BOARD_EROFS_COMPRESS_HINTS\n\t$(hide) cp $(BOARD_EROFS_COMPRESS_HINTS) $(zip_root)/META/erofs_default_compress_hints.txt\nendif\nifdef BOARD_SYSTEMIMAGE_EROFS_COMPRESS_HINTS\n\t$(hide) cp $(BOARD_SYSTEMIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/system_erofs_compress_hints.txt\nendif\nifdef BOARD_SYSTEM_EXTIMAGE_EROFS_COMPRESS_HINTS\n\t$(hide) cp $(BOARD_SYSTEM_EXTIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/system_ext_erofs_compress_hints.txt\nendif\nifdef BOARD_PRODUCTIMAGE_EROFS_COMPRESS_HINTS\n\t$(hide) cp $(BOARD_PRODUCTIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/product_erofs_compress_hints.txt\nendif\nifdef BOARD_VENDORIMAGE_EROFS_COMPRESS_HINTS\n\t$(hide) cp $(BOARD_VENDORIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/vendor_erofs_compress_hints.txt\nendif\nifdef BOARD_ODMIMAGE_EROFS_COMPRESS_HINTS\n\t$(hide) cp $(BOARD_ODMIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/odm_erofs_compress_hints.txt\nendif\nifdef BOARD_VENDOR_DLKMIMAGE_EROFS_COMPRESS_HINTS\n\t$(hide) cp $(BOARD_VENDOR_DLKMIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/vendor_dlkm_erofs_compress_hints.txt\nendif\nifdef BOARD_ODM_DLKMIMAGE_EROFS_COMPRESS_HINTS\n\t$(hide) cp $(BOARD_ODM_DLKMIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/odm_dlkm_erofs_compress_hints.txt\nendif\nifdef BOARD_SYSTEM_DLKMIMAGE_EROFS_COMPRESS_HINTS\n\t$(hide) cp $(BOARD_SYSTEM_DLKMIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/system_dlkm_erofs_compress_hints.txt\nendif\nifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),)\n\t$(call fs_config,$(zip_root)/VENDOR_BOOT/RAMDISK,) > $(zip_root)/META/vendor_boot_filesystem_config.txt\nendif\nifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)\n\t$(hide) $(call fs_config,$(zip_root)/RECOVERY/RAMDISK,) > $(zip_root)/META/recovery_filesystem_config.txt\nendif\nifdef BUILDING_SYSTEM_OTHER_IMAGE\n\t$(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEMOTHERIMAGE_TARGET),$(zip_root)/IMAGES)\n\t$(hide) $(call fs_config,$(zip_root)/SYSTEM_OTHER,system/) > $(zip_root)/META/system_other_filesystem_config.txt\nendif\n\t@# Metadata for compatibility verification.\nifdef BUILT_KERNEL_CONFIGS_FILE\n\t$(hide) cp $(BUILT_KERNEL_CONFIGS_FILE) $(zip_root)/META/kernel_configs.txt\nendif\nifdef BUILT_KERNEL_VERSION_FILE\n\t$(hide) cp $(BUILT_KERNEL_VERSION_FILE) $(zip_root)/META/kernel_version.txt\nendif\n\trm -rf $(zip_root)/META/dynamic_partitions_info.txt\nifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS))\n\t$(call dump-dynamic-partitions-info, $(zip_root)/META/dynamic_partitions_info.txt)\nendif\n\tPATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \\\n\t    $(ADD_IMG_TO_TARGET_FILES) -a -v -p $(HOST_OUT) $(zip_root)\nifeq ($(BUILD_QEMU_IMAGES),true)\n\t$(hide) AVBTOOL=$(AVBTOOL) $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(zip_root)/IMAGES/vbmeta.img \\\n\t    $(zip_root)/IMAGES/system.img $(zip_root)/IMAGES/VerifiedBootParams.textproto\nendif\n\t@# Zip everything up, preserving symlinks and placing META/ files first to\n\t@# help early validation of the .zip file while uploading it.\n\t$(hide) find $(zip_root)/META | sort >$@\n\t$(hide) find $(zip_root) -path $(zip_root)/META -prune -o -print | sort >>$@\n\n$(BUILT_TARGET_FILES_PACKAGE): $(BUILT_TARGET_FILES_DIR)\n\t@echo \"Packaging target files: $@\"\n\t$(hide) $(SOONG_ZIP) -d -o $@ -C $(zip_root) -r $@.list -sha256\n\n.PHONY: target-files-package\ntarget-files-package: $(BUILT_TARGET_FILES_PACKAGE)\n\n.PHONY: target-files-dir\ntarget-files-dir: $(BUILT_TARGET_FILES_DIR)\n\n$(call declare-1p-container,$(BUILT_TARGET_FILES_PACKAGE),)\n$(call declare-container-license-deps,$(BUILT_TARGET_FILES_PACKAGE), $(INSTALLED_RADIOIMAGE_TARGET) \\\n            $(INSTALLED_RECOVERYIMAGE_TARGET) \\\n            $(INSTALLED_CACHEIMAGE_TARGET) \\\n            $(INSTALLED_DTBOIMAGE_TARGET) \\\n            $(INSTALLED_PVMFWIMAGE_TARGET) \\\n            $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) \\\n            $(INSTALLED_CUSTOMIMAGES_TARGET) \\\n            $(INSTALLED_ANDROID_INFO_TXT_TARGET) \\\n            $(INSTALLED_KERNEL_TARGET) \\\n            $(INSTALLED_RAMDISK_TARGET) \\\n            $(INSTALLED_DTBIMAGE_TARGET) \\\n            $(INSTALLED_2NDBOOTLOADER_TARGET) \\\n            $(BOARD_PREBUILT_DTBOIMAGE) \\\n            $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) \\\n            $(BOARD_RECOVERY_ACPIO) \\\n            $(PRODUCT_SYSTEM_BASE_FS_PATH) \\\n            $(PRODUCT_VENDOR_BASE_FS_PATH) \\\n            $(PRODUCT_PRODUCT_BASE_FS_PATH) \\\n            $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH) \\\n            $(PRODUCT_ODM_BASE_FS_PATH) \\\n            $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH) \\\n            $(PRODUCT_ODM_DLKM_BASE_FS_PATH) \\\n            $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH) \\\n            $(LPMAKE) \\\n            $(SELINUX_FC) \\\n            $(INSTALLED_MISC_INFO_TARGET) \\\n            $(INSTALLED_FASTBOOT_INFO_TARGET) \\\n            $(APKCERTS_FILE) \\\n            $(APEX_KEYS_FILE) \\\n            $(HOST_OUT_EXECUTABLES)/fs_config \\\n            $(HOST_OUT_EXECUTABLES)/map_file_generator \\\n            $(ADD_IMG_TO_TARGET_FILES) \\\n            $(MAKE_RECOVERY_PATCH) \\\n            $(BUILT_KERNEL_CONFIGS_FILE) \\\n            $(BUILT_KERNEL_VERSION_FILE),$(BUILT_TARGET_FILES_PACKAGE):)\n\n$(call dist-for-goals-with-filenametag, target-files-package, $(BUILT_TARGET_FILES_PACKAGE))\n\n# -----------------------------------------------------------------\n# NDK Sysroot Package\nNDK_SYSROOT_TARGET := $(PRODUCT_OUT)/ndk_sysroot.tar.bz2\n.PHONY: ndk_sysroot\nndk_sysroot: $(NDK_SYSROOT_TARGET)\n$(NDK_SYSROOT_TARGET): $(SOONG_OUT_DIR)/ndk.timestamp\n\t@echo Package NDK sysroot...\n\t$(hide) tar cjf $@ -C $(SOONG_OUT_DIR) ndk\n\nifeq ($(HOST_OS),linux)\n$(call dist-for-goals,sdk ndk_sysroot,$(NDK_SYSROOT_TARGET))\nendif\n\nifeq ($(build_ota_package),true)\n# -----------------------------------------------------------------\n# OTA update package\n\n# $(1): output file\n# $(2): additional args\ndefine build-ota-package-target\nPATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$(dir $(ZIP2ZIP)):$$PATH \\\n    $(OTA_FROM_TARGET_FILES) \\\n        --verbose \\\n        --path $(HOST_OUT) \\\n        $(if $(OEM_OTA_CONFIG), --oem_settings $(OEM_OTA_CONFIG)) \\\n        $(if $(BOOT_VAR_OTA_CONFIG), --boot_variable_file $(BOOT_VAR_OTA_CONFIG)) \\\n        $(2) \\\n        $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) $(1)\nendef\n\nproduct_name := $(TARGET_PRODUCT)\nifeq ($(TARGET_BUILD_TYPE),debug)\n  product_name := $(product_name)_debug\nendif\nname := $(product_name)-ota\n\nINTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip\nINTERNAL_OTA_METADATA := $(PRODUCT_OUT)/ota_metadata\n\n$(call declare-0p-target,$(INTERNAL_OTA_METADATA))\n\n$(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)\n$(INTERNAL_OTA_PACKAGE_TARGET): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_OTA_METADATA)\n$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_DIR) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES)\n\t@echo \"Package OTA: $@\"\n\t$(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --output_metadata_path $(INTERNAL_OTA_METADATA))\n\n$(call declare-1p-container,$(INTERNAL_OTA_PACKAGE_TARGET),)\n$(call declare-container-license-deps,$(INTERNAL_OTA_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES),$(PRODUCT_OUT)/:/)\n\n.PHONY: otapackage\notapackage: $(INTERNAL_OTA_PACKAGE_TARGET)\n\nifeq ($(BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE),true)\nname := $(product_name)-ota-retrofit\n\nINTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip\n$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)\n$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): \\\n    $(BUILT_TARGET_FILES_PACKAGE) \\\n    $(OTA_FROM_TARGET_FILES) \\\n    $(INTERNAL_OTATOOLS_FILES)\n\t@echo \"Package OTA (retrofit dynamic partitions): $@\"\n\t$(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --retrofit_dynamic_partitions)\n\n$(call declare-1p-container,$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET),)\n$(call declare-container-license-deps,$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES),$(PRODUCT_OUT)/:/)\n\n.PHONY: otardppackage\n\notapackage otardppackage: $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET)\n\nendif # BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE\n\nifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),)\nname := $(product_name)-partial-ota\n\nINTERNAL_OTA_PARTIAL_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip\n$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)\n$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): $(BUILT_TARGET_FILES_DIR) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES)\n\t@echo \"Package partial OTA: $@\"\n\t$(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --partial \"$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)\")\n\n$(call declare-1p-container,$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET),)\n$(call declare-container-license-deps,$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES),$(PRODUCT_OUT)/:/)\n\n\n.PHONY: partialotapackage\npartialotapackage: $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET)\n\nendif # BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST\n\nendif    # build_ota_package\n\n# -----------------------------------------------------------------\n# A zip of the appcompat directory containing logs\nAPPCOMPAT_ZIP := $(PRODUCT_OUT)/appcompat.zip\n# For apps_only build we'll establish the dependency later in build/make/core/main.mk.\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n$(APPCOMPAT_ZIP): $(FULL_SYSTEMIMAGE_DEPS) \\\n\t    $(INTERNAL_RAMDISK_FILES) \\\n\t    $(INTERNAL_USERDATAIMAGE_FILES) \\\n\t    $(INTERNAL_VENDORIMAGE_FILES) \\\n\t    $(INTERNAL_PRODUCTIMAGE_FILES) \\\n\t    $(INTERNAL_SYSTEM_EXTIMAGE_FILES)\nendif\n$(APPCOMPAT_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,appcompat)/filelist\n$(APPCOMPAT_ZIP): $(SOONG_ZIP)\n\t@echo \"appcompat logs: $@\"\n\t$(hide) rm -rf $@ $(PRIVATE_LIST_FILE)\n\t$(hide) mkdir -p $(dir $@) $(PRODUCT_OUT)/appcompat $(dir $(PRIVATE_LIST_FILE))\n\t$(hide) find $(PRODUCT_OUT)/appcompat | sort >$(PRIVATE_LIST_FILE)\n\t$(hide) $(SOONG_ZIP) -d -o $@ -C $(PRODUCT_OUT)/appcompat -l $(PRIVATE_LIST_FILE)\n\nDEXPREOPT_CONFIG_ZIP := $(PRODUCT_OUT)/dexpreopt_config.zip\n\n$(DEXPREOPT_CONFIG_ZIP): $(INSTALLED_SYSTEMIMAGE_TARGET) \\\n    $(INSTALLED_VENDORIMAGE_TARGET) \\\n    $(INSTALLED_ODMIMAGE_TARGET) \\\n    $(INSTALLED_PRODUCTIMAGE_TARGET) \\\n\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n$(DEXPREOPT_CONFIG_ZIP): $(DEX_PREOPT_CONFIG_FOR_MAKE) \\\n\t  $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) \\\n\nendif\n\n$(DEXPREOPT_CONFIG_ZIP): PRIVATE_DEXPREOPT_CONFIG_ZIP_PARAMS :=\nifeq (,$(TARGET_BUILD_UNBUNDLED))\nifneq (,$(DEX_PREOPT_CONFIG_FOR_MAKE))\n$(DEXPREOPT_CONFIG_ZIP): PRIVATE_DEXPREOPT_CONFIG_ZIP_PARAMS += -e $(notdir $(DEX_PREOPT_CONFIG_FOR_MAKE)) -f $(DEX_PREOPT_CONFIG_FOR_MAKE)\nendif\nifneq (,$(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE))\n$(DEXPREOPT_CONFIG_ZIP): PRIVATE_DEXPREOPT_CONFIG_ZIP_PARAMS += -e $(notdir $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)) -f $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)\nendif\nendif #!TARGET_BUILD_UNBUNDLED\n\n$(DEXPREOPT_CONFIG_ZIP): $(SOONG_ZIP)\n\t$(hide) mkdir -p $(dir $@) $(PRODUCT_OUT)/dexpreopt_config\n\t$(hide) $(SOONG_ZIP) -d -o $@ -C $(PRODUCT_OUT)/dexpreopt_config -D $(PRODUCT_OUT)/dexpreopt_config $(PRIVATE_DEXPREOPT_CONFIG_ZIP_PARAMS)\n\n.PHONY: dexpreopt_config_zip\ndexpreopt_config_zip: $(DEXPREOPT_CONFIG_ZIP)\n\n$(call declare-1p-target,$(DEXPREOPT_CONFIG_ZIP),)\n\n# -----------------------------------------------------------------\n# Zips of the symbols directory per test suites\n#\n\n$(foreach suite,$(ALL_COMPATIBILITY_SUITES),$(eval $(call create-suite-symbols-map,$(suite))))\n\n# -----------------------------------------------------------------\n# A zip of the symbols directory.  Keep the full paths to make it\n# more obvious where these files came from.\n# Also produces a textproto containing mappings from elf IDs to symbols\n# filename, which will allow finding the appropriate symbols to deobfuscate\n# a stack trace frame.\n#\n\nname := $(TARGET_PRODUCT)\nifeq ($(TARGET_BUILD_TYPE),debug)\n  name := $(name)_debug\nendif\n\n# The path to the zip file containing binaries with symbols.\nSYMBOLS_ZIP := $(PRODUCT_OUT)/$(name)-symbols.zip\n# The path to a file containing mappings from elf IDs to filenames.\nSYMBOLS_MAPPING := $(PRODUCT_OUT)/$(name)-symbols-mapping.textproto\n.KATI_READONLY := SYMBOLS_ZIP SYMBOLS_MAPPING\n\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n  _symbols_zip_modules := $(call product-installed-modules,$(INTERNAL_PRODUCT))\n  $(SYMBOLS_ZIP): $(updater_dep)\nelse\n  _symbols_zip_modules := $(unbundled_build_modules)\nendif\n\n_symbols_zip_modules_symbols_files := $(foreach m,$(_symbols_zip_modules),$(ALL_MODULES.$(m).SYMBOLIC_OUTPUT_PATH))\n_symbols_zip_modules_mapping_files := $(foreach m,$(_symbols_zip_modules),$(ALL_MODULES.$(m).ELF_SYMBOL_MAPPING_PATH))\n\n$(SYMBOLS_ZIP): PRIVATE_SYMBOLS_MODULES_FILES := $(_symbols_zip_modules_symbols_files)\n$(SYMBOLS_ZIP): PRIVATE_SYMBOLS_MODULES_MAPPING_FILES := $(_symbols_zip_modules_mapping_files)\n$(SYMBOLS_ZIP): $(SOONG_ZIP) $(SYMBOLS_MAP) $(_symbols_zip_modules_symbols_files) $(_symbols_zip_modules_mapping_files)\n\t@echo \"Package symbols: $@\"\n\t$(hide) rm -rf $@ $@.symbols_list $@.mapping_list\n\t# Find all installed files in the symbols directory and zip them into the symbols zip.\n\techo \"$(PRIVATE_SYMBOLS_MODULES_FILES)\" | tr \" \" \"\\n\" | sort > $@.symbols_list\n\t$(hide) $(SOONG_ZIP) -d -o $@ -l $@.symbols_list\n\t# Find all installed files in the symbols mapping directory and merge them into the symbols mapping textproto.\n\techo \"$(PRIVATE_SYMBOLS_MODULES_MAPPING_FILES)\" | tr \" \" \"\\n\" | sort > $@.mapping_list\n\t$(hide) $(SYMBOLS_MAP) -merge $(SYMBOLS_MAPPING) @$@.mapping_list\n$(SYMBOLS_ZIP): .KATI_IMPLICIT_OUTPUTS := $(SYMBOLS_MAPPING)\n\n$(call declare-1p-container,$(SYMBOLS_ZIP),)\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n$(call declare-container-license-deps,$(SYMBOLS_ZIP),$(PRIVATE_SYMBOLS_MODULES_FILES) $(updater_dep),$(PRODUCT_OUT)/:/)\nendif\n\n_symbols_zip_modules_symbols_files :=\n_symbols_zip_modules_mapping_files :=\n# -----------------------------------------------------------------\n# A zip of the coverage directory.\n#\nname := gcov-report-files-all\nifeq ($(TARGET_BUILD_TYPE),debug)\nname := $(name)_debug\nendif\nCOVERAGE_ZIP := $(PRODUCT_OUT)/$(name).zip\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n$(COVERAGE_ZIP): $(INTERNAL_ALLIMAGES_FILES)\nendif\n$(COVERAGE_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,coverage)/filelist\n$(COVERAGE_ZIP): $(SOONG_ZIP)\n\t@echo \"Package coverage: $@\"\n\t$(hide) rm -rf $@ $(PRIVATE_LIST_FILE)\n\t$(hide) mkdir -p $(dir $@) $(TARGET_OUT_COVERAGE) $(dir $(PRIVATE_LIST_FILE))\n\t$(hide) find $(TARGET_OUT_COVERAGE) | sort >$(PRIVATE_LIST_FILE)\n\t$(hide) $(SOONG_ZIP) -d -o $@ -C $(TARGET_OUT_COVERAGE) -l $(PRIVATE_LIST_FILE)\n\n$(call declare-1p-container,$(COVERAGE_ZIP),)\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n$(call declare-container-license-deps,$(COVERAGE_ZIP),$(INTERNAL_ALLIMAGE_FILES),$(PRODUCT_OUT)/:/)\nendif\n\nSYSTEM_NOTICE_DEPS += $(COVERAGE_ZIP)\n\n#------------------------------------------------------------------\n# Export the LLVM profile data tool and dependencies for Clang coverage processing\n#\nifeq (true,$(CLANG_COVERAGE))\n  LLVM_PROFDATA := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-profdata\n  LLVM_COV := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-cov\n  LIBCXX := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib/x86_64-unknown-linux-gnu/libc++.so\n  # Use llvm-profdata.zip for backwards compatibility with tradefed code.\n  LLVM_COVERAGE_TOOLS_ZIP := $(PRODUCT_OUT)/llvm-profdata.zip\n\n  $(LLVM_COVERAGE_TOOLS_ZIP): $(SOONG_ZIP)\n\t$(hide) $(SOONG_ZIP) -d -o $@ -C $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION) -f $(LLVM_PROFDATA) -f $(LIBCXX) -f $(LLVM_COV)\n\n  $(call dist-for-goals,droidcore-unbundled apps_only,$(LLVM_COVERAGE_TOOLS_ZIP))\nendif\n\nifeq (true,$(EMMA_INSTRUMENT))\n#------------------------------------------------------------------\n# An archive of classes for use in generating code-coverage reports\n# These are the uninstrumented versions of any classes that were\n# to be instrumented.\n# Any dependencies are set up later in build/make/core/main.mk.\n\nJACOCO_REPORT_CLASSES_ALL := $(PRODUCT_OUT)/jacoco-report-classes-all.jar\n$(JACOCO_REPORT_CLASSES_ALL): PRIVATE_TARGET_JACOCO_DIR := $(call intermediates-dir-for,PACKAGING,jacoco)\n$(JACOCO_REPORT_CLASSES_ALL): PRIVATE_HOST_JACOCO_DIR := $(call intermediates-dir-for,PACKAGING,jacoco,HOST)\n$(JACOCO_REPORT_CLASSES_ALL): PRIVATE_TARGET_PROGUARD_USAGE_DIR := $(call intermediates-dir-for,PACKAGING,proguard_usage)\n$(JACOCO_REPORT_CLASSES_ALL): PRIVATE_HOST_PROGUARD_USAGE_DIR := $(call intermediates-dir-for,PACKAGING,proguard_usage,HOST)\n$(JACOCO_REPORT_CLASSES_ALL) :\n\t@echo \"Collecting uninstrumented classes\"\n\tmkdir -p $(PRIVATE_TARGET_JACOCO_DIR) $(PRIVATE_HOST_JACOCO_DIR) $(PRIVATE_TARGET_PROGUARD_USAGE_DIR) $(PRIVATE_HOST_PROGUARD_USAGE_DIR)\n\t$(SOONG_ZIP) -o $@ -L 0 \\\n\t  -C $(PRIVATE_TARGET_JACOCO_DIR) -P out/target/common/obj -D $(PRIVATE_TARGET_JACOCO_DIR) \\\n\t  -C $(PRIVATE_HOST_JACOCO_DIR) -P out/target/common/obj -D $(PRIVATE_HOST_JACOCO_DIR) \\\n\t  -C $(PRIVATE_TARGET_PROGUARD_USAGE_DIR) -P out/target/common/obj -D $(PRIVATE_TARGET_PROGUARD_USAGE_DIR) \\\n\t  -C $(PRIVATE_HOST_PROGUARD_USAGE_DIR) -P out/target/common/obj -D $(PRIVATE_HOST_PROGUARD_USAGE_DIR)\n\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n  $(JACOCO_REPORT_CLASSES_ALL): $(INTERNAL_ALLIMAGES_FILES)\nendif\n\n# This is not ideal, but it is difficult to correctly figure out the actual jacoco report\n# jars we need to add here as dependencies, so we add the device-tests as a dependency when\n# the env variable is set and this should guarantee thaat all the jacoco report jars are ready\n# when we package the final report jar here.\nifeq ($(JACOCO_PACKAGING_INCLUDE_DEVICE_TESTS),true)\n  $(JACOCO_REPORT_CLASSES_ALL): $(COMPATIBILITY.device-tests.FILES)\nendif\nendif # EMMA_INSTRUMENT=true\n\n\n#------------------------------------------------------------------\n# A zip of Proguard obfuscation dictionary files.\n# Also produces a textproto containing mappings from the hashes of the\n# dictionary contents (which are also stored in the dex files on the\n# devices) to the filename of the proguard dictionary, which will allow\n# finding the appropriate dictionary to deobfuscate a stack trace frame.\n#\n\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n  _proguard_dict_zip_modules := $(call product-installed-modules,$(INTERNAL_PRODUCT))\nelse\n  _proguard_dict_zip_modules := $(unbundled_build_modules)\nendif\n\n# Filter out list to avoid uncessary proguard related file generation\nifeq (,$(TARGET_BUILD_UNBUNDLED))\nfilter_out_proguard_dict_zip_modules :=\n# product.img\nifndef BUILDING_PRODUCT_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/product/%\nendif\n# system.img\nifndef BUILDING_SYSTEM_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system/%\nendif\n# system_dlkm.img\nifndef BUILDING_SYSTEM_DLKM_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_dlkm/%\nendif\n# system_ext.img\nifndef BUILDING_SYSTEM_EXT_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_ext/%\nendif\n# system_other.img\nifndef BUILDING_SYSTEM_OTHER_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_other/%\nendif\n# odm.img\nifndef BUILDING_ODM_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/odm/%\nendif\n# odm_dlkm.img\nifndef BUILDING_ODM_DLKM_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/odm_dlkm/%\nendif\n# vendor.img\nifndef BUILDING_VENDOR_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/vendor/%\nendif\n# vendor_dlkm.img\nifndef BUILDING_VENDOR_DLKM_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/vendor_dlkm/%\nendif\n# cache.img\nifndef BUILDING_CACHE_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/cache/%\nendif\n# ramdisk.img\nifndef BUILDING_RAMDISK_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/ramdisk/%\nendif\n# recovery.img\nifndef INSTALLED_RECOVERYIMAGE_TARGET\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/recovery/%\nendif\n# userdata.img\nifndef BUILDING_USERDATA_IMAGE\nfilter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/data/%\nendif\n\n# Check the installed files of each module and return the module name\n# or return empty if none of the files remain to be installed\ndefine filter-out-proguard-modules\n$(if $(filter-out $(filter_out_proguard_dict_zip_modules),$(call module-installed-files,$(1))),$(1))\nendef\n\n# Filter out proguard dict zip modules those are not installed at the built image\n_proguard_dict_zip_modules := $(foreach m,$(_proguard_dict_zip_modules),$(strip $(call filter-out-proguard-modules,$(m))))\nendif\n\n# The path to the zip file containing proguard dictionaries.\nPROGUARD_DICT_ZIP :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict.zip\n$(PROGUARD_DICT_ZIP): PRIVATE_SOONG_ZIP_ARGUMENTS := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS))\n$(PROGUARD_DICT_ZIP): $(SOONG_ZIP) $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_FILES))\n\t@echo \"Packaging Proguard obfuscation dictionary files.\"\n\t# Zip all of the files in PROGUARD_DICTIONARY_FILES.\n\techo -n > $@.tmparglist\n\t$(foreach arg,$(PRIVATE_SOONG_ZIP_ARGUMENTS),printf \"%s\\n\" \"$(arg)\" >> $@.tmparglist$(newline))\n\t$(SOONG_ZIP) -d -o $@ @$@.tmparglist\n\trm -f $@.tmparglist\n\n# The path to the zip file containing mappings from dictionary hashes to filenames.\nPROGUARD_DICT_MAPPING :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict-mapping.textproto\n_proguard_dict_mapping_files := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_MAPPING))\n$(PROGUARD_DICT_MAPPING): PRIVATE_MAPPING_FILES := $(_proguard_dict_mapping_files)\n$(PROGUARD_DICT_MAPPING): $(SYMBOLS_MAP) $(_proguard_dict_mapping_files)\n\t@echo \"Packaging Proguard obfuscation dictionary mapping files.\"\n\t# Merge all the mapping files together\n\techo -n > $@.tmparglist\n\t$(foreach mf,$(PRIVATE_MAPPING_FILES),echo \"$(mf)\" >> $@.tmparglist$(newline))\n\t$(SYMBOLS_MAP) -merge $(PROGUARD_DICT_MAPPING) @$@.tmparglist\n\trm -f $@.tmparglist\n\n$(call declare-1p-container,$(PROGUARD_DICT_ZIP),)\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n$(call declare-container-license-deps,$(PROGUARD_DICT_ZIP),$(INTERNAL_ALLIMAGES_FILES) $(updater_dep),$(PRODUCT_OUT)/:/)\nendif\n\n#------------------------------------------------------------------\n# A zip of Proguard usage files.\n#\nPROGUARD_USAGE_ZIP :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-usage.zip\n_proguard_usage_zips := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_USAGE_ZIP))\n$(PROGUARD_USAGE_ZIP): PRIVATE_ZIPS := $(_proguard_usage_zips)\n$(PROGUARD_USAGE_ZIP): $(MERGE_ZIPS) $(_proguard_usage_zips)\n\t@echo \"Packaging Proguard usage files.\"\n\techo -n > $@.tmparglist\n\t$(foreach z,$(PRIVATE_ZIPS),echo \"$(z)\" >> $@.tmparglist$(newline))\n\t$(MERGE_ZIPS) $@ @$@.tmparglist\n\trm -rf $@.tmparglist\n\n_proguard_dict_mapping_files :=\n_proguard_usage_zips :=\n_proguard_dict_zip_modules :=\n\n$(call declare-1p-container,$(PROGUARD_USAGE_ZIP),)\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n$(call declare-container-license-deps,$(PROGUARD_USAGE_ZIP),$(INSTALLED_SYSTEMIMAGE_TARGET) \\\n    $(INSTALLED_RAMDISK_TARGET) \\\n    $(INSTALLED_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \\\n    $(INSTALLED_USERDATAIMAGE_TARGET) \\\n    $(INSTALLED_VENDORIMAGE_TARGET) \\\n    $(INSTALLED_PRODUCTIMAGE_TARGET) \\\n    $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \\\n    $(INSTALLED_ODMIMAGE_TARGET) \\\n    $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \\\n    $(INSTALLED_ODM_DLKMIMAGE_TARGET) \\\n    $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \\\n    $(updater_dep),$(PROGUARD_USAGE_ZIP):/)\nendif\n\nifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS))\n\n# Dump variables used by build_super_image.py (for building super.img and super_empty.img).\n# $(1): output file\ndefine dump-super-image-info\n  $(call dump-dynamic-partitions-info,$(1))\n  $(if $(filter true,$(AB_OTA_UPDATER)), \\\n    echo \"ab_update=true\" >> $(1))\nendef\n\nendif # PRODUCT_USE_DYNAMIC_PARTITIONS\n\n# -----------------------------------------------------------------\n# super partition image (dist)\n\nifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION))\n\n# BOARD_SUPER_PARTITION_SIZE must be defined to build super image.\nifneq ($(BOARD_SUPER_PARTITION_SIZE),)\n\nifneq (true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS))\n\n# For real devices and for dist builds, build super image from target files to an intermediate directory.\nINTERNAL_SUPERIMAGE_DIST_TARGET := $(call intermediates-dir-for,PACKAGING,super.img)/super.img\n$(INTERNAL_SUPERIMAGE_DIST_TARGET): extracted_input_target_files := $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE))\n$(INTERNAL_SUPERIMAGE_DIST_TARGET): $(LPMAKE) $(BUILT_TARGET_FILES_DIR) $(BUILD_SUPER_IMAGE)\n\t$(call pretty,\"Target super fs image from target files: $@\")\n\tPATH=$(dir $(LPMAKE)):$$PATH \\\n\t    $(BUILD_SUPER_IMAGE) -v $(extracted_input_target_files) $@\n\n# Skip packing it in dist package because it is in update package.\nifneq (true,$(BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE))\n$(call dist-for-goals,dist_files,$(INTERNAL_SUPERIMAGE_DIST_TARGET))\nendif\n\n.PHONY: superimage_dist\nsuperimage_dist: $(INTERNAL_SUPERIMAGE_DIST_TARGET)\n\nendif # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS != \"true\"\nendif # BOARD_SUPER_PARTITION_SIZE != \"\"\nendif # PRODUCT_BUILD_SUPER_PARTITION == \"true\"\n\n# -----------------------------------------------------------------\n# super partition image for development\n\nifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION))\nifneq ($(BOARD_SUPER_PARTITION_SIZE),)\nifneq (true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS))\n\n# Build super.img by using $(INSTALLED_*IMAGE_TARGET) to $(1)\n# $(1): built image path\n# $(2): misc_info.txt path; its contents should match expectation of build_super_image.py\ndefine build-superimage-target\n  mkdir -p $(dir $(2))\n  rm -rf $(2)\n  $(call dump-super-image-info,$(2))\n  $(foreach p,$(BOARD_SUPER_PARTITION_PARTITION_LIST), \\\n    echo \"$(p)_image=$(INSTALLED_$(call to-upper,$(p))IMAGE_TARGET)\" >> $(2);)\n  $(if $(BUILDING_SYSTEM_OTHER_IMAGE), $(if $(filter system,$(BOARD_SUPER_PARTITION_PARTITION_LIST)), \\\n    echo \"system_other_image=$(INSTALLED_SYSTEMOTHERIMAGE_TARGET)\" >> $(2);))\n  mkdir -p $(dir $(1))\n  PATH=$(dir $(LPMAKE)):$$PATH \\\n    $(BUILD_SUPER_IMAGE) -v $(2) $(1)\nendef\n\nINSTALLED_SUPERIMAGE_TARGET := $(PRODUCT_OUT)/super.img\nINSTALLED_SUPERIMAGE_DEPENDENCIES := $(LPMAKE) $(BUILD_SUPER_IMAGE) \\\n    $(foreach p, $(BOARD_SUPER_PARTITION_PARTITION_LIST), $(INSTALLED_$(call to-upper,$(p))IMAGE_TARGET))\n\nifdef BUILDING_SYSTEM_OTHER_IMAGE\nifneq ($(filter system,$(BOARD_SUPER_PARTITION_PARTITION_LIST)),)\nINSTALLED_SUPERIMAGE_DEPENDENCIES += $(INSTALLED_SYSTEMOTHERIMAGE_TARGET)\nendif\nendif\n\n$(INSTALLED_SUPERIMAGE_TARGET): $(INSTALLED_SUPERIMAGE_DEPENDENCIES)\n\t$(call pretty,\"Target super fs image for debug: $@\")\n\t$(call build-superimage-target,$(INSTALLED_SUPERIMAGE_TARGET),\\\n          $(call intermediates-dir-for,PACKAGING,superimage_debug)/misc_info.txt)\n\n# For devices that uses super image directly, the superimage target points to the file in $(PRODUCT_OUT).\n.PHONY: superimage\nsuperimage: $(INSTALLED_SUPERIMAGE_TARGET)\n\n# If BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT is set, super.img is built from images in the\n# $(PRODUCT_OUT) directory, and is built to $(PRODUCT_OUT)/super.img. Also, it will\n# be built for non-dist builds. This is useful for devices that uses super.img directly, e.g.\n# virtual devices.\nifeq (true,$(BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT))\ndroidcore-unbundled: $(INSTALLED_SUPERIMAGE_TARGET)\n\n$(call dist-for-goals,dist_files,$(INSTALLED_MISC_INFO_TARGET):super_misc_info.txt)\nendif # BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT\n\n# Build $(PRODUCT_OUT)/super.img without dependencies.\n.PHONY: superimage-nodeps supernod\nsuperimage-nodeps supernod: intermediates :=\nsuperimage-nodeps supernod: | $(INSTALLED_SUPERIMAGE_DEPENDENCIES)\n\t$(call pretty,\"make $(INSTALLED_SUPERIMAGE_TARGET): ignoring dependencies\")\n\t$(call build-superimage-target,$(INSTALLED_SUPERIMAGE_TARGET),\\\n\t  $(call intermediates-dir-for,PACKAGING,superimage-nodeps)/misc_info.txt)\n\nendif # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS != \"true\"\nendif # BOARD_SUPER_PARTITION_SIZE != \"\"\nendif # PRODUCT_BUILD_SUPER_PARTITION == \"true\"\n\n# -----------------------------------------------------------------\n# super empty image\nifdef BUILDING_SUPER_EMPTY_IMAGE\n\nINSTALLED_SUPERIMAGE_EMPTY_TARGET := $(PRODUCT_OUT)/super_empty.img\n$(INSTALLED_SUPERIMAGE_EMPTY_TARGET): intermediates := $(call intermediates-dir-for,PACKAGING,super_empty)\n$(INSTALLED_SUPERIMAGE_EMPTY_TARGET): $(LPMAKE) $(BUILD_SUPER_IMAGE)\n\t$(call pretty,\"Target empty super fs image: $@\")\n\tmkdir -p $(intermediates)\n\trm -rf $(intermediates)/misc_info.txt\n\t$(call dump-super-image-info,$(intermediates)/misc_info.txt)\n\tPATH=$(dir $(LPMAKE)):$$PATH \\\n\t    $(BUILD_SUPER_IMAGE) -v $(intermediates)/misc_info.txt $@\n\n$(call dist-for-goals,dist_files,$(INSTALLED_SUPERIMAGE_EMPTY_TARGET))\n\n$(call declare-0p-target,$(INSTALLED_SUPERIMAGE_EMPTY_TARGET))\n\nendif # BUILDING_SUPER_EMPTY_IMAGE\n\n\n# -----------------------------------------------------------------\n# The update package\n\nname := $(TARGET_PRODUCT)\nifeq ($(TARGET_BUILD_TYPE),debug)\n  name := $(name)_debug\nendif\nname := $(name)-img\n\nINTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip\n\n$(INTERNAL_UPDATE_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(IMG_FROM_TARGET_FILES)\n\t$(call pretty,\"Package: $@\")\n\tPATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$(dir $(ZIP2ZIP)):$$PATH \\\n\t    $(IMG_FROM_TARGET_FILES) \\\n\t        --additional IMAGES/VerifiedBootParams.textproto:VerifiedBootParams.textproto \\\n\t\t\t$(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \\\n\t\t\t\t\t$(if $(BOARD_$(call to-upper,$(partition))_IMAGE_NO_FLASHALL), \\\n\t\t\t\t\t\t--exclude IMAGES/$(partition).img \\\n\t\t\t\t\t\t--exclude IMAGES/$(partition).map \\\n\t\t\t\t\t) \\\n\t\t\t) \\\n\t        --build_super_image $(BUILD_SUPER_IMAGE) \\\n\t        $(BUILT_TARGET_FILES_PACKAGE) $@\n\n$(call declare-1p-container,$(INTERNAL_UPDATE_PACKAGE_TARGET),)\n$(call declare-container-license-deps,$(INTERNAL_UPDATE_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(IMG_FROM_TARGET_FILES),$(PRODUCT_OUT)/:/)\n\n.PHONY: updatepackage\nupdatepackage: $(INTERNAL_UPDATE_PACKAGE_TARGET)\n$(call dist-for-goals-with-filenametag,updatepackage,$(INTERNAL_UPDATE_PACKAGE_TARGET))\n\n\n# -----------------------------------------------------------------\n# dalvik something\n.PHONY: dalvikfiles\ndalvikfiles: $(INTERNAL_DALVIK_MODULES)\n\nifeq ($(BUILD_QEMU_IMAGES),true)\nMK_QEMU_IMAGE_SH := device/generic/goldfish/tools/mk_qemu_image.sh\nMK_COMBINE_QEMU_IMAGE := $(HOST_OUT_EXECUTABLES)/mk_combined_img\nSGDISK_HOST := $(HOST_OUT_EXECUTABLES)/sgdisk\n\nifdef INSTALLED_SYSTEMIMAGE_TARGET\nINSTALLED_QEMU_SYSTEMIMAGE := $(PRODUCT_OUT)/system-qemu.img\nINSTALLED_SYSTEM_QEMU_CONFIG := $(PRODUCT_OUT)/system-qemu-config.txt\n$(INSTALLED_SYSTEM_QEMU_CONFIG): $(INSTALLED_SUPERIMAGE_TARGET) $(INSTALLED_VBMETAIMAGE_TARGET)\n\t@echo \"$(PRODUCT_OUT)/vbmeta.img vbmeta 1\" > $@\n\t@echo \"$(INSTALLED_SUPERIMAGE_TARGET) super 2\" >> $@\n$(INSTALLED_QEMU_SYSTEMIMAGE): $(INSTALLED_VBMETAIMAGE_TARGET) $(MK_COMBINE_QEMU_IMAGE) $(SGDISK_HOST) $(SIMG2IMG) \\\n    $(INSTALLED_SUPERIMAGE_TARGET) $(INSTALLED_SYSTEM_QEMU_CONFIG)\n\t@echo Create system-qemu.img now\n\t(export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); \\\n     $(MK_COMBINE_QEMU_IMAGE) -i $(INSTALLED_SYSTEM_QEMU_CONFIG) -o $@)\n\nsystemimage: $(INSTALLED_QEMU_SYSTEMIMAGE)\ndroidcore-unbundled: $(INSTALLED_QEMU_SYSTEMIMAGE)\nendif\nifdef INSTALLED_VENDORIMAGE_TARGET\nINSTALLED_QEMU_VENDORIMAGE := $(PRODUCT_OUT)/vendor-qemu.img\n$(INSTALLED_QEMU_VENDORIMAGE): $(INSTALLED_VENDORIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) $(SIMG2IMG)\n\t@echo Create vendor-qemu.img\n\t(export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); $(MK_QEMU_IMAGE_SH) $(INSTALLED_VENDORIMAGE_TARGET))\n\nvendorimage: $(INSTALLED_QEMU_VENDORIMAGE)\ndroidcore-unbundled: $(INSTALLED_QEMU_VENDORIMAGE)\nendif\n\nifdef INSTALLED_RAMDISK_TARGET\nifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET\nifdef INTERNAL_VENDOR_RAMDISK_TARGET\nINSTALLED_QEMU_RAMDISKIMAGE := $(PRODUCT_OUT)/ramdisk-qemu.img\n$(INSTALLED_QEMU_RAMDISKIMAGE): $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_RAMDISK_TARGET)\n\t@echo Create ramdisk-qemu.img\n\t(cat $(INSTALLED_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_TARGET) > $(INSTALLED_QEMU_RAMDISKIMAGE))\n\ndroidcore-unbundled: $(INSTALLED_QEMU_RAMDISKIMAGE)\nendif\nendif\nendif\n\nifdef INSTALLED_PRODUCTIMAGE_TARGET\nINSTALLED_QEMU_PRODUCTIMAGE := $(PRODUCT_OUT)/product-qemu.img\n$(INSTALLED_QEMU_PRODUCTIMAGE): $(INSTALLED_PRODUCTIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) $(SIMG2IMG)\n\t@echo Create product-qemu.img\n\t(export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); $(MK_QEMU_IMAGE_SH) $(INSTALLED_PRODUCTIMAGE_TARGET))\n\nproductimage: $(INSTALLED_QEMU_PRODUCTIMAGE)\ndroidcore-unbundled: $(INSTALLED_QEMU_PRODUCTIMAGE)\nendif\nifdef INSTALLED_SYSTEM_EXTIMAGE_TARGET\nINSTALLED_QEMU_SYSTEM_EXTIMAGE := $(PRODUCT_OUT)/system_ext-qemu.img\n$(INSTALLED_QEMU_SYSTEM_EXTIMAGE): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) $(SIMG2IMG)\n\t@echo Create system_ext-qemu.img\n\t(export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); $(MK_QEMU_IMAGE_SH) $(INSTALLED_SYSTEM_EXTIMAGE_TARGET))\n\nsystemextimage: $(INSTALLED_QEMU_SYSTEM_EXTIMAGE)\ndroidcore-unbundled: $(INSTALLED_QEMU_SYSTEM_EXTIMAGE)\nendif\nifdef INSTALLED_ODMIMAGE_TARGET\nINSTALLED_QEMU_ODMIMAGE := $(PRODUCT_OUT)/odm-qemu.img\n$(INSTALLED_QEMU_ODMIMAGE): $(INSTALLED_ODMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST)\n\t@echo Create odm-qemu.img\n\t(export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_ODMIMAGE_TARGET))\n\nodmimage: $(INSTALLED_QEMU_ODMIMAGE)\ndroidcore-unbundled: $(INSTALLED_QEMU_ODMIMAGE)\nendif\n\nifdef INSTALLED_VENDOR_DLKMIMAGE_TARGET\nINSTALLED_QEMU_VENDOR_DLKMIMAGE := $(PRODUCT_OUT)/vendor_dlkm-qemu.img\n$(INSTALLED_QEMU_VENDOR_DLKMIMAGE): $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST)\n\t@echo Create vendor_dlkm-qemu.img\n\t(export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_VENDOR_DLKMIMAGE_TARGET))\n\nvendor_dlkmimage: $(INSTALLED_QEMU_VENDOR_DLKMIMAGE)\ndroidcore-unbundled: $(INSTALLED_QEMU_VENDOR_DLKMIMAGE)\nendif\n\nifdef INSTALLED_ODM_DLKMIMAGE_TARGET\nINSTALLED_QEMU_ODM_DLKMIMAGE := $(PRODUCT_OUT)/odm_dlkm-qemu.img\n$(INSTALLED_QEMU_ODM_DLKMIMAGE): $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST)\n\t@echo Create odm_dlkm-qemu.img\n\t(export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_ODM_DLKMIMAGE_TARGET))\n\nodm_dlkmimage: $(INSTALLED_QEMU_ODM_DLKMIMAGE)\ndroidcore-unbundled: $(INSTALLED_QEMU_ODM_DLKMIMAGE)\nendif\n\nifdef INSTALLED_SYSTEM_DLKMIMAGE_TARGET\nINSTALLED_QEMU_SYSTEM_DLKMIMAGE := $(PRODUCT_OUT)/system_dlkm-qemu.img\n$(INSTALLED_QEMU_SYSTEM_DLKMIMAGE): $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST)\n\t@echo Create system_dlkm-qemu.img\n\t(export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET))\n\nsystem_dlkmimage: $(INSTALLED_QEMU_SYSTEM_DLKMIMAGE)\ndroidcore-unbundled: $(INSTALLED_QEMU_SYSTEM_DLKMIMAGE)\nendif\n\nQEMU_VERIFIED_BOOT_PARAMS := $(PRODUCT_OUT)/VerifiedBootParams.textproto\n$(QEMU_VERIFIED_BOOT_PARAMS): $(INSTALLED_VBMETAIMAGE_TARGET) $(INSTALLED_SYSTEMIMAGE_TARGET) \\\n    $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(AVBTOOL)\n\t@echo Creating $@\n\t(export AVBTOOL=$(AVBTOOL); $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(INSTALLED_VBMETAIMAGE_TARGET) \\\n    $(INSTALLED_SYSTEMIMAGE_TARGET) $(QEMU_VERIFIED_BOOT_PARAMS))\n\nsystemimage: $(QEMU_VERIFIED_BOOT_PARAMS)\ndroidcore-unbundled: $(QEMU_VERIFIED_BOOT_PARAMS)\n\nendif\n\n# Preprocess files for emulator and sdk.\n-include development/build/tools/sdk-preprocess-files.mk\n\n# -----------------------------------------------------------------\n# The emulator package\nifeq ($(BUILD_EMULATOR),true)\nINTERNAL_EMULATOR_PACKAGE_FILES += \\\n        $(HOST_OUT_EXECUTABLES)/emulator$(HOST_EXECUTABLE_SUFFIX) \\\n        $(INSTALLED_RAMDISK_TARGET) \\\n        $(INSTALLED_SYSTEMIMAGE_TARGET) \\\n        $(INSTALLED_USERDATAIMAGE_TARGET)\n\nname := $(TARGET_PRODUCT)-emulator\n\nINTERNAL_EMULATOR_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip\n\n$(INTERNAL_EMULATOR_PACKAGE_TARGET): $(INTERNAL_EMULATOR_PACKAGE_FILES)\n\t@echo \"Package: $@\"\n\t$(hide) zip -qjX $@ $(INTERNAL_EMULATOR_PACKAGE_FILES)\n\nendif\n\n\n# -----------------------------------------------------------------\n# The SDK\n\nifneq ($(filter sdk,$(MAKECMDGOALS)),)\n\n# The SDK includes host-specific components, so it belongs under HOST_OUT.\nsdk_dir := $(HOST_OUT)/sdk/$(TARGET_PRODUCT)\n\n# Build a name that looks like:\n#\n#     linux-x86   --> android-sdk_12345_linux-x86\n#     darwin-x86  --> android-sdk_12345_mac-x86\n#     windows-x86 --> android-sdk_12345_windows\n#\nifneq ($(HOST_OS),linux)\n  $(error Building the monolithic SDK is only supported on Linux)\nendif\nsdk_name := android-sdk\nINTERNAL_SDK_HOST_OS_NAME := linux-$(SDK_HOST_ARCH)\nsdk_name := $(sdk_name)_$(INTERNAL_SDK_HOST_OS_NAME)\n\nsdk_dep_file := $(sdk_dir)/sdk_deps.mk\n\nATREE_FILES :=\n-include $(sdk_dep_file)\n\n# if we don't have a real list, then use \"everything\"\nifeq ($(strip $(ATREE_FILES)),)\nATREE_FILES := \\\n\t$(ALL_DOCS) \\\n\t$(ALL_SDK_FILES)\nendif\n\natree_dir := development/build\n\n\nsdk_atree_files := $(atree_dir)/sdk.exclude.atree\n\n# development/build/sdk-android-<abi>.atree is used to differentiate\n# between architecture models (e.g. ARMv5TE versus ARMv7) when copying\n# files like the kernel image. We use TARGET_CPU_ABI because we don't\n# have a better way to distinguish between CPU models.\nifneq (,$(strip $(wildcard $(atree_dir)/sdk-android-$(TARGET_CPU_ABI).atree)))\n  sdk_atree_files += $(atree_dir)/sdk-android-$(TARGET_CPU_ABI).atree\nendif\n\nifneq ($(PRODUCT_SDK_ATREE_FILES),)\nsdk_atree_files += $(PRODUCT_SDK_ATREE_FILES)\nelse\nsdk_atree_files += $(atree_dir)/sdk.atree\nendif\n\nSDK_METADATA_DIR :=$= $(call intermediates-dir-for,PACKAGING,framework-doc-stubs-metadata,,COMMON)\nSDK_METADATA_FILES :=$= $(addprefix $(SDK_METADATA_DIR)/,\\\n    activity_actions.txt \\\n    broadcast_actions.txt \\\n    categories.txt \\\n    features.txt \\\n    service_actions.txt \\\n    widgets.txt)\nSDK_METADATA :=$= $(firstword $(SDK_METADATA_FILES))\n$(SDK_METADATA): .KATI_IMPLICIT_OUTPUTS := $(filter-out $(SDK_METADATA),$(SDK_METADATA_FILES))\n$(SDK_METADATA): $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/framework-doc-stubs-metadata.zip\n\trm -rf $(SDK_METADATA_DIR)\n\tmkdir -p $(SDK_METADATA_DIR)\n\tunzip -DDqo $< -d $(SDK_METADATA_DIR)\n\n.PHONY: framework-doc-stubs\nframework-doc-stubs: $(SDK_METADATA)\n\ndeps := \\\n\t$(OUT_DOCS)/offline-sdk-timestamp \\\n\t$(SDK_METADATA_FILES) \\\n  $(INSTALLED_SDK_BUILD_PROP_TARGET) \\\n\t$(ATREE_FILES) \\\n\t$(sdk_atree_files) \\\n\t$(HOST_OUT_EXECUTABLES)/atree \\\n\t$(HOST_OUT_EXECUTABLES)/line_endings\n\n# The name of the subdir within the platforms dir of the sdk. One of:\n# - android-<SDK_INT> (stable base dessert SDKs)\n# - android-<CODENAME> (stable extension SDKs)\n# - android-<SDK_INT>-ext<EXT_INT> (codename SDKs)\nsdk_platform_dir_name := $(strip \\\n  $(if $(filter REL,$(PLATFORM_VERSION_CODENAME)), \\\n    $(if $(filter $(PLATFORM_SDK_EXTENSION_VERSION),$(PLATFORM_BASE_SDK_EXTENSION_VERSION)), \\\n      android-$(PLATFORM_SDK_VERSION), \\\n      android-$(PLATFORM_SDK_VERSION)-ext$(PLATFORM_SDK_EXTENSION_VERSION) \\\n    ), \\\n    android-$(PLATFORM_VERSION_CODENAME) \\\n  ) \\\n)\n\nINTERNAL_SDK_TARGET := $(sdk_dir)/$(sdk_name).zip\n$(INTERNAL_SDK_TARGET): PRIVATE_NAME := $(sdk_name)\n$(INTERNAL_SDK_TARGET): PRIVATE_DIR := $(sdk_dir)/$(sdk_name)\n$(INTERNAL_SDK_TARGET): PRIVATE_DEP_FILE := $(sdk_dep_file)\n$(INTERNAL_SDK_TARGET): PRIVATE_INPUT_FILES := $(sdk_atree_files)\n$(INTERNAL_SDK_TARGET): PRIVATE_PLATFORM_NAME := $(sdk_platform_dir_name)\n# Set SDK_GNU_ERROR to non-empty to fail when a GNU target is built.\n#\n#SDK_GNU_ERROR := true\n\n$(INTERNAL_SDK_TARGET): $(deps)\n\t@echo \"Package SDK: $@\"\n\t$(hide) rm -rf $(PRIVATE_DIR) $@\n\t$(hide) for f in $(strip $(target_gnu_MODULES)); do \\\n\t  if [ -f $$f ]; then \\\n\t    echo SDK: $(if $(SDK_GNU_ERROR),ERROR:,warning:) \\\n\t        including GNU target $$f >&2; \\\n\t    FAIL=$(SDK_GNU_ERROR); \\\n\t  fi; \\\n\tdone; \\\n\tif [ $$FAIL ]; then exit 1; fi\n\t$(hide) ( \\\n\t    ATREE_STRIP=\"$(HOST_STRIP) -x\" \\\n\t    $(HOST_OUT_EXECUTABLES)/atree \\\n\t    $(addprefix -f ,$(PRIVATE_INPUT_FILES)) \\\n\t        -m $(PRIVATE_DEP_FILE) \\\n\t        -I . \\\n\t        -I $(PRODUCT_OUT) \\\n\t        -I $(HOST_OUT) \\\n\t        -I $(TARGET_COMMON_OUT_ROOT) \\\n\t        -v \"PLATFORM_NAME=$(PRIVATE_PLATFORM_NAME)\" \\\n\t        -v \"OUT_DIR=$(OUT_DIR)\" \\\n\t        -v \"HOST_OUT=$(HOST_OUT)\" \\\n\t        -v \"TARGET_ARCH=$(TARGET_ARCH)\" \\\n\t        -v \"TARGET_CPU_ABI=$(TARGET_CPU_ABI)\" \\\n\t        -v \"DLL_EXTENSION=$(HOST_SHLIB_SUFFIX)\" \\\n\t        -o $(PRIVATE_DIR) && \\\n\t    HOST_OUT_EXECUTABLES=$(HOST_OUT_EXECUTABLES) HOST_OS=$(HOST_OS) \\\n\t        development/build/tools/sdk_clean.sh $(PRIVATE_DIR) && \\\n\t    chmod -R ug+rwX $(PRIVATE_DIR) && \\\n\t    cd $(dir $@) && zip -rqX $(notdir $@) $(PRIVATE_NAME) \\\n\t) || ( rm -rf $(PRIVATE_DIR) $@ && exit 44 )\n\nMAIN_SDK_DIR  := $(sdk_dir)\nMAIN_SDK_ZIP  := $(INTERNAL_SDK_TARGET)\n\nendif # sdk in MAKECMDGOALS\n\n# -----------------------------------------------------------------\n# Findbugs\nINTERNAL_FINDBUGS_XML_TARGET := $(PRODUCT_OUT)/findbugs.xml\nINTERNAL_FINDBUGS_HTML_TARGET := $(PRODUCT_OUT)/findbugs.html\n$(INTERNAL_FINDBUGS_XML_TARGET): $(ALL_FINDBUGS_FILES)\n\t@echo UnionBugs: $@\n\t$(hide) $(FINDBUGS_DIR)/unionBugs $(ALL_FINDBUGS_FILES) \\\n\t> $@\n$(INTERNAL_FINDBUGS_HTML_TARGET): $(INTERNAL_FINDBUGS_XML_TARGET)\n\t@echo ConvertXmlToText: $@\n\t$(hide) $(FINDBUGS_DIR)/convertXmlToText -html:fancy.xsl \\\n\t$(INTERNAL_FINDBUGS_XML_TARGET) > $@\n\n# -----------------------------------------------------------------\n# Findbugs\n\n# -----------------------------------------------------------------\n# These are some additional build tasks that need to be run.\nifneq ($(dont_bother),true)\ninclude $(sort $(wildcard $(BUILD_SYSTEM)/tasks/*.mk))\n-include $(sort $(wildcard vendor/*/build/tasks/*.mk))\n-include $(sort $(wildcard device/*/build/tasks/*.mk))\n-include $(sort $(wildcard product/*/build/tasks/*.mk))\n# Also the project-specific tasks\n-include $(sort $(wildcard vendor/*/*/build/tasks/*.mk))\n-include $(sort $(wildcard device/*/*/build/tasks/*.mk))\n-include $(sort $(wildcard product/*/*/build/tasks/*.mk))\n# Also add test specifc tasks\ninclude $(sort $(wildcard platform_testing/build/tasks/*.mk))\ninclude $(sort $(wildcard test/vts/tools/build/tasks/*.mk))\nendif\n\ninclude $(BUILD_SYSTEM)/product-graph.mk\n\n# -----------------------------------------------------------------\n# Create SDK repository packages. Must be done after tasks/* since\n# we need the addon rules defined.\nifneq ($(sdk_repo_goal),)\ninclude $(TOPDIR)development/build/tools/sdk_repo.mk\nendif\n\n# -----------------------------------------------------------------\n# Soong generates the list of all shared libraries that are depended on by fuzz\n# targets. It saves this list as a source:destination pair to\n# FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS, where the source is the path to the\n# build of the unstripped shared library, and the destination is the\n# /data/fuzz/$ARCH/lib (for device) or /fuzz/$ARCH/lib (for host) directory\n# where fuzz target shared libraries are to be \"reinstalled\". The\n# copy-many-files below generates the rules to copy the unstripped shared\n# libraries to the device or host \"reinstallation\" directory. These rules are\n# depended on by each module in soong_cc_prebuilt.mk, where the module will have\n# a dependency on each shared library that it needs to be \"reinstalled\".\nFUZZ_SHARED_DEPS := $(call copy-many-files,$(strip $(FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS)))\n\n# -----------------------------------------------------------------\n# The rule to build all fuzz targets for C++ and Rust, and package them.\n# Note: The packages are created in Soong, and in a perfect world,\n# we'd be able to create the phony rule there. But, if we want to\n# have dist goals for the fuzz target, we need to have the PHONY\n# target defined in make. MakeVarsContext.DistForGoal doesn't take\n# into account that a PHONY rule create by Soong won't be available\n# during make, and such will fail with `writing to readonly\n# directory`, because kati will see 'haiku' as being a file, not a\n# phony target.\n.PHONY: haiku\nhaiku: $(SOONG_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_FUZZ_TARGETS)\n$(call dist-for-goals,haiku,$(SOONG_FUZZ_PACKAGING_ARCH_MODULES))\n$(call dist-for-goals,haiku,$(PRODUCT_OUT)/module-info.json)\n.PHONY: haiku-java\nhaiku-java: $(SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_JAVA_FUZZ_TARGETS)\n$(call dist-for-goals,haiku-java,$(SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES))\n.PHONY: haiku-rust\nhaiku-rust: $(SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_RUST_FUZZ_TARGETS)\n$(call dist-for-goals,haiku-rust,$(SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES))\n$(call dist-for-goals,haiku-rust,$(PRODUCT_OUT)/module-info.json)\n.PHONY: haiku-presubmit\nhaiku-presubmit: $(SOONG_PRESUBMIT_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_PRESUBMIT_FUZZ_TARGETS)\n$(call dist-for-goals,haiku-presubmit,$(SOONG_PRESUBMIT_FUZZ_PACKAGING_ARCH_MODULES))\n\n# -----------------------------------------------------------------\n# Extract additional data files used in Layoutlib\ninclude $(BUILD_SYSTEM)/layoutlib_data.mk\n\n# -----------------------------------------------------------------\n# Desktop pack common variables.\nPACK_IMAGE_SCRIPT := $(HOST_OUT_EXECUTABLES)/pack_image\nUPDATE_PARTITION_SCRIPT := $(HOST_OUT_EXECUTABLES)/update-partition\nIMAGES := $(INSTALLED_BOOTIMAGE_TARGET) \\\n\t$(INSTALLED_SUPERIMAGE_TARGET) \\\n\t$(INSTALLED_INIT_BOOT_IMAGE_TARGET) \\\n\t$(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \\\n\t$(INSTALLED_VBMETAIMAGE_TARGET) \\\n\t$(INSTALLED_USERDATAIMAGE_TARGET)\n\n# -----------------------------------------------------------------\n# Desktop generated firmware filesystem.\nTARGET_PRODUCT_FW_IMAGE_PACKAGE := prebuilt-$(TARGET_PRODUCT)-firmware-image\nGENERATED_FW_IMAGE := $(PRODUCT_OUT)/product/etc/$(TARGET_PRODUCT)-firmware.img\n\ngenerated_fw_image_found := $(strip $(foreach pp,$(PRODUCT_PACKAGES),\\\n\t$(if $(findstring $(TARGET_PRODUCT_FW_IMAGE_PACKAGE),$(pp)),$(pp))))\n\nifneq (,$(generated_fw_image_found))\n$(call dist-for-goals,dist_files,$(GENERATED_FW_IMAGE))\nendif\n\n# -----------------------------------------------------------------\n# Desktop pack image hook.\nifneq (,$(strip $(PACK_DESKTOP_FILESYSTEM_IMAGES)))\nPACK_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_image.bin\n\n$(PACK_IMAGE_TARGET): $(IMAGES) $(PACK_IMAGE_SCRIPT)\n\t$(PACK_IMAGE_SCRIPT) --out_dir $(PRODUCT_OUT) --noarchive\n\nPACKED_IMAGE_ARCHIVE_TARGET := $(PACK_IMAGE_TARGET).gz\n\n$(PACKED_IMAGE_ARCHIVE_TARGET): $(PACK_IMAGE_TARGET) | $(GZIP)\n\t$(GZIP) -fk $(PACK_IMAGE_TARGET)\n\n$(call dist-for-goals,dist_files,$(PACKED_IMAGE_ARCHIVE_TARGET))\n\n.PHONY: pack-image\npack-image: $(PACK_IMAGE_TARGET)\n\nendif # PACK_DESKTOP_FILESYSTEM_IMAGES\n\n# -----------------------------------------------------------------\n# Desktop pack recovery image hook.\nifeq ($(BOARD_USES_DESKTOP_RECOVERY_IMAGE),true)\nPACK_RECOVERY_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_recovery_image.bin\nPACK_RECOVERY_IMAGE_ARGS := --noarchive --recovery\n\nifneq (,$(strip $(PACK_RECOVERY_IMAGE_EXPERIMENTAL)))\nPACK_RECOVERY_IMAGE_ARGS += --experimental\nendif # PACK_RECOVERY_IMAGE_EXPERIMENTAL\n\n$(PACK_RECOVERY_IMAGE_TARGET): $(IMAGES) $(PACK_IMAGE_SCRIPT)\n\t$(PACK_IMAGE_SCRIPT) --out_dir $(PRODUCT_OUT) $(PACK_RECOVERY_IMAGE_ARGS)\n\nPACKED_RECOVERY_IMAGE_ARCHIVE_TARGET := $(PACK_RECOVERY_IMAGE_TARGET).gz\n\n$(PACKED_RECOVERY_IMAGE_ARCHIVE_TARGET): $(PACK_RECOVERY_IMAGE_TARGET) | $(GZIP)\n\t$(GZIP) -fk $(PACK_RECOVERY_IMAGE_TARGET)\n\n$(call dist-for-goals,dist_files,$(PACKED_RECOVERY_IMAGE_ARCHIVE_TARGET))\n\n.PHONY: pack-recovery-image\npack-recovery-image: $(PACK_RECOVERY_IMAGE_TARGET)\n\nRECOVERY_SWAP_KERNEL_TARGET := $(PRODUCT_OUT)/recovery-kernel-swap\n\n# Has swap kernel for insecure recovery image.\nifeq ($(BOARD_USES_DESKTOP_RECOVERY_SWAP_KERNEL),true)\n\n$(call dist-for-goals,dist_files,$(RECOVERY_SWAP_KERNEL_TARGET))\n\nPACK_INSECURE_RECOVERY_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_insecure_recovery_image.bin\n\n$(PACK_INSECURE_RECOVERY_IMAGE_TARGET): PRIVATE_SGDISK := $(HOST_OUT_EXECUTABLES)/sgdisk\n$(PACK_INSECURE_RECOVERY_IMAGE_TARGET): $(PACK_RECOVERY_IMAGE_TARGET) $(UPDATE_PARTITION_SCRIPT) $(RECOVERY_SWAP_KERNEL_TARGET)\n\t@cp -f $< $@\n\t(export SGDISK=$(PRIVATE_SGDISK); $(UPDATE_PARTITION_SCRIPT) KERN-A $@ $(RECOVERY_SWAP_KERNEL_TARGET))\n\nPACKED_INSECURE_RECOVERY_IMAGE_ARCHIVE_TARGET := $(PACK_INSECURE_RECOVERY_IMAGE_TARGET).gz\n\n$(PACKED_INSECURE_RECOVERY_IMAGE_ARCHIVE_TARGET): $(PACK_INSECURE_RECOVERY_IMAGE_TARGET) | $(GZIP)\n\t$(GZIP) -fk $(PACK_INSECURE_RECOVERY_IMAGE_TARGET)\n\n$(call dist-for-goals,dist_files,$(PACKED_INSECURE_RECOVERY_IMAGE_ARCHIVE_TARGET))\n\n.PHONY: pack-insecure-recovery-image\npack-insecure-recovery-image: $(PACK_INSECURE_RECOVERY_IMAGE_TARGET)\n\nendif # BOARD_USES_DESKTOP_RECOVERY_SWAP_KERNEL\n\nendif # BOARD_USES_DESKTOP_RECOVERY_IMAGE\n\n# -----------------------------------------------------------------\n# Desktop pack update image hook.\nifeq ($(BOARD_USES_DESKTOP_UPDATE_IMAGE),true)\nPACK_UPDATE_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_update_image.bin\nPACK_UPDATE_IMAGE_ARGS := --noarchive --update\n\nifneq (,$(strip $(PACK_UPDATE_IMAGE_EXPERIMENTAL)))\nPACK_UPDATE_IMAGE_ARGS += --experimental\nendif # PACK_UPDATE_IMAGE_EXPERIMENTAL\n\n$(PACK_UPDATE_IMAGE_TARGET): $(IMAGES) $(PACK_IMAGE_SCRIPT)\n\t$(PACK_IMAGE_SCRIPT) --out_dir $(PRODUCT_OUT) $(PACK_UPDATE_IMAGE_ARGS)\n\nPACKED_UPDATE_IMAGE_ARCHIVE_TARGET := $(PACK_UPDATE_IMAGE_TARGET).gz\n\n$(PACKED_UPDATE_IMAGE_ARCHIVE_TARGET): $(PACK_UPDATE_IMAGE_TARGET) | $(GZIP)\n\t$(GZIP) -fk $(PACK_UPDATE_IMAGE_TARGET)\n\n$(call dist-for-goals,dist_files,$(PACKED_UPDATE_IMAGE_ARCHIVE_TARGET))\n\n.PHONY: pack-update-image\npack-update-image: $(PACK_UPDATE_IMAGE_TARGET)\n\nendif # BOARD_USES_DESKTOP_UPDATE_IMAGE\n\nPACK_MIGRATION_IMAGE_SCRIPT := $(HOST_OUT_EXECUTABLES)/pack_migration_image\n\n# -----------------------------------------------------------------\n# Desktop pack migration image hook.\nifeq ($(ANDROID_DESKTOP_MIGRATION_IMAGE),true)\nPACK_MIGRATION_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_migration_image.bin\n\n$(PACK_MIGRATION_IMAGE_TARGET): $(IMAGES) $(PACK_MIGRATION_IMAGE_SCRIPT)\n\t$(PACK_MIGRATION_IMAGE_SCRIPT) --out_dir $(PRODUCT_OUT) --noarchive\n\nPACKED_MIGRATION_IMAGE_ARCHIVE_TARGET := $(PACK_MIGRATION_IMAGE_TARGET).gz\n\n$(PACKED_MIGRATION_IMAGE_ARCHIVE_TARGET): $(PACK_MIGRATION_IMAGE_TARGET) | $(GZIP)\n\t$(GZIP) -fk $(PACK_MIGRATION_IMAGE_TARGET)\n\n$(call dist-for-goals,dist_files,$(PACKED_MIGRATION_IMAGE_ARCHIVE_TARGET))\n\n.PHONY: pack-migration-image\npack-migration-image: $(PACK_MIGRATION_IMAGE_TARGET)\n\nendif # ANDROID_DESKTOP_MIGRATION_IMAGE\n\nifdef SOONG_ONLY_ALL_IMAGES_ZIP\n\nallimages_soong_zip_args :=\nallimages_deps :=\n\ndefine include_image\n$(if $(1), \\\n  $(eval allimages_soong_zip_args += -e $(notdir $(1)) -f $(1)) \\\n  $(eval allimages_deps += $(1)))\nendef\n\n$(call include_image,$(INSTALLED_SUPERIMAGE_TARGET))\n$(call include_image,$(INSTALLED_BOOTIMAGE_TARGET))\n$(call include_image,$(INSTALLED_INIT_BOOT_IMAGE_TARGET))\n$(call include_image,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET))\n$(call include_image,$(INSTALLED_USERDATAIMAGE_TARGET))\n$(call include_image,$(INSTALLED_RECOVERYIMAGE_TARGET))\n$(call include_image,$(INSTALLED_VBMETAIMAGE_TARGET))\n$(call include_image,$(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET))\n$(call include_image,$(INSTALLED_VBMETA_VENDORIMAGE_TARGET))\n$(foreach partition,$(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)), \\\n  $(call include_image,$(INSTALLED_VBMETA_$(partition)IMAGE_TARGET)))\n\nallimages_zip := $(PRODUCT_OUT)/all_images.zip\n$(allimages_zip): PRIVATE_SOONG_ZIP_ARGUMENTS := $(allimages_soong_zip_args)\n$(allimages_zip): $(SOONG_ZIP) $(allimages_deps)\n\t$(SOONG_ZIP) -o $@ $(PRIVATE_SOONG_ZIP_ARGUMENTS)\n\n.PHONY: soong_only_diff_test\nsoong_only_diff_test: PRIVATE_ALLIMAGES_ZIP := $(allimages_zip)\nsoong_only_diff_test: $(allimages_zip) $(SOONG_ONLY_ALL_IMAGES_ZIP)\n\tdiff $(PRIVATE_ALLIMAGES_ZIP) $(SOONG_ONLY_ALL_IMAGES_ZIP)\n\nallimages_soong_zip_args :=\nallimages_deps :=\nallimages_zip :=\ninclude_image :=\n\nendif # ifdef SOONG_ONLY_ALL_IMAGES_ZIP\n\n# -----------------------------------------------------------------\n# OS Licensing\n\ninclude $(BUILD_SYSTEM)/os_licensing.mk\n\n# When appending new code to this file, please insert above OS Licensing\n"
  },
  {
    "path": "core/OWNERS",
    "content": "\n# For global Proguard rules\nper-file proguard*.flags = jdduke@google.com\n\n# For version updates\nper-file version_defaults.mk = ankurbakshi@google.com,bkhalife@google.com,jainne@google.com,lokeshgoel@google.com,lubomir@google.com,pscovanner@google.com\n\n# For sdk extensions version updates\nper-file version_defaults.mk = amhk@google.com,gurpreetgs@google.com,mkhokhlova@google.com,robertogil@google.com\n\n# For Ravenwood test configs\nper-file ravenwood_test_config_template.xml =omakoto@google.com\n\n"
  },
  {
    "path": "core/WINPTHREADS_COPYING",
    "content": "Copyright (c) 2011 mingw-w64 project\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the \"Software\"),\nto deal in the Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, sublicense,\nand/or sell copies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n\n\n/*\n * Parts of this library are derived by:\n *\n * Posix Threads library for Microsoft Windows\n *\n * Use at own risk, there is no implied warranty to this code.\n * It uses undocumented features of Microsoft Windows that can change\n * at any time in the future.\n *\n * (C) 2010 Lockless Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n *\n *\n *  * Redistributions of source code must retain the above copyright notice,\n *    this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *  * Neither the name of Lockless Inc. nor the names of its contributors may be\n *    used to endorse or promote products derived from this software without\n *    specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AN\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n"
  },
  {
    "path": "core/aapt2.mk",
    "content": "######################################\n# Compile resource with AAPT2\n# Input variables:\n# - full_android_manifest\n# - my_res_resources\n# - my_overlay_resources\n# - my_compiled_res_base_dir\n# - my_asset_dirs\n# - my_full_asset_paths\n# - my_res_package\n# - R_file_stamp\n# - proguard_options_file\n# - my_generated_res_dirs: Resources generated during the build process and we have to compile them in a single run of aapt2.\n# - my_generated_res_dirs_deps: the dependency to use for my_generated_res_dirs.\n# - my_generated_res_zips: Zip files containing resources\n# - my_apk_split_configs: The configurations for which to generate splits.\n# - built_apk_splits: The paths where AAPT should generate the splits.\n#\n# Output variables:\n# - my_res_resources_flat\n# - my_overlay_resources_flat\n# - my_generated_resources_flata\n#\n######################################\n\n# Compile all the resource files.\nmy_res_resources_flat := \\\n  $(foreach r, $(my_res_resources),\\\n    $(eval o := $(call aapt2-compiled-resource-out-file,$(r),$(my_compiled_res_base_dir)))\\\n    $(eval $(call aapt2-compile-one-resource-file-rule,$(r),$(o)))\\\n    $(o))\n\nmy_overlay_resources_flat := \\\n  $(foreach r, $(my_overlay_resources),\\\n    $(eval o := $(call aapt2-compiled-resource-out-file,$(r),$(my_compiled_res_base_dir)))\\\n    $(eval $(call aapt2-compile-one-resource-file-rule,$(r),$(o)))\\\n    $(o))\n\nmy_resources_flata :=\n# Compile generated resources\nifneq ($(my_generated_res_dirs),)\nmy_generated_resources_flata := $(my_compiled_res_base_dir)/gen_res.flata\n$(my_generated_resources_flata): PRIVATE_SOURCE_RES_DIRS := $(my_generated_res_dirs)\n$(my_generated_resources_flata) : $(my_generated_res_dirs_deps) $(AAPT2)\n\t@echo \"AAPT2 compile $@ <- $(PRIVATE_SOURCE_RES_DIRS)\"\n\t$(call aapt2-compile-resource-dirs)\n\nmy_resources_flata += $(my_generated_resources_flata)\nendif\n\n# Compile zipped resources\nifneq ($(my_generated_res_zips),)\nmy_zipped_resources_flata := $(my_compiled_res_base_dir)/zip_res.flata\n$(my_zipped_resources_flata): PRIVATE_SOURCE_RES_ZIPS := $(my_generated_res_zips)\n$(my_zipped_resources_flata) : $(my_generated_res_zips) $(AAPT2) $(ZIPSYNC)\n\t@echo \"AAPT2 compile $@ <- $(PRIVATE_SOURCE_RES_ZIPS)\"\n\t$(call aapt2-compile-resource-zips)\n\nmy_resources_flata += $(my_zipped_resources_flata)\nendif\n\n# Always set --pseudo-localize, it will be stripped out later for release\n# builds that don't want it.\n$(my_res_resources_flat) $(my_overlay_resources_flat) $(my_resources_flata) $(my_generated_resources_flata) $(my_zippped_resources_flata): \\\n  PRIVATE_AAPT2_CFLAGS := --pseudo-localize $(filter --legacy,$(LOCAL_AAPT_FLAGS))\n\n# TODO(b/78447299): Forbid LOCAL_STATIC_JAVA_AAR_LIBRARIES in aapt2 and remove\n# support for it.\nmy_static_library_resources := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\\\n  $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/package-res.apk)\nmy_static_library_transitive_resource_packages_lists := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\\\n  $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/transitive-res-packages)\nmy_static_library_extra_packages := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\\\n  $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/extra_packages)\nmy_shared_library_resources := $(foreach l, $(LOCAL_SHARED_ANDROID_LIBRARIES),\\\n  $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/package-res.apk)\n\nifneq ($(my_static_library_resources),)\n$(my_res_package): PRIVATE_AAPT_FLAGS += --auto-add-overlay\nendif\n\nifneq ($(my_apk_split_configs),)\n# Join the Split APK paths with their configuration, separated by a ':'.\n$(my_res_package): PRIVATE_AAPT_FLAGS += $(addprefix --split ,$(join $(built_apk_splits),$(addprefix :,$(my_apk_split_configs))))\nendif\n\nmy_srcjar := $(intermediates.COMMON)/aapt2.srcjar\nLOCAL_SRCJARS += $(my_srcjar)\n\naapt_extra_packages := $(intermediates.COMMON)/extra_packages\n\n$(my_res_package): PRIVATE_RES_FLAT := $(my_res_resources_flat)\n$(my_res_package): PRIVATE_OVERLAY_FLAT := $(my_static_library_resources) $(my_resources_flata) $(my_overlay_resources_flat)\n$(my_res_package): PRIVATE_SHARED_ANDROID_LIBRARIES := $(my_shared_library_resources)\n$(my_res_package): PRIVATE_PROGUARD_OPTIONS_FILE := $(proguard_options_file)\n$(my_res_package): PRIVATE_ASSET_DIRS := $(my_asset_dirs)\n$(my_res_package): PRIVATE_JAVA_GEN_DIR := $(intermediates.COMMON)/aapt2\n$(my_res_package): PRIVATE_SRCJAR := $(my_srcjar)\n$(my_res_package): PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES := $(my_static_library_extra_packages)\n$(my_res_package): PRIVATE_STATIC_LIBRARY_TRANSITIVE_RES_PACKAGES_LISTS := $(my_static_library_transitive_resource_packages_lists)\n$(my_res_package): PRIVATE_AAPT_EXTRA_PACKAGES := $(aapt_extra_packages)\n$(my_res_package): .KATI_IMPLICIT_OUTPUTS := $(my_srcjar) $(aapt_extra_packages)\n\nifdef R_file_stamp\n$(my_res_package): PRIVATE_R_FILE_STAMP := $(R_file_stamp)\n$(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(R_file_stamp)\nendif\n\nresource_export_package :=\nifdef LOCAL_EXPORT_PACKAGE_RESOURCES\n# Put this module's resources into a PRODUCT-agnositc package that\n# other packages can use to build their own PRODUCT-agnostic R.java (etc.)\n# files.\nresource_export_package := $(intermediates.COMMON)/package-export.apk\n$(my_res_package): PRIVATE_RESOURCE_EXPORT_PACKAGE := $(resource_export_package)\n$(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(resource_export_package)\nendif\n\nifdef proguard_options_file\n$(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(proguard_options_file)\nendif\n\n$(my_res_package): $(full_android_manifest) $(my_static_library_resources) $(my_static_library_transitive_resource_packages_lists) $(my_shared_library_resources)\n$(my_res_package): $(my_full_asset_paths)\n$(my_res_package): $(my_res_resources_flat) $(my_overlay_resources_flat) \\\n  $(my_resources_flata) $(my_static_library_resources) $(my_static_library_extra_packages) \\\n  $(AAPT2) $(SOONG_ZIP) $(EXTRACT_JAR_PACKAGES)\n\t@echo \"AAPT2 link $@\"\n\t$(call aapt2-link)\nifdef R_file_stamp\n\t@rm -f $(PRIVATE_R_FILE_STAMP)\n\t$(call find-generated-R.java,$(PRIVATE_JAVA_GEN_DIR),$(PRIVATE_R_FILE_STAMP))\nendif\nifdef LOCAL_EXPORT_PACKAGE_RESOURCES\n\t@rm -f $(PRIVATE_RESOURCE_EXPORT_PACKAGE)\n\n\tcp $@ $(PRIVATE_RESOURCE_EXPORT_PACKAGE)\nendif\n\n# Clear inputs only used in this file, so that they're not re-used during the next build\nmy_res_resources :=\nmy_overlay_resources :=\nmy_compiled_res_base_dir :=\nmy_asset_dirs :=\nmy_full_asset_paths :=\nmy_apk_split_configs :=\nmy_generated_res_dirs :=\nmy_generated_res_dirs_deps :=\nmy_generated_res_zips :=\n"
  },
  {
    "path": "core/aapt_flags.mk",
    "content": "## AAPT Flags\n# aapt doesn't accept multiple --extra-packages flags.\n# We have to collapse them into a single --extra-packages flag here.\nLOCAL_AAPT_FLAGS := $(strip $(LOCAL_AAPT_FLAGS))\nifdef LOCAL_AAPT_FLAGS\n  ifeq ($(filter 0 1,$(words $(filter --extra-packages,$(LOCAL_AAPT_FLAGS)))),)\n    aapt_flags := $(subst --extra-packages$(space),--extra-packages@,$(LOCAL_AAPT_FLAGS))\n    aapt_flags_extra_packages := $(patsubst --extra-packages@%,%,$(filter --extra-packages@%,$(aapt_flags)))\n    aapt_flags_extra_packages := $(sort $(subst :,$(space),$(aapt_flags_extra_packages)))\n    LOCAL_AAPT_FLAGS := $(filter-out --extra-packages@%,$(aapt_flags)) \\\n        --extra-packages $(subst $(space),:,$(aapt_flags_extra_packages))\n    aapt_flags_extra_packages :=\n    aapt_flags :=\n  endif\nendif\n"
  },
  {
    "path": "core/allowed_ndk_types.mk",
    "content": "# Determines the types of NDK modules the current module is allowed to link to.\n# Input variables:\n#   LOCAL_MODULE\n#   LOCAL_MODULE_CLASS\n#   LOCAL_NDK_STL_VARIANT\n#   LOCAL_SDK_VERSION\n# Output variables:\n#   my_ndk_stl_family: Family of the NDK STL.\n#   my_ndk_stl_link_type: STL link type, static or shared.\n#   my_allowed_ndk_types: Types of NDK modules that may be linked.\n#   my_warn_ndk_types: Types of NDK modules that shouldn't be linked, but are.\n\nmy_allowed_ndk_types :=\nmy_warn_ndk_types :=\nmy_ndk_stl_family :=\nmy_ndk_stl_link_type :=\n\nifdef LOCAL_SDK_VERSION\n    ifeq ($(LOCAL_NDK_STL_VARIANT),)\n        my_ndk_stl_family := system\n        my_ndk_stl_link_type := shared\n    else ifeq ($(LOCAL_NDK_STL_VARIANT),system)\n        my_ndk_stl_family := system\n        my_ndk_stl_link_type := shared\n    else ifeq ($(LOCAL_NDK_STL_VARIANT),c++_shared)\n        my_ndk_stl_family := libc++\n        my_ndk_stl_link_type := shared\n    else ifeq ($(LOCAL_NDK_STL_VARIANT),c++_static)\n        my_ndk_stl_family := libc++\n        my_ndk_stl_link_type := static\n    else ifeq ($(LOCAL_NDK_STL_VARIANT),none)\n        my_ndk_stl_family := none\n        my_ndk_stl_link_type := none\n    else\n        $(call pretty-error,invalid LOCAL_NDK_STL_VARIANT: $(LOCAL_NDK_STL_VARIANT))\n    endif\n\n    ifeq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES)\n        # The \"none\" link type indicates that nothing is actually linked. Since\n        # this is a static library, it's still up to the final use of the\n        # library whether a static or shared STL should be used.\n        my_ndk_stl_link_type := none\n    endif\n\n    # The system STL is only the C++ ABI layer, so it's compatible with any STL.\n    my_allowed_ndk_types += native:ndk:system:shared\n    my_allowed_ndk_types += native:ndk:system:none\n\n    # Libaries that don't use the STL can be linked to anything.\n    my_allowed_ndk_types += native:ndk:none:none\n\n    # And it's always okay to link a static library that uses your own STL type.\n    # Since nothing was actually linked for the static library, it is up to the\n    # first linked library in the dependency chain which gets used.\n    my_allowed_ndk_types += native:ndk:$(my_ndk_stl_family):none\n\n    ifeq ($(LOCAL_MODULE_CLASS),APPS)\n        # For an app package, it's actually okay to depend on any set of STLs.\n        # If any of the individual libraries depend on each other they've\n        # already been checked for consistency, and if they don't they'll be\n        # kept isolated by RTLD_LOCAL anyway.\n        my_allowed_ndk_types += \\\n            native:ndk:libc++:shared native:ndk:libc++:static\n\n        # The \"none\" link type that used by static libraries is intentionally\n        # omitted here. We should only be dealing with shared libraries in\n        # LOCAL_JNI_SHARED_LIBRARIES.\n    else ifeq ($(my_ndk_stl_link_type),shared)\n        # Modules linked to a shared STL can only use another shared STL.\n        my_allowed_ndk_types += native:ndk:$(my_ndk_stl_family):shared\n    endif\n    # Else we are a non-static library that uses a static STL, and are\n    # incompatible with all other shared libraries that use an STL.\nelse\n    my_allowed_ndk_types := \\\n        native:ndk:none:none \\\n        native:ndk:system:none \\\n        native:ndk:system:shared \\\n\n    ifeq ($(LOCAL_MODULE_CLASS),APPS)\n        # CTS is bad and it should feel bad: http://b/13249737\n        my_warn_ndk_types += native:ndk:libc++:static\n    endif\nendif\n"
  },
  {
    "path": "core/android_manifest.mk",
    "content": "# Handle AndroidManifest.xmls\n# Input: LOCAL_MANIFEST_FILE, LOCAL_FULL_MANIFEST_FILE, LOCAL_FULL_LIBS_MANIFEST_FILES,\n#        LOCAL_USE_EMBEDDED_NATIVE_LIBS\n# Output: full_android_manifest\n\nifeq ($(strip $(LOCAL_MANIFEST_FILE)),)\n  LOCAL_MANIFEST_FILE := AndroidManifest.xml\nendif\nifdef LOCAL_FULL_MANIFEST_FILE\n  main_android_manifest := $(LOCAL_FULL_MANIFEST_FILE)\nelse\n  main_android_manifest := $(LOCAL_PATH)/$(LOCAL_MANIFEST_FILE)\nendif\n\nLOCAL_STATIC_JAVA_AAR_LIBRARIES := $(strip $(LOCAL_STATIC_JAVA_AAR_LIBRARIES))\n\nmy_full_libs_manifest_files :=\n\nifndef LOCAL_DONT_MERGE_MANIFESTS\n  my_full_libs_manifest_files += $(LOCAL_FULL_LIBS_MANIFEST_FILES)\n\n  my_full_libs_manifest_files += $(foreach lib, $(LOCAL_STATIC_JAVA_AAR_LIBRARIES) $(LOCAL_STATIC_ANDROID_LIBRARIES),\\\n    $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/manifest/AndroidManifest.xml)\nendif\n\nfull_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml\n\nifneq (,$(strip $(my_full_libs_manifest_files)))\n  # Set up rules to merge library manifest files\n  fixed_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml.fixed\n\n  $(full_android_manifest): PRIVATE_LIBS_MANIFESTS := $(my_full_libs_manifest_files)\n  $(full_android_manifest): $(ANDROID_MANIFEST_MERGER)\n  $(full_android_manifest) : $(fixed_android_manifest) $(my_full_libs_manifest_files)\n\t@echo \"Merge android manifest files: $@ <-- $< $(PRIVATE_LIBS_MANIFESTS)\"\n\t@mkdir -p $(dir $@)\n\t$(hide) $(ANDROID_MANIFEST_MERGER) --main $< \\\n\t    --libs $(call normalize-path-list,$(PRIVATE_LIBS_MANIFESTS)) \\\n\t    --out $@\nelse\n  fixed_android_manifest := $(full_android_manifest)\nendif\n\nmy_target_sdk_version := $(call module-target-sdk-version)\nmy_min_sdk_version := $(call module-min-sdk-version)\n\nifdef TARGET_BUILD_APPS\n  ifndef TARGET_BUILD_USE_PREBUILT_SDKS\n    ifeq ($(my_target_sdk_version),$(PLATFORM_VERSION_CODENAME))\n      ifdef UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT\n        my_target_sdk_version := $(my_target_sdk_version).$$(cat $(API_FINGERPRINT))\n        my_min_sdk_version := $(my_min_sdk_version).$$(cat $(API_FINGERPRINT))\n        $(fixed_android_manifest): $(API_FINGERPRINT)\n      else ifdef UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA\n        my_target_sdk_version := $(UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA)\n        my_min_sdk_version := $(UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA)\n      endif\n    endif\n  endif\nendif\n\n$(fixed_android_manifest): PRIVATE_MIN_SDK_VERSION := $(my_min_sdk_version)\n$(fixed_android_manifest): PRIVATE_TARGET_SDK_VERSION := $(my_target_sdk_version)\n\nmy_exported_sdk_libs_file := $(call local-intermediates-dir,COMMON)/exported-sdk-libs\n$(fixed_android_manifest): PRIVATE_EXPORTED_SDK_LIBS_FILE := $(my_exported_sdk_libs_file)\n$(fixed_android_manifest): $(my_exported_sdk_libs_file)\n\nmy_manifest_fixer_flags :=\nifneq ($(LOCAL_MODULE_CLASS),APPS)\n    my_manifest_fixer_flags += --library\nendif\nifeq ($(LOCAL_PRIVATE_PLATFORM_APIS),true)\n    my_manifest_fixer_flags += --uses-non-sdk-api\nendif\n\nifeq (true,$(LOCAL_USE_EMBEDDED_DEX))\n    my_manifest_fixer_flags += --use-embedded-dex\nendif\n\nifeq ($(LOCAL_MODULE_CLASS),APPS)\n  ifeq (true,$(call math_gt_or_eq,$(patsubst $(PLATFORM_VERSION_CODENAME),100,$(call module-min-sdk-version)),23))\n    ifeq (true,$(LOCAL_USE_EMBEDDED_NATIVE_LIBS))\n      my_manifest_fixer_flags += --extract-native-libs=false\n    else\n      my_manifest_fixer_flags += --extract-native-libs=true\n    endif\n  else ifeq (true,$(LOCAL_USE_EMBEDDED_NATIVE_LIBS))\n    $(call pretty-error,LOCAL_USE_EMBEDDED_NATIVE_LIBS is set but minSdkVersion $(call module-min-sdk-version) does not support it)\n  endif\nendif\n\n# TODO: Replace this hardcoded list of optional uses-libraries with build logic\n# that propagates optionality via the generated exported-sdk-libs files.\n# Hardcodng doesn't scale and enforces a single choice on each library, while in\n# reality this is a choice of the library users (which may differ).\nmy_optional_sdk_lib_names := \\\n    android.test.base \\\n    android.test.mock \\\n    androidx.window.extensions \\\n    androidx.window.sidecar\n\n$(fixed_android_manifest): PRIVATE_MANIFEST_FIXER_FLAGS := $(my_manifest_fixer_flags)\n# These two libs are added as optional dependencies (<uses-library> with\n# android:required set to false). This is because they haven't existed in pre-P\n# devices, but classes in them were in bootclasspath jars, etc. So making them\n# hard dependencies (andriod:required=true) would prevent apps from being\n# installed to such legacy devices.\n$(fixed_android_manifest): PRIVATE_OPTIONAL_SDK_LIB_NAMES := $(my_optional_sdk_lib_names)\n$(fixed_android_manifest): $(MANIFEST_FIXER)\n$(fixed_android_manifest): $(main_android_manifest)\n\techo $(PRIVATE_OPTIONAL_SDK_LIB_NAMES) | tr ' ' '\\n' > $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional\n\t@echo \"Fix manifest: $@\"\n\t$(MANIFEST_FIXER) \\\n\t  --minSdkVersion $(PRIVATE_MIN_SDK_VERSION) \\\n          --targetSdkVersion $(PRIVATE_TARGET_SDK_VERSION) \\\n          --raise-min-sdk-version \\\n\t  $(PRIVATE_MANIFEST_FIXER_FLAGS) \\\n\t  $(if (PRIVATE_EXPORTED_SDK_LIBS_FILE),\\\n\t    $$(cat $(PRIVATE_EXPORTED_SDK_LIBS_FILE) | grep -v -f $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional | sort -u | sed -e 's/^/\\ --uses-library\\ /' | tr '\\n' ' ') \\\n\t    $$(cat $(PRIVATE_EXPORTED_SDK_LIBS_FILE) | grep -f $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional | sort -u | sed -e 's/^/\\ --optional-uses-library\\ /' | tr '\\n' ' ') \\\n\t   ) \\\n\t  $< $@\n\trm $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional\n\nmy_optional_sdk_lib_names :=\n"
  },
  {
    "path": "core/android_soong_config_vars.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This file defines the Soong Config Variable namespace ANDROID, and also any\n# variables in that namespace.\n\n# The expectation is that no vendor should be using the ANDROID namespace. This\n# check ensures that we don't collide with any existing vendor usage.\n\nifdef SOONG_CONFIG_ANDROID\n$(error The Soong config namespace ANDROID is reserved.)\nendif\n\n$(call add_soong_config_namespace,ANDROID)\n\n# Add variables to the namespace below:\n\n$(call add_soong_config_var,ANDROID,BOARD_USES_ODMIMAGE)\n$(call soong_config_set_bool,ANDROID,BOARD_USES_RECOVERY_AS_BOOT,$(BOARD_USES_RECOVERY_AS_BOOT))\n$(call soong_config_set_bool,ANDROID,BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT,$(BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT))\n$(call add_soong_config_var,ANDROID,CHECK_DEV_TYPE_VIOLATIONS)\n$(call soong_config_set_bool,ANDROID,HAS_BOARD_SYSTEM_EXT_PREBUILT_DIR,$(if $(BOARD_SYSTEM_EXT_PREBUILT_DIR),true,false))\n$(call soong_config_set_bool,ANDROID,HAS_BOARD_PRODUCT_PREBUILT_DIR,$(if $(BOARD_PRODUCT_PREBUILT_DIR),true,false))\n$(call add_soong_config_var,ANDROID,PLATFORM_SEPOLICY_VERSION)\n$(call add_soong_config_var,ANDROID,PLATFORM_SEPOLICY_COMPAT_VERSIONS)\n$(call add_soong_config_var,ANDROID,PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT)\n$(call soong_config_set_bool,ANDROID,RELEASE_BOARD_API_LEVEL_FROZEN,$(RELEASE_BOARD_API_LEVEL_FROZEN))\n$(call add_soong_config_var,ANDROID,TARGET_DYNAMIC_64_32_DRMSERVER)\n$(call add_soong_config_var,ANDROID,TARGET_ENABLE_MEDIADRM_64)\n$(call add_soong_config_var,ANDROID,TARGET_DYNAMIC_64_32_MEDIASERVER)\n$(call soong_config_set_bool,ANDROID,TARGET_SUPPORTS_32_BIT_APPS,$(if $(filter true,$(TARGET_SUPPORTS_32_BIT_APPS)),true,false))\n$(call soong_config_set_bool,ANDROID,TARGET_SUPPORTS_64_BIT_APPS,$(if $(filter true,$(TARGET_SUPPORTS_64_BIT_APPS)),true,false))\n$(call add_soong_config_var,ANDROID,BOARD_GENFS_LABELS_VERSION)\n$(call soong_config_set_bool,ANDROID,PRODUCT_FSVERITY_GENERATE_METADATA,$(if $(filter true,$(PRODUCT_FSVERITY_GENERATE_METADATA)),true,false))\n\n$(call add_soong_config_var,ANDROID,ADDITIONAL_M4DEFS,$(if $(BOARD_SEPOLICY_M4DEFS),$(addprefix -D,$(BOARD_SEPOLICY_M4DEFS))))\n$(call add_soong_config_var,ANDROID,TARGET_ADD_ROOT_EXTRA_VENDOR_SYMLINKS)\n\n# For BUILDING_GSI\n$(call soong_config_set_bool,gsi,building_gsi,$(if $(filter true,$(BUILDING_GSI)),true,false))\n\n# For bootable/recovery\nRECOVERY_API_VERSION := 3\nRECOVERY_FSTAB_VERSION := 2\n$(call soong_config_set, recovery, recovery_api_version, $(RECOVERY_API_VERSION))\n$(call soong_config_set, recovery, recovery_fstab_version, $(RECOVERY_FSTAB_VERSION))\n$(call soong_config_set_bool, recovery ,target_userimages_use_f2fs ,$(if $(TARGET_USERIMAGES_USE_F2FS),true,false))\n$(call soong_config_set_bool, recovery ,has_board_cacheimage_partition_size ,$(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),true,false))\nifdef TARGET_RECOVERY_UI_LIB\n  $(call soong_config_set_string_list, recovery, target_recovery_ui_lib, $(TARGET_RECOVERY_UI_LIB))\nendif\n\n# For Sanitizers\n$(call soong_config_set_bool,ANDROID,ASAN_ENABLED,$(if $(filter address,$(SANITIZE_TARGET)),true,false))\n$(call soong_config_set_bool,ANDROID,HWASAN_ENABLED,$(if $(filter hwaddress,$(SANITIZE_TARGET)),true,false))\n$(call soong_config_set_bool,ANDROID,SANITIZE_TARGET_SYSTEM_ENABLED,$(if $(filter true,$(SANITIZE_TARGET_SYSTEM)),true,false))\n\n# For init.environ.rc\n$(call soong_config_set_bool,ANDROID,GCOV_COVERAGE,$(NATIVE_COVERAGE))\n$(call soong_config_set_bool,ANDROID,CLANG_COVERAGE,$(CLANG_COVERAGE))\n$(call soong_config_set,ANDROID,SCUDO_ALLOCATION_RING_BUFFER_SIZE,$(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE))\n\n$(call soong_config_set_bool,ANDROID,EMMA_INSTRUMENT,$(if $(filter true,$(EMMA_INSTRUMENT)),true,false))\n\n# PRODUCT_PRECOMPILED_SEPOLICY defaults to true. Explicitly check if it's \"false\" or not.\n$(call soong_config_set_bool,ANDROID,PRODUCT_PRECOMPILED_SEPOLICY,$(if $(filter false,$(PRODUCT_PRECOMPILED_SEPOLICY)),false,true))\n\n# For art modules\n$(call soong_config_set_bool,art_module,host_prefer_32_bit,$(if $(filter true,$(HOST_PREFER_32_BIT)),true,false))\nifdef ART_DEBUG_OPT_FLAG\n$(call soong_config_set,art_module,art_debug_opt_flag,$(ART_DEBUG_OPT_FLAG))\nendif\n# The default value of ART_BUILD_HOST_DEBUG is true\n$(call soong_config_set_bool,art_module,art_build_host_debug,$(if $(filter false,$(ART_BUILD_HOST_DEBUG)),false,true))\n\n# For chre\n$(call soong_config_set_bool,chre,chre_daemon_lpma_enabled,$(if $(filter true,$(CHRE_DAEMON_LPMA_ENABLED)),true,false))\n$(call soong_config_set_bool,chre,chre_dedicated_transport_channel_enabled,$(if $(filter true,$(CHRE_DEDICATED_TRANSPORT_CHANNEL_ENABLED)),true,false))\n$(call soong_config_set_bool,chre,chre_log_atom_extension_enabled,$(if $(filter true,$(CHRE_LOG_ATOM_EXTENSION_ENABLED)),true,false))\n$(call soong_config_set_bool,chre,building_vendor_image,$(if $(filter true,$(BUILDING_VENDOR_IMAGE)),true,false))\n$(call soong_config_set_bool,chre,chre_usf_daemon_enabled,$(if $(filter true,$(CHRE_USF_DAEMON_ENABLED)),true,false))\n\nifdef TARGET_BOARD_AUTO\n  $(call add_soong_config_var_value, ANDROID, target_board_auto, $(TARGET_BOARD_AUTO))\nendif\n\n# Apex build mode variables\nifdef APEX_BUILD_FOR_PRE_S_DEVICES\n$(call add_soong_config_var_value,ANDROID,library_linking_strategy,prefer_static)\nelse\nifdef KEEP_APEX_INHERIT\n$(call add_soong_config_var_value,ANDROID,library_linking_strategy,prefer_static)\nendif\nendif\n\n# Enable SystemUI optimizations by default unless explicitly set.\nSYSTEMUI_OPTIMIZE_JAVA ?= true\n$(call add_soong_config_var,ANDROID,SYSTEMUI_OPTIMIZE_JAVA)\n\n# Flag to use baseline profile for SystemUI.\n$(call soong_config_set,ANDROID,release_systemui_use_speed_profile,$(RELEASE_SYSTEMUI_USE_SPEED_PROFILE))\n\n# Flag for enabling compose for Launcher.\n$(call soong_config_set,ANDROID,release_enable_compose_in_launcher,$(RELEASE_ENABLE_COMPOSE_IN_LAUNCHER))\n\nifdef PRODUCT_AVF_ENABLED\n$(call add_soong_config_var_value,ANDROID,avf_enabled,$(PRODUCT_AVF_ENABLED))\nendif\n\n# Enable AVF remote attestation according to the flag value if PRODUCT_AVF_REMOTE_ATTESTATION_DISABLED is not\n# set to true explicitly.\nifneq (true,$(PRODUCT_AVF_REMOTE_ATTESTATION_DISABLED))\n  $(call add_soong_config_var_value,ANDROID,avf_remote_attestation_enabled,$(RELEASE_AVF_ENABLE_REMOTE_ATTESTATION))\nendif\n\nifdef PRODUCT_AVF_MICRODROID_GUEST_GKI_VERSION\n$(call add_soong_config_var_value,ANDROID,avf_microdroid_guest_gki_version,$(PRODUCT_AVF_MICRODROID_GUEST_GKI_VERSION))\nendif\n\nifdef TARGET_BOOTS_16K\n$(call soong_config_set_bool,ANDROID,target_boots_16k,$(filter true,$(TARGET_BOOTS_16K)))\nendif\n\nifdef PRODUCT_CGROUP_V2_SYS_APP_ISOLATION_ENABLED\n$(call add_soong_config_var_value,ANDROID,cgroup_v2_sys_app_isolation,$(PRODUCT_CGROUP_V2_SYS_APP_ISOLATION_ENABLED))\nelse\n$(call add_soong_config_var_value,ANDROID,cgroup_v2_sys_app_isolation,true)\nendif\n\n$(call add_soong_config_var_value,ANDROID,release_avf_allow_preinstalled_apps,$(RELEASE_AVF_ALLOW_PREINSTALLED_APPS))\n$(call add_soong_config_var_value,ANDROID,release_avf_enable_device_assignment,$(RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT))\n$(call add_soong_config_var_value,ANDROID,release_avf_enable_dice_changes,$(RELEASE_AVF_ENABLE_DICE_CHANGES))\n$(call add_soong_config_var_value,ANDROID,release_avf_enable_early_vm,$(RELEASE_AVF_ENABLE_EARLY_VM))\n$(call add_soong_config_var_value,ANDROID,release_avf_enable_llpvm_changes,$(RELEASE_AVF_ENABLE_LLPVM_CHANGES))\n$(call add_soong_config_var_value,ANDROID,release_avf_enable_multi_tenant_microdroid_vm,$(RELEASE_AVF_ENABLE_MULTI_TENANT_MICRODROID_VM))\n$(call add_soong_config_var_value,ANDROID,release_avf_enable_network,$(RELEASE_AVF_ENABLE_NETWORK))\n# TODO(b/341292601): This flag is needed until the V release. We with clean it up after V together\n# with most of the release_avf_ flags here.\n$(call add_soong_config_var_value,ANDROID,release_avf_enable_remote_attestation,$(RELEASE_AVF_ENABLE_REMOTE_ATTESTATION))\n$(call add_soong_config_var_value,ANDROID,release_avf_enable_vendor_modules,$(RELEASE_AVF_ENABLE_VENDOR_MODULES))\n$(call add_soong_config_var_value,ANDROID,release_avf_enable_virt_cpufreq,$(RELEASE_AVF_ENABLE_VIRT_CPUFREQ))\n$(call add_soong_config_var_value,ANDROID,release_avf_microdroid_kernel_version,$(RELEASE_AVF_MICRODROID_KERNEL_VERSION))\n$(call add_soong_config_var_value,ANDROID,release_avf_support_custom_vm_with_paravirtualized_devices,$(RELEASE_AVF_SUPPORT_CUSTOM_VM_WITH_PARAVIRTUALIZED_DEVICES))\n\n$(call add_soong_config_var_value,ANDROID,release_binder_death_recipient_weak_from_jni,$(RELEASE_BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI))\n\n$(call add_soong_config_var_value,ANDROID,release_libpower_no_lock_binder_txn,$(RELEASE_LIBPOWER_NO_LOCK_BINDER_TXN))\n\n$(call add_soong_config_var_value,ANDROID,release_package_libandroid_runtime_punch_holes,$(RELEASE_PACKAGE_LIBANDROID_RUNTIME_PUNCH_HOLES))\n\n$(call add_soong_config_var_value,ANDROID,release_selinux_data_data_ignore,$(RELEASE_SELINUX_DATA_DATA_IGNORE))\nifneq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT)))\n    # write appcompat system properties on userdebug and eng builds\n    $(call add_soong_config_var_value,ANDROID,release_write_appcompat_override_system_properties,true)\nendif\n\n# Enable system_server optimizations by default unless explicitly set or if\n# there may be dependent runtime jars.\n# TODO(b/240588226): Remove the off-by-default exceptions after handling\n# system_server jars automatically w/ R8.\nifeq (true,$(PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS))\n  # If system_server jar ordering is broken, don't assume services.jar can be\n  # safely optimized in isolation, as there may be dependent jars.\n  SYSTEM_OPTIMIZE_JAVA ?= false\nelse ifneq (platform:services,$(lastword $(PRODUCT_SYSTEM_SERVER_JARS)))\n  # If services is not the final jar in the dependency ordering, don't assume\n  # it can be safely optimized in isolation, as there may be dependent jars.\n  # TODO(b/212737576): Remove this exception after integrating use of `$(system_server_trace_refs)`.\n  SYSTEM_OPTIMIZE_JAVA ?= false\nelse\n  SYSTEM_OPTIMIZE_JAVA ?= true\nendif\n\nifeq (true,$(FULL_SYSTEM_OPTIMIZE_JAVA))\n  SYSTEM_OPTIMIZE_JAVA := true\nendif\n\n$(call add_soong_config_var,ANDROID,SYSTEM_OPTIMIZE_JAVA)\n$(call add_soong_config_var,ANDROID,FULL_SYSTEM_OPTIMIZE_JAVA)\n\nifeq (true, $(SYSTEM_OPTIMIZE_JAVA))\n  # Create a list of (non-prefixed) system server jars that follow `services` in\n  # the classpath. This can be used when optimizing `services` to trace any\n  # downstream references that need keeping.\n  # Example: \"foo:service1 platform:services bar:services2\" -> \"services2\"\n  system_server_jars_dependent_on_services := $(shell \\\n      echo \"$(PRODUCT_SYSTEM_SERVER_JARS)\" | \\\n      awk '{found=0; for(i=1;i<=NF;i++){if($$i==\"platform:services\"){found=1; continue} if(found){split($$i,a,\":\"); print a[2]}}}' | \\\n      xargs)\n  ifneq ($(strip $(system_server_jars_dependent_on_services)),)\n    $(call soong_config_set_string_list,ANDROID,system_server_trace_refs,$(system_server_jars_dependent_on_services))\n  endif\nendif\n\n# TODO(b/319697968): Remove this build flag support when metalava fully supports flagged api\n$(call soong_config_set,ANDROID,release_hidden_api_exportable_stubs,$(RELEASE_HIDDEN_API_EXPORTABLE_STUBS))\n\n# Check for SupplementalApi module.\nifeq ($(wildcard packages/modules/SupplementalApi),)\n$(call add_soong_config_var_value,ANDROID,include_nonpublic_framework_api,false)\nelse\n$(call add_soong_config_var_value,ANDROID,include_nonpublic_framework_api,true)\nendif\n\n# Add nfc build flag to soong\nifneq ($(RELEASE_PACKAGE_NFC_STACK),NfcNci)\n  $(call soong_config_set,bootclasspath,nfc_apex_bootclasspath_fragment,true)\nendif\n\n# Add uwb build flag to soong\n$(call soong_config_set,bootclasspath,release_ranging_stack,$(RELEASE_RANGING_STACK))\n\n# Add crashrecovery build flag to soong\n$(call soong_config_set,ANDROID,release_crashrecovery_module,$(RELEASE_CRASHRECOVERY_MODULE))\n# Add crashrecovery file move flags to soong, for both platform and module\nifeq (true,$(RELEASE_CRASHRECOVERY_FILE_MOVE))\n  $(call soong_config_set,ANDROID,crashrecovery_files_in_module,true)\n  $(call soong_config_set,ANDROID,crashrecovery_files_in_platform,false)\nelse\n  $(call soong_config_set,ANDROID,crashrecovery_files_in_module,false)\n  $(call soong_config_set,ANDROID,crashrecovery_files_in_platform,true)\nendif\n# Required as platform_bootclasspath is using this namespace\n$(call soong_config_set,bootclasspath,release_crashrecovery_module,$(RELEASE_CRASHRECOVERY_MODULE))\n\n\n# Add ondeviceintelligence module build flag to soong\nifeq (true,$(RELEASE_ONDEVICE_INTELLIGENCE_MODULE))\n    $(call soong_config_set,ANDROID,release_ondevice_intelligence_module,true)\n    # Required as platform_bootclasspath is using this namespace\n    $(call soong_config_set,bootclasspath,release_ondevice_intelligence_module,true)\n\nelse\n    $(call soong_config_set,ANDROID,release_ondevice_intelligence_platform,true)\n    $(call soong_config_set,bootclasspath,release_ondevice_intelligence_platform,true)\n\nendif\n\n# Add uprobestats build flag to soong\n$(call soong_config_set,ANDROID,release_uprobestats_module,$(RELEASE_UPROBESTATS_MODULE))\n# Add uprobestats file move flags to soong, for both platform and module\nifeq (true,$(RELEASE_UPROBESTATS_FILE_MOVE))\n  $(call soong_config_set,ANDROID,uprobestats_files_in_module,true)\n  $(call soong_config_set,ANDROID,uprobestats_files_in_platform,false)\nelse\n  $(call soong_config_set,ANDROID,uprobestats_files_in_module,false)\n  $(call soong_config_set,ANDROID,uprobestats_files_in_platform,true)\nendif\n\n# Enable Profiling module. Also used by platform_bootclasspath.\n$(call soong_config_set,ANDROID,release_package_profiling_module,$(RELEASE_PACKAGE_PROFILING_MODULE))\n$(call soong_config_set,bootclasspath,release_package_profiling_module,$(RELEASE_PACKAGE_PROFILING_MODULE))\n\n# Move VCN from platform to the Tethering module; used by both platform and module\n$(call soong_config_set,ANDROID,is_vcn_in_mainline,$(RELEASE_MOVE_VCN_TO_MAINLINE))\n\n# Add perf-setup build flag to soong\n# Note: BOARD_PERFSETUP_SCRIPT location must be under platform_testing/scripts/perf-setup/.\nifdef BOARD_PERFSETUP_SCRIPT\n  $(call soong_config_set,perf,board_perfsetup_script,$(notdir $(BOARD_PERFSETUP_SCRIPT)))\nendif\n\n# Add target_use_pan_display flag for hardware/libhardware:gralloc.default\n$(call soong_config_set_bool,gralloc,target_use_pan_display,$(if $(filter true,$(TARGET_USE_PAN_DISPLAY)),true,false))\n\n# Add use_camera_v4l2_hal flag for hardware/libhardware/modules/camera/3_4:camera.v4l2\n$(call soong_config_set_bool,camera,use_camera_v4l2_hal,$(if $(filter true,$(USE_CAMERA_V4L2_HAL)),true,false))\n\n# Add audioserver_multilib flag for hardware/interfaces/soundtrigger/2.0/default:android.hardware.soundtrigger@2.0-impl\nifneq ($(strip $(AUDIOSERVER_MULTILIB)),)\n  $(call soong_config_set,soundtrigger,audioserver_multilib,$(AUDIOSERVER_MULTILIB))\nendif\n\n# Add sim_count, disable_rild_oem_hook, and use_aosp_rild flag for ril related modules\n$(call soong_config_set,ril,sim_count,$(SIM_COUNT))\nifneq ($(DISABLE_RILD_OEM_HOOK), false)\n  $(call soong_config_set_bool,ril,disable_rild_oem_hook,true)\nendif\nifneq ($(ENABLE_VENDOR_RIL_SERVICE), true)\n  $(call soong_config_set_bool,ril,use_aosp_rild,true)\nendif\n\n# Export target_board_platform to soong for hardware/google/graphics/common/libmemtrack:memtrack.$(TARGET_BOARD_PLATFORM)\n$(call soong_config_set,ANDROID,target_board_platform,$(TARGET_BOARD_PLATFORM))\n\n# Export board_uses_scaler_m2m1shot and board_uses_align_restriction to soong for hardware/google/graphics/common/libscaler:libexynosscaler\n$(call soong_config_set_bool,google_graphics,board_uses_scaler_m2m1shot,$(if $(filter true,$(BOARD_USES_SCALER_M2M1SHOT)),true,false))\n$(call soong_config_set_bool,google_graphics,board_uses_align_restriction,$(if $(filter true,$(BOARD_USES_ALIGN_RESTRICTION)),true,false))\n\n# Export related variables to soong for hardware/google/graphics/common/libacryl:libacryl\nifdef BOARD_LIBACRYL_DEFAULT_COMPOSITOR\n  $(call soong_config_set,acryl,libacryl_default_compositor,$(BOARD_LIBACRYL_DEFAULT_COMPOSITOR))\nendif\nifdef BOARD_LIBACRYL_DEFAULT_SCALER\n  $(call soong_config_set,acryl,libacryl_default_scaler,$(BOARD_LIBACRYL_DEFAULT_SCALER))\nendif\nifdef BOARD_LIBACRYL_DEFAULT_BLTER\n  $(call soong_config_set,acryl,libacryl_default_blter,$(BOARD_LIBACRYL_DEFAULT_BLTER))\nendif\nifdef BOARD_LIBACRYL_G2D_HDR_PLUGIN\n  #BOARD_LIBACRYL_G2D_HDR_PLUGIN is set in each board config\n  $(call soong_config_set_bool,acryl,libacryl_use_g2d_hdr_plugin,true)\nendif\n\n# Export related variables to soong for hardware/google/graphics/common/BoardConfigCFlags.mk\n$(call soong_config_set_bool,google_graphics,hwc_no_support_skip_validate,$(if $(filter true,$(HWC_NO_SUPPORT_SKIP_VALIDATE)),true,false))\n$(call soong_config_set_bool,google_graphics,hwc_support_color_transform,$(if $(filter true,$(HWC_SUPPORT_COLOR_TRANSFORM)),true,false))\n$(call soong_config_set_bool,google_graphics,hwc_support_render_intent,$(if $(filter true,$(HWC_SUPPORT_RENDER_INTENT)),true,false))\n$(call soong_config_set_bool,google_graphics,board_uses_virtual_display,$(if $(filter true,$(BOARD_USES_VIRTUAL_DISPLAY)),true,false))\n$(call soong_config_set_bool,google_graphics,board_uses_dt,$(if $(filter true,$(BOARD_USES_DT)),true,false))\n$(call soong_config_set_bool,google_graphics,board_uses_decon_64bit_address,$(if $(filter true,$(BOARD_USES_DECON_64BIT_ADDRESS)),true,false))\n$(call soong_config_set_bool,google_graphics,board_uses_hdrui_gles_conversion,$(if $(filter true,$(BOARD_USES_HDRUI_GLES_CONVERSION)),true,false))\n$(call soong_config_set_bool,google_graphics,uses_idisplay_intf_sec,$(if $(filter true,$(USES_IDISPLAY_INTF_SEC)),true,false))\n\n# Variables for fs_config\n$(call soong_config_set_bool,fs_config,vendor,$(if $(BOARD_USES_VENDORIMAGE)$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),true,false))\n$(call soong_config_set_bool,fs_config,oem,$(if $(BOARD_USES_OEMIMAGE)$(BOARD_OEMIMAGE_FILE_SYSTEM_TYPE),true,false))\n$(call soong_config_set_bool,fs_config,odm,$(if $(BOARD_USES_ODMIMAGE)$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),true,false))\n$(call soong_config_set_bool,fs_config,vendor_dlkm,$(if $(BOARD_USES_VENDOR_DLKMIMAGE)$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false))\n$(call soong_config_set_bool,fs_config,odm_dlkm,$(if $(BOARD_USES_ODM_DLKMIMAGE)$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false))\n$(call soong_config_set_bool,fs_config,system_dlkm,$(if $(BOARD_USES_SYSTEM_DLKMIMAGE)$(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false))\n\n# Variables for telephony\n$(call soong_config_set_bool,telephony,sec_cp_secure_boot,$(if $(filter true,$(SEC_CP_SECURE_BOOT)),true,false))\n$(call soong_config_set_bool,telephony,cbd_protocol_sit,$(if $(filter true,$(CBD_PROTOCOL_SIT)),true,false))\n$(call soong_config_set_bool,telephony,use_radioexternal_hal_aidl,$(if $(filter true,$(USE_RADIOEXTERNAL_HAL_AIDL)),true,false))\n\n# Variables for hwcomposer.$(TARGET_BOARD_PLATFORM)\n$(call soong_config_set_bool,google_graphics,board_uses_hwc_services,$(if $(filter true,$(BOARD_USES_HWC_SERVICES)),true,false))\n\n# Variables for controlling android.hardware.composer.hwc3-service.pixel\n$(call soong_config_set,google_graphics,board_hwc_version,$(BOARD_HWC_VERSION))\n\n# Flag ExcludeExtractApk is to support \"extract_apk\" property for the following conditions.\nifneq ($(WITH_DEXPREOPT),true)\n  $(call soong_config_set_bool,PrebuiltGmsCore,ExcludeExtractApk,true)\nendif\nifeq ($(DONT_DEXPREOPT_PREBUILTS),true)\n  $(call soong_config_set_bool,PrebuiltGmsCore,ExcludeExtractApk,true)\nendif\nifeq ($(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY),true)\n  $(call soong_config_set_bool,PrebuiltGmsCore,ExcludeExtractApk,true)\nendif\n\n# Variables for extra branches\n# TODO(b/383238397): Use bootstrap_go_package to enable extra flags.\n-include vendor/google/build/extra_soong_config_vars.mk\n\n# Variable for CI test packages\nifneq ($(filter arm x86 true,$(TARGET_ARCH) $(TARGET_2ND_ARCH) $(TARGET_ENABLE_MEDIADRM_64)),)\n  $(call soong_config_set_bool,ci_tests,uses_widevine_tests, true)\nendif\n\n# Flags used in GTVS prebuilt apps\n$(call soong_config_set_bool,GTVS,GTVS_COMPRESSED_PREBUILTS,$(if $(findstring $(GTVS_COMPRESSED_PREBUILTS),true yes),true,false))\n$(call soong_config_set_bool,GTVS,GTVS_GMSCORE_BETA,$(if $(findstring $(GTVS_GMSCORE_BETA),true yes),true,false))\n$(call soong_config_set_bool,GTVS,GTVS_SETUPWRAITH_BETA,$(if $(findstring $(GTVS_SETUPWRAITH_BETA),true yes),true,false))\n$(call soong_config_set_bool,GTVS,PRODUCT_USE_PREBUILT_GTVS,$(if $(findstring $(PRODUCT_USE_PREBUILT_GTVS),true yes),true,false))\n\n# Flags used in GTVS_GTV prebuilt apps\n$(call soong_config_set_bool,GTVS_GTV,PRODUCT_USE_PREBUILT_GTVS_GTV,$(if $(findstring $(PRODUCT_USE_PREBUILT_GTVS_GTV),true yes),true,false))\n\n# Check modules to be built in \"otatools-package\".\nifneq ($(wildcard vendor/google/tools/build_mixed_kernels_ramdisk),)\n  $(call soong_config_set_bool,otatools,use_build_mixed_kernels_ramdisk,true)\nendif\nifneq ($(wildcard bootable/deprecated-ota/applypatch),)\n  $(call soong_config_set_bool,otatools,use_bootable_deprecated_ota_applypatch,true)\nendif\n"
  },
  {
    "path": "core/app_certificate_validate.mk",
    "content": "\nifeq (true,$(non_system_module))\n  ifneq (,$(filter $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))%,$(LOCAL_CERTIFICATE)))\n    CERTIFICATE_VIOLATION_MODULES += $(LOCAL_MODULE)\n    ifeq (true,$(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT))\n      $(if $(filter $(LOCAL_MODULE),$(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)),,\\\n        $(call pretty-error,The module in product partition cannot be signed with certificate in system.))\n    endif\n  endif\nendif\n"
  },
  {
    "path": "core/app_prebuilt_internal.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n############################################################\n# Internal build rules for APPS prebuilt modules\n############################################################\n\nifneq (APPS,$(LOCAL_MODULE_CLASS))\n$(call pretty-error,app_prebuilt_internal.mk is for APPS modules only)\nendif\n\nifdef LOCAL_COMPRESSED_MODULE\n  ifneq (true,$(LOCAL_COMPRESSED_MODULE))\n    $(call pretty-error, Unknown value for LOCAL_COMPRESSED_MODULE $(LOCAL_COMPRESSED_MODULE))\n  endif\n  LOCAL_BUILT_MODULE_STEM := package.apk.gz\n  ifndef LOCAL_INSTALLED_MODULE_STEM\n    PACKAGES.$(LOCAL_MODULE).COMPRESSED := gz\n    LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk.gz\n  endif\nelse  # LOCAL_COMPRESSED_MODULE\n  LOCAL_BUILT_MODULE_STEM := package.apk\n  ifndef LOCAL_INSTALLED_MODULE_STEM\n    LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk\n  endif\nendif  # LOCAL_COMPRESSED_MODULE\n\ninclude $(BUILD_SYSTEM)/base_rules.mk\nbuilt_module := $(LOCAL_BUILT_MODULE)\n\n# Run veridex on product, system_ext and vendor modules.\n# We skip it for unbundled app builds where we cannot build veridex.\nmodule_run_appcompat :=\nifeq (true,$(non_system_module))\nifeq (,$(TARGET_BUILD_APPS))  # ! unbundled app build\nifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)\n  module_run_appcompat := true\nendif\nendif\nendif\n\nPACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES))\n\nmy_extract_apk := $(strip $(LOCAL_EXTRACT_APK))\n\n# Select dpi-specific source\nifdef LOCAL_DPI_VARIANTS\nmy_dpi := $(firstword $(filter $(LOCAL_DPI_VARIANTS),$(PRODUCT_AAPT_PREF_CONFIG) $(PRODUCT_AAPT_PREBUILT_DPI)))\nifdef my_dpi\nifdef LOCAL_DPI_FILE_STEM\nmy_prebuilt_dpi_file_stem := $(LOCAL_DPI_FILE_STEM)\nelse\nmy_prebuilt_dpi_file_stem := $(LOCAL_MODULE)_%.apk\nendif\nmy_prebuilt_src_file := $(dir $(my_prebuilt_src_file))$(subst %,$(my_dpi),$(my_prebuilt_dpi_file_stem))\n\nifneq ($(strip $(LOCAL_EXTRACT_DPI_APK)),)\nmy_extract_apk := $(subst %,$(my_dpi),$(LOCAL_EXTRACT_DPI_APK))\nendif  # LOCAL_EXTRACT_DPI_APK\nendif  # my_dpi\nendif  # LOCAL_DPI_VARIANTS\n\nifdef my_extract_apk\nmy_extracted_apk := $(intermediates)/extracted.apk\n\n$(my_extracted_apk): PRIVATE_EXTRACT := $(my_extract_apk)\n$(my_extracted_apk): $(my_prebuilt_src_file)\n\t@echo Extract APK: $@\n\t$(hide) mkdir -p $(dir $@) && rm -f $@\n\t$(hide) unzip -p $< $(PRIVATE_EXTRACT) >$@\n\nmy_prebuilt_src_file := $(my_extracted_apk)\nmy_extracted_apk :=\nmy_extract_apk :=\nendif\n\nrs_compatibility_jni_libs :=\ninclude $(BUILD_SYSTEM)/install_jni_libs.mk\n\nifeq ($(LOCAL_CERTIFICATE),EXTERNAL)\n  # The magic string \"EXTERNAL\" means this package will be signed with\n  # the default dev key throughout the build process, but we expect\n  # the final package to be signed with a different key.\n  #\n  # This can be used for packages where we don't have access to the\n  # keys, but want the package to be predexopt'ed.\n  LOCAL_CERTIFICATE := $(DEFAULT_SYSTEM_DEV_CERTIFICATE)\n  PACKAGES.$(LOCAL_MODULE).EXTERNAL_KEY := 1\n\n  $(built_module) : $(LOCAL_CERTIFICATE).pk8 $(LOCAL_CERTIFICATE).x509.pem\n  $(built_module) : PRIVATE_PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8\n  $(built_module) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem\nendif\nifeq ($(LOCAL_CERTIFICATE),)\n  # It is now a build error to add a prebuilt .apk without\n  # specifying a key for it.\n  $(error No LOCAL_CERTIFICATE specified for prebuilt \"$(my_prebuilt_src_file)\")\nelse ifeq ($(LOCAL_CERTIFICATE),PRESIGNED)\n  # The magic string \"PRESIGNED\" means this package is already checked\n  # signed with its release key.\n  #\n  # By setting .CERTIFICATE but not .PRIVATE_KEY, this package will be\n  # mentioned in apkcerts.txt (with certificate set to \"PRESIGNED\")\n  # but the dexpreopt process will not try to re-sign the app.\n  PACKAGES.$(LOCAL_MODULE).CERTIFICATE := PRESIGNED\n  PACKAGES := $(PACKAGES) $(LOCAL_MODULE)\nelse\n  # If this is not an absolute certificate, assign it to a generic one.\n  ifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./)\n    LOCAL_CERTIFICATE := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))$(LOCAL_CERTIFICATE)\n  endif\n\n  # NOTE(ruperts): Consider moving the logic below out of a conditional,\n  # to avoid the possibility of silently ignoring user settings.\n\n  PACKAGES.$(LOCAL_MODULE).PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8\n  PACKAGES.$(LOCAL_MODULE).CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem\n  PACKAGES := $(PACKAGES) $(LOCAL_MODULE)\n\n  $(built_module) : $(LOCAL_CERTIFICATE).pk8 $(LOCAL_CERTIFICATE).x509.pem\n  $(built_module) : PRIVATE_PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8\n  $(built_module) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem\n\n  additional_certificates := $(foreach c,$(LOCAL_ADDITIONAL_CERTIFICATES), $(c).x509.pem $(c).pk8)\n  $(built_module): $(additional_certificates)\n  $(built_module): PRIVATE_ADDITIONAL_CERTIFICATES := $(additional_certificates)\n\n  $(built_module): $(LOCAL_CERTIFICATE_LINEAGE)\n  $(built_module): PRIVATE_CERTIFICATE_LINEAGE := $(LOCAL_CERTIFICATE_LINEAGE)\n\n  $(built_module): PRIVATE_ROTATION_MIN_SDK_VERSION := $(LOCAL_ROTATION_MIN_SDK_VERSION)\nendif\n\nifneq ($(LOCAL_MODULE_STEM),)\n  PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE_STEM)\nelse\n  PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE)\nendif\n\ninclude $(BUILD_SYSTEM)/app_certificate_validate.mk\n\n# Set a actual_partition_tag (calculated in base_rules.mk) for the package.\nPACKAGES.$(LOCAL_MODULE).PARTITION := $(actual_partition_tag)\n\n# Disable dex-preopt of prebuilts to save space, if requested.\nifndef LOCAL_DEX_PREOPT\nifeq ($(DONT_DEXPREOPT_PREBUILTS),true)\nLOCAL_DEX_PREOPT := false\nendif\nendif\n\n# If the module is a compressed module, we don't pre-opt it because its final\n# installation location will be the data partition.\nifdef LOCAL_COMPRESSED_MODULE\nLOCAL_DEX_PREOPT := false\nendif\n\nmy_dex_jar := $(my_prebuilt_src_file)\ndex_preopt_profile_src_file := $(my_prebuilt_src_file)\n\n#######################################\n# defines built_odex along with rule to install odex\nmy_manifest_or_apk := $(my_prebuilt_src_file)\ninclude $(BUILD_SYSTEM)/dex_preopt_odex_install.mk\nmy_manifest_or_apk :=\n#######################################\nifneq ($(LOCAL_REPLACE_PREBUILT_APK_INSTALLED),)\n# There is a replacement for the prebuilt .apk we can install without any processing.\n$(built_module) : $(LOCAL_REPLACE_PREBUILT_APK_INSTALLED)\n\t$(transform-prebuilt-to-target)\n\nelse  # ! LOCAL_REPLACE_PREBUILT_APK_INSTALLED\n\n# If the SDK version is 30 or higher, the apk is signed with a v2+ scheme.\n# Altering it will invalidate the signature. Just do error checks instead.\ndo_not_alter_apk :=\nifeq (PRESIGNED,$(LOCAL_CERTIFICATE))\n  ifneq (,$(LOCAL_SDK_VERSION))\n    ifeq ($(call math_is_number,$(LOCAL_SDK_VERSION)),true)\n      ifeq ($(call math_gt,$(LOCAL_SDK_VERSION),29),true)\n        do_not_alter_apk := true\n      endif\n    endif\n    # TODO: Add system_current after fixing the existing modules.\n    ifneq ($(filter current test_current core_current,$(LOCAL_SDK_VERSION)),)\n        do_not_alter_apk := true\n    endif\n  endif\nendif\n\nifeq ($(do_not_alter_apk),true)\n$(built_module) : $(my_prebuilt_src_file) | $(ZIPALIGN)\n\t$(transform-prebuilt-to-target)\n\t$(check-jni-dex-compression)\n\t$(check-package-alignment)\nelse\n# Sign and align non-presigned .apks.\n# The embedded prebuilt jni to uncompress.\nifeq ($(LOCAL_CERTIFICATE),PRESIGNED)\n# For PRESIGNED apks we must uncompress every .so file:\n# even if the .so file isn't for the current TARGET_ARCH,\n# we can't strip the file.\nembedded_prebuilt_jni_libs :=\nendif\nifndef embedded_prebuilt_jni_libs\n# No LOCAL_PREBUILT_JNI_LIBS, uncompress all.\nembedded_prebuilt_jni_libs :=\nendif\n$(built_module): PRIVATE_EMBEDDED_JNI_LIBS := $(embedded_prebuilt_jni_libs)\n\nifdef LOCAL_COMPRESSED_MODULE\n$(built_module) : $(GZIP)\nendif\n\nifeq ($(module_run_appcompat),true)\n$(built_module) : $(appcompat-files)\n$(LOCAL_BUILT_MODULE): PRIVATE_INSTALLED_MODULE := $(LOCAL_INSTALLED_MODULE)\nendif\n\nifeq ($(module_run_appcompat),true)\n$(built_module) : $(AAPT2)\nendif\n$(built_module) : $(my_prebuilt_src_file) | $(ZIPALIGN) $(ZIP2ZIP) $(SIGNAPK_JAR) $(SIGNAPK_JNI_LIBRARY_PATH)\n\t$(transform-prebuilt-to-target)\n\t$(uncompress-prebuilt-embedded-jni-libs)\n\t$(remove-unwanted-prebuilt-embedded-jni-libs)\nifeq (true, $(LOCAL_UNCOMPRESS_DEX))\n\t$(uncompress-dexs)\nendif  # LOCAL_UNCOMPRESS_DEX\nifneq ($(LOCAL_CERTIFICATE),PRESIGNED)\nifeq ($(module_run_appcompat),true)\n\t$(call appcompat-header, aapt2)\n\t$(run-appcompat)\nendif  # module_run_appcompat\n\t$(sign-package)\n\t# No need for align-package because sign-package takes care of alignment\nelse  # LOCAL_CERTIFICATE == PRESIGNED\n\t$(align-package)\nendif  # LOCAL_CERTIFICATE\nifdef LOCAL_COMPRESSED_MODULE\n\t$(compress-package)\nendif  # LOCAL_COMPRESSED_MODULE\nendif  # ! do_not_alter_apk\nendif  # ! LOCAL_REPLACE_PREBUILT_APK_INSTALLED\n\n\n###############################\n## Install split apks.\nifdef LOCAL_PACKAGE_SPLITS\nifdef LOCAL_COMPRESSED_MODULE\n$(error $(LOCAL_MODULE): LOCAL_COMPRESSED_MODULE is not currently supported for split installs)\nendif  # LOCAL_COMPRESSED_MODULE\n\n# LOCAL_PACKAGE_SPLITS is a list of apks to be installed.\nbuilt_apk_splits := $(addprefix $(intermediates)/,$(notdir $(LOCAL_PACKAGE_SPLITS)))\ninstalled_apk_splits := $(addprefix $(my_module_path)/,$(notdir $(LOCAL_PACKAGE_SPLITS)))\n\n# Rules to sign the split apks.\nmy_src_dir := $(sort $(dir $(LOCAL_PACKAGE_SPLITS)))\nifneq (1,$(words $(my_src_dir)))\n$(error You must put all the split source apks in the same folder: $(LOCAL_PACKAGE_SPLITS))\nendif\nmy_src_dir := $(LOCAL_PATH)/$(my_src_dir)\n\n$(built_apk_splits) : $(LOCAL_CERTIFICATE).pk8 $(LOCAL_CERTIFICATE).x509.pem | $(ZIPALIGN) $(ZIP2ZIP) $(SIGNAPK_JAR) $(SIGNAPK_JNI_LIBRARY_PATH)\n$(built_apk_splits) : PRIVATE_PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8\n$(built_apk_splits) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem\n$(built_apk_splits) : $(intermediates)/%.apk : $(my_src_dir)/%.apk\n\t$(copy-file-to-new-target)\n\t$(sign-package)\n\n# Rules to install the split apks.\n$(installed_apk_splits) : $(my_module_path)/%.apk : $(intermediates)/%.apk\n\t@echo \"Install: $@\"\n\t$(copy-file-to-new-target)\n\n# Register the additional built and installed files.\nALL_MODULES.$(my_register_name).INSTALLED += $(installed_apk_splits)\nALL_MODULES.$(my_register_name).BUILT_INSTALLED += \\\n  $(foreach s,$(LOCAL_PACKAGE_SPLITS),$(intermediates)/$(notdir $(s)):$(my_module_path)/$(notdir $(s)))\n\n# Make sure to install the splits when you run \"make <module_name>\".\n$(my_all_targets): $(installed_apk_splits)\n\nendif # LOCAL_PACKAGE_SPLITS\n\n###########################################################\n## SBOM generation\n###########################################################\ninclude $(BUILD_SBOM_GEN)\n"
  },
  {
    "path": "core/art_config.mk",
    "content": "# ART configuration that has to be determined after product config is resolved.\n#\n# Inputs:\n# PRODUCT_ENABLE_UFFD_GC: See comments in build/make/core/product.mk.\n# OVERRIDE_ENABLE_UFFD_GC: Overrides PRODUCT_ENABLE_UFFD_GC. Can be passed from the commandline for\n# debugging purposes.\n# BOARD_API_LEVEL: See comments in build/make/core/main.mk.\n# BOARD_SHIPPING_API_LEVEL: See comments in build/make/core/main.mk.\n# PRODUCT_SHIPPING_API_LEVEL: See comments in build/make/core/product.mk.\n#\n# Outputs:\n# ENABLE_UFFD_GC: Whether to use userfaultfd GC.\n\nconfig_enable_uffd_gc := \\\n  $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default)\n\nifeq (,$(filter default true false,$(config_enable_uffd_gc)))\n  $(error Unknown PRODUCT_ENABLE_UFFD_GC value: $(config_enable_uffd_gc))\nendif\n\nENABLE_UFFD_GC := $(config_enable_uffd_gc)\n\n# Create APEX_BOOT_JARS_EXCLUDED which is a list of jars to be removed from\n# ApexBoorJars when built from mainline prebuilts.\n# Note that RELEASE_APEX_BOOT_JARS_PREBUILT_EXCLUDED_LIST is the list of module names\n# and library names of jars that need to be removed. We have to keep separated list per\n# release config due to possibility of different prebuilt content.\n#\n# If a device has opted to not use google prebuilts (determined using\n# PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS), then no jars need to be removed.\n# Example of products where PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS is true are\n# 1. aosp devices (they do not use google apexes)\n# 2. hwasan devices (apex prebuilts are not compatible with these devices)\n# 3. coverage builds\nifneq (true, $(PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS))\n  APEX_BOOT_JARS_EXCLUDED += $(RELEASE_APEX_BOOT_JARS_PREBUILT_EXCLUDED_LIST)\nendif\n"
  },
  {
    "path": "core/artifact_path_requirements.mk",
    "content": "# This file contains logic to enforce artifact path requirements\n# defined in product makefiles.\n\n# Fakes don't get installed, and NDK stubs aren't installed to device.\nstatic_allowed_patterns := $(TARGET_OUT_FAKE)/% $(SOONG_OUT_DIR)/ndk/%\n# RROs become REQUIRED by the source module, but are always placed on the vendor partition.\nstatic_allowed_patterns += %__auto_generated_characteristics_rro.apk\nstatic_allowed_patterns += %__auto_generated_rro_product.apk\nstatic_allowed_patterns += %__auto_generated_rro_vendor.apk\n# Auto-included targets are not considered\nstatic_allowed_patterns += $(call product-installed-files,)\n# $(PRODUCT_OUT)/apex is where shared libraries in APEXes get installed.\n# The path can be considered as a fake path, as the shared libraries\n# are installed there just to have symbols files for them under\n# $(PRODUCT_OUT)/symbols/apex for debugging purpose. The /apex directory\n# is never compiled into a filesystem image.\nstatic_allowed_patterns += $(PRODUCT_OUT)/apex/%\nifeq (true,$(BOARD_USES_SYSTEM_OTHER_ODEX))\n  # Allow system_other odex space optimization.\n  static_allowed_patterns += \\\n    $(TARGET_OUT_SYSTEM_OTHER)/%.odex \\\n    $(TARGET_OUT_SYSTEM_OTHER)/%.vdex \\\n    $(TARGET_OUT_SYSTEM_OTHER)/%.art\nendif\n\nifneq (,$(filter-out true false relaxed strict,$(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS))$(filter-out 1 0,$(words $(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS))))\n  $(error PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS must be one of [true, false, relaxed, strict], found: $(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS))\nendif\n\nall_offending_files :=\n$(foreach makefile,$(ARTIFACT_PATH_REQUIREMENT_PRODUCTS),\\\n  $(eval requirements := $(PRODUCTS.$(makefile).ARTIFACT_PATH_REQUIREMENTS)) \\\n  $(eval ### Verify that the product only produces files inside its path requirements.) \\\n  $(eval allowed := $(PRODUCTS.$(makefile).ARTIFACT_PATH_ALLOWED_LIST)) \\\n  $(eval path_patterns := $(call resolve-product-relative-paths,$(requirements),%)) \\\n  $(eval allowed_patterns := $(call resolve-product-relative-paths,$(allowed))) \\\n  $(eval files := $(call product-installed-files, $(makefile))) \\\n  $(eval offending_files := $(filter-out $(path_patterns) $(allowed_patterns) $(static_allowed_patterns),$(files))) \\\n  $(call maybe-print-list-and-error,$(offending_files),\\\n    $(makefile) produces files outside its artifact path requirement. \\\n    Allowed paths are $(subst $(space),$(comma)$(space),$(addsuffix *,$(requirements)))) \\\n  $(eval unused_allowed := $(filter-out $(files),$(allowed_patterns))) \\\n  $(if $(PRODUCTS.$(makefile).ARTIFACT_PATH_REQUIREMENT_IS_RELAXED),, \\\n    $(call maybe-print-list-and-error,$(unused_allowed),$(makefile) includes redundant allowed entries in its artifact path requirement.) \\\n  ) \\\n  $(eval ### Optionally verify that nothing else produces files inside this artifact path requirement.) \\\n  $(eval extra_files := $(filter-out $(files) $(HOST_OUT)/%,$(product_target_FILES))) \\\n  $(eval files_in_requirement := $(filter $(path_patterns),$(extra_files))) \\\n  $(eval all_offending_files += $(files_in_requirement)) \\\n  $(eval allowed := $(PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST)) \\\n  $(eval allowed_patterns := $(call resolve-product-relative-paths,$(allowed))) \\\n  $(eval offending_files := $(filter-out $(allowed_patterns),$(files_in_requirement))) \\\n  $(eval enforcement := $(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS)) \\\n  $(if $(filter-out false,$(enforcement)),\\\n    $(call maybe-print-list-and-error,$(offending_files),\\\n      $(INTERNAL_PRODUCT) produces files inside $(makefile)s artifact path requirement. \\\n      $(PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT)) \\\n    $(eval unused_allowed := $(if $(filter true strict,$(enforcement)),\\\n      $(foreach p,$(allowed_patterns),$(if $(filter $(p),$(extra_files)),,$(p))))) \\\n    $(call maybe-print-list-and-error,$(unused_allowed),$(INTERNAL_PRODUCT) includes redundant artifact path requirement allowed list entries.) \\\n  ) \\\n)\n$(PRODUCT_OUT)/offending_artifacts.txt:\n\trm -f $@\n\t$(foreach f,$(sort $(all_offending_files)),echo $(f) >> $@;)\n"
  },
  {
    "path": "core/autogen_test_config.mk",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This build rule allows TradeFed test config file to be created based on\n# following inputs:\n#   is_native: If the test is a native test.\n#   full_android_manifest: Name of the AndroidManifest file for the test.\n# Output:\n#   autogen_test_config_file: Path to the test config file generated.\n\nautogen_test_config_file := $(dir $(LOCAL_BUILT_MODULE))$(LOCAL_MODULE).config\n# TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base.\nautogen_test_install_base := /data/local/tmp\n# Automatically setup test root for native test.\nifeq (true,$(is_native))\n  ifeq (true,$(LOCAL_VENDOR_MODULE))\n    autogen_test_install_base = /data/local/tests/vendor\n  endif\n  ifeq (true,$(call module-in-vendor-or-product))\n    autogen_test_install_base = /data/local/tests/vendor\n  endif\nendif\nifeq (true,$(is_native))\nifeq ($(LOCAL_NATIVE_BENCHMARK),true)\nautogen_test_config_template := $(NATIVE_BENCHMARK_TEST_CONFIG_TEMPLATE)\nelse\n  ifeq ($(LOCAL_IS_HOST_MODULE),true)\n    autogen_test_config_template := $(NATIVE_HOST_TEST_CONFIG_TEMPLATE)\n  else\n    autogen_test_config_template := $(NATIVE_TEST_CONFIG_TEMPLATE)\n  endif\nendif\n# Auto generating test config file for native test\n$(autogen_test_config_file): PRIVATE_TEST_INSTALL_BASE := $(autogen_test_install_base)\n$(autogen_test_config_file): PRIVATE_MODULE_NAME := $(LOCAL_MODULE)\n$(autogen_test_config_file) : $(autogen_test_config_template)\n\t@echo \"Auto generating test config $(notdir $@)\"\n\t$(hide) sed 's&{MODULE}&$(PRIVATE_MODULE_NAME)&g;s&{TEST_INSTALL_BASE}&$(PRIVATE_TEST_INSTALL_BASE)&g;s&{EXTRA_CONFIGS}&&g' $< > $@\nmy_auto_generate_config := true\nelse\n# Auto generating test config file for instrumentation test\nifneq (,$(full_android_manifest))\n$(autogen_test_config_file): PRIVATE_AUTOGEN_TEST_CONFIG_SCRIPT := $(AUTOGEN_TEST_CONFIG_SCRIPT)\n$(autogen_test_config_file): PRIVATE_TEST_CONFIG_ANDROID_MANIFEST := $(full_android_manifest)\n$(autogen_test_config_file): PRIVATE_EMPTY_TEST_CONFIG := $(EMPTY_TEST_CONFIG)\n$(autogen_test_config_file): PRIVATE_TEMPLATE := $(INSTRUMENTATION_TEST_CONFIG_TEMPLATE)\n$(autogen_test_config_file) : $(full_android_manifest) $(EMPTY_TEST_CONFIG) $(INSTRUMENTATION_TEST_CONFIG_TEMPLATE) $(AUTOGEN_TEST_CONFIG_SCRIPT)\n\t@echo \"Auto generating test config $(notdir $@)\"\n\t@rm -f $@\n\t$(hide) $(PRIVATE_AUTOGEN_TEST_CONFIG_SCRIPT) $@ $(PRIVATE_TEST_CONFIG_ANDROID_MANIFEST) $(PRIVATE_EMPTY_TEST_CONFIG) $(PRIVATE_TEMPLATE)\nmy_auto_generate_config := true\nendif # ifneq (,$(full_android_manifest))\nendif # ifneq (true,$(is_native))\n\nifeq (true,$(my_auto_generate_config))\n  LOCAL_INTERMEDIATE_TARGETS += $(autogen_test_config_file)\n  $(LOCAL_BUILT_MODULE): $(autogen_test_config_file)\n  ALL_MODULES.$(my_register_name).auto_test_config := true\n  $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_autogen := true\nelse\n  autogen_test_config_file :=\nendif\n\nmy_auto_generate_config :=\n"
  },
  {
    "path": "core/base_rules.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Catch users that directly include base_rules.mk\n$(call record-module-type,base_rules)\n\n# Users can define base-rules-hook in their buildspec.mk to perform\n# arbitrary operations as each module is included.\nifdef base-rules-hook\n  ifndef _has_warned_about_base_rules_hook\n    $(warning base-rules-hook is deprecated, please remove usages of it and/or convert to Soong.)\n    _has_warned_about_base_rules_hook := true\n  endif\n  $(if $(base-rules-hook),)\nendif\n\n###########################################################\n## Common instructions for a generic module.\n###########################################################\n\nLOCAL_MODULE := $(strip $(LOCAL_MODULE))\nifeq ($(LOCAL_MODULE),)\n  $(error $(LOCAL_PATH): LOCAL_MODULE is not defined)\nendif\n$(call verify-module-name)\n\nmy_test_data :=\nmy_test_config :=\n\nLOCAL_IS_HOST_MODULE := $(strip $(LOCAL_IS_HOST_MODULE))\nifdef LOCAL_IS_HOST_MODULE\n  ifneq ($(LOCAL_IS_HOST_MODULE),true)\n    $(error $(LOCAL_PATH): LOCAL_IS_HOST_MODULE must be \"true\" or empty, not \"$(LOCAL_IS_HOST_MODULE)\")\n  endif\n  ifeq ($(LOCAL_HOST_PREFIX),)\n    my_prefix := HOST_\n  else\n    my_prefix := $(LOCAL_HOST_PREFIX)\n  endif\n  my_host := host-\n  my_kind := HOST\nelse\n  my_prefix := TARGET_\n  my_kind :=\n  my_host :=\nendif\n\nifeq ($(my_prefix),HOST_CROSS_)\n  my_host_cross := true\nelse\n  my_host_cross :=\nendif\n\nifeq (true, $(LOCAL_PRODUCT_MODULE))\nifneq (,$(filter $(LOCAL_MODULE),$(PRODUCT_FORCE_PRODUCT_MODULES_TO_SYSTEM_PARTITION)))\n  LOCAL_PRODUCT_MODULE :=\nendif\nendif\n\n_path := $(LOCAL_MODULE_PATH) $(LOCAL_MODULE_PATH_32) $(LOCAL_MODULE_PATH_64)\nifneq ($(filter $(TARGET_OUT_VENDOR)%,$(_path)),)\nLOCAL_VENDOR_MODULE := true\nelse ifneq ($(filter $(TARGET_OUT_OEM)/%,$(_path)),)\nLOCAL_OEM_MODULE := true\nelse ifneq ($(filter $(TARGET_OUT_ODM)/%,$(_path)),)\nLOCAL_ODM_MODULE := true\nelse ifneq ($(filter $(TARGET_OUT_PRODUCT)/%,$(_path)),)\nLOCAL_PRODUCT_MODULE := true\nelse ifneq ($(filter $(TARGET_OUT_SYSTEM_EXT)/%,$(_path)),)\nLOCAL_SYSTEM_EXT_MODULE := true\nendif\n_path :=\n\n# TODO(b/135957588) Remove following workaround\n# LOCAL_PRODUCT_SERVICES_MODULE to LOCAL_PRODUCT_MODULE for all Android.mk\nifndef LOCAL_PRODUCT_MODULE\nLOCAL_PRODUCT_MODULE := $(LOCAL_PRODUCT_SERVICES_MODULE)\nendif\n\nifndef LOCAL_PROPRIETARY_MODULE\n  LOCAL_PROPRIETARY_MODULE := $(LOCAL_VENDOR_MODULE)\nendif\nifndef LOCAL_VENDOR_MODULE\n  LOCAL_VENDOR_MODULE := $(LOCAL_PROPRIETARY_MODULE)\nendif\nifneq ($(filter-out $(LOCAL_PROPRIETARY_MODULE),$(LOCAL_VENDOR_MODULE))$(filter-out $(LOCAL_VENDOR_MODULE),$(LOCAL_PROPRIETARY_MODULE)),)\n$(call pretty-error,Only one of LOCAL_PROPRIETARY_MODULE[$(LOCAL_PROPRIETARY_MODULE)] and LOCAL_VENDOR_MODULE[$(LOCAL_VENDOR_MODULE)] may be set, or they must be equal)\nendif\n\nifeq ($(LOCAL_HOST_MODULE),true)\nmy_image_variant := host\nelse ifeq ($(LOCAL_VENDOR_MODULE),true)\nmy_image_variant := vendor\nelse ifeq ($(LOCAL_OEM_MODULE),true)\nmy_image_variant := vendor\nelse ifeq ($(LOCAL_ODM_MODULE),true)\nmy_image_variant := vendor\nelse ifeq ($(LOCAL_PRODUCT_MODULE),true)\nmy_image_variant := product\nelse\nmy_image_variant := core\nendif\n\nnon_system_module := $(filter true, \\\n   $(LOCAL_PRODUCT_MODULE) \\\n   $(LOCAL_SYSTEM_EXT_MODULE) \\\n   $(LOCAL_VENDOR_MODULE) \\\n   $(LOCAL_PROPRIETARY_MODULE))\n\ninclude $(BUILD_SYSTEM)/local_vendor_product.mk\n\n# local_current_sdk needs to run before local_systemsdk because the former may override\n# LOCAL_SDK_VERSION which is used by the latter.\ninclude $(BUILD_SYSTEM)/local_current_sdk.mk\n\n# Check if the use of System SDK is correct. Note that, for Soong modules, the system sdk version\n# check is done in Soong. No need to do it twice.\nifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\ninclude $(BUILD_SYSTEM)/local_systemsdk.mk\nendif\n\n# Ninja has an implicit dependency on the command being run, and kati will\n# regenerate the ninja manifest if any read makefile changes, so there is no\n# need to have dependencies on makefiles.\n# This won't catch all the cases where LOCAL_ADDITIONAL_DEPENDENCIES contains\n# a .mk file, because a few users of LOCAL_ADDITIONAL_DEPENDENCIES don't include\n# base_rules.mk, but it will fix the most common ones.\nLOCAL_ADDITIONAL_DEPENDENCIES := $(filter-out %.mk,$(LOCAL_ADDITIONAL_DEPENDENCIES))\n\nmy_bad_deps := $(strip $(foreach dep,$(filter-out | ||,$(LOCAL_ADDITIONAL_DEPENDENCIES)),\\\n                 $(if $(findstring /,$(dep)),,$(dep))))\nifneq ($(my_bad_deps),)\n$(call pretty-warning,\"Bad LOCAL_ADDITIONAL_DEPENDENCIES: $(my_bad_deps)\")\n$(call pretty-error,\"LOCAL_ADDITIONAL_DEPENDENCIES must only contain paths (not module names)\")\nendif\n\n###########################################################\n## Validate and define fallbacks for input LOCAL_* variables.\n###########################################################\n\nLOCAL_UNINSTALLABLE_MODULE := $(strip $(LOCAL_UNINSTALLABLE_MODULE))\n\n# Only the tags mentioned in this test are expected to be set by module\n# makefiles. Anything else is either a typo or a source of unexpected\n# behaviors.\nifneq ($(filter-out tests optional samples,$(LOCAL_MODULE_TAGS)),)\n$(call pretty-error,unusual tags: $(filter-out tests optional samples,$(LOCAL_MODULE_TAGS)))\nendif\n\nLOCAL_MODULE_CLASS := $(strip $(LOCAL_MODULE_CLASS))\nifneq ($(words $(LOCAL_MODULE_CLASS)),1)\n  $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS must contain exactly one word, not \"$(LOCAL_MODULE_CLASS)\")\nendif\n\nmy_32_64_bit_suffix := $(if $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT),64,32)\n\nifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))\nmy_multilib_module_path := $(strip $(LOCAL_MODULE_PATH_$(my_32_64_bit_suffix)))\nifdef my_multilib_module_path\nmy_module_path := $(my_multilib_module_path)\nelse\nmy_module_path := $(strip $(LOCAL_MODULE_PATH))\nendif\nmy_module_path := $(patsubst %/,%,$(my_module_path))\nmy_module_relative_path := $(strip $(LOCAL_MODULE_RELATIVE_PATH))\n\nifdef LOCAL_IS_HOST_MODULE\n  partition_tag :=\n  actual_partition_tag :=\nelse\nifeq (true,$(strip $(LOCAL_VENDOR_MODULE)))\n  partition_tag := _VENDOR\n  # A vendor module could be on the vendor partition at \"vendor\" or the system\n  # partition at \"system/vendor\".\n  actual_partition_tag := $(if $(filter true,$(BOARD_USES_VENDORIMAGE)),vendor,system)\nelse ifeq (true,$(strip $(LOCAL_OEM_MODULE)))\n  partition_tag := _OEM\n  actual_partition_tag := oem\nelse ifeq (true,$(strip $(LOCAL_ODM_MODULE)))\n  partition_tag := _ODM\n  # An ODM module could be on the odm partition at \"odm\", the vendor partition\n  # at \"vendor/odm\", or the system partition at \"system/vendor/odm\".\n  actual_partition_tag := $(if $(filter true,$(BOARD_USES_ODMIMAGE)),odm,$(if $(filter true,$(BOARD_USES_VENDORIMAGE)),vendor,system))\nelse ifeq (true,$(strip $(LOCAL_PRODUCT_MODULE)))\n  partition_tag := _PRODUCT\n  # A product module could be on the product partition at \"product\" or the\n  # system partition at \"system/product\".\n  actual_partition_tag := $(if $(filter true,$(BOARD_USES_PRODUCTIMAGE)),product,system)\nelse ifeq (true,$(strip $(LOCAL_SYSTEM_EXT_MODULE)))\n  partition_tag := _SYSTEM_EXT\n  # A system_ext-specific module could be on the system_ext partition at\n  # \"system_ext\" or the system partition at \"system/system_ext\".\n  actual_partition_tag := $(if $(filter true,$(BOARD_USES_SYSTEM_EXTIMAGE)),system_ext,system)\nelse ifeq (NATIVE_TESTS,$(LOCAL_MODULE_CLASS))\n  partition_tag := _DATA\n  actual_partition_tag := data\nelse\n  # The definition of should-install-to-system will be different depending\n  # on which goal (e.g., sdk or just droid) is being built.\n  partition_tag := $(if $(call should-install-to-system,$(LOCAL_MODULE_TAGS)),,_DATA)\n  actual_partition_tag := $(if $(partition_tag),data,system)\nendif\nendif\n\n# if this is a soong module, verify that LOCAL_COMPATIBILITY_SUITE (legacy) matches\n# LOCAL_SOONG_PROVIDER_TEST_SUITES (new, via TestSuiteInfoProvider instead of AndroidMk stuff),\n# modulo \"null-sute\", \"mts\", and \"mcts\". mts/mcts are automatically added if there's a different\n# suite starting with \"m(c)ts-\". null-suite seems useless and is sometimes automatically added\n# if no other suites are added.\nifneq (,$(filter $(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)))\n  a := $(filter-out null-suite mts mcts,$(sort $(LOCAL_COMPATIBILITY_SUITE)))\n  b := $(filter-out null-suite mts mcts,$(sort $(LOCAL_SOONG_PROVIDER_TEST_SUITES)))\n  ifneq ($(a),$(b))\n    $(error $(LOCAL_MODULE): LOCAL_COMPATIBILITY_SUITE did not match LOCAL_SOONG_PROVIDER_TEST_SUITES$(newline)  LOCAL_COMPATIBILITY_SUITE: $(a)$(newline)  LOCAL_SOONG_PROVIDER_TEST_SUITES: $(b)$(newline))\n  endif\n  a :=\n  b :=\nendif\n\n# For test modules that lack a suite tag, set null-suite as the default.\n# We only support adding a default suite to native tests, native benchmarks, and instrumentation tests.\n# This is because they are the only tests we currently auto-generate test configs for.\nifndef LOCAL_COMPATIBILITY_SUITE\n  ifneq ($(filter NATIVE_TESTS NATIVE_BENCHMARK, $(LOCAL_MODULE_CLASS)),)\n    LOCAL_COMPATIBILITY_SUITE := null-suite\n  endif\n  ifneq ($(filter APPS, $(LOCAL_MODULE_CLASS)),)\n    ifneq ($(filter $(LOCAL_MODULE_TAGS),tests),)\n      LOCAL_COMPATIBILITY_SUITE := null-suite\n    endif\n  endif\nendif\n\nuse_testcase_folder :=\nifeq ($(my_module_path),)\n  ifneq ($(LOCAL_MODULE),$(filter $(LOCAL_MODULE),$(DEFAULT_DATA_OUT_MODULES)))\n    ifdef LOCAL_COMPATIBILITY_SUITE\n      ifneq (true, $(LOCAL_IS_HOST_MODULE))\n        use_testcase_folder := true\n      endif\n    endif\n  endif\nendif\n\nifeq ($(LOCAL_IS_UNIT_TEST),true)\n  ifeq ($(LOCAL_IS_HOST_MODULE),true)\n    LOCAL_COMPATIBILITY_SUITE += host-unit-tests\n  endif\nendif\n\nifeq ($(my_module_path),)\n  install_path_var := $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT$(partition_tag)_$(LOCAL_MODULE_CLASS)\n  ifeq (true,$(LOCAL_PRIVILEGED_MODULE))\n    install_path_var := $(install_path_var)_PRIVILEGED\n  endif\n\n  my_module_path := $($(install_path_var))\n\n  # If use_testcase_folder be set, and LOCAL_MODULE_PATH not set,\n  # overwrite the default path under testcase.\n  ifeq ($(use_testcase_folder),true)\n    arch_dir := $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)\n    testcase_folder := $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)/$(arch_dir)\n    my_module_path := $(testcase_folder)\n    arch_dir :=\n  endif\n\n  ifeq ($(strip $(my_module_path)),)\n    $(error $(LOCAL_PATH): unhandled install path \"$(install_path_var) for $(LOCAL_MODULE)\")\n  endif\nendif\nifneq ($(my_module_relative_path),)\n  my_module_path := $(my_module_path)/$(my_module_relative_path)\nendif\nendif # not LOCAL_UNINSTALLABLE_MODULE\n\nifneq ($(strip $(LOCAL_BUILT_MODULE)$(LOCAL_INSTALLED_MODULE)),)\n  $(error $(LOCAL_PATH): LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE must not be defined by component makefiles)\nendif\n\nmy_register_name := $(LOCAL_MODULE)\nifeq ($(my_host_cross),true)\n  my_register_name := host_cross_$(LOCAL_MODULE)\nendif\nifdef LOCAL_2ND_ARCH_VAR_PREFIX\nifndef LOCAL_NO_2ND_ARCH_MODULE_SUFFIX\nmy_register_name := $(my_register_name)$($(my_prefix)2ND_ARCH_MODULE_SUFFIX)\nendif\nendif\n\nifeq ($(my_host_cross),true)\n  my_all_targets := host_cross_$(my_register_name)_all_targets\nelse ifneq ($(LOCAL_IS_HOST_MODULE),)\n  my_all_targets := host_$(my_register_name)_all_targets\nelse\n  my_all_targets := device_$(my_register_name)_all_targets\nendif\n\n# Make sure that this IS_HOST/CLASS/MODULE combination is unique.\nmodule_id := MODULE.$(if \\\n    $(LOCAL_IS_HOST_MODULE),$($(my_prefix)OS),TARGET).$(LOCAL_MODULE_CLASS).$(my_register_name)\nifdef $(module_id)\n$(error $(LOCAL_PATH): $(module_id) already defined by $($(module_id)))\nendif\n$(module_id) := $(LOCAL_PATH)\n\n# These are the same as local-intermediates-dir / local-generated-sources dir, but faster\nintermediates.COMMON := $($(my_prefix)OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates\nifneq (,$(filter $(my_prefix)$(LOCAL_MODULE_CLASS),$(COMMON_MODULE_CLASSES)))\n  intermediates := $($(my_prefix)OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates\n  generated_sources_dir := $($(my_prefix)OUT_COMMON_GEN)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates\nelse\n  ifneq (,$(filter $(LOCAL_MODULE_CLASS),$(PER_ARCH_MODULE_CLASSES)))\n    intermediates := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates\n  else\n    intermediates := $($(my_prefix)OUT_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates\n  endif\n  generated_sources_dir := $($(my_prefix)OUT_GEN)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates\nendif\n\nifneq ($(LOCAL_OVERRIDES_MODULES),)\n  ifndef LOCAL_IS_HOST_MODULE\n    ifeq ($(LOCAL_MODULE_CLASS),EXECUTABLES)\n      EXECUTABLES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_MODULES))\n    else ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)\n      SHARED_LIBRARIES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_MODULES))\n    else ifeq ($(LOCAL_MODULE_CLASS),ETC)\n      ETC.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_MODULES))\n    else\n      $(call pretty-error,LOCAL_MODULE_CLASS := $(LOCAL_MODULE_CLASS) cannot use LOCAL_OVERRIDES_MODULES)\n    endif\n  else\n    $(call pretty-error,host modules cannot use LOCAL_OVERRIDES_MODULES)\n  endif\nendif\n\n###########################################################\n# Pick a name for the intermediate and final targets\n###########################################################\ninclude $(BUILD_SYSTEM)/configure_module_stem.mk\n\nLOCAL_BUILT_MODULE := $(intermediates)/$(my_built_module_stem)\n\nifneq (,$(LOCAL_SOONG_INSTALLED_MODULE))\n  ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n    $(call pretty-error, LOCAL_MODULE_MAKEFILE can only be used from $(SOONG_ANDROID_MK))\n  endif\n  # Use the install path requested by Soong.\n  LOCAL_INSTALLED_MODULE := $(LOCAL_SOONG_INSTALLED_MODULE)\nelse ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))\n  # Apk and its attachments reside in its own subdir.\n  ifeq ($(LOCAL_MODULE_CLASS),APPS)\n    # framework-res.apk doesn't like the additional layer.\n    ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true)\n      # Neither do Runtime Resource Overlay apks, which contain just the overlaid resources.\n    else ifeq ($(LOCAL_IS_RUNTIME_RESOURCE_OVERLAY),true)\n    else\n      ifneq ($(use_testcase_folder),true)\n        my_module_path := $(my_module_path)/$(LOCAL_MODULE)\n      endif\n    endif\n  endif\n  LOCAL_INSTALLED_MODULE := $(my_module_path)/$(my_installed_module_stem)\nendif\n\n# Assemble the list of targets to create PRIVATE_ variables for.\nLOCAL_INTERMEDIATE_TARGETS += $(LOCAL_BUILT_MODULE)\n\n###########################################################\n## Create .toc files from shared objects to reduce unnecessary rebuild\n# .toc files have the list of external dynamic symbols without their addresses.\n# As .KATI_RESTAT is specified to .toc files and commit-change-for-toc is used,\n# dependent binaries of a .toc file will be rebuilt only when the content of\n# the .toc file is changed.\n#\n# Don't create .toc files for Soong shared libraries, that is handled in\n# Soong and soong_cc_prebuilt.mk\n###########################################################\nifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\nifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)\nLOCAL_INTERMEDIATE_TARGETS += $(LOCAL_BUILT_MODULE).toc\n$(LOCAL_BUILT_MODULE).toc: $(LOCAL_BUILT_MODULE)\n\t$(call $(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)transform-shared-lib-to-toc,$<,$@.tmp)\n\t$(call commit-change-for-toc,$@)\n\n# Kati adds restat=1 to ninja. GNU make does nothing for this.\n.KATI_RESTAT: $(LOCAL_BUILT_MODULE).toc\n# Build .toc file when using mm, mma, or make $(my_register_name)\n$(my_all_targets): $(LOCAL_BUILT_MODULE).toc\nendif\nendif\n\n###########################################################\n## logtags: Add .logtags files to global list\n###########################################################\n\nlogtags_sources := $(filter %.logtags,$(LOCAL_SRC_FILES)) $(LOCAL_LOGTAGS_FILES)\n\nifneq ($(strip $(logtags_sources) $(LOCAL_SOONG_LOGTAGS_FILES)),)\nevent_log_tags := $(foreach f,$(LOCAL_SOONG_LOGTAGS_FILES) $(addprefix $(LOCAL_PATH)/,$(logtags_sources)),$(call clean-path,$(f)))\nelse\nevent_log_tags :=\nendif\n\n###########################################################\n## make clean- targets\n###########################################################\ncleantarget := clean-$(my_register_name)\n.PHONY: $(cleantarget)\n$(cleantarget) : PRIVATE_MODULE := $(my_register_name)\n$(cleantarget) : PRIVATE_CLEAN_FILES := \\\n    $(LOCAL_BUILT_MODULE) \\\n    $(LOCAL_INSTALLED_MODULE) \\\n    $(intermediates)\n$(cleantarget)::\n\t@echo \"Clean: $(PRIVATE_MODULE)\"\n\t$(hide) rm -rf $(PRIVATE_CLEAN_FILES)\n\n###########################################################\n## Common definitions for module.\n###########################################################\n$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PATH:=$(LOCAL_PATH)\n$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_IS_HOST_MODULE := $(LOCAL_IS_HOST_MODULE)\n$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_HOST:= $(my_host)\n$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PREFIX := $(my_prefix)\n$(LOCAL_INTERMEDIATE_TARGETS) : .KATI_TAGS += ;module_name=$(LOCAL_MODULE)\nifeq ($(LOCAL_MODULE_CLASS),)\n$(error \"$(LOCAL_MODULE) in $(LOCAL_PATH) does not set $(LOCAL_MODULE_CLASS)\")\nelse\n$(LOCAL_INTERMEDIATE_TARGETS) : .KATI_TAGS += ;module_type=$(LOCAL_MODULE_CLASS)\nendif\n\n$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_INTERMEDIATES_DIR:= $(intermediates)\n$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_2ND_ARCH_VAR_PREFIX := $(LOCAL_2ND_ARCH_VAR_PREFIX)\n\n# Tell the module and all of its sub-modules who it is.\n$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_MODULE:= $(my_register_name)\n\n# Provide a short-hand for building this module.\n# We name both BUILT and INSTALLED in case\n# LOCAL_UNINSTALLABLE_MODULE is set.\n.PHONY: $(my_all_targets)\n$(my_all_targets): $(LOCAL_BUILT_MODULE) $(LOCAL_INSTALLED_MODULE) $(LOCAL_ADDITIONAL_CHECKED_MODULE)\n\n.PHONY: $(my_register_name)\n$(my_register_name): $(my_all_targets)\n\nifneq ($(my_register_name),$(LOCAL_MODULE))\n# $(LOCAL_MODULE) covers all the multilib targets.\n.PHONY: $(LOCAL_MODULE)\n$(LOCAL_MODULE) : $(my_all_targets)\nendif\n\n# Set up phony targets that covers all modules under the given paths.\n# This allows us to build everything in given paths by running mmma/mma.\ndefine my_path_comp\nparent := $(patsubst %/,%,$(dir $(1)))\nparent_target := MODULES-IN-$$(subst /,-,$$(parent))\n.PHONY: $$(parent_target)\n$$(parent_target): $(2)\nifndef $$(parent_target)\n  $$(parent_target) := true\n  ifneq (,$$(findstring /,$$(parent)))\n    $$(eval $$(call my_path_comp,$$(parent),$$(parent_target)))\n  endif\nendif\nendef\n\n_local_path := $(patsubst %/,%,$(LOCAL_PATH))\n_local_path_target := MODULES-IN-$(subst /,-,$(_local_path))\n\n.PHONY: $(_local_path_target)\n$(_local_path_target): $(my_register_name)\n\nifndef $(_local_path_target)\n  $(_local_path_target) := true\n  ifneq (,$(findstring /,$(_local_path)))\n    $(eval $(call my_path_comp,$(_local_path),$(_local_path_target)))\n  endif\nendif\n\n_local_path :=\n_local_path_target :=\nmy_path_comp :=\n\n###########################################################\n## Module installation rule\n###########################################################\n\nmy_installed_symlinks :=\n\nifneq (,$(LOCAL_SOONG_INSTALLED_MODULE))\n  # Soong already generated the copy rule, but make the installed location depend on the Make\n  # copy of the intermediates for now, as some rules that collect intermediates may expect\n  # them to exist.\n  $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)\nelse ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))\n  $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)\n  $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)\n\t@echo \"Install: $@\"\n  ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n\t$(copy-file-or-link-to-new-target)\n  else\n\t$(copy-file-to-new-target)\n  endif\n\t$(PRIVATE_POST_INSTALL_CMD)\n\n  # Rule to install the module's companion symlinks\n  my_installed_symlinks := $(addprefix $(my_module_path)/,$(LOCAL_MODULE_SYMLINKS) $(LOCAL_MODULE_SYMLINKS_$(my_32_64_bit_suffix)))\n  $(foreach symlink,$(my_installed_symlinks),\\\n      $(call symlink-file,$(LOCAL_INSTALLED_MODULE),$(my_installed_module_stem),$(symlink))\\\n      $(call declare-0p-target,$(symlink)))\n\n  $(my_all_targets) : | $(my_installed_symlinks)\n\nendif # !LOCAL_UNINSTALLABLE_MODULE\n\n# Add dependencies on LOCAL_SOONG_INSTALL_SYMLINKS if we're installing any kind of module, not just\n# ones that set LOCAL_SOONG_INSTALLED_MODULE. This is so we can have a soong module that only\n# installs symlinks (e.g. install_symlink). We can't set LOCAL_SOONG_INSTALLED_MODULE to a symlink\n# because cp commands will fail on symlinks.\nifneq (,$(or $(LOCAL_SOONG_INSTALLED_MODULE),$(call boolean-not,$(LOCAL_UNINSTALLABLE_MODULE))))\n  $(foreach symlink, $(LOCAL_SOONG_INSTALL_SYMLINKS), $(call declare-0p-target,$(symlink)))\n  $(my_all_targets) : | $(LOCAL_SOONG_INSTALL_SYMLINKS)\nendif\n\n###########################################################\n## VINTF manifest fragment and init.rc goals\n###########################################################\n\nmy_vintf_installed:=\nmy_vintf_path:=\nmy_vintf_pairs:=\nmy_init_rc_installed :=\nmy_init_rc_path :=\nmy_init_rc_pairs :=\nifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))\n  ifndef LOCAL_IS_HOST_MODULE\n    # Rule to install the module's companion vintf fragments.\n    ifneq ($(strip $(LOCAL_FULL_VINTF_FRAGMENTS)),)\n      my_vintf_fragments := $(LOCAL_FULL_VINTF_FRAGMENTS)\n    else\n      my_vintf_fragments := $(foreach xml,$(LOCAL_VINTF_FRAGMENTS),$(LOCAL_PATH)/$(xml))\n    endif\n    ifneq ($(strip $(my_vintf_fragments)),)\n      # Make doesn't support recovery as an output partition, but some Soong modules installed in recovery\n      # have init.rc files that need to be installed alongside them. Manually handle the case where the\n      # output file is in the recovery partition.\n      my_vintf_path := $(if $(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path)),$(TARGET_RECOVERY_ROOT_OUT)/system/etc,$(TARGET_OUT$(partition_tag)_ETC))\n      my_vintf_pairs := $(foreach xml,$(my_vintf_fragments),$(xml):$(my_vintf_path)/vintf/manifest/$(notdir $(xml)))\n      my_vintf_installed := $(foreach xml,$(my_vintf_pairs),$(call word-colon,2,$(xml)))\n\n      # Only set up copy rules once, even if another arch variant shares it\n      my_vintf_new_pairs := $(filter-out $(ALL_VINTF_MANIFEST_FRAGMENTS_LIST),$(my_vintf_pairs))\n      ALL_VINTF_MANIFEST_FRAGMENTS_LIST += $(my_vintf_new_pairs)\n\n      ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n        $(call copy-many-vintf-manifest-files-checked,$(my_vintf_new_pairs))\n        $(my_all_targets) : $(my_vintf_installed)\n        # Install fragments together with the target\n        $(LOCAL_INSTALLED_MODULE) : | $(my_vintf_installed)\n     endif\n    endif # my_vintf_fragments\n\n    # Rule to install the module's companion init.rc.\n    ifneq ($(strip $(LOCAL_FULL_INIT_RC)),)\n      my_init_rc := $(LOCAL_FULL_INIT_RC)\n    else\n      my_init_rc := $(foreach rc,$(LOCAL_INIT_RC_$(my_32_64_bit_suffix)) $(LOCAL_INIT_RC),$(LOCAL_PATH)/$(rc))\n    endif\n    ifneq ($(strip $(my_init_rc)),)\n      # Make doesn't support recovery or ramdisk as an output partition,\n      # but some Soong modules installed in recovery or ramdisk\n      # have init.rc files that need to be installed alongside them.\n      # Manually handle the case where the\n      # output file is in the recovery or ramdisk partition.\n      ifneq (,$(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path)))\n        ifneq (,$(filter $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/%,$(my_module_path)))\n            my_init_rc_path := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/system/etc\n        else\n            my_init_rc_path := $(TARGET_RECOVERY_ROOT_OUT)/system/etc\n        endif\n      else ifneq (,$(filter $(TARGET_RAMDISK_OUT)/%,$(my_module_path)))\n        my_init_rc_path := $(TARGET_RAMDISK_OUT)/system/etc\n      else\n        my_init_rc_path := $(TARGET_OUT$(partition_tag)_ETC)\n      endif\n      my_init_rc_pairs := $(foreach rc,$(my_init_rc),$(rc):$(my_init_rc_path)/init/$(notdir $(rc)))\n      my_init_rc_installed := $(foreach rc,$(my_init_rc_pairs),$(call word-colon,2,$(rc)))\n\n      # Make sure we only set up the copy rules once, even if another arch variant\n      # shares a common LOCAL_INIT_RC.\n      my_init_rc_new_pairs := $(filter-out $(ALL_INIT_RC_INSTALLED_PAIRS),$(my_init_rc_pairs))\n      ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs)\n\n      ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n        $(call copy-many-init-script-files-checked,$(my_init_rc_new_pairs))\n        $(my_all_targets) : $(my_init_rc_installed)\n        # Install init_rc together with the target\n        $(LOCAL_INSTALLED_MODULE) : | $(my_init_rc_installed)\n      endif\n    endif # my_init_rc\n\n  endif # !LOCAL_IS_HOST_MODULE\nendif # !LOCAL_UNINSTALLABLE_MODULE\n\n###########################################################\n## CHECK_BUILD goals\n###########################################################\nmy_checked_module :=\n# If nobody has defined a more specific module for the\n# checked modules, use LOCAL_BUILT_MODULE.\nifdef LOCAL_CHECKED_MODULE\n  my_checked_module := $(LOCAL_CHECKED_MODULE)\nelse\n  my_checked_module := $(LOCAL_BUILT_MODULE)\nendif\n\nmy_checked_module += $(LOCAL_ADDITIONAL_CHECKED_MODULE)\n\n# If they request that this module not be checked, then don't.\n# PLEASE DON'T SET THIS.  ANY PLACES THAT SET THIS WITHOUT\n# GOOD REASON WILL HAVE IT REMOVED.\nifdef LOCAL_DONT_CHECK_MODULE\n  my_checked_module :=\nendif\n# Don't check build target module defined for the 2nd arch\nifndef LOCAL_IS_HOST_MODULE\nifdef LOCAL_2ND_ARCH_VAR_PREFIX\n  my_checked_module :=\nendif\nendif\n\n###########################################################\n## Test Data\n###########################################################\nmy_test_data_pairs :=\nmy_installed_test_data :=\n# Source to relative dst file paths for reuse in LOCAL_COMPATIBILITY_SUITE.\nmy_test_data_file_pairs :=\n\nifneq ($(strip $(filter NATIVE_TESTS,$(LOCAL_MODULE_CLASS)) $(LOCAL_IS_FUZZ_TARGET)),)\nifneq ($(strip $(LOCAL_TEST_DATA)),)\nifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))\n\n# Soong LOCAL_TEST_DATA is of the form <from_base>:<file>:<relative_install_path>\n# or <from_base>:<file>, to be installed to\n# <install_root>/<relative_install_path>/<file> or <install_root>/<file>,\n# respectively.\nifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n  define copy_test_data_pairs\n    _src_base := $$(call word-colon,1,$$(td))\n    _file := $$(call word-colon,2,$$(td))\n    _relative_install_path := $$(call word-colon,3,$$(td))\n    ifeq (,$$(_relative_install_path))\n        _relative_dest_file := $$(_file)\n    else\n        _relative_dest_file := $$(call append-path,$$(_relative_install_path),$$(_file))\n    endif\n    my_test_data_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(call append-path,$$(my_module_path),$$(_relative_dest_file))\n    my_test_data_file_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(_relative_dest_file)\n  endef\nelse\n  define copy_test_data_pairs\n    _src_base := $$(call word-colon,1,$$(td))\n    _file := $$(call word-colon,2,$$(td))\n    ifndef _file\n      _file := $$(_src_base)\n      _src_base := $$(LOCAL_PATH)\n    endif\n    ifneq (,$$(findstring ..,$$(_file)))\n      $$(call pretty-error,LOCAL_TEST_DATA may not include '..': $$(_file))\n    endif\n    ifneq (,$$(filter/%,$$(_src_base) $$(_file)))\n      $$(call pretty-error,LOCAL_TEST_DATA may not include absolute paths: $$(_src_base) $$(_file))\n    endif\n    my_test_data_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(call append-path,$$(my_module_path),$$(_file))\n    my_test_data_file_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(_file)\n  endef\nendif\n\n$(foreach td,$(LOCAL_TEST_DATA),$(eval $(copy_test_data_pairs)))\n\ncopy_test_data_pairs :=\n\nifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n  my_installed_test_data := $(call copy-many-files,$(my_test_data_pairs))\n  $(LOCAL_INSTALLED_MODULE): $(my_installed_test_data)\nelse\n  # Skip installing test data for Soong modules, it's already been handled.\n  # Just compute my_installed_test_data.\n  my_installed_test_data := $(foreach f, $(my_test_data_pairs), $(call word-colon,2,$(f)))\nendif\n\nendif\nendif\nendif\n\n###########################################################\n## SOONG INSTALL PAIRS\n###########################################################\n# Declare dependencies for LOCAL_SOONG_INSTALL_PAIRS in soong to the module it relies on.\nifneq (,$(LOCAL_SOONG_INSTALLED_MODULE))\n$(my_all_targets): \\\n    $(foreach f, $(LOCAL_SOONG_INSTALL_PAIRS),\\\n      $(word 2,$(subst :,$(space),$(f))))\nendif\n\n###########################################################\n## Compatibility suite files.\n###########################################################\nifdef LOCAL_COMPATIBILITY_SUITE\n\nifneq (,$(LOCAL_FULL_TEST_CONFIG))\n  test_config := $(LOCAL_FULL_TEST_CONFIG)\nelse ifneq (,$(LOCAL_TEST_CONFIG))\n  test_config := $(LOCAL_PATH)/$(LOCAL_TEST_CONFIG)\nelse\n  test_config := $(wildcard $(LOCAL_PATH)/AndroidTest.xml)\nendif\n\nifeq ($(EXCLUDE_MCTS),true)\nifeq (,$(filter $(LOCAL_MODULE),$(mcts_whitelist)))\n  ifneq (,$(test_config))\n    ifneq (,$(filter mcts-%,$(LOCAL_COMPATIBILITY_SUITE)))\n      LOCAL_COMPATIBILITY_SUITE := $(filter-out cts,$(LOCAL_COMPATIBILITY_SUITE))\n    endif\n  endif\nendif\nendif\n\nifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))\n\nifeq ($(EXCLUDE_MCTS),true)\n  ifneq (,$(test_config))\n    ifneq (,$(filter mcts-%,$(LOCAL_COMPATIBILITY_SUITE)))\n      LOCAL_COMPATIBILITY_SUITE := $(filter-out cts,$(LOCAL_COMPATIBILITY_SUITE))\n    endif\n  endif\nendif\n\n# If we are building a native test or benchmark and its stem variants are not defined,\n# separate the multiple architectures into subdirectories of the testcase folder.\narch_dir :=\nis_native :=\nmulti_arch :=\nifeq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS)\n  is_native := true\n  multi_arch := true\nendif\nifdef LOCAL_MULTILIB\n  multi_arch := true\n# These conditionals allow this functionality to be mimicked in Soong\nelse ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n  ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)\n    multi_arch := true\n  endif\nendif\n\nifdef multi_arch\narch_dir := /$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)\nelse\nifeq ($(use_testcase_folder),true)\n  arch_dir := /$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)\nendif\nendif\n\nmulti_arch :=\n\nmy_default_test_module :=\nmy_default_test_module := $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)$(arch_dir)/$(my_installed_module_stem)\nifneq ($(LOCAL_INSTALLED_MODULE),$(my_default_test_module))\n# Install into the testcase folder\n$(LOCAL_INSTALLED_MODULE) : $(my_default_test_module)\nendif\n\n# The module itself.\n$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n  $(eval my_compat_dist_$(suite) := $(patsubst %:$(LOCAL_INSTALLED_MODULE),$(LOCAL_INSTALLED_MODULE):$(LOCAL_INSTALLED_MODULE),\\\n    $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \\\n      $(LOCAL_BUILT_MODULE):$(dir)/$(my_installed_module_stem)))) \\\n  $(eval my_compat_module_arch_dir_$(suite).$(my_register_name) :=) \\\n  $(foreach dir,$(call compatibility_suite_dirs,$(suite),$(arch_dir)),$(eval my_compat_module_arch_dir_$(suite).$(my_register_name) += $(dir))) \\\n  $(eval my_compat_dist_config_$(suite) := ))\n\nifneq (,$(LOCAL_SOONG_CLASSES_JAR))\n    $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n      $(eval my_compat_api_map_$(suite) += $(LOCAL_SOONG_CLASSES_JAR)))\nendif\n\n# Auto-generate build config.\nifeq (,$(test_config))\n  ifneq (true,$(is_native))\n    is_instrumentation_test := true\n    ifeq (true, $(LOCAL_IS_HOST_MODULE))\n      is_instrumentation_test := false\n    endif\n    # If LOCAL_MODULE_CLASS is not APPS, it's certainly not an instrumentation\n    # test. However, some packages for test data also have LOCAL_MODULE_CLASS\n    # set to APPS. These will require flag LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG\n    # to disable auto-generating test config file.\n    ifneq (APPS, $(LOCAL_MODULE_CLASS))\n      is_instrumentation_test := false\n    endif\n  endif\n  # CTS modules can be used for test data, so test config files must be\n  # explicitly created using AndroidTest.xml\n  ifeq (,$(filter cts, $(LOCAL_COMPATIBILITY_SUITE)))\n    ifneq (true, $(LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG))\n      ifeq (true, $(filter true,$(is_native) $(is_instrumentation_test)))\n        include $(BUILD_SYSTEM)/autogen_test_config.mk\n        test_config := $(autogen_test_config_file)\n        autogen_test_config_file :=\n      endif\n    endif\n  endif\nendif\nis_instrumentation_test :=\n\n# Currently this flag variable is true only for the `android_test_helper_app` type module\n# which should not have any .config file\nifeq (true, $(LOCAL_DISABLE_TEST_CONFIG))\n  test_config :=\nendif\n\n# Make sure we only add the files once for multilib modules.\nifdef $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_compat_files\n  # Sync the auto_test_config value for multilib modules.\n  ifdef $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_autogen\n    ALL_MODULES.$(my_register_name).auto_test_config := true\n  endif\nelse\n  $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_compat_files := true\n  # LOCAL_COMPATIBILITY_SUPPORT_FILES is a list of <src>[:<dest>].\n  $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n    $(eval my_compat_dist_$(suite) += $(foreach f, $(LOCAL_COMPATIBILITY_SUPPORT_FILES), \\\n      $(eval p := $(subst :,$(space),$(f))) \\\n      $(eval s := $(word 1,$(p))) \\\n      $(eval n := $(or $(word 2,$(p)),$(notdir $(word 1, $(p))))) \\\n      $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \\\n        $(s):$(dir)/$(n)))))\n\n  $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n     $(eval my_compat_api_map_$(suite) += $(foreach f, $(LOCAL_COMPATIBILITY_SUPPORT_FILES), \\\n       $(eval p := $(subst :,$(space),$(f))) \\\n       $(eval s := $(word 1,$(p))) \\\n       $(if $(filter %.apk,$(s)) $(filter %.jar,$(s)),$(s),))))\n\n  ifneq (,$(test_config))\n    $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n      $(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \\\n        $(test_config):$(dir)/$(LOCAL_MODULE).config$(LOCAL_TEST_CONFIG_SUFFIX))))\n  endif\n\n  ifneq (,$(LOCAL_EXTRA_FULL_TEST_CONFIGS))\n    $(foreach test_config_file, $(LOCAL_EXTRA_FULL_TEST_CONFIGS), \\\n      $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n        $(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \\\n          $(test_config_file):$(dir)/$(basename $(notdir $(test_config_file))).config))))\n  endif\n\n  ifneq (,$(wildcard $(LOCAL_PATH)/DynamicConfig.xml))\n    $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n      $(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \\\n        $(LOCAL_PATH)/DynamicConfig.xml:$(dir)/$(LOCAL_MODULE).dynamic)))\n  endif\nendif # $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_compat_files\n\n\nifeq ($(use_testcase_folder),true)\nifneq ($(my_test_data_file_pairs),)\n# Filter out existng installed test data paths when collecting test data files to be installed and\n# indexed as they cause build rule conflicts. Instead put them in a separate list which is only\n# used for indexing.\n$(foreach pair, $(my_test_data_file_pairs), \\\n  $(eval parts := $(subst :,$(space),$(pair))) \\\n  $(eval src_path := $(word 1,$(parts))) \\\n  $(eval file := $(word 2,$(parts))) \\\n  $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n    $(eval my_compat_dist_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \\\n      $(call filter-copy-pair,$(src_path),$(call append-path,$(dir),$(file)),$(my_installed_test_data)))) \\\n    $(eval my_compat_dist_test_data_$(suite) += \\\n      $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \\\n        $(filter $(my_installed_test_data),$(call append-path,$(dir),$(file))))) \\\n    $(eval my_compat_api_map_$(suite) += \\\n      $(if $(filter %.apk,$(src_path)) $(filter %.jar,$(src_path)),$(src_path),))))\nendif\nelse\nifneq ($(my_test_data_file_pairs),)\n$(foreach pair, $(my_test_data_file_pairs), \\\n  $(eval parts := $(subst :,$(space),$(pair))) \\\n  $(eval src_path := $(word 1,$(parts))) \\\n  $(eval file := $(word 2,$(parts))) \\\n  $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n    $(eval my_compat_dist_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \\\n      $(src_path):$(call append-path,$(dir),$(file)))) \\\n    $(eval my_compat_api_map_$(suite) += \\\n      $(if $(filter %.apk,$(src_path)) $(filter %.jar,$(src_path)),$(src_path),))))\nendif\nendif\n\n\n\narch_dir :=\nis_native :=\n\n$(call create-suite-dependencies)\n$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n  $(eval my_compat_dist_config_$(suite) := ) \\\n  $(eval my_compat_dist_test_data_$(suite) := ) \\\n  $(eval my_compat_api_map_$(suite) := ))\n\nendif  # LOCAL_UNINSTALLABLE_MODULE\n\n# HACK: pretend a soong LOCAL_FULL_TEST_CONFIG is autogenerated by setting the flag in\n# module-info.json\n# TODO: (b/113029686) Add explicit flag from Soong to determine if a test was\n# autogenerated.\nifneq (,$(filter $(SOONG_OUT_DIR)%,$(LOCAL_FULL_TEST_CONFIG)))\n  ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n    ALL_MODULES.$(my_register_name).auto_test_config := true\n  endif\nendif\n\nendif  # LOCAL_COMPATIBILITY_SUITE\n\nmy_supported_variant :=\nifeq ($(my_host_cross),true)\n  my_supported_variant := HOST_CROSS\nelse\n  ifdef LOCAL_IS_HOST_MODULE\n    my_supported_variant := HOST\n  else\n    my_supported_variant := DEVICE\n  endif\nendif\n\n###########################################################\n## Register with ALL_MODULES\n###########################################################\n\nifndef ALL_MODULES.$(my_register_name).PATH\n    # These keys are no longer used, they've been replaced by keys that specify\n    # target/host/host_cross (REQUIRED_FROM_TARGET / REQUIRED_FROM_HOST) and similar.\n    #\n    # Marking them obsolete to ensure that anyone using these internal variables looks for\n    # alternates.\n    $(KATI_obsolete_var ALL_MODULES.$(my_register_name).REQUIRED)\n    $(KATI_obsolete_var ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED)\n    $(KATI_obsolete_var ALL_MODULES.$(my_register_name).HOST_REQUIRED)\n    $(KATI_obsolete_var ALL_MODULES.$(my_register_name).TARGET_REQUIRED)\nendif\n\nALL_MODULES += $(my_register_name)\n\n# Don't use += on subvars, or else they'll end up being\n# recursively expanded.\nALL_MODULES.$(my_register_name).CLASS := \\\n    $(ALL_MODULES.$(my_register_name).CLASS) $(LOCAL_MODULE_CLASS)\nALL_MODULES.$(my_register_name).PATH := \\\n    $(ALL_MODULES.$(my_register_name).PATH) $(LOCAL_PATH)\nALL_MODULES.$(my_register_name).TAGS := \\\n    $(ALL_MODULES.$(my_register_name).TAGS) $(LOCAL_MODULE_TAGS)\nALL_MODULES.$(my_register_name).CHECKED := \\\n    $(ALL_MODULES.$(my_register_name).CHECKED) $(my_checked_module)\nALL_MODULES.$(my_register_name).BUILT := \\\n    $(ALL_MODULES.$(my_register_name).BUILT) $(LOCAL_BUILT_MODULE)\nALL_MODULES.$(my_register_name).SOONG_MODULE_TYPE := \\\n    $(ALL_MODULES.$(my_register_name).SOONG_MODULE_TYPE) $(LOCAL_SOONG_MODULE_TYPE)\nALL_MODULES.$(my_register_name).IS_SOONG_MODULE := \\\n    $(if $(filter $(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)),true)\nifndef LOCAL_IS_HOST_MODULE\nALL_MODULES.$(my_register_name).TARGET_BUILT := \\\n    $(ALL_MODULES.$(my_register_name).TARGET_BUILT) $(LOCAL_BUILT_MODULE)\nendif\nifneq (,$(LOCAL_SOONG_INSTALLED_MODULE))\n  # Store the list of paths to installed locations of files provided by this\n  # module.  Used as dependencies of the image packaging rules when the module\n  # is installed by the current product.\n  ALL_MODULES.$(my_register_name).INSTALLED := \\\n    $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \\\n      $(foreach f, $(LOCAL_SOONG_INSTALL_PAIRS),\\\n        $(word 2,$(subst :,$(space),$(f)))) \\\n      $(LOCAL_SOONG_INSTALL_SYMLINKS) \\\n      $(my_init_rc_installed) \\\n      $(my_installed_test_data) \\\n      $(my_vintf_installed))\n\n  ALL_MODULES.$(my_register_name).INSTALLED_SYMLINKS := $(LOCAL_SOONG_INSTALL_SYMLINKS)\n\n  # Store the list of colon-separated pairs of the built and installed locations\n  # of files provided by this module.  Used by custom packaging rules like\n  # package-modules.mk that need to copy the built files to a custom install\n  # location during packaging.\n  #\n  # Translate copies from $(LOCAL_PREBUILT_MODULE_FILE) to $(LOCAL_BUILT_MODULE)\n  # so that package-modules.mk gets any transtive dependencies added to\n  # $(LOCAL_BUILT_MODULE), for example unstripped symbols files.\n  ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \\\n    $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \\\n      $(patsubst $(LOCAL_PREBUILT_MODULE_FILE):%,$(LOCAL_BUILT_MODULE):%,$(LOCAL_SOONG_INSTALL_PAIRS)) \\\n      $(my_init_rc_pairs) \\\n      $(my_test_data_pairs) \\\n      $(my_vintf_pairs))\n  # Store the list of vintf/init_rc as order-only dependencies\n  ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED := \\\n    $(strip $(ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED) \\\n      $(my_init_rc_installed) \\\n      $(my_vintf_installed))\nelse ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))\n  ALL_MODULES.$(my_register_name).INSTALLED := \\\n    $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \\\n    $(LOCAL_INSTALLED_MODULE) $(my_init_rc_installed) $(my_installed_symlinks) \\\n    $(my_installed_test_data) $(my_vintf_installed))\n  ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \\\n    $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \\\n    $(LOCAL_BUILT_MODULE):$(LOCAL_INSTALLED_MODULE) \\\n    $(my_init_rc_pairs) $(my_test_data_pairs) $(my_vintf_pairs))\n  ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED := \\\n    $(strip $(ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED) \\\n      $(my_init_rc_installed) \\\n      $(my_vintf_installed))\nendif\n\n# Mark LOCAL_SOONG_INSTALL_SYMLINKS as installed if we're installing any kind of module, not just\n# ones that set LOCAL_SOONG_INSTALLED_MODULE. This is so we can have a soong module that only\n# installs symlinks (e.g. installed_symlink). We can't set LOCAL_SOONG_INSTALLED_MODULE to a symlink\n# because cp commands will fail on symlinks.\nifneq (,$(or $(LOCAL_SOONG_INSTALLED_MODULE),$(call boolean-not,$(LOCAL_UNINSTALLABLE_MODULE))))\n  ALL_MODULES.$(my_register_name).INSTALLED += $(LOCAL_SOONG_INSTALL_SYMLINKS)\n  ALL_MODULES.$(my_register_name).INSTALLED_SYMLINKS := $(LOCAL_SOONG_INSTALL_SYMLINKS)\nendif\n\nifdef LOCAL_PICKUP_FILES\n# Files or directories ready to pick up by the build system\n# when $(LOCAL_BUILT_MODULE) is done.\nALL_MODULES.$(my_register_name).PICKUP_FILES := \\\n    $(ALL_MODULES.$(my_register_name).PICKUP_FILES) $(LOCAL_PICKUP_FILES)\nendif\n# Record the platform availability of this module. Note that the availability is not\n# meaningful for non-installable modules (e.g., static libs) or host modules.\n# We only care about modules that are installable to the device.\nifeq (true,$(LOCAL_NOT_AVAILABLE_FOR_PLATFORM))\n  ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))\n    ifndef LOCAL_IS_HOST_MODULE\n      ALL_MODULES.$(my_register_name).NOT_AVAILABLE_FOR_PLATFORM := true\n    endif\n  endif\nendif\n\nmy_required_modules := $(LOCAL_REQUIRED_MODULES) \\\n    $(LOCAL_REQUIRED_MODULES_$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH))\nifdef LOCAL_IS_HOST_MODULE\nmy_required_modules += $(LOCAL_REQUIRED_MODULES_$($(my_prefix)OS))\nendif\n\nifdef LOCAL_ACONFIG_FILES\n  ALL_MODULES.$(my_register_name).ACONFIG_FILES := \\\n      $(ALL_MODULES.$(my_register_name).ACONFIG_FILES) $(LOCAL_ACONFIG_FILES)\nendif\n\nifdef LOCAL_FILESYSTEM_FILELIST\n  ALL_MODULES.$(my_register_name).FILESYSTEM_FILELIST := \\\n      $(ALL_MODULES.$(my_register_name).FILESYSTEM_FILELIST) $(LOCAL_FILESYSTEM_FILELIST)\nendif\n\nifndef LOCAL_SOONG_MODULE_INFO_JSON\n  ALL_MAKE_MODULE_INFO_JSON_MODULES += $(my_register_name)\n  ALL_MODULES.$(my_register_name).SHARED_LIBS := \\\n      $(ALL_MODULES.$(my_register_name).SHARED_LIBS) $(LOCAL_SHARED_LIBRARIES)\n\n  ALL_MODULES.$(my_register_name).STATIC_LIBS := \\\n      $(ALL_MODULES.$(my_register_name).STATIC_LIBS) $(LOCAL_STATIC_LIBRARIES)\n\n  ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS := \\\n      $(ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS) $(LOCAL_SYSTEM_SHARED_LIBRARIES)\n\n  ALL_MODULES.$(my_register_name).LOCAL_RUNTIME_LIBRARIES := \\\n      $(ALL_MODULES.$(my_register_name).LOCAL_RUNTIME_LIBRARIES) $(LOCAL_RUNTIME_LIBRARIES) \\\n      $(LOCAL_JAVA_LIBRARIES)\n\n  ALL_MODULES.$(my_register_name).LOCAL_STATIC_LIBRARIES := \\\n      $(ALL_MODULES.$(my_register_name).LOCAL_STATIC_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES)\n\n  ifneq ($(my_test_data_file_pairs),)\n    # Export the list of targets that are handled as data inputs and required\n    # by tests at runtime. The format of my_test_data_file_pairs is\n    # is $(path):$(relative_file) but for module-info, only the string after\n    # \":\" is needed.\n    ALL_MODULES.$(my_register_name).TEST_DATA := \\\n      $(strip $(ALL_MODULES.$(my_register_name).TEST_DATA) \\\n        $(foreach f, $(my_test_data_file_pairs),\\\n          $(call word-colon,2,$(f))))\n  endif\n\n  ifdef LOCAL_TEST_DATA_BINS\n    ALL_MODULES.$(my_register_name).TEST_DATA_BINS := \\\n        $(ALL_MODULES.$(my_register_name).TEST_DATA_BINS) $(LOCAL_TEST_DATA_BINS)\n  endif\n\n  ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS := \\\n      $(ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS) \\\n      $(filter-out $(ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS),$(my_supported_variant))\n\n  ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES := \\\n      $(ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES) $(LOCAL_COMPATIBILITY_SUITE)\n  ALL_MODULES.$(my_register_name).MODULE_NAME := $(LOCAL_MODULE)\n  ALL_MODULES.$(my_register_name).TEST_CONFIG := $(test_config)\n  ALL_MODULES.$(my_register_name).EXTRA_TEST_CONFIGS := $(LOCAL_EXTRA_FULL_TEST_CONFIGS)\n  ALL_MODULES.$(my_register_name).TEST_MAINLINE_MODULES := $(LOCAL_TEST_MAINLINE_MODULES)\n  ifdef LOCAL_IS_UNIT_TEST\n    ALL_MODULES.$(my_register_name).IS_UNIT_TEST := $(LOCAL_IS_UNIT_TEST)\n  endif\n  ifdef LOCAL_TEST_OPTIONS_TAGS\n    ALL_MODULES.$(my_register_name).TEST_OPTIONS_TAGS := $(LOCAL_TEST_OPTIONS_TAGS)\n  endif\n\n  ##########################################################\n  # Track module-level dependencies.\n  # (b/204397180) Unlock RECORD_ALL_DEPS was acknowledged reasonable for better Atest performance.\n  ALL_MODULES.$(my_register_name).ALL_DEPS := \\\n    $(ALL_MODULES.$(my_register_name).ALL_DEPS) \\\n    $(LOCAL_STATIC_LIBRARIES) \\\n    $(LOCAL_WHOLE_STATIC_LIBRARIES) \\\n    $(LOCAL_SHARED_LIBRARIES) \\\n    $(LOCAL_DYLIB_LIBRARIES) \\\n    $(LOCAL_RLIB_LIBRARIES) \\\n    $(LOCAL_PROC_MACRO_LIBRARIES) \\\n    $(LOCAL_HEADER_LIBRARIES) \\\n    $(LOCAL_STATIC_JAVA_LIBRARIES) \\\n    $(LOCAL_JAVA_LIBRARIES) \\\n    $(LOCAL_JNI_SHARED_LIBRARIES)\n\nendif\nALL_MODULES.$(my_register_name).TEST_MODULE_CONFIG_BASE := $(LOCAL_TEST_MODULE_CONFIG_BASE)\n\n##########################################################################\n## When compiling against API imported module, use API import stub\n## libraries.\n##########################################################################\nifneq ($(call module-in-vendor-or-product),)\n  ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n    apiimport_postfix := .apiimport\n    ifeq ($(LOCAL_IN_PRODUCT),true)\n      apiimport_postfix := .apiimport.product\n    else\n      apiimport_postfix := .apiimport.vendor\n    endif\n\n    my_required_modules := $(foreach l,$(my_required_modules), \\\n      $(if $(filter $(l), $(API_IMPORTED_SHARED_LIBRARIES)), $(l)$(apiimport_postfix), $(l)))\n  endif\nendif\n\n##########################################################################\n## When compiling against the VNDK, add the .vendor or .product suffix to\n## required modules.\n##########################################################################\nifneq ($(call module-in-vendor-or-product),)\n  #####################################################\n  ## Soong modules may be built three times, once for\n  ## /system, once for /vendor and once for /product.\n  ## If we're using the VNDK, switch all soong\n  ## libraries over to the /vendor or /product variant.\n  #####################################################\n  ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n    # We don't do this renaming for soong-defined modules since they already\n    # have correct names (with .vendor or .product suffix when necessary) in\n    # their LOCAL_*_LIBRARIES.\n    ifeq ($(LOCAL_IN_PRODUCT),true)\n      my_required_modules := $(foreach l,$(my_required_modules),\\\n        $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l)))\n    else\n      my_required_modules := $(foreach l,$(my_required_modules),\\\n        $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l)))\n    endif\n  endif\nendif\n\nifdef LOCAL_IS_HOST_MODULE\n    ifneq ($(my_host_cross),true)\n        ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST := \\\n            $(strip $(ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST) $(my_required_modules))\n        ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST := \\\n            $(strip $(ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST)\\\n                $(my_required_modules))\n        ALL_MODULES.$(my_register_name).TARGET_REQUIRED_FROM_HOST := \\\n            $(strip $(ALL_MODULES.$(my_register_name).TARGET_REQUIRED_FROM_HOST)\\\n                $(LOCAL_TARGET_REQUIRED_MODULES))\n    else\n        ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST_CROSS := \\\n            $(strip $(ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST_CROSS) $(my_required_modules))\n        ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST_CROSS := \\\n            $(strip $(ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST_CROSS)\\\n                $(my_required_modules))\n        ifdef LOCAL_TARGET_REQUIRED_MODULES\n            $(call pretty-error,LOCAL_TARGET_REQUIRED_MODULES may not be used from host_cross modules)\n        endif\n    endif\n    ifdef LOCAL_HOST_REQUIRED_MODULES\n        $(call pretty-error,LOCAL_HOST_REQUIRED_MODULES may not be used from host modules. Use LOCAL_REQUIRED_MODULES instead)\n    endif\nelse\n    ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET := \\\n        $(strip $(ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET) $(my_required_modules))\n    ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_TARGET := \\\n        $(strip $(ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_TARGET)\\\n            $(my_required_modules))\n    ALL_MODULES.$(my_register_name).HOST_REQUIRED_FROM_TARGET := \\\n        $(strip $(ALL_MODULES.$(my_register_name).HOST_REQUIRED_FROM_TARGET)\\\n            $(LOCAL_HOST_REQUIRED_MODULES))\n    ifdef LOCAL_TARGET_REQUIRED_MODULES\n        $(call pretty-error,LOCAL_TARGET_REQUIRED_MODULES may not be used from target modules. Use LOCAL_REQUIRED_MODULES instead)\n    endif\nendif\n\nifdef event_log_tags\n  ALL_MODULES.$(my_register_name).EVENT_LOG_TAGS := \\\n      $(ALL_MODULES.$(my_register_name).EVENT_LOG_TAGS) $(event_log_tags)\nendif\n\nALL_MODULES.$(my_register_name).MAKEFILE := \\\n    $(ALL_MODULES.$(my_register_name).MAKEFILE) $(LOCAL_MODULE_MAKEFILE)\n\nifdef LOCAL_MODULE_OWNER\n  ALL_MODULES.$(my_register_name).OWNER := \\\n      $(sort $(ALL_MODULES.$(my_register_name).OWNER) $(LOCAL_MODULE_OWNER))\nendif\n\nifdef LOCAL_2ND_ARCH_VAR_PREFIX\nALL_MODULES.$(my_register_name).FOR_2ND_ARCH := true\nendif\nALL_MODULES.$(my_register_name).FOR_HOST_CROSS := $(my_host_cross)\nifndef LOCAL_IS_HOST_MODULE\nALL_MODULES.$(my_register_name).APEX_KEYS_FILE := $(LOCAL_APEX_KEY_PATH)\nendif\ntest_config :=\n\nINSTALLABLE_FILES.$(LOCAL_INSTALLED_MODULE).MODULE := $(my_register_name)\n\n###########################################################\n## umbrella targets used to verify builds\n###########################################################\nj_or_n :=\nifneq (,$(filter EXECUTABLES SHARED_LIBRARIES STATIC_LIBRARIES HEADER_LIBRARIES NATIVE_TESTS RLIB_LIBRARIES DYLIB_LIBRARIES PROC_MACRO_LIBRARIES,$(LOCAL_MODULE_CLASS)))\nj_or_n := native\nelse\nifneq (,$(filter JAVA_LIBRARIES APPS,$(LOCAL_MODULE_CLASS)))\nj_or_n := java\nendif\nendif\nifdef LOCAL_IS_HOST_MODULE\nh_or_t := host\nifeq ($(my_host_cross),true)\nh_or_hc_or_t := host-cross\nelse\nh_or_hc_or_t := host\nendif\nelse\nh_or_hc_or_t := target\nh_or_t := target\nendif\n\n\nifdef j_or_n\n$(j_or_n) $(h_or_t) $(j_or_n)-$(h_or_hc_or_t) : $(my_checked_module)\nifneq (,$(filter $(LOCAL_MODULE_TAGS),tests))\n$(j_or_n)-$(h_or_t)-tests $(j_or_n)-tests $(h_or_t)-tests : $(my_checked_module)\nendif\n$(LOCAL_MODULE)-$(h_or_hc_or_t) : $(my_all_targets)\n.PHONY: $(LOCAL_MODULE)-$(h_or_hc_or_t)\nifeq ($(j_or_n),native)\n$(LOCAL_MODULE)-$(h_or_hc_or_t)$(my_32_64_bit_suffix) : $(my_all_targets)\n.PHONY: $(LOCAL_MODULE)-$(h_or_hc_or_t)$(my_32_64_bit_suffix)\nendif\nendif\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=base_rules))\n\n###########################################################\n# Ensure privileged applications always have LOCAL_PRIVILEGED_MODULE\n###########################################################\nifndef LOCAL_PRIVILEGED_MODULE\n  ifneq (,$(filter $(TARGET_OUT_APPS_PRIVILEGED)/% $(TARGET_OUT_VENDOR_APPS_PRIVILEGED)/%,$(my_module_path)))\n    LOCAL_PRIVILEGED_MODULE := true\n  endif\nendif\n\n###########################################################\n## NOTICE files\n###########################################################\n\ninclude $(BUILD_NOTICE_FILE)\n\n###########################################################\n## SBOM generation\n###########################################################\ninclude $(BUILD_SBOM_GEN)\n"
  },
  {
    "path": "core/binary.mk",
    "content": "###########################################################\n## Standard rules for building binary object files from\n## asm/c/cpp/yacc/lex/etc source files.\n##\n## The list of object files is exported in $(all_objects).\n###########################################################\n\n#######################################\ninclude $(BUILD_SYSTEM)/base_rules.mk\ninclude $(BUILD_SYSTEM)/use_lld_setup.mk\n#######################################\n\n##################################################\n# Compute the dependency of the shared libraries\n##################################################\n# On the target, we compile with -nostdlib, so we must add in the\n# default system shared libraries, unless they have requested not\n# to by supplying a LOCAL_SYSTEM_SHARED_LIBRARIES value.  One would\n# supply that, for example, when building libc itself.\nifdef LOCAL_IS_HOST_MODULE\n  ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none)\n    ifdef USE_HOST_MUSL\n      my_system_shared_libraries := libc_musl\n    else\n      my_system_shared_libraries :=\n    endif\n  else\n      my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES)\n  endif\nelse\n  ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none)\n      my_system_shared_libraries := libc libm libdl\n  else\n      my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES)\n      my_system_shared_libraries := $(patsubst libc,libc libdl,$(my_system_shared_libraries))\n  endif\nendif\n\n# Third party code has additional no-override flags.\nis_third_party :=\nifneq ($(filter external/% hardware/% vendor/%,$(LOCAL_PATH)),)\n  is_third_party := true\nendif\n\nmy_soong_problems :=\n\n# The following LOCAL_ variables will be modified in this file.\n# Because the same LOCAL_ variables may be used to define modules for both 1st arch and 2nd arch,\n# we can't modify them in place.\nmy_src_files := $(LOCAL_SRC_FILES)\nmy_src_files_exclude := $(LOCAL_SRC_FILES_EXCLUDE)\nmy_static_libraries := $(LOCAL_STATIC_LIBRARIES)\nmy_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES)\nmy_shared_libraries := $(filter-out $(my_system_shared_libraries),$(LOCAL_SHARED_LIBRARIES))\nmy_header_libraries := $(LOCAL_HEADER_LIBRARIES)\nmy_cflags := $(LOCAL_CFLAGS)\nmy_conlyflags := $(LOCAL_CONLYFLAGS)\nmy_cppflags := $(LOCAL_CPPFLAGS)\nmy_cflags_no_override := $(GLOBAL_CLANG_CFLAGS_NO_OVERRIDE)\nmy_cppflags_no_override := $(GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE)\nifeq ($(my_32_64_bit_suffix), 64)\n  my_cflags_no_override += $(GLOBAL_CLANG_CFLAGS_64_NO_OVERRIDE)\nendif\nifdef is_third_party\n    my_cflags_no_override += $(GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE)\n    my_cppflags_no_override += $(GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE)\nendif\nmy_ldflags := $(LOCAL_LDFLAGS)\nmy_ldlibs := $(LOCAL_LDLIBS)\nmy_asflags := $(LOCAL_ASFLAGS)\nmy_cc := $(LOCAL_CC)\nmy_cc_wrapper := $(CC_WRAPPER)\nmy_cxx := $(LOCAL_CXX)\nmy_cxx_link := $(LOCAL_CXX)\nmy_cxx_ldlibs :=\nmy_cxx_wrapper := $(CXX_WRAPPER)\nmy_c_includes := $(LOCAL_C_INCLUDES)\nmy_generated_sources := $(LOCAL_GENERATED_SOURCES)\nmy_additional_dependencies := $(LOCAL_ADDITIONAL_DEPENDENCIES)\nmy_export_c_include_dirs := $(LOCAL_EXPORT_C_INCLUDE_DIRS)\nmy_export_c_include_deps := $(LOCAL_EXPORT_C_INCLUDE_DEPS)\nmy_arflags :=\n\n# Disable clang-tidy if it is not found.\nifeq ($(PATH_TO_CLANG_TIDY),)\n  my_tidy_enabled := false\nelse\n  # If LOCAL_TIDY is not defined, use global WITH_TIDY\n  my_tidy_enabled := $(LOCAL_TIDY)\n  ifeq ($(my_tidy_enabled),)\n    my_tidy_enabled := $(WITH_TIDY)\n  endif\nendif\n\n# my_tidy_checks is empty if clang-tidy is disabled.\nmy_tidy_checks :=\nmy_tidy_flags :=\nifneq (,$(filter 1 true,$(my_tidy_enabled)))\n  # Set up global default checks\n  my_tidy_checks := $(WITH_TIDY_CHECKS)\n  ifeq ($(my_tidy_checks),)\n    my_tidy_checks := $(call default_global_tidy_checks,$(LOCAL_PATH))\n  endif\n  # Append local clang-tidy checks.\n  ifneq ($(LOCAL_TIDY_CHECKS),)\n    my_tidy_checks := $(my_tidy_checks),$(LOCAL_TIDY_CHECKS)\n  endif\n  my_tidy_flags := $(strip $(WITH_TIDY_FLAGS) $(LOCAL_TIDY_FLAGS))\n  # If tidy flags are not specified, default to check all header files.\n  ifeq ($(my_tidy_flags),)\n    my_tidy_flags := $(call default_tidy_header_filter,$(LOCAL_PATH))\n  endif\n  # If clang-tidy is not enabled globally, add the -quiet flag.\n  ifeq (,$(filter 1 true,$(WITH_TIDY)))\n    my_tidy_flags += -quiet -extra-arg-before=-fno-caret-diagnostics\n  endif\n\n  ifneq ($(my_tidy_checks),)\n    # We might be using the static analyzer through clang-tidy.\n    # https://bugs.llvm.org/show_bug.cgi?id=32914\n    my_tidy_flags += -extra-arg-before=-D__clang_analyzer__\n\n    # A recent change in clang-tidy (r328258) enabled destructor inlining,\n    # which appears to cause a number of false positives. Until that's\n    # resolved, this turns off the effects of r328258.\n    # https://bugs.llvm.org/show_bug.cgi?id=37459\n    my_tidy_flags += -extra-arg-before=-Xclang\n    my_tidy_flags += -extra-arg-before=-analyzer-config\n    my_tidy_flags += -extra-arg-before=-Xclang\n    my_tidy_flags += -extra-arg-before=c++-temp-dtor-inlining=false\n  endif\nendif\n\nmy_tidy_checks := $(subst $(space),,$(my_tidy_checks))\n\n# Configure the pool to use for clang rules.\n# If LOCAL_CC or LOCAL_CXX is set don't use goma or RBE.\n# If clang-tidy is being used, don't use the RBE pool (as clang-tidy runs in\n# the same action, and is not remoted)\nmy_pool :=\nifeq (,$(strip $(my_cc))$(strip $(my_cxx))$(strip $(my_tidy_checks)))\n  my_pool := $(GOMA_OR_RBE_POOL)\nendif\n\nifneq (,$(strip $(foreach dir,$(NATIVE_COVERAGE_PATHS),$(filter $(dir)%,$(LOCAL_PATH)))))\nifeq (,$(strip $(foreach dir,$(NATIVE_COVERAGE_EXCLUDE_PATHS),$(filter $(dir)%,$(LOCAL_PATH)))))\n  my_native_coverage := true\n  my_clang_coverage := true\nelse\n  my_native_coverage := false\n  my_clang_coverage := false\nendif\nelse\n  my_native_coverage := false\n  my_clang_coverage := false\nendif\nifneq ($(NATIVE_COVERAGE),true)\n  my_native_coverage := false\nendif\nifneq ($(CLANG_COVERAGE),true)\n  my_clang_coverage := false\nendif\n\n# Exclude directories from checking allowed manual binder interface lists.\n# TODO(b/145621474): Move this check into IInterface.h when clang-tidy no longer uses absolute paths.\nifneq (,$(filter $(addsuffix %,$(ALLOWED_MANUAL_INTERFACE_PATHS)),$(LOCAL_PATH)))\n  my_cflags += -DDO_NOT_CHECK_MANUAL_BINDER_INTERFACES\nendif\n\nmy_allow_undefined_symbols := $(strip $(LOCAL_ALLOW_UNDEFINED_SYMBOLS))\nifdef SANITIZE_HOST\nifdef LOCAL_IS_HOST_MODULE\nmy_allow_undefined_symbols := true\nendif\nendif\n\nmy_ndk_sysroot :=\nmy_ndk_sysroot_lib :=\nmy_api_level := 10000\n\nmy_arch := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)\n\nifneq ($(LOCAL_SDK_VERSION),)\n  ifdef LOCAL_IS_HOST_MODULE\n    $(error $(LOCAL_PATH): LOCAL_SDK_VERSION cannot be used in host module)\n  endif\n\n  # Make sure we've built the NDK.\n  my_additional_dependencies += $(SOONG_OUT_DIR)/ndk_base.timestamp\n\n  my_min_sdk_version := $(MIN_SUPPORTED_SDK_VERSION)\n\n  # Historically we've just set up a bunch of symlinks in prebuilts/ndk to map\n  # missing API levels to existing ones where necessary, but we're not doing\n  # that for the generated libraries. Clip the API level to the minimum where\n  # appropriate.\n  my_ndk_api := $(LOCAL_SDK_VERSION)\n  ifneq ($(my_ndk_api),current)\n    my_ndk_api := $(call math_max,$(my_ndk_api),$(my_min_sdk_version))\n  endif\n\n  my_ndk_crt_version := $(my_ndk_api)\n\n  ifneq ($(my_ndk_api),current)\n    my_api_level := $(my_ndk_api)\n  endif\n\n  my_built_ndk := $(SOONG_OUT_DIR)/ndk\n  my_ndk_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_NDK_TRIPLE)\n  my_ndk_sysroot := $(my_built_ndk)/sysroot\n\n  my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/lib/$(my_ndk_triple)/$(my_ndk_api)\n\n  # The bionic linker now has support for packed relocations and gnu style\n  # hashes (which are much faster!), but shipping to older devices requires\n  # the old style hash. Fortunately, we can build with both and it'll work\n  # anywhere.\n  my_ldflags += -Wl,--hash-style=both\n\n  # We don't want to expose the relocation packer to the NDK just yet.\n  LOCAL_PACK_MODULE_RELOCATIONS := false\n\n  # Set up the NDK stl variant. Starting from NDK-r5 the c++ stl resides in a separate location.\n  # See ndk/docs/CPLUSPLUS-SUPPORT.html\n  my_ndk_stl_include_path :=\n  my_ndk_stl_shared_lib_fullpath :=\n  my_ndk_stl_static_lib :=\n  my_cpu_variant := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)CPU_ABI)\n  LOCAL_NDK_STL_VARIANT := $(strip $(LOCAL_NDK_STL_VARIANT))\n  ifeq (,$(LOCAL_NDK_STL_VARIANT))\n    LOCAL_NDK_STL_VARIANT := system\n  endif\n  ifneq (1,$(words $(filter none system c++_static c++_shared, $(LOCAL_NDK_STL_VARIANT))))\n    $(error $(LOCAL_PATH): Unknown LOCAL_NDK_STL_VARIANT $(LOCAL_NDK_STL_VARIANT))\n  endif\n\n  ifeq (system,$(LOCAL_NDK_STL_VARIANT))\n    my_ndk_source_root := \\\n        $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources\n    my_ndk_stl_include_path := $(my_ndk_source_root)/cxx-stl/system/include\n    my_system_shared_libraries += libstdc++\n  else ifneq (,$(filter c++_%, $(LOCAL_NDK_STL_VARIANT)))\n    my_llvm_dir := $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)\n    my_libcxx_arch_dir := $(my_llvm_dir)/android_libc++/ndk/$($(LOCAL_2ND_ARCH_VAR_PREFIX)PREBUILT_LIBCXX_ARCH_DIR)\n\n    # Include the target-specific __config_site file followed by the generic libc++ headers.\n    my_ndk_stl_include_path := $(my_libcxx_arch_dir)/include/c++/v1\n    my_ndk_stl_include_path += $(my_llvm_dir)/include/c++/v1\n    my_libcxx_libdir := $(my_libcxx_arch_dir)/lib\n\n    ifeq (c++_static,$(LOCAL_NDK_STL_VARIANT))\n      my_ndk_stl_static_lib := \\\n        $(my_libcxx_libdir)/libc++_static.a \\\n        $(my_libcxx_libdir)/libc++abi.a\n    else\n      my_ndk_stl_shared_lib_fullpath := $(my_libcxx_libdir)/libc++_shared.so\n    endif\n\n    my_ndk_stl_static_lib += $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_LIBUNWIND)\n    my_ldlibs += -ldl\n  else # LOCAL_NDK_STL_VARIANT must be none\n    # Do nothing.\n  endif\n\n  # Clang's coverage/profile runtime needs symbols like 'stderr' that were not\n  # exported from libc prior to API level 23\n  ifneq ($(my_ndk_api),current)\n    ifeq ($(call math_lt, $(my_ndk_api),23),true)\n      my_native_coverage := false\n      my_clang_coverage := false\n    endif\n  endif\nendif\n\nifneq ($(LOCAL_MIN_SDK_VERSION),)\n  ifdef LOCAL_IS_HOST_MODULE\n    $(error $(LOCAL_PATH): LOCAL_MIN_SDK_VERSION cannot be used in host module)\n  endif\n  my_api_level := $(LOCAL_MIN_SDK_VERSION)\nendif\n\nifeq ($(NATIVE_COVERAGE),true)\n  ifndef LOCAL_IS_HOST_MODULE\n    my_ldflags += -Wl,--wrap,getenv\n\n    ifneq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES)\n      ifeq ($(LOCAL_SDK_VERSION),)\n        my_whole_static_libraries += libprofile-extras\n      else\n        my_whole_static_libraries += libprofile-extras_ndk\n      endif\n    endif\n  endif\nendif\n\nifeq ($(CLANG_COVERAGE),true)\n  ifndef LOCAL_IS_HOST_MODULE\n    my_ldflags += $(CLANG_COVERAGE_HOST_LDFLAGS)\n    ifneq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES)\n      my_whole_static_libraries += libclang_rt.profile\n      ifeq ($(LOCAL_SDK_VERSION),)\n        my_whole_static_libraries += libprofile-clang-extras\n      else\n        my_whole_static_libraries += libprofile-clang-extras_ndk\n      endif\n    endif\n  endif\n  ifeq ($(my_clang_coverage),true)\n    my_profile_instr_generate := $(CLANG_COVERAGE_INSTR_PROFILE)\n    ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true)\n      my_cflags += $(CLANG_COVERAGE_CONTINUOUS_FLAGS)\n      my_ldflags += $(CLANG_COVERAGE_CONTINUOUS_FLAGS)\n    endif\n\n    my_profile_instr_generate += $(CLANG_COVERAGE_CONFIG_COMMFLAGS)\n    my_cflags += $(CLANG_COVERAGE_INSTR_PROFILE) $(CLANG_COVERAGE_CONFIG_CFLAGS) $(CLANG_COVERAGE_CONFIG_COMMFLAGS)\n    my_ldflags += $(CLANG_COVERAGE_CONFIG_COMMFLAGS)\n\n    ifneq ($(filter hwaddress,$(my_sanitize)),)\n      my_cflags += $(CLANG_COVERAGE_HWASAN_FLAGS)\n      my_ldflags += $(CLANG_COVERAGE_HWASAN_FLAGS)\n    endif\n  endif\nendif\n\nifneq ($(call module-in-vendor-or-product),)\n  my_cflags += -D__ANDROID_VNDK__\n  ifneq ($(LOCAL_IN_VENDOR),)\n    # Vendor modules have LOCAL_IN_VENDOR\n    my_cflags += -D__ANDROID_VENDOR__\n  else ifneq ($(LOCAL_IN_PRODUCT),)\n    # Product modules have LOCAL_IN_PRODUCT\n    my_cflags += -D__ANDROID_PRODUCT__\n  endif\n\n  # Define __ANDROID_VENDOR_API__ for both product and vendor variants because\n  # they both use the same LLNDK libraries.\n  ifeq ($(BOARD_API_LEVEL),)\n    # TODO(b/314036847): This is a fallback for UDC targets.\n    # This must be a build failure when UDC is no longer built from this source tree.\n    my_cflags += -D__ANDROID_VENDOR_API__=$(PLATFORM_SDK_VERSION)\n  else\n    my_cflags += -D__ANDROID_VENDOR_API__=$(BOARD_API_LEVEL)\n  endif\nendif\n\nifndef LOCAL_IS_HOST_MODULE\n# For device libraries, move LOCAL_LDLIBS references to my_shared_libraries. We\n# no longer need to use my_ldlibs to pick up NDK prebuilt libraries since we're\n# linking my_shared_libraries by full path now.\nmy_allowed_ldlibs :=\n\n# Sort ldlibs and ldflags between -l and other linker flags\n# We'll do this again later, since there are still changes happening, but that's fine.\nmy_ldlib_flags := $(my_ldflags) $(my_ldlibs)\nmy_ldlibs := $(filter -l%,$(my_ldlib_flags))\nmy_ldflags := $(filter-out -l%,$(my_ldlib_flags))\nmy_ldlib_flags :=\n\n# Move other ldlibs back to shared libraries\nmy_shared_libraries += $(patsubst -l%,lib%,$(filter-out $(my_allowed_ldlibs),$(my_ldlibs)))\nmy_ldlibs := $(filter $(my_allowed_ldlibs),$(my_ldlibs))\nelse # LOCAL_IS_HOST_MODULE\n  # Add -ldl, -lpthread, -lm and -lrt to host builds to match the default behavior of\n  # device builds\n  ifndef USE_HOST_MUSL\n    my_ldlibs += -ldl -lpthread -lm\n    ifneq ($(HOST_OS),darwin)\n      my_ldlibs += -lrt\n    endif\n  endif\nendif\n\nifneq ($(LOCAL_SDK_VERSION),)\n  my_all_ndk_libraries := $(NDK_KNOWN_LIBS)\n  my_ndk_shared_libraries := \\\n      $(filter $(my_all_ndk_libraries),\\\n        $(my_shared_libraries) $(my_system_shared_libraries))\n\n  my_shared_libraries := \\\n      $(filter-out $(my_all_ndk_libraries),$(my_shared_libraries))\n  my_system_shared_libraries := \\\n      $(filter-out $(my_all_ndk_libraries),$(my_system_shared_libraries))\nendif\n\n# MinGW spits out warnings about -fPIC even for -fpie?!) being ignored because\n# all code is position independent, and then those warnings get promoted to\n# errors.\nifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)\n  my_cflags += -fPIE\n  ifndef BUILD_HOST_static\n    ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)\n      my_ldflags += -pie\n    endif\n  endif\nelse\n  my_cflags += -fPIC\nendif\n\nifdef LOCAL_IS_HOST_MODULE\nmy_src_files += $(LOCAL_SRC_FILES_$($(my_prefix)OS)) $(LOCAL_SRC_FILES_$($(my_prefix)OS)_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH))\nmy_static_libraries += $(LOCAL_STATIC_LIBRARIES_$($(my_prefix)OS))\nmy_shared_libraries += $(LOCAL_SHARED_LIBRARIES_$($(my_prefix)OS))\nmy_header_libraries += $(LOCAL_HEADER_LIBRARIES_$($(my_prefix)OS))\nmy_cflags += $(LOCAL_CFLAGS_$($(my_prefix)OS))\nmy_cppflags += $(LOCAL_CPPFLAGS_$($(my_prefix)OS))\nmy_ldflags += $(LOCAL_LDFLAGS_$($(my_prefix)OS))\nmy_ldlibs += $(LOCAL_LDLIBS_$($(my_prefix)OS))\nmy_asflags += $(LOCAL_ASFLAGS_$($(my_prefix)OS))\nmy_c_includes += $(LOCAL_C_INCLUDES_$($(my_prefix)OS))\nmy_generated_sources += $(LOCAL_GENERATED_SOURCES_$($(my_prefix)OS))\nendif\n\nmy_src_files += $(LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SRC_FILES_$(my_32_64_bit_suffix))\nmy_src_files_exclude += $(LOCAL_SRC_FILES_EXCLUDE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SRC_FILES_EXCLUDE_$(my_32_64_bit_suffix))\nmy_shared_libraries += $(LOCAL_SHARED_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SHARED_LIBRARIES_$(my_32_64_bit_suffix))\nmy_cflags += $(LOCAL_CFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CFLAGS_$(my_32_64_bit_suffix))\nmy_cppflags += $(LOCAL_CPPFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CPPFLAGS_$(my_32_64_bit_suffix))\nmy_ldflags += $(LOCAL_LDFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_LDFLAGS_$(my_32_64_bit_suffix))\nmy_asflags += $(LOCAL_ASFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_ASFLAGS_$(my_32_64_bit_suffix))\nmy_c_includes += $(LOCAL_C_INCLUDES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_C_INCLUDES_$(my_32_64_bit_suffix))\nmy_generated_sources += $(LOCAL_GENERATED_SOURCES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_GENERATED_SOURCES_$(my_32_64_bit_suffix))\n\nmy_missing_exclude_files := $(filter-out $(my_src_files),$(my_src_files_exclude))\nifneq ($(my_missing_exclude_files),)\n$(warning Files are listed in LOCAL_SRC_FILES_EXCLUDE but not LOCAL_SRC_FILES)\n$(error $(my_missing_exclude_files))\nendif\nmy_src_files := $(filter-out $(my_src_files_exclude),$(my_src_files))\n\n# Strip '/' from the beginning of each src file. This helps the ../ detection in case\n# the source file is in the form of /../file\nmy_src_files := $(patsubst /%,%,$(my_src_files))\n\nmy_clang := $(strip $(LOCAL_CLANG))\nifdef LOCAL_CLANG_$(my_32_64_bit_suffix)\nmy_clang := $(strip $(LOCAL_CLANG_$(my_32_64_bit_suffix)))\nendif\nifdef LOCAL_CLANG_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)\nmy_clang := $(strip $(LOCAL_CLANG_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)))\nendif\nifeq ($(my_clang),false)\n    $(call pretty-error,LOCAL_CLANG false is no longer supported)\nendif\n\nifeq ($(LOCAL_C_STD),)\n    my_c_std_version := $(DEFAULT_C_STD_VERSION)\nelse ifeq ($(LOCAL_C_STD),experimental)\n    my_c_std_version := $(EXPERIMENTAL_C_STD_VERSION)\nelse\n    my_c_std_version := $(LOCAL_C_STD)\nendif\n\nifeq ($(LOCAL_CPP_STD),)\n    my_cpp_std_version := $(DEFAULT_CPP_STD_VERSION)\nelse ifeq ($(LOCAL_CPP_STD),experimental)\n    my_cpp_std_version := $(EXPERIMENTAL_CPP_STD_VERSION)\nelse\n    my_cpp_std_version := $(LOCAL_CPP_STD)\nendif\n\nmy_c_std_conlyflags :=\nmy_cpp_std_cppflags :=\nifneq (,$(my_c_std_version))\n    my_c_std_conlyflags := -std=$(my_c_std_version)\nendif\n\nifneq (,$(my_cpp_std_version))\n   my_cpp_std_cppflags := -std=$(my_cpp_std_version)\nendif\n\n# Extra cflags for projects under external/ directory\nifneq ($(filter external/%,$(LOCAL_PATH)),)\n    my_cflags += $(CLANG_EXTERNAL_CFLAGS)\nendif\n\n# Extra cflags for projects under hardware/ directory.\n# This should match the definition of `thirdPartyDirPrefixExceptions`\n# in build/soong/android/paths.go.\n# Get the second element of LOCAL_PATH\nifneq ($(filter hardware/%,$(LOCAL_PATH)),)\n  my_subdir := $(word 2,$(subst /,$(space),$(LOCAL_PATH)))\n  must_compile_hardware_subdirs := \\\n                  hardware/google/% \\\n                  hardware/interfaces/% \\\n                  hardware/libhardware/% \\\n                  hardware/libhardware_legacy/% \\\n                  hardware/ril/%\n  ifeq ($(filter $(must_compile_hardware_subdirs),$(my_subdir)),)\n    my_cflags += $(CLANG_EXTERNAL_CFLAGS)\n  endif\nendif\n\n# Extra cflags for projects under vendor/ directory.\n# This should match the definition of `thirdPartyDirPrefixExceptions`\n# in build/soong/android/paths.go.\nifneq ($(filter vendor/%,$(LOCAL_PATH)),)\n  my_subdir := $(word 2,$(subst /,$(space),$(LOCAL_PATH)))\n  # Do not add the flags for any subdir that contains the string \"google\".\n  ifneq ($(findstring google,$(my_subdir)),)\n    my_cflags += $(CLANG_EXTERNAL_CFLAGS)\n  endif\nendif\n\n# arch-specific static libraries go first so that generic ones can depend on them\nmy_static_libraries := $(LOCAL_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_static_libraries)\nmy_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_WHOLE_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_whole_static_libraries)\nmy_header_libraries := $(LOCAL_HEADER_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_HEADER_LIBRARIES_$(my_32_64_bit_suffix)) $(my_header_libraries)\n\ninclude $(BUILD_SYSTEM)/cxx_stl_setup.mk\n\nifneq ($(strip $(CUSTOM_$(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)LINKER)),)\n  my_linker := $(CUSTOM_$(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)LINKER)\nelse\n  my_linker := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LINKER)\nendif\n\ninclude $(BUILD_SYSTEM)/config_sanitizers.mk\n\nifneq ($(filter ../%,$(my_src_files)),)\nmy_soong_problems += dotdot_srcs\nendif\nifneq ($(foreach i,$(my_c_includes),$(filter %/..,$(i))$(findstring /../,$(i))),)\nmy_soong_problems += dotdot_incs\nendif\n\n###########################################################\n## Explicitly declare assembly-only __ASSEMBLY__ macro for\n## assembly source\n###########################################################\nmy_asflags += -D__ASSEMBLY__\n\n###########################################################\n# TODO: support a mix of standard extensions so that this isn't necessary\nLOCAL_CPP_EXTENSION := $(strip $(LOCAL_CPP_EXTENSION))\nifeq ($(LOCAL_CPP_EXTENSION),)\n  LOCAL_CPP_EXTENSION := .cpp\nendif\n\n# Certain modules like libdl have to have symbols resolved at runtime and blow\n# up if --no-undefined is passed to the linker.\nifeq ($(strip $(LOCAL_NO_DEFAULT_COMPILER_FLAGS)),)\n  ifeq ($(my_allow_undefined_symbols),)\n    ifneq ($(HOST_OS),darwin)\n      my_ldflags += -Wl,--no-undefined\n    endif\n  else\n    ifdef LOCAL_IS_HOST_MODULE\n      ifeq ($(HOST_OS),darwin)\n        # darwin defaults to treating undefined symbols as errors\n        my_ldflags += -Wl,-undefined,dynamic_lookup\n      endif\n    endif\n  endif\nendif\n\nifeq (true,$(LOCAL_GROUP_STATIC_LIBRARIES))\n$(LOCAL_BUILT_MODULE): PRIVATE_GROUP_STATIC_LIBRARIES := true\nelse\n$(LOCAL_BUILT_MODULE): PRIVATE_GROUP_STATIC_LIBRARIES :=\nendif\n\n###########################################################\n## Define arm-vs-thumb-mode flags.\n###########################################################\nLOCAL_ARM_MODE := $(strip $(LOCAL_ARM_MODE))\nifeq ($($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),arm)\nnormal_objects_mode := $(if $(LOCAL_ARM_MODE),$(LOCAL_ARM_MODE),thumb)\n\n# Read the values from something like TARGET_arm_CFLAGS or\n# TARGET_thumb_CFLAGS.  HOST_(arm|thumb)_CFLAGS values aren't\n# actually used (although they are usually empty).\nnormal_objects_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)$(normal_objects_mode)_CFLAGS)\n\nelse\nnormal_objects_mode :=\nnormal_objects_cflags :=\nendif\n\n###########################################################\n## Define per-module debugging flags.  Users can turn on\n## debugging for a particular module by setting DEBUG_MODULE_ModuleName\n## to a non-empty value in their environment or buildspec.mk,\n## and setting HOST_/TARGET_CUSTOM_DEBUG_CFLAGS to the\n## debug flags that they want to use.\n###########################################################\nifdef DEBUG_MODULE_$(strip $(LOCAL_MODULE))\n  debug_cflags := $($(my_prefix)CUSTOM_DEBUG_CFLAGS)\nelse\n  debug_cflags :=\nendif\n\n####################################################\n## Keep track of src -> obj mapping\n####################################################\n\nmy_tracked_gen_files :=\nmy_tracked_src_files :=\n\n###########################################################\n## Stuff source generated from one-off tools\n###########################################################\n$(my_generated_sources): PRIVATE_MODULE := $(my_register_name)\n\nmy_gen_sources_copy := $(patsubst $(generated_sources_dir)/%,$(intermediates)/%,$(filter $(generated_sources_dir)/%,$(my_generated_sources)))\n\n$(my_gen_sources_copy): $(intermediates)/% : $(generated_sources_dir)/%\n\t@echo \"Copy: $@\"\n\t$(copy-file-to-target)\n\nmy_generated_sources := $(patsubst $(generated_sources_dir)/%,$(intermediates)/%,$(my_generated_sources))\n\n# Generated sources that will actually produce object files.\n# Other files (like headers) are allowed in LOCAL_GENERATED_SOURCES,\n# since other compiled sources may depend on them, and we set up\n# the dependencies.\nmy_gen_src_files := $(filter %.c %$(LOCAL_CPP_EXTENSION) %.S %.s,$(my_generated_sources))\n\n####################################################\n## Compile RenderScript with reflected C++\n####################################################\n\nrenderscript_sources := $(filter %.rscript %.fs,$(my_src_files))\n\nifneq (,$(renderscript_sources))\nmy_soong_problems += rs\n\nrenderscript_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(renderscript_sources))\nRenderScript_file_stamp := $(intermediates)/RenderScriptCPP.stamp\nrenderscript_intermediate := $(intermediates)/renderscript\n\nrenderscript_target_api :=\n\nifneq (,$(LOCAL_RENDERSCRIPT_TARGET_API))\nrenderscript_target_api := $(LOCAL_RENDERSCRIPT_TARGET_API)\nelse\nifneq (,$(LOCAL_SDK_VERSION))\n# Set target-api for LOCAL_SDK_VERSIONs other than current.\nifneq (,$(filter-out current system_current test_current, $(LOCAL_SDK_VERSION)))\nrenderscript_target_api := $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION))\nendif\nendif  # LOCAL_SDK_VERSION is set\nendif  # LOCAL_RENDERSCRIPT_TARGET_API is set\n\n\nifeq ($(LOCAL_RENDERSCRIPT_CC),)\nLOCAL_RENDERSCRIPT_CC := $(LLVM_RS_CC)\nendif\n\n# Turn on all warnings and warnings as errors for RS compiles.\n# This can be disabled with LOCAL_RENDERSCRIPT_FLAGS := -Wno-error\nrenderscript_flags := -Wall -Werror\nrenderscript_flags += $(LOCAL_RENDERSCRIPT_FLAGS)\n# -m32 or -m64\nrenderscript_flags += -m$(my_32_64_bit_suffix)\n\nrenderscript_includes := \\\n    $(TOPDIR)external/clang/lib/Headers \\\n    $(TOPDIR)frameworks/rs/script_api/include \\\n    $(LOCAL_RENDERSCRIPT_INCLUDES)\n\nifneq ($(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE),)\nrenderscript_includes := $(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE)\nendif\n\nbc_dep_files := $(addprefix $(renderscript_intermediate)/, \\\n    $(patsubst %.fs,%.d, $(patsubst %.rscript,%.d, $(notdir $(renderscript_sources)))))\n\n$(RenderScript_file_stamp): PRIVATE_RS_INCLUDES := $(renderscript_includes)\n$(RenderScript_file_stamp): PRIVATE_RS_CC := $(LOCAL_RENDERSCRIPT_CC)\n$(RenderScript_file_stamp): PRIVATE_RS_FLAGS := $(renderscript_flags)\n$(RenderScript_file_stamp): PRIVATE_RS_SOURCE_FILES := $(renderscript_sources_fullpath)\n$(RenderScript_file_stamp): PRIVATE_RS_OUTPUT_DIR := $(renderscript_intermediate)\n$(RenderScript_file_stamp): PRIVATE_RS_TARGET_API := $(patsubst current,0,$(renderscript_target_api))\n$(RenderScript_file_stamp): PRIVATE_DEP_FILES := $(bc_dep_files)\n$(RenderScript_file_stamp): $(renderscript_sources_fullpath) $(LOCAL_RENDERSCRIPT_CC)\n\t$(transform-renderscripts-to-cpp-and-bc)\n\n# include the dependency files (.d) generated by llvm-rs-cc.\n$(call include-depfile,$(RenderScript_file_stamp).d,$(RenderScript_file_stamp))\n\nLOCAL_INTERMEDIATE_TARGETS += $(RenderScript_file_stamp)\n\nrs_generated_cpps := $(addprefix \\\n    $(renderscript_intermediate)/ScriptC_,$(patsubst %.fs,%.cpp, $(patsubst %.rscript,%.cpp, \\\n    $(notdir $(renderscript_sources)))))\n\n$(call track-src-file-gen,$(renderscript_sources),$(rs_generated_cpps))\n\n# This is just a no-op rule to make sure gmake doesn't skip updating the dependents.\n$(rs_generated_cpps) : $(RenderScript_file_stamp)\n\t@echo \"Updated RS generated cpp file $@.\"\n\t$(hide) touch $@\n\nmy_c_includes += $(renderscript_intermediate)\nmy_generated_sources += $(rs_generated_cpps)\n\nendif\n\n\n###########################################################\n## Compile the .proto files to .cc (or .c) and then to .o\n###########################################################\nifeq ($(strip $(LOCAL_PROTOC_OPTIMIZE_TYPE)),)\n  LOCAL_PROTOC_OPTIMIZE_TYPE := lite\nendif\nproto_sources := $(filter %.proto,$(my_src_files))\nifneq ($(proto_sources),)\nproto_gen_dir := $(generated_sources_dir)/proto\nproto_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(proto_sources))\n\nmy_rename_cpp_ext :=\nifneq (,$(filter nanopb-c nanopb-c-enable_malloc nanopb-c-16bit nanopb-c-enable_malloc-16bit nanopb-c-32bit nanopb-c-enable_malloc-32bit, $(LOCAL_PROTOC_OPTIMIZE_TYPE)))\nmy_proto_source_suffix := .c\nmy_proto_c_includes := external/nanopb-c\nmy_protoc_flags := --nanopb_out=$(proto_gen_dir) \\\n    --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-nanopb\nmy_protoc_deps := $(NANOPB_SRCS) $(proto_sources_fullpath:%.proto=%.options)\nelse\nmy_proto_source_suffix := $(LOCAL_CPP_EXTENSION)\nifneq ($(my_proto_source_suffix),.cc)\n# aprotoc is hardcoded to write out only .cc file.\n# We need to rename the extension to $(LOCAL_CPP_EXTENSION) if it's not .cc.\nmy_rename_cpp_ext := true\nendif\nmy_proto_c_includes := external/protobuf/src\nmy_cflags += -DGOOGLE_PROTOBUF_NO_RTTI\nmy_protoc_flags := --cpp_out=$(if $(filter lite lite-static,$(LOCAL_PROTOC_OPTIMIZE_TYPE)),lite:,)$(proto_gen_dir)\nmy_protoc_deps :=\nendif\nmy_proto_c_includes += $(proto_gen_dir)\n\nproto_generated_cpps := $(addprefix $(proto_gen_dir)/, \\\n    $(patsubst %.proto,%.pb$(my_proto_source_suffix),$(proto_sources_fullpath)))\n\n# Ensure the transform-proto-to-cc rule is only defined once in multilib build.\nifndef $(my_host)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_proto_defined\n$(proto_generated_cpps): PRIVATE_PROTO_INCLUDES := $(TOP)\n$(proto_generated_cpps): PRIVATE_PROTOC_FLAGS := $(LOCAL_PROTOC_FLAGS) $(my_protoc_flags)\n$(proto_generated_cpps): PRIVATE_RENAME_CPP_EXT := $(my_rename_cpp_ext)\n$(proto_generated_cpps): $(proto_gen_dir)/%.pb$(my_proto_source_suffix): %.proto $(my_protoc_deps) $(PROTOC)\n\t$(transform-proto-to-cc)\n\n$(my_host)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_proto_defined := true\nendif\n# Ideally we can generate the source directly into $(intermediates).\n# But many Android.mks assume the .pb.hs are in $(generated_sources_dir).\n# As a workaround, we make a copy in the $(intermediates).\nproto_intermediate_dir := $(intermediates)/proto\nproto_intermediate_cpps := $(patsubst $(proto_gen_dir)/%,$(proto_intermediate_dir)/%,\\\n    $(proto_generated_cpps))\n$(proto_intermediate_cpps) : $(proto_intermediate_dir)/% : $(proto_gen_dir)/%\n\t@echo \"Copy: $@\"\n\t$(copy-file-to-target)\n\t$(hide) cp $(basename $<).h $(basename $@).h\n$(call track-src-file-gen,$(proto_sources),$(proto_intermediate_cpps))\n\nmy_generated_sources += $(proto_intermediate_cpps)\n\nmy_c_includes += $(my_proto_c_includes)\n# Auto-export the generated proto source dir.\nmy_export_c_include_dirs += $(my_proto_c_includes)\n\nifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-enable_malloc)\n    my_static_libraries += libprotobuf-c-nano-enable_malloc\nelse ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c)\n    my_static_libraries += libprotobuf-c-nano\nelse ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-enable_malloc-16bit)\n    my_static_libraries += libprotobuf-c-nano-enable_malloc-16bit\nelse ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-16bit)\n    my_static_libraries += libprotobuf-c-nano-16bit\nelse ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-enable_malloc-32bit)\n    my_static_libraries += libprotobuf-c-nano-enable_malloc-32bit\nelse ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-32bit)\n    my_static_libraries += libprotobuf-c-nano-32bit\nelse ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),full)\n    ifdef LOCAL_SDK_VERSION\n        my_static_libraries += libprotobuf-cpp-full-ndk\n    else\n        my_shared_libraries += libprotobuf-cpp-full\n    endif\nelse ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),lite-static)\n    my_static_libraries += libprotobuf-cpp-lite\nelse\n    ifdef LOCAL_SDK_VERSION\n        my_static_libraries += libprotobuf-cpp-lite-ndk\n    else\n        my_shared_libraries += libprotobuf-cpp-lite\n    endif\nendif\nendif  # $(proto_sources) non-empty\n\n###########################################################\n## AIDL: Compile .aidl files to .cpp and .h files\n###########################################################\naidl_src := $(strip $(filter %.aidl,$(my_src_files)))\naidl_gen_cpp :=\nifneq ($(aidl_src),)\n\n# Use the intermediates directory to avoid writing our own .cpp -> .o rules.\naidl_gen_cpp_root := $(intermediates)/aidl-generated/src\naidl_gen_include_root := $(intermediates)/aidl-generated/include\n\n# Multi-architecture builds have distinct intermediates directories.\n# Thus we'll actually generate source for each architecture.\n$(foreach s,$(aidl_src),\\\n    $(eval $(call define-aidl-cpp-rule,$(s),$(aidl_gen_cpp_root),aidl_gen_cpp)))\n$(foreach cpp,$(aidl_gen_cpp), \\\n    $(call include-depfile,$(addsuffix .aidl.d,$(basename $(cpp))),$(cpp)))\n$(call track-src-file-gen,$(aidl_src),$(aidl_gen_cpp))\n\n$(aidl_gen_cpp) : PRIVATE_MODULE := $(LOCAL_MODULE)\n$(aidl_gen_cpp) : PRIVATE_HEADER_OUTPUT_DIR := $(aidl_gen_include_root)\n$(aidl_gen_cpp) : PRIVATE_AIDL_FLAGS := $(addprefix -I,$(LOCAL_AIDL_INCLUDES))\n\n# Add generated headers to include paths.\nmy_c_includes += $(aidl_gen_include_root)\nmy_export_c_include_dirs += $(aidl_gen_include_root)\n# Pick up the generated C++ files later for transformation to .o files.\nmy_generated_sources += $(aidl_gen_cpp)\n\nendif  # $(aidl_src) non-empty\n\n###########################################################\n## Compile the .vts files to .cc (or .c) and then to .o\n###########################################################\n\nvts_src := $(strip $(filter %.vts,$(my_src_files)))\nvts_gen_cpp :=\nifneq ($(vts_src),)\nmy_soong_problems += vts\n\n# Use the intermediates directory to avoid writing our own .cpp -> .o rules.\nvts_gen_cpp_root := $(intermediates)/vts-generated/src\nvts_gen_include_root := $(intermediates)/vts-generated/include\n\n# Multi-architecture builds have distinct intermediates directories.\n# Thus we'll actually generate source for each architecture.\n$(foreach s,$(vts_src),\\\n    $(eval $(call define-vts-cpp-rule,$(s),$(vts_gen_cpp_root),vts_gen_cpp)))\n$(call track-src-file-gen,$(vts_src),$(vts_gen_cpp))\n\n$(vts_gen_cpp) : PRIVATE_MODULE := $(LOCAL_MODULE)\n$(vts_gen_cpp) : PRIVATE_HEADER_OUTPUT_DIR := $(vts_gen_include_root)\n$(vts_gen_cpp) : PRIVATE_VTS_FLAGS := $(addprefix -I,$(LOCAL_VTS_INCLUDES)) $(addprefix -m,$(LOCAL_VTS_MODE))\n\n# Add generated headers to include paths.\nmy_c_includes += $(vts_gen_include_root)\nmy_export_c_include_dirs += $(vts_gen_include_root)\n# Pick up the generated C++ files later for transformation to .o files.\nmy_generated_sources += $(vts_gen_cpp)\n\nendif  # $(vts_src) non-empty\n\n###########################################################\n## YACC: Compile .y/.yy files to .c/.cpp and then to .o.\n###########################################################\n\ny_yacc_sources := $(filter %.y,$(my_src_files))\ny_yacc_cs := $(addprefix \\\n    $(intermediates)/,$(y_yacc_sources:.y=.c))\nifneq ($(y_yacc_cs),)\n$(y_yacc_cs): $(intermediates)/%.c: \\\n    $(TOPDIR)$(LOCAL_PATH)/%.y $(BISON) $(BISON_DATA) $(M4) \\\n    $(my_additional_dependencies)\n\t$(call transform-y-to-c-or-cpp)\n$(call track-src-file-gen,$(y_yacc_sources),$(y_yacc_cs))\n\nmy_generated_sources += $(y_yacc_cs)\nendif\n\nyy_yacc_sources := $(filter %.yy,$(my_src_files))\nyy_yacc_cpps := $(addprefix \\\n    $(intermediates)/,$(yy_yacc_sources:.yy=$(LOCAL_CPP_EXTENSION)))\nifneq ($(yy_yacc_cpps),)\n$(yy_yacc_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \\\n    $(TOPDIR)$(LOCAL_PATH)/%.yy $(BISON) $(BISON_DATA) $(M4) \\\n    $(my_additional_dependencies)\n\t$(call transform-y-to-c-or-cpp)\n$(call track-src-file-gen,$(yy_yacc_sources),$(yy_yacc_cpps))\n\nmy_generated_sources += $(yy_yacc_cpps)\nendif\n\n###########################################################\n## LEX: Compile .l/.ll files to .c/.cpp and then to .o.\n###########################################################\n\nl_lex_sources := $(filter %.l,$(my_src_files))\nl_lex_cs := $(addprefix \\\n    $(intermediates)/,$(l_lex_sources:.l=.c))\nifneq ($(l_lex_cs),)\n$(l_lex_cs): $(LEX) $(M4)\n$(l_lex_cs): $(intermediates)/%.c: \\\n    $(TOPDIR)$(LOCAL_PATH)/%.l\n\t$(transform-l-to-c-or-cpp)\n$(call track-src-file-gen,$(l_lex_sources),$(l_lex_cs))\n\nmy_generated_sources += $(l_lex_cs)\nendif\n\nll_lex_sources := $(filter %.ll,$(my_src_files))\nll_lex_cpps := $(addprefix \\\n    $(intermediates)/,$(ll_lex_sources:.ll=$(LOCAL_CPP_EXTENSION)))\nifneq ($(ll_lex_cpps),)\n$(ll_lex_cpps): $(LEX) $(M4)\n$(ll_lex_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \\\n    $(TOPDIR)$(LOCAL_PATH)/%.ll\n\t$(transform-l-to-c-or-cpp)\n$(call track-src-file-gen,$(ll_lex_sources),$(ll_lex_cpps))\n\nmy_generated_sources += $(ll_lex_cpps)\nendif\n\n###########################################################\n## C++: Compile .cpp files to .o.\n###########################################################\n\nifneq ($(filter %$(LOCAL_CPP_EXTENSION).arm,$(my_src_files)),)\n$(call pretty-error,Files ending in $(LOCAL_CPP_EXTENSION).arm are deprecated. See $(CHANGES_URL)#file_arm)\nendif\n\ndotdot_sources := $(filter ../%$(LOCAL_CPP_EXTENSION),$(my_src_files))\ndotdot_objects :=\n$(foreach s,$(dotdot_sources),\\\n  $(eval $(call compile-dotdot-cpp-file,$(s),\\\n    $(my_additional_dependencies),\\\n    dotdot_objects,\\\n    $(my_pool))))\n$(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects))\n\ncpp_normal_sources := $(filter-out ../%,$(filter %$(LOCAL_CPP_EXTENSION),$(my_src_files)))\ncpp_objects := $(addprefix $(intermediates)/,$(cpp_normal_sources:$(LOCAL_CPP_EXTENSION)=.o))\n$(call track-src-file-obj,$(cpp_normal_sources),$(cpp_objects))\n\n$(dotdot_objects) $(cpp_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)\n$(dotdot_objects) $(cpp_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)\n\nifneq ($(strip $(cpp_objects)),)\n$(cpp_objects): .KATI_NINJA_POOL := $(my_pool)\n$(cpp_objects): $(intermediates)/%.o: \\\n    $(TOPDIR)$(LOCAL_PATH)/%$(LOCAL_CPP_EXTENSION) \\\n    $(my_additional_dependencies) $(CLANG_CXX)\n\t$(transform-$(PRIVATE_HOST)cpp-to-o)\n$(call include-depfiles-for-objs, $(cpp_objects))\nendif\n\ncpp_objects += $(dotdot_objects)\n\n###########################################################\n## C++: Compile generated .cpp files to .o.\n###########################################################\n\ngen_cpp_sources := $(filter %$(LOCAL_CPP_EXTENSION),$(my_generated_sources))\ngen_cpp_objects := $(gen_cpp_sources:%$(LOCAL_CPP_EXTENSION)=%.o)\n$(call track-gen-file-obj,$(gen_cpp_sources),$(gen_cpp_objects))\n\nifneq ($(strip $(gen_cpp_objects)),)\n# Compile all generated files as thumb.\n$(gen_cpp_objects): .KATI_NINJA_POOL := $(my_pool)\n$(gen_cpp_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)\n$(gen_cpp_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)\n$(gen_cpp_objects): $(intermediates)/%.o: \\\n    $(intermediates)/%$(LOCAL_CPP_EXTENSION) \\\n    $(my_additional_dependencies) $(CLANG_CXX)\n\t$(transform-$(PRIVATE_HOST)cpp-to-o)\n$(call include-depfiles-for-objs, $(gen_cpp_objects))\nendif\n\n###########################################################\n## S: Compile generated .S and .s files to .o.\n###########################################################\n\ngen_S_sources := $(filter %.S,$(my_generated_sources))\ngen_S_objects := $(gen_S_sources:%.S=%.o)\n$(call track-gen-file-obj,$(gen_S_sources),$(gen_S_objects))\n\nifneq ($(strip $(gen_S_sources)),)\n$(gen_S_objects): .KATI_NINJA_POOL := $(my_pool)\n$(gen_S_objects): $(intermediates)/%.o: $(intermediates)/%.S \\\n    $(my_additional_dependencies) $(CLANG)\n\t$(transform-$(PRIVATE_HOST)s-to-o)\n$(call include-depfiles-for-objs, $(gen_S_objects))\nendif\n\ngen_s_sources := $(filter %.s,$(my_generated_sources))\ngen_s_objects := $(gen_s_sources:%.s=%.o)\n$(call track-gen-file-obj,$(gen_s_sources),$(gen_s_objects))\n\nifneq ($(strip $(gen_s_objects)),)\n$(gen_s_objects): .KATI_NINJA_POOL := $(my_pool)\n$(gen_s_objects): $(intermediates)/%.o: $(intermediates)/%.s \\\n    $(my_additional_dependencies) $(CLANG)\n\t$(transform-$(PRIVATE_HOST)s-to-o)\nendif\n\ngen_asm_objects := $(gen_S_objects) $(gen_s_objects)\n$(gen_asm_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)\n\n###########################################################\n## o: Include generated .o files in output.\n###########################################################\n\ngen_o_objects := $(filter %.o,$(my_generated_sources))\n\n###########################################################\n## C: Compile .c files to .o.\n###########################################################\n\nifneq ($(filter %.c.arm,$(my_src_files)),)\n$(call pretty-error,Files ending in .c.arm are deprecated. See $(CHANGES_URL)#file_arm)\nendif\n\ndotdot_sources := $(filter ../%.c, $(my_src_files))\ndotdot_objects :=\n$(foreach s, $(dotdot_sources),\\\n  $(eval $(call compile-dotdot-c-file,$(s),\\\n    $(my_additional_dependencies),\\\n    dotdot_objects,\\\n    $(my_pool))))\n$(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects))\n\nc_normal_sources := $(filter-out ../%,$(filter %.c,$(my_src_files)))\nc_objects := $(addprefix $(intermediates)/,$(c_normal_sources:.c=.o))\n$(call track-src-file-obj,$(c_normal_sources),$(c_objects))\n\n$(dotdot_objects) $(c_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)\n$(dotdot_objects) $(c_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)\n\nifneq ($(strip $(c_objects)),)\n$(c_objects): .KATI_NINJA_POOL := $(my_pool)\n$(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c \\\n    $(my_additional_dependencies) $(CLANG)\n\t$(transform-$(PRIVATE_HOST)c-to-o)\n$(call include-depfiles-for-objs, $(c_objects))\nendif\n\nc_objects += $(dotdot_objects)\n\n###########################################################\n## C: Compile generated .c files to .o.\n###########################################################\n\ngen_c_sources := $(filter %.c,$(my_generated_sources))\ngen_c_objects := $(gen_c_sources:%.c=%.o)\n$(call track-gen-file-obj,$(gen_c_sources),$(gen_c_objects))\n\nifneq ($(strip $(gen_c_objects)),)\n# Compile all generated files as thumb.\n$(gen_c_objects): .KATI_NINJA_POOL := $(my_pool)\n$(gen_c_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)\n$(gen_c_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)\n$(gen_c_objects): $(intermediates)/%.o: $(intermediates)/%.c \\\n    $(my_additional_dependencies) $(CLANG)\n\t$(transform-$(PRIVATE_HOST)c-to-o)\n$(call include-depfiles-for-objs, $(gen_c_objects))\nendif\n\n###########################################################\n## ObjC: Compile .m files to .o\n###########################################################\n\nobjc_sources := $(filter %.m,$(my_src_files))\nobjc_objects := $(addprefix $(intermediates)/,$(objc_sources:.m=.o))\n$(call track-src-file-obj,$(objc_sources),$(objc_objects))\n\nifneq ($(strip $(objc_objects)),)\nmy_soong_problems += objc\n$(objc_objects): .KATI_NINJA_POOL := $(my_pool)\n$(objc_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.m \\\n    $(my_additional_dependencies) $(CLANG)\n\t$(transform-$(PRIVATE_HOST)m-to-o)\n$(call include-depfiles-for-objs, $(objc_objects))\nendif\n\n###########################################################\n## ObjC++: Compile .mm files to .o\n###########################################################\n\nobjcpp_sources := $(filter %.mm,$(my_src_files))\nobjcpp_objects := $(addprefix $(intermediates)/,$(objcpp_sources:.mm=.o))\n$(call track-src-file-obj,$(objcpp_sources),$(objcpp_objects))\n\nifneq ($(strip $(objcpp_objects)),)\n$(objcpp_objects): .KATI_NINJA_POOL := $(my_pool)\n$(objcpp_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.mm \\\n    $(my_additional_dependencies) $(CLANG_CXX)\n\t$(transform-$(PRIVATE_HOST)mm-to-o)\n$(call include-depfiles-for-objs, $(objcpp_objects))\nendif\n\n###########################################################\n## AS: Compile .S files to .o.\n###########################################################\n\nasm_sources_S := $(filter %.S,$(my_src_files))\ndotdot_sources := $(filter ../%,$(asm_sources_S))\nasm_sources_S := $(filter-out ../%,$(asm_sources_S))\nasm_objects_S := $(addprefix $(intermediates)/,$(asm_sources_S:.S=.o))\n$(call track-src-file-obj,$(asm_sources_S),$(asm_objects_S))\n\ndotdot_objects_S :=\n$(foreach s,$(dotdot_sources),\\\n  $(eval $(call compile-dotdot-s-file,$(s),\\\n    $(my_additional_dependencies),\\\n    dotdot_objects_S,\\\n    $(my_pool))))\n$(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects_S))\n\nifneq ($(strip $(asm_objects_S)),)\n$(asm_objects_S): .KATI_NINJA_POOL := $(my_pool)\n$(asm_objects_S): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.S \\\n    $(my_additional_dependencies) $(CLANG)\n\t$(transform-$(PRIVATE_HOST)s-to-o)\n$(call include-depfiles-for-objs, $(asm_objects_S))\nendif\n\nasm_sources_s := $(filter %.s,$(my_src_files))\ndotdot_sources := $(filter ../%,$(asm_sources_s))\nasm_sources_s := $(filter-out ../%,$(asm_sources_s))\nasm_objects_s := $(addprefix $(intermediates)/,$(asm_sources_s:.s=.o))\n$(call track-src-file-obj,$(asm_sources_s),$(asm_objects_s))\n\ndotdot_objects_s :=\n$(foreach s,$(dotdot_sources),\\\n  $(eval $(call compile-dotdot-s-file-no-deps,$(s),\\\n    $(my_additional_dependencies),\\\n    dotdot_objects_s,\\\n    $(my_pool))))\n$(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects_s))\n\nifneq ($(strip $(asm_objects_s)),)\n$(asm_objects_s): .KATI_NINJA_POOL := $(my_pool)\n$(asm_objects_s): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.s \\\n    $(my_additional_dependencies) $(CLANG)\n\t$(transform-$(PRIVATE_HOST)s-to-o)\nendif\n\nasm_objects := $(dotdot_objects_S) $(dotdot_objects_s) $(asm_objects_S) $(asm_objects_s)\n$(asm_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)\n\n\n# .asm for x86/x86_64 needs to be compiled with yasm.\nasm_sources_asm := $(filter %.asm,$(my_src_files))\nifneq ($(strip $(asm_sources_asm)),)\nasm_objects_asm := $(addprefix $(intermediates)/,$(asm_sources_asm:.asm=.o))\n$(asm_objects_asm): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.asm \\\n    $(my_additional_dependencies) $(YASM)\n\t$(transform-asm-to-o)\n$(call track-src-file-obj,$(asm_sources_asm),$(asm_objects_asm))\n\nasm_objects += $(asm_objects_asm)\nendif\n\n###################################################################\n## Convert to sanitized names where they exist.\n## These lists come from sanitizerStaticLibsMap; see\n## build/soong/cc/sanitize.go\n##\n## $(1): list of static dependencies\n## $(2): name of sanitizer (e.g. cfi, hwasan)\n##################################################################\ndefine use_soong_sanitized_static_libraries\n  $(foreach lib,$(1),$(if $(filter $(lib),\\\n      $(SOONG_$(2)_$(my_image_variant)_$(my_arch)_STATIC_LIBRARIES)),\\\n      $(lib).$(2),$(lib)))\nendef\n\n###################################################################\n## When compiling a CFI enabled target, use the .cfi variant of any\n## static dependencies (where they exist).\n##################################################################\nifneq ($(filter cfi,$(my_sanitize)),)\n  my_whole_static_libraries := $(call use_soong_sanitized_static_libraries,\\\n    $(my_whole_static_libraries),cfi)\n  my_static_libraries := $(call use_soong_sanitized_static_libraries,\\\n    $(my_static_libraries),cfi)\nendif\n\n###################################################################\n## When compiling a hwasan enabled target, use the .hwasan variant\n## of any static dependencies (where they exist).\n##################################################################\nifneq ($(filter hwaddress,$(my_sanitize)),)\n  my_whole_static_libraries := $(call use_soong_sanitized_static_libraries,\\\n    $(my_whole_static_libraries),hwasan)\n  my_static_libraries := $(call use_soong_sanitized_static_libraries,\\\n    $(my_static_libraries),hwasan)\nendif\n\n###################################################################\n## When compiling a memtag_stack enabled target, use the .memtag_stack variant\n## of any static dependencies (where they exist).\n##################################################################\nifneq ($(filter memtag_stack,$(my_sanitize)),)\n  my_whole_static_libraries := $(call use_soong_sanitized_static_libraries,\\\n    $(my_whole_static_libraries),memtag_stack)\n  my_static_libraries := $(call use_soong_sanitized_static_libraries,\\\n    $(my_static_libraries),memtag_stack)\nendif\n\n###################################################################\n## When compiling against API imported module, use API import stub\n## libraries.\n##################################################################\n\napiimport_postfix := .apiimport\n\nifneq ($(call module-in-vendor-or-product),)\n  ifeq ($(LOCAL_IN_PRODUCT),true)\n    apiimport_postfix := .apiimport.product\n  else\n    apiimport_postfix := .apiimport.vendor\n  endif\nendif\n\nmy_shared_libraries := $(foreach l,$(my_shared_libraries), \\\n $(if $(filter $(l), $(API_IMPORTED_SHARED_LIBRARIES)), $(l)$(apiimport_postfix), $(l)))\nmy_system_shared_libraries := $(foreach l,$(my_system_shared_libraries), \\\n $(if $(filter $(l), $(API_IMPORTED_SHARED_LIBRARIES)), $(l)$(apiimport_postfix), $(l)))\nmy_header_libraries := $(foreach l,$(my_header_libraries), \\\n $(if $(filter $(l), $(API_IMPORTED_HEADER_LIBRARIES)), $(l)$(apiimport_postfix), $(l)))\n\n###########################################################\n## When compiling against the VNDK, use LL-NDK libraries\n###########################################################\nifneq ($(call module-in-vendor-or-product),)\n  #####################################################\n  ## Soong modules may be built three times, once for\n  ## /system, once for /vendor and once for /product.\n  ## If we're using the VNDK, switch all soong\n  ## libraries over to the /vendor or /product variant.\n  #####################################################\n  ifeq ($(LOCAL_IN_PRODUCT),true)\n    my_whole_static_libraries := $(foreach l,$(my_whole_static_libraries),\\\n      $(if $(SPLIT_PRODUCT.STATIC_LIBRARIES.$(l)),$(l).product,$(l)))\n    my_static_libraries := $(foreach l,$(my_static_libraries),\\\n      $(if $(SPLIT_PRODUCT.STATIC_LIBRARIES.$(l)),$(l).product,$(l)))\n    my_shared_libraries := $(foreach l,$(my_shared_libraries),\\\n      $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l)))\n    my_system_shared_libraries := $(foreach l,$(my_system_shared_libraries),\\\n      $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l)))\n    my_header_libraries := $(foreach l,$(my_header_libraries),\\\n      $(if $(SPLIT_PRODUCT.HEADER_LIBRARIES.$(l)),$(l).product,$(l)))\n  else\n    my_whole_static_libraries := $(foreach l,$(my_whole_static_libraries),\\\n      $(if $(SPLIT_VENDOR.STATIC_LIBRARIES.$(l)),$(l).vendor,$(l)))\n    my_static_libraries := $(foreach l,$(my_static_libraries),\\\n      $(if $(SPLIT_VENDOR.STATIC_LIBRARIES.$(l)),$(l).vendor,$(l)))\n    my_shared_libraries := $(foreach l,$(my_shared_libraries),\\\n      $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l)))\n    my_system_shared_libraries := $(foreach l,$(my_system_shared_libraries),\\\n      $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l)))\n    my_header_libraries := $(foreach l,$(my_header_libraries),\\\n      $(if $(SPLIT_VENDOR.HEADER_LIBRARIES.$(l)),$(l).vendor,$(l)))\n  endif\nendif\n\n# Platform can use vendor public libraries. If a required shared lib is one of\n# the vendor public libraries, the lib is switched to the stub version of the lib.\nifeq ($(call module-in-vendor-or-product),)\n  my_shared_libraries := $(foreach l,$(my_shared_libraries),\\\n    $(if $(filter $(l),$(VENDOR_PUBLIC_LIBRARIES)),$(l).vendorpublic,$(l)))\nendif\n\n###########################################################\n## When compiling against the NDK, use SDK variants of Soong libraries\n###########################################################\n\nifneq ($(LOCAL_SDK_VERSION),)\n  my_whole_static_libraries := $(call use_soong_sdk_libraries,$(my_whole_static_libraries))\n  my_static_libraries := $(call use_soong_sdk_libraries,$(my_static_libraries))\n  my_shared_libraries := $(call use_soong_sdk_libraries,$(my_shared_libraries))\n  my_system_shared_libraries := $(call use_soong_sdk_libraries,$(my_system_shared_libraries))\n  my_header_libraries := $(call use_soong_sdk_libraries,$(my_header_libraries))\nendif\n\n##########################################################\n## Set up installed module dependency\n## We cannot compute the full path of the LOCAL_SHARED_LIBRARIES for\n## they may cusomize their install path with LOCAL_MODULE_PATH\n##########################################################\n# Get the list of INSTALLED libraries as module names.\nifneq ($(LOCAL_SDK_VERSION),)\n  installed_shared_library_module_names := \\\n      $(my_shared_libraries)\nelse\n  installed_shared_library_module_names := \\\n      $(my_shared_libraries) $(my_system_shared_libraries)\nendif\n\n# The real dependency will be added after all Android.mks are loaded and the install paths\n# of the shared libraries are determined.\nifdef LOCAL_INSTALLED_MODULE\nifdef installed_shared_library_module_names\n$(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \\\n    $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(installed_shared_library_module_names))\nendif\nendif\n\n\n####################################################\n## Verify that NDK-built libraries only link against\n## other NDK-built libraries\n####################################################\n\ninclude $(BUILD_SYSTEM)/allowed_ndk_types.mk\n\nifdef LOCAL_SDK_VERSION\nmy_link_type := native:ndk:$(my_ndk_stl_family):$(my_ndk_stl_link_type)\nmy_warn_types := $(my_warn_ndk_types)\nmy_allowed_types := $(my_allowed_ndk_types)\nelse ifeq ($(call module-in-vendor-or-product),true)\n    _name := $(patsubst %.vendor,%,$(LOCAL_MODULE))\n    _name := $(patsubst %.product,%,$(LOCAL_MODULE))\n    ifneq ($(filter $(_name),$(VNDK_CORE_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) $(LLNDK_LIBRARIES)),)\n        ifeq ($(filter $(_name),$(VNDK_PRIVATE_LIBRARIES)),)\n            my_link_type := native:vndk\n        else\n            my_link_type := native:vndk_private\n        endif\n        my_warn_types :=\n        my_allowed_types := native:vndk native:vndk_private\n    else ifeq ($(LOCAL_IN_PRODUCT),true)\n        # Modules installed to /product cannot directly depend on modules marked\n        # with vendor_available: false\n        my_link_type := native:product\n        my_warn_types :=\n        my_allowed_types := native:product native:vndk native:platform_vndk\n    else\n        # Modules installed to /vendor cannot directly depend on modules marked\n        # with vendor_available: false\n        my_link_type := native:vendor\n        my_warn_types :=\n        my_allowed_types := native:vendor native:vndk native:platform_vndk\n    endif\nelse ifneq ($(filter $(TARGET_RECOVERY_OUT)/%,$(call get_non_asan_path,$(LOCAL_MODULE_PATH))),)\nmy_link_type := native:recovery\nmy_warn_types :=\n# TODO(b/113303515) remove native:platform and my_allowed_ndk_types\nmy_allowed_types := native:recovery native:platform native:platform_vndk $(my_allowed_ndk_types)\nelse\nmy_link_type := native:platform\nmy_warn_types := $(my_warn_ndk_types)\nmy_allowed_types := $(my_allowed_ndk_types) native:platform native:platform_vndk\nendif\n\nALL_MODULES.$(my_register_name).WHOLE_STATIC_LIBS := $(my_whole_static_libraries)\n\nmy_link_deps := $(addprefix STATIC_LIBRARIES:,$(my_whole_static_libraries) $(my_static_libraries))\nifneq ($(filter-out STATIC_LIBRARIES HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)),)\nmy_link_deps += $(addprefix SHARED_LIBRARIES:,$(my_shared_libraries))\nendif\n\nmy_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX)\nmy_common :=\ninclude $(BUILD_SYSTEM)/link_type.mk\n\n###########################################################\n## Common object handling.\n###########################################################\n\nmy_unused_src_files := $(filter-out $(logtags_sources) $(my_tracked_src_files),$(my_src_files) $(my_gen_src_files))\nifneq ($(my_unused_src_files),)\n  $(error $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Unused source files: $(my_unused_src_files))\nendif\n\n# some rules depend on asm_objects being first.  If your code depends on\n# being first, it's reasonable to require it to be assembly\nnormal_objects := \\\n    $(asm_objects) \\\n    $(cpp_objects) \\\n    $(gen_cpp_objects) \\\n    $(gen_asm_objects) \\\n    $(c_objects) \\\n    $(gen_c_objects) \\\n    $(objc_objects) \\\n    $(objcpp_objects)\n\nnew_order_normal_objects := $(foreach f,$(my_src_files),$(my_src_file_obj_$(f)))\nnew_order_normal_objects += $(foreach f,$(my_gen_src_files),$(my_src_file_obj_$(f)))\n\nifneq ($(sort $(normal_objects)),$(sort $(new_order_normal_objects)))\n$(warning $(LOCAL_MODULE_MAKEFILE) Internal build system warning: New object list does not match old)\n$(info Only in old: $(filter-out $(new_order_normal_objects),$(sort $(normal_objects))))\n$(info Only in new: $(filter-out $(normal_objects),$(sort $(new_order_normal_objects))))\nendif\n\nifeq ($(BINARY_OBJECTS_ORDER),soong)\nnormal_objects := $(new_order_normal_objects)\nendif\n\nnormal_objects += $(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES))\n\nall_objects := $(normal_objects) $(gen_o_objects)\n\nLOCAL_INTERMEDIATE_TARGETS += $(all_objects)\n\n# Cleanup file tracking\n$(foreach f,$(my_tracked_gen_files),$(eval my_src_file_gen_$(s):=))\nmy_tracked_gen_files :=\n$(foreach f,$(my_tracked_src_files),$(eval my_src_file_obj_$(s):=))\nmy_tracked_src_files :=\n\nmy_c_includes += $(TOPDIR)$(LOCAL_PATH) $(intermediates) $(generated_sources_dir)\n\nmy_c_includes := $(foreach inc,$(my_c_includes),$(call clean-path,$(inc)))\n\nmy_outside_includes := $(filter-out $(OUT_DIR)/%,$(filter /%,$(my_c_includes)) $(filter ../%,$(my_c_includes)))\nifneq ($(my_outside_includes),)\n  ifeq ($(BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS),true)\n    $(call pretty-warning,C_INCLUDES must be under the source or output directories: $(my_outside_includes))\n  else\n    $(call pretty-error,C_INCLUDES must be under the source or output directories: $(my_outside_includes))\n  endif\nendif\n\n# all_objects includes gen_o_objects which were part of LOCAL_GENERATED_SOURCES;\n# use normal_objects here to avoid creating circular dependencies. This assumes\n# that custom build rules which generate .o files don't consume other generated\n# sources as input (or if they do they take care of that dependency themselves).\n$(normal_objects) : | $(my_generated_sources)\nALL_C_CPP_ETC_OBJECTS += $(all_objects)\n\n\n###########################################################\n# Standard library handling.\n###########################################################\n\n###########################################################\n# The list of libraries that this module will link against are in\n# these variables.  Each is a list of bare module names like \"libc libm\".\n#\n# LOCAL_SHARED_LIBRARIES\n# LOCAL_STATIC_LIBRARIES\n# LOCAL_WHOLE_STATIC_LIBRARIES\n#\n# We need to convert the bare names into the dependencies that\n# we'll use for LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE.\n# LOCAL_BUILT_MODULE should depend on the BUILT versions of the\n# libraries, so that simply building this module doesn't force\n# an install of a library.  Similarly, LOCAL_INSTALLED_MODULE\n# should depend on the INSTALLED versions of the libraries so\n# that they get installed when this module does.\n###########################################################\n# NOTE:\n# WHOLE_STATIC_LIBRARIES are libraries that are pulled into the\n# module without leaving anything out, which is useful for turning\n# a collection of .a files into a .so file.  Linking against a\n# normal STATIC_LIBRARY will only pull in code/symbols that are\n# referenced by the module. (see gcc/ld's --whole-archive option)\n###########################################################\n\n# Get the list of BUILT libraries, which are under\n# various intermediates directories.\nso_suffix := $($(my_prefix)SHLIB_SUFFIX)\na_suffix := $($(my_prefix)STATIC_LIB_SUFFIX)\n\nifneq ($(LOCAL_SDK_VERSION),)\nbuilt_shared_libraries := \\\n    $(foreach lib,$(my_shared_libraries), \\\n      $(call intermediates-dir-for, \\\n        SHARED_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(so_suffix))\nbuilt_shared_library_deps := $(addsuffix .toc, $(built_shared_libraries))\n\n# Add the NDK libraries to the built module dependency\nmy_system_shared_libraries_fullpath := \\\n    $(my_ndk_stl_shared_lib_fullpath) \\\n    $(addprefix $(my_ndk_sysroot_lib)/, \\\n        $(addsuffix $(so_suffix), $(my_system_shared_libraries)))\n\n# We need to preserve the ordering of LOCAL_SHARED_LIBRARIES regardless of\n# whether the libs are generated or prebuilt, so we simply can't split into two\n# lists and use addprefix.\nmy_ndk_shared_libraries_fullpath := \\\n    $(foreach _lib,$(my_ndk_shared_libraries),\\\n        $(if $(filter $(NDK_KNOWN_LIBS),$(_lib)),\\\n            $(my_ndk_sysroot_lib)/$(_lib)$(so_suffix)))\n\nbuilt_shared_libraries += \\\n    $(my_ndk_shared_libraries_fullpath) \\\n    $(my_system_shared_libraries_fullpath) \\\n\nbuilt_shared_library_deps += \\\n    $(my_ndk_shared_libraries_fullpath) \\\n    $(my_system_shared_libraries_fullpath) \\\n\nelse\nbuilt_shared_libraries := \\\n    $(foreach lib,$(installed_shared_library_module_names), \\\n      $(call intermediates-dir-for, \\\n        SHARED_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(so_suffix))\nbuilt_shared_library_deps := $(addsuffix .toc, $(built_shared_libraries))\nmy_system_shared_libraries_fullpath :=\nendif\n\nbuilt_static_libraries := \\\n    $(foreach lib,$(my_static_libraries), \\\n      $(call intermediates-dir-for, \\\n        STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(a_suffix))\n\nifdef LOCAL_SDK_VERSION\nbuilt_static_libraries += $(my_ndk_stl_static_lib)\nendif\n\nbuilt_whole_libraries := \\\n    $(foreach lib,$(my_whole_static_libraries), \\\n      $(call intermediates-dir-for, \\\n        STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(a_suffix))\n\n# Default is -fno-rtti.\nifeq ($(strip $(LOCAL_RTTI_FLAG)),)\nLOCAL_RTTI_FLAG := -fno-rtti\nendif\n\n###########################################################\n# Rule-specific variable definitions\n###########################################################\n\nmy_cflags += $(LOCAL_CLANG_CFLAGS)\nmy_conlyflags += $(LOCAL_CLANG_CONLYFLAGS)\nmy_cppflags += $(LOCAL_CLANG_CPPFLAGS)\nmy_asflags += $(LOCAL_CLANG_ASFLAGS)\nmy_ldflags += $(LOCAL_CLANG_LDFLAGS)\nmy_cflags += $(LOCAL_CLANG_CFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_CFLAGS_$(my_32_64_bit_suffix))\nmy_conlyflags += $(LOCAL_CLANG_CONLYFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_CONLYFLAGS_$(my_32_64_bit_suffix))\nmy_cppflags += $(LOCAL_CLANG_CPPFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_CPPFLAGS_$(my_32_64_bit_suffix))\nmy_ldflags += $(LOCAL_CLANG_LDFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_LDFLAGS_$(my_32_64_bit_suffix))\nmy_asflags += $(LOCAL_CLANG_ASFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_ASFLAGS_$(my_32_64_bit_suffix))\nmy_cflags := $(call convert-to-clang-flags,$(my_cflags))\nmy_cppflags := $(call convert-to-clang-flags,$(my_cppflags))\nmy_asflags := $(call convert-to-clang-flags,$(my_asflags))\nmy_ldflags := $(call convert-to-clang-flags,$(my_ldflags))\n\n# No one should ever use this flag. On GCC it's mere presence will disable all\n# warnings, even those that are specified after it (contrary to typical warning\n# flag behavior). This circumvents CFLAGS_NO_OVERRIDE from forcibly enabling the\n# warnings that are *always* bugs.\nmy_illegal_flags := -w\nmy_cflags := $(filter-out $(my_illegal_flags),$(my_cflags))\nmy_cppflags := $(filter-out $(my_illegal_flags),$(my_cppflags))\nmy_conlyflags := $(filter-out $(my_illegal_flags),$(my_conlyflags))\n\n# We can enforce some rules more strictly in the code we own. my_strict\n# indicates if this is code that we can be stricter with. If we have rules that\n# we want to apply to *our* code (but maybe can't for vendor/device specific\n# things), we could extend this to be a ternary value.\nmy_strict := true\nifneq ($(filter external/%,$(LOCAL_PATH)),)\n    my_strict := false\nendif\n\n# Can be used to make some annotations stricter for code we can fix (such as\n# when we mark functions as deprecated).\nifeq ($(my_strict),true)\n    my_cflags += -DANDROID_STRICT\nendif\n\n# Check if -Werror or -Wno-error is used in C compiler flags.\n# Header libraries do not need cflags.\nmy_all_cflags := $(my_cflags) $(my_cppflags) $(my_cflags_no_override)\nifneq (HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS))\n  # Prebuilt modules do not need cflags.\n  ifeq (,$(LOCAL_PREBUILT_MODULE_FILE))\n    # Issue warning if -Wno-error is used.\n    ifneq (,$(filter -Wno-error,$(my_all_cflags)))\n      $(eval MODULES_USING_WNO_ERROR := $(MODULES_USING_WNO_ERROR) $(LOCAL_MODULE_MAKEFILE):$(LOCAL_MODULE))\n    else\n      # Issue warning if -Werror is not used. Add it.\n      ifeq (,$(filter -Werror,$(my_all_cflags)))\n        # Add -Wall -Werror unless the project is in the WARNING_ALLOWED project list.\n        ifeq (,$(strip $(call find_warning_allowed_projects,$(LOCAL_PATH))))\n          my_cflags := -Wall -Werror $(my_cflags)\n        else\n          $(eval MODULES_WARNINGS_ALLOWED := $(MODULES_USING_WNO_ERROR) $(LOCAL_MODULE_MAKEFILE):$(LOCAL_MODULE))\n          my_cflags := -Wall $(my_cflags)\n        endif\n      endif\n    endif\n  endif\nendif\n\nifneq (,$(filter -Weverything,$(my_all_cflags)))\n  ifeq (,$(ANDROID_TEMPORARILY_ALLOW_WEVERYTHING))\n    $(call pretty-error, -Weverything is not allowed in Android.mk files.\\\n      Build with `m ANDROID_TEMPORARILY_ALLOW_WEVERYTHING=true` to experiment locally with -Weverything.)\n  endif\nendif\n\nifneq ($(my_tidy_checks),)\n  tidy_only: $(cpp_objects) $(c_objects) $(gen_c_objects) $(gen_cpp_objects)\n\n  # Add dependency of clang-tidy and clang-tidy.sh\n  $(cpp_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY)\n  $(c_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY)\n  $(gen_cpp_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY)\n  $(gen_c_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY)\nendif\n\n# Move -l* entries from ldflags to ldlibs, and everything else to ldflags\nmy_ldlib_flags := $(my_ldflags) $(my_ldlibs)\nmy_ldlibs := $(filter -l%,$(my_ldlib_flags))\nmy_ldflags := $(filter-out -l%,$(my_ldlib_flags))\n\n# One last verification check for ldlibs\nmy_allowed_ldlibs :=\nifndef LOCAL_IS_HOST_MODULE\n  ifneq ($(LOCAL_SDK_VERSION),)\n    my_allowed_ldlibs := $(NDK_KNOWN_LIBS:lib%=-l%)\n  endif\nelse\n  my_allowed_ldlibs := $($(my_prefix)AVAILABLE_LIBRARIES)\nendif\n\nmy_bad_ldlibs := $(filter-out $(my_allowed_ldlibs),$(my_ldlibs))\nifneq ($(my_bad_ldlibs),)\n  $(error $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Bad LOCAL_LDLIBS entries: $(my_bad_ldlibs))\nendif\n\n# my_cxx_ldlibs may contain linker flags need to wrap certain libraries\n# (start-group/end-group), so append after the check above.\nmy_ldlibs += $(my_cxx_ldlibs)\n\n###########################################################\n## Define PRIVATE_ variables from global vars\n###########################################################\nifndef LOCAL_IS_HOST_MODULE\n\nmy_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS)\nmy_target_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CONLYFLAGS) $(my_c_std_conlyflags)\nmy_target_global_cppflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CPPFLAGS) $(my_cpp_std_cppflags)\nifeq ($(my_use_clang_lld),true)\n  my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LLDFLAGS)\n  include $(BUILD_SYSTEM)/pack_dyn_relocs_setup.mk\n  ifeq ($(my_pack_module_relocations),true)\n    my_target_global_ldflags += -Wl,--pack-dyn-relocs=android+relr -Wl,--use-android-relr-tags\n  else\n    my_target_global_ldflags += -Wl,--pack-dyn-relocs=none\n  endif\nelse\n  my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LDFLAGS)\nendif # my_use_clang_lld\n\nifeq ($(call module-in-vendor-or-product),true)\n  my_target_global_c_includes :=\n  my_target_global_c_system_includes := $(TARGET_OUT_HEADERS)\n  my_target_global_cflags += -nostdlibinc\nelse ifdef LOCAL_SDK_VERSION\n  my_target_global_c_includes :=\n  my_target_global_c_system_includes := $(my_ndk_stl_include_path)\n  my_target_global_cflags += --sysroot $(my_ndk_sysroot)\nelse\n  my_target_global_c_includes := $(SRC_HEADERS) \\\n    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)\n  my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \\\n    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES)\n  my_target_global_cflags += -nostdlibinc\nendif\n\nmy_target_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)TRIPLE)\nifndef LOCAL_IS_HOST_MODULE\n  my_target_triple_flag := -target $(my_target_triple)$(my_api_level)\nelse\n  my_target_triple_flag := -target $(my_target_triple)\nendif\nmy_asflags += $(my_target_triple_flag)\nmy_cflags += $(my_target_triple_flag)\nmy_ldflags += $(my_target_triple_flag)\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_INCLUDES := $(my_target_global_c_includes)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_SYSTEM_INCLUDES := $(my_target_global_c_system_includes)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CFLAGS := $(my_target_global_cflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CONLYFLAGS := $(my_target_global_conlyflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CPPFLAGS := $(my_target_global_cppflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_LDFLAGS := $(my_target_global_ldflags)\n\nelse # LOCAL_IS_HOST_MODULE\n\nmy_host_global_c_includes := $(SRC_HEADERS) \\\n    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)\nmy_host_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \\\n    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES)\n\nmy_host_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS)\nmy_host_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CONLYFLAGS) $(my_c_std_conlyflags)\nmy_host_global_cppflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CPPFLAGS) $(my_cpp_std_cppflags)\nifeq ($(my_use_clang_lld),true)\n  my_host_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LLDFLAGS)\nelse\n  my_host_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LDFLAGS)\nendif # my_use_clang_lld\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_INCLUDES := $(my_host_global_c_includes)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_SYSTEM_INCLUDES := $(my_host_global_c_system_includes)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CFLAGS := $(my_host_global_cflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CONLYFLAGS := $(my_host_global_conlyflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CPPFLAGS := $(my_host_global_cppflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_LDFLAGS := $(my_host_global_ldflags)\nendif # LOCAL_IS_HOST_MODULE\n\n# To enable coverage for a given module, set LOCAL_NATIVE_COVERAGE=true and\n# build with NATIVE_COVERAGE=true in your enviornment.\nifeq ($(NATIVE_COVERAGE),true)\n    ifeq ($(my_native_coverage),true)\n        # Note that clang coverage doesn't play nicely with acov out of the box.\n        # Clang apparently generates .gcno files that aren't compatible with\n        # gcov-4.8.  This can be solved by installing gcc-4.6 and invoking lcov\n        # with `--gcov-tool /usr/bin/gcov-4.6`.\n        #\n        # http://stackoverflow.com/questions/17758126/clang-code-coverage-invalid-output\n        my_cflags += --coverage -O0\n        my_ldflags += --coverage\n    endif\n\n    my_coverage_lib := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBPROFILE_RT)\n\n    $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_COVERAGE_LIB := $(my_coverage_lib)\n    $(LOCAL_INTERMEDIATE_TARGETS): $(my_coverage_lib)\nendif\n\n####################################################\n## Import includes\n####################################################\nimported_includes :=\n\nifeq (true,$(call module-in-vendor-or-product))\n  imported_includes += $(call intermediates-dir-for,HEADER_LIBRARIES,device_kernel_headers,$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))\nelse\n  # everything else should manually specify headers\nendif\n\nimported_includes := $(strip \\\n    $(imported_includes) \\\n    $(foreach l, $(installed_shared_library_module_names), \\\n      $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))) \\\n    $(foreach l, $(my_static_libraries) $(my_whole_static_libraries), \\\n      $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))) \\\n    $(foreach l, $(my_header_libraries), \\\n      $(call intermediates-dir-for,HEADER_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))))\n\n$(foreach dep,$(imported_includes),\\\n  $(eval EXPORTS.$$(dep).USERS := $$(EXPORTS.$$(dep).USERS) $$(all_objects)))\n\n###########################################################\n## Define PRIVATE_ variables used by multiple module types\n###########################################################\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_NO_DEFAULT_COMPILER_FLAGS := \\\n    $(strip $(LOCAL_NO_DEFAULT_COMPILER_FLAGS))\n\nifeq ($(strip $(WITH_STATIC_ANALYZER)),)\n  LOCAL_NO_STATIC_ANALYZER := true\nendif\n\nifneq ($(strip $(LOCAL_IS_HOST_MODULE)),)\n  my_syntax_arch := host\nelse\n  my_syntax_arch := $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)\nendif\n\nifeq ($(strip $(my_cc)),)\n  my_cc := $(my_cc_wrapper) $(CLANG)\nendif\n\nSYNTAX_TOOLS_PREFIX := \\\n    $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/libexec\n\nifneq ($(LOCAL_NO_STATIC_ANALYZER),true)\n  my_cc := CCC_CC=$(CLANG) CLANG=$(CLANG) \\\n           $(SYNTAX_TOOLS_PREFIX)/ccc-analyzer\nendif\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CC := $(my_cc)\n\nifeq ($(strip $(my_cxx)),)\n  my_cxx := $(my_cxx_wrapper) $(CLANG_CXX)\nendif\n\nifeq ($(strip $(my_cxx_link)),)\n  my_cxx_link := $(CLANG_CXX)\nendif\n\nifneq ($(LOCAL_NO_STATIC_ANALYZER),true)\n  my_cxx := CCC_CXX=$(CLANG_CXX) CLANG_CXX=$(CLANG_CXX) \\\n            $(SYNTAX_TOOLS_PREFIX)/c++-analyzer\n  my_cxx_link := CCC_CXX=$(CLANG_CXX) CLANG_CXX=$(CLANG_CXX) \\\n                 $(SYNTAX_TOOLS_PREFIX)/c++-analyzer\nendif\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LINKER := $(my_linker)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CXX := $(my_cxx)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CXX_LINK := $(my_cxx_link)\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_YACCFLAGS := $(LOCAL_YACCFLAGS)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASFLAGS := $(my_asflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CONLYFLAGS := $(my_conlyflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CFLAGS := $(my_cflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CPPFLAGS := $(my_cppflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CFLAGS_NO_OVERRIDE := $(my_cflags_no_override)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CPPFLAGS_NO_OVERRIDE := $(my_cppflags_no_override)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RTTI_FLAG := $(LOCAL_RTTI_FLAG)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_DEBUG_CFLAGS := $(debug_cflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_C_INCLUDES := $(my_c_includes)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_IMPORTED_INCLUDES := $(imported_includes)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDFLAGS := $(my_ldflags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDLIBS := $(my_ldlibs)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TIDY_CHECKS := $(my_tidy_checks)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TIDY_FLAGS := $(my_tidy_flags)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ARFLAGS := $(my_arflags)\n\n# this is really the way to get the files onto the command line instead\n# of using $^, because then LOCAL_ADDITIONAL_DEPENDENCIES doesn't work\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_SHARED_LIBRARIES := $(built_shared_libraries)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_STATIC_LIBRARIES := $(built_static_libraries)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(built_whole_libraries)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_OBJECTS := $(strip $(all_objects))\n\n###########################################################\n# Define library dependencies.\n###########################################################\n# all_libraries is used for the dependencies on LOCAL_BUILT_MODULE.\nall_libraries := \\\n    $(built_shared_library_deps) \\\n    $(my_system_shared_libraries_fullpath) \\\n    $(built_static_libraries) \\\n    $(built_whole_libraries)\n\n###########################################################\n# Export includes\n###########################################################\n\n# Headers exported by whole static libraries are also exported by this library.\nexport_include_deps := $(strip \\\n   $(foreach l,$(my_whole_static_libraries), \\\n     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))))\n# Re-export requested headers from shared libraries.\nexport_include_deps += $(strip \\\n   $(foreach l,$(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS), \\\n     $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))))\n# Re-export requested headers from static libraries.\nexport_include_deps += $(strip \\\n   $(foreach l,$(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS), \\\n     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))))\n# Re-export requested headers from header libraries.\nexport_include_deps += $(strip \\\n   $(foreach l,$(LOCAL_EXPORT_HEADER_LIBRARY_HEADERS), \\\n     $(call intermediates-dir-for,HEADER_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))))\n\nifneq ($(strip $(my_export_c_include_dirs)$(export_include_deps)),)\n  EXPORTS_LIST += $(intermediates)\n  EXPORTS.$(intermediates).FLAGS := $(foreach d,$(my_export_c_include_dirs),-I $(call clean-path,$(d)))\n  EXPORTS.$(intermediates).REEXPORT := $(export_include_deps)\n  EXPORTS.$(intermediates).DEPS := $(my_export_c_include_deps) $(my_generated_sources) $(LOCAL_EXPORT_C_INCLUDE_DEPS)\nendif\n\nifneq (,$(filter-out $(LOCAL_PATH)/%,$(my_export_c_include_dirs)))\nmy_soong_problems += non_local__export_c_include_dirs\nendif\n\nSOONG_CONV.$(LOCAL_MODULE).PROBLEMS := \\\n    $(SOONG_CONV.$(LOCAL_MODULE).PROBLEMS) $(my_soong_problems)\nSOONG_CONV.$(LOCAL_MODULE).DEPS := \\\n    $(SOONG_CONV.$(LOCAL_MODULE).DEPS) \\\n    $(filter-out $($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_RUNTIME_LIBRARY),\\\n        $(my_static_libraries) \\\n        $(my_whole_static_libraries) \\\n        $(my_shared_libraries) \\\n        $(my_system_shared_libraries))\nSOONG_CONV.$(LOCAL_MODULE).TYPE := native\nSOONG_CONV.$(LOCAL_MODULE).MAKEFILES := \\\n    $(SOONG_CONV.$(LOCAL_MODULE).MAKEFILES) $(LOCAL_MODULE_MAKEFILE)\nSOONG_CONV.$(LOCAL_MODULE).INSTALLED:= \\\n    $(SOONG_CONV.$(LOCAL_MODULE).INSTALLED) $(LOCAL_INSTALLED_MODULE)\nSOONG_CONV := $(SOONG_CONV) $(LOCAL_MODULE)\n\n###########################################################\n# Coverage packaging.\n###########################################################\nifeq ($(my_native_coverage),true)\nmy_gcno_objects := \\\n    $(cpp_objects) \\\n    $(gen_cpp_objects) \\\n    $(c_objects) \\\n    $(gen_c_objects) \\\n    $(objc_objects) \\\n    $(objcpp_objects)\n\nLOCAL_GCNO_FILES := $(patsubst %.o,%.gcno,$(my_gcno_objects))\n$(foreach f,$(my_gcno_objects),$(eval $(call gcno-touch-rule,$(f),$(f:.o=.gcno))))\nendif\n"
  },
  {
    "path": "core/board_config.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# ###############################################################\n# This file includes BoardConfig.mk for the device being built,\n# and checks the variable defined therein.\n# ###############################################################\n\n_board_strip_readonly_list :=\n_board_strip_readonly_list += BOARD_BOOTLOADER_IN_UPDATE_PACKAGE\n_board_strip_readonly_list += BOARD_EGL_CFG\n_board_strip_readonly_list += BOARD_HAVE_BLUETOOTH\n_board_strip_readonly_list += BOARD_INSTALLER_CMDLINE\n_board_strip_readonly_list += BOARD_KERNEL_CMDLINE\n_board_strip_readonly_list += BOARD_BOOT_HEADER_VERSION\n_board_strip_readonly_list += BOARD_BOOTCONFIG\n_board_strip_readonly_list += BOARD_BOOTCONFIG_FILE\n_board_strip_readonly_list += BOARD_KERNEL_BASE\n_board_strip_readonly_list += BOARD_USES_GENERIC_AUDIO\n_board_strip_readonly_list += BOARD_USES_RECOVERY_AS_BOOT\n_board_strip_readonly_list += BOARD_VENDOR_USE_AKMD\n_board_strip_readonly_list += BOARD_WPA_SUPPLICANT_DRIVER\n_board_strip_readonly_list += BOARD_WLAN_DEVICE\n_board_strip_readonly_list += TARGET_BOARD_PLATFORM\n_board_strip_readonly_list += TARGET_BOARD_PLATFORM_GPU\n_board_strip_readonly_list += TARGET_BOOTLOADER_BOARD_NAME\n_board_strip_readonly_list += TARGET_FS_CONFIG_GEN\n_board_strip_readonly_list += TARGET_NO_BOOTLOADER\n_board_strip_readonly_list += TARGET_NO_KERNEL\n_board_strip_readonly_list += TARGET_NO_RECOVERY\n_board_strip_readonly_list += TARGET_NO_RADIOIMAGE\n_board_strip_readonly_list += TARGET_HARDWARE_3D\n_board_strip_readonly_list += WITH_DEXPREOPT\n\n# Arch variables\n_board_strip_readonly_list += TARGET_ARCH\n_board_strip_readonly_list += TARGET_ARCH_VARIANT\n_board_strip_readonly_list += TARGET_CPU_ABI\n_board_strip_readonly_list += TARGET_CPU_ABI2\n_board_strip_readonly_list += TARGET_CPU_VARIANT\n_board_strip_readonly_list += TARGET_CPU_VARIANT_RUNTIME\n_board_strip_readonly_list += TARGET_2ND_ARCH\n_board_strip_readonly_list += TARGET_2ND_ARCH_VARIANT\n_board_strip_readonly_list += TARGET_2ND_CPU_ABI\n_board_strip_readonly_list += TARGET_2ND_CPU_ABI2\n_board_strip_readonly_list += TARGET_2ND_CPU_VARIANT\n_board_strip_readonly_list += TARGET_2ND_CPU_VARIANT_RUNTIME\n# TARGET_ARCH_SUITE is an alternative arch configuration to TARGET_ARCH (and related variables),\n# that can be used for soong-only builds to build for several architectures at once.\n# Allowed values currently are \"ndk\" and \"mainline_sdk\".\n_board_strip_readonly_list += TARGET_ARCH_SUITE\n\n# File system variables\n_board_strip_readonly_list += BOARD_FLASH_BLOCK_SIZE\n_board_strip_readonly_list += BOARD_BOOTIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_RECOVERYIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_SYSTEMIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE\n_board_strip_readonly_list += BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE\n_board_strip_readonly_list += BOARD_USERDATAIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE\n_board_strip_readonly_list += BOARD_CACHEIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_VENDORIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE\n_board_strip_readonly_list += BOARD_PRODUCTIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE\n_board_strip_readonly_list += BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE\n_board_strip_readonly_list += BOARD_ODMIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_ODMIMAGE_FILE_SYSTEM_TYPE\n_board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE\n_board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE\n_board_strip_readonly_list += BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE\n_board_strip_readonly_list += BOARD_PVMFWIMAGE_PARTITION_SIZE\n\n# Logical partitions related variables.\n_board_strip_readonly_list += BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE\n_board_strip_readonly_list += BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE\n_board_strip_readonly_list += BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE\n_board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE\n_board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE\n_board_strip_readonly_list += BOARD_SYSTEM_DLKMIMAGE_PARTITION_RESERVED_SIZE\n_board_strip_readonly_list += BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE\n_board_strip_readonly_list += BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE\n_board_strip_readonly_list += BOARD_SUPER_PARTITION_SIZE\n_board_strip_readonly_list += BOARD_SUPER_PARTITION_GROUPS\n\n# Kernel related variables\n_board_strip_readonly_list += BOARD_KERNEL_BINARIES\n_board_strip_readonly_list += BOARD_KERNEL_MODULE_INTERFACE_VERSIONS\n\n# Variables related to generic kernel image (GKI) and generic boot image\n# - BOARD_USES_GENERIC_KERNEL_IMAGE is the global variable that defines if the\n#   board uses GKI and generic boot image.\n#   Update mechanism of the boot image is not enforced by this variable.\n# - BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE controls whether the recovery image\n#   contains a kernel or not.\n# - BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT controls whether ramdisk\n#   recovery resources are built to vendor_boot.\n# - BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT controls whether recovery\n#   resources are built as a standalone recovery ramdisk in vendor_boot.\n# - BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT controls whether GSI AVB keys are\n#   built to vendor_boot.\n# - BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES controls whether boot images in $OUT are added\n#   to target files package directly.\n_board_strip_readonly_list += BOARD_USES_GENERIC_KERNEL_IMAGE\n_board_strip_readonly_list += BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE\n_board_strip_readonly_list += BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT\n_board_strip_readonly_list += BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT\n_board_strip_readonly_list += BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT\n_board_strip_readonly_list += BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES\n\n# Prebuilt image variables\n_board_strip_readonly_list += BOARD_PREBUILT_INIT_BOOT_IMAGE\n\n# Defines the list of logical vendor ramdisk names to build or include in vendor_boot.\n_board_strip_readonly_list += BOARD_VENDOR_RAMDISK_FRAGMENTS\n\n# These are all variables used to build $(INSTALLED_MISC_INFO_TARGET)\n# in build/make/core/Makefile. Their values get used in command line\n# arguments, so they have to be stripped to make the ninja files stable.\n_board_strip_list :=\n_board_strip_list += BOARD_DTBOIMG_PARTITION_SIZE\n_board_strip_list += BOARD_AVB_DTBO_KEY_PATH\n_board_strip_list += BOARD_AVB_DTBO_ALGORITHM\n_board_strip_list += BOARD_AVB_DTBO_ROLLBACK_INDEX_LOCATION\n_board_strip_list += BOARD_AVB_PVMFW_KEY_PATH\n_board_strip_list += BOARD_AVB_PVMFW_ALGORITHM\n_board_strip_list += BOARD_AVB_PVMFW_ROLLBACK_INDEX_LOCATION\n_board_strip_list += BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST\n_board_strip_list += BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION\n_board_strip_list += BOARD_AVB_VBMETA_VENDOR_ALGORITHM\n_board_strip_list += BOARD_AVB_VBMETA_VENDOR_KEY_PATH\n_board_strip_list += BOARD_AVB_VBMETA_VENDOR\n_board_strip_list += BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION\n_board_strip_list += BOARD_AVB_VBMETA_SYSTEM_ALGORITHM\n_board_strip_list += BOARD_AVB_VBMETA_SYSTEM_KEY_PATH\n_board_strip_list += BOARD_AVB_VBMETA_SYSTEM\n_board_strip_list += BOARD_AVB_RECOVERY_KEY_PATH\n_board_strip_list += BOARD_AVB_RECOVERY_ALGORITHM\n_board_strip_list += BOARD_AVB_RECOVERY_ROLLBACK_INDEX_LOCATION\n_board_strip_list += BOARD_AVB_VENDOR_BOOT_KEY_PATH\n_board_strip_list += BOARD_AVB_VENDOR_BOOT_ALGORITHM\n_board_strip_list += BOARD_AVB_VENDOR_BOOT_ROLLBACK_INDEX_LOCATION\n_board_strip_list += BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH\n_board_strip_list += BOARD_AVB_VENDOR_KERNEL_BOOT_ALGORITHM\n_board_strip_list += BOARD_AVB_VENDOR_KERNEL_BOOT_ROLLBACK_INDEX_LOCATION\n_board_strip_list += BOARD_MKBOOTIMG_ARGS\n_board_strip_list += BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE\n_board_strip_list += BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE\n_board_strip_list += ODM_MANIFEST_SKUS\n\n\n_build_broken_var_list := \\\n  BUILD_BROKEN_CLANG_PROPERTY \\\n  BUILD_BROKEN_CLANG_ASFLAGS \\\n  BUILD_BROKEN_CLANG_CFLAGS \\\n  BUILD_BROKEN_DEPFILE \\\n  BUILD_BROKEN_DUP_RULES \\\n  BUILD_BROKEN_DUP_SYSPROP \\\n  BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES \\\n  BUILD_BROKEN_ENFORCE_SYSPROP_OWNER \\\n  BUILD_BROKEN_INPUT_DIR_MODULES \\\n  BUILD_BROKEN_MISSING_REQUIRED_MODULES \\\n  BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS \\\n  BUILD_BROKEN_PREBUILT_ELF_FILES \\\n  BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW \\\n  BUILD_BROKEN_USES_NETWORK \\\n  BUILD_BROKEN_VENDOR_PROPERTY_NAMESPACE \\\n  BUILD_BROKEN_VINTF_PRODUCT_COPY_FILES \\\n  BUILD_BROKEN_INCORRECT_PARTITION_IMAGES \\\n  BUILD_BROKEN_GENRULE_SANDBOXING \\\n  BUILD_BROKEN_DONT_CHECK_SYSTEMSDK \\\n\n_build_broken_var_list += \\\n  $(foreach m,$(AVAILABLE_BUILD_MODULE_TYPES) \\\n              $(DEFAULT_WARNING_BUILD_MODULE_TYPES) \\\n              $(DEFAULT_ERROR_BUILD_MODULE_TYPES), \\\n    BUILD_BROKEN_USES_$(m))\n\n_board_true_false_vars := $(_build_broken_var_list)\n_board_strip_readonly_list += $(_build_broken_var_list) \\\n  BUILD_BROKEN_NINJA_USES_ENV_VARS\n\n# Conditional to building on linux, as dex2oat currently does not work on darwin.\nifeq ($(HOST_OS),linux)\n  WITH_DEXPREOPT ?= true\nendif\n\n# ###############################################################\n# Broken build defaults\n# ###############################################################\n$(foreach v,$(_build_broken_var_list),$(eval $(v) :=))\nBUILD_BROKEN_NINJA_USES_ENV_VARS :=\n\n# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)\n# or under vendor/*/$(TARGET_DEVICE).  Search in both places, but\n# make sure only one exists.\n# Real boards should always be associated with an OEM vendor.\nifdef TARGET_DEVICE_DIR\n  ifneq ($(origin TARGET_DEVICE_DIR),command line)\n    $(error TARGET_DEVICE_DIR may not be set manually)\n  endif\n  board_config_mk := $(TARGET_DEVICE_DIR)/BoardConfig.mk\nelse\n  board_config_mk := \\\n    $(strip $(sort $(wildcard \\\n      $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \\\n      device/generic/goldfish/board/$(TARGET_DEVICE)/BoardConfig.mk \\\n      device/google/cuttlefish/board/$(TARGET_DEVICE)/BoardConfig.mk \\\n      vendor/google/products/cuttlefish/pixel_watch/board/$(TARGET_DEVICE)/BoardConfig.mk \\\n      $(shell test -d device && find -L device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \\\n      $(shell test -d vendor && find -L vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \\\n    )))\n  ifeq ($(board_config_mk),)\n    $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))\n  endif\n  ifneq ($(words $(board_config_mk)),1)\n    $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))\n  endif\n  TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))\n  .KATI_READONLY := TARGET_DEVICE_DIR\nendif\n\n$(call dump-phase-start,BOARD,,,, build/make/core/board_config.mk)\nifndef RBC_PRODUCT_CONFIG\ninclude $(board_config_mk)\nelse\n  $(shell mkdir -p $(OUT_DIR)/rbc)\n  $(call dump-variables-rbc, $(OUT_DIR)/rbc/make_vars_pre_board_config.mk)\n\n  $(shell $(OUT_DIR)/mk2rbc \\\n    --mode=write -r --outdir $(OUT_DIR)/rbc \\\n    --boardlauncher=$(OUT_DIR)/rbc/boardlauncher.rbc \\\n    --input_variables=$(OUT_DIR)/rbc/make_vars_pre_board_config.mk \\\n    --makefile_list=$(OUT_DIR)/.module_paths/configuration.list \\\n    $(board_config_mk))\n  ifneq ($(.SHELLSTATUS),0)\n    $(error board configuration converter failed: $(.SHELLSTATUS))\n  endif\n\n  $(shell build/soong/scripts/update_out $(OUT_DIR)/rbc/rbc_board_config_results.mk \\\n    $(OUT_DIR)/rbcrun --mode=rbc $(OUT_DIR)/rbc/boardlauncher.rbc)\n  ifneq ($(.SHELLSTATUS),0)\n    $(error board configuration runner failed: $(.SHELLSTATUS))\n  endif\n\n  include $(OUT_DIR)/rbc/rbc_board_config_results.mk\nendif\n$(call dump-phase-end, build/make/core/board_config.mk)\n\nifneq (,$(and $(TARGET_ARCH),$(TARGET_ARCH_SUITE)))\n  $(error $(board_config_mk) erroneously sets both TARGET_ARCH and TARGET_ARCH_SUITE)\nendif\nifeq ($(TARGET_ARCH)$(TARGET_ARCH_SUITE),)\n  $(error Target architectures not defined by board config: $(board_config_mk))\nendif\nifeq ($(TARGET_CPU_ABI)$(TARGET_ARCH_SUITE),)\n  $(error TARGET_CPU_ABI not defined by board config: $(board_config_mk))\nendif\n\nifneq ($(MALLOC_IMPL),)\n  $(warning *** Unsupported option MALLOC_IMPL defined by board config: $(board_config_mk).)\n  $(error Use `MALLOC_LOW_MEMORY := true` to use low-memory allocator config)\nendif\nboard_config_mk :=\n\n# Clean up and verify BoardConfig variables\n$(foreach var,$(_board_strip_readonly_list),$(eval $(var) := $$(strip $$($(var)))))\n$(foreach var,$(_board_strip_list),$(eval $(var) := $$(strip $$($(var)))))\n$(foreach var,$(_board_true_false_vars), \\\n  $(if $(filter-out true false,$($(var))), \\\n    $(error Valid values of $(var) are \"true\", \"false\", and \"\". Not \"$($(var))\")))\n\ninclude $(BUILD_SYSTEM)/board_config_wifi.mk\ninclude $(BUILD_SYSTEM)/board_config_wpa_supplicant.mk\n\n# Set up soong config for \"soong_config_value_variable\".\n-include hardware/interfaces/configstore/1.1/default/surfaceflinger.mk\n-include vendor/google/build/soong/soong_config_namespace/camera.mk\n\n# Default *_CPU_VARIANT_RUNTIME to CPU_VARIANT if unspecified.\nTARGET_CPU_VARIANT_RUNTIME := $(or $(TARGET_CPU_VARIANT_RUNTIME),$(TARGET_CPU_VARIANT))\nTARGET_2ND_CPU_VARIANT_RUNTIME := $(or $(TARGET_2ND_CPU_VARIANT_RUNTIME),$(TARGET_2ND_CPU_VARIANT))\n\nifdef TARGET_ARCH\n  # The combo makefiles check and set defaults for various CPU configuration\n  combo_target := TARGET_\n  combo_2nd_arch_prefix :=\n  include $(BUILD_SYSTEM)/combo/select.mk\nendif\n\nifdef TARGET_2ND_ARCH\n  combo_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX)\n  include $(BUILD_SYSTEM)/combo/select.mk\nendif\n\n.KATI_READONLY := $(_board_strip_readonly_list)\n\nINTERNAL_KERNEL_CMDLINE := $(BOARD_KERNEL_CMDLINE)\nifneq (,$(BOARD_BOOTCONFIG)$(BOARD_BOOTCONFIG_FILE))\n  INTERNAL_KERNEL_CMDLINE += bootconfig\n  INTERNAL_BOOTCONFIG := $(BOARD_BOOTCONFIG)\n  INTERNAL_BOOTCONFIG_FILE := $(BOARD_BOOTCONFIG_FILE)\nendif\n\nifneq ($(filter %64,$(TARGET_ARCH)),)\n  TARGET_IS_64_BIT := true\nendif\n\nifeq (,$(filter true,$(TARGET_SUPPORTS_32_BIT_APPS) $(TARGET_SUPPORTS_64_BIT_APPS)))\n  TARGET_SUPPORTS_32_BIT_APPS := true\nendif\n\n# Quick check to warn about likely cryptic errors later in the build.\nifeq ($(TARGET_IS_64_BIT),true)\n  ifeq (,$(filter true false,$(TARGET_SUPPORTS_64_BIT_APPS)))\n    $(error Building a 32-bit-app-only product on a 64-bit device. \\\n      If this is intentional, set TARGET_SUPPORTS_64_BIT_APPS := false)\n  endif\nendif\n\n# \"ro.product.cpu.abilist32\" and \"ro.product.cpu.abilist64\" are\n# comma separated lists of the 32 and 64 bit ABIs (in order of\n# preference) that the target supports. If TARGET_CPU_ABI_LIST_{32,64}_BIT\n# are defined by the board config, we use them. Else, we construct\n# these lists based on whether TARGET_IS_64_BIT is set.\n#\n# Note that this assumes that the 2ND_CPU_ABI for a 64 bit target\n# is always 32 bits. If this isn't the case, these variables should\n# be overriden in the board configuration.\n#\n# Similarly, TARGET_NATIVE_BRIDGE_2ND_ABI for a 64 bit target is always\n# 32 bits. Note that all CPU_ABIs are preferred over all NATIVE_BRIDGE_ABIs.\n_target_native_bridge_abi_list_32_bit :=\n_target_native_bridge_abi_list_64_bit :=\n\nifeq (,$(TARGET_CPU_ABI_LIST_64_BIT))\n  ifeq (true|true,$(TARGET_IS_64_BIT)|$(TARGET_SUPPORTS_64_BIT_APPS))\n    TARGET_CPU_ABI_LIST_64_BIT := $(TARGET_CPU_ABI) $(TARGET_CPU_ABI2)\n    _target_native_bridge_abi_list_64_bit := $(TARGET_NATIVE_BRIDGE_ABI)\n  endif\nendif\n\n# \"arm64-v8a-hwasan\", the ABI for libraries compiled with HWASAN, is supported\n# in all builds with SANITIZE_TARGET=hwaddress.\nifneq ($(filter hwaddress,$(SANITIZE_TARGET)),)\n  ifneq ($(filter arm64-v8a,$(TARGET_CPU_ABI_LIST_64_BIT)),)\n    TARGET_CPU_ABI_LIST_64_BIT := arm64-v8a-hwasan $(TARGET_CPU_ABI_LIST_64_BIT)\n  endif\nendif\n\nifeq (,$(TARGET_CPU_ABI_LIST_32_BIT))\n  ifneq (true,$(TARGET_IS_64_BIT))\n    TARGET_CPU_ABI_LIST_32_BIT := $(TARGET_CPU_ABI) $(TARGET_CPU_ABI2)\n    _target_native_bridge_abi_list_32_bit := $(TARGET_NATIVE_BRIDGE_ABI)\n  else\n    ifeq (true,$(TARGET_SUPPORTS_32_BIT_APPS))\n      # For a 64 bit target, assume that the 2ND_CPU_ABI\n      # is a 32 bit ABI.\n      TARGET_CPU_ABI_LIST_32_BIT := $(TARGET_2ND_CPU_ABI) $(TARGET_2ND_CPU_ABI2)\n      _target_native_bridge_abi_list_32_bit := $(TARGET_NATIVE_BRIDGE_2ND_ABI)\n    endif\n  endif\nendif\n\n# \"ro.product.cpu.abilist\" is a comma separated list of ABIs (in order\n# of preference) that the target supports. If a TARGET_CPU_ABI_LIST\n# is specified by the board configuration, we use that. If not, we\n# build a list out of the TARGET_CPU_ABIs specified by the config.\n# Add NATIVE_BRIDGE_ABIs at the end to keep order of preference.\nifeq (,$(TARGET_CPU_ABI_LIST))\n  TARGET_CPU_ABI_LIST := $(TARGET_CPU_ABI_LIST_64_BIT) $(TARGET_CPU_ABI_LIST_32_BIT) \\\n                         $(_target_native_bridge_abi_list_64_bit) $(_target_native_bridge_abi_list_32_bit)\nendif\n\n# Add NATIVE_BRIDGE_ABIs at the end of 32 and 64 bit CPU_ABIs to keep order of preference.\nTARGET_CPU_ABI_LIST_32_BIT += $(_target_native_bridge_abi_list_32_bit)\nTARGET_CPU_ABI_LIST_64_BIT += $(_target_native_bridge_abi_list_64_bit)\n\n# Strip whitespace from the ABI list string.\nTARGET_CPU_ABI_LIST := $(subst $(space),$(comma),$(strip $(TARGET_CPU_ABI_LIST)))\nTARGET_CPU_ABI_LIST_32_BIT := $(subst $(space),$(comma),$(strip $(TARGET_CPU_ABI_LIST_32_BIT)))\nTARGET_CPU_ABI_LIST_64_BIT := $(subst $(space),$(comma),$(strip $(TARGET_CPU_ABI_LIST_64_BIT)))\n\n# Check if config about image building is valid or not.\ndefine check_image_config\n  $(eval _uc_name := $(call to-upper,$(1))) \\\n  $(eval _lc_name := $(call to-lower,$(1))) \\\n  $(if $(filter $(_lc_name),$(TARGET_COPY_OUT_$(_uc_name))), \\\n    $(if $(BOARD_USES_$(_uc_name)IMAGE),, \\\n      $(error If TARGET_COPY_OUT_$(_uc_name) is '$(_lc_name)', either BOARD_PREBUILT_$(_uc_name)IMAGE or BOARD_$(_uc_name)IMAGE_FILE_SYSTEM_TYPE must be set)), \\\n  $(if $(BOARD_USES_$(_uc_name)IMAGE), \\\n    $(error TARGET_COPY_OUT_$(_uc_name) must be set to '$(_lc_name)' to use a $(_lc_name) image))) \\\n  $(eval _uc_name :=) \\\n  $(eval _lc_name :=)\nendef\n\n###########################################\n# Configure whether we're building the system image\nBUILDING_SYSTEM_IMAGE := true\nifeq ($(PRODUCT_BUILD_SYSTEM_IMAGE),)\n  ifndef PRODUCT_USE_DYNAMIC_PARTITION_SIZE\n    ifndef BOARD_SYSTEMIMAGE_PARTITION_SIZE\n      BUILDING_SYSTEM_IMAGE :=\n    endif\n  endif\nelse ifeq ($(PRODUCT_BUILD_SYSTEM_IMAGE),false)\n  BUILDING_SYSTEM_IMAGE :=\nendif\n.KATI_READONLY := BUILDING_SYSTEM_IMAGE\n\n# Are we building a system_other image\nBUILDING_SYSTEM_OTHER_IMAGE :=\nifeq ($(PRODUCT_BUILD_SYSTEM_OTHER_IMAGE),)\n  ifdef BUILDING_SYSTEM_IMAGE\n    ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true)\n      BUILDING_SYSTEM_OTHER_IMAGE := true\n    endif\n  endif\nelse ifeq ($(PRODUCT_BUILD_SYSTEM_OTHER_IMAGE),true)\n  BUILDING_SYSTEM_OTHER_IMAGE := true\n  ifndef BUILDING_SYSTEM_IMAGE\n    $(error PRODUCT_BUILD_SYSTEM_OTHER_IMAGE = true requires building the system image)\n  endif\nendif\n.KATI_READONLY := BUILDING_SYSTEM_OTHER_IMAGE\n\n# Are we building a cache image\nBUILDING_CACHE_IMAGE :=\nifeq ($(PRODUCT_BUILD_CACHE_IMAGE),)\n  ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE\n    BUILDING_CACHE_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_CACHE_IMAGE),true)\n  BUILDING_CACHE_IMAGE := true\n  ifndef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE\n    $(error PRODUCT_BUILD_CACHE_IMAGE set to true, but BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE not defined)\n  endif\nendif\n.KATI_READONLY := BUILDING_CACHE_IMAGE\n\n# Are we building a boot image\nBUILDING_BOOT_IMAGE :=\nifeq ($(PRODUCT_BUILD_BOOT_IMAGE),)\n  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n    BUILDING_BOOT_IMAGE :=\n  else ifdef BOARD_PREBUILT_BOOTIMAGE\n    BUILDING_BOOT_IMAGE :=\n  else ifdef BOARD_BOOTIMAGE_PARTITION_SIZE\n    BUILDING_BOOT_IMAGE := true\n  else ifneq (,$(foreach kernel,$(BOARD_KERNEL_BINARIES),$(BOARD_$(call to-upper,$(kernel))_BOOTIMAGE_PARTITION_SIZE)))\n    BUILDING_BOOT_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_BOOT_IMAGE),true)\n  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n    $(warning *** PRODUCT_BUILD_BOOT_IMAGE is true, but so is BOARD_USES_RECOVERY_AS_BOOT.)\n    $(warning *** Skipping building boot image.)\n    BUILDING_BOOT_IMAGE :=\n  else\n    BUILDING_BOOT_IMAGE := true\n  endif\nendif\n.KATI_READONLY := BUILDING_BOOT_IMAGE\n\n# Are we building an init boot image\nBUILDING_INIT_BOOT_IMAGE :=\nifeq ($(PRODUCT_BUILD_INIT_BOOT_IMAGE),)\n  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n    BUILDING_INIT_BOOT_IMAGE :=\n  else ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE\n    BUILDING_INIT_BOOT_IMAGE :=\n  else ifdef BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE\n    BUILDING_INIT_BOOT_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_INIT_BOOT_IMAGE),true)\n  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n    $(error PRODUCT_BUILD_INIT_BOOT_IMAGE is true, but so is BOARD_USES_RECOVERY_AS_BOOT. Use only one option.)\n  else\n    BUILDING_INIT_BOOT_IMAGE := true\n  endif\nendif\n.KATI_READONLY := BUILDING_INIT_BOOT_IMAGE\n\n# Are we building a recovery image\nBUILDING_RECOVERY_IMAGE :=\nifeq ($(PRODUCT_BUILD_RECOVERY_IMAGE),)\n  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n    BUILDING_RECOVERY_IMAGE := true\n  else ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true)\n    # Set to true to build recovery resources for vendor_boot\n    BUILDING_RECOVERY_IMAGE := true\n  else ifdef BOARD_RECOVERYIMAGE_PARTITION_SIZE\n    ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))\n      BUILDING_RECOVERY_IMAGE := true\n    endif\n  endif\nelse ifeq ($(PRODUCT_BUILD_RECOVERY_IMAGE),true)\n  BUILDING_RECOVERY_IMAGE := true\nendif\n.KATI_READONLY := BUILDING_RECOVERY_IMAGE\n\n# Are we building a vendor boot image\nBUILDING_VENDOR_BOOT_IMAGE :=\nifdef BOARD_BOOT_HEADER_VERSION\n  ifneq ($(call math_gt_or_eq,$(BOARD_BOOT_HEADER_VERSION),3),)\n    ifeq ($(PRODUCT_BUILD_VENDOR_BOOT_IMAGE),)\n      BUILDING_VENDOR_BOOT_IMAGE := true\n    else ifeq ($(PRODUCT_BUILD_VENDOR_BOOT_IMAGE),true)\n      BUILDING_VENDOR_BOOT_IMAGE := true\n    endif\n  endif\nendif\n.KATI_READONLY := BUILDING_VENDOR_BOOT_IMAGE\n\n# Are we building a vendor kernel boot image\nBUILDING_VENDOR_KERNEL_BOOT_IMAGE :=\nifeq ($(PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE),true)\n  ifneq ($(BUILDING_VENDOR_BOOT_IMAGE),true)\n    $(error BUILDING_VENDOR_BOOT_IMAGE is required, but BUILDING_VENDOR_BOOT_IMAGE is not true)\n  endif\n  ifndef BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE\n    $(error BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE is required when PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE is true)\n  endif\n  BUILDING_VENDOR_KERNEL_BOOT_IMAGE := true\nelse ifeq ($(PRODUCT_BUILD_VENDOR_KERNEL),)\n  ifdef BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE\n    ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true)\n      BUILDING_VENDOR_KERNEL_BOOT_IMAGE := true\n    endif\n  endif\nendif # end of PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE\n.KATI_READONLY := BUILDING_VENDOR_KERNEL_BOOT_IMAGE\n\n# Are we building a ramdisk image\nBUILDING_RAMDISK_IMAGE := true\nifeq ($(PRODUCT_BUILD_RAMDISK_IMAGE),)\n  # TODO: Be smarter about this. This probably only needs to happen when one of the follow is true:\n  #  BUILDING_BOOT_IMAGE\n  #  BUILDING_RECOVERY_IMAGE\nelse ifeq ($(PRODUCT_BUILD_RAMDISK_IMAGE),false)\n  BUILDING_RAMDISK_IMAGE :=\nendif\n.KATI_READONLY := BUILDING_RAMDISK_IMAGE\n\n# Are we building a debug vendor_boot image\nBUILDING_DEBUG_VENDOR_BOOT_IMAGE :=\n# Can't build vendor_boot-debug.img if we're not building a ramdisk.\nifndef BUILDING_RAMDISK_IMAGE\n  ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)\n    $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but we're not building a ramdisk image. \\\n      Skip building the debug vendor_boot image.)\n  endif\n# Can't build vendor_boot-debug.img if we're not building a vendor_boot.img.\nelse ifndef BUILDING_VENDOR_BOOT_IMAGE\n  ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)\n    $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but we're not building a vendor_boot image. \\\n      Skip building the debug vendor_boot image.)\n  endif\nelse\n  ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),)\n    BUILDING_DEBUG_VENDOR_BOOT_IMAGE := true\n  else ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)\n    BUILDING_DEBUG_VENDOR_BOOT_IMAGE := true\n  endif\nendif\n.KATI_READONLY := BUILDING_DEBUG_VENDOR_BOOT_IMAGE\n\n_has_boot_img_artifact :=\nifneq ($(strip $(TARGET_NO_KERNEL)),true)\n  ifdef BUILDING_BOOT_IMAGE\n    _has_boot_img_artifact := true\n  endif\n  # BUILDING_RECOVERY_IMAGE && BOARD_USES_RECOVERY_AS_BOOT implies that\n  # recovery is being built with the file name *boot.img*, which still counts\n  # as \"building boot.img\".\n  ifdef BUILDING_RECOVERY_IMAGE\n    ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n      _has_boot_img_artifact := true\n    endif\n  endif\nendif\n\n# Are we building a debug boot image\nBUILDING_DEBUG_BOOT_IMAGE :=\n# Can't build boot-debug.img if we're not building a ramdisk.\nifndef BUILDING_RAMDISK_IMAGE\n  ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)\n    $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a ramdisk image. \\\n      Skip building the debug boot image.)\n  endif\n# Can't build boot-debug.img if we're not building a boot.img.\nelse ifndef _has_boot_img_artifact\n  ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)\n    $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a boot image. \\\n      Skip building the debug boot image.)\n  endif\nelse ifdef BUILDING_INIT_BOOT_IMAGE\n  ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)\n    $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we don't have a ramdisk in the boot image. \\\n      Skip building the debug boot image.)\n  endif\nelse\n  ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),)\n    BUILDING_DEBUG_BOOT_IMAGE := true\n    # Don't build boot-debug.img if we're already building vendor_boot-debug.img.\n    ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE\n      BUILDING_DEBUG_BOOT_IMAGE :=\n    endif\n  else ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)\n    BUILDING_DEBUG_BOOT_IMAGE := true\n  endif\nendif\n.KATI_READONLY := BUILDING_DEBUG_BOOT_IMAGE\n_has_boot_img_artifact :=\n\n# Are we building a userdata image\nBUILDING_USERDATA_IMAGE :=\nifeq ($(PRODUCT_BUILD_USERDATA_IMAGE),)\n  ifdef BOARD_USERDATAIMAGE_PARTITION_SIZE\n    BUILDING_USERDATA_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_USERDATA_IMAGE),true)\n  BUILDING_USERDATA_IMAGE := true\nendif\n.KATI_READONLY := BUILDING_USERDATA_IMAGE\n\n# Are we building a vbmeta image\nBUILDING_VBMETA_IMAGE := true\nifeq ($(PRODUCT_BUILD_VBMETA_IMAGE),false)\n  BUILDING_VBMETA_IMAGE :=\nendif\n.KATI_READONLY := BUILDING_VBMETA_IMAGE\n\n# Are we building a super_empty image\nBUILDING_SUPER_EMPTY_IMAGE :=\nifeq ($(PRODUCT_BUILD_SUPER_EMPTY_IMAGE),)\n  ifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS))\n    ifneq ($(BOARD_SUPER_PARTITION_SIZE),)\n      BUILDING_SUPER_EMPTY_IMAGE := true\n    endif\n  endif\nelse ifeq ($(PRODUCT_BUILD_SUPER_EMPTY_IMAGE),true)\n  ifneq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS))\n    $(error PRODUCT_BUILD_SUPER_EMPTY_IMAGE set to true, but PRODUCT_USE_DYNAMIC_PARTITIONS is not true)\n  endif\n  ifeq ($(BOARD_SUPER_PARTITION_SIZE),)\n    $(error PRODUCT_BUILD_SUPER_EMPTY_IMAGE set to true, but BOARD_SUPER_PARTITION_SIZE is not defined)\n  endif\n  BUILDING_SUPER_EMPTY_IMAGE := true\nendif\n.KATI_READONLY := BUILDING_SUPER_EMPTY_IMAGE\n\n###########################################\n# Now we can substitute with the real value of TARGET_COPY_OUT_VENDOR\nifeq ($(TARGET_COPY_OUT_VENDOR),$(_vendor_path_placeholder))\n  TARGET_COPY_OUT_VENDOR := system/vendor\nelse ifeq ($(filter vendor system/vendor,$(TARGET_COPY_OUT_VENDOR)),)\n  $(error TARGET_COPY_OUT_VENDOR must be either 'vendor' or 'system/vendor', seeing '$(TARGET_COPY_OUT_VENDOR)'.)\nendif\nPRODUCT_COPY_FILES := $(subst $(_vendor_path_placeholder),$(TARGET_COPY_OUT_VENDOR),$(PRODUCT_COPY_FILES))\n\nBOARD_USES_VENDORIMAGE :=\nifdef BOARD_PREBUILT_VENDORIMAGE\n  BOARD_USES_VENDORIMAGE := true\nendif\nifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE\n  BOARD_USES_VENDORIMAGE := true\nendif\n# TODO(b/137169253): For now, some AOSP targets build with prebuilt vendor image.\n# But target's BOARD_PREBUILT_VENDORIMAGE is not filled.\nifeq ($(TARGET_COPY_OUT_VENDOR),vendor)\n  BOARD_USES_VENDORIMAGE := true\nelse ifdef BOARD_USES_VENDORIMAGE\n  $(error TARGET_COPY_OUT_VENDOR must be set to 'vendor' to use a vendor image)\nendif\n.KATI_READONLY := BOARD_USES_VENDORIMAGE\n\nBUILDING_VENDOR_IMAGE :=\nifeq ($(PRODUCT_BUILD_VENDOR_IMAGE),)\n  ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE\n    BUILDING_VENDOR_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_VENDOR_IMAGE),true)\n  BUILDING_VENDOR_IMAGE := true\n  ifndef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE\n    $(error PRODUCT_BUILD_VENDOR_IMAGE set to true, but BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE not defined)\n  endif\nendif\nifdef BOARD_PREBUILT_VENDORIMAGE\n  BUILDING_VENDOR_IMAGE :=\nendif\n.KATI_READONLY := BUILDING_VENDOR_IMAGE\n\n###########################################\n# Now we can substitute with the real value of TARGET_COPY_OUT_PRODUCT\nifeq ($(TARGET_COPY_OUT_PRODUCT),$(_product_path_placeholder))\nTARGET_COPY_OUT_PRODUCT := system/product\nelse ifeq ($(filter product system/product,$(TARGET_COPY_OUT_PRODUCT)),)\n$(error TARGET_COPY_OUT_PRODUCT must be either 'product' or 'system/product', seeing '$(TARGET_COPY_OUT_PRODUCT)'.)\nendif\nPRODUCT_COPY_FILES := $(subst $(_product_path_placeholder),$(TARGET_COPY_OUT_PRODUCT),$(PRODUCT_COPY_FILES))\n\nBOARD_USES_PRODUCTIMAGE :=\nifdef BOARD_PREBUILT_PRODUCTIMAGE\n  BOARD_USES_PRODUCTIMAGE := true\nendif\nifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE\n  BOARD_USES_PRODUCTIMAGE := true\nendif\n$(call check_image_config,product)\n.KATI_READONLY := BOARD_USES_PRODUCTIMAGE\n\nBUILDING_PRODUCT_IMAGE :=\nifeq ($(PRODUCT_BUILD_PRODUCT_IMAGE),)\n  ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE\n    BUILDING_PRODUCT_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_PRODUCT_IMAGE),true)\n  BUILDING_PRODUCT_IMAGE := true\n  ifndef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE\n    $(error PRODUCT_BUILD_PRODUCT_IMAGE set to true, but BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE not defined)\n  endif\nendif\nifdef BOARD_PREBUILT_PRODUCTIMAGE\n  BUILDING_PRODUCT_IMAGE :=\nendif\n.KATI_READONLY := BUILDING_PRODUCT_IMAGE\n\n###########################################\n# TODO(b/135957588) TARGET_COPY_OUT_PRODUCT_SERVICES will be set to\n# TARGET_COPY_OUT_PRODUCT as a workaround.\nTARGET_COPY_OUT_PRODUCT_SERVICES := $(TARGET_COPY_OUT_PRODUCT)\n\n###########################################\n# Now we can substitute with the real value of TARGET_COPY_OUT_SYSTEM_EXT\nifeq ($(TARGET_COPY_OUT_SYSTEM_EXT),$(_system_ext_path_placeholder))\nTARGET_COPY_OUT_SYSTEM_EXT := system/system_ext\nelse ifeq ($(filter system_ext system/system_ext,$(TARGET_COPY_OUT_SYSTEM_EXT)),)\n$(error TARGET_COPY_OUT_SYSTEM_EXT must be either 'system_ext' or 'system/system_ext', seeing '$(TARGET_COPY_OUT_SYSTEM_EXT)'.)\nendif\nPRODUCT_COPY_FILES := $(subst $(_system_ext_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_EXT),$(PRODUCT_COPY_FILES))\n\nBOARD_USES_SYSTEM_EXTIMAGE :=\nifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE\n  BOARD_USES_SYSTEM_EXTIMAGE := true\nendif\nifdef BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE\n  BOARD_USES_SYSTEM_EXTIMAGE := true\nendif\n$(call check_image_config,system_ext)\n.KATI_READONLY := BOARD_USES_SYSTEM_EXTIMAGE\n\nBUILDING_SYSTEM_EXT_IMAGE :=\nifeq ($(PRODUCT_BUILD_SYSTEM_EXT_IMAGE),)\n  ifdef BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE\n    BUILDING_SYSTEM_EXT_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_SYSTEM_EXT_IMAGE),true)\n  BUILDING_SYSTEM_EXT_IMAGE := true\n  ifndef BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE\n    $(error PRODUCT_BUILD_SYSTEM_EXT_IMAGE set to true, but BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE not defined)\n  endif\nendif\nifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE\n  BUILDING_SYSTEM_EXT_IMAGE :=\nendif\n.KATI_READONLY := BUILDING_SYSTEM_EXT_IMAGE\n\n###########################################\n# Now we can substitute with the real value of TARGET_COPY_OUT_VENDOR_DLKM\nifeq ($(TARGET_COPY_OUT_VENDOR_DLKM),$(_vendor_dlkm_path_placeholder))\n  TARGET_COPY_OUT_VENDOR_DLKM := $(TARGET_COPY_OUT_VENDOR)/vendor_dlkm\nelse ifeq ($(filter vendor_dlkm system/vendor/vendor_dlkm vendor/vendor_dlkm,$(TARGET_COPY_OUT_VENDOR_DLKM)),)\n  $(error TARGET_COPY_OUT_VENDOR_DLKM must be either 'vendor_dlkm', 'system/vendor/vendor_dlkm' or 'vendor/vendor_dlkm', seeing '$(TARGET_COPY_OUT_VENDOR_DLKM)'.)\nendif\nPRODUCT_COPY_FILES := $(subst $(_vendor_dlkm_path_placeholder),$(TARGET_COPY_OUT_VENDOR_DLKM),$(PRODUCT_COPY_FILES))\n\nBOARD_USES_VENDOR_DLKMIMAGE :=\nifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE\n  BOARD_USES_VENDOR_DLKMIMAGE := true\nendif\nifdef BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE\n  BOARD_USES_VENDOR_DLKMIMAGE := true\nendif\n$(call check_image_config,vendor_dlkm)\n\nBUILDING_VENDOR_DLKM_IMAGE :=\nifeq ($(PRODUCT_BUILD_VENDOR_DLKM_IMAGE),)\n  ifdef BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE\n    BUILDING_VENDOR_DLKM_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_VENDOR_DLKM_IMAGE),true)\n  BUILDING_VENDOR_DLKM_IMAGE := true\n  ifndef BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE\n    $(error PRODUCT_BUILD_VENDOR_DLKM_IMAGE set to true, but BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE not defined)\n  endif\nendif\nifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE\n  BUILDING_VENDOR_DLKM_IMAGE :=\nendif\n.KATI_READONLY := BUILDING_VENDOR_DLKM_IMAGE\n\n###########################################\n# Now we can substitute with the real value of TARGET_COPY_OUT_ODM\nifeq ($(TARGET_COPY_OUT_ODM),$(_odm_path_placeholder))\n  TARGET_COPY_OUT_ODM := $(TARGET_COPY_OUT_VENDOR)/odm\nelse ifeq ($(filter odm system/vendor/odm vendor/odm,$(TARGET_COPY_OUT_ODM)),)\n  $(error TARGET_COPY_OUT_ODM must be either 'odm', 'system/vendor/odm' or 'vendor/odm', seeing '$(TARGET_COPY_OUT_ODM)'.)\nendif\nPRODUCT_COPY_FILES := $(subst $(_odm_path_placeholder),$(TARGET_COPY_OUT_ODM),$(PRODUCT_COPY_FILES))\n\nBOARD_USES_ODMIMAGE :=\nifdef BOARD_PREBUILT_ODMIMAGE\n  BOARD_USES_ODMIMAGE := true\nendif\nifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE\n  BOARD_USES_ODMIMAGE := true\nendif\n$(call check_image_config,odm)\n\nBUILDING_ODM_IMAGE :=\nifeq ($(PRODUCT_BUILD_ODM_IMAGE),)\n  ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE\n    BUILDING_ODM_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_ODM_IMAGE),true)\n  BUILDING_ODM_IMAGE := true\n  ifndef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE\n    $(error PRODUCT_BUILD_ODM_IMAGE set to true, but BOARD_ODMIMAGE_FILE_SYSTEM_TYPE not defined)\n  endif\nendif\nifdef BOARD_PREBUILT_ODMIMAGE\n  BUILDING_ODM_IMAGE :=\nendif\n.KATI_READONLY := BUILDING_ODM_IMAGE\n\n\n###########################################\n# Now we can substitute with the real value of TARGET_COPY_OUT_ODM_DLKM\nifeq ($(TARGET_COPY_OUT_ODM_DLKM),$(_odm_dlkm_path_placeholder))\n  TARGET_COPY_OUT_ODM_DLKM := $(TARGET_COPY_OUT_VENDOR)/odm_dlkm\nelse ifeq ($(filter odm_dlkm system/vendor/odm_dlkm vendor/odm_dlkm,$(TARGET_COPY_OUT_ODM_DLKM)),)\n  $(error TARGET_COPY_OUT_ODM_DLKM must be either 'odm_dlkm', 'system/vendor/odm_dlkm' or 'vendor/odm_dlkm', seeing '$(TARGET_COPY_OUT_ODM_DLKM)'.)\nendif\nPRODUCT_COPY_FILES := $(subst $(_odm_dlkm_path_placeholder),$(TARGET_COPY_OUT_ODM_DLKM),$(PRODUCT_COPY_FILES))\n\nBOARD_USES_ODM_DLKMIMAGE :=\nifdef BOARD_PREBUILT_ODM_DLKMIMAGE\n  BOARD_USES_ODM_DLKMIMAGE := true\nendif\nifdef BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE\n  BOARD_USES_ODM_DLKMIMAGE := true\nendif\n$(call check_image_config,odm_dlkm)\n\nBUILDING_ODM_DLKM_IMAGE :=\nifeq ($(PRODUCT_BUILD_ODM_DLKM_IMAGE),)\n  ifdef BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE\n    BUILDING_ODM_DLKM_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_ODM_DLKM_IMAGE),true)\n  BUILDING_ODM_DLKM_IMAGE := true\n  ifndef BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE\n    $(error PRODUCT_BUILD_ODM_DLKM_IMAGE set to true, but BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE not defined)\n  endif\nendif\nifdef BOARD_PREBUILT_ODM_DLKMIMAGE\n  BUILDING_ODM_DLKM_IMAGE :=\nendif\n.KATI_READONLY := BUILDING_ODM_DLKM_IMAGE\n\n###########################################\n# Now we can substitute with the real value of TARGET_COPY_OUT_SYSTEM_DLKM\nifeq ($(TARGET_COPY_OUT_SYSTEM_DLKM),$(_system_dlkm_path_placeholder))\n  TARGET_COPY_OUT_SYSTEM_DLKM := $(TARGET_COPY_OUT_SYSTEM)/system_dlkm\nelse ifeq ($(filter system_dlkm system/system_dlkm,$(TARGET_COPY_OUT_SYSTEM_DLKM)),)\n  $(error TARGET_COPY_OUT_SYSTEM_DLKM must be either 'system_dlkm' or 'system/system_dlkm', seeing '$(TARGET_COPY_OUT_ODM_DLKM)'.)\nendif\nPRODUCT_COPY_FILES := $(subst $(_system_dlkm_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_DLKM),$(PRODUCT_COPY_FILES))\n\nBOARD_USES_SYSTEM_DLKMIMAGE :=\nifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE\n  BOARD_USES_SYSTEM_DLKMIMAGE := true\nendif\nifdef BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE\n  BOARD_USES_SYSTEM_DLKMIMAGE := true\nendif\n$(call check_image_config,system_dlkm)\n\nBUILDING_SYSTEM_DLKM_IMAGE :=\nifeq ($(PRODUCT_BUILD_SYSTEM_DLKM_IMAGE),)\n  ifdef BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE\n    BUILDING_SYSTEM_DLKM_IMAGE := true\n  endif\nelse ifeq ($(PRODUCT_BUILD_SYSTEM_DLKM_IMAGE),true)\n  BUILDING_SYSTEM_DLKM_IMAGE := true\n  ifndef BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE\n    $(error PRODUCT_BUILD_SYSTEM_DLKM_IMAGE set to true, but BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE not defined)\n  endif\nendif\nifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE\n  BUILDING_SYSTEM_DLKM_IMAGE :=\nendif\n.KATI_READONLY := BUILDING_SYSTEM_DLKM_IMAGE\n\nBOARD_USES_PVMFWIMAGE :=\nifeq ($(PRODUCT_BUILD_PVMFW_IMAGE),true)\n  BOARD_USES_PVMFWIMAGE := true\nendif\n.KATI_READONLY := BOARD_USES_PVMFWIMAGE\n\nBOARD_USES_DESKTOP_RECOVERY_IMAGE :=\nifeq ($(PRODUCT_BUILD_DESKTOP_RECOVERY_IMAGE),true)\n  BOARD_USES_DESKTOP_RECOVERY_IMAGE := true\nendif\n.KATI_READONLY := BOARD_USES_DESKTOP_RECOVERY_IMAGE\n\nBOARD_USES_DESKTOP_RECOVERY_SWAP_KERNEL :=\nifeq ($(PRODUCT_USES_DESKTOP_RECOVERY_SWAP_KERNEL),true)\n  BOARD_USES_DESKTOP_RECOVERY_SWAP_KERNEL := true\nendif\n.KATI_READONLY := BOARD_USES_DESKTOP_RECOVERY_SWAP_KERNEL\n\nBOARD_USES_DESKTOP_UPDATE_IMAGE :=\nifeq ($(PRODUCT_BUILD_DESKTOP_UPDATE_IMAGE),true)\n  BOARD_USES_DESKTOP_UPDATE_IMAGE := true\nendif\n.KATI_READONLY := BOARD_USES_DESKTOP_UPDATE_IMAGE\n\n###########################################\n# Ensure consistency among TARGET_RECOVERY_UPDATER_LIBS, AB_OTA_UPDATER, and PRODUCT_OTA_FORCE_NON_AB_PACKAGE.\nTARGET_RECOVERY_UPDATER_LIBS ?=\nifeq ($(AB_OTA_UPDATER),)\nAB_OTA_UPDATER := true\nendif\n.KATI_READONLY := TARGET_RECOVERY_UPDATER_LIBS AB_OTA_UPDATER\n\n# Ensure that if PRODUCT_OTA_FORCE_NON_AB_PACKAGE == true, then AB_OTA_UPDATER must be true\nifeq ($(PRODUCT_OTA_FORCE_NON_AB_PACKAGE),true)\n  ifneq ($(AB_OTA_UPDATER),true)\n    $(error AB_OTA_UPDATER must be set to true when PRODUCT_OTA_FORCE_NON_AB_PACKAGE is true)\n  endif\nendif\n\n# In some configurations, A/B and non-A/B may coexist. Check TARGET_OTA_ALLOW_NON_AB\n# to see if non-A/B is supported.\nTARGET_OTA_ALLOW_NON_AB := false\nifneq ($(AB_OTA_UPDATER),true)\n  TARGET_OTA_ALLOW_NON_AB := true\nelse ifeq ($(PRODUCT_OTA_FORCE_NON_AB_PACKAGE),true)\n  TARGET_OTA_ALLOW_NON_AB := true\nendif\n.KATI_READONLY := TARGET_OTA_ALLOW_NON_AB\n\nifneq ($(TARGET_OTA_ALLOW_NON_AB),true)\n  ifneq ($(strip $(TARGET_RECOVERY_UPDATER_LIBS)),)\n    $(error Do not use TARGET_RECOVERY_UPDATER_LIBS when using TARGET_OTA_ALLOW_NON_AB)\n  endif\nendif\n\n# For Non A/B full OTA, disable brotli compression.\nifeq ($(TARGET_OTA_ALLOW_NON_AB),true)\n  BOARD_NON_AB_OTA_DISABLE_COMPRESSION := true\nendif\n\n# Quick check for building generic OTA packages. Currently it only supports A/B OTAs.\nifeq ($(PRODUCT_BUILD_GENERIC_OTA_PACKAGE),true)\n  ifneq ($(AB_OTA_UPDATER),true)\n    $(error PRODUCT_BUILD_GENERIC_OTA_PACKAGE with 'AB_OTA_UPDATER != true' is not supported)\n  endif\nendif\n\nifdef BOARD_PREBUILT_DTBIMAGE_DIR\n  ifneq ($(BOARD_INCLUDE_DTB_IN_BOOTIMG),true)\n    $(error BOARD_PREBUILT_DTBIMAGE_DIR with 'BOARD_INCLUDE_DTB_IN_BOOTIMG != true' is not supported)\n  endif\nendif\n\n# Check BOARD_VNDK_VERSION\ndefine check_vndk_version\n  $(eval vndk_path := prebuilts/vndk/v$(1)) \\\n  $(if $(wildcard $(vndk_path)/*/Android.bp),,$(error VNDK version $(1) not found))\nendef\n\nTARGET_VENDOR_TEST_SUFFIX := /vendor\n\nifeq (,$(TARGET_BUILD_UNBUNDLED))\nifdef PRODUCT_EXTRA_VNDK_VERSIONS\n  $(foreach v,$(PRODUCT_EXTRA_VNDK_VERSIONS),$(call check_vndk_version,$(v)))\nendif\nendif\n\n# Ensure that BOARD_SYSTEMSDK_VERSIONS are all within PLATFORM_SYSTEMSDK_VERSIONS\n_unsupported_systemsdk_versions := $(filter-out $(PLATFORM_SYSTEMSDK_VERSIONS),$(BOARD_SYSTEMSDK_VERSIONS))\nifneq (,$(_unsupported_systemsdk_versions))\n  $(error System SDK versions '$(_unsupported_systemsdk_versions)' in BOARD_SYSTEMSDK_VERSIONS are not supported.\\\n          Supported versions are $(PLATFORM_SYSTEMSDK_VERSIONS))\nendif\n\n###########################################\n# BOARD_API_LEVEL for vendor API surface\nifdef RELEASE_BOARD_API_LEVEL\n  ifdef BOARD_API_LEVEL\n    $(error BOARD_API_LEVEL must not be set manually. The build system automatically sets this value.)\n  endif\n  BOARD_API_LEVEL := $(RELEASE_BOARD_API_LEVEL)\n  .KATI_READONLY := BOARD_API_LEVEL\nendif\n\n###########################################\n# Handle BUILD_BROKEN_USES_BUILD_*\n\n$(foreach m,$(DEFAULT_WARNING_BUILD_MODULE_TYPES),\\\n  $(if $(filter false,$(BUILD_BROKEN_USES_$(m))),\\\n    $(KATI_obsolete_var $(m),Please convert to Soong),\\\n    $(KATI_deprecated_var $(m),Please convert to Soong)))\n\n$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\\\n  $(KATI_deprecated_var BUILD_COPY_HEADERS,See $(CHANGES_URL)\\#copy_headers),\\\n  $(KATI_obsolete_var BUILD_COPY_HEADERS,See $(CHANGES_URL)\\#copy_headers))\n\n$(foreach m,$(filter-out BUILD_COPY_HEADERS,$(DEFAULT_ERROR_BUILD_MODULE_TYPES)),\\\n  $(if $(filter true,$(BUILD_BROKEN_USES_$(m))),\\\n    $(KATI_deprecated_var $(m),Please convert to Soong),\\\n    $(KATI_obsolete_var $(m),Please convert to Soong)))\n\nifndef BUILDING_RECOVERY_IMAGE\n  ifeq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE))\n    $(error Should not set BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE if not building recovery image)\n  endif\nendif\n\nifndef BUILDING_VENDOR_BOOT_IMAGE\n  ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))\n    $(error Should not set BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT if not building vendor_boot image)\n  endif\n  ifdef BOARD_VENDOR_RAMDISK_FRAGMENTS\n    $(error Should not set BOARD_VENDOR_RAMDISK_FRAGMENTS if not building vendor_boot image)\n  endif\nelse # BUILDING_VENDOR_BOOT_IMAGE\n  ifneq (,$(call math_lt,$(BOARD_BOOT_HEADER_VERSION),4))\n    ifdef BOARD_VENDOR_RAMDISK_FRAGMENTS\n      $(error Should not set BOARD_VENDOR_RAMDISK_FRAGMENTS if \\\n        BOARD_BOOT_HEADER_VERSION is less than 4)\n    endif\n    ifeq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))\n      $(error Should not set BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT if \\\n        BOARD_BOOT_HEADER_VERSION is less than 4)\n    endif\n  endif\nendif # BUILDING_VENDOR_BOOT_IMAGE\n\nifneq ($(words $(BOARD_VENDOR_RAMDISK_FRAGMENTS)),$(words $(sort $(BOARD_VENDOR_RAMDISK_FRAGMENTS))))\n  $(error BOARD_VENDOR_RAMDISK_FRAGMENTS has duplicate entries: $(BOARD_VENDOR_RAMDISK_FRAGMENTS))\nendif\n\nifeq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))\n  ifneq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))\n    $(error Should not set BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT if \\\n      BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT is not set)\n  endif\nendif\n\n# If BOARD_USES_GENERIC_KERNEL_IMAGE is set, BOARD_USES_RECOVERY_AS_BOOT must not be set.\n# Devices without a dedicated recovery partition uses BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT to\n# build recovery into vendor_boot.\nifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))\n  ifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT))\n    $(error BOARD_USES_RECOVERY_AS_BOOT cannot be true if BOARD_USES_GENERIC_KERNEL_IMAGE is true. \\\n      Use BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT instead)\n  endif\nendif\n\nifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))\n  ifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT))\n    $(error BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT and BOARD_USES_RECOVERY_AS_BOOT cannot be \\\n      both true. Recovery resources should be installed to either boot or vendor_boot, but not both)\n  endif\nendif\n"
  },
  {
    "path": "core/board_config_wifi.mk",
    "content": "#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# ###############################################################\n# This file adds WIFI variables into soong config namespace (`wifi`)\n# ###############################################################\n\nifdef BOARD_WLAN_DEVICE\n    $(call soong_config_set,wifi,board_wlan_device,$(BOARD_WLAN_DEVICE))\nendif\nifdef WIFI_DRIVER_MODULE_PATH\n    $(call soong_config_set,wifi,driver_module_path,$(WIFI_DRIVER_MODULE_PATH))\nendif\nifdef WIFI_DRIVER_MODULE_ARG\n    $(call soong_config_set,wifi,driver_module_arg,$(WIFI_DRIVER_MODULE_ARG))\nendif\nifdef WIFI_DRIVER_MODULE_NAME\n    $(call soong_config_set,wifi,driver_module_name,$(WIFI_DRIVER_MODULE_NAME))\nendif\nifdef WIFI_DRIVER_FW_PATH_STA\n    $(call soong_config_set,wifi,driver_fw_path_sta,$(WIFI_DRIVER_FW_PATH_STA))\nendif\nifdef WIFI_DRIVER_FW_PATH_AP\n    $(call soong_config_set,wifi,driver_fw_path_ap,$(WIFI_DRIVER_FW_PATH_AP))\nendif\nifdef WIFI_DRIVER_FW_PATH_P2P\n    $(call soong_config_set,wifi,driver_fw_path_p2p,$(WIFI_DRIVER_FW_PATH_P2P))\nendif\nifdef WIFI_DRIVER_FW_PATH_PARAM\n    $(call soong_config_set,wifi,driver_fw_path_param,$(WIFI_DRIVER_FW_PATH_PARAM))\nendif\nifdef WIFI_DRIVER_STATE_CTRL_PARAM\n    $(call soong_config_set,wifi,driver_state_ctrl_param,$(WIFI_DRIVER_STATE_CTRL_PARAM))\nendif\nifdef WIFI_DRIVER_STATE_ON\n    $(call soong_config_set,wifi,driver_state_on,$(WIFI_DRIVER_STATE_ON))\nendif\nifdef WIFI_DRIVER_STATE_OFF\n    $(call soong_config_set,wifi,driver_state_off,$(WIFI_DRIVER_STATE_OFF))\nendif\nifdef WIFI_MULTIPLE_VENDOR_HALS\n    $(call soong_config_set,wifi,multiple_vendor_hals,$(WIFI_MULTIPLE_VENDOR_HALS))\nendif\nifneq ($(wildcard vendor/google/libraries/GoogleWifiConfigLib),)\n    $(call soong_config_set,wifi,google_wifi_config_lib,true)\nendif\nifdef WIFI_HAL_INTERFACE_COMBINATIONS\n    $(call soong_config_set,wifi,hal_interface_combinations,$(WIFI_HAL_INTERFACE_COMBINATIONS))\nendif\nifdef WIFI_HIDL_FEATURE_AWARE\n    $(call soong_config_set,wifi,hidl_feature_aware,true)\nendif\nifdef WIFI_HIDL_FEATURE_DUAL_INTERFACE\n    $(call soong_config_set,wifi,hidl_feature_dual_interface,true)\nendif\nifdef WIFI_HIDL_FEATURE_DISABLE_AP\n    $(call soong_config_set,wifi,hidl_feature_disable_ap,true)\nendif\nifdef WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION\n    $(call soong_config_set,wifi,hidl_feature_disable_ap_mac_randomization,true)\nendif\nifdef WIFI_AVOID_IFACE_RESET_MAC_CHANGE\n    $(call soong_config_set,wifi,avoid_iface_reset_mac_change,true)\nendif\nifdef WIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN\n    $(call soong_config_set,wifi,wifi_skip_state_toggle_off_on_for_nan,true)\nendif\nifeq ($(strip $(TARGET_USES_AOSP_FOR_WLAN)),true)\n    $(call soong_config_set,wifi,target_uses_aosp_for_wlan,true)\nendif"
  },
  {
    "path": "core/board_config_wpa_supplicant.mk",
    "content": "#\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# ###############################################################\n# This file adds wpa_supplicant_8 variables into soong config namespace (`wpa_supplicant_8`)\n# ###############################################################\n\nifdef BOARD_HOSTAPD_DRIVER\n$(call soong_config_set_bool,wpa_supplicant_8,wpa_build_hostapd,true)\nifneq ($(BOARD_HOSTAPD_DRIVER),NL80211)\n    $(error BOARD_HOSTAPD_DRIVER set to $(BOARD_HOSTAPD_DRIVER) but current soong expected it should be NL80211 only!)\nendif\nendif\n\nifdef BOARD_WPA_SUPPLICANT_DRIVER\nifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),NL80211)\n    $(error BOARD_WPA_SUPPLICANT_DRIVER set to $(BOARD_WPA_SUPPLICANT_DRIVER) but current soong expected it should be NL80211 only!)\nendif\nendif\n\n# This is for CONFIG_DRIVER_NL80211_BRCM, CONFIG_DRIVER_NL80211_SYNA, CONFIG_DRIVER_NL80211_QCA\n# And it is only used for a cflags setting in driver.\n$(call soong_config_set,wpa_supplicant_8,board_wlan_device,$(BOARD_WLAN_DEVICE))\n\n# Belong to CONFIG_IEEE80211AX definition\nifeq ($(WIFI_FEATURE_HOSTAPD_11AX),true)\n$(call soong_config_set_bool,wpa_supplicant_8,hostapd_11ax,true)\nendif\n\n# Belong to CONFIG_IEEE80211BE definition\nifeq ($(WIFI_FEATURE_HOSTAPD_11BE),true)\n$(call soong_config_set_bool,wpa_supplicant_8,hostapd_11be,true)\nendif\n\n# PLATFORM_VERSION\n$(call soong_config_set,wpa_supplicant_8,platform_version,$(PLATFORM_VERSION))\n\n# BOARD_HOSTAPD_PRIVATE_LIB\nifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)\n$(call soong_config_set_bool,wpa_supplicant_8,hostapd_use_stub_lib,true)\nelse\n$(call soong_config_set,wpa_supplicant_8,board_hostapd_private_lib,$(BOARD_HOSTAPD_PRIVATE_LIB))\nendif\n\nifeq ($(BOARD_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL),true)\n$(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_config_80211w_mfp_optional,true)\nendif\n\nifneq ($(BOARD_HOSTAPD_PRIVATE_LIB_EVENT),)\n$(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_private_lib_event,true)\nendif\n\n# BOARD_WPA_SUPPLICANT_PRIVATE_LIB\nifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)\n$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_use_stub_lib,true)\nelse\n$(call soong_config_set,wpa_supplicant_8,board_wpa_supplicant_private_lib,$(BOARD_WPA_SUPPLICANT_PRIVATE_LIB))\nendif\n\nifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB_EVENT),)\n$(call soong_config_set_bool,wpa_supplicant_8,board_wpa_supplicant_private_lib_event,true)\nendif\n\nifeq ($(WIFI_PRIV_CMD_UPDATE_MBO_CELL_STATUS), enabled)\n$(call soong_config_set_bool,wpa_supplicant_8,wifi_priv_cmd_update_mbo_cell_status,true)\nendif\n\nifeq ($(WIFI_HIDL_UNIFIED_SUPPLICANT_SERVICE_RC_ENTRY), true)\n$(call soong_config_set_bool,wpa_supplicant_8,wifi_hidl_unified_supplicant_service_rc_entry,true)\nendif\n\n# New added in internal main\nifeq ($(WIFI_BRCM_OPEN_SOURCE_MULTI_AKM), enabled)\n$(call soong_config_set_bool,wpa_supplicant_8,wifi_brcm_open_source_multi_akm,true)\nendif\n"
  },
  {
    "path": "core/build-system.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n\n<!--\n  A lot of people read this document template.  Please keep it clean:\n\n   - keep the document xhtml-compliant, as many people use validating editors\n   - check your edits for typos, spelling errors, and questionable grammar\n   - prefer css styles to formatting tags like <font>, <tt>, etc.\n   - keep it human-readable and human-editable in a plain text editor:\n     - strive to keep lines wrapped at 80 columns, unless a link prevents it\n     - use plenty of whitespace\n     - try to pretty-format (wrt nesting and indenting) any hairy html\n   - check your inline javascript for errors using the javascript console\n   \n  Your readers will be very appreciative.\n-->\n\n<head>\n  <title>Android Build System</title>\n\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n\n  <link href=\"../android.css\" type=\"text/css\" rel=\"stylesheet\" />\n\n<!-- commenting out so the xhtml validator doesn't whine about < and &&;\n     the browser should still find the script tag. -->\n<script language=\"JavaScript1.2\" type=\"text/javascript\">\n<!--\nfunction highlight(name) {\n  if (document.getElementsByTagName) {\n    tags              = [ 'span', 'div', 'tr', 'td' ];\n    for (i in tags) {\n      elements        = document.getElementsByTagName(tags[i]);\n      if (elements) {\n        for (j = 0; j < elements.length; j++) {\n          elementName = elements[j].getAttribute(\"class\");\n          if (elementName == name) {\n            elements[j].style.backgroundColor = \"#C0F0C0\";\n          } else if (elementName && elementName.indexOf(\"rev\") == 0) {\n            elements[j].style.backgroundColor = \"#FFFFFF\";\n          }\n        }\n      }\n    }\n  }\n}\n//-->\n  </script>\n  <!-- this style sheet is for the style of the toc -->\n  <link href=\"toc.css\" type=\"text/css\" rel=\"stylesheet\" />\n\n  <style type=\"text/css\">\n    .warning {\n        border: 1px solid red;\n        padding: 8px;\n        color: red;\n    }\n    pre.prettyprint {\n        margin-top: 0;\n    }\n    li {\n        margin-top: 8px;\n    }\n  </style>\n</head>\n\n<body onload=\"prettyPrint()\">\n\n<h1><a name=\"My_Project_\" />Android Build System</h1>\n\n<!-- Status is one of: Draft, Current, Needs Update, Obsolete -->\n<p style=\"text-align:center\">\n  <strong>Status:</strong> <em>Draft </em> &nbsp;\n  <small>(as of May 18, 2006)</small>\n</p>\n\n<p><b>Contents</b></p>\n<!-- this div expands out to a list of contents based on the H2 and H3 headings.\nBelieve it! -->\n <div id=\"nav\"  class=\"nav-2-levels\"></div>\n\n<h2>Objective</h2>\n<p>The primary goals of reworking the build system are (1) to make dependencies\nwork more reliably, so that when files need to rebuilt, they are, and (2) to\nimprove performance of the build system so that unnecessary modules are not\nrebuilt, and so doing a top-level build when little or nothing needs to be done\nfor a build takes as little time as possible.</p>\n\n<h2>Principles and Use Cases and Policy</h2>\n<p>Given the above objective, these are the overall principles and use cases\nthat we will support.  This is not an exhaustive list.</p>\n<h3>Multiple Targets</h3>\n<p>It needs to be possible to build the Android platform for multiple targets.\nThis means:</p>\n<ul>\n    <li>The build system will support building tools for the host platform,\n    both ones that are used in the build process itself, and developer tools\n    like the simulator.</li>\n    <li>The build system will need to be able to build tools on Linux\n    (definitely Goobuntu and maybe Grhat), MacOS, and to some degree on\n    Windows.</li>\n    <li>The build system will need to be able to build the OS on Linux, and in\n    the short-term, MacOS.  Note that this is a conscious decision to stop\n    building the OS on Windows.  We are going to rely on the emulator there\n    and not attempt to use the simulator.  This is a requirement change now\n    that the emulator story is looking brighter.</li>\n</ul>\n<h3>Non-Recursive Make</h3>\n<p>To achieve the objectives, the build system will be rewritten to use make\nnon-recursively.  For more background on this, read <a href=\"http://aegis.sourceforge.net/auug97.pdf\">Recursive Make Considered Harmful</a>.  For those that don't\nwant PDF, here is the\n<a href=\"http://72.14.203.104/search?q=cache:HwuX7YF2uBIJ:aegis.sourceforge.net/auug97.pdf&hl=en&gl=us&ct=clnk&cd=2&client=firefox\">Google translated version</a>.\n<h3>Rapid Compile-Test Cycles</h3>\n<p>When developing a component, for example a C++ shared library, it must be\npossible to easily rebuild just that component, and not have to wait more than a\ncouple seconds for dependency checks, and not have to wait for unneeded\ncomponents to be built.</p>\n<h3>Both Environment and Config File Based Settings</h3>\n<p>To set the target, and other options, some people on the team like to have a\nconfiguration file in a directory so they do not have an environment setup\nscript to run, and others want an environment setup script to run so they can\nrun builds in different terminals on the same tree, or switch back and forth\nin one terminal.  We will support both.</p>\n<h3>Object File Directory / make clean</h3>\n<p>Object files and other intermediate files will be generated into a directory\nthat is separate from the source tree.  The goal is to have make clean be\n\"rm -rf <obj>\" in the tree root directory.  The primary goals of\nthis are to simplify searching the source tree, and to make \"make clean\" more\nreliable.</p>\n\n<h3>SDK</h3>\n<p>The SDK will be a tarball that will allow non-OS-developers to write apps.\nThe apps will actually be built by first building the SDK, and then building\nthe apps against that SDK.  This will hopefully (1) make writing apps easier\nfor us, because we won't have to rebuild the OS as much, and we can use the\nstandard java-app development tools, and (2) allow us to dog-food the SDK, to\nhelp ensure its quality.  Cedric has suggested (and I agree) that apps built\nfrom the SDK should be built with ant.  Stay tuned for more details as we\nfigure out exactly how this will work.</p>\n\n<h3>Dependecies</h3>\n<p>Dependencies should all be automatic.  Unless there is a custom tool involved\n(e.g. the webkit has several), the dependencies for shared and static libraries,\n.c, .cpp, .h, .java, java libraries, etc., should all work without intervention\nin the Android.mk file.</p>\n\n<h3>Wildcard source files</h3>\n<p>Wildcarding source file will be discouraged.  It may be useful in some\nscenarios.  The default <code>$(wildcard *)</code> will not work due to the\ncurrent directory being set to the root of the build tree.<p>\n\n<h3>Multiple targets in one directory</h3>\n<p>It will be possible to generate more than one target from a given\nsubdirectory.  For example, libutils generates a shared library for the target\nand a static library for the host.</p>\n\n<h3>Makefile fragments for modules</h3>\n<p><b>Android.mk</b> is the standard name for the makefile fragments that\ncontrol the building of a given module.  Only the top directory should\nhave a file named \"Makefile\".</p>\n\n<h3>Use shared libraries</h3>\n<p>Currently, the simulator is not built to use shared libraries.  This should\nbe fixed, and now is a good time to do it.  This implies getting shared\nlibraries to work on Mac OS.</p>\n\n\n<h2>Nice to Have</h2>\n\n<p>These things would be nice to have, and this is a good place to record them,\nhowever these are not promises.</p>\n\n<h3>Simultaneous Builds</h3>\n<p>The hope is to be able to do two builds for different combos in the same\ntree at the same time, but this is a stretch goal, not a requirement.\nDoing two builds in the same tree, not at the same time must work.  (update:\nit's looking like we'll get the two builds at the same time working)</p>\n\n<h3>Deleting headers (or other dependecies)</h3>\n<p>Problems can arise if you delete a header file that is referenced in\n\".d\" files.  The easy way to deal with this is \"make clean\".  There\nshould be a better way to handle it. (from fadden)</p>\n<p>One way of solving this is introducing a dependency on the directory.  The\nproblem is that this can create extra dependecies and slow down the build.\nIt's a tradeoff.</p>\n\n<h3>Multiple builds</h3>\n<p>General way to perform builds across the set of known platforms.  This\nwould make it easy to perform multiple platform builds when testing a\nchange, and allow a wide-scale \"make clean\".  Right now the buildspec.mk\nor environment variables need to be updated before each build. (from fadden)</p>\n\n<h3>Aftermarket Locales and Carrier</h3>\n<p>We will eventually need to add support for creating locales and carrier\ncustomizations to the SDK, but that will not be addressed right now.</p>\n\n\n<h2><a id=\"usage\"/>Usage</h2>\n<p>You've read (or scrolled past) all of the motivations for this build system,\nand you want to know how to use it.  This is the place.</p>\n\n<h3>Your first build</h3>\n<p>The <a href=\"../building.html\">Building</a> document describes how do do\nbuilds.</p>\n\n<h3>build/envsetup.sh functions</h3>\nIf you source the file build/envsetup.sh into your bash environment,\n<code>. build/envsetup.sh</code>you'll get a few helpful shell functions:\n\n<ul>\n<li><b>printconfig</b> - Prints the current configuration as set by the\nlunch and choosecombo commands.</li>\n<li><b>m</b> - Runs <code>make</code> from the top of the tree.  This is\nuseful because you can run make from within subdirectories.  If you have the\n<code>TOP</code> environment variable set, it uses that.  If you don't, it looks\nup the tree from the current directory, trying to find the top of the tree.</li>\n<li><b>croot</b> - <code>cd</code> to the top of the tree.</li>\n<li><b>sgrep</b> - grep for the regex you provide in all .c, .cpp, .h, .java,\nand .xml files below the current directory.</li>\n</ul>\n\n<h3>Build flavors/types</h3>\n<p>\nWhen building for a particular product, it's often useful to have minor\nvariations on what is ultimately the final release build.  These are the\ncurrently-defined \"flavors\" or \"types\" (we need to settle on a real name\nfor these).\n</p>\n\n<table border=1>\n<tr>\n    <td>\n        <code>eng<code>\n    </td>\n    <td>\n        This is the default flavor. A plain \"<code>make</code>\" is the\n        same as \"<code>make eng</code>\".  <code>droid</code> is an alias\n        for <code>eng</code>.\n        <ul>\n        <li>Installs modules tagged with: <code>eng</code>, <code>debug</code>,\n            <code>user</code>, and/or <code>development</code>.\n        <li>Installs non-APK modules that have no tags specified.\n        <li>Installs APKs according to the product definition files, in\n            addition to tagged APKs.\n        <li><code>ro.secure=0</code>\n        <li><code>ro.debuggable=1</code>\n        <li><code>ro.kernel.android.checkjni=1</code>\n        <li><code>adb</code> is enabled by default.\n    </td>\n</tr>\n<tr>\n    <td>\n        <code>user<code>\n    </td>\n    <td>\n        \"<code>make user</code>\"\n        <p>\n        This is the flavor intended to be the final release bits.\n        <ul>\n        <li>Installs modules tagged with <code>user</code>.\n        <li>Installs non-APK modules that have no tags specified.\n        <li>Installs APKs according to the product definition files; tags\n            are ignored for APK modules.\n        <li><code>ro.adb.secure=1</code>\n        <li><code>ro.secure=1</code>\n        <li><code>ro.debuggable=0</code>\n        <li><code>adb</code> is disabled by default.\n    </td>\n</tr>\n<tr>\n    <td>\n        <code>userdebug<code>\n    </td>\n    <td>\n        \"<code>make userdebug</code>\"\n        <p>\n        The same as <code>user</code>, except:\n        <ul>\n        <li>Also installs modules tagged with <code>debug</code>.\n        <li><code>ro.debuggable=1</code>\n        <li><code>adb</code> is enabled by default.\n    </td>\n</tr>\n</table>\n\n<p>\nIf you build one flavor and then want to build another, you should run\n\"<code>make installclean</code>\" between the two makes to guarantee that\nyou don't pick up files installed by the previous flavor.  \"<code>make\nclean</code>\" will also suffice, but it takes a lot longer.\n</p>\n\n\n<h3>More pseudotargets</h3>\n<p>Sometimes you want to just build one thing.  The following pseudotargets are\nthere for your convenience:</p>\n\n<ul>\n<li><b>droid</b> - <code>make droid</code> is the normal build.  This target\nis here because the default target has to have a name.</li>\n<li><b>all</b> - <code>make all</code> builds everything <code>make\ndroid</code> does, plus everything whose <code>LOCAL_MODULE_TAGS</code> do not\ninclude the \"droid\" tag.  The build server runs this to make sure\nthat everything that is in the tree and has an Android.mk builds.</li>\n<li><b>clean-$(LOCAL_MODULE)</b> and <b>clean-$(LOCAL_PACKAGE_NAME)</b> - \nLet you selectively clean one target.  For example, you can type\n<code>make clean-libutils</code> and it will delete libutils.so and all of the\nintermediate files, or you can type <code>make clean-Home</code> and it will\nclean just the Home app.</li>\n<li><b>clean</b> - <code>make clean</code> deletes all of the output and\nintermediate files for this configuration.  This is the same as <code>rm -rf\nout/&lt;configuration&gt;/</code></li>\n<li><b>clobber</b> - <code>make clobber</code> deletes all of the output\nand intermediate files for all configurations.  This is the same as\n<code>rm -rf out/</code>.</li>\n<li><b>dataclean</b> - <code>make dataclean</code> deletes contents of the data \ndirectory inside the current combo directory.  This is especially useful on the\nsimulator and emulator, where the persistent data remains present between \nbuilds.</li>\n<li><b>LOCAL_MODULE</b> - Anything you specify as a <code>LOCAL_MODULE</code>\nin an Android.mk is made into a pseudotarget.  For example, <code>make\nruntime</code> might be shorthand for <code>make\nout/linux-x86-debug/system/bin/runtime</code> (which would work), and\n<code>make libkjs</code> might be shorthand for <code>make\nout/linux-x86-debug/system/lib/libkjs.so</code> (which would also work).</li>\n<li><b>targets</b> - <code>make targets</code> will print a list of all of\nthe LOCAL_MODULE names you can make.</li>\n</ul>\n\n<h3><a name=\"templates\"/>How to add another component to the build - Android.mk templates</h3>\n<p>You have a new library, a new app, or a new executable.  For each of the\ncommon types of modules, there is a corresponding file in the templates\ndirectory.  It will usually be enough to copy one of these, and fill in your\nown values.  Some of the more esoteric values are not included in the\ntemplates, but are instead just documented here, as is the documentation\non using custom tools to generate files.</p>\n<p>Mostly, you can just look for the TODO comments in the templates and do\nwhat it says.  Please remember to delete the TODO comments when you're done\nto keep the files clean.  The templates have minimal documentation in them,\nbecause they're going to be copied, and when that gets stale, the copies just\nwon't get updated.  So read on...</p>\n\n<h4>Apps</h4>\n<p>Use the <code>templates/apps</code> file.</p>\n<p>This template is pretty self-explanitory.  See the variables below for more\ndetails.</p>\n\n<h4>Java Libraries</h4>\n<p>Use the <code>templates/java_library</code> file.</p>\n<p>The interesting thing here is the value of LOCAL_MODULE, which becomes\nthe name of the jar file.  (Actually right now, we're not making jar files yet,\njust directories of .class files,  but the directory is named according to\nwhat you put in LOCAL_MODULE).  This name will be what goes in the \nLOCAL_JAVA_LIBRARIES variable in modules that depend on your java library.</p>\n\n<h4>C/C++ Executables</h4>\n<p>Use the <code>templates/executable</code> file, or the\n<code>templates/executable_host</code> file.</p>\n<p>This template has a couple extra options that you usually don't need.\nPlease delete the ones you don't need, and remove the TODO comments.  It makes\nthe rest of them easier to read, and you can always refer back to the templates\nif you need them again later.</p>\n<p>By default, on the target these are built into /system/bin, and on the\nhost, they're built into <combo>/host/bin.  These can be overridden by setting\n<code>LOCAL_MODULE_PATH</code> or <code>LOCAL_MODULE_RELATIVE_PATH</code>.  See\n<a href=\"#moving-targets\">Putting targets elsewhere</a>\nfor more.</p>\n\n<h4>Shared Libraries</h4>\n<p>Use the <code>templates/shared_library</code> file, or the\n<code>templates/shared_library_host</code> file.</p>\n<p>Remember that on the target, we use shared libraries, and on the host,\nwe use static libraries, since executable size isn't as big an issue, and it\nsimplifies distribution in the SDK.</p>\n\n<h4>Static Libraries</h4>\n<p>Use the <code>templates/static_library</code> file, or the\n<code>templates/static_library_host</code> file.</p>\n<p>Remember that on the target, we use shared libraries, and on the host,\nwe use static libraries, since executable size isn't as big an issue, and it\nsimplifies distribution in the SDK.</p>\n\n<h4><a name=\"custom-tools\"/>Using Custom Tools</h4>\n<p>If you have a tool that generates source files for you, it's possible\nto have the build system get the dependencies correct for it.  Here are\na couple of examples.  <code>$@</code> is the make built-in variable for\n\"the current target.\" The <font color=red>red</font> parts are the parts you'll\nneed to change.</p>\n\n<p>You need to put this after you have declared <code>LOCAL_PATH</code> and\n<code>LOCAL_MODULE</code>, because the <code>$(local-generated-sources-dir)</code>\nand <code>$(local-host-generated-sources-dir)</code> macros use these variables\nto determine where to put the files.\n\n<h5>Example 1</h5>\n<p>Here, there is one generated file, called\nchartables.c, which doesn't depend on anything.  And is built by the tool\nbuilt to $(HOST_OUT_EXECUTABLES)/dftables.  Note on the second to last line\nthat a dependency is created on the tool.</p>\n<pre>\nintermediates:= $(local-generated-sources-dir)\nGEN := $(intermediates)/<font color=red>chartables.c</font>\n$(GEN): PRIVATE_CUSTOM_TOOL = <font color=red>$(HOST_OUT_EXECUTABLES)/dftables $@</font>\n$(GEN): <font color=red>$(HOST_OUT_EXECUTABLES)/dftables</font>\n\t$(transform-generated-source)\nLOCAL_GENERATED_SOURCES += $(GEN)\n</pre>\n\n<h5>Example 2</h5>\n<p>Here as a hypothetical example, we use use cat as if it were to transform\na file.  Pretend that it does something useful.  Note how we use a\ntarget-specific variable called PRIVATE_INPUT_FILE to store the name of the\ninput file.</p>\n<pre>\nintermediates:= $(local-generated-sources-dir)\nGEN := $(intermediates)/<font color=red>file.c</font>\n$(GEN): PRIVATE_INPUT_FILE := $(LOCAL_PATH)/<font color=red>input.file</font>\n$(GEN): PRIVATE_CUSTOM_TOOL = <font color=red>cat $(PRIVATE_INPUT_FILE) &gt; $@</font>\n$(GEN): <font color=red>$(LOCAL_PATH)/input.file</font>\n\t$(transform-generated-source)\nLOCAL_GENERATED_SOURCES += $(GEN)\n</pre>\n\n<h5>Example 3</h5>\n<p>If you have several files that are all similar in\nname, and use the same tool, you can combine them.  (here the *.lut.h files are\nthe generated ones, and the *.cpp files are the input files)</p>\n<pre>\nintermediates:= $(local-generated-sources-dir)\nGEN := $(addprefix $(intermediates)<font color=red>/kjs/, \\\n            array_object.lut.h \\\n            bool_object.lut.h \\</font>\n        )\n$(GEN): PRIVATE_CUSTOM_TOOL = <font color=red>perl libs/WebKitLib/WebKit/JavaScriptCore/kjs/create_hash_table $< -i > $@</font>\n$(GEN): $(intermediates)/<font color=red>%.lut.h</font> : $(LOCAL_PATH)/<font color=red>%.cpp</font>\n\t$(transform-generated-source)\nLOCAL_GENERATED_SOURCES += $(GEN)\n</pre>\n\n<h3><a name=\"unbundled-build\"/>Unbundled build</h3>\n<p>Unbundled build has several meanings by the context.\nLet me explain the meaning by the flags related to \"unbundled build\"</p>\n<h4>TARGET_BUILD_UNBUNDLED</h4>\n<p>\n    The source tree might not have the full platform sources. It turns on\n    <code>TARGET_BUILD_USE_PREBUILT_SDKS</code>, unless\n    <code>UNBUNDLED_BUILD_SDKS_FROM_SOURCE</code> is set. It is always set if\n    <code>TARGET_BUILD_APPS</code> or <code>TARGET_BUILD_UNBUNDLED_IMAGE</code> is set.\n</p>\n<h4>TARGET_BUILD_USE_PREBUILT_SDKS</h4>\n<p>It is an internal flag. If it is set, prebuilt SDKs are used, even if a module's\n<code>LOCAL_SDK_VERSION</code> is <code>current</code> (including <code>system_current</code>,\n<code>core_current</code>, and so on). If it is unset, build current SDKs,\nand use them as usual.</p>\n<h4>DISABLE_PREOPT</h4>\n<p>It is an internal flag as well. If it is set, dexpreopt is disabled.\nIt is always set if <code>TARGET_BUILD_APPS</code> or <code>TARGET_BUILD_UNBUNDLED_IMAGE</code> is set,\nbecause dexpreopt tightly depends on the platform.</p>\n<h4>TARGET_BUILD_APPS</h4>\n<p>Build the apps that can be distributed outside the platform, so it turns on\n<code>TARGET_BUILD_UNBUNDLED</code> and <code>DISABLE_PREOPT</code>.\nAlso, it turns on <code>TARGET_BUILD_USE_PREBUILT_SDKS</code>, unless\n<code>UNBUNDLED_BUILD_SDKS_FROM_SOURCE</code> is set.</p>\n<h4>TARGET_BUILD_UNBUNDLED_IMAGE</h4>\n<p>It is similar to <code>TARGET_BUILD_APPS</code>, but its target is an unbundled partition\n(such as the vendor partition). Accordingly, it sets <code>TARGET_BUILD_UNBUNDLED</code> and <code>DISABLE_PREOPT</code>.\nWe can call the partition unbundled, because the partition can be distributed outside the platform.\nAnd also, it turns on <code>TARGET_BUILD_USE_PREBUILT_SDKS</code>, unless\n<code>UNBUNDLED_BUILD_SDKS_FROM_SOURCE</code> is set.</p>\n\n<h3><a name=\"platform-specific\"/>Platform specific conditionals</h3>\n<p>Sometimes you need to set flags specifically for different platforms.  Here\nis a list of which values the different build-system defined variables will be\nset to and some examples.</p>\n<table cellspacing=25>\n<tr>\n    <td valign=top align=center>\n        <b>HOST_OS</b><br/>\n        linux<br/>\n        darwin\n    </td>\n    <td valign=top align=center>\n        <b>HOST_ARCH</b><br/>\n        x86<br/>\n        x86_64\n    </td>\n    <td valign=top align=center>\n        <b>HOST_BUILD_TYPE</b><br/>\n        release<br/>\n        debug\n    </td>\n</tr>\n<tr>\n    <td valign=top align=center>\n        <b>TARGET_ARCH</b><br/>\n        arm<br/>\n        arm64<br/>\n        x86<br/>\n        x86_64\n    </td>\n    <td valign=top align=center>\n        <b>TARGET_BUILD_TYPE</b><br/>\n        release<br/>\n        debug\n    </td>\n</tr>\n</table>\n\n<p>There are also special variables to use instead of conditionals. Many of the\nnormal variables (LOCAL_SRC_FILES, LOCAL_CFLAGS, etc) can be conditionally added\nto with _{arch} _{32|64}, and for the host, _{os}.</p>\n\n<h4>Some Examples</h4>\n<pre>ifeq ($(TARGET_BUILD_TYPE),release)\nLOCAL_CFLAGS += -DNDEBUG=1\nendif\n\nLOCAL_CFLAGS_arm += -DTARGET_IS_ARM\n\nLOCAL_CFLAGS_64 += -DBIG_POINTER\n\n# from libutils\n# Use the futex based mutex and condition variable\n# implementation from android-arm because it's shared mem safe\nLOCAL_SRC_FILES_linux += futex_synchro.c\nLOCAL_LDLIBS_linux += -lrt -ldl\n\n</pre>\n\n\n<h3><a name=\"moving-modules\"/>Putting modules elsewhere</h3>\n<p>If you have modules that normally go somewhere, and you need to have them\nbuild somewhere else, read this.</p>\n<p>If you have modules that need to go in a subdirectory of their normal\nlocation, for example HAL modules that need to go in /system/lib/hw or\n/vendor/lib/hw, set LOCAL_MODULE_RELATIVE_PATH in your Android.mk, for\nexample:</p>\n<pre>\nLOCAL_MODULE_RELATIVE_PATH := hw\n</pre>\n<p>If you have modules that need to go in an entirely different location, for\nexample the root filesystem instead of in /system, add these lines to your\nAndroid.mk:</p>\n<pre>\nLOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)\nLOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)\n</pre>\n<p>For executables and libraries, you need to specify a\n<code>LOCAL_UNSTRIPPED_PATH</code> location if you specified a\n<code>LOCAL_MODULE_PATH</code>, because on target builds, we keep\nthe unstripped executables so GDB can find the symbols.\n<code>LOCAL_UNSTRIPPED_PATH</code> is not necessary if you only specified\n<code>LOCAL_MODULE_RELATIVE_PATH</code>.</p>\n<p>Look in <code>core/envsetup.mk</code> for all of the variables defining\nplaces to build things.</p>\n\n\n<h3>Android.mk variables</h3>\n<p>These are the variables that you'll commonly see in Android.mk files, listed\nalphabetically.</p>\n<p>But first, a note on variable naming:\n<ul>\n    <li><b>LOCAL_</b> - These variables are set per-module.  They are cleared\n    by the <code>include $(CLEAR_VARS)</code> line, so you can rely on them\n    being empty after including that file.  Most of the variables you'll use\n    in most modules are LOCAL_ variables.</li>\n    <li><b>PRIVATE_</b> - These variables are make-target-specific variables.  That\n    means they're only usable within the commands for that module.  It also\n    means that they're unlikely to change behind your back from modules that\n    are included after yours.  This \n    <a href=\"http://www.gnu.org/software/make/manual/make.html#Target_002dspecific\">link to the make documentation</a>\n    describes more about target-specific variables.  Please note that there\n    are a couple of these laying around the tree that aren't prefixed with\n    PRIVATE_.  It is safe, and they will be fixed as they are discovered.\n    Sorry for the confusion.</li>\n    <li><b>INTERNAL_</b> - These variables are critical to functioning of\n    the build system, so you shouldn't create variables named like this, and\n    you probably shouldn't be messing with these variables in your makefiles.\n    </li>\n    <li><b>HOST_</b> and <b>TARGET_</b> - These contain the directories\n    and definitions that are specific to either the host or the target builds.\n    Do not set variables that start with HOST_ or TARGET_ in your makefiles.\n    </li>\n    <li><b>HOST_CROSS_</b> - These contain the directories and definitions that\n    are specific to cross-building host binaries. The common case is building\n    windows host tools on linux. Do not set variables that start with\n    HOST_CROSS_ in your makefiles.\n    </li>\n    <li><b>BUILD_</b> and <b>CLEAR_VARS</b> - These contain the names of\n    well-defined template makefiles to include.  Some examples are CLEAR_VARS\n    and BUILD_HOST_PACKAGE.</li>\n    <li>Any other name is fair-game for you to use in your Android.mk.  However,\n    remember that this is a non-recursive build system, so it is possible that\n    your variable will be changed by another Android.mk included later, and be\n    different when the commands for your rule / module are executed.</li>\n</ul>\n</p>\n\n<h4>LOCAL_ANNOTATION_PROCESSORS</h4>\n<p>Set this to a list of modules built with <code>BUILD_HOST_JAVA_LIBRARY</code>\nto have their jars passed to javac with -processorpath for use as annotation\nprocessors.</p>\n\n<h4>LOCAL_ANNOTATION_PROCESSOR_CLASSES</h4>\n<p>Set this to a list of classes to be passed to javac as -processor arguments.\nThis list is would be unnecessary, as javac will autodetect annotation processor\nclasses, except that the Grok tool that is used on the Android source code\ndoes not autodetect them and requires listing them manually.</p>\n\n<h4>LOCAL_ASSET_FILES</h4>\n<p>In Android.mk files that <code>include $(BUILD_PACKAGE)</code> set this\nto the set of files you want built into your app.  Usually:</p>\n<p><code>LOCAL_ASSET_FILES += $(call find-subdir-assets)</code></p>\n<p>This will probably change when we switch to ant for the apps' build\nsystem.</p>\n\n<h4>LOCAL_CC</h4>\n<p>If you want to use a different C compiler for this module, set LOCAL_CC\nto the path to the compiler.  If LOCAL_CC is blank, the appropriate default\ncompiler is used.</p>\n\n<h4>LOCAL_CXX</h4>\n<p>If you want to use a different C++ compiler for this module, set LOCAL_CXX\nto the path to the compiler.  If LOCAL_CXX is blank, the appropriate default\ncompiler is used.</p>\n\n<h4>LOCAL_CFLAGS</h4>\n<p>If you have additional flags to pass into the C or C++ compiler, add\nthem here.  For example:</p>\n<p><code>LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1</code></p>\n\n<h4>LOCAL_CPPFLAGS</h4>\n<p>If you have additional flags to pass into <i>only</i> the C++ compiler, add\nthem here.  For example:</p>\n<p><code>LOCAL_CPPFLAGS += -ffriend-injection</code></p>\n<code>LOCAL_CPPFLAGS</code> is guaranteed to be after <code>LOCAL_CFLAGS</code>\non the compile line, so you can use it to override flags listed in\n<code>LOCAL_CFLAGS</code>.\n\n<h4>LOCAL_CPP_EXTENSION</h4>\n<p>If your C++ files end in something other than \"<code>.cpp</code>\",\nyou can specify the custom extension here.  For example:</p>\n<p><code>LOCAL_CPP_EXTENSION := .cc</code></p>\nNote that all C++ files for a given module must have the same\nextension; it is not currently possible to mix different extensions.\n\n<h4>LOCAL_NO_DEFAULT_COMPILER_FLAGS</h4>\n<p>Normally, the compile line for C and C++ files includes global include\npaths and global cflags.  If <code>LOCAL_NO_DEFAULT_COMPILER_FLAGS</code>\nis non-empty, none of the default includes or flags will be used when compiling\nC and C++ files in this module.\n<code>LOCAL_C_INCLUDES</code>, <code>LOCAL_CFLAGS</code>, and\n<code>LOCAL_CPPFLAGS</code> will still be used in this case, as will\nany <code>DEBUG_CFLAGS</code> that are defined for the module.\n\n<h4>LOCAL_COPY_HEADERS</h4>\n<p class=warning>This will be going away.</p>\n<p>The set of files to copy to the install include tree.  You must also\nsupply <code>LOCAL_COPY_HEADERS_TO</code>.</p>\n<p>This is going away because copying headers messes up the error messages, and\nmay lead to people editing those headers instead of the correct ones.  It also\nmakes it easier to do bad layering in the system, which we want to avoid.  We\nalso aren't doing a C/C++ SDK, so there is no ultimate requirement to copy any\nheaders.</p>\n\n<h4>LOCAL_COPY_HEADERS_TO</h4>\n<p class=warning>This will be going away.</p>\n<p>The directory within \"include\" to copy the headers listed in\n<code>LOCAL_COPY_HEADERS</code> to.</p>\n<p>This is going away because copying headers messes up the error messages, and\nmay lead to people editing those headers instead of the correct ones.  It also\nmakes it easier to do bad layering in the system, which we want to avoid.  We\nalso aren't doing a C/C++ SDK, so there is no ultimate requirement to copy any\nheaders.</p>\n\n<h4>LOCAL_C_INCLUDES</h4>\n<p>Additional directories to instruct the C/C++ compilers to look for header\nfiles in.  These paths are rooted at the top of the tree.  Use\n<code>LOCAL_PATH</code> if you have subdirectories of your own that you\nwant in the include paths.  For example:</p>\n<p><code>\nLOCAL_C_INCLUDES += extlibs/zlib-1.2.3<br/>\nLOCAL_C_INCLUDES += $(LOCAL_PATH)/src\n</code></p>\n<p>You should not add subdirectories of include to\n<code>LOCAL_C_INCLUDES</code>, instead you should reference those files\nin the <code>#include</code> statement with their subdirectories.  For\nexample:</p>\n<p><code>#include &lt;utils/KeyedVector.h&gt;</code><br/>\nnot <code><s>#include &lt;KeyedVector.h&gt;</s></code></p>\n<p>There are some components that are doing this wrong, and should be cleaned\nup.</p>\n\n<h4>LOCAL_MODULE_TAGS</h4>\n<p>Set <code>LOCAL_MODULE_TAGS</code> to any number of whitespace-separated\ntags.  If the tag list is empty or contains <code>droid</code>, the module\nwill get installed as part of a <code>make droid</code>.  Otherwise, it will\nonly get installed by running <code>make &lt;your-module&gt;</code>\nor with the <code>make all</code> pseudotarget.</p>\n\n<h4>LOCAL_REQUIRED_MODULES</h4>\n<p>Set <code>LOCAL_REQUIRED_MODULES</code> to any number of whitespace-separated\nmodule names, like \"libblah\" or \"Email\".  If this module is installed, all\nof the modules that it requires will be installed as well.  This can be\nused to, e.g., ensure that necessary shared libraries or providers are\ninstalled when a given app is installed.\n\n<h4>LOCAL_FORCE_STATIC_EXECUTABLE</h4>\n<p>If your executable should be linked statically, set \n<code>LOCAL_FORCE_STATIC_EXECUTABLE:=true</code>.  There is a very short\nlist of libraries that we have in static form (currently only libc).</p>\n\n<h4>LOCAL_GENERATED_SOURCES</h4>\n<p>Files that you add to <code>LOCAL_GENERATED_SOURCES</code> will be\nautomatically generated and then linked in when your module is built.\nSee the <a href=\"#custom-tools\">Custom Tools</a> template makefile for an\nexample.</p>\n\n<h4>LOCAL_JAVACFLAGS</h4>\n<p>If you have additional flags to pass into the javac compiler, add\nthem here.  For example:</p>\n<p><code>LOCAL_JAVACFLAGS += -Xlint:deprecation</code></p>\n\n<h4>LOCAL_ERROR_PRONE_FLAGS</h4>\n<p>If you have additional flags to pass into the error prone compiler, add\nthem here.  For example:</p>\n<p><code>LOCAL_ERROR_PRONE_FLAGS += -Xep:ClassCanBeStatic:ERROR</code></p>\n\n<h4>LOCAL_JAVA_LIBRARIES</h4>\n<p>When linking Java apps and libraries, <code>LOCAL_JAVA_LIBRARIES</code>\nspecifies which sets of java classes to include.  Currently there are\ntwo of these: <code>core</code> and <code>framework</code>.\nIn most cases, it will look like this:</p>\n<p><code>LOCAL_JAVA_LIBRARIES := core framework</code></p>\n<p>Note that setting <code>LOCAL_JAVA_LIBRARIES</code> is not necessary\n(and is not allowed) when building an APK with\n\"<code>include $(BUILD_PACKAGE)</code>\".  The appropriate libraries\nwill be included automatically.</p>\n\n<h4>LOCAL_LDFLAGS</h4>\n<p>You can pass additional flags to the linker by setting\n<code>LOCAL_LDFLAGS</code>.  Keep in mind that the order of parameters is\nvery important to ld, so test whatever you do on all platforms.</p>\n\n<h4>LOCAL_LDLIBS</h4>\n<p><code>LOCAL_LDLIBS</code> allows you to specify additional libraries\nthat are not part of the build for your executable or library.  Specify\nthe libraries you want in -lxxx format; they're passed directly to the \nlink line.  However, keep in mind that there will be no dependency generated\nfor these libraries.  It's most useful in simulator builds where you want\nto use a library preinstalled on the host.  The linker (ld) is a particularly\nfussy beast, so it's sometimes necessary to pass other flags here if you're\ndoing something sneaky. Some examples:</p>\n<p><code>LOCAL_LDLIBS += -lcurses -lpthread<br/>\nLOCAL_LDLIBS += -Wl,-z,origin\n</code></p>\n\n<h4>LOCAL_NO_MANIFEST</h4>\n<p>If your package doesn't have a manifest (AndroidManifest.xml), then\nset <code>LOCAL_NO_MANIFEST:=true</code>.  The common resources package\ndoes this.</p>\n\n<h4>LOCAL_PACKAGE_NAME</h4>\n<p><code>LOCAL_PACKAGE_NAME</code> is the name of an app.  For example,\nDialer, Contacts, etc.  This will probably change or go away when we switch\nto an ant-based build system for the apps.</p>\n\n<h4>LOCAL_PATCH_MODULE (experimental option)</h4>\n<p>As of January 2018, you almost certainly don't need this option, so please\nask and only use it if you understand what you're doing. This feature is\nexperimental and may go away in future.</p>\n<p>\nWhen compiling language level 9+ .java code in packages that are part of a\na system module, <code>LOCAL_PATCH_MODULE</code> names the module that your\nsources and dependencies should be patched into. The Android runtime currently\n(Jan 2018) doesn't implement the JEP 261 module system so this option is only\nsupported at compile time. It should only be needed to compile tests in packages\nthat exist in libcore and which are inconvenient to move elsewhere.\n</p>\n\n<h4>LOCAL_PATH</h4>\n<p>The directory your Android.mk file is in. You can set it by putting the\nfollowing as the first line in your Android.mk:</p>\n<p><code>LOCAL_PATH := $(my-dir)</code></p>\n<p>The <code>my-dir</code> macro uses the \n<code><a href=\"http://www.gnu.org/software/make/manual/make.html#MAKEFILE_005fLIST-Variable\">MAKEFILE_LIST</a></code>\nvariable, so you must call it before you include any other makefiles.  Also,\nconsider that any subdirectories you inlcude might reset LOCAL_PATH, so do your\nown stuff before you include them.  This also means that if you try to write\nseveral <code>include</code> lines that reference <code>LOCAL_PATH</code>,\nit won't work, because those included makefiles might reset LOCAL_PATH.\n\n<h4>LOCAL_POST_PROCESS_COMMAND</h4>\n<p>For host executables, you can specify a command to run on the module\nafter it's been linked.  You might have to go through some contortions\nto get variables right because of early or late variable evaluation:</p>\n<p><code>module := $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)<br/>\nLOCAL_POST_PROCESS_COMMAND := /Developer/Tools/Rez -d __DARWIN__ -t APPL\\<br/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-d __WXMAC__ -o $(module) Carbon.r\n</code></p>\n\n<h4>LOCAL_PREBUILT_EXECUTABLES</h4>\n<p>When including $(BUILD_MULTI_PREBUILT) or $(BUILD_HOST_PREBUILT), set these\nto executables that you want copied.  They're located automatically into the\nright bin directory.</p>\n\n<h4>LOCAL_PREBUILT_LIBS</h4>\n<p>When including $(BUILD_MULTI_PREBUILT) or $(BUILD_HOST_PREBUILT), set these\nto libraries that you want copied.  They're located automatically into the\nright lib directory.</p>\n\n<h4>LOCAL_SHARED_LIBRARIES</h4>\n<p>These are the libraries you directly link against.  You don't need to\npass transitively included libraries.  Specify the name without the suffix:</p>\n<p><code>LOCAL_SHARED_LIBRARIES := \\<br/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;libutils \\<br/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;libui \\<br/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;libaudio \\<br/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;libexpat \\<br/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;libsgl\n</code></p>\n\n<h4>LOCAL_SRC_FILES</h4>\n<p>The build system looks at <code>LOCAL_SRC_FILES</code> to know what source\nfiles to compile -- .cpp .c .y .l .java.  For lex and yacc files, it knows\nhow to correctly do the intermediate .h and .c/.cpp files automatically.  If\nthe files are in a subdirectory of the one containing the Android.mk, prefix\nthem with the directory name:</p>\n<p><code>LOCAL_SRC_FILES := \\<br/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;file1.cpp \\<br/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;dir/file2.cpp\n</code></p>\n\n<h4>LOCAL_STATIC_LIBRARIES</h4>\n<p>These are the static libraries that you want to include in your module.\nMostly, we use shared libraries, but there are a couple of places, like\nhost executables where we use static libraries instead.\n<p><code>LOCAL_STATIC_LIBRARIES := \\<br/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;libutils \\<br/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;libtinyxml\n</code></p>\n\n<h4>LOCAL_MODULE</h4>\n<p><code>LOCAL_MODULE</code> is the name of what's supposed to be generated\nfrom your Android.mk.  For exmample, for libkjs, the <code>LOCAL_MODULE</code>\nis \"libkjs\" (the build system adds the appropriate suffix -- .so .dylib .dll).\nFor app modules, use <code>LOCAL_PACKAGE_NAME</code> instead of \n<code>LOCAL_MODULE</code>.  We're planning on switching to ant for the apps,\nso this might become moot.</p>\n\n<h4>LOCAL_MODULE_PATH</h4>\n<p>Instructs the build system to put the module somewhere other than what's\nnormal for its type.  If you override this, make sure you also set\n<code>LOCAL_UNSTRIPPED_PATH</code> if it's an executable or a shared library\nso the unstripped binary has somewhere to go.  An error will occur if you forget\nto.</p>\n<p>See <a href=\"#moving-modules\">Putting modules elsewhere</a> for more.</p>\n\n<h4>LOCAL_MODULE_RELATIVE_PATH</h4>\n<p>Instructs the build system to put the module in a subdirectory under the\ndirectory that is normal for its type.  If you set this you do not need to\nset <code>LOCAL_UNSTRIPPED_PATH</code>, the unstripped binaries will also use\nthe relative path.</p>\n<p>See <a href=\"#moving-modules\">Putting modules elsewhere</a> for more.</p>\n\n<h4>LOCAL_MODULE_HOST_OS</h4>\n<p>This specifies which OSes are supported by this host module. It is not used\nfor target builds. The accepted values here are combinations of\n<code>linux</code>, <code>darwin</code>, and <code>windows</code>. By default,\nlinux and darwin(MacOS) are considered to be supported. If a module should\nbuild under windows, you must specify windows, and any others to be supported.\nSome examples:</p>\n<p><code>LOCAL_MODULE_HOST_OS := linux<br/>\nLOCAL_MODULE_HOST_OS := darwin linux windows</code></p>\n\n<h4>LOCAL_UNSTRIPPED_PATH</h4>\n<p>Instructs the build system to put the unstripped version of the module\nsomewhere other than what's normal for its type.  Usually, you override this\nbecause you overrode <code>LOCAL_MODULE_PATH</code> for an executable or a\nshared library.  If you overrode <code>LOCAL_MODULE_PATH</code>, but not \n<code>LOCAL_UNSTRIPPED_PATH</code>, an error will occur.</p>\n<p>See <a href=\"#moving-modules\">Putting modules elsewhere</a> for more.</p>\n\n<h4>LOCAL_WHOLE_STATIC_LIBRARIES</h4>\n<p>These are the static libraries that you want to include in your module without allowing\nthe linker to remove dead code from them. This is mostly useful if you want to add a static library\nto a shared library and have the static library's content exposed from the shared library.\n<p><code>LOCAL_WHOLE_STATIC_LIBRARIES := \\<br/>\n\t&nbsp;&nbsp;&nbsp;&nbsp;libsqlite3_android<br/>\n</code></p>\n\n<h4>LOCAL_YACCFLAGS</h4>\n<p>Any flags to pass to invocations of yacc for your module.  A known limitation\nhere is that the flags will be the same for all invocations of YACC for your\nmodule.  This can be fixed.  If you ever need it to be, just ask.</p>\n<p><code>LOCAL_YACCFLAGS := -p kjsyy</code></p>\n\n\n\n<h2>Implementation Details</h2>\n\n<p>You should never have to touch anything in the config directory unless\nyou're adding a new platform, new tools, or adding new features to the\nbuild system.  In general, please consult with the build system owner(s)\n(<a href=\"mailto:android-build-team\">android-build-team</a>) before you go\nmucking around in here.  That said, here are some notes on what's going on\nunder the hood.</p>\n\n<h3>Environment Setup / buildspec.mk Versioning</h3>\n<p>In order to make easier for people when the build system changes, when\nit is necessary to make changes to buildspec.mk or to rerun the environment\nsetup scripts, they contain a version number in the variable\nBUILD_ENV_SEQUENCE_NUMBER.  If this variable does not match what the build\nsystem expects, it fails printing an error message explaining what happened.\nIf you make a change that requires an update, you need to update two places\nso this message will be printed.\n<ul>\n    <li>In core/envsetup.mk, increment the\n        CORRECT_BUILD_ENV_SEQUENCE_NUMBER definition.</li>\n    <li>In buildspec.mk.default, update the BUILD_ENV_SEQUENCE_DUMBER\n        definition to match the one in core/envsetup.mk</li>\n</ul>\nThe scripts automatically get the value from the build system, so they will\ntrigger the warning as well.\n</p>\n\n<h3>Additional makefile variables</h3>\n<p>You probably shouldn't use these variables.  Please consult\n<a href=\"mailto:android-build-team\">android-build-team</a> before using them.\nThese are mostly there for workarounds for other issues, or things that aren't\ncompletely done right.</p>\n\n<h4>LOCAL_ADDITIONAL_DEPENDENCIES</h4>\n<p>If your module needs to depend on anything else that\nisn't actually built in to it, you can add those make targets to \n<code>LOCAL_ADDITIONAL_DEPENDENCIES</code>.  Usually this is a workaround\nfor some other dependency that isn't created automatically.</p>\n\n<h4>LOCAL_BUILT_MODULE</h4>\n<p class=warning>This should not be used, since multiple binaries are now\ncreated from a single module defintiion.</p>\n<p>When a module is built, the module is created in an intermediate\ndirectory then copied to its final location.  LOCAL_BUILT_MODULE is\nthe full path to the intermediate file.  See LOCAL_INSTALLED_MODULE\nfor the path to the final installed location of the module.</p>\n\n<h4>LOCAL_IS_HOST_MODULE</h4>\n<p>Set by the host_xxx.mk includes to tell base_rules.mk and the other\nincludes that we're building for the host.</p>\n\n<h4>LOCAL_INSTALLED_MODULE</h4>\n<p class=warning>This should not be used, since multiple binaries are now\ncreated from a single module defintiion.</p>\n<p>The fully qualified path name of the final location of the module.\nSee LOCAL_BUILT_MODULE for the location of the intermediate file that\nthe make rules should actually be constructing.</p>\n\n<h4>LOCAL_MODULE_CLASS</h4>\n<p>Which kind of module this is.  This variable is used to construct other\nvariable names used to locate the modules.  See base_rules.mk and\nenvsetup.mk.</p>\n\n<h4>LOCAL_MODULE_SUFFIX</h4>\n<p>The suffix that will be appended to <code>LOCAL_MODULE</code> to form\n<code>LOCAL_MODULE_NAME</code>.  For example, .so, .a, .dylib.</p>\n\n<h4>LOCAL_STRIP_MODULE</h4>\n<p>If set to true (the default), the binary will be stripped and a debug\nlink will be set up so that GDB will still work. If set to no_debuglink,\nthe binary will be stripped, but no debug link will be added. If set to\nkeep_symbols, it will strip the debug information, but keep the symbol table.\nAny other value will prevent stripping.</p>\n\n<h4>LOCAL_SYSTEM_SHARED_LIBRARIES</h4>\n<p>Used while building the base libraries: libc, libm, libdl.  Usually\nit should be set to \"none,\" as it is in $(CLEAR_VARS).  When building\nthese libraries, it's set to the ones they link against.  For example,\nlibc, libstdc++ and libdl don't link against anything, and libm links against\nlibc.  Normally, when the value is none, these libraries are automatically\nlinked in to executables and libraries, so you don't need to specify them\nmanually.</p>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "core/build_id.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# BUILD_ID is usually used to specify the branch name\n# (like \"MAIN\") or a branch name and a release candidate\n# (like \"CRB01\").  It must be a single word, and is\n# capitalized by convention.\n\nBUILD_ID=MAIN\n"
  },
  {
    "path": "core/build_rro_package.mk",
    "content": "#############################################################################\n## Standard rules for installing runtime resouce overlay APKs.\n##\n## Set LOCAL_RRO_THEME to the theme name if the package should apply only to\n## a particular theme as set by ro.boot.vendor.overlay.theme system property.\n##\n## If LOCAL_RRO_THEME is not set, the package will apply always, independent\n## of themes.\n##\n#############################################################################\n\nLOCAL_IS_RUNTIME_RESOURCE_OVERLAY := true\n\nifneq ($(LOCAL_SRC_FILES),)\n  $(error runtime resource overlay package should not contain sources)\nendif\n\npartition :=\nifeq ($(strip $(LOCAL_ODM_MODULE)),true)\n  partition := $(TARGET_OUT_ODM)\nelse ifeq ($(strip $(LOCAL_VENDOR_MODULE)),true)\n  partition := $(TARGET_OUT_VENDOR)\nelse ifeq ($(strip $(LOCAL_SYSTEM_EXT_MODULE)),true)\n  partition := $(TARGET_OUT_SYSTEM_EXT)\nelse\n  partition := $(TARGET_OUT_PRODUCT)\nendif\n\nifeq ($(LOCAL_RRO_THEME),)\n  LOCAL_MODULE_PATH := $(partition)/overlay\nelse\n  LOCAL_MODULE_PATH := $(partition)/overlay/$(LOCAL_RRO_THEME)\nendif\n\n# Do not remove resources without default values nor dedupe resource\n# configurations with the same value\nLOCAL_AAPT_FLAGS += \\\n    --no-resource-deduping \\\n    --no-resource-removal\n\npartition :=\n\ninclude $(BUILD_SYSTEM)/package.mk\n"
  },
  {
    "path": "core/cc_prebuilt_internal.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n############################################################\n# Internal build rules for native prebuilt modules\n############################################################\n\nprebuilt_module_classes := STATIC_LIBRARIES SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS\nifeq ($(filter $(prebuilt_module_classes),$(LOCAL_MODULE_CLASS)),)\n$(call pretty-error,cc_prebuilt_internal.mk is for $(prebuilt_module_classes) modules only)\nendif\n\nmy_strip_module := $(firstword \\\n  $(LOCAL_STRIP_MODULE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) \\\n  $(LOCAL_STRIP_MODULE))\n\nifeq (SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS))\n  ifeq ($(LOCAL_IS_HOST_MODULE)$(my_strip_module),)\n    # Strip but not try to add debuglink\n    my_strip_module := no_debuglink\n  endif\nendif\n\nifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS)),)\n  prebuilt_module_is_a_library := true\nelse\n  prebuilt_module_is_a_library :=\nendif\n\n# Don't install static libraries by default.\nifndef LOCAL_UNINSTALLABLE_MODULE\nifeq (STATIC_LIBRARIES,$(LOCAL_MODULE_CLASS))\n  LOCAL_UNINSTALLABLE_MODULE := true\nendif\nendif\n\nmy_check_elf_file_shared_lib_files :=\n\nifneq ($(filter true keep_symbols no_debuglink mini-debug-info,$(my_strip_module)),)\n  ifdef LOCAL_IS_HOST_MODULE\n    $(call pretty-error,Cannot strip/pack host module)\n  endif\n  ifeq ($(filter SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)\n    $(call pretty-error,Can strip/pack only shared libraries or executables)\n  endif\n  # Set the arch-specific variables to set up the strip rules\n  LOCAL_STRIP_MODULE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) := $(my_strip_module)\n  include $(BUILD_SYSTEM)/dynamic_binary.mk\n  built_module := $(linked_module)\n\n  ifneq ($(LOCAL_SDK_VERSION),)\n    # binary.mk filters out NDK_KNOWN_LIBS from my_shared_libs, thus those NDK libs are not added\n    # to DEPENDENCIES_ON_SHARED_LIBRARIES. Assign $(my_ndk_shared_libraries_fullpath) to\n    # my_check_elf_file_shared_lib_files so that check_elf_file.py can see those NDK stub libs.\n    my_check_elf_file_shared_lib_files := $(my_ndk_shared_libraries_fullpath)\n  endif\nelse  # my_strip_module not true\n  include $(BUILD_SYSTEM)/base_rules.mk\n  built_module := $(LOCAL_BUILT_MODULE)\n\nifdef prebuilt_module_is_a_library\nEXPORTS_LIST += $(intermediates)\nEXPORTS.$(intermediates).FLAGS := $(foreach d,$(LOCAL_EXPORT_C_INCLUDE_DIRS),-I $(d))\nEXPORTS.$(intermediates).DEPS := $(LOCAL_EXPORT_C_INCLUDE_DEPS)\n\ninclude $(BUILD_SYSTEM)/allowed_ndk_types.mk\n\nifdef LOCAL_SDK_VERSION\nmy_link_type := native:ndk:$(my_ndk_stl_family):$(my_ndk_stl_link_type)\nelse ifeq ($(call module-in-vendor-or-product),true)\n    _name := $(patsubst %.vendor,%,$(LOCAL_MODULE))\n    _name := $(patsubst %.product,%,$(LOCAL_MODULE))\n    ifneq ($(filter $(_name),$(VNDK_CORE_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) $(LLNDK_LIBRARIES)),)\n        ifeq ($(filter $(_name),$(VNDK_PRIVATE_LIBRARIES)),)\n            my_link_type := native:vndk\n        else\n            my_link_type := native:vndk_private\n        endif\n    else\n        ifeq ($(LOCAL_IN_PRODUCT),true)\n            my_link_type := native:product\n        else\n            my_link_type := native:vendor\n        endif\n    endif\nelse ifneq ($(filter $(TARGET_RECOVERY_OUT)/%,$(LOCAL_MODULE_PATH)),)\nmy_link_type := native:recovery\nelse\nmy_link_type := native:platform\nendif\n\n# TODO: check dependencies of prebuilt files\nmy_link_deps :=\n\nmy_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX)\nmy_common :=\ninclude $(BUILD_SYSTEM)/link_type.mk\nendif  # prebuilt_module_is_a_library\n\n# The real dependency will be added after all Android.mks are loaded and the install paths\n# of the shared libraries are determined.\nifdef LOCAL_INSTALLED_MODULE\nifdef LOCAL_IS_HOST_MODULE\n    ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none)\n        my_system_shared_libraries :=\n    else\n        my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES)\n    endif\nelse\n    ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none)\n        my_system_shared_libraries := libc libm libdl\n    else\n        my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES)\n        my_system_shared_libraries := $(patsubst libc,libc libdl,$(my_system_shared_libraries))\n    endif\nendif\n\nmy_shared_libraries := $(strip \\\n    $(filter-out $(my_system_shared_libraries),$(LOCAL_SHARED_LIBRARIES)) \\\n    $(my_system_shared_libraries))\n\n# Extra shared libraries introduced by LOCAL_CXX_STL (may append some libraries to\n# my_shared_libraries).\ninclude $(BUILD_SYSTEM)/cxx_stl_setup.mk\n\n# When compiling against API imported module, use API import stub libraries.\napiimport_postfix := .apiimport\n\nifeq ($(call module-in-vendor-or-product),true)\n  ifeq ($(LOCAL_IN_PRODUCT),true)\n    apiimport_postfix := .apiimport.product\n  else\n    apiimport_postfix := .apiimport.vendor\n  endif\nendif\n\nifdef my_shared_libraries\nmy_shared_libraries := $(foreach l,$(my_shared_libraries), \\\n $(if $(filter $(l), $(API_IMPORTED_SHARED_LIBRARIES)), $(l)$(apiimport_postfix), $(l)))\nendif #my_shared_libraries\n\nifdef my_system_shared_libraries\nmy_system_shared_libraries := $(foreach l,$(my_system_shared_libraries), \\\n $(if $(filter $(l), $(API_IMPORTED_SHARED_LIBRARIES)), $(l)$(apiimport_postfix), $(l)))\nendif #my_system_shared_libraries\n\nifdef my_shared_libraries\nifeq ($(call module-in-vendor-or-product),true)\n  ifeq ($(LOCAL_IN_PRODUCT),true)\n    my_shared_libraries := $(foreach l,$(my_shared_libraries),\\\n      $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l)))\n  else\n    my_shared_libraries := $(foreach l,$(my_shared_libraries),\\\n      $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l)))\n  endif\nendif\n$(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \\\n  $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_shared_libraries))\nendif  # my_shared_libraries\nendif  # LOCAL_INSTALLED_MODULE\n\n# We need to enclose the above export_includes and my_built_shared_libraries in\n# \"my_strip_module not true\" because otherwise the rules are defined in dynamic_binary.mk.\nendif  # my_strip_module not true\n\n\n# Check prebuilt ELF binaries.\ninclude $(BUILD_SYSTEM)/check_elf_file.mk\n\nifeq ($(NATIVE_COVERAGE),true)\nifneq (,$(strip $(LOCAL_PREBUILT_COVERAGE_ARCHIVE)))\n  $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(intermediates)/$(LOCAL_MODULE).gcnodir))\n  ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true)\n    ifdef LOCAL_IS_HOST_MODULE\n      my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path))\n    else\n      my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path))\n    endif\n    my_coverage_path := $(my_coverage_path)/$(patsubst %.so,%,$(my_installed_module_stem)).gcnodir\n    $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(my_coverage_path)))\n    $(LOCAL_BUILT_MODULE): $(my_coverage_path)\n  endif\nelse\n# Coverage information is needed when static lib is a dependency of another\n# coverage-enabled module.\nifeq (STATIC_LIBRARIES, $(LOCAL_MODULE_CLASS))\nGCNO_ARCHIVE := $(LOCAL_MODULE).gcnodir\n$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS :=\n$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES :=\n$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_PREFIX := $(my_prefix)\n$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_2ND_ARCH_VAR_PREFIX := $(LOCAL_2ND_ARCH_VAR_PREFIX)\n$(intermediates)/$(GCNO_ARCHIVE) :\n\t$(transform-o-to-static-lib)\nendif\nendif\nendif\n\n$(built_module) : $(my_prebuilt_src_file)\n\t$(transform-prebuilt-to-target)\nifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)\n\t$(hide) chmod +x $@\nendif\n\n"
  },
  {
    "path": "core/ccache.mk",
    "content": "#\n# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# We no longer provide a ccache prebuilt.\n#\n# Ours was old, and had a number of issues that triggered non-reproducible\n# results and other failures. Newer ccache versions may fix some of those\n# issues, but at the large scale of our build servers, we weren't seeing\n# significant performance gains from using ccache -- you end up needing very\n# good locality and/or very large caches if you're building many different\n# configurations.\n#\n# Local no-change full rebuilds were showing better results, but why not just\n# use incremental builds at that point?\n#\n# So if you still want to use ccache, continue setting USE_CCACHE, but also set\n# the CCACHE_EXEC environment variable to the path to your ccache executable.\nifneq ($(CCACHE_EXEC),)\nifneq ($(filter-out false,$(USE_CCACHE)),)\n  # The default check uses size and modification time, causing false misses\n  # since the mtime depends when the repo was checked out\n  CCACHE_COMPILERCHECK ?= content\n\n  # See man page, optimizations to get more cache hits\n  # implies that __DATE__ and __TIME__ are not critical for functionality.\n  # Ignore include file modification time since it will depend on when\n  # the repo was checked out\n  CCACHE_SLOPPINESS := time_macros,include_file_mtime,file_macro\n\n  # Turn all preprocessor absolute paths into relative paths.\n  # Fixes absolute paths in preprocessed source due to use of -g.\n  # We don't really use system headers much so the rootdir is\n  # fine; ensures these paths are relative for all Android trees\n  # on a workstation.\n  CCACHE_BASEDIR := /\n\n  # Workaround for ccache with clang.\n  # See http://petereisentraut.blogspot.com/2011/09/ccache-and-clang-part-2.html\n  CCACHE_CPP2 := true\n\n  ifndef CC_WRAPPER\n    CC_WRAPPER := $(CCACHE_EXEC)\n  endif\n  ifndef CXX_WRAPPER\n    CXX_WRAPPER := $(CCACHE_EXEC)\n  endif\nendif\nendif\n"
  },
  {
    "path": "core/check_elf_file.mk",
    "content": "# Check the correctness of the prebuilt ELF files\n#\n# This check ensures that DT_SONAME matches with the filename, DT_NEEDED\n# matches the shared libraries specified in LOCAL_SHARED_LIBRARIES, and all\n# undefined symbols in the prebuilt binary can be found in one of the shared\n# libraries specified in LOCAL_SHARED_LIBRARIES.\n#\n# Inputs:\n# - LOCAL_ALLOW_UNDEFINED_SYMBOLS\n# - LOCAL_IGNORE_MAX_PAGE_SIZE\n# - LOCAL_BUILT_MODULE\n# - LOCAL_IS_HOST_MODULE\n# - LOCAL_MODULE_CLASS\n# - TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE\n# - TARGET_MAX_PAGE_SIZE_SUPPORTED\n# - intermediates\n# - my_installed_module_stem\n# - my_prebuilt_src_file\n# - my_check_elf_file_shared_lib_files\n# - my_system_shared_libraries\n\nifndef LOCAL_IS_HOST_MODULE\nifneq ($(filter $(LOCAL_MODULE_CLASS),SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS),)\ncheck_elf_files_stamp := $(intermediates)/check_elf_files.timestamp\n$(check_elf_files_stamp): PRIVATE_SONAME := $(if $(filter $(LOCAL_MODULE_CLASS),SHARED_LIBRARIES),$(my_installed_module_stem))\n$(check_elf_files_stamp): PRIVATE_ALLOW_UNDEFINED_SYMBOLS := $(LOCAL_ALLOW_UNDEFINED_SYMBOLS)\n$(check_elf_files_stamp): PRIVATE_SYSTEM_SHARED_LIBRARIES := $(my_system_shared_libraries)\n# PRIVATE_SHARED_LIBRARY_FILES are file paths to built shared libraries.\n# In addition to $(my_check_elf_file_shared_lib_files), some file paths are\n# added by `resolve-shared-libs-for-elf-file-check` from `core/main.mk`.\n$(check_elf_files_stamp): PRIVATE_SHARED_LIBRARY_FILES := $(my_check_elf_file_shared_lib_files)\n\n# For different page sizes to work, we must support a larger max page size\n# as well as properly reflect page size at runtime. Limit this check, since many\n# devices set the max page size (for future proof) than actually use the\n# larger page size.\nifeq ($(strip $(TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE)),true)\nifeq ($(strip $(LOCAL_IGNORE_MAX_PAGE_SIZE)),true)\n$(check_elf_files_stamp): PRIVATE_MAX_PAGE_SIZE :=\nelse\n$(check_elf_files_stamp): PRIVATE_MAX_PAGE_SIZE := $(TARGET_MAX_PAGE_SIZE_SUPPORTED)\nendif\nelse\n$(check_elf_files_stamp): PRIVATE_MAX_PAGE_SIZE :=\nendif\n\n$(check_elf_files_stamp): $(my_prebuilt_src_file) $(my_check_elf_file_shared_lib_files) $(CHECK_ELF_FILE) $(LLVM_READOBJ)\n\t@echo Check prebuilt ELF binary: $<\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) rm -f $@\n\t$(hide) $(CHECK_ELF_FILE) \\\n\t    --skip-bad-elf-magic \\\n\t    --skip-unknown-elf-machine \\\n\t    $(if $(PRIVATE_MAX_PAGE_SIZE),--max-page-size=$(PRIVATE_MAX_PAGE_SIZE)) \\\n\t    $(if $(PRIVATE_SONAME),--soname $(PRIVATE_SONAME)) \\\n\t    $(foreach l,$(PRIVATE_SHARED_LIBRARY_FILES),--shared-lib $(l)) \\\n\t    $(foreach l,$(PRIVATE_SYSTEM_SHARED_LIBRARIES),--system-shared-lib $(l)) \\\n\t    $(if $(PRIVATE_ALLOW_UNDEFINED_SYMBOLS),--allow-undefined-symbols) \\\n\t    --llvm-readobj=$(LLVM_READOBJ) \\\n\t    $<\n\t$(hide) touch $@\n\nCHECK_ELF_FILES.$(check_elf_files_stamp) := 1\n\nifneq ($(strip $(LOCAL_CHECK_ELF_FILES)),false)\nifneq ($(strip $(BUILD_BROKEN_PREBUILT_ELF_FILES)),true)\n$(LOCAL_BUILT_MODULE): $(check_elf_files_stamp)\ncheck-elf-files: $(check_elf_files_stamp)\nendif  # BUILD_BROKEN_PREBUILT_ELF_FILES\nendif  # LOCAL_CHECK_ELF_FILES\n\nendif  # SHARED_LIBRARIES, EXECUTABLES, NATIVE_TESTS\nendif  # !LOCAL_IS_HOST_MODULE\n"
  },
  {
    "path": "core/checktree",
    "content": "#!/usr/bin/python -E\n\nimport sys, os, re\n\nexcludes = [r'.*?/\\.obj.*?',\n            r'.*?~',\n            r'.*?\\/.DS_Store',\n            r'.*?\\/.gdb_history',\n            r'.*?\\/buildspec.mk',\n            r'.*?/\\..*?\\.swp',\n            r'.*?/out/.*?',\n            r'.*?/install/.*?']\n\nexcludes_compiled = map(re.compile, excludes)\n\ndef filter_excludes(str):\n    for e in excludes_compiled:\n        if e.match(str):\n            return False\n    return True\n\ndef split_perforce_parts(s):\n    spaces = ((s.count(\" \") + 1) / 3) * 2\n    pos = 0\n    while spaces > 0:\n        pos = s.find(\" \", pos) + 1\n        spaces = spaces - 1\n    return s[pos:]\n\ndef quotate(s):\n    return '\"' + s + '\"'\n\nclass PerforceError(Exception):\n    def __init__(self,value):\n        self.value = value\n    def __str__(self):\n        return repr(self.value)\n    \n\ndef run(command, regex, filt):\n    def matchit(s):\n        m = regex_compiled.match(s)\n        if m:\n            return m.group(1)\n        else:\n            return \"\"\n    def filterit(s):\n        if filt_compiled.match(s):\n            return True\n        else:\n            return False\n\n    fd = os.popen(command);\n    lines = fd.readlines()\n    status = fd.close()\n    if status:\n        raise PerforceError(\"error calling \" + command)\n        \n    regex_compiled = re.compile(regex)\n    filt_compiled = re.compile(filt)\n\n    if len(lines) >= 1:\n        lines = filter(filterit, lines)\n        if len(lines) >= 1:\n            return map(matchit, lines)\n    return None\n\ntry:\n    if len(sys.argv) == 1:\n        do_exclude = True\n    elif len(sys.argv) == 2 and sys.argv[1] == \"-a\":\n        do_exclude = False\n    else:\n        print \"usage: checktree [-a]\"\n        print \"  -a  don't filter common crud in the tree\"\n        sys.exit(1)\n\n    have = run(\"p4 have ...\", r'[^#]+#[0-9]+ - (.*)', r'.*')\n\n    cwd = os.getcwd()\n    files = run(\"find . -not -type d\", r'.(.*)', r'.*')\n    files = map(lambda s: cwd+s, files)\n\n    added_depot_path = run(\"p4 opened ...\", r'([^#]+)#.*', r'.*?#[0-9]+ - add .*');\n    added = []\n    if added_depot_path:\n        added_depot_path = map(quotate, added_depot_path)\n\n        where = \"p4 where \" + \" \".join(added_depot_path)\n        added = run(where, r'(.*)', r'.*')\n        added = map(split_perforce_parts, added)\n\n    extras = []\n\n    # Python 2.3 -- still default on Mac OS X -- does not have set()\n    # Make dict's here to support the \"in\" operations below\n    have = dict().fromkeys(have, 1)\n    added = dict().fromkeys(added, 1)\n\n    for file in files:\n        if not file in have:\n            if not file in added:\n                extras.append(file)\n\n    if do_exclude:\n        extras = filter(filter_excludes, extras)\n\n    for s in extras:\n        print s.replace(\" \", \"\\\\ \")\n\nexcept PerforceError, e:\n    sys.exit(2)\n\n"
  },
  {
    "path": "core/clang/HOST_x86.mk",
    "content": "$(clang_2nd_arch_prefix)HOST_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-i386.a\n$(clang_2nd_arch_prefix)HOST_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-i386.a\n"
  },
  {
    "path": "core/clang/HOST_x86_64.mk",
    "content": "HOST_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-x86_64.a\nHOST_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-x86_64.a\n"
  },
  {
    "path": "core/clang/TARGET_arm.mk",
    "content": "$(clang_2nd_arch_prefix)RS_TRIPLE := renderscript32-linux-androideabi\n$(clang_2nd_arch_prefix)RS_TRIPLE_CFLAGS :=\n$(clang_2nd_arch_prefix)RS_COMPAT_TRIPLE := armv7-none-linux-gnueabi\n\n$(clang_2nd_arch_prefix)TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-arm-android.a\n$(clang_2nd_arch_prefix)TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-arm-android.a\n$(clang_2nd_arch_prefix)TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/arm/libunwind.a\n\n# Address sanitizer clang config\n$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan\n$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan\n\n$(clang_2nd_arch_prefix)PREBUILT_LIBCXX_ARCH_DIR := arm\n"
  },
  {
    "path": "core/clang/TARGET_arm64.mk",
    "content": "RS_TRIPLE := renderscript64-linux-android\nRS_TRIPLE_CFLAGS :=\nRS_COMPAT_TRIPLE := aarch64-linux-android\n\nTARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-aarch64-android.a\nTARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-aarch64-android.a\nTARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/aarch64/libunwind.a\n\n# Address sanitizer clang config\nADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64\nADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64\n\nPREBUILT_LIBCXX_ARCH_DIR := aarch64\n"
  },
  {
    "path": "core/clang/TARGET_riscv64.mk",
    "content": "RS_TRIPLE := renderscript64-linux-android\nRS_TRIPLE_CFLAGS := -D__riscv64__\nRS_COMPAT_TRIPLE := riscv64-linux-android\n\nTARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-riscv64-android.a\nTARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-riscv64-android.a\nTARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/riscv64/libunwind.a\n\n# Address sanitizer clang config\nADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64\nADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64\n\nPREBUILT_LIBCXX_ARCH_DIR := riscv64\n"
  },
  {
    "path": "core/clang/TARGET_x86.mk",
    "content": "$(clang_2nd_arch_prefix)RS_TRIPLE := renderscript32-linux-androideabi\n$(clang_2nd_arch_prefix)RS_TRIPLE_CFLAGS := -D__i386__\n$(clang_2nd_arch_prefix)RS_COMPAT_TRIPLE := i686-linux-android\n\n$(clang_2nd_arch_prefix)TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-i686-android.a\n$(clang_2nd_arch_prefix)TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-i686-android.a\n$(clang_2nd_arch_prefix)TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/i386/libunwind.a\n\n# Address sanitizer clang config\n$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan\n$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan\n\n$(clang_2nd_arch_prefix)PREBUILT_LIBCXX_ARCH_DIR := i386\n"
  },
  {
    "path": "core/clang/TARGET_x86_64.mk",
    "content": "RS_TRIPLE := renderscript64-linux-android\nRS_TRIPLE_CFLAGS := -D__x86_64__\nRS_COMPAT_TRIPLE := x86_64-linux-android\n\nTARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-x86_64-android.a\nTARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-x86_64-android.a\nTARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/x86_64/libunwind.a\n\n# Address sanitizer clang config\nADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64\nADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64\n\nPREBUILT_LIBCXX_ARCH_DIR := x86_64\n"
  },
  {
    "path": "core/clang/config.mk",
    "content": "## Clang configurations.\n\nLLVM_READOBJ := $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-readobj\n\nLLVM_RTLIB_PATH := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib/clang/$(LLVM_RELEASE_VERSION)/lib/linux/\n\ndefine convert-to-clang-flags\n$(strip $(filter-out $(CLANG_CONFIG_UNKNOWN_CFLAGS),$(1)))\nendef\n\nCLANG_DEFAULT_UB_CHECKS := \\\n  bool \\\n  integer-divide-by-zero \\\n  return \\\n  returns-nonnull-attribute \\\n  shift-exponent \\\n  unreachable \\\n  vla-bound \\\n\n# TODO(danalbert): The following checks currently have compiler performance\n# issues.\n# CLANG_DEFAULT_UB_CHECKS += alignment\n# CLANG_DEFAULT_UB_CHECKS += bounds\n# CLANG_DEFAULT_UB_CHECKS += enum\n# CLANG_DEFAULT_UB_CHECKS += float-cast-overflow\n# CLANG_DEFAULT_UB_CHECKS += float-divide-by-zero\n# CLANG_DEFAULT_UB_CHECKS += nonnull-attribute\n# CLANG_DEFAULT_UB_CHECKS += null\n# CLANG_DEFAULT_UB_CHECKS += shift-base\n# CLANG_DEFAULT_UB_CHECKS += signed-integer-overflow\n\n# TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on.\n# https://llvm.org/PR19302\n# http://reviews.llvm.org/D6974\n# CLANG_DEFAULT_UB_CHECKS += object-size\n\n# HOST config\nclang_2nd_arch_prefix :=\ninclude $(BUILD_SYSTEM)/clang/HOST_$(HOST_ARCH).mk\n\n# HOST_2ND_ARCH config\nifdef HOST_2ND_ARCH\nclang_2nd_arch_prefix := $(HOST_2ND_ARCH_VAR_PREFIX)\ninclude $(BUILD_SYSTEM)/clang/HOST_$(HOST_2ND_ARCH).mk\nendif\n\n# TARGET config\nclang_2nd_arch_prefix :=\ninclude $(BUILD_SYSTEM)/clang/TARGET_$(TARGET_ARCH).mk\n\n# TARGET_2ND_ARCH config\nifdef TARGET_2ND_ARCH\nclang_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX)\ninclude $(BUILD_SYSTEM)/clang/TARGET_$(TARGET_2ND_ARCH).mk\nendif\n\ninclude $(BUILD_SYSTEM)/clang/tidy.mk\n"
  },
  {
    "path": "core/clang/tidy.mk",
    "content": "#\n# Copyright (C) 2016 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Returns 2nd word of $(1) if $(2) has prefix of the 1st word of $(1).\ndefine find_default_local_tidy_check2\n$(if $(filter $(word 1,$(1))%,$(2)/),$(word 2,$(1)))\nendef\n\n# Returns 2nd part of $(1) if $(2) has prefix of the 1st part of $(1).\ndefine find_default_local_tidy_check\n$(call find_default_local_tidy_check2,$(subst :,$(space),$(1)),$(2))\nendef\n\n# Returns the default tidy check list for local project path $(1).\n# Match $(1) with all patterns in DEFAULT_LOCAL_TIDY_CHECKS and use the last\n# most specific pattern.\ndefine default_global_tidy_checks\n$(lastword \\\n  $(DEFAULT_GLOBAL_TIDY_CHECKS) \\\n  $(foreach pattern,$(DEFAULT_LOCAL_TIDY_CHECKS), \\\n    $(call find_default_local_tidy_check,$(pattern),$(1)) \\\n  ) \\\n)\nendef\n\n# Default filter contains current directory $1 and optional DEFAULT_TIDY_HEADER_DIRS.\ndefine default_tidy_header_filter\n  -header-filter=$(if $(DEFAULT_TIDY_HEADER_DIRS),\"($1/|$(DEFAULT_TIDY_HEADER_DIRS))\",$1/)\nendef\n"
  },
  {
    "path": "core/cleanbuild.mk",
    "content": "# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Absolute path of the present working direcotry.\n# This overrides the shell variable $PWD, which does not necessarily points to\n# the top of the source tree, for example when \"make -C\" is used in m/mm/mmm.\nPWD := $(shell pwd)\n\nTOP := .\nTOPDIR :=\n\nBUILD_SYSTEM := $(TOPDIR)build/make/core\n\n# Set up various standard variables based on configuration\n# and host information.\ninclude $(BUILD_SYSTEM)/config.mk\n\ninclude $(SOONG_MAKEVARS_MK)\n\ninclude $(BUILD_SYSTEM)/clang/config.mk\n\n# CTS-specific config.\n-include cts/build/config.mk\n# device-tests-specific-config.\n-include tools/tradefederation/build/suites/device-tests/config.mk\n# general-tests-specific-config.\n-include tools/tradefederation/build/suites/general-tests/config.mk\n\nINTERNAL_CLEAN_STEPS :=\n\n# Builds up a list of clean steps.  Creates a unique\n# id for each step by taking makefile path, INTERNAL_CLEAN_BUILD_VERSION\n# and appending an increasing number of '@' characters.\n#\n# $(1): shell command to run\n# $(2): indicate to not use makefile path as part of step id if not empty.\n#       $(2) should only be used in build/make/core/cleanspec.mk: just for compatibility.\ndefine _add-clean-step\n  $(if $(strip $(INTERNAL_CLEAN_BUILD_VERSION)),, \\\n      $(error INTERNAL_CLEAN_BUILD_VERSION not set))\n  $(eval _acs_makefile_prefix := $(lastword $(MAKEFILE_LIST)))\n  $(eval _acs_makefile_prefix := $(subst /,_,$(_acs_makefile_prefix)))\n  $(eval _acs_makefile_prefix := $(subst .,-,$(_acs_makefile_prefix)))\n  $(eval _acs_makefile_prefix := $(_acs_makefile_prefix)_acs)\n  $(if $($(_acs_makefile_prefix)),,\\\n      $(eval $(_acs_makefile_prefix) := $(INTERNAL_CLEAN_BUILD_VERSION)))\n  $(eval $(_acs_makefile_prefix) := $($(_acs_makefile_prefix))@)\n  $(if $(strip $(2)),$(eval _acs_id := $($(_acs_makefile_prefix))),\\\n      $(eval _acs_id := $(_acs_makefile_prefix)$($(_acs_makefile_prefix))))\n  $(eval INTERNAL_CLEAN_STEPS += $(_acs_id))\n  $(eval INTERNAL_CLEAN_STEP.$(_acs_id) := $(1))\n  $(eval _acs_id :=)\n  $(eval _acs_makefile_prefix :=)\nendef\ndefine add-clean-step\n$(eval # for build/make/core/cleanspec.mk, dont use makefile path as part of step id) \\\n$(if $(filter %/cleanspec.mk,$(lastword $(MAKEFILE_LIST))),\\\n    $(eval $(call _add-clean-step,$(1),true)),\\\n    $(eval $(call _add-clean-step,$(1))))\nendef\n\n# Defines INTERNAL_CLEAN_BUILD_VERSION and the individual clean steps.\n# cleanspec.mk is outside of the core directory so that more people\n# can have permission to touch it.\ninclude $(BUILD_SYSTEM)/cleanspec.mk\nINTERNAL_CLEAN_BUILD_VERSION := $(strip $(INTERNAL_CLEAN_BUILD_VERSION))\nINTERNAL_CLEAN_STEPS := $(strip $(INTERNAL_CLEAN_STEPS))\n\n# If the clean_steps.mk file is missing (usually after a clean build)\n# then we won't do anything.\nCURRENT_CLEAN_BUILD_VERSION := MISSING\nCURRENT_CLEAN_STEPS := $(INTERNAL_CLEAN_STEPS)\n\n# Read the current state from the file, if present.\n# Will set CURRENT_CLEAN_BUILD_VERSION and CURRENT_CLEAN_STEPS.\n#\nclean_steps_file := $(PRODUCT_OUT)/clean_steps.mk\n-include $(clean_steps_file)\n\nifeq ($(CURRENT_CLEAN_BUILD_VERSION),MISSING)\n  # Do nothing\nelse ifneq ($(CURRENT_CLEAN_BUILD_VERSION),$(INTERNAL_CLEAN_BUILD_VERSION))\n  # The major clean version is out-of-date.  Do a full clean, and\n  # don't even bother with the clean steps.\n  $(info *** A clean build is required because of a recent change.)\n  $(shell rm -rf $(OUT_DIR))\n  $(info *** Done with the cleaning, now starting the real build.)\nelse\n  # The major clean version is correct.  Find the list of clean steps\n  # that we need to execute to get up-to-date.\n  steps := \\\n      $(filter-out $(CURRENT_CLEAN_STEPS),$(INTERNAL_CLEAN_STEPS))\n  $(foreach step,$(steps), \\\n    $(info Clean step: $(INTERNAL_CLEAN_STEP.$(step))) \\\n    $(shell $(INTERNAL_CLEAN_STEP.$(step))) \\\n   )\n\n  # Rewrite the clean step for the second arch.\n  ifdef TARGET_2ND_ARCH\n  # $(1): the clean step cmd\n  # $(2): the prefix to search for\n  # $(3): the prefix to replace with\n  define -cs-rewrite-cleanstep\n  $(if $(filter $(2)/%,$(1)),\\\n    $(eval _crs_new_cmd := $(patsubst $(2)/%,$(3)/%,$(1)))\\\n    $(info Clean step: $(_crs_new_cmd))\\\n    $(shell $(_crs_new_cmd)))\n  endef\n  $(foreach step,$(steps), \\\n    $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$(TARGET_OUT_INTERMEDIATES),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES))\\\n    $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$(TARGET_OUT_SHARED_LIBRARIES),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES))\\\n    $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$(TARGET_OUT_VENDOR_SHARED_LIBRARIES),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES))\\\n    $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES),$(TARGET_OUT_INTERMEDIATES))\\\n    $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES),$(TARGET_OUT_SHARED_LIBRARIES))\\\n    $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES),$(TARGET_OUT_VENDOR_SHARED_LIBRARIES))\\\n    )\n  endif\n  _crs_new_cmd :=\n  steps :=\nendif\n\n# Write the new state to the file.\n#\nifneq ($(CURRENT_CLEAN_BUILD_VERSION)-$(CURRENT_CLEAN_STEPS),$(INTERNAL_CLEAN_BUILD_VERSION)-$(INTERNAL_CLEAN_STEPS))\n$(shell mkdir -p $(dir $(clean_steps_file)))\n$(file >$(clean_steps_file).tmp,CURRENT_CLEAN_BUILD_VERSION := $(INTERNAL_CLEAN_BUILD_VERSION)$(newline)CURRENT_CLEAN_STEPS := $(INTERNAL_CLEAN_STEPS)$(newline))\n$(shell if ! cmp -s $(clean_steps_file).tmp $(clean_steps_file); then \\\n          mv $(clean_steps_file).tmp $(clean_steps_file); \\\n        else \\\n          rm $(clean_steps_file).tmp; \\\n        fi)\nendif\n\nCURRENT_CLEAN_BUILD_VERSION :=\nCURRENT_CLEAN_STEPS :=\nclean_steps_file :=\nINTERNAL_CLEAN_STEPS :=\nINTERNAL_CLEAN_BUILD_VERSION :=\n"
  },
  {
    "path": "core/cleanspec.mk",
    "content": "# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Just bump this if you want to force a clean build.\n# **********************************************************************\n# WHEN DOING SO\n# 1. DELETE ANY \"add-clean-step\" ENTRIES THAT HAVE PILED UP IN THIS FILE.\n# 2. REMOVE ALL FILES NAMED CleanSpec.mk.\n# 3. BUMP THE VERSION.\n# IDEALLY, THOSE STEPS SHOULD BE DONE ATOMICALLY.\n# **********************************************************************\n#\nINTERNAL_CLEAN_BUILD_VERSION := 6\n#\n# ***********************************************************************\n# Do not touch INTERNAL_CLEAN_BUILD_VERSION if you've added a clean step!\n# ***********************************************************************\n\n# If you don't need to do a full clean build but would like to touch\n# a file or delete some intermediate files, add a clean step to the end\n# of the list.  These steps will only be run once, if they haven't been\n# run before.\n#\n# E.g.:\n#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)\n#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)\n#\n# Always use \"touch -c\" and \"rm -f\" or \"rm -rf\" to gracefully deal with\n# files that are missing or have been moved.\n#\n# Use $(PRODUCT_OUT) to get to the \"out/target/product/blah/\" directory.\n# Use $(OUT_DIR) to refer to the \"out\" directory.\n#\n# If you need to re-do something that's already mentioned, just copy\n# the command and add it to the bottom of the list.  E.g., if a change\n# that you made last week required touching a file and a change you\n# made today requires touching the same file, just copy the old\n# touch step and add it to the end of the list.\n#\n# ************************************************\n# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST\n# ************************************************\n\n# For example:\n#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)\n#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)\n#$(call add-clean-step, find $(OUT_DIR) -type f -name \"IGTalkSession*\" -print0 | xargs -0 rm -f)\n#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)\n$(call add-clean-step, rm -rf $(OUT_DIR)/obj/ETC/build_manifest-vendor_intermediates)\n$(call add-clean-step, rm -rf $(OUT_DIR)/obj/ETC/build_manifest-odm_intermediates)\n$(call add-clean-step, rm -rf $(OUT_DIR)/obj/ETC/build_manifest-product_intermediates)\n$(call add-clean-step, rm -rf $(TARGET_OUT_VENDOR)/etc/security/fsverity)\n$(call add-clean-step, rm -rf $(TARGET_OUT_ODM)/etc/security/fsverity)\n$(call add-clean-step, rm -rf $(TARGET_OUT_PRODUCT)/etc/security/fsverity)\n\n# ************************************************\n# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST\n# ************************************************\n\nsubdir_cleanspecs := \\\n    $(file <$(OUT_DIR)/.module_paths/CleanSpec.mk.list)\ninclude $(subdir_cleanspecs)\nsubdir_cleanspecs :=\n"
  },
  {
    "path": "core/clear_vars.mk",
    "content": "###########################################################\n## Clear out values of all variables used by rule templates.\n###########################################################\n\n# '',true\nLOCAL_2ND_ARCH_VAR_PREFIX:=\nLOCAL_32_BIT_ONLY:=\nLOCAL_AAPT_FLAGS:=\nLOCAL_AAPT_INCLUDE_ALL_RESOURCES:=\nLOCAL_AAPT_NAMESPACES:=\nLOCAL_ACONFIG_FILES:=\nLOCAL_ADDITIONAL_CERTIFICATES:=\nLOCAL_ADDITIONAL_CHECKED_MODULE:=\nLOCAL_ADDITIONAL_DEPENDENCIES:=\nLOCAL_AIDL_INCLUDES:=\nLOCAL_ALLOW_UNDEFINED_SYMBOLS:=\nLOCAL_ANNOTATION_PROCESSORS:=\nLOCAL_ANNOTATION_PROCESSOR_CLASSES:=\nLOCAL_APEX_KEY_PATH:=\nLOCAL_APK_LIBRARIES:=\nLOCAL_APK_SET_INSTALL_FILE:=\nLOCAL_APKCERTS_FILE:=\nLOCAL_ARM_MODE:=\nLOCAL_ASFLAGS:=\nLOCAL_ASSET_DIR:=\nLOCAL_BUILT_MODULE:=\nLOCAL_BUILT_MODULE_STEM:=\nLOCAL_CC:=\nLOCAL_CERTIFICATE:=\nLOCAL_CFLAGS:=\nLOCAL_CHECK_SAME_VNDK_VARIANTS:=\nLOCAL_CHECKED_MODULE:=\nLOCAL_C_INCLUDES:=\nLOCAL_CLANG:=\nLOCAL_CLANG_ASFLAGS:=\nLOCAL_CLANG_CFLAGS:=\nLOCAL_CLANG_CONLYFLAGS:=\nLOCAL_CLANG_CPPFLAGS:=\nLOCAL_CLANG_LDFLAGS:=\nLOCAL_CLASSPATH:=\nLOCAL_COMPATIBILITY_SUITE:=\nLOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY:=\nLOCAL_COMPATIBILITY_SUPPORT_FILES:=\nLOCAL_COMPRESSED_MODULE:=\nLOCAL_CONLYFLAGS:=\nLOCAL_COPY_HEADERS:=\nLOCAL_COPY_HEADERS_TO:=\nLOCAL_CPP_EXTENSION:=\nLOCAL_CPPFLAGS:=\nLOCAL_CPP_STD:=\nLOCAL_C_STD:=\nLOCAL_CXX:=\nLOCAL_CXX_STL := default\nLOCAL_DEX_PREOPT_APP_IMAGE:=\nLOCAL_DEX_PREOPT_FLAGS:=\nLOCAL_DEX_PREOPT_GENERATE_PROFILE:=\nLOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING:=\nLOCAL_DEX_PREOPT:= # '',true,false\nLOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG:=\nLOCAL_DISABLE_TEST_CONFIG:=\nLOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES:=\nLOCAL_DONT_CHECK_MODULE:=\n# Don't delete the META_INF dir when merging static Java libraries.\nLOCAL_DONT_DELETE_JAR_META_INF:=\nLOCAL_DONT_MERGE_MANIFESTS:=\nLOCAL_DPI_FILE_STEM:=\nLOCAL_DPI_VARIANTS:=\nLOCAL_DROIDDOC_ANNOTATIONS_ZIP :=\nLOCAL_DROIDDOC_API_VERSIONS_XML :=\nLOCAL_DROIDDOC_DOC_ZIP :=\nLOCAL_DROIDDOC_METADATA_ZIP:=\nLOCAL_DROIDDOC_STUBS_SRCJAR :=\nLOCAL_DX_FLAGS:=\nLOCAL_DYLIB_LIBRARIES:=\nLOCAL_EMMA_INSTRUMENT:=\nLOCAL_ENFORCE_USES_LIBRARIES:=\nLOCAL_ERROR_PRONE_FLAGS:=\nLOCAL_EXPORT_CFLAGS:=\nLOCAL_EXPORT_C_INCLUDE_DEPS:=\nLOCAL_EXPORT_C_INCLUDE_DIRS:=\nLOCAL_EXPORT_HEADER_LIBRARY_HEADERS:=\nLOCAL_EXPORT_PACKAGE_RESOURCES:=\nLOCAL_EXPORT_PROGUARD_FLAG_FILES:=\nLOCAL_EXPORT_SDK_LIBRARIES:=\nLOCAL_EXPORT_SHARED_LIBRARY_HEADERS:=\nLOCAL_EXPORT_STATIC_LIBRARY_HEADERS:=\nLOCAL_EXTRA_FULL_TEST_CONFIGS:=\nLOCAL_EXTRACT_APK:=\nLOCAL_EXTRACT_DPI_APK:=\nLOCAL_FILESYSTEM_FILELIST:=\nLOCAL_FINDBUGS_FLAGS:=\nLOCAL_FORCE_STATIC_EXECUTABLE:=\nLOCAL_FULL_CLASSES_JACOCO_JAR:=\nLOCAL_FULL_CLASSES_PRE_JACOCO_JAR:=\nLOCAL_FULL_INIT_RC:=\nLOCAL_FULL_LIBS_MANIFEST_FILES:=\nLOCAL_FULL_MANIFEST_FILE:=\nLOCAL_FULL_TEST_CONFIG:=\nLOCAL_FULL_VINTF_FRAGMENTS:=\nLOCAL_FUZZ_ENGINE:=\nLOCAL_FUZZ_INSTALLED_SHARED_DEPS:=\nLOCAL_GCNO_FILES:=\nLOCAL_GENERATED_SOURCES:=\n# Group static libraries with \"-Wl,--start-group\" and \"-Wl,--end-group\" when linking.\nLOCAL_GROUP_STATIC_LIBRARIES:=\nLOCAL_GTEST:=true\nLOCAL_HEADER_LIBRARIES:=\nLOCAL_HOST_PREFIX:=\nLOCAL_HOST_REQUIRED_MODULES:=\nLOCAL_IGNORE_MAX_PAGE_SIZE:=\nLOCAL_INIT_RC:=\nLOCAL_INJECT_BSSL_HASH:=\nLOCAL_INSTALLED_MODULE:=\nLOCAL_INSTALLED_MODULE_STEM:=\nLOCAL_INSTRUMENTATION_FOR:=\nLOCAL_INTERMEDIATE_SOURCES:=\nLOCAL_INTERMEDIATE_TARGETS:=\nLOCAL_IS_FUZZ_TARGET:=\nLOCAL_IS_HOST_MODULE:=\nLOCAL_IS_RUNTIME_RESOURCE_OVERLAY:=\nLOCAL_IS_UNIT_TEST:=\nLOCAL_TEST_OPTIONS_TAGS:=\nLOCAL_JACK_COVERAGE_EXCLUDE_FILTER:=\nLOCAL_JACK_COVERAGE_INCLUDE_FILTER:=\nLOCAL_JAR_EXCLUDE_FILES:=\nLOCAL_JAR_EXCLUDE_PACKAGES:=\nLOCAL_JARJAR_RULES:=\nLOCAL_JAR_MANIFEST:=\nLOCAL_JAR_PACKAGES:=\nLOCAL_JAR_PROCESSOR:=\nLOCAL_JAR_PROCESSOR_ARGS:=\nLOCAL_JAVACFLAGS:=\nLOCAL_JAVA_LANGUAGE_VERSION:=\nLOCAL_JAVA_LIBRARIES:=\nLOCAL_JAVA_RESOURCE_DIRS:=\nLOCAL_JAVA_RESOURCE_FILES:=\nLOCAL_JNI_SHARED_LIBRARIES:=\nLOCAL_JNI_SHARED_LIBRARIES_ABI:=\nLOCAL_CERTIFICATE_LINEAGE:=\nLOCAL_LDFLAGS:=\nLOCAL_LDLIBS:=\nLOCAL_LICENSE_CONDITIONS:=\nLOCAL_LICENSE_KINDS:=\nLOCAL_LICENSE_INSTALL_MAP:=\nLOCAL_LICENSE_PACKAGE_NAME:=\nLOCAL_LOGTAGS_FILES:=\nLOCAL_MANIFEST_FILE:=\nLOCAL_MANIFEST_INSTRUMENTATION_FOR:=\nLOCAL_MANIFEST_PACKAGE_NAME:=\nLOCAL_MIN_SDK_VERSION:=\nLOCAL_MODULE:=\nLOCAL_MODULE_CLASS:=\nLOCAL_MODULE_HOST_ARCH:=\nLOCAL_MODULE_HOST_ARCH_WARN:=\nLOCAL_MODULE_HOST_CROSS_ARCH:=\nLOCAL_MODULE_HOST_OS:=\nLOCAL_MODULE_IS_CONTAINER:=\nLOCAL_MODULE_OWNER:=\nLOCAL_MODULE_PATH:=\nLOCAL_MODULE_RELATIVE_PATH :=\nLOCAL_MODULE_STEM:=\nLOCAL_MODULE_SUFFIX:=\nLOCAL_MODULE_SYMLINKS:=\nLOCAL_MODULE_TAGS:=\nLOCAL_MODULE_TARGET_ARCH:=\nLOCAL_MODULE_TARGET_ARCH_WARN:=\nLOCAL_MODULE_TYPE:=\nLOCAL_MODULE_UNSUPPORTED_HOST_ARCH:=\nLOCAL_MODULE_UNSUPPORTED_HOST_ARCH_WARN:=\nLOCAL_MODULE_UNSUPPORTED_TARGET_ARCH:=\nLOCAL_MODULE_UNSUPPORTED_TARGET_ARCH_WARN:=\nLOCAL_MULTILIB:=\nLOCAL_NATIVE_BENCHMARK:=\nLOCAL_NDK_STL_VARIANT:=\nLOCAL_NDK_VERSION:=current\nLOCAL_NO_CRT:=\nLOCAL_NO_DEFAULT_COMPILER_FLAGS:=\nLOCAL_NO_LIBCRT_BUILTINS:=\nLOCAL_NO_NOTICE_FILE:=\nLOCAL_NOSANITIZE:=\nLOCAL_NO_STANDARD_LIBRARIES:=\nLOCAL_NO_STATIC_ANALYZER:=\nLOCAL_NOT_AVAILABLE_FOR_PLATFORM:=\nLOCAL_NOTICE_FILE:=\nLOCAL_ODM_MODULE:=\nLOCAL_OEM_MODULE:=\nLOCAL_OPTIONAL_USES_LIBRARIES:=\nLOCAL_OVERRIDES_PACKAGES:=\nLOCAL_OVERRIDES_MODULES:=\nLOCAL_PACKAGE_NAME:=\nLOCAL_PACKAGE_SPLITS:=\nLOCAL_PACK_MODULE_RELOCATIONS:=\nLOCAL_PATCH_MODULE:=\nLOCAL_PICKUP_FILES:=\nLOCAL_POST_INSTALL_CMD:=\nLOCAL_POST_LINK_CMD:=\nLOCAL_PREBUILT_COVERAGE_ARCHIVE:=\nLOCAL_PREBUILT_EXECUTABLES:=\nLOCAL_PREBUILT_JAVA_LIBRARIES:=\nLOCAL_PREBUILT_JNI_LIBS:=\nLOCAL_PREBUILT_LIBS:=\nLOCAL_PREBUILT_MODULE_FILE:=\nLOCAL_PREBUILT_OBJ_FILES:=\nLOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:=\nLOCAL_USE_EMBEDDED_DEX:=\nLOCAL_USE_EMBEDDED_NATIVE_LIBS:=\nLOCAL_PRIVATE_PLATFORM_APIS:=\nLOCAL_PRIVILEGED_MODULE:=\nLOCAL_PROC_MACRO_LIBRARIES:=\n# '',full,custom,disabled,obfuscation,optimization\nLOCAL_PRODUCT_MODULE:=\n# TODO(b/135957588) Remove LOCAL_PRODUCT_SERVICES_MODULE\nLOCAL_PRODUCT_SERVICES_MODULE :=\nLOCAL_PROGUARD_ENABLED:=\nLOCAL_PROGUARD_FLAG_FILES:=\nLOCAL_PROGUARD_FLAGS:=\nLOCAL_PROGUARD_FLAGS_DEPS:=\nLOCAL_PROPRIETARY_MODULE:=\nLOCAL_PROTOC_FLAGS:=\n# lite(default),micro,nano,stream,full,nanopb-c,nanopb-c-enable_malloc,nanopb-c-16bit,nanopb-c-enable_malloc-16bit,nanopb-c-32bit,nanopb-c-enable_malloc-32bit\nLOCAL_PROTOC_OPTIMIZE_TYPE:=\nLOCAL_PROTO_JAVA_OUTPUT_PARAMS:=\nLOCAL_PROVIDES_USES_LIBRARY:=\nLOCAL_R8_FLAG_FILES:=\nLOCAL_RECORDED_MODULE_TYPE:=\nLOCAL_RENDERSCRIPT_CC:=\nLOCAL_RENDERSCRIPT_COMPATIBILITY:=\nLOCAL_RENDERSCRIPT_FLAGS:=\nLOCAL_RENDERSCRIPT_INCLUDES:=\nLOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE:=\nLOCAL_RENDERSCRIPT_TARGET_API:=\n# Used to replace the installed file of a presigned prebuilt apk in PDK fusion build,\n# to avoid installing the presigned apks with classes.dex unstripped.\nLOCAL_REPLACE_PREBUILT_APK_INSTALLED:=\nLOCAL_REQUIRED_MODULES:=\nLOCAL_RES_LIBRARIES:=\nLOCAL_RESOURCE_DIR:=\nLOCAL_RLIB_LIBRARIES:=\nLOCAL_ROTATION_MIN_SDK_VERSION:=\nLOCAL_RUNTIME_LIBRARIES:=\nLOCAL_RRO_THEME:=\nLOCAL_RTTI_FLAG:=\nLOCAL_SANITIZE:=\nLOCAL_SANITIZE_DIAG:=\nLOCAL_SANITIZE_RECOVER:=\nLOCAL_SANITIZE_NO_RECOVER:=\nLOCAL_SANITIZE_BLOCKLIST :=\nLOCAL_SDK_LIBRARIES :=\nLOCAL_SDK_RES_VERSION:=\nLOCAL_SDK_VERSION:=\nLOCAL_SHARED_ANDROID_LIBRARIES:=\nLOCAL_SHARED_LIBRARIES:=\nLOCAL_SOONG_AAR :=\nLOCAL_SOONG_BUILT_INSTALLED :=\nLOCAL_SOONG_BUNDLE :=\nLOCAL_SOONG_CLASSES_JAR :=\nLOCAL_SOONG_DEX_JAR :=\nLOCAL_SOONG_DEXPREOPT_CONFIG :=\nLOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=\nLOCAL_SOONG_HEADER_JAR :=\nLOCAL_SOONG_INSTALL_PAIRS :=\nLOCAL_SOONG_INSTALL_SYMLINKS :=\nLOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES:=\nLOCAL_SOONG_INSTALLED_MODULE :=\nLOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=\nLOCAL_SOONG_LICENSE_METADATA :=\nLOCAL_SOONG_LINK_TYPE :=\nLOCAL_SOONG_LINT_REPORTS :=\nLOCAL_SOONG_LOGTAGS_FILES :=\nLOCAL_SOONG_MODULE_INFO_JSON :=\nLOCAL_SOONG_MODULE_TYPE :=\nLOCAL_SOONG_PROGUARD_DICT :=\nLOCAL_SOONG_PROGUARD_USAGE_ZIP :=\nLOCAL_SOONG_PROVIDER_TEST_SUITES :=\nLOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=\nLOCAL_SOONG_TRANSITIVE_RES_PACKAGES :=\nLOCAL_SOONG_DEVICE_RRO_DIRS :=\nLOCAL_SOONG_PRODUCT_RRO_DIRS :=\nLOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=\nLOCAL_SOONG_SYMBOL_PATH :=\nLOCAL_SOONG_TOC :=\nLOCAL_SOONG_UNSTRIPPED_BINARY :=\nLOCAL_SOONG_VNDK_VERSION :=\n# '',true\nLOCAL_SOURCE_FILES_ALL_GENERATED:=\nLOCAL_SRC_FILES:=\nLOCAL_SRC_FILES_EXCLUDE:=\nLOCAL_SRCJARS:=\nLOCAL_STATIC_ANDROID_LIBRARIES:=\nLOCAL_STATIC_JAVA_AAR_LIBRARIES:=\nLOCAL_STATIC_JAVA_LIBRARIES:=\nLOCAL_STATIC_LIBRARIES:=\nLOCAL_SYSTEM_EXT_MODULE:=\nLOCAL_STRIP_MODULE:=\nLOCAL_SYSTEM_SHARED_LIBRARIES:=none\nLOCAL_TARGET_REQUIRED_MODULES:=\nLOCAL_TEST_CONFIG:=\nLOCAL_TEST_CONFIG_SUFFIX:=\nLOCAL_TEST_DATA:=\nLOCAL_TEST_DATA_BINS:=\nLOCAL_TEST_MAINLINE_MODULES:=\nLOCAL_TEST_MODULE_TO_PROGUARD_WITH:=\nLOCAL_TEST_MODULE_CONFIG_BASE:=\nLOCAL_TIDY:=\nLOCAL_TIDY_CHECKS:=\nLOCAL_TIDY_FLAGS:=\nLOCAL_UNCOMPRESS_DEX:=\nLOCAL_UNINSTALLABLE_MODULE:=\nLOCAL_UNSTRIPPED_PATH:=\nLOCAL_USE_AAPT2:=\nLOCAL_USE_CLANG_LLD:=\nLOCAL_USE_VNDK:=\nLOCAL_IN_VENDOR:=\nLOCAL_IN_PRODUCT:=\nLOCAL_USES_LIBRARIES:=\nLOCAL_VENDOR_MODULE:=\nLOCAL_VINTF_FRAGMENTS:=\nLOCAL_VNDK_DEPEND_ON_CORE_VARIANT:=\nLOCAL_WARNINGS_ENABLE:=\nLOCAL_WHOLE_STATIC_LIBRARIES:=\nLOCAL_YACCFLAGS:=\nLOCAL_CHECK_ELF_FILES:=\n# arch specific variables\nLOCAL_ASFLAGS_$(TARGET_ARCH):=\nLOCAL_CFLAGS_$(TARGET_ARCH):=\nLOCAL_C_INCLUDES_$(TARGET_ARCH):=\nLOCAL_CLANG_ASFLAGS_$(TARGET_ARCH):=\nLOCAL_CLANG_CFLAGS_$(TARGET_ARCH):=\nLOCAL_CLANG_CPPFLAGS_$(TARGET_ARCH):=\nLOCAL_CLANG_LDFLAGS_$(TARGET_ARCH):=\nLOCAL_CLANG_$(TARGET_ARCH):=\nLOCAL_CPPFLAGS_$(TARGET_ARCH):=\nLOCAL_GENERATED_SOURCES_$(TARGET_ARCH):=\nLOCAL_HEADER_LIBRARIES_$(TARGET_ARCH):=\nLOCAL_LDFLAGS_$(TARGET_ARCH):=\nLOCAL_PACK_MODULE_RELOCATIONS_$(TARGET_ARCH):=\nLOCAL_PREBUILT_JNI_LIBS_$(TARGET_ARCH):=\nLOCAL_REQUIRED_MODULES_$(TARGET_ARCH):=\nLOCAL_RUNTIME_LIBRARIES_$(TARGET_ARCH):=\nLOCAL_SHARED_LIBRARIES_$(TARGET_ARCH):=\nLOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH):=\nLOCAL_SOONG_JNI_LIBS_SYMBOLS:=\nLOCAL_SRC_FILES_EXCLUDE_$(TARGET_ARCH):=\nLOCAL_SRC_FILES_$(TARGET_ARCH):=\nLOCAL_STATIC_LIBRARIES_$(TARGET_ARCH):=\nLOCAL_STRIP_MODULE_$(TARGET_ARCH):=\nLOCAL_WHOLE_STATIC_LIBRARIES_$(TARGET_ARCH):=\nifdef TARGET_2ND_ARCH\nLOCAL_ASFLAGS_$(TARGET_2ND_ARCH):=\nLOCAL_CFLAGS_$(TARGET_2ND_ARCH):=\nLOCAL_C_INCLUDES_$(TARGET_2ND_ARCH):=\nLOCAL_CLANG_ASFLAGS_$(TARGET_2ND_ARCH):=\nLOCAL_CLANG_CFLAGS_$(TARGET_2ND_ARCH):=\nLOCAL_CLANG_CPPFLAGS_$(TARGET_2ND_ARCH):=\nLOCAL_CLANG_LDFLAGS_$(TARGET_2ND_ARCH):=\nLOCAL_CLANG_$(TARGET_2ND_ARCH):=\nLOCAL_CPPFLAGS_$(TARGET_2ND_ARCH):=\nLOCAL_GENERATED_SOURCES_$(TARGET_2ND_ARCH):=\nLOCAL_HEADER_LIBRARIES_$(TARGET_2ND_ARCH):=\nLOCAL_LDFLAGS_$(TARGET_2ND_ARCH):=\nLOCAL_PACK_MODULE_RELOCATIONS_$(TARGET_2ND_ARCH):=\nLOCAL_PREBUILT_JNI_LIBS_$(TARGET_2ND_ARCH):=\nLOCAL_REQUIRED_MODULES_$(TARGET_2ND_ARCH):=\nLOCAL_RUNTIME_LIBRARIES_$(TARGET_2ND_ARCH):=\nLOCAL_SHARED_LIBRARIES_$(TARGET_2ND_ARCH):=\nLOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH):=\nLOCAL_SRC_FILES_EXCLUDE_$(TARGET_2ND_ARCH):=\nLOCAL_SRC_FILES_$(TARGET_2ND_ARCH):=\nLOCAL_STATIC_LIBRARIES_$(TARGET_2ND_ARCH):=\nLOCAL_STRIP_MODULE_$(TARGET_2ND_ARCH):=\nLOCAL_WHOLE_STATIC_LIBRARIES_$(TARGET_2ND_ARCH):=\nendif\nLOCAL_ASFLAGS_$(HOST_ARCH):=\nLOCAL_CFLAGS_$(HOST_ARCH):=\nLOCAL_C_INCLUDES_$(HOST_ARCH):=\nLOCAL_CLANG_ASFLAGS_$(HOST_ARCH):=\nLOCAL_CLANG_CFLAGS_$(HOST_ARCH):=\nLOCAL_CLANG_CPPFLAGS_$(HOST_ARCH):=\nLOCAL_CLANG_$(HOST_ARCH):=\nLOCAL_CLANG_LDFLAGS_$(HOST_ARCH):=\nLOCAL_CPPFLAGS_$(HOST_ARCH):=\nLOCAL_GENERATED_SOURCES_$(HOST_ARCH):=\nLOCAL_HEADER_LIBRARIES_$(HOST_ARCH):=\nLOCAL_LDFLAGS_$(HOST_ARCH):=\nLOCAL_REQUIRED_MODULES_$(HOST_ARCH):=\nLOCAL_RUNTIME_LIBRARIES_$(HOST_ARCH):=\nLOCAL_SHARED_LIBRARIES_$(HOST_ARCH):=\nLOCAL_SRC_FILES_EXCLUDE_$(HOST_ARCH):=\nLOCAL_SRC_FILES_$(HOST_ARCH):=\nLOCAL_STATIC_LIBRARIES_$(HOST_ARCH):=\nLOCAL_WHOLE_STATIC_LIBRARIES_$(HOST_ARCH):=\nifdef HOST_2ND_ARCH\nLOCAL_ASFLAGS_$(HOST_2ND_ARCH):=\nLOCAL_CFLAGS_$(HOST_2ND_ARCH):=\nLOCAL_C_INCLUDES_$(HOST_2ND_ARCH):=\nLOCAL_CLANG_ASFLAGS_$(HOST_2ND_ARCH):=\nLOCAL_CLANG_CFLAGS_$(HOST_2ND_ARCH):=\nLOCAL_CLANG_CPPFLAGS_$(HOST_2ND_ARCH):=\nLOCAL_CLANG_$(HOST_2ND_ARCH):=\nLOCAL_CLANG_LDFLAGS_$(HOST_2ND_ARCH):=\nLOCAL_CPPFLAGS_$(HOST_2ND_ARCH):=\nLOCAL_GENERATED_SOURCES_$(HOST_2ND_ARCH):=\nLOCAL_HEADER_LIBRARIES_$(HOST_2ND_ARCH):=\nLOCAL_LDFLAGS_$(HOST_2ND_ARCH):=\nLOCAL_REQUIRED_MODULES_$(HOST_2ND_ARCH):=\nLOCAL_RUNTIME_LIBRARIES_$(HOST_2ND_ARCH):=\nLOCAL_SHARED_LIBRARIES_$(HOST_2ND_ARCH):=\nLOCAL_SRC_FILES_EXCLUDE_$(HOST_2ND_ARCH):=\nLOCAL_SRC_FILES_$(HOST_2ND_ARCH):=\nLOCAL_STATIC_LIBRARIES_$(HOST_2ND_ARCH):=\nLOCAL_WHOLE_STATIC_LIBRARIES_$(HOST_2ND_ARCH):=\nendif\n\nLOCAL_ASFLAGS_$(HOST_OS):=\nLOCAL_CFLAGS_$(HOST_OS):=\nLOCAL_C_INCLUDES_$(HOST_OS):=\nLOCAL_CPPFLAGS_$(HOST_OS):=\nLOCAL_GENERATED_SOURCES_$(HOST_OS):=\nLOCAL_HEADER_LIBRARIES_$(HOST_OS):=\nLOCAL_LDFLAGS_$(HOST_OS):=\nLOCAL_LDLIBS_$(HOST_OS):=\nLOCAL_REQUIRED_MODULES_$(HOST_OS):=\nLOCAL_RUNTIME_LIBRARIES_$(HOST_OS):=\nLOCAL_SHARED_LIBRARIES_$(HOST_OS):=\nLOCAL_SRC_FILES_$(HOST_OS):=\nLOCAL_STATIC_LIBRARIES_$(HOST_OS):=\n\nLOCAL_SRC_FILES_$(HOST_OS)_$(HOST_ARCH):=\nifdef HOST_2ND_ARCH\nLOCAL_SRC_FILES_$(HOST_OS)_$(HOST_2ND_ARCH):=\nendif\n\nLOCAL_ASFLAGS_32:=\nLOCAL_ASFLAGS_64:=\nLOCAL_CFLAGS_32:=\nLOCAL_CFLAGS_64:=\nLOCAL_C_INCLUDES_32:=\nLOCAL_C_INCLUDES_64:=\nLOCAL_CLANG_32:=\nLOCAL_CLANG_64:=\nLOCAL_CLANG_ASFLAGS_32:=\nLOCAL_CLANG_ASFLAGS_64:=\nLOCAL_CLANG_CFLAGS_32:=\nLOCAL_CLANG_CFLAGS_64:=\nLOCAL_CLANG_CPPFLAGS_32:=\nLOCAL_CLANG_CPPFLAGS_64:=\nLOCAL_CLANG_LDFLAGS_32:=\nLOCAL_CLANG_LDFLAGS_64:=\nLOCAL_CPPFLAGS_32:=\nLOCAL_CPPFLAGS_64:=\nLOCAL_GENERATED_SOURCES_32:=\nLOCAL_GENERATED_SOURCES_64:=\nLOCAL_HEADER_LIBRARIES_32:=\nLOCAL_HEADER_LIBRARIES_64:=\nLOCAL_INIT_RC_32:=\nLOCAL_INIT_RC_64:=\nLOCAL_LDFLAGS_32:=\nLOCAL_LDFLAGS_64:=\nLOCAL_MODULE_PATH_32:=\nLOCAL_MODULE_PATH_64:=\nLOCAL_MODULE_STEM_32:=\nLOCAL_MODULE_STEM_64:=\nLOCAL_MODULE_SYMLINKS_32:=\nLOCAL_MODULE_SYMLINKS_64:=\nLOCAL_RUNTIME_LIBRARIES_32:=\nLOCAL_RUNTIME_LIBRARIES_64:=\nLOCAL_SHARED_LIBRARIES_32:=\nLOCAL_SHARED_LIBRARIES_64:=\nLOCAL_SRC_FILES_32:=\nLOCAL_SRC_FILES_64:=\nLOCAL_SRC_FILES_EXCLUDE_32:=\nLOCAL_SRC_FILES_EXCLUDE_64:=\nLOCAL_STATIC_LIBRARIES_32:=\nLOCAL_STATIC_LIBRARIES_64:=\nLOCAL_WHOLE_STATIC_LIBRARIES_32:=\nLOCAL_WHOLE_STATIC_LIBRARIES_64:=\n\n# Robolectric variables\nLOCAL_INSTRUMENT_SOURCE_DIRS :=\nLOCAL_INSTRUMENT_SRCJARS :=\nLOCAL_ROBOTEST_FAILURE_FATAL :=\nLOCAL_ROBOTEST_FILES :=\nLOCAL_ROBOTEST_TIMEOUT :=\nLOCAL_TEST_PACKAGE :=\n\nfull_android_manifest :=\nnon_system_module :=\n\nmodule_license_metadata :=\n\n# Trim MAKEFILE_LIST so that $(call my-dir) doesn't need to\n# iterate over thousands of entries every time.\n# Leave the current makefile to make sure we don't break anything\n# that expects to be able to find the name of the current makefile.\nMAKEFILE_LIST := $(lastword $(MAKEFILE_LIST))\n"
  },
  {
    "path": "core/combo/HOST_darwin.mk",
    "content": "#\n# Copyright (C) 2006 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Configuration for Darwin (Mac OS X).\n# Included by combo/select.mk\n\ndefine $(combo_var_prefix)transform-shared-lib-to-toc\n$(call _gen_toc_command_for_macho,$(1),$(2))\nendef\n\nHOST_GLOBAL_ARFLAGS := cqs\n\nHOST_CUSTOM_LD_COMMAND := true\n\ndefine transform-host-o-to-shared-lib-inner\n$(hide) $(PRIVATE_CXX) \\\n        -dynamiclib -single_module -read_only_relocs suppress \\\n        $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \\\n            $(PRIVATE_HOST_GLOBAL_LDFLAGS) \\\n        ) \\\n        $(PRIVATE_ALL_OBJECTS) \\\n        $(addprefix -force_load , $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \\\n        $(PRIVATE_ALL_SHARED_LIBRARIES) \\\n        $(PRIVATE_ALL_STATIC_LIBRARIES) \\\n        $(PRIVATE_LDLIBS) \\\n        -o $@ \\\n        -install_name @rpath/$(notdir $@) \\\n        -Wl,-rpath,@loader_path/../$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES)) \\\n        -Wl,-rpath,@loader_path/$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES)) \\\n        $(PRIVATE_LDFLAGS)\nendef\n\ndefine transform-host-o-to-executable-inner\n$(hide) $(PRIVATE_CXX) \\\n        $(foreach path,$(PRIVATE_RPATHS), \\\n          -Wl,-rpath,@loader_path/$(path)) \\\n        -o $@ \\\n        -Wl,-headerpad_max_install_names \\\n        $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \\\n           $(PRIVATE_HOST_GLOBAL_LDFLAGS) \\\n        ) \\\n        $(PRIVATE_ALL_SHARED_LIBRARIES) \\\n        $(PRIVATE_ALL_OBJECTS) \\\n        $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \\\n        $(PRIVATE_ALL_STATIC_LIBRARIES) \\\n        $(PRIVATE_LDFLAGS) \\\n        $(PRIVATE_LDLIBS)\nendef\n"
  },
  {
    "path": "core/combo/HOST_linux.mk",
    "content": "#\n# Copyright (C) 2006 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Configuration for builds hosted on linux.\n# Included by combo/select.mk\n\ndefine $(combo_var_prefix)transform-shared-lib-to-toc\n$(call _gen_toc_command_for_elf,$(1),$(2))\nendef\n\n############################################################\n## Macros after this line are shared by the 64-bit config.\n"
  },
  {
    "path": "core/combo/TARGET_linux-arm.mk",
    "content": "#\n# Copyright (C) 2006 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Configuration for Linux on ARM.\n# Included by combo/select.mk\n\n# You can set TARGET_ARCH_VARIANT to use an arch version other\n# than ARMv5TE. Each value should correspond to a file named\n# $(BUILD_COMBOS)/arch/<name>.mk which must contain\n# makefile variable definitions. Their\n# purpose is to allow module Android.mk files to selectively compile\n# different versions of code based upon the funtionality and\n# instructions available in a given architecture version.\n#\n# The blocks also define specific arch_variant_cflags, which\n# include defines, and compiler settings for the given architecture\n# version.\n#\n\nKNOWN_ARMv8_CORES := cortex-a53 cortex-a53.a57 cortex-a55 cortex-a73 cortex-a75 cortex-a76\nKNOWN_ARMv8_CORES += kryo kryo385 exynos-m1 exynos-m2\n\nKNOWN_ARMv82a_CORES := cortex-a55 cortex-a75 kryo385\n\nifeq (,$(strip $(TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT)))\n  TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT := generic\nendif\n\n# This quickly checks TARGET_2ND_ARCH_VARIANT against the lists above.\nifneq (,$(filter $(TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT), $(KNOWN_ARMv82a_CORES)))\n  ifeq (,$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT))\n    TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT := armv8-2a\n  else ifneq (armv8-2a,$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT))\n    $(error Incorrect TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT, $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT). Use armv8-2a instead.)\n  endif\nelse ifneq (,$(filter $(TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT), $(KNOWN_ARMv8_CORES)))\n  ifeq (,$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT))\n    TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT := armv8-a\n  else ifneq ($(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT),armv8-a)\n    $(error Incorrect TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT, $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT). Use armv8-a instead.)\n  endif\nendif\n\nifeq ($(strip $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)),)\n  $(error TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT must be set)\nendif\n\n# NEON is mandatory, see: https://developer.android.com/ndk/guides/abis#v7a\nARCH_ARM_HAVE_VFP               := true\nARCH_ARM_HAVE_VFP_D32           := true\nARCH_ARM_HAVE_NEON              := true\n\ndefine $(combo_var_prefix)transform-shared-lib-to-toc\n$(call _gen_toc_command_for_elf,$(1),$(2))\nendef\n\n$(combo_2nd_arch_prefix)TARGET_PACK_MODULE_RELOCATIONS := true\n\n$(combo_2nd_arch_prefix)TARGET_LINKER := /system/bin/linker\n"
  },
  {
    "path": "core/combo/TARGET_linux-arm64.mk",
    "content": "#\n# Copyright (C) 2013 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Configuration for Linux on ARM.\n# Included by combo/select.mk\n\n# You can set TARGET_ARCH_VARIANT to use an arch version other\n# than ARMv5TE. Each value should correspond to a file named\n# $(BUILD_COMBOS)/arch/<name>.mk which must contain\n# makefile variable definitions. Their\n# purpose is to allow module Android.mk files to selectively compile\n# different versions of code based upon the funtionality and\n# instructions available in a given architecture version.\n#\n# The blocks also define specific arch_variant_cflags, which\n# include defines, and compiler settings for the given architecture\n# version.\n#\nifeq ($(strip $(TARGET_ARCH_VARIANT)),)\nTARGET_ARCH_VARIANT := armv8\nendif\n\ndefine $(combo_var_prefix)transform-shared-lib-to-toc\n$(call _gen_toc_command_for_elf,$(1),$(2))\nendef\n\nTARGET_PACK_MODULE_RELOCATIONS := true\n\nTARGET_LINKER := /system/bin/linker64\n"
  },
  {
    "path": "core/combo/TARGET_linux-riscv64.mk",
    "content": "#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Configuration for Linux on riscv64 as a target.\n# Included by combo/select.mk\n\n# Provide a default variant.\nifeq ($(strip $(TARGET_ARCH_VARIANT)),)\nTARGET_ARCH_VARIANT := riscv64\nendif\n\ndefine $(combo_var_prefix)transform-shared-lib-to-toc\n$(call _gen_toc_command_for_elf,$(1),$(2))\nendef\n\nTARGET_LINKER := /system/bin/linker64\n"
  },
  {
    "path": "core/combo/TARGET_linux-x86.mk",
    "content": "#\n# Copyright (C) 2006 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Configuration for Linux on x86 as a target.\n# Included by combo/select.mk\n\n# Provide a default variant.\nifeq ($(strip $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)),)\nTARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT := x86\nendif\n\ndefine $(combo_var_prefix)transform-shared-lib-to-toc\n$(call _gen_toc_command_for_elf,$(1),$(2))\nendef\n\n$(combo_2nd_arch_prefix)TARGET_PACK_MODULE_RELOCATIONS := true\n\n$(combo_2nd_arch_prefix)TARGET_LINKER := /system/bin/linker\n\n$(combo_2nd_arch_prefix)TARGET_GLOBAL_YASM_FLAGS := -f elf32 -m x86\n"
  },
  {
    "path": "core/combo/TARGET_linux-x86_64.mk",
    "content": "#\n# Copyright (C) 2006 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Configuration for Linux on x86_64 as a target.\n# Included by combo/select.mk\n\n# Provide a default variant.\nifeq ($(strip $(TARGET_ARCH_VARIANT)),)\nTARGET_ARCH_VARIANT := x86_64\nendif\n\ndefine $(combo_var_prefix)transform-shared-lib-to-toc\n$(call _gen_toc_command_for_elf,$(1),$(2))\nendef\n\nTARGET_LINKER := /system/bin/linker64\n\nTARGET_GLOBAL_YASM_FLAGS := -f elf64 -m amd64\n"
  },
  {
    "path": "core/combo/javac.mk",
    "content": "# Selects a Java compiler.\n#\n# Outputs:\n#   ANDROID_JAVA_TOOLCHAIN -- Directory that contains javac and other java tools\n#\n\nANDROID_COMPILE_WITH_JACK := false\n\nifdef TARGET_BUILD_APPS\n  ifndef TURBINE_ENABLED\n    TURBINE_ENABLED := false\n  endif\nendif\n\nANDROID_JAVA_TOOLCHAIN := $(ANDROID_JAVA_HOME)/bin\n\n# TODO(ccross): remove this, it is needed for now because it is used by\n# config.mk before makevars from soong are loaded\nJAVA := $(ANDROID_JAVA_TOOLCHAIN)/java -XX:OnError=\"cat hs_err_pid%p.log\" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads\n\n"
  },
  {
    "path": "core/combo/select.mk",
    "content": "#\n# Copyright (C) 2006 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Select a combo based on the compiler being used.\n#\n# Inputs:\n#\tcombo_target -- prefix for final variables (HOST_ or TARGET_)\n#\tcombo_2nd_arch_prefix -- it's defined if this is loaded for the 2nd arch.\n#\n\nifeq ($(combo_target),HOST_)\n  combo_os_arch := $(HOST_OS)\nelse\n  # Build a target string like \"linux-arm\" or \"darwin-x86\".\n  combo_os_arch := $($(combo_target)OS)-$($(combo_target)$(combo_2nd_arch_prefix)ARCH)\nendif\n\ncombo_var_prefix := $(combo_2nd_arch_prefix)$(combo_target)\n\n# Set reasonable defaults for the various variables\nifeq ($(combo_target),HOST_CROSS_)\n$(KATI_obsolete_var \\\n  $(combo_var_prefix)GLOBAL_ARFLAGS \\\n  $(combo_var_prefix)STATIC_LIB_SUFFIX \\\n  $(combo_var_prefix)transform-shared-lib-to-toc \\\n  ,HOST_CROSS builds are not supported in Make)\nelse\n\n$(combo_var_prefix)GLOBAL_ARFLAGS := crsPD --format=gnu\n\n$(combo_var_prefix)STATIC_LIB_SUFFIX := .a\n\n# Now include the combo for this specific target.\ninclude $(BUILD_COMBOS)/$(combo_target)$(combo_os_arch).mk\n\nendif\n"
  },
  {
    "path": "core/config.mk",
    "content": "# This is included by the top-level Makefile.\n# It sets up standard variables based on the\n# current configuration and platform, which\n# are not specific to what is being built.\n\nifndef KATI\n$(warning Directly using config.mk from make is no longer supported.)\n$(warning )\n$(warning If you are just attempting to build, you probably need to re-source envsetup.sh:)\n$(warning )\n$(warning $$ source build/envsetup.sh)\n$(warning )\n$(warning If you are attempting to emulate get_build_var, use one of the following:)\n$(warning $$ build/soong/soong_ui.bash --dumpvar-mode)\n$(warning $$ build/soong/soong_ui.bash --dumpvars-mode)\n$(warning )\n$(error done)\nendif\n\nBUILD_SYSTEM :=$= build/make/core\nBUILD_SYSTEM_COMMON :=$= build/make/common\n\ninclude $(BUILD_SYSTEM_COMMON)/core.mk\n\n# -----------------------------------------------------------------\n# Rules and functions to help copy important files to DIST_DIR\n# when requested. This must be included once only, and must be included before\n# soong_config (as soong_config calls make_vars-$(TARGET).mk, and soong may\n# propagate calls to dist-for-goals there).\ninclude $(BUILD_SYSTEM)/distdir.mk\n\n# Mark variables that should be coming as environment variables from soong_ui\n# as readonly\n.KATI_READONLY := OUT_DIR TMPDIR BUILD_DATETIME_FILE\nifdef CALLED_FROM_SETUP\n  .KATI_READONLY := CALLED_FROM_SETUP\nendif\nifdef KATI_PACKAGE_MK_DIR\n  .KATI_READONLY := KATI_PACKAGE_MK_DIR\nendif\n\n# Mark variables deprecated/obsolete\nCHANGES_URL := https://android.googlesource.com/platform/build/+/master/Changes.md\n.KATI_READONLY := CHANGES_URL\n$(KATI_deprecated_var TARGET_USES_64_BIT_BINDER,All devices use 64-bit binder by default now. Uses of TARGET_USES_64_BIT_BINDER should be removed.)\n$(KATI_deprecated_var PRODUCT_SEPOLICY_SPLIT,All devices are built with split sepolicy.)\n$(KATI_deprecated_var PRODUCT_SEPOLICY_SPLIT_OVERRIDE,All devices are built with split sepolicy.)\n$(KATI_obsolete_var PATH,Do not use PATH directly. See $(CHANGES_URL)#PATH)\n$(KATI_obsolete_var PYTHONPATH,Do not use PYTHONPATH directly. See $(CHANGES_URL)#PYTHONPATH)\n$(KATI_obsolete_var OUT,Use OUT_DIR instead. See $(CHANGES_URL)#OUT)\n$(KATI_obsolete_var ANDROID_HOST_OUT,Use HOST_OUT instead. See $(CHANGES_URL)#ANDROID_HOST_OUT)\n$(KATI_obsolete_var ANDROID_PRODUCT_OUT,Use PRODUCT_OUT instead. See $(CHANGES_URL)#ANDROID_PRODUCT_OUT)\n$(KATI_obsolete_var ANDROID_HOST_OUT_TESTCASES,Use HOST_OUT_TESTCASES instead. See $(CHANGES_URL)#ANDROID_HOST_OUT_TESTCASES)\n$(KATI_obsolete_var ANDROID_TARGET_OUT_TESTCASES,Use TARGET_OUT_TESTCASES instead. See $(CHANGES_URL)#ANDROID_TARGET_OUT_TESTCASES)\n$(KATI_obsolete_var ANDROID_BUILD_TOP,Use '.' instead. See $(CHANGES_URL)#ANDROID_BUILD_TOP)\n$(KATI_obsolete_var \\\n  ANDROID_TOOLCHAIN \\\n  ANDROID_TOOLCHAIN_2ND_ARCH \\\n  ANDROID_DEV_SCRIPTS \\\n  ANDROID_EMULATOR_PREBUILTS \\\n  ANDROID_PRE_BUILD_PATHS \\\n  ,See $(CHANGES_URL)#other_envsetup_variables)\n$(KATI_obsolete_var PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE,Set FCM Version in device manifest instead. See $(CHANGES_URL)#PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE)\n$(KATI_obsolete_var USE_CLANG_PLATFORM_BUILD,Clang is the only supported Android compiler. See $(CHANGES_URL)#USE_CLANG_PLATFORM_BUILD)\n$(KATI_obsolete_var BUILD_DROIDDOC,Droiddoc is only supported in Soong. See details on build/soong/java/droiddoc.go)\n$(KATI_obsolete_var BUILD_APIDIFF,Apidiff is only supported in Soong. See details on build/soong/java/droiddoc.go)\n$(KATI_obsolete_var \\\n  DEFAULT_GCC_CPP_STD_VERSION \\\n  HOST_GLOBAL_CFLAGS 2ND_HOST_GLOBAL_CFLAGS \\\n  HOST_GLOBAL_CONLYFLAGS 2ND_HOST_GLOBAL_CONLYFLAGS \\\n  HOST_GLOBAL_CPPFLAGS 2ND_HOST_GLOBAL_CPPFLAGS \\\n  HOST_GLOBAL_LDFLAGS 2ND_HOST_GLOBAL_LDFLAGS \\\n  HOST_GLOBAL_LLDFLAGS 2ND_HOST_GLOBAL_LLDFLAGS \\\n  HOST_CLANG_SUPPORTED 2ND_HOST_CLANG_SUPPORTED \\\n  HOST_CC 2ND_HOST_CC \\\n  HOST_CXX 2ND_HOST_CXX \\\n  HOST_CROSS_GLOBAL_CFLAGS 2ND_HOST_CROSS_GLOBAL_CFLAGS \\\n  HOST_CROSS_GLOBAL_CONLYFLAGS 2ND_HOST_CROSS_GLOBAL_CONLYFLAGS \\\n  HOST_CROSS_GLOBAL_CPPFLAGS 2ND_HOST_CROSS_GLOBAL_CPPFLAGS \\\n  HOST_CROSS_GLOBAL_LDFLAGS 2ND_HOST_CROSS_GLOBAL_LDFLAGS \\\n  HOST_CROSS_GLOBAL_LLDFLAGS 2ND_HOST_CROSS_GLOBAL_LLDFLAGS \\\n  HOST_CROSS_CLANG_SUPPORTED 2ND_HOST_CROSS_CLANG_SUPPORTED \\\n  HOST_CROSS_CC 2ND_HOST_CROSS_CC \\\n  HOST_CROSS_CXX 2ND_HOST_CROSS_CXX \\\n  TARGET_GLOBAL_CFLAGS 2ND_TARGET_GLOBAL_CFLAGS \\\n  TARGET_GLOBAL_CONLYFLAGS 2ND_TARGET_GLOBAL_CONLYFLAGS \\\n  TARGET_GLOBAL_CPPFLAGS 2ND_TARGET_GLOBAL_CPPFLAGS \\\n  TARGET_GLOBAL_LDFLAGS 2ND_TARGET_GLOBAL_LDFLAGS \\\n  TARGET_GLOBAL_LLDFLAGS 2ND_TARGET_GLOBAL_LLDFLAGS \\\n  TARGET_CLANG_SUPPORTED 2ND_TARGET_CLANG_SUPPORTED \\\n  TARGET_CC 2ND_TARGET_CC \\\n  TARGET_CXX 2ND_TARGET_CXX \\\n  TARGET_TOOLCHAIN_ROOT 2ND_TARGET_TOOLCHAIN_ROOT \\\n  HOST_TOOLCHAIN_ROOT 2ND_HOST_TOOLCHAIN_ROOT \\\n  HOST_CROSS_TOOLCHAIN_ROOT 2ND_HOST_CROSS_TOOLCHAIN_ROOT \\\n  HOST_TOOLS_PREFIX 2ND_HOST_TOOLS_PREFIX \\\n  HOST_CROSS_TOOLS_PREFIX 2ND_HOST_CROSS_TOOLS_PREFIX \\\n  HOST_GCC_VERSION 2ND_HOST_GCC_VERSION \\\n  HOST_CROSS_GCC_VERSION 2ND_HOST_CROSS_GCC_VERSION \\\n  TARGET_NDK_GCC_VERSION 2ND_TARGET_NDK_GCC_VERSION \\\n  GLOBAL_CFLAGS_NO_OVERRIDE GLOBAL_CPPFLAGS_NO_OVERRIDE \\\n  ,GCC support has been removed. Use Clang instead)\n$(KATI_obsolete_var DIST_DIR dist_goal,Use dist-for-goals instead. See $(CHANGES_URL)#dist)\n$(KATI_obsolete_var TARGET_ANDROID_FILESYSTEM_CONFIG_H,Use TARGET_FS_CONFIG_GEN instead)\n$(KATI_deprecated_var USER,Use BUILD_USERNAME instead. See $(CHANGES_URL)#USER)\n$(KATI_obsolete_var TARGET_ROOT_OUT_SBIN,/sbin has been removed, use /system/bin instead)\n$(KATI_obsolete_var TARGET_ROOT_OUT_SBIN_UNSTRIPPED,/sbin has been removed, use /system/bin instead)\n$(KATI_obsolete_var BUILD_BROKEN_PHONY_TARGETS)\n$(KATI_obsolete_var BUILD_BROKEN_DUP_COPY_HEADERS)\n$(KATI_obsolete_var BUILD_BROKEN_ENG_DEBUG_TAGS)\n$(KATI_obsolete_export It is a global setting. See $(CHANGES_URL)#export_keyword)\n$(KATI_obsolete_var BUILD_BROKEN_ANDROIDMK_EXPORTS)\n$(KATI_obsolete_var PRODUCT_NOTICE_SPLIT_OVERRIDE,Stop using this, keep calm, and carry on.)\n$(KATI_obsolete_var PRODUCT_STATIC_BOOT_CONTROL_HAL,Use shared library module instead. See $(CHANGES_URL)#PRODUCT_STATIC_BOOT_CONTROL_HAL)\n$(KATI_obsolete_var \\\n  ARCH_ARM_HAVE_ARMV7A \\\n  ARCH_DSP_REV \\\n  ARCH_HAVE_ALIGNED_DOUBLES \\\n  ARCH_MIPS_HAS_DSP \\\n  ARCH_MIPS_HAS_FPU \\\n  ARCH_MIPS_REV6 \\\n  ARCH_X86_HAVE_AES_NI \\\n  ARCH_X86_HAVE_AVX \\\n  ARCH_X86_HAVE_AVX2 \\\n  ARCH_X86_HAVE_AVX512 \\\n  ARCH_X86_HAVE_MOVBE \\\n  ARCH_X86_HAVE_POPCNT \\\n  ARCH_X86_HAVE_SSE4 \\\n  ARCH_X86_HAVE_SSE4_2 \\\n  ARCH_X86_HAVE_SSSE3 \\\n)\n$(KATI_obsolete_var PRODUCT_IOT)\n$(KATI_obsolete_var MD5SUM)\n$(KATI_obsolete_var BOARD_HAL_STATIC_LIBRARIES, See $(CHANGES_URL)#BOARD_HAL_STATIC_LIBRARIES)\n$(KATI_obsolete_var LOCAL_HAL_STATIC_LIBRARIES, See $(CHANGES_URL)#BOARD_HAL_STATIC_LIBRARIES)\n$(KATI_obsolete_var \\\n  TARGET_AUX_OS_VARIANT_LIST \\\n  LOCAL_AUX_ARCH \\\n  LOCAL_AUX_CPU \\\n  LOCAL_AUX_OS \\\n  LOCAL_AUX_OS_VARIANT \\\n  LOCAL_AUX_SUBARCH \\\n  LOCAL_AUX_TOOLCHAIN \\\n  LOCAL_CUSTOM_BUILD_STEP_INPUT \\\n  LOCAL_CUSTOM_BUILD_STEP_OUTPUT \\\n  LOCAL_IS_AUX_MODULE \\\n  ,AUX support has been removed)\n$(KATI_obsolete_var HOST_OUT_TEST_CONFIG TARGET_OUT_TEST_CONFIG LOCAL_TEST_CONFIG_OPTIONS)\n$(KATI_obsolete_var \\\n  TARGET_PROJECT_INCLUDES \\\n  2ND_TARGET_PROJECT_INCLUDES \\\n  TARGET_PROJECT_SYSTEM_INCLUDES \\\n  2ND_TARGET_PROJECT_SYSTEM_INCLUDES \\\n  ,Project include variables have been removed)\n$(KATI_obsolete_var TARGET_PREFER_32_BIT TARGET_PREFER_32_BIT_APPS TARGET_PREFER_32_BIT_EXECUTABLES)\n$(KATI_obsolete_var PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST,Use PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)\n$(KATI_obsolete_var PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST,Use PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST)\n$(KATI_obsolete_var COVERAGE_PATHS,Use NATIVE_COVERAGE_PATHS instead)\n$(KATI_obsolete_var COVERAGE_EXCLUDE_PATHS,Use NATIVE_COVERAGE_EXCLUDE_PATHS instead)\n$(KATI_obsolete_var BOARD_VNDK_RUNTIME_DISABLE,VNDK-Lite is no longer supported)\n$(KATI_obsolete_var LOCAL_SANITIZE_BLACKLIST,Use LOCAL_SANITIZE_BLOCKLIST instead)\n$(KATI_obsolete_var BOARD_PLAT_PUBLIC_SEPOLICY_DIR,Use SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS instead)\n$(KATI_obsolete_var BOARD_PLAT_PRIVATE_SEPOLICY_DIR,Use SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS instead)\n$(KATI_obsolete_var TARGET_NO_VENDOR_BOOT,Use PRODUCT_BUILD_VENDOR_BOOT_IMAGE instead)\n$(KATI_obsolete_var PRODUCT_CHECK_ELF_FILES,Use BUILD_BROKEN_PREBUILT_ELF_FILES instead)\n$(KATI_obsolete_var ALL_GENERATED_SOURCES,ALL_GENERATED_SOURCES is no longer used)\n$(KATI_obsolete_var ALL_ORIGINAL_DYNAMIC_BINARIES,ALL_ORIGINAL_DYNAMIC_BINARIES is no longer used)\n$(KATI_obsolete_var PRODUCT_SUPPORTS_VERITY,VB 1.0 and related variables are no longer supported)\n$(KATI_obsolete_var PRODUCT_SUPPORTS_VERITY_FEC,VB 1.0 and related variables are no longer supported)\n$(KATI_obsolete_var PRODUCT_SUPPORTS_BOOT_SIGNER,VB 1.0 and related variables are no longer supported)\n$(KATI_obsolete_var PRODUCT_VERITY_SIGNING_KEY,VB 1.0 and related variables are no longer supported)\n$(KATI_obsolete_var BOARD_PREBUILT_PVMFWIMAGE,pvmfw.bin is now built in AOSP and custom versions are no longer supported)\n$(KATI_obsolete_var BUILDING_PVMFW_IMAGE,BUILDING_PVMFW_IMAGE is no longer used)\n$(KATI_obsolete_var BOARD_BUILD_SYSTEM_ROOT_IMAGE)\n$(KATI_obsolete_var FS_GET_STATS)\n$(KATI_obsolete_var BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES)\n\n# Used to force goals to build.  Only use for conditionally defined goals.\n.PHONY: FORCE\nFORCE:\n\nORIGINAL_MAKECMDGOALS := $(MAKECMDGOALS)\n\nUNAME := $(shell uname -sm)\n\nSRC_TARGET_DIR := $(TOPDIR)build/make/target\n\n# Some specific paths to tools\nSRC_DROIDDOC_DIR := $(TOPDIR)build/make/tools/droiddoc\n\n# Mark some inputs as readonly\nifdef TARGET_DEVICE_DIR\n  .KATI_READONLY := TARGET_DEVICE_DIR\nendif\n\nONE_SHOT_MAKEFILE :=\n.KATI_READONLY := ONE_SHOT_MAKEFILE\n\n# Set up efficient math functions which are used in make.\n# Here since this file is included by envsetup as well as during build.\ninclude $(BUILD_SYSTEM_COMMON)/math.mk\n\ninclude $(BUILD_SYSTEM_COMMON)/strings.mk\n\ninclude $(BUILD_SYSTEM_COMMON)/json.mk\n\n# Various mappings to avoid hard-coding paths all over the place\ninclude $(BUILD_SYSTEM)/pathmap.mk\n\n# Allow projects to define their own globally-available variables\ninclude $(BUILD_SYSTEM)/project_definitions.mk\n\n# ###############################################################\n# Build system internal files\n# ###############################################################\n\nBUILD_COMBOS :=$= $(BUILD_SYSTEM)/combo\n\nCLEAR_VARS :=$= $(BUILD_SYSTEM)/clear_vars.mk\n\nBUILD_HOST_STATIC_LIBRARY :=$= $(BUILD_SYSTEM)/host_static_library.mk\nBUILD_HOST_SHARED_LIBRARY :=$= $(BUILD_SYSTEM)/host_shared_library.mk\nBUILD_STATIC_LIBRARY :=$= $(BUILD_SYSTEM)/static_library.mk\nBUILD_HEADER_LIBRARY :=$= $(BUILD_SYSTEM)/header_library.mk\nBUILD_SHARED_LIBRARY :=$= $(BUILD_SYSTEM)/shared_library.mk\nBUILD_EXECUTABLE :=$= $(BUILD_SYSTEM)/executable.mk\nBUILD_HOST_EXECUTABLE :=$= $(BUILD_SYSTEM)/host_executable.mk\nBUILD_PACKAGE :=$= $(BUILD_SYSTEM)/package.mk\nBUILD_PHONY_PACKAGE :=$= $(BUILD_SYSTEM)/phony_package.mk\nBUILD_RRO_PACKAGE :=$= $(BUILD_SYSTEM)/build_rro_package.mk\nBUILD_HOST_PREBUILT :=$= $(BUILD_SYSTEM)/host_prebuilt.mk\nBUILD_PREBUILT :=$= $(BUILD_SYSTEM)/prebuilt.mk\nBUILD_MULTI_PREBUILT :=$= $(BUILD_SYSTEM)/multi_prebuilt.mk\nBUILD_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/java_library.mk\nBUILD_STATIC_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/static_java_library.mk\nBUILD_HOST_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/host_java_library.mk\nBUILD_COPY_HEADERS :=$= $(BUILD_SYSTEM)/copy_headers.mk\nBUILD_NATIVE_TEST :=$= $(BUILD_SYSTEM)/native_test.mk\nBUILD_FUZZ_TEST :=$= $(BUILD_SYSTEM)/fuzz_test.mk\n\nBUILD_NOTICE_FILE :=$= $(BUILD_SYSTEM)/notice_files.mk\nBUILD_SBOM_GEN :=$= $(BUILD_SYSTEM)/sbom.mk\n\ninclude $(BUILD_SYSTEM)/deprecation.mk\n\n# ###############################################################\n# Parse out any modifier targets.\n# ###############################################################\n\nhide := @\n\n################################################################\n# Tools needed in product configuration makefiles.\n################################################################\nNORMALIZE_PATH := build/make/tools/normalize_path.py\n\n# $(1): the paths to be normalized\ndefine normalize-paths\n$(if $(1),$(shell $(NORMALIZE_PATH) $(1)))\nendef\n\n# ###############################################################\n# Set common values\n# ###############################################################\n\n# Initialize SOONG_CONFIG_NAMESPACES so that it isn't recursive.\nSOONG_CONFIG_NAMESPACES :=\n\n# TODO(asmundak): remove add_soong_config_namespace, add_soong_config_var,\n# and add_soong_config_var_value once all their usages are replaced with\n# soong_config_set/soong_config_append.\n\n# The add_soong_config_namespace function adds a namespace and initializes it\n# to be empty.\n# $1 is the namespace.\n# Ex: $(call add_soong_config_namespace,acme)\n\ndefine add_soong_config_namespace\n$(eval SOONG_CONFIG_NAMESPACES += $(strip $1)) \\\n$(eval SOONG_CONFIG_$(strip $1) :=)\nendef\n\n# The add_soong_config_var function adds a a list of soong config variables to\n# SOONG_CONFIG_*. The variables and their values are then available to a\n# soong_config_module_type in an Android.bp file.\n# $1 is the namespace. $2 is the list of variables.\n# Ex: $(call add_soong_config_var,acme,COOL_FEATURE_A COOL_FEATURE_B)\ndefine add_soong_config_var\n$(eval SOONG_CONFIG_$(strip $1) += $(strip $2)) \\\n$(foreach v,$(strip $2),$(eval SOONG_CONFIG_$(strip $1)_$v := $(strip $($v))))\nendef\n\n# The add_soong_config_var_value function defines a make variable and also adds\n# the variable to SOONG_CONFIG_*.\n# $1 is the namespace. $2 is the variable name. $3 is the variable value.\n# Ex: $(call add_soong_config_var_value,acme,COOL_FEATURE,true)\n\ndefine add_soong_config_var_value\n$(eval $(strip $2) := $(strip $3)) \\\n$(call add_soong_config_var,$1,$2)\nendef\n\n# Soong config namespace variables manipulation.\n#\n# internal utility to define a namespace and a variable in it.\ndefine soong_config_define_internal\n$(if $(filter $1,$(SOONG_CONFIG_NAMESPACES)),,$(eval SOONG_CONFIG_NAMESPACES:=$(SOONG_CONFIG_NAMESPACES) $(strip $1))) \\\n$(if $(filter $2,$(SOONG_CONFIG_$(strip $1))),,$(eval SOONG_CONFIG_$(strip $1):=$(SOONG_CONFIG_$(strip $1)) $(strip $2)))\nendef\n\n# soong_config_set defines the variable in the given Soong config namespace\n# and sets its value. If the namespace does not exist, it will be defined.\n# $1 is the namespace. $2 is the variable name. $3 is the variable value.\n# Ex: $(call soong_config_set,acme,COOL_FEATURE,true)\ndefine soong_config_set\n$(call soong_config_define_internal,$1,$2) \\\n$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(strip $3))\nendef\n\n# soong_config_set_bool is the same as soong_config_set, but it will\n# also type the variable as a bool, so that when using select() expressions\n# in blueprint files they can use boolean values instead of strings.\n# It will only accept \"true\" for its value, any other value will be\n# treated as false.\n# $1 is the namespace. $2 is the variable name. $3 is the variable value.\n# Ex: $(call soong_config_set_bool,acme,COOL_FEATURE,true)\ndefine soong_config_set_bool\n$(call soong_config_define_internal,$1,$2) \\\n$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(filter true,$3))\n$(eval SOONG_CONFIG_TYPE_$(strip $1)_$(strip $2):=bool)\nendef\n\n# soong_config_set_int is the same as soong_config_set, but it will\n# also type the variable as an integer, so that when using select() expressions\n# in blueprint files they can use integer values instead of strings.\n# It will error out if a non-integer is supplied\n# $1 is the namespace. $2 is the variable name. $3 is the variable value.\n# Ex: $(call soong_config_set_bool,acme,COOL_FEATURE,34)\ndefine soong_config_set_int\n$(call soong_config_define_internal,$1,$2) \\\n$(if $(call math_is_int,$3),,$(error soong_config_set_int called with non-integer value $(3)))\n$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(strip $3))\n$(eval SOONG_CONFIG_TYPE_$(strip $1)_$(strip $2):=int)\nendef\n\n# soong_config_set_string_list is the same as soong_config_set, but it will\n# also type the variable as a list of strings, so that when using select() expressions\n# in blueprint files they can use list values instead of strings.\n# The values of the list must be space-separated.\n# $1 is the namespace. $2 is the variable name. $3 is the variable value.\n# Ex: $(call soong_config_set_string_list,acme,COOL_LIBS,a b)\ndefine soong_config_set_string_list\n$(call soong_config_define_internal,$1,$2) \\\n$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(strip $3))\n$(eval SOONG_CONFIG_TYPE_$(strip $1)_$(strip $2):=string_list)\nendef\n\n# soong_config_append appends to the value of the variable in the given Soong\n# config namespace. If the variable does not exist, it will be defined. If the\n# namespace does not  exist, it will be defined.\n# $1 is the namespace, $2 is the variable name, $3 is the value\ndefine soong_config_append\n$(call soong_config_define_internal,$1,$2) \\\n$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(SOONG_CONFIG_$(strip $1)_$(strip $2)) $(strip $3))\nendef\n\n# soong_config_append gets to the value of the variable in the given Soong\n# config namespace. If the namespace or variables does not exist, an\n# empty string will be returned.\n# $1 is the namespace, $2 is the variable name\ndefine soong_config_get\n$(SOONG_CONFIG_$(strip $1)_$(strip $2))\nendef\n\n# Set the extensions used for various packages\nCOMMON_PACKAGE_SUFFIX := .zip\nCOMMON_JAVA_PACKAGE_SUFFIX := .jar\nCOMMON_ANDROID_PACKAGE_SUFFIX := .apk\n\nifdef TMPDIR\nJAVA_TMPDIR_ARG := -Djava.io.tmpdir=$(TMPDIR)\nelse\nJAVA_TMPDIR_ARG :=\nendif\n\n# These build broken variables are intended to be set in a buildspec file,\n# while other build broken flags are expected to be set in a board config.\n# These are build broken variables that are expected to apply across board\n# configs, generally for cross-cutting features.\n\n# Build broken variables that should be treated as booleans\n_build_broken_bool_vars :=\n\n# Build broken variables that should be treated as lists\n_build_broken_list_vars := \\\n  BUILD_BROKEN_PLUGIN_VALIDATION \\\n\n_build_broken_var_names := $(_build_broken_bool_vars)\n_build_broken_var_names += $(_build_broken_list_vars)\n$(foreach v,$(_build_broken_var_names),$(eval $(v) :=))\n\n# ###############################################################\n# Include sub-configuration files\n# ###############################################################\n\n# ---------------------------------------------------------------\n# Try to include buildspec.mk, which will try to set stuff up.\n# If this file doesn't exist, the environment variables will\n# be used, and if that doesn't work, then the default is an\n# arm build\nifndef ANDROID_BUILDSPEC\nANDROID_BUILDSPEC := $(TOPDIR)buildspec.mk\nendif\n-include $(ANDROID_BUILDSPEC)\n\n# ---------------------------------------------------------------\n# Define most of the global variables.  These are the ones that\n# are specific to the user's build configuration.\ninclude $(BUILD_SYSTEM)/envsetup.mk\n\n\n$(foreach var,$(_build_broken_bool_vars), \\\n  $(if $(filter-out true false,$($(var))), \\\n    $(error Valid values of $(var) are \"true\", \"false\", and \"\". Not \"$($(var))\")))\n\n.KATI_READONLY := $(_build_broken_var_names)\n\n# Returns true if it is a low memory device, otherwise it returns false.\ndefine is-low-mem-device\n$(if $(findstring ro.config.low_ram=true,$(PRODUCT_PROPERTY_OVERRIDES)),true,\\\n$(if $(findstring ro.config.low_ram=true,$(PRODUCT_DEFAULT_PROPERTY_OVERRIDES)),true,\\\n$(if $(findstring ro.config.low_ram=true,$(PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE)),true,\\\n$(if $(findstring ro.config.low_ram=true,$(PRODUCT_COMPATIBLE_PROPERTY)),true,\\\n$(if $(findstring ro.config.low_ram=true,$(PRODUCT_SYSTEM_DEFAULT_PROPERTIES)),true,\\\n$(if $(findstring ro.config.low_ram=true,$(PRODUCT_SYSTEM_EXT_PROPERTIES)),true,\\\n$(if $(findstring ro.config.low_ram=true,$(PRODUCT_PRODUCT_PROPERTIES)),true,\\\n$(if $(findstring ro.config.low_ram=true,$(PRODUCT_VENDOR_PROPERTIES)),true,\\\n$(if $(findstring ro.config.low_ram=true,$(PRODUCT_ODM_PROPERTIES)),true,false)))))))))\nendef\n\n# Set TARGET_MAX_PAGE_SIZE_SUPPORTED.\n# TARGET_MAX_PAGE_SIZE_SUPPORTED indicates the alignment of the ELF segments.\nifdef PRODUCT_MAX_PAGE_SIZE_SUPPORTED\n  TARGET_MAX_PAGE_SIZE_SUPPORTED := $(PRODUCT_MAX_PAGE_SIZE_SUPPORTED)\nelse ifeq ($(strip $(call is-low-mem-device)),true)\n  # Low memory device will have 4096 binary alignment.\n  TARGET_MAX_PAGE_SIZE_SUPPORTED := 4096\nelse ifeq ($(call math_lt,$(VSR_VENDOR_API_LEVEL),34),true)\n  TARGET_MAX_PAGE_SIZE_SUPPORTED := 4096\nelse ifeq (,$(filter arm64 x86_64,$(TARGET_ARCH)))\n  # TARGET_MAX_PAGE_SIZE_SUPPORTED > 4096 is only supported in arm64 and\n  # x86_64 targets.\n  TARGET_MAX_PAGE_SIZE_SUPPORTED := 4096\nelse\n  # The default binary alignment for userspace is 16384.\n  TARGET_MAX_PAGE_SIZE_SUPPORTED := 16384\nendif\n.KATI_READONLY := TARGET_MAX_PAGE_SIZE_SUPPORTED\n\n# Boolean variable determining if AOSP relies on bionic's PAGE_SIZE macro.\nifdef PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO\n  TARGET_NO_BIONIC_PAGE_SIZE_MACRO := $(PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO)\nelse ifeq ($(call math_lt,$(VSR_VENDOR_API_LEVEL),35),true)\n  TARGET_NO_BIONIC_PAGE_SIZE_MACRO := false\nelse\n  TARGET_NO_BIONIC_PAGE_SIZE_MACRO := true\nendif\n.KATI_READONLY := TARGET_NO_BIONIC_PAGE_SIZE_MACRO\n\n# Pruned directory options used when using findleaves.py\n# See envsetup.mk for a description of SCAN_EXCLUDE_DIRS\nFIND_LEAVES_EXCLUDES := $(addprefix --prune=, $(SCAN_EXCLUDE_DIRS) .repo .git)\n\n# The build system exposes several variables for where to find the kernel\n# headers:\n#   TARGET_DEVICE_KERNEL_HEADERS is automatically created for the current\n#       device being built. It is set as $(TARGET_DEVICE_DIR)/kernel-headers,\n#       e.g. device/samsung/tuna/kernel-headers. This directory is not\n#       explicitly set by anyone, the build system always adds this subdir.\n#\n#   TARGET_BOARD_KERNEL_HEADERS is specified by the BoardConfig.mk file\n#       to allow other directories to be included. This is useful if there's\n#       some common place where a few headers are being kept for a group\n#       of devices. For example, device/<vendor>/common/kernel-headers could\n#       contain some headers for several of <vendor>'s devices.\n#\n#   TARGET_PRODUCT_KERNEL_HEADERS is generated by the product inheritance\n#       graph. This allows architecture products to provide headers for the\n#       devices using that architecture. For example,\n#       hardware/ti/omap4xxx/omap4.mk will specify\n#       PRODUCT_VENDOR_KERNEL_HEADERS variable that specify where the omap4\n#       specific headers are, e.g. hardware/ti/omap4xxx/kernel-headers.\n#       The build system then combines all the values specified by all the\n#       PRODUCT_VENDOR_KERNEL_HEADERS directives in the product inheritance\n#       tree and then exports a TARGET_PRODUCT_KERNEL_HEADERS variable.\n#\n# The layout of subdirs in any of the kernel-headers dir should mirror the\n# layout of the kernel include/ directory. For example,\n#     device/samsung/tuna/kernel-headers/linux/,\n#     hardware/ti/omap4xxx/kernel-headers/media/,\n#     etc.\n#\n# NOTE: These directories MUST contain post-processed headers using the\n# bionic/libc/kernel/tools/clean_header.py tool. Additionally, the original\n# kernel headers must also be checked in, but in a different subdirectory. By\n# convention, the originals should be checked into original-kernel-headers\n# directory of the same parent dir. For example,\n#     device/samsung/tuna/kernel-headers            <----- post-processed\n#     device/samsung/tuna/original-kernel-headers   <----- originals\n#\nTARGET_DEVICE_KERNEL_HEADERS := $(strip $(wildcard $(TARGET_DEVICE_DIR)/kernel-headers))\n\ndefine validate-kernel-headers\n$(if $(firstword $(foreach hdr_dir,$(1),\\\n         $(filter-out kernel-headers,$(notdir $(hdr_dir))))),\\\n     $(error Kernel header dirs must be end in kernel-headers: $(1)))\nendef\n# also allow the board config to provide additional directories since\n# there could be device/oem/base_hw and device/oem/derived_hw\n# that both are valid devices but derived_hw needs to use kernel headers\n# from base_hw.\nTARGET_BOARD_KERNEL_HEADERS := $(strip $(wildcard $(TARGET_BOARD_KERNEL_HEADERS)))\nTARGET_BOARD_KERNEL_HEADERS := $(patsubst %/,%,$(TARGET_BOARD_KERNEL_HEADERS))\n$(call validate-kernel-headers,$(TARGET_BOARD_KERNEL_HEADERS))\n\n# then add product-inherited includes, to allow for\n# hardware/sivendor/chip/chip.mk to include their own headers\nTARGET_PRODUCT_KERNEL_HEADERS := $(strip $(wildcard $(PRODUCT_VENDOR_KERNEL_HEADERS)))\nTARGET_PRODUCT_KERNEL_HEADERS := $(patsubst %/,%,$(TARGET_PRODUCT_KERNEL_HEADERS))\n$(call validate-kernel-headers,$(TARGET_PRODUCT_KERNEL_HEADERS))\n.KATI_READONLY := TARGET_DEVICE_KERNEL_HEADERS TARGET_BOARD_KERNEL_HEADERS TARGET_PRODUCT_KERNEL_HEADERS\n\n# Commands to generate .toc file common to ELF .so files.\ndefine _gen_toc_command_for_elf\n$(hide) ($($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)READELF) -d $(1) | grep SONAME || echo \"No SONAME for $1\") > $(2)\n$(hide) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)READELF) --dyn-syms $(1) | awk '{$$2=\"\"; $$3=\"\"; print}' >> $(2)\nendef\n\n# Commands to generate .toc file from Darwin dynamic library.\ndefine _gen_toc_command_for_macho\n$(hide) $(HOST_OTOOL) -l $(1) | grep LC_ID_DYLIB -A 5 > $(2)\n$(hide) $(HOST_NM) -gP $(1) | cut -f1-2 -d\" \" | (grep -v U$$ >> $(2) || true)\nendef\n\n# Pick a Java compiler.\ninclude $(BUILD_SYSTEM)/combo/javac.mk\n\nifeq ($(CALLED_FROM_SETUP),true)\ninclude $(BUILD_SYSTEM)/ccache.mk\ninclude $(BUILD_SYSTEM)/rbe.mk\nendif\n\n# GCC version selection\nTARGET_GCC_VERSION := 4.9\nifdef TARGET_2ND_ARCH\n2ND_TARGET_GCC_VERSION := 4.9\nendif\n\n# Normalize WITH_STATIC_ANALYZER\nifeq ($(strip $(WITH_STATIC_ANALYZER)),0)\n  WITH_STATIC_ANALYZER :=\nendif\n\n# Unset WITH_TIDY_ONLY if global WITH_TIDY_ONLY is not true nor 1.\nifeq (,$(filter 1 true,$(WITH_TIDY_ONLY)))\n  WITH_TIDY_ONLY :=\nendif\n\n# ---------------------------------------------------------------\n# Check that the configuration is current.  We check that\n# BUILD_ENV_SEQUENCE_NUMBER is current against this value.\n# Don't fail if we're called from envsetup, so they have a\n# chance to update their environment.\n\nifeq (,$(strip $(CALLED_FROM_SETUP)))\nifneq (,$(strip $(BUILD_ENV_SEQUENCE_NUMBER)))\nifneq ($(BUILD_ENV_SEQUENCE_NUMBER),$(CORRECT_BUILD_ENV_SEQUENCE_NUMBER))\n$(warning BUILD_ENV_SEQUENCE_NUMBER is set incorrectly.)\n$(info *** If you use envsetup/lunch/choosecombo:)\n$(info ***   - Re-execute envsetup (\". envsetup.sh\"))\n$(info ***   - Re-run lunch or choosecombo)\n$(info *** If you use buildspec.mk:)\n$(info ***   - Look at buildspec.mk.default to see what has changed)\n$(info ***   - Update BUILD_ENV_SEQUENCE_NUMBER to \"$(CORRECT_BUILD_ENV_SEQUENCE_NUMBER)\")\n$(error bailing..)\nendif\nendif\nendif\n\n# ---------------------------------------------------------------\n# Whether we can expect a full build graph\nALLOW_MISSING_DEPENDENCIES := $(filter true,$(ALLOW_MISSING_DEPENDENCIES))\nifneq ($(TARGET_BUILD_APPS),)\nALLOW_MISSING_DEPENDENCIES := true\nendif\nifeq ($(TARGET_BUILD_UNBUNDLED_IMAGE),true)\nALLOW_MISSING_DEPENDENCIES := true\nendif\nifneq ($(filter true,$(SOONG_ALLOW_MISSING_DEPENDENCIES)),)\nALLOW_MISSING_DEPENDENCIES := true\nendif\n# Mac builds default to ALLOW_MISSING_DEPENDENCIES, at least until the host\n# tools aren't enabled by default for Mac.\nifeq ($(HOST_OS),darwin)\n  ALLOW_MISSING_DEPENDENCIES := true\nendif\n.KATI_READONLY := ALLOW_MISSING_DEPENDENCIES\n\nTARGET_BUILD_USE_PREBUILT_SDKS :=\nDISABLE_PREOPT :=\nDISABLE_PREOPT_BOOT_IMAGES :=\nifneq (,$(TARGET_BUILD_APPS)$(TARGET_BUILD_UNBUNDLED_IMAGE))\n  DISABLE_PREOPT := true\n  DISABLE_PREOPT_BOOT_IMAGES := true\nendif\nifeq (true,$(TARGET_BUILD_UNBUNDLED))\n  ifneq (true,$(UNBUNDLED_BUILD_SDKS_FROM_SOURCE))\n    TARGET_BUILD_USE_PREBUILT_SDKS := true\n  endif\nendif\n\n.KATI_READONLY := \\\n  TARGET_BUILD_USE_PREBUILT_SDKS \\\n  DISABLE_PREOPT \\\n  DISABLE_PREOPT_BOOT_IMAGES \\\n\nprebuilt_sdk_tools := prebuilts/sdk/tools\nprebuilt_sdk_tools_bin := $(prebuilt_sdk_tools)/$(HOST_OS)/bin\n\nprebuilt_build_tools := prebuilts/build-tools\nprebuilt_build_tools_wrappers := prebuilts/build-tools/common/bin\nprebuilt_build_tools_jars := prebuilts/build-tools/common/framework\nprebuilt_build_tools_bin_noasan := $(prebuilt_build_tools)/$(HOST_PREBUILT_TAG)/bin\nifeq ($(filter address,$(SANITIZE_HOST)),)\nprebuilt_build_tools_bin := $(prebuilt_build_tools_bin_noasan)\nelse\nprebuilt_build_tools_bin := $(prebuilt_build_tools)/$(HOST_PREBUILT_TAG)/asan/bin\nendif\n\n# Work around for b/68406220\n# This should match the soong version.\nUSE_D8 := true\n.KATI_READONLY := USE_D8\n\n#\n# Tools that are prebuilts for TARGET_BUILD_USE_PREBUILT_SDKS\n#\nifeq (,$(TARGET_BUILD_USE_PREBUILT_SDKS))\n  AAPT := $(HOST_OUT_EXECUTABLES)/aapt\nelse # TARGET_BUILD_USE_PREBUILT_SDKS\n  AAPT := $(prebuilt_sdk_tools_bin)/aapt\nendif # TARGET_BUILD_USE_PREBUILT_SDKS\n\nifeq (,$(TARGET_BUILD_USE_PREBUILT_SDKS))\n  # Use RenderScript prebuilts for unbundled builds\n  LLVM_RS_CC := $(HOST_OUT_EXECUTABLES)/llvm-rs-cc\n  BCC_COMPAT := $(HOST_OUT_EXECUTABLES)/bcc_compat\nelse\n  LLVM_RS_CC := $(prebuilt_sdk_tools_bin)/llvm-rs-cc\n  BCC_COMPAT := $(prebuilt_sdk_tools_bin)/bcc_compat\nendif\n\nprebuilt_sdk_tools :=\nprebuilt_sdk_tools_bin :=\n\nACP := $(prebuilt_build_tools_bin)/acp\nCKATI := $(prebuilt_build_tools_bin)/ckati\nDEPMOD := $(HOST_OUT_EXECUTABLES)/depmod\nFILESLIST := $(HOST_OUT_EXECUTABLES)/fileslist\nFILESLIST_UTIL :=$= build/make/tools/fileslist_util.py\nHOST_INIT_VERIFIER := $(HOST_OUT_EXECUTABLES)/host_init_verifier\nXMLLINT := $(HOST_OUT_EXECUTABLES)/xmllint\nACONFIG := $(HOST_OUT_EXECUTABLES)/aconfig\n\n# SOONG_ZIP is exported by Soong, but needs to be defined early for\n# $OUT/dexpreopt.global.  It will be verified against the Soong version.\nSOONG_ZIP := $(HOST_OUT_EXECUTABLES)/soong_zip\n\n# ---------------------------------------------------------------\n# Generic tools.\n\n# These dependencies are now handled via dependencies on prebuilt_build_tool\nBISON_DATA :=$=\n\nYASM := prebuilts/misc/$(BUILD_OS)-$(HOST_PREBUILT_ARCH)/yasm/yasm\n\nDOXYGEN:= doxygen\nifeq ($(HOST_OS),linux)\nBREAKPAD_DUMP_SYMS := $(HOST_OUT_EXECUTABLES)/dump_syms\nelse\n# For non-supported hosts, do not generate breakpad symbols.\nBREAKPAD_GENERATE_SYMBOLS := false\nendif\nGZIP := prebuilts/build-tools/path/$(BUILD_OS)-$(HOST_PREBUILT_ARCH)/gzip\nPROTOC := $(HOST_OUT_EXECUTABLES)/aprotoc$(HOST_EXECUTABLE_SUFFIX)\nNANOPB_SRCS := $(HOST_OUT_EXECUTABLES)/protoc-gen-nanopb\nMKBOOTFS := $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX)\nMINIGZIP := $(GZIP)\nLZ4 := $(HOST_OUT_EXECUTABLES)/lz4$(HOST_EXECUTABLE_SUFFIX)\nifeq (,$(strip $(BOARD_CUSTOM_MKBOOTIMG)))\nMKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX)\nelse\nMKBOOTIMG := $(BOARD_CUSTOM_MKBOOTIMG)\nendif\nifeq (,$(strip $(BOARD_CUSTOM_AVBTOOL)))\nAVBTOOL := $(HOST_OUT_EXECUTABLES)/avbtool$(HOST_EXECUTABLE_SUFFIX)\nelse\nAVBTOOL := $(BOARD_CUSTOM_AVBTOOL)\nendif\nAPICHECK := $(HOST_OUT_JAVA_LIBRARIES)/metalava$(COMMON_JAVA_PACKAGE_SUFFIX)\nMKEXTUSERIMG := $(HOST_OUT_EXECUTABLES)/mkuserimg_mke2fs\nMKE2FS_CONF := system/extras/ext4_utils/mke2fs.conf\nMKEROFS := $(HOST_OUT_EXECUTABLES)/mkfs.erofs\nMKSQUASHFSUSERIMG := $(HOST_OUT_EXECUTABLES)/mksquashfsimage\nMKF2FSUSERIMG := $(HOST_OUT_EXECUTABLES)/mkf2fsuserimg\nSIMG2IMG := $(HOST_OUT_EXECUTABLES)/simg2img$(HOST_EXECUTABLE_SUFFIX)\nE2FSCK := $(HOST_OUT_EXECUTABLES)/e2fsck$(HOST_EXECUTABLE_SUFFIX)\nTUNE2FS := $(HOST_OUT_EXECUTABLES)/tune2fs$(HOST_EXECUTABLE_SUFFIX)\nJARJAR := $(HOST_OUT_JAVA_LIBRARIES)/jarjar.jar\nDATA_BINDING_COMPILER := $(HOST_OUT_JAVA_LIBRARIES)/databinding-compiler.jar\nFAT16COPY := build/make/tools/fat16copy.py\nCHECK_ELF_FILE := $(HOST_OUT_EXECUTABLES)/check_elf_file$(HOST_EXECUTABLE_SUFFIX)\nLPMAKE := $(HOST_OUT_EXECUTABLES)/lpmake$(HOST_EXECUTABLE_SUFFIX)\nADD_IMG_TO_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/add_img_to_target_files$(HOST_EXECUTABLE_SUFFIX)\nBUILD_IMAGE := $(HOST_OUT_EXECUTABLES)/build_image$(HOST_EXECUTABLE_SUFFIX)\nifeq (,$(strip $(BOARD_CUSTOM_BUILD_SUPER_IMAGE)))\nBUILD_SUPER_IMAGE := $(HOST_OUT_EXECUTABLES)/build_super_image$(HOST_EXECUTABLE_SUFFIX)\nelse\nBUILD_SUPER_IMAGE := $(BOARD_CUSTOM_BUILD_SUPER_IMAGE)\nendif\nIMG_FROM_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/img_from_target_files$(HOST_EXECUTABLE_SUFFIX)\nUNPACK_BOOTIMG := $(HOST_OUT_EXECUTABLES)/unpack_bootimg\nMAKE_RECOVERY_PATCH := $(HOST_OUT_EXECUTABLES)/make_recovery_patch$(HOST_EXECUTABLE_SUFFIX)\nOTA_FROM_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/ota_from_target_files$(HOST_EXECUTABLE_SUFFIX)\nOTA_FROM_RAW_IMG := $(HOST_OUT_EXECUTABLES)/ota_from_raw_img$(HOST_EXECUTABLE_SUFFIX)\nSPARSE_IMG := $(HOST_OUT_EXECUTABLES)/sparse_img$(HOST_EXECUTABLE_SUFFIX)\nCHECK_PARTITION_SIZES := $(HOST_OUT_EXECUTABLES)/check_partition_sizes$(HOST_EXECUTABLE_SUFFIX)\nSYMBOLS_MAP := $(HOST_OUT_EXECUTABLES)/symbols_map\n\nPROGUARD_HOME := external/proguard\nPROGUARD := $(PROGUARD_HOME)/bin/proguard.sh\nPROGUARD_DEPS := $(PROGUARD) $(PROGUARD_HOME)/lib/proguard.jar\nJAVATAGS := $(HOST_OUT_EXECUTABLES)/java-event-log-tags\nMERGETAGS := $(HOST_OUT_EXECUTABLES)/merge-event-log-tags\nAPPEND2SIMG := $(HOST_OUT_EXECUTABLES)/append2simg\nVERITY_SIGNER := $(HOST_OUT_EXECUTABLES)/verity_signer\nBUILD_VERITY_METADATA := $(HOST_OUT_EXECUTABLES)/build_verity_metadata\nBUILD_VERITY_TREE := $(HOST_OUT_EXECUTABLES)/build_verity_tree\n\nDEXDUMP := $(HOST_OUT_EXECUTABLES)/dexdump$(BUILD_EXECUTABLE_SUFFIX)\nPROFMAN := $(HOST_OUT_EXECUTABLES)/profman\n\nGEN_SBOM := $(HOST_OUT_EXECUTABLES)/generate-sbom\n\nFINDBUGS_DIR := external/owasp/sanitizer/tools/findbugs/bin\nFINDBUGS := $(FINDBUGS_DIR)/findbugs\n\nJETIFIER := prebuilts/sdk/tools/jetifier/jetifier-standalone/bin/jetifier-standalone\n\nEXTRACT_KERNEL := build/make/tools/extract_kernel.py\n\n# Path to tools.jar\nHOST_JDK_TOOLS_JAR := $(ANDROID_JAVA8_HOME)/lib/tools.jar\n\nAPICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK)\n\n# Boolean variable determining if the allow list for compatible properties is enabled\nPRODUCT_COMPATIBLE_PROPERTY := true\nifeq ($(PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE),false)\n  $(error PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE is obsolete)\nendif\n\n.KATI_READONLY := \\\n    PRODUCT_COMPATIBLE_PROPERTY\n\n# TODO: remove all code referencing these, and remove override variables\nPRODUCT_FULL_TREBLE := true\nPRODUCT_TREBLE_LINKER_NAMESPACES := true\nPRODUCT_ENFORCE_VINTF_MANIFEST := true\n\n# TODO(b/114488870): disallow PRODUCT_FULL_TREBLE_OVERRIDE from being used.\n.KATI_READONLY := \\\n    PRODUCT_FULL_TREBLE \\\n    PRODUCT_TREBLE_LINKER_NAMESPACES \\\n    PRODUCT_ENFORCE_VINTF_MANIFEST \\\n\n# TODO(b/114488870): remove all sets of these everwhere, and disallow them to be used\n$(KATI_obsolete_var PRODUCT_TREBLE_LINKER_NAMESPACES_OVERRIDE,Deprecated.)\n$(KATI_obsolete_var PRODUCT_ENFORCE_VINTF_MANIFEST_OVERRIDE,Deprecated.)\n$(KATI_obsolete_var PRODUCT_FULL_TREBLE_OVERRIDE,Deprecated.)\n\n# BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED can be true only if early-mount of\n# partitions is supported. But the early-mount must be supported for full\n# treble products, and so BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED should be set\n# by default for full treble products.\nifeq ($(PRODUCT_FULL_TREBLE),true)\n  BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED ?= true\nendif\n\nifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),36),)\n  ifneq ($(NEED_AIDL_NDK_PLATFORM_BACKEND),)\n    $(error Must not set NEED_AIDL_NDK_PLATFORM_BACKEND, but it is set to: $(NEED_AIDL_NDK_PLATFORM_BACKEND). Support will be removed.)\n  endif\nendif\n\nifdef PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE\n  TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := $(PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE)\nelse ifeq (true,$(TARGET_BUILD_UNBUNDLED))\n  # unbundled builds may not have updated build sources\n  TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false\nelse ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),36),)\n  TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := true\nelse\n  TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false\nendif\n.KATI_READONLY := TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE\n\n# Set BOARD_SYSTEMSDK_VERSIONS to the latest SystemSDK version starting from P-launching\n# devices if unset.\nifndef BOARD_SYSTEMSDK_VERSIONS\n  ifeq (REL,$(PLATFORM_VERSION_CODENAME))\n    BOARD_SYSTEMSDK_VERSIONS := $(PLATFORM_SDK_VERSION)\n  else\n    BOARD_SYSTEMSDK_VERSIONS := $(PLATFORM_VERSION_CODENAME)\n  endif\nendif\n\nifndef BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES\n  BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES := current\nelse\n  ifdef PRODUCT_SHIPPING_API_LEVEL\n    ifneq ($(call math_lt,$(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES),$(PRODUCT_SHIPPING_API_LEVEL)),)\n      $(error BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES ($(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES)) must be greater than or equal to PRODUCT_SHIPPING_API_LEVEL ($(PRODUCT_SHIPPING_API_LEVEL)))\n    endif\n  endif\nendif\n.KATI_READONLY := BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES\n\nifdef PRODUCT_SHIPPING_API_LEVEL\n  ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29),)\n    ifneq ($(BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE),)\n      $(error When PRODUCT_SHIPPING_API_LEVEL >= 29, BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE cannot be set)\n    endif\n  endif\nendif\n\n# The default key if not set as LOCAL_CERTIFICATE\nifdef PRODUCT_DEFAULT_DEV_CERTIFICATE\n  DEFAULT_SYSTEM_DEV_CERTIFICATE := $(PRODUCT_DEFAULT_DEV_CERTIFICATE)\nelse\n  DEFAULT_SYSTEM_DEV_CERTIFICATE := build/make/target/product/security/testkey\nendif\n.KATI_READONLY := DEFAULT_SYSTEM_DEV_CERTIFICATE\n\n# Certificate for the NetworkStack sepolicy context\nifdef PRODUCT_MAINLINE_SEPOLICY_DEV_CERTIFICATES\n  MAINLINE_SEPOLICY_DEV_CERTIFICATES := $(PRODUCT_MAINLINE_SEPOLICY_DEV_CERTIFICATES)\nelse\n  MAINLINE_SEPOLICY_DEV_CERTIFICATES := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))\nendif\n.KATI_READONLY := MAINLINE_SEPOLICY_DEV_CERTIFICATES\n\nBUILD_NUMBER_FROM_FILE := $$(cat $(SOONG_OUT_DIR)/build_number.txt)\nBUILD_HOSTNAME_FROM_FILE := $$(cat $(SOONG_OUT_DIR)/build_hostname.txt)\nBUILD_DATETIME_FROM_FILE := $$(cat $(BUILD_DATETIME_FILE))\n\n# SEPolicy versions\n\n# PLATFORM_SEPOLICY_VERSION is a number of the form \"YYYYMM\" with \"YYYYMM\"\n# mapping to vFRC version.\nPLATFORM_SEPOLICY_VERSION := $(BOARD_API_LEVEL)\nBOARD_SEPOLICY_VERS := $(PLATFORM_SEPOLICY_VERSION)\n.KATI_READONLY := PLATFORM_SEPOLICY_VERSION BOARD_SEPOLICY_VERS\n\n# A list of SEPolicy versions, besides PLATFORM_SEPOLICY_VERSION, that the framework supports.\nPLATFORM_SEPOLICY_COMPAT_VERSIONS := \\\n    29.0 \\\n    30.0 \\\n    31.0 \\\n    32.0 \\\n    33.0 \\\n    34.0 \\\n\nPLATFORM_SEPOLICY_COMPAT_VERSIONS += $(foreach ver,\\\n    202404 \\\n    202504 \\\n    ,$(if $(filter true,$(call math_gt,$(PLATFORM_SEPOLICY_VERSION),$(ver))),$(ver)))\n\n.KATI_READONLY := \\\n    PLATFORM_SEPOLICY_COMPAT_VERSIONS \\\n    PLATFORM_SEPOLICY_VERSION \\\n\nBOARD_GENFS_LABELS_VERSION ?= $(BOARD_API_LEVEL)\nifeq ($(call math_gt,$(BOARD_API_LEVEL),$(BOARD_GENFS_LABELS_VERSION)),true)\n  $(error BOARD_GENFS_LABELS_VERSION ($(BOARD_GENFS_LABELS_VERSION)) must be greater than or equal to BOARD_API_LEVEL ($(BOARD_API_LEVEL)))\nendif\n\nifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true)\n  ifneq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)\n    $(error PRODUCT_USE_DYNAMIC_PARTITIONS must be true when PRODUCT_RETROFIT_DYNAMIC_PARTITIONS \\\n        is set)\n  endif\n  ifdef PRODUCT_SHIPPING_API_LEVEL\n    ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29))\n      $(error Devices with shipping API level $(PRODUCT_SHIPPING_API_LEVEL) must not set \\\n          PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)\n    endif\n  endif\nendif\n\nifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)\n    ifneq ($(PRODUCT_USE_DYNAMIC_PARTITION_SIZE),true)\n        $(error PRODUCT_USE_DYNAMIC_PARTITION_SIZE must be true for devices with dynamic partitions)\n    endif\nendif\n\nifeq ($(PRODUCT_BUILD_SUPER_PARTITION),true)\n    ifneq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)\n        $(error Can only build super partition for devices with dynamic partitions)\n    endif\nendif\n\n\nifeq ($(PRODUCT_USE_DYNAMIC_PARTITION_SIZE),true)\n\nifneq ($(BOARD_SYSTEMIMAGE_PARTITION_SIZE),)\nifneq ($(BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE),)\n$(error Should not define BOARD_SYSTEMIMAGE_PARTITION_SIZE and \\\n    BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE together)\nendif\nendif\n\nifneq ($(BOARD_VENDORIMAGE_PARTITION_SIZE),)\nifneq ($(BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE),)\n$(error Should not define BOARD_VENDORIMAGE_PARTITION_SIZE and \\\n    BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE together)\nendif\nendif\n\nifneq ($(BOARD_ODMIMAGE_PARTITION_SIZE),)\nifneq ($(BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE),)\n$(error Should not define BOARD_ODMIMAGE_PARTITION_SIZE and \\\n    BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE together)\nendif\nendif\n\nifneq ($(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE),)\nifneq ($(BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE),)\n$(error Should not define BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE and \\\n    BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE together)\nendif\nendif\n\nifneq ($(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE),)\nifneq ($(BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE),)\n$(error Should not define BOARD_ODM_DLKMIMAGE_PARTITION_SIZE and \\\n    BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE together)\nendif\nendif\n\nifneq ($(BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE),)\nifneq ($(BOARD_SYSTEM_DLKMIMAGE_PARTITION_RESERVED_SIZE),)\n$(error Should not define BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE and \\\n    BOARD_SYSTEM_DLKMIMAGE_PARTITION_RESERVED_SIZE together)\nendif\nendif\n\nifneq ($(BOARD_PRODUCTIMAGE_PARTITION_SIZE),)\nifneq ($(BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE),)\n$(error Should not define BOARD_PRODUCTIMAGE_PARTITION_SIZE and \\\n    BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE together)\nendif\nendif\n\nifneq ($(BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE),)\nifneq ($(BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE),)\n$(error Should not define BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE and \\\n    BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE together)\nendif\nendif\n\nendif # PRODUCT_USE_DYNAMIC_PARTITION_SIZE\n\nifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)\n\n# BOARD_SUPER_PARTITION_GROUPS defines a list of \"updatable groups\". Each updatable group is a\n# group of partitions that share the same pool of free spaces.\n# For each group in BOARD_SUPER_PARTITION_GROUPS, a BOARD_{GROUP}_SIZE and\n# BOARD_{GROUP}_PARTITION_PARTITION_LIST may be defined.\n#     - BOARD_{GROUP}_SIZE: The maximum sum of sizes of all partitions in the group.\n#       Must not be empty.\n#     - BOARD_{GROUP}_PARTITION_PARTITION_LIST: the list of partitions that belongs to this group.\n#       If empty, no partitions belong to this group, and the sum of sizes is effectively 0.\n$(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)), \\\n    $(eval BOARD_$(group)_SIZE := $(strip $(BOARD_$(group)_SIZE))) \\\n    $(if $(BOARD_$(group)_SIZE),,$(error BOARD_$(group)_SIZE must not be empty)) \\\n    $(eval .KATI_READONLY := BOARD_$(group)_SIZE) \\\n    $(eval BOARD_$(group)_PARTITION_LIST ?=) \\\n    $(eval .KATI_READONLY := BOARD_$(group)_PARTITION_LIST) \\\n)\n\n# Define BOARD_SUPER_PARTITION_PARTITION_LIST, the sum of all BOARD_*_PARTITION_LIST\nifdef BOARD_SUPER_PARTITION_PARTITION_LIST\n$(error BOARD_SUPER_PARTITION_PARTITION_LIST should not be defined, but computed from \\\n    BOARD_SUPER_PARTITION_GROUPS and BOARD_*_PARTITION_LIST)\nendif\nBOARD_SUPER_PARTITION_PARTITION_LIST := \\\n    $(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)),$(BOARD_$(group)_PARTITION_LIST))\n.KATI_READONLY := BOARD_SUPER_PARTITION_PARTITION_LIST\n\nifneq ($(BOARD_SUPER_PARTITION_SIZE),)\nifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true)\n\n# The metadata device must be specified manually for retrofitting.\nifeq ($(BOARD_SUPER_PARTITION_METADATA_DEVICE),)\n$(error Must specify BOARD_SUPER_PARTITION_METADATA_DEVICE if PRODUCT_RETROFIT_DYNAMIC_PARTITIONS=true.)\nendif\n\n# The super partition block device list must be specified manually for retrofitting.\nifeq ($(BOARD_SUPER_PARTITION_BLOCK_DEVICES),)\n$(error Must specify BOARD_SUPER_PARTITION_BLOCK_DEVICES if PRODUCT_RETROFIT_DYNAMIC_PARTITIONS=true.)\nendif\n\n# The metadata device must be included in the super partition block device list.\nifeq (,$(filter $(BOARD_SUPER_PARTITION_METADATA_DEVICE),$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)))\n$(error BOARD_SUPER_PARTITION_METADATA_DEVICE is not listed in BOARD_SUPER_PARTITION_BLOCK_DEVICES.)\nendif\n\n# The metadata device must be supplied to init via the kernel command-line.\nINTERNAL_KERNEL_CMDLINE += androidboot.super_partition=$(BOARD_SUPER_PARTITION_METADATA_DEVICE)\n\nBOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE := true\n\n# If \"vendor\" is listed as one of the dynamic partitions but without its image available (e.g. an\n# AOSP target built without vendor image), don't build the retrofit full OTA package. Because we\n# won't be able to build meaningful super_* images for retrofitting purpose.\nifneq (,$(filter vendor,$(BOARD_SUPER_PARTITION_PARTITION_LIST)))\nifndef BUILDING_VENDOR_IMAGE\nifndef BOARD_PREBUILT_VENDORIMAGE\nBOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE :=\nendif # BOARD_PREBUILT_VENDORIMAGE\nendif # BUILDING_VENDOR_IMAGE\nendif # BOARD_SUPER_PARTITION_PARTITION_LIST\n\nelse # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS\n\n# For normal devices, we populate BOARD_SUPER_PARTITION_BLOCK_DEVICES so the\n# build can handle both cases consistently.\nifeq ($(BOARD_SUPER_PARTITION_METADATA_DEVICE),)\nBOARD_SUPER_PARTITION_METADATA_DEVICE := super\nendif\n\nifeq ($(BOARD_SUPER_PARTITION_BLOCK_DEVICES),)\nBOARD_SUPER_PARTITION_BLOCK_DEVICES := $(BOARD_SUPER_PARTITION_METADATA_DEVICE)\nendif\n\n# If only one super block device, default to super partition size.\nifeq ($(word 2,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)),)\nBOARD_SUPER_PARTITION_$(call to-upper,$(strip $(BOARD_SUPER_PARTITION_BLOCK_DEVICES)))_DEVICE_SIZE ?= \\\n    $(BOARD_SUPER_PARTITION_SIZE)\nendif\n\nifneq ($(BOARD_SUPER_PARTITION_METADATA_DEVICE),super)\nINTERNAL_KERNEL_CMDLINE += androidboot.super_partition=$(BOARD_SUPER_PARTITION_METADATA_DEVICE)\nendif\nBOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE :=\n\nendif # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS\nendif # BOARD_SUPER_PARTITION_SIZE\nBOARD_SUPER_PARTITION_BLOCK_DEVICES ?=\n.KATI_READONLY := BOARD_SUPER_PARTITION_BLOCK_DEVICES\nBOARD_SUPER_PARTITION_METADATA_DEVICE ?=\n.KATI_READONLY := BOARD_SUPER_PARTITION_METADATA_DEVICE\nBOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE ?=\n.KATI_READONLY := BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE\n\n$(foreach device,$(call to-upper,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)), \\\n    $(eval BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE := $(strip $(BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE))) \\\n    $(if $(BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE),, \\\n        $(error BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE must not be empty)) \\\n    $(eval .KATI_READONLY := BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE))\n\nendif # PRODUCT_USE_DYNAMIC_PARTITIONS\n\n# By default, we build the hidden API csv files from source. You can use\n# prebuilt hiddenapi files by setting BOARD_PREBUILT_HIDDENAPI_DIR to the name\n# of a directory containing both prebuilt hiddenapi-flags.csv and\n# hiddenapi-index.csv.\nBOARD_PREBUILT_HIDDENAPI_DIR ?=\n.KATI_READONLY := BOARD_PREBUILT_HIDDENAPI_DIR\n\n# ###############################################################\n# Set up final options.\n# ###############################################################\n\n# We run gcc/clang with PWD=/proc/self/cwd to remove the $TOP\n# from the debug output. That way two builds in two different\n# directories will create the same output.\n# /proc doesn't exist on Darwin.\nifeq ($(HOST_OS),linux)\nRELATIVE_PWD := PWD=/proc/self/cwd\nelse\nRELATIVE_PWD :=\nendif\n\n# Flags for DEX2OAT\nfirst_non_empty_of_three = $(if $(1),$(1),$(if $(2),$(2),$(3)))\nDEX2OAT_TARGET_ARCH := $(TARGET_ARCH)\nDEX2OAT_TARGET_CPU_VARIANT := $(call first_non_empty_of_three,$(TARGET_CPU_VARIANT),$(TARGET_ARCH_VARIANT),default)\nDEX2OAT_TARGET_CPU_VARIANT_RUNTIME := $(call first_non_empty_of_three,$(TARGET_CPU_VARIANT_RUNTIME),$(TARGET_ARCH_VARIANT),default)\nDEX2OAT_TARGET_INSTRUCTION_SET_FEATURES := default\n\nifdef TARGET_2ND_ARCH\n$(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH := $(TARGET_2ND_ARCH)\n$(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT := $(call first_non_empty_of_three,$(TARGET_2ND_CPU_VARIANT),$(TARGET_2ND_ARCH_VARIANT),default)\n$(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT_RUNTIME := $(call first_non_empty_of_three,$(TARGET_2ND_CPU_VARIANT_RUNTIME),$(TARGET_2ND_ARCH_VARIANT),default)\n$(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES := default\nendif\n\n# ###############################################################\n# Collect a list of the SDK versions that we could compile against\n# For use with the LOCAL_SDK_VERSION variable for include $(BUILD_PACKAGE)\n# ###############################################################\n\nHISTORICAL_SDK_VERSIONS_ROOT := $(TOPDIR)prebuilts/sdk\nHISTORICAL_NDK_VERSIONS_ROOT := $(TOPDIR)prebuilts/ndk\n\n# The path where app can reference the support library resources.\nifdef TARGET_BUILD_USE_PREBUILT_SDKS\nSUPPORT_LIBRARY_ROOT := $(HISTORICAL_SDK_VERSIONS_ROOT)/current/support\nelse\nSUPPORT_LIBRARY_ROOT := frameworks/support\nendif\n\nget-sdk-version = $(if $(findstring _,$(1)),$(subst core_,,$(subst system_,,$(subst test_,,$(1)))),$(1))\nget-sdk-api = $(if $(findstring _,$(1)),$(patsubst %_$(call get-sdk-version,$(1)),%,$(1)),public)\nget-prebuilt-sdk-dir = $(HISTORICAL_SDK_VERSIONS_ROOT)/$(call get-sdk-version,$(1))/$(call get-sdk-api,$(1))\n\n# Resolve LOCAL_SDK_VERSION to prebuilt module name, e.g.:\n# 23 -> sdk_public_23_android\n# system_current -> sdk_system_current_android\n# $(1): An sdk version (LOCAL_SDK_VERSION)\n# $(2): optional library name (default: android)\ndefine resolve-prebuilt-sdk-module\n$(if $(findstring _,$(1)),\\\n  sdk_$(1)_$(or $(2),android),\\\n  sdk_public_$(1)_$(or $(2),android))\nendef\n\n# Resolve LOCAL_SDK_VERSION to prebuilt android.jar\n# $(1): LOCAL_SDK_VERSION\nresolve-prebuilt-sdk-jar-path = $(call get-prebuilt-sdk-dir,$(1))/android.jar\n\n# Resolve LOCAL_SDK_VERSION to prebuilt framework.aidl\n# $(1): An sdk version (LOCAL_SDK_VERSION)\nresolve-prebuilt-sdk-aidl-path = $(call get-prebuilt-sdk-dir,$(call get-sdk-version,$(1)))/framework.aidl\n\n# Historical SDK version N is stored in $(HISTORICAL_SDK_VERSIONS_ROOT)/N.\n# The 'current' version is whatever this source tree is.\n#\n# sgrax     is the opposite of xargs.  It takes the list of args and puts them\n#           on each line for sort to process.\n# sort -g   is a numeric sort, so 1 2 3 10 instead of 1 10 2 3.\n\n# Numerically sort a list of numbers\n# $(1): the list of numbers to be sorted\ndefine numerically_sort\n$(shell function sgrax() { \\\n    while [ -n \"$$1\" ] ; do echo $$1 ; shift ; done \\\n    } ; \\\n    ( sgrax $(1) | sort -g ) )\nendef\n\n# This produces a list like \"current/core current/public current/system 4/public\"\nTARGET_AVAILABLE_SDK_VERSIONS := $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/*/android.jar)\nTARGET_AVAILABLE_SDK_VERSIONS := $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/android.jar,%,$(TARGET_AVAILABLE_SDK_VERSIONS))\n# Strips and reorganizes the \"public\", \"core\", \"system\" and \"test\" subdirs.\nTARGET_AVAILABLE_SDK_VERSIONS := $(subst /public,,$(TARGET_AVAILABLE_SDK_VERSIONS))\nTARGET_AVAILABLE_SDK_VERSIONS := $(patsubst %/core,core_%,$(TARGET_AVAILABLE_SDK_VERSIONS))\nTARGET_AVAILABLE_SDK_VERSIONS := $(patsubst %/system,system_%,$(TARGET_AVAILABLE_SDK_VERSIONS))\nTARGET_AVAILABLE_SDK_VERSIONS := $(patsubst %/test,test_%,$(TARGET_AVAILABLE_SDK_VERSIONS))\n# module-lib and system-server are not supported in Make.\nTARGET_AVAILABLE_SDK_VERSIONS := $(filter-out %/module-lib %/system-server,$(TARGET_AVAILABLE_SDK_VERSIONS))\nTARGET_AVAIALBLE_SDK_VERSIONS := $(call numerically_sort,$(TARGET_AVAILABLE_SDK_VERSIONS))\n\nTARGET_SDK_VERSIONS_WITHOUT_JAVA_1_9_SUPPORT := $(call numbers_less_than,30,$(TARGET_AVAILABLE_SDK_VERSIONS))\nTARGET_SDK_VERSIONS_WITHOUT_JAVA_11_SUPPORT := $(call numbers_less_than,32,$(TARGET_AVAILABLE_SDK_VERSIONS))\nTARGET_SDK_VERSIONS_WITHOUT_JAVA_17_SUPPORT := $(call numbers_less_than,34,$(TARGET_AVAILABLE_SDK_VERSIONS))\n\nJAVA_LANGUAGE_VERSIONS_WITHOUT_SYSTEM_MODULES := 1.7 1.8\n\n# This is the standard way to name a directory containing prebuilt target\n# objects. E.g., prebuilt/$(TARGET_PREBUILT_TAG)/libc.so\nTARGET_PREBUILT_TAG := android-$(TARGET_ARCH)\nifdef TARGET_2ND_ARCH\nTARGET_2ND_PREBUILT_TAG := android-$(TARGET_2ND_ARCH)\nendif\n\n# Set up RS prebuilt variables for compatibility library\n\nRS_PREBUILT_CLCORE := prebuilts/sdk/renderscript/lib/$(TARGET_ARCH)/librsrt_$(TARGET_ARCH).bc\nRS_PREBUILT_COMPILER_RT := prebuilts/sdk/renderscript/lib/$(TARGET_ARCH)/libcompiler_rt.a\n\n# API Level lists for Renderscript Compat lib.\nRSCOMPAT_32BIT_ONLY_API_LEVELS := 8 9 10 11 12 13 14 15 16 17 18 19 20\nRSCOMPAT_NO_USAGEIO_API_LEVELS := 8 9 10 11 12 13\n\nAPPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION)\n\n# Add BUILD_NUMBER to apps if PRODUCT_BUILD_APPS_WITH_BUILD_NUMBER is defined.\nifeq ($(PRODUCT_BUILD_APPS_WITH_BUILD_NUMBER),true)\n  APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION)-$(BUILD_NUMBER_FROM_FILE)\nendif\n\n# ANDROID_WARNING_ALLOWED_PROJECTS is generated by build/soong.\ndefine find_warning_allowed_projects\n    $(filter $(ANDROID_WARNING_ALLOWED_PROJECTS),$(1)/)\nendef\n\nGOMA_POOL :=\nRBE_POOL :=\nGOMA_OR_RBE_POOL :=\n# When goma or RBE are enabled, kati will be passed --default_pool=local_pool to put\n# most rules into the local pool.  Explicitly set the pool to \"none\" for rules that\n# should be run outside the local pool, i.e. with -j500.\nifneq (,$(filter-out false,$(USE_GOMA)))\n  GOMA_POOL := none\n  GOMA_OR_RBE_POOL := none\nelse ifneq (,$(filter-out false,$(USE_RBE)))\n  RBE_POOL := none\n  GOMA_OR_RBE_POOL := none\nendif\n.KATI_READONLY := GOMA_POOL RBE_POOL GOMA_OR_RBE_POOL\n\nJAVAC_NINJA_POOL :=\nR8_NINJA_POOL :=\nD8_NINJA_POOL :=\n\nifneq ($(filter-out false,$(USE_RBE)),)\n  ifdef RBE_JAVAC\n    JAVAC_NINJA_POOL := $(RBE_POOL)\n  endif\n  ifdef RBE_R8\n    R8_NINJA_POOL := $(RBE_POOL)\n  endif\n  ifdef RBE_D8\n    D8_NINJA_POOL := $(RBE_POOL)\n  endif\nendif\n\n.KATI_READONLY := JAVAC_NINJA_POOL R8_NINJA_POOL D8_NINJA_POOL\n\n# Soong modules that are known to have broken optional_uses_libs dependencies.\nBUILD_WARNING_BAD_OPTIONAL_USES_LIBS_ALLOWLIST := LegacyCamera Gallery2\n\n# These goals don't need to collect and include Android.mks/CleanSpec.mks\n# in the source tree.\ndont_bother_goals := out product-graph\n\nifeq ($(TARGET_SYSTEM_PROP),)\nTARGET_SYSTEM_PROP := $(wildcard $(TARGET_DEVICE_DIR)/system.prop)\nendif\n\nifeq ($(TARGET_SYSTEM_EXT_PROP),)\nTARGET_SYSTEM_EXT_PROP := $(wildcard $(TARGET_DEVICE_DIR)/system_ext.prop)\nendif\n\nifeq ($(TARGET_PRODUCT_PROP),)\nTARGET_PRODUCT_PROP := $(wildcard $(TARGET_DEVICE_DIR)/product.prop)\nendif\n\nifeq ($(TARGET_ODM_PROP),)\nTARGET_ODM_PROP := $(wildcard $(TARGET_DEVICE_DIR)/odm.prop)\nendif\n\n.KATI_READONLY := \\\n    TARGET_SYSTEM_PROP \\\n    TARGET_SYSTEM_EXT_PROP \\\n    TARGET_PRODUCT_PROP \\\n    TARGET_ODM_PROP \\\n\ninclude $(BUILD_SYSTEM)/sysprop_config.mk\n\n# Make ANDROID Soong config variables visible to Android.mk files, for\n# consistency with those defined in BoardConfig.mk files.\ninclude $(BUILD_SYSTEM)/android_soong_config_vars.mk\n\n# EMMA_INSTRUMENT is set to true when coverage is enabled. Creates a suffix to\n# differeciate the coverage version of ninja files. This will save 5 minutes of\n# build time used to regenerate ninja.\nifeq (true,$(EMMA_INSTRUMENT))\nCOVERAGE_SUFFIX := .coverage\nendif\n\nSOONG_VARIABLES := $(SOONG_OUT_DIR)/soong.$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).variables\nSOONG_EXTRA_VARIABLES := $(SOONG_OUT_DIR)/soong.$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).extra.variables\n\nifeq ($(CALLED_FROM_SETUP),true)\ninclude $(BUILD_SYSTEM)/ninja_config.mk\ninclude $(BUILD_SYSTEM)/soong_config.mk\nendif\n\nSOONG_VARIABLES :=\nSOONG_EXTRA_VARIABLES :=\n\ninclude $(BUILD_SYSTEM)/dumpvar.mk\n\nifdef BOARD_VNDK_VERSION\nBOARD_VNDK_VERSION=\nendif\nifdef PLATFORM_VNDK_VERSION\nPLATFORM_VNDK_VERSION=\nendif\n\nifeq (true,$(FULL_SYSTEM_OPTIMIZE_JAVA))\nifeq (false,$(SYSTEM_OPTIMIZE_JAVA))\n$(error SYSTEM_OPTIMIZE_JAVA must be enabled when FULL_SYSTEM_OPTIMIZE_JAVA is enabled)\nendif\nendif\n\n# -----------------------------------------------------------------\n# Define fingerprint, thumbprint, and version tags for the current build\n#\n# BUILD_VERSION_TAGS is a comma-separated list of tags chosen by the device\n# implementer that further distinguishes the build. It's basically defined\n# by the device implementer. Here, we are adding a mandatory tag that\n# identifies the signing config of the build.\nBUILD_VERSION_TAGS := $(BUILD_VERSION_TAGS)\nifeq ($(TARGET_BUILD_TYPE),debug)\n  BUILD_VERSION_TAGS += debug\nendif\n# The \"test-keys\" tag marks builds signed with the old test keys,\n# which are available in the SDK.  \"dev-keys\" marks builds signed with\n# non-default dev keys (usually private keys from a vendor directory).\n# Both of these tags will be removed and replaced with \"release-keys\"\n# when the target-files is signed in a post-build step.\nifeq ($(DEFAULT_SYSTEM_DEV_CERTIFICATE),build/make/target/product/security/testkey)\nBUILD_KEYS := test-keys\nelse\nBUILD_KEYS := dev-keys\nendif\nBUILD_VERSION_TAGS += $(BUILD_KEYS)\nBUILD_VERSION_TAGS := $(subst $(space),$(comma),$(sort $(BUILD_VERSION_TAGS)))\n\n# BUILD_FINGERPRINT is used used to uniquely identify the combined build and\n# product; used by the OTA server.\nifeq (,$(strip $(BUILD_FINGERPRINT)))\n  BUILD_FINGERPRINT := $(PRODUCT_BRAND)/$(TARGET_PRODUCT)/$(TARGET_DEVICE):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER_FROM_FILE):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)\nendif\n\nBUILD_FINGERPRINT_FILE := $(PRODUCT_OUT)/build_fingerprint.txt\nifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_FINGERPRINT) >$(BUILD_FINGERPRINT_FILE).tmp && (if ! cmp -s $(BUILD_FINGERPRINT_FILE).tmp $(BUILD_FINGERPRINT_FILE); then mv $(BUILD_FINGERPRINT_FILE).tmp $(BUILD_FINGERPRINT_FILE); else rm $(BUILD_FINGERPRINT_FILE).tmp; fi) && grep \" \" $(BUILD_FINGERPRINT_FILE)))\n  $(error BUILD_FINGERPRINT cannot contain spaces: \"$(file <$(BUILD_FINGERPRINT_FILE))\")\nendif\nBUILD_FINGERPRINT_FROM_FILE := $$(cat $(BUILD_FINGERPRINT_FILE))\n# unset it for safety.\nBUILD_FINGERPRINT :=\n\n# BUILD_THUMBPRINT is used to uniquely identify the system build; used by the\n# OTA server. This purposefully excludes any product-specific variables.\nifeq (,$(strip $(BUILD_THUMBPRINT)))\n  BUILD_THUMBPRINT := $(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER_FROM_FILE):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)\nendif\n\nBUILD_THUMBPRINT_FILE := $(PRODUCT_OUT)/build_thumbprint.txt\nifeq ($(strip $(HAS_BUILD_NUMBER)),true)\n$(BUILD_THUMBPRINT_FILE): $(BUILD_NUMBER_FILE)\nendif\nifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_THUMBPRINT) >$(BUILD_THUMBPRINT_FILE) && grep \" \" $(BUILD_THUMBPRINT_FILE)))\n  $(error BUILD_THUMBPRINT cannot contain spaces: \"$(file <$(BUILD_THUMBPRINT_FILE))\")\nendif\n# unset it for safety.\nBUILD_THUMBPRINT_FILE :=\nBUILD_THUMBPRINT :=\n"
  },
  {
    "path": "core/config_sanitizers.mk",
    "content": "##############################################\n## Perform configuration steps for sanitizers.\n##############################################\n\nmy_sanitize := $(strip $(LOCAL_SANITIZE))\nmy_sanitize_diag := $(strip $(LOCAL_SANITIZE_DIAG))\n\nmy_global_sanitize :=\nmy_global_sanitize_diag :=\nifdef LOCAL_IS_HOST_MODULE\n  ifneq ($($(my_prefix)OS),windows)\n    my_global_sanitize := $(strip $(SANITIZE_HOST))\n\n    # SANITIZE_HOST=true is a deprecated way to say SANITIZE_HOST=address.\n    my_global_sanitize := $(subst true,address,$(my_global_sanitize))\n  endif\nelse\n  my_global_sanitize := $(strip $(SANITIZE_TARGET))\n  my_global_sanitize_diag := $(strip $(SANITIZE_TARGET_DIAG))\nendif\n\n# Disable global integer_overflow in excluded paths.\nifneq ($(filter integer_overflow, $(my_global_sanitize)),)\n  combined_exclude_paths := $(INTEGER_OVERFLOW_EXCLUDE_PATHS) \\\n                            $(PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS)\n\n  ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\\\n         $(filter $(dir)%,$(LOCAL_PATH)))),)\n    my_global_sanitize := $(filter-out integer_overflow,$(my_global_sanitize))\n    my_global_sanitize_diag := $(filter-out integer_overflow,$(my_global_sanitize_diag))\n  endif\nendif\n\n# Global integer sanitization doesn't support static modules.\nifeq ($(filter SHARED_LIBRARIES EXECUTABLES,$(LOCAL_MODULE_CLASS)),)\n  my_global_sanitize := $(filter-out integer_overflow,$(my_global_sanitize))\n  my_global_sanitize_diag := $(filter-out integer_overflow,$(my_global_sanitize_diag))\nendif\nifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)\n  my_global_sanitize := $(filter-out integer_overflow,$(my_global_sanitize))\n  my_global_sanitize_diag := $(filter-out integer_overflow,$(my_global_sanitize_diag))\nendif\n\n# Disable global CFI in excluded paths\nifneq ($(filter cfi, $(my_global_sanitize)),)\n  combined_exclude_paths := $(CFI_EXCLUDE_PATHS) \\\n                            $(PRODUCT_CFI_EXCLUDE_PATHS)\n\n  ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\\\n         $(filter $(dir)%,$(LOCAL_PATH)))),)\n    my_global_sanitize := $(filter-out cfi,$(my_global_sanitize))\n    my_global_sanitize_diag := $(filter-out cfi,$(my_global_sanitize_diag))\n  endif\nendif\n\n# Disable global memtag_heap in excluded paths\nifneq ($(filter memtag_heap, $(my_global_sanitize)),)\n  combined_exclude_paths := $(MEMTAG_HEAP_EXCLUDE_PATHS) \\\n                            $(PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS)\n\n  ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\\\n         $(filter $(dir)%,$(LOCAL_PATH)))),)\n    my_global_sanitize := $(filter-out memtag_heap,$(my_global_sanitize))\n    my_global_sanitize_diag := $(filter-out memtag_heap,$(my_global_sanitize_diag))\n  endif\nendif\n\n# Disable global HWASan in excluded paths\nifneq ($(filter hwaddress, $(my_global_sanitize)),)\n  combined_exclude_paths := $(HWASAN_EXCLUDE_PATHS) \\\n                            $(PRODUCT_HWASAN_EXCLUDE_PATHS)\n\n  ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\\\n         $(filter $(dir)%,$(LOCAL_PATH)))),)\n    my_global_sanitize := $(filter-out hwaddress,$(my_global_sanitize))\n    my_global_sanitize_diag := $(filter-out hwaddress,$(my_global_sanitize_diag))\n  endif\nendif\n\nifneq ($(my_global_sanitize),)\n  my_sanitize := $(my_global_sanitize) $(my_sanitize)\nendif\nifneq ($(my_global_sanitize_diag),)\n  my_sanitize_diag := $(my_global_sanitize_diag) $(my_sanitize_diag)\nendif\n\n# The sanitizer specified in the product configuration wins over the previous.\nifneq ($(SANITIZER.$(TARGET_PRODUCT).$(LOCAL_MODULE).CONFIG),)\n  my_sanitize := $(SANITIZER.$(TARGET_PRODUCT).$(LOCAL_MODULE).CONFIG)\n  ifeq ($(my_sanitize),never)\n    my_sanitize :=\n    my_sanitize_diag :=\n  endif\nendif\n\nifndef LOCAL_IS_HOST_MODULE\n  # Add a filter point for 32-bit vs 64-bit sanitization (to lighten the burden)\n  SANITIZE_TARGET_ARCH ?= $(TARGET_ARCH) $(TARGET_2ND_ARCH)\n  ifeq ($(filter $(SANITIZE_TARGET_ARCH),$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)),)\n    my_sanitize :=\n    my_sanitize_diag :=\n  endif\nendif\n\n# Add a filter point based on module owner (to lighten the burden). The format is a space- or\n# colon-separated list of owner names.\nifneq (,$(SANITIZE_NEVER_BY_OWNER))\n  ifneq (,$(LOCAL_MODULE_OWNER))\n    ifneq (,$(filter $(LOCAL_MODULE_OWNER),$(subst :, ,$(SANITIZE_NEVER_BY_OWNER))))\n      $(warning Not sanitizing $(LOCAL_MODULE) based on module owner.)\n      my_sanitize :=\n      my_sanitize_diag :=\n    endif\n  endif\nendif\n\n# Don't apply sanitizers to NDK code.\nifdef LOCAL_SDK_VERSION\n  my_sanitize :=\n  my_global_sanitize :=\n  my_sanitize_diag :=\nendif\n\n# Never always wins.\nifeq ($(LOCAL_SANITIZE),never)\n  my_sanitize :=\n  my_sanitize_diag :=\nendif\n\n# Enable CFI in included paths.\nifeq ($(filter cfi, $(my_sanitize)),)\n  combined_include_paths := $(CFI_INCLUDE_PATHS) \\\n                            $(PRODUCT_CFI_INCLUDE_PATHS)\n  combined_exclude_paths := $(CFI_EXCLUDE_PATHS) \\\n                            $(PRODUCT_CFI_EXCLUDE_PATHS)\n\n  ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_include_paths)),\\\n         $(filter $(dir)%,$(LOCAL_PATH)))),)\n    ifeq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\\\n         $(filter $(dir)%,$(LOCAL_PATH)))),)\n      my_sanitize := cfi $(my_sanitize)\n    endif\n  endif\nendif\n\n# Enable memtag_heap in included paths (for Arm64 only).\nifeq ($(filter memtag_heap, $(my_sanitize)),)\n  ifneq ($(filter arm64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)),)\n    combined_sync_include_paths := $(MEMTAG_HEAP_SYNC_INCLUDE_PATHS) \\\n                                   $(PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS)\n    combined_async_include_paths := $(MEMTAG_HEAP_ASYNC_INCLUDE_PATHS) \\\n                                    $(PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS)\n    combined_exclude_paths := $(MEMTAG_HEAP_EXCLUDE_PATHS) \\\n                              $(PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS)\n    ifneq ($(PRODUCT_MEMTAG_HEAP_SKIP_DEFAULT_PATHS),true)\n      combined_sync_include_paths += $(PRODUCT_MEMTAG_HEAP_SYNC_DEFAULT_INCLUDE_PATHS)\n      combined_async_include_paths += $(PRODUCT_MEMTAG_HEAP_ASYNC_DEFAULT_INCLUDE_PATHS)\n    endif\n\n    ifeq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\\\n          $(filter $(dir)%,$(LOCAL_PATH)))),)\n      ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_sync_include_paths)),\\\n             $(filter $(dir)%,$(LOCAL_PATH)))),)\n        my_sanitize := memtag_heap $(my_sanitize)\n        my_sanitize_diag := memtag_heap $(my_sanitize_diag)\n      else ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_async_include_paths)),\\\n             $(filter $(dir)%,$(LOCAL_PATH)))),)\n        my_sanitize := memtag_heap $(my_sanitize)\n      endif\n    endif\n  endif\nendif\n\n# Enable HWASan in included paths.\nifeq ($(filter hwaddress, $(my_sanitize)),)\n  combined_include_paths := $(HWASAN_INCLUDE_PATHS) \\\n                            $(PRODUCT_HWASAN_INCLUDE_PATHS)\n\n  ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_include_paths)),\\\n         $(filter $(dir)%,$(LOCAL_PATH)))),)\n    my_sanitize := hwaddress $(my_sanitize)\n  endif\nendif\n\n# If CFI is disabled globally, remove it from my_sanitize.\nifeq ($(strip $(ENABLE_CFI)),false)\n  my_sanitize := $(filter-out cfi,$(my_sanitize))\n  my_sanitize_diag := $(filter-out cfi,$(my_sanitize_diag))\nendif\n\n# Also disable CFI and MTE if ASAN is enabled.\nifneq ($(filter address,$(my_sanitize)),)\n  my_sanitize := $(filter-out cfi,$(my_sanitize))\n  my_sanitize := $(filter-out memtag_stack,$(my_sanitize))\n  my_sanitize := $(filter-out memtag_globals,$(my_sanitize))\n  my_sanitize := $(filter-out memtag_heap,$(my_sanitize))\n  my_sanitize_diag := $(filter-out cfi,$(my_sanitize_diag))\nendif\n\n# Disable memtag for host targets. Host executables in AndroidMk files are\n# deprecated, but some partners still have them floating around.\nifdef LOCAL_IS_HOST_MODULE\n  my_sanitize := $(filter-out memtag_heap memtag_stack memtag_globals,$(my_sanitize))\n  my_sanitize_diag := $(filter-out memtag_heap memtag_stack memtag_globals,$(my_sanitize_diag))\nendif\n\n# Disable sanitizers which need the UBSan runtime for host targets.\nifdef LOCAL_IS_HOST_MODULE\n  my_sanitize := $(filter-out cfi,$(my_sanitize))\n  my_sanitize_diag := $(filter-out cfi,$(my_sanitize_diag))\n  my_sanitize := $(filter-out signed-integer-overflow unsigned-integer-overflow integer_overflow,$(my_sanitize))\n  my_sanitize_diag := $(filter-out signed-integer-overflow unsigned-integer-overflow integer_overflow,$(my_sanitize_diag))\nendif\n\n# Support for local sanitize blacklist paths.\nifneq ($(my_sanitize)$(my_global_sanitize),)\n  ifneq ($(LOCAL_SANITIZE_BLOCKLIST),)\n    my_cflags += -fsanitize-blacklist=$(LOCAL_PATH)/$(LOCAL_SANITIZE_BLOCKLIST)\n  endif\nendif\n\n# Disable integer_overflow if LOCAL_NOSANITIZE=integer.\nifneq ($(filter integer_overflow, $(my_global_sanitize) $(my_sanitize)),)\n  ifneq ($(filter integer, $(strip $(LOCAL_NOSANITIZE))),)\n    my_sanitize := $(filter-out integer_overflow,$(my_sanitize))\n    my_sanitize_diag := $(filter-out integer_overflow,$(my_sanitize_diag))\n  endif\nendif\n\nmy_nosanitize = $(strip $(LOCAL_NOSANITIZE))\nifneq ($(my_nosanitize),)\n  my_sanitize := $(filter-out $(my_nosanitize),$(my_sanitize))\nendif\n\nifneq ($(filter arm x86 x86_64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)),)\n  my_sanitize := $(filter-out hwaddress,$(my_sanitize))\n  my_sanitize := $(filter-out memtag_heap,$(my_sanitize))\n  my_sanitize := $(filter-out memtag_stack,$(my_sanitize))\n  my_sanitize := $(filter-out memtag_globals,$(my_sanitize))\nendif\n\nifneq ($(filter hwaddress,$(my_sanitize)),)\n  my_sanitize := $(filter-out address,$(my_sanitize))\n  my_sanitize := $(filter-out memtag_stack,$(my_sanitize))\n  my_sanitize := $(filter-out memtag_globals,$(my_sanitize))\n  my_sanitize := $(filter-out memtag_heap,$(my_sanitize))\n  my_sanitize := $(filter-out thread,$(my_sanitize))\n  my_sanitize := $(filter-out cfi,$(my_sanitize))\nendif\n\nifneq ($(filter hwaddress,$(my_sanitize)),)\n  my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)HWADDRESS_SANITIZER_RUNTIME_LIBRARY)\n  ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)\n    ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)\n      my_static_libraries := $(my_static_libraries) \\\n                             $($(LOCAL_2ND_ARCH_VAR_PREFIX)HWADDRESS_SANITIZER_STATIC_LIBRARY) \\\n                             libdl\n    endif\n  endif\nendif\n\nifneq ($(filter memtag_heap memtag_stack memtag_globals,$(my_sanitize)),)\n  ifneq ($(filter memtag_heap,$(my_sanitize_diag)),)\n    my_ldflags += -fsanitize-memtag-mode=sync\n    my_sanitize_diag := $(filter-out memtag_heap,$(my_sanitize_diag))\n  else\n    my_ldflags += -fsanitize-memtag-mode=async\n  endif\nendif\n\n# Ignore SANITIZE_TARGET_DIAG=memtag_heap without SANITIZE_TARGET=memtag_heap\n# This can happen if a condition above filters out memtag_heap from\n# my_sanitize. It is easier to handle all of these cases here centrally.\nifneq ($(filter memtag_heap,$(my_sanitize_diag)),)\n  my_sanitize_diag := $(filter-out memtag_heap,$(my_sanitize_diag))\nendif\n\nifneq ($(filter memtag_heap,$(my_sanitize)),)\n  my_cflags += -fsanitize=memtag-heap\n  my_ldflags += -fsanitize=memtag-heap\n  my_sanitize := $(filter-out memtag_heap,$(my_sanitize))\nendif\n\nifneq ($(filter memtag_stack,$(my_sanitize)),)\n  my_cflags += -fsanitize=memtag-stack\n  my_ldflags += -fsanitize=memtag-stack\n  my_cflags += -Xclang -target-feature -Xclang +mte\n  my_ldflags += -Xclang -target-feature -Xclang +mte\n  my_asflags += -Xclang -target-feature -Xclang +mte\n  my_sanitize := $(filter-out memtag_stack,$(my_sanitize))\nendif\n\nifneq ($(filter memtag_globals,$(my_sanitize)),)\n  my_cflags += -fsanitize=memtag-globals\n  my_ldflags += -fsanitize=memtag-globals\n  # TODO(mitchp): For now, enable memtag-heap with memtag-globals because the\n  # linker isn't new enough\n  # (https://reviews.llvm.org/differential/changeset/?ref=4243566).\n  my_sanitize := $(filter-out memtag_globals,$(my_sanitize))\nendif\n\n# TSAN is not supported on 32-bit architectures. For non-multilib cases, make\n# its use an error. For multilib cases, don't use it for the 32-bit case.\nifneq ($(filter thread,$(my_sanitize)),)\n  ifeq ($(my_32_64_bit_suffix),32)\n    ifeq ($(my_module_multilib),both)\n        my_sanitize := $(filter-out thread,$(my_sanitize))\n    else\n        $(error $(LOCAL_PATH): $(LOCAL_MODULE): TSAN cannot be used for 32-bit modules.)\n    endif\n  else\n    my_shared_libraries += $(TSAN_RUNTIME_LIBRARY)\n  endif\nendif\n\nifneq ($(filter safe-stack,$(my_sanitize)),)\n  ifeq ($(my_32_64_bit_suffix),32)\n    my_sanitize := $(filter-out safe-stack,$(my_sanitize))\n  endif\nendif\n\n# Disable Scudo if ASan or TSan is enabled.\nifneq ($(filter address thread hwaddress,$(my_sanitize)),)\n  my_sanitize := $(filter-out scudo,$(my_sanitize))\nendif\n\n# Or if disabled globally.\nifeq ($(PRODUCT_DISABLE_SCUDO),true)\n  my_sanitize := $(filter-out scudo,$(my_sanitize))\nendif\n\n# Undefined symbols can occur if a non-sanitized library links\n# sanitized static libraries. That's OK, because the executable\n# always depends on the ASan runtime library, which defines these\n# symbols.\nifneq ($(filter address thread,$(strip $(SANITIZE_TARGET))),)\n  ifndef LOCAL_IS_HOST_MODULE\n    ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)\n      ifeq ($(my_sanitize),)\n        my_allow_undefined_symbols := true\n      endif\n    endif\n  endif\nendif\n\nifneq ($(filter default-ub,$(my_sanitize)),)\n  my_sanitize := $(CLANG_DEFAULT_UB_CHECKS)\nendif\n\nifneq ($(filter fuzzer,$(my_sanitize)),)\n  # SANITIZE_TARGET='fuzzer' actually means to create the fuzzer coverage\n  # information, not to link against the fuzzer main().\n  my_sanitize := $(filter-out fuzzer,$(my_sanitize))\n  my_sanitize += fuzzer-no-link\n\n  # TODO(b/131771163): Disable LTO for fuzzer builds. Note that Cfi causes\n  # dependency on LTO.\n  my_sanitize := $(filter-out cfi,$(my_sanitize))\n  my_cflags += -fno-lto\n  my_ldflags += -fno-lto\n\n  # TODO(b/142430592): Upstream linker scripts for sanitizer runtime libraries\n  # discard the sancov_lowest_stack symbol, because it's emulated TLS (and thus\n  # doesn't match the linker script due to the \"__emutls_v.\" prefix).\n  my_cflags += -fno-sanitize-coverage=stack-depth\n  my_ldflags += -fno-sanitize-coverage=stack-depth\nendif\n\nifneq ($(filter integer_overflow,$(my_sanitize)),)\n  # Respect LOCAL_NOSANITIZE for integer-overflow flags.\n  ifeq ($(filter signed-integer-overflow, $(strip $(LOCAL_NOSANITIZE))),)\n    my_sanitize += signed-integer-overflow\n  endif\n  ifeq ($(filter unsigned-integer-overflow, $(strip $(LOCAL_NOSANITIZE))),)\n    my_sanitize += unsigned-integer-overflow\n  endif\n  my_cflags += $(INTEGER_OVERFLOW_EXTRA_CFLAGS)\n\n  # Check for diagnostics mode.\n  ifneq ($(filter integer_overflow,$(my_sanitize_diag)),)\n    ifneq ($(filter SHARED_LIBRARIES EXECUTABLES,$(LOCAL_MODULE_CLASS)),)\n      ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)\n        my_sanitize_diag += signed-integer-overflow\n        my_sanitize_diag += unsigned-integer-overflow\n      else\n        $(call pretty-error,Make cannot apply integer overflow diagnostics to static binary.)\n      endif\n    else\n      $(call pretty-error,Make cannot apply integer overflow diagnostics to static library.)\n    endif\n  endif\n  my_sanitize := $(filter-out integer_overflow,$(my_sanitize))\nendif\n\n# Makes sure integer_overflow diagnostics is removed from the diagnostics list\n# even if integer_overflow is not set for some reason.\nifneq ($(filter integer_overflow,$(my_sanitize_diag)),)\n  my_sanitize_diag := $(filter-out integer_overflow,$(my_sanitize_diag))\nendif\n\nifneq ($(my_sanitize),)\n  fsanitize_arg := $(subst $(space),$(comma),$(my_sanitize))\n  my_cflags += -fsanitize=$(fsanitize_arg)\n  my_asflags += -fsanitize=$(fsanitize_arg)\n\n  # When fuzzing, we wish to crash with diagnostics on any bug.\n  ifneq ($(filter fuzzer-no-link,$(my_sanitize)),)\n    my_cflags += -fno-sanitize-trap=all\n    my_cflags += -fno-sanitize-recover=all\n    my_ldflags += -fsanitize=fuzzer-no-link\n  else ifdef LOCAL_IS_HOST_MODULE\n    my_cflags += -fno-sanitize-recover=all\n    my_ldflags += -fsanitize=$(fsanitize_arg)\n  else\n    my_cflags += -fsanitize-trap=all\n    ifneq ($(filter address thread,$(my_sanitize)),)\n      my_cflags += -fno-sanitize-trap=address,thread\n      my_shared_libraries += libdl\n    endif\n  endif\nendif\n\nifneq ($(filter cfi,$(my_sanitize)),)\n  # __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp).\n  # LLVM is not set up to do this on a function basis, so force Thumb on the\n  # entire module.\n  LOCAL_ARM_MODE := thumb\n  my_cflags += $(CFI_EXTRA_CFLAGS)\n  my_asflags += $(CFI_EXTRA_ASFLAGS)\n  # Only append the default visibility flag if -fvisibility has not already been\n  # set to hidden.\n  ifeq ($(filter -fvisibility=hidden,$(LOCAL_CFLAGS)),)\n    my_cflags += -fvisibility=default\n  endif\n  my_ldflags += $(CFI_EXTRA_LDFLAGS)\n\n  ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)\n        my_ldflags := $(filter-out -fsanitize-cfi-cross-dso,$(my_ldflags))\n        my_cflags := $(filter-out -fsanitize-cfi-cross-dso,$(my_cflags))\n  else\n        # Apply the version script to non-static executables\n        my_ldflags += -Wl,--version-script,build/soong/cc/config/cfi_exports.map\n        LOCAL_ADDITIONAL_DEPENDENCIES += build/soong/cc/config/cfi_exports.map\n  endif\nendif\n\n# If local or global modules need ASAN, add linker flags.\nifneq ($(filter address,$(my_global_sanitize) $(my_sanitize)),)\n  my_ldflags += $(ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS)\n  ifdef LOCAL_IS_HOST_MODULE\n    # -nodefaultlibs (provided with libc++) prevents the driver from linking\n    # libraries needed with -fsanitize=address. http://b/18650275 (WAI)\n    my_ldflags += -Wl,--no-as-needed\n  else\n    # Add asan libraries unless LOCAL_MODULE is the asan library.\n    # ASan runtime library must be the first in the link order.\n    ifeq (,$(filter $(LOCAL_MODULE),$($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_RUNTIME_LIBRARY)))\n      my_shared_libraries := $($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_RUNTIME_LIBRARY) \\\n                             $(my_shared_libraries)\n    endif\n\n    # Do not add unnecessary dependency in shared libraries.\n    ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)\n      my_ldflags += -Wl,--as-needed\n    endif\n\n    ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)\n      ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)\n        my_linker := $($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_LINKER)\n        # Make sure linker_asan get installed.\n        $(LOCAL_INSTALLED_MODULE) : | $(PRODUCT_OUT)$($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_LINKER_FILE)\n      endif\n    endif\n  endif\nendif\n\n# If local module needs ASAN, add compiler flags.\nifneq ($(filter address,$(my_sanitize)),)\n  # Frame pointer based unwinder in ASan requires ARM frame setup.\n  LOCAL_ARM_MODE := arm\n  my_cflags += $(ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS)\n  ifndef LOCAL_IS_HOST_MODULE\n    my_cflags += -mllvm -asan-globals=0\n  endif\nendif\n\n# If local module needs HWASAN, add compiler flags.\nifneq ($(filter hwaddress,$(my_sanitize)),)\n  my_cflags += $(HWADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS)\n\n  ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)\n    ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)\n      my_linker := /system/bin/linker_hwasan64\n    endif\n  endif\n\nendif\n\n# Use minimal diagnostics when integer overflow is enabled; never do it for HOST modules\nifeq ($(LOCAL_IS_HOST_MODULE),)\n  # Pre-emptively add UBSAN minimal runtime incase a static library dependency requires it\n  ifeq ($(filter STATIC_LIBRARIES,$(LOCAL_MODULE_CLASS)),)\n    ifndef LOCAL_SDK_VERSION\n      my_static_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_MINIMAL_RUNTIME_LIBRARY)\n      my_ldflags += -Wl,--exclude-libs,$($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_MINIMAL_RUNTIME_LIBRARY).a\n    endif\n  endif\n  ifneq ($(filter unsigned-integer-overflow signed-integer-overflow integer,$(my_sanitize)),)\n    ifeq ($(filter unsigned-integer-overflow signed-integer-overflow integer,$(my_sanitize_diag)),)\n      ifeq ($(filter cfi,$(my_sanitize_diag)),)\n        ifeq ($(filter address hwaddress fuzzer-no-link,$(my_sanitize)),)\n          my_cflags += -fsanitize-minimal-runtime\n          my_cflags += -fno-sanitize-trap=integer\n          my_cflags += -fno-sanitize-recover=integer\n        endif\n      endif\n    endif\n  endif\nendif\n\n# For Scudo, we opt for the minimal runtime, unless some diagnostics are enabled.\nifneq ($(filter scudo,$(my_sanitize)),)\n  ifeq ($(filter unsigned-integer-overflow signed-integer-overflow integer cfi,$(my_sanitize_diag)),)\n    my_cflags += -fsanitize-minimal-runtime\n  endif\n  ifneq ($(filter -fsanitize-minimal-runtime,$(my_cflags)),)\n    my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)SCUDO_MINIMAL_RUNTIME_LIBRARY)\n  else\n    my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)SCUDO_RUNTIME_LIBRARY)\n  endif\nendif\n\nifneq ($(strip $(LOCAL_SANITIZE_RECOVER)),)\n  recover_arg := $(subst $(space),$(comma),$(LOCAL_SANITIZE_RECOVER)),\n  my_cflags += -fsanitize-recover=$(recover_arg)\nendif\n\nifneq ($(strip $(LOCAL_SANITIZE_NO_RECOVER)),)\n  no_recover_arg := $(subst $(space),$(comma),$(LOCAL_SANITIZE_NO_RECOVER)),\n  my_cflags += -fno-sanitize-recover=$(no_recover_arg)\nendif\n\nifneq ($(my_sanitize_diag),)\n  # TODO(vishwath): Add diagnostic support for static executables once\n  # we switch to clang-4393122 (which adds the static ubsan runtime\n  # that this depends on)\n  ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)\n    notrap_arg := $(subst $(space),$(comma),$(my_sanitize_diag)),\n    my_cflags += -fno-sanitize-trap=$(notrap_arg)\n    # Diagnostic requires a runtime library, unless ASan or TSan are also enabled.\n    ifeq ($(filter address thread scudo hwaddress,$(my_sanitize)),)\n      # Does not have to be the first DT_NEEDED unlike ASan.\n      my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_RUNTIME_LIBRARY)\n    endif\n  endif\nendif\n\n# http://b/119329758, Android core does not boot up with this sanitizer yet.\n# Previously sanitized modules might not pass new implicit-integer-sign-change check.\n# Disable this check unless it has been explicitly specified.\nifneq ($(findstring fsanitize,$(my_cflags)),)\n  ifneq ($(findstring integer,$(my_cflags)),)\n    ifeq ($(findstring sanitize=implicit-integer-sign-change,$(my_cflags)),)\n      my_cflags += -fno-sanitize=implicit-integer-sign-change\n    endif\n  endif\nendif\n\n# http://b/177566116, libc++ may crash with this sanitizer.\n# Disable this check unless it has been explicitly specified.\nifneq ($(findstring fsanitize,$(my_cflags)),)\n  ifneq ($(findstring integer,$(my_cflags)),)\n    ifeq ($(findstring sanitize=unsigned-shift-base,$(my_cflags)),)\n      my_cflags += -fno-sanitize=unsigned-shift-base\n    endif\n  endif\nendif\n"
  },
  {
    "path": "core/configure_module_stem.mk",
    "content": "my_multilib_stem := $(LOCAL_MODULE_STEM_$(if $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT),64,32))\nifdef my_multilib_stem\n  my_module_stem := $(my_multilib_stem)\n  $(call verify-module-stem,my_multilib_stem)\nelse ifdef LOCAL_MODULE_STEM\n  my_module_stem := $(LOCAL_MODULE_STEM)\n  $(call verify-module-stem,LOCAL_MODULE_STEM)\nelse\n  my_module_stem := $(LOCAL_MODULE)\nendif\n\nifdef LOCAL_BUILT_MODULE_STEM\n  my_built_module_stem := $(LOCAL_BUILT_MODULE_STEM)\n  $(call verify-module-stem,LOCAL_BUILT_MODULE_STEM)\nelse\n  my_built_module_stem := $(my_module_stem)$(LOCAL_MODULE_SUFFIX)\n  $(call verify-module-stem,LOCAL_MODULE_SUFFIX)\nendif\n\nifdef LOCAL_INSTALLED_MODULE_STEM\n  my_installed_module_stem := $(LOCAL_INSTALLED_MODULE_STEM)\n  $(call verify-module-stem,LOCAL_INSTALLED_MODULE_STEM)\nelse\n  my_installed_module_stem := $(my_module_stem)$(LOCAL_MODULE_SUFFIX)\n  $(call verify-module-stem,LOCAL_MODULE_SUFFIX)\nendif\n"
  },
  {
    "path": "core/copy_headers.mk",
    "content": "ifneq (,$(strip $(LOCAL_COPY_HEADERS)))\n###########################################################\n## Copy headers to the install tree\n###########################################################\n$(call record-module-type,COPY_HEADERS)\nifneq ($(strip $(LOCAL_IS_HOST_MODULE)),)\n  $(call pretty-error,LOCAL_COPY_HEADERS may not be used with host modules)\nendif\n\n# Modules linking against the SDK do not have the include path to use\n# COPY_HEADERS, so prevent them from exporting any either.\nifdef LOCAL_SDK_VERSION\n  $(call pretty-error,Modules using LOCAL_SDK_VERSION may not use LOCAL_COPY_HEADERS)\nendif\n\ninclude $(BUILD_SYSTEM)/local_vendor_product.mk\n\n# Modules in vendor or product may use LOCAL_COPY_HEADERS.\n# Platform libraries will not have the include path present.\nifeq ($(call module-in-vendor-or-product),)\n  $(call pretty-error,Only modules in vendor or product may use LOCAL_COPY_HEADERS)\nendif\n\n# Clean up LOCAL_COPY_HEADERS_TO, since soong_ui will be comparing cleaned\n# paths to figure out which headers are obsolete and should be removed.\nLOCAL_COPY_HEADERS_TO := $(call clean-path,$(LOCAL_COPY_HEADERS_TO))\nifneq ($(filter /% .. ../%,$(LOCAL_COPY_HEADERS_TO)),)\n  $(call pretty-error,LOCAL_COPY_HEADERS_TO may not start with / or ../ : $(LOCAL_COPY_HEADERS_TO))\nendif\nifeq ($(LOCAL_COPY_HEADERS_TO),.)\n  LOCAL_COPY_HEADERS_TO :=\nendif\n\n# Create a rule to copy each header, and make the\n# all_copied_headers phony target depend on each\n# destination header.  copy-one-header defines the\n# actual rule.\n#\n$(foreach header,$(LOCAL_COPY_HEADERS), \\\n  $(eval _chFrom := $(LOCAL_PATH)/$(header)) \\\n  $(eval _chTo := \\\n      $(if $(LOCAL_COPY_HEADERS_TO),\\\n        $(TARGET_OUT_HEADERS)/$(LOCAL_COPY_HEADERS_TO)/$(notdir $(header)),\\\n        $(TARGET_OUT_HEADERS)/$(notdir $(header)))) \\\n  $(eval ALL_COPIED_HEADERS.$(_chTo).MAKEFILE += $(LOCAL_MODULE_MAKEFILE)) \\\n  $(eval ALL_COPIED_HEADERS.$(_chTo).SRC += $(_chFrom)) \\\n  $(if $(filter $(_chTo),$(ALL_COPIED_HEADERS)),, \\\n      $(eval ALL_COPIED_HEADERS += $(_chTo))) \\\n )\n_chFrom :=\n_chTo :=\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=COPY_HEADERS))\nendif # LOCAL_COPY_HEADERS\n"
  },
  {
    "path": "core/cxx_stl_setup.mk",
    "content": "#############################################################\n## Set up flags based on LOCAL_CXX_STL.\n## Input variables: LOCAL_CXX_STL, my_prefix\n## Output variables: My_cflags, my_c_includes, my_shared_libraries, etc.\n#############################################################\n\n# Select the appropriate C++ STL\nifeq ($(strip $(LOCAL_CXX_STL)),default)\n    ifndef LOCAL_SDK_VERSION\n        # Platform code. Select the appropriate STL.\n        my_cxx_stl := libc++\n        ifdef LOCAL_IS_HOST_MODULE\n            ifneq (,$(BUILD_HOST_static))\n                my_cxx_stl := libc++_static\n            endif\n        endif\n    else\n        my_cxx_stl := ndk\n    endif\nelse\n    my_cxx_stl := $(strip $(LOCAL_CXX_STL))\n    ifdef LOCAL_SDK_VERSION\n        # The NDK has historically used LOCAL_NDK_STL_VARIANT to specify the\n        # STL. An Android.mk that specifies both LOCAL_CXX_STL and\n        # LOCAL_SDK_VERSION will incorrectly try (and most likely fail) to use\n        # the platform STL in an NDK binary. Emit an error to direct the user\n        # toward the correct option.\n        #\n        # Note that we could also accept LOCAL_CXX_STL as an alias for\n        # LOCAL_NDK_STL_VARIANT (and in fact soong does use the same name), but\n        # the two options use different names for the STLs.\n        $(error $(LOCAL_PATH): $(LOCAL_MODULE): Must use LOCAL_NDK_STL_VARIANT rather than LOCAL_CXX_STL for NDK binaries)\n    endif\nendif\n\nmy_link_type := dynamic\nifdef LOCAL_IS_HOST_MODULE\n    ifneq (,$(BUILD_HOST_static))\n        my_link_type := static\n    endif\n    ifeq (-static,$(filter -static,$(my_ldflags)))\n        my_link_type := static\n    endif\nelse\n    ifeq (true,$(LOCAL_FORCE_STATIC_EXECUTABLE))\n        my_link_type := static\n    endif\nendif\n\nmy_cxx_ldlibs :=\nifneq ($(filter $(my_cxx_stl),libc++ libc++_static),)\n    ifeq ($($(my_prefix)OS),darwin)\n        # libc++'s headers are annotated with availability macros that indicate\n        # which version of Mac OS was the first to ship with a libc++ feature\n        # available in its *system's* libc++.dylib. We do not use the system's\n        # library, but rather ship our own. As such, these availability\n        # attributes are meaningless for us but cause build breaks when we try\n        # to use code that would not be available in the system's dylib.\n        my_cppflags += -D_LIBCPP_DISABLE_AVAILABILITY\n    endif\n\n    # Note that the structure of this means that LOCAL_CXX_STL := libc++ will\n    # use the static libc++ for static executables.\n    ifeq ($(my_link_type),dynamic)\n        ifeq ($(my_cxx_stl),libc++)\n            my_shared_libraries += libc++\n        else\n            my_static_libraries += libc++_static\n        endif\n    else\n        my_static_libraries += libc++_static\n    endif\n\n    ifdef LOCAL_IS_HOST_MODULE\n        my_cppflags += -nostdinc++\n        my_ldflags += -nostdlib++\n    else\n        my_static_libraries += libc++demangle\n\n        ifeq ($(my_link_type),static)\n            my_static_libraries += libm libc libunwind libstatic_rustlibs_for_make\n        endif\n    endif\nelse ifeq ($(my_cxx_stl),ndk)\n    # Using an NDK STL. Handled in binary.mk.\nelse ifeq ($(my_cxx_stl),libstdc++)\n    $(error $(LOCAL_PATH): $(LOCAL_MODULE): libstdc++ is not supported)\nelse ifeq ($(my_cxx_stl),none)\n    ifdef LOCAL_IS_HOST_MODULE\n        my_cppflags += -nostdinc++\n        my_ldflags += -nostdlib++\n    endif\nelse\n    $(error $(LOCAL_PATH): $(LOCAL_MODULE): $(my_cxx_stl) is not a supported STL.)\nendif\n"
  },
  {
    "path": "core/definitions.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##\n## Common build system definitions.  Mostly standard\n## commands for building various types of targets, which\n## are used by others to construct the final targets.\n##\n\n# These are variables we use to collect overall lists\n# of things being processed.\n\n# Full paths to all of the documentation\nALL_DOCS:=\n\n# The short names of all of the targets in the system.\n# For each element of ALL_MODULES, two other variables\n# are defined:\n#   $(ALL_MODULES.$(target)).BUILT\n#   $(ALL_MODULES.$(target)).INSTALLED\n# The BUILT variable contains LOCAL_BUILT_MODULE for that\n# target, and the INSTALLED variable contains the LOCAL_INSTALLED_MODULE.\n# Some targets may have multiple files listed in the BUILT and INSTALLED\n# sub-variables.\nALL_MODULES:=\n\nALL_MAKE_MODULE_INFO_JSON_MODULES:=\n\n# The relative paths of the non-module targets in the system.\nALL_NON_MODULES:=\nNON_MODULES_WITHOUT_LICENSE_METADATA:=\n\n# List of copied targets that need license metadata copied.\nALL_COPIED_TARGETS:=\n\n# Full paths to targets that should be added to the \"make droid\"\n# set of installed targets.\nALL_DEFAULT_INSTALLED_MODULES:=\n\n# Full path to all asm, C, C++, lex and yacc generated C files.\n# These all have an order-only dependency on the copied headers\nALL_C_CPP_ETC_OBJECTS:=\n\n# These files go into the SDK\nALL_SDK_FILES:=\n\n# Files for dalvik.  This is often build without building the rest of the OS.\nINTERNAL_DALVIK_MODULES:=\n\n# All findbugs xml files\nALL_FINDBUGS_FILES:=\n\n# Packages with certificate violation\nCERTIFICATE_VIOLATION_MODULES :=\n\n# Target and host installed module's dependencies on shared libraries.\n# They are list of \"<module_name>:<installed_file>:lib1,lib2...\".\nTARGET_DEPENDENCIES_ON_SHARED_LIBRARIES :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_DEPENDENCIES_ON_SHARED_LIBRARIES :=\nHOST_DEPENDENCIES_ON_SHARED_LIBRARIES :=\n$(HOST_2ND_ARCH_VAR_PREFIX)HOST_DEPENDENCIES_ON_SHARED_LIBRARIES :=\nHOST_CROSS_DEPENDENCIES_ON_SHARED_LIBRARIES :=\n$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_DEPENDENCIES_ON_SHARED_LIBRARIES :=\n\n# Generated class file names for Android resource.\n# They are escaped and quoted so can be passed safely to a bash command.\nANDROID_RESOURCE_GENERATED_CLASSES := 'R.class' 'R$$*.class' 'Manifest.class' 'Manifest$$*.class'\n\n# Display names for various build targets\nTARGET_DISPLAY := target\nHOST_DISPLAY := host\nHOST_CROSS_DISPLAY := host cross\n\n# All installed initrc files\nALL_INIT_RC_INSTALLED_PAIRS :=\n\n# All installed vintf manifest fragments for a partition at\nALL_VINTF_MANIFEST_FRAGMENTS_LIST:=\n\n# All compatibility suites mentioned in LOCAL_COMPATIBILITY_SUITE\nALL_COMPATIBILITY_SUITES :=\n\n# All compatibility suite files to dist.\nALL_COMPATIBILITY_DIST_FILES :=\n\n# All LINK_TYPE entries\nALL_LINK_TYPES :=\n\n# All exported/imported include entries\nEXPORTS_LIST :=\n\n# All modules already converted to Soong\nSOONG_ALREADY_CONV :=\n\n###########################################################\n## Debugging; prints a variable list to stdout\n###########################################################\n\n# $(1): variable name list, not variable values\ndefine print-vars\n$(foreach var,$(1), \\\n  $(info $(var):) \\\n  $(foreach word,$($(var)), \\\n    $(info $(space)$(space)$(word)) \\\n   ) \\\n )\nendef\n\n###########################################################\n## Evaluates to true if the string contains the word true,\n## and empty otherwise\n## $(1): a var to test\n###########################################################\n\ndefine true-or-empty\n$(filter true, $(1))\nendef\n\ndefine boolean-not\n$(if $(filter true,$(1)),,true)\nendef\n\n###########################################################\n## Rule for touching GCNO files.\n###########################################################\ndefine gcno-touch-rule\n$(2): $(1)\n\ttouch -c $$@\nendef\n\n###########################################################\n\n###########################################################\n## Retrieve the directory of the current makefile\n## Must be called before including any other makefile!!\n###########################################################\n\n# Figure out where we are.\ndefine my-dir\n$(strip \\\n  $(eval LOCAL_MODULE_MAKEFILE := $$(lastword $$(MAKEFILE_LIST))) \\\n  $(if $(filter $(BUILD_SYSTEM)/% $(OUT_DIR)/%,$(LOCAL_MODULE_MAKEFILE)), \\\n    $(error my-dir must be called before including any other makefile.) \\\n   , \\\n    $(patsubst %/,%,$(dir $(LOCAL_MODULE_MAKEFILE))) \\\n   ) \\\n )\nendef\n\n\n###########################################################\n## Retrieve a list of all makefiles immediately below some directory\n###########################################################\n\ndefine all-makefiles-under\n$(wildcard $(1)/*/Android.mk)\nendef\n\n###########################################################\n## Look under a directory for makefiles that don't have parent\n## makefiles.\n###########################################################\n\n# $(1): directory to search under\n# Ignores $(1)/Android.mk\ndefine first-makefiles-under\n$(shell build/make/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) \\\n        --mindepth=2 $(addprefix --dir=,$(1)) Android.mk)\nendef\n\n###########################################################\n## Retrieve a list of all makefiles immediately below your directory\n## Must be called before including any other makefile!!\n###########################################################\n\ndefine all-subdir-makefiles\n$(call all-makefiles-under,$(call my-dir))\nendef\n\n###########################################################\n## Look in the named list of directories for makefiles,\n## relative to the current directory.\n## Must be called before including any other makefile!!\n###########################################################\n\n# $(1): List of directories to look for under this directory\ndefine all-named-subdir-makefiles\n$(wildcard $(addsuffix /Android.mk, $(addprefix $(call my-dir)/,$(1))))\nendef\n\n###########################################################\n## Find all of the directories under the named directories with\n## the specified name.\n## Meant to be used like:\n##    INC_DIRS := $(call all-named-dirs-under,inc,.)\n###########################################################\n\ndefine all-named-dirs-under\n$(call find-subdir-files,$(2) -type d -name \"$(1)\")\nendef\n\n###########################################################\n## Find all the directories under the current directory that\n## haves name that match $(1)\n###########################################################\n\ndefine all-subdir-named-dirs\n$(call all-named-dirs-under,$(1),.)\nendef\n\n###########################################################\n## Find all of the files under the named directories with\n## the specified name.\n## Meant to be used like:\n##    SRC_FILES := $(call all-named-files-under,*.h,src tests)\n###########################################################\n\ndefine all-named-files-under\n$(call find-files-in-subdirs,$(LOCAL_PATH),\"$(1)\",$(2))\nendef\n\n###########################################################\n## Find all of the files under the current directory with\n## the specified name.\n###########################################################\n\ndefine all-subdir-named-files\n$(call all-named-files-under,$(1),.)\nendef\n\n###########################################################\n## Find all of the java files under the named directories.\n## Meant to be used like:\n##    SRC_FILES := $(call all-java-files-under,src tests)\n###########################################################\n\ndefine all-java-files-under\n$(call all-named-files-under,*.java,$(1))\nendef\n\n###########################################################\n## Find all of the java files from here.  Meant to be used like:\n##    SRC_FILES := $(call all-subdir-java-files)\n###########################################################\n\ndefine all-subdir-java-files\n$(call all-java-files-under,.)\nendef\n\n###########################################################\n## Find all of the c files under the named directories.\n## Meant to be used like:\n##    SRC_FILES := $(call all-c-files-under,src tests)\n###########################################################\n\ndefine all-c-files-under\n$(call all-named-files-under,*.c,$(1))\nendef\n\n###########################################################\n## Find all of the c files from here.  Meant to be used like:\n##    SRC_FILES := $(call all-subdir-c-files)\n###########################################################\n\ndefine all-subdir-c-files\n$(call all-c-files-under,.)\nendef\n\n###########################################################\n## Find all of the cpp files under the named directories.\n## LOCAL_CPP_EXTENSION is respected if set.\n## Meant to be used like:\n##    SRC_FILES := $(call all-cpp-files-under,src tests)\n###########################################################\n\ndefine all-cpp-files-under\n$(sort $(patsubst ./%,%, \\\n  $(shell cd $(LOCAL_PATH) ; \\\n          find -L $(1) -name \"*$(or $(LOCAL_CPP_EXTENSION),.cpp)\" -and -not -name \".*\") \\\n ))\nendef\n\n###########################################################\n## Find all of the cpp files from here.  Meant to be used like:\n##    SRC_FILES := $(call all-subdir-cpp-files)\n###########################################################\n\ndefine all-subdir-cpp-files\n$(call all-cpp-files-under,.)\nendef\n\n###########################################################\n## Find all files named \"I*.aidl\" under the named directories,\n## which must be relative to $(LOCAL_PATH).  The returned list\n## is relative to $(LOCAL_PATH).\n###########################################################\n\ndefine all-Iaidl-files-under\n$(call all-named-files-under,I*.aidl,$(1))\nendef\n\n###########################################################\n## Find all of the \"I*.aidl\" files under $(LOCAL_PATH).\n###########################################################\n\ndefine all-subdir-Iaidl-files\n$(call all-Iaidl-files-under,.)\nendef\n\n###########################################################\n## Find all files named \"*.vts\" under the named directories,\n## which must be relative to $(LOCAL_PATH).  The returned list\n## is relative to $(LOCAL_PATH).\n###########################################################\n\ndefine all-vts-files-under\n$(call all-named-files-under,*.vts,$(1))\nendef\n\n###########################################################\n## Find all of the \"*.vts\" files under $(LOCAL_PATH).\n###########################################################\n\ndefine all-subdir-vts-files\n$(call all-vts-files-under,.)\nendef\n\n###########################################################\n## Find all of the logtags files under the named directories.\n## Meant to be used like:\n##    SRC_FILES := $(call all-logtags-files-under,src)\n###########################################################\n\ndefine all-logtags-files-under\n$(call all-named-files-under,*.logtags,$(1))\nendef\n\n###########################################################\n## Find all of the .proto files under the named directories.\n## Meant to be used like:\n##    SRC_FILES := $(call all-proto-files-under,src)\n###########################################################\n\ndefine all-proto-files-under\n$(call all-named-files-under,*.proto,$(1))\nendef\n\n###########################################################\n## Find all of the RenderScript files under the named directories.\n##  Meant to be used like:\n##    SRC_FILES := $(call all-renderscript-files-under,src)\n###########################################################\n\ndefine all-renderscript-files-under\n$(call find-subdir-files,$(1) \\( -name \"*.rscript\" -or -name \"*.fs\" \\) -and -not -name \".*\")\nendef\n\n###########################################################\n## Find all of the S files under the named directories.\n## Meant to be used like:\n##    SRC_FILES := $(call all-c-files-under,src tests)\n###########################################################\n\ndefine all-S-files-under\n$(call all-named-files-under,*.S,$(1))\nendef\n\n###########################################################\n## Find all of the html files under the named directories.\n## Meant to be used like:\n##    SRC_FILES := $(call all-html-files-under,src tests)\n###########################################################\n\ndefine all-html-files-under\n$(call all-named-files-under,*.html,$(1))\nendef\n\n###########################################################\n## Find all of the html files from here.  Meant to be used like:\n##    SRC_FILES := $(call all-subdir-html-files)\n###########################################################\n\ndefine all-subdir-html-files\n$(call all-html-files-under,.)\nendef\n\n###########################################################\n## Find all of the files matching pattern\n##    SRC_FILES := $(call find-subdir-files, <pattern>)\n###########################################################\n\ndefine find-subdir-files\n$(sort $(patsubst ./%,%,$(shell cd $(LOCAL_PATH) ; find -L $(1))))\nendef\n\n###########################################################\n# find the files in the subdirectory $1 of LOCAL_DIR\n# matching pattern $2, filtering out files $3\n# e.g.\n#     SRC_FILES += $(call find-subdir-subdir-files, \\\n#                         css, *.cpp, DontWantThis.cpp)\n###########################################################\n\ndefine find-subdir-subdir-files\n$(sort $(filter-out $(patsubst %,$(1)/%,$(3)),$(patsubst ./%,%,$(shell cd \\\n            $(LOCAL_PATH) ; find -L $(1) -maxdepth 1 -name $(2)))))\nendef\n\n###########################################################\n## Find all of the files matching pattern\n##    SRC_FILES := $(call all-subdir-java-files)\n###########################################################\n\ndefine find-subdir-assets\n$(sort $(if $(1),$(patsubst ./%,%, \\\n  $(shell if [ -d $(1) ] ; then cd $(1) ; find -L ./ -not -name '.*' -and -type f ; fi)), \\\n  $(warning Empty argument supplied to find-subdir-assets in $(LOCAL_PATH)) \\\n))\nendef\n\n###########################################################\n## Find various file types in a list of directories relative to $(LOCAL_PATH)\n###########################################################\n\ndefine find-other-java-files\n$(call all-java-files-under,$(1))\nendef\n\ndefine find-other-html-files\n$(call all-html-files-under,$(1))\nendef\n\n###########################################################\n# Use utility find to find given files in the given subdirs.\n# This function uses $(1), instead of LOCAL_PATH as the base.\n# $(1): the base dir, relative to the root of the source tree.\n# $(2): the file name pattern to be passed to find as \"-name\".\n# $(3): a list of subdirs of the base dir.\n# Returns: a list of paths relative to the base dir.\n###########################################################\n\ndefine find-files-in-subdirs\n$(sort $(patsubst ./%,%, \\\n  $(shell cd $(1) ; \\\n          find -L $(3) -name $(2) -and -not -name \".*\") \\\n ))\nendef\n\n###########################################################\n## Scan through each directory of $(1) looking for files\n## that match $(2) using $(wildcard).  Useful for seeing if\n## a given directory or one of its parents contains\n## a particular file.  Returns the first match found,\n## starting furthest from the root.\n###########################################################\n\ndefine find-parent-file\n$(strip \\\n  $(eval _fpf := $(sort $(wildcard $(foreach f, $(2), $(strip $(1))/$(f))))) \\\n  $(if $(_fpf),$(_fpf), \\\n       $(if $(filter-out ./ .,$(1)), \\\n             $(call find-parent-file,$(patsubst %/,%,$(dir $(1))),$(2)) \\\n        ) \\\n   ) \\\n)\nendef\n\n###########################################################\n## Find test data in a form required by LOCAL_TEST_DATA\n## $(1): the base dir, relative to the root of the source tree.\n## $(2): the file name pattern to be passed to find as \"-name\"\n## $(3): a list of subdirs of the base dir\n###########################################################\n\ndefine find-test-data-in-subdirs\n$(foreach f,$(sort $(patsubst ./%,%, \\\n  $(shell cd $(1) ; \\\n          find -L $(3) -type f -and -name $(2) -and -not -name \".*\") \\\n)),$(1):$(f))\nendef\n\n###########################################################\n## Function we can evaluate to introduce a dynamic dependency\n###########################################################\n\ndefine add-dependency\n$(1): $(2)\nendef\n\n###########################################################\n## Reverse order of a list\n###########################################################\n\ndefine reverse-list\n$(if $(1),$(call reverse-list,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1))\nendef\n\n###########################################################\n## Sometimes a notice dependency will reference an unadorned\n## module name that only appears in ALL_MODULES adorned with\n## an ARCH suffix or a `host_cross_` prefix.\n##\n## After all of the modules are processed in base_rules.mk,\n## replace all such dependencies with every matching adorned\n## module name.\n###########################################################\n\ndefine fix-notice-deps\n$(strip \\\n  $(eval _all_module_refs := \\\n    $(sort \\\n      $(foreach m,$(sort $(ALL_MODULES)), \\\n        $(call word-colon,1,$(ALL_MODULES.$(m).NOTICE_DEPS)) \\\n      ) \\\n    ) \\\n  ) \\\n  $(foreach m, $(_all_module_refs), \\\n    $(eval _lookup.$(m) := \\\n      $(sort \\\n        $(if $(strip $(ALL_MODULES.$(m).PATH)), \\\n          $(m), \\\n          $(filter $(m)_32 $(m)_64 host_cross_$(m) host_cross_$(m)_32 host_cross_$(m)_64, $(ALL_MODULES)) \\\n        ) \\\n      ) \\\n    ) \\\n  ) \\\n  $(foreach m, $(ALL_MODULES), \\\n    $(eval ALL_MODULES.$(m).NOTICE_DEPS := \\\n      $(sort \\\n         $(foreach d,$(sort $(ALL_MODULES.$(m).NOTICE_DEPS)), \\\n           $(foreach n,$(_lookup.$(call word-colon,1,$(d))),$(n):$(call wordlist-colon,2,9999,$(d))) \\\n        ) \\\n      ) \\\n    ) \\\n  ) \\\n)\nendef\n\n###########################################################\n## Target directory for license metadata files.\n###########################################################\ndefine license-metadata-dir\n$(call generated-sources-dir-for,META,lic,$(filter-out $(PRODUCT_OUT)%,$(1)))\nendef\n\nTARGETS_MISSING_LICENSE_METADATA:=\n\n###########################################################\n# License metadata targets corresponding to targets in $(1)\n###########################################################\ndefine corresponding-license-metadata\n$(strip $(filter-out 0p,$(foreach target, $(sort $(1)), \\\n  $(if $(strip $(ALL_MODULES.$(target).META_LIC)), \\\n    $(ALL_MODULES.$(target).META_LIC), \\\n    $(if $(strip $(ALL_TARGETS.$(target).META_LIC)), \\\n      $(ALL_TARGETS.$(target).META_LIC), \\\n      $(eval TARGETS_MISSING_LICENSE_METADATA += $(target)) \\\n    ) \\\n  ) \\\n)))\nendef\n\n###########################################################\n## Record a target $(1) copied from another target(s) $(2) that will need\n## license metadata.\n###########################################################\ndefine declare-copy-target-license-metadata\n$(strip $(if $(filter $(OUT_DIR)%,$(2)),\\\n  $(eval _tgt:=$(strip $(1)))\\\n  $(eval ALL_COPIED_TARGETS.$(_tgt).SOURCES := $(sort $(ALL_COPIED_TARGETS.$(_tgt).SOURCES) $(filter $(OUT_DIR)%,$(2))))\\\n  $(eval ALL_COPIED_TARGETS += $(_tgt))))\nendef\n\n###########################################################\n## License metadata build rule for my_register_name $(1)\n###########################################################\ndefine license-metadata-rule\n$(foreach meta_lic, $(ALL_MODULES.$(1).DELAYED_META_LIC),$(call _license-metadata-rule,$(1),$(meta_lic)))\nendef\n\n$(KATI_obsolete_var notice-rule, This function has been removed)\n\ndefine _license-metadata-rule\n$(strip $(eval _srcs := $(strip $(foreach d,$(ALL_MODULES.$(1).NOTICE_DEPS),$(if $(strip $(ALL_MODULES.$(call word-colon,1,$(d)).INSTALLED)), $(ALL_MODULES.$(call word-colon,1,$(d)).INSTALLED),$(if $(strip $(ALL_MODULES.$(call word-colon,1,$(d)).BUILT)), $(ALL_MODULES.$(call word-colon,1,$(d)).BUILT), $(call word-colon,1,$d)))))))\n$(strip $(eval _deps := $(sort $(filter-out $(2)%,\\\n   $(foreach d,$(ALL_MODULES.$(1).NOTICE_DEPS),\\\n     $(addsuffix :$(call wordlist-colon,2,9999,$(d)), \\\n       $(foreach dt,$(ALL_MODULES.$(d).BUILT) $(ALL_MODULES.$(d).INSTALLED),\\\n         $(ALL_TARGETS.$(dt).META_LIC))))))))\n$(strip $(eval _notices := $(sort $(ALL_MODULES.$(1).NOTICES))))\n$(strip $(eval _tgts := $(sort $(ALL_MODULES.$(1).BUILT))))\n$(strip $(eval _inst := $(sort $(ALL_MODULES.$(1).INSTALLED))))\n$(strip $(eval _path := $(sort $(ALL_MODULES.$(1).PATH))))\n$(strip $(eval _map := $(strip $(foreach _m,$(sort $(ALL_MODULES.$(1).LICENSE_INSTALL_MAP)), \\\n  $(eval _s := $(call word-colon,1,$(_m))) \\\n  $(eval _d := $(call word-colon,2,$(_m))) \\\n  $(eval _ns := $(if $(strip $(ALL_MODULES.$(_s).INSTALLED)),$(ALL_MODULES.$(_s).INSTALLED),$(if $(strip $(ALL_MODULES.$(_s).BUILT)),$(ALL_MODULES.$(_s).BUILT),$(_s)))) \\\n  $(foreach ns,$(_ns),$(ns):$(_d) ) \\\n))))\n\n$(2): PRIVATE_KINDS := $(sort $(ALL_MODULES.$(1).LICENSE_KINDS))\n$(2): PRIVATE_CONDITIONS := $(sort $(ALL_MODULES.$(1).LICENSE_CONDITIONS))\n$(2): PRIVATE_NOTICES := $(_notices)\n$(2): PRIVATE_NOTICE_DEPS := $(_deps)\n$(2): PRIVATE_SOURCES := $(_srcs)\n$(2): PRIVATE_TARGETS := $(_tgts)\n$(2): PRIVATE_INSTALLED := $(_inst)\n$(2): PRIVATE_PATH := $(_path)\n$(2): PRIVATE_IS_CONTAINER := $(ALL_MODULES.$(1).IS_CONTAINER)\n$(2): PRIVATE_PACKAGE_NAME := $(strip $(ALL_MODULES.$(1).LICENSE_PACKAGE_NAME))\n$(2): PRIVATE_INSTALL_MAP := $(_map)\n$(2): PRIVATE_MODULE_NAME := $(1)\n$(2): PRIVATE_MODULE_TYPE := $(ALL_MODULES.$(1).MODULE_TYPE)\n$(2): PRIVATE_MODULE_CLASS := $(ALL_MODULES.$(1).MODULE_CLASS)\n$(2): PRIVATE_INSTALL_MAP := $(_map)\n$(2): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,PACKAGING,notice)/$(2)/arguments\n$(2): $(BUILD_LICENSE_METADATA)\n$(2) : $(foreach d,$(_deps),$(call word-colon,1,$(d))) $(foreach n,$(_notices),$(call word-colon,1,$(n)) )\n\trm -f $$@\n\tmkdir -p $$(dir $$@)\n\tmkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE))\n\t$$(call dump-words-to-file,\\\n\t    $$(addprefix -mn ,$$(PRIVATE_MODULE_NAME))\\\n\t    $$(addprefix -mt ,$$(PRIVATE_MODULE_TYPE))\\\n\t    $$(addprefix -mc ,$$(PRIVATE_MODULE_CLASS))\\\n\t    $$(addprefix -k ,$$(PRIVATE_KINDS))\\\n\t    $$(addprefix -c ,$$(PRIVATE_CONDITIONS))\\\n\t    $$(addprefix -n ,$$(PRIVATE_NOTICES))\\\n\t    $$(addprefix -d ,$$(PRIVATE_NOTICE_DEPS))\\\n\t    $$(addprefix -s ,$$(PRIVATE_SOURCES))\\\n\t    $$(addprefix -m ,$$(PRIVATE_INSTALL_MAP))\\\n\t    $$(addprefix -t ,$$(PRIVATE_TARGETS))\\\n\t    $$(addprefix -i ,$$(PRIVATE_INSTALLED))\\\n\t    $$(addprefix -r ,$$(PRIVATE_PATH)),\\\n\t    $$(PRIVATE_ARGUMENT_FILE))\n\tOUT_DIR=$(OUT_DIR) $(BUILD_LICENSE_METADATA) \\\n\t  $$(if $$(PRIVATE_IS_CONTAINER),-is_container) \\\n\t  -p '$$(PRIVATE_PACKAGE_NAME)' \\\n\t  @$$(PRIVATE_ARGUMENT_FILE) \\\n\t  -o $$@\nendef\n\n\n###########################################################\n## License metadata build rule for non-module target $(1)\n###########################################################\ndefine non-module-license-metadata-rule\n$(strip $(eval _dir := $(call license-metadata-dir,$(1))))\n$(strip $(eval _tgt := $(strip $(1))))\n$(strip $(eval _meta := $(call append-path,$(_dir),$(patsubst $(OUT_DIR)%,out%,$(_tgt).meta_lic))))\n$(strip $(eval _deps := $(sort $(filter-out 0p: :,$(foreach d,$(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES)),$(ALL_TARGETS.$(call word-colon,1,$(d)).META_LIC):$(call wordlist-colon,2,9999,$(d)))))))\n$(strip $(eval _notices := $(sort $(ALL_NON_MODULES.$(_tgt).NOTICES))))\n$(strip $(eval _path := $(sort $(ALL_NON_MODULES.$(_tgt).PATH))))\n$(strip $(eval _install_map := $(ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS)))\n\n$(_meta): PRIVATE_KINDS := $(sort $(ALL_NON_MODULES.$(_tgt).LICENSE_KINDS))\n$(_meta): PRIVATE_CONDITIONS := $(sort $(ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS))\n$(_meta): PRIVATE_NOTICES := $(_notices)\n$(_meta): PRIVATE_NOTICE_DEPS := $(_deps)\n$(_meta): PRIVATE_SOURCES := $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES)\n$(_meta): PRIVATE_TARGETS := $(_tgt)\n$(_meta): PRIVATE_PATH := $(_path)\n$(_meta): PRIVATE_IS_CONTAINER := $(ALL_NON_MODULES.$(_tgt).IS_CONTAINER)\n$(_meta): PRIVATE_PACKAGE_NAME := $(strip $(ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME))\n$(_meta): PRIVATE_INSTALL_MAP := $(strip $(_install_map))\n$(_meta): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,PACKAGING,notice)/$(_meta)/arguments\n$(_meta): $(BUILD_LICENSE_METADATA)\n$(_meta) : $(foreach d,$(_deps),$(call word-colon,1,$(d))) $(foreach n,$(_notices),$(call word-colon,1,$(n)) )\n\trm -f $$@\n\tmkdir -p $$(dir $$@)\n\tmkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE))\n\t$$(call dump-words-to-file,\\\n\t    $$(addprefix -k ,$$(PRIVATE_KINDS))\\\n\t    $$(addprefix -c ,$$(PRIVATE_CONDITIONS))\\\n\t    $$(addprefix -n ,$$(PRIVATE_NOTICES))\\\n\t    $$(addprefix -d ,$$(PRIVATE_NOTICE_DEPS))\\\n\t    $$(addprefix -s ,$$(PRIVATE_SOURCES))\\\n\t    $$(addprefix -m ,$$(PRIVATE_INSTALL_MAP))\\\n\t    $$(addprefix -t ,$$(PRIVATE_TARGETS))\\\n\t    $$(addprefix -r ,$$(PRIVATE_PATH)),\\\n\t    $$(PRIVATE_ARGUMENT_FILE))\n\tOUT_DIR=$(OUT_DIR) $(BUILD_LICENSE_METADATA) \\\n          -mt raw -mc unknown \\\n\t  $$(if $$(PRIVATE_IS_CONTAINER),-is_container) \\\n\t  $$(addprefix -r ,$$(PRIVATE_PATH)) \\\n\t  @$$(PRIVATE_ARGUMENT_FILE) \\\n\t  -o $$@\n\nendef\n\n###########################################################\n## Record missing dependencies for non-module target $(1)\n###########################################################\ndefine record-missing-non-module-dependencies\n$(strip $(eval _tgt := $(strip $(1))))\n$(strip $(foreach d,$(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES)), \\\n  $(if $(strip $(ALL_TARGETS.$(d).META_LIC)), \\\n    , \\\n    $(eval NON_MODULES_WITHOUT_LICENSE_METADATA += $(d))) \\\n))\nendef\n\n###########################################################\n## License metadata build rule for copied target $(1)\n###########################################################\ndefine copied-target-license-metadata-rule\n$(if $(strip $(ALL_TARGETS.$(1).META_LIC)),,$(call _copied-target-license-metadata-rule,$(1)))\nendef\n\ndefine _copied-target-license-metadata-rule\n$(strip $(eval _dir := $(call license-metadata-dir,$(1))))\n$(strip $(eval _meta := $(call append-path,$(_dir),$(patsubst $(OUT_DIR)%,out%,$(1).meta_lic))))\n$(strip $(eval ALL_TARGETS.$(1).META_LIC:=$(_meta)))\n$(strip $(eval _dep:=))\n$(strip $(foreach s,$(ALL_COPIED_TARGETS.$(1).SOURCES),\\\n  $(eval _dmeta:=$(ALL_TARGETS.$(s).META_LIC))\\\n  $(if $(filter-out 0p,$(_dep)),\\\n      $(if $(filter-out $(_dep),$(_dmeta)),$(error cannot copy target from multiple modules: $(1) from $(_dep) and $(_dmeta))),\\\n      $(eval _dep:=$(_dmeta)))))\n$(if $(filter 0p,$(_dep)),$(eval ALL_TARGETS.$(1).META_LIC:=0p))\n$(strip $(if $(strip $(_dep)),,$(error cannot copy target from unknown module: $(1) from $(ALL_COPIED_TARGETS.$(1).SOURCES))))\n\nifneq (0p,$(ALL_TARGETS.$(1).META_LIC))\n$(_meta): PRIVATE_DEST_TARGET := $(1)\n$(_meta): PRIVATE_SOURCE_TARGETS := $(ALL_COPIED_TARGETS.$(1).SOURCES)\n$(_meta): PRIVATE_SOURCE_METADATA := $(_dep)\n$(_meta): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,PACKAGING,copynotice)/$(_meta)/arguments\n$(_meta) : $(_dep) $(COPY_LICENSE_METADATA)\n\trm -f $$@\n\tmkdir -p $$(dir $$@)\n\tmkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE))\n\t$$(call dump-words-to-file,\\\n\t    $$(addprefix -i ,$$(PRIVATE_DEST_TARGET))\\\n\t    $$(addprefix -s ,$$(PRIVATE_SOURCE_TARGETS))\\\n\t    $$(addprefix -d ,$$(PRIVATE_SOURCE_METADATA)),\\\n\t    $$(PRIVATE_ARGUMENT_FILE))\n\tOUT_DIR=$(OUT_DIR) $(COPY_LICENSE_METADATA) \\\n\t  @$$(PRIVATE_ARGUMENT_FILE) \\\n\t  -o $$@\n\nendif\n\n$(eval _dep:=)\n$(eval _dmeta:=)\n$(eval _meta:=)\n$(eval _dir:=)\nendef\n\n###########################################################\n## Declare the license metadata for non-module target $(1).\n##\n## $(2) -- license kinds e.g. SPDX-license-identifier-Apache-2.0\n## $(3) -- license conditions e.g. notice by_exception_only\n## $(4) -- license text filenames (notices)\n## $(5) -- package name\n## $(6) -- project path\n###########################################################\ndefine declare-license-metadata\n$(strip \\\n  $(eval _tgt := $(subst //,/,$(strip $(1)))) \\\n  $(eval ALL_NON_MODULES += $(_tgt)) \\\n  $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir,$(1))/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \\\n  $(eval ALL_NON_MODULES.$(_tgt).LICENSE_KINDS := $(strip $(2))) \\\n  $(eval ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS := $(strip $(3))) \\\n  $(eval ALL_NON_MODULES.$(_tgt).NOTICES := $(strip $(4))) \\\n  $(eval ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME := $(strip $(5))) \\\n  $(eval ALL_NON_MODULES.$(_tgt).PATH := $(strip $(6))) \\\n)\nendef\n\n###########################################################\n## Declare that non-module targets copied from project $(1) and\n## optionally ending in $(2) have the following license\n## metadata:\n##\n## $(3) -- license kinds e.g. SPDX-license-identifier-Apache-2.0\n## $(4) -- license conditions e.g. notice by_exception_only\n## $(5) -- license text filenames (notices)\n## $(6) -- package name\n###########################################################\ndefine declare-copy-files-license-metadata\n$(strip \\\n  $(foreach _pair,$(filter $(1)%$(2),$(PRODUCT_COPY_FILES)),$(eval $(call declare-license-metadata,$(PRODUCT_OUT)/$(call word-colon,2,$(_pair)),$(3),$(4),$(5),$(6),$(1)))) \\\n)\nendef\n\n###########################################################\n## Declare the license metadata for non-module container-type target $(1).\n##\n## Container-type targets are targets like .zip files that\n## merely aggregate other files.\n##\n## $(2) -- license kinds e.g. SPDX-license-identifier-Apache-2.0\n## $(3) -- license conditions e.g. notice by_exception_only\n## $(4) -- license text filenames (notices)\n## $(5) -- package name\n## $(6) -- project path\n###########################################################\ndefine declare-container-license-metadata\n$(strip \\\n  $(eval _tgt := $(subst //,/,$(strip $(1)))) \\\n  $(eval ALL_NON_MODULES += $(_tgt)) \\\n  $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir,$(1))/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \\\n  $(eval ALL_NON_MODULES.$(_tgt).LICENSE_KINDS := $(strip $(2))) \\\n  $(eval ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS := $(strip $(3))) \\\n  $(eval ALL_NON_MODULES.$(_tgt).NOTICES := $(strip $(4))) \\\n  $(eval ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME := $(strip $(5))) \\\n  $(eval ALL_NON_MODULES.$(_tgt).PATH := $(strip $(6))) \\\n  $(eval ALL_NON_MODULES.$(_tgt).IS_CONTAINER := true) \\\n)\nendef\n\n###########################################################\n## Declare that non-module target $(1) is a non-copyrightable file.\n##\n## e.g. an information-only file merely listing other files.\n###########################################################\ndefine declare-0p-target\n$(strip \\\n  $(eval _tgt := $(subst //,/,$(strip $(1)))) \\\n  $(eval ALL_0P_TARGETS += $(_tgt)) \\\n)\nendef\n\n###########################################################\n## Declare non-module target $(1) to have a first-party license\n## (Android Apache 2.0)\n##\n## $(2) -- project path\n###########################################################\ndefine declare-1p-target\n$(call declare-license-metadata,$(1),SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,Android,$(2))\nendef\n\n###########################################################\n## Declare that non-module targets copied from project $(1) and\n## optionally ending in $(2) are first-party licensed\n## (Android Apache 2.0)\n###########################################################\ndefine declare-1p-copy-files\n$(foreach _pair,$(filter $(1)%$(2),$(PRODUCT_COPY_FILES)),$(call declare-1p-target,$(PRODUCT_OUT)/$(call word-colon,2,$(_pair)),$(1)))\nendef\n\n###########################################################\n## Declare non-module container-type target $(1) to have a\n## first-party license (Android Apache 2.0).\n##\n## Container-type targets are targets like .zip files that\n## merely aggregate other files.\n##\n## $92) -- project path\n###########################################################\ndefine declare-1p-container\n$(call declare-container-license-metadata,$(1),SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,Android,$(2))\nendef\n\n###########################################################\n## Declare license dependencies $(2) with optional colon-separated\n## annotations for non-module target $(1)\n###########################################################\ndefine declare-license-deps\n$(strip \\\n  $(eval _tgt := $(subst //,/,$(strip $(1)))) \\\n  $(eval ALL_NON_MODULES += $(_tgt)) \\\n  $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir,$(1))/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \\\n  $(eval ALL_NON_MODULES.$(_tgt).DEPENDENCIES := $(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(2))) \\\n)\nendef\n\n###########################################################\n## Declare license dependencies $(2) with optional colon-separated\n## annotations for non-module container-type target $(1)\n##\n## Container-type targets are targets like .zip files that\n## merely aggregate other files.\n##\n## $(3) -- root mappings space-separated source:target\n###########################################################\ndefine declare-container-license-deps\n$(strip \\\n  $(eval _tgt := $(subst //,/,$(strip $(1)))) \\\n  $(eval ALL_NON_MODULES += $(_tgt)) \\\n  $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir,$(1))/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \\\n  $(eval ALL_NON_MODULES.$(_tgt).DEPENDENCIES := $(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(2))) \\\n  $(eval ALL_NON_MODULES.$(_tgt).IS_CONTAINER := true) \\\n  $(eval ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS := $(strip $(ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS) $(3))) \\\n)\nendef\n\n###########################################################\n## Declares the rule to report targets with no license metadata.\n###########################################################\ndefine report-missing-licenses-rule\n.PHONY: reportmissinglicenses\nreportmissinglicenses: PRIVATE_NON_MODULES:=$(sort $(NON_MODULES_WITHOUT_LICENSE_METADATA) $(TARGETS_MISSING_LICENSE_METADATA))\nreportmissinglicenses: PRIVATE_COPIED_FILES:=$(sort $(filter $(NON_MODULES_WITHOUT_LICENSE_METADATA) $(TARGETS_MISSING_LICENSE_METADATA),\\\n  $(foreach _pair,$(PRODUCT_COPY_FILES), $(PRODUCT_OUT)/$(call word-colon,2,$(_pair)))))\nreportmissinglicenses:\n\t@echo Reporting $$(words $$(PRIVATE_NON_MODULES)) targets without license metadata\n\t$$(foreach t,$$(PRIVATE_NON_MODULES),if ! [ -h $$(t) ]; then echo No license metadata for $$(t) >&2; fi;)\n\t$$(foreach t,$$(PRIVATE_COPIED_FILES),if ! [ -h $$(t) ]; then echo No license metadata for copied file $$(t) >&2; fi;)\n\techo $$(words $$(PRIVATE_NON_MODULES)) targets missing license metadata >&2\n\nendef\n\n\n###########################################################\n# Returns the unique list of built license metadata files.\n###########################################################\ndefine all-license-metadata\n$(sort \\\n  $(foreach t,$(ALL_NON_MODULES),$(if $(filter 0p,$(ALL_TARGETS.$(t).META_LIC)),, $(ALL_TARGETS.$(t).META_LIC))) \\\n  $(foreach m,$(ALL_MODULES), $(ALL_MODULES.$(m).META_LIC)) \\\n)\nendef\n\n###########################################################\n# Declares the rule to report all library names used in any notice files.\n###########################################################\ndefine report-all-notice-library-names-rule\n$(strip $(eval _all := $(call all-license-metadata)))\n\n.PHONY: reportallnoticelibrarynames\nreportallnoticelibrarynames: PRIVATE_LIST_FILE := $(call license-metadata-dir,COMMON)/filelist\nreportallnoticelibrarynames: | $(COMPLIANCENOTICE_SHIPPEDLIBS)\nreportallnoticelibrarynames: $(_all)\n\t@echo Reporting notice library names for at least $$(words $(_all)) license metadata files\n\t$(hide) rm -f $$(PRIVATE_LIST_FILE)\n\t$(hide) mkdir -p $$(dir $$(PRIVATE_LIST_FILE))\n\t$(hide) find out -name '*meta_lic' -type f -printf '\"%p\"\\n' >$$(PRIVATE_LIST_FILE)\n\tOUT_DIR=$(OUT_DIR) $(COMPLIANCENOTICE_SHIPPEDLIBS) @$$(PRIVATE_LIST_FILE)\nendef\n\n###########################################################\n# Declares the rule to build all license metadata.\n###########################################################\ndefine build-all-license-metadata-rule\n$(strip $(eval _all := $(call all-license-metadata)))\n\n.PHONY: alllicensemetadata\nalllicensemetadata: $(_all)\n\t@echo Building all $(words $(_all)) license metadata files\nendef\n\n\n###########################################################\n## Declares a license metadata build rule for ALL_MODULES\n###########################################################\ndefine build-license-metadata\n$(strip \\\n  $(foreach t,$(sort $(ALL_0P_TARGETS)), \\\n    $(eval ALL_TARGETS.$(t).META_LIC := 0p) \\\n  ) \\\n  $(foreach t,$(sort $(ALL_COPIED_TARGETS)),$(eval $(call copied-target-license-metadata-rule,$(t)))) \\\n  $(foreach t,$(sort $(ALL_NON_MODULES)),$(eval $(call non-module-license-metadata-rule,$(t)))) \\\n  $(foreach m,$(sort $(ALL_MODULES)),$(eval $(call license-metadata-rule,$(m)))) \\\n  $(eval $(call build-all-license-metadata-rule)))\nendef\n\n###########################################################\n## Returns correct _idfPrefix from the list:\n##   { HOST, HOST_CROSS, TARGET }\n###########################################################\n# the following rules checked in order:\n# ($1 is in {HOST_CROSS} => $1;\n# ($1 is empty) => TARGET;\n# ($2 is not empty) => HOST_CROSS;\n# => HOST;\ndefine find-idf-prefix\n$(strip \\\n    $(eval _idf_pfx_:=$(strip $(filter HOST_CROSS,$(1)))) \\\n    $(eval _idf_pfx_:=$(if $(strip $(1)),$(if $(_idf_pfx_),$(_idf_pfx_),$(if $(strip $(2)),HOST_CROSS,HOST)),TARGET)) \\\n    $(_idf_pfx_)\n)\nendef\n\n###########################################################\n## The intermediates directory.  Where object files go for\n## a given target.  We could technically get away without\n## the \"_intermediates\" suffix on the directory, but it's\n## nice to be able to grep for that string to find out if\n## anyone's abusing the system.\n###########################################################\n\n# $(1): target class, like \"APPS\"\n# $(2): target name, like \"NotePad\"\n# $(3): { HOST, HOST_CROSS, <empty (TARGET)>, <other non-empty (HOST)> }\n# $(4): if non-empty, force the intermediates to be COMMON\n# $(5): if non-empty, force the intermediates to be for the 2nd arch\n# $(6): if non-empty, force the intermediates to be for the host cross os\ndefine intermediates-dir-for\n$(strip \\\n    $(eval _idfClass := $(strip $(1))) \\\n    $(if $(_idfClass),, \\\n        $(error $(LOCAL_PATH): Class not defined in call to intermediates-dir-for)) \\\n    $(eval _idfName := $(strip $(2))) \\\n    $(if $(_idfName),, \\\n        $(error $(LOCAL_PATH): Name not defined in call to intermediates-dir-for)) \\\n    $(eval _idfPrefix := $(call find-idf-prefix,$(3),$(6))) \\\n    $(eval _idf2ndArchPrefix := $(if $(strip $(5)),$(TARGET_2ND_ARCH_VAR_PREFIX))) \\\n    $(if $(filter $(_idfPrefix)_$(_idfClass),$(COMMON_MODULE_CLASSES))$(4), \\\n        $(eval _idfIntBase := $($(_idfPrefix)_OUT_COMMON_INTERMEDIATES)) \\\n      ,$(if $(filter $(_idfClass),$(PER_ARCH_MODULE_CLASSES)),\\\n          $(eval _idfIntBase := $($(_idf2ndArchPrefix)$(_idfPrefix)_OUT_INTERMEDIATES)) \\\n       ,$(eval _idfIntBase := $($(_idfPrefix)_OUT_INTERMEDIATES)) \\\n       ) \\\n     ) \\\n    $(_idfIntBase)/$(_idfClass)/$(_idfName)_intermediates \\\n)\nendef\n\n# Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE\n# to determine the intermediates directory.\n#\n# $(1): if non-empty, force the intermediates to be COMMON\n# $(2): if non-empty, force the intermediates to be for the 2nd arch\n# $(3): if non-empty, force the intermediates to be for the host cross os\ndefine local-intermediates-dir\n$(strip \\\n    $(if $(strip $(LOCAL_MODULE_CLASS)),, \\\n        $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-intermediates-dir)) \\\n    $(if $(strip $(LOCAL_MODULE)),, \\\n        $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-intermediates-dir)) \\\n    $(call intermediates-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST),$(1),$(2),$(3)) \\\n)\nendef\n\n# Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE\n# to determine the intermediates directory.\n#\n# $(1): if non-empty, force the intermediates to be COMMON\n# $(2): if non-empty, force the intermediates to be for the 2nd arch\n# $(3): if non-empty, force the intermediates to be for the host cross os\ndefine local-meta-intermediates-dir\n$(strip \\\n    $(if $(strip $(LOCAL_MODULE_CLASS)),, \\\n        $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-meta-intermediates-dir)) \\\n    $(if $(strip $(LOCAL_MODULE)),, \\\n        $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-meta-intermediates-dir)) \\\n    $(call intermediates-dir-for,META$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST),$(1),$(2),$(3)) \\\n)\nendef\n\n###########################################################\n## The generated sources directory.  Placing generated\n## source files directly in the intermediates directory\n## causes problems for multiarch builds, where there are\n## two intermediates directories for a single target. Put\n## them in a separate directory, and they will be copied to\n## each intermediates directory automatically.\n###########################################################\n\n# $(1): target class, like \"APPS\"\n# $(2): target name, like \"NotePad\"\n# $(3): { HOST, HOST_CROSS, <empty (TARGET)>, <other non-empty (HOST)> }\n# $(4): if non-empty, force the generated sources to be COMMON\ndefine generated-sources-dir-for\n$(strip \\\n    $(eval _idfClass := $(strip $(1))) \\\n    $(if $(_idfClass),, \\\n        $(error $(LOCAL_PATH): Class not defined in call to generated-sources-dir-for)) \\\n    $(eval _idfName := $(strip $(2))) \\\n    $(if $(_idfName),, \\\n        $(error $(LOCAL_PATH): Name not defined in call to generated-sources-dir-for)) \\\n    $(eval _idfPrefix := $(call find-idf-prefix,$(3),)) \\\n    $(if $(filter $(_idfPrefix)_$(_idfClass),$(COMMON_MODULE_CLASSES))$(4), \\\n        $(eval _idfIntBase := $($(_idfPrefix)_OUT_COMMON_GEN)) \\\n      , \\\n        $(eval _idfIntBase := $($(_idfPrefix)_OUT_GEN)) \\\n     ) \\\n    $(_idfIntBase)/$(_idfClass)/$(_idfName)_intermediates \\\n)\nendef\n\n# Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE\n# to determine the generated sources directory.\n#\n# $(1): if non-empty, force the intermediates to be COMMON\ndefine local-generated-sources-dir\n$(strip \\\n    $(if $(strip $(LOCAL_MODULE_CLASS)),, \\\n        $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-generated-sources-dir)) \\\n    $(if $(strip $(LOCAL_MODULE)),, \\\n        $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-generated-sources-dir)) \\\n    $(call generated-sources-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST),$(1)) \\\n)\nendef\n\n###########################################################\n## The packaging directory for a module.  Similar to intermedates, but\n## in a location that will be wiped by an m installclean.\n###########################################################\n\n# $(1): subdir in PACKAGING\n# $(2): target class, like \"APPS\"\n# $(3): target name, like \"NotePad\"\n# $(4): { HOST, HOST_CROSS, <empty (TARGET)>, <other non-empty (HOST)> }\ndefine packaging-dir-for\n$(strip \\\n    $(eval _pdfClass := $(strip $(2))) \\\n    $(if $(_pdfClass),, \\\n        $(error $(LOCAL_PATH): Class not defined in call to generated-sources-dir-for)) \\\n    $(eval _pdfName := $(strip $(3))) \\\n    $(if $(_pdfName),, \\\n        $(error $(LOCAL_PATH): Name not defined in call to generated-sources-dir-for)) \\\n    $(call intermediates-dir-for,PACKAGING,$(1),$(4))/$(_pdfClass)/$(_pdfName)_intermediates \\\n)\nendef\n\n# Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE\n# to determine the packaging directory.\n#\n# $(1): subdir in PACKAGING\ndefine local-packaging-dir\n$(strip \\\n    $(if $(strip $(LOCAL_MODULE_CLASS)),, \\\n        $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-generated-sources-dir)) \\\n    $(if $(strip $(LOCAL_MODULE)),, \\\n        $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-generated-sources-dir)) \\\n    $(call packaging-dir-for,$(1),$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST)) \\\n)\nendef\n\n\n###########################################################\n## Convert a list of short module names (e.g., \"framework\", \"Browser\")\n## into the list of files that are built for those modules.\n## NOTE: this won't return reliable results until after all\n## sub-makefiles have been included.\n## $(1): target list\n###########################################################\n\ndefine module-built-files\n$(foreach module,$(1),$(ALL_MODULES.$(module).BUILT))\nendef\n\n###########################################################\n## Convert a list of short modules names (e.g., \"framework\", \"Browser\")\n## into the list of files that are installed for those modules.\n## NOTE: this won't return reliable results until after all\n## sub-makefiles have been included.\n## $(1): target list\n###########################################################\n\ndefine module-installed-files\n$(foreach module,$(1),$(ALL_MODULES.$(module).INSTALLED))\nendef\n\n###########################################################\n## Convert a list of short modules names (e.g., \"framework\", \"Browser\")\n## into the list of files that are built *for the target* for those modules.\n## NOTE: this won't return reliable results until after all\n## sub-makefiles have been included.\n## $(1): target list\n###########################################################\n\ndefine module-target-built-files\n$(foreach module,$(1),$(ALL_MODULES.$(module).TARGET_BUILT))\nendef\n\n###########################################################\n## Convert a list of short modules names (e.g., \"framework\", \"Browser\")\n## into the list of files that should be used when linking\n## against that module as a public API.\n## TODO: Allow this for more than JAVA_LIBRARIES modules\n## NOTE: this won't return reliable results until after all\n## sub-makefiles have been included.\n## $(1): target list\n###########################################################\n\ndefine module-stubs-files\n$(foreach module,$(1),$(if $(filter $(module),$(JAVA_SDK_LIBRARIES)),\\\n$(call java-lib-files,$(module).stubs),$(ALL_MODULES.$(module).STUBS)))\nendef\n\n###########################################################\n## Evaluates to the timestamp file for a doc module, which\n## is the dependency that should be used.\n## $(1): doc module\n###########################################################\n\ndefine doc-timestamp-for\n$(OUT_DOCS)/$(strip $(1))-timestamp\nendef\n\n\n###########################################################\n## Convert \"core ext framework\" to \"out/.../javalib.jar ...\"\n## $(1): library list\n## $(2): Non-empty if IS_HOST_MODULE\n###########################################################\n\n# Get the jar files (you can pass to \"javac -classpath\") of static or shared\n# Java libraries that you want to link against.\n# $(1): library name list\n# $(2): Non-empty if IS_HOST_MODULE\ndefine java-lib-files\n$(foreach lib,$(1),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),$(2),COMMON)/classes.jar)\nendef\n\n# Get the header jar files (you can pass to \"javac -classpath\") of static or shared\n# Java libraries that you want to link against.\n# $(1): library name list\n# $(2): Non-empty if IS_HOST_MODULE\nifneq ($(TURBINE_ENABLED),false)\ndefine java-lib-header-files\n$(foreach lib,$(1),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),$(2),COMMON)/classes-header.jar)\nendef\nelse\ndefine java-lib-header-files\n$(call java-lib-files,$(1),$(2))\nendef\nendif\n\n# Get the dependency files (you can put on the right side of \"|\" of a build rule)\n# of the Java libraries.\n# $(1): library name list\n# $(2): Non-empty if IS_HOST_MODULE\n# Historically for target Java libraries we used a different file (javalib.jar)\n# as the dependency.\n# Now we can use classes.jar as dependency, so java-lib-deps is the same\n# as java-lib-files.\ndefine java-lib-deps\n$(call java-lib-files,$(1),$(2))\nendef\n\n# Get the jar files (you can pass to \"javac -classpath\") of static or shared\n# APK libraries that you want to link against.\n# $(1): library name list\ndefine app-lib-files\n$(foreach lib,$(1),$(call intermediates-dir-for,APPS,$(lib),,COMMON)/classes.jar)\nendef\n\n# Get the header jar files (you can pass to \"javac -classpath\") of static or shared\n# APK libraries that you want to link against.\n# $(1): library name list\nifneq ($(TURBINE_ENABLED),false)\ndefine app-lib-header-files\n$(foreach lib,$(1),$(call intermediates-dir-for,APPS,$(lib),,COMMON)/classes-header.jar)\nendef\nelse\ndefine app-lib-header-files\n$(call app-lib-files,$(1))\nendef\nendif\n\n# Get the exported-sdk-libs files which collectively give you the list of exported java sdk\n# lib names that are (transitively) exported from the given set of java libs\n# $(1): library name list\ndefine exported-sdk-libs-files\n$(foreach lib,$(1),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/exported-sdk-libs)\nendef\n\n###########################################################\n## Append a leaf to a base path.  Properly deals with\n## base paths ending in /.\n##\n## $(1): base path\n## $(2): leaf path\n###########################################################\n\ndefine append-path\n$(subst //,/,$(1)/$(2))\nendef\n\n\n###########################################################\n## Color-coded warnings and errors\n## Use echo-(warning|error) in a build rule\n## Use pretty-(warning|error) instead of $(warning)/$(error)\n###########################################################\nESC_BOLD := \\033[1m\nESC_WARNING := \\033[35m\nESC_ERROR := \\033[31m\nESC_RESET := \\033[0m\n\n# $(1): path (and optionally line) information\n# $(2): message to print\ndefine echo-warning\necho -e \"$(ESC_BOLD)$(1): $(ESC_WARNING)warning:$(ESC_RESET)$(ESC_BOLD)\" '$(subst ','\\'',$(2))'  \"$(ESC_RESET)\" >&2\nendef\n\n# $(1): path (and optionally line) information\n# $(2): message to print\ndefine echo-error\necho -e \"$(ESC_BOLD)$(1): $(ESC_ERROR)error:$(ESC_RESET)$(ESC_BOLD)\" '$(subst ','\\'',$(2))'  \"$(ESC_RESET)\" >&2\nendef\n\n###########################################################\n## Legacy showcommands compatibility\n###########################################################\n\ndefine pretty\n@echo $1\nendef\n\n###########################################################\n## Commands for including the dependency files the compiler generates\n###########################################################\n# $(1): the .P file\n# $(2): the main build target\ndefine include-depfile\n$(eval $(2) : .KATI_DEPFILE := $1)\nendef\n\n# $(1): object files\ndefine include-depfiles-for-objs\n$(foreach obj, $(1), $(call include-depfile, $(obj:%.o=%.d), $(obj)))\nendef\n\n###########################################################\n## Track source files compiled to objects\n###########################################################\n# $(1): list of sources\n# $(2): list of matching objects\ndefine track-src-file-obj\n$(eval $(call _track-src-file-obj,$(1)))\nendef\ndefine _track-src-file-obj\ni := w\n$(foreach s,$(1),\nmy_tracked_src_files += $(s)\nmy_src_file_obj_$(s) := $$(word $$(words $$(i)),$$(2))\ni += w)\nendef\n\n# $(1): list of sources\n# $(2): list of matching generated sources\ndefine track-src-file-gen\n$(eval $(call _track-src-file-gen,$(2)))\nendef\ndefine _track-src-file-gen\ni := w\n$(foreach s,$(1),\nmy_tracked_gen_files += $(s)\nmy_src_file_gen_$(s) := $$(word $$(words $$(i)),$$(1))\ni += w)\nendef\n\n# $(1): list of generated sources\n# $(2): list of matching objects\ndefine track-gen-file-obj\n$(call track-src-file-obj,$(foreach f,$(1),\\\n  $(or $(my_src_file_gen_$(f)),$(f))),$(2))\nendef\n\n###########################################################\n## Commands for running lex\n###########################################################\n\ndefine transform-l-to-c-or-cpp\n@echo \"Lex: $(PRIVATE_MODULE) <= $<\"\n@mkdir -p $(dir $@)\nM4=$(M4) $(LEX) -o$@ $<\nendef\n\n###########################################################\n## Commands for running yacc\n##\n###########################################################\n\ndefine transform-y-to-c-or-cpp\n@echo \"Yacc: $(PRIVATE_MODULE) <= $<\"\n@mkdir -p $(dir $@)\nM4=$(M4) $(YACC) $(PRIVATE_YACCFLAGS) \\\n  --defines=$(basename $@).h \\\n  -o $@ $<\nendef\n\n###########################################################\n## Commands to compile RenderScript to Java\n###########################################################\n\n## Merge multiple .d files generated by llvm-rs-cc. This is necessary\n## because ninja can handle only a single depfile per build target.\n## .d files generated by llvm-rs-cc define .stamp, .bc, and optionally\n## .java as build targets. However, there's no way to let ninja know\n## dependencies to .bc files and .java files, so we give up build\n## targets for them. As we write the .stamp file as the target by\n## ourselves, the awk script removes the first lines before the colon\n## and append a backslash to the last line to concatenate contents of\n## multiple files.\n# $(1): .d files to be merged\n# $(2): merged .d file\ndefine _merge-renderscript-d\n$(hide) echo '$@: $(backslash)' > $2\n$(foreach d,$1, \\\n  $(hide) awk 'start { sub(/( \\\\)?$$/, \" \\\\\"); print } /:/ { start=1 }' < $d >> $2$(newline))\n$(hide) echo >> $2\nendef\n\n# b/37755219\nRS_CC_ASAN_OPTIONS := ASAN_OPTIONS=detect_leaks=0:detect_container_overflow=0\n\ndefine transform-renderscripts-to-java-and-bc\n@echo \"RenderScript: $(PRIVATE_MODULE) <= $(PRIVATE_RS_SOURCE_FILES)\"\n$(hide) rm -rf $(PRIVATE_RS_OUTPUT_DIR)\n$(hide) mkdir -p $(PRIVATE_RS_OUTPUT_DIR)/res/raw\n$(hide) mkdir -p $(PRIVATE_RS_OUTPUT_DIR)/src\n$(hide) $(RS_CC_ASAN_OPTIONS) $(PRIVATE_RS_CC) \\\n  -o $(PRIVATE_RS_OUTPUT_DIR)/res/raw \\\n  -p $(PRIVATE_RS_OUTPUT_DIR)/src \\\n  -d $(PRIVATE_RS_OUTPUT_DIR) \\\n  -a $@ -MD \\\n  $(addprefix -target-api , $(PRIVATE_RS_TARGET_API)) \\\n  $(PRIVATE_RS_FLAGS) \\\n  $(foreach inc,$(PRIVATE_RS_INCLUDES),$(addprefix -I , $(inc))) \\\n  $(PRIVATE_RS_SOURCE_FILES)\n$(SOONG_ZIP) -o $@ -C $(PRIVATE_RS_OUTPUT_DIR)/src -D $(PRIVATE_RS_OUTPUT_DIR)/src\n$(SOONG_ZIP) -o $(PRIVATE_RS_OUTPUT_RES_ZIP) -C $(PRIVATE_RS_OUTPUT_DIR)/res -D $(PRIVATE_RS_OUTPUT_DIR)/res\n$(call _merge-renderscript-d,$(PRIVATE_DEP_FILES),$@.d)\nendef\n\ndefine transform-bc-to-so\n@echo \"Renderscript compatibility: $(notdir $@) <= $(notdir $<)\"\n$(hide) mkdir -p $(dir $@)\n$(hide) $(BCC_COMPAT) -O3 -o $(dir $@)/$(notdir $(<:.bc=.o)) -fPIC -shared \\\n  -rt-path $(RS_PREBUILT_CLCORE) -mtriple $(RS_COMPAT_TRIPLE) $<\n$(hide) $(PRIVATE_CXX_LINK) -fuse-ld=lld -target $(CLANG_TARGET_TRIPLE) -shared -Wl,-soname,$(notdir $@) -nostdlib \\\n  -Wl,-rpath,\\$$ORIGIN/../lib \\\n  $(dir $@)/$(notdir $(<:.bc=.o)) \\\n  $(RS_PREBUILT_COMPILER_RT) \\\n  -o $@ $(CLANG_TARGET_GLOBAL_LLDFLAGS) -Wl,--hash-style=sysv \\\n  -L $(SOONG_OUT_DIR)/ndk/platforms/android-$(PRIVATE_SDK_VERSION)/arch-$(TARGET_ARCH)/usr/lib64 \\\n  -L $(SOONG_OUT_DIR)/ndk/platforms/android-$(PRIVATE_SDK_VERSION)/arch-$(TARGET_ARCH)/usr/lib \\\n  $(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupport)/libRSSupport.so \\\n  -lm -lc\nendef\n\n###########################################################\n## Commands to compile RenderScript to C++\n###########################################################\n\ndefine transform-renderscripts-to-cpp-and-bc\n@echo \"RenderScript: $(PRIVATE_MODULE) <= $(PRIVATE_RS_SOURCE_FILES)\"\n$(hide) rm -rf $(PRIVATE_RS_OUTPUT_DIR)\n$(hide) mkdir -p $(PRIVATE_RS_OUTPUT_DIR)/\n$(hide) $(RS_CC_ASAN_OPTIONS) $(PRIVATE_RS_CC) \\\n  -o $(PRIVATE_RS_OUTPUT_DIR)/ \\\n  -d $(PRIVATE_RS_OUTPUT_DIR) \\\n  -a $@ -MD \\\n  -reflect-c++ \\\n  $(addprefix -target-api , $(PRIVATE_RS_TARGET_API)) \\\n  $(PRIVATE_RS_FLAGS) \\\n  $(addprefix -I , $(PRIVATE_RS_INCLUDES)) \\\n  $(PRIVATE_RS_SOURCE_FILES)\n$(call _merge-renderscript-d,$(PRIVATE_DEP_FILES),$@.d)\n$(hide) mkdir -p $(dir $@)\n$(hide) touch $@\nendef\n\n\n###########################################################\n## Commands for running aidl\n###########################################################\n\ndefine transform-aidl-to-java\n@mkdir -p $(dir $@)\n@echo \"Aidl: $(PRIVATE_MODULE) <= $<\"\n$(hide) $(AIDL) -d$(patsubst %.java,%.P,$@) $(PRIVATE_AIDL_FLAGS) $< $@\nendef\n#$(AIDL) $(PRIVATE_AIDL_FLAGS) $< - | indent -nut -br -npcs -l1000 > $@\n\ndefine transform-aidl-to-cpp\n@mkdir -p $(dir $@)\n@mkdir -p $(PRIVATE_HEADER_OUTPUT_DIR)\n@echo \"Generating C++ from AIDL: $(PRIVATE_MODULE) <= $<\"\n$(hide) $(AIDL_CPP) -d$(basename $@).aidl.d --ninja $(PRIVATE_AIDL_FLAGS) \\\n    $< $(PRIVATE_HEADER_OUTPUT_DIR) $@\nendef\n\n## Given a .aidl file path, generate the rule to compile it a .java file\n# $(1): a .aidl source file\n# $(2): a directory to place the generated .java files in\n# $(3): name of a variable to add the path to the generated source file to\n#\n# You must call this with $(eval).\ndefine define-aidl-java-rule\ndefine_aidl_java_rule_src := $(patsubst %.aidl,%.java,$(subst ../,dotdot/,$(addprefix $(2)/,$(1))))\n$$(define_aidl_java_rule_src) : $(call clean-path,$(LOCAL_PATH)/$(1)) $(AIDL)\n\t$$(transform-aidl-to-java)\n$(3) += $$(define_aidl_java_rule_src)\nendef\n\n## Given a .aidl file path generate the rule to compile it a .cpp file.\n# $(1): a .aidl source file\n# $(2): a directory to place the generated .cpp files in\n# $(3): name of a variable to add the path to the generated source file to\n#\n# You must call this with $(eval).\ndefine define-aidl-cpp-rule\ndefine_aidl_cpp_rule_src := $(patsubst %.aidl,%$(LOCAL_CPP_EXTENSION),$(subst ../,dotdot/,$(addprefix $(2)/,$(1))))\n$$(define_aidl_cpp_rule_src) : $(call clean-path,$(LOCAL_PATH)/$(1)) $(AIDL_CPP)\n\t$$(transform-aidl-to-cpp)\n$(3) += $$(define_aidl_cpp_rule_src)\nendef\n\n###########################################################\n## Commands for running vts\n###########################################################\n\ndefine transform-vts-to-cpp\n@mkdir -p $(dir $@)\n@mkdir -p $(PRIVATE_HEADER_OUTPUT_DIR)\n@echo \"Generating C++ from VTS: $(PRIVATE_MODULE) <= $<\"\n$(hide) $(VTSC) -TODO_b/120496070 $(PRIVATE_VTS_FLAGS) \\\n    $< $(PRIVATE_HEADER_OUTPUT_DIR) $@\nendef\n\n## Given a .vts file path generate the rule to compile it a .cpp file.\n# $(1): a .vts source file\n# $(2): a directory to place the generated .cpp files in\n# $(3): name of a variable to add the path to the generated source file to\n#\n# You must call this with $(eval).\ndefine define-vts-cpp-rule\ndefine_vts_cpp_rule_src := $(patsubst %.vts,%$(LOCAL_CPP_EXTENSION),$(subst ../,dotdot/,$(addprefix $(2)/,$(1))))\n$$(define_vts_cpp_rule_src) : $(LOCAL_PATH)/$(1) $(VTSC)\n\t$$(transform-vts-to-cpp)\n$(3) += $$(define_vts_cpp_rule_src)\nendef\n\n###########################################################\n## Commands for running java-event-log-tags.py\n###########################################################\n\ndefine transform-logtags-to-java\n@mkdir -p $(dir $@)\n@echo \"logtags: $@ <= $<\"\n$(hide) $(JAVATAGS) -o $@ $<\nendef\n\n\n###########################################################\n## Commands for running protoc to compile .proto into .java\n###########################################################\n\ndefine transform-proto-to-java\n@mkdir -p $(dir $@)\n@echo \"Protoc: $@ <= $(PRIVATE_PROTO_SRC_FILES)\"\n@rm -rf $(PRIVATE_PROTO_JAVA_OUTPUT_DIR)\n@mkdir -p $(PRIVATE_PROTO_JAVA_OUTPUT_DIR)\n$(hide) for f in $(PRIVATE_PROTO_SRC_FILES); do \\\n        $(PROTOC) \\\n        $(addprefix --proto_path=, $(PRIVATE_PROTO_INCLUDES)) \\\n        $(PRIVATE_PROTO_JAVA_OUTPUT_OPTION)=\"$(PRIVATE_PROTO_JAVA_OUTPUT_PARAMS):$(PRIVATE_PROTO_JAVA_OUTPUT_DIR)\" \\\n        $(PRIVATE_PROTOC_FLAGS) \\\n        $$f || exit 33; \\\n        done\n$(SOONG_ZIP) -o $@ -C $(PRIVATE_PROTO_JAVA_OUTPUT_DIR) -D $(PRIVATE_PROTO_JAVA_OUTPUT_DIR)\nendef\n\n######################################################################\n## Commands for running protoc to compile .proto into .pb.cc (or.pb.c) and .pb.h\n######################################################################\n\ndefine transform-proto-to-cc\n@echo \"Protoc: $@ <= $<\"\n@mkdir -p $(dir $@)\n$(hide) \\\n  $(PROTOC) \\\n  $(addprefix --proto_path=, $(PRIVATE_PROTO_INCLUDES)) \\\n  $(PRIVATE_PROTOC_FLAGS) \\\n  $<\n@# aprotoc outputs only .cc. Rename it to .cpp if necessary.\n$(if $(PRIVATE_RENAME_CPP_EXT),\\\n  $(hide) mv $(basename $@).cc $@)\nendef\n\n###########################################################\n## Helper to set include paths form transform-*-to-o\n###########################################################\ndefine c-includes\n$(addprefix -I , $(PRIVATE_C_INCLUDES)) \\\n$(foreach i,$(PRIVATE_IMPORTED_INCLUDES),$(EXPORTS.$(i)))\\\n$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),,\\\n    $(addprefix -I ,\\\n        $(filter-out $(PRIVATE_C_INCLUDES), \\\n            $(PRIVATE_GLOBAL_C_INCLUDES))) \\\n    $(addprefix -isystem ,\\\n        $(filter-out $(PRIVATE_C_INCLUDES), \\\n            $(PRIVATE_GLOBAL_C_SYSTEM_INCLUDES))))\nendef\n\n###########################################################\n## Commands for running gcc to compile a C++ file\n###########################################################\n\ndefine transform-cpp-to-o-compiler-args\n$(c-includes) \\\n-c \\\n$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \\\n    $(PRIVATE_TARGET_GLOBAL_CFLAGS) \\\n    $(PRIVATE_TARGET_GLOBAL_CPPFLAGS) \\\n    $(PRIVATE_ARM_CFLAGS) \\\n ) \\\n$(PRIVATE_RTTI_FLAG) \\\n$(PRIVATE_CFLAGS) \\\n$(PRIVATE_CPPFLAGS) \\\n$(PRIVATE_DEBUG_CFLAGS) \\\n$(PRIVATE_CFLAGS_NO_OVERRIDE) \\\n$(PRIVATE_CPPFLAGS_NO_OVERRIDE)\nendef\n\n# PATH_TO_CLANG_TIDY is defined in build/soong\ndefine call-clang-tidy\n$(PATH_TO_CLANG_TIDY) \\\n  $(PRIVATE_TIDY_FLAGS) \\\n  -checks=$(PRIVATE_TIDY_CHECKS)\nendef\n\ndefine clang-tidy-cpp\n$(hide) $(call-clang-tidy) $< -- $(transform-cpp-to-o-compiler-args)\nendef\n\nifneq (,$(filter 1 true,$(WITH_TIDY_ONLY)))\ndefine transform-cpp-to-o\n$(if $(PRIVATE_TIDY_CHECKS),\n  @echo \"$($(PRIVATE_PREFIX)DISPLAY) tidy $(PRIVATE_ARM_MODE) C++: $<\"\n  $(clang-tidy-cpp))\nendef\nelse\ndefine transform-cpp-to-o\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<\"\n@mkdir -p $(dir $@)\n$(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-cpp))\n$(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \\\n  $(transform-cpp-to-o-compiler-args) \\\n  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<\nendef\nendif\n\n\n###########################################################\n## Commands for running gcc to compile a C file\n###########################################################\n\n# $(1): extra flags\ndefine transform-c-or-s-to-o-compiler-args\n$(c-includes) \\\n-c \\\n$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \\\n    $(PRIVATE_TARGET_GLOBAL_CFLAGS) \\\n    $(PRIVATE_TARGET_GLOBAL_CONLYFLAGS) \\\n    $(PRIVATE_ARM_CFLAGS) \\\n ) \\\n $(1)\nendef\n\ndefine transform-c-to-o-compiler-args\n$(call transform-c-or-s-to-o-compiler-args, \\\n  $(PRIVATE_CFLAGS) \\\n  $(PRIVATE_CONLYFLAGS) \\\n  $(PRIVATE_DEBUG_CFLAGS) \\\n  $(PRIVATE_CFLAGS_NO_OVERRIDE))\nendef\n\ndefine clang-tidy-c\n$(hide) $(call-clang-tidy) $< -- $(transform-c-to-o-compiler-args)\nendef\n\nifneq (,$(filter 1 true,$(WITH_TIDY_ONLY)))\ndefine transform-c-to-o\n$(if $(PRIVATE_TIDY_CHECKS),\n  @echo \"$($(PRIVATE_PREFIX)DISPLAY) tidy $(PRIVATE_ARM_MODE) C: $<\"\n  $(clang-tidy-c))\nendef\nelse\ndefine transform-c-to-o\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) $(PRIVATE_ARM_MODE) C: $(PRIVATE_MODULE) <= $<\"\n@mkdir -p $(dir $@)\n$(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-c))\n$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \\\n  $(transform-c-to-o-compiler-args) \\\n  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<\nendef\nendif\n\ndefine transform-s-to-o\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) asm: $(PRIVATE_MODULE) <= $<\"\n@mkdir -p $(dir $@)\n$(RELATIVE_PWD) $(PRIVATE_CC) \\\n  $(call transform-c-or-s-to-o-compiler-args, $(PRIVATE_ASFLAGS)) \\\n  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<\nendef\n\n# YASM compilation\ndefine transform-asm-to-o\n@mkdir -p $(dir $@)\n$(hide) $(YASM) \\\n    $(addprefix -I , $(PRIVATE_C_INCLUDES)) \\\n    $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_YASM_FLAGS) \\\n    $(PRIVATE_ASFLAGS) \\\n    -o $@ $<\nendef\n\n###########################################################\n## Commands for running gcc to compile an Objective-C file\n## This should never happen for target builds but this\n## will error at build time.\n###########################################################\n\ndefine transform-m-to-o\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) ObjC: $(PRIVATE_MODULE) <= $<\"\n$(call transform-c-or-s-to-o, $(PRIVATE_CFLAGS) $(PRIVATE_DEBUG_CFLAGS))\nendef\n\n###########################################################\n## Commands for running gcc to compile a host C++ file\n###########################################################\n\ndefine transform-host-cpp-to-o-compiler-args\n$(c-includes) \\\n-c \\\n$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \\\n    $(PRIVATE_HOST_GLOBAL_CFLAGS) \\\n    $(PRIVATE_HOST_GLOBAL_CPPFLAGS) \\\n ) \\\n$(PRIVATE_CFLAGS) \\\n$(PRIVATE_CPPFLAGS) \\\n$(PRIVATE_DEBUG_CFLAGS) \\\n$(PRIVATE_CFLAGS_NO_OVERRIDE) \\\n$(PRIVATE_CPPFLAGS_NO_OVERRIDE)\nendef\n\ndefine clang-tidy-host-cpp\n$(hide) $(call-clang-tidy) $< -- $(transform-host-cpp-to-o-compiler-args)\nendef\n\nifneq (,$(filter 1 true,$(WITH_TIDY_ONLY)))\ndefine transform-host-cpp-to-o\n$(if $(PRIVATE_TIDY_CHECKS),\n  @echo \"tidy $($(PRIVATE_PREFIX)DISPLAY) C++: $<\"\n  $(clang-tidy-host-cpp))\nendef\nelse\ndefine transform-host-cpp-to-o\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) C++: $(PRIVATE_MODULE) <= $<\"\n@mkdir -p $(dir $@)\n$(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-host-cpp))\n$(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \\\n  $(transform-host-cpp-to-o-compiler-args) \\\n  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<\nendef\nendif\n\n\n###########################################################\n## Commands for running gcc to compile a host C file\n###########################################################\n\ndefine transform-host-c-or-s-to-o-common-args\n$(c-includes) \\\n-c \\\n$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \\\n    $(PRIVATE_HOST_GLOBAL_CFLAGS) \\\n    $(PRIVATE_HOST_GLOBAL_CONLYFLAGS) \\\n )\nendef\n\n# $(1): extra flags\ndefine transform-host-c-or-s-to-o\n@mkdir -p $(dir $@)\n$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \\\n  $(transform-host-c-or-s-to-o-common-args) \\\n  $(1) \\\n  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<\nendef\n\ndefine transform-host-c-to-o-compiler-args\n  $(transform-host-c-or-s-to-o-common-args) \\\n  $(PRIVATE_CFLAGS) $(PRIVATE_CONLYFLAGS) \\\n  $(PRIVATE_DEBUG_CFLAGS) $(PRIVATE_CFLAGS_NO_OVERRIDE)\nendef\n\ndefine clang-tidy-host-c\n$(hide) $(call-clang-tidy) $< -- $(transform-host-c-to-o-compiler-args)\nendef\n\nifneq (,$(filter 1 true,$(WITH_TIDY_ONLY)))\ndefine transform-host-c-to-o\n$(if $(PRIVATE_TIDY_CHECKS),\n  @echo \"tidy $($(PRIVATE_PREFIX)DISPLAY) C: $<\"\n  $(clang-tidy-host-c))\nendef\nelse\ndefine transform-host-c-to-o\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) C: $(PRIVATE_MODULE) <= $<\"\n@mkdir -p $(dir $@)\n$(if $(PRIVATE_TIDY_CHECKS), $(clang-tidy-host-c))\n$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \\\n  $(transform-host-c-to-o-compiler-args) \\\n  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<\nendef\nendif\n\ndefine transform-host-s-to-o\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) asm: $(PRIVATE_MODULE) <= $<\"\n$(call transform-host-c-or-s-to-o, $(PRIVATE_ASFLAGS))\nendef\n\n###########################################################\n## Commands for running gcc to compile a host Objective-C file\n###########################################################\n\ndefine transform-host-m-to-o\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) ObjC: $(PRIVATE_MODULE) <= $<\"\n$(call transform-host-c-or-s-to-o, $(PRIVATE_CFLAGS) $(PRIVATE_DEBUG_CFLAGS) $(PRIVATE_CFLAGS_NO_OVERRIDE))\nendef\n\n###########################################################\n## Commands for running gcc to compile a host Objective-C++ file\n###########################################################\n\ndefine transform-host-mm-to-o\n$(transform-host-cpp-to-o)\nendef\n\n\n###########################################################\n## Rules to compile a single C/C++ source with ../ in the path\n###########################################################\n# Replace \"../\" in object paths with $(DOTDOT_REPLACEMENT).\nDOTDOT_REPLACEMENT := dotdot/\n\n## Rule to compile a C++ source file with ../ in the path.\n## Must be called with $(eval).\n# $(1): the C++ source file in LOCAL_SRC_FILES.\n# $(2): the additional dependencies.\n# $(3): the variable name to collect the output object file.\n# $(4): the ninja pool to use for the rule\ndefine compile-dotdot-cpp-file\no := $(intermediates)/$(patsubst %$(LOCAL_CPP_EXTENSION),%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1)))\n$$(o) : .KATI_NINJA_POOL := $(4)\n$$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG_CXX)\n\t$$(transform-$$(PRIVATE_HOST)cpp-to-o)\n$$(call include-depfiles-for-objs, $$(o))\n$(3) += $$(o)\nendef\n\n## Rule to compile a C source file with ../ in the path.\n## Must be called with $(eval).\n# $(1): the C source file in LOCAL_SRC_FILES.\n# $(2): the additional dependencies.\n# $(3): the variable name to collect the output object file.\n# $(4): the ninja pool to use for the rule\ndefine compile-dotdot-c-file\no := $(intermediates)/$(patsubst %.c,%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1)))\n$$(o) : .KATI_NINJA_POOL := $(4)\n$$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG)\n\t$$(transform-$$(PRIVATE_HOST)c-to-o)\n$$(call include-depfiles-for-objs, $$(o))\n$(3) += $$(o)\nendef\n\n## Rule to compile a .S source file with ../ in the path.\n## Must be called with $(eval).\n# $(1): the .S source file in LOCAL_SRC_FILES.\n# $(2): the additional dependencies.\n# $(3): the variable name to collect the output object file.\n# $(4): the ninja pool to use for the rule\ndefine compile-dotdot-s-file\no := $(intermediates)/$(patsubst %.S,%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1)))\n$$(o) : .KATI_NINJA_POOL := $(4)\n$$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG)\n\t$$(transform-$$(PRIVATE_HOST)s-to-o)\n$$(call include-depfiles-for-objs, $$(o))\n$(3) += $$(o)\nendef\n\n## Rule to compile a .s source file with ../ in the path.\n## Must be called with $(eval).\n# $(1): the .s source file in LOCAL_SRC_FILES.\n# $(2): the additional dependencies.\n# $(3): the variable name to collect the output object file.\n# $(4): the ninja pool to use for the rule\ndefine compile-dotdot-s-file-no-deps\no := $(intermediates)/$(patsubst %.s,%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1)))\n$$(o) : .KATI_NINJA_POOL := $(4)\n$$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG)\n\t$$(transform-$$(PRIVATE_HOST)s-to-o)\n$(3) += $$(o)\nendef\n\n###########################################################\n## Commands for running ar\n###########################################################\n\ndefine _concat-if-arg2-not-empty\n$(if $(2),$(hide) $(1) $(2))\nendef\n\n# Split long argument list into smaller groups and call the command repeatedly\n# Call the command at least once even if there are no arguments, as otherwise\n# the output file won't be created.\n#\n# $(1): the command without arguments\n# $(2): the arguments\ndefine split-long-arguments\n$(hide) $(1) $(wordlist 1,500,$(2))\n$(call _concat-if-arg2-not-empty,$(1),$(wordlist 501,1000,$(2)))\n$(call _concat-if-arg2-not-empty,$(1),$(wordlist 1001,1500,$(2)))\n$(call _concat-if-arg2-not-empty,$(1),$(wordlist 1501,2000,$(2)))\n$(call _concat-if-arg2-not-empty,$(1),$(wordlist 2001,2500,$(2)))\n$(call _concat-if-arg2-not-empty,$(1),$(wordlist 2501,3000,$(2)))\n$(call _concat-if-arg2-not-empty,$(1),$(wordlist 3001,99999,$(2)))\nendef\n\n# $(1): the full path of the source static library.\n# $(2): the full path of the destination static library.\ndefine _extract-and-include-single-target-whole-static-lib\n$(hide) ldir=$(PRIVATE_INTERMEDIATES_DIR)/WHOLE/$(basename $(notdir $(1)))_objs;\\\n    rm -rf $$ldir; \\\n    mkdir -p $$ldir; \\\n    cp $(1) $$ldir; \\\n    lib_to_include=$$ldir/$(notdir $(1)); \\\n    filelist=; \\\n    subdir=0; \\\n    for f in `$($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) t $(1)`; do \\\n        if [ -e $$ldir/$$f ]; then \\\n            mkdir $$ldir/$$subdir; \\\n            ext=$$subdir/; \\\n            subdir=$$((subdir+1)); \\\n            $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) m $$lib_to_include $$f; \\\n        else \\\n            ext=; \\\n        fi; \\\n        $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) p $$lib_to_include $$f > $$ldir/$$ext$$f; \\\n        filelist=\"$$filelist $$ldir/$$ext$$f\"; \\\n    done ; \\\n    $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_ARFLAGS) \\\n        $(PRIVATE_ARFLAGS) $(2) $$filelist\n\nendef\n\n# $(1): the full path of the source static library.\n# $(2): the full path of the destination static library.\ndefine extract-and-include-whole-static-libs-first\n$(if $(strip $(1)),\n$(hide) cp $(1) $(2))\nendef\n\n# $(1): the full path of the destination static library.\ndefine extract-and-include-target-whole-static-libs\n$(call extract-and-include-whole-static-libs-first, $(firstword $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)),$(1))\n$(foreach lib,$(wordlist 2,999,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)), \\\n    $(call _extract-and-include-single-target-whole-static-lib, $(lib), $(1)))\nendef\n\n# Explicitly delete the archive first so that ar doesn't\n# try to add to an existing archive.\ndefine transform-o-to-static-lib\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) StaticLib: $(PRIVATE_MODULE) ($@)\"\n@mkdir -p $(dir $@)\n@rm -f $@ $@.tmp\n$(call extract-and-include-target-whole-static-libs,$@.tmp)\n$(call split-long-arguments,$($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) \\\n    $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_ARFLAGS) \\\n    $(PRIVATE_ARFLAGS) \\\n    $@.tmp,$(PRIVATE_ALL_OBJECTS))\n$(hide) mv -f $@.tmp $@\nendef\n\n###########################################################\n## Commands for running host ar\n###########################################################\n\n# $(1): the full path of the source static library.\n# $(2): the full path of the destination static library.\ndefine _extract-and-include-single-host-whole-static-lib\n$(hide) ldir=$(PRIVATE_INTERMEDIATES_DIR)/WHOLE/$(basename $(notdir $(1)))_objs;\\\n    rm -rf $$ldir; \\\n    mkdir -p $$ldir; \\\n    cp $(1) $$ldir; \\\n    lib_to_include=$$ldir/$(notdir $(1)); \\\n    filelist=; \\\n    subdir=0; \\\n    for f in `$($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) t $(1) | \\grep '\\.o$$'`; do \\\n        if [ -e $$ldir/$$f ]; then \\\n           mkdir $$ldir/$$subdir; \\\n           ext=$$subdir/; \\\n           subdir=$$((subdir+1)); \\\n           $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) m $$lib_to_include $$f; \\\n        else \\\n           ext=; \\\n        fi; \\\n        $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) p $$lib_to_include $$f > $$ldir/$$ext$$f; \\\n        filelist=\"$$filelist $$ldir/$$ext$$f\"; \\\n    done ; \\\n    $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)GLOBAL_ARFLAGS) \\\n        $(2) $$filelist\n\nendef\n\ndefine extract-and-include-host-whole-static-libs\n$(call extract-and-include-whole-static-libs-first, $(firstword $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)),$(1))\n$(foreach lib,$(wordlist 2,999,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)), \\\n    $(call _extract-and-include-single-host-whole-static-lib, $(lib),$(1)))\nendef\n\nifeq ($(HOST_OS),darwin)\n# On Darwin the host ar fails if there is nothing to add to .a at all.\n# We work around by adding a dummy.o and then deleting it.\ndefine create-dummy.o-if-no-objs\n$(if $(PRIVATE_ALL_OBJECTS),,$(hide) touch $(dir $(1))dummy.o)\nendef\n\ndefine get-dummy.o-if-no-objs\n$(if $(PRIVATE_ALL_OBJECTS),,$(dir $(1))dummy.o)\nendef\n\ndefine delete-dummy.o-if-no-objs\n$(if $(PRIVATE_ALL_OBJECTS),,$(hide) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) d $(1) $(dir $(1))dummy.o \\\n  && rm -f $(dir $(1))dummy.o)\nendef\nelse\ncreate-dummy.o-if-no-objs =\nget-dummy.o-if-no-objs =\ndelete-dummy.o-if-no-objs =\nendif  # HOST_OS is darwin\n\n# Explicitly delete the archive first so that ar doesn't\n# try to add to an existing archive.\ndefine transform-host-o-to-static-lib\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) StaticLib: $(PRIVATE_MODULE) ($@)\"\n@mkdir -p $(dir $@)\n@rm -f $@ $@.tmp\n$(call extract-and-include-host-whole-static-libs,$@.tmp)\n$(call create-dummy.o-if-no-objs,$@.tmp)\n$(call split-long-arguments,$($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) \\\n    $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)GLOBAL_ARFLAGS) $@.tmp,\\\n    $(PRIVATE_ALL_OBJECTS) $(call get-dummy.o-if-no-objs,$@.tmp))\n$(call delete-dummy.o-if-no-objs,$@.tmp)\n$(hide) mv -f $@.tmp $@\nendef\n\n\n###########################################################\n## Commands for running gcc to link a shared library or package\n###########################################################\n\n# ld just seems to be so finicky with command order that we allow\n# it to be overriden en-masse see combo/linux-arm.make for an example.\nifneq ($(HOST_CUSTOM_LD_COMMAND),true)\ndefine transform-host-o-to-shared-lib-inner\n$(hide) $(PRIVATE_CXX_LINK) \\\n  -Wl,-rpath,\\$$ORIGIN/../$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)OUT_SHARED_LIBRARIES)) \\\n  -Wl,-rpath,\\$$ORIGIN/$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)OUT_SHARED_LIBRARIES)) \\\n  -shared -Wl,-soname,$(notdir $@) \\\n  $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \\\n     $(PRIVATE_HOST_GLOBAL_LDFLAGS) \\\n  ) \\\n  $(PRIVATE_LDFLAGS) \\\n  $(PRIVATE_CRTBEGIN) \\\n  $(PRIVATE_ALL_OBJECTS) \\\n  -Wl,--whole-archive \\\n  $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \\\n  -Wl,--no-whole-archive \\\n  $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \\\n  $(PRIVATE_ALL_STATIC_LIBRARIES) \\\n  $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \\\n  $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_HOST_LIBPROFILE_RT)) \\\n  $(PRIVATE_LIBCRT_BUILTINS) \\\n  $(PRIVATE_ALL_SHARED_LIBRARIES) \\\n  -o $@ \\\n  $(PRIVATE_CRTEND) \\\n  $(PRIVATE_LDLIBS)\nendef\nendif\n\ndefine transform-host-o-to-shared-lib\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) SharedLib: $(PRIVATE_MODULE) ($@)\"\n@mkdir -p $(dir $@)\n$(transform-host-o-to-shared-lib-inner)\nendef\n\ndefine transform-host-o-to-package\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) Package: $(PRIVATE_MODULE) ($@)\"\n@mkdir -p $(dir $@)\n$(transform-host-o-to-shared-lib-inner)\nendef\n\n\n###########################################################\n## Commands for running gcc to link a shared library or package\n###########################################################\n\ndefine transform-o-to-shared-lib-inner\n$(hide) $(PRIVATE_CXX_LINK) \\\n  -nostdlib -Wl,-soname,$(notdir $@) \\\n  -Wl,--gc-sections \\\n  -shared \\\n  $(PRIVATE_TARGET_CRTBEGIN_SO_O) \\\n  $(PRIVATE_ALL_OBJECTS) \\\n  -Wl,--whole-archive \\\n  $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \\\n  -Wl,--no-whole-archive \\\n  $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \\\n  $(PRIVATE_ALL_STATIC_LIBRARIES) \\\n  $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \\\n  $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_COVERAGE_LIB)) \\\n  $(PRIVATE_TARGET_LIBCRT_BUILTINS) \\\n  $(PRIVATE_TARGET_GLOBAL_LDFLAGS) \\\n  $(PRIVATE_LDFLAGS) \\\n  $(PRIVATE_ALL_SHARED_LIBRARIES) \\\n  -o $@ \\\n  $(PRIVATE_TARGET_CRTEND_SO_O) \\\n  $(PRIVATE_LDLIBS)\nendef\n\ndefine transform-o-to-shared-lib\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) SharedLib: $(PRIVATE_MODULE) ($@)\"\n@mkdir -p $(dir $@)\n$(transform-o-to-shared-lib-inner)\nendef\n\n###########################################################\n## Commands for running gcc to link an executable\n###########################################################\n\ndefine transform-o-to-executable-inner\n$(hide) $(PRIVATE_CXX_LINK) -pie \\\n  -nostdlib -Bdynamic \\\n  -Wl,-dynamic-linker,$(PRIVATE_LINKER) \\\n  -Wl,--gc-sections \\\n  -Wl,-z,nocopyreloc \\\n  $(PRIVATE_TARGET_CRTBEGIN_DYNAMIC_O) \\\n  $(PRIVATE_ALL_OBJECTS) \\\n  -Wl,--whole-archive \\\n  $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \\\n  -Wl,--no-whole-archive \\\n  $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \\\n  $(PRIVATE_ALL_STATIC_LIBRARIES) \\\n  $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \\\n  $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_COVERAGE_LIB)) \\\n  $(PRIVATE_TARGET_LIBCRT_BUILTINS) \\\n  $(PRIVATE_TARGET_GLOBAL_LDFLAGS) \\\n  $(PRIVATE_LDFLAGS) \\\n  $(PRIVATE_ALL_SHARED_LIBRARIES) \\\n  -o $@ \\\n  $(PRIVATE_TARGET_CRTEND_O) \\\n  $(PRIVATE_LDLIBS)\nendef\n\ndefine transform-o-to-executable\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) Executable: $(PRIVATE_MODULE) ($@)\"\n@mkdir -p $(dir $@)\n$(transform-o-to-executable-inner)\nendef\n\n\n###########################################################\n## Commands for linking a static executable. In practice,\n## we only use this on arm, so the other platforms don't\n## have transform-o-to-static-executable defined.\n## Clang driver needs -static to create static executable.\n## However, bionic/linker uses -shared to overwrite.\n## Linker for x86 targets does not allow coexistance of -static and -shared,\n## so we add -static only if -shared is not used.\n###########################################################\n\ndefine transform-o-to-static-executable-inner\n$(hide) $(PRIVATE_CXX_LINK) \\\n  -nostdlib -Bstatic \\\n  $(if $(filter $(PRIVATE_LDFLAGS),-shared),,-static) \\\n  -Wl,--gc-sections \\\n  -o $@ \\\n  $(PRIVATE_TARGET_CRTBEGIN_STATIC_O) \\\n  $(PRIVATE_TARGET_GLOBAL_LDFLAGS) \\\n  $(PRIVATE_LDFLAGS) \\\n  $(PRIVATE_ALL_OBJECTS) \\\n  -Wl,--whole-archive \\\n  $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \\\n  -Wl,--no-whole-archive \\\n  $(filter-out %libcompiler_rt.hwasan.a %libc_nomalloc.hwasan.a %libc.hwasan.a %libcompiler_rt.a %libc_nomalloc.a %libc.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \\\n  -Wl,--start-group \\\n  $(filter %libc.a %libc.hwasan.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \\\n  $(filter %libc_nomalloc.a %libc_nomalloc.hwasan.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \\\n  $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_COVERAGE_LIB)) \\\n  $(filter %libcompiler_rt.a %libcompiler_rt.hwasan.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \\\n  $(PRIVATE_TARGET_LIBCRT_BUILTINS) \\\n  -Wl,--end-group \\\n  $(PRIVATE_TARGET_CRTEND_O)\nendef\n\ndefine transform-o-to-static-executable\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) StaticExecutable: $(PRIVATE_MODULE) ($@)\"\n@mkdir -p $(dir $@)\n$(transform-o-to-static-executable-inner)\nendef\n\n\n###########################################################\n## Commands for running gcc to link a host executable\n###########################################################\n\nifneq ($(HOST_CUSTOM_LD_COMMAND),true)\ndefine transform-host-o-to-executable-inner\n$(hide) $(PRIVATE_CXX_LINK) \\\n  $(PRIVATE_CRTBEGIN) \\\n  $(PRIVATE_ALL_OBJECTS) \\\n  -Wl,--whole-archive \\\n  $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \\\n  -Wl,--no-whole-archive \\\n  $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \\\n  $(PRIVATE_ALL_STATIC_LIBRARIES) \\\n  $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \\\n  $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_HOST_LIBPROFILE_RT)) \\\n  $(PRIVATE_LIBCRT_BUILTINS) \\\n  $(PRIVATE_ALL_SHARED_LIBRARIES) \\\n  $(foreach path,$(PRIVATE_RPATHS), \\\n    -Wl,-rpath,\\$$ORIGIN/$(path)) \\\n  $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \\\n      $(PRIVATE_HOST_GLOBAL_LDFLAGS) \\\n  ) \\\n  $(PRIVATE_LDFLAGS) \\\n  -o $@ \\\n  $(PRIVATE_CRTEND) \\\n  $(PRIVATE_LDLIBS)\nendef\nendif\n\ndefine transform-host-o-to-executable\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) Executable: $(PRIVATE_MODULE) ($@)\"\n@mkdir -p $(dir $@)\n$(transform-host-o-to-executable-inner)\nendef\n\n###########################################################\n## Commands for packaging native coverage files\n###########################################################\ndefine package-coverage-files\n  @rm -f $@ $@.lst $@.premerged\n  @touch $@.lst\n  $(foreach obj,$(strip $(PRIVATE_ALL_OBJECTS)), $(hide) echo $(obj) >> $@.lst$(newline))\n  $(hide) $(SOONG_ZIP) -o $@.premerged -C $(OUT_DIR) -l $@.lst\n  $(hide) $(MERGE_ZIPS) -ignore-duplicates $@ $@.premerged $(strip $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES))\nendef\n\n###########################################################\n## Commands for running javac to make .class files\n###########################################################\n\n# b/37750224\nAAPT_ASAN_OPTIONS := ASAN_OPTIONS=detect_leaks=0\n\n# Search for generated R.java in $1, copy the found R.java as $2.\ndefine find-generated-R.java\n$(hide) for GENERATED_R_FILE in `find $(1) \\\n  -name R.java 2> /dev/null`; do \\\n    cp $$GENERATED_R_FILE $(2) || exit 32; \\\n  done;\n@# Ensure that the target file is always created, i.e. also in case we did not\n@# enter the GENERATED_R_FILE-loop above. This avoids unnecessary rebuilding.\n$(hide) touch $(2)\nendef\n\n###########################################################\n# AAPT2 compilation and link\n###########################################################\ndefine aapt2-compile-one-resource-file\n@mkdir -p $(dir $@)\n$(hide) $(AAPT2) compile -o $(dir $@) $(PRIVATE_AAPT2_CFLAGS) $<\nendef\n\ndefine aapt2-compile-resource-dirs\n@mkdir -p $(dir $@)\n$(hide) $(AAPT2) compile -o $@ $(addprefix --dir ,$(PRIVATE_SOURCE_RES_DIRS)) \\\n  $(PRIVATE_AAPT2_CFLAGS)\nendef\n\n# TODO(b/74574557): use aapt2 compile --zip if it gets implemented\ndefine aapt2-compile-resource-zips\n@mkdir -p $(dir $@)\n$(ZIPSYNC) -d $@.contents -l $@.list $(PRIVATE_SOURCE_RES_ZIPS)\n$(hide) $(AAPT2) compile -o $@ --dir $@.contents $(PRIVATE_AAPT2_CFLAGS)\nendef\n\n# Set up rule to compile one resource file with aapt2.\n# Must be called with $(eval).\n# $(1): the source file\n# $(2): the output file\ndefine aapt2-compile-one-resource-file-rule\n$(2) : $(1) $(AAPT2)\n\t@echo \"AAPT2 compile $$@ <- $$<\"\n\t$$(call aapt2-compile-one-resource-file)\nendef\n\n# Convert input resource file path to output file path.\n# values-[config]/<file>.xml -> values-[config]_<file>.arsc.flat;\n# For other resource file, just replace the last \"/\" with \"_\" and\n# add .flat extension.\n#\n# $(1): the input resource file path\n# $(2): the base dir of the output file path\n# Returns: the compiled output file path\ndefine aapt2-compiled-resource-out-file\n$(strip \\\n  $(eval _p_w := $(strip $(subst /,$(space),$(dir $(call clean-path,$(1))))))\n  $(2)/$(subst $(space),/,$(_p_w))_$(if $(filter values%,$(lastword $(_p_w))),$(patsubst %.xml,%.arsc,$(notdir $(1))),$(notdir $(1))).flat)\nendef\n\ndefine aapt2-link\n@mkdir -p $(dir $@)\nrm -rf $(PRIVATE_JAVA_GEN_DIR)\nmkdir -p $(PRIVATE_JAVA_GEN_DIR)\n$(call dump-words-to-file,$(PRIVATE_RES_FLAT),$(dir $@)aapt2-flat-list)\n$(call dump-words-to-file,$(PRIVATE_OVERLAY_FLAT),$(dir $@)aapt2-flat-overlay-list)\ncat $(PRIVATE_STATIC_LIBRARY_TRANSITIVE_RES_PACKAGES_LISTS) | sort -u | tr '\\n' ' ' > $(dir $@)aapt2-transitive-overlay-list\n$(hide) $(AAPT2) link -o $@ \\\n  $(PRIVATE_AAPT_FLAGS) \\\n  $(if $(PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES),$$(cat $(PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES))) \\\n  $(addprefix --manifest ,$(PRIVATE_ANDROID_MANIFEST)) \\\n  $(addprefix -I ,$(PRIVATE_AAPT_INCLUDES)) \\\n  $(addprefix -I ,$(PRIVATE_SHARED_ANDROID_LIBRARIES)) \\\n  $(addprefix -A ,$(foreach d,$(PRIVATE_ASSET_DIR),$(call clean-path,$(d)))) \\\n  $(addprefix --java ,$(PRIVATE_JAVA_GEN_DIR)) \\\n  $(addprefix --proguard ,$(PRIVATE_PROGUARD_OPTIONS_FILE)) \\\n  $(addprefix --min-sdk-version ,$(PRIVATE_DEFAULT_APP_TARGET_SDK)) \\\n  $(addprefix --target-sdk-version ,$(PRIVATE_DEFAULT_APP_TARGET_SDK)) \\\n  $(if $(filter --product,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --product ,$(PRIVATE_TARGET_AAPT_CHARACTERISTICS))) \\\n  $(addprefix -c ,$(PRIVATE_PRODUCT_AAPT_CONFIG)) \\\n  $(addprefix --preferred-density ,$(PRIVATE_PRODUCT_AAPT_PREF_CONFIG)) \\\n  $(if $(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,--version-code $(PLATFORM_SDK_VERSION)) \\\n  $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,--version-name $(APPS_DEFAULT_VERSION_NAME)) \\\n  $(addprefix --rename-manifest-package ,$(PRIVATE_MANIFEST_PACKAGE_NAME)) \\\n  $(addprefix --rename-instrumentation-target-package ,$(PRIVATE_MANIFEST_INSTRUMENTATION_FOR)) \\\n  -R \\@$(dir $@)aapt2-flat-overlay-list \\\n  -R \\@$(dir $@)aapt2-transitive-overlay-list \\\n  \\@$(dir $@)aapt2-flat-list\n$(SOONG_ZIP) -o $(PRIVATE_SRCJAR) -C $(PRIVATE_JAVA_GEN_DIR) -D $(PRIVATE_JAVA_GEN_DIR)\n$(EXTRACT_JAR_PACKAGES) -i $(PRIVATE_SRCJAR) -o $(PRIVATE_AAPT_EXTRA_PACKAGES) --prefix '--extra-packages '\nendef\n\ndefine _create-default-manifest-file\n$(1):\n\trm -f $1\n\t(echo '<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"missing.manifest\">' && \\\n\t echo '    <uses-sdk android:minSdkVersion=\"$(2)\" />' && \\\n\t echo '</manifest>' ) > $1\nendef\n\ndefine create-default-manifest-file\n  $(eval $(call _create-default-manifest-file,$(1),$(2)))\nendef\n\n\n###########################################################\nxlint_unchecked := -Xlint:unchecked\n\n# emit-line, <word list>, <output file>\ndefine emit-line\n   $(if $(1),echo -n '$(strip $(1)) ' >> $(2))\nendef\n\n# dump-words-to-file, <word list>, <output file>\ndefine dump-words-to-file\n        @rm -f $(2)\n        @touch $(2)\n        @$(call emit-line,$(wordlist 1,500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 501,1000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 1001,1500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 1501,2000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 2001,2500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 2501,3000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 3001,3500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 3501,4000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 4001,4500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 4501,5000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 5001,5500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 5501,6000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 6001,6500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 6501,7000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 7001,7500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 7501,8000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 8001,8500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 8501,9000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 9001,9500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 9501,10000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 10001,10500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 10501,11000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 11001,11500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 11501,12000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 12001,12500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 12501,13000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 13001,13500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 13501,14000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 14001,14500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 14501,15000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 15001,15500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 15501,16000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 16001,16500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 16501,17000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 17001,17500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 17501,18000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 18001,18500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 18501,19000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 19001,19500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 19501,20000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 20001,20500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 20501,21000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 21001,21500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 21501,22000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 22001,22500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 22501,23000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 23001,23500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 23501,24000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 24001,24500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 24501,25000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 25001,25500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 25501,26000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 26001,26500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 26501,27000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 27001,27500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 27501,28000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 28001,28500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 28501,29000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 29001,29500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 29501,30000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 30001,30500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 30501,31000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 31001,31500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 31501,32000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 32001,32500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 32501,33000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 33001,33500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 33501,34000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 34001,34500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 34501,35000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 35001,35500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 35501,36000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 36001,36500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 36501,37000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 37001,37500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 37501,38000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 38001,38500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 38501,39000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 39001,39500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 39501,40000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 40001,40500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 40501,41000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 41001,41500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 41501,42000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 42001,42500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 42501,43000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 43001,43500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 43501,44000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 44001,44500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 44501,45000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 45001,45500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 45501,46000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 46001,46500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 46501,47000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 47001,47500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 47501,48000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 48001,48500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 48501,49000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 49001,49500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 49501,50000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 50001,50500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 50501,51000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 51001,51500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 51501,52000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 52001,52500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 52501,53000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 53001,53500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 53501,54000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 54001,54500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 54501,55000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 55001,55500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 55501,56000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 56001,56500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 56501,57000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 57001,57500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 57501,58000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 58001,58500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 58501,59000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 59001,59500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 59501,60000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 60001,60500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 60501,61000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 61001,61500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 61501,62000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 62001,62500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 62501,63000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 63001,63500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 63501,64000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 64001,64500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 64501,65000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 65001,65500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 65501,66000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 66001,66500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 66501,67000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 67001,67500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 67501,68000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 68001,68500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 68501,69000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 69001,69500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 69501,70000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 70001,70500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 70501,71000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 71001,71500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 71501,72000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 72001,72500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 72501,73000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 73001,73500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 73501,74000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 74001,74500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 74501,75000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 75001,75500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 75501,76000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 76001,76500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 76501,77000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 77001,77500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 77501,78000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 78001,78500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 78501,79000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 79001,79500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 79501,80000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 80001,80500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 80501,81000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 81001,81500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 81501,82000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 82001,82500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 82501,83000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 83001,83500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 83501,84000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 84001,84500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 84501,85000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 85001,85500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 85501,86000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 86001,86500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 86501,87000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 87001,87500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 87501,88000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 88001,88500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 88501,89000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 89001,89500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 89501,90000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 90001,90500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 90501,91000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 91001,91500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 91501,92000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 92001,92500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 92501,93000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 93001,93500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 93501,94000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 94001,94500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 94501,95000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 95001,95500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 95501,96000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 96001,96500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 96501,97000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 97001,97500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 97501,98000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 98001,98500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 98501,99000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 99001,99500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 99501,100000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 100001,100500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 100501,101000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 101001,101500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 101501,102000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 102001,102500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 102501,103000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 103001,103500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 103501,104000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 104001,104500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 104501,105000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 105001,105500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 105501,106000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 106001,106500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 106501,107000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 107001,107500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 107501,108000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 108001,108500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 108501,109000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 109001,109500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 109501,110000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 110001,110500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 110501,111000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 111001,111500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 111501,112000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 112001,112500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 112501,113000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 113001,113500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 113501,114000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 114001,114500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 114501,115000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 115001,115500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 115501,116000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 116001,116500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 116501,117000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 117001,117500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 117501,118000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 118001,118500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 118501,119000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 119001,119500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 119501,120000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 120001,120500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 120501,121000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 121001,121500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 121501,122000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 122001,122500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 122501,123000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 123001,123500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 123501,124000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 124001,124500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 124501,125000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 125001,125500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 125501,126000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 126001,126500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 126501,127000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 127001,127500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 127501,128000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 128001,128500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 128501,129000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 129001,129500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 129501,130000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 130001,130500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 130501,131000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 131001,131500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 131501,132000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 132001,132500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 132501,133000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 133001,133500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 133501,134000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 134001,134500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 134501,135000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 135001,135500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 135501,136000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 136001,136500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 136501,137000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 137001,137500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 137501,138000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 138001,138500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 138501,139000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 139001,139500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 139501,140000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 140001,140500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 140501,141000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 141001,141500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 141501,142000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 142001,142500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 142501,143000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 143001,143500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 143501,144000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 144001,144500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 144501,145000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 145001,145500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 145501,146000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 146001,146500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 146501,147000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 147001,147500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 147501,148000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 148001,148500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 148501,149000,$(1)),$(2))\n        @$(call emit-line,$(wordlist 149001,149500,$(1)),$(2))\n        @$(call emit-line,$(wordlist 149501,150000,$(1)),$(2))\n        @$(if $(wordlist 150001,150002,$(1)),$(error dump-words-to-file: Too many words ($(words $(1)))))\nendef\n# Return jar arguments to compress files in a given directory\n# $(1): directory\n#\n# Returns an @-file argument that contains the output of a subshell\n# that looks like -C $(1) path/to/file1 -C $(1) path/to/file2\n# Also adds \"-C out/empty .\" which avoids errors in jar when\n# there are no files in the directory.\ndefine jar-args-sorted-files-in-directory\n    @<(find $(1) -type f | sort | $(JAR_ARGS) $(1); echo \"-C $(EMPTY_DIRECTORY) .\")\nendef\n\n# append additional Java sources(resources/Proto sources, and etc) to $(1).\ndefine fetch-additional-java-source\n$(hide) if [ -d \"$(PRIVATE_SOURCE_INTERMEDIATES_DIR)\" ]; then \\\n    find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' -and -not -name '.*' >> $(1); \\\nfi\nendef\n\n# Some historical notes:\n# - below we write the list of java files to java-source-list to avoid argument\n#   list length problems with Cygwin\n# - we filter out duplicate java file names because eclipse's compiler\n#   doesn't like them.\ndefine write-java-source-list\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) Java source list: $(PRIVATE_MODULE)\"\n$(hide) rm -f $@\n$(call dump-words-to-file,$(sort $(PRIVATE_JAVA_SOURCES)),$@.tmp)\n$(call fetch-additional-java-source,$@.tmp)\n$(hide) tr ' ' '\\n' < $@.tmp | $(NORMALIZE_PATH) | sort -u > $@\nendef\n\n# Common definition to invoke javac on the host and target.\n#\n# $(1): javac\n# $(2): classpath_libs\ndefine compile-java\n$(hide) rm -f $@\n$(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) $(PRIVATE_ANNO_INTERMEDIATES_DIR)\n$(hide) mkdir -p $(dir $@)\n$(hide) mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR) $(PRIVATE_ANNO_INTERMEDIATES_DIR)\n$(if $(PRIVATE_SRCJARS),\\\n    $(ZIPSYNC) -d $(PRIVATE_SRCJAR_INTERMEDIATES_DIR) -l $(PRIVATE_SRCJAR_LIST_FILE) -f \"*.java\" $(PRIVATE_SRCJARS))\n$(hide) if [ -s $(PRIVATE_JAVA_SOURCE_LIST) $(if $(PRIVATE_SRCJARS),-o -s $(PRIVATE_SRCJAR_LIST_FILE) )] ; then \\\n    $(SOONG_JAVAC_WRAPPER) $(JAVAC_WRAPPER) $(1) -encoding UTF-8 \\\n    $(if $(findstring true,$(PRIVATE_WARNINGS_ENABLE)),$(xlint_unchecked),) \\\n    $(if $(PRIVATE_USE_SYSTEM_MODULES), \\\n      $(addprefix --system=,$(PRIVATE_SYSTEM_MODULES_DIR)), \\\n      $(addprefix -bootclasspath ,$(strip \\\n          $(call normalize-path-list,$(PRIVATE_BOOTCLASSPATH)) \\\n          $(PRIVATE_EMPTY_BOOTCLASSPATH)))) \\\n    $(if $(PRIVATE_USE_SYSTEM_MODULES), \\\n      $(if $(PRIVATE_PATCH_MODULE), \\\n        --patch-module=$(PRIVATE_PATCH_MODULE)=$(call normalize-path-list,. $(2)))) \\\n    $(addprefix -classpath ,$(call normalize-path-list,$(strip \\\n      $(if $(PRIVATE_USE_SYSTEM_MODULES), \\\n        $(filter-out $(PRIVATE_SYSTEM_MODULES_LIBS),$(PRIVATE_BOOTCLASSPATH))) \\\n      $(2)))) \\\n    $(if $(findstring true,$(PRIVATE_WARNINGS_ENABLE)),$(xlint_unchecked),) \\\n    -d $(PRIVATE_CLASS_INTERMEDIATES_DIR) -s $(PRIVATE_ANNO_INTERMEDIATES_DIR) \\\n    $(PRIVATE_JAVACFLAGS) \\\n    \\@$(PRIVATE_JAVA_SOURCE_LIST) \\\n    $(if $(PRIVATE_SRCJARS),\\@$(PRIVATE_SRCJAR_LIST_FILE)) \\\n    || ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 ) \\\nfi\n$(if $(PRIVATE_JAR_EXCLUDE_FILES), $(hide) find $(PRIVATE_CLASS_INTERMEDIATES_DIR) \\\n    -name $(word 1, $(PRIVATE_JAR_EXCLUDE_FILES)) \\\n    $(addprefix -o -name , $(wordlist 2, 999, $(PRIVATE_JAR_EXCLUDE_FILES))) \\\n    | xargs rm -rf)\n$(if $(PRIVATE_JAR_PACKAGES), \\\n    $(hide) find $(PRIVATE_CLASS_INTERMEDIATES_DIR) -mindepth 1 -type f \\\n        $(foreach pkg, $(PRIVATE_JAR_PACKAGES), \\\n            -not -path $(PRIVATE_CLASS_INTERMEDIATES_DIR)/$(subst .,/,$(pkg))/\\*) -delete ; \\\n        find $(PRIVATE_CLASS_INTERMEDIATES_DIR) -empty -delete)\n$(if $(PRIVATE_JAR_EXCLUDE_PACKAGES), $(hide) rm -rf \\\n    $(foreach pkg, $(PRIVATE_JAR_EXCLUDE_PACKAGES), \\\n        $(PRIVATE_CLASS_INTERMEDIATES_DIR)/$(subst .,/,$(pkg))))\n$(hide) $(SOONG_ZIP) -jar -o $@ -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) -D $(PRIVATE_CLASS_INTERMEDIATES_DIR)\n$(if $(PRIVATE_EXTRA_JAR_ARGS),$(call add-java-resources-to,$@))\nendef\n\ndefine transform-java-to-header.jar\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) Turbine: $(PRIVATE_MODULE)\"\n@mkdir -p $(dir $@)\n@rm -rf $(dir $@)/classes-turbine\n@mkdir $(dir $@)/classes-turbine\n$(hide) if [ -s $(PRIVATE_JAVA_SOURCE_LIST) -o -n \"$(PRIVATE_SRCJARS)\" ] ; then \\\n    $(JAVA) -jar $(TURBINE) \\\n    --output $@.premerged --temp_dir $(dir $@)/classes-turbine \\\n    --sources \\@$(PRIVATE_JAVA_SOURCE_LIST) --source_jars $(PRIVATE_SRCJARS) \\\n    --javacopts $(PRIVATE_JAVACFLAGS) $(COMMON_JDK_FLAGS) -- \\\n    $(if $(PRIVATE_USE_SYSTEM_MODULES), \\\n      --system $(PRIVATE_SYSTEM_MODULES_DIR), \\\n      --bootclasspath $(strip $(PRIVATE_BOOTCLASSPATH))) \\\n    --classpath $(strip $(if $(PRIVATE_USE_SYSTEM_MODULES), \\\n        $(filter-out $(PRIVATE_SYSTEM_MODULES_LIBS),$(PRIVATE_BOOTCLASSPATH))) \\\n      $(PRIVATE_ALL_JAVA_HEADER_LIBRARIES)) \\\n    || ( rm -rf $(dir $@)/classes-turbine ; exit 41 ) && \\\n    $(MERGE_ZIPS) -j --ignore-duplicates -stripDir META-INF $@.tmp $@.premerged $(PRIVATE_STATIC_JAVA_HEADER_LIBRARIES) ; \\\nelse \\\n    $(MERGE_ZIPS) -j --ignore-duplicates -stripDir META-INF $@.tmp $(PRIVATE_STATIC_JAVA_HEADER_LIBRARIES) ; \\\nfi\n$(hide) $(ZIPTIME) $@.tmp\n$(hide) $(call commit-change-for-toc,$@)\nendef\n\n# Runs jarjar on an input file.  Jarjar doesn't exit with a nonzero return code\n# when there is a syntax error in a rules file and doesn't write the output\n# file, so removes the output file before running jarjar and check if it exists\n# after running jarjar.\ndefine transform-jarjar\necho $($(PRIVATE_PREFIX)DISPLAY) JarJar: $@\nrm -f $@\n$(JAVA) -jar $(JARJAR) process $(PRIVATE_JARJAR_RULES) $< $@\n[ -e $@ ] || (echo \"Missing output file\"; exit 1)\nendef\n\n# Moves $1.tmp to $1 if necessary. This is designed to be used with\n# .KATI_RESTAT. For kati, this function doesn't update the timestamp\n# of $1 when $1.tmp is identical to $1 so that ninja won't rebuild\n# targets which depend on $1.\ndefine commit-change-for-toc\n$(hide) if cmp -s $1.tmp $1 ; then \\\n rm $1.tmp ; \\\nelse \\\n mv $1.tmp $1 ; \\\nfi\nendef\n\nifeq (,$(TARGET_BUILD_APPS))\n\n## Rule to create a table of contents from a .dex file.\n## Must be called with $(eval).\n# $(1): The directory which contains classes*.dex files\ndefine _transform-dex-to-toc\n$1/classes.dex.toc: PRIVATE_INPUT_DEX_FILES := $1/classes*.dex\n$1/classes.dex.toc: $1/classes.dex $(DEXDUMP)\n\t@echo Generating TOC: $$@\n\t$(hide) ANDROID_LOG_TAGS=\"*:e\" $(DEXDUMP) -l xml $$(PRIVATE_INPUT_DEX_FILES) > $$@.tmp\n\t$$(call commit-change-for-toc,$$@)\nendef\n\n## Define a rule which generates .dex.toc and mark it as .KATI_RESTAT.\n# $(1): The directory which contains classes*.dex files\ndefine define-dex-to-toc-rule\n$(eval $(call _transform-dex-to-toc,$1))\\\n$(eval .KATI_RESTAT: $1/classes.dex.toc)\nendef\n\nelse\n\n# Turn off .toc optimization for apps build as we cannot build dexdump.\ndefine define-dex-to-toc-rule\nendef\n\nendif  # TARGET_BUILD_APPS\n\n\n# Takes an sdk version that might be PLATFORM_VERSION_CODENAME (for example P),\n# returns a number greater than the highest existing sdk version if it is, or\n# the input if it is not.\ndefine codename-or-sdk-to-sdk\n$(if $(filter $(1),$(PLATFORM_VERSION_CODENAME)),10000,$(1))\nendef\n\n# Uses LOCAL_SDK_VERSION and PLATFORM_SDK_VERSION to determine a compileSdkVersion\n# in the form of a number or a codename (28 or P)\ndefine module-sdk-version\n$(strip \\\n  $(if $(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION)), \\\n    $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)), \\\n    $(PLATFORM_SDK_VERSION)))\nendef\n\n# Uses LOCAL_SDK_VERSION and DEFAULT_APP_TARGET_SDK to determine\n# a targetSdkVersion in the form of a number or a codename (28 or P).\ndefine module-target-sdk-version\n$(strip \\\n  $(if $(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION)), \\\n    $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)), \\\n    $(DEFAULT_APP_TARGET_SDK)))\nendef\n\n# Uses LOCAL_MIN_SDK_VERSION, LOCAL_SDK_VERSION and DEFAULT_APP_TARGET_SDK to determine\n# a minSdkVersion in the form of a number or a codename (28 or P).\ndefine module-min-sdk-version\n$(if $(LOCAL_MIN_SDK_VERSION),$(LOCAL_MIN_SDK_VERSION),$(call module-target-sdk-version))\nendef\n\n# Checks if module is in vendor or product\ndefine module-in-vendor-or-product\n$(if $(filter true,$(LOCAL_IN_VENDOR) $(LOCAL_IN_PRODUCT)),true)\nendef\n\ndefine transform-classes.jar-to-dex\n@echo \"target Dex: $(PRIVATE_MODULE)\"\n@mkdir -p $(dir $@)tmp\n$(hide) rm -f $(dir $@)classes*.dex $(dir $@)d8_input.jar\n$(hide) $(ZIP2ZIP) -j -i $< -o $(dir $@)d8_input.jar \"**/*.class\"\n$(hide) $(D8_WRAPPER) $(D8_COMMAND) \\\n    --output $(dir $@)tmp \\\n    $(addprefix --lib ,$(PRIVATE_D8_LIBS)) \\\n    --min-api $(PRIVATE_MIN_SDK_VERSION) \\\n    $(subst --main-dex-list=, --main-dex-list , \\\n        $(filter-out --core-library --multi-dex --minimal-main-dex,$(PRIVATE_DX_FLAGS))) \\\n    $(dir $@)d8_input.jar\n$(hide) mv $(dir $@)tmp/* $(dir $@)\n$(hide) rm -f $(dir $@)d8_input.jar\n$(hide) rm -rf $(dir $@)tmp\nendef\n\n# We need the extra blank line, so that the command will be on a separate line.\n# $(1): the package\n# $(2): the ABI name\n# $(3): the list of shared libraies\ndefine _add-jni-shared-libs-to-package-per-abi\n$(hide) cp $(3) $(dir $(1))lib/$(2)\n\nendef\n\n# $(1): the package file\n# $(2): if true, uncompress jni libs\ndefine create-jni-shared-libs-package\nrm -rf $(dir $(1))lib\nmkdir -p $(addprefix $(dir $(1))lib/,$(PRIVATE_JNI_SHARED_LIBRARIES_ABI))\n$(foreach abi,$(PRIVATE_JNI_SHARED_LIBRARIES_ABI),\\\n  $(call _add-jni-shared-libs-to-package-per-abi,$(1),$(abi),\\\n    $(patsubst $(abi):%,%,$(filter $(abi):%,$(PRIVATE_JNI_SHARED_LIBRARIES)))))\n$(SOONG_ZIP) $(if $(2),-L 0) -o $(1) -C $(dir $(1)) -D $(dir $(1))lib\nrm -rf $(dir $(1))lib\nendef\n\n# $(1): the jar file.\n# $(2): the classes.dex file.\ndefine create-dex-jar\nfind $(dir $(2)) -maxdepth 1 -name \"classes*.dex\" | sort > $(1).lst\n$(SOONG_ZIP) -o $(1) -C $(dir $(2)) -l $(1).lst\nendef\n\n# Add java resources added by the current module to an existing package.\n# $(1) destination package.\ndefine add-java-resources-to\n  $(call _java-resources,$(1),u)\nendef\n\n# Add java resources added by the current module to a new jar.\n# $(1) destination jar.\ndefine create-java-resources-jar\n  $(call _java-resources,$(1),c)\nendef\n\ndefine _java-resources\n$(call dump-words-to-file, $(PRIVATE_EXTRA_JAR_ARGS), $(1).jar-arg-list)\n$(hide) $(JAR) $(2)f $(1) @$(1).jar-arg-list\n@rm -f $(1).jar-arg-list\nendef\n\n# Add resources (non .class files) from a jar to a package\n# $(1): the package file\n# $(2): the jar file\n# $(3): temporary directory\ndefine add-jar-resources-to-package\n  rm -rf $(3)\n  mkdir -p $(3)\n  zipinfo -1 $(2) > /dev/null\n  unzip -qo $(2) -d $(3) $$(zipinfo -1 $(2) | grep -v -E \"\\.class$$\")\n  $(JAR) uf $(1) $(call jar-args-sorted-files-in-directory,$(3))\nendef\n\n# $(1): the output resources jar.\n# $(2): the input jar\ndefine extract-resources-jar\n  $(ZIP2ZIP) -i $(2) -o $(1) -x '**/*.class' -x '**/*/'\nendef\n\n# Sign a package using the specified key/cert.\n#\ndefine sign-package\n$(call sign-package-arg,$@)\nendef\n\n# $(1): the package file we are signing.\ndefine sign-package-arg\n$(hide) mv $(1) $(1).unsigned\n$(hide) $(JAVA) -Djava.library.path=$$(dirname $(SIGNAPK_JNI_LIBRARY_PATH)) -jar $(SIGNAPK_JAR) \\\n    $(if $(strip $(PRIVATE_CERTIFICATE_LINEAGE)), --lineage $(PRIVATE_CERTIFICATE_LINEAGE)) \\\n    $(if $(strip $(PRIVATE_ROTATION_MIN_SDK_VERSION)), --rotation-min-sdk-version $(PRIVATE_ROTATION_MIN_SDK_VERSION)) \\\n    $(PRIVATE_CERTIFICATE) $(PRIVATE_PRIVATE_KEY) \\\n    $(PRIVATE_ADDITIONAL_CERTIFICATES) $(1).unsigned $(1).signed\n$(hide) mv $(1).signed $(1)\nendef\n\n# Align STORED entries of a package on 4-byte boundaries to make them easier to mmap.\n#\ndefine align-package\n$(hide) if ! $(ZIPALIGN) -c -p 4 $@ >/dev/null ; then \\\n  mv $@ $@.unaligned; \\\n  $(ZIPALIGN) \\\n    -f \\\n    -p \\\n    4 \\\n    $@.unaligned $@.aligned; \\\n  mv $@.aligned $@; \\\n  fi\nendef\n\n# Verifies ZIP alignment of a package.\n#\ndefine check-package-alignment\n$(hide) if ! $(ZIPALIGN) -c -p 4 $@ >/dev/null ; then \\\n    $(call echo-error,$@,Improper package alignment); \\\n    exit 1; \\\n  fi\nendef\n\n# Compress a package using the standard gzip algorithm.\ndefine compress-package\n$(hide) \\\n  mv $@ $@.uncompressed; \\\n  $(GZIP) -9 -c $@.uncompressed > $@.compressed; \\\n  rm -f $@.uncompressed; \\\n  mv $@.compressed $@;\nendef\n\nifeq ($(HOST_OS),linux)\n# Runs appcompat and store logs in $(PRODUCT_OUT)/appcompat\ndefine extract-package\n$(AAPT2) dump resources $@ | awk -F ' |=' '/^Package/{print $$3; exit}' >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log &&\nendef\ndefine appcompat-header\n$(hide) \\\n  mkdir -p $(PRODUCT_OUT)/appcompat && \\\n  rm -f $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \\\n  echo -n \"Package name: \" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \\\n  $(extract-package) \\\n  echo \"Module name in Android tree: $(PRIVATE_MODULE)\" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \\\n  echo \"Local path in Android tree: $(PRIVATE_PATH)\" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \\\n  echo \"Install path: $(patsubst $(PRODUCT_OUT)/%,%,$(PRIVATE_INSTALLED_MODULE))\" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \\\n  echo >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log\nendef\nART_VERIDEX_APPCOMPAT:=$(HOST_OUT)/bin/appcompat\ndefine run-appcompat\n$(hide) \\\n  echo \"appcompat output:\" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \\\n  ANDROID_LOG_TAGS=\"*:e\" $(ART_VERIDEX_APPCOMPAT) --dex-file=$@ 2>&1 >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log\nendef\nappcompat-files = \\\n  $(AAPT2) \\\n  $(ART_VERIDEX_APPCOMPAT) \\\nelse\nappcompat-header =\nrun-appcompat =\nappcompat-files =\nendif  # HOST_OS == linux\n.KATI_READONLY: appcompat-header run-appcompat appcompat-files\n\n# Remove dynamic timestamps from packages\n#\ndefine remove-timestamps-from-package\n$(hide) $(ZIPTIME) $@\nendef\n\n# Uncompress dex files embedded in an apk.\n#\ndefine uncompress-dexs\n  if (zipinfo $@ '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \\\n    $(ZIP2ZIP) -i $@ -o $@.tmp -0 \"classes*.dex\" && \\\n    mv -f $@.tmp $@ ; \\\n  fi\nendef\n\n# Uncompress shared JNI libraries embedded in an apk.\n#\ndefine uncompress-prebuilt-embedded-jni-libs\n  if (zipinfo $@ 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \\\n    $(ZIP2ZIP) -i $@ -o $@.tmp -0 'lib/**/*.so' && mv -f $@.tmp $@ ; \\\n  fi\nendef\n\n# Verifies shared JNI libraries and dex files in an apk are uncompressed.\n#\ndefine check-jni-dex-compression\n  if (zipinfo $@ 'lib/*.so' '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \\\n    $(call echo-error,$@,Contains compressed JNI libraries and/or dex files); \\\n    exit 1; \\\n  fi\nendef\n\n# Remove unwanted shared JNI libraries embedded in an apk.\n#\ndefine remove-unwanted-prebuilt-embedded-jni-libs\n  $(if $(PRIVATE_EMBEDDED_JNI_LIBS), \\\n    $(ZIP2ZIP) -i $@ -o $@.tmp \\\n      -x 'lib/**/*.so' $(addprefix -X ,$(PRIVATE_EMBEDDED_JNI_LIBS)) && \\\n    mv -f $@.tmp $@)\nendef\n\n# TODO(joeo): If we can ever upgrade to post 3.81 make and get the\n# new prebuilt rules to work, we should change this to copy the\n# resources to the out directory and then copy the resources.\n\n# Note: we intentionally don't clean PRIVATE_CLASS_INTERMEDIATES_DIR\n# in transform-java-to-classes for the sake of vm-tests.\ndefine transform-host-java-to-package\n@echo \"Host Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))\"\n$(call compile-java,$(HOST_JAVAC),$(PRIVATE_ALL_JAVA_LIBRARIES))\nendef\n\n# Note: we intentionally don't clean PRIVATE_CLASS_INTERMEDIATES_DIR\n# in transform-java-to-classes for the sake of vm-tests.\ndefine transform-host-java-to-dalvik-package\n@echo \"Dalvik Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))\"\n$(call compile-java,$(HOST_JAVAC),$(PRIVATE_ALL_JAVA_HEADER_LIBRARIES))\nendef\n\n###########################################################\n## Commands for copying files\n###########################################################\n\n# Define a rule to copy a header.  Used via $(eval) by copy_headers.make.\n# $(1): source header\n# $(2): destination header\ndefine copy-one-header\n$(2): $(1)\n\t@echo \"Header: $$@\"\n\t$$(copy-file-to-new-target-with-cp)\nendef\n\n# Define a rule to copy a file.  For use via $(eval).\n# $(1): source file\n# $(2): destination file\ndefine copy-one-file\n$(2): $(1)\n\t@echo \"Copy: $$@\"\n\t$$(copy-file-to-target)\nendef\n\n# Define a rule to copy a license metadata file. For use via $(eval).\n# $(1): source license metadata file\n# $(2): destination license metadata file\n# $(3): built targets\n# $(4): installed targets\ndefine copy-one-license-metadata-file\n$(2): PRIVATE_BUILT=$(3)\n$(2): PRIVATE_INSTALLED=$(4)\n$(2): $(1)\n\t@echo \"Copy: $$@\"\n\t$$(call copy-license-metadata-file-to-target,$$(PRIVATE_BUILT),$$(PRIVATE_INSTALLED))\nendef\n\ndefine copy-and-uncompress-dexs\n$(2): $(1) $(ZIPALIGN) $(ZIP2ZIP)\n\t@echo \"Uncompress dexs in: $$@\"\n\t$$(copy-file-to-target)\n\t$$(uncompress-dexs)\n\t$$(align-package)\nendef\n\n# Create copy pair for compatibility suite\n# Filter out $(LOCAL_INSTALLED_MODULE) to prevent overriding target\n# $(1): source path\n# $(2): destination path\n# The format of copy pair is src:dst\ndefine compat-copy-pair\n$(if $(filter-out $(2), $(LOCAL_INSTALLED_MODULE)), $(1):$(2))\nendef\n\n# Create copy pair for $(1) $(2)\n# If $(2) is substring of $(3) do nothing.\n# $(1): source path\n# $(2): destination path\n# $(3): filter-out target\n# The format of copy pair is src:dst\ndefine filter-copy-pair\n$(if $(findstring $(2), $(3)),,$(1):$(2))\nendef\n\n# Copies many files.\n# $(1): The files to copy.  Each entry is a ':' separated src:dst pair\n# $(2): An optional directory to prepend to the destination\n# Evaluates to the list of the dst files (ie suitable for a dependency list)\ndefine copy-many-files\n$(foreach f, $(1), $(strip \\\n    $(eval _cmf_tuple := $(subst :, ,$(f))) \\\n    $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \\\n    $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \\\n    $(if $(strip $(2)), \\\n      $(eval _cmf_dest := $(patsubst %/,%,$(strip $(2)))/$(patsubst /%,%,$(_cmf_dest)))) \\\n    $(if $(filter-out $(_cmf_src), $(_cmf_dest)), \\\n      $(eval $(call copy-one-file,$(_cmf_src),$(_cmf_dest)))) \\\n    $(_cmf_dest)))\nendef\n\n# Copy the file only if it's a well-formed init script file. For use via $(eval).\n# $(1): source file\n# $(2): destination file\ndefine copy-init-script-file-checked\nifdef TARGET_BUILD_UNBUNDLED\n# TODO (b/185624993): Remove the check on TARGET_BUILD_UNBUNDLED when host_init_verifier can run\n# without requiring the HIDL interface map.\n$(2): $(1)\nelse ifneq ($(HOST_OS),darwin)\n# Host init verifier doesn't exist on darwin.\n$(2): \\\n\t$(1) \\\n\t$(HOST_INIT_VERIFIER) \\\n\t$(call intermediates-dir-for,ETC,passwd_system)/passwd_system \\\n\t$(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \\\n\t$(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \\\n\t$(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \\\n\t$(call intermediates-dir-for,ETC,passwd_product)/passwd_product \\\n\t$(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \\\n\t$(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \\\n\t$(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \\\n\t$(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \\\n\t$(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts\n\t$(hide) $(HOST_INIT_VERIFIER) \\\n\t  -p $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \\\n\t  -p $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \\\n\t  -p $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \\\n\t  -p $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \\\n\t  -p $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \\\n\t  --property-contexts=$(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \\\n\t  --property-contexts=$(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \\\n\t  --property-contexts=$(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \\\n\t  --property-contexts=$(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \\\n\t  --property-contexts=$(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts \\\n\t  $$<\nelse\n$(2): $(1)\nendif\n\t@echo \"Copy init script: $$@\"\n\t$$(copy-file-to-target)\nendef\n\n# Copies many init script files and check they are well-formed.\n# $(1): The init script files to copy.  Each entry is a ':' separated src:dst pair.\ndefine copy-many-init-script-files-checked\n$(foreach f, $(1), $(strip \\\n    $(eval _cmf_tuple := $(subst :, ,$(f))) \\\n    $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \\\n    $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \\\n    $(eval $(call copy-init-script-file-checked,$(_cmf_src),$(_cmf_dest)))))\nendef\n\n# Copy the file only if it's a well-formed xml file. For use via $(eval).\n# $(1): source file\n# $(2): destination file, must end with .xml.\ndefine copy-xml-file-checked\n$(2): $(1) $(XMLLINT)\n\t@echo \"Copy xml: $$@\"\n\t$(hide) $(XMLLINT) $$< >/dev/null  # Don't print the xml file to stdout.\n\t$$(copy-file-to-target)\nendef\n\n# Copies many xml files and check they are well-formed.\n# $(1): The xml files to copy.  Each entry is a ':' separated src:dst pair.\n# Evaluates to the list of the dst files. (ie suitable for a dependency list.)\ndefine copy-many-xml-files-checked\n$(foreach f, $(1), $(strip \\\n    $(eval _cmf_tuple := $(subst :, ,$(f))) \\\n    $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \\\n    $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \\\n    $(eval $(call copy-xml-file-checked,$(_cmf_src),$(_cmf_dest))) \\\n    $(_cmf_dest)))\nendef\n\n# Copy the file only if it is a well-formed manifest file. For use viea $(eval)\n# $(1): source file\n# $(2): destination file\ndefine copy-vintf-manifest-checked\n$(2): $(1) $(HOST_OUT_EXECUTABLES)/assemble_vintf\n\t@echo \"Copy xml: $$@\"\n\t$(hide) mkdir -p \"$$(dir $$@)\"\n\t$(hide) VINTF_IGNORE_TARGET_FCM_VERSION=true\\\n\t\t$(HOST_OUT_EXECUTABLES)/assemble_vintf -i $$< -o $$@\nendef\n\n# Copies many vintf manifest files checked.\n# $(1): The files to copy.  Each entry is a ':' separated src:dst pair\ndefine copy-many-vintf-manifest-files-checked\n$(foreach f, $(1), $(strip \\\n    $(eval _cmf_tuple := $(subst :, ,$(f))) \\\n    $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \\\n    $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \\\n    $(eval $(call copy-vintf-manifest-checked,$(_cmf_src),$(_cmf_dest)))))\nendef\n\n# Copy the file only if it's not an ELF file. For use via $(eval).\n# $(1): source file\n# $(2): destination file\n# $(3): message to print on error\ndefine copy-non-elf-file-checked\n$(eval check_non_elf_file_timestamp := \\\n    $(call intermediates-dir-for,FAKE,check-non-elf-file-timestamps)/$(2).timestamp)\n$(check_non_elf_file_timestamp): $(1) $(LLVM_READOBJ)\n\t@echo \"Check non-ELF: $$<\"\n\t$(hide) mkdir -p \"$$(dir $$@)\"\n\t$(hide) rm -f \"$$@\"\n\t$(hide) \\\n\t    if $(LLVM_READOBJ) -h \"$$<\" 2>/dev/null | grep -q \"^Format: elf\"; then \\\n\t        $(call echo-error,$(2),$(3)); \\\n\t        $(call echo-error,$(2),found ELF file: $$<); \\\n\t        false; \\\n\t    fi\n\t$(hide) touch \"$$@\"\n\n$(2): $(1) $(check_non_elf_file_timestamp)\n\t@echo \"Copy non-ELF: $$@\"\n\t$$(copy-file-to-target)\n\ncheck-elf-prebuilt-product-copy-files: $(check_non_elf_file_timestamp)\nendef\n\n# The -t option to acp and the -p option to cp is\n# required for OSX.  OSX has a ridiculous restriction\n# where it's an error for a .a file's modification time\n# to disagree with an internal timestamp, and this\n# macro is used to install .a files (among other things).\n\n# Copy a single file from one place to another,\n# preserving permissions and overwriting any existing\n# file.\n# When we used acp, it could not handle high resolution timestamps\n# on file systems like ext4. Because of that, '-t' option was disabled\n# and copy-file-to-target was identical to copy-file-to-new-target.\n# Keep the behavior until we audit and ensure that switching this back\n# won't break anything.\ndefine copy-file-to-target\n@mkdir -p $(dir $@)\n$(hide) rm -f $@\n$(hide) cp \"$<\" \"$@\"\nendef\n\n# Same as copy-file-to-target, but assume file is a licenes metadata file,\n# and append built from $(1) and installed from $(2).\ndefine copy-license-metadata-file-to-target\n@mkdir -p $(dir $@)\n$(hide) rm -f $@\n$(hide) cp \"$<\" \"$@\" $(strip \\\n  $(foreach b,$(1), && (grep -F 'built: \"'\"$(b)\"'\"' \"$@\" >/dev/null || echo 'built: \"'\"$(b)\"'\"' >>\"$@\")) \\\n  $(foreach i,$(2), && (grep -F 'installed: \"'\"$(i)\"'\"' \"$@\" >/dev/null || echo 'installed: \"'\"$(i)\"'\"' >>\"$@\")) \\\n)\nendef\n\n# The same as copy-file-to-target, but use the local\n# cp command instead of acp.\ndefine copy-file-to-target-with-cp\n@mkdir -p $(dir $@)\n$(hide) rm -f $@\n$(hide) cp -p \"$<\" \"$@\"\nendef\n\n# The same as copy-file-to-target, but don't preserve\n# the old modification time.\ndefine copy-file-to-new-target\n@mkdir -p $(dir $@)\n$(hide) rm -f $@\n$(hide) cp $< $@\nendef\n\n# The same as copy-file-to-new-target, but use the local\n# cp command instead of acp.\ndefine copy-file-to-new-target-with-cp\n@mkdir -p $(dir $@)\n$(hide) rm -f $@\n$(hide) cp $< $@\nendef\n\n# The same as copy-file-to-new-target, but preserve symlinks. Symlinks are\n# converted to absolute to not break.\ndefine copy-file-or-link-to-new-target\n@mkdir -p $(dir $@)\n$(hide) rm -f $@\n$(hide) if [ -h $< ]; then \\\n  ln -s $$(realpath $<) $@; \\\nelse \\\n  cp $< $@; \\\nfi\nendef\n\n# Copy a prebuilt file to a target location.\ndefine transform-prebuilt-to-target\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) Prebuilt: $(PRIVATE_MODULE) ($@)\"\n$(copy-file-to-target)\nendef\n\n# Copy a prebuilt file to a target location, but preserve symlinks rather than\n# dereference them.\ndefine copy-or-link-prebuilt-to-target\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) Prebuilt: $(PRIVATE_MODULE) ($@)\"\n$(copy-file-or-link-to-new-target)\nendef\n\n# Copy a list of files/directories to target location, with sub dir structure preserved.\n# For example $(HOST_OUT_EXECUTABLES)/aapt -> $(staging)/bin/aapt .\n# $(1): the source list of files/directories.\n# $(2): the path prefix to strip. In the above example it would be $(HOST_OUT).\n# $(3): the target location.\ndefine copy-files-with-structure\n$(foreach t,$(1),\\\n  $(eval s := $(patsubst $(2)%,%,$(t)))\\\n  $(hide) mkdir -p $(dir $(3)/$(s)); cp -Rf $(t) $(3)/$(s)$(newline))\nendef\n\n# Define a rule to create a symlink to a file.\n# $(1): any dependencies\n# $(2): source (may be relative)\n# $(3): full path to destination\ndefine symlink-file\n$(eval $(_symlink-file))\n$(eval $(call declare-license-metadata,$(3),,,,,,))\n$(eval $(call declare-license-deps,$(3),$(1)))\nendef\n\ndefine _symlink-file\n$(3): $(1)\n\t@echo \"Symlink: $$@ -> $(2)\"\n\t@mkdir -p $$(dir $$@)\n\t@rm -rf $$@\n\t$(hide) ln -sf $(2) $$@\nendef\n\n# Copy an apk to a target location while removing classes*.dex\n# $(1): source file\n# $(2): destination file\n# $(3): LOCAL_STRIP_DEX, if non-empty then strip classes*.dex\ndefine dexpreopt-copy-jar\n$(2): $(1)\n\t@echo \"Copy: $$@\"\n\t$$(copy-file-to-target)\n\t$(if $(3),$$(call dexpreopt-remove-classes.dex,$$@))\nendef\n\n# $(1): the .jar or .apk to remove classes.dex. Note that if all dex files\n# are uncompressed in the archive, then dexopt will not do a copy of the dex\n# files and we should not strip.\ndefine dexpreopt-remove-classes.dex\n$(hide) if (zipinfo $1 '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \\\nzip --quiet --delete $(1) classes.dex; \\\ndex_index=2; \\\nwhile zip --quiet --delete $(1) classes$${dex_index}.dex > /dev/null; do \\\n  let dex_index=dex_index+1; \\\ndone \\\nfi\nendef\n\n# Copy an unstripped binary to the symbols directory while also extracting\n# a hash mapping to the mapping directory.\n# $(1): unstripped intermediates file\n# $(2): path in symbols directory\n# $(3): path in elf_symbol_mapping packaging directory\ndefine copy-unstripped-elf-file-with-mapping\n$(call _copy-symbols-file-with-mapping,$(1),$(2),elf,$(3))\nendef\n\n# Copy an R8 dictionary to the packaging directory while also extracting\n# a hash mapping to the mapping directory.\n# $(1): unstripped intermediates file\n# $(2): path in packaging directory\n# $(3): path in mappings packaging directory\ndefine copy-r8-dictionary-file-with-mapping\n$(call _copy-symbols-file-with-mapping,$(1),$(2),r8,$(3))\nendef\n\n# Copy an unstripped binary or R8 dictionary to the symbols directory\n# while also extracting a hash mapping to the mapping directory.\n# $(1): unstripped intermediates file\n# $(2): path in symbols directory\n# $(3): file type (elf or r8)\n# $(4): path in the mappings directory\n#\n# Regarding the restats at the end: I think you should only need to use KATI_RESTAT on $(2), but\n# there appears to be a bug in kati where it was not adding restat=true in the ninja file unless we\n# also added 4 to KATI_RESTAT.\ndefine _copy-symbols-file-with-mapping\n$(2): .KATI_IMPLICIT_OUTPUTS := $(4)\n$(2): $(SYMBOLS_MAP)\n$(2): $(1)\n\t@echo \"Copy symbols with mapping: $$@\"\n\t$$(copy-file-to-target)\n\t$(SYMBOLS_MAP) -$(strip $(3)) $(2) -write_if_changed $(4)\n.KATI_RESTAT: $(2)\n.KATI_RESTAT: $(4)\nendef\n\n\n###########################################################\n## Commands to call R8\n###########################################################\n\n# Use --debug flag for eng builds by default\nifeq (eng,$(TARGET_BUILD_VARIANT))\nR8_DEBUG_MODE := --debug\nelse\nR8_DEBUG_MODE :=\nendif\n\ndefine transform-jar-to-dex-r8\n@echo R8: $@\n$(hide) rm -f $(PRIVATE_PROGUARD_DICTIONARY)\n$(hide) $(R8_WRAPPER) $(R8_COMMAND) \\\n    -injars '$<' \\\n    --min-api $(PRIVATE_MIN_SDK_VERSION) \\\n    --no-data-resources \\\n    --force-proguard-compatibility --output $(subst classes.dex,,$@) \\\n    $(R8_DEBUG_MODE) \\\n    $(PRIVATE_PROGUARD_FLAGS) \\\n    $(addprefix -injars , $(PRIVATE_EXTRA_INPUT_JAR)) \\\n    $(PRIVATE_DX_FLAGS) \\\n    -ignorewarnings\n$(hide) touch $(PRIVATE_PROGUARD_DICTIONARY)\nendef\n\n###########################################################\n## Stuff source generated from one-off tools\n###########################################################\n\ndefine transform-generated-source\n@echo \"$($(PRIVATE_PREFIX)DISPLAY) Generated: $(PRIVATE_MODULE) <= $<\"\n@mkdir -p $(dir $@)\n$(hide) $(PRIVATE_CUSTOM_TOOL)\nendef\n\n\n###########################################################\n## Assertions about attributes of the target\n###########################################################\n\n# $(1): The file to check\ndefine get-file-size\nstat -c \"%s\" \"$(1)\" | tr -d '\\n'\nendef\n\n# $(1): The file(s) to check (often $@)\n# $(2): The partition size.\ndefine assert-max-image-size\n$(if $(2), \\\n  size=$$(for i in $(1); do $(call get-file-size,$$i); echo +; done; echo 0); \\\n  total=$$(( $$( echo \"$$size\" ) )); \\\n  printname=$$(echo -n \"$(1)\" | tr \" \" +); \\\n  maxsize=$$(($(2))); \\\n  if [ \"$$total\" -gt \"$$maxsize\" ]; then \\\n    echo \"error: $$printname too large ($$total > $$maxsize)\"; \\\n    false; \\\n  elif [ \"$$total\" -gt $$((maxsize - 32768)) ]; then \\\n    echo \"WARNING: $$printname approaching size limit ($$total now; limit $$maxsize)\"; \\\n  fi \\\n , \\\n  true \\\n )\nendef\n\n\n###########################################################\n## Define device-specific radio files\n###########################################################\nINSTALLED_RADIOIMAGE_TARGET :=\n\n# Copy a radio image file to the output location, and add it to\n# INSTALLED_RADIOIMAGE_TARGET.\n# $(1): filename\ndefine add-radio-file\n  $(eval $(call add-radio-file-internal,$(1),$(notdir $(1))))\nendef\ndefine add-radio-file-internal\nINSTALLED_RADIOIMAGE_TARGET += $$(PRODUCT_OUT)/$(2)\n$$(PRODUCT_OUT)/$(2) : $$(LOCAL_PATH)/$(1)\n\t$$(transform-prebuilt-to-target)\nendef\n\n# Version of add-radio-file that also arranges for the version of the\n# file to be checked against the contents of\n# $(TARGET_BOARD_INFO_FILE).\n# $(1): filename\n# $(2): name of version variable in board-info (eg, \"version-baseband\")\ndefine add-radio-file-checked\n  $(eval $(call add-radio-file-checked-internal,$(1),$(notdir $(1)),$(2)))\nendef\ndefine add-radio-file-checked-internal\nINSTALLED_RADIOIMAGE_TARGET += $$(PRODUCT_OUT)/$(2)\nBOARD_INFO_CHECK += $(3):$(LOCAL_PATH)/$(1)\n$$(PRODUCT_OUT)/$(2) : $$(LOCAL_PATH)/$(1)\n\t$$(transform-prebuilt-to-target)\nendef\n\n## Whether to build from source if prebuilt alternative exists\n###########################################################\n# $(1): module name\n# $(2): LOCAL_PATH\n# Expands to empty string if not from source.\nifeq (true,$(ANDROID_BUILD_FROM_SOURCE))\ndefine if-build-from-source\ntrue\nendef\nelse\ndefine if-build-from-source\n$(if $(filter $(ANDROID_NO_PREBUILT_MODULES),$(1))$(filter \\\n    $(addsuffix %,$(ANDROID_NO_PREBUILT_PATHS)),$(2)),true)\nendef\nendif\n\n# Include makefile $(1) if build from source for module $(2)\n# $(1): the makefile to include\n# $(2): module name\n# $(3): LOCAL_PATH\ndefine include-if-build-from-source\n$(if $(call if-build-from-source,$(2),$(3)),$(eval include $(1)))\nendef\n\n# Return the arch for the source file of a prebuilt\n# Return \"none\" if no matching arch found and return empty\n# if the input is empty, so the result can be passed to\n# LOCAL_MODULE_TARGET_ARCH.\n# $(1) the list of archs supported by the prebuilt\ndefine get-prebuilt-src-arch\n$(strip $(if $(filter $(TARGET_ARCH),$(1)),$(TARGET_ARCH),\\\n  $(if $(filter $(TARGET_2ND_ARCH),$(1)),$(TARGET_2ND_ARCH),$(if $(1),none))))\nendef\n\n# ###############################################################\n# Set up statistics gathering\n# ###############################################################\nSTATS.MODULE_TYPE := \\\n  HOST_STATIC_LIBRARY \\\n  HOST_SHARED_LIBRARY \\\n  STATIC_LIBRARY \\\n  SHARED_LIBRARY \\\n  EXECUTABLE \\\n  HOST_EXECUTABLE \\\n  PACKAGE \\\n  PHONY_PACKAGE \\\n  HOST_PREBUILT \\\n  PREBUILT \\\n  MULTI_PREBUILT \\\n  JAVA_LIBRARY \\\n  STATIC_JAVA_LIBRARY \\\n  HOST_JAVA_LIBRARY \\\n  DROIDDOC \\\n  COPY_HEADERS \\\n  NATIVE_TEST \\\n  NATIVE_BENCHMARK \\\n  HOST_NATIVE_TEST \\\n  FUZZ_TEST \\\n  HOST_FUZZ_TEST \\\n  STATIC_TEST_LIBRARY \\\n  HOST_STATIC_TEST_LIBRARY \\\n  NOTICE_FILE \\\n  base_rules \\\n  HEADER_LIBRARY \\\n  HOST_TEST_CONFIG \\\n  TARGET_TEST_CONFIG\n\n$(foreach s,$(STATS.MODULE_TYPE),$(eval STATS.MODULE_TYPE.$(s) :=))\ndefine record-module-type\n$(strip $(if $(LOCAL_RECORDED_MODULE_TYPE),,\n  $(if $(filter-out $(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)),\n    $(if $(filter $(1),$(STATS.MODULE_TYPE)),\n      $(eval LOCAL_RECORDED_MODULE_TYPE := true)\n        $(eval STATS.MODULE_TYPE.$(1) += 1),\n      $(error Invalid module type: $(1))))))\nendef\n\n###########################################################\n## Compatibility suite tools\n###########################################################\n\n# Return a list of output directories for a given suite and the current LOCAL_MODULE.\n# Can be passed a subdirectory to use for the common testcase directory.\ndefine compatibility_suite_dirs\n  $(strip \\\n    $(if $(COMPATIBILITY_TESTCASES_OUT_$(1)), \\\n      $(if $(COMPATIBILITY_TESTCASES_OUT_INCLUDE_MODULE_FOLDER_$(1))$(LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY),\\\n        $(COMPATIBILITY_TESTCASES_OUT_$(1))/$(LOCAL_MODULE)$(2),\\\n        $(COMPATIBILITY_TESTCASES_OUT_$(1)))) \\\n    $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)$(2))\nendef\n\n# For each suite:\n# 1. Copy the files to the many suite output directories.\n#    And for test config files, we'll check the .xml is well-formed before copy.\n# 2. Add all the files to each suite's dependent files list.\n# 3. Do the dependency addition to my_all_targets.\n# 4. Save the module name to COMPATIBILITY.$(suite).MODULES for each suite.\n# 5. Collect files to dist to ALL_COMPATIBILITY_DIST_FILES.\n# Requires for each suite: use my_compat_dist_config_$(suite) to define the test config.\n#    and use my_compat_dist_$(suite) to define the others.\ndefine create-suite-dependencies\n$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n  $(eval $(if $(strip $(module_license_metadata)),\\\n    $$(foreach f,$$(my_compat_dist_$(suite)),$$(call declare-copy-target-license-metadata,$$(call word-colon,2,$$(f)),$$(call word-colon,1,$$(f)))),\\\n    $$(eval my_test_data += $$(my_compat_dist_$(suite))) \\\n  )) \\\n  $(eval $(if $(strip $(module_license_metadata)),\\\n    $$(foreach f,$$(my_compat_dist_config_$(suite)),$$(call declare-copy-target-license-metadata,$$(call word-colon,2,$$(f)),$$(call word-colon,1,$$(f)))),\\\n    $$(eval my_test_config += $$(my_compat_dist_config_$(suite))) \\\n  )) \\\n  $(if $(filter $(suite),$(ALL_COMPATIBILITY_SUITES)),,\\\n    $(eval ALL_COMPATIBILITY_SUITES += $(suite)) \\\n    $(eval COMPATIBILITY.$(suite).FILES :=) \\\n    $(eval COMPATIBILITY.$(suite).MODULES :=) \\\n    $(eval COMPATIBILITY.$(suite).API_MAP_FILES :=)) \\\n  $(eval COMPATIBILITY.$(suite).FILES += \\\n    $$(foreach f,$$(my_compat_dist_$(suite)),$$(call word-colon,2,$$(f))) \\\n    $$(foreach f,$$(my_compat_dist_config_$(suite)),$$(call word-colon,2,$$(f))) \\\n    $$(my_compat_dist_test_data_$(suite))) \\\n  $(eval COMPATIBILITY.$(suite).ARCH_DIRS.$(my_register_name) := $(my_compat_module_arch_dir_$(suite).$(my_register_name))) \\\n  $(eval COMPATIBILITY.$(suite).API_MAP_FILES += $$(my_compat_api_map_$(suite))) \\\n  $(eval COMPATIBILITY.$(suite).SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES += $(LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) \\\n  $(eval ALL_COMPATIBILITY_DIST_FILES += $$(my_compat_dist_$(suite))) \\\n  $(eval COMPATIBILITY.$(suite).MODULES += $$(my_register_name))) \\\n$(eval $(my_all_targets) : \\\n  $(sort $(foreach suite,$(LOCAL_COMPATIBILITY_SUITE), \\\n    $(foreach f,$(my_compat_dist_$(suite)), $(call word-colon,2,$(f))))) \\\n  $(call copy-many-xml-files-checked, \\\n    $(sort $(foreach suite,$(LOCAL_COMPATIBILITY_SUITE),$(my_compat_dist_config_$(suite))))))\nendef\n\n# Define symbols.zip and symbols-mapping.textproto build rule per test suite\n#\n# $(1): Name of the test suite to create the zip and mapping build rules\ndefine create-suite-symbols-map\n_suite_symbols_zip := $$(subst -tests-,-tests_-,$$(PRODUCT_OUT)/$(1)-symbols.zip)\n_suite_symbols_mapping := $$(subst -tests-,-tests_-,$$(PRODUCT_OUT)/$(1)-symbols-mapping.textproto)\n_suite_modules_symbols_files := $$(foreach m,$$(COMPATIBILITY.$(1).MODULES),$$(ALL_MODULES.$$(m).SYMBOLIC_OUTPUT_PATH))\n_suite_modules_mapping_files := $$(foreach m,$$(COMPATIBILITY.$(1).MODULES),$$(ALL_MODULES.$$(m).ELF_SYMBOL_MAPPING_PATH))\n\n$$(_suite_symbols_zip): PRIVATE_SUITE_SYMBOLS_MAPPING := $$(_suite_symbols_mapping)\n$$(_suite_symbols_zip): PRIVATE_SUITE_MODULES_SYMBOLS_FILES := $$(_suite_modules_symbols_files)\n$$(_suite_symbols_zip): PRIVATE_SUITE_MODULES_MAPPING_FILES := $$(_suite_modules_mapping_files)\n$$(_suite_symbols_zip): $$(SOONG_ZIP) $$(SYMBOLS_MAP) $$(_suite_modules_symbols_files) $$(_suite_modules_mapping_files)\n\t@echo \"Package $(1) symbols: $$@\"\n\t$(hide) rm -rf $$@ $$@.symbols_list $$@.mapping_list\n\techo \"$$(PRIVATE_SUITE_MODULES_SYMBOLS_FILES)\" | tr \" \" \"\\n\" | sort > $$@.symbols_list\n\t$(hide) $$(SOONG_ZIP) -d -o $$@ -l $$@.symbols_list\n\techo \"$$(PRIVATE_SUITE_MODULES_MAPPING_FILES)\" | tr \" \" \"\\n\" | sort > $$@.mapping_list\n\t$(hide) $$(SYMBOLS_MAP) -merge $$(PRIVATE_SUITE_SYMBOLS_MAPPING) @$$@.mapping_list\n$$(_suite_symbols_zip): .KATI_IMPLICIT_OUTPUTS := $$(_suite_symbols_mapping)\n\n.PHONY: $(1)\n$(1): $$(_suite_symbols_zip) $$(_suite_symbols_mapping)\n$$(call dist-for-goals-with-filenametag,$(1), $$(_suite_symbols_zip) $$(_suite_symbols_mapping))\nendef\n\n###########################################################\n## Path Cleaning\n###########################################################\n\n# Remove \"dir ..\" combinations (but keep \".. ..\")\n#\n# $(1): The expanded path, where / is converted to ' ' to work with $(word)\ndefine _clean-path-strip-dotdot\n$(strip \\\n  $(if $(word 2,$(1)),\n    $(if $(call streq,$(word 2,$(1)),..),\n      $(if $(call streq,$(word 1,$(1)),..),\n        $(word 1,$(1)) $(call _clean-path-strip-dotdot,$(wordlist 2,$(words $(1)),$(1)))\n      ,\n        $(call _clean-path-strip-dotdot,$(wordlist 3,$(words $(1)),$(1)))\n      )\n    ,\n      $(word 1,$(1)) $(call _clean-path-strip-dotdot,$(wordlist 2,$(words $(1)),$(1)))\n    )\n  ,\n    $(1)\n  )\n)\nendef\n\n# Remove any leading .. from the path (in case of /..)\n#\n# Should only be called if the original path started with /\n# $(1): The expanded path, where / is converted to ' ' to work with $(word)\ndefine _clean-path-strip-root-dotdots\n$(strip $(if $(call streq,$(firstword $(1)),..),\n  $(call _clean-path-strip-root-dotdots,$(wordlist 2,$(words $(1)),$(1))),\n  $(1)))\nendef\n\n# Call _clean-path-strip-dotdot until the path stops changing\n# $(1): Non-empty if this path started with a /\n# $(2): The expanded path, where / is converted to ' ' to work with $(word)\ndefine _clean-path-expanded\n$(strip \\\n  $(eval _ep := $(call _clean-path-strip-dotdot,$(2)))\n  $(if $(1),$(eval _ep := $(call _clean-path-strip-root-dotdots,$(_ep))))\n  $(if $(call streq,$(2),$(_ep)),\n    $(_ep),\n    $(call _clean-path-expanded,$(1),$(_ep))))\nendef\n\n# Clean the file path -- remove //, dir/.., extra .\n#\n# This should be the same semantics as golang's filepath.Clean\n#\n# $(1): The file path to clean\ndefine clean-path\n$(strip \\\n  $(if $(call streq,$(words $(1)),1),\n    $(eval _rooted := $(filter /%,$(1)))\n    $(eval _expanded_path := $(filter-out .,$(subst /,$(space),$(1))))\n    $(eval _path := $(if $(_rooted),/)$(subst $(space),/,$(call _clean-path-expanded,$(_rooted),$(_expanded_path))))\n    $(if $(_path),\n      $(_path),\n      .\n     )\n  ,\n    $(if $(call streq,$(words $(1)),0),\n      .,\n      $(error Call clean-path with only one path (without spaces))\n    )\n  )\n)\nendef\n\nifeq ($(TEST_MAKE_clean_path),true)\n  define my_test\n    $(if $(call streq,$(call clean-path,$(1)),$(2)),,\n      $(eval my_failed := true)\n      $(warning clean-path test '$(1)': expected '$(2)', got '$(call clean-path,$(1))'))\n  endef\n  my_failed :=\n\n  # Already clean\n  $(call my_test,abc,abc)\n  $(call my_test,abc/def,abc/def)\n  $(call my_test,a/b/c,a/b/c)\n  $(call my_test,.,.)\n  $(call my_test,..,..)\n  $(call my_test,../..,../..)\n  $(call my_test,../../abc,../../abc)\n  $(call my_test,/abc,/abc)\n  $(call my_test,/,/)\n\n  # Empty is current dir\n  $(call my_test,,.)\n\n  # Remove trailing slash\n  $(call my_test,abc/,abc)\n  $(call my_test,abc/def/,abc/def)\n  $(call my_test,a/b/c/,a/b/c)\n  $(call my_test,./,.)\n  $(call my_test,../,..)\n  $(call my_test,../../,../..)\n  $(call my_test,/abc/,/abc)\n\n  # Remove doubled slash\n  $(call my_test,abc//def//ghi,abc/def/ghi)\n  $(call my_test,//abc,/abc)\n  $(call my_test,///abc,/abc)\n  $(call my_test,//abc//,/abc)\n  $(call my_test,abc//,abc)\n\n  # Remove . elements\n  $(call my_test,abc/./def,abc/def)\n  $(call my_test,/./abc/def,/abc/def)\n  $(call my_test,abc/.,abc)\n\n  # Remove .. elements\n  $(call my_test,abc/def/ghi/../jkl,abc/def/jkl)\n  $(call my_test,abc/def/../ghi/../jkl,abc/jkl)\n  $(call my_test,abc/def/..,abc)\n  $(call my_test,abc/def/../..,.)\n  $(call my_test,/abc/def/../..,/)\n  $(call my_test,abc/def/../../..,..)\n  $(call my_test,/abc/def/../../..,/)\n  $(call my_test,abc/def/../../../ghi/jkl/../../../mno,../../mno)\n  $(call my_test,/../abc,/abc)\n\n  # Combinations\n  $(call my_test,abc/./../def,def)\n  $(call my_test,abc//./../def,def)\n  $(call my_test,abc/../../././../def,../../def)\n\n  ifdef my_failed\n    $(error failed clean-path test)\n  endif\nendif\n\n###########################################################\n## Given a filepath, returns nonempty if the path cannot be\n## validated to be contained in the current directory\n## This is, this function checks for '/' and '..'\n##\n## $(1): path to validate\ndefine try-validate-path-is-subdir\n$(strip \\\n    $(if $(filter /%,$(1)),\n        $(1) starts with a slash\n    )\n    $(if $(filter ../%,$(call clean-path,$(1))),\n        $(1) escapes its parent using '..'\n    )\n    $(if $(strip $(1)),\n    ,\n        '$(1)' is empty\n    )\n)\nendef\n\ndefine validate-path-is-subdir\n$(if $(call try-validate-path-is-subdir,$(1)),\n  $(call pretty-error, Illegal path: $(call try-validate-path-is-subdir,$(1)))\n)\nendef\n\n###########################################################\n## Given a space-delimited list of filepaths, returns\n## nonempty if any cannot be validated to be contained in\n## the current directory\n##\n## $(1): path list to validate\ndefine try-validate-paths-are-subdirs\n$(strip \\\n  $(foreach my_path,$(1),\\\n    $(call try-validate-path-is-subdir,$(my_path))\\\n  )\n)\nendef\n\ndefine validate-paths-are-subdirs\n$(if $(call try-validate-paths-are-subdirs,$(1)),\n    $(call pretty-error,Illegal paths:\\'$(call try-validate-paths-are-subdirs,$(1))\\')\n)\nendef\n\n###########################################################\n## Tests of try-validate-path-is-subdir\n##     and  try-validate-paths-are-subdirs\ndefine test-validate-paths-are-subdirs\n$(eval my_error := $(call try-validate-path-is-subdir,/tmp)) \\\n$(if $(call streq,$(my_error),/tmp starts with a slash),\n,\n  $(error incorrect error message for path /tmp. Got '$(my_error)')\n) \\\n$(eval my_error := $(call try-validate-path-is-subdir,../sibling)) \\\n$(if $(call streq,$(my_error),../sibling escapes its parent using '..'),\n,\n  $(error incorrect error message for path ../sibling. Got '$(my_error)')\n) \\\n$(eval my_error := $(call try-validate-path-is-subdir,child/../../sibling)) \\\n$(if $(call streq,$(my_error),child/../../sibling escapes its parent using '..'),\n,\n  $(error incorrect error message for path child/../../sibling. Got '$(my_error)')\n) \\\n$(eval my_error := $(call try-validate-path-is-subdir,)) \\\n$(if $(call streq,$(my_error),'' is empty),\n,\n  $(error incorrect error message for empty path ''. Got '$(my_error)')\n) \\\n$(eval my_error := $(call try-validate-path-is-subdir,subdir/subsubdir)) \\\n$(if $(call streq,$(my_error),),\n,\n  $(error rejected valid path 'subdir/subsubdir'. Got '$(my_error)')\n)\n\n$(eval my_error := $(call try-validate-paths-are-subdirs,a/b /c/d e/f))\n$(if $(call streq,$(my_error),/c/d starts with a slash),\n,\n  $(error incorrect error message for path list 'a/b /c/d e/f'. Got '$(my_error)')\n)\n$(eval my_error := $(call try-validate-paths-are-subdirs,a/b c/d))\n$(if $(call streq,$(my_error),),\n,\n  $(error rejected valid path list 'a/b c/d'. Got '$(my_error)')\n)\nendef\n# run test\n$(strip $(call test-validate-paths-are-subdirs))\n\n###########################################################\n## Validate jacoco class filters and convert them to\n## file arguments\n## Jacoco class filters are comma-separated lists of class\n## files (android.app.Application), and may have '*' as the\n## last character to match all classes in a package\n## including subpackages.\ndefine jacoco-class-filter-to-file-args\n$(strip $(call jacoco-validate-file-args,\\\n  $(subst $(comma),$(space),\\\n    $(subst .,/,\\\n      $(strip $(1))))))\nendef\n\ndefine jacoco-validate-file-args\n$(strip $(1)\\\n  $(call validate-paths-are-subdirs,$(1))\n  $(foreach arg,$(1),\\\n    $(if $(findstring ?,$(arg)),$(call pretty-error,\\\n      '?' filters are not supported in LOCAL_JACK_COVERAGE_INCLUDE_FILTER or LOCAL_JACK_COVERAGE_EXCLUDE_FILTER))\\\n    $(if $(findstring *,$(patsubst %*,%,$(arg))),$(call pretty-error,\\\n      '*' is only supported at the end of a filter in LOCAL_JACK_COVERAGE_INCLUDE_FILTER or LOCAL_JACK_COVERAGE_EXCLUDE_FILTER))\\\n  ))\nendef\n\n###########################################################\n## Other includes\n###########################################################\n\n# Include any vendor specific definitions.mk file\n-include $(TOPDIR)vendor/*/build/core/definitions.mk\n-include $(TOPDIR)device/*/build/core/definitions.mk\n-include $(TOPDIR)product/*/build/core/definitions.mk\n# Also the project-specific definitions.mk file\n-include $(TOPDIR)vendor/*/*/build/core/definitions.mk\n-include $(TOPDIR)device/*/*/build/core/definitions.mk\n-include $(TOPDIR)product/*/*/build/core/definitions.mk\n\n# broken:\n#\t$(foreach file,$^,$(if $(findstring,.a,$(suffix $file)),-l$(file),$(file)))\n\n###########################################################\n## Misc notes\n###########################################################\n\n#DEPDIR = .deps\n#df = $(DEPDIR)/$(*F)\n\n#SRCS = foo.c bar.c ...\n\n#%.o : %.c\n#\t@$(MAKEDEPEND); \\\n#\t  cp $(df).d $(df).P; \\\n#\t  sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\\\$$//' \\\n#\t      -e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \\\n#\t  rm -f $(df).d\n#\t$(COMPILE.c) -o $@ $<\n\n#-include $(SRCS:%.c=$(DEPDIR)/%.P)\n\n\n#%.o : %.c\n#\t$(COMPILE.c) -MD -o $@ $<\n#\t@cp $*.d $*.P; \\\n#\t  sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\\\$$//' \\\n#\t      -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \\\n#\t  rm -f $*.d\n\n\n###########################################################\n# Append the information to generate a RRO package for the\n# source module.\n#\n#  $(1): Source module name.\n#  $(2): Whether $(3) is a manifest package name or not.\n#  $(3): Manifest package name if $(2) is true.\n#        Otherwise, android manifest file path of the\n#        source module.\n#  $(4): Whether LOCAL_EXPORT_PACKAGE_RESOURCES is set or\n#        not for the source module.\n#  $(5): Resource overlay list.\n#  $(6): Target partition\n###########################################################\ndefine append_enforce_rro_sources\n  $(eval ENFORCE_RRO_SOURCES += \\\n      $(strip $(1))||$(strip $(2))||$(strip $(3))||$(strip $(4))||$(call normalize-path-list, $(strip $(5)))||$(strip $(6)) \\\n  )\nendef\n\n###########################################################\n# Generate all RRO packages for source modules stored in\n# ENFORCE_RRO_SOURCES\n###########################################################\ndefine generate_all_enforce_rro_packages\n$(foreach source,$(ENFORCE_RRO_SOURCES), \\\n  $(eval _o := $(subst ||,$(space),$(source))) \\\n  $(eval enforce_rro_source_module := $(word 1,$(_o))) \\\n  $(eval enforce_rro_source_is_manifest_package_name := $(word 2,$(_o))) \\\n  $(eval enforce_rro_source_manifest_package_info := $(word 3,$(_o))) \\\n  $(eval enforce_rro_use_res_lib := $(word 4,$(_o))) \\\n  $(eval enforce_rro_source_overlays := $(subst :, ,$(word 5,$(_o)))) \\\n  $(eval enforce_rro_partition := $(word 6,$(_o))) \\\n  $(eval include $(BUILD_SYSTEM)/generate_enforce_rro.mk) \\\n  $(eval ALL_MODULES.$$(enforce_rro_source_module).REQUIRED_FROM_TARGET += $$(LOCAL_PACKAGE_NAME)) \\\n)\nendef\n\n###########################################################\n## Find system_$(VER) in LOCAL_SDK_VERSION\n## note: system_server_* is excluded. It's a different API surface\n##\n## $(1): LOCAL_SDK_VERSION\n###########################################################\ndefine has-system-sdk-version\n$(filter-out system_server_%,$(filter system_%,$(1)))\nendef\n\n###########################################################\n## Get numerical version in LOCAL_SDK_VERSION\n##\n## $(1): LOCAL_SDK_VERSION\n###########################################################\ndefine get-numeric-sdk-version\n$(filter-out current,\\\n  $(if $(call has-system-sdk-version,$(1)),$(patsubst system_%,%,$(1)),$(1)))\nendef\n\n###########################################################\n## Verify module name meets character requirements:\n##   a-z A-Z 0-9\n##   _.+-,@~\n##\n## This is a subset of bazel's target name restrictions:\n##   https://docs.bazel.build/versions/master/build-ref.html#name\n##\n## Kati has problems with '=': https://github.com/google/kati/issues/138\n###########################################################\ndefine verify-module-name\n$(if $(filter-out $(LOCAL_MODULE),$(subst /,,$(LOCAL_MODULE))), \\\n  $(call pretty-warning,Module name contains a /$(comma) use LOCAL_MODULE_STEM and LOCAL_MODULE_RELATIVE_PATH instead)) \\\n$(if $(call _invalid-name-chars,$(LOCAL_MODULE)), \\\n  $(call pretty-error,Invalid characters in module name: $(call _invalid-name-chars,$(LOCAL_MODULE))))\nendef\ndefine _invalid-name-chars\n$(subst _,,$(subst .,,$(subst +,,$(subst -,,$(subst $(comma),,$(subst @,,$(subst ~,,$(subst 0,,$(subst 1,,$(subst 2,,$(subst 3,,$(subst 4,,$(subst 5,,$(subst 6,,$(subst 7,,$(subst 8,,$(subst 9,,$(subst a,,$(subst b,,$(subst c,,$(subst d,,$(subst e,,$(subst f,,$(subst g,,$(subst h,,$(subst i,,$(subst j,,$(subst k,,$(subst l,,$(subst m,,$(subst n,,$(subst o,,$(subst p,,$(subst q,,$(subst r,,$(subst s,,$(subst t,,$(subst u,,$(subst v,,$(subst w,,$(subst x,,$(subst y,,$(subst z,,$(call to-lower,$(1)))))))))))))))))))))))))))))))))))))))))))))\nendef\n.KATI_READONLY := verify-module-name _invalid-name-chars\n\n###########################################################\n## Verify module stem meets character requirements:\n##   a-z A-Z 0-9\n##   _.+-,@~\n##\n## This is a subset of bazel's target name restrictions:\n##   https://docs.bazel.build/versions/master/build-ref.html#name\n##\n## $(1): The module stem variable to check\n###########################################################\ndefine verify-module-stem\n$(if $(filter-out $($(1)),$(subst /,,$($(1)))), \\\n  $(call pretty-warning,Module stem \\($(1)\\) contains a /$(comma) use LOCAL_MODULE_RELATIVE_PATH instead)) \\\n$(if $(call _invalid-name-chars,$($(1))), \\\n  $(call pretty-error,Invalid characters in module stem \\($(1)\\): $(call _invalid-name-chars,$($(1)))))\nendef\n.KATI_READONLY := verify-module-stem\n\n$(KATI_obsolete_var \\\n  create-empty-package \\\n  initialize-package-file \\\n  add-jni-shared-libs-to-package \\\n  inherit-package,\\\n  These functions have been removed)\n\n###########################################################\n## Verify the variants of a VNDK library are identical\n##\n## $(1): Path to the core variant shared library file.\n## $(2): Path to the vendor variant shared library file.\n## $(3): TOOLS_PREFIX\n###########################################################\nLIBRARY_IDENTITY_CHECK_SCRIPT := build/make/tools/check_identical_lib.sh\ndefine verify-vndk-libs-identical\n@echo \"Checking VNDK vendor variant: $(2)\"\n$(hide) CLANG_BIN=\"$(LLVM_PREBUILTS_PATH)\" \\\n  CROSS_COMPILE=\"$(strip $(3))\" \\\n  XZ=\"$(XZ)\" \\\n  $(LIBRARY_IDENTITY_CHECK_SCRIPT) $(SOONG_STRIP_PATH) $(1) $(2)\nendef\n\n# Convert Soong libraries that have SDK variant\ndefine use_soong_sdk_libraries\n  $(foreach l,$(1),$(if $(filter $(l),$(SOONG_SDK_VARIANT_MODULES)),\\\n      $(l).sdk,$(l)))\nendef\n"
  },
  {
    "path": "core/deprecation.mk",
    "content": "# These module types can still be used without warnings or errors.\nAVAILABLE_BUILD_MODULE_TYPES :=$= \\\n  BUILD_EXECUTABLE \\\n  BUILD_FUZZ_TEST \\\n  BUILD_HEADER_LIBRARY \\\n  BUILD_HOST_JAVA_LIBRARY \\\n  BUILD_HOST_PREBUILT \\\n  BUILD_JAVA_LIBRARY \\\n  BUILD_MULTI_PREBUILT \\\n  BUILD_NATIVE_TEST \\\n  BUILD_NOTICE_FILE \\\n  BUILD_PACKAGE \\\n  BUILD_PHONY_PACKAGE \\\n  BUILD_PREBUILT \\\n  BUILD_RRO_PACKAGE \\\n  BUILD_SHARED_LIBRARY \\\n  BUILD_STATIC_JAVA_LIBRARY \\\n  BUILD_STATIC_LIBRARY \\\n\n# These are BUILD_* variables that will throw a warning when used. This is\n# generally a temporary state until all the devices are marked with the\n# relevant BUILD_BROKEN_USES_BUILD_* variables, then these would move to\n# DEFAULT_ERROR_BUILD_MODULE_TYPES.\nDEFAULT_WARNING_BUILD_MODULE_TYPES :=$= \\\n\n# These are BUILD_* variables that are errors to reference, but you can set\n# BUILD_BROKEN_USES_BUILD_* in your BoardConfig.mk in order to turn them back\n# to warnings.\nDEFAULT_ERROR_BUILD_MODULE_TYPES :=$= \\\n  BUILD_COPY_HEADERS \\\n  BUILD_HOST_EXECUTABLE \\\n  BUILD_HOST_SHARED_LIBRARY \\\n  BUILD_HOST_STATIC_LIBRARY \\\n\n# These are BUILD_* variables that are always errors to reference.\n# Setting the BUILD_BROKEN_USES_BUILD_* variables is also an error.\nOBSOLETE_BUILD_MODULE_TYPES :=$= \\\n  BUILD_AUX_EXECUTABLE \\\n  BUILD_AUX_STATIC_LIBRARY \\\n  BUILD_HOST_DALVIK_JAVA_LIBRARY \\\n  BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY \\\n  BUILD_HOST_FUZZ_TEST \\\n  BUILD_HOST_NATIVE_TEST \\\n  BUILD_HOST_SHARED_TEST_LIBRARY \\\n  BUILD_HOST_STATIC_TEST_LIBRARY \\\n  BUILD_HOST_TEST_CONFIG \\\n  BUILD_NATIVE_BENCHMARK \\\n  BUILD_SHARED_TEST_LIBRARY \\\n  BUILD_STATIC_TEST_LIBRARY \\\n  BUILD_TARGET_TEST_CONFIG \\\n\n$(foreach m,$(OBSOLETE_BUILD_MODULE_TYPES),\\\n  $(KATI_obsolete_var $(m),Please convert to Soong) \\\n  $(KATI_obsolete_var BUILD_BROKEN_USES_$(m),Please convert to Soong))\n\n"
  },
  {
    "path": "core/dex_preopt.mk",
    "content": "####################################\n# dexpreopt support - typically used on user builds to run dexopt (for Dalvik) or dex2oat (for ART) ahead of time\n#\n####################################\n\ninclude $(BUILD_SYSTEM)/dex_preopt_config.mk\n\n# Method returning whether the install path $(1) should be for system_other.\n# Under SANITIZE_LITE, we do not want system_other. Just put things under /data/asan.\nifeq ($(SANITIZE_LITE),true)\ninstall-on-system-other =\nelse\ninstall-on-system-other = $(filter-out $(PRODUCT_DEXPREOPT_SPEED_APPS) $(PRODUCT_SYSTEM_SERVER_APPS),$(basename $(notdir $(filter $(foreach f,$(SYSTEM_OTHER_ODEX_FILTER),$(TARGET_OUT)/$(f)),$(1)))))\nendif\n\nifeq ($(WITH_DEXPREOPT), true)\nifneq ($(WITH_DEXPREOPT_ART_BOOT_IMG_ONLY), true)\nifeq ($(PRODUCT_USES_DEFAULT_ART_CONFIG), true)\n\n# Infix can be 'art' (ART image for testing), 'boot' (primary), or 'mainline' (mainline extension).\n# Soong creates a set of variables for Make, one or each boot image. The only reason why the ART\n# image is exposed to Make is testing (art gtests) and benchmarking (art golem benchmarks). Install\n# rules that use those variables are in dex_preopt_libart.mk. Here for dexpreopt purposes the infix\n# is always 'boot' or 'mainline'.\nDEXPREOPT_INFIX := $(if $(filter true,$(DEX_PREOPT_WITH_UPDATABLE_BCP)),mainline,boot)\n\nendif  #PRODUCT_USES_DEFAULT_ART_CONFIG\nendif  #WITH_DEXPREOPT_ART_BOOT_IMG_ONLY\nendif  #WITH_DEXPREOPT\n"
  },
  {
    "path": "core/dex_preopt_config.mk",
    "content": "DEX_PREOPT_CONFIG := $(SOONG_OUT_DIR)/dexpreopt${COVERAGE_SUFFIX}.config\n\nENABLE_PREOPT := true\nENABLE_PREOPT_BOOT_IMAGES := true\nifneq (true,$(filter true,$(WITH_DEXPREOPT)))\n  # Disable dexpreopt for libraries/apps and for boot images.\n  ENABLE_PREOPT :=\n  ENABLE_PREOPT_BOOT_IMAGES :=\nelse ifneq (true,$(filter true,$(PRODUCT_USES_DEFAULT_ART_CONFIG)))\n  # Disable dexpreopt for libraries/apps and for boot images: not having default\n  # ART config means that some important system properties are not set, which\n  # would result in passing bad arguments to dex2oat and failing the build.\n  ENABLE_PREOPT :=\n  ENABLE_PREOPT_BOOT_IMAGES :=\nelse\n  ifeq (true,$(DISABLE_PREOPT))\n    # Disable dexpreopt for libraries/apps, but may compile boot images.\n    ENABLE_PREOPT :=\n  endif\n  ifeq (true,$(DISABLE_PREOPT_BOOT_IMAGES))\n    # Disable dexpreopt for boot images, but may compile libraries/apps.\n    ENABLE_PREOPT_BOOT_IMAGES :=\n  endif\nendif\n\n# The default value for LOCAL_DEX_PREOPT\nDEX_PREOPT_DEFAULT ?= $(ENABLE_PREOPT)\n\n# Whether to fail immediately if verify_uses_libraries check fails, or to keep\n# going and restrict dexpreopt to not compile any code for the failed module.\n#\n# The intended use case for this flag is to have a smoother migration path for\n# the Java modules that need to add <uses-library> information in their build\n# files. The flag allows to quickly silence build errors. This flag should be\n# used with caution and only as a temporary measure, as it masks real errors\n# and affects performance.\nifndef RELAX_USES_LIBRARY_CHECK\n  RELAX_USES_LIBRARY_CHECK := $(if \\\n    $(filter true,$(PRODUCT_BROKEN_VERIFY_USES_LIBRARIES)),true,false)\nelse\n  # Let the environment variable override PRODUCT_BROKEN_VERIFY_USES_LIBRARIES.\nendif\n.KATI_READONLY := RELAX_USES_LIBRARY_CHECK\n\n# The default filter for which files go into the system_other image (if it is\n# being used). Note that each pattern p here matches both '/<p>' and /system/<p>'.\n# To bundle everything one should set this to '%'.\nSYSTEM_OTHER_ODEX_FILTER ?= \\\n    app/% \\\n    priv-app/% \\\n    system_ext/app/% \\\n    system_ext/priv-app/% \\\n    product/app/% \\\n    product/priv-app/% \\\n\n# Global switch to control if updatable boot jars are included in dexpreopt.\nDEX_PREOPT_WITH_UPDATABLE_BCP := true\n\n# Conditional to building on linux, as dex2oat currently does not work on darwin.\nifeq ($(HOST_OS),linux)\n  # Add mini-debug-info to the boot classpath unless explicitly asked not to.\n  ifneq (false,$(WITH_DEXPREOPT_DEBUG_INFO))\n    PRODUCT_DEX_PREOPT_BOOT_FLAGS += --generate-mini-debug-info\n  endif\nendif\n\n# Get value of a property. It is first searched from PRODUCT_VENDOR_PROPERTIES\n# and then falls back to PRODUCT_SYSTEM_PROPERTIES\n# $1: name of the property\ndefine get-product-default-property\n$(strip \\\n  $(eval _prop := $(patsubst $(1)=%,%,$(filter $(1)=%,$(PRODUCT_VENDOR_PROPERTIES))))\\\n  $(if $(_prop),$(_prop),$(patsubst $(1)=%,%,$(filter $(1)=%,$(PRODUCT_SYSTEM_PROPERTIES)))))\nendef\n\nDEX2OAT_IMAGE_XMS := $(call get-product-default-property,dalvik.vm.image-dex2oat-Xms)\nDEX2OAT_IMAGE_XMX := $(call get-product-default-property,dalvik.vm.image-dex2oat-Xmx)\nDEX2OAT_XMS := $(call get-product-default-property,dalvik.vm.dex2oat-Xms)\nDEX2OAT_XMX := $(call get-product-default-property,dalvik.vm.dex2oat-Xmx)\n\nifeq ($(WRITE_SOONG_VARIABLES),true)\n\n  $(call json_start)\n\n  $(call add_json_bool, DisablePreopt,                           $(call invert_bool,$(ENABLE_PREOPT)))\n  $(call add_json_bool, DisablePreoptBootImages,                 $(call invert_bool,$(ENABLE_PREOPT_BOOT_IMAGES)))\n  $(call add_json_list, DisablePreoptModules,                    $(DEXPREOPT_DISABLED_MODULES))\n  $(call add_json_bool, OnlyPreoptArtBootImage            ,      $(filter true,$(WITH_DEXPREOPT_ART_BOOT_IMG_ONLY)))\n  $(call add_json_bool, PreoptWithUpdatableBcp,                  $(filter true,$(DEX_PREOPT_WITH_UPDATABLE_BCP)))\n  $(call add_json_bool, DontUncompressPrivAppsDex,               $(filter true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS)))\n  $(call add_json_list, ModulesLoadedByPrivilegedModules,        $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))\n  $(call add_json_bool, HasSystemOther,                          $(BOARD_USES_SYSTEM_OTHER_ODEX))\n  $(call add_json_list, PatternsOnSystemOther,                   $(SYSTEM_OTHER_ODEX_FILTER))\n  $(call add_json_bool, DisableGenerateProfile,                  $(filter false,$(WITH_DEX_PREOPT_GENERATE_PROFILE)))\n  $(call add_json_str,  ProfileDir,                              $(PRODUCT_DEX_PREOPT_PROFILE_DIR))\n  $(call add_json_list, BootJars,                                $(PRODUCT_BOOT_JARS))\n  $(call add_json_list, ApexBootJars,                            $(filter-out $(APEX_BOOT_JARS_EXCLUDED), $(PRODUCT_APEX_BOOT_JARS)))\n  $(call add_json_list, ArtApexJars,                             $(filter $(PRODUCT_BOOT_JARS),$(ART_APEX_JARS)))\n  $(call add_json_list, TestOnlyArtBootImageJars,                $(PRODUCT_TEST_ONLY_ART_BOOT_IMAGE_JARS))\n  $(call add_json_list, SystemServerJars,                        $(PRODUCT_SYSTEM_SERVER_JARS))\n  $(call add_json_list, SystemServerApps,                        $(PRODUCT_SYSTEM_SERVER_APPS))\n  $(call add_json_list, ApexSystemServerJars,                    $(PRODUCT_APEX_SYSTEM_SERVER_JARS))\n  $(call add_json_list, StandaloneSystemServerJars,              $(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS))\n  $(call add_json_list, ApexStandaloneSystemServerJars,          $(PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS))\n  $(call add_json_bool, BrokenSuboptimalOrderOfSystemServerJars, $(PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS))\n  $(call add_json_list, SpeedApps,                               $(PRODUCT_DEXPREOPT_SPEED_APPS))\n  $(call add_json_list, PreoptFlags,                             $(PRODUCT_DEX_PREOPT_DEFAULT_FLAGS))\n  $(call add_json_str,  DefaultCompilerFilter,                   $(PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER))\n  $(call add_json_str,  SystemServerCompilerFilter,              $(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER))\n  $(call add_json_bool, GenerateDmFiles,                         $(PRODUCT_DEX_PREOPT_GENERATE_DM_FILES))\n  $(call add_json_bool, NeverAllowStripping,                     $(PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING))\n  $(call add_json_bool, NoDebugInfo,                             $(filter false,$(WITH_DEXPREOPT_DEBUG_INFO)))\n  $(call add_json_bool, DontResolveStartupStrings,               $(filter false,$(PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS)))\n  $(call add_json_bool, AlwaysSystemServerDebugInfo,             $(filter true,$(PRODUCT_SYSTEM_SERVER_DEBUG_INFO)))\n  $(call add_json_bool, NeverSystemServerDebugInfo,              $(filter false,$(PRODUCT_SYSTEM_SERVER_DEBUG_INFO)))\n  $(call add_json_bool, AlwaysOtherDebugInfo,                    $(filter true,$(PRODUCT_OTHER_JAVA_DEBUG_INFO)))\n  $(call add_json_bool, NeverOtherDebugInfo,                     $(filter false,$(PRODUCT_OTHER_JAVA_DEBUG_INFO)))\n  $(call add_json_bool, IsEng,                                   $(filter eng,$(TARGET_BUILD_VARIANT)))\n  $(call add_json_bool, SanitizeLite,                            $(SANITIZE_LITE))\n  $(call add_json_bool, DefaultAppImages,                        $(WITH_DEX_PREOPT_APP_IMAGE))\n  $(call add_json_bool, RelaxUsesLibraryCheck,                   $(filter true,$(RELAX_USES_LIBRARY_CHECK)))\n  $(call add_json_str,  Dex2oatXmx,                              $(DEX2OAT_XMX))\n  $(call add_json_str,  Dex2oatXms,                              $(DEX2OAT_XMS))\n  $(call add_json_str,  EmptyDirectory,                          $(OUT_DIR)/empty)\n  $(call add_json_str,  EnableUffdGc,                            $(ENABLE_UFFD_GC))\n\nifdef TARGET_ARCH\n  $(call add_json_map,  CpuVariant)\n  $(call add_json_str,  $(TARGET_ARCH), $(DEX2OAT_TARGET_CPU_VARIANT))\n  ifdef TARGET_2ND_ARCH\n    $(call add_json_str, $(TARGET_2ND_ARCH), $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT))\n  endif\n  $(call end_json_map)\n\n  $(call add_json_map,  InstructionSetFeatures)\n  $(call add_json_str,  $(TARGET_ARCH), $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES))\n  ifdef TARGET_2ND_ARCH\n    $(call add_json_str, $(TARGET_2ND_ARCH), $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES))\n  endif\n  $(call end_json_map)\nendif\n\n  $(call add_json_list, BootImageProfiles,                  $(PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION))\n  $(call add_json_str,  BootFlags,                          $(PRODUCT_DEX_PREOPT_BOOT_FLAGS))\n  $(call add_json_str,  Dex2oatImageXmx,                    $(DEX2OAT_IMAGE_XMX))\n  $(call add_json_str,  Dex2oatImageXms,                    $(DEX2OAT_IMAGE_XMS))\n\n  $(call json_end)\n\n  $(shell mkdir -p $(dir $(DEX_PREOPT_CONFIG)))\n  $(file >$(DEX_PREOPT_CONFIG).tmp,$(json_contents))\n\n  $(shell \\\n    if ! cmp -s $(DEX_PREOPT_CONFIG).tmp $(DEX_PREOPT_CONFIG); then \\\n      mv $(DEX_PREOPT_CONFIG).tmp $(DEX_PREOPT_CONFIG); \\\n    else \\\n      rm $(DEX_PREOPT_CONFIG).tmp; \\\n    fi)\nendif\n"
  },
  {
    "path": "core/dex_preopt_config_merger.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\"\"\"\nA tool for merging dexpreopt.config files for <uses-library> dependencies into\nthe dexpreopt.config file of the library/app that uses them. This is needed to\ngenerate class loader context (CLC) for dexpreopt.\n\nIn Make there is no topological order when processing different modules, so a\n<uses-library> dependency module may have not been processed yet by the time the\ndependent module is processed. Therefore makefiles communicate the information\nfrom dependencies via dexpreopt.config files and add file-level dependencies\nfrom a module dexpreopt.config to its dependency configs. The actual patching\nof configs is done by this script, which is called from the makefiles.\n\"\"\"\n\nfrom __future__ import print_function\n\nimport json\nfrom collections import OrderedDict\nimport sys\n\n\ndef main():\n  \"\"\"Program entry point.\"\"\"\n  if len(sys.argv) < 2:\n    raise SystemExit('usage: %s <main-config> [dep-config ...]' % sys.argv[0])\n\n  # Read all JSON configs.\n  cfgs = []\n  for arg in sys.argv[1:]:\n    with open(arg, 'r') as f:\n      cfgs.append(json.load(f, object_pairs_hook=OrderedDict))\n\n  # The first config is the dexpreopted library/app, the rest are its\n  # <uses-library> dependencies.\n  cfg0 = cfgs[0]\n\n  # Put dependency configs in a map keyed on module name (for easier lookup).\n  uses_libs = {}\n  for cfg in cfgs[1:]:\n    uses_libs[cfg['Name']] = cfg\n\n  # Load the original CLC map.\n  clc_map = cfg0['ClassLoaderContexts']\n\n  # Create a new CLC map that will be a copy of the original one with patched\n  # fields from dependency dexpreopt.config files.\n  clc_map2 = OrderedDict()\n\n  # Patch CLC for each SDK version. Although this should not be necessary for\n  # compatibility libraries (so-called \"conditional CLC\"), because they all have\n  # known names, known paths in system/framework, and no subcontext. But keep\n  # the loop in case this changes in the future.\n  for sdk_ver in clc_map:\n    clcs = clc_map[sdk_ver]\n    clcs2 = []\n    for clc in clcs:\n      lib = clc['Name']\n      if lib in uses_libs:\n        ulib = uses_libs[lib]\n        # The real <uses-library> name (may be different from the module name).\n        clc['Name'] = ulib['ProvidesUsesLibrary']\n        # On-device (install) path to the dependency DEX jar file.\n        clc['Device'] = ulib['DexLocation']\n        # CLC of the dependency becomes a subcontext. We only need sub-CLC for\n        # 'any' version because all other versions are for compatibility\n        # libraries, which exist only for apps and not for libraries.\n        clc['Subcontexts'] = ulib['ClassLoaderContexts'].get('any')\n      else:\n        # dexpreopt.config for this <uses-library> is not among the script\n        # arguments, which may be the case with compatibility libraries that\n        # don't need patching anyway. Just use the original CLC.\n        pass\n      clcs2.append(clc)\n    clc_map2[sdk_ver] = clcs2\n\n  # Overwrite the original class loader context with the patched one.\n  cfg0['ClassLoaderContexts'] = clc_map2\n\n  # Update dexpreopt.config file.\n  with open(sys.argv[1], 'w') as f:\n    f.write(json.dumps(cfgs[0], indent=4, separators=(',', ': ')))\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "core/dex_preopt_odex_install.mk",
    "content": "# dexpreopt_odex_install.mk is used to define odex creation rules for JARs and APKs\n# This file depends on variables set in base_rules.mk\n# Input variables: my_manifest_or_apk\n# Output variables: LOCAL_DEX_PREOPT, LOCAL_UNCOMPRESS_DEX\n\nifeq (true,$(LOCAL_USE_EMBEDDED_DEX))\n  LOCAL_UNCOMPRESS_DEX := true\nelse\n  LOCAL_UNCOMPRESS_DEX :=\nendif\n\n# We explicitly uncompress APKs of privileged apps, and used by\n# privileged apps\nifneq (true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS))\n  ifeq (true,$(LOCAL_PRIVILEGED_MODULE))\n    LOCAL_UNCOMPRESS_DEX := true\n  endif\n\n  ifneq (,$(filter $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES), $(LOCAL_MODULE)))\n    LOCAL_UNCOMPRESS_DEX := true\n  endif\nendif  # DONT_UNCOMPRESS_PRIV_APPS_DEXS\n\n# Setting LOCAL_DEX_PREOPT based on WITH_DEXPREOPT, LOCAL_DEX_PREOPT, etc\nLOCAL_DEX_PREOPT := $(strip $(LOCAL_DEX_PREOPT))\nifndef LOCAL_DEX_PREOPT # LOCAL_DEX_PREOPT undefined\n  LOCAL_DEX_PREOPT := $(DEX_PREOPT_DEFAULT)\nendif\n\nifeq (false,$(LOCAL_DEX_PREOPT))\n  LOCAL_DEX_PREOPT :=\nendif\n\n# Disable preopt for tests.\nifneq (,$(filter $(LOCAL_MODULE_TAGS),tests))\n  LOCAL_DEX_PREOPT :=\nendif\n\n# If we have product-specific config for this module?\nifneq (,$(filter $(LOCAL_MODULE),$(DEXPREOPT_DISABLED_MODULES)))\n  LOCAL_DEX_PREOPT :=\nendif\n\n# Disable preopt for DISABLE_PREOPT\nifeq (true,$(DISABLE_PREOPT))\n  LOCAL_DEX_PREOPT :=\nendif\n\n# Disable preopt if not WITH_DEXPREOPT\nifneq (true,$(WITH_DEXPREOPT))\n  LOCAL_DEX_PREOPT :=\nendif\n\nifdef LOCAL_UNINSTALLABLE_MODULE\n  LOCAL_DEX_PREOPT :=\nendif\n\n# Disable preopt if the app contains no java code.\nifeq (,$(strip $(built_dex)$(my_prebuilt_src_file)$(LOCAL_SOONG_DEX_JAR)))\n  LOCAL_DEX_PREOPT :=\nendif\n\nifeq (true,$(WITH_DEXPREOPT_ART_BOOT_IMG_ONLY))\n  LOCAL_DEX_PREOPT :=\nendif\n\nmy_process_profile :=\nmy_profile_is_text_listing :=\n\nifeq (false,$(WITH_DEX_PREOPT_GENERATE_PROFILE))\n  LOCAL_DEX_PREOPT_GENERATE_PROFILE := false\nendif\n\nifndef LOCAL_DEX_PREOPT_GENERATE_PROFILE\n  # If LOCAL_DEX_PREOPT_GENERATE_PROFILE is not defined, default it based on the existence of the\n  # profile class listing. TODO: Use product specific directory here.\n  ifdef PRODUCT_DEX_PREOPT_PROFILE_DIR\n    LOCAL_DEX_PREOPT_PROFILE := $(PRODUCT_DEX_PREOPT_PROFILE_DIR)/$(LOCAL_MODULE).prof\n\n    ifneq (,$(wildcard $(LOCAL_DEX_PREOPT_PROFILE)))\n      my_process_profile := true\n      my_profile_is_text_listing :=\n    endif\n  endif\nelse\n  my_process_profile := $(LOCAL_DEX_PREOPT_GENERATE_PROFILE)\n  my_profile_is_text_listing := true\n  LOCAL_DEX_PREOPT_PROFILE := $(LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING)\nendif\n\nifeq (true,$(my_process_profile))\n  ifndef LOCAL_DEX_PREOPT_PROFILE\n    $(call pretty-error,Must have specified class listing (LOCAL_DEX_PREOPT_PROFILE))\n  endif\n  ifeq (,$(dex_preopt_profile_src_file))\n    $(call pretty-error, Internal error: dex_preopt_profile_src_file must be set)\n  endif\nendif\n\n################################################################################\n# Local module variables and functions used in dexpreopt and manifest_check.\n################################################################################\n\n# TODO(b/132357300): This may filter out too much, as PRODUCT_PACKAGES doesn't\n# include all packages (the full list is unknown until reading all Android.mk\n# makefiles). As a consequence, a library may be present but not included in\n# dexpreopt, which will result in class loader context mismatch and a failure\n# to load dexpreopt code on device.\n# However, we have to do filtering here. Otherwise, we may include extra\n# libraries that Soong and Make don't generate build rules for (e.g., a library\n# that exists in the source tree but not installable), and therefore get Ninja\n# errors.\n# We have deferred CLC computation to the Ninja phase, but the dependency\n# computation still needs to be done early. For now, this is the best we can do.\nmy_filtered_optional_uses_libraries := $(filter $(PRODUCT_PACKAGES), \\\n  $(LOCAL_OPTIONAL_USES_LIBRARIES))\n\nifeq ($(LOCAL_MODULE_CLASS),APPS)\n  # compatibility libraries are added to class loader context of an app only if\n  # targetSdkVersion in the app's manifest is lower than the given SDK version\n\n  my_dexpreopt_libs_compat_28 := \\\n    org.apache.http.legacy\n\n  my_dexpreopt_libs_compat_29 := \\\n    android.hidl.manager-V1.0-java \\\n    android.hidl.base-V1.0-java\n\n  my_dexpreopt_libs_compat_30 := \\\n    android.test.base \\\n    android.test.mock\n\n  my_dexpreopt_libs_compat := \\\n    $(my_dexpreopt_libs_compat_28) \\\n    $(my_dexpreopt_libs_compat_29) \\\n    $(my_dexpreopt_libs_compat_30)\nelse\n  my_dexpreopt_libs_compat :=\nendif\n\nmy_dexpreopt_libs := \\\n  $(LOCAL_USES_LIBRARIES) \\\n  $(my_filtered_optional_uses_libraries)\n\n# The order needs to be deterministic.\nmy_dexpreopt_libs_all := $(sort $(my_dexpreopt_libs) $(my_dexpreopt_libs_compat))\n\n# Module dexpreopt.config depends on dexpreopt.config files of each\n# <uses-library> dependency, because these libraries may be processed after\n# the current module by Make (there's no topological order), so the dependency\n# information (paths, class loader context) may not be ready yet by the time\n# this dexpreopt.config is generated. So it's necessary to add file-level\n# dependencies between dexpreopt.config files.\nmy_dexpreopt_dep_configs := $(foreach lib, \\\n  $(filter-out $(my_dexpreopt_libs_compat) $(FRAMEWORK_LIBRARIES),$(LOCAL_USES_LIBRARIES) $(my_filtered_optional_uses_libraries)), \\\n  $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,)/dexpreopt.config)\n\n# 1: SDK version\n# 2: list of libraries\n#\n# Make does not process modules in topological order wrt. <uses-library>\n# dependencies, therefore we cannot rely on variables to get the information\n# about dependencies (in particular, their on-device path and class loader\n# context). This information is communicated via dexpreopt.config files: each\n# config depends on configs for <uses-library> dependencies of this module,\n# and the dex_preopt_config_merger.py script reads all configs and inserts the\n# missing bits from dependency configs into the module config.\n#\n# By default on-device path is /system/framework/*.jar, and class loader\n# subcontext is empty. These values are correct for compatibility libraries,\n# which are special and not handled by dex_preopt_config_merger.py.\n#\nadd_json_class_loader_context = \\\n  $(call add_json_array, $(1)) \\\n  $(foreach lib, $(2),\\\n    $(call add_json_map_anon) \\\n    $(call add_json_str, Name, $(lib)) \\\n    $(call add_json_str, Host, $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar) \\\n    $(call add_json_str, Device, /system/framework/$(lib).jar) \\\n    $(call add_json_val, Subcontexts, null) \\\n    $(call end_json_map)) \\\n  $(call end_json_array)\n\n################################################################################\n# Verify <uses-library> coherence between the build system and the manifest.\n################################################################################\n\n# Some libraries do not have a manifest, so there is nothing to check against.\n# Handle it as if the manifest had zero <uses-library> tags: it is ok unless the\n# module has non-empty LOCAL_USES_LIBRARIES or LOCAL_OPTIONAL_USES_LIBRARIES.\nifndef my_manifest_or_apk\n  ifneq (,$(strip $(LOCAL_USES_LIBRARIES)$(LOCAL_OPTIONAL_USES_LIBRARIES)))\n    $(error $(LOCAL_MODULE) has non-empty <uses-library> list but no manifest)\n  else\n    LOCAL_ENFORCE_USES_LIBRARIES := false\n  endif\nendif\n\n# Disable the check for tests.\nifneq (,$(filter $(LOCAL_MODULE_TAGS),tests))\n  LOCAL_ENFORCE_USES_LIBRARIES := false\nendif\nifneq (,$(LOCAL_COMPATIBILITY_SUITE))\n  LOCAL_ENFORCE_USES_LIBRARIES := false\n\n  # Enable the check for WTS\n  ifneq ($(filter wts,$(LOCAL_COMPATIBILITY_SUITE)),)\n    LOCAL_ENFORCE_USES_LIBRARIES := true\n  endif\n\nendif\n\n# Disable the check if the app contains no java code.\nifeq (,$(strip $(built_dex)$(my_prebuilt_src_file)$(LOCAL_SOONG_DEX_JAR)))\n  LOCAL_ENFORCE_USES_LIBRARIES := false\nendif\n\n# Disable <uses-library> checks if dexpreopt is globally disabled.\n# Without dexpreopt the check is not necessary, and although it is good to have,\n# it is difficult to maintain on non-linux build platforms where dexpreopt is\n# generally disabled (the check may fail due to various unrelated reasons, such\n# as a failure to get manifest from an APK).\nifneq (true,$(WITH_DEXPREOPT))\n  LOCAL_ENFORCE_USES_LIBRARIES := false\nelse ifeq (true,$(WITH_DEXPREOPT_ART_BOOT_IMG_ONLY))\n  LOCAL_ENFORCE_USES_LIBRARIES := false\nendif\n\n# Verify LOCAL_USES_LIBRARIES/LOCAL_OPTIONAL_USES_LIBRARIES against the manifest.\nifndef LOCAL_ENFORCE_USES_LIBRARIES\n  LOCAL_ENFORCE_USES_LIBRARIES := true\nendif\n\nmy_enforced_uses_libraries :=\nifeq (true,$(LOCAL_ENFORCE_USES_LIBRARIES))\n  my_verify_script := build/soong/scripts/manifest_check.py\n  my_uses_libs_args := $(patsubst %,--uses-library %,$(LOCAL_USES_LIBRARIES))\n  my_optional_uses_libs_args := $(patsubst %,--optional-uses-library %, \\\n    $(LOCAL_OPTIONAL_USES_LIBRARIES))\n  my_relax_check_arg := $(if $(filter true,$(RELAX_USES_LIBRARY_CHECK)), \\\n    --enforce-uses-libraries-relax,)\n  my_dexpreopt_config_args := $(patsubst %,--dexpreopt-config %,$(my_dexpreopt_dep_configs))\n\n  my_enforced_uses_libraries := $(intermediates)/enforce_uses_libraries.status\n  $(my_enforced_uses_libraries): PRIVATE_USES_LIBRARIES := $(my_uses_libs_args)\n  $(my_enforced_uses_libraries): PRIVATE_OPTIONAL_USES_LIBRARIES := $(my_optional_uses_libs_args)\n  $(my_enforced_uses_libraries): PRIVATE_DEXPREOPT_CONFIGS := $(my_dexpreopt_config_args)\n  $(my_enforced_uses_libraries): PRIVATE_RELAX_CHECK := $(my_relax_check_arg)\n  $(my_enforced_uses_libraries): $(AAPT2)\n  $(my_enforced_uses_libraries): $(my_verify_script)\n  $(my_enforced_uses_libraries): $(my_dexpreopt_dep_configs)\n  $(my_enforced_uses_libraries): $(my_manifest_or_apk)\n\t@echo Verifying uses-libraries: $<\n\trm -f $@\n\t$(my_verify_script) \\\n\t  --enforce-uses-libraries \\\n\t  --enforce-uses-libraries-status $@ \\\n\t  --aapt $(AAPT2) \\\n\t  $(PRIVATE_USES_LIBRARIES) \\\n\t  $(PRIVATE_OPTIONAL_USES_LIBRARIES) \\\n\t  $(PRIVATE_DEXPREOPT_CONFIGS) \\\n\t  $(PRIVATE_RELAX_CHECK) \\\n\t  $<\n  $(LOCAL_BUILT_MODULE) : $(my_enforced_uses_libraries)\nendif\n\n################################################################################\n# Dexpreopt command.\n################################################################################\n\nmy_dexpreopt_archs :=\nmy_dexpreopt_images :=\nmy_dexpreopt_images_deps :=\nmy_dexpreopt_image_locations_on_host :=\nmy_dexpreopt_image_locations_on_device :=\nmy_dexpreopt_infix := $(DEXPREOPT_INFIX)\nmy_create_dexpreopt_config :=\n\nifdef LOCAL_DEX_PREOPT\n  ifeq (,$(filter PRESIGNED,$(LOCAL_CERTIFICATE)))\n    # Store uncompressed dex files preopted in /system\n    ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true)\n      ifeq ($(call install-on-system-other, $(my_module_path)),)\n        LOCAL_UNCOMPRESS_DEX := true\n      endif  # install-on-system-other\n    else  # BOARD_USES_SYSTEM_OTHER_ODEX\n      LOCAL_UNCOMPRESS_DEX := true\n    endif\n  endif\n  my_create_dexpreopt_config := true\nendif\n\n# dexpreopt is disabled when TARGET_BUILD_UNBUNDLED_IMAGE is true,\n# but dexpreopt config files are required to dexpreopt in post-processing.\nifeq ($(TARGET_BUILD_UNBUNDLED_IMAGE),true)\n  my_create_dexpreopt_config := true\nendif\n\nifeq ($(my_create_dexpreopt_config), true)\n  ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES)\n    my_module_multilib := $(LOCAL_MULTILIB)\n    # If the module is not an SDK library and it's a system server jar, only preopt the primary arch.\n    ifeq (,$(filter $(JAVA_SDK_LIBRARIES),$(LOCAL_MODULE)))\n      # For a Java library, by default we build odex for both 1st arch and 2nd arch.\n      # But it can be overridden with \"LOCAL_MULTILIB := first\".\n      ifneq (,$(filter $(PRODUCT_SYSTEM_SERVER_JARS),$(LOCAL_MODULE)))\n        # For system server jars, we build for only \"first\".\n        my_module_multilib := first\n      endif\n    endif\n\n    # Only preopt primary arch for translated arch since there is only an image there.\n    ifeq ($(TARGET_TRANSLATE_2ND_ARCH),true)\n      my_module_multilib := first\n    endif\n\n    # #################################################\n    # Odex for the 1st arch\n    my_dexpreopt_archs += $(TARGET_ARCH)\n    my_dexpreopt_images += $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_ARCH))\n    my_dexpreopt_images_deps += $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_ARCH))\n    # Odex for the 2nd arch\n    ifdef TARGET_2ND_ARCH\n      ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true)\n        ifneq (first,$(my_module_multilib))\n          my_dexpreopt_archs += $(TARGET_2ND_ARCH)\n          my_dexpreopt_images += $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_2ND_ARCH))\n          my_dexpreopt_images_deps += $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_2ND_ARCH))\n        endif  # my_module_multilib is not first.\n      endif  # TARGET_TRANSLATE_2ND_ARCH not true\n    endif  # TARGET_2ND_ARCH\n    # #################################################\n  else  # must be APPS\n    # The preferred arch\n    # Save the module multilib since setup_one_odex modifies it.\n    my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX)\n    my_dexpreopt_archs += $(TARGET_$(my_2nd_arch_prefix)ARCH)\n    my_dexpreopt_images += \\\n        $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH))\n    my_dexpreopt_images_deps += \\\n        $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH))\n    ifdef TARGET_2ND_ARCH\n      ifeq ($(my_module_multilib),both)\n        # The non-preferred arch\n        my_2nd_arch_prefix := $(if $(LOCAL_2ND_ARCH_VAR_PREFIX),,$(TARGET_2ND_ARCH_VAR_PREFIX))\n        my_dexpreopt_archs += $(TARGET_$(my_2nd_arch_prefix)ARCH)\n        my_dexpreopt_images += \\\n            $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH))\n        my_dexpreopt_images_deps += \\\n            $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH))\n      endif  # LOCAL_MULTILIB is both\n    endif  # TARGET_2ND_ARCH\n  endif  # LOCAL_MODULE_CLASS\n\n  my_dexpreopt_image_locations_on_host += $(DEXPREOPT_IMAGE_LOCATIONS_ON_HOST$(my_dexpreopt_infix))\n  my_dexpreopt_image_locations_on_device += $(DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE$(my_dexpreopt_infix))\n\n  # Record dex-preopt config.\n  DEXPREOPT.$(LOCAL_MODULE).DEX_PREOPT := $(LOCAL_DEX_PREOPT)\n  DEXPREOPT.$(LOCAL_MODULE).MULTILIB := $(LOCAL_MULTILIB)\n  DEXPREOPT.$(LOCAL_MODULE).DEX_PREOPT_FLAGS := $(LOCAL_DEX_PREOPT_FLAGS)\n  DEXPREOPT.$(LOCAL_MODULE).PRIVILEGED_MODULE := $(LOCAL_PRIVILEGED_MODULE)\n  DEXPREOPT.$(LOCAL_MODULE).VENDOR_MODULE := $(LOCAL_VENDOR_MODULE)\n  DEXPREOPT.$(LOCAL_MODULE).TARGET_ARCH := $(LOCAL_MODULE_TARGET_ARCH)\n  DEXPREOPT.$(LOCAL_MODULE).INSTALLED_STRIPPED := $(LOCAL_INSTALLED_MODULE)\n  DEXPREOPT.MODULES.$(LOCAL_MODULE_CLASS) := $(sort \\\n    $(DEXPREOPT.MODULES.$(LOCAL_MODULE_CLASS)) $(LOCAL_MODULE))\n\n  $(call json_start)\n\n  # DexPath is not set: it will be filled in by dexpreopt_gen.\n\n  $(call add_json_str,  Name,                           $(LOCAL_MODULE))\n  $(call add_json_str,  DexLocation,                    $(patsubst $(PRODUCT_OUT)%,%,$(LOCAL_INSTALLED_MODULE)))\n  $(call add_json_str,  BuildPath,                      $(LOCAL_BUILT_MODULE))\n  $(call add_json_str,  ManifestPath,                   $(full_android_manifest))\n  $(call add_json_str,  ExtrasOutputPath,               $$2)\n  $(call add_json_bool, Privileged,                     $(filter true,$(LOCAL_PRIVILEGED_MODULE)))\n  $(call add_json_bool, UncompressedDex,                $(filter true,$(LOCAL_UNCOMPRESS_DEX)))\n  $(call add_json_bool, HasApkLibraries,                $(LOCAL_APK_LIBRARIES))\n  $(call add_json_list, PreoptFlags,                    $(LOCAL_DEX_PREOPT_FLAGS))\n  $(call add_json_str,  ProfileClassListing,            $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE)))\n  $(call add_json_bool, ProfileIsTextListing,           $(my_profile_is_text_listing))\n  $(call add_json_str,  EnforceUsesLibrariesStatusFile, $(my_enforced_uses_libraries))\n  $(call add_json_bool, EnforceUsesLibraries,           $(filter true,$(LOCAL_ENFORCE_USES_LIBRARIES)))\n  $(call add_json_str,  ProvidesUsesLibrary,            $(firstword $(LOCAL_PROVIDES_USES_LIBRARY) $(LOCAL_MODULE)))\n  $(call add_json_map,  ClassLoaderContexts)\n  $(call add_json_class_loader_context, any, $(my_dexpreopt_libs))\n  $(call add_json_class_loader_context,  28, $(my_dexpreopt_libs_compat_28))\n  $(call add_json_class_loader_context,  29, $(my_dexpreopt_libs_compat_29))\n  $(call add_json_class_loader_context,  30, $(my_dexpreopt_libs_compat_30))\n  $(call end_json_map)\n  $(call add_json_list, Archs,                          $(my_dexpreopt_archs))\n  $(call add_json_list, DexPreoptImages,                $(my_dexpreopt_images))\n  $(call add_json_list, DexPreoptImageLocationsOnHost,  $(my_dexpreopt_image_locations_on_host))\n  $(call add_json_list, DexPreoptImageLocationsOnDevice,$(my_dexpreopt_image_locations_on_device))\n  $(call add_json_list, PreoptBootClassPathDexFiles,    $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES))\n  $(call add_json_list, PreoptBootClassPathDexLocations,$(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS))\n  $(call add_json_bool, NoCreateAppImage,               $(filter false,$(LOCAL_DEX_PREOPT_APP_IMAGE)))\n  $(call add_json_bool, ForceCreateAppImage,            $(filter true,$(LOCAL_DEX_PREOPT_APP_IMAGE)))\n  $(call add_json_bool, PresignedPrebuilt,              $(filter PRESIGNED,$(LOCAL_CERTIFICATE)))\n\n  $(call json_end)\n\n  my_dexpreopt_config := $(intermediates)/dexpreopt.config\n  my_dexpreopt_config_for_postprocessing := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config\n  my_dexpreopt_config_merger := $(BUILD_SYSTEM)/dex_preopt_config_merger.py\n\n  $(my_dexpreopt_config): $(my_dexpreopt_dep_configs) $(my_dexpreopt_config_merger)\n  $(my_dexpreopt_config): PRIVATE_MODULE := $(LOCAL_MODULE)\n  $(my_dexpreopt_config): PRIVATE_CONTENTS := $(json_contents)\n  $(my_dexpreopt_config): PRIVATE_DEP_CONFIGS := $(my_dexpreopt_dep_configs)\n  $(my_dexpreopt_config): PRIVATE_CONFIG_MERGER := $(my_dexpreopt_config_merger)\n  $(my_dexpreopt_config):\n\t@echo \"$(PRIVATE_MODULE) dexpreopt.config\"\n\techo -e -n '$(subst $(newline),\\n,$(subst ','\\'',$(subst \\,\\\\,$(PRIVATE_CONTENTS))))' > $@\n\t$(PRIVATE_CONFIG_MERGER) $@ $(PRIVATE_DEP_CONFIGS)\n\n$(eval $(call copy-one-file,$(my_dexpreopt_config),$(my_dexpreopt_config_for_postprocessing)))\n\n$(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_config_for_postprocessing)\n\n# System server jars defined in Android.mk are deprecated.\nifneq (true, $(PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS))\n  ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_SYSTEM_SERVER_JARS) $(PRODUCT_APEX_SYSTEM_SERVER_JARS)))\n    $(error System server jars defined in Android.mk are deprecated. \\\n      Convert $(LOCAL_MODULE) to Android.bp or temporarily disable the error \\\n      with 'PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS := true')\n  endif\nendif\n\nifdef LOCAL_DEX_PREOPT\n  # System server jars must be copied into predefined locations expected by\n  # dexpreopt. Copy rule must be exposed to Ninja (as it uses these files as\n  # inputs), so it cannot go in dexpreopt.sh.\n  ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_SYSTEM_SERVER_JARS)))\n    my_dexpreopt_jar_copy := $(OUT_DIR)/soong/system_server_dexjars/$(LOCAL_MODULE).jar\n    $(my_dexpreopt_jar_copy): PRIVATE_BUILT_MODULE := $(LOCAL_BUILT_MODULE)\n    $(my_dexpreopt_jar_copy): $(LOCAL_BUILT_MODULE)\n\t  @cp $(PRIVATE_BUILT_MODULE) $@\n  endif\n\n  # The root \"product_packages.txt\" is generated by `build/make/core/Makefile`. It contains a list\n  # of all packages that are installed on the device. We use `grep` to filter the list by the app's\n  # dependencies to create a per-app list, and use `rsync --checksum` to prevent the file's mtime\n  # from being changed if the contents don't change. This avoids unnecessary dexpreopt reruns.\n  my_dexpreopt_product_packages := $(intermediates)/product_packages.txt\n  .KATI_RESTAT: $(my_dexpreopt_product_packages)\n  $(my_dexpreopt_product_packages): PRIVATE_MODULE := $(LOCAL_MODULE)\n  $(my_dexpreopt_product_packages): PRIVATE_LIBS := $(my_dexpreopt_libs_all)\n  $(my_dexpreopt_product_packages): PRIVATE_STAGING := $(my_dexpreopt_product_packages).tmp\n  $(my_dexpreopt_product_packages): $(PRODUCT_OUT)/product_packages.txt\n\t@echo \"$(PRIVATE_MODULE) dexpreopt product_packages\"\n  ifneq (,$(my_dexpreopt_libs_all))\n\t\tgrep -F -x \\\n\t\t\t$(addprefix -e ,$(PRIVATE_LIBS)) \\\n\t\t\t$(PRODUCT_OUT)/product_packages.txt \\\n\t\t\t> $(PRIVATE_STAGING) \\\n\t\t\t|| true\n  else\n\t\trm -f $(PRIVATE_STAGING) && touch $(PRIVATE_STAGING)\n  endif\n\trsync --checksum $(PRIVATE_STAGING) $@\n\n  my_dexpreopt_script := $(intermediates)/dexpreopt.sh\n  .KATI_RESTAT: $(my_dexpreopt_script)\n  $(my_dexpreopt_script): PRIVATE_MODULE := $(LOCAL_MODULE)\n  $(my_dexpreopt_script): PRIVATE_GLOBAL_SOONG_CONFIG := $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)\n  $(my_dexpreopt_script): PRIVATE_GLOBAL_CONFIG := $(DEX_PREOPT_CONFIG_FOR_MAKE)\n  $(my_dexpreopt_script): PRIVATE_MODULE_CONFIG := $(my_dexpreopt_config)\n  $(my_dexpreopt_script): PRIVATE_PRODUCT_PACKAGES := $(my_dexpreopt_product_packages)\n  $(my_dexpreopt_script): $(DEXPREOPT_GEN)\n  $(my_dexpreopt_script): $(my_dexpreopt_jar_copy)\n  $(my_dexpreopt_script): $(my_dexpreopt_config) $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) $(DEX_PREOPT_CONFIG_FOR_MAKE) $(my_dexpreopt_product_packages)\n\t@echo \"$(PRIVATE_MODULE) dexpreopt gen\"\n\t$(DEXPREOPT_GEN) \\\n\t-global_soong $(PRIVATE_GLOBAL_SOONG_CONFIG) \\\n\t-global $(PRIVATE_GLOBAL_CONFIG) \\\n\t-module $(PRIVATE_MODULE_CONFIG) \\\n\t-dexpreopt_script $@ \\\n\t-out_dir $(OUT_DIR) \\\n\t-product_packages $(PRIVATE_PRODUCT_PACKAGES)\n\n  my_dexpreopt_deps := $(my_dex_jar)\n  my_dexpreopt_deps += $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE))\n  my_dexpreopt_deps += \\\n    $(foreach lib, $(my_dexpreopt_libs_all), \\\n      $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar)\n  my_dexpreopt_deps += $(my_dexpreopt_images_deps)\n  my_dexpreopt_deps += $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)\n  ifeq ($(LOCAL_ENFORCE_USES_LIBRARIES),true)\n    my_dexpreopt_deps += $(intermediates)/enforce_uses_libraries.status\n  endif\n\n  # We need to add all the installed files to ALL_MODULES.$(my_register_name).INSTALLED in order\n  # for the build system to properly track installed files. (for sbom, installclean, etc)\n  # We install all the files in a zip file generated at execution time, which means we have to guess\n  # what's going to be in that zip file before it's created. We then check at executation time that\n  # our guess is correct.\n  # _system_other corresponds to OdexOnSystemOtherByName() in soong.\n  # The other paths correspond to dexpreoptCommand()\n  _dexlocation := $(patsubst $(PRODUCT_OUT)/%,%,$(LOCAL_INSTALLED_MODULE))\n  _dexname := $(basename $(notdir $(_dexlocation)))\n  _system_other := $(strip $(if $(strip $(BOARD_USES_SYSTEM_OTHER_ODEX)), \\\n    $(if $(strip $(SANITIZE_LITE)),, \\\n      $(if $(filter $(_dexname),$(PRODUCT_DEXPREOPT_SPEED_APPS))$(filter $(_dexname),$(PRODUCT_SYSTEM_SERVER_APPS)),, \\\n        $(if $(strip $(foreach myfilter,$(SYSTEM_OTHER_ODEX_FILTER),$(filter system/$(myfilter),$(_dexlocation))$(filter $(myfilter),$(_dexlocation)))), \\\n            system_other/)))))\n  # _dexdir has a trailing /\n  _dexdir := $(_system_other)$(dir $(_dexlocation))\n  my_dexpreopt_zip_contents := $(sort \\\n    $(foreach arch,$(my_dexpreopt_archs), \\\n      $(_dexdir)oat/$(arch)/$(_dexname).odex \\\n      $(_dexdir)oat/$(arch)/$(_dexname).vdex \\\n      $(if $(filter false,$(LOCAL_DEX_PREOPT_APP_IMAGE)),, \\\n        $(if $(my_process_profile)$(filter true,$(LOCAL_DEX_PREOPT_APP_IMAGE)), \\\n          $(_dexdir)oat/$(arch)/$(_dexname).art))) \\\n    $(if $(my_process_profile),$(_dexlocation).prof))\n  _dexlocation :=\n  _dexdir :=\n  _dexname :=\n  _system_other :=\n\n  my_dexpreopt_zip := $(intermediates)/dexpreopt.zip\n  $(my_dexpreopt_zip): PRIVATE_MODULE := $(LOCAL_MODULE)\n  $(my_dexpreopt_zip): $(my_dexpreopt_deps)\n  $(my_dexpreopt_zip): | $(DEXPREOPT_GEN_DEPS)\n  $(my_dexpreopt_zip): .KATI_DEPFILE := $(my_dexpreopt_zip).d\n  $(my_dexpreopt_zip): PRIVATE_DEX := $(my_dex_jar)\n  $(my_dexpreopt_zip): PRIVATE_SCRIPT := $(my_dexpreopt_script)\n  $(my_dexpreopt_zip): PRIVATE_ZIP_CONTENTS := $(my_dexpreopt_zip_contents)\n  $(my_dexpreopt_zip): $(my_dexpreopt_script)\n\t@echo \"$(PRIVATE_MODULE) dexpreopt\"\n\trm -f $@\n\techo -n > $@.contents\n\t$(foreach f,$(PRIVATE_ZIP_CONTENTS),echo \"$(f)\" >> $@.contents$(newline))\n\tbash $(PRIVATE_SCRIPT) $(PRIVATE_DEX) $@\n\tif ! diff <(zipinfo -1 $@ | sort) $@.contents >&2; then \\\n\t  echo \"Contents of $@ did not match what make was expecting.\" >&2 && exit 1; \\\n\tfi\n\n  $(foreach installed_dex_file,$(my_dexpreopt_zip_contents),\\\n    $(eval $(PRODUCT_OUT)/$(installed_dex_file): $(my_dexpreopt_zip) \\\n$(newline)\tunzip -qoDD -d $(PRODUCT_OUT) $(my_dexpreopt_zip) $(installed_dex_file)))\n\n  ALL_MODULES.$(my_register_name).INSTALLED += $(addprefix $(PRODUCT_OUT)/,$(my_dexpreopt_zip_contents))\n\n  # Normally this happens in sbom.mk, which is included from base_rules.mk. But since\n  # dex_preopt_odex_install.mk is included after base_rules.mk, it misses these odex files.\n  $(foreach installed_file,$(addprefix $(PRODUCT_OUT)/,$(my_dexpreopt_zip_contents)), \\\n    $(eval ALL_INSTALLED_FILES.$(installed_file) := $(my_register_name)))\n\n  my_dexpreopt_config :=\n  my_dexpreopt_config_for_postprocessing :=\n  my_dexpreopt_jar_copy :=\n  my_dexpreopt_product_packages :=\n  my_dexpreopt_script :=\n  my_dexpreopt_zip :=\n  my_dexpreopt_zip_contents :=\nendif # LOCAL_DEX_PREOPT\nendif # my_create_dexpreopt_config\n\nmy_dexpreopt_libs_all :=\n"
  },
  {
    "path": "core/distdir.mk",
    "content": "#\n# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# When specifying \"dist\", the user has asked that we copy the important\n# files from this build into DIST_DIR.\n\n# list of all goals that depend on any dist files\n_all_dist_goals :=\n# pairs of goal:distfile\n_all_dist_goal_output_pairs :=\n# pairs of srcfile:distfile\n_all_dist_src_dst_pairs :=\n\n# Other parts of the system should use this function to associate\n# certain files with certain goals.  When those goals are built\n# and \"dist\" is specified, the marked files will be copied to DIST_DIR.\n#\n# $(1): a list of goals  (e.g. droid, sdk, ndk). These must be PHONY\n# $(2): the dist files to add to those goals.  If the file contains ':',\n#       the text following the colon is the name that the file is copied\n#       to under the dist directory.  Subdirs are ok, and will be created\n#       at copy time if necessary.\ndefine dist-for-goals\n$(if $(strip $(2)), \\\n  $(eval _all_dist_goals += $$(1))) \\\n$(foreach file,$(2), \\\n  $(eval src := $(call word-colon,1,$(file))) \\\n  $(eval dst := $(call word-colon,2,$(file))) \\\n  $(if $(dst),,$(eval dst := $$(notdir $$(src)))) \\\n  $(eval _all_dist_src_dst_pairs += $$(src):$$(dst)) \\\n  $(foreach goal,$(1), \\\n    $(eval _all_dist_goal_output_pairs += $$(goal):$$(dst))))\nendef\n\ndefine add_file_name_tag_suffix\n$(basename $(notdir $1))-FILE_NAME_TAG_PLACEHOLDER$(suffix $1)\nendef\n\n# This function appends suffix FILE_NAME_TAG_PLACEHOLDER from the input file\n# $(1): a list of goals  (e.g. droid, sdk, ndk). These must be PHONY\n# $(2): the dist files to add to those goals.\ndefine dist-for-goals-with-filenametag\n$(if $(strip $(2)), \\\n  $(foreach file,$(2), \\\n    $(call dist-for-goals,$(1),$(file):$(call add_file_name_tag_suffix,$(file)))))\nendef\n.PHONY: shareprojects\n\ndefine __share-projects-rule\n$(1) : PRIVATE_TARGETS := $(2)\n$(1): $(2) $(COMPLIANCE_LISTSHARE)\n\t$(hide) rm -f $$@\n\tmkdir -p $$(dir $$@)\n\t$$(if $$(strip $$(PRIVATE_TARGETS)),OUT_DIR=$(OUT_DIR) $(COMPLIANCE_LISTSHARE) -o $$@ $$(PRIVATE_TARGETS),touch $$@)\nendef\n\n# build list of projects to share in $(1) for meta_lic in $(2)\n#\n# $(1): the intermediate project sharing file\n# $(2): the license metadata to base the sharing on\ndefine _share-projects-rule\n$(eval $(call __share-projects-rule,$(1),$(2)))\nendef\n\n.PHONY: alllicensetexts\n\ndefine __license-texts-rule\n$(2) : PRIVATE_GOAL := $(1)\n$(2) : PRIVATE_TARGETS := $(3)\n$(2) : PRIVATE_ROOTS := $(4)\n$(2) : PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,METAPACKAGING,licensetexts)/$(2)/arguments\n$(2): $(3) $(TEXTNOTICE)\n\t$(hide) rm -f $$@\n\tmkdir -p $$(dir $$@)\n\tmkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE))\n\t$$(if $$(strip $$(PRIVATE_TARGETS)),$$(call dump-words-to-file,\\\n            -product=\"$$(PRIVATE_GOAL)\" -title=\"$$(PRIVATE_GOAL)\" \\\n            $$(addprefix -strip_prefix ,$$(PRIVATE_ROOTS)) \\\n            -strip_prefix=$(PRODUCT_OUT)/ -strip_prefix=$(HOST_OUT)/\\\n            $$(PRIVATE_TARGETS),\\\n            $$(PRIVATE_ARGUMENT_FILE)))\n\t$$(if $$(strip $$(PRIVATE_TARGETS)),OUT_DIR=$(OUT_DIR) $(TEXTNOTICE) -o $$@ @$$(PRIVATE_ARGUMENT_FILE),touch $$@)\nendef\n\n# build list of projects to share in $(2) for meta_lic in $(3) for dist goals $(1)\n# Strip `out/dist/` used as proxy for 'DIST_DIR'\n#\n# $(1): the name of the dist goals\n# $(2): the intermediate project sharing file\n# $(3): the license metadata to base the sharing on\ndefine _license-texts-rule\n$(eval $(call __license-texts-rule,$(1),$(2),$(3),out/dist/))\nendef\n\n###########################################################\n## License metadata build rule for dist target $(1) with meta_lic $(2) copied from $(3)\n###########################################################\ndefine _dist-target-license-metadata-rule\n$(strip $(eval _meta :=$(2)))\n$(strip $(eval _dep:=))\n# 0p is the indicator for a non-copyrightable file where no party owns the copyright.\n# i.e. pure data with no copyrightable expression.\n# If all of the sources are 0p and only 0p, treat the copied file as 0p. Otherwise, all\n# of the sources must either be 0p or originate from a single metadata file to copy.\n$(strip $(foreach s,$(strip $(3)),\\\n  $(eval _dmeta:=$(ALL_TARGETS.$(s).META_LIC))\\\n  $(if $(strip $(_dmeta)),\\\n    $(if $(filter-out 0p,$(_dep)),\\\n      $(if $(filter-out $(_dep) 0p,$(_dmeta)),\\\n        $(error cannot copy target from multiple modules: $(1) from $(_dep) and $(_dmeta)),\\\n        $(if $(filter 0p,$(_dep)),$(eval _dep:=$(_dmeta)))),\\\n      $(eval _dep:=$(_dmeta))\\\n    ),\\\n    $(eval TARGETS_MISSING_LICENSE_METADATA += $(s) $(1)))))\n\n\nifeq (0p,$(strip $(_dep)))\n# Not copyrightable. No emcumbrances, no license text, no license kind etc.\n$(_meta): PRIVATE_CONDITIONS := unencumbered\n$(_meta): PRIVATE_SOURCES := $(3)\n$(_meta): PRIVATE_INSTALLED := $(1)\n# use `$(1)` which is the unique and relatively short `out/dist/$(target)`\n$(_meta): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,METAPACKAGING,notice)/$(1)/arguments\n$(_meta): $(BUILD_LICENSE_METADATA)\n$(_meta) :\n\trm -f $$@\n\tmkdir -p $$(dir $$@)\n\tmkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE))\n\t$$(call dump-words-to-file,\\\n\t    $$(addprefix -c ,$$(PRIVATE_CONDITIONS))\\\n\t    $$(addprefix -s ,$$(PRIVATE_SOURCES))\\\n\t    $$(addprefix -t ,$$(PRIVATE_TARGETS))\\\n\t    $$(addprefix -i ,$$(PRIVATE_INSTALLED)),\\\n\t    $$(PRIVATE_ARGUMENT_FILE))\n\tOUT_DIR=$(OUT_DIR) $(BUILD_LICENSE_METADATA) \\\n\t  @$$(PRIVATE_ARGUMENT_FILE) \\\n\t  -o $$@\n\nelse ifneq (,$(strip $(_dep)))\n# Not a missing target, copy metadata and `is_container` etc. from license metadata file `$(_dep)`\n$(_meta): PRIVATE_DEST_TARGET := $(1)\n$(_meta): PRIVATE_SOURCE_TARGETS := $(3)\n$(_meta): PRIVATE_SOURCE_METADATA := $(_dep)\n# use `$(1)` which is the unique and relatively short `out/dist/$(target)`\n$(_meta): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,METAPACKAGING,copynotice)/$(1)/arguments\n$(_meta) : $(_dep) $(COPY_LICENSE_METADATA)\n\trm -f $$@\n\tmkdir -p $$(dir $$@)\n\tmkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE))\n\t$$(call dump-words-to-file,\\\n\t    $$(addprefix -i ,$$(PRIVATE_DEST_TARGET))\\\n\t    $$(addprefix -s ,$$(PRIVATE_SOURCE_TARGETS))\\\n\t    $$(addprefix -d ,$$(PRIVATE_SOURCE_METADATA)),\\\n\t    $$(PRIVATE_ARGUMENT_FILE))\n\tOUT_DIR=$(OUT_DIR) $(COPY_LICENSE_METADATA) \\\n\t  @$$(PRIVATE_ARGUMENT_FILE) \\\n\t  -o $$@\n\nendif\nendef\n\n# use `out/dist/` as a proxy for 'DIST_DIR'\ndefine _add_projects_to_share\n$(strip $(eval _mdir := $(call intermediates-dir-for,METAPACKAGING,meta)/out/dist)) \\\n$(strip $(eval _idir := $(call intermediates-dir-for,METAPACKAGING,shareprojects))) \\\n$(strip $(eval _tdir := $(call intermediates-dir-for,METAPACKAGING,licensetexts))) \\\n$(strip $(eval _allt := $(sort $(foreach goal,$(_all_dist_goal_output_pairs),$(call word-colon,2,$(goal)))))) \\\n$(foreach target,$(_allt), \\\n  $(eval _goals := $(sort $(foreach dg,$(filter %:$(target),$(_all_dist_goal_output_pairs)),$(call word-colon,1,$(dg))))) \\\n  $(eval _srcs := $(sort $(foreach sdp,$(filter %:$(target),$(_all_dist_src_dst_pairs)),$(call word-colon,1,$(sdp))))) \\\n  $(eval $(call _dist-target-license-metadata-rule,out/dist/$(target),$(_mdir)/out/dist/$(target).meta_lic,$(_srcs))) \\\n  $(eval _f := $(_idir)/$(target).shareprojects) \\\n  $(eval _n := $(_tdir)/$(target).txt) \\\n  $(eval $(call dist-for-goals,$(_goals),$(_f):shareprojects/$(target).shareprojects)) \\\n  $(eval $(call dist-for-goals,$(_goals),$(_n):licensetexts/$(target).txt)) \\\n  $(eval $(call _share-projects-rule,$(_f),$(foreach t, $(filter-out $(TARGETS_MISSING_LICENSE_METADATA),out/dist/$(target)),$(_mdir)/$(t).meta_lic))) \\\n  $(eval $(call _license-texts-rule,$(_goals),$(_n),$(foreach t,$(filter-out $(TARGETS_MISSING_LICENSE_METADATA),out/dist/$(target)),$(_mdir)/$(t).meta_lic))) \\\n)\nendef\n\n#------------------------------------------------------------------\n# To be used at the end of the build to collect all the uses of\n# dist-for-goals, and write them into a file for the packaging step to use.\n\n# $(1): The file to write\ndefine dist-write-file\n$(strip \\\n  $(call _add_projects_to_share)\\\n  $(if $(strip $(ANDROID_REQUIRE_LICENSE_METADATA)),\\\n    $(foreach target,$(sort $(TARGETS_MISSING_LICENSE_METADATA)),$(warning target $(target) missing license metadata))\\\n    $(if $(strip $(TARGETS_MISSING_LICENSE_METADATA)),\\\n      $(if $(filter true error,$(ANDROID_REQUIRE_LICENSE_METADATA)),\\\n        $(error $(words $(sort $(TARGETS_MISSING_LICENSE_METADATA))) targets need license metadata))))\\\n  $(foreach t,$(sort $(ALL_NON_MODULES)),$(call record-missing-non-module-dependencies,$(t))) \\\n  $(eval $(call report-missing-licenses-rule)) \\\n  $(eval $(call report-all-notice-library-names-rule)) \\\n  $(KATI_obsolete_var dist-for-goals,Cannot be used after dist-write-file) \\\n  $(foreach goal,$(sort $(_all_dist_goals)), \\\n    $(eval $$(goal): _dist_$$(goal))) \\\n  $(shell mkdir -p $(dir $(1))) \\\n  $(file >$(1).tmp, \\\n    DIST_GOAL_OUTPUT_PAIRS := $(sort $(_all_dist_goal_output_pairs)) \\\n    $(newline)DIST_SRC_DST_PAIRS := $(sort $(_all_dist_src_dst_pairs))) \\\n  $(shell if ! cmp -s $(1).tmp $(1); then \\\n            mv $(1).tmp $(1); \\\n          else \\\n            rm $(1).tmp; \\\n          fi))\nendef\n\n.KATI_READONLY := dist-for-goals dist-write-file dist-for-goals-with-filenametag\n"
  },
  {
    "path": "core/dumpconfig.mk",
    "content": "# Read and dump the product configuration.\n\n# Called from the product-config tool, not from the main build system.\n\n#\n# Ensure we are being called correctly\n#\nifndef KATI\n    $(warning Kati must be used to call dumpconfig.mk, not make.)\n    $(error stopping)\nendif\n\nifdef DEFAULT_GOAL\n    $(warning Calling dumpconfig.mk from inside the make build system is not)\n    $(warning supported. It is only meant to be called via kati by product-confing.)\n    $(error stopping)\nendif\n\nifndef TARGET_PRODUCT\n    $(warning dumpconfig.mk requires TARGET_PRODUCT to be set)\n    $(error stopping)\nendif\n\nifndef TARGET_BUILD_VARIANT\n    $(warning dumpconfig.mk requires TARGET_BUILD_VARIANT to be set)\n    $(error stopping)\nendif\n\nifneq (build/make/core/config.mk,$(wildcard build/make/core/config.mk))\n    $(warning dumpconfig must be called from the root of the source tree)\n    $(error stopping)\nendif\n\nifeq (,$(DUMPCONFIG_FILE))\n    $(warning dumpconfig requires DUMPCONFIG_FILE to be set)\n    $(error stopping)\nendif\n\n# Skip the second inclusion of all of the product config files, because\n# we will do these checks in the product_config tool.\nSKIP_ARTIFACT_PATH_REQUIREMENT_PRODUCTS_CHECK := true\n\n# Before we do anything else output the format version.\n$(file > $(DUMPCONFIG_FILE),dumpconfig_version,1)\n$(file >> $(DUMPCONFIG_FILE),dumpconfig_file,$(DUMPCONFIG_FILE))\n\n# Default goal for dumpconfig\ndumpconfig:\n\t$(file >> $(DUMPCONFIG_FILE),***DONE***)\n\t@echo ***DONE***\n\n# TODO(Remove): These need to be set externally\nOUT_DIR := out\nTMPDIR = /tmp/build-temp\nBUILD_DATETIME_FILE := $(OUT_DIR)/build_date.txt\n\n# Escape quotation marks for CSV, and wraps in quotation marks.\ndefine escape-for-csv\n\"$(subst \",\"\",$(subst $(newline), ,$1))\"\nendef\n\n# Args:\n#   $(1): include stack\ndefine dump-import-start\n$(eval $(file >> $(DUMPCONFIG_FILE),import,$(strip $(1))))\nendef\n\n# Args:\n#   $(1): include stack\ndefine dump-import-done\n$(eval $(file >> $(DUMPCONFIG_FILE),imported,$(strip $(1)),$(filter-out $(1),$(MAKEFILE_LIST))))\nendef\n\n# Args:\n#   $(1): Current file\n#   $(2): Inherited file\ndefine dump-inherit\n$(eval $(file >> $(DUMPCONFIG_FILE),inherit,$(strip $(1)),$(strip $(2))))\nendef\n\n# Args:\n#   $(1): Config phase (PRODUCT, EXPAND, or DEVICE)\n#   $(2): Root nodes to import\n#   $(3): All variable names\n#   $(4): Single-value variables\n#   $(5): Makefile being processed\ndefine dump-phase-start\n$(eval $(file >> $(DUMPCONFIG_FILE),phase,$(strip $(1)),$(strip $(2)))) \\\n$(foreach var,$(3), \\\n    $(eval $(file >> $(DUMPCONFIG_FILE),var,$(if $(filter $(4),$(var)),single,list),$(var))) \\\n) \\\n$(call dump-config-vals,$(strip $(5)),initial)\nendef\n\n# Args:\n#   $(1): Makefile being processed\ndefine dump-phase-end\n$(call dump-config-vals,$(strip $(1)),final)\nendef\n\ndefine dump-debug\n$(eval $(file >> $(DUMPCONFIG_FILE),debug,$(1)))\nendef\n\n# Skip these when dumping. They're not used and they cause a lot of noise in the dump.\nDUMPCONFIG_SKIP_VARS := \\\n\t.VARIABLES \\\n\t.KATI_SYMBOLS \\\n\t1 \\\n\t2 \\\n\t3 \\\n\t4 \\\n\t5 \\\n\t6 \\\n\t7 \\\n\t8 \\\n\t9 \\\n\tLOCAL_PATH \\\n\tMAKEFILE_LIST \\\n\tcurrent_mk \\\n\t_eiv_ev \\\n\t_eiv_i \\\n\t_eiv_sv \\\n\t_eiv_tv \\\n\tinherit_var \\\n\tnp \\\n\t_node_import_context \\\n\t_included \\\n\t_include_stack \\\n\t_in \\\n\t_nic.%\n\n# Args:\n#   $(1): Makefile that was included\n#   $(2): block (before,import,after,initial,final)\ndefine dump-config-vals\n$(foreach var,$(filter-out $(DUMPCONFIG_SKIP_VARS),$(.KATI_SYMBOLS)),\\\n    $(eval $(file >> $(DUMPCONFIG_FILE),val,$(call escape-for-csv,$(1)),$(2),$(call escape-for-csv,$(var)),$(call escape-for-csv,$($(var))),$(call escape-for-csv,$(KATI_variable_location $(var))))) \\\n)\nendef\n\ninclude build/make/core/config.mk\n\n"
  },
  {
    "path": "core/dumpvar.mk",
    "content": "# ---------------------------------------------------------------\n# the setpath shell function in envsetup.sh uses this to figure out\n# what to add to the path given the config we have chosen.\nifeq ($(CALLED_FROM_SETUP),true)\n\nANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG)\nANDROID_GCC_PREBUILTS := prebuilts/gcc/$(HOST_PREBUILT_TAG)\nANDROID_CLANG_PREBUILTS := prebuilts/clang/host/$(HOST_PREBUILT_TAG)\n\n# Dump mulitple variables to \"<var>=<value>\" pairs, one per line.\n# The output may be executed as bash script.\n# Input variables:\n#   DUMP_MANY_VARS: the list of variable names.\n#   DUMP_VAR_PREFIX: an optional prefix of the variable name added to the output.\n# The value is printed in parts because large variables like PRODUCT_PACKAGES\n# can exceed the maximum linux command line size\n.PHONY: dump-many-vars\ndump-many-vars :\n\t@$(foreach v, $(DUMP_MANY_VARS),\\\n\t  printf \"%s='%s\" '$(DUMP_VAR_PREFIX)$(v)' '$(firstword $($(v)))'; \\\n\t  $(foreach part, $(wordlist 2, $(words $($(v))), $($(v))),\\\n\t    printf \" %s\" '$(part)'$(newline))\\\n\t  printf \"'\\n\";)\n\nendif # CALLED_FROM_SETUP\n\nifneq (,$(RBC_DUMP_CONFIG_FILE))\n$(call dump-variables-rbc,$(RBC_DUMP_CONFIG_FILE))\nendif\n"
  },
  {
    "path": "core/dupcheck.sh",
    "content": "#!/bin/bash\n\n# Find duplicate shared libraries by md5 checksum and possible duplicates by size.\n# Results will be available in the out directory of the build.\n# Usage:\n# ./dupcheck.sh <out_dir> <image>\n\nOUT_DIR=\"$1\"\nIMG=\"$2\"\nTMP_MD5=\"${OUT_DIR}/_dup_md5\"\nTMP_SIZE=\"${OUT_DIR}/_dup_size\"\nTMP_CHECK=\"${OUT_DIR}/_dup_tmp_check\"\nTMP_SIZE_REAL=\"${OUT_DIR}/_dup_size_real\"\nTMP_FILE1=\"${OUT_DIR}/_dup_f1\"\nTMP_FILE2=\"${OUT_DIR}/_dup_f2\"\nMD5_DUPLICATES=\"${OUT_DIR}/duplicate-libs-md5-${IMG}.txt\"\nSIZE_DUPLICATES=\"${OUT_DIR}/duplicate-libs-size-${IMG}.txt\"\n\n# Check arguments\nif [ \"$#\" -ne 2 ]; then\n\techo \"Usage: ./dupcheck.sh <out_dir> <image>\"\n\texit 1\nfi\n\n# Check host and toolchain version\nCHECK_HOST=$(uname)\nif [ \"${CHECK_HOST}\" == \"Linux\" ]; then\n\tARCH=\"linux-x86\"\nelse\n\tARCH=\"darwin-x86\"\nfi\nBINUTILS_PATH=\"./prebuilts/clang/host/${ARCH}/llvm-binutils-stable\"\n\n# Remove any old files if they exist.\nif [ -f \"${MD5_DUPLICATES}\" ]; then\n\trm \"${MD5_DUPLICATES}\"\nfi\n\nif [ -f \"${SIZE_DUPLICATES}\" ]; then\n\trm \"${SIZE_DUPLICATES}\"\nfi\n\n# Find all .so files and calculate their md5.\nfind ./\"${OUT_DIR}\"/${IMG}/ -name \"lib*.so\" -type f -print0 | xargs -0 md5sum | sed -e \"s# .*/# #\" | sort | uniq -c | sort -g | sed \"/^.*1 /d\" | sed \"s/^. *[0-9] //\" > \"${TMP_MD5}\" 2>&1\n\nif [ -s \"${TMP_MD5}\" ]; then\n\twhile read -r list; do\n\t\tchecksum=$(echo \"${list}\" | cut -f1 -d ' ')\n\t\tfilename=$(echo \"${list}\" | cut -f2 -d ' ')\n\t\t# For each md5, list the file paths that match.\n\t\t{\n\t\t\techo \"MD5: ${checksum}\";\t\t\t\t\t\t\t\t\t\t\t                \\\n\t\t\tfind ./\"${OUT_DIR}\"/${IMG}/ -name \"${filename}\" -type f -print0 | xargs -0 md5sum | grep \"${checksum}\" | sed 's/^.* //';\t\\\n\t\t\techo \"\";\t\t\t\t\t\t\t\t\t\t\t\t\t                \\\n\t\t} >> \"${MD5_DUPLICATES}\"\n\tdone <\"${TMP_MD5}\"\nelse\n\techo \"No duplicate files by md5 found.\" >> \"${MD5_DUPLICATES}\"\nfi\n\n# Cleanup\nrm \"${TMP_MD5}\"\n\n# Find possible duplicate .so files by size.\nfind ./\"${OUT_DIR}\"/${IMG}/ -name \"*.so\" -type f -print0 | xargs -0 stat --format=\"%s %n\" 2>/dev/null | sed -e \"s# .*/# #\" | sort | uniq -c | sort -g | sed \"/^.*1 /d\" > \"${TMP_SIZE}\" 2>&1\nif [ -s \"${TMP_SIZE}\" ]; then\n\twhile read -r list; do\n\t\tsize=$(echo \"${list}\" | cut -f2 -d ' ')\n\t\tfilename=$(echo \"${list}\" | cut -f3 -d ' ')\n\t\t# Check if the files are not in the md5sum list and do nothing if that is the case.\n\t\tfind ./\"${OUT_DIR}\"/${IMG}/ -name \"${filename}\" -type f -print0 | xargs -0 stat --format=\"%s %n\" 2>/dev/null | grep \"${size}\" | sed \"s/^.* //\" | sort > \"${TMP_CHECK}\" 2>&1\n\t\twhile read -r filepath; do\n\t\t\tfound=$(grep -F \"${filepath}\" \"${MD5_DUPLICATES}\")\n\t\t\tif [ -z \"${found}\" ]; then\n\t\t\t\techo \"${filepath}\" >> \"${TMP_SIZE_REAL}\"\n\t\t\tfi\n\t\tdone<\"${TMP_CHECK}\"\n\t\t# For every duplication found, diff the .note and .text sections.\n\t\tif [ -s \"${TMP_SIZE_REAL}\" ]; then\n\t\t\t{\n\t\t\t\techo \"File: ${filename}, Size: ${size}\";\t\\\n\t\t\t\tcat \"${TMP_SIZE_REAL}\";\t\t\t\t\\\n\t\t\t\techo \"\";\t\t\t\t\t\\\n\t\t\t} >> \"${SIZE_DUPLICATES}\"\n\t\t\tcount=$(wc -l \"${TMP_SIZE_REAL}\" | cut -f1 -d ' ')\n\t\t\t# Limitation: this only works for file pairs. If more than two possible duplications are found, the user need to check manually\n\t\t\t# all the possible combinations using the llvm-readelf and llvm-objdump commands below.\n\t\t\tif [ \"${count}\" = 2 ]; then\n\t\t\t\tfile1=$(head -n 1 \"${TMP_SIZE_REAL}\")\n\t\t\t\tfile2=$(tail -n 1 \"${TMP_SIZE_REAL}\")\n\t\t\t\t# Check .note section\n\t\t\t\t${BINUTILS_PATH}/llvm-readelf --wide --notes \"${file1}\" > \"${TMP_FILE1}\" 2>&1\n\t\t\t\t${BINUTILS_PATH}/llvm-readelf --wide --notes \"${file2}\" > \"${TMP_FILE2}\" 2>&1\n\t\t\t\t{\n\t\t\t\t\tdiff -u \"${TMP_FILE1}\" \"${TMP_FILE2}\" | sed \"1d;2d;3d\";\t\\\n\t\t\t\t\techo \"\";\n\t\t\t\t} >> \"${SIZE_DUPLICATES}\"\n\t\t\t\t# Check .text section\n\t\t\t\t${BINUTILS_PATH}/llvm-objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn --section=.text \"${file1}\" | sed \"1d;2d\"> \"${TMP_FILE1}\" 2>&1\n\t\t\t\t${BINUTILS_PATH}/llvm-objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn --section=.text \"${file2}\" | sed \"1d;2d\"> \"${TMP_FILE2}\" 2>&1\n\t\t\t\t{\n\t\t\t\t\tdiff -u \"${TMP_FILE1}\" \"${TMP_FILE2}\" | sed \"1d;2d;3d\";\t\\\n\t\t\t\t\techo \"\";\n\t\t\t\t} >> \"${SIZE_DUPLICATES}\"\n\t\t\t\t# Cleanup\n\t\t\t\trm \"${TMP_FILE1}\" \"${TMP_FILE2}\"\n\t\t\telse\n\t\t\t\techo \"*Note: more than one duplicate. Manually verify all possible combinations.\" >> \"${SIZE_DUPLICATES}\"\n\t\t\tfi\n\t\t\trm \"${TMP_SIZE_REAL}\"\n\t\t\techo \"\" >> \"${SIZE_DUPLICATES}\"\n\t\tfi\n\tdone <\"${TMP_SIZE}\"\n\t# Cleanup\n\trm \"${TMP_SIZE}\" \"${TMP_CHECK}\"\nelse\n\techo \"No duplicate files by size found.\" >> \"${SIZE_DUPLICATES}\"\nfi\n"
  },
  {
    "path": "core/dynamic_binary.mk",
    "content": "###########################################################\n## Standard rules for building any target-side binaries\n## with dynamic linkage (dynamic libraries or executables\n## that link with dynamic libraries)\n##\n## Files including this file must define a rule to build\n## the target $(linked_module).\n###########################################################\n\n# This constraint means that we can hard-code any $(TARGET_*) variables.\nifdef LOCAL_IS_HOST_MODULE\n$(error This file should not be used to build host binaries.  Included by (or near) $(lastword $(filter-out config/%,$(MAKEFILE_LIST))))\nendif\n\n# The name of the target file, without any path prepended.\n# This duplicates logic from base_rules.mk because we need to\n# know its results before base_rules.mk is included.\ninclude $(BUILD_SYSTEM)/configure_module_stem.mk\n\nintermediates := $(call local-intermediates-dir,,$(LOCAL_2ND_ARCH_VAR_PREFIX))\n\n# Define the target that is the unmodified output of the linker.\n# The basename of this target must be the same as the final output\n# binary name, because it's used to set the \"soname\" in the binary.\n# The includer of this file will define a rule to build this target.\nlinked_module := $(intermediates)/LINKED/$(notdir $(my_installed_module_stem))\n\n# This tells binary.make to explicitly define the PRIVATE_ variables for\n# linked_module as well as for LOCAL_BUILT_MODULE.\nLOCAL_INTERMEDIATE_TARGETS := $(linked_module)\n\n###################################\ninclude $(BUILD_SYSTEM)/use_lld_setup.mk\ninclude $(BUILD_SYSTEM)/binary.mk\n###################################\n\nifdef LOCAL_INJECT_BSSL_HASH\ninject_module := $(intermediates)/INJECT_BSSL_HASH/$(notdir $(my_installed_module_stem))\nLOCAL_INTERMEDIATE_TARGETS += $(inject_module)\n$(inject_module): $(SOONG_HOST_OUT)/bin/bssl_inject_hash\n$(inject_module): $(linked_module)\n\t@echo \"target inject BSSL hash: $(PRIVATE_MODULE) ($@)\"\n\t$(SOONG_HOST_OUT)/bin/bssl_inject_hash -in-object $< -o $@\nelse\ninject_module := $(linked_module)\nendif\n\n###########################################################\n## Store a copy with symbols for symbolic debugging\n###########################################################\nifeq ($(LOCAL_UNSTRIPPED_PATH),)\nmy_unstripped_path := $(TARGET_OUT_UNSTRIPPED)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path))\nelse\nmy_unstripped_path := $(LOCAL_UNSTRIPPED_PATH)\nendif\nsymbolic_input := $(inject_module)\nsymbolic_output := $(my_unstripped_path)/$(my_installed_module_stem)\nelf_mapping_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(symbolic_output).textproto)\n\nALL_MODULES.$(my_register_name).SYMBOLIC_OUTPUT_PATH := $(symbolic_output)\nALL_MODULES.$(my_register_name).ELF_SYMBOL_MAPPING_PATH := $(elf_mapping_path)\n\n$(eval $(call copy-unstripped-elf-file-with-mapping,$(symbolic_input),$(symbolic_output),$(elf_mapping_path)))\n\n###########################################################\n## Store breakpad symbols\n###########################################################\n\nifeq ($(BREAKPAD_GENERATE_SYMBOLS),true)\nmy_breakpad_path := $(TARGET_OUT_BREAKPAD)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path))\nbreakpad_input := $(inject_module)\nbreakpad_output := $(my_breakpad_path)/$(my_installed_module_stem).sym\n$(breakpad_output) : $(breakpad_input) | $(BREAKPAD_DUMP_SYMS) $(PRIVATE_READELF)\n\t@echo \"target breakpad: $(PRIVATE_MODULE) ($@)\"\n\t@mkdir -p $(dir $@)\n\t$(hide) if $(PRIVATE_READELF) -S $< > /dev/null 2>&1 ; then \\\n\t  $(BREAKPAD_DUMP_SYMS) -c $< > $@ ; \\\n\telse \\\n\t  echo \"skipped for non-elf file.\"; \\\n\t  touch $@; \\\n\tfi\n$(LOCAL_BUILT_MODULE) : $(breakpad_output)\nendif\n\n###########################################################\n## Strip\n###########################################################\nstrip_input := $(inject_module)\nstrip_output := $(LOCAL_BUILT_MODULE)\n\n# Use an order-only dependency to ensure the unstripped file in the symbols\n# directory is copied when the module is built, but does not force the\n# module to be rebuilt when the symbols directory is cleaned by installclean.\n$(strip_output): | $(symbolic_output)\n\nmy_strip_module := $(firstword \\\n  $(LOCAL_STRIP_MODULE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) \\\n  $(LOCAL_STRIP_MODULE))\nifeq ($(my_strip_module),)\n  my_strip_module := mini-debug-info\nendif\n\nifeq ($(my_strip_module),false)\n  my_strip_module :=\nendif\n\nmy_strip_args :=\nifeq ($(my_strip_module),mini-debug-info)\n  my_strip_args += --keep-mini-debug-info\nelse ifeq ($(my_strip_module),keep_symbols)\n  my_strip_args += --keep-symbols\nendif\n\nifeq (,$(filter no_debuglink mini-debug-info,$(my_strip_module)))\n  ifneq ($(TARGET_BUILD_VARIANT),user)\n    my_strip_args += --add-gnu-debuglink\n  endif\nendif\n\nifeq ($($(my_prefix)OS),darwin)\n  # llvm-strip does not support Darwin Mach-O yet.\n  my_strip_args += --use-gnu-strip\nendif\n\nvalid_strip := mini-debug-info keep_symbols true no_debuglink\nifneq (,$(filter-out $(valid_strip),$(my_strip_module)))\n  $(call pretty-error,Invalid strip value $(my_strip_module), only one of $(valid_strip) allowed)\nendif\n\nifneq (,$(my_strip_module))\n  $(strip_output): PRIVATE_STRIP_ARGS := $(my_strip_args)\n  $(strip_output): PRIVATE_TOOLS_PREFIX := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)TOOLS_PREFIX)\n  $(strip_output): $(strip_input) $(SOONG_STRIP_PATH) $(XZ)\n\t@echo \"$($(PRIVATE_PREFIX)DISPLAY) Strip: $(PRIVATE_MODULE) ($@)\"\n\tCLANG_BIN=$(LLVM_PREBUILTS_PATH) \\\n\tCROSS_COMPILE=$(PRIVATE_TOOLS_PREFIX) \\\n\tXZ=$(XZ) \\\n\tCREATE_MINIDEBUGINFO=${CREATE_MINIDEBUGINFO} \\\n\t$(SOONG_STRIP_PATH) -i $< -o $@ -d $@.strip.d $(PRIVATE_STRIP_ARGS)\n  ifneq ($(HOST_OS),darwin)\n    $(strip_output): $(CREATE_MINIDEBUGINFO)\n  endif\n  $(call include-depfile,$(strip_output).strip.d,$(strip_output))\nelse\n  # Don't strip the binary, just copy it.  We can't skip this step\n  # because a copy of the binary must appear at LOCAL_BUILT_MODULE.\n  $(strip_output): $(strip_input)\n\t@echo \"target Unstripped: $(PRIVATE_MODULE) ($@)\"\n\t$(copy-file-to-target)\nendif # my_strip_module\n\n$(cleantarget): PRIVATE_CLEAN_FILES += \\\n    $(linked_module) \\\n    $(inject_module) \\\n    $(breakpad_output) \\\n    $(symbolic_output) \\\n    $(strip_output)\n"
  },
  {
    "path": "core/empty_test_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2017 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- No AndroidTest.xml was provided and the manifest does not include\n     instrumentation, hence this apk is not instrumentable.\n-->\n<configuration description=\"Empty Configuration\" />\n"
  },
  {
    "path": "core/envsetup.mk",
    "content": "# Variables we check:\n#     HOST_BUILD_TYPE = { release debug }\n#     TARGET_BUILD_TYPE = { release debug }\n# and we output a bunch of variables, see the case statement at\n# the bottom for the full list\n#     OUT_DIR is also set to \"out\" if it's not already set.\n#         this allows you to set it to somewhere else if you like\n#     SCAN_EXCLUDE_DIRS is an optional, whitespace separated list of\n#         directories that will also be excluded from full checkout tree\n#         searches for source or make files, in addition to OUT_DIR.\n#         This can be useful if you set OUT_DIR to be a different directory\n#         than other outputs of your build system.\n\n# Returns all words in $1 up to and including $2\ndefine find_and_earlier\n  $(strip $(if $(1),\n    $(firstword $(1))\n    $(if $(filter $(firstword $(1)),$(2)),,\n      $(call find_and_earlier,$(wordlist 2,$(words $(1)),$(1)),$(2)))))\nendef\n\n#$(warning $(call find_and_earlier,A B C,A))\n#$(warning $(call find_and_earlier,A B C,B))\n#$(warning $(call find_and_earlier,A B C,C))\n#$(warning $(call find_and_earlier,A B C,D))\n\n# Runs a starlark file, and sets all the variables in its top-level\n# variables_to_export_to_make variable as make variables.\n#\n# In order to avoid running starlark every time the stamp file is checked, we use\n# $(KATI_shell_no_rerun). Then, to make sure that we actually do rerun kati when\n# modifying the starlark files, we add the starlark files to the kati stamp file with\n# $(KATI_extra_file_deps).\n#\n# Arguments:\n#  $(1): A single starlark file to use as the entrypoint\n#  $(2): An optional list of starlark files to NOT include as kati dependencies.\n#  $(3): An optional list of extra flags to pass to rbcrun\ndefine run-starlark\n$(eval _starlark_results := $(OUT_DIR)/starlark_results/$(subst /,_,$(1)).mk)\n$(KATI_shell_no_rerun mkdir -p $(OUT_DIR)/starlark_results && $(OUT_DIR)/rbcrun --mode=make $(3) $(1) >$(_starlark_results) && touch -t 200001010000 $(_starlark_results))\n$(if $(filter-out 0,$(.SHELLSTATUS)),$(error Starlark failed to run))\n$(eval include $(_starlark_results))\n$(KATI_extra_file_deps $(filter-out $(2),$(LOADED_STARLARK_FILES)))\n$(eval LOADED_STARLARK_FILES :=)\n$(eval _starlark_results :=)\nendef\n\n# ---------------------------------------------------------------\n# Release config\ninclude $(BUILD_SYSTEM)/release_config.mk\n\n# ---------------------------------------------------------------\n# Set up version information\ninclude $(BUILD_SYSTEM)/version_util.mk\n\n# This used to be calculated, but is now fixed and not expected\n# to change over time anymore. New code attempting to use a\n# variable like IS_AT_LAST_* should instead use a\n# build system flag.\n\nENABLED_VERSIONS := \"OPR1 OPD1 OPD2 OPM1 OPM2 PPR1 PPD1 PPD2 PPM1 PPM2 QPR1 QP1A QP1B QP2A QP2B QD1A QD1B QD2A QD2B QQ1A QQ1B QQ2A QQ2B QQ3A QQ3B RP1A RP1B RP2A RP2B RD1A RD1B RD2A RD2B RQ1A RQ1B RQ2A RQ2B RQ3A RQ3B SP1A SP1B SP2A SP2B SD1A SD1B SD2A SD2B SQ1A SQ1B SQ2A SQ2B SQ3A SQ3B TP1A TP1B TP2A TP2B TD1A TD1B TD2A TD2B TQ1A TQ1B TQ2A TQ2B TQ3A TQ3B UP1A UP1B UP2A UP2B UD1A UD1B UD2A UD2B UQ1A UQ1B UQ2A UQ2B UQ3A UQ3B\"\n\n$(foreach v,$(ENABLED_VERSIONS), \\\n  $(eval IS_AT_LEAST_$(v) := true))\n\n# ---------------------------------------------------------------\n# If you update the build system such that the environment setup\n# or buildspec.mk need to be updated, increment this number, and\n# people who haven't re-run those will have to do so before they\n# can build.  Make sure to also update the corresponding value in\n# buildspec.mk.default and envsetup.sh.\nCORRECT_BUILD_ENV_SEQUENCE_NUMBER := 13\n\n# ---------------------------------------------------------------\n# The product defaults to generic on hardware\nifeq ($(TARGET_PRODUCT),)\nTARGET_PRODUCT := aosp_arm64\nendif\n\n\n# the variant -- the set of files that are included for a build\nifeq ($(strip $(TARGET_BUILD_VARIANT)),)\nTARGET_BUILD_VARIANT := eng\nendif\n\nTARGET_BUILD_APPS ?=\nTARGET_BUILD_UNBUNDLED_IMAGE ?=\n\n# Set to true for an unbundled build, i.e. a build without\n# support for platform targets like the system image. This also\n# disables consistency checks that only apply to full platform\n# builds.\nTARGET_BUILD_UNBUNDLED ?=\n\n# TARGET_BUILD_APPS implies unbundled build, otherwise we default\n# to bundled (i.e. platform targets such as the system image are\n# included).\nifneq ($(TARGET_BUILD_APPS),)\n  TARGET_BUILD_UNBUNDLED := true\nendif\n\n# TARGET_BUILD_UNBUNDLED_IMAGE also implies unbundled build.\n# (i.e. it targets to only unbundled image, such as the vendor image,\n# ,or the product image). \nifneq ($(TARGET_BUILD_UNBUNDLED_IMAGE),)\n  TARGET_BUILD_UNBUNDLED := true\nendif\n\n.KATI_READONLY := \\\n  TARGET_PRODUCT \\\n  TARGET_BUILD_VARIANT \\\n  TARGET_BUILD_APPS \\\n  TARGET_BUILD_UNBUNDLED \\\n  TARGET_BUILD_UNBUNDLED_IMAGE \\\n\n# ---------------------------------------------------------------\n# Set up configuration for host machine.  We don't do cross-\n# compiles except for arm, so the HOST is whatever we are\n# running on\n\n# HOST_OS\nifneq (,$(findstring Linux,$(UNAME)))\n  HOST_OS := linux\nendif\nifneq (,$(findstring Darwin,$(UNAME)))\n  HOST_OS := darwin\nendif\n\nifeq ($(CALLED_FROM_SETUP),true)\n  HOST_OS_EXTRA := $(shell uname -rsm)\n  ifeq ($(HOST_OS),linux)\n    ifneq ($(wildcard /etc/os-release),)\n      HOST_OS_EXTRA += $(shell source /etc/os-release; echo $$PRETTY_NAME)\n    endif\n  else ifeq ($(HOST_OS),darwin)\n    HOST_OS_EXTRA += $(shell sw_vers -productVersion)\n  endif\n  HOST_OS_EXTRA := $(subst $(space),-,$(HOST_OS_EXTRA))\nendif\n\n# BUILD_OS is the real host doing the build.\nBUILD_OS := $(HOST_OS)\n\n# We can do the cross-build only on Linux\nifeq ($(HOST_OS),linux)\n  # Windows has been the default host_cross OS\n  ifeq (,$(filter-out windows,$(HOST_CROSS_OS)))\n    # We can only create static host binaries for Linux, so if static host\n    # binaries are requested, turn off Windows cross-builds.\n    ifeq ($(BUILD_HOST_static),)\n      HOST_CROSS_OS := windows\n      HOST_CROSS_ARCH := x86\n      HOST_CROSS_2ND_ARCH := x86_64\n      2ND_HOST_CROSS_IS_64_BIT := true\n    endif\n  else ifeq ($(HOST_CROSS_OS),linux_bionic)\n    ifeq (,$(HOST_CROSS_ARCH))\n      $(error HOST_CROSS_ARCH missing.)\n    endif\n  else\n    $(error Unsupported HOST_CROSS_OS $(HOST_CROSS_OS))\n  endif\nelse ifeq ($(HOST_OS),darwin)\n  HOST_CROSS_OS := darwin\n  HOST_CROSS_ARCH := arm64\n  HOST_CROSS_2ND_ARCH :=\nendif\n\nifeq ($(HOST_OS),)\n$(error Unable to determine HOST_OS from uname -sm: $(UNAME)!)\nendif\n\n# HOST_ARCH\nifneq (,$(findstring x86_64,$(UNAME)))\n  HOST_ARCH := x86_64\n  HOST_2ND_ARCH := x86\n  HOST_IS_64_BIT := true\nelse\nifneq (,$(findstring i686,$(UNAME))$(findstring x86,$(UNAME)))\n$(error Building on a 32-bit x86 host is not supported: $(UNAME)!)\nendif\nendif\n\nifeq ($(HOST_OS),darwin)\n  # Mac no longer supports 32-bit executables\n  HOST_2ND_ARCH :=\nendif\n\nHOST_2ND_ARCH_VAR_PREFIX := 2ND_\nHOST_2ND_ARCH_MODULE_SUFFIX := _32\nHOST_CROSS_2ND_ARCH_VAR_PREFIX := 2ND_\nHOST_CROSS_2ND_ARCH_MODULE_SUFFIX := _64\nTARGET_2ND_ARCH_VAR_PREFIX := 2ND_\n.KATI_READONLY := \\\n  HOST_ARCH \\\n  HOST_2ND_ARCH \\\n  HOST_IS_64_BIT \\\n  HOST_2ND_ARCH_VAR_PREFIX \\\n  HOST_2ND_ARCH_MODULE_SUFFIX \\\n  HOST_CROSS_2ND_ARCH_VAR_PREFIX \\\n  HOST_CROSS_2ND_ARCH_MODULE_SUFFIX \\\n  TARGET_2ND_ARCH_VAR_PREFIX \\\n\ncombo_target := HOST_\ncombo_2nd_arch_prefix :=\ninclude $(BUILD_COMBOS)/select.mk\n\nifdef HOST_2ND_ARCH\n  combo_2nd_arch_prefix := $(HOST_2ND_ARCH_VAR_PREFIX)\n  include $(BUILD_SYSTEM)/combo/select.mk\nendif\n\n# Load the windows cross compiler under Linux\nifdef HOST_CROSS_OS\n  combo_target := HOST_CROSS_\n  combo_2nd_arch_prefix :=\n  include $(BUILD_SYSTEM)/combo/select.mk\n\n  ifdef HOST_CROSS_2ND_ARCH\n    combo_2nd_arch_prefix := $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)\n    include $(BUILD_SYSTEM)/combo/select.mk\n  endif\nendif\n\n# on windows, the tools have .exe at the end, and we depend on the\n# host config stuff being done first\n\nBUILD_ARCH := $(HOST_ARCH)\nBUILD_2ND_ARCH := $(HOST_2ND_ARCH)\n\nifeq ($(HOST_ARCH),)\n$(error Unable to determine HOST_ARCH from uname -sm: $(UNAME)!)\nendif\n\n# the host build defaults to release, and it must be release or debug\nifeq ($(HOST_BUILD_TYPE),)\nHOST_BUILD_TYPE := release\nendif\n\nifneq ($(HOST_BUILD_TYPE),release)\nifneq ($(HOST_BUILD_TYPE),debug)\n$(error HOST_BUILD_TYPE must be either release or debug, not '$(HOST_BUILD_TYPE)')\nendif\nendif\n\n# We don't want to move all the prebuilt host tools to a $(HOST_OS)-x86_64 dir.\nHOST_PREBUILT_ARCH := x86\n# This is the standard way to name a directory containing prebuilt host\n# objects. E.g., prebuilt/$(HOST_PREBUILT_TAG)/cc\n# This must match the logic in get_host_prebuilt_prefix in envsetup.sh\nHOST_PREBUILT_TAG := $(BUILD_OS)-$(HOST_PREBUILT_ARCH)\n\n# TARGET_COPY_OUT_* are all relative to the staging directory, ie PRODUCT_OUT.\n# Define them here so they can be used in product config files.\nTARGET_COPY_OUT_SYSTEM := system\nTARGET_COPY_OUT_SYSTEM_DLKM := system_dlkm\nTARGET_COPY_OUT_SYSTEM_OTHER := system_other\nTARGET_COPY_OUT_DATA := data\nTARGET_COPY_OUT_ASAN := $(TARGET_COPY_OUT_DATA)/asan\nTARGET_COPY_OUT_OEM := oem\nTARGET_COPY_OUT_RAMDISK := ramdisk\nTARGET_COPY_OUT_DEBUG_RAMDISK := debug_ramdisk\nTARGET_COPY_OUT_VENDOR_DEBUG_RAMDISK := vendor_debug_ramdisk\nTARGET_COPY_OUT_TEST_HARNESS_RAMDISK := test_harness_ramdisk\nTARGET_COPY_OUT_ROOT := root\nTARGET_COPY_OUT_RECOVERY := recovery\n# The directory used for optional partitions depend on the BoardConfig, so\n# they're defined to placeholder values here and swapped after reading the\n# BoardConfig, to be either the partition dir, or a subdir within 'system'.\n_vendor_path_placeholder := ||VENDOR-PATH-PH||\n_product_path_placeholder := ||PRODUCT-PATH-PH||\n_system_ext_path_placeholder := ||SYSTEM_EXT-PATH-PH||\n_odm_path_placeholder := ||ODM-PATH-PH||\n_vendor_dlkm_path_placeholder := ||VENDOR_DLKM-PATH-PH||\n_odm_dlkm_path_placeholder := ||ODM_DLKM-PATH-PH||\n_system_dlkm_path_placeholder := ||SYSTEM_DLKM-PATH-PH||\nTARGET_COPY_OUT_VENDOR := $(_vendor_path_placeholder)\nTARGET_COPY_OUT_VENDOR_RAMDISK := vendor_ramdisk\nTARGET_COPY_OUT_VENDOR_KERNEL_RAMDISK := vendor_kernel_ramdisk\nTARGET_COPY_OUT_PRODUCT := $(_product_path_placeholder)\n# TODO(b/135957588) TARGET_COPY_OUT_PRODUCT_SERVICES will copy the target to\n# product\nTARGET_COPY_OUT_PRODUCT_SERVICES := $(_product_path_placeholder)\nTARGET_COPY_OUT_SYSTEM_EXT := $(_system_ext_path_placeholder)\nTARGET_COPY_OUT_ODM := $(_odm_path_placeholder)\nTARGET_COPY_OUT_VENDOR_DLKM := $(_vendor_dlkm_path_placeholder)\nTARGET_COPY_OUT_ODM_DLKM := $(_odm_dlkm_path_placeholder)\nTARGET_COPY_OUT_SYSTEM_DLKM := $(_system_dlkm_path_placeholder)\n\n# Returns the non-sanitized version of the path provided in $1.\ndefine get_non_asan_path\n$(patsubst $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/%,$(PRODUCT_OUT)/%,$1)\nendef\n\n#################################################################\n# Set up minimal BOOTCLASSPATH list of jars to build/execute\n# java code with dalvikvm/art.\n# Jars present in the ART apex. These should match exactly the list of Java\n# libraries in art-bootclasspath-fragment. The APEX variant name\n# (com.android.art) is the same regardless which Soong module provides the ART\n# APEX. See the long comment in build/soong/java/dexprepopt_bootjars.go for\n# details.\nART_APEX_JARS := \\\n    com.android.art:core-oj \\\n    com.android.art:core-libart \\\n    com.android.art:okhttp \\\n    com.android.art:bouncycastle \\\n    com.android.art:apache-xml\n# With EMMA_INSTRUMENT_FRAMEWORK=true the Core libraries depend on jacoco.\nifeq (true,$(EMMA_INSTRUMENT_FRAMEWORK))\n  ART_APEX_JARS += com.android.art:jacocoagent\nendif\n#################################################################\n\n# Dumps all variables that match [A-Z][A-Z0-9_]* (with a few exceptions)\n# to the file at $(1). It is used to print only the variables that are\n# likely to be relevant to the product or board configuration.\n# Soong config variables are dumped as $(call soong_config_set) calls\n# instead of the raw variable values, because mk2rbc can't read the\n# raw ones. There is a final sed command on the output file to\n# remove leading spaces because I couldn't figure out how to remove\n# them in pure make code.\ndefine dump-variables-rbc\n$(eval _dump_variables_rbc_excluded := \\\n  BUILD_NUMBER \\\n  DATE \\\n  LOCAL_PATH \\\n  MAKEFILE_LIST \\\n  PRODUCTS \\\n  PRODUCT_COPY_OUT_% \\\n  RBC_PRODUCT_CONFIG \\\n  RBC_BOARD_CONFIG \\\n  SOONG_% \\\n  TARGET_RELEASE \\\n  TOPDIR \\\n  TRACE_BEGIN_SOONG \\\n  USER)\n$(file >$(OUT_DIR)/dump-variables-rbc-temp.txt,$(subst $(space),$(newline),$(sort $(filter-out $(_dump_variables_rbc_excluded),$(.VARIABLES)))))\n$(file >$(1),\\\n$(foreach v, $(shell grep -he \"^[A-Z][A-Z0-9_]*$$\" $(OUT_DIR)/dump-variables-rbc-temp.txt),\\\n$(v) := $(strip $($(v)))$(newline))\\\n$(foreach ns,$(sort $(SOONG_CONFIG_NAMESPACES)),\\\n$(foreach v,$(sort $(SOONG_CONFIG_$(ns))),\\\n$$(call soong_config_set,$(ns),$(v),$(SOONG_CONFIG_$(ns)_$(v)))$(newline))))\n$(shell sed -i \"s/^ *//g\" $(1))\nendef\n\n# Read the product specs so we can get TARGET_DEVICE and other\n# variables that we need in order to locate the output files.\ninclude $(BUILD_SYSTEM)/product_config.mk\n\nbuild_variant := $(filter-out eng user userdebug,$(TARGET_BUILD_VARIANT))\nifneq ($(build_variant)-$(words $(TARGET_BUILD_VARIANT)),-1)\n$(warning bad TARGET_BUILD_VARIANT: $(TARGET_BUILD_VARIANT))\n$(error must be empty or one of: eng user userdebug)\nendif\n\nSDK_HOST_ARCH := x86\nTARGET_OS := linux\n\n# Some board configuration files use $(PRODUCT_OUT)\nTARGET_OUT_ROOT := $(OUT_DIR)/target\nTARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/product\nPRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE)\n.KATI_READONLY := TARGET_OUT_ROOT TARGET_PRODUCT_OUT_ROOT PRODUCT_OUT\n\ninclude $(BUILD_SYSTEM)/board_config.mk\n\n# the target build type defaults to release\nifneq ($(TARGET_BUILD_TYPE),debug)\nTARGET_BUILD_TYPE := release\nendif\n\ninclude $(BUILD_SYSTEM)/product_validation_checks.mk\n\n# ---------------------------------------------------------------\n# figure out the output directories\n\nSOONG_OUT_DIR := $(OUT_DIR)/soong\n\nHOST_OUT_ROOT := $(OUT_DIR)/host\n\n.KATI_READONLY := SOONG_OUT_DIR HOST_OUT_ROOT\n\n# We want to avoid two host bin directories in multilib build.\nHOST_OUT := $(HOST_OUT_ROOT)/$(HOST_OS)-$(HOST_PREBUILT_ARCH)\n\n# Soong now installs to the same directory as Make.\nSOONG_HOST_OUT := $(HOST_OUT)\n\nHOST_CROSS_OUT := $(HOST_OUT_ROOT)/$(HOST_CROSS_OS)-$(HOST_CROSS_ARCH)\n\n.KATI_READONLY := HOST_OUT SOONG_HOST_OUT HOST_CROSS_OUT\n\nTARGET_COMMON_OUT_ROOT := $(TARGET_OUT_ROOT)/common\nHOST_COMMON_OUT_ROOT := $(HOST_OUT_ROOT)/common\n\n.KATI_READONLY := TARGET_COMMON_OUT_ROOT HOST_COMMON_OUT_ROOT\n\nOUT_DOCS := $(TARGET_COMMON_OUT_ROOT)/docs\nOUT_NDK_DOCS := $(TARGET_COMMON_OUT_ROOT)/ndk-docs\n.KATI_READONLY := OUT_DOCS OUT_NDK_DOCS\n\n$(call KATI_obsolete,BUILD_OUT,Use HOST_OUT instead)\n\nBUILD_OUT_EXECUTABLES := $(HOST_OUT)/bin\nSOONG_HOST_OUT_EXECUTABLES := $(SOONG_HOST_OUT)/bin\n.KATI_READONLY := BUILD_OUT_EXECUTABLES SOONG_HOST_OUT_EXECUTABLES\n\nHOST_OUT_EXECUTABLES := $(HOST_OUT)/bin\nHOST_OUT_SHARED_LIBRARIES := $(HOST_OUT)/lib64\nHOST_OUT_DYLIB_LIBRARIES := $(HOST_OUT)/lib64\nHOST_OUT_RENDERSCRIPT_BITCODE := $(HOST_OUT_SHARED_LIBRARIES)\nHOST_OUT_JAVA_LIBRARIES := $(HOST_OUT)/framework\nHOST_OUT_SDK_ADDON := $(HOST_OUT)/sdk_addon\nHOST_OUT_NATIVE_TESTS := $(HOST_OUT)/nativetest64\nHOST_OUT_COVERAGE := $(HOST_OUT)/coverage\nHOST_OUT_TESTCASES := $(HOST_OUT)/testcases\nHOST_OUT_ETC := $(HOST_OUT)/etc\n.KATI_READONLY := \\\n  HOST_OUT_EXECUTABLES \\\n  HOST_OUT_SHARED_LIBRARIES \\\n  HOST_OUT_RENDERSCRIPT_BITCODE \\\n  HOST_OUT_JAVA_LIBRARIES \\\n  HOST_OUT_SDK_ADDON \\\n  HOST_OUT_NATIVE_TESTS \\\n  HOST_OUT_COVERAGE \\\n  HOST_OUT_TESTCASES \\\n  HOST_OUT_ETC\n\nHOST_CROSS_OUT_EXECUTABLES := $(HOST_CROSS_OUT)/bin\nHOST_CROSS_OUT_SHARED_LIBRARIES := $(HOST_CROSS_OUT)/lib\nHOST_CROSS_OUT_NATIVE_TESTS := $(HOST_CROSS_OUT)/nativetest\nHOST_CROSS_OUT_COVERAGE := $(HOST_CROSS_OUT)/coverage\nHOST_CROSS_OUT_TESTCASES := $(HOST_CROSS_OUT)/testcases\n.KATI_READONLY := \\\n  HOST_CROSS_OUT_EXECUTABLES \\\n  HOST_CROSS_OUT_SHARED_LIBRARIES \\\n  HOST_CROSS_OUT_NATIVE_TESTS \\\n  HOST_CROSS_OUT_COVERAGE \\\n  HOST_CROSS_OUT_TESTCASES\n\nHOST_OUT_INTERMEDIATES := $(HOST_OUT)/obj\nHOST_OUT_NOTICE_FILES := $(HOST_OUT_INTERMEDIATES)/NOTICE_FILES\nHOST_OUT_COMMON_INTERMEDIATES := $(HOST_COMMON_OUT_ROOT)/obj\nHOST_OUT_FAKE := $(HOST_OUT)/fake_packages\n.KATI_READONLY := \\\n  HOST_OUT_INTERMEDIATES \\\n  HOST_OUT_NOTICE_FILES \\\n  HOST_OUT_COMMON_INTERMEDIATES \\\n  HOST_OUT_FAKE\n\nHOST_CROSS_OUT_INTERMEDIATES := $(HOST_CROSS_OUT)/obj\nHOST_CROSS_OUT_NOTICE_FILES := $(HOST_CROSS_OUT_INTERMEDIATES)/NOTICE_FILES\n.KATI_READONLY := \\\n  HOST_CROSS_OUT_INTERMEDIATES \\\n  HOST_CROSS_OUT_NOTICE_FILES\n\nHOST_OUT_GEN := $(HOST_OUT)/gen\nHOST_OUT_COMMON_GEN := $(HOST_COMMON_OUT_ROOT)/gen\n.KATI_READONLY := \\\n  HOST_OUT_GEN \\\n  HOST_OUT_COMMON_GEN\n\nHOST_CROSS_OUT_GEN := $(HOST_CROSS_OUT)/gen\n.KATI_READONLY := HOST_CROSS_OUT_GEN\n\n# Out for HOST_2ND_ARCH\n$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_INTERMEDIATES := $(HOST_OUT)/obj32\n$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES := $(HOST_OUT)/lib\n$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_EXECUTABLES := $(HOST_OUT_EXECUTABLES)\n$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_JAVA_LIBRARIES := $(HOST_OUT_JAVA_LIBRARIES)\n$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_NATIVE_TESTS := $(HOST_OUT)/nativetest\n$(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_TESTCASES := $(HOST_OUT_TESTCASES)\n.KATI_READONLY := \\\n  $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_INTERMEDIATES \\\n  $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES \\\n  $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_EXECUTABLES \\\n  $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_JAVA_LIBRARIES \\\n  $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_NATIVE_TESTS \\\n  $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_TESTCASES\n\n# The default host library path.\n# It always points to the path where we build libraries in the default bitness.\nHOST_LIBRARY_PATH := $(HOST_OUT_SHARED_LIBRARIES)\n.KATI_READONLY := HOST_LIBRARY_PATH\n\n# Out for HOST_CROSS_2ND_ARCH\n$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_INTERMEDIATES := $(HOST_CROSS_OUT)/obj64\n$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_SHARED_LIBRARIES := $(HOST_CROSS_OUT)/lib64\n$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_EXECUTABLES := $(HOST_CROSS_OUT_EXECUTABLES)\n$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_NATIVE_TESTS := $(HOST_CROSS_OUT)/nativetest64\n.KATI_READONLY := \\\n  $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_INTERMEDIATES \\\n  $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_SHARED_LIBRARIES \\\n  $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_EXECUTABLES \\\n  $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_NATIVE_TESTS\n\nifneq ($(filter address,$(SANITIZE_TARGET)),)\n  TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj_asan\nelse\n  TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj\nendif\nTARGET_OUT_HEADERS := $(TARGET_OUT_INTERMEDIATES)/include\n.KATI_READONLY := TARGET_OUT_INTERMEDIATES TARGET_OUT_HEADERS\n\nifneq ($(filter address,$(SANITIZE_TARGET)),)\n  TARGET_OUT_COMMON_INTERMEDIATES := $(TARGET_COMMON_OUT_ROOT)/obj_asan\nelse\n  TARGET_OUT_COMMON_INTERMEDIATES := $(TARGET_COMMON_OUT_ROOT)/obj\nendif\n.KATI_READONLY := TARGET_OUT_COMMON_INTERMEDIATES\n\nTARGET_OUT_GEN := $(PRODUCT_OUT)/gen\nTARGET_OUT_COMMON_GEN := $(TARGET_COMMON_OUT_ROOT)/gen\n.KATI_READONLY := TARGET_OUT_GEN TARGET_OUT_COMMON_GEN\n\nTARGET_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM)\n.KATI_READONLY := TARGET_OUT\nifneq ($(filter address,$(SANITIZE_TARGET)),)\ntarget_out_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/system\nifeq ($(SANITIZE_LITE),true)\n# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not\n# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/.\ntarget_out_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/system\nelse\ntarget_out_app_base := $(TARGET_OUT)\nendif\nelse\ntarget_out_shared_libraries_base := $(TARGET_OUT)\ntarget_out_app_base := $(TARGET_OUT)\nendif\n\nTARGET_OUT_EXECUTABLES := $(TARGET_OUT)/bin\nTARGET_OUT_OPTIONAL_EXECUTABLES := $(TARGET_OUT)/xbin\nifeq ($(TARGET_IS_64_BIT),true)\n# /system/lib always contains 32-bit libraries,\n# and /system/lib64 (if present) always contains 64-bit libraries.\nTARGET_OUT_SHARED_LIBRARIES := $(target_out_shared_libraries_base)/lib64\nelse\nTARGET_OUT_SHARED_LIBRARIES := $(target_out_shared_libraries_base)/lib\nendif\nTARGET_OUT_RENDERSCRIPT_BITCODE := $(TARGET_OUT_SHARED_LIBRARIES)\nTARGET_OUT_JAVA_LIBRARIES := $(TARGET_OUT)/framework\nTARGET_OUT_APPS := $(target_out_app_base)/app\nTARGET_OUT_APPS_PRIVILEGED := $(target_out_app_base)/priv-app\nTARGET_OUT_KEYLAYOUT := $(TARGET_OUT)/usr/keylayout\nTARGET_OUT_KEYCHARS := $(TARGET_OUT)/usr/keychars\nTARGET_OUT_ETC := $(TARGET_OUT)/etc\nTARGET_OUT_NOTICE_FILES := $(TARGET_OUT_INTERMEDIATES)/NOTICE_FILES\nTARGET_OUT_FAKE := $(PRODUCT_OUT)/fake_packages\nTARGET_OUT_TESTCASES := $(PRODUCT_OUT)/testcases\nTARGET_OUT_FLAGS := $(TARGET_OUT_INTERMEDIATES)/FLAGS\n\n.KATI_READONLY := \\\n  TARGET_OUT_EXECUTABLES \\\n  TARGET_OUT_OPTIONAL_EXECUTABLES \\\n  TARGET_OUT_SHARED_LIBRARIES \\\n  TARGET_OUT_RENDERSCRIPT_BITCODE \\\n  TARGET_OUT_JAVA_LIBRARIES \\\n  TARGET_OUT_APPS \\\n  TARGET_OUT_APPS_PRIVILEGED \\\n  TARGET_OUT_KEYLAYOUT \\\n  TARGET_OUT_KEYCHARS \\\n  TARGET_OUT_ETC \\\n  TARGET_OUT_NOTICE_FILES \\\n  TARGET_OUT_FAKE \\\n  TARGET_OUT_TESTCASES \\\n  TARGET_OUT_FLAGS\n\nifeq ($(SANITIZE_LITE),true)\n# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not\n# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/.\nTARGET_OUT_SYSTEM_OTHER := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_SYSTEM_OTHER)\nelse\nTARGET_OUT_SYSTEM_OTHER := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_OTHER)\nendif\n.KATI_READONLY := TARGET_OUT_SYSTEM_OTHER\n\n# Out for TARGET_2ND_ARCH\nTARGET_2ND_ARCH_MODULE_SUFFIX := $(HOST_2ND_ARCH_MODULE_SUFFIX)\n.KATI_READONLY := TARGET_2ND_ARCH_MODULE_SUFFIX\n\nifneq ($(filter address,$(SANITIZE_TARGET)),)\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj_$(TARGET_2ND_ARCH)_asan\nelse\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj_$(TARGET_2ND_ARCH)\nendif\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES := $(target_out_shared_libraries_base)/lib\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_RENDERSCRIPT_BITCODE := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_EXECUTABLES := $(TARGET_OUT_EXECUTABLES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS := $(TARGET_OUT_APPS)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS_PRIVILEGED := $(TARGET_OUT_APPS_PRIVILEGED)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_TESTCASES := $(TARGET_OUT_TESTCASES)\n.KATI_READONLY := \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_RENDERSCRIPT_BITCODE \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_EXECUTABLES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS_PRIVILEGED \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_TESTCASES\n\nMODULE_CLASS_APPS := app\nMODULE_CLASS_EXECUTABLES := bin\nMODULE_CLASS_JAVA_LIBRARIES := framework\nMODULE_CLASS_NATIVE_TESTS := nativetest\nMODULE_CLASS_METRIC_TESTS := benchmarktest\nTARGET_OUT_DATA := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DATA)\nTARGET_OUT_DATA_EXECUTABLES := $(TARGET_OUT_EXECUTABLES)\nTARGET_OUT_DATA_SHARED_LIBRARIES := $(TARGET_OUT_SHARED_LIBRARIES)\nTARGET_OUT_DATA_JAVA_LIBRARIES := $(TARGET_OUT_DATA)/framework\nTARGET_OUT_DATA_APPS := $(TARGET_OUT_DATA)/app\nTARGET_OUT_DATA_KEYLAYOUT := $(TARGET_OUT_KEYLAYOUT)\nTARGET_OUT_DATA_KEYCHARS := $(TARGET_OUT_KEYCHARS)\nTARGET_OUT_DATA_ETC := $(TARGET_OUT_ETC)\nifeq ($(TARGET_IS_64_BIT),true)\nTARGET_OUT_DATA_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest64\nTARGET_OUT_DATA_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest64\nTARGET_OUT_VENDOR_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest64$(TARGET_VENDOR_TEST_SUFFIX)\nTARGET_OUT_VENDOR_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest64$(TARGET_VENDOR_TEST_SUFFIX)\nelse\nTARGET_OUT_DATA_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest\nTARGET_OUT_DATA_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest\nTARGET_OUT_VENDOR_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest$(TARGET_VENDOR_TEST_SUFFIX)\nTARGET_OUT_VENDOR_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest$(TARGET_VENDOR_TEST_SUFFIX)\nendif\nMODULE_CLASS_FAKE := fake_packages\nTARGET_OUT_DATA_FAKE := $(TARGET_OUT_DATA)/fake_packages\n.KATI_READONLY := \\\n  TARGET_OUT_DATA \\\n  TARGET_OUT_DATA_EXECUTABLES \\\n  TARGET_OUT_DATA_SHARED_LIBRARIES \\\n  TARGET_OUT_DATA_JAVA_LIBRARIES \\\n  TARGET_OUT_DATA_APPS \\\n  TARGET_OUT_DATA_KEYLAYOUT \\\n  TARGET_OUT_DATA_KEYCHARS \\\n  TARGET_OUT_DATA_ETC \\\n  TARGET_OUT_DATA_NATIVE_TESTS \\\n  TARGET_OUT_DATA_METRIC_TESTS \\\n  TARGET_OUT_VENDOR_NATIVE_TESTS \\\n  TARGET_OUT_VENDOR_METRIC_TESTS \\\n  TARGET_OUT_DATA_FAKE \\\n  MODULE_CLASS_APPS \\\n  MODULE_CLASS_EXECUTABLES \\\n  MODULE_CLASS_JAVA_LIBRARIES \\\n  MODULE_CLASS_NATIVE_TESTS \\\n  MODULE_CLASS_METRIC_TESTS \\\n  MODULE_CLASS_FAKE\n\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_EXECUTABLES := $(TARGET_OUT_DATA_EXECUTABLES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_SHARED_LIBRARIES := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_APPS := $(TARGET_OUT_DATA_APPS)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest$(TARGET_VENDOR_TEST_SUFFIX)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest$(TARGET_VENDOR_TEST_SUFFIX)\n.KATI_READONLY := \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_EXECUTABLES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_SHARED_LIBRARIES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_APPS \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_METRIC_TESTS \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_NATIVE_TESTS \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_METRIC_TESTS \\\n\nTARGET_OUT_CACHE := $(PRODUCT_OUT)/cache\n.KATI_READONLY := TARGET_OUT_CACHE\n\nTARGET_OUT_VENDOR := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)\n.KATI_READONLY := TARGET_OUT_VENDOR\nifneq ($(filter address,$(SANITIZE_TARGET)),)\ntarget_out_vendor_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_VENDOR)\nifeq ($(SANITIZE_LITE),true)\n# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not\n# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/.\ntarget_out_vendor_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_VENDOR)\nelse\ntarget_out_vendor_app_base := $(TARGET_OUT_VENDOR)\nendif\nelse\ntarget_out_vendor_shared_libraries_base := $(TARGET_OUT_VENDOR)\ntarget_out_vendor_app_base := $(TARGET_OUT_VENDOR)\nendif\n\nTARGET_OUT_VENDOR_EXECUTABLES := $(TARGET_OUT_VENDOR)/bin\nTARGET_OUT_VENDOR_OPTIONAL_EXECUTABLES := $(TARGET_OUT_VENDOR)/xbin\nifeq ($(TARGET_IS_64_BIT),true)\nTARGET_OUT_VENDOR_SHARED_LIBRARIES := $(target_out_vendor_shared_libraries_base)/lib64\nelse\nTARGET_OUT_VENDOR_SHARED_LIBRARIES := $(target_out_vendor_shared_libraries_base)/lib\nendif\nTARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)\nTARGET_OUT_VENDOR_JAVA_LIBRARIES := $(TARGET_OUT_VENDOR)/framework\nTARGET_OUT_VENDOR_APPS := $(target_out_vendor_app_base)/app\nTARGET_OUT_VENDOR_APPS_PRIVILEGED := $(target_out_vendor_app_base)/priv-app\nTARGET_OUT_VENDOR_ETC := $(TARGET_OUT_VENDOR)/etc\nTARGET_OUT_VENDOR_FAKE := $(PRODUCT_OUT)/vendor_fake_packages\n.KATI_READONLY := \\\n  TARGET_OUT_VENDOR_EXECUTABLES \\\n  TARGET_OUT_VENDOR_OPTIONAL_EXECUTABLES \\\n  TARGET_OUT_VENDOR_SHARED_LIBRARIES \\\n  TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE \\\n  TARGET_OUT_VENDOR_JAVA_LIBRARIES \\\n  TARGET_OUT_VENDOR_APPS \\\n  TARGET_OUT_VENDOR_APPS_PRIVILEGED \\\n  TARGET_OUT_VENDOR_ETC \\\n  TARGET_OUT_VENDOR_FAKE\n\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_EXECUTABLES := $(TARGET_OUT_VENDOR_EXECUTABLES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES := $(target_out_vendor_shared_libraries_base)/lib\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS := $(TARGET_OUT_VENDOR_APPS)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS_PRIVILEGED := $(TARGET_OUT_VENDOR_APPS_PRIVILEGED)\n.KATI_READONLY := \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_EXECUTABLES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS_PRIVILEGED\n\nTARGET_OUT_OEM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_OEM)\nTARGET_OUT_OEM_EXECUTABLES := $(TARGET_OUT_OEM)/bin\nifeq ($(TARGET_IS_64_BIT),true)\nTARGET_OUT_OEM_SHARED_LIBRARIES := $(TARGET_OUT_OEM)/lib64\nelse\nTARGET_OUT_OEM_SHARED_LIBRARIES := $(TARGET_OUT_OEM)/lib\nendif\n# We don't expect Java libraries in the oem.img.\n# TARGET_OUT_OEM_JAVA_LIBRARIES:= $(TARGET_OUT_OEM)/framework\nTARGET_OUT_OEM_APPS := $(TARGET_OUT_OEM)/app\nTARGET_OUT_OEM_ETC := $(TARGET_OUT_OEM)/etc\n.KATI_READONLY := \\\n  TARGET_OUT_OEM \\\n  TARGET_OUT_OEM_EXECUTABLES \\\n  TARGET_OUT_OEM_SHARED_LIBRARIES \\\n  TARGET_OUT_OEM_APPS \\\n  TARGET_OUT_OEM_ETC\n\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_EXECUTABLES := $(TARGET_OUT_OEM_EXECUTABLES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_SHARED_LIBRARIES := $(TARGET_OUT_OEM)/lib\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_APPS := $(TARGET_OUT_OEM_APPS)\n.KATI_READONLY := \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_EXECUTABLES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_SHARED_LIBRARIES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_APPS \\\n\nTARGET_OUT_ODM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ODM)\nifneq ($(filter address,$(SANITIZE_TARGET)),)\ntarget_out_odm_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_OEM)\nifeq ($(SANITIZE_LITE),true)\n# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not\n# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/.\ntarget_out_odm_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_OEM)\nelse\ntarget_out_odm_app_base := $(TARGET_OUT_ODM)\nendif\nelse\ntarget_out_odm_shared_libraries_base := $(TARGET_OUT_ODM)\ntarget_out_odm_app_base := $(TARGET_OUT_ODM)\nendif\n\nTARGET_OUT_ODM_EXECUTABLES := $(TARGET_OUT_ODM)/bin\nTARGET_OUT_ODM_OPTIONAL_EXECUTABLES := $(TARGET_OUT_ODM)/xbin\nifeq ($(TARGET_IS_64_BIT),true)\nTARGET_OUT_ODM_SHARED_LIBRARIES := $(target_out_odm_shared_libraries_base)/lib64\nelse\nTARGET_OUT_ODM_SHARED_LIBRARIES := $(target_out_odm_shared_libraries_base)/lib\nendif\nTARGET_OUT_ODM_RENDERSCRIPT_BITCODE := $(TARGET_OUT_ODM_SHARED_LIBRARIES)\nTARGET_OUT_ODM_JAVA_LIBRARIES := $(TARGET_OUT_ODM)/framework\nTARGET_OUT_ODM_APPS := $(target_out_odm_app_base)/app\nTARGET_OUT_ODM_APPS_PRIVILEGED := $(target_out_odm_app_base)/priv-app\nTARGET_OUT_ODM_ETC := $(TARGET_OUT_ODM)/etc\nTARGET_OUT_ODM_FAKE := $(PRODUCT_OUT)/odm_fake_packages\n.KATI_READONLY := \\\n  TARGET_OUT_ODM \\\n  TARGET_OUT_ODM_EXECUTABLES \\\n  TARGET_OUT_ODM_OPTIONAL_EXECUTABLES \\\n  TARGET_OUT_ODM_SHARED_LIBRARIES \\\n  TARGET_OUT_ODM_RENDERSCRIPT_BITCODE \\\n  TARGET_OUT_ODM_JAVA_LIBRARIES \\\n  TARGET_OUT_ODM_APPS \\\n  TARGET_OUT_ODM_APPS_PRIVILEGED \\\n  TARGET_OUT_ODM_ETC \\\n  TARGET_OUT_ODM_FAKE\n\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_EXECUTABLES := $(TARGET_OUT_ODM_EXECUTABLES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_SHARED_LIBRARIES := $(target_out_odm_shared_libraries_base)/lib\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_RENDERSCRIPT_BITCODE := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_SHARED_LIBRARIES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS := $(TARGET_OUT_ODM_APPS)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS_PRIVILEGED := $(TARGET_OUT_ODM_APPS_PRIVILEGED)\n.KATI_READONLY := \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_EXECUTABLES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_SHARED_LIBRARIES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_RENDERSCRIPT_BITCODE \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS_PRIVILEGED\n\nTARGET_OUT_VENDOR_DLKM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_DLKM)\n\nTARGET_OUT_VENDOR_DLKM_ETC := $(TARGET_OUT_VENDOR_DLKM)/etc\n.KATI_READONLY := \\\n  TARGET_OUT_VENDOR_DLKM_ETC\n\n# Unlike other partitions, vendor_dlkm should only contain kernel modules.\nTARGET_OUT_VENDOR_DLKM_EXECUTABLES :=\nTARGET_OUT_VENDOR_DLKM_OPTIONAL_EXECUTABLES :=\nTARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES :=\nTARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE :=\nTARGET_OUT_VENDOR_DLKM_JAVA_LIBRARIES :=\nTARGET_OUT_VENDOR_DLKM_APPS :=\nTARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_EXECUTABLES :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED :=\n$(KATI_obsolete_var \\\n    TARGET_OUT_VENDOR_DLKM_EXECUTABLES \\\n    TARGET_OUT_VENDOR_DLKM_OPTIONAL_EXECUTABLES \\\n    TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES \\\n    TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE \\\n    TARGET_OUT_VENDOR_DLKM_JAVA_LIBRARIES \\\n    TARGET_OUT_VENDOR_DLKM_APPS \\\n    TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_EXECUTABLES \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED \\\n    , vendor_dlkm should not contain any executables, libraries, or apps)\n\nTARGET_OUT_ODM_DLKM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ODM_DLKM)\n\nTARGET_OUT_ODM_DLKM_ETC := $(TARGET_OUT_ODM_DLKM)/etc\n.KATI_READONLY := \\\n  TARGET_OUT_ODM_DLKM_ETC\n\n# Unlike other partitions, odm_dlkm should only contain kernel modules.\nTARGET_OUT_ODM_DLKM_EXECUTABLES :=\nTARGET_OUT_ODM_DLKM_OPTIONAL_EXECUTABLES :=\nTARGET_OUT_ODM_DLKM_SHARED_LIBRARIES :=\nTARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE :=\nTARGET_OUT_ODM_DLKM_JAVA_LIBRARIES :=\nTARGET_OUT_ODM_DLKM_APPS :=\nTARGET_OUT_ODM_DLKM_APPS_PRIVILEGED :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_EXECUTABLES :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED :=\n$(KATI_obsolete_var \\\n    TARGET_OUT_ODM_DLKM_EXECUTABLES \\\n    TARGET_OUT_ODM_DLKM_OPTIONAL_EXECUTABLES \\\n    TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES \\\n    TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE \\\n    TARGET_OUT_ODM_DLKM_JAVA_LIBRARIES \\\n    TARGET_OUT_ODM_DLKM_APPS \\\n    TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_EXECUTABLES \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED \\\n    , odm_dlkm should not contain any executables, libraries, or apps)\n\nTARGET_OUT_SYSTEM_DLKM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_DLKM)\n\n# Unlike other partitions, system_dlkm should only contain kernel modules.\nTARGET_OUT_SYSTEM_DLKM_EXECUTABLES :=\nTARGET_OUT_SYSTEM_DLKM_OPTIONAL_EXECUTABLES :=\nTARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES :=\nTARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE :=\nTARGET_OUT_SYSTEM_DLKM_JAVA_LIBRARIES :=\nTARGET_OUT_SYSTEM_DLKM_APPS :=\nTARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_EXECUTABLES :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS :=\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED :=\n$(KATI_obsolete_var \\\n    TARGET_OUT_SYSTEM_DLKM_EXECUTABLES \\\n    TARGET_OUT_SYSTEM_DLKM_OPTIONAL_EXECUTABLES \\\n    TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES \\\n    TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE \\\n    TARGET_OUT_SYSTEM_DLKM_JAVA_LIBRARIES \\\n    TARGET_OUT_SYSTEM_DLKM_APPS \\\n    TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_EXECUTABLES \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS \\\n    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED \\\n    , system_dlkm should not contain any executables, libraries, or apps)\n\nTARGET_OUT_PRODUCT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_PRODUCT)\nTARGET_OUT_PRODUCT_EXECUTABLES := $(TARGET_OUT_PRODUCT)/bin\n.KATI_READONLY := TARGET_OUT_PRODUCT\nifneq ($(filter address,$(SANITIZE_TARGET)),)\ntarget_out_product_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_PRODUCT)\nifeq ($(SANITIZE_LITE),true)\n# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not\n# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/.\ntarget_out_product_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_PRODUCT)\nelse\ntarget_out_product_app_base := $(TARGET_OUT_PRODUCT)\nendif\nelse\ntarget_out_product_shared_libraries_base := $(TARGET_OUT_PRODUCT)\ntarget_out_product_app_base := $(TARGET_OUT_PRODUCT)\nendif\n\nifeq ($(TARGET_IS_64_BIT),true)\nTARGET_OUT_PRODUCT_SHARED_LIBRARIES := $(target_out_product_shared_libraries_base)/lib64\nelse\nTARGET_OUT_PRODUCT_SHARED_LIBRARIES := $(target_out_product_shared_libraries_base)/lib\nendif\nTARGET_OUT_PRODUCT_JAVA_LIBRARIES := $(TARGET_OUT_PRODUCT)/framework\nTARGET_OUT_PRODUCT_APPS := $(target_out_product_app_base)/app\nTARGET_OUT_PRODUCT_APPS_PRIVILEGED := $(target_out_product_app_base)/priv-app\nTARGET_OUT_PRODUCT_ETC := $(TARGET_OUT_PRODUCT)/etc\nTARGET_OUT_PRODUCT_FAKE := $(TARGET_OUT_PRODUCT)/product_fake_packages\n.KATI_READONLY := \\\n  TARGET_OUT_PRODUCT_EXECUTABLES \\\n  TARGET_OUT_PRODUCT_SHARED_LIBRARIES \\\n  TARGET_OUT_PRODUCT_JAVA_LIBRARIES \\\n  TARGET_OUT_PRODUCT_APPS \\\n  TARGET_OUT_PRODUCT_APPS_PRIVILEGED \\\n  TARGET_OUT_PRODUCT_ETC \\\n  TARGET_OUT_PRODUCT_FAKE\n\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_EXECUTABLES := $(TARGET_OUT_PRODUCT_EXECUTABLES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_SHARED_LIBRARIES := $(target_out_product_shared_libraries_base)/lib\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS := $(TARGET_OUT_PRODUCT_APPS)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS_PRIVILEGED := $(TARGET_OUT_PRODUCT_APPS_PRIVILEGED)\n.KATI_READONLY := \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_EXECUTABLES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_SHARED_LIBRARIES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS_PRIVILEGED\n\nTARGET_OUT_SYSTEM_EXT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_EXT)\nifneq ($(filter address,$(SANITIZE_TARGET)),)\ntarget_out_system_ext_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_SYSTEM_EXT)\nifeq ($(SANITIZE_LITE),true)\n# When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not\n# work with unsanitized app_process. For simplicity, generate APKs into /data/asan/.\ntarget_out_system_ext_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_SYSTEM_EXT)\nelse\ntarget_out_system_ext_app_base := $(TARGET_OUT_SYSTEM_EXT)\nendif\nelse\ntarget_out_system_ext_shared_libraries_base := $(TARGET_OUT_SYSTEM_EXT)\ntarget_out_system_ext_app_base := $(TARGET_OUT_SYSTEM_EXT)\nendif\n\nifeq ($(TARGET_IS_64_BIT),true)\nTARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES := $(target_out_system_ext_shared_libraries_base)/lib64\nelse\nTARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES := $(target_out_system_ext_shared_libraries_base)/lib\nendif\nTARGET_OUT_SYSTEM_EXT_JAVA_LIBRARIES:= $(TARGET_OUT_SYSTEM_EXT)/framework\nTARGET_OUT_SYSTEM_EXT_APPS := $(target_out_system_ext_app_base)/app\nTARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED := $(target_out_system_ext_app_base)/priv-app\nTARGET_OUT_SYSTEM_EXT_ETC := $(TARGET_OUT_SYSTEM_EXT)/etc\nTARGET_OUT_SYSTEM_EXT_EXECUTABLES := $(TARGET_OUT_SYSTEM_EXT)/bin\nTARGET_OUT_SYSTEM_EXT_FAKE := $(PRODUCT_OUT)/system_ext_fake_packages\n.KATI_READONLY := \\\n  TARGET_OUT_SYSTEM_EXT_EXECUTABLES \\\n  TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES \\\n  TARGET_OUT_SYSTEM_EXT_JAVA_LIBRARIES \\\n  TARGET_OUT_SYSTEM_EXT_APPS \\\n  TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED \\\n  TARGET_OUT_SYSTEM_EXT_ETC \\\n  TARGET_OUT_SYSTEM_EXT_FAKE\n\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_EXECUTABLES := $(TARGET_OUT_SYSTEM_EXT_EXECUTABLES)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES := $(target_out_system_ext_shared_libraries_base)/lib\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS := $(TARGET_OUT_SYSTEM_EXT_APPS)\n$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED := $(TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED)\n.KATI_READONLY := \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_EXECUTABLES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS \\\n  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED\n\nTARGET_OUT_BREAKPAD := $(PRODUCT_OUT)/breakpad\n.KATI_READONLY := TARGET_OUT_BREAKPAD\n\nTARGET_OUT_UNSTRIPPED := $(PRODUCT_OUT)/symbols\nTARGET_OUT_EXECUTABLES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/bin\nTARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/lib\nTARGET_OUT_VENDOR_SHARED_LIBRARIES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/$(TARGET_COPY_OUT_VENDOR)/lib\nTARGET_ROOT_OUT_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)\nTARGET_ROOT_OUT_BIN_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/bin\nTARGET_OUT_COVERAGE := $(PRODUCT_OUT)/coverage\n.KATI_READONLY := \\\n  TARGET_OUT_UNSTRIPPED \\\n  TARGET_OUT_EXECUTABLES_UNSTRIPPED \\\n  TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED \\\n  TARGET_OUT_VENDOR_SHARED_LIBRARIES_UNSTRIPPED \\\n  TARGET_ROOT_OUT_UNSTRIPPED \\\n  TARGET_ROOT_OUT_BIN_UNSTRIPPED \\\n  TARGET_OUT_COVERAGE\n\nTARGET_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_RAMDISK)\nTARGET_RAMDISK_OUT_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)\nTARGET_DEBUG_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DEBUG_RAMDISK)\nTARGET_VENDOR_DEBUG_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_DEBUG_RAMDISK)\nTARGET_TEST_HARNESS_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_TEST_HARNESS_RAMDISK)\n\nTARGET_SYSTEM_DLKM_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_DLKM)\n.KATI_READONLY := TARGET_SYSTEM_DLKM_OUT\n\nTARGET_VENDOR_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_RAMDISK)\nTARGET_VENDOR_KERNEL_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_KERNEL_RAMDISK)\n\nTARGET_ROOT_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ROOT)\nTARGET_ROOT_OUT_BIN := $(TARGET_ROOT_OUT)/bin\nTARGET_ROOT_OUT_ETC := $(TARGET_ROOT_OUT)/etc\nTARGET_ROOT_OUT_USR := $(TARGET_ROOT_OUT)/usr\n.KATI_READONLY := \\\n  TARGET_ROOT_OUT \\\n  TARGET_ROOT_OUT_BIN \\\n  TARGET_ROOT_OUT_ETC \\\n  TARGET_ROOT_OUT_USR\n\nTARGET_RECOVERY_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_RECOVERY)\nTARGET_RECOVERY_ROOT_OUT := $(TARGET_RECOVERY_OUT)/root\n.KATI_READONLY := \\\n  TARGET_RECOVERY_OUT \\\n  TARGET_RECOVERY_ROOT_OUT\n\nTARGET_SYSLOADER_OUT := $(PRODUCT_OUT)/sysloader\nTARGET_SYSLOADER_ROOT_OUT := $(TARGET_SYSLOADER_OUT)/root\nTARGET_SYSLOADER_SYSTEM_OUT := $(TARGET_SYSLOADER_OUT)/root/system\n.KATI_READONLY := \\\n  TARGET_SYSLOADER_OUT \\\n  TARGET_SYSLOADER_ROOT_OUT \\\n  TARGET_SYSLOADER_SYSTEM_OUT\n\nTARGET_INSTALLER_OUT := $(PRODUCT_OUT)/installer\nTARGET_INSTALLER_DATA_OUT := $(TARGET_INSTALLER_OUT)/data\nTARGET_INSTALLER_ROOT_OUT := $(TARGET_INSTALLER_OUT)/root\nTARGET_INSTALLER_SYSTEM_OUT := $(TARGET_INSTALLER_OUT)/root/system\n.KATI_READONLY := \\\n  TARGET_INSTALLER_OUT \\\n  TARGET_INSTALLER_DATA_OUT \\\n  TARGET_INSTALLER_ROOT_OUT \\\n  TARGET_INSTALLER_SYSTEM_OUT\n\nCOMMON_MODULE_CLASSES := TARGET_NOTICE_FILES HOST_NOTICE_FILES HOST_JAVA_LIBRARIES\nPER_ARCH_MODULE_CLASSES := SHARED_LIBRARIES STATIC_LIBRARIES EXECUTABLES GYP RENDERSCRIPT_BITCODE NATIVE_TESTS HEADER_LIBRARIES RLIB_LIBRARIES DYLIB_LIBRARIES\n.KATI_READONLY := COMMON_MODULE_CLASSES PER_ARCH_MODULE_CLASSES\n\nifeq ($(CALLED_FROM_SETUP),true)\nPRINT_BUILD_CONFIG ?= true\nendif\n"
  },
  {
    "path": "core/executable.mk",
    "content": "# We don't automatically set up rules to build executables for both\n# TARGET_ARCH and TARGET_2ND_ARCH.\n# By default, an executable is built for TARGET_ARCH.\n# To build it for TARGET_2ND_ARCH in a 64bit product, use \"LOCAL_MULTILIB := 32\"\n# To build it for both set LOCAL_MULTILIB := both and specify\n# LOCAL_MODULE_PATH_32 and LOCAL_MODULE_PATH_64 or LOCAL_MODULE_STEM_32 and\n# LOCAL_MODULE_STEM_64\n\nifdef LOCAL_IS_HOST_MODULE\n  $(call pretty-error,BUILD_EXECUTABLE is incompatible with LOCAL_IS_HOST_MODULE. Use BUILD_HOST_EXECUTABLE instead.)\nendif\n\nmy_skip_this_target :=\nifneq ($(filter address,$(SANITIZE_TARGET)),)\n  ifeq (true,$(LOCAL_FORCE_STATIC_EXECUTABLE))\n    my_skip_this_target := true\n  else ifeq (false, $(LOCAL_CLANG))\n    my_skip_this_target := true\n  else ifeq (never, $(LOCAL_SANITIZE))\n    my_skip_this_target := true\n  endif\nendif\n\nifneq (true,$(my_skip_this_target))\n$(call record-module-type,EXECUTABLE)\n\nmy_prefix := TARGET_\ninclude $(BUILD_SYSTEM)/multilib.mk\n\nifeq ($(my_module_multilib),both)\nifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS)\nifeq ($(LOCAL_MODULE_PATH_32)$(LOCAL_MODULE_STEM_32),)\n$(error $(LOCAL_PATH): LOCAL_MODULE_STEM_32 or LOCAL_MODULE_PATH_32 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE))\nendif\nifeq ($(LOCAL_MODULE_PATH_64)$(LOCAL_MODULE_STEM_64),)\n$(error $(LOCAL_PATH): LOCAL_MODULE_STEM_64 or LOCAL_MODULE_PATH_64 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE))\nendif\nendif\nelse #!LOCAL_MULTILIB == both\nLOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true\nendif\n\nifdef TARGET_2ND_ARCH\nLOCAL_2ND_ARCH_VAR_PREFIX :=\nendif\n\nmy_skip_non_preferred_arch :=\n\n# check if preferred arch is supported\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\nifeq ($(my_module_arch_supported),true)\n# first arch is supported\ninclude $(BUILD_SYSTEM)/executable_internal.mk\nifneq ($(my_module_multilib),both)\nmy_skip_non_preferred_arch := true\nendif\nendif\n\n# check if preferred arch was not supported or asked to build both\nifndef my_skip_non_preferred_arch\nifdef TARGET_2ND_ARCH\n\nLOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX)\n\n# check if non-preferred arch is supported\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\nifeq ($(my_module_arch_supported),true)\n# non-preferred arch is supported\nLOCAL_BUILT_MODULE :=\nLOCAL_INSTALLED_MODULE :=\nLOCAL_INTERMEDIATE_TARGETS :=\ninclude $(BUILD_SYSTEM)/executable_internal.mk\nendif\nendif # TARGET_2ND_ARCH\nendif # !my_skip_non_preferred_arch || LOCAL_MULTILIB\nLOCAL_2ND_ARCH_VAR_PREFIX :=\nLOCAL_NO_2ND_ARCH_MODULE_SUFFIX :=\n\nmy_module_arch_supported :=\n\nendif\n"
  },
  {
    "path": "core/executable_internal.mk",
    "content": "###########################################################\n## Standard rules for building an executable file.\n##\n## Additional inputs from base_rules.make:\n## None.\n###########################################################\n\nifeq ($(strip $(LOCAL_MODULE_CLASS)),)\nLOCAL_MODULE_CLASS := EXECUTABLES\nendif\nifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)\nLOCAL_MODULE_SUFFIX := $(TARGET_EXECUTABLE_SUFFIX)\nendif\n\nifdef target-executable-hook\n$(call target-executable-hook)\nendif\n\nskip_build_from_source :=\nifdef LOCAL_PREBUILT_MODULE_FILE\nifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH)))\ninclude $(BUILD_SYSTEM)/prebuilt_internal.mk\nskip_build_from_source := true\nendif\nendif\n\nifndef skip_build_from_source\n\ninclude $(BUILD_SYSTEM)/dynamic_binary.mk\n\n# Check for statically linked libc\nifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)\nifneq ($(filter $(my_static_libraries),libc),)\n$(error $(LOCAL_PATH): $(LOCAL_MODULE) is statically linking libc to dynamic executable, please remove libc from static libs or set LOCAL_FORCE_STATIC_EXECUTABLE := true)\nendif\nendif\n\n# Define PRIVATE_ variables from global vars\nifeq ($(LOCAL_NO_LIBCRT_BUILTINS),true)\nmy_target_libcrt_builtins :=\nelse\nmy_target_libcrt_builtins := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBCRT_BUILTINS)\nendif\nifeq ($(LOCAL_NO_CRT),true)\nmy_target_crtbegin_dynamic_o :=\nmy_target_crtbegin_static_o :=\nmy_target_crtend_o :=\nelse ifeq (true,$(call module-in-vendor-or-product))\nmy_target_crtbegin_dynamic_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_dynamic.vendor)\nmy_target_crtbegin_static_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_static.vendor)\nmy_target_crtend_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_android.vendor)\nelse\nmy_target_crtbegin_dynamic_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_dynamic)\nmy_target_crtbegin_static_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_static)\nmy_target_crtend_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_android)\nendif\nifneq ($(LOCAL_SDK_VERSION),)\nmy_target_crtbegin_dynamic_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_dynamic.sdk.$(my_ndk_crt_version))\nmy_target_crtbegin_static_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_static.sdk.$(my_ndk_crt_version))\nmy_target_crtend_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_android.sdk.$(my_ndk_crt_version))\nendif\n$(linked_module): PRIVATE_TARGET_LIBCRT_BUILTINS := $(my_target_libcrt_builtins)\n$(linked_module): PRIVATE_TARGET_CRTBEGIN_DYNAMIC_O := $(my_target_crtbegin_dynamic_o)\n$(linked_module): PRIVATE_TARGET_CRTBEGIN_STATIC_O := $(my_target_crtbegin_static_o)\n$(linked_module): PRIVATE_TARGET_CRTEND_O := $(my_target_crtend_o)\n$(linked_module): PRIVATE_POST_LINK_CMD := $(LOCAL_POST_LINK_CMD)\n\nifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)\n$(linked_module): $(my_target_crtbegin_static_o) $(all_objects) $(all_libraries) $(my_target_crtend_o) $(my_target_libcrt_builtins) $(CLANG_CXX)\n\t$(transform-o-to-static-executable)\n\t$(PRIVATE_POST_LINK_CMD)\nelse\n$(linked_module): $(my_target_crtbegin_dynamic_o) $(all_objects) $(all_libraries) $(my_target_crtend_o) $(my_target_libcrt_builtins) $(CLANG_CXX)\n\t$(transform-o-to-executable)\n\t$(PRIVATE_POST_LINK_CMD)\nendif\n\nifeq ($(my_native_coverage),true)\ngcno_suffix := .zip\n\nbuilt_whole_gcno_libraries := \\\n    $(foreach lib,$(my_whole_static_libraries), \\\n      $(call intermediates-dir-for, \\\n        STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \\\n        $(my_host_cross))/$(lib)$(gcno_suffix))\n\nbuilt_static_gcno_libraries := \\\n    $(foreach lib,$(my_static_libraries), \\\n      $(call intermediates-dir-for, \\\n        STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \\\n        $(my_host_cross))/$(lib)$(gcno_suffix))\n\nifdef LOCAL_IS_HOST_MODULE\nmy_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path))\nelse\nmy_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path))\nendif\n\nGCNO_ARCHIVE := $(my_installed_module_stem)$(gcno_suffix)\n\n$(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS)\n$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(strip $(LOCAL_GCNO_FILES))\n$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(strip $(built_whole_gcno_libraries)) $(strip $(built_static_gcno_libraries))\n$(intermediates)/$(GCNO_ARCHIVE) : $(LOCAL_GCNO_FILES) $(built_whole_gcno_libraries) $(built_static_gcno_libraries)\n\t$(package-coverage-files)\n\n$(my_coverage_path)/$(GCNO_ARCHIVE) : $(intermediates)/$(GCNO_ARCHIVE)\n\t$(copy-file-to-target)\n\n$(LOCAL_BUILT_MODULE): $(my_coverage_path)/$(GCNO_ARCHIVE)\nendif\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=EXECUTABLE))\n\nendif  # skip_build_from_source\n"
  },
  {
    "path": "core/executable_prefer_symlink.mk",
    "content": "# include this makefile to create the LOCAL_MODULE symlink to the primary version binary.\n# but this requires the primary version name specified via LOCAL_MODULE_STEM_32 or LOCAL_MODULE_STEM_64,\n# and different with the LOCAL_MODULE value\n#\n# Note: now only limited to the binaries that will be installed under system/bin directory\n\n# Create link to the one used depending on the target\n# configuration.\nifneq ($(LOCAL_IS_HOST_MODULE),true)\n  my_symlink := $(addprefix $(TARGET_OUT)/bin/, $(LOCAL_MODULE))\n  my_src_binary_name :=\n  ifeq ($(TARGET_IS_64_BIT),true)\n    ifeq ($(TARGET_SUPPORTS_64_BIT_APPS)|$(TARGET_SUPPORTS_32_BIT_APPS),true|true)\n      my_src_binary_name := $(LOCAL_MODULE_STEM_64)\n    else ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true)\n      # We support only 64 bit apps.\n      my_src_binary_name := $(LOCAL_MODULE_STEM_64)\n    else\n      # We support only 32 bit apps.\n      my_src_binary_name := $(LOCAL_MODULE_STEM_32)\n    endif\n  else\n    my_src_binary_name := $(LOCAL_MODULE_STEM_32)\n  endif\nelse\n  my_symlink := $(addprefix $(HOST_OUT)/bin/, $(LOCAL_MODULE))\n  my_src_binary_name := $(LOCAL_MODULE_STEM_64)\nendif\n\n$(call symlink-file,$(my_module_path)/$(my_src_binary_name),$(my_src_binary_name),$(my_symlink))\n\n# We need this so that the installed files could be picked up based on the\n# local module name\nALL_MODULES.$(my_register_name).INSTALLED += $(my_symlink)\n\n# Create the symlink when you run mm/mmm or \"make <module_name>\"\n$(LOCAL_MODULE) : $(my_symlink)\n\nmy_symlink :=\n"
  },
  {
    "path": "core/filter_symbols.sh",
    "content": "NM=$1\n\nshift\n\nPREFIX=$1\n\nshift\n\nSUFFIX=$1\n\nshift\n\nwhile test \"$1\" != \"\"\ndo\n    $NM -g -fp $1 | while read -a line\n    do\n\ttype=${line[1]}\n\t# if [[ \"$type\" != \"V\" && \"$type\" != \"U\" ]]; then\n\t#if [[ \"$type\" != \"W\" && \"$type\" != \"V\" && \"$type\" != \"U\" ]]; then\n\t    echo \"$PREFIX${line[0]}$SUFFIX # ${line[1]}\"\n\t#fi\n    done\n\n    shift\ndone\n"
  },
  {
    "path": "core/force_aapt2.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Including this makefile will force AAPT2 on,\n# rewriting some properties to convert standard AAPT usage to AAPT2.\n\nifeq ($(LOCAL_USE_AAPT2),false)\n  $(call pretty-error, LOCAL_USE_AAPT2 := false is no longer supported)\nendif\n\n# Filter out support library resources\nLOCAL_RESOURCE_DIR := $(filter-out \\\n  prebuilts/sdk/current/% \\\n  frameworks/support/%,\\\n    $(LOCAL_RESOURCE_DIR))\n# Filter out unnecessary aapt flags\nifneq (,$(filter --extra-packages,$(LOCAL_AAPT_FLAGS)))\n  LOCAL_AAPT_FLAGS := $(subst --extra-packages=,--extra-packages$(space), \\\n    $(filter-out \\\n      --extra-packages=android.support.% \\\n      --extra-packages=androidx.%, \\\n        $(subst --extra-packages$(space),--extra-packages=,$(LOCAL_AAPT_FLAGS))))\n    ifeq (,$(filter --extra-packages,$(LOCAL_AAPT_FLAGS)))\n      LOCAL_AAPT_FLAGS := $(filter-out --auto-add-overlay,$(LOCAL_AAPT_FLAGS))\n    endif\nendif\n\n# AAPT2 is pickier about missing resources.  Support library may have references to resources\n# added in current, so always treat LOCAL_SDK_VERSION := <number> as LOCAL_SDK_RES_VERSION := current.\nifneq (,$(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION)))\n  LOCAL_SDK_RES_VERSION := current\nendif\n\n"
  },
  {
    "path": "core/fuzz_test.mk",
    "content": "###########################################\n## A thin wrapper around BUILD_EXECUTABLE\n## Common flags for fuzz tests are added.\n###########################################\n$(call record-module-type,FUZZ_TEST)\n\nifdef LOCAL_SDK_VERSION\n    $(error $(LOCAL_PATH): $(LOCAL_MODULE): NDK fuzz tests are not supported.)\nendif\n\nmy_fuzzer:=libFuzzer\nifdef LOCAL_FUZZ_ENGINE\n    my_fuzzer:=$(LOCAL_FUZZ_ENGINE)\nelse ifdef TARGET_FUZZ_ENGINE\n    my_fuzzer:=$(TARGET_FUZZ_ENGINE)\nendif\n\nLOCAL_SANITIZE += fuzzer\n\nifeq ($(my_fuzzer),libFuzzer)\nLOCAL_STATIC_LIBRARIES += libFuzzer\nelse\n$(call pretty-error, Unknown fuzz engine $(my_fuzzer))\nendif\n\nifdef LOCAL_MODULE_PATH\n$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH when building test $(LOCAL_MODULE))\nendif\n\nifdef LOCAL_MODULE_PATH_32\n$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_32 when building test $(LOCAL_MODULE))\nendif\n\nifdef LOCAL_MODULE_PATH_64\n$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_64 when building test $(LOCAL_MODULE))\nendif\n\nLOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/fuzzers/$(my_fuzzer)/$(LOCAL_MODULE)\nLOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/fuzzers/$(my_fuzzer)/$(LOCAL_MODULE)\n\nifndef LOCAL_STRIP_MODULE\nLOCAL_STRIP_MODULE := keep_symbols\nendif\n\ninclude $(BUILD_EXECUTABLE)\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=FUZZ_TEST))"
  },
  {
    "path": "core/generate_enforce_rro.mk",
    "content": "include $(CLEAR_VARS)\n\nenforce_rro_module := $(enforce_rro_source_module)__$(PRODUCT_NAME)__auto_generated_rro_$(enforce_rro_partition)\nLOCAL_PACKAGE_NAME := $(enforce_rro_module)\n\nintermediates := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON)\nrro_android_manifest_file := $(intermediates)/AndroidManifest.xml\n\nifeq (true,$(enforce_rro_source_is_manifest_package_name))\n  use_package_name_arg := --use-package-name\nelse\n  use_package_name_arg :=\n$(rro_android_manifest_file): $(enforce_rro_source_manifest_package_info)\nendif\n\n$(rro_android_manifest_file): PRIVATE_PACKAGE_INFO := $(enforce_rro_source_manifest_package_info)\n$(rro_android_manifest_file): PRIVATE_USE_PACKAGE_NAME := $(use_package_name_arg)\n$(rro_android_manifest_file): PRIVATE_PARTITION := $(enforce_rro_partition)\n# There should be no duplicate overrides, but just in case, set the priority of\n# /product overlays to be higher than /vendor, to at least get deterministic results.\n$(rro_android_manifest_file): PRIVATE_PRIORITY := $(if $(filter product,$(enforce_rro_partition)),1,0)\n$(rro_android_manifest_file): build/make/tools/generate-enforce-rro-android-manifest.py\n\t$(hide) build/make/tools/generate-enforce-rro-android-manifest.py \\\n\t    --package-info $(PRIVATE_PACKAGE_INFO) \\\n\t    $(PRIVATE_USE_PACKAGE_NAME) \\\n\t    --partition $(PRIVATE_PARTITION) \\\n\t    --priority $(PRIVATE_PRIORITY) \\\n\t    -o $@\n\nLOCAL_PATH:= $(intermediates)\n\n# TODO(b/187404676): remove this condition when the prebuilt for packges exporting resource exists.\nifeq (,$(TARGET_BUILD_UNBUNDLED))\nifeq ($(enforce_rro_use_res_lib),true)\n  LOCAL_RES_LIBRARIES := $(enforce_rro_source_module)\nendif\nendif\n\nLOCAL_FULL_MANIFEST_FILE := $(rro_android_manifest_file)\n\nLOCAL_AAPT_FLAGS += --auto-add-overlay --keep-raw-values\nLOCAL_RESOURCE_DIR := $(enforce_rro_source_overlays)\n\nifeq (product,$(enforce_rro_partition))\n  LOCAL_PRODUCT_MODULE := true\nelse ifeq (vendor,$(enforce_rro_partition))\n  LOCAL_VENDOR_MODULE := true\nelse\n  $(error Unsupported partition. Want: [vendor/product] Got: [$(enforce_rro_partition)])\nendif\nifneq (,$(TARGET_BUILD_UNBUNDLED))\n  LOCAL_SDK_VERSION := current\nelse ifneq (,$(LOCAL_RES_LIBRARIES))\n  # Technically we are linking against the app (if only to grab its resources),\n  # and because it's potentially not building against the SDK, we can't either.\n  LOCAL_PRIVATE_PLATFORM_APIS := true\nelse ifeq (framework-res,$(enforce_rro_source_module))\n  LOCAL_PRIVATE_PLATFORM_APIS := true\nelse\n  LOCAL_SDK_VERSION := current\nendif\n\ninclude $(BUILD_RRO_PACKAGE)\n"
  },
  {
    "path": "core/header_library.mk",
    "content": "$(call record-module-type,HEADER_LIBRARY)\nifdef LOCAL_IS_HOST_MODULE\n  my_prefix := HOST_\n  LOCAL_HOST_PREFIX :=\nelse\n  my_prefix := TARGET_\nendif\ninclude $(BUILD_SYSTEM)/multilib.mk\n\nifndef my_module_multilib\n  # libraries default to building for both architecturess\n  my_module_multilib := both\nendif\n\nLOCAL_2ND_ARCH_VAR_PREFIX :=\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\n\nifeq ($(my_module_arch_supported),true)\n  include $(BUILD_SYSTEM)/header_library_internal.mk\nendif\n\nifdef $(my_prefix)2ND_ARCH\n  LOCAL_2ND_ARCH_VAR_PREFIX := $($(my_prefix)2ND_ARCH_VAR_PREFIX)\n  include $(BUILD_SYSTEM)/module_arch_supported.mk\n\n  ifeq ($(my_module_arch_supported),true)\n    # Build for 2ND_ARCH\n    LOCAL_BUILT_MODULE :=\n    LOCAL_INSTALLED_MODULE :=\n    LOCAL_INTERMEDIATE_TARGETS :=\n    include $(BUILD_SYSTEM)/header_library_internal.mk\n  endif\n  LOCAL_2ND_ARCH_VAR_PREFIX :=\nendif # 2ND_ARCH\n\nmy_module_arch_supported :=\n"
  },
  {
    "path": "core/header_library_internal.mk",
    "content": "###########################################################\n## Standard rules for building a header library.\n##\n## Additional inputs from base_rules.make:\n## None.\n###########################################################\n\nLOCAL_MODULE_CLASS := HEADER_LIBRARIES\nLOCAL_UNINSTALLABLE_MODULE := true\nifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),)\n$(error $(LOCAL_PATH): Cannot set module stem for a library)\nendif\n\ninclude $(BUILD_SYSTEM)/binary.mk\n\nifneq ($(strip $(all_objects)),)\n$(call pretty-error,Header libraries may not have any sources)\nendif\n\n$(LOCAL_BUILT_MODULE):\n\t$(hide) touch $@\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HEADER_LIBRARY))"
  },
  {
    "path": "core/host_executable.mk",
    "content": "$(call record-module-type,HOST_EXECUTABLE)\nLOCAL_IS_HOST_MODULE := true\nmy_prefix := HOST_\nLOCAL_HOST_PREFIX :=\ninclude $(BUILD_SYSTEM)/multilib.mk\n\nifndef LOCAL_MODULE_HOST_ARCH\nifndef my_module_multilib\n# By default we only build host module for the first arch.\nmy_module_multilib := first\nendif\nendif\n\nifeq ($(my_module_multilib),both)\nifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS)\nifeq ($(LOCAL_MODULE_PATH_32)$(LOCAL_MODULE_STEM_32),)\n$(error $(LOCAL_PATH): LOCAL_MODULE_STEM_32 or LOCAL_MODULE_PATH_32 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE))\nendif\nifeq ($(LOCAL_MODULE_PATH_64)$(LOCAL_MODULE_STEM_64),)\n$(error $(LOCAL_PATH): LOCAL_MODULE_STEM_64 or LOCAL_MODULE_PATH_64 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE))\nendif\nendif\nelse #!LOCAL_MULTILIB == both\nLOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true\nendif\n\nLOCAL_2ND_ARCH_VAR_PREFIX :=\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\n\nifeq ($(my_module_arch_supported),true)\ninclude $(BUILD_SYSTEM)/host_executable_internal.mk\nendif\n\nifdef HOST_2ND_ARCH\nLOCAL_2ND_ARCH_VAR_PREFIX := $(HOST_2ND_ARCH_VAR_PREFIX)\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\nifeq ($(my_module_arch_supported),true)\n# Build for HOST_2ND_ARCH\nLOCAL_BUILT_MODULE :=\nLOCAL_INSTALLED_MODULE :=\nLOCAL_INTERMEDIATE_TARGETS :=\n\ninclude $(BUILD_SYSTEM)/host_executable_internal.mk\nendif\nLOCAL_2ND_ARCH_VAR_PREFIX :=\nendif  # HOST_2ND_ARCH\n\nLOCAL_NO_2ND_ARCH_MODULE_SUFFIX :=\nmy_module_arch_supported :=\n"
  },
  {
    "path": "core/host_executable_internal.mk",
    "content": "###########################################################\n## Standard rules for building an executable file.\n##\n## Additional inputs from base_rules.make:\n## None.\n###########################################################\n\nifeq ($(strip $(LOCAL_MODULE_CLASS)),)\nLOCAL_MODULE_CLASS := EXECUTABLES\nendif\nifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)\nLOCAL_MODULE_SUFFIX := $($(my_prefix)EXECUTABLE_SUFFIX)\nendif\n\nifdef host-executable-hook\n$(call host-executable-hook)\nendif\n\nskip_build_from_source :=\nifdef LOCAL_PREBUILT_MODULE_FILE\nifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH)))\ninclude $(BUILD_SYSTEM)/prebuilt_internal.mk\nskip_build_from_source := true\nendif\nendif\n\nifndef skip_build_from_source\n\ninclude $(BUILD_SYSTEM)/binary.mk\n\nmy_host_libprofile_rt := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBPROFILE_RT)\n$(LOCAL_BUILT_MODULE): PRIVATE_HOST_LIBPROFILE_RT := $(my_host_libprofile_rt)\n\nmy_libdir := $(notdir $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_SHARED_LIBRARIES))\nifeq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS)\n$(LOCAL_BUILT_MODULE): PRIVATE_RPATHS := ../../$(my_libdir) ../../../$(my_libdir)\nelse\n$(LOCAL_BUILT_MODULE): PRIVATE_RPATHS := ../$(my_libdir) $(my_libdir)\nendif\nmy_libdir :=\n\nmy_crtbegin :=\nmy_crtend :=\nmy_libcrt_builtins :=\nifdef USE_HOST_MUSL\n  my_crtbegin := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)HOST_OBJECT_libc_musl_crtbegin_dynamic)\n  my_crtend := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)HOST_OBJECT_libc_musl_crtend)\n  my_libcrt_builtins := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBCRT_BUILTINS)\n  $(LOCAL_BUILT_MODULE): PRIVATE_LDFLAGS += -Wl,--no-dynamic-linker\nendif\n\n$(LOCAL_BUILT_MODULE): PRIVATE_CRTBEGIN := $(my_crtbegin)\n$(LOCAL_BUILT_MODULE): PRIVATE_CRTEND := $(my_crtend)\n$(LOCAL_BUILT_MODULE): PRIVATE_LIBCRT_BUILTINS := $(my_libcrt_builtins)\n$(LOCAL_BUILT_MODULE): $(my_crtbegin) $(my_crtend) $(my_libcrt_builtins)\n\n$(LOCAL_BUILT_MODULE): $(all_objects) $(all_libraries) $(CLANG_CXX)\n\t$(transform-host-o-to-executable)\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HOST_EXECUTABLE))\n\nendif  # skip_build_from_source\n"
  },
  {
    "path": "core/host_java_library.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call record-module-type,HOST_JAVA_LIBRARY)\n\n#\n# Standard rules for building a host java library.\n#\n\n#######################################\ninclude $(BUILD_SYSTEM)/host_java_library_common.mk\n#######################################\n\n# Enable emma instrumentation only if the module asks so.\nifeq (true,$(LOCAL_EMMA_INSTRUMENT))\nifneq (true,$(EMMA_INSTRUMENT))\nLOCAL_EMMA_INSTRUMENT :=\nendif\nendif\n\nfull_classes_compiled_jar := $(intermediates.COMMON)/classes-full-debug.jar\nfull_classes_jarjar_jar := $(intermediates.COMMON)/classes-jarjar.jar\nfull_classes_jar := $(intermediates.COMMON)/classes.jar\njava_source_list_file := $(intermediates.COMMON)/java-source-list\nfull_classes_header_jar := $(intermediates.COMMON)/classes-header.jar\nfull_classes_combined_jar := $(intermediates.COMMON)/classes-combined.jar\n\nLOCAL_INTERMEDIATE_TARGETS += \\\n    $(full_classes_compiled_jar) \\\n    $(full_classes_jarjar_jar) \\\n    $(java_source_list_file) \\\n    $(full_classes_combined_jar)\n\n#######################################\ninclude $(BUILD_SYSTEM)/base_rules.mk\n#######################################\n\njava_sources := $(addprefix $(LOCAL_PATH)/, $(filter %.java,$(LOCAL_SRC_FILES))) \\\n                $(filter %.java,$(LOCAL_GENERATED_SOURCES))\nall_java_sources := $(java_sources)\n\nALL_MODULES.$(my_register_name).SRCS := $(ALL_MODULES.$(my_register_name).SRCS) $(all_java_sources)\n\ninclude $(BUILD_SYSTEM)/java_common.mk\n\n# List of dependencies for anything that needs all java sources in place\njava_sources_deps := \\\n    $(java_sources) \\\n    $(java_resource_sources) \\\n    $(LOCAL_SRCJARS) \\\n    $(LOCAL_ADDITIONAL_DEPENDENCIES)\n\n$(java_source_list_file): $(java_sources_deps)\n\t$(write-java-source-list)\n\n# TODO(b/143658984): goma can't handle the --system argument to javac.\n#$(full_classes_compiled_jar): .KATI_NINJA_POOL := $(GOMA_POOL)\n$(full_classes_compiled_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS) $(annotation_processor_flags)\n$(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_FILES :=\n$(full_classes_compiled_jar): PRIVATE_JAR_PACKAGES :=\n$(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_PACKAGES :=\n$(full_classes_compiled_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS)\n$(full_classes_compiled_jar): PRIVATE_SRCJAR_LIST_FILE := $(intermediates.COMMON)/srcjar-list\n$(full_classes_compiled_jar): PRIVATE_SRCJAR_INTERMEDIATES_DIR := $(intermediates.COMMON)/srcjars\n$(full_classes_compiled_jar): \\\n    $(java_source_list_file) \\\n    $(java_sources_deps) \\\n    $(full_java_libs) \\\n    $(full_java_bootclasspath_libs) \\\n    $(annotation_processor_deps) \\\n    $(NORMALIZE_PATH) \\\n    $(ZIPTIME) \\\n    $(JAR_ARGS) \\\n    $(ZIPSYNC) \\\n    $(SOONG_ZIP) \\\n    | $(SOONG_JAVAC_WRAPPER)\n\t$(transform-host-java-to-package)\n\t$(remove-timestamps-from-package)\n\njavac-check : $(full_classes_compiled_jar)\njavac-check-$(LOCAL_MODULE) : $(full_classes_compiled_jar)\n.PHONY: javac-check-$(LOCAL_MODULE)\n\n$(full_classes_combined_jar): PRIVATE_DONT_DELETE_JAR_META_INF := $(LOCAL_DONT_DELETE_JAR_META_INF)\n$(full_classes_combined_jar): $(full_classes_compiled_jar) \\\n                              $(jar_manifest_file) \\\n                              $(full_static_java_libs) | $(MERGE_ZIPS)\n\t$(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(PRIVATE_JAR_MANIFEST)) \\\n            $(if $(PRIVATE_DONT_DELETE_JAR_META_INF),,-stripDir META-INF -zipToNotStrip $<) \\\n            $@ $< $(PRIVATE_STATIC_JAVA_LIBRARIES)\n\n# Run jarjar if necessary, otherwise just copy the file.\nifneq ($(strip $(LOCAL_JARJAR_RULES)),)\n$(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES)\n$(full_classes_jarjar_jar): $(full_classes_combined_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR)\n\t$(call transform-jarjar)\nelse\nfull_classes_jarjar_jar := $(full_classes_combined_jar)\nendif\n\n\n#######################################\nLOCAL_FULL_CLASSES_PRE_JACOCO_JAR := $(full_classes_jarjar_jar)\n\ninclude $(BUILD_SYSTEM)/jacoco.mk\n#######################################\n\n$(eval $(call copy-one-file,$(LOCAL_FULL_CLASSES_JACOCO_JAR),$(LOCAL_BUILT_MODULE)))\n$(eval $(call copy-one-file,$(LOCAL_FULL_CLASSES_JACOCO_JAR),$(full_classes_jar)))\n\nifeq ($(TURBINE_ENABLED),false)\n$(eval $(call copy-one-file,$(LOCAL_FULL_CLASSES_JACOCO_JAR),$(full_classes_header_jar)))\nendif\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HOST_JAVA_LIBRARY))"
  },
  {
    "path": "core/host_java_library_common.mk",
    "content": "#\n# Copyright (C) 2013 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# Common rules for building a host java library.\n#\n\nLOCAL_MODULE_CLASS := JAVA_LIBRARIES\nLOCAL_MODULE_SUFFIX := $(COMMON_JAVA_PACKAGE_SUFFIX)\nLOCAL_IS_HOST_MODULE := true\nLOCAL_BUILT_MODULE_STEM := javalib.jar\n\nintermediates := $(call local-intermediates-dir)\nintermediates.COMMON := $(call local-intermediates-dir,COMMON)\n\n# base_rules.mk looks at this\nall_res_assets :=\n\nproto_sources := $(filter %.proto,$(LOCAL_SRC_FILES))\nifneq ($(proto_sources),)\nifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),micro)\n    LOCAL_JAVA_LIBRARIES += libprotobuf-java-micro\nelse\n  ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nano)\n    LOCAL_JAVA_LIBRARIES += libprotobuf-java-nano\n  else\n    ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),full)\n      LOCAL_JAVA_LIBRARIES += libprotobuf-java-full\n    else\n      LOCAL_JAVA_LIBRARIES += libprotobuf-java-lite\n    endif\n  endif\nendif\nendif\n\nLOCAL_JAVA_LIBRARIES := $(sort $(LOCAL_JAVA_LIBRARIES))\n"
  },
  {
    "path": "core/host_prebuilt.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call record-module-type,HOST_PREBUILT)\nLOCAL_IS_HOST_MODULE := true\ninclude $(BUILD_MULTI_PREBUILT)\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HOST_PREBUILT))"
  },
  {
    "path": "core/host_shared_library.mk",
    "content": "$(call record-module-type,HOST_SHARED_LIBRARY)\nLOCAL_IS_HOST_MODULE := true\nmy_prefix := HOST_\nLOCAL_HOST_PREFIX :=\ninclude $(BUILD_SYSTEM)/multilib.mk\n\nifndef LOCAL_MODULE_HOST_ARCH\nifndef my_module_multilib\n# libraries default to building for both architecturess\nmy_module_multilib := both\nendif\nendif\n\nLOCAL_2ND_ARCH_VAR_PREFIX :=\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\n\nifeq ($(my_module_arch_supported),true)\ninclude $(BUILD_SYSTEM)/host_shared_library_internal.mk\nendif\n\nifdef HOST_2ND_ARCH\nLOCAL_2ND_ARCH_VAR_PREFIX := $(HOST_2ND_ARCH_VAR_PREFIX)\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\nifeq ($(my_module_arch_supported),true)\n# Build for HOST_2ND_ARCH\nLOCAL_BUILT_MODULE :=\nLOCAL_INSTALLED_MODULE :=\nLOCAL_INTERMEDIATE_TARGETS :=\n\ninclude $(BUILD_SYSTEM)/host_shared_library_internal.mk\nendif\nLOCAL_2ND_ARCH_VAR_PREFIX :=\nendif  # HOST_2ND_ARCH\n\nmy_module_arch_supported :=\n\n###########################################################\n## Copy headers to the install tree\n###########################################################\nifdef LOCAL_COPY_HEADERS\n$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\\\n  $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\\\n  $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers))\ninclude $(BUILD_SYSTEM)/copy_headers.mk\nendif\n"
  },
  {
    "path": "core/host_shared_library_internal.mk",
    "content": "###########################################################\n## Standard rules for building a normal shared library.\n##\n## Additional inputs from base_rules.make:\n## None.\n##\n## LOCAL_MODULE_SUFFIX will be set for you.\n###########################################################\n\nifeq ($(strip $(LOCAL_MODULE_CLASS)),)\nLOCAL_MODULE_CLASS := SHARED_LIBRARIES\nendif\nifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)\nLOCAL_MODULE_SUFFIX := $($(my_prefix)SHLIB_SUFFIX)\nendif\nifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),)\n$(error $(LOCAL_PATH): Cannot set module stem for a library)\nendif\n\nifdef host-shared-library-hook\n$(call host-shared-library-hook)\nendif\n\nskip_build_from_source :=\nifdef LOCAL_PREBUILT_MODULE_FILE\nifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH)))\ninclude $(BUILD_SYSTEM)/prebuilt_internal.mk\nskip_build_from_source := true\nendif\nendif\n\nifndef skip_build_from_source\n\ninclude $(BUILD_SYSTEM)/binary.mk\n\nmy_host_libprofile_rt := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBPROFILE_RT)\n$(LOCAL_BUILT_MODULE): PRIVATE_HOST_LIBPROFILE_RT := $(my_host_libprofile_rt)\n\nifdef USE_HOST_MUSL\n  my_crtbegin := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)HOST_OBJECT_libc_musl_crtbegin_so)\n  my_crtend := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)HOST_OBJECT_libc_musl_crtend_so)\n  my_libcrt_builtins := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBCRT_BUILTINS)\nendif\n\n$(LOCAL_BUILT_MODULE): PRIVATE_CRTBEGIN := $(my_crtbegin)\n$(LOCAL_BUILT_MODULE): PRIVATE_CRTEND := $(my_crtend)\n$(LOCAL_BUILT_MODULE): PRIVATE_LIBCRT_BUILTINS := $(my_libcrt_builtins)\n$(LOCAL_BUILT_MODULE): $(my_crtbegin) $(my_crtend) $(my_libcrt_builtins)\n\n$(LOCAL_BUILT_MODULE): \\\n        $(all_objects) \\\n        $(all_libraries) \\\n        $(LOCAL_ADDITIONAL_DEPENDENCIES)\n\t$(transform-host-o-to-shared-lib)\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HOST_SHARED_LIBRARY))\n\nendif  # skip_build_from_source\n"
  },
  {
    "path": "core/host_static_library.mk",
    "content": "$(call record-module-type,HOST_STATIC_LIBRARY)\nLOCAL_IS_HOST_MODULE := true\nmy_prefix := HOST_\nLOCAL_HOST_PREFIX :=\ninclude $(BUILD_SYSTEM)/multilib.mk\n\nifndef LOCAL_MODULE_HOST_ARCH\nifndef my_module_multilib\n# libraries default to building for both architecturess\nmy_module_multilib := both\nendif\nendif\n\nLOCAL_2ND_ARCH_VAR_PREFIX :=\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\n\nifeq ($(my_module_arch_supported),true)\ninclude $(BUILD_SYSTEM)/host_static_library_internal.mk\nendif\n\nifdef HOST_2ND_ARCH\nLOCAL_2ND_ARCH_VAR_PREFIX := $(HOST_2ND_ARCH_VAR_PREFIX)\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\nifeq ($(my_module_arch_supported),true)\n# Build for HOST_2ND_ARCH\nLOCAL_BUILT_MODULE :=\nLOCAL_INSTALLED_MODULE :=\nLOCAL_INTERMEDIATE_TARGETS :=\n\ninclude $(BUILD_SYSTEM)/host_static_library_internal.mk\nendif\nLOCAL_2ND_ARCH_VAR_PREFIX :=\nendif  # HOST_2ND_ARCH\n\nmy_module_arch_supported :=\n\n###########################################################\n## Copy headers to the install tree\n###########################################################\nifdef LOCAL_COPY_HEADERS\n$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\\\n  $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\\\n  $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers))\ninclude $(BUILD_SYSTEM)/copy_headers.mk\nendif\n"
  },
  {
    "path": "core/host_static_library_internal.mk",
    "content": "###########################################################\n## Standard rules for building a static library for the host.\n##\n## Additional inputs from base_rules.make:\n## None.\n##\n## LOCAL_MODULE_SUFFIX will be set for you.\n###########################################################\n\nifeq ($(strip $(LOCAL_MODULE_CLASS)),)\nLOCAL_MODULE_CLASS := STATIC_LIBRARIES\nendif\nifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)\nLOCAL_MODULE_SUFFIX := .a\nendif\nifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),)\n$(error $(LOCAL_PATH): Cannot set module stem for a library)\nendif\nLOCAL_UNINSTALLABLE_MODULE := true\n\ninclude $(BUILD_SYSTEM)/binary.mk\n\n$(LOCAL_BUILT_MODULE): $(built_whole_libraries)\n$(LOCAL_BUILT_MODULE): $(all_objects)\n\t$(transform-host-o-to-static-lib)\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HOST_STATIC_LIBRARY))"
  },
  {
    "path": "core/install_jni_libs.mk",
    "content": "# Decides how to install the jni libraries needed by an apk.\n# Input variables:\n#   my_module_multilib, LOCAL_2ND_ARCH_VAR_PREFIX (from package.mk or prebuilt.mk)\n#   rs_compatibility_jni_libs (from java.mk)\n#   my_module_path (from base_rules.mk)\n#   partition_tag (from base_rules.mk)\n#   my_prebuilt_src_file (from prebuilt_internal.mk)\n#\n# Output variables:\n#   jni_shared_libraries, jni_shared_libraries_abi, jni_shared_libraries_with_abis if we are going to embed the libraries into the apk;\n#   embedded_prebuilt_jni_libs, prebuilt jni libs embedded in prebuilt apk.\n#\n\nmy_embed_jni :=\nifneq ($(TARGET_BUILD_APPS),)\n  my_embed_jni := true\nendif\nifneq ($(filter tests samples, $(LOCAL_MODULE_TAGS)),)\n  my_embed_jni := true\nendif\n\n# If the APK is not installed in one of the following partitions, force its libraries\n# to be embedded inside the APK instead of installed to /<partition>/lib[64]/.\nsupported_partition_patterns := \\\n    $(TARGET_OUT)/% \\\n    $(TARGET_OUT_VENDOR)/% \\\n    $(TARGET_OUT_OEM)/% \\\n    $(TARGET_OUT_PRODUCT)/% \\\n    $(TARGET_OUT_SYSTEM_EXT)/% \\\n\nifeq ($(filter $(supported_partition_patterns),$(my_module_path)),)\n  my_embed_jni := true\nendif\n\n# If we're installing this APP as a compressed module, we include all JNI libraries\n# in the compressed artifact, rather than as separate files on the partition in question.\nifdef LOCAL_COMPRESSED_MODULE\n  my_embed_jni := true\nendif\n\njni_shared_libraries :=\njni_shared_libraries_abis :=\n# jni_shared_libraries_with_abis is a list of <abi>:<path-to-the-built-jni-lib>\njni_shared_libraries_with_abis :=\nembedded_prebuilt_jni_libs :=\n\n#######################################\n# For TARGET_ARCH\nmy_2nd_arch_prefix :=\nmy_add_jni :=\n# The module is built for TARGET_ARCH\nifeq ($(my_2nd_arch_prefix),$(LOCAL_2ND_ARCH_VAR_PREFIX))\n  my_add_jni := true\nendif\n# Or it explicitly requires both\nifeq ($(my_module_multilib),both)\n  my_add_jni := true\nendif\nifeq ($(my_add_jni),true)\n  my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS_$(TARGET_ARCH))\n  ifndef my_prebuilt_jni_libs\n    my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS)\n  endif\n  include $(BUILD_SYSTEM)/install_jni_libs_internal.mk\n  jni_shared_libraries += $(my_jni_shared_libraries)\n  jni_shared_libraries_abis += $(my_jni_shared_libraries_abi)\n  jni_shared_libraries_with_abis += $(addprefix $(my_jni_shared_libraries_abi):,\\\n      $(my_jni_shared_libraries))\n  embedded_prebuilt_jni_libs += $(my_embedded_prebuilt_jni_libs)\n\n  # Include RS dynamically-generated libraries as well\n  # TODO: Add multilib support once RS supports generating multilib libraries.\n  jni_shared_libraries += $(rs_compatibility_jni_libs)\n  jni_shared_libraries_with_abis += $(addprefix $(my_jni_shared_libraries_abi):,\\\n      $(rs_compatibility_jni_libs))\nendif  # my_add_jni\n\n#######################################\n# For TARGET_2ND_ARCH\nifdef TARGET_2ND_ARCH\n  my_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX)\n  my_add_jni :=\n  # The module is built for TARGET_2ND_ARCH\n  ifeq ($(my_2nd_arch_prefix),$(LOCAL_2ND_ARCH_VAR_PREFIX))\n    my_add_jni := true\n  endif\n  # Or it explicitly requires both\n  ifeq ($(my_module_multilib),both)\n    my_add_jni := true\n  endif\n  ifeq ($(my_add_jni),true)\n    my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS_$(TARGET_2ND_ARCH))\n    ifndef my_prebuilt_jni_libs\n      my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS)\n    endif\n    include $(BUILD_SYSTEM)/install_jni_libs_internal.mk\n    jni_shared_libraries += $(my_jni_shared_libraries)\n    jni_shared_libraries_abis += $(my_jni_shared_libraries_abi)\n    jni_shared_libraries_with_abis += $(addprefix $(my_jni_shared_libraries_abi):,\\\n        $(my_jni_shared_libraries))\n    embedded_prebuilt_jni_libs += $(my_embedded_prebuilt_jni_libs)\n  endif  # my_add_jni\nendif  # TARGET_2ND_ARCH\n\njni_shared_libraries := $(strip $(jni_shared_libraries))\njni_shared_libraries_abis := $(sort $(jni_shared_libraries_abis))\njni_shared_libraries_with_abis := $(strip $(jni_shared_libraries_with_abis))\nembedded_prebuilt_jni_libs := $(strip $(embedded_prebuilt_jni_libs))\n"
  },
  {
    "path": "core/install_jni_libs_internal.mk",
    "content": "# Install jni libraries for one arch.\n# Input variables:\n#   my_2nd_arch_prefix: indicate if this is for TARGET_2ND_ARCH.\n#   my_embed_jni: indicate if we want to embed the jni libs in the apk.\n#   my_prebuilt_jni_libs\n#   my_installed_module_stem (from configure_module_stem.mk)\n#   partition_tag (from base_rules.mk)\n#   partition_lib_pairs\n#   my_prebuilt_src_file (from prebuilt_internal.mk)\n#\n# Output variables:\n#   my_jni_shared_libraries, my_jni_shared_libraries_abi, if we are going to embed the libraries into the apk;\n#   my_embedded_prebuilt_jni_libs, prebuilt jni libs embedded in prebuilt apk.\n#\n\nmy_sdk_variant = $(1)\nifneq (,$(and $(my_embed_jni),$(LOCAL_SDK_VERSION)))\n  # Soong produces $(lib).so in $(lib).sdk_intermediates so that the library\n  # has the correct name for embedding in an APK.  Append .sdk to the name\n  # of the intermediates directory, but not the .so name.\n  my_sdk_variant = $(call use_soong_sdk_libraries,$(1))\nendif\n\nmy_jni_shared_libraries := $(strip \\\n  $(foreach lib,$(LOCAL_JNI_SHARED_LIBRARIES), \\\n    $(call intermediates-dir-for,SHARED_LIBRARIES,$(call my_sdk_variant,$(lib)),,,$(my_2nd_arch_prefix))/$(lib).so))\n\n\n# App-specific lib path.\nmy_app_lib_path := $(dir $(LOCAL_INSTALLED_MODULE))lib/$(TARGET_$(my_2nd_arch_prefix)ARCH)\nmy_embedded_prebuilt_jni_libs :=\n\nifdef my_embed_jni\n  # App explicitly requires the prebuilt NDK stl shared libraies.\n  # The NDK stl shared libraries should never go to the system image.\n  ifeq ($(LOCAL_NDK_STL_VARIANT),c++_shared)\n    ifndef LOCAL_SDK_VERSION\n      $(error LOCAL_SDK_VERSION must be defined with LOCAL_NDK_STL_VARIANT, \\\n          LOCAL_PACKAGE_NAME=$(LOCAL_PACKAGE_NAME))\n    endif\n    my_libcxx_arch := $($(LOCAL_2ND_ARCH_VAR_PREFIX)PREBUILT_LIBCXX_ARCH_DIR)\n    my_jni_shared_libraries += \\\n        $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/android_libc++/ndk/$(my_libcxx_arch)/lib/libc++_shared.so\n  endif\n\n  # Set the abi directory used by the local JNI shared libraries.\n  # (Doesn't change how the local shared libraries are compiled, just\n  # sets where they are stored in the apk.)\n  ifeq ($(LOCAL_JNI_SHARED_LIBRARIES_ABI),)\n    my_jni_shared_libraries_abi := $(TARGET_$(my_2nd_arch_prefix)CPU_ABI)\n  else\n    my_jni_shared_libraries_abi := $(LOCAL_JNI_SHARED_LIBRARIES_ABI)\n  endif\n\nelse ifneq ($(my_jni_shared_libraries),) # not my_embed_jni\n\n  # The jni libaries will be installed to the system.img.\n  my_jni_filenames := $(notdir $(my_jni_shared_libraries))\n  # Make sure the JNI libraries get installed\n  my_shared_library_path := $(call get_non_asan_path,\\\n      $($(my_2nd_arch_prefix)TARGET_OUT$(partition_tag)_SHARED_LIBRARIES))\n\n  bit_suffix := $(if $(filter %64,$(TARGET_$(my_2nd_arch_prefix)ARCH)),:64,:32)\n  ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET += $(addsuffix $(bit_suffix),$(LOCAL_JNI_SHARED_LIBRARIES))\n\n  # Create symlink in the app specific lib path\n  # Skip creating this symlink when running the second part of a target sanitization build.\n  ifeq ($(filter address,$(SANITIZE_TARGET)),)\n    my_symlink_target_dir := $(patsubst $(PRODUCT_OUT)%,%,\\\n      $(my_shared_library_path))\n\n    ifdef partition_lib_pairs\n      # Support cross-partition jni lib dependency for bp modules\n      # API domain check is done in Soong\n      $(foreach pl_pair,$(partition_lib_pairs),\\\n        $(eval lib_name := $(call word-colon, 1, $(pl_pair)))\\\n        $(eval lib_partition := $(call word-colon, 2, $(pl_pair)))\\\n        $(eval shared_library_path := $(call get_non_asan_path,\\\n        $($(my_2nd_arch_prefix)TARGET_OUT$(lib_partition)_SHARED_LIBRARIES)))\\\n        $(call symlink-file,\\\n          $(shared_library_path)/$(lib_name).so,\\\n          $(my_symlink_target_dir)/$(lib_name).so,\\\n          $(my_app_lib_path)/$(lib_name).so)\\\n        $(eval $$(LOCAL_INSTALLED_MODULE) : $$(my_app_lib_path)/$$(lib_name).so)\\\n        $(eval ALL_MODULES.$(my_register_name).INSTALLED += $$(my_app_lib_path)/$$(lib_name).so))\n\n    else\n      # Cross-partition jni lib dependency currently not supported for mk modules\n      $(foreach lib,$(my_jni_filenames),\\\n        $(call symlink-file, \\\n          $(my_shared_library_path)/$(lib), \\\n          $(my_symlink_target_dir)/$(lib), \\\n          $(my_app_lib_path)/$(lib)) \\\n        $(eval $$(LOCAL_INSTALLED_MODULE) : $$(my_app_lib_path)/$$(lib)) \\\n        $(eval ALL_MODULES.$(my_register_name).INSTALLED += $$(my_app_lib_path)/$$(lib)))\n    endif # partition_lib_pairs\n  endif\n\n  # Clear jni_shared_libraries to not embed it into the apk.\n  my_jni_shared_libraries :=\nendif  # my_embed_jni\n\nifdef my_prebuilt_jni_libs\n  # Files like @lib/<abi>/libfoo.so (path inside the apk) are JNI libs embedded prebuilt apk;\n  # Files like path/to/libfoo.so (path relative to LOCAL_PATH) are prebuilts in the source tree.\n  my_embedded_prebuilt_jni_libs := $(patsubst @%,%, \\\n      $(filter @%, $(my_prebuilt_jni_libs)))\n\n  # prebuilt JNI exsiting as separate source files.\n  my_prebuilt_jni_libs := $(addprefix $(LOCAL_PATH)/, \\\n      $(filter-out @%, $(my_prebuilt_jni_libs)))\n  ifdef my_prebuilt_jni_libs\n    ifdef my_embed_jni\n      # Embed my_prebuilt_jni_libs to the apk\n      my_jni_shared_libraries += $(my_prebuilt_jni_libs)\n    else # not my_embed_jni\n      # Install my_prebuilt_jni_libs as separate files.\n      $(foreach lib, $(my_prebuilt_jni_libs), \\\n          $(eval $(call copy-one-file, $(lib), $(my_app_lib_path)/$(notdir $(lib)))))\n\n      my_installed_library := $(addprefix $(my_app_lib_path)/, $(notdir $(my_prebuilt_jni_libs)))\n      $(LOCAL_INSTALLED_MODULE) : $(my_installed_library)\n\n      ALL_MODULES.$(my_register_name).INSTALLED += $(my_installed_library)\n    endif  # my_embed_jni\n  endif  # inner my_prebuilt_jni_libs\nendif  # outer my_prebuilt_jni_libs\n\n# Verify that all included libraries are built against the NDK\ninclude $(BUILD_SYSTEM)/allowed_ndk_types.mk\n\nifneq ($(strip $(LOCAL_JNI_SHARED_LIBRARIES)),)\n  ifneq ($(LOCAL_SDK_VERSION),)\n    my_link_type := app:sdk\n    my_warn_types := native:platform $(my_warn_ndk_types)\n    my_allowed_types := $(my_allowed_ndk_types)\n    ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE)))\n      my_allowed_types += native:vendor native:vndk native:platform_vndk\n    else ifeq ($(LOCAL_PRODUCT_MODULE),true)\n      my_allowed_types += native:product native:vndk native:platform_vndk\n    endif\n  else\n    my_link_type := app:platform\n    my_warn_types := $(my_warn_ndk_types)\n    my_allowed_types := $(my_allowed_ndk_types) native:platform native:product native:vendor native:vndk native:vndk_private native:platform_vndk\n  endif\n\n  ifeq ($(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE))\n    # SOONG_SDK_VARIANT_MODULES isn't complete yet while parsing Soong modules, and Soong has\n    # already ensured that apps link against the correct SDK variants, don't check them.\n  else\n    ifneq (,$(LOCAL_SDK_VERSION))\n      my_link_deps := $(addprefix SHARED_LIBRARIES:,$(call use_soong_sdk_libraries,$(LOCAL_JNI_SHARED_LIBRARIES)))\n    else\n      my_link_deps := $(addprefix SHARED_LIBRARIES:,$(LOCAL_JNI_SHARED_LIBRARIES))\n    endif\n  endif\n\n  my_common :=\n  include $(BUILD_SYSTEM)/link_type.mk\nendif\n"
  },
  {
    "path": "core/instrumentation_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2023 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs {LABEL}.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-instrumentation\" />\n    {EXTRA_CONFIGS}\n    <target_preparer class=\"com.android.tradefed.targetprep.suite.SuiteApkInstaller\">\n        <option name=\"cleanup-apks\" value=\"true\" />\n        <option name=\"test-file-name\" value=\"{MODULE}.apk\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.{TEST_TYPE}\" >\n        {EXTRA_TEST_RUNNER_CONFIGS}<option name=\"package\" value=\"{PACKAGE}\" />\n        <option name=\"runner\" value=\"{RUNNER}\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/jacoco.mk",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This file sets up Java code coverage via Jacoco\n# This file is only intended to be included internally by the build system\n# (at the time of authorship, it is included by java.mk and\n# java_host_library.mk)\n\n# determine Jacoco include/exclude filters even when coverage is not enabled\n# to get syntax checking on LOCAL_JACK_COVERAGE_(INCLUDE|EXCLUDE)_FILTER\n# copy filters from Jack but also skip some known java packages\nmy_include_filter := $(strip $(LOCAL_JACK_COVERAGE_INCLUDE_FILTER))\nmy_exclude_filter := $(strip $(DEFAULT_JACOCO_EXCLUDE_FILTER),$(LOCAL_JACK_COVERAGE_EXCLUDE_FILTER))\n\nmy_include_args := $(call jacoco-class-filter-to-file-args, $(my_include_filter))\nmy_exclude_args := $(call jacoco-class-filter-to-file-args, $(my_exclude_filter))\n\n# single-quote each arg of the include args so the '*' gets evaluated by zip\n# don't quote the exclude args they need to be evaluated by bash for rm -rf\nmy_include_args := $(foreach arg,$(my_include_args),'$(arg)')\n\nifeq ($(LOCAL_EMMA_INSTRUMENT),true)\n  my_files := $(intermediates.COMMON)/jacoco\n\n  # make a task that unzips the classes that we want to instrument from the\n  # input jar\n  my_unzipped_path := $(my_files)/work/classes-to-instrument/classes\n  my_unzipped_timestamp_path := $(my_files)/work/classes-to-instrument/updated.stamp\n$(my_unzipped_timestamp_path): PRIVATE_UNZIPPED_PATH := $(my_unzipped_path)\n$(my_unzipped_timestamp_path): PRIVATE_UNZIPPED_TIMESTAMP_PATH := $(my_unzipped_timestamp_path)\n$(my_unzipped_timestamp_path): PRIVATE_INCLUDE_ARGS := $(my_include_args)\n$(my_unzipped_timestamp_path): PRIVATE_EXCLUDE_ARGS := $(my_exclude_args)\n$(my_unzipped_timestamp_path): PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR := $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR)\n$(my_unzipped_timestamp_path): $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR)\n\trm -rf $(PRIVATE_UNZIPPED_PATH) $@\n\tmkdir -p $(PRIVATE_UNZIPPED_PATH)\n\tunzip -qDD $(PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR) \\\n\t  -d $(PRIVATE_UNZIPPED_PATH) \\\n\t  $(PRIVATE_INCLUDE_ARGS)\n\tchmod -R =rwX $(PRIVATE_UNZIPPED_PATH)\n\t(cd $(PRIVATE_UNZIPPED_PATH) && rm -rf $(PRIVATE_EXCLUDE_ARGS))\n\t(cd $(PRIVATE_UNZIPPED_PATH) && find -not -name \"*.class\" -type f -exec rm {} \\;)\n\ttouch $(PRIVATE_UNZIPPED_TIMESTAMP_PATH)\n# Unfortunately in the previous task above,\n# 'rm -rf $(PRIVATE_EXCLUDE_ARGS)' needs to be a separate\n# shell command after 'unzip'.\n# We can't just use the '-x' (exclude) option of 'unzip' because if both\n# inclusions and exclusions are specified and an exclusion matches no\n# inclusions, then 'unzip' exits with an error (error 11).\n# We could ignore the error, but that would make the process less reliable\n\n\n  # make a task that zips only the classes that will be instrumented\n  # (for passing in to the report generator later)\n  my_classes_to_report_on_path := $(my_files)/report-resources/jacoco-report-classes.jar\n$(my_classes_to_report_on_path): PRIVATE_UNZIPPED_PATH := $(my_unzipped_path)\n$(my_classes_to_report_on_path): $(my_unzipped_timestamp_path)\n\trm -f $@\n\tzip -q $@ \\\n\t  -r $(PRIVATE_UNZIPPED_PATH)\n\n# Make a rule to copy the jacoco-report-classes.jar to a packaging directory.\n$(eval $(call copy-one-file,$(my_classes_to_report_on_path),\\\n  $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar))\n$(call add-dependency,$(LOCAL_BUILT_MODULE),\\\n  $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar)\n\n  # make a task that invokes instrumentation\n  my_instrumented_path := $(my_files)/work/instrumented/classes\n  my_instrumented_timestamp_path := $(my_files)/work/instrumented/updated.stamp\n$(my_instrumented_timestamp_path): PRIVATE_INSTRUMENTED_PATH := $(my_instrumented_path)\n$(my_instrumented_timestamp_path): PRIVATE_INSTRUMENTED_TIMESTAMP_PATH := $(my_instrumented_timestamp_path)\n$(my_instrumented_timestamp_path): PRIVATE_UNZIPPED_PATH := $(my_unzipped_path)\n$(my_instrumented_timestamp_path): $(my_unzipped_timestamp_path) $(JACOCO_CLI_JAR)\n\trm -rf $(PRIVATE_INSTRUMENTED_PATH)\n\tmkdir -p $(PRIVATE_INSTRUMENTED_PATH)\n\tjava -jar $(JACOCO_CLI_JAR) \\\n\t  instrument \\\n\t  --quiet \\\n\t  --dest '$(PRIVATE_INSTRUMENTED_PATH)' \\\n\t  $(PRIVATE_UNZIPPED_PATH)\n\ttouch $(PRIVATE_INSTRUMENTED_TIMESTAMP_PATH)\n\n\n  # make a task that zips both the instrumented classes and the uninstrumented\n  # classes (this jar is the instrumented application to execute)\n  my_temp_jar_path := $(my_files)/work/usable.jar\n  LOCAL_FULL_CLASSES_JACOCO_JAR := $(intermediates.COMMON)/classes-jacoco.jar\n$(LOCAL_FULL_CLASSES_JACOCO_JAR): PRIVATE_TEMP_JAR_PATH := $(my_temp_jar_path)\n$(LOCAL_FULL_CLASSES_JACOCO_JAR): PRIVATE_INSTRUMENTED_PATH := $(my_instrumented_path)\n$(LOCAL_FULL_CLASSES_JACOCO_JAR): PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR := $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR)\n$(LOCAL_FULL_CLASSES_JACOCO_JAR): $(JAR_ARGS)\n$(LOCAL_FULL_CLASSES_JACOCO_JAR): $(my_instrumented_timestamp_path) $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR)\n\trm -f $@ $(PRIVATE_TEMP_JAR_PATH)\n\t# copy the pre-jacoco jar (containing files excluded from instrumentation)\n\tcp $(PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR) $(PRIVATE_TEMP_JAR_PATH)\n\t# copy instrumented files back into the resultant jar\n\t$(JAR) -uf $(PRIVATE_TEMP_JAR_PATH) $(call jar-args-sorted-files-in-directory,$(PRIVATE_INSTRUMENTED_PATH))\n\tmv $(PRIVATE_TEMP_JAR_PATH) $@\n\n  # this is used to trigger $(my_classes_to_report_on_path) to build\n  # when $(LOCAL_FULL_CLASSES_JACOCO_JAR) builds, but it isn't truly a\n  # dependency.\n$(LOCAL_FULL_CLASSES_JACOCO_JAR): $(my_classes_to_report_on_path)\n\nelse # LOCAL_EMMA_INSTRUMENT != true\n  LOCAL_FULL_CLASSES_JACOCO_JAR := $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR)\nendif # LOCAL_EMMA_INSTRUMENT == true\n\nLOCAL_INTERMEDIATE_TARGETS += $(LOCAL_FULL_CLASSES_JACOCO_JAR)\n"
  },
  {
    "path": "core/java.mk",
    "content": "# Target Java.\n# Requires:\n# LOCAL_MODULE_SUFFIX\n# LOCAL_MODULE_CLASS\n# all_res_assets\n\nLOCAL_NO_STANDARD_LIBRARIES:=$(strip $(LOCAL_NO_STANDARD_LIBRARIES))\nLOCAL_SDK_VERSION:=$(strip $(LOCAL_SDK_VERSION))\n\nproto_sources := $(filter %.proto,$(LOCAL_SRC_FILES))\nifneq ($(proto_sources),)\nifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),micro)\n    LOCAL_STATIC_JAVA_LIBRARIES += libprotobuf-java-micro\nelse\n  ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nano)\n    LOCAL_STATIC_JAVA_LIBRARIES += libprotobuf-java-nano\n  else\n    ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),stream)\n      # No library for stream protobufs\n    else\n      LOCAL_STATIC_JAVA_LIBRARIES += libprotobuf-java-lite\n    endif\n  endif\nendif\nendif\n\n# LOCAL_STATIC_JAVA_AAR_LIBRARIES and LOCAL_STATIC_ANDROID_LIBRARIES are also LOCAL_STATIC_JAVA_LIBRARIES.\nLOCAL_STATIC_JAVA_LIBRARIES := $(strip $(LOCAL_STATIC_JAVA_LIBRARIES) \\\n    $(LOCAL_STATIC_JAVA_AAR_LIBRARIES) \\\n    $(LOCAL_STATIC_ANDROID_LIBRARIES))\n# LOCAL_SHARED_ANDROID_LIBRARIES are also LOCAL_JAVA_LIBRARIES.\nLOCAL_JAVA_LIBRARIES := $(sort $(LOCAL_JAVA_LIBRARIES) $(LOCAL_SHARED_ANDROID_LIBRARIES))\n\nLOCAL_BUILT_MODULE_STEM := $(strip $(LOCAL_BUILT_MODULE_STEM))\nifeq ($(LOCAL_BUILT_MODULE_STEM),)\n$(error $(LOCAL_PATH): Target java template must define LOCAL_BUILT_MODULE_STEM)\nendif\nifneq ($(filter classes-compiled.jar classes.jar,$(LOCAL_BUILT_MODULE_STEM)),)\n$(error LOCAL_BUILT_MODULE_STEM may not be \"$(LOCAL_BUILT_MODULE_STEM)\")\nendif\n\n\n##############################################################################\n# Define the intermediate targets before including base_rules so they get\n# the correct environment.\n##############################################################################\n\nintermediates := $(call local-intermediates-dir)\nintermediates.COMMON := $(call local-intermediates-dir,COMMON)\n\nifeq ($(LOCAL_PROGUARD_ENABLED),disabled)\nLOCAL_PROGUARD_ENABLED :=\nendif\n\nfull_classes_turbine_jar := $(intermediates.COMMON)/classes-turbine.jar\nfull_classes_header_jarjar := $(intermediates.COMMON)/classes-header-jarjar.jar\nfull_classes_header_jar := $(intermediates.COMMON)/classes-header.jar\nfull_classes_compiled_jar := $(intermediates.COMMON)/classes-full-debug.jar\nfull_classes_processed_jar := $(intermediates.COMMON)/classes-processed.jar\nfull_classes_jarjar_jar := $(intermediates.COMMON)/classes-jarjar.jar\nfull_classes_combined_jar := $(intermediates.COMMON)/classes-combined.jar\nbuilt_dex_intermediate := $(intermediates.COMMON)/dex/classes.dex\nfull_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar\njava_source_list_file := $(intermediates.COMMON)/java-source-list\n\nifeq ($(LOCAL_MODULE_CLASS)$(LOCAL_SRC_FILES)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),APPS)\n# If this is an apk without any Java code (e.g. framework-res), we should skip compiling Java.\nfull_classes_jar :=\nbuilt_dex :=\nelse\nfull_classes_jar := $(intermediates.COMMON)/classes.jar\nbuilt_dex := $(intermediates.COMMON)/classes.dex\nendif\n\nLOCAL_INTERMEDIATE_TARGETS += \\\n    $(full_classes_turbine_jar) \\\n    $(full_classes_compiled_jar) \\\n    $(full_classes_jarjar_jar) \\\n    $(full_classes_jar) \\\n    $(full_classes_combined_jar) \\\n    $(built_dex_intermediate) \\\n    $(built_dex) \\\n    $(full_classes_stubs_jar) \\\n    $(java_source_list_file)\n\n###########################################################\n## AIDL: Compile .aidl files to .java\n###########################################################\naidl_sources := $(filter %.aidl,$(LOCAL_SRC_FILES))\naidl_java_sources :=\n\nifneq ($(strip $(aidl_sources)),)\n\naidl_preprocess_import :=\nifdef LOCAL_SDK_VERSION\nifneq ($(filter current system_current test_current core_current, $(LOCAL_SDK_VERSION)$(TARGET_BUILD_USE_PREBUILT_SDKS)),)\n  # LOCAL_SDK_VERSION is current and no TARGET_BUILD_USE_PREBUILT_SDKS\n  aidl_preprocess_import := $(FRAMEWORK_AIDL)\nelse\n  aidl_preprocess_import := $(call resolve-prebuilt-sdk-aidl-path,$(LOCAL_SDK_VERSION))\nendif # not current or system_current\nelse\n# build against the platform.\nLOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)\nendif # LOCAL_SDK_VERSION\n\n$(foreach s,$(aidl_sources),\\\n    $(eval $(call define-aidl-java-rule,$(s),$(intermediates.COMMON)/aidl,aidl_java_sources)))\n$(foreach java,$(aidl_java_sources), \\\n    $(call include-depfile,$(java:%.java=%.P),$(java)))\n\n$(aidl_java_sources) : $(LOCAL_ADDITIONAL_DEPENDENCIES) $(aidl_preprocess_import)\n\n$(aidl_java_sources): PRIVATE_AIDL_FLAGS := $(addprefix -p,$(aidl_preprocess_import)) -I$(LOCAL_PATH) -I$(LOCAL_PATH)/src $(addprefix -I,$(LOCAL_AIDL_INCLUDES))\n$(aidl_java_sources): PRIVATE_MODULE := $(LOCAL_MODULE)\n\nendif\n\n##########################################\n\n# All of the rules after full_classes_compiled_jar are very unlikely\n# to fail except for bugs in their respective tools.  If you would\n# like to run these rules, add the \"all\" modifier goal to the make\n# command line.\nifndef LOCAL_CHECKED_MODULE\nifdef full_classes_jar\nLOCAL_CHECKED_MODULE := $(full_classes_compiled_jar)\nendif\nendif\n\n#######################################\ninclude $(BUILD_SYSTEM)/base_rules.mk\n#######################################\n\n###########################################################\n## logtags: emit java source\n###########################################################\nifneq ($(strip $(logtags_sources)),)\n\nlogtags_java_sources := $(patsubst %.logtags,%.java,$(addprefix $(intermediates.COMMON)/logtags/, $(logtags_sources)))\nlogtags_sources := $(addprefix $(LOCAL_PATH)/, $(logtags_sources))\n\n$(logtags_java_sources): $(intermediates.COMMON)/logtags/%.java: $(LOCAL_PATH)/%.logtags $(JAVATAGS)\n\t$(transform-logtags-to-java)\n\nelse\nlogtags_java_sources :=\nendif\n\n##########################################\njava_sources := $(addprefix $(LOCAL_PATH)/, $(filter %.java,$(LOCAL_SRC_FILES))) $(aidl_java_sources) $(logtags_java_sources) \\\n                $(filter %.java,$(LOCAL_GENERATED_SOURCES))\njava_intermediate_sources := $(addprefix $(TARGET_OUT_COMMON_INTERMEDIATES)/, $(filter %.java,$(LOCAL_INTERMEDIATE_SOURCES)))\nall_java_sources := $(java_sources) $(java_intermediate_sources)\nALL_MODULES.$(my_register_name).SRCS := $(ALL_MODULES.$(my_register_name).SRCS) $(all_java_sources)\n\ninclude $(BUILD_SYSTEM)/java_common.mk\n\ninclude $(BUILD_SYSTEM)/sdk_check.mk\n\n# Set the profile source so that the odex / profile code included from java.mk\n# can find it.\n#\n# TODO: b/64896089, this is broken when called from package_internal.mk, since the file\n# we preopt from is a temporary file. This will be addressed in a follow up, possibly\n# by disabling stripping for profile guided preopt (which may be desirable for other\n# reasons anyway).\n#\n# Note that we set this only when called from package_internal.mk and not in other cases.\nifneq (,$(called_from_package_internal)\ndex_preopt_profile_src_file := $(LOCAL_BUILT_MODULE)\nendif\n\n#######################################\n# defines built_odex along with rule to install odex\nmy_manifest_or_apk := $(full_android_manifest)\ninclude $(BUILD_SYSTEM)/dex_preopt_odex_install.mk\nmy_manifest_or_apk :=\n#######################################\n\n# Make sure there's something to build.\nifdef full_classes_jar\nifndef need_compile_java\n$(call pretty-error,Target java module does not define any source or resource files)\nendif\nendif\n\n# Since we're using intermediates.COMMON, make sure that it gets cleaned\n# properly.\n$(cleantarget): PRIVATE_CLEAN_FILES += $(intermediates.COMMON)\n\nifdef full_classes_jar\n\n# Droiddoc isn't currently able to generate stubs for modules, so we're just\n# allowing it to use the classes.jar as the \"stubs\" that would be use to link\n# against, for the cases where someone needs the jar to link against.\n$(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_stubs_jar)))\nALL_MODULES.$(my_register_name).STUBS := $(full_classes_stubs_jar)\n\n$(full_classes_compiled_jar): PRIVATE_WARNINGS_ENABLE := $(LOCAL_WARNINGS_ENABLE)\n\n# Compile the java files to a .jar file.\n# This intentionally depends on java_sources, not all_java_sources.\n# Deps for generated source files must be handled separately,\n# via deps on the target that generates the sources.\n\n# For user / userdebug builds, strip the local variable table and the local variable\n# type table. This has no bearing on stack traces, but will leave less information\n# available via JDWP.\nifneq (,$(PRODUCT_MINIMIZE_JAVA_DEBUG_INFO))\nifneq (,$(filter userdebug user,$(TARGET_BUILD_VARIANT)))\nLOCAL_JAVACFLAGS+= -g:source,lines\nendif\nendif\n\n# List of dependencies for anything that needs all java sources in place\njava_sources_deps := \\\n    $(java_sources) \\\n    $(java_resource_sources) \\\n    $(LOCAL_SRCJARS) \\\n    $(LOCAL_ADDITIONAL_DEPENDENCIES)\n\n$(java_source_list_file): $(java_sources_deps) $(NORMALIZE_PATH)\n\t$(write-java-source-list)\n\nALL_MODULES.$(my_register_name).SRCJARS := $(LOCAL_SRCJARS)\n\nifneq ($(TURBINE_ENABLED),false)\n\n$(full_classes_turbine_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS) $(annotation_processor_flags)\n$(full_classes_turbine_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS)\n$(full_classes_turbine_jar): \\\n    $(java_source_list_file) \\\n    $(java_sources_deps) \\\n    $(full_java_header_libs) \\\n    $(full_java_bootclasspath_libs) \\\n    $(full_java_system_modules_deps) \\\n    $(NORMALIZE_PATH) \\\n    $(JAR_ARGS) \\\n    $(ZIPTIME) \\\n    | $(TURBINE) \\\n    $(MERGE_ZIPS)\n\t$(transform-java-to-header.jar)\n\n.KATI_RESTAT: $(full_classes_turbine_jar)\n\n# Run jarjar before generate classes-header.jar if necessary.\nifneq ($(strip $(LOCAL_JARJAR_RULES)),)\n$(full_classes_header_jarjar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES)\n$(full_classes_header_jarjar): $(full_classes_turbine_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR)\n\t$(call transform-jarjar)\nelse\nfull_classes_header_jarjar := $(full_classes_turbine_jar)\nendif\n\n$(eval $(call copy-one-file,$(full_classes_header_jarjar),$(full_classes_header_jar)))\n\nendif # TURBINE_ENABLED != false\n\n# TODO(b/143658984): goma can't handle the --system argument to javac.\n#$(full_classes_compiled_jar): .KATI_NINJA_POOL := $(GOMA_POOL)\n$(full_classes_compiled_jar): .KATI_NINJA_POOL := $(JAVAC_NINJA_POOL)\n$(full_classes_compiled_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS) $(annotation_processor_flags)\n$(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_FILES := $(LOCAL_JAR_EXCLUDE_FILES)\n$(full_classes_compiled_jar): PRIVATE_JAR_PACKAGES := $(LOCAL_JAR_PACKAGES)\n$(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_PACKAGES := $(LOCAL_JAR_EXCLUDE_PACKAGES)\n$(full_classes_compiled_jar): PRIVATE_JAVA_SOURCE_LIST := $(java_source_list_file)\n$(full_classes_compiled_jar): PRIVATE_ALL_JAVA_HEADER_LIBRARIES := $(full_java_header_libs)\n$(full_classes_compiled_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS)\n$(full_classes_compiled_jar): PRIVATE_SRCJAR_LIST_FILE := $(intermediates.COMMON)/srcjar-list\n$(full_classes_compiled_jar): PRIVATE_SRCJAR_INTERMEDIATES_DIR := $(intermediates.COMMON)/srcjars\n$(full_classes_compiled_jar): \\\n    $(java_source_list_file) \\\n    $(full_java_header_libs) \\\n    $(java_sources_deps) \\\n    $(full_java_bootclasspath_libs) \\\n    $(full_java_system_modules_deps) \\\n    $(layers_file) \\\n    $(annotation_processor_deps) \\\n    $(NORMALIZE_PATH) \\\n    $(JAR_ARGS) \\\n    $(ZIPSYNC) \\\n    $(SOONG_ZIP) \\\n    | $(SOONG_JAVAC_WRAPPER)\n\t@echo \"Target Java: $@\n\t$(call compile-java,$(TARGET_JAVAC),$(PRIVATE_ALL_JAVA_HEADER_LIBRARIES))\n\njavac-check : $(full_classes_compiled_jar)\njavac-check-$(LOCAL_MODULE) : $(full_classes_compiled_jar)\n.PHONY: javac-check-$(LOCAL_MODULE)\n\n$(full_classes_combined_jar): PRIVATE_DONT_DELETE_JAR_META_INF := $(LOCAL_DONT_DELETE_JAR_META_INF)\n$(full_classes_combined_jar): $(full_classes_compiled_jar) \\\n                              $(jar_manifest_file) \\\n                              $(full_static_java_libs) | $(MERGE_ZIPS)\n\t$(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(PRIVATE_JAR_MANIFEST)) \\\n            $(if $(PRIVATE_DONT_DELETE_JAR_META_INF),,-stripDir META-INF -zipToNotStrip $<) \\\n            $@ $< $(PRIVATE_STATIC_JAVA_LIBRARIES)\n\nifdef LOCAL_JAR_PROCESSOR\n# LOCAL_JAR_PROCESSOR_ARGS must be evaluated here to set up the rule-local\n# PRIVATE_JAR_PROCESSOR_ARGS variable, but $< and $@ are not available yet.\n# Set ${in} and ${out} so they can be referenced by LOCAL_JAR_PROCESSOR_ARGS\n# using deferred evaluation (LOCAL_JAR_PROCESSOR_ARGS = instead of :=).\nin := $(full_classes_combined_jar)\nout := $(full_classes_processed_jar).tmp\nmy_jar_processor := $(HOST_OUT_JAVA_LIBRARIES)/$(LOCAL_JAR_PROCESSOR).jar\n\n$(full_classes_processed_jar): PRIVATE_JAR_PROCESSOR_ARGS := $(LOCAL_JAR_PROCESSOR_ARGS)\n$(full_classes_processed_jar): PRIVATE_JAR_PROCESSOR := $(my_jar_processor)\n$(full_classes_processed_jar): PRIVATE_TMP_OUT := $(out)\nin :=\nout :=\n\n$(full_classes_processed_jar): $(full_classes_combined_jar) $(my_jar_processor)\n\t@echo Processing $@ with $(PRIVATE_JAR_PROCESSOR)\n\t$(hide) rm -f $@ $(PRIVATE_TMP_OUT)\n\t$(hide) $(JAVA) -jar $(PRIVATE_JAR_PROCESSOR) $(PRIVATE_JAR_PROCESSOR_ARGS)\n\t$(hide) mv $(PRIVATE_TMP_OUT) $@\n\nmy_jar_processor :=\nelse\nfull_classes_processed_jar := $(full_classes_combined_jar)\nendif\n\n# Run jarjar if necessary\nifneq ($(strip $(LOCAL_JARJAR_RULES)),)\n$(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES)\n$(full_classes_jarjar_jar): $(full_classes_processed_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR)\n\t$(call transform-jarjar)\nelse\nfull_classes_jarjar_jar := $(full_classes_processed_jar)\nendif\n\n$(eval $(call copy-one-file,$(full_classes_jarjar_jar),$(full_classes_jar)))\n\n#######################################\nLOCAL_FULL_CLASSES_PRE_JACOCO_JAR := $(full_classes_jar)\n\ninclude $(BUILD_SYSTEM)/jacoco.mk\n#######################################\n\n# Temporarily enable --multi-dex until proguard supports v53 class files\n# ( http://b/67673860 ) or we move away from proguard altogether.\nLOCAL_DX_FLAGS := $(filter-out --multi-dex,$(LOCAL_DX_FLAGS)) --multi-dex\n\nfull_classes_pre_proguard_jar := $(LOCAL_FULL_CLASSES_JACOCO_JAR)\n\n# Keep a copy of the jar just before proguard processing.\n$(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),$(intermediates.COMMON)/classes-pre-proguard.jar))\n\n# Run proguard if necessary\nifdef LOCAL_PROGUARD_ENABLED\nifneq ($(filter-out full custom obfuscation optimization,$(LOCAL_PROGUARD_ENABLED)),)\n    $(warning while processing: $(LOCAL_MODULE))\n    $(error invalid value for LOCAL_PROGUARD_ENABLED: $(LOCAL_PROGUARD_ENABLED))\nendif\nproguard_dictionary := $(intermediates.COMMON)/proguard_dictionary\nproguard_configuration := $(intermediates.COMMON)/proguard_configuration\n\n# When an app contains references to APIs that are not in the SDK specified by\n# its LOCAL_SDK_VERSION for example added by support library or by runtime\n# classes added by desugar, we artifically raise the \"SDK version\" \"linked\" by\n# ProGuard, to\n# - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version.\n# - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version.\n# See b/20667396\nmy_proguard_sdk_raise :=\nifdef LOCAL_SDK_VERSION\nifdef TARGET_BUILD_APPS\nifeq (,$(filter current system_current test_current core_current, $(LOCAL_SDK_VERSION)))\n  my_proguard_sdk_raise := $(call java-lib-header-files, $(call resolve-prebuilt-sdk-module,current))\nendif\nelse\n  # For platform build, we can't just raise to the \"current\" SDK,\n  # that would break apps that use APIs removed from the current SDK.\n  my_proguard_sdk_raise := $(call java-lib-header-files,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES))\nendif\nifdef BOARD_SYSTEMSDK_VERSIONS\nifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE)))\n  # But for vendor or odm apks, don't raise SDK as the apks are required to\n  # use SDK APIs only\n  my_proguard_sdk_raise :=\nendif\nendif\nendif\n\nlegacy_proguard_flags := $(addprefix -libraryjars ,$(my_proguard_sdk_raise) \\\n  $(filter-out $(my_proguard_sdk_raise), \\\n    $(full_java_bootclasspath_libs) \\\n    $(full_shared_java_header_libs)))\n\nlegacy_proguard_lib_deps := $(my_proguard_sdk_raise) \\\n  $(filter-out $(my_proguard_sdk_raise),$(full_java_bootclasspath_libs) $(full_shared_java_header_libs))\n\nlegacy_proguard_flags += -printmapping $(proguard_dictionary)\nlegacy_proguard_flags += -printconfiguration $(proguard_configuration)\n\ncommon_proguard_flags :=\ncommon_proguard_flag_files := $(BUILD_SYSTEM)/proguard.flags\nifneq ($(LOCAL_INSTRUMENTATION_FOR)$(filter tests,$(LOCAL_MODULE_TAGS)),)\ncommon_proguard_flags += -dontshrink # don't shrink tests by default\nendif # test package\nifneq ($(LOCAL_PROGUARD_ENABLED),custom)\n  common_proguard_flag_files += $(foreach l,$(LOCAL_STATIC_ANDROID_LIBRARIES),\\\n      $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/export_proguard_flags)\nendif\nifneq ($(common_proguard_flag_files),)\ncommon_proguard_flags += $(addprefix -include , $(common_proguard_flag_files))\n# This is included from $(BUILD_SYSTEM)/proguard.flags\ncommon_proguard_flag_files += $(BUILD_SYSTEM)/proguard_basic_keeps.flags\nendif\n\nifeq ($(filter obfuscation,$(LOCAL_PROGUARD_ENABLED)),)\n# By default no obfuscation\ncommon_proguard_flags += -dontobfuscate\nendif  # No obfuscation\nifeq ($(filter optimization,$(LOCAL_PROGUARD_ENABLED)),)\n# By default no optimization\ncommon_proguard_flags += -dontoptimize\nendif  # No optimization\n\nifdef LOCAL_INSTRUMENTATION_FOR\nifeq ($(filter obfuscation,$(LOCAL_PROGUARD_ENABLED)),)\n# If no obfuscation, link in the instrmented package's classes.jar as a library.\n# link_instr_classes_jar is defined in base_rule.mk\nlegacy_proguard_flags += -libraryjars $(link_instr_classes_jar)\nlegacy_proguard_lib_deps += $(link_instr_classes_jar)\nelse # obfuscation\n# If obfuscation is enabled, the main app must be obfuscated too.\n# We need to run obfuscation using the main app's dictionary,\n# and treat the main app's class.jar as injars instead of libraryjars.\nlegacy_proguard_flags := -injars  $(link_instr_classes_jar) \\\n    -outjars $(intermediates.COMMON)/proguard.$(LOCAL_INSTRUMENTATION_FOR).jar \\\n    -include $(link_instr_intermediates_dir.COMMON)/proguard_options \\\n    -applymapping $(link_instr_intermediates_dir.COMMON)/proguard_dictionary \\\n    -verbose \\\n    $(legacy_proguard_flags)\nlegacy_proguard_lib_deps += \\\n  $(link_instr_classes_jar) \\\n  $(link_instr_intermediates_dir.COMMON)/proguard_options \\\n  $(link_instr_intermediates_dir.COMMON)/proguard_dictionary \\\n\n# Sometimes (test + main app) uses different keep rules from the main app -\n# apply the main app's dictionary anyway.\nlegacy_proguard_flags += -ignorewarnings\n\nendif # no obfuscation\nendif # LOCAL_INSTRUMENTATION_FOR\n\nproguard_flag_files := $(addprefix $(LOCAL_PATH)/, $(LOCAL_PROGUARD_FLAG_FILES))\nproguard_flag_files += $(addprefix $(LOCAL_PATH)/, $(LOCAL_R8_FLAG_FILES))\nLOCAL_PROGUARD_FLAGS += $(addprefix -include , $(proguard_flag_files))\nLOCAL_PROGUARD_FLAGS_DEPS += $(proguard_flag_files)\nproguard_flag_files :=\n\nifdef LOCAL_TEST_MODULE_TO_PROGUARD_WITH\nextra_input_jar := $(call intermediates-dir-for,APPS,$(LOCAL_TEST_MODULE_TO_PROGUARD_WITH),,COMMON)/classes.jar\nelse\nextra_input_jar :=\nendif\n\nifneq ($(filter obfuscation,$(LOCAL_PROGUARD_ENABLED)),)\n  $(built_dex_intermediate): .KATI_IMPLICIT_OUTPUTS := $(proguard_dictionary) $(proguard_configuration)\n\n  # Make a rule to copy the proguard_dictionary to a packaging directory.\n  $(eval $(call copy-one-file,$(proguard_dictionary),\\\n    $(call local-packaging-dir,proguard_dictionary)/proguard_dictionary))\n  $(call add-dependency,$(LOCAL_BUILT_MODULE),\\\n    $(call local-packaging-dir,proguard_dictionary)/proguard_dictionary)\n\n  $(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),\\\n    $(call local-packaging-dir,proguard_dictionary)/classes.jar))\n  $(call add-dependency,$(LOCAL_BUILT_MODULE),\\\n    $(call local-packaging-dir,proguard_dictionary)/classes.jar)\nendif\n\nendif # LOCAL_PROGUARD_ENABLED defined\n\nifneq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true)\n$(built_dex_intermediate): PRIVATE_DX_FLAGS := $(LOCAL_DX_FLAGS)\n\nifdef LOCAL_PROGUARD_ENABLED\n  $(built_dex_intermediate): .KATI_NINJA_POOL := $(R8_NINJA_POOL)\n  $(built_dex_intermediate): PRIVATE_EXTRA_INPUT_JAR := $(extra_input_jar)\n  $(built_dex_intermediate): PRIVATE_PROGUARD_FLAGS := $(legacy_proguard_flags) $(common_proguard_flags) $(LOCAL_PROGUARD_FLAGS)\n  $(built_dex_intermediate): PRIVATE_PROGUARD_DICTIONARY := $(proguard_dictionary)\n  $(built_dex_intermediate) : $(full_classes_pre_proguard_jar) $(extra_input_jar) $(my_proguard_sdk_raise) $(common_proguard_flag_files) $(legacy_proguard_lib_deps) $(R8) $(LOCAL_PROGUARD_FLAGS_DEPS)\n\t$(transform-jar-to-dex-r8)\nelse # !LOCAL_PROGUARD_ENABLED\n  $(built_dex_intermediate): .KATI_NINJA_POOL := $(D8_NINJA_POOL)\n  $(built_dex_intermediate): PRIVATE_D8_LIBS := $(full_java_bootclasspath_libs) $(full_shared_java_header_libs)\n  $(built_dex_intermediate): $(full_java_bootclasspath_libs) $(full_shared_java_header_libs)\n  $(built_dex_intermediate): $(full_classes_pre_proguard_jar) $(D8) $(ZIP2ZIP)\n\t$(transform-classes.jar-to-dex)\nendif\n\n$(foreach pair,$(PRODUCT_BOOT_JARS), \\\n  $(if $(filter $(LOCAL_MODULE),$(call word-colon,2,$(pair))), \\\n    $(call pretty-error,Modules in PRODUCT_BOOT_JARS must be defined in Android.bp files)))\n\n$(built_dex): $(built_dex_intermediate)\n\t@echo Copying: $@\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) rm -f $(dir $@)/classes*.dex\n\t$(hide) cp -fp $(dir $<)/classes*.dex $(dir $@)\n\njava-dex: $(built_dex)\n\nendif # !LOCAL_IS_STATIC_JAVA_LIBRARY\n\nfindbugs_xml := $(intermediates.COMMON)/findbugs.xml\n$(findbugs_xml): PRIVATE_AUXCLASSPATH := $(addprefix -auxclasspath ,$(strip \\\n    $(call normalize-path-list,$(filter %.jar,$(full_java_libs)))))\n$(findbugs_xml): PRIVATE_FINDBUGS_FLAGS := $(LOCAL_FINDBUGS_FLAGS)\n$(findbugs_xml) : $(full_classes_pre_proguard_jar) $(filter %.xml, $(LOCAL_FINDBUGS_FLAGS))\n\t@echo Findbugs: $@\n\t$(hide) $(FINDBUGS) -textui -effort:min -xml:withMessages \\\n\t\t$(PRIVATE_AUXCLASSPATH) $(PRIVATE_FINDBUGS_FLAGS) \\\n\t\t$< \\\n\t\t> $@\n\nALL_FINDBUGS_FILES += $(findbugs_xml)\n\nfindbugs_html := $(PRODUCT_OUT)/findbugs/$(LOCAL_MODULE).html\n$(findbugs_html) : PRIVATE_XML_FILE := $(findbugs_xml)\n$(LOCAL_MODULE)-findbugs : $(findbugs_html)\n.PHONY: $(LOCAL_MODULE)-findbugs\n$(findbugs_html) : $(findbugs_xml)\n\t@mkdir -p $(dir $@)\n\t@echo ConvertXmlToText: $@\n\t$(hide) $(FINDBUGS_DIR)/convertXmlToText -html:fancy.xsl $(PRIVATE_XML_FILE) \\\n\t> $@\n\n$(LOCAL_MODULE)-findbugs : $(findbugs_html)\n\nendif  # full_classes_jar is defined\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_DEFAULT_APP_TARGET_SDK := $(call module-target-sdk-version)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SDK_VERSION := $(call module-sdk-version)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MIN_SDK_VERSION := $(call codename-or-sdk-to-sdk,$(call module-min-sdk-version))\n"
  },
  {
    "path": "core/java_common.mk",
    "content": "# Common to host and target Java modules.\n\nmy_soong_problems :=\n\nifneq ($(filter ../%,$(LOCAL_SRC_FILES)),)\nmy_soong_problems += dotdot_srcs\nendif\n\n###########################################################\n## Java version\n###########################################################\n# Use the LOCAL_JAVA_LANGUAGE_VERSION if it is set, otherwise\n# use one based on the LOCAL_SDK_VERSION.\n#\n# The LOCAL_SDK_VERSION behavior is to ensure that, by default,\n# code that is expected to run on older releases of Android\n# does not use any 1.8 language features that are not supported\n# on earlier runtimes (like default / static interface methods).\n# Modules can override this logic by specifying\n# LOCAL_JAVA_LANGUAGE_VERSION explicitly.\nifeq (,$(LOCAL_JAVA_LANGUAGE_VERSION))\n  ifdef LOCAL_IS_HOST_MODULE\n    # Host modules always default to 1.9\n    LOCAL_JAVA_LANGUAGE_VERSION := 1.9\n  else\n    ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_1_9_SUPPORT)))\n      LOCAL_JAVA_LANGUAGE_VERSION := 1.8\n    else ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_11_SUPPORT)))\n      LOCAL_JAVA_LANGUAGE_VERSION := 1.9\n    else ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_17_SUPPORT)))\n      LOCAL_JAVA_LANGUAGE_VERSION := 11\n    else ifneq (,$(LOCAL_SDK_VERSION)$(TARGET_BUILD_USE_PREBUILT_SDKS))\n      # TODO(ccross): allow 1.9 for current and unbundled once we have SDK system modules\n      LOCAL_JAVA_LANGUAGE_VERSION := 1.8\n    else ifeq ($(RELEASE_TARGET_JAVA_21),true)\n      LOCAL_JAVA_LANGUAGE_VERSION := 21\n    else\n      LOCAL_JAVA_LANGUAGE_VERSION := 17\n    endif\n  endif\nendif\nLOCAL_JAVACFLAGS += -source $(LOCAL_JAVA_LANGUAGE_VERSION) -target $(LOCAL_JAVA_LANGUAGE_VERSION)\n\n###########################################################\n\n# OpenJDK versions up to 8 shipped with bootstrap and tools jars\n# (rt.jar, jce.jar, tools.jar etc.). These are no longer part of\n# OpenJDK 9, but we still make them available for host tools that\n# are targeting older versions.\nUSE_HOST_BOOTSTRAP_JARS := true\nifeq (,$(filter $(LOCAL_JAVA_LANGUAGE_VERSION), 1.6 1.7 1.8))\nUSE_HOST_BOOTSTRAP_JARS := false\nendif\n\n###########################################################\n\n# Drop HOST_JDK_TOOLS_JAR from classpath when targeting versions > 9 (which don't have it).\n# TODO: Remove HOST_JDK_TOOLS_JAR and all references to it once host\n# bootstrap jars are no longer supported (ie. when USE_HOST_BOOTSTRAP_JARS\n# is always false). http://b/38418220\nifneq ($(USE_HOST_BOOTSTRAP_JARS),true)\nLOCAL_CLASSPATH := $(filter-out $(HOST_JDK_TOOLS_JAR),$(LOCAL_CLASSPATH))\nendif\n\n###########################################################\n## .proto files: Compile proto files to .java\n###########################################################\nifeq ($(strip $(LOCAL_PROTOC_OPTIMIZE_TYPE)),)\n  LOCAL_PROTOC_OPTIMIZE_TYPE := lite\nendif\nproto_sources := $(filter %.proto,$(LOCAL_SRC_FILES))\nifneq ($(proto_sources),)\nproto_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(proto_sources))\n\nproto_java_intemediate_dir := $(intermediates.COMMON)/proto\nproto_java_sources_dir := $(proto_java_intemediate_dir)/src\nproto_java_srcjar := $(intermediates.COMMON)/proto.srcjar\n\nLOCAL_SRCJARS += $(proto_java_srcjar)\n\n$(proto_java_srcjar): PRIVATE_PROTO_INCLUDES := $(TOP)\n$(proto_java_srcjar): PRIVATE_PROTO_SRC_FILES := $(proto_sources_fullpath)\n$(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_DIR := $(proto_java_sources_dir)\n$(proto_java_srcjar): PRIVATE_PROTOC_FLAGS := $(LOCAL_PROTOC_FLAGS)\nifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),micro)\n  $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --javamicro_out\n  $(proto_java_srcjar): PRIVATE_PROTOC_FLAGS += --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-javamicro\n  $(proto_java_srcjar): $(HOST_OUT_EXECUTABLES)/protoc-gen-javamicro\nelse ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nano)\n  $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --javanano_out\n  $(proto_java_srcjar): PRIVATE_PROTOC_FLAGS += --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-javanano\n  $(proto_java_srcjar): $(HOST_OUT_EXECUTABLES)/protoc-gen-javanano\nelse ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),stream)\n  $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --javastream_out\n  $(proto_java_srcjar): PRIVATE_PROTOC_FLAGS += --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-javastream\n  $(proto_java_srcjar): $(HOST_OUT_EXECUTABLES)/protoc-gen-javastream\nelse\n  $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --java_out\nendif\n$(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_PARAMS := $(if $(filter lite,$(LOCAL_PROTOC_OPTIMIZE_TYPE)),lite$(if $(LOCAL_PROTO_JAVA_OUTPUT_PARAMS),:,),)$(LOCAL_PROTO_JAVA_OUTPUT_PARAMS)\n$(proto_java_srcjar) : $(proto_sources_fullpath) $(PROTOC) $(SOONG_ZIP)\n\t$(call transform-proto-to-java)\n\n#TODO: protoc should output the dependencies introduced by imports.\n\nALL_MODULES.$(my_register_name).PROTO_FILES := $(proto_sources_fullpath)\nendif # proto_sources\n\n#########################################\n## Java resources\n\n# Look for resource files in any specified directories.\n# Non-java and non-doc files will be picked up as resources\n# and included in the output jar file.\njava_resource_file_groups :=\n\nLOCAL_JAVA_RESOURCE_DIRS := $(strip $(LOCAL_JAVA_RESOURCE_DIRS))\nifneq ($(LOCAL_JAVA_RESOURCE_DIRS),)\n  # This makes a list of words like\n  #     <dir1>::<file1>:<file2> <dir2>::<file1> <dir3>:\n  # where each of the files is relative to the directory it's grouped with.\n  # Directories that don't contain any resource files will result in groups\n  # that end with a colon, and they are stripped out in the next step.\n  java_resource_file_groups += \\\n    $(foreach dir,$(LOCAL_JAVA_RESOURCE_DIRS), \\\n\t$(subst $(space),:,$(strip \\\n\t\t$(LOCAL_PATH)/$(dir): \\\n\t    $(patsubst ./%,%,$(sort $(shell cd $(LOCAL_PATH)/$(dir) && \\\n\t\tfind . \\\n\t\t    -type d -a -name \".svn\" -prune -o \\\n\t\t    -type f \\\n\t\t\t-a \\! -name \"*.java\" \\\n\t\t\t-a \\! -name \"package.html\" \\\n\t\t\t-a \\! -name \"overview.html\" \\\n\t\t\t-a \\! -name \".*.swp\" \\\n\t\t\t-a \\! -name \".DS_Store\" \\\n\t\t\t-a \\! -name \"*~\" \\\n\t\t\t-print \\\n\t\t    ))) \\\n\t)) \\\n    )\n  java_resource_file_groups := $(filter-out %:,$(java_resource_file_groups))\nendif # LOCAL_JAVA_RESOURCE_DIRS\n\nifneq ($(LOCAL_JAVA_RESOURCE_FILES),)\n  # Converts LOCAL_JAVA_RESOURCE_FILES := <file> to $(dir $(file))::$(notdir $(file))\n  # and LOCAL_JAVA_RESOURCE_FILES := <dir>:<file> to <dir>::<file>\n  java_resource_file_groups += $(strip $(foreach res,$(LOCAL_JAVA_RESOURCE_FILES), \\\n    $(eval _file := $(call word-colon,2,$(res))) \\\n    $(if $(_file), \\\n      $(eval _base := $(call word-colon,1,$(res))), \\\n      $(eval _base := $(dir $(res))) \\\n        $(eval _file := $(notdir $(res)))) \\\n    $(if $(filter /%, \\\n      $(filter-out $(OUT_DIR)/%,$(_base) $(_file))), \\\n        $(call pretty-error,LOCAL_JAVA_RESOURCE_FILES may not include absolute paths: $(_base) $(_file))) \\\n    $(patsubst %/,%,$(_base))::$(_file)))\n\nendif # LOCAL_JAVA_RESOURCE_FILES\n\nifdef java_resource_file_groups\n  # The full paths to all resources, used for dependencies.\n  java_resource_sources := \\\n    $(foreach group,$(java_resource_file_groups), \\\n\t$(addprefix $(word 1,$(subst :,$(space),$(group)))/, \\\n\t    $(wordlist 2,9999,$(subst :,$(space),$(group))) \\\n\t) \\\n    )\n  # The arguments to jar that will include these files in a jar file.\n  # Quote the file name to handle special characters (such as #) correctly.\n  extra_jar_args := \\\n    $(foreach group,$(java_resource_file_groups), \\\n\t$(addprefix -C \"$(word 1,$(subst :,$(space),$(group)))\" , \\\n\t    $(foreach w, $(wordlist 2,9999,$(subst :,$(space),$(group))), \"$(w)\" ) \\\n\t) \\\n    )\n  java_resource_file_groups :=\nelse\n  java_resource_sources :=\n  extra_jar_args :=\nendif # java_resource_file_groups\n\n#####################################\n## Warn if there is unrecognized file in LOCAL_SRC_FILES.\nmy_unknown_src_files := $(filter-out \\\n  %.java %.aidl %.proto %.logtags, \\\n  $(LOCAL_SRC_FILES) $(LOCAL_INTERMEDIATE_SOURCES) $(LOCAL_GENERATED_SOURCES))\nifneq ($(my_unknown_src_files),)\n$(warning $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Unused source files: $(my_unknown_src_files))\nendif\n\n######################################\n## PRIVATE java vars\n# LOCAL_SOURCE_FILES_ALL_GENERATED is set only if the module does not have static source files,\n# but generated source files.\n# You have to set up the dependency in some other way.\nneed_compile_java := $(strip $(all_java_sources)$(LOCAL_SRCJARS)$(all_res_assets)$(java_resource_sources))$(LOCAL_STATIC_JAVA_LIBRARIES)$(filter true,$(LOCAL_SOURCE_FILES_ALL_GENERATED))\nifdef need_compile_java\n\nannotation_processor_flags :=\nannotation_processor_deps :=\nannotation_processor_jars :=\n\n# If error prone is enabled then add LOCAL_ERROR_PRONE_FLAGS to LOCAL_JAVACFLAGS\nifeq ($(RUN_ERROR_PRONE),true)\nannotation_processor_jars += $(ERROR_PRONE_JARS)\nLOCAL_JAVACFLAGS += $(ERROR_PRONE_FLAGS)\nLOCAL_JAVACFLAGS += '-Xplugin:ErrorProne $(ERROR_PRONE_CHECKS) $(LOCAL_ERROR_PRONE_FLAGS)'\nendif\n\nifdef LOCAL_ANNOTATION_PROCESSORS\n  annotation_processor_jars += $(call java-lib-files,$(LOCAL_ANNOTATION_PROCESSORS),true)\n\n  # b/25860419: annotation processors must be explicitly specified for grok\n  annotation_processor_flags += $(foreach class,$(LOCAL_ANNOTATION_PROCESSOR_CLASSES),-processor $(class))\nendif\n\nifneq (,$(strip $(annotation_processor_jars)))\nannotation_processor_flags += -processorpath $(call normalize-path-list,$(annotation_processor_jars))\nannotation_processor_deps += $(annotation_processor_jars)\nendif\n\nfull_static_java_libs := $(call java-lib-files,$(LOCAL_STATIC_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))\nfull_static_java_header_libs := $(call java-lib-header-files,$(LOCAL_STATIC_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_STATIC_JAVA_LIBRARIES := $(full_static_java_libs)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_STATIC_JAVA_HEADER_LIBRARIES := $(full_static_java_header_libs)\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASSET_DIR := $(LOCAL_ASSET_DIR)\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CLASS_INTERMEDIATES_DIR := $(intermediates.COMMON)/classes\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ANNO_INTERMEDIATES_DIR := $(intermediates.COMMON)/anno\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SOURCE_INTERMEDIATES_DIR := $(intermediates.COMMON)/src\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HAS_RS_SOURCES :=\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_SOURCES := $(all_java_sources)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_SOURCE_LIST := $(java_source_list_file)\n\n# Quickly check class path vars.\ndisallowed_deps := $(foreach sdk,$(TARGET_AVAILABLE_SDK_VERSIONS),$(call resolve-prebuilt-sdk-module,$(sdk)))\ndisallowed_deps += $(foreach sdk,$(TARGET_AVAILABLE_SDK_VERSIONS),\\\n  $(foreach sdk_lib,$(JAVA_SDK_LIBRARIES),$(call resolve-prebuilt-sdk-module,$(sdk),$(sdk_lib))))\nbad_deps := $(filter $(disallowed_deps),$(LOCAL_JAVA_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES))\nifneq (,$(bad_deps))\n  $(call pretty-error,SDK modules should not be depended on directly. Please use LOCAL_SDK_VERSION for $(bad_deps))\nendif\n\nfull_java_bootclasspath_libs :=\nempty_bootclasspath :=\nmy_system_modules :=\nexported_sdk_libs_files :=\nmy_exported_sdk_libs_file :=\n\nifndef LOCAL_IS_HOST_MODULE\n  sdk_libs :=\n\n  # When an sdk lib name is listed in LOCAL_JAVA_LIBRARIES, move it to LOCAL_SDK_LIBRARIES, so that\n  # it is correctly redirected to the stubs library.\n  LOCAL_SDK_LIBRARIES += $(filter $(JAVA_SDK_LIBRARIES),$(LOCAL_JAVA_LIBRARIES))\n  LOCAL_JAVA_LIBRARIES := $(filter-out $(JAVA_SDK_LIBRARIES),$(LOCAL_JAVA_LIBRARIES))\n\n  ifeq ($(LOCAL_SDK_VERSION),)\n    ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true)\n      # No bootclasspath. But we still need \"\" to prevent javac from using default host bootclasspath.\n      empty_bootclasspath := \"\"\n      # Most users of LOCAL_NO_STANDARD_LIBRARIES really mean no framework libs,\n      # and manually add back the core libs.  The ones that don't are in soong\n      # now, so just always assume that they want the default system modules\n      my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES)\n    else  # LOCAL_NO_STANDARD_LIBRARIES\n      full_java_bootclasspath_libs := $(call java-lib-header-files,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES))\n      LOCAL_JAVA_LIBRARIES := $(filter-out $(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES),$(LOCAL_JAVA_LIBRARIES))\n      my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES)\n    endif  # LOCAL_NO_STANDARD_LIBRARIES\n\n    ifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS))\n      sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(call resolve-prebuilt-sdk-module,system_current,$(lib_name)))\n    else\n      # When SDK libraries are referenced from modules built without SDK, provide the all APIs to them\n      sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(lib_name))\n    endif\n  else\n    ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true)\n      $(call pretty-error,Must not define both LOCAL_NO_STANDARD_LIBRARIES and LOCAL_SDK_VERSION)\n    endif\n    ifeq ($(strip $(filter $(LOCAL_SDK_VERSION),$(TARGET_AVAILABLE_SDK_VERSIONS))),)\n      $(call pretty-error,Invalid LOCAL_SDK_VERSION '$(LOCAL_SDK_VERSION)' \\\n             Choices are: $(TARGET_AVAILABLE_SDK_VERSIONS))\n    endif\n\n    ifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)$(filter-out %current,$(LOCAL_SDK_VERSION)))\n      # TARGET_BUILD_USE_PREBUILT_SDKS mode or numbered SDK. Use prebuilt modules.\n      sdk_module := $(call resolve-prebuilt-sdk-module,$(LOCAL_SDK_VERSION))\n      sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(call resolve-prebuilt-sdk-module,$(LOCAL_SDK_VERSION),$(lib_name)))\n    else\n      # Note: the lib naming scheme must be kept in sync with build/soong/java/sdk_library.go.\n      sdk_lib_suffix = $(call pretty-error,sdk_lib_suffix was not set correctly)\n      ifeq (current,$(LOCAL_SDK_VERSION))\n        sdk_module := $(ANDROID_PUBLIC_STUBS)\n        sdk_lib_suffix := .stubs\n      else ifeq (system_current,$(LOCAL_SDK_VERSION))\n        sdk_module := $(ANDROID_SYSTEM_STUBS)\n        sdk_lib_suffix := .stubs.system\n      else ifeq (test_current,$(LOCAL_SDK_VERSION))\n        sdk_module := $(ANDROID_TEST_STUBS)\n        sdk_lib_suffix := .stubs.test\n      else ifeq (core_current,$(LOCAL_SDK_VERSION))\n        sdk_module := $(ANDROID_CORE_STUBS)\n        sdk_lib_suffix = $(call pretty-error,LOCAL_SDK_LIBRARIES not supported for LOCAL_SDK_VERSION = core_current)\n      endif\n      sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(lib_name)$(sdk_lib_suffix))\n    endif\n    full_java_bootclasspath_libs := $(call java-lib-header-files,$(sdk_module))\n  endif # LOCAL_SDK_VERSION\n\n  ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true)\n    ifneq ($(LOCAL_MODULE),jacocoagent)\n      ifeq ($(EMMA_INSTRUMENT),true)\n        ifneq ($(EMMA_INSTRUMENT_STATIC),true)\n          # For instrumented build, if Jacoco is not being included statically\n          # in instrumented packages then include Jacoco classes into the\n          # bootclasspath.\n          full_java_bootclasspath_libs += $(call java-lib-header-files,jacocoagent)\n        endif # EMMA_INSTRUMENT_STATIC\n      endif # EMMA_INSTRUMENT\n    endif # LOCAL_MODULE == jacocoagent\n  endif # LOCAL_NO_STANDARD_LIBRARIES\n\n  # In order to compile lambda code javac requires various invokedynamic-\n  # related classes to be present. This change adds stubs needed for\n  # javac to compile lambdas.\n  ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true)\n    ifdef TARGET_BUILD_USE_PREBUILT_SDKS\n      full_java_bootclasspath_libs += $(call java-lib-header-files,sdk-core-lambda-stubs)\n    else\n      full_java_bootclasspath_libs += $(call java-lib-header-files,core-lambda-stubs)\n    endif\n  endif\n  full_shared_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES) $(sdk_libs),$(LOCAL_IS_HOST_MODULE))\n  full_shared_java_header_libs := $(call java-lib-header-files,$(LOCAL_JAVA_LIBRARIES) $(sdk_libs),$(LOCAL_IS_HOST_MODULE))\n  sdk_libs :=\n\n  # Files that contains the names of SDK libraries exported from dependencies. These will be re-exported.\n  # Note: No need to consider LOCAL_*_ANDROID_LIBRARIES and LOCAL_STATIC_JAVA_AAR_LIBRARIES. They are all appended to\n  # LOCAL_*_JAVA_LIBRARIES in java.mk\n  exported_sdk_libs_files := $(call exported-sdk-libs-files,$(LOCAL_JAVA_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES))\n  # The file that contains the names of all SDK libraries that this module exports and re-exports\n  my_exported_sdk_libs_file := $(call local-intermediates-dir,COMMON)/exported-sdk-libs\n\nelse # LOCAL_IS_HOST_MODULE\n\n  ifeq ($(USE_CORE_LIB_BOOTCLASSPATH),true)\n    ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true)\n      empty_bootclasspath := \"\"\n    else\n      full_java_bootclasspath_libs := $(call java-lib-header-files,$(addsuffix -hostdex,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES)),true)\n    endif\n\n    my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES)\n    full_shared_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES),true)\n    full_shared_java_header_libs := $(call java-lib-header-files,$(LOCAL_JAVA_LIBRARIES),true)\n  else # !USE_CORE_LIB_BOOTCLASSPATH\n    # Give host-side tools a version of OpenJDK's standard libraries\n    # close to what they're targeting. As of Dec 2017, AOSP is only\n    # bundling OpenJDK 8 and 9, so nothing < 8 is available.\n    #\n    # When building with OpenJDK 8, the following should have no\n    # effect since those jars would be available by default.\n    #\n    # When building with OpenJDK 9 but targeting a version < 1.8,\n    # putting them on the bootclasspath means that:\n    # a) code can't (accidentally) refer to OpenJDK 9 specific APIs\n    # b) references to existing APIs are not reinterpreted in an\n    #    OpenJDK 9-specific way, eg. calls to subclasses of\n    #    java.nio.Buffer as in http://b/70862583\n    ifeq ($(USE_HOST_BOOTSTRAP_JARS),true)\n      full_java_bootclasspath_libs += $(ANDROID_JAVA8_HOME)/jre/lib/jce.jar\n      full_java_bootclasspath_libs += $(ANDROID_JAVA8_HOME)/jre/lib/rt.jar\n    endif\n    full_shared_java_libs := $(addprefix $(HOST_OUT_JAVA_LIBRARIES)/,\\\n      $(addsuffix $(COMMON_JAVA_PACKAGE_SUFFIX),$(LOCAL_JAVA_LIBRARIES)))\n    full_shared_java_header_libs := $(full_shared_java_libs)\n  endif # USE_CORE_LIB_BOOTCLASSPATH\nendif # !LOCAL_IS_HOST_MODULE\n\n# (b/204397180) Record ALL_DEPS by default.\nALL_MODULES.$(my_register_name).ALL_DEPS := $(ALL_MODULES.$(my_register_name).ALL_DEPS) $(full_java_bootclasspath_libs)\n\n# Export the SDK libs. The sdk library names listed in LOCAL_SDK_LIBRARIES are first exported.\n# Then sdk library names exported from dependencies are all re-exported.\n$(my_exported_sdk_libs_file): PRIVATE_EXPORTED_SDK_LIBS_FILES := $(exported_sdk_libs_files)\n$(my_exported_sdk_libs_file): PRIVATE_SDK_LIBS := $(sort $(LOCAL_SDK_LIBRARIES))\n$(my_exported_sdk_libs_file): $(exported_sdk_libs_files)\n\t@echo \"Export SDK libs $@\"\n\t$(hide) mkdir -p $(dir $@) && rm -f $@ $@.temp\n\t$(if $(PRIVATE_SDK_LIBS),\\\n\t\techo $(PRIVATE_SDK_LIBS) | tr ' ' '\\n' > $@.temp,\\\n\t\ttouch $@.temp)\n\t$(if $(PRIVATE_EXPORTED_SDK_LIBS_FILES),\\\n\t\tcat $(PRIVATE_EXPORTED_SDK_LIBS_FILES) >> $@.temp)\n\t$(hide) cat $@.temp | sort -u > $@\n\t$(hide) rm -f $@.temp\n\nifdef empty_bootclasspath\n  ifdef full_java_bootclasspath_libs\n    $(call pretty-error,internal error: empty_bootclasspath and full_java_bootclasspath_libs should not both be set)\n  endif\nendif\n\nfull_java_system_modules_deps :=\nmy_system_modules_dir :=\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_USE_SYSTEM_MODULES :=\nifeq (,$(filter $(LOCAL_JAVA_LANGUAGE_VERSION),$(JAVA_LANGUAGE_VERSIONS_WITHOUT_SYSTEM_MODULES)))\n  $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_USE_SYSTEM_MODULES := true\n  ifdef my_system_modules\n    ifneq ($(my_system_modules),none)\n      ifndef SOONG_SYSTEM_MODULES_$(my_system_modules)\n        $(call pretty-error, Invalid system modules $(my_system_modules))\n      endif\n      full_java_system_modules_deps := $(SOONG_SYSTEM_MODULES_DEPS_$(my_system_modules))\n      my_system_modules_dir := $(SOONG_SYSTEM_MODULES_$(my_system_modules))\n    endif\n  endif\nendif\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_BOOTCLASSPATH := $(full_java_bootclasspath_libs)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EMPTY_BOOTCLASSPATH := $(empty_bootclasspath)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SYSTEM_MODULES := $(my_system_modules)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SYSTEM_MODULES_DIR := $(my_system_modules_dir)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SYSTEM_MODULES_LIBS := $(call java-lib-files,$(SOONG_SYSTEM_MODULES_LIBS_$(my_system_modules)))\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PATCH_MODULE := $(LOCAL_PATCH_MODULE)\n\nifndef LOCAL_IS_HOST_MODULE\n# This is set by packages that are linking to other packages that export\n# shared libraries, allowing them to make use of the code in the linked apk.\napk_libraries := $(sort $(LOCAL_APK_LIBRARIES) $(LOCAL_RES_LIBRARIES))\nifneq ($(apk_libraries),)\n  link_apk_libraries := $(call app-lib-files,$(apk_libraries))\n  link_apk_header_libs := $(call app-lib-header-files,$(apk_libraries))\n\n  # link against the jar with full original names (before proguard processing).\n  full_shared_java_libs += $(link_apk_libraries)\n  full_shared_java_header_libs += $(link_apk_header_libs)\nendif\n\n# This is set by packages that contain instrumentation, allowing them to\n# link against the package they are instrumenting.  Currently only one such\n# package is allowed.\nLOCAL_INSTRUMENTATION_FOR := $(strip $(LOCAL_INSTRUMENTATION_FOR))\nifdef LOCAL_INSTRUMENTATION_FOR\n  ifneq ($(words $(LOCAL_INSTRUMENTATION_FOR)),1)\n    $(error \\\n        $(LOCAL_PATH): Multiple LOCAL_INSTRUMENTATION_FOR members defined)\n  endif\n\n  link_instr_intermediates_dir.COMMON := $(call intermediates-dir-for, \\\n      APPS,$(LOCAL_INSTRUMENTATION_FOR),,COMMON)\n  # link against the jar with full original names (before proguard processing).\n  link_instr_classes_jar := $(link_instr_intermediates_dir.COMMON)/classes-pre-proguard.jar\n  ifneq ($(TURBINE_ENABLED),false)\n    link_instr_classes_header_jar := $(link_instr_intermediates_dir.COMMON)/classes-header.jar\n  else\n    link_instr_classes_header_jar := $(link_instr_intermediates_dir.COMMON)/classes.jar\n  endif\n  full_shared_java_libs += $(link_instr_classes_jar)\n  full_shared_java_header_libs += $(link_instr_classes_header_jar)\nendif  # LOCAL_INSTRUMENTATION_FOR\nendif  # LOCAL_IS_HOST_MODULE\n\nendif  # need_compile_java\n\n# We may want to add jar manifest or jar resource files even if there is no java code at all.\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EXTRA_JAR_ARGS := $(extra_jar_args)\njar_manifest_file :=\nifneq ($(strip $(LOCAL_JAR_MANIFEST)),)\njar_manifest_file := $(LOCAL_PATH)/$(LOCAL_JAR_MANIFEST)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAR_MANIFEST := $(jar_manifest_file)\nelse\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAR_MANIFEST :=\nendif\n\n##########################################################\n\nfull_java_libs := $(full_shared_java_libs) $(full_static_java_libs) $(LOCAL_CLASSPATH)\nfull_java_header_libs := $(full_shared_java_header_libs) $(full_static_java_header_libs)\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_JAVA_LIBRARIES := $(full_java_libs)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_JAVA_HEADER_LIBRARIES := $(full_java_header_libs)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SHARED_JAVA_HEADER_LIBRARIES := $(full_shared_java_header_libs)\n\n###########################################################\n# Verify that all libraries are safe to use\n###########################################################\nifndef LOCAL_IS_HOST_MODULE\nifeq ($(LOCAL_SDK_VERSION),system_current)\nmy_link_type := java:system\nmy_warn_types :=\nmy_allowed_types := java:sdk java:system java:core\nelse ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION)))\nmy_link_type := java:system\nmy_warn_types :=\nmy_allowed_types := java:sdk java:system java:core\nelse ifeq ($(LOCAL_SDK_VERSION),core_current)\nmy_link_type := java:core\nmy_warn_types :=\nmy_allowed_types := java:core\nelse ifneq ($(LOCAL_SDK_VERSION),)\nmy_link_type := java:sdk\nmy_warn_types :=\nmy_allowed_types := java:sdk java:core\nelse\nmy_link_type := java:platform\nmy_warn_types :=\nmy_allowed_types := java:sdk java:system java:platform java:core\nendif\n\nmy_link_deps := $(addprefix JAVA_LIBRARIES:,$(LOCAL_STATIC_JAVA_LIBRARIES) $(LOCAL_JAVA_LIBRARIES))\nmy_link_deps += $(addprefix APPS:,$(apk_libraries))\n\nmy_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX)\nmy_common := COMMON\ninclude $(BUILD_SYSTEM)/link_type.mk\nendif  # !LOCAL_IS_HOST_MODULE\n\nifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n\nSOONG_CONV.$(LOCAL_MODULE).PROBLEMS := \\\n    $(SOONG_CONV.$(LOCAL_MODULE).PROBLEMS) $(my_soong_problems)\nSOONG_CONV.$(LOCAL_MODULE).DEPS := \\\n    $(SOONG_CONV.$(LOCAL_MODULE).DEPS) \\\n    $(LOCAL_STATIC_JAVA_LIBRARIES) \\\n    $(LOCAL_JAVA_LIBRARIES) \\\n    $(LOCAL_JNI_SHARED_LIBRARIES)\nSOONG_CONV.$(LOCAL_MODULE).TYPE := java\nSOONG_CONV.$(LOCAL_MODULE).MAKEFILES := \\\n    $(SOONG_CONV.$(LOCAL_MODULE).MAKEFILES) $(LOCAL_MODULE_MAKEFILE)\nSOONG_CONV.$(LOCAL_MODULE).INSTALLED := \\\n    $(SOONG_CONV.$(LOCAL_MODULE).INSTALLED) $(LOCAL_INSTALLED_MODULE)\nSOONG_CONV := $(SOONG_CONV) $(LOCAL_MODULE)\n\nendif\n"
  },
  {
    "path": "core/java_host_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2023 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs {MODULE}\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-junit\" />\n\n    {EXTRA_CONFIGS}\n\n    <test class=\"com.android.tradefed.testtype.HostTest\" >\n        {EXTRA_TEST_RUNNER_CONFIGS}<option name=\"jar\" value=\"{MODULE}.jar\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/java_host_unit_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2020 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs {MODULE}\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-unit-tests\" />\n    <option name=\"config-descriptor:metadata\" key=\"component\" value=\"{MODULE}\" />\n\n    {EXTRA_CONFIGS}\n\n    <test class=\"com.android.tradefed.testtype.IsolatedHostTest\" >\n        <option name=\"jar\" value=\"{MODULE}.jar\" />\n        <option name=\"java-flags\" value=\"--add-modules=jdk.compiler\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED\"/>\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/java_library.mk",
    "content": "###########################################################\n## Standard rules for building a java library.\n##\n###########################################################\n$(call record-module-type,JAVA_LIBRARY)\n\nifdef LOCAL_IS_HOST_MODULE\n$(error $(LOCAL_PATH): Host java libraries must use BUILD_HOST_JAVA_LIBRARY)\nendif\n\nLOCAL_MODULE_SUFFIX := $(COMMON_JAVA_PACKAGE_SUFFIX)\nLOCAL_MODULE_CLASS := JAVA_LIBRARIES\n\nifneq (,$(LOCAL_ASSET_DIR))\n$(error $(LOCAL_PATH): Target java libraries may not set LOCAL_ASSET_DIR)\nendif\n\nifneq (true,$(LOCAL_IS_STATIC_JAVA_LIBRARY))\nifneq (,$(LOCAL_RESOURCE_DIR))\n$(error $(LOCAL_PATH): Target java libraries may not set LOCAL_RESOURCE_DIR)\nendif\n# base_rules.mk looks at this\nall_res_assets :=\nendif\n\nLOCAL_BUILT_MODULE_STEM := javalib.jar\n\n# For java libraries, other modules should depend on\n# out/target/common/obj/JAVA_LIBRARIES/.../classes.jar.\n# There are some dependencies outside the build system that assume static\n# java libraries produce javalib.jar, so we will copy classes.jar there too.\nintermediates.COMMON := $(call local-intermediates-dir,COMMON)\ncommon_javalib.jar := $(intermediates.COMMON)/javalib.jar\ndex_preopt_profile_src_file := $(common_javalib.jar)\nLOCAL_INTERMEDIATE_TARGETS += $(common_javalib.jar)\n\nifeq ($(LOCAL_PROGUARD_ENABLED),disabled)\n  LOCAL_PROGUARD_ENABLED :=\nendif\n\nifeq (true,$(EMMA_INSTRUMENT))\nifeq (true,$(LOCAL_EMMA_INSTRUMENT))\nifeq (true,$(EMMA_INSTRUMENT_STATIC))\nLOCAL_STATIC_JAVA_LIBRARIES += jacocoagent\n# Exclude jacoco classes from proguard\nLOCAL_PROGUARD_FLAGS += -include $(BUILD_SYSTEM)/proguard.jacoco.flags\nLOCAL_PROGUARD_FLAGS_DEPS += $(BUILD_SYSTEM)/proguard.jacoco.flags\nendif # LOCAL_EMMA_INSTRUMENT\nendif # EMMA_INSTRUMENT_STATIC\nelse\nLOCAL_EMMA_INSTRUMENT := false\nendif # EMMA_INSTRUMENT\n\nmy_dex_jar := $(common_javalib.jar)\n\n#################################\ninclude $(BUILD_SYSTEM)/java.mk\n#################################\n\nifeq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true)\n# There are some dependencies outside the build system that assume classes.jar\n# is available as javalib.jar so copy it there too.\n$(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),$(common_javalib.jar)))\n\n$(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),$(LOCAL_BUILT_MODULE)))\n\nelse # !LOCAL_IS_STATIC_JAVA_LIBRARY\n\n$(common_javalib.jar): PRIVATE_DEX_FILE := $(built_dex)\n$(common_javalib.jar): PRIVATE_SOURCE_ARCHIVE := $(full_classes_pre_proguard_jar)\n$(common_javalib.jar): $(MERGE_ZIPS) $(SOONG_ZIP) $(ZIP2ZIP)\n$(common_javalib.jar) : $(full_classes_pre_proguard_jar) $(built_dex) $(java_resource_sources) | $(ZIPTIME) $(ZIPALIGN)\n\t@echo \"target Jar: $(PRIVATE_MODULE) ($@)\"\n\trm -rf $@.parts && mkdir -p $@.parts\n\t$(call create-dex-jar,$@.parts/dex.zip,$(PRIVATE_DEX_FILE))\n\t$(call extract-resources-jar,$@.parts/res.zip,$(PRIVATE_SOURCE_ARCHIVE))\n\t$(MERGE_ZIPS) -j $@.tmp $@.parts/dex.zip $@.parts/res.zip\n\trm -rf $@.parts\n\t$(hide) $(ZIPTIME) $@.tmp\n\t$(call commit-change-for-toc,$@)\nifeq (true, $(LOCAL_UNCOMPRESS_DEX))\n\t$(uncompress-dexs)\n\t$(align-package)\nendif  # LOCAL_UNCOMPRESS_DEX\n\n.KATI_RESTAT: $(common_javalib.jar)\n\n$(eval $(call copy-one-file,$(common_javalib.jar),$(LOCAL_BUILT_MODULE)))\n\nendif # !LOCAL_IS_STATIC_JAVA_LIBRARY\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=JAVA_LIBRARY))"
  },
  {
    "path": "core/java_prebuilt_internal.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n############################################################\n# Internal build rules for JAVA_LIBRARIES prebuilt modules\n############################################################\n\nifneq (JAVA_LIBRARIES,$(LOCAL_MODULE_CLASS))\n$(call pretty-error,java_prebuilt_internal.mk is for JAVA_LIBRARIES modules only)\nendif\n\ninclude $(BUILD_SYSTEM)/base_rules.mk\nbuilt_module := $(LOCAL_BUILT_MODULE)\n\nifeq (,$(LOCAL_IS_HOST_MODULE)$(filter true,$(LOCAL_UNINSTALLABLE_MODULE)))\n  prebuilt_module_is_dex_javalib := true\nelse\n  prebuilt_module_is_dex_javalib :=\nendif\n\nifeq ($(prebuilt_module_is_dex_javalib),true)\nmy_dex_jar := $(my_prebuilt_src_file)\n# This is a target shared library, i.e. a jar with classes.dex.\n\n$(foreach pair,$(PRODUCT_BOOT_JARS), \\\n  $(if $(filter $(LOCAL_MODULE),$(call word-colon,2,$(pair))), \\\n    $(call pretty-error,Modules in PRODUCT_BOOT_JARS must be defined in Android.bp files)))\n\nALL_MODULES.$(my_register_name).CLASSES_JAR := $(common_classes_jar)\n\n#######################################\n# defines built_odex along with rule to install odex\nmy_manifest_or_apk := $(my_prebuilt_src_file)\ninclude $(BUILD_SYSTEM)/dex_preopt_odex_install.mk\nmy_manifest_or_apk :=\n#######################################\n$(built_module) : $(my_prebuilt_src_file)\n\t$(call copy-file-to-target)\n\nelse  # ! prebuilt_module_is_dex_javalib\n$(built_module) : $(my_prebuilt_src_file)\n\t$(transform-prebuilt-to-target)\nendif # ! prebuilt_module_is_dex_javalib\n\nmy_src_jar := $(my_prebuilt_src_file)\n\nifdef LOCAL_IS_HOST_MODULE\n# for host java libraries deps should be in the common dir, so we make a copy in\n# the common dir.\ncommon_classes_jar := $(intermediates.COMMON)/classes.jar\ncommon_header_jar := $(intermediates.COMMON)/classes-header.jar\n\n$(common_classes_jar): PRIVATE_MODULE := $(LOCAL_MODULE)\n$(common_classes_jar): PRIVATE_PREFIX := $(my_prefix)\n\n$(common_classes_jar) : $(my_src_jar)\n\t$(transform-prebuilt-to-target)\n\nifneq ($(TURBINE_ENABLED),false)\n$(common_header_jar) : $(my_src_jar)\n\t$(transform-prebuilt-to-target)\nendif\n\nelse # !LOCAL_IS_HOST_MODULE\n# for target java libraries, the LOCAL_BUILT_MODULE is in a product-specific dir,\n# while the deps should be in the common dir, so we make a copy in the common dir.\ncommon_classes_jar := $(intermediates.COMMON)/classes.jar\ncommon_header_jar := $(intermediates.COMMON)/classes-header.jar\ncommon_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar\ncommon_javalib_jar := $(intermediates.COMMON)/javalib.jar\n\n$(common_classes_jar) $(common_classes_pre_proguard_jar) $(common_javalib_jar): PRIVATE_MODULE := $(LOCAL_MODULE)\n$(common_classes_jar) $(common_classes_pre_proguard_jar) $(common_javalib_jar): PRIVATE_PREFIX := $(my_prefix)\n\nifeq ($(LOCAL_SDK_VERSION),system_current)\nmy_link_type := java:system\nelse ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION)))\nmy_link_type := java:system\nelse ifeq ($(LOCAL_SDK_VERSION),core_current)\nmy_link_type := java:core\nelse ifneq ($(LOCAL_SDK_VERSION),)\nmy_link_type := java:sdk\nelse\nmy_link_type := java:platform\nendif\n\n# TODO: check dependencies of prebuilt files\nmy_link_deps :=\n\nmy_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX)\nmy_common := COMMON\ninclude $(BUILD_SYSTEM)/link_type.mk\n\nifeq ($(prebuilt_module_is_dex_javalib),true)\n# For prebuilt shared Java library we don't have classes.jar.\n$(common_javalib_jar) : $(my_src_jar)\n\t$(transform-prebuilt-to-target)\n\nelse  # ! prebuilt_module_is_dex_javalib\n\nmy_src_aar := $(filter %.aar, $(my_prebuilt_src_file))\nifneq ($(my_src_aar),)\n# This is .aar file, archive of classes.jar and Android resources.\n\nmy_src_jar := $(intermediates.COMMON)/aar/classes.jar\nmy_src_proguard_options := $(intermediates.COMMON)/aar/proguard.txt\nmy_src_android_manifest := $(intermediates.COMMON)/aar/AndroidManifest.xml\n\n$(my_src_jar) : .KATI_IMPLICIT_OUTPUTS := $(my_src_proguard_options)\n$(my_src_jar) : .KATI_IMPLICIT_OUTPUTS += $(my_src_android_manifest)\n$(my_src_jar) : $(my_src_aar)\n\t$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@) $(dir $@)/res\n\t$(hide) unzip -qoDD -d $(dir $@) $<\n\t# Make sure the proguard and AndroidManifest.xml files exist\n\t$(hide) touch $(dir $@)/proguard.txt\n\t$(hide) touch $(dir $@)/AndroidManifest.xml\n\nmy_prebuilt_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml\n$(eval $(call copy-one-file,$(my_src_android_manifest),$(my_prebuilt_android_manifest)))\n$(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_prebuilt_android_manifest))\n\nendif\n\n$(common_classes_jar) : $(my_src_jar)\n\t$(transform-prebuilt-to-target)\n\nifneq ($(TURBINE_ENABLED),false)\n$(common_header_jar) : $(my_src_jar)\n\t$(transform-prebuilt-to-target)\nendif\n\n$(common_classes_pre_proguard_jar) : $(my_src_jar)\n\t$(transform-prebuilt-to-target)\n\n$(common_javalib_jar) : $(common_classes_jar)\n\t$(transform-prebuilt-to-target)\n\ninclude $(BUILD_SYSTEM)/force_aapt2.mk\n\nifneq ($(my_src_aar),)\n\n$(intermediates.COMMON)/export_proguard_flags : $(my_src_proguard_options)\n\t$(transform-prebuilt-to-target)\n\nLOCAL_SDK_RES_VERSION:=$(strip $(LOCAL_SDK_RES_VERSION))\nifeq ($(LOCAL_SDK_RES_VERSION),)\n  LOCAL_SDK_RES_VERSION:=$(LOCAL_SDK_VERSION)\nendif\n\nframework_res_package_export :=\n# Please refer to package.mk\nifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true)\nifneq ($(filter-out current system_current test_current,$(LOCAL_SDK_RES_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_RES_VERSION))),)\nframework_res_package_export := \\\n    $(call resolve-prebuilt-sdk-jar-path,$(LOCAL_SDK_RES_VERSION))\nelse\nframework_res_package_export := \\\n    $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk\nendif\nendif\n\n# transitive-res-packages is only populated for Soong modules for now, but needs\n# to exist so that other Make modules can depend on it.  Create an empty file.\nmy_transitive_res_packages := $(intermediates.COMMON)/transitive-res-packages\n$(my_transitive_res_packages):\n\ttouch $@\n\nmy_res_package := $(intermediates.COMMON)/package-res.apk\n\n# We needed only very few PRIVATE variables and aapt2.mk input variables. Reset the unnecessary ones.\n$(my_res_package): PRIVATE_AAPT2_CFLAGS :=\n$(my_res_package): PRIVATE_AAPT_FLAGS := --static-lib --no-static-lib-packages --auto-add-overlay\n$(my_res_package): PRIVATE_ANDROID_MANIFEST := $(my_src_android_manifest)\n$(my_res_package): PRIVATE_AAPT_INCLUDES := $(framework_res_package_export)\n$(my_res_package): PRIVATE_SOURCE_INTERMEDIATES_DIR :=\n$(my_res_package): PRIVATE_PROGUARD_OPTIONS_FILE :=\n$(my_res_package): PRIVATE_DEFAULT_APP_TARGET_SDK :=\n$(my_res_package): PRIVATE_DEFAULT_APP_TARGET_SDK :=\n$(my_res_package): PRIVATE_PRODUCT_AAPT_CONFIG :=\n$(my_res_package): PRIVATE_PRODUCT_AAPT_PREF_CONFIG :=\n$(my_res_package): PRIVATE_TARGET_AAPT_CHARACTERISTICS :=\n$(my_res_package) : $(framework_res_package_export)\n$(my_res_package) : $(my_src_android_manifest)\n\nfull_android_manifest :=\nmy_res_resources :=\nmy_overlay_resources :=\nmy_compiled_res_base_dir := $(intermediates.COMMON)/flat-res\nR_file_stamp :=\nproguard_options_file :=\nmy_generated_res_dirs := $(intermediates.COMMON)/aar/res\nmy_generated_res_dirs_deps := $(my_src_jar)\ninclude $(BUILD_SYSTEM)/aapt2.mk\n\n# Make sure my_res_package is created when you run mm/mmm.\n$(built_module) : $(my_res_package)\nendif  # $(my_src_aar)\n\n# make sure the classes.jar and javalib.jar are built before $(LOCAL_BUILT_MODULE)\n$(built_module) : $(common_javalib_jar)\n\nmy_exported_sdk_libs_file := $(intermediates.COMMON)/exported-sdk-libs\n$(my_exported_sdk_libs_file): PRIVATE_EXPORTED_SDK_LIBS := $(LOCAL_EXPORT_SDK_LIBRARIES)\n$(my_exported_sdk_libs_file):\n\t@echo \"Export SDK libs $@\"\n\t$(hide) mkdir -p $(dir $@) && rm -f $@\n\t$(if $(PRIVATE_EXPORTED_SDK_LIBS),\\\n\t\t$(hide) echo $(PRIVATE_EXPORTED_SDK_LIBS) | tr ' ' '\\n' > $@,\\\n\t\t$(hide) touch $@)\n\nendif # ! prebuilt_module_is_dex_javalib\nendif # LOCAL_IS_HOST_MODULE is not set\n"
  },
  {
    "path": "core/java_renderscript.mk",
    "content": "###############################################################\n## Renderscript support for java\n## Adds rules to convert .rscript files to .java and .bc files\n###############################################################\n\nrenderscript_sources := $(filter %.rscript,$(LOCAL_SRC_FILES))\nLOCAL_SRC_FILES := $(filter-out %.rscript,$(LOCAL_SRC_FILES))\n\nrs_generated_res_zip :=\nrs_generated_src_jar :=\nrs_compatibility_jni_libs :=\nifneq ($(renderscript_sources),)\nrenderscript_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(renderscript_sources))\nrenderscript_intermediate.COMMON := $(intermediates.COMMON)/renderscript\nrs_generated_res_zip := $(renderscript_intermediate.COMMON)/res.zip\nrs_generated_src_jar := $(renderscript_intermediate.COMMON)/rs.srcjar\n\nLOCAL_SRCJARS += $(rs_generated_src_jar)\n\n# Defaulting to an empty string uses the latest available platform SDK.\nrenderscript_target_api :=\n\nifneq (,$(LOCAL_RENDERSCRIPT_TARGET_API))\n  renderscript_target_api := $(LOCAL_RENDERSCRIPT_TARGET_API)\nelse\n  ifneq (,$(LOCAL_SDK_VERSION))\n    # Set target-api for LOCAL_SDK_VERSIONs other than current.\n    ifneq (,$(filter-out current system_current test_current core_current, $(LOCAL_SDK_VERSION)))\n      renderscript_target_api := $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION))\n    endif\n  endif  # LOCAL_SDK_VERSION is set\nendif  # LOCAL_RENDERSCRIPT_TARGET_API is set\n\n# For 64-bit, we always have to upgrade to at least 21 for compat build.\nifneq ($(LOCAL_RENDERSCRIPT_COMPATIBILITY),)\n  ifeq ($(TARGET_IS_64_BIT),true)\n    ifneq ($(filter $(RSCOMPAT_32BIT_ONLY_API_LEVELS),$(renderscript_target_api)),)\n      renderscript_target_api := 21\n    endif\n  endif\nendif\n\nifeq ($(LOCAL_RENDERSCRIPT_CC),)\nLOCAL_RENDERSCRIPT_CC := $(LLVM_RS_CC)\nendif\n\n# Turn on all warnings and warnings as errors for RS compiles.\n# This can be disabled with LOCAL_RENDERSCRIPT_FLAGS := -Wno-error\nrenderscript_flags := -Wall -Werror\nrenderscript_flags += $(LOCAL_RENDERSCRIPT_FLAGS)\n\n# prepend the RenderScript system include path\nifneq ($(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_VERSION))),)\n# if a numeric LOCAL_SDK_VERSION, or current LOCAL_SDK_VERSION with TARGET_BUILD_USE_PREBUILT_SDKS\nLOCAL_RENDERSCRIPT_INCLUDES := \\\n    $(HISTORICAL_SDK_VERSIONS_ROOT)/renderscript/clang-include \\\n    $(HISTORICAL_SDK_VERSIONS_ROOT)/renderscript/include \\\n    $(LOCAL_RENDERSCRIPT_INCLUDES)\nelse\nLOCAL_RENDERSCRIPT_INCLUDES := \\\n    $(TOPDIR)external/clang/lib/Headers \\\n    $(TOPDIR)frameworks/rs/script_api/include \\\n    $(LOCAL_RENDERSCRIPT_INCLUDES)\nendif\n\nifneq ($(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE),)\nLOCAL_RENDERSCRIPT_INCLUDES := $(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE)\nendif\n\nbc_files := $(patsubst %.rscript,%.bc, $(notdir $(renderscript_sources)))\nbc_dep_files := $(addprefix $(renderscript_intermediate.COMMON)/,$(patsubst %.bc,%.d,$(bc_files)))\n\n$(rs_generated_src_jar): PRIVATE_RS_INCLUDES := $(LOCAL_RENDERSCRIPT_INCLUDES)\n$(rs_generated_src_jar): PRIVATE_RS_CC := $(LOCAL_RENDERSCRIPT_CC)\n$(rs_generated_src_jar): PRIVATE_RS_FLAGS := $(renderscript_flags)\n$(rs_generated_src_jar): PRIVATE_RS_SOURCE_FILES := $(renderscript_sources_fullpath)\n$(rs_generated_src_jar): PRIVATE_RS_OUTPUT_DIR := $(renderscript_intermediate.COMMON)\n$(rs_generated_src_jar): PRIVATE_RS_TARGET_API := $(patsubst current,0,$(renderscript_target_api))\n$(rs_generated_src_jar): PRIVATE_DEP_FILES := $(bc_dep_files)\n$(rs_generated_src_jar): PRIVATE_RS_OUTPUT_RES_ZIP := $(rs_generated_res_zip)\n$(rs_generated_src_jar): .KATI_IMPLICIT_OUTPUTS := $(rs_generated_res_zip)\n$(rs_generated_src_jar): $(renderscript_sources_fullpath) $(LOCAL_RENDERSCRIPT_CC) $(SOONG_ZIP)\n\t$(transform-renderscripts-to-java-and-bc)\n\n# include the dependency files (.d) generated by llvm-rs-cc.\n$(call include-depfile,$(rs_generated_src_jar).d,$(rs_generated_src_jar))\n\nifneq ($(LOCAL_RENDERSCRIPT_COMPATIBILITY),)\n\n\nifeq ($(filter $(RSCOMPAT_32BIT_ONLY_API_LEVELS),$(renderscript_target_api)),)\nifeq ($(TARGET_IS_64_BIT),true)\nrenderscript_intermediate.bc_folder := $(renderscript_intermediate.COMMON)/res/raw/bc64/\nelse\nrenderscript_intermediate.bc_folder := $(renderscript_intermediate.COMMON)/res/raw/bc32/\nendif\nelse\nrenderscript_intermediate.bc_folder := $(renderscript_intermediate.COMMON)/res/raw/\nendif\n\nrs_generated_bc := $(addprefix \\\n    $(renderscript_intermediate.bc_folder), $(bc_files))\n\nrenderscript_intermediate := $(intermediates)/renderscript\n\n# We don't need the .so files in bundled branches\n# Prevent these from showing up on the device\n# One exception is librsjni.so, which is needed for\n# both native path and compat path.\nrs_jni_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,librsjni)/librsjni.so\nLOCAL_JNI_SHARED_LIBRARIES += librsjni\n\nifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)$(FORCE_BUILD_RS_COMPAT))\n\nrs_compatibility_jni_libs := $(addprefix \\\n    $(renderscript_intermediate)/librs., \\\n    $(patsubst %.bc,%.so, $(bc_files)))\n\n$(rs_generated_src_jar): .KATI_IMPLICIT_OUTPUTS += $(rs_generated_bc)\n\nrs_support_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupport)/libRSSupport.so\nLOCAL_JNI_SHARED_LIBRARIES += libRSSupport\n\nrs_support_io_lib :=\n# check if the target api level support USAGE_IO\nifeq ($(filter $(RSCOMPAT_NO_USAGEIO_API_LEVELS),$(renderscript_target_api)),)\nrs_support_io_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupportIO)/libRSSupportIO.so\nLOCAL_JNI_SHARED_LIBRARIES += libRSSupportIO\nendif\n\nmy_arch := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)\nifneq (,$(filter arm64 x86_64,$(my_arch)))\n  my_min_sdk_version := 21\nelse\n  my_min_sdk_version := $(MIN_SUPPORTED_SDK_VERSION)\nendif\n\n$(rs_compatibility_jni_libs): $(RS_PREBUILT_CLCORE) \\\n    $(rs_support_lib) $(rs_support_io_lib) $(rs_jni_lib) $(rs_compiler_rt)\n$(rs_compatibility_jni_libs): $(BCC_COMPAT)\n$(rs_compatibility_jni_libs): PRIVATE_CXX := $(CXX_WRAPPER) $(CLANG_CXX)\n$(rs_compatibility_jni_libs): PRIVATE_CXX_LINK := $(CLANG_CXX)\n$(rs_compatibility_jni_libs): PRIVATE_SDK_VERSION := $(my_min_sdk_version)\n$(rs_compatibility_jni_libs): $(renderscript_intermediate)/librs.%.so: \\\n    $(renderscript_intermediate.bc_folder)%.bc \\\n    $(SOONG_OUT_DIR)/ndk.timestamp\n\t$(transform-bc-to-so)\n\nendif\n\nendif\n\nLOCAL_INTERMEDIATE_TARGETS += $(rs_generated_src_jar)\n# Make sure the generated resource will be added to the apk.\nLOCAL_RESOURCE_DIR := $(renderscript_intermediate.COMMON)/res $(LOCAL_RESOURCE_DIR)\nendif\n"
  },
  {
    "path": "core/java_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2018 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs {MODULE}.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-junit\" />\n    <target_preparer class=\"com.android.compatibility.common.tradefed.targetprep.FilePusher\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"cts-dalvik-device-test-runner.jar->/data/local/tmp/{MODULE}/cts-dalvik-device-test-runner.jar\" />\n        <option name=\"push\" value=\"{MODULE}.jar->/data/local/tmp/{MODULE}/{MODULE}.jar\" />\n    </target_preparer>\n\n    {EXTRA_CONFIGS}\n\n    <test class=\"com.android.compatibility.testtype.DalvikTest\" >\n        <option name=\"run-name\" value=\"{MODULE}\" />\n        <option name=\"classpath\" value=\"/data/local/tmp/{MODULE}/{MODULE}.jar\" />\n        <option name=\"classpath\" value=\"/data/local/tmp/{MODULE}/cts-dalvik-device-test-runner.jar\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/layoutlib_data.mk",
    "content": "# Data files for layoutlib\n\nFONT_TEMP := $(call intermediates-dir-for,PACKAGING,fonts,HOST,COMMON)\n\n# The font configuration files - system_fonts.xml, fallback_fonts.xml etc.\nfont_config := $(filter $(TARGET_OUT)/etc/font%.xml, $(INTERNAL_SYSTEMIMAGE_FILES))\nfont_config := $(addprefix $(FONT_TEMP)/, $(notdir $(font_config)))\n\n$(font_config): $(FONT_TEMP)/%: $(TARGET_OUT)/etc/%\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) cp -vf $< $@\n\n# List of fonts on the device that we want to ship. This is all .ttf, .ttc and .otf fonts.\nfonts_device := $(filter $(TARGET_OUT)/fonts/%.ttf $(TARGET_OUT)/fonts/%.ttc $(TARGET_OUT)/fonts/%.otf, $(INTERNAL_SYSTEMIMAGE_FILES))\nfonts_device := $(addprefix $(FONT_TEMP)/, $(notdir $(fonts_device)))\n\n# TODO: If the font file is a symlink, reuse the font renamed from the symlink\n# target.\n$(fonts_device): $(FONT_TEMP)/%: $(TARGET_OUT)/fonts/%\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) cp -vf $< $@\n\nKEYBOARD_TEMP := $(call intermediates-dir-for,PACKAGING,keyboards,HOST,COMMON)\n\n# The key character map files needed for supporting KeyEvent\nkeyboards := $(sort $(wildcard frameworks/base/data/keyboards/*.kcm))\nkeyboards := $(addprefix $(KEYBOARD_TEMP)/, $(notdir $(keyboards)))\n\n$(keyboards): $(KEYBOARD_TEMP)/%.kcm: frameworks/base/data/keyboards/%.kcm\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) cp -vf $< $@\n\nHYPHEN_TEMP := $(call intermediates-dir-for,PACKAGING,hyphen,HOST,COMMON)\n\n# The hyphenation pattern files needed to support text hyphenation\nhyphen := $(filter $(TARGET_OUT)/usr/hyphen-data/%.hyb, $(INTERNAL_SYSTEMIMAGE_FILES))\nhyphen := $(addprefix $(HYPHEN_TEMP)/, $(notdir $(hyphen)))\n\n$(hyphen): $(HYPHEN_TEMP)/%: $(TARGET_OUT)/usr/hyphen-data/%\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) cp -vf $< $@\n\n# List of all data files - font files, font configuration files, key character map files, hyphenation pattern files\nLAYOUTLIB_FILES := $(fonts_device) $(font_config) $(keyboards) $(hyphen)\n\n.PHONY: layoutlib layoutlib-tests\nlayoutlib layoutlib-tests: $(LAYOUTLIB_FILES)\n\n$(call dist-for-goals, layoutlib, $(foreach m,$(fonts_device), $(m):layoutlib_native/fonts/$(notdir $(m))))\n$(call dist-for-goals, layoutlib, $(foreach m,$(font_config), $(m):layoutlib_native/fonts/$(notdir $(m))))\n$(call dist-for-goals, layoutlib, $(foreach m,$(keyboards), $(m):layoutlib_native/keyboards/$(notdir $(m))))\n$(call dist-for-goals, layoutlib, $(foreach m,$(hyphen), $(m):layoutlib_native/hyphen-data/$(notdir $(m))))\n\nFONT_TEMP :=\nfont_config :=\nfonts_device :=\nFONT_FILES :=\n\n# The following build process of build.prop, layoutlib-res.zip is moved here from release_layoutlib.sh\n# so the SBOM of all platform neutral artifacts and Linux/Windows artifacts of layoutlib can be built in Make/Soong.\n# See go/layoutlib-sbom.\n\n# build.prop shipped with layoutlib\nLAYOUTLIB_BUILD_PROP := $(call intermediates-dir-for,PACKAGING,layoutlib-build-prop,HOST,COMMON)\n$(LAYOUTLIB_BUILD_PROP)/layoutlib-build.prop: $(INSTALLED_SDK_BUILD_PROP_TARGET)\n\trm -rf $@\n\tcp $< $@\n\t# Remove all the uncommon build properties\n\tsed -i '/^ro\\.\\(build\\|product\\|config\\|system\\)/!d' $@\n\t# Mark the build as layoutlib. This can be read at runtime by apps\n\tsed -i 's|ro.product.brand=generic|ro.product.brand=studio|' $@\n\tsed -i 's|ro.product.device=generic|ro.product.device=layoutlib|' $@\n\n$(call dist-for-goals,layoutlib,$(LAYOUTLIB_BUILD_PROP)/layoutlib-build.prop:layoutlib_native/build.prop)\n\n# Resource files from frameworks/base/core/res/res\nLAYOUTLIB_RES := $(call intermediates-dir-for,PACKAGING,layoutlib-res,HOST,COMMON)\nLAYOUTLIB_RES_FILES := $(shell find frameworks/base/core/res/res -type f -not -path 'frameworks/base/core/res/res/values-m[nc]c*' | sort)\nEMULATED_OVERLAYS_FILES := $(shell find frameworks/base/packages/overlays/*/res/ | sort)\nLAYOUTLIB_SUPPORTED_DEVICES := raviole/oriole raviole/raven bluejay/bluejay pantah/panther pantah/cheetah lynx/lynx felix/felix shusky/shiba shusky/husky akita/akita caimito/tokay caimito/caiman caimito/komodo comet/comet tangorpro/tangorpro\nLAYOUTLIB_DEVICE_OVERLAYS_FILES := $(addsuffix /overlay/frameworks/base/core/res/res/values/*, $(addprefix device/google/, $(LAYOUTLIB_SUPPORTED_DEVICES)))\nLAYOUTLIB_DEVICE_OVERLAYS_FILES := $(shell find $(LAYOUTLIB_DEVICE_OVERLAYS_FILES) | sort)\n$(LAYOUTLIB_RES)/layoutlib-res.zip: $(SOONG_ZIP) $(HOST_OUT_EXECUTABLES)/aapt2 $(LAYOUTLIB_RES_FILES) $(EMULATED_OVERLAYS_FILES) $(LAYOUTLIB_DEVICE_OVERLAYS_FILES) frameworks/layoutlib/overlay_codenames.txt\n\trm -rf $@\n\techo $(LAYOUTLIB_RES_FILES) > $(LAYOUTLIB_RES)/filelist_res.txt\n\t$(SOONG_ZIP) -C frameworks/base/core/res -l $(LAYOUTLIB_RES)/filelist_res.txt -o $(LAYOUTLIB_RES)/temp_res.zip\n\techo $(EMULATED_OVERLAYS_FILES) > $(LAYOUTLIB_RES)/filelist_emulated_overlays.txt\n\t$(SOONG_ZIP) -C frameworks/base/packages -l $(LAYOUTLIB_RES)/filelist_emulated_overlays.txt -o $(LAYOUTLIB_RES)/temp_emulated_overlays.zip\n\tfor line in $$(cut -f 1 frameworks/layoutlib/overlay_codenames.txt); \\\n\t  do splitLine=($${line//:/ }) \\\n\t  origin_dir=device/google/*/$${splitLine[0]}/overlay/frameworks/base/core/res/res/values; \\\n\t  target_dir=$(LAYOUTLIB_RES)/overlays/$${splitLine[1]}/res/; \\\n\t  mkdir -p $$target_dir; \\\n\t  cp -r $$origin_dir $$target_dir; \\\n\tdone\n\t$(SOONG_ZIP) -C $(LAYOUTLIB_RES) -D $(LAYOUTLIB_RES)/overlays/ -o $(LAYOUTLIB_RES)/temp_device_overlays.zip\n\trm -rf $(LAYOUTLIB_RES)/data && unzip -q -d $(LAYOUTLIB_RES)/data $(LAYOUTLIB_RES)/temp_res.zip\n\tunzip -q -d $(LAYOUTLIB_RES)/data $(LAYOUTLIB_RES)/temp_emulated_overlays.zip\n\tunzip -q -d $(LAYOUTLIB_RES)/data $(LAYOUTLIB_RES)/temp_device_overlays.zip\n\trm -rf $(LAYOUTLIB_RES)/compiled && mkdir $(LAYOUTLIB_RES)/compiled && $(HOST_OUT_EXECUTABLES)/aapt2 compile $(LAYOUTLIB_RES)/data/res/**/*.9.png -o $(LAYOUTLIB_RES)/compiled\n\tprintf '<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"com.google.android.layoutlib\" />' > $(LAYOUTLIB_RES)/AndroidManifest.xml\n\t$(HOST_OUT_EXECUTABLES)/aapt2 link -R $(LAYOUTLIB_RES)/compiled/* -o $(LAYOUTLIB_RES)/compiled.apk --manifest $(LAYOUTLIB_RES)/AndroidManifest.xml\n\trm -rf $(LAYOUTLIB_RES)/compiled_apk && unzip -q -d $(LAYOUTLIB_RES)/compiled_apk $(LAYOUTLIB_RES)/compiled.apk\n\tfor f in $(LAYOUTLIB_RES)/compiled_apk/res/*; do mv \"$$f\" \"$${f/-v4/}\";done\n\tfor f in $(LAYOUTLIB_RES)/compiled_apk/res/**/*.9.png; do mv \"$$f\" \"$${f/.9.png/.compiled.9.png}\";done\n\tcp -r $(LAYOUTLIB_RES)/compiled_apk/res $(LAYOUTLIB_RES)/data\n\t$(SOONG_ZIP) -C $(LAYOUTLIB_RES)/data -D $(LAYOUTLIB_RES)/data/ -o $@\n\n$(call dist-for-goals,layoutlib,$(LAYOUTLIB_RES)/layoutlib-res.zip:layoutlib_native/res.zip)\n\n# SBOM of layoutlib artifacts\nLAYOUTLIB_SBOM := $(call intermediates-dir-for,PACKAGING,layoutlib-sbom,HOST)\n_layoutlib_font_config_files := $(sort $(wildcard frameworks/base/data/fonts/*.xml))\n_layoutlib_fonts_files := $(filter $(TARGET_OUT)/fonts/%.ttf $(TARGET_OUT)/fonts/%.ttc $(TARGET_OUT)/fonts/%.otf, $(INTERNAL_SYSTEMIMAGE_FILES))\n_layoutlib_keyboard_files := $(sort $(wildcard frameworks/base/data/keyboards/*.kcm))\n_layoutlib_hyphen_files := $(filter $(TARGET_OUT)/usr/hyphen-data/%.hyb, $(INTERNAL_SYSTEMIMAGE_FILES))\n\n# Find out files disted with layoutlib in Soong.\n### Filter out static libraries for Windows and files already handled in make.\n_layoutlib_filter_out_disted := $(addprefix layoutlib_native/,fonts/% keyboards/% build.prop res.zip windows/%.a)\n_layoutlib_files_disted_by_soong := \\\n  $(strip \\\n    $(foreach p,$(_all_dist_src_dst_pairs), \\\n      $(if $(filter-out $(_layoutlib_filter_out_disted),$(filter layoutlib_native/% layoutlib.jar,$(call word-colon,2,$p))),$p)))\n\n$(LAYOUTLIB_SBOM)/sbom-metadata.csv:\n\trm -rf $@\n\techo installed_file,module_path,soong_module_type,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,build_output_path,static_libraries,whole_static_libraries,is_static_lib >> $@\n\techo build.prop,,,,,,Y,$(LAYOUTLIB_BUILD_PROP)/layoutlib-build.prop,,, >> $@\n\n\t$(foreach f,$(_layoutlib_font_config_files),\\\n\t  echo data/fonts/$(notdir $f),frameworks/base/data/fonts,prebuilt_etc,,,,,$f,,, >> $@; \\\n\t)\n\n\t$(foreach f,$(_layoutlib_fonts_files), \\\n\t  $(eval _module_name := $(ALL_INSTALLED_FILES.$f)) \\\n\t  $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \\\n\t  $(eval _soong_module_type := $(strip $(sort $(ALL_MODULES.$(_module_name).SOONG_MODULE_TYPE)))) \\\n\t  echo data/fonts/$(notdir $f),$(_module_path),$(_soong_module_type),,,,,$f,,, >> $@; \\\n\t)\n\n\t$(foreach f,$(_layoutlib_keyboard_files), \\\n\t  echo data/keyboards/$(notdir $f),frameworks/base/data/keyboards,prebuilt_etc,,,,,$f,,, >> $@; \\\n\t)\n\n\t$(foreach f,$(_layoutlib_hyphen_files), \\\n\t  $(eval _module_name := $(ALL_INSTALLED_FILES.$f)) \\\n\t  $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \\\n\t  $(eval _soong_module_type := $(strip $(sort $(ALL_MODULES.$(_module_name).SOONG_MODULE_TYPE)))) \\\n\t  echo data/hyphen-data/$(notdir $f),$(_module_path),$(_soong_module_type),,,,,$f,,, >> $@; \\\n\t)\n\n\t$(foreach f,$(_layoutlib_files_disted_by_soong), \\\n\t  $(eval _prebuilt_module_file := $(call word-colon,1,$f)) \\\n\t  $(eval _dist_file := $(call word-colon,2,$f)) \\\n\t  $(eval _dist_file := $(patsubst data/windows/%,data/win/lib64/%,$(patsubst layoutlib_native/%,data/%,$(_dist_file)))) \\\n\t  $(eval _dist_file := $(subst layoutlib.jar,data/layoutlib.jar,$(_dist_file))) \\\n\t  $(eval _module_name := $(strip $(foreach m,$(ALL_MODULES),$(if $(filter $(_prebuilt_module_file),$(ALL_MODULES.$m.CHECKED)),$m)))) \\\n\t  $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \\\n\t  $(eval _soong_module_type := $(strip $(sort $(ALL_MODULES.$(_module_name).SOONG_MODULE_TYPE)))) \\\n\t  echo $(patsubst layoutlib_native/%,%,$(_dist_file)),$(_module_path),$(_soong_module_type),,,,,$(_prebuilt_module_file),,, >> $@; \\\n\t)\n\n\t$(foreach f,$(LAYOUTLIB_RES_FILES), \\\n\t  $(eval _path := $(subst frameworks/base/core/res,data,$f)) \\\n\t  echo $(_path),,,,,,Y,$f,,, >> $@; \\\n\t)\n\n\t$(foreach f,$(EMULATED_OVERLAYS_FILES), \\\n\t  $(eval _path := $(subst frameworks/base/packages,data,$f)) \\\n\t  echo $(_path),,,,,,Y,$f,,, >> $@; \\\n\t)\n\n\tfor line in $$(cut -f 1 frameworks/layoutlib/overlay_codenames.txt); do \\\n\t  splitLine=($${line//:/ }); \\\n\t  for f in $(LAYOUTLIB_DEVICE_OVERLAYS_FILES); do \\\n\t    if [[ $$f == */$${splitLine[0]}/* ]]; then \\\n\t      echo data/overlays/$${splitLine[1]}/res/values/$$(basename $$f),,,,,,Y,$$f,,, >> $@; \\\n\t    fi \\\n\t  done \\\n\tdone\n\n.PHONY: layoutlib-sbom\nlayoutlib-sbom: $(LAYOUTLIB_SBOM)/layoutlib.spdx.json\n$(LAYOUTLIB_SBOM)/layoutlib.spdx.json: $(PRODUCT_OUT)/always_dirty_file.txt $(GEN_SBOM) $(LAYOUTLIB_SBOM)/sbom-metadata.csv $(_layoutlib_font_config_files) $(_layoutlib_fonts_files) $(LAYOUTLIB_BUILD_PROP)/layoutlib-build.prop $(_layoutlib_keyboard_files) $(_layoutlib_hyphen_files) $(LAYOUTLIB_RES_FILES) $(EMULATED_OVERLAYS_FILES) $(LAYOUTLIB_DEVICE_OVERLAYS_FILES) frameworks/layoutlib/overlay_codenames.txt\n\trm -rf $@\n\t$(GEN_SBOM) --output_file $@ --metadata $(LAYOUTLIB_SBOM)/sbom-metadata.csv --build_version $(BUILD_FINGERPRINT_FROM_FILE) --product_mfr \"$(PRODUCT_MANUFACTURER)\" --module_name \"layoutlib\" --json\n\n$(call dist-for-goals,layoutlib,$(LAYOUTLIB_SBOM)/layoutlib.spdx.json:layoutlib_native/sbom/layoutlib.spdx.json)\n\n# Generate SBOM of framework_res.jar that is created in release_layoutlib.sh.\n# The generated SBOM contains placeholders for release_layoutlib.sh to substitute, and the placeholders include:\n# document name, document namespace, document creation info, organization and SHA1 value of framework_res.jar.\nGEN_SBOM_FRAMEWORK_RES := $(HOST_OUT_EXECUTABLES)/generate-sbom-framework_res\n.PHONY: layoutlib-framework_res-sbom\nlayoutlib-framework_res-sbom: $(LAYOUTLIB_SBOM)/framework_res.jar.spdx.json\n$(LAYOUTLIB_SBOM)/framework_res.jar.spdx.json: $(LAYOUTLIB_SBOM)/layoutlib.spdx.json $(GEN_SBOM_FRAMEWORK_RES)\n\trm -rf $@\n\t$(GEN_SBOM_FRAMEWORK_RES) --output_file $(LAYOUTLIB_SBOM)/framework_res.jar.spdx.json --layoutlib_sbom $(LAYOUTLIB_SBOM)/layoutlib.spdx.json\n\n$(call dist-for-goals,layoutlib,$(LAYOUTLIB_SBOM)/framework_res.jar.spdx.json:layoutlib_native/sbom/framework_res.jar.spdx.json)"
  },
  {
    "path": "core/link_type.mk",
    "content": "# Inputs:\n#   LOCAL_MODULE_CLASS, LOCAL_MODULE, LOCAL_MODULE_MAKEFILE, LOCAL_BUILT_MODULE\n#   from base_rules.mk: my_kind, my_host_cross\n#   my_common: empty or COMMON, like the argument to intermediates-dir-for\n#   my_2nd_arch_prefix: usually LOCAL_2ND_ARCH_VAR_PREFIX, separate for JNI installation\n#\n#   my_link_type: the tags to apply to this module\n#   my_warn_types: the tags to warn about in our dependencies\n#   my_allowed_types: the tags to allow in our dependencies\n#   my_link_deps: the dependencies, in the form of <MODULE_CLASS>:<name>\n#\n\nmy_link_prefix := LINK_TYPE:$(call find-idf-prefix,$(my_kind),$(my_host_cross)):$(if $(my_common),$(my_common):_,_:$(if $(my_2nd_arch_prefix),$(my_2nd_arch_prefix),_))\nlink_type := $(my_link_prefix):$(LOCAL_MODULE_CLASS):$(LOCAL_MODULE)\nALL_LINK_TYPES += $(link_type)\n$(link_type).TYPE := $(my_link_type)\n$(link_type).MAKEFILE := $(LOCAL_MODULE_MAKEFILE)\n$(link_type).WARN := $(my_warn_types)\n$(link_type).ALLOWED := $(my_allowed_types)\n$(link_type).DEPS := $(addprefix $(my_link_prefix):,$(my_link_deps))\n$(link_type).BUILT := $(LOCAL_BUILT_MODULE)\n\nlink_type :=\nmy_allowed_types :=\nmy_link_prefix :=\nmy_link_type :=\nmy_warn_types :=\n"
  },
  {
    "path": "core/local_current_sdk.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nifdef BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES\n  _override_to := $(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES)\n\n  # b/314011075: apks and jars in the vendor or odm partitions cannot use\n  # system SDK 35 and beyond. In order not to suddenly break those vendor\n  # modules using current or system_current as their LOCAL_SDK_VERSION,\n  # override it to 34, which is the maximum API level allowed for them.\n  ifneq (,$(filter JAVA_LIBRARIES APPS,$(LOCAL_MODULE_CLASS)))\n    _override_to := 34\n  endif\n\n  ifneq (current,$(_override_to))\n    ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE)))\n      ifeq (current,$(LOCAL_SDK_VERSION))\n        LOCAL_SDK_VERSION := $(_override_to)\n      else ifeq (system_current,$(LOCAL_SDK_VERSION))\n        LOCAL_SDK_VERSION := system_$(_override_to)\n      endif\n    endif\n  endif\n  _override_to :=\nendif\n"
  },
  {
    "path": "core/local_systemsdk.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nifdef BOARD_SYSTEMSDK_VERSIONS\n  # Apps and jars in vendor, product or odm partition are forced to build against System SDK.\n  _cannot_use_platform_apis :=\n  ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE)))\n    # Note: no need to check LOCAL_MODULE_PATH* since LOCAL_[VENDOR|ODM|OEM]_MODULE is already\n    # set correctly before this is included.\n    _cannot_use_platform_apis := true\n  else ifeq ($(LOCAL_PRODUCT_MODULE),true)\n    ifeq ($(PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE),true)\n      _cannot_use_platform_apis := true\n    endif\n  endif\n  ifneq (,$(filter JAVA_LIBRARIES APPS,$(LOCAL_MODULE_CLASS)))\n    ifndef LOCAL_SDK_VERSION\n      ifeq ($(_cannot_use_platform_apis),true)\n        ifeq (,$(LOCAL_IS_RUNTIME_RESOURCE_OVERLAY))\n          # Runtime resource overlays are exempted from building against System SDK.\n          # TODO(b/155027019): remove this, after no product/vendor apps rely on this behavior.\n          LOCAL_SDK_VERSION := system_current\n          # We have run below again since LOCAL_SDK_VERSION is newly set and the \"_current\"\n          # may have to be updated\n          include $(BUILD_SYSTEM)/local_current_sdk.mk\n        endif\n      endif\n    endif\n  endif\nendif\n\n# Ensure that the selected System SDK version is one of the supported versions.\n# The range of support versions becomes narrower when BOARD_SYSTEMSDK_VERSIONS\n# is set, which is a subset of PLATFORM_SYSTEMSDK_VERSIONS.\nifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION)))\n  ifneq ($(_cannot_use_platform_apis),true)\n    # apps bundled in system partition can use all system sdk versions provided by the platform\n    _supported_systemsdk_versions := $(PLATFORM_SYSTEMSDK_VERSIONS)\n  else ifdef BOARD_SYSTEMSDK_VERSIONS\n    # When BOARD_SYSTEMSDK_VERSIONS is set, vendors apps are restricted to use those versions\n    # which is equal to or smaller than PLATFORM_SYSTEMSDK_VERSIONS\n    _supported_systemsdk_versions := $(BOARD_SYSTEMSDK_VERSIONS)\n  else\n    # If not, vendor apks are treated equally to system apps\n    _supported_systemsdk_versions := $(PLATFORM_SYSTEMSDK_VERSIONS)\n  endif\n\n  # b/314011075: apks and jars in the vendor or odm partitions cannot use system SDK 35 and beyond.\n  # This is to discourage the use of Java APIs in the partitions, which hasn't been supported since\n  # the beginning of the project Treble back in Android 10. Ultimately, we'd like to completely\n  # disallow any Java API in the partitions, but it shall be done progressively.\n  ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE)))\n    # 28 is the API level when BOARD_SYSTEMSDK_VERSIONS was introduced. So, it's the oldset API\n    # we allow.\n    _supported_systemsdk_versions := $(call int_range_list, 28, 34)\n  endif\n\n  # Extract version number from LOCAL_SDK_VERSION (ex: system_34 -> 34)\n  _system_sdk_version := $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION))\n  # However, the extraction may fail if it doesn't have any number (i.e. current, core_current,\n  # system_current, or similar) Then use the latest platform SDK version number or the actual\n  # codename.\n  ifeq (,$(_system_sdk_version)\n    ifeq (REL,$(PLATFORM_VERSION_CODENAME))\n      _system_sdk_version := $(PLATFORM_SDK_VERSION)\n    else\n      _system_sdk_version := $(PLATFORM_VERSION_CODENAME)\n    endif\n  endif\n\n  ifneq ($(_system_sdk_version),$(filter $(_system_sdk_version),$(_supported_systemsdk_versions)))\n    ifneq (true,$(BUILD_BROKEN_DONT_CHECK_SYSTEMSDK)\n      $(call pretty-error,Incompatible LOCAL_SDK_VERSION '$(LOCAL_SDK_VERSION)'. \\\n             System SDK version '$(_system_sdk_version)' is not supported. Supported versions are: $(_supported_systemsdk_versions))\n    endif\n  endif\n  _system_sdk_version :=\n  _supported_systemsdk_versions :=\nendif\n"
  },
  {
    "path": "core/local_vendor_product.mk",
    "content": "# LOCAL_USE_VNDK is not the variable which set by module directly, but there are some modules do so.\n# Set those as LOCAL_IN_VENDOR to make those modules work as expected.\nifeq (true,$(LOCAL_USE_VNDK))\n  $(warning LOCAL_USE_VNDK must not be used. Please use LOCAL_VENDOR_MODULE or LOCAL_PRODUCT_MODULE instead.)\n  LOCAL_IN_VENDOR:=true\nendif\n\n# Set LOCAL_IN_VENDOR for modules going into vendor or odm partition and LOCAL_IN_PRODUCT for product\n# except for host modules. If LOCAL_SDK_VERSION is set, thats a more restrictive set, so they don't need\n# LOCAL_IN_VENDOR or LOCAL_IN_PRODUCT\nifndef LOCAL_IS_HOST_MODULE\nifndef LOCAL_SDK_VERSION\n  ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_OEM_MODULE) $(LOCAL_PROPRIETARY_MODULE)))\n    LOCAL_IN_VENDOR:=true\n    # Note: no need to check LOCAL_MODULE_PATH* since LOCAL_[VENDOR|ODM|OEM]_MODULE is already\n    # set correctly before this is included.\n  endif\n  ifeq (true,$(LOCAL_PRODUCT_MODULE))\n    LOCAL_IN_PRODUCT:=true\n  endif\nendif\nendif\n"
  },
  {
    "path": "core/main.mk",
    "content": "ifndef KATI\n$(warning Calling make directly is no longer supported.)\n$(warning Either use 'envsetup.sh; m' or 'build/soong/soong_ui.bash --make-mode')\n$(error done)\nendif\n\n$(info [1/1] initializing legacy Make module parser ...)\n\n# Absolute path of the present working direcotry.\n# This overrides the shell variable $PWD, which does not necessarily points to\n# the top of the source tree, for example when \"make -C\" is used in m/mm/mmm.\nPWD := $(shell pwd)\n\n# This is the default target.  It must be the first declared target.\n.PHONY: droid\nDEFAULT_GOAL := droid\n$(DEFAULT_GOAL): droid_targets\n\n.PHONY: droid_targets\ndroid_targets:\n\n# Set up various standard variables based on configuration\n# and host information.\ninclude build/make/core/config.mk\n\nifneq ($(filter $(dont_bother_goals), $(MAKECMDGOALS)),)\ndont_bother := true\nendif\n\n.KATI_READONLY := SOONG_CONFIG_NAMESPACES\n.KATI_READONLY := $(foreach n,$(SOONG_CONFIG_NAMESPACES),SOONG_CONFIG_$(n))\n.KATI_READONLY := $(foreach n,$(SOONG_CONFIG_NAMESPACES),$(foreach k,$(SOONG_CONFIG_$(n)),SOONG_CONFIG_$(n)_$(k)))\n\ninclude $(SOONG_OUT_DIR)/make_vars-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk\nYACC :=$= $(BISON) -d\n\ninclude $(BUILD_SYSTEM)/clang/config.mk\n\n# Write the build number to a file so it can be read back in\n# without changing the command line every time.  Avoids rebuilds\n# when using ninja.\nBUILD_NUMBER_FILE := $(SOONG_OUT_DIR)/build_number.txt\n$(KATI_obsolete_var BUILD_NUMBER,See https://android.googlesource.com/platform/build/+/master/Changes.md#BUILD_NUMBER)\nBUILD_HOSTNAME_FILE := $(SOONG_OUT_DIR)/build_hostname.txt\n$(KATI_obsolete_var BUILD_HOSTNAME,Use BUILD_HOSTNAME_FROM_FILE instead)\n$(KATI_obsolete_var FILE_NAME_TAG,https://android.googlesource.com/platform/build/+/master/Changes.md#FILE_NAME_TAG)\n\n.KATI_RESTAT: $(BUILD_NUMBER_FILE)\n.KATI_RESTAT: $(BUILD_HOSTNAME_FILE)\n\nDATE_FROM_FILE := date -d @$(BUILD_DATETIME_FROM_FILE)\n.KATI_READONLY := DATE_FROM_FILE\n\n\n# Make an empty directory, which can be used to make empty jars\nEMPTY_DIRECTORY := $(OUT_DIR)/empty\n$(shell mkdir -p $(EMPTY_DIRECTORY) && rm -rf $(EMPTY_DIRECTORY)/*)\n\n# CTS-specific config.\n-include cts/build/config.mk\n# device-tests-specific-config.\n-include tools/tradefederation/build/suites/device-tests/config.mk\n# general-tests-specific-config.\n-include tools/tradefederation/build/suites/general-tests/config.mk\n# STS-specific config.\n-include test/sts/tools/sts-tradefed/build/config.mk\n# CTS-Instant-specific config\n-include test/suite_harness/tools/cts-instant-tradefed/build/config.mk\n# MTS-specific config.\n-include test/mts/tools/build/config.mk\n# VTS-Core-specific config.\n-include test/vts/tools/vts-core-tradefed/build/config.mk\n# CSUITE-specific config.\n-include test/app_compat/csuite/tools/build/config.mk\n# CATBox-specific config.\n-include test/catbox/tools/build/config.mk\n# CTS-Root-specific config.\n-include test/cts-root/tools/build/config.mk\n# WVTS-specific config.\n-include test/wvts/tools/build/config.mk\n# DTS-specific config.\n-include test/dts/tools/build/config.mk\n\n\n# Clean rules\n.PHONY: clean-dex-files\nclean-dex-files:\n\t$(hide) find $(OUT_DIR) -name \"*.dex\" | xargs rm -f\n\t$(hide) for i in `find $(OUT_DIR) -name \"*.jar\" -o -name \"*.apk\"` ; do ((unzip -l $$i 2> /dev/null | \\\n\t\t\t\tgrep -q \"\\.dex$$\" && rm -f $$i) || continue ) ; done\n\t@echo \"All dex files and archives containing dex files have been removed.\"\n\n# Include the google-specific config\n-include vendor/google/build/config.mk\n\n# These are the modifier targets that don't do anything themselves, but\n# change the behavior of the build.\n# (must be defined before including definitions.make)\nINTERNAL_MODIFIER_TARGETS := all\n\n# EMMA_INSTRUMENT_STATIC merges the static jacoco library to each\n# jacoco-enabled module.\nifeq (true,$(EMMA_INSTRUMENT_STATIC))\nEMMA_INSTRUMENT := true\nendif\n\nifdef TARGET_ARCH_SUITE\n  # TODO(b/175577370): Enable this error.\n  # $(error TARGET_ARCH_SUITE is not supported in kati/make builds)\nendif\n\n$(KATI_obsolete_var ADDITIONAL_BUILD_PROPERTIES, Please use ADDITIONAL_SYSTEM_PROPERTIES)\n\n# Bring in standard build system definitions.\ninclude $(BUILD_SYSTEM)/definitions.mk\n\nifneq ($(filter user userdebug eng,$(MAKECMDGOALS)),)\n$(info ***************************************************************)\n$(info ***************************************************************)\n$(info Do not pass '$(filter user userdebug eng,$(MAKECMDGOALS))' on \\\n       the make command line.)\n$(info Set TARGET_BUILD_VARIANT in buildspec.mk, or use lunch or)\n$(info choosecombo.)\n$(info ***************************************************************)\n$(info ***************************************************************)\n$(error stopping)\nendif\n\n# These are the valid values of TARGET_BUILD_VARIANT.\nINTERNAL_VALID_VARIANTS := user userdebug eng\nifneq ($(filter-out $(INTERNAL_VALID_VARIANTS),$(TARGET_BUILD_VARIANT)),)\n$(info ***************************************************************)\n$(info ***************************************************************)\n$(info Invalid variant: $(TARGET_BUILD_VARIANT))\n$(info Valid values are: $(INTERNAL_VALID_VARIANTS))\n$(info ***************************************************************)\n$(info ***************************************************************)\n$(error stopping)\nendif\n\n# -----------------------------------------------------------------\n# PDK builds are no longer supported, this is always platform\nTARGET_BUILD_JAVA_SUPPORT_LEVEL :=$= platform\n\n$(KATI_obsolete_var PRODUCT_FULL_TREBLE,\\\n\tCode should be written to work regardless of a device being Treble)\n# -----------------------------------------------------------------\n###\n### In this section we set up the things that are different\n### between the build variants\n###\n\nis_sdk_build :=\n\nifneq ($(filter sdk sdk_addon,$(MAKECMDGOALS)),)\nis_sdk_build := true\nendif\n\ntags_to_install :=\n\nifeq ($(TARGET_BUILD_VARIANT),userdebug)\n# Pick up some extra useful tools\ntags_to_install := debug\nendif\n\nifeq ($(TARGET_BUILD_VARIANT),eng)\ntags_to_install := debug eng\nendif\n\n## asan ##\n\n# Install some additional tools on ASAN builds IFF we are also installing debug tools\nifneq ($(filter address,$(SANITIZE_TARGET)),)\nifneq (,$(filter debug,$(tags_to_install)))\n  tags_to_install += asan\nendif\nendif\n\n## java coverage ##\n# Install additional tools on java coverage builds\nifeq (true,$(EMMA_INSTRUMENT))\nifneq (,$(filter debug,$(tags_to_install)))\n  tags_to_install += java_coverage\nendif\nendif\n\n\n## sdk ##\n\nifdef is_sdk_build\n\n# Detect if we want to build a repository for the SDK\nsdk_repo_goal := $(strip $(filter sdk_repo,$(MAKECMDGOALS)))\nMAKECMDGOALS := $(strip $(filter-out sdk_repo,$(MAKECMDGOALS)))\n\nifneq ($(words $(sort $(filter-out $(INTERNAL_MODIFIER_TARGETS) checkbuild emulator_tests,$(MAKECMDGOALS)))),1)\n$(error The 'sdk' target may not be specified with any other targets)\nendif\n\n# TODO: this should be eng I think.  Since the sdk is built from the eng\n# variant.\ntags_to_install := debug eng\nelse # !sdk\nendif\n\nBUILD_WITHOUT_PV := true\n\n# ------------------------------------------------------------\n# Define a function that, given a list of module tags, returns\n# non-empty if that module should be installed in /system.\n\n# For most goals, anything not tagged with the \"tests\" tag should\n# be installed in /system.\ndefine should-install-to-system\n$(if $(filter tests,$(1)),,true)\nendef\n\nifdef is_sdk_build\n# For the sdk goal, anything with the \"samples\" tag should be\n# installed in /data even if that module also has \"eng\"/\"debug\"/\"user\".\ndefine should-install-to-system\n$(if $(filter samples tests,$(1)),,true)\nendef\nendif\n\n\n# If they only used the modifier goals (all, etc), we'll actually\n# build the default target.\nifeq ($(filter-out $(INTERNAL_MODIFIER_TARGETS),$(MAKECMDGOALS)),)\n.PHONY: $(INTERNAL_MODIFIER_TARGETS)\n$(INTERNAL_MODIFIER_TARGETS): $(DEFAULT_GOAL)\nendif\n\n#\n# Typical build; include any Android.mk files we can find.\n#\n\ninclude $(BUILD_SYSTEM)/art_config.mk\n\n# Bring in dex_preopt.mk\n# This creates some modules so it needs to be included after\n# should-install-to-system is defined (in order for base_rules.mk to function\n# properly), but before readonly-final-product-vars is called.\ninclude $(BUILD_SYSTEM)/dex_preopt.mk\n\n# Strip and readonly a few more variables so they won't be modified.\n$(readonly-final-product-vars)\n\nifneq ($(PRODUCT_ENFORCE_RRO_TARGETS),)\nENFORCE_RRO_SOURCES :=\nendif\n\n# Color-coded warnings including current module info\n# $(1): message to print\ndefine pretty-warning\n$(shell $(call echo-warning,$(LOCAL_MODULE_MAKEFILE),$(LOCAL_MODULE): $(1)))\nendef\n\n# Color-coded errors including current module info\n# $(1): message to print\ndefine pretty-error\n$(shell $(call echo-error,$(LOCAL_MODULE_MAKEFILE),$(LOCAL_MODULE): $(1)))\n$(error done)\nendef\n\nsubdir_makefiles_inc := .\nFULL_BUILD :=\n\nifneq ($(dont_bother),true)\nFULL_BUILD := true\n#\n# Include all of the makefiles in the system\n#\n\nsubdir_makefiles := \\\n    $(SOONG_OUT_DIR)/installs-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk \\\n    $(SOONG_ANDROID_MK) \\\n    build/make/target/board/android-info.mk\n\n# Android.mk files are only used on Linux builds, Mac only supports Android.bp\nifeq ($(HOST_OS),linux)\n  ifeq ($(PRODUCT_IGNORE_ALL_ANDROIDMK),true)\n    allowed_androidmk_files :=\n    ifdef PRODUCT_ANDROIDMK_ALLOWLIST_FILE\n      -include $(PRODUCT_ANDROIDMK_ALLOWLIST_FILE)\n    endif\n    allowed_androidmk_files += $(PRODUCT_ALLOWED_ANDROIDMK_FILES)\n    subdir_makefiles += $(filter $(allowed_androidmk_files),$(file <$(OUT_DIR)/.module_paths/Android.mk.list))\n    allowed_androidmk_files :=\n  else\n    subdir_makefiles += $(file <$(OUT_DIR)/.module_paths/Android.mk.list)\n  endif\nendif\n\nsubdir_makefiles += $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk\n\nsubdir_makefiles_total := $(words int $(subdir_makefiles) post finish)\n.KATI_READONLY := subdir_makefiles_total\n\n$(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))\n\n-include device/generic/goldfish/tasks/emu_img_zip.mk\n\n# Build bootloader.img/radio.img, and unpack the partitions.\n-include vendor/google_devices/$(TARGET_SOC)/prebuilts/misc_bins/update_bootloader_radio_image.mk\n\n# For an unbundled image, we can skip blueprint_tools because unbundled image\n# aims to remove a large number framework projects from the manifest, the\n# sources or dependencies for these tools may be missing from the tree.\nifeq (,$(TARGET_BUILD_UNBUNDLED_IMAGE))\ndroid_targets : blueprint_tools\ncheckbuild: blueprint_tests\nendif\n\n# Create necessary directories and symlinks in the root filesystem\ninclude system/core/rootdir/create_root_structure.mk\n\nendif # dont_bother\n\nifndef subdir_makefiles_total\nsubdir_makefiles_total := $(words init post finish)\nendif\n\n$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] finishing legacy Make module parsing ...)\n\n# -------------------------------------------------------------------\n# All module makefiles have been included at this point.\n# -------------------------------------------------------------------\n\n# -------------------------------------------------------------------\n# Use basic warning/error messages now that LOCAL_MODULE_MAKEFILE\n# and LOCAL_MODULE aren't useful anymore.\n# -------------------------------------------------------------------\ndefine pretty-warning\n$(warning $(1))\nendef\n\ndefine pretty-error\n$(error $(1))\nendef\n\n# -------------------------------------------------------------------\n# Enforce to generate all RRO packages for modules having resource\n# overlays.\n# -------------------------------------------------------------------\nifneq ($(PRODUCT_ENFORCE_RRO_TARGETS),)\n$(call generate_all_enforce_rro_packages)\nendif\n\n# -------------------------------------------------------------------\n# Sort ALL_MODULES to remove duplicate entries.\n# -------------------------------------------------------------------\nALL_MODULES := $(sort $(ALL_MODULES))\n# Cannot set to readonly because Makefile extends ALL_MODULES\n# .KATI_READONLY := ALL_MODULES\n\n# -------------------------------------------------------------------\n# Fix up CUSTOM_MODULES to refer to installed files rather than\n# just bare module names.  Leave unknown modules alone in case\n# they're actually full paths to a particular file.\nknown_custom_modules := $(filter $(ALL_MODULES),$(CUSTOM_MODULES))\nunknown_custom_modules := $(filter-out $(ALL_MODULES),$(CUSTOM_MODULES))\nCUSTOM_MODULES := \\\n\t$(call module-installed-files,$(known_custom_modules)) \\\n\t$(unknown_custom_modules)\n\n# -------------------------------------------------------------------\n# Define dependencies for modules that require other modules.\n# This can only happen now, after we've read in all module makefiles.\n#\n# TODO: deal with the fact that a bare module name isn't\n# unambiguous enough.  Maybe declare short targets like\n# APPS:Quake or HOST:SHARED_LIBRARIES:libutils.\n# BUG: the system image won't know to depend on modules that are\n# brought in as requirements of other modules.\n#\n# Resolve the required module name to 32-bit or 64-bit variant.\n\n# Get a list of corresponding module names for the second arch, if they exist.\n# $(1): TARGET, HOST or HOST_CROSS\n# $(2): A list of module names\ndefine get-modules-for-2nd-arch\n$(strip \\\n  $(foreach m,$(2), \\\n    $(if $(filter true,$(ALL_MODULES.$(m)$($(1)_2ND_ARCH_MODULE_SUFFIX).FOR_2ND_ARCH)), \\\n      $(m)$($(1)_2ND_ARCH_MODULE_SUFFIX) \\\n    ) \\\n  ) \\\n)\nendef\n\n# Resolves module bitness for PRODUCT_PACKAGES and PRODUCT_HOST_PACKAGES.\n# The returned list of module names can be used to access\n# ALL_MODULES.<module>.<*> variables.\n# Name resolution for PRODUCT_PACKAGES / PRODUCT_HOST_PACKAGES:\n#   foo:32 resolves to foo_32;\n#   foo:64 resolves to foo;\n#   foo resolves to both foo and foo_32 (if foo_32 is defined).\n#\n# Name resolution for HOST_CROSS modules:\n#   foo:32 resolves to foo;\n#   foo:64 resolves to foo_64;\n#   foo resolves to both foo and foo_64 (if foo_64 is defined).\n#\n# $(1): TARGET, HOST or HOST_CROSS\n# $(2): A list of simple module names with :32 and :64 suffix\ndefine resolve-bitness-for-modules\n$(strip \\\n  $(eval modules_32 := $(patsubst %:32,%,$(filter %:32,$(2)))) \\\n  $(eval modules_64 := $(patsubst %:64,%,$(filter %:64,$(2)))) \\\n  $(eval modules_both := $(filter-out %:32 %:64,$(2))) \\\n  $(eval ### if 2ND_HOST_CROSS_IS_64_BIT, then primary/secondary are reversed for HOST_CROSS modules) \\\n  $(if $(filter HOST_CROSS_true,$(1)_$(2ND_HOST_CROSS_IS_64_BIT)), \\\n    $(eval modules_1st_arch := $(modules_32)) \\\n    $(eval modules_2nd_arch := $(modules_64)), \\\n    $(eval modules_1st_arch := $(modules_64)) \\\n    $(eval modules_2nd_arch := $(modules_32))) \\\n  $(eval ### Note for 32-bit product, 32 and 64 will be added as their original module names.) \\\n  $(eval modules := $(modules_1st_arch)) \\\n  $(if $($(1)_2ND_ARCH), \\\n    $(eval modules += $(call get-modules-for-2nd-arch,$(1),$(modules_2nd_arch))), \\\n    $(eval modules += $(modules_2nd_arch))) \\\n  $(eval ### For the rest we add both) \\\n  $(eval modules += $(modules_both)) \\\n  $(if $($(1)_2ND_ARCH), \\\n    $(eval modules += $(call get-modules-for-2nd-arch,$(1),$(modules_both)))) \\\n  $(modules) \\\n)\nendef\n\n# Resolve the required module names to 32-bit or 64-bit variant for:\n#   ALL_MODULES.<*>.REQUIRED_FROM_TARGET\n#   ALL_MODULES.<*>.REQUIRED_FROM_HOST\n#   ALL_MODULES.<*>.REQUIRED_FROM_HOST_CROSS\n#\n# If a module is for cross host OS, the required modules are also for that OS.\n# Required modules explicitly suffixed with :32 or :64 resolve to that bitness.\n# Otherwise if the requiring module is native and the required module is shared\n# library or native test, then the required module resolves to the same bitness.\n# Otherwise the required module resolves to both variants, if they exist.\n# $(1): TARGET, HOST or HOST_CROSS\ndefine select-bitness-of-required-modules\n$(foreach m,$(ALL_MODULES), \\\n  $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_$(1))) \\\n  $(if $(r), \\\n    $(if $(filter HOST_CROSS,$(1)), \\\n      $(if $(ALL_MODULES.$(m).FOR_HOST_CROSS),,$(error Only expected REQUIRED_FROM_HOST_CROSS on FOR_HOST_CROSS modules - $(m))) \\\n      $(eval r := $(addprefix host_cross_,$(r)))) \\\n    $(eval module_is_native := \\\n      $(filter EXECUTABLES SHARED_LIBRARIES NATIVE_TESTS,$(ALL_MODULES.$(m).CLASS))) \\\n    $(eval r_r := \\\n      $(foreach r_i,$(r), \\\n        $(if $(filter %:32 %:64,$(r_i)), \\\n          $(eval r_m := $(call resolve-bitness-for-modules,$(1),$(r_i))), \\\n          $(eval r_m := \\\n            $(eval r_i_2nd := $(call get-modules-for-2nd-arch,$(1),$(r_i))) \\\n            $(eval required_is_shared_library_or_native_test := \\\n              $(filter SHARED_LIBRARIES NATIVE_TESTS, \\\n                $(ALL_MODULES.$(r_i).CLASS) $(ALL_MODULES.$(r_i_2nd).CLASS))) \\\n            $(if $(and $(module_is_native),$(required_is_shared_library_or_native_test)), \\\n              $(if $(ALL_MODULES.$(m).FOR_2ND_ARCH),$(r_i_2nd),$(r_i)), \\\n              $(r_i) $(r_i_2nd)))) \\\n        $(eval r_m := $(foreach r_j,$(r_m),$(if $(ALL_MODULES.$(r_j).PATH),$(r_j)))) \\\n        $(if $(r_m),,$(eval _nonexistent_required += $(1)$(comma)$(m)$(comma)$(1)$(comma)$(r_i))) \\\n        $(r_m))) \\\n    $(eval ALL_MODULES.$(m).REQUIRED_FROM_$(1) := $(sort $(r_r))) \\\n  ) \\\n)\nendef\n\n# Resolve the required module names to 32-bit or 64-bit variant for:\n#   ALL_MODULES.<*>.TARGET_REQUIRED_FROM_HOST\n#   ALL_MODULES.<*>.HOST_REQUIRED_FROM_TARGET\n#\n# This is like select-bitness-of-required-modules, but it doesn't have\n# complicated logic for various module types.\n# Calls resolve-bitness-for-modules to resolve module names.\n# $(1): TARGET or HOST\n# $(2): HOST or TARGET\ndefine select-bitness-of-target-host-required-modules\n$(foreach m,$(ALL_MODULES), \\\n  $(eval r := $(ALL_MODULES.$(m).$(1)_REQUIRED_FROM_$(2))) \\\n  $(if $(r), \\\n    $(eval r_r := \\\n      $(foreach r_i,$(r), \\\n        $(eval r_m := $(call resolve-bitness-for-modules,$(1),$(r_i))) \\\n        $(eval r_m := $(foreach r_j,$(r_m),$(if $(ALL_MODULES.$(r_j).PATH),$(r_j)))) \\\n        $(if $(r_m),,$(eval _nonexistent_required += $(2)$(comma)$(m)$(comma)$(1)$(comma)$(r_i))) \\\n        $(r_m))) \\\n    $(eval ALL_MODULES.$(m).$(1)_REQUIRED_FROM_$(2) := $(sort $(r_r))) \\\n  ) \\\n)\nendef\n\n_nonexistent_required :=\n$(call select-bitness-of-required-modules,TARGET)\n$(call select-bitness-of-required-modules,HOST)\n$(call select-bitness-of-required-modules,HOST_CROSS)\n$(call select-bitness-of-target-host-required-modules,TARGET,HOST)\n$(call select-bitness-of-target-host-required-modules,HOST,TARGET)\n_nonexistent_required := $(sort $(_nonexistent_required))\n\ncheck_missing_required_modules := true\nifneq (,$(filter true,$(ALLOW_MISSING_DEPENDENCIES) $(BUILD_BROKEN_MISSING_REQUIRED_MODULES)))\n  check_missing_required_modules :=\nendif # ALLOW_MISSING_DEPENDENCIES == true || BUILD_BROKEN_MISSING_REQUIRED_MODULES == true\n\n# Some executables are skipped in ASAN SANITIZE_TARGET build, thus breaking their dependencies.\nifneq (,$(filter address,$(SANITIZE_TARGET)))\n  check_missing_required_modules :=\nendif # SANITIZE_TARGET has ASAN\n\n# HOST OS darwin build is broken, disable this check for darwin for now.\n# TODO(b/162102724): Remove this when darwin host has no broken dependency.\nifneq (,$(filter $(HOST_OS),darwin))\n  check_missing_required_modules :=\nendif # HOST_OS == darwin\n\nifeq (true,$(check_missing_required_modules))\nifneq (,$(_nonexistent_required))\n  $(warning Missing required dependencies:)\n  $(foreach r_i,$(_nonexistent_required), \\\n    $(eval r := $(subst $(comma),$(space),$(r_i))) \\\n    $(info $(word 1,$(r)) module $(word 2,$(r)) requires non-existent $(word 3,$(r)) module: $(word 4,$(r))) \\\n  )\n  $(warning Set BUILD_BROKEN_MISSING_REQUIRED_MODULES := true to bypass this check if this is intentional)\n  ifneq (,$(PRODUCT_SOURCE_ROOT_DIRS))\n    $(warning PRODUCT_SOURCE_ROOT_DIRS is non-empty. Some necessary modules may have been skipped by Soong)\n  endif\n  $(error Build failed)\nendif # _nonexistent_required != empty\nendif # check_missing_required_modules == true\n\ndefine add-required-deps\n$(1): | $(2)\nendef\n\n# Use a normal dependency instead of an order-only dependency when installing\n# host dynamic binaries so that the timestamp of the final binary always\n# changes, even if the toc optimization has skipped relinking the binary\n# and its dependant shared libraries.\ndefine add-required-host-so-deps\n$(1): $(2)\nendef\n\n# Sets up dependencies such that whenever a host module is installed,\n# any other host modules listed in $(ALL_MODULES.$(m).REQUIRED_FROM_HOST) will also be installed\ndefine add-all-host-to-host-required-modules-deps\n$(foreach m,$(ALL_MODULES), \\\n  $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_HOST)) \\\n  $(if $(r), \\\n    $(eval r := $(call module-installed-files,$(r))) \\\n    $(eval h_m := $(filter $(HOST_OUT)/%, $(ALL_MODULES.$(m).INSTALLED))) \\\n    $(eval h_r := $(filter $(HOST_OUT)/%, $(r))) \\\n    $(eval h_r := $(filter-out $(h_m), $(h_r))) \\\n    $(if $(h_m), $(eval $(call add-required-deps, $(h_m),$(h_r)))) \\\n  ) \\\n)\nendef\n$(call add-all-host-to-host-required-modules-deps)\n\n# Sets up dependencies such that whenever a host cross module is installed,\n# any other host cross modules listed in $(ALL_MODULES.$(m).REQUIRED_FROM_HOST_CROSS) will also be installed\ndefine add-all-host-cross-to-host-cross-required-modules-deps\n$(foreach m,$(ALL_MODULES), \\\n  $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_HOST_CROSS)) \\\n  $(if $(r), \\\n    $(eval r := $(call module-installed-files,$(r))) \\\n    $(eval hc_m := $(filter $(HOST_CROSS_OUT)/%, $(ALL_MODULES.$(m).INSTALLED))) \\\n    $(eval hc_r := $(filter $(HOST_CROSS_OUT)/%, $(r))) \\\n    $(eval hc_r := $(filter-out $(hc_m), $(hc_r))) \\\n    $(if $(hc_m), $(eval $(call add-required-deps, $(hc_m),$(hc_r)))) \\\n  ) \\\n)\nendef\n$(call add-all-host-cross-to-host-cross-required-modules-deps)\n\n# Sets up dependencies such that whenever a target module is installed,\n# any other target modules listed in $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET) will also be installed\n# This doesn't apply to ORDERONLY_INSTALLED items.\ndefine add-all-target-to-target-required-modules-deps\n$(foreach m,$(ALL_MODULES), \\\n  $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET)) \\\n  $(if $(r), \\\n    $(eval r := $(call module-installed-files,$(r))) \\\n    $(eval t_m := $(filter $(TARGET_OUT_ROOT)/%, $(ALL_MODULES.$(m).INSTALLED))) \\\n    $(eval t_m := $(filter-out $(ALL_MODULES.$(m).ORDERONLY_INSTALLED), $(ALL_MODULES.$(m).INSTALLED))) \\\n    $(eval t_r := $(filter $(TARGET_OUT_ROOT)/%, $(r))) \\\n    $(eval t_r := $(filter-out $(t_m), $(t_r))) \\\n    $(if $(t_m), $(eval $(call add-required-deps, $(t_m),$(t_r)))) \\\n  ) \\\n)\nendef\n$(call add-all-target-to-target-required-modules-deps)\n\n# Sets up dependencies such that whenever a host module is installed,\n# any target modules listed in $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST) will also be installed\ndefine add-all-host-to-target-required-modules-deps\n$(foreach m,$(ALL_MODULES), \\\n  $(eval req_mods := $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST))\\\n  $(if $(req_mods), \\\n    $(eval req_files := )\\\n    $(foreach req_mod,$(req_mods), \\\n      $(eval req_file := $(filter $(TARGET_OUT_ROOT)/%, $(call module-installed-files,$(req_mod)))) \\\n      $(if $(filter true,$(ALLOW_MISSING_DEPENDENCIES)), \\\n        , \\\n        $(if $(strip $(req_file)), \\\n          , \\\n          $(error $(m).LOCAL_TARGET_REQUIRED_MODULES : illegal value $(req_mod) : not a device module. If you want to specify host modules to be required to be installed along with your host module, add those module names to LOCAL_REQUIRED_MODULES instead) \\\n        ) \\\n      ) \\\n      $(eval req_files := $(req_files)$(space)$(req_file))\\\n    )\\\n    $(eval req_files := $(strip $(req_files)))\\\n    $(eval mod_files := $(filter $(HOST_OUT)/%, $(call module-installed-files,$(m)))) \\\n    $(if $(mod_files),\\\n      $(eval $(call add-required-deps, $(mod_files),$(req_files))) \\\n    )\\\n  )\\\n)\nendef\n$(call add-all-host-to-target-required-modules-deps)\n\n# Sets up dependencies such that whenever a target module is installed,\n# any host modules listed in $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET) will also be installed\ndefine add-all-target-to-host-required-modules-deps\n$(foreach m,$(ALL_MODULES), \\\n  $(eval req_mods := $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET))\\\n  $(if $(req_mods), \\\n    $(eval req_files := )\\\n    $(foreach req_mod,$(req_mods), \\\n      $(eval req_file := $(filter $(HOST_OUT)/%, $(call module-installed-files,$(req_mod)))) \\\n      $(if $(filter true,$(ALLOW_MISSING_DEPENDENCIES)), \\\n        , \\\n        $(if $(strip $(req_file)), \\\n          , \\\n          $(error $(m).LOCAL_HOST_REQUIRED_MODULES : illegal value $(req_mod) : not a host module. If you want to specify target modules to be required to be installed along with your target module, add those module names to LOCAL_REQUIRED_MODULES instead) \\\n        ) \\\n      ) \\\n      $(eval req_files := $(req_files)$(space)$(req_file))\\\n    )\\\n    $(eval req_files := $(strip $(req_files)))\\\n    $(eval mod_files := $(filter $(TARGET_OUT_ROOT)/%, $(call module-installed-files,$(m))))\\\n    $(if $(mod_files),\\\n      $(eval $(call add-required-deps, $(mod_files),$(req_files))) \\\n    )\\\n  )\\\n)\nendef\n$(call add-all-target-to-host-required-modules-deps)\n\nt_m :=\nh_m :=\nhc_m :=\nt_r :=\nh_r :=\nhc_r :=\n\n# Establish the dependencies on the shared libraries.\n# It also adds the shared library module names to ALL_MODULES.$(m).REQUIRED_FROM_(TARGET|HOST|HOST_CROSS),\n# so they can be expanded to product_MODULES later.\n# $(1): TARGET_ or HOST_ or HOST_CROSS_.\n# $(2): non-empty for 2nd arch.\n# $(3): non-empty for host cross compile.\ndefine resolve-shared-libs-depes\n$(foreach m,$($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))$(1)DEPENDENCIES_ON_SHARED_LIBRARIES),\\\n  $(eval p := $(subst :,$(space),$(m)))\\\n  $(eval mod := $(firstword $(p)))\\\n  $(eval deps := $(subst $(comma),$(space),$(lastword $(p))))\\\n  $(eval root := $(1)OUT$(if $(call streq,$(1),TARGET_),_ROOT))\\\n  $(if $(2),$(eval deps := $(addsuffix $($(1)2ND_ARCH_MODULE_SUFFIX),$(deps))))\\\n  $(if $(3),$(eval deps := $(addprefix host_cross_,$(deps))))\\\n  $(eval r := $(filter $($(root))/%,$(call module-installed-files,\\\n    $(deps))))\\\n  $(if $(filter $(1),HOST_),\\\n    $(eval ALL_MODULES.$(mod).HOST_SHARED_LIBRARY_FILES := $$(ALL_MODULES.$(mod).HOST_SHARED_LIBRARY_FILES) $(word 2,$(p)) $(r))\\\n    $(eval ALL_MODULES.$(mod).HOST_SHARED_LIBRARIES := $$(ALL_MODULES.$(mod).HOST_SHARED_LIBRARIES) $(deps))\\\n    $(eval $(call add-required-host-so-deps,$(word 2,$(p)),$(r))),\\\n    $(eval $(call add-required-deps,$(word 2,$(p)),$(r))))\\\n  $(eval ALL_MODULES.$(mod).REQUIRED_FROM_$(patsubst %_,%,$(1)) += $(deps)))\nendef\n\n# Recursively resolve host shared library dependency for a given module.\n# $(1): module name\n# Returns all dependencies of shared library.\ndefine get-all-shared-libs-deps\n$(if $(_all_deps_for_$(1)_set_),$(_all_deps_for_$(1)_),\\\n  $(eval _all_deps_for_$(1)_ :=) \\\n  $(foreach dep,$(ALL_MODULES.$(1).HOST_SHARED_LIBRARIES),\\\n    $(foreach m,$(call get-all-shared-libs-deps,$(dep)),\\\n      $(eval _all_deps_for_$(1)_ := $$(_all_deps_for_$(1)_) $(m))\\\n      $(eval _all_deps_for_$(1)_ := $(sort $(_all_deps_for_$(1)_))))\\\n    $(eval _all_deps_for_$(1)_ := $$(_all_deps_for_$(1)_) $(dep))\\\n    $(eval _all_deps_for_$(1)_ := $(sort $(_all_deps_for_$(1)_) $(dep)))\\\n    $(eval _all_deps_for_$(1)_set_ := true))\\\n$(_all_deps_for_$(1)_))\nendef\n\n# Scan all modules in general-tests, device-tests and other selected suites and\n# flatten the shared library dependencies.\ndefine update-host-shared-libs-deps-for-suites\n$(foreach suite,general-tests device-tests vts tvts art-host-tests host-unit-tests camera-hal-tests,\\\n  $(foreach m,$(COMPATIBILITY.$(suite).MODULES),\\\n    $(eval my_deps := $(call get-all-shared-libs-deps,$(m)))\\\n    $(foreach dep,$(my_deps),\\\n      $(foreach f,$(ALL_MODULES.$(dep).HOST_SHARED_LIBRARY_FILES),\\\n        $(if $(filter $(suite),device-tests general-tests art-host-tests host-unit-tests camera-hal-tests),\\\n          $(eval my_testcases := $(HOST_OUT_TESTCASES)),\\\n          $(eval my_testcases := $$(COMPATIBILITY_TESTCASES_OUT_$(suite))))\\\n        $(eval target := $(my_testcases)/$(lastword $(subst /, ,$(dir $(f))))/$(notdir $(f)))\\\n        $(eval prefix := ../../..)\n        $(if $(strip $(patsubst %x86,,$(COMPATIBILITY.$(suite).ARCH_DIRS.$(m)))), \\\n          $(if $(strip $(patsubst %x86_64,,$(COMPATIBILITY.$(suite).ARCH_DIRS.$(m)))),$(eval prefix := ../..),),) \\\n        $(eval link_target := $(prefix)/$(lastword $(subst /, ,$(dir $(f))))/$(notdir $(f)))\\\n        $(eval symlink := $(COMPATIBILITY.$(suite).ARCH_DIRS.$(m))/shared_libs/$(notdir $(f)))\\\n        $(eval COMPATIBILITY.$(suite).SYMLINKS := \\\n          $$(COMPATIBILITY.$(suite).SYMLINKS) $(f):$(link_target):$(symlink))\\\n        $(if $(strip $(ALL_TARGETS.$(target).META_LIC)),,$(call declare-copy-target-license-metadata,$(target),$(f)))\\\n        $(eval COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES := \\\n          $$(COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES) $(f):$(target))\\\n        $(eval COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES := \\\n          $(sort $(COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES))))))\\\n  $(eval COMPATIBILITY.$(suite).SYMLINKS := $(sort $(COMPATIBILITY.$(suite).SYMLINKS))))\nendef\n\n$(call resolve-shared-libs-depes,TARGET_)\nifdef TARGET_2ND_ARCH\n$(call resolve-shared-libs-depes,TARGET_,true)\nendif\n$(call resolve-shared-libs-depes,HOST_)\nifdef HOST_2ND_ARCH\n$(call resolve-shared-libs-depes,HOST_,true)\nendif\n# Update host side shared library dependencies for tests in suite device-tests and general-tests.\n# This should be called after calling resolve-shared-libs-depes for HOST_2ND_ARCH.\n$(call update-host-shared-libs-deps-for-suites)\nifdef HOST_CROSS_OS\n$(call resolve-shared-libs-depes,HOST_CROSS_,,true)\nifdef HOST_CROSS_2ND_ARCH\n$(call resolve-shared-libs-depes,HOST_CROSS_,true,true)\nendif\nendif\n\n# Pass the shared libraries dependencies to prebuilt ELF file check.\ndefine add-elf-file-check-shared-lib\n$(1): PRIVATE_SHARED_LIBRARY_FILES += $(2)\n$(1): $(2)\nendef\n\ndefine resolve-shared-libs-for-elf-file-check\n$(foreach m,$($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))$(1)DEPENDENCIES_ON_SHARED_LIBRARIES),\\\n  $(eval p := $(subst :,$(space),$(m)))\\\n  $(eval mod := $(firstword $(p)))\\\n  \\\n  $(eval deps := $(subst $(comma),$(space),$(lastword $(p))))\\\n  $(if $(2),$(eval deps := $(addsuffix $($(1)2ND_ARCH_MODULE_SUFFIX),$(deps))))\\\n  $(eval root := $(1)OUT$(if $(call streq,$(1),TARGET_),_ROOT))\\\n  $(eval deps := $(filter $($(root))/%$($(1)SHLIB_SUFFIX),$(call module-built-files,$(deps))))\\\n  \\\n  $(eval r := $(firstword $(filter \\\n    $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/EXECUTABLES/%\\\n    $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/NATIVE_TESTS/%\\\n    $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/SHARED_LIBRARIES/%,\\\n    $(call module-built-files,$(mod)))))\\\n  \\\n  $(if $(and $(r),$(deps)),\\\n    $(eval stamp := $(dir $(r))check_elf_files.timestamp)\\\n    $(if $(CHECK_ELF_FILES.$(stamp)),\\\n      $(eval $(call add-elf-file-check-shared-lib,$(stamp),$(deps))))\\\n  ))\nendef\n\n$(call resolve-shared-libs-for-elf-file-check,TARGET_)\nifdef TARGET_2ND_ARCH\n$(call resolve-shared-libs-for-elf-file-check,TARGET_,true)\nendif\n\nm :=\nr :=\np :=\nstamp :=\ndeps :=\nadd-required-deps :=\n\n################################################################################\n# Link type checking\n#\n# ALL_LINK_TYPES contains a list of all link type prefixes (generally one per\n# module, but APKs can \"link\" to both java and native code). The link type\n# prefix consists of all the information needed by intermediates-dir-for:\n#\n#  LINK_TYPE:TARGET:_:2ND:STATIC_LIBRARIES:libfoo\n#\n#   1: LINK_TYPE literal\n#   2: prefix\n#     - TARGET\n#     - HOST\n#     - HOST_CROSS\n#   3: Whether to use the common intermediates directory or not\n#     - _\n#     - COMMON\n#   4: Whether it's the second arch or not\n#     - _\n#     - 2ND_\n#   5: Module Class\n#     - STATIC_LIBRARIES\n#     - SHARED_LIBRARIES\n#     - ...\n#   6: Module Name\n#\n# Then fields under that are separated by a period and the field name:\n#   - TYPE: the link types for this module\n#   - MAKEFILE: Where this module was defined\n#   - BUILT: The built module location\n#   - DEPS: the link type prefixes for the module's dependencies\n#   - ALLOWED: the link types to allow in this module's dependencies\n#   - WARN: the link types to warn about in this module's dependencies\n#\n# All of the dependency link types not listed in ALLOWED or WARN will become\n# errors.\n################################################################################\n\nlink_type_error :=\n\ndefine link-type-prefix\n$(word 2,$(subst :,$(space),$(1)))\nendef\ndefine link-type-common\n$(patsubst _,,$(word 3,$(subst :,$(space),$(1))))\nendef\ndefine link-type-2ndarchprefix\n$(patsubst _,,$(word 4,$(subst :,$(space),$(1))))\nendef\ndefine link-type-class\n$(word 5,$(subst :,$(space),$(1)))\nendef\ndefine link-type-name\n$(word 6,$(subst :,$(space),$(1)))\nendef\ndefine link-type-os\n$(strip $(eval _p := $(link-type-prefix))\\\n  $(if $(filter HOST HOST_CROSS,$(_p)),\\\n    $($(_p)_OS),\\\n    android))\nendef\ndefine link-type-arch\n$($(link-type-prefix)_$(link-type-2ndarchprefix)ARCH)\nendef\ndefine link-type-name-variant\n$(link-type-name) ($(link-type-class) $(link-type-os)-$(link-type-arch))\nendef\n\n# $(1): the prefix of the module doing the linking\n# $(2): the prefix of the linked module\ndefine link-type-warning\n$(shell $(call echo-warning,$($(1).MAKEFILE),\"$(call link-type-name,$(1)) ($($(1).TYPE)) should not link against $(call link-type-name,$(2)) ($(3))\"))\nendef\n\n# $(1): the prefix of the module doing the linking\n# $(2): the prefix of the linked module\ndefine link-type-error\n$(shell $(call echo-error,$($(1).MAKEFILE),\"$(call link-type-name,$(1)) ($($(1).TYPE)) can not link against $(call link-type-name,$(2)) ($(3))\"))\\\n$(eval link_type_error := true)\nendef\n\nlink-type-missing :=\nifneq ($(ALLOW_MISSING_DEPENDENCIES),true)\n  # Print an error message if the linked-to module is missing\n  # $(1): the prefix of the module doing the linking\n  # $(2): the prefix of the missing module\n  define link-type-missing\n    $(shell $(call echo-error,$($(1).MAKEFILE),\"$(call link-type-name-variant,$(1)) missing $(call link-type-name-variant,$(2))\"))\\\n    $(eval available_variants := $(filter %:$(call link-type-name,$(2)),$(ALL_LINK_TYPES)))\\\n    $(if $(available_variants),\\\n      $(info Available variants:)\\\n      $(foreach v,$(available_variants),$(info $(space)$(space)$(call link-type-name-variant,$(v)))))\\\n    $(info You can set ALLOW_MISSING_DEPENDENCIES=true in your environment if this is intentional, but that may defer real problems until later in the build.)\\\n    $(eval link_type_error := true)\n  endef\nelse\n  define link-type-missing\n    $(eval $$(1).MISSING := true)\n  endef\nendif\n\n# Verify that $(1) can link against $(2)\n# Both $(1) and $(2) are the link type prefix defined above\ndefine verify-link-type\n$(foreach t,$($(2).TYPE),\\\n  $(if $(filter-out $($(1).ALLOWED),$(t)),\\\n    $(if $(filter $(t),$($(1).WARN)),\\\n      $(call link-type-warning,$(1),$(2),$(t)),\\\n      $(call link-type-error,$(1),$(2),$(t)))))\nendef\n\n$(foreach lt,$(ALL_LINK_TYPES),\\\n  $(foreach d,$($(lt).DEPS),\\\n    $(if $($(d).TYPE),\\\n      $(call verify-link-type,$(lt),$(d)),\\\n      $(call link-type-missing,$(lt),$(d)))))\n\nifdef link_type_error\n  $(error exiting from previous errors)\nendif\n\n# -------------------------------------------------------------------\n# Handle exported/imported includes\n\n# Recursively calculate flags\n$(foreach export,$(EXPORTS_LIST), \\\n  $(eval EXPORTS.$$(export) = $$(EXPORTS.$(export).FLAGS) \\\n    $(foreach dep,$(EXPORTS.$(export).REEXPORT),$$(EXPORTS.$(dep)))))\n\n# Recursively calculate dependencies\n$(foreach export,$(EXPORTS_LIST), \\\n  $(eval EXPORT_DEPS.$$(export) = $$(EXPORTS.$(export).DEPS) \\\n    $(foreach dep,$(EXPORTS.$(export).REEXPORT),$$(EXPORT_DEPS.$(dep)))))\n\n# Converts the recursive variables to simple variables so that we don't have to\n# evaluate them for every .o rule\n$(foreach export,$(EXPORTS_LIST),$(eval EXPORTS.$$(export) := $$(strip $$(EXPORTS.$$(export)))))\n$(foreach export,$(EXPORTS_LIST),$(eval EXPORT_DEPS.$$(export) := $$(sort $$(EXPORT_DEPS.$$(export)))))\n\n# Add dependencies\n$(foreach export,$(EXPORTS_LIST),$(eval $(call add-dependency,$$(EXPORTS.$$(export).USERS),$$(EXPORT_DEPS.$$(export)))))\n\n# -------------------------------------------------------------------\n# Figure out our module sets.\n#\n# Of the modules defined by the component makefiles,\n# determine what we actually want to build.\n\n\n# Expand a list of modules to the modules that they override (if any)\n# $(1): The list of modules.\ndefine module-overrides\n$(foreach m,$(1),\\\n  $(eval _mo_overrides := $(PACKAGES.$(m).OVERRIDES) $(EXECUTABLES.$(m).OVERRIDES) $(SHARED_LIBRARIES.$(m).OVERRIDES) $(ETC.$(m).OVERRIDES))\\\n  $(if $(filter $(m),$(_mo_overrides)),\\\n    $(error Module $(m) cannot override itself),\\\n    $(_mo_overrides)))\nendef\n\n###########################################################\n## Expand a module name list with REQUIRED modules\n###########################################################\n# $(1): The variable name that holds the initial module name list.\n#       the variable will be modified to hold the expanded results.\n# $(2): The initial module name list.\n# $(3): The list of overridden modules.\n# Returns empty string (maybe with some whitespaces).\ndefine expand-required-modules\n$(eval _erm_req := $(foreach m,$(2),$(ALL_MODULES.$(m).REQUIRED_FROM_TARGET))) \\\n$(eval _erm_new_modules := $(sort $(filter-out $($(1)),$(_erm_req)))) \\\n$(eval _erm_new_overrides := $(call module-overrides,$(_erm_new_modules))) \\\n$(eval _erm_all_overrides := $(3) $(_erm_new_overrides)) \\\n$(eval _erm_new_modules := $(filter-out $(_erm_all_overrides), $(_erm_new_modules))) \\\n$(eval $(1) := $(filter-out $(_erm_new_overrides),$($(1)))) \\\n$(eval $(1) += $(_erm_new_modules)) \\\n$(if $(_erm_new_modules),\\\n  $(call expand-required-modules,$(1),$(_erm_new_modules),$(_erm_all_overrides)))\nendef\n\n# Same as expand-required-modules above, but does not handle module overrides, as\n# we don't intend to support them on the host.\n# $(1): The variable name that holds the initial module name list.\n#       the variable will be modified to hold the expanded results.\n# $(2): The initial module name list.\n# $(3): HOST or HOST_CROSS depending on whether we're expanding host or host cross modules\n# Returns empty string (maybe with some whitespaces).\ndefine expand-required-host-modules\n$(eval _erm_req := $(foreach m,$(2),$(ALL_MODULES.$(m).REQUIRED_FROM_$(3)))) \\\n$(eval _erm_new_modules := $(sort $(filter-out $($(1)),$(_erm_req)))) \\\n$(eval $(1) += $(_erm_new_modules)) \\\n$(if $(_erm_new_modules),\\\n  $(call expand-required-host-modules,$(1),$(_erm_new_modules),$(3)))\nendef\n\n# Transforms paths relative to PRODUCT_OUT to absolute paths.\n# $(1): list of relative paths\n# $(2): optional suffix to append to paths\ndefine resolve-product-relative-paths\n  $(subst $(_vendor_path_placeholder),$(TARGET_COPY_OUT_VENDOR),\\\n    $(subst $(_product_path_placeholder),$(TARGET_COPY_OUT_PRODUCT),\\\n      $(subst $(_system_ext_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_EXT),\\\n        $(subst $(_odm_path_placeholder),$(TARGET_COPY_OUT_ODM),\\\n          $(subst $(_vendor_dlkm_path_placeholder),$(TARGET_COPY_OUT_VENDOR_DLKM),\\\n            $(subst $(_odm_dlkm_path_placeholder),$(TARGET_COPY_OUT_ODM_DLKM),\\\n              $(subst $(_system_dlkm_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_DLKM),\\\n                $(foreach p,$(1),$(call append-path,$(PRODUCT_OUT),$(p)$(2))))))))))\nendef\n\n# Returns modules included automatically as a result of certain BoardConfig\n# variables being set.\ndefine auto-included-modules\n  $(foreach vndk_ver,$(PRODUCT_EXTRA_VNDK_VERSIONS),com.android.vndk.v$(vndk_ver)) \\\n  llndk.libraries.txt \\\n  $(if $(DEVICE_MANIFEST_FILE),vendor_manifest.xml) \\\n  $(if $(DEVICE_MANIFEST_SKUS),$(foreach sku, $(DEVICE_MANIFEST_SKUS),vendor_manifest_$(sku).xml)) \\\n  $(if $(ODM_MANIFEST_FILES),odm_manifest.xml) \\\n  $(if $(ODM_MANIFEST_SKUS),$(foreach sku, $(ODM_MANIFEST_SKUS),odm_manifest_$(sku).xml)) \\\n\nendef\n\n# Lists the modules particular product installs.\n# The base list of modules to build for this product is specified\n# by the appropriate product definition file, which was included\n# by product_config.mk.\n# Name resolution for PRODUCT_PACKAGES:\n#   foo:32 resolves to foo_32;\n#   foo:64 resolves to foo;\n#   foo resolves to both foo and foo_32 (if foo_32 is defined).\n#\n# Name resolution for LOCAL_REQUIRED_MODULES:\n#   See the select-bitness-of-required-modules definition.\n# $(1): product makefile\ndefine product-installed-modules\n  $(eval _pif_modules := \\\n    $(call get-product-var,$(1),PRODUCT_PACKAGES) \\\n    $(if $(filter eng,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ENG)) \\\n    $(if $(filter debug,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \\\n    $(if $(filter tests,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_TESTS)) \\\n    $(if $(filter asan,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \\\n    $(if $(filter java_coverage,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \\\n    $(if $(filter arm64,$(TARGET_ARCH) $(TARGET_2ND_ARCH)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ARM64)) \\\n    $(if $(PRODUCT_SHIPPING_API_LEVEL), \\\n      $(if $(call math_gt_or_eq,29,$(PRODUCT_SHIPPING_API_LEVEL)),$(call get-product-var,$(1),PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29)) \\\n      $(if $(call math_gt_or_eq,33,$(PRODUCT_SHIPPING_API_LEVEL)),$(call get-product-var,$(1),PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33)) \\\n      $(if $(call math_gt_or_eq,34,$(PRODUCT_SHIPPING_API_LEVEL)),$(call get-product-var,$(1),PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)) \\\n    ) \\\n    $(call auto-included-modules) \\\n  ) \\\n  $(eval ### Filter out the overridden packages and executables before doing expansion) \\\n  $(eval _pif_overrides := $(call module-overrides,$(_pif_modules))) \\\n  $(eval _pif_modules := $(filter-out $(_pif_overrides), $(_pif_modules))) \\\n  $(eval ### Resolve the :32 :64 module name) \\\n  $(eval _pif_modules := $(sort $(call resolve-bitness-for-modules,TARGET,$(_pif_modules)))) \\\n  $(call expand-required-modules,_pif_modules,$(_pif_modules),$(_pif_overrides)) \\\n  $(_pif_modules)\nendef\n\n# Lists most of the files a particular product installs.\n# It gives all the installed files for all modules returned by product-installed-modules,\n# and also includes PRODUCT_COPY_FILES.\ndefine product-installed-files\n  $(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(call product-installed-modules,$(1)))) \\\n  $(call resolve-product-relative-paths,\\\n    $(foreach cf,$(call get-product-var,$(1),PRODUCT_COPY_FILES),$(call word-colon,2,$(cf))))\nendef\n\n# Similar to product-installed-files above, but handles PRODUCT_HOST_PACKAGES instead\n# This does support the :32 / :64 syntax, but does not support module overrides.\ndefine host-installed-files\n  $(eval _hif_modules := $(call get-product-var,$(1),PRODUCT_HOST_PACKAGES)) \\\n  $(eval ### Split host vs host cross modules) \\\n  $(eval _hcif_modules := $(filter host_cross_%,$(_hif_modules))) \\\n  $(eval _hif_modules := $(filter-out host_cross_%,$(_hif_modules))) \\\n  $(eval ### Resolve the :32 :64 module name) \\\n  $(eval _hif_modules := $(sort $(call resolve-bitness-for-modules,HOST,$(_hif_modules)))) \\\n  $(eval _hcif_modules := $(sort $(call resolve-bitness-for-modules,HOST_CROSS,$(_hcif_modules)))) \\\n  $(call expand-required-host-modules,_hif_modules,$(_hif_modules),HOST) \\\n  $(call expand-required-host-modules,_hcif_modules,$(_hcif_modules),HOST_CROSS) \\\n  $(filter $(HOST_OUT)/%,$(call module-installed-files, $(_hif_modules))) \\\n  $(filter $(HOST_CROSS_OUT)/%,$(call module-installed-files, $(_hcif_modules)))\nendef\n\n# Fails the build if the given list is non-empty, and prints it entries (stripping PRODUCT_OUT).\n# $(1): list of files to print\n# $(2): heading to print on failure\ndefine maybe-print-list-and-error\n$(if $(strip $(1)), \\\n  $(warning $(2)) \\\n  $(info Offending entries:) \\\n  $(foreach e,$(sort $(1)),$(info    $(patsubst $(PRODUCT_OUT)/%,%,$(e)))) \\\n  $(error Build failed) \\\n)\nendef\n\nifeq ($(HOST_OS),darwin)\n  # Target builds are not supported on Mac\n  product_target_FILES :=\n  product_host_FILES := $(call host-installed-files,$(INTERNAL_PRODUCT))\nelse ifdef FULL_BUILD\n  ifneq (true,$(ALLOW_MISSING_DEPENDENCIES))\n    # Check to ensure that all modules in PRODUCT_PACKAGES exist (opt in per product)\n    ifeq (true,$(PRODUCT_ENFORCE_PACKAGES_EXIST))\n      _allow_list := $(PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST)\n      _modules := $(PRODUCT_PACKAGES)\n      # Strip :32 and :64 suffixes\n      _modules := $(patsubst %:32,%,$(_modules))\n      _modules := $(patsubst %:64,%,$(_modules))\n      # Quickly check all modules in PRODUCT_PACKAGES exist. We check for the\n      # existence if either <module> or the <module>_32 variant.\n      _nonexistent_modules := $(foreach m,$(_modules), \\\n        $(if $(or $(ALL_MODULES.$(m).PATH),$(call get-modules-for-2nd-arch,TARGET,$(m))),,$(m)))\n      $(call maybe-print-list-and-error,$(filter-out $(_allow_list),$(_nonexistent_modules)),\\\n        $(INTERNAL_PRODUCT) includes non-existent modules in PRODUCT_PACKAGES)\n      # TODO(b/182105280): Consider re-enabling this check when the ART modules\n      # have been cleaned up from the allowed_list in target/product/generic.mk.\n      #$(call maybe-print-list-and-error,$(filter-out $(_nonexistent_modules),$(_allow_list)),\\\n      #  $(INTERNAL_PRODUCT) includes redundant allow list entries for non-existent PRODUCT_PACKAGES)\n    endif\n\n    # Check to ensure that all modules in PRODUCT_HOST_PACKAGES exist\n    #\n    # Many host modules are Linux-only, so skip this check on Mac. If we ever have Mac-only modules,\n    # maybe it would make sense to have PRODUCT_HOST_PACKAGES_LINUX/_DARWIN?\n    ifneq ($(HOST_OS),darwin)\n      _modules := $(PRODUCT_HOST_PACKAGES)\n      # Strip :32 and :64 suffixes\n      _modules := $(patsubst %:32,%,$(_modules))\n      _modules := $(patsubst %:64,%,$(_modules))\n      _nonexistent_modules := $(foreach m,$(_modules),\\\n        $(if $(ALL_MODULES.$(m).REQUIRED_FROM_HOST)$(filter $(HOST_OUT_ROOT)/%,$(ALL_MODULES.$(m).INSTALLED)),,$(m)))\n      $(call maybe-print-list-and-error,$(_nonexistent_modules),\\\n        $(INTERNAL_PRODUCT) includes non-existent modules in PRODUCT_HOST_PACKAGES)\n    endif\n  endif\n\n  # Modules may produce only host installed files in unbundled builds.\n  ifeq (,$(TARGET_BUILD_UNBUNDLED))\n    _modules := $(call resolve-bitness-for-modules,TARGET, \\\n      $(PRODUCT_PACKAGES) \\\n      $(PRODUCT_PACKAGES_DEBUG) \\\n      $(PRODUCT_PACKAGES_DEBUG_ASAN) \\\n      $(PRODUCT_PACKAGES_ENG) \\\n      $(PRODUCT_PACKAGES_TESTS))\n    _host_modules := $(foreach m,$(_modules), \\\n                  $(if $(ALL_MODULES.$(m).INSTALLED),\\\n                    $(if $(filter-out $(HOST_OUT_ROOT)/%,$(ALL_MODULES.$(m).INSTALLED)),,\\\n                      $(m))))\n    ifeq ($(TARGET_ARCH),riscv64)\n      # HACK: riscv64 can't build the device version of bcc and ld.mc due to a\n      # dependency on an old version of LLVM, but they are listed in\n      # base_system.mk which can't add them conditionally based on the target\n      # architecture.\n      _host_modules := $(filter-out bcc ld.mc,$(_host_modules))\n    endif\n    $(call maybe-print-list-and-error,$(sort $(_host_modules)),\\\n      Host modules should be in PRODUCT_HOST_PACKAGES$(comma) not PRODUCT_PACKAGES)\n  endif\n\n  product_host_FILES := $(call host-installed-files,$(INTERNAL_PRODUCT))\n  product_target_FILES := $(call product-installed-files, $(INTERNAL_PRODUCT))\n  # WARNING: The product_MODULES variable is depended on by external files.\n  # It contains the list of register names that will be installed on the device\n  product_MODULES := $(_pif_modules)\n\n  # Verify the artifact path requirements made by included products.\n  is_asan := $(if $(filter address,$(SANITIZE_TARGET)),true)\n  ifeq (,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS)))\n    include $(BUILD_SYSTEM)/artifact_path_requirements.mk\n  endif\nelse\n  # We're not doing a full build, and are probably only including\n  # a subset of the module makefiles.  Don't try to build any modules\n  # requested by the product, because we probably won't have rules\n  # to build them.\n  product_target_FILES :=\n  product_host_FILES :=\nendif\n\n# TODO: Remove the 3 places in the tree that use ALL_DEFAULT_INSTALLED_MODULES\n# and get rid of it from this list.\nmodules_to_install := $(sort \\\n    $(ALL_DEFAULT_INSTALLED_MODULES) \\\n    $(product_target_FILES) \\\n    $(product_host_FILES) \\\n    $(CUSTOM_MODULES) \\\n  )\n\n# Deduplicate compatibility suite dist files across modules and packages before\n# copying them to their requested locations. Assign the eval result to an unused\n# var to prevent Make from trying to make a sense of it.\n_unused := $(call copy-many-files, $(sort $(ALL_COMPATIBILITY_DIST_FILES)))\n\nifdef is_sdk_build\n  # Ensure every module listed in PRODUCT_PACKAGES* gets something installed\n  # TODO: Should we do this for all builds and not just the sdk?\n  dangling_modules :=\n  $(foreach m, $(PRODUCT_PACKAGES), \\\n    $(if $(strip $(ALL_MODULES.$(m).INSTALLED) $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).INSTALLED)),,\\\n      $(eval dangling_modules += $(m))))\n  ifneq ($(dangling_modules),)\n    $(warning: Modules '$(dangling_modules)' in PRODUCT_PACKAGES have nothing to install!)\n  endif\n  $(foreach m, $(PRODUCT_PACKAGES_DEBUG), \\\n    $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\\\n      $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_DEBUG has nothing to install!)))\n  $(foreach m, $(PRODUCT_PACKAGES_ENG), \\\n    $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\\\n      $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_ENG has nothing to install!)))\n  $(foreach m, $(PRODUCT_PACKAGES_TESTS), \\\n    $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\\\n      $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_TESTS has nothing to install!)))\nendif\n\nifneq ($(TARGET_BUILD_APPS),)\n  # If this build is just for apps, only build apps and not the full system by default.\n  ifneq ($(filter all,$(TARGET_BUILD_APPS)),)\n    # The magic goal \"all\" used to build all apps in the source tree. This was deprecated\n    # so that we can know all TARGET_BUILD_APPS apps are built with soong for soong-only builds.\n    $(error TARGET_BUILD_APPS=all is deprecated)\n  else\n    unbundled_build_modules := $(sort $(TARGET_BUILD_APPS))\n  endif\nendif\n\n# build/make/core/Makefile contains extra stuff that we don't want to pollute this\n# top-level makefile with.  It expects that ALL_DEFAULT_INSTALLED_MODULES\n# contains everything that's built during the current make, but it also further\n# extends ALL_DEFAULT_INSTALLED_MODULES.\nALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)\nifeq ($(HOST_OS),linux)\n  include $(BUILD_SYSTEM)/Makefile\nendif\nmodules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))\nALL_DEFAULT_INSTALLED_MODULES :=\n\nifdef FULL_BUILD\n#\n# Used by the cleanup logic in soong_ui to remove files that should no longer\n# be installed.\n#\n\n# Include all tests, so that we remove them from the test suites / testcase\n# folders when they are removed.\ntest_files := $(foreach ts,$(ALL_COMPATIBILITY_SUITES),$(COMPATIBILITY.$(ts).FILES))\n\n$(shell mkdir -p $(PRODUCT_OUT) $(HOST_OUT))\n\n$(file >$(PRODUCT_OUT)/.installable_files$(if $(filter address,$(SANITIZE_TARGET)),_asan), \\\n  $(sort $(patsubst $(PRODUCT_OUT)/%,%,$(filter $(PRODUCT_OUT)/%, \\\n    $(modules_to_install) $(test_files)))))\n\n$(file >$(HOST_OUT)/.installable_test_files,$(sort \\\n  $(patsubst $(HOST_OUT)/%,%,$(filter $(HOST_OUT)/%, \\\n    $(test_files)))))\n\ntest_files :=\nendif\n\n# Some notice deps refer to module names without prefix or arch suffix where\n# only the variants with them get built.\n# fix-notice-deps replaces those unadorned module names with every built variant.\n$(call fix-notice-deps)\n\n# These are additional goals that we build, in order to make sure that there\n# is as little code as possible in the tree that doesn't build.\nmodules_to_check := $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).CHECKED))\n\n# If you would like to build all goals, and not skip any intermediate\n# steps, you can pass the \"all\" modifier goal on the commandline.\nifneq ($(filter all,$(MAKECMDGOALS)),)\nmodules_to_check += $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).BUILT))\nendif\n\n# Build docs as part of checkbuild to catch more breakages.\nmodules_to_check += $(ALL_DOCS)\n\n# for easier debugging\nmodules_to_check := $(sort $(modules_to_check))\n#$(error modules_to_check $(modules_to_check))\n\n# -------------------------------------------------------------------\n# This is used to to get the ordering right, you can also use these,\n# but they're considered undocumented, so don't complain if their\n# behavior changes.\n# An internal target that depends on all copied headers\n# (see copy_headers.make).  Other targets that need the\n# headers to be copied first can depend on this target.\n.PHONY: all_copied_headers\nall_copied_headers: ;\n\n$(ALL_C_CPP_ETC_OBJECTS): | all_copied_headers\n\n# All the droid stuff, in directories\n.PHONY: files\nfiles: $(modules_to_install) \\\n       $(INSTALLED_ANDROID_INFO_TXT_TARGET)\n\n# -------------------------------------------------------------------\n\n.PHONY: checkbuild\ncheckbuild: $(modules_to_check) droid_targets check-elf-files\n\nifeq (true,$(ANDROID_BUILD_EVERYTHING_BY_DEFAULT))\ndroid: checkbuild\nendif\n\n.PHONY: ramdisk\nramdisk: $(INSTALLED_RAMDISK_TARGET)\n\n.PHONY: ramdisk_debug\nramdisk_debug: $(INSTALLED_DEBUG_RAMDISK_TARGET)\n\n.PHONY: ramdisk_test_harness\nramdisk_test_harness: $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)\n\n.PHONY: userdataimage\nuserdataimage: $(INSTALLED_USERDATAIMAGE_TARGET)\n\nifneq (,$(filter userdataimage, $(MAKECMDGOALS)))\n$(call dist-for-goals, userdataimage, $(BUILT_USERDATAIMAGE_TARGET))\nendif\n\n.PHONY: cacheimage\ncacheimage: $(INSTALLED_CACHEIMAGE_TARGET)\n\n.PHONY: bptimage\nbptimage: $(INSTALLED_BPTIMAGE_TARGET)\n\n.PHONY: vendorimage\nvendorimage: $(INSTALLED_VENDORIMAGE_TARGET)\n\n.PHONY: vendorbootimage\nvendorbootimage: $(INSTALLED_VENDOR_BOOTIMAGE_TARGET)\n\n.PHONY: vendorkernelbootimage\nvendorkernelbootimage: $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET)\n\n.PHONY: vendorbootimage_debug\nvendorbootimage_debug: $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET)\n\n.PHONY: vendorbootimage_test_harness\nvendorbootimage_test_harness: $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET)\n\n.PHONY: vendorramdisk\nvendorramdisk: $(INSTALLED_VENDOR_RAMDISK_TARGET)\n\n.PHONY: vendorkernelramdisk\nvendorkernelramdisk: $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET)\n\n.PHONY: vendorramdisk_debug\nvendorramdisk_debug: $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET)\n\n.PHONY: vendorramdisk_test_harness\nvendorramdisk_test_harness: $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET)\n\n.PHONY: productimage\nproductimage: $(INSTALLED_PRODUCTIMAGE_TARGET)\n\n.PHONY: systemextimage\nsystemextimage: $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)\n\n.PHONY: odmimage\nodmimage: $(INSTALLED_ODMIMAGE_TARGET)\n\n.PHONY: vendor_dlkmimage\nvendor_dlkmimage: $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)\n\n.PHONY: odm_dlkmimage\nodm_dlkmimage: $(INSTALLED_ODM_DLKMIMAGE_TARGET)\n\n.PHONY: system_dlkmimage\nsystem_dlkmimage: $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)\n\n.PHONY: systemotherimage\nsystemotherimage: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET)\n\n.PHONY: superimage_empty\nsuperimage_empty: $(INSTALLED_SUPERIMAGE_EMPTY_TARGET)\n\n.PHONY: bootimage\nbootimage: $(INSTALLED_BOOTIMAGE_TARGET)\n\n.PHONY: initbootimage\ninitbootimage: $(INSTALLED_INIT_BOOT_IMAGE_TARGET)\n\nifeq (true,$(PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST))\n$(call dist-for-goals, bootimage, $(INSTALLED_BOOTIMAGE_TARGET))\nendif\n\n.PHONY: bootimage_debug\nbootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)\n\n.PHONY: bootimage_test_harness\nbootimage_test_harness: $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET)\n\n.PHONY: vbmetaimage\nvbmetaimage: $(INSTALLED_VBMETAIMAGE_TARGET)\n\n.PHONY: vbmetasystemimage\nvbmetasystemimage: $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET)\n\n.PHONY: vbmetavendorimage\nvbmetavendorimage: $(INSTALLED_VBMETA_VENDORIMAGE_TARGET)\n\n.PHONY: vbmetacustomimages\nvbmetacustomimages: $(foreach partition,$(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)),$(INSTALLED_VBMETA_$(partition)IMAGE_TARGET))\n\n# The droidcore-unbundled target depends on the subset of targets necessary to\n# perform a full system build (either unbundled or not).\n.PHONY: droidcore-unbundled\ndroidcore-unbundled: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \\\n    $(INSTALLED_FILES_OUTSIDE_IMAGES) \\\n    $(INSTALLED_SYSTEMIMAGE_TARGET) \\\n    $(INSTALLED_RAMDISK_TARGET) \\\n    $(INSTALLED_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \\\n    $(INSTALLED_DTBOIMAGE_TARGET) \\\n    $(INSTALLED_RADIOIMAGE_TARGET) \\\n    $(INSTALLED_DEBUG_RAMDISK_TARGET) \\\n    $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_RECOVERYIMAGE_TARGET) \\\n    $(INSTALLED_VBMETAIMAGE_TARGET) \\\n    $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) \\\n    $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) \\\n    $(INSTALLED_USERDATAIMAGE_TARGET) \\\n    $(INSTALLED_CACHEIMAGE_TARGET) \\\n    $(INSTALLED_BPTIMAGE_TARGET) \\\n    $(INSTALLED_VENDORIMAGE_TARGET) \\\n    $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) \\\n    $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_VENDOR_RAMDISK_TARGET) \\\n    $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) \\\n    $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) \\\n    $(INSTALLED_ODMIMAGE_TARGET) \\\n    $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \\\n    $(INSTALLED_ODM_DLKMIMAGE_TARGET) \\\n    $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \\\n    $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \\\n    $(INSTALLED_PRODUCTIMAGE_TARGET) \\\n    $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \\\n    $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \\\n    $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \\\n    $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_FILES_FILE) \\\n    $(INSTALLED_FILES_JSON) \\\n    $(INSTALLED_FILES_FILE_VENDOR) \\\n    $(INSTALLED_FILES_JSON_VENDOR) \\\n    $(INSTALLED_FILES_FILE_ODM) \\\n    $(INSTALLED_FILES_JSON_ODM) \\\n    $(INSTALLED_FILES_FILE_VENDOR_DLKM) \\\n    $(INSTALLED_FILES_JSON_VENDOR_DLKM) \\\n    $(INSTALLED_FILES_FILE_ODM_DLKM) \\\n    $(INSTALLED_FILES_JSON_ODM_DLKM) \\\n    $(INSTALLED_FILES_FILE_SYSTEM_DLKM) \\\n    $(INSTALLED_FILES_JSON_SYSTEM_DLKM) \\\n    $(INSTALLED_FILES_FILE_PRODUCT) \\\n    $(INSTALLED_FILES_JSON_PRODUCT) \\\n    $(INSTALLED_FILES_FILE_SYSTEM_EXT) \\\n    $(INSTALLED_FILES_JSON_SYSTEM_EXT) \\\n    $(INSTALLED_FILES_FILE_SYSTEMOTHER) \\\n    $(INSTALLED_FILES_JSON_SYSTEMOTHER) \\\n    $(INSTALLED_FILES_FILE_RAMDISK) \\\n    $(INSTALLED_FILES_JSON_RAMDISK) \\\n    $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) \\\n    $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) \\\n    $(INSTALLED_FILES_FILE_VENDOR_RAMDISK) \\\n    $(INSTALLED_FILES_JSON_VENDOR_RAMDISK) \\\n    $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) \\\n    $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK) \\\n    $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK) \\\n    $(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK) \\\n    $(INSTALLED_FILES_FILE_ROOT) \\\n    $(INSTALLED_FILES_JSON_ROOT) \\\n    $(INSTALLED_FILES_FILE_RECOVERY) \\\n    $(INSTALLED_FILES_JSON_RECOVERY) \\\n    $(INSTALLED_ANDROID_INFO_TXT_TARGET)\n\n# The droidcore target depends on the droidcore-unbundled subset and any other\n# targets for a non-unbundled (full source) full system build.\n.PHONY: droidcore\ndroidcore: droidcore-unbundled\n\n# dist_files only for putting your library into the dist directory with a full build.\n.PHONY: dist_files\n\n$(call dist-for-goals, dist_files, $(PRODUCT_OUT)/module-info.json)\n\n.PHONY: apps_only\nifeq ($(HOST_OS),darwin)\n  # Mac only supports building host modules\n  droid_targets: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) dist_files\n\nelse ifneq ($(TARGET_BUILD_APPS),)\n  # If this build is just for apps, only build apps and not the full system by default.\n\n  # Dist the installed files if they exist, except the installed symlinks. dist-for-goals emits\n  # `cp src dest` commands, which will fail to copy dangling symlinks.\n  apps_only_installed_files := $(foreach m,$(unbundled_build_modules),\\\n    $(filter-out $(ALL_MODULES.$(m).INSTALLED_SYMLINKS),$(ALL_MODULES.$(m).INSTALLED)))\n  $(call dist-for-goals,apps_only, $(apps_only_installed_files))\n\n  # Dist the bundle files if they exist.\n  apps_only_bundle_files := $(foreach m,$(unbundled_build_modules),\\\n    $(if $(ALL_MODULES.$(m).BUNDLE),$(ALL_MODULES.$(m).BUNDLE):$(m)-base.zip))\n  $(call dist-for-goals,apps_only, $(apps_only_bundle_files))\n\n  # Dist the lint reports if they exist.\n  apps_only_lint_report_files := $(foreach m,$(unbundled_build_modules),\\\n    $(foreach report,$(ALL_MODULES.$(m).LINT_REPORTS),\\\n      $(report):$(m)-$(notdir $(report))))\n  .PHONY: lint-check\n  lint-check: $(foreach f, $(apps_only_lint_report_files), $(call word-colon,1,$(f)))\n  $(call dist-for-goals,lint-check, $(apps_only_lint_report_files))\n\n  # For uninstallable modules such as static Java library, we have to dist the built file,\n  # as <module_name>.<suffix>\n  apps_only_dist_built_files := $(foreach m,$(unbundled_build_modules),$(if $(ALL_MODULES.$(m).INSTALLED),,\\\n      $(if $(ALL_MODULES.$(m).BUILT),$(ALL_MODULES.$(m).BUILT):$(m)$(suffix $(ALL_MODULES.$(m).BUILT)))\\\n      $(if $(ALL_MODULES.$(m).AAR),$(ALL_MODULES.$(m).AAR):$(m).aar)\\\n      ))\n  $(call dist-for-goals,apps_only, $(apps_only_dist_built_files))\n\n  ifeq ($(EMMA_INSTRUMENT),true)\n    $(JACOCO_REPORT_CLASSES_ALL) : $(apps_only_installed_files)\n    $(call dist-for-goals,apps_only, $(JACOCO_REPORT_CLASSES_ALL))\n  endif\n\n  $(PROGUARD_DICT_ZIP) : $(apps_only_installed_files)\n  $(call dist-for-goals-with-filenametag,apps_only, $(PROGUARD_DICT_ZIP) $(PROGUARD_DICT_ZIP) $(PROGUARD_DICT_MAPPING))\n  $(call declare-container-license-deps,$(PROGUARD_DICT_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/)\n\n  $(PROGUARD_USAGE_ZIP) : $(apps_only_installed_files)\n  $(call dist-for-goals-with-filenametag,apps_only, $(PROGUARD_USAGE_ZIP))\n  $(call declare-container-license-deps,$(PROGUARD_USAGE_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/)\n\n  $(SYMBOLS_ZIP) : $(apps_only_installed_files)\n  $(call dist-for-goals-with-filenametag,apps_only, $(SYMBOLS_ZIP) $(SYMBOLS_MAPPING))\n  $(call declare-container-license-deps,$(SYMBOLS_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/)\n\n  $(COVERAGE_ZIP) : $(apps_only_installed_files)\n  $(call dist-for-goals,apps_only, $(COVERAGE_ZIP))\n  $(call declare-container-license-deps,$(COVERAGE_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/)\n\napps_only: $(unbundled_build_modules)\n\ndroid_targets: apps_only\n\n# NOTICE files for a apps_only build\n$(eval $(call html-notice-rule,$(target_notice_file_html_or_xml),\"Apps\",\"Notices for files for apps:\",$(unbundled_build_modules),$(PRODUCT_OUT)/ $(HOST_OUT)/))\n\n$(eval $(call text-notice-rule,$(target_notice_file_txt),\"Apps\",\"Notices for files for apps:\",$(unbundled_build_modules),$(PRODUCT_OUT)/ $(HOST_OUT)/))\n\n$(call declare-0p-target,$(target_notice_file_txt))\n$(call declare-0p-target,$(target_notice_html_or_xml))\n\n\nelse ifeq ($(TARGET_BUILD_UNBUNDLED),$(TARGET_BUILD_UNBUNDLED_IMAGE))\n\n  # Truth table for entering this block of code:\n  # TARGET_BUILD_UNBUNDLED | TARGET_BUILD_UNBUNDLED_IMAGE | Action\n  # -----------------------|------------------------------|-------------------------\n  # not set                | not set                      | droidcore path\n  # not set                | true                         | invalid\n  # true                   | not set                      | skip\n  # true                   | true                         | droidcore-unbundled path\n\n  # We dist the following targets only for droidcore full build. These items\n  # can include java-related targets that would cause building framework java\n  # sources in a droidcore full build.\n\n  $(call dist-for-goals, droidcore, \\\n    $(APPCOMPAT_ZIP) \\\n  )\n\n  # We dist the following targets for droidcore-unbundled (and droidcore since\n  # droidcore depends on droidcore-unbundled). The droidcore-unbundled target\n  # is a subset of droidcore. It can be used used for an unbundled build to\n  # avoid disting targets that would cause building framework java sources,\n  # which we want to avoid in an unbundled build.\n\n  $(call dist-for-goals-with-filenametag, droidcore-unbundled, \\\n    $(INTERNAL_UPDATE_PACKAGE_TARGET) \\\n    $(INTERNAL_OTA_PACKAGE_TARGET) \\\n    $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET) \\\n    $(BUILT_RAMDISK_16K_TARGET) \\\n    $(BUILT_KERNEL_16K_TARGET) \\\n    $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET) \\\n    $(SYMBOLS_ZIP) \\\n    $(SYMBOLS_MAPPING) \\\n    $(PROGUARD_DICT_ZIP) \\\n    $(PROGUARD_DICT_MAPPING) \\\n    $(PROGUARD_USAGE_ZIP) \\\n    $(BUILT_TARGET_FILES_PACKAGE) \\\n  )\n\n  $(call dist-for-goals, droidcore-unbundled, \\\n    $(INTERNAL_OTA_METADATA) \\\n    $(COVERAGE_ZIP) \\\n    $(INSTALLED_FILES_FILE) \\\n    $(INSTALLED_FILES_JSON) \\\n    $(INSTALLED_FILES_FILE_VENDOR) \\\n    $(INSTALLED_FILES_JSON_VENDOR) \\\n    $(INSTALLED_FILES_FILE_ODM) \\\n    $(INSTALLED_FILES_JSON_ODM) \\\n    $(INSTALLED_FILES_FILE_VENDOR_DLKM) \\\n    $(INSTALLED_FILES_JSON_VENDOR_DLKM) \\\n    $(INSTALLED_FILES_FILE_ODM_DLKM) \\\n    $(INSTALLED_FILES_JSON_ODM_DLKM) \\\n    $(INSTALLED_FILES_FILE_SYSTEM_DLKM) \\\n    $(INSTALLED_FILES_JSON_SYSTEM_DLKM) \\\n    $(INSTALLED_FILES_FILE_PRODUCT) \\\n    $(INSTALLED_FILES_JSON_PRODUCT) \\\n    $(INSTALLED_FILES_FILE_SYSTEM_EXT) \\\n    $(INSTALLED_FILES_JSON_SYSTEM_EXT) \\\n    $(INSTALLED_FILES_FILE_SYSTEMOTHER) \\\n    $(INSTALLED_FILES_JSON_SYSTEMOTHER) \\\n    $(INSTALLED_FILES_FILE_RECOVERY) \\\n    $(INSTALLED_FILES_JSON_RECOVERY) \\\n    $(if $(BUILDING_VENDOR_IMAGE), $(INSTALLED_VENDOR_BUILD_PROP_TARGET):build.prop-vendor) \\\n    $(INSTALLED_ANDROID_INFO_TXT_TARGET) \\\n    $(INSTALLED_MISC_INFO_TARGET) \\\n    $(INSTALLED_RAMDISK_TARGET) \\\n    $(DEXPREOPT_CONFIG_ZIP) \\\n  )\n\n  # Put a copy of the radio/bootloader files in the dist dir.\n  $(foreach f,$(INSTALLED_RADIOIMAGE_TARGET), \\\n    $(call dist-for-goals, droidcore-unbundled, $(f)))\n\n  ifneq ($(ANDROID_BUILD_EMBEDDED),true)\n    $(call dist-for-goals-with-filenametag, droidcore, \\\n      $(INTERNAL_EMULATOR_PACKAGE_TARGET) \\\n    )\n  endif\n\n  $(call dist-for-goals, droidcore-unbundled, \\\n    $(INSTALLED_FILES_FILE_ROOT) \\\n    $(INSTALLED_FILES_JSON_ROOT) \\\n  )\n\n  $(call dist-for-goals, droidcore-unbundled, \\\n    $(INSTALLED_FILES_FILE_RAMDISK) \\\n    $(INSTALLED_FILES_JSON_RAMDISK) \\\n    $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) \\\n    $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) \\\n    $(INSTALLED_FILES_FILE_VENDOR_RAMDISK) \\\n    $(INSTALLED_FILES_JSON_VENDOR_RAMDISK) \\\n    $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK) \\\n    $(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK) \\\n    $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) \\\n    $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK) \\\n    $(INSTALLED_DEBUG_RAMDISK_TARGET) \\\n    $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \\\n    $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) \\\n    $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_VENDOR_RAMDISK_TARGET) \\\n    $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) \\\n    $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) \\\n  )\n\n  ifeq ($(PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST),true)\n    $(call dist-for-goals, droidcore-unbundled, $(INSTALLED_BOOTIMAGE_TARGET))\n  endif\n\n  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)\n    $(call dist-for-goals, droidcore-unbundled, \\\n      $(recovery_ramdisk) \\\n    )\n  endif\n\n  ifeq ($(EMMA_INSTRUMENT),true)\n    $(call dist-for-goals, dist_files, $(JACOCO_REPORT_CLASSES_ALL))\n  endif\n\n  ifdef CLANG_COVERAGE\n    $(foreach f,$(SOONG_NDK_API_XML), \\\n        $(call dist-for-goals,droidcore,$(f):ndk_apis/$(notdir $(f))))\n    $(foreach f,$(SOONG_CC_API_XML), \\\n        $(call dist-for-goals,droidcore,$(f):cc_apis/$(notdir $(f))))\n  endif\n\n  # For full system build (whether unbundled or not), we configure\n  # droid_targets to depend on droidcore-unbundled, which will set up the full\n  # system dependencies and also dist the subset of targets that correspond to\n  # an unbundled build (exclude building some framework sources).\n\n  droid_targets: droidcore-unbundled\n\n  ifeq (,$(TARGET_BUILD_UNBUNDLED_IMAGE))\n\n    # If we're building a full system (including the framework sources excluded\n    # by droidcore-unbundled), we configure droid_targets also to depend on\n    # droidcore, which includes all dist for droidcore, and will build the\n    # necessary framework sources.\n\n    droid_targets: droidcore dist_files\n\n  endif\n\nendif # TARGET_BUILD_UNBUNDLED == TARGET_BUILD_UNBUNDLED_IMAGE\n\n.PHONY: docs\ndocs: $(ALL_DOCS)\n\n.PHONY: sdk sdk_addon\nifeq ($(HOST_OS),linux)\nALL_SDK_TARGETS := $(INTERNAL_SDK_TARGET)\nsdk: $(ALL_SDK_TARGETS)\n$(call dist-for-goals-with-filenametag,sdk,$(ALL_SDK_TARGETS))\nendif\n\n# umbrella targets to assit engineers in verifying builds\n.PHONY: java native target host java-host java-target native-host native-target \\\n        java-host-tests java-target-tests native-host-tests native-target-tests \\\n        java-tests native-tests host-tests target-tests tests java-dex \\\n        native-host-cross\n# some synonyms\n.PHONY: host-java target-java host-native target-native \\\n        target-java-tests target-native-tests\nhost-java : java-host\ntarget-java : java-target\nhost-native : native-host\ntarget-native : native-target\ntarget-java-tests : java-target-tests\ntarget-native-tests : native-target-tests\ntests : host-tests target-tests\n\n# Phony target to run all java compilations that use javac\n.PHONY: javac-check\n\n.PHONY: findbugs\nfindbugs: $(INTERNAL_FINDBUGS_HTML_TARGET) $(INTERNAL_FINDBUGS_XML_TARGET)\n\n.PHONY: check-elf-files\ncheck-elf-files:\n\n.PHONY: dump-files\ndump-files:\n\t@echo \"Target files for $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT) ($(INTERNAL_PRODUCT)):\"\n\t@echo $(sort $(patsubst $(PRODUCT_OUT)/%,%,$(filter $(PRODUCT_OUT)/%,$(modules_to_install)))) | tr -s ' ' '\\n'\n\t@echo Successfully dumped product target file list.\n\n.PHONY: tidy_only\ntidy_only:\n\t@echo Successfully make tidy_only.\n\nndk: $(SOONG_OUT_DIR)/ndk.timestamp\n.PHONY: ndk\n\n# Checks that allowed_deps.txt remains up to date\nifneq ($(UNSAFE_DISABLE_APEX_ALLOWED_DEPS_CHECK),true)\n  droidcore: ${APEX_ALLOWED_DEPS_CHECK}\nendif\n\n# Create a license metadata rule per module. Could happen in base_rules.mk or\n# notice_files.mk; except, it has to happen after fix-notice-deps to avoid\n# missing dependency errors.\n$(call build-license-metadata)\n\n# Generate SBOM in SPDX format\nproduct_copy_files_without_owner := $(foreach pcf,$(PRODUCT_COPY_FILES),$(call word-colon,1,$(pcf)):$(call word-colon,2,$(pcf)))\nifeq ($(TARGET_BUILD_APPS),)\ndest_files_without_source := $(sort $(foreach pcf,$(product_copy_files_without_owner),$(if $(wildcard $(call word-colon,1,$(pcf))),,$(call word-colon,2,$(pcf)))))\ndest_files_without_source := $(addprefix $(PRODUCT_OUT)/,$(dest_files_without_source))\nfilter_out_files := \\\n  $(PRODUCT_OUT)/apex/% \\\n  $(PRODUCT_OUT)/fake_packages/% \\\n  $(PRODUCT_OUT)/testcases/% \\\n  $(dest_files_without_source) \\\n  $(PRODUCT_OUT)/required_images\n# Check if each partition image is built, if not filter out all its installed files\n# Also check if a partition uses prebuilt image file, save the info if prebuilt image is used.\nPREBUILT_PARTITION_COPY_FILES :=\n# product.img\nifndef BUILDING_PRODUCT_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/product/%\nifdef BOARD_PREBUILT_PRODUCTIMAGE\nPREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_PRODUCTIMAGE):$(INSTALLED_PRODUCTIMAGE_TARGET)\nendif\nendif\n\n# system.img\nifndef BUILDING_SYSTEM_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/system/%\nendif\n# system_dlkm.img\nifndef BUILDING_SYSTEM_DLKM_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/system_dlkm/%\nifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE\nPREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_SYSTEM_DLKMIMAGE):$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)\nendif\nendif\n# system_ext.img\nifndef BUILDING_SYSTEM_EXT_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/system_ext/%\nifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE\nPREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_SYSTEM_EXTIMAGE):$(INSTALLED_SYSTEM_EXTIMAGE_TARGET)\nendif\nendif\n# system_other.img\nifndef BUILDING_SYSTEM_OTHER_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/system_other/%\nendif\n\n# odm.img\nifndef BUILDING_ODM_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/odm/%\nifdef BOARD_PREBUILT_ODMIMAGE\nPREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_ODMIMAGE):$(INSTALLED_ODMIMAGE_TARGET)\nendif\nendif\n# odm_dlkm.img\nifndef BUILDING_ODM_DLKM_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/odm_dlkm/%\nifdef BOARD_PREBUILT_ODM_DLKMIMAGE\nPREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_ODM_DLKMIMAGE):$(INSTALLED_ODM_DLKMIMAGE_TARGET)\nendif\nendif\n\n# vendor.img\nifndef BUILDING_VENDOR_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/vendor/%\nifdef BOARD_PREBUILT_VENDORIMAGE\nPREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_VENDORIMAGE):$(INSTALLED_VENDORIMAGE_TARGET)\nendif\nendif\n# vendor_dlkm.img\nifndef BUILDING_VENDOR_DLKM_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/vendor_dlkm/%\nifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE\nPREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_VENDOR_DLKMIMAGE):$(INSTALLED_VENDOR_DLKMIMAGE_TARGET)\nendif\nendif\n\n# cache.img\nifndef BUILDING_CACHE_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/cache/%\nendif\n\n# boot.img\nifndef BUILDING_BOOT_IMAGE\nifdef BOARD_PREBUILT_BOOTIMAGE\nPREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_BOOTIMAGE):$(INSTALLED_BOOTIMAGE_TARGET)\nendif\nendif\n# init_boot.img\nifndef BUILDING_INIT_BOOT_IMAGE\nifdef BOARD_PREBUILT_INIT_BOOT_IMAGE\nPREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_INIT_BOOT_IMAGE):$(INSTALLED_INIT_BOOT_IMAGE_TARGET)\nendif\nendif\n\n# ramdisk.img\nifndef BUILDING_RAMDISK_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/ramdisk/%\nendif\n\n# recovery.img\nifndef INSTALLED_RECOVERYIMAGE_TARGET\nfilter_out_files += $(PRODUCT_OUT)/recovery/%\nendif\n\n# userdata.img\nifndef BUILDING_USERDATA_IMAGE\nfilter_out_files += $(PRODUCT_OUT)/data/%\nendif\n\ninstalled_files := $(sort $(filter-out $(filter_out_files),$(filter $(PRODUCT_OUT)/%,$(modules_to_install))))\nelse\ninstalled_files := $(apps_only_installed_files)\nendif  # TARGET_BUILD_APPS\n\nmetadata_list := $(OUT_DIR)/.module_paths/METADATA.list\nmetadata_files := $(subst $(newline),$(space),$(file <$(metadata_list)))\n\n# Create metadata for compliance support in Soong\n.PHONY: make-compliance-metadata\nmake-compliance-metadata: \\\n    $(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-metadata.csv \\\n    $(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-modules.csv\n\n$(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-metadata.csv:\n\trm -f $@\n\techo 'installed_file,module_path,is_soong_module,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,static_libs,whole_static_libs,license_text' >> $@\n\t$(foreach f,$(installed_files),\\\n\t  $(eval _module_name := $(ALL_INSTALLED_FILES.$f)) \\\n\t  $(eval _path_on_device := $(patsubst $(PRODUCT_OUT)/%,%,$f)) \\\n\t  $(eval _build_output_path := $(PRODUCT_OUT)/$(_path_on_device)) \\\n\t  $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \\\n\t  $(eval _is_soong_module := $(ALL_MODULES.$(_module_name).IS_SOONG_MODULE)) \\\n\t  $(eval _is_prebuilt_make_module := $(ALL_MODULES.$(_module_name).IS_PREBUILT_MAKE_MODULE)) \\\n\t  $(eval _product_copy_files := $(sort $(filter %:$(_path_on_device),$(product_copy_files_without_owner)))) \\\n\t  $(eval _kernel_module_copy_files := $(sort $(filter %$(_path_on_device),$(KERNEL_MODULE_COPY_FILES)))) \\\n\t  $(eval _is_build_prop := $(call is-build-prop,$f)) \\\n\t  $(eval _is_notice_file := $(call is-notice-file,$f)) \\\n\t  $(eval _is_product_system_other_avbkey := $(if $(findstring $f,$(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET)),Y)) \\\n\t  $(eval _is_event_log_tags_file := $(if $(findstring $f,$(event_log_tags_file)),Y)) \\\n\t  $(eval _is_system_other_odex_marker := $(if $(findstring $f,$(INSTALLED_SYSTEM_OTHER_ODEX_MARKER)),Y)) \\\n\t  $(eval _is_kernel_modules_blocklist := $(if $(findstring $f,$(ALL_KERNEL_MODULES_BLOCKLIST)),Y)) \\\n\t  $(eval _is_fsverity_build_manifest_apk := $(if $(findstring $f,$(ALL_FSVERITY_BUILD_MANIFEST_APK)),Y)) \\\n\t  $(eval _is_linker_config := $(if $(findstring $f,$(SYSTEM_LINKER_CONFIG) $(vendor_linker_config_file) $(product_linker_config_file)),Y)) \\\n\t  $(eval _is_partition_compat_symlink := $(if $(findstring $f,$(PARTITION_COMPAT_SYMLINKS)),Y)) \\\n\t  $(eval _is_flags_file := $(if $(findstring $f, $(ALL_FLAGS_FILES)),Y)) \\\n\t  $(eval _is_rootdir_symlink := $(if $(findstring $f, $(ALL_ROOTDIR_SYMLINKS)),Y)) \\\n\t  $(eval _is_platform_generated := $(if $(_is_soong_module),,$(_is_build_prop)$(_is_notice_file)$(_is_product_system_other_avbkey)$(_is_event_log_tags_file)$(_is_system_other_odex_marker)$(_is_kernel_modules_blocklist)$(_is_fsverity_build_manifest_apk)$(_is_linker_config)$(_is_partition_compat_symlink)$(_is_flags_file)$(_is_rootdir_symlink))) \\\n\t  $(eval _static_libs := $(if $(_is_soong_module),,$(ALL_INSTALLED_FILES.$f.STATIC_LIBRARIES))) \\\n\t  $(eval _whole_static_libs := $(if $(_is_soong_module),,$(ALL_INSTALLED_FILES.$f.WHOLE_STATIC_LIBRARIES))) \\\n\t  $(eval _license_text := $(if $(filter $(_build_output_path),$(ALL_NON_MODULES)),$(ALL_NON_MODULES.$(_build_output_path).NOTICES),\\\n\t                          $(if $(_is_partition_compat_symlink),build/soong/licenses/LICENSE))) \\\n\t  echo '$(_build_output_path),$(_module_path),$(_is_soong_module),$(_is_prebuilt_make_module),$(_product_copy_files),$(_kernel_module_copy_files),$(_is_platform_generated),$(_static_libs),$(_whole_static_libs),$(_license_text)' >> $@; \\\n\t)\n\n$(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-modules.csv:\n\trm -f $@\n\techo 'name,module_path,module_class,module_type,static_libs,whole_static_libs,built_files,installed_files' >> $@\n\t$(foreach m,$(ALL_MODULES), \\\n\t  $(eval _module_name := $m) \\\n\t  $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \\\n\t  $(eval _make_module_class := $(ALL_MODULES.$(_module_name).CLASS)) \\\n\t  $(eval _make_module_type := $(ALL_MODULES.$(_module_name).MAKE_MODULE_TYPE)) \\\n\t  $(eval _static_libs := $(strip $(sort $(ALL_MODULES.$(_module_name).STATIC_LIBS)))) \\\n\t  $(eval _whole_static_libs := $(strip $(sort $(ALL_MODULES.$(_module_name).WHOLE_STATIC_LIBS)))) \\\n\t  $(eval _built_files := $(strip $(sort $(ALL_MODULES.$(_module_name).BUILT)))) \\\n\t  $(eval _installed_files := $(strip $(sort $(ALL_MODULES.$(_module_name).INSTALLED)))) \\\n\t  $(eval _is_soong_module := $(ALL_MODULES.$(_module_name).IS_SOONG_MODULE)) \\\n\t  $(if $(_is_soong_module),, \\\n\t\techo '$(_module_name),$(_module_path),$(_make_module_class),$(_make_module_type),$(_static_libs),$(_whole_static_libs),$(_built_files),$(_installed_files)' >> $@; \\\n\t  ) \\\n\t)\n\n$(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/installed_files.stamp: $(installed_files)\n\ttouch $@\n\n# Remove the always_dirty_file.txt whenever the makefile is evaluated\n$(shell rm -f $(PRODUCT_OUT)/always_dirty_file.txt)\n$(PRODUCT_OUT)/always_dirty_file.txt:\n\ttouch $@\n\n.PHONY: sbom\nifneq ($(TARGET_BUILD_APPS),)\n# Create build rules for generating SBOMs of unbundled APKs and APEXs\n# $1: sbom file\n# $2: sbom fragment file\n# $3: installed file\n# $4: sbom-metadata.csv file\ndefine generate-app-sbom\n$(eval _path_on_device := $(patsubst $(PRODUCT_OUT)/%,%,$(3)))\n$(eval _module_name := $(ALL_INSTALLED_FILES.$(3)))\n$(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH))))\n$(eval _soong_module_type := $(strip $(sort $(ALL_MODULES.$(_module_name).SOONG_MODULE_TYPE))))\n$(eval _dep_modules := $(filter %.$(_module_name),$(ALL_MODULES)) $(filter %.$(_module_name)$(TARGET_2ND_ARCH_MODULE_SUFFIX),$(ALL_MODULES)))\n$(eval _is_apex := $(filter %.apex,$(3)))\n\n$(4):\n\trm -rf $$@\n\techo installed_file,module_path,soong_module_type,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,build_output_path,static_libraries,whole_static_libraries,is_static_lib >> $$@\n\techo /$(_path_on_device),$(_module_path),$(_soong_module_type),,,,,$(3),,, >> $$@\n\t$(if $(filter %.apex,$(3)),\\\n\t  $(foreach m,$(_dep_modules),\\\n\t    echo $(patsubst $(PRODUCT_OUT)/apex/$(_module_name)/%,%,$(ALL_MODULES.$m.INSTALLED)),$(sort $(ALL_MODULES.$m.PATH)),$(sort $(ALL_MODULES.$m.SOONG_MODULE_TYPE)),,,,,$(strip $(ALL_MODULES.$m.BUILT)),,, >> $$@;))\n\n$(2): $(1)\n$(1): $(4) $(3) $(GEN_SBOM) $(installed_files) $(metadata_list) $(metadata_files)\n\trm -rf $$@\n\t$(GEN_SBOM) --output_file $$@ --metadata $(4) --build_version $$(BUILD_FINGERPRINT_FROM_FILE) --product_mfr \"$(PRODUCT_MANUFACTURER)\" --json $(if $(filter %.apk,$(3)),--unbundled_apk,--unbundled_apex)\nendef\n\napps_only_sbom_files :=\napps_only_fragment_files :=\n$(foreach f,$(filter %.apk %.apex,$(installed_files)), \\\n  $(eval _metadata_csv_file := $(patsubst %,%-sbom-metadata.csv,$f)) \\\n  $(eval _sbom_file := $(patsubst %,%.spdx.json,$f)) \\\n  $(eval _fragment_file := $(patsubst %,%-fragment.spdx,$f)) \\\n  $(eval apps_only_sbom_files += $(_sbom_file)) \\\n  $(eval apps_only_fragment_files += $(_fragment_file)) \\\n  $(eval $(call generate-app-sbom,$(_sbom_file),$(_fragment_file),$f,$(_metadata_csv_file))) \\\n)\n\nsbom: $(apps_only_sbom_files)\n\n$(foreach f,$(apps_only_fragment_files),$(eval apps_only_fragment_dist_files += :sbom/$(notdir $f)))\n$(foreach f,$(apps_only_sbom_files),$(eval apps_only_sbom_dist_files += :sbom/$(notdir $f)))\n$(call dist-for-goals,apps_only,$(join $(apps_only_sbom_files),$(apps_only_sbom_dist_files)) $(join $(apps_only_fragment_files),$(apps_only_fragment_dist_files)))\nendif\n\n$(call dist-write-file,$(KATI_PACKAGE_MK_DIR)/dist.mk)\n\n$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] writing legacy Make module rules ...)\n"
  },
  {
    "path": "core/misc_prebuilt_internal.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n############################################################\n# Internal build rules for misc prebuilt modules that don't need additional processing\n############################################################\n\nprebuilt_module_classes := SCRIPT ETC DATA RENDERSCRIPT_BITCODE\nifeq ($(filter $(prebuilt_module_classes),$(LOCAL_MODULE_CLASS)),)\n$(call pretty-error,misc_prebuilt_internal.mk is for $(prebuilt_module_classes) modules only)\nendif\n\ninclude $(BUILD_SYSTEM)/base_rules.mk\n\nifneq ($(filter init%rc,$(notdir $(LOCAL_INSTALLED_MODULE)))$(filter %/etc/init/,$(dir $(LOCAL_INSTALLED_MODULE))),)\n  $(eval $(call copy-init-script-file-checked,$(my_prebuilt_src_file),$(LOCAL_BUILT_MODULE)))\nelse\n$(LOCAL_BUILT_MODULE) : $(my_prebuilt_src_file)\n\t$(transform-prebuilt-to-target)\nendif\n\nbuilt_module := $(LOCAL_BUILT_MODULE)\n"
  },
  {
    "path": "core/module_arch_supported.mk",
    "content": "###########################################################\n## Determine if a module can be built for an arch\n##\n## Inputs from module makefile:\n## my_prefix   TARGET_ or HOST_\n## my_module_multilib\n## LOCAL_MODULE_$(my_prefix)ARCH\n## LOCAL_MODULE_$(my_prefix)ARCH_WARN\n## LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH\n## LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH_WARN\n## LOCAL_IS_HOST_MODULE\n## LOCAL_MODULE_HOST_OS\n##\n## Inputs from build system:\n## $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT\n## LOCAL_2ND_ARCH_VAR_PREFIX\n##\n## Outputs:\n## my_module_arch_supported := (true|false)\n###########################################################\n\nmy_module_arch_supported := true\n\nifeq ($(my_module_multilib),none)\nmy_module_arch_supported := false\nendif\n\nifeq ($($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT)|$(my_module_multilib),true|32)\nmy_module_arch_supported := false\nendif\nifeq ($($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT)|$(my_module_multilib),|64)\nmy_module_arch_supported := false\nendif\n\nifneq ($(LOCAL_2ND_ARCH_VAR_PREFIX),)\nifeq ($(my_module_multilib),first)\nmy_module_arch_supported := false\nendif\nendif\n\nifneq (,$(LOCAL_MODULE_$(my_prefix)ARCH))\nifeq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH)))\nmy_module_arch_supported := false\nendif\nendif\n\nifneq (,$(LOCAL_MODULE_$(my_prefix)ARCH_WARN))\nifeq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH_WARN)))\nmy_module_arch_supported := false\n$(warning $(LOCAL_MODULE): architecture $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) not supported)\nendif\nendif\n\nifneq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH)))\nmy_module_arch_supported := false\nendif\n\nifneq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH_WARN)))\nmy_module_arch_supported := false\n$(warning $(LOCAL_MODULE): architecture $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) unsupported)\nendif\n\nifdef LOCAL_IS_HOST_MODULE\nifneq (,$(LOCAL_MODULE_HOST_OS))\n  ifneq (,$(filter windows,$(LOCAL_MODULE_HOST_OS)))\n    $(call pretty-error,Windows is only supported in Android.bp files)\n  endif\n  ifeq (,$(filter $($(my_prefix)OS),$(LOCAL_MODULE_HOST_OS)))\n    my_module_arch_supported := false\n  endif\nendif\nendif\n"
  },
  {
    "path": "core/multi_prebuilt.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call record-module-type,MULTI_PREBUILT)\nifneq ($(LOCAL_MODULE)$(LOCAL_MODULE_CLASS),)\n$(error $(LOCAL_PATH): LOCAL_MODULE or LOCAL_MODULE_CLASS not needed by \\\n  BUILD_MULTI_PREBUILT, use BUILD_PREBUILT instead!)\nendif\n\n# Save these before they get cleared by CLEAR_VARS.\nprebuilt_static_libs := $(filter %.a,$(LOCAL_PREBUILT_LIBS))\nprebuilt_shared_libs := $(filter-out %.a,$(LOCAL_PREBUILT_LIBS))\nprebuilt_executables := $(LOCAL_PREBUILT_EXECUTABLES)\nprebuilt_java_libraries := $(LOCAL_PREBUILT_JAVA_LIBRARIES)\nprebuilt_static_java_libraries := $(LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES)\nprebuilt_is_host := $(LOCAL_IS_HOST_MODULE)\nprebuilt_module_tags := $(LOCAL_MODULE_TAGS)\nprebuilt_strip_module := $(LOCAL_STRIP_MODULE)\n\n\nifndef multi_prebuilt_once\nmulti_prebuilt_once := true\n\n# $(1): file list\n# $(2): IS_HOST_MODULE\n# $(3): MODULE_CLASS\n# $(4): MODULE_TAGS\n# $(6): UNINSTALLABLE_MODULE\n# $(7): BUILT_MODULE_STEM\n# $(8): LOCAL_STRIP_MODULE\n#\n# Elements in the file list may be bare filenames,\n# or of the form \"<modulename>:<filename>\".\n# If the module name is not specified, the module\n# name will be the filename with the suffix removed.\n#\ndefine auto-prebuilt-boilerplate\n$(if $(filter %: :%,$(1)), \\\n  $(error $(LOCAL_PATH): Leading or trailing colons in \"$(1)\")) \\\n$(foreach t,$(1), \\\n  $(eval include $(CLEAR_VARS)) \\\n  $(eval LOCAL_IS_HOST_MODULE := $(2)) \\\n  $(eval LOCAL_MODULE_CLASS := $(3)) \\\n  $(eval LOCAL_MODULE_TAGS := $(4)) \\\n  $(eval LOCAL_UNINSTALLABLE_MODULE := $(6)) \\\n  $(eval tw := $(subst :, ,$(strip $(t)))) \\\n  $(if $(word 3,$(tw)),$(error $(LOCAL_PATH): Bad prebuilt filename '$(t)')) \\\n  $(if $(word 2,$(tw)), \\\n    $(eval LOCAL_MODULE := $(word 1,$(tw))) \\\n    $(eval LOCAL_SRC_FILES := $(word 2,$(tw))) \\\n   , \\\n    $(eval LOCAL_MODULE := $(basename $(notdir $(t)))) \\\n    $(eval LOCAL_SRC_FILES := $(t)) \\\n   ) \\\n  $(if $(7), \\\n    $(eval LOCAL_BUILT_MODULE_STEM := $(7)) \\\n   , \\\n    $(if $(word 2,$(tw)), \\\n      $(eval LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)$(suffix $(LOCAL_SRC_FILES))) \\\n     , \\\n      $(eval LOCAL_BUILT_MODULE_STEM := $(notdir $(LOCAL_SRC_FILES))) \\\n     ) \\\n   ) \\\n  $(eval LOCAL_MODULE_SUFFIX := $(suffix $(LOCAL_SRC_FILES))) \\\n  $(eval LOCAL_STRIP_MODULE := $(8)) \\\n  $(eval include $(BUILD_PREBUILT)) \\\n )\nendef\n\nendif # multi_prebuilt_once\n\n\n$(call auto-prebuilt-boilerplate, \\\n    $(prebuilt_static_libs), \\\n    $(prebuilt_is_host), \\\n    STATIC_LIBRARIES, \\\n    $(prebuilt_module_tags), \\\n    , \\\n    true)\n\n$(call auto-prebuilt-boilerplate, \\\n    $(prebuilt_shared_libs), \\\n    $(prebuilt_is_host), \\\n    SHARED_LIBRARIES, \\\n    $(prebuilt_module_tags), \\\n    , \\\n    , \\\n    , \\\n    $(prebuilt_strip_module))\n\n$(call auto-prebuilt-boilerplate, \\\n    $(prebuilt_executables), \\\n    $(prebuilt_is_host), \\\n    EXECUTABLES, \\\n    $(prebuilt_module_tags))\n\n$(call auto-prebuilt-boilerplate, \\\n    $(prebuilt_java_libraries), \\\n    $(prebuilt_is_host), \\\n    JAVA_LIBRARIES, \\\n    $(prebuilt_module_tags), \\\n    , \\\n    , \\\n    javalib.jar)\n\n$(call auto-prebuilt-boilerplate, \\\n    $(prebuilt_static_java_libraries), \\\n    $(prebuilt_is_host), \\\n    JAVA_LIBRARIES, \\\n    $(prebuilt_module_tags), \\\n    , \\\n    true, \\\n    javalib.jar)\n\nprebuilt_static_libs :=\nprebuilt_shared_libs :=\nprebuilt_executables :=\nprebuilt_java_libraries :=\nprebuilt_static_java_libraries :=\nprebuilt_is_host :=\nprebuilt_module_tags :=\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=MULTI_PREBUILT))"
  },
  {
    "path": "core/multilib.mk",
    "content": "# Translate LOCAL_32_BIT_ONLY to LOCAL_MULTILIB,\n# and check LOCAL_MULTILIB is a valid value.  Returns module's multilib\n# setting in my_module_multilib, or empty if not set.\n\nmy_module_multilib := $(strip $(LOCAL_MULTILIB))\n\nifndef my_module_multilib\nifeq ($(LOCAL_32_BIT_ONLY),true)\nmy_module_multilib := 32\nendif\nelse # my_module_multilib defined\nifeq (,$(filter 32 64 first both none,$(my_module_multilib)))\n$(error $(LOCAL_PATH): Invalid LOCAL_MULTILIB specified for module $(LOCAL_MODULE))\nendif\nendif # my_module_multilib defined\n"
  },
  {
    "path": "core/native_benchmark_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2018 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs {MODULE}.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-native-metric\" />\n\n    {EXTRA_CONFIGS}\n\n    <target_preparer class=\"com.android.tradefed.targetprep.PushFilePreparer\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"{MODULE}->/data/local/tmp/{MODULE}\" />\n    </target_preparer>\n    <test class=\"com.android.tradefed.testtype.GoogleBenchmarkTest\" >\n        <option name=\"native-benchmark-device-path\" value=\"/data/local/tmp\" />\n        <option name=\"benchmark-module-name\" value=\"{MODULE}\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/native_host_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2018 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs {MODULE}.\">\n    <option name=\"null-device\" value=\"true\" />\n\n    {EXTRA_CONFIGS}\n\n    <test class=\"com.android.tradefed.testtype.HostGTest\" >\n        <option name=\"module-name\" value=\"{MODULE}\" />\n    </test>\n</configuration>\n\n"
  },
  {
    "path": "core/native_test.mk",
    "content": "###########################################\n## A thin wrapper around BUILD_EXECUTABLE\n## Common flags for native tests are added.\n###########################################\n$(call record-module-type,NATIVE_TEST)\n\nifdef LOCAL_MODULE_CLASS\nifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS)\n$(error $(LOCAL_PATH): LOCAL_MODULE_CLASS must be NATIVE_TESTS with BUILD_HOST_NATIVE_TEST)\nendif\nendif\n\nLOCAL_MODULE_CLASS := NATIVE_TESTS\n\ninclude $(BUILD_SYSTEM)/target_test_internal.mk\n\nifndef LOCAL_MULTILIB\nifndef LOCAL_32_BIT_ONLY\nLOCAL_MULTILIB := both\nendif\nendif\n\ninclude $(BUILD_EXECUTABLE)\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=NATIVE_TEST))"
  },
  {
    "path": "core/native_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2023 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs {MODULE}.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-native\" />\n\n    {EXTRA_CONFIGS}\n\n    <target_preparer class=\"com.android.tradefed.targetprep.PushFilePreparer\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"{MODULE}->{TEST_INSTALL_BASE}/{MODULE}\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.GTest\" >\n        {EXTRA_TEST_RUNNER_CONFIGS}<option name=\"native-test-device-path\" value=\"{TEST_INSTALL_BASE}\" />\n        <option name=\"module-name\" value=\"{MODULE}\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/ninja_config.mk",
    "content": "ifeq ($(filter address,$(SANITIZE_HOST)),)\nNINJA ?= prebuilts/build-tools/$(HOST_PREBUILT_TAG)/bin/ninja\nelse\nNINJA ?= prebuilts/build-tools/$(HOST_PREBUILT_TAG)/asan/bin/ninja\nendif\n\nKATI_OUTPUT_PATTERNS := $(OUT_DIR)/build%.ninja $(OUT_DIR)/ninja%.sh\n\n# Modifier goals we don't need to pass to Ninja.\nNINJA_EXCLUDE_GOALS := all\n\n# A list of goals which affect parsing of makefiles and we need to pass to Kati.\nPARSE_TIME_MAKE_GOALS := \\\n\t$(PARSE_TIME_MAKE_GOALS) \\\n\t$(dont_bother_goals) \\\n\tall \\\n\tbrillo_tests \\\n\tbtnod \\\n\tbuild-art% \\\n\tbuild_kernel-nodeps \\\n\tclean-oat% \\\n\tcustom_images \\\n\tdicttool_aosp \\\n\tdocs \\\n\teng \\\n\toem_image \\\n\tonline-system-api-sdk-docs \\\n\tproduct-graph \\\n\tsamplecode \\\n\tsdk \\\n\tsdk_addon \\\n\tsdk_repo \\\n\tstnod \\\n\ttest-art% \\\n\tuser \\\n\tuserdataimage \\\n\tuserdebug\n\ninclude $(wildcard vendor/*/build/ninja_config.mk)\n\n# Any Android goals that need to be built.\nANDROID_GOALS := $(filter-out $(KATI_OUTPUT_PATTERNS),\\\n    $(sort $(ORIGINAL_MAKECMDGOALS) $(MAKECMDGOALS)))\n# Temporary compatibility support until the build server configs are updated\nANDROID_GOALS := $(patsubst win_sdk,sdk,$(ANDROID_GOALS))\nifneq ($(HOST_OS),linux)\n  ANDROID_GOALS := $(filter-out sdk,$(ANDROID_GOALS))\n  ANDROID_GOALS := $(patsubst sdk_repo,sdk-repo-build-tools sdk-repo-platform-tools,$(ANDROID_GOALS))\nendif\n# Goals we need to pass to Ninja.\nNINJA_GOALS := $(filter-out $(NINJA_EXCLUDE_GOALS), $(ANDROID_GOALS))\nifndef NINJA_GOALS\n  NINJA_GOALS := droid\nendif\n# Goals we need to pass to Kati.\nKATI_GOALS := $(filter $(PARSE_TIME_MAKE_GOALS), $(ANDROID_GOALS))\n"
  },
  {
    "path": "core/node_fns.mk",
    "content": "#\n# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# Clears a list of variables using \":=\".\n#\n# E.g.,\n#   $(call clear-var-list,A B C)\n# would be the same as:\n#   A :=\n#   B :=\n#   C :=\n#\n# $(1): list of variable names to clear\n#\ndefine clear-var-list\n$(foreach v,$(1),$(eval $(v):=))\nendef\n\n#\n# Copies a list of variables into another list of variables.\n# The target list is the same as the source list, but has\n# a dotted prefix affixed to it.\n#\n# E.g.,\n#   $(call copy-var-list, PREFIX, A B)\n# would be the same as:\n#   PREFIX.A := $(A)\n#   PREFIX.B := $(B)\n#\n# $(1): destination prefix\n# $(2): list of variable names to copy\n#\ndefine copy-var-list\n$(foreach v,$(2),$(eval $(strip $(1)).$(v):=$($(v))))\nendef\n\n#\n# Moves a list of variables into another list of variables.\n# The variable names differ by a prefix.  After moving, the\n# source variable is cleared.\n#\n# NOTE: Spaces are not allowed around the prefixes.\n#\n# E.g.,\n#   $(call move-var-list,SRC,DST,A B)\n# would be the same as:\n#   DST.A := $(SRC.A)\n#   SRC.A :=\n#   DST.B := $(SRC.B)\n#   SRC.B :=\n#\n# $(1): source prefix\n# $(2): destination prefix\n# $(3): list of variable names to move\n#\ndefine move-var-list\n$(foreach v,$(3), \\\n  $(eval $(2).$(v) := $($(1).$(v))) \\\n  $(eval $(1).$(v) :=) \\\n )\nendef\n\n#\n# $(1): haystack\n# $(2): needle\n#\n# Guarantees that needle appears at most once in haystack,\n# without changing the order of other elements in haystack.\n# If needle appears multiple times, only the first occurrance\n# will survive.\n#\ndefine uniq-word\n$(strip \\\n  $(if $(filter-out 0 1,$(words $(filter $(2),$(1)))), \\\n    $(eval _uniq_word_seen :=) \\\n    $(foreach w,$(1), \\\n      $(if $(filter $(2),$(w)), \\\n        $(if $(_uniq_word_seen),, \\\n          $(w) \\\n          $(eval _uniq_word_seen := true)), \\\n        $(w))), \\\n  $(1)))\nendef\n\nINHERIT_TAG := @inherit:\n\n#\n# Walks through the list of variables, each qualified by the prefix,\n# and finds instances of words beginning with INHERIT_TAG.  Scrape\n# off INHERIT_TAG from each matching word, and return the sorted,\n# unique set of those words.\n#\n# E.g., given\n#   PREFIX.A := A $(INHERIT_TAG)aaa B C\n#   PREFIX.B := B $(INHERIT_TAG)aaa C $(INHERIT_TAG)bbb D E\n# Then\n#   $(call get-inherited-nodes,PREFIX,A B)\n# returns\n#   aaa bbb\n#\n# $(1): variable prefix\n# $(2): list of variables to check\n#\ndefine get-inherited-nodes\n$(sort \\\n  $(subst $(INHERIT_TAG),, \\\n    $(filter $(INHERIT_TAG)%, \\\n      $(foreach v,$(2),$($(1).$(v))) \\\n )))\nendef\n\n#\n# for each variable ( (prefix + name) * vars ):\n#   get list of inherited words; if not empty:\n#     for each inherit:\n#       replace the first occurrence with (prefix + inherited + var)\n#       clear the source var so we can't inherit the value twice\n#\n# $(1): context prefix\n# $(2): name of this node\n# $(3): list of node variable names\n# $(4): list of single value variable names (subset of $(3))\n#\ndefine _expand-inherited-values\n  $(foreach v,$(3), \\\n    $(eval ### \"Shorthand for the name of the target variable\") \\\n    $(eval _eiv_tv := $(1).$(2).$(v)) \\\n    $(eval ### \"Get the list of nodes that this variable inherits\") \\\n    $(eval _eiv_i := \\\n        $(sort \\\n            $(patsubst $(INHERIT_TAG)%,%, \\\n                $(filter $(INHERIT_TAG)%, $($(_eiv_tv)) \\\n     )))) \\\n    $(eval ### \"Whether this variable should only take a single value\") \\\n    $(eval _eiv_sv := $(filter $(v),$(4))) \\\n    $(foreach i,$(_eiv_i), \\\n      $(eval ### \"Make sure that this inherit appears only once\") \\\n      $(eval $(_eiv_tv) := \\\n          $(call uniq-word,$($(_eiv_tv)),$(INHERIT_TAG)$(i))) \\\n      $(eval ### \"The expanded value, empty if we want a single value and have one\") \\\n      $(eval _eiv_ev := \\\n        $(if $(and $(_eiv_sv),$(filter-out $(INHERIT_TAG)%,$($(_eiv_tv)))),,\\\n          $($(1).$(i).$(v)) \\\n        ) \\\n      ) \\\n      $(eval ### \"Expand the inherit tag\") \\\n      $(eval $(_eiv_tv) := \\\n          $(strip $(patsubst $(INHERIT_TAG)$(i),$(_eiv_ev),$($(_eiv_tv))))) \\\n      $(eval ### \"Clear the child so DAGs don't create duplicate entries\" ) \\\n      $(eval $(1).$(i).$(v) :=) \\\n      $(eval ### \"If we just inherited ourselves, it's a cycle.\") \\\n      $(if $(filter $(INHERIT_TAG)$(2),$($(_eiv_tv))), \\\n        $(warning Cycle detected between \"$(2)\" and \"$(i)\" for context \"$(1)\") \\\n        $(error import of \"$(2)\" failed) \\\n      ) \\\n     ) \\\n   ) \\\n   $(eval _eiv_tv :=) \\\n   $(eval _eiv_i :=)\nendef\n\n#\n# $(1): context prefix\n# $(2): makefile representing this node\n# $(3): list of node variable names\n# $(4): list of single value variable names (subset of $(3))\n#\n# _include_stack contains the list of included files, with the most recent files first.\ndefine _import-node\n  $(eval _include_stack := $(2) $$(_include_stack))\n  $(call clear-var-list, $(3))\n  $(eval LOCAL_PATH := $(patsubst %/,%,$(dir $(2))))\n  $(eval MAKEFILE_LIST :=)\n  $(call dump-import-start,$(_include_stack))\n  $(call dump-config-vals,$(2),before)\n  $(eval include $(2))\n  $(call dump-import-done,$(_include_stack))\n  $(call dump-config-vals,$(2),after)\n  $(eval _included := $(filter-out $(2),$(MAKEFILE_LIST)))\n  $(eval MAKEFILE_LIST :=)\n  $(eval LOCAL_PATH :=)\n  $(call copy-var-list, $(1).$(2), $(3))\n  $(call clear-var-list, $(3))\n\n  $(eval $(1).$(2).inherited := \\\n      $(call get-inherited-nodes,$(1).$(2),$(3)))\n  $(call _import-nodes-inner,$(1),$($(1).$(2).inherited),$(3),$(4))\n\n  $(call _expand-inherited-values,$(1),$(2),$(3),$(4))\n\n  $(eval $(1).$(2).inherited :=)\n  $(eval _include_stack := $(wordlist 2,9999,$(_include_stack)))\nendef\n\n#\n# This will generate a warning for _included above\n#  $(if $(_included), \\\n#      $(eval $(warning product spec file: $(2)))\\\n#      $(foreach _inc,$(_included),$(eval $(warning $(space)$(space)$(space)includes: $(_inc)))),)\n#\n\n#\n# $(1): context prefix\n# $(2): list of makefiles representing nodes to import\n# $(3): list of node variable names\n# $(4): list of single value variable names (subset of $(3))\n#\n#TODO: Make the \"does not exist\" message more helpful;\n#      should print out the name of the file trying to include it.\ndefine _import-nodes-inner\n  $(foreach _in,$(2), \\\n    $(if $(wildcard $(_in)), \\\n      $(if $($(1).$(_in).seen), \\\n        $(eval ### \"skipping already-imported $(_in)\") \\\n       , \\\n        $(eval $(1).$(_in).seen := true) \\\n        $(call _import-node,$(1),$(strip $(_in)),$(3),$(4)) \\\n       ) \\\n     , \\\n      $(error $(1): \"$(_in)\" does not exist) \\\n     ) \\\n   )\nendef\n\n#\n# $(1): output list variable name, like \"PRODUCTS\" or \"DEVICES\"\n# $(2): list of makefiles representing nodes to import\n# $(3): list of node variable names\n# $(4): list with subset of variable names that take only a single value, instead\n#       of the default list semantics\n#\ndefine import-nodes\n$(call dump-phase-start,$(1),$(2),$(3),$(4),build/make/core/node_fns.mk) \\\n$(if \\\n  $(foreach _in,$(2), \\\n    $(eval _node_import_context := _nic.$(1).[[$(_in)]]) \\\n    $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \\\n                should be empty here: $(_include_stack))),) \\\n    $(eval _include_stack := ) \\\n    $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3),$(4)) \\\n    $(call move-var-list,$(_node_import_context).$(_in),$(1).$(_in),$(3)) \\\n    $(eval _node_import_context :=) \\\n    $(eval $(1) := $($(1)) $(_in)) \\\n    $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \\\n                should be empty here: $(_include_stack))),) \\\n   ) \\\n,) \\\n$(call dump-phase-end,build/make/core/node_fns.mk)\nendef\n"
  },
  {
    "path": "core/notice_files.mk",
    "content": "###########################################################\n## Track NOTICE files\n###########################################################\n\nmodule_license_metadata := $(call local-meta-intermediates-dir)/$(my_register_name).meta_lic\n\n$(foreach target,$(ALL_MODULES.$(my_register_name).BUILT) $(ALL_MODULES.$(my_register_name).INSTALLED) $(foreach bi,$(LOCAL_SOONG_BUILT_INSTALLED),$(call word-colon,1,$(bi))),\\\n  $(eval ALL_TARGETS.$(target).META_LIC := $(module_license_metadata)))\n\n$(foreach f,$(my_test_data) $(my_test_config),\\\n  $(if $(strip $(ALL_TARGETS.$(call word-colon,1,$(f)).META_LIC)), \\\n    $(call declare-copy-target-license-metadata,$(call word-colon,2,$(f)),$(call word-colon,1,$(f))), \\\n    $(eval ALL_TARGETS.$(call word-colon,2,$(f)).META_LIC := $(module_license_metadata))))\n\nALL_MODULES.$(my_register_name).META_LIC := $(strip $(ALL_MODULES.$(my_register_name).META_LIC) $(module_license_metadata))\n\nifdef LOCAL_SOONG_LICENSE_METADATA\n  # Soong modules have already produced a license metadata file, copy it to where Make expects it.\n  $(eval $(call copy-one-license-metadata-file, $(LOCAL_SOONG_LICENSE_METADATA), $(module_license_metadata),$(ALL_MODULES.$(my_register_name).BUILT),$(ALL_MODUES.$(my_register_name).INSTALLED)))\nelse\n  # Make modules don't have enough information to produce a license metadata rule until after fix-notice-deps\n  # has been called, store the necessary information until later.\n\n  ifneq ($(LOCAL_NOTICE_FILE),)\n    notice_file:=$(strip $(LOCAL_NOTICE_FILE))\n  else\n    notice_file:=$(strip $(wildcard $(LOCAL_PATH)/LICENSE $(LOCAL_PATH)/LICENCE $(LOCAL_PATH)/NOTICE))\n  endif\n\n  ifeq ($(LOCAL_MODULE_CLASS),GYP)\n    # We ignore NOTICE files for modules of type GYP.\n    notice_file :=\n  endif\n\n  ifeq ($(LOCAL_MODULE_CLASS),FAKE)\n    # We ignore NOTICE files for modules of type FAKE.\n    notice_file :=\n  endif\n\n  # Soong generates stub libraries that don't need NOTICE files\n  ifdef LOCAL_NO_NOTICE_FILE\n    ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n      $(call pretty-error,LOCAL_NO_NOTICE_FILE should not be used by Android.mk files)\n    endif\n    notice_file :=\n  endif\n\n  ifneq (,$(strip $(LOCAL_LICENSE_PACKAGE_NAME)))\n    license_package_name:=$(strip $(LOCAL_LICENSE_PACKAGE_NAME))\n  else\n    license_package_name:=\n  endif\n\n  ifneq (,$(strip $(LOCAL_LICENSE_INSTALL_MAP)))\n    install_map:=$(strip $(LOCAL_LICENSE_INSTALL_MAP))\n  else\n    install_map:=\n  endif\n\n  ifneq (,$(strip $(LOCAL_LICENSE_KINDS)))\n    license_kinds:=$(strip $(LOCAL_LICENSE_KINDS))\n  else\n    license_kinds:=legacy_by_exception_only\n  endif\n\n  ifneq (,$(strip $(LOCAL_LICENSE_CONDITIONS)))\n    license_conditions:=$(strip $(LOCAL_LICENSE_CONDITIONS))\n  else\n    license_conditions:=by_exception_only\n  endif\n\n  is_container:=$(strip $(LOCAL_MODULE_IS_CONTAINER))\n  ifeq (,$(is_container))\n    ifneq (,$(strip $(filter %.zip %.tar %.tgz %.tar.gz %.apk %.img %.srcszip %.apex, $(LOCAL_BUILT_MODULE))))\n      is_container:=true\n    else\n      is_container:=false\n    endif\n  else ifneq (,$(strip $(filter-out true false,$(is_container))))\n    $(error Unrecognized value '$(is_container)' for LOCAL_MODULE_IS_CONTAINER)\n  endif\n\n  ifeq (true,$(is_container))\n    # Include shared libraries' notices for \"container\" types, but not for binaries etc.\n    notice_deps := \\\n        $(strip \\\n            $(foreach d, \\\n                $(LOCAL_REQUIRED_MODULES) \\\n                $(LOCAL_STATIC_LIBRARIES) \\\n                $(LOCAL_WHOLE_STATIC_LIBRARIES) \\\n                $(LOCAL_SHARED_LIBRARIES) \\\n                $(LOCAL_DYLIB_LIBRARIES) \\\n                $(LOCAL_RLIB_LIBRARIES) \\\n                $(LOCAL_PROC_MACRO_LIBRARIES) \\\n                $(LOCAL_HEADER_LIBRARIES) \\\n                $(LOCAL_STATIC_JAVA_LIBRARIES) \\\n                $(LOCAL_JAVA_LIBRARIES) \\\n                $(LOCAL_JNI_SHARED_LIBRARIES) \\\n                ,$(subst :,_,$(d)):static \\\n            ) \\\n        )\n  else\n    notice_deps := \\\n        $(strip \\\n            $(foreach d, \\\n                $(LOCAL_REQUIRED_MODULES) \\\n                $(LOCAL_STATIC_LIBRARIES) \\\n                $(LOCAL_WHOLE_STATIC_LIBRARIES) \\\n                $(LOCAL_RLIB_LIBRARIES) \\\n                $(LOCAL_PROC_MACRO_LIBRARIES) \\\n                $(LOCAL_HEADER_LIBRARIES) \\\n                $(LOCAL_STATIC_JAVA_LIBRARIES) \\\n                ,$(subst :,_,$(d)):static \\\n            )$(foreach d, \\\n                $(LOCAL_SHARED_LIBRARIES) \\\n                $(LOCAL_DYLIB_LIBRARIES) \\\n                $(LOCAL_JAVA_LIBRARIES) \\\n                $(LOCAL_JNI_SHARED_LIBRARIES) \\\n                ,$(subst :,_,$(d)):dynamic \\\n            ) \\\n        )\n  endif\n  ifeq ($(LOCAL_IS_HOST_MODULE),true)\n    notice_deps := $(strip $(notice_deps) $(foreach d,$(LOCAL_HOST_REQUIRED_MODULES),$(subst :,_,$(d)):static))\n  else\n    notice_deps := $(strip $(notice_deps) $(foreach d,$(LOCAL_TARGET_REQUIRED_MODULES),$(subst :,_,$(d)):static))\n  endif\n\n  ALL_MODULES.$(my_register_name).DELAYED_META_LIC := $(strip $(ALL_MODULES.$(my_register_name).DELAYED_META_LIC) $(module_license_metadata))\n  ALL_MODULES.$(my_register_name).LICENSE_PACKAGE_NAME := $(strip $(license_package_name))\n  ALL_MODULES.$(my_register_name).MODULE_TYPE := $(strip $(ALL_MODULES.$(my_register_name).MODULE_TYPE) $(LOCAL_MODULE_TYPE))\n  ALL_MODULES.$(my_register_name).MODULE_CLASS := $(strip $(ALL_MODULES.$(my_register_name).MODULE_CLASS) $(LOCAL_MODULE_CLASS))\n  ALL_MODULES.$(my_register_name).LICENSE_KINDS := $(ALL_MODULES.$(my_register_name).LICENSE_KINDS) $(license_kinds)\n  ALL_MODULES.$(my_register_name).LICENSE_CONDITIONS := $(ALL_MODULES.$(my_register_name).LICENSE_CONDITIONS) $(license_conditions)\n  ALL_MODULES.$(my_register_name).LICENSE_INSTALL_MAP := $(ALL_MODULES.$(my_register_name).LICENSE_INSTALL_MAP) $(install_map)\n  ALL_MODULES.$(my_register_name).NOTICE_DEPS := $(ALL_MODULES.$(my_register_name).NOTICE_DEPS) $(notice_deps)\n  ALL_MODULES.$(my_register_name).IS_CONTAINER := $(strip $(filter-out false,$(ALL_MODULES.$(my_register_name).IS_CONTAINER) $(is_container)))\n  ALL_MODULES.$(my_register_name).PATH := $(strip $(ALL_MODULES.$(my_register_name).PATH) $(local_path))\n\n  ifdef notice_file\n    ALL_MODULES.$(my_register_name).NOTICES := $(ALL_MODULES.$(my_register_name).NOTICES) $(notice_file)\n  endif  # notice_file\nendif\n\n"
  },
  {
    "path": "core/os_licensing.mk",
    "content": "ifeq ($(TARGET_BUILD_APPS),)\n\n.PHONY: systemlicense\nsystemlicense: $(call corresponding-license-metadata, $(SYSTEM_NOTICE_DEPS)) reportmissinglicenses\n\nifneq (,$(SYSTEM_NOTICE_DEPS))\n\nSYSTEM_NOTICE_DEPS += $(UNMOUNTED_NOTICE_DEPS) $(UNMOUNTED_NOTICE_VENDOR_DEPS)\n\n$(eval $(call xml-notice-rule,$(target_notice_file_xml_gz),\"System image\",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS)))\n\n$(eval $(call text-notice-rule,$(target_notice_file_txt),\"System image\",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS)))\n\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(installed_notice_html_or_xml_gz): $(target_notice_file_xml_gz)\n\t$(copy-file-to-target)\nendif\n\n$(call declare-1p-target,$(target_notice_file_xml_gz))\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(call declare-1p-target,$(installed_notice_html_or_xml_gz))\nendif\nendif\n\n.PHONY: vendorlicense\nvendorlicense: $(call corresponding-license-metadata, $(VENDOR_NOTICE_DEPS)) reportmissinglicenses\n\nifneq (,$(VENDOR_NOTICE_DEPS))\n\nVENDOR_NOTICE_DEPS += $(UNMOUNTED_NOTICE_VENDOR_DEPS)\n\n$(eval $(call text-notice-rule,$(target_vendor_notice_file_txt),\"Vendor image\", \\\n         \"Notices for files contained in all filesystem images except system/system_ext/product/odm/vendor_dlkm/odm_dlkm in this directory:\", \\\n         $(VENDOR_NOTICE_DEPS),$(VENDOR_NOTICE_DEPS)))\n\n$(eval $(call xml-notice-rule,$(target_vendor_notice_file_xml_gz),\"Vendor image\", \\\n         \"Notices for files contained in all filesystem images except system/system_ext/product/odm/vendor_dlkm/odm_dlkm in this directory:\", \\\n         $(VENDOR_NOTICE_DEPS),$(VENDOR_NOTICE_DEPS)))\n\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(installed_vendor_notice_xml_gz): $(target_vendor_notice_file_xml_gz)\n\t$(copy-file-to-target)\nendif\n\n$(call declare-1p-target,$(target_vendor_notice_file_xml_gz))\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(call declare-1p-target,$(installed_vendor_notice_xml_gz))\nendif\nendif\n\n.PHONY: odmlicense\nodmlicense: $(call corresponding-license-metadata, $(ODM_NOTICE_DEPS)) reportmissinglicenses\n\nifneq (,$(ODM_NOTICE_DEPS))\n$(eval $(call text-notice-rule,$(target_odm_notice_file_txt),\"ODM filesystem image\", \\\n         \"Notices for files contained in the odm filesystem image in this directory:\", \\\n         $(ODM_NOTICE_DEPS),$(ODM_NOTICE_DEPS)))\n\n$(eval $(call xml-notice-rule,$(target_odm_notice_file_xml_gz),\"ODM filesystem image\", \\\n         \"Notices for files contained in the odm filesystem image in this directory:\", \\\n         $(ODM_NOTICE_DEPS),$(ODM_NOTICE_DEPS)))\n\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(installed_odm_notice_xml_gz): $(target_odm_notice_file_xml_gz)\n\t$(copy-file-to-target)\nendif\n\n$(call declare-1p-target,$(target_odm_notice_file_xml_gz))\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(call declare-1p-target,$(installed_odm_notice_xml_gz))\nendif\nendif\n\n.PHONY: oemlicense\noemlicense: $(call corresponding-license-metadata, $(OEM_NOTICE_DEPS)) reportmissinglicenses\n\n.PHONY: productlicense\nproductlicense: $(call corresponding-license-metadata, $(PRODUCT_NOTICE_DEPS)) reportmissinglicenses\n\nifneq (,$(PRODUCT_NOTICE_DEPS))\n$(eval $(call text-notice-rule,$(target_product_notice_file_txt),\"Product image\", \\\n         \"Notices for files contained in the product filesystem image in this directory:\", \\\n         $(PRODUCT_NOTICE_DEPS),$(PRODUCT_NOTICE_DEPS)))\n\n$(eval $(call xml-notice-rule,$(target_product_notice_file_xml_gz),\"Product image\", \\\n         \"Notices for files contained in the product filesystem image in this directory:\", \\\n         $(PRODUCT_NOTICE_DEPS),$(PRODUCT_NOTICE_DEPS)))\n\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(installed_product_notice_xml_gz): $(target_product_notice_file_xml_gz)\n\t$(copy-file-to-target)\nendif\n\n$(call declare-1p-target,$(target_product_notice_file_xml_gz))\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(call declare-1p-target,$(installed_product_notice_xml_gz))\nendif\nendif\n\n.PHONY: systemextlicense\nsystemextlicense: $(call corresponding-license-metadata, $(SYSTEM_EXT_NOTICE_DEPS)) reportmissinglicenses\n\nifneq (,$(SYSTEM_EXT_NOTICE_DEPS))\n$(eval $(call text-notice-rule,$(target_system_ext_notice_file_txt),\"System_ext image\", \\\n         \"Notices for files contained in the system_ext filesystem image in this directory:\", \\\n         $(SYSTEM_EXT_NOTICE_DEPS),$(SYSTEM_EXT_NOTICE_DEPS)))\n\n$(eval $(call xml-notice-rule,$(target_system_ext_notice_file_xml_gz),\"System_ext image\", \\\n         \"Notices for files contained in the system_ext filesystem image in this directory:\", \\\n         $(SYSTEM_EXT_NOTICE_DEPS),$(SYSTEM_EXT_NOTICE_DEPS)))\n\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(installed_system_ext_notice_xml_gz): $(target_system_ext_notice_file_xml_gz)\n\t$(copy-file-to-target)\nendif\n\n$(call declare-1p-target,$(target_system_ext_notice_file_xml_gz))\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(call declare-1p-target,$(installed_system_ext_notice_xml_gz))\nendif\nendif\n\n.PHONY: vendor_dlkmlicense\nvendor_dlkmlicense: $(call corresponding-license-metadata, $(VENDOR_DLKM_NOTICE_DEPS)) reportmissinglicenses\n\nifneq (,$(VENDOR_DLKM_NOTICE_DEPS))\n$(eval $(call text-notice-rule,$(target_vendor_dlkm_notice_file_txt),\"Vendor_dlkm image\", \\\n         \"Notices for files contained in the vendor_dlkm filesystem image in this directory:\", \\\n         $(VENDOR_DLKM_NOTICE_DEPS),$(VENDOR_DLKM_NOTICE_DEPS)))\n\n$(eval $(call xml-notice-rule,$(target_vendor_dlkm_notice_file_xml_gz),\"Vendor_dlkm image\", \\\n         \"Notices for files contained in the vendor_dlkm filesystem image in this directory:\", \\\n         $(VENDOR_DLKM_NOTICE_DEPS),$(VENDOR_DLKM_NOTICE_DEPS)))\n\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(installed_vendor_dlkm_notice_xml_gz): $(target_vendor_dlkm_notice_file_xml_gz)\n\t$(copy-file-to-target)\nendif\n\n$(call declare-1p-target,$(target_vendor_dlkm_notice_file_xml_gz))\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(call declare-1p-target,$(installed_vendor_dlkm_notice_xml_gz))\nendif\nendif\n\n.PHONY: odm_dlkmlicense\nodm_dlkmlicense: $(call corresponding-license-metadata, $(ODM_DLKM_NOTICE_DEPS)) reportmissinglicenses\n\nifneq (,$(ODM_DLKM_NOTICE_DEPS))\n$(eval $(call text-notice-rule,$(target_odm_dlkm_notice_file_txt),\"ODM_dlkm filesystem image\", \\\n         \"Notices for files contained in the odm_dlkm filesystem image in this directory:\", \\\n         $(ODM_DLKM_NOTICE_DEPS),$(ODM_DLKM_NOTICE_DEPS)))\n\n$(eval $(call xml-notice-rule,$(target_odm_dlkm_notice_file_xml_gz),\"ODM_dlkm filesystem image\", \\\n         \"Notices for files contained in the odm_dlkm filesystem image in this directory:\", \\\n         $(ODM_DLKM_NOTICE_DEPS),$(ODM_DLKM_NOTICE_DEPS)))\n\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(installed_odm_dlkm_notice_xml_gz): $(target_odm_dlkm_notice_file_xml_gz)\n\t$(copy-file-to-target)\nendif\n\n$(call declare-1p-target,$(target_odm_dlkm_notice_file_xml_gz))\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(call declare-1p-target,$(installed_odm_dlkm_notice_xml_gz))\nendif\nendif\n\n.PHONY: system_dlkmlicense\nsystem_dlkmlicense: $(call corresponding-license-metadata, $(SYSTEM_DLKM_NOTICE_DEPS)) reportmissinglicenses\n\nifneq (,$(SYSTEM_DLKM_NOTICE_DEPS))\n$(eval $(call text-notice-rule,$(target_system_dlkm_notice_file_txt),\"System_dlkm filesystem image\", \\\n         \"Notices for files contained in the system_dlkm filesystem image in this directory:\", \\\n         $(SYSTEM_DLKM_NOTICE_DEPS),$(SYSTEM_DLKM_NOTICE_DEPS)))\n\n$(eval $(call xml-notice-rule,$(target_system_dlkm_notice_file_xml_gz),\"System_dlkm filesystem image\", \\\n         \"Notices for files contained in the system_dlkm filesystem image in this directory:\", \\\n         $(SYSTEM_DLKM_NOTICE_DEPS),$(SYSTEM_DLKM_NOTICE_DEPS)))\n\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(installed_system_dlkm_notice_xml_gz): $(target_system_dlkm_notice_file_xml_gz)\n\t$(copy-file-to-target)\nendif\n\n$(call declare-1p-target,$(target_system_dlkm_notice_file_xml_gz))\nifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)\n$(call declare-1p-target,$(installed_sysetm_dlkm_notice_xml_gz))\nendif\nendif\n\nendif # not TARGET_BUILD_APPS\n"
  },
  {
    "path": "core/pack_dyn_relocs_setup.mk",
    "content": "#############################################################\n## Set up my_pack_module_relocations\n## Input variables:\n##   DISABLE_RELOCATION_PACKER,\n##   LOCAL_PACK_MODULE_RELOCATIONS*,\n##   *TARGET_PACK_MODULE_RELOCATIONS,\n##   LOCAL_MODULE_CLASS, HOST_OS\n##   LOCAL_IS_HOST_MODULE\n## Output variables:\n##   my_pack_module_relocations, if false skip relocation_packer\n#############################################################\n\nmy_pack_module_relocations := false\nifneq ($(DISABLE_RELOCATION_PACKER),true)\n  my_pack_module_relocations := $(firstword \\\n    $(LOCAL_PACK_MODULE_RELOCATIONS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) \\\n    $(LOCAL_PACK_MODULE_RELOCATIONS))\nendif\n\nifeq ($(my_pack_module_relocations),)\n  my_pack_module_relocations := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_PACK_MODULE_RELOCATIONS)\nendif\n\n# Do not pack relocations for executables. Because packing results in\n# non-zero p_vaddr which causes kernel to load executables to lower\n# address (starting at 0x8000) http://b/20665974\nifeq ($(filter SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS)),)\n  my_pack_module_relocations := false\nendif\n\nifdef LOCAL_IS_HOST_MODULE\n  # Do not pack relocations on host modules\n  my_pack_module_relocations := false\nendif\n\n# Lld relocation packing cannot be enabled for binaries before Android Pie.\nifneq ($(LOCAL_SDK_VERSION),)\n  ifneq ($(LOCAL_SDK_VERSION),current)\n    ifeq ($(call math_lt,$(LOCAL_SDK_VERSION),28),true)\n      my_pack_module_relocations := false\n    endif\n  endif\nendif\n"
  },
  {
    "path": "core/package.mk",
    "content": "# We don't automatically set up rules to build packages for both\n# TARGET_ARCH and TARGET_2ND_ARCH.\n# To build it for TARGET_2ND_ARCH in a 64bit product, use \"LOCAL_MULTILIB := 32\".\n\n$(call record-module-type,PACKAGE)\n\nmy_prefix := TARGET_\ninclude $(BUILD_SYSTEM)/multilib.mk\n\nifeq ($(TARGET_SUPPORTS_32_BIT_APPS)|$(TARGET_SUPPORTS_64_BIT_APPS),true|true)\n  # packages default to building for either architecture,\n  # the preferred if its supported, otherwise the non-preferred.\nelse ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true)\n  # only 64-bit apps supported\n  ifeq ($(filter $(my_module_multilib),64 both first),$(my_module_multilib))\n    # if my_module_multilib was 64, both, first, or unset, build for 64-bit\n    my_module_multilib := 64\n  else\n    # otherwise don't build this app\n    my_module_multilib := none\n  endif\nelse\n  # only 32-bit apps supported\n  ifeq ($(filter $(my_module_multilib),32 both),$(my_module_multilib))\n    # if my_module_multilib was 32, both, or unset, build for 32-bit\n    my_module_multilib := 32\n  else ifeq ($(my_module_multilib),first)\n    ifndef TARGET_IS_64_BIT\n      # if my_module_multilib was first and this is a 32-bit build, build for\n      # 32-bit\n      my_module_multilib := 32\n    else\n      # if my_module_multilib was first and this is a 64-bit build, don't build\n      # this app\n      my_module_multilib := none\n    endif\n  else\n    # my_module_mulitlib was 64 or none, don't build this app\n    my_module_multilib := none\n  endif\nendif\n\nLOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true\nLOCAL_2ND_ARCH_VAR_PREFIX :=\n\n# check if preferred arch is supported\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\nifeq ($(my_module_arch_supported),true)\n# first arch is supported\ninclude $(BUILD_SYSTEM)/package_internal.mk\nelse ifneq (,$(TARGET_2ND_ARCH))\nLOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX)\n# check if non-preferred arch is supported\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\nifeq ($(my_module_arch_supported),true)\n# secondary arch is supported\ninclude $(BUILD_SYSTEM)/package_internal.mk\nendif\nendif # TARGET_2ND_ARCH\n\nLOCAL_2ND_ARCH_VAR_PREFIX :=\nLOCAL_NO_2ND_ARCH_MODULE_SUFFIX :=\n\nmy_module_arch_supported :=\n"
  },
  {
    "path": "core/package_internal.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n###########################################################\n## Standard rules for building an application package.\n##\n## Additional inputs from base_rules.make:\n## LOCAL_PACKAGE_NAME: The name of the package; the directory\n## will be called this.\n##\n## MODULE, MODULE_PATH, and MODULE_SUFFIX will\n## be set for you.\n###########################################################\n\nLOCAL_PACKAGE_NAME := $(strip $(LOCAL_PACKAGE_NAME))\nifeq ($(LOCAL_PACKAGE_NAME),)\n$(error $(LOCAL_PATH): Package modules must define LOCAL_PACKAGE_NAME)\nendif\n\nifneq ($(strip $(LOCAL_MODULE_SUFFIX)),)\n$(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE_SUFFIX)\nendif\nLOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)\n\nifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),)\n$(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE_STEM or LOCAL_BUILT_MODULE_STEM)\nendif\n\nifneq ($(strip $(LOCAL_MODULE)),)\n$(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE)\nendif\nLOCAL_MODULE := $(LOCAL_PACKAGE_NAME)\n\nifneq ($(strip $(LOCAL_MODULE_CLASS)),)\n$(error $(LOCAL_PATH): Package modules may not set LOCAL_MODULE_CLASS)\nendif\nLOCAL_MODULE_CLASS := APPS\n\nintermediates := $(call local-intermediates-dir)\nintermediates.COMMON := $(call local-intermediates-dir,COMMON)\n\n# Package LOCAL_MODULE_TAGS default to optional\nLOCAL_MODULE_TAGS := $(strip $(LOCAL_MODULE_TAGS))\nifeq ($(LOCAL_MODULE_TAGS),)\nLOCAL_MODULE_TAGS := optional\nendif\n\nifeq ($(filter tests, $(LOCAL_MODULE_TAGS)),)\n# Force localization check if it's not tagged as tests.\nLOCAL_AAPT_FLAGS := $(LOCAL_AAPT_FLAGS) -z\nendif\n\nneed_compile_asset :=\nifeq (,$(LOCAL_ASSET_DIR))\nLOCAL_ASSET_DIR := $(LOCAL_PATH)/assets\nelse\nneed_compile_asset := true\nendif\n\n# LOCAL_RESOURCE_DIR may point to resource generated during the build\nneed_compile_res :=\nifeq (,$(LOCAL_RESOURCE_DIR))\n  LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res\nelse\n  need_compile_res := true\n  LOCAL_RESOURCE_DIR := $(foreach d,$(LOCAL_RESOURCE_DIR),$(call clean-path,$(d)))\nendif\n\n# If LOCAL_MODULE matches a rule in PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES,\n# override the manifest package name by the (first) rule matched\noverride_manifest_name := $(strip $(word 1,\\\n  $(foreach rule,$(PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES),\\\n    $(eval _pkg_name_pat := $(call word-colon,1,$(rule)))\\\n    $(eval _manifest_name_pat := $(call word-colon,2,$(rule)))\\\n    $(if $(filter $(_pkg_name_pat),$(LOCAL_MODULE)),\\\n      $(patsubst $(_pkg_name_pat),$(_manifest_name_pat),$(LOCAL_MODULE))\\\n     )\\\n   )\\\n))\n\nifneq (,$(override_manifest_name))\n# Note: this can override LOCAL_MANIFEST_PACKAGE_NAME value set in Android.mk\nLOCAL_MANIFEST_PACKAGE_NAME := $(override_manifest_name)\nendif\n\ninclude $(BUILD_SYSTEM)/force_aapt2.mk\n# validate that app contains a manifest file for aapt2\nifeq (,$(strip $(LOCAL_MANIFEST_FILE)$(LOCAL_FULL_MANIFEST_FILE)))\n  ifeq (,$(wildcard $(LOCAL_PATH)/AndroidManifest.xml))\n    $(call pretty-error,App missing manifest file which is required by aapt2. \\\nProvide a manifest file by either setting LOCAL_MANIFEST_FILE in Android.mk \\\nor via a AndroidManifest.xml in this directory)\n  endif\nendif\n\n# Process Support Library dependencies.\ninclude $(BUILD_SYSTEM)/support_libraries.mk\n\n# Determine whether auto-RRO is enabled for this package.\nenforce_rro_enabled :=\nifeq (,$(filter tests,$(LOCAL_MODULE_TAGS)))\n  ifneq (,$(filter *, $(PRODUCT_ENFORCE_RRO_TARGETS)))\n    # * means all system and system_ext APKs, so enable conditionally based on module path.\n\n    # Note that base_rules.mk has not yet been included, so it's likely that only\n    # one of LOCAL_MODULE_PATH and the LOCAL_X_MODULE flags has been set.\n    ifeq (,$(LOCAL_MODULE_PATH))\n      non_rro_target_module := $(filter true,\\\n          $(LOCAL_ODM_MODULE) \\\n          $(LOCAL_OEM_MODULE) \\\n          $(LOCAL_PRODUCT_MODULE) \\\n          $(LOCAL_PROPRIETARY_MODULE) \\\n          $(LOCAL_VENDOR_MODULE))\n      enforce_rro_enabled := $(if $(non_rro_target_module),,true)\n    else ifneq ($(filter $(TARGET_OUT)/%,$(LOCAL_MODULE_PATH)),)\n      enforce_rro_enabled := true\n    endif\n  else ifneq (,$(filter $(LOCAL_PACKAGE_NAME), $(PRODUCT_ENFORCE_RRO_TARGETS)))\n    enforce_rro_enabled := true\n  endif\nendif\n\nproduct_package_overlays := $(strip \\\n    $(wildcard $(foreach dir, $(PRODUCT_PACKAGE_OVERLAYS), \\\n      $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))))\ndevice_package_overlays := $(strip \\\n    $(wildcard $(foreach dir, $(DEVICE_PACKAGE_OVERLAYS), \\\n      $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))))\n\nstatic_resource_overlays :=\nruntime_resource_overlays_product :=\nruntime_resource_overlays_vendor :=\nifdef enforce_rro_enabled\n  ifneq ($(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS),)\n    # The PRODUCT_ exclusion variable applies to both inclusion variables..\n    static_resource_overlays += $(filter $(addsuffix %,$(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS)),$(product_package_overlays))\n    static_resource_overlays += $(filter $(addsuffix %,$(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS)),$(device_package_overlays))\n  endif\n  runtime_resource_overlays_product := $(filter-out $(static_resource_overlays),$(product_package_overlays))\n  runtime_resource_overlays_vendor := $(filter-out $(static_resource_overlays),$(device_package_overlays))\nelse\n  static_resource_overlays := $(product_package_overlays) $(device_package_overlays)\nendif\n\n# Add the static overlays. Auto-RRO is created later, as it depends on\n# other logic in this file.\nLOCAL_RESOURCE_DIR := $(static_resource_overlays) $(LOCAL_RESOURCE_DIR)\n\nall_assets := $(strip \\\n    $(foreach dir, $(LOCAL_ASSET_DIR), \\\n      $(addprefix $(dir)/, \\\n        $(patsubst assets/%,%, \\\n          $(call find-subdir-assets, $(dir)) \\\n         ) \\\n       ) \\\n     ))\n\nifneq ($(all_assets),)\nneed_compile_asset := true\nendif\n\nmy_res_package :=\n# In aapt2 the last takes precedence.\nmy_resource_dirs := $(call reverse-list,$(LOCAL_RESOURCE_DIR))\nmy_res_dir :=\nmy_overlay_res_dirs :=\n\nifneq ($(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),)\n# If we are using static android libraries, every source file becomes an overlay.\n# This is to emulate old AAPT behavior which simulated library support.\nmy_res_dir :=\nmy_overlay_res_dirs := $(my_resource_dirs)\nelse\n# Without static libraries, the first directory is our directory, which can then be\n# overlaid by the rest. (First directory in my_resource_dirs is last directory in\n# $(LOCAL_RESOURCE_DIR) due to it being reversed.\nmy_res_dir := $(firstword $(my_resource_dirs))\nmy_overlay_res_dirs := $(wordlist 2,999,$(my_resource_dirs))\nendif\n\nmy_overlay_resources := $(strip \\\n  $(foreach d,$(my_overlay_res_dirs),\\\n    $(addprefix $(d)/, \\\n        $(call find-subdir-assets,$(d)))))\n\nmy_res_resources := $(if $(my_res_dir),$(strip \\\n    $(addprefix $(my_res_dir)/, \\\n        $(call find-subdir-assets,$(my_res_dir)))))\n\nall_resources := $(strip $(my_res_resources) $(my_overlay_resources))\n\n# The linked resource package.\nmy_res_package := $(intermediates.COMMON)/package-res.apk\nLOCAL_INTERMEDIATE_TARGETS += $(my_res_package)\n\nmy_bundle_module := $(intermediates.COMMON)/base.zip\nLOCAL_INTERMEDIATE_TARGETS += $(my_bundle_module)\n\n# Always run aapt2, because we need to at least compile the AndroidManifest.xml.\nneed_compile_res := true\n\nifneq ($(all_resources),)\n  need_compile_res := true\nendif\n\nall_res_assets := $(strip $(all_assets) $(all_resources))\n\n# If no assets or resources were found, clear the directory variables so\n# we don't try to build them.\nifneq (true,$(need_compile_asset))\nLOCAL_ASSET_DIR:=\nendif\nifneq (true,$(need_compile_res))\nLOCAL_RESOURCE_DIR:=\nR_file_stamp :=\nelse\n# Make sure that R_file_stamp inherits the proper PRIVATE vars.\n# If R.stamp moves, be sure to update the framework makefile,\n# which has intimate knowledge of its location.\nR_file_stamp := $(intermediates.COMMON)/src/R.stamp\nLOCAL_INTERMEDIATE_TARGETS += $(R_file_stamp)\nendif\n\nifdef LOCAL_COMPRESSED_MODULE\nifneq (true,$(LOCAL_COMPRESSED_MODULE))\n$(call pretty-error, Unknown value for LOCAL_COMPRESSED_MODULE $(LOCAL_COMPRESSED_MODULE))\nendif\nendif\n\nifdef LOCAL_COMPRESSED_MODULE\nPACKAGES.$(LOCAL_PACKAGE_NAME).COMPRESSED := gz\nLOCAL_BUILT_MODULE_STEM := package.apk.gz\nLOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk.gz\nelse  # !LOCAL_COMPRESSED_MODULE\nLOCAL_BUILT_MODULE_STEM := package.apk\nLOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk\nendif\n\nLOCAL_PROGUARD_ENABLED:=$(strip $(LOCAL_PROGUARD_ENABLED))\nifndef LOCAL_PROGUARD_ENABLED\nifneq ($(DISABLE_PROGUARD),true)\n    LOCAL_PROGUARD_ENABLED :=full\nendif\nendif\nifeq ($(LOCAL_PROGUARD_ENABLED),disabled)\n    # the package explicitly request to disable proguard.\n    LOCAL_PROGUARD_ENABLED :=\nendif\nproguard_options_file :=\nifneq ($(LOCAL_PROGUARD_ENABLED),custom)\nifeq ($(need_compile_res),true)\n    proguard_options_file := $(intermediates.COMMON)/proguard_options\nendif # need_compile_res\nendif # !custom\nLOCAL_PROGUARD_FLAGS := $(addprefix -include ,$(proguard_options_file)) $(LOCAL_PROGUARD_FLAGS)\nLOCAL_PROGUARD_FLAGS_DEPS += $(proguard_options_file)\n\nifeq (true,$(EMMA_INSTRUMENT))\nifndef LOCAL_EMMA_INSTRUMENT\n# No jacoco for test apks.\nifeq (,$(LOCAL_INSTRUMENTATION_FOR))\nLOCAL_EMMA_INSTRUMENT := true\nendif # No test apk\nendif # LOCAL_EMMA_INSTRUMENT is not set\nelse\nLOCAL_EMMA_INSTRUMENT := false\nendif # EMMA_INSTRUMENT is true\n\nifeq (true,$(LOCAL_EMMA_INSTRUMENT))\nifeq (true,$(EMMA_INSTRUMENT_STATIC))\nifneq ($(LOCAL_SRC_FILES)$(LOCAL_SRCJARS)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),)\n# Only add jacocoagent if the package contains some java code\nLOCAL_STATIC_JAVA_LIBRARIES += jacocoagent\n# Exclude jacoco classes from proguard\nLOCAL_PROGUARD_FLAGS += -include $(BUILD_SYSTEM)/proguard.jacoco.flags\nLOCAL_PROGUARD_FLAGS_DEPS += $(BUILD_SYSTEM)/proguard.jacoco.flags\nendif # Contains java code\nelse\nifdef LOCAL_SDK_VERSION\nifdef TARGET_BUILD_APPS\n# In unbundled build, merge the coverage library into the apk.\nifneq ($(LOCAL_SRC_FILES)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),)\n# Only add jacocoagent if the package contains some java code\nLOCAL_STATIC_JAVA_LIBRARIES += jacocoagent\n# Exclude jacoco classes from proguard\nLOCAL_PROGUARD_FLAGS += -include $(BUILD_SYSTEM)/proguard.jacoco.flags\nLOCAL_PROGUARD_FLAGS_DEPS += $(BUILD_SYSTEM)/proguard.jacoco.flags\nendif # Contains java code\nendif # TARGET_BUILD_APPS\nendif # LOCAL_SDK_VERSION\nendif # EMMA_INSTRUMENT_STATIC\nendif # LOCAL_EMMA_INSTRUMENT\n\nrs_compatibility_jni_libs :=\n\n# If the module is a compressed module, we don't pre-opt it because its final\n# installation location will be the data partition.\nifdef LOCAL_COMPRESSED_MODULE\nLOCAL_DEX_PREOPT := false\nendif\n\n# Default to use uncompressed native libraries in APKs if minSdkVersion >= marshmallow\nifndef LOCAL_USE_EMBEDDED_NATIVE_LIBS\n  LOCAL_USE_EMBEDDED_NATIVE_LIBS := $(call math_gt_or_eq, \\\n    $(patsubst $(PLATFORM_VERSION_CODENAME),100,$(call module-min-sdk-version)),23)\nendif\n\ninclude $(BUILD_SYSTEM)/android_manifest.mk\n\nresource_export_package :=\n\ninclude $(BUILD_SYSTEM)/java_renderscript.mk\n\ninclude $(BUILD_SYSTEM)/aapt_flags.mk\n\nifeq ($(need_compile_res),true)\n\n###############################\n## APK splits\nbuilt_apk_splits :=\ninstalled_apk_splits :=\nmy_apk_split_configs :=\n\nifdef LOCAL_PACKAGE_SPLITS\nifdef LOCAL_COMPRESSED_MODULE\n$(error $(LOCAL_MODULE): LOCAL_COMPRESSED_MODULE is not currently supported for split installs)\nendif  # LOCAL_COMPRESSED_MODULE\n\nmy_apk_split_configs := $(LOCAL_PACKAGE_SPLITS)\nmy_split_suffixes := $(subst $(comma),_,$(my_apk_split_configs))\nbuilt_apk_splits := $(foreach s,$(my_split_suffixes),$(intermediates)/package_$(s).apk)\nendif\n\n$(R_file_stamp) $(my_res_package): PRIVATE_AAPT_FLAGS := $(filter-out --legacy,$(LOCAL_AAPT_FLAGS))\n$(R_file_stamp) $(my_res_package): PRIVATE_TARGET_AAPT_CHARACTERISTICS := $(TARGET_AAPT_CHARACTERISTICS)\n$(R_file_stamp) $(my_res_package): PRIVATE_MANIFEST_PACKAGE_NAME := $(LOCAL_MANIFEST_PACKAGE_NAME)\n$(R_file_stamp) $(my_res_package): PRIVATE_MANIFEST_INSTRUMENTATION_FOR := $(LOCAL_MANIFEST_INSTRUMENTATION_FOR)\n\n###############################\n## AAPT2\n\nmy_compiled_res_base_dir := $(intermediates.COMMON)/flat-res\nifneq (,$(filter-out current,$(renderscript_target_api)))\n  ifneq ($(call math_gt_or_eq,$(renderscript_target_api),21),true)\n    my_generated_res_zips := $(rs_generated_res_zip)\n  endif  # renderscript_target_api < 21\nendif  # renderscript_target_api is set\nmy_asset_dirs := $(LOCAL_ASSET_DIR)\nmy_full_asset_paths := $(all_assets)\n\n# Add AAPT2 link specific flags.\nifndef LOCAL_AAPT_NAMESPACES\n  $(my_res_package): PRIVATE_AAPT_FLAGS += --no-static-lib-packages\nendif\n\ninclude $(BUILD_SYSTEM)/aapt2.mk\n\nendif  # need_compile_res\n\nmy_dex_jar := $(intermediates.COMMON)/dex.jar\n\ncalled_from_package_internal := true\n#################################\ninclude $(BUILD_SYSTEM)/java.mk\n#################################\ncalled_from_package_internal :=\n\nifeq ($(need_compile_res),true)\n\n# Other modules should depend on the BUILT module if\n# they want to use this module's R.java file.\n$(LOCAL_BUILT_MODULE): $(R_file_stamp)\n\nifneq ($(full_classes_jar),)\n# The R.java file must exist by the time the java source\n# list is generated\n$(java_source_list_file): $(R_file_stamp)\nendif\n\nendif # need_compile_res\n\nLOCAL_SDK_RES_VERSION:=$(strip $(LOCAL_SDK_RES_VERSION))\nifeq ($(LOCAL_SDK_RES_VERSION),)\n  LOCAL_SDK_RES_VERSION:=$(LOCAL_SDK_VERSION)\nendif\n\n$(LOCAL_INTERMEDIATE_TARGETS): \\\n    PRIVATE_ANDROID_MANIFEST := $(full_android_manifest)\n\nframework_res_package_export :=\n\nifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true)\n# Most packages should link against the resources defined by framework-res.\n# Even if they don't have their own resources, they may use framework\n# resources.\nifeq ($(LOCAL_SDK_RES_VERSION),core_current)\n# core_current doesn't contain any framework resources.\nelse ifneq ($(filter-out current system_current test_current,$(LOCAL_SDK_RES_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_RES_VERSION))),)\n# for released sdk versions, the platform resources were built into android.jar.\nframework_res_package_export := \\\n    $(call resolve-prebuilt-sdk-jar-path,$(LOCAL_SDK_RES_VERSION))\nelse # LOCAL_SDK_RES_VERSION\nframework_res_package_export := \\\n    $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk\nendif # LOCAL_SDK_RES_VERSION\nendif # LOCAL_NO_STANDARD_LIBRARIES\n\nall_library_res_package_exports := \\\n    $(framework_res_package_export) \\\n    $(foreach lib,$(LOCAL_RES_LIBRARIES),\\\n        $(call intermediates-dir-for,APPS,$(lib),,COMMON)/package-export.apk)\n\nall_library_res_package_export_deps := \\\n    $(framework_res_package_export) \\\n    $(foreach lib,$(LOCAL_RES_LIBRARIES),\\\n        $(call intermediates-dir-for,APPS,$(lib),,COMMON)/src/R.stamp)\n$(resource_export_package) $(R_file_stamp) $(LOCAL_BUILT_MODULE): $(all_library_res_package_export_deps)\n$(LOCAL_INTERMEDIATE_TARGETS): \\\n    PRIVATE_AAPT_INCLUDES := $(all_library_res_package_exports)\n\n$(my_res_package) : $(all_library_res_package_export_deps)\n\nifneq ($(full_classes_jar),)\n$(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := $(built_dex)\n# Use the jarjar processed arhive as the initial package file.\n$(LOCAL_BUILT_MODULE): PRIVATE_SOURCE_ARCHIVE := $(full_classes_pre_proguard_jar)\n$(LOCAL_BUILT_MODULE): $(built_dex) $(full_classes_pre_proguard_jar)\nelse\n$(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE :=\n$(LOCAL_BUILT_MODULE): PRIVATE_SOURCE_ARCHIVE :=\nendif # full_classes_jar\n\ninclude $(BUILD_SYSTEM)/install_jni_libs.mk\n\n# Pick a key to sign the package with.  If this package hasn't specified\n# an explicit certificate, use the default.\n# Secure release builds will have their packages signed after the fact,\n# so it's ok for these private keys to be in the clear.\nifeq ($(LOCAL_CERTIFICATE),)\n    LOCAL_CERTIFICATE := $(DEFAULT_SYSTEM_DEV_CERTIFICATE)\nendif\n\nifeq ($(LOCAL_CERTIFICATE),EXTERNAL)\n  # The special value \"EXTERNAL\" means that we will sign it with the\n  # default devkey, apply predexopt, but then expect the final .apk\n  # (after dexopting) to be signed by an outside tool.\n  LOCAL_CERTIFICATE := $(DEFAULT_SYSTEM_DEV_CERTIFICATE)\n  PACKAGES.$(LOCAL_PACKAGE_NAME).EXTERNAL_KEY := 1\nendif\n\n# If this is not an absolute certificate, assign it to a generic one.\nifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./)\n    LOCAL_CERTIFICATE := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))$(LOCAL_CERTIFICATE)\nendif\ninclude $(BUILD_SYSTEM)/app_certificate_validate.mk\nprivate_key := $(LOCAL_CERTIFICATE).pk8\ncertificate := $(LOCAL_CERTIFICATE).x509.pem\nadditional_certificates := $(foreach c,$(LOCAL_ADDITIONAL_CERTIFICATES), $(c).x509.pem $(c).pk8)\n\n$(LOCAL_BUILT_MODULE): $(private_key) $(certificate) $(SIGNAPK_JAR) $(SIGNAPK_JNI_LIBRARY_PATH)\n$(LOCAL_BUILT_MODULE): PRIVATE_PRIVATE_KEY := $(private_key)\n$(LOCAL_BUILT_MODULE): PRIVATE_CERTIFICATE := $(certificate)\n\nPACKAGES.$(LOCAL_PACKAGE_NAME).PRIVATE_KEY := $(private_key)\nPACKAGES.$(LOCAL_PACKAGE_NAME).CERTIFICATE := $(certificate)\n\n$(LOCAL_BUILT_MODULE): $(additional_certificates)\n$(LOCAL_BUILT_MODULE): PRIVATE_ADDITIONAL_CERTIFICATES := $(additional_certificates)\n\n$(LOCAL_BUILT_MODULE): $(LOCAL_CERTIFICATE_LINEAGE)\n$(LOCAL_BUILT_MODULE): PRIVATE_CERTIFICATE_LINEAGE := $(LOCAL_CERTIFICATE_LINEAGE)\n\n$(LOCAL_BUILT_MODULE): PRIVATE_ROTATION_MIN_SDK_VERSION := $(LOCAL_ROTATION_MIN_SDK_VERSION)\n\n# Set a actual_partition_tag (calculated in base_rules.mk) for the package.\nPACKAGES.$(LOCAL_PACKAGE_NAME).PARTITION := $(actual_partition_tag)\n\n# Define the rule to build the actual package.\n# PRIVATE_JNI_SHARED_LIBRARIES is a list of <abi>:<path_of_built_lib>.\n$(LOCAL_BUILT_MODULE): PRIVATE_JNI_SHARED_LIBRARIES := $(jni_shared_libraries_with_abis)\n# PRIVATE_JNI_SHARED_LIBRARIES_ABI is a list of ABI names.\n$(LOCAL_BUILT_MODULE): PRIVATE_JNI_SHARED_LIBRARIES_ABI := $(jni_shared_libraries_abis)\nifneq ($(TARGET_BUILD_APPS),)\n    # Include all resources for unbundled apps.\n    LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true\nendif\nifeq ($(LOCAL_AAPT_INCLUDE_ALL_RESOURCES),true)\n    $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_CONFIG :=\n    $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_PREF_CONFIG :=\nelse\n    $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_CONFIG := $(PRODUCT_AAPT_CONFIG)\nifdef LOCAL_PACKAGE_SPLITS\n    $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_PREF_CONFIG :=\nelse\n    $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := $(PRODUCT_AAPT_PREF_CONFIG)\nendif\nendif\n\n# Run veridex on product, system_ext and vendor modules.\n# We skip it for unbundled app builds where we cannot build veridex.\nmodule_run_appcompat :=\nifeq (true,$(non_system_module))\nifeq (,$(TARGET_BUILD_APPS))  # ! unbundled app build\nifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)\n  module_run_appcompat := true\nendif\nendif\nendif\n\nifeq ($(module_run_appcompat),true)\n$(LOCAL_BUILT_MODULE) : $(appcompat-files)\n$(LOCAL_BUILT_MODULE): PRIVATE_INSTALLED_MODULE := $(LOCAL_INSTALLED_MODULE)\nendif\n\n$(LOCAL_BUILT_MODULE): PRIVATE_RESOURCE_INTERMEDIATES_DIR := $(intermediates.COMMON)/resources\n$(LOCAL_BUILT_MODULE) : $(jni_shared_libraries)\n$(LOCAL_BUILT_MODULE) : $(JAR_ARGS) $(SOONG_ZIP) $(MERGE_ZIPS) $(ZIP2ZIP)\n$(LOCAL_BUILT_MODULE): PRIVATE_RES_PACKAGE := $(my_res_package)\n$(LOCAL_BUILT_MODULE) : $(my_res_package) $(AAPT2)\nifdef LOCAL_COMPRESSED_MODULE\n$(LOCAL_BUILT_MODULE) : $(GZIP)\nendif\nifeq (true, $(LOCAL_UNCOMPRESS_DEX))\n$(LOCAL_BUILT_MODULE) : $(ZIP2ZIP)\nendif\nifeq ($(full_classes_jar),)\n  # We don't build jar, need to add the Java resources here.\n  $(LOCAL_BUILT_MODULE): $(java_resource_sources)\nendif\n$(LOCAL_BUILT_MODULE): PRIVATE_USE_EMBEDDED_NATIVE_LIBS := $(LOCAL_USE_EMBEDDED_NATIVE_LIBS)\n$(LOCAL_BUILT_MODULE):\n\t@echo \"target Package: $(PRIVATE_MODULE) ($@)\"\n\trm -rf $@.parts\n\tmkdir -p $@.parts\n\tcp -f $(PRIVATE_RES_PACKAGE) $@.parts/apk.zip\nifneq ($(jni_shared_libraries),)\n\t$(call create-jni-shared-libs-package,$@.parts/jni.zip,$(PRIVATE_USE_EMBEDDED_NATIVE_LIBS))\nendif\nifeq ($(full_classes_jar),)\n# We don't build jar, need to add the Java resources here.\n\t$(if $(PRIVATE_EXTRA_JAR_ARGS),$(call create-java-resources-jar,$@.parts/res.zip))\nelse  # full_classes_jar\n\t$(call create-dex-jar,$@.parts/dex.zip,$(PRIVATE_DEX_FILE))\n\t$(call extract-resources-jar,$@.parts/res.zip,$(PRIVATE_SOURCE_ARCHIVE))\nendif  # full_classes_jar\n\t$(MERGE_ZIPS) $@ $@.parts/*.zip\n\trm -rf $@.parts\nifeq (true, $(LOCAL_UNCOMPRESS_DEX))\n\t@# No need to align, sign-package below will do it.\n\t$(uncompress-dexs)\nendif\n# Run appcompat before signing.\nifeq ($(module_run_appcompat),true)\n\t$(appcompat-header)\n\t$(run-appcompat)\nendif  # module_run_appcompat\n\t$(sign-package)\nifdef LOCAL_COMPRESSED_MODULE\n\t$(compress-package)\nendif  # LOCAL_COMPRESSED_MODULE\n\nmy_package_res_pb := $(intermediates.COMMON)/package-res.pb.apk\n$(my_package_res_pb): $(my_res_package) $(AAPT2)\n\t$(AAPT2) convert --output-format proto $< -o $@\n\n$(my_bundle_module): $(my_package_res_pb)\n$(my_bundle_module): PRIVATE_RES_PACKAGE := $(my_package_res_pb)\n\n$(my_bundle_module): $(jni_shared_libraries)\n$(my_bundle_module): PRIVATE_JNI_SHARED_LIBRARIES := $(jni_shared_libraries_with_abis)\n$(my_bundle_module): PRIVATE_JNI_SHARED_LIBRARIES_ABI := $(jni_shared_libraries_abis)\n\nifneq ($(full_classes_jar),)\n  $(my_bundle_module): PRIVATE_DEX_FILE := $(built_dex)\n  # Use the jarjar processed archive as the initial package file.\n  $(my_bundle_module): PRIVATE_SOURCE_ARCHIVE := $(full_classes_pre_proguard_jar)\n  $(my_bundle_module): $(built_dex)\nelse\n  $(my_bundle_module): PRIVATE_DEX_FILE :=\n  $(my_bundle_module): PRIVATE_SOURCE_ARCHIVE :=\n  # We don't build jar, need to add the Java resources here.\n  $(my_bundle_module): $(java_resource_sources)\nendif # full_classes_jar\n\n$(my_bundle_module): $(MERGE_ZIPS) $(SOONG_ZIP) $(ZIP2ZIP)\n\t@echo \"target Bundle: $(PRIVATE_MODULE) ($@)\"\n\trm -rf $@.parts\n\tmkdir -p $@.parts\n\t$(ZIP2ZIP) -i $(PRIVATE_RES_PACKAGE) -o $@.parts/apk.zip AndroidManifest.xml:manifest/AndroidManifest.xml resources.pb \"res/**/*\" \"assets/**/*\"\n      ifneq ($(jni_shared_libraries),)\n\t  $(call create-jni-shared-libs-package,$@.parts/jni.zip)\n      endif\n      ifeq ($(full_classes_jar),)\n      # We don't build jar, need to add the Java resources here.\n\t  $(if $(PRIVATE_EXTRA_JAR_ARGS),\\\n\t    $(call create-java-resources-jar,$@.parts/res.zip) && \\\n\t    $(ZIP2ZIP) -i $@.parts/res.zip -o $@.parts/res.zip.tmp \"**/*:root/\" && \\\n\t    mv -f $@.parts/res.zip.tmp $@.parts/res.zip)\n      else  # full_classes_jar\n\t  $(call create-dex-jar,$@.parts/dex.zip,$(PRIVATE_DEX_FILE))\n\t  $(ZIP2ZIP) -i $@.parts/dex.zip -o $@.parts/dex.zip.tmp \"classes*.dex:dex/\"\n\t  mv -f $@.parts/dex.zip.tmp $@.parts/dex.zip\n\t  $(call extract-resources-jar,$@.parts/res.zip,$(PRIVATE_SOURCE_ARCHIVE))\n\t  $(ZIP2ZIP) -i $@.parts/res.zip -o $@.parts/res.zip.tmp \"**/*:root/\"\n\t  mv -f $@.parts/res.zip.tmp $@.parts/res.zip\n      endif  # full_classes_jar\n\t$(MERGE_ZIPS) $@ $@.parts/*.zip\n\trm -rf $@.parts\nALL_MODULES.$(my_register_name).BUNDLE := $(my_bundle_module)\n\nifdef TARGET_BUILD_APPS\n  ifdef LOCAL_DPI_VARIANTS\n    $(call pretty-error,Building DPI-specific APKs is no longer supported)\n  endif\nendif\n\n###############################\n## Rule to build a jar containing dex files to dexpreopt without waiting for\n## the APK\nifdef LOCAL_DEX_PREOPT\n  $(my_dex_jar): PRIVATE_DEX_FILE := $(built_dex)\n  $(my_dex_jar): $(built_dex) $(SOONG_ZIP)\n\t$(hide) mkdir -p $(dir $@) && rm -f $@\n\t$(call create-dex-jar,$@,$(PRIVATE_DEX_FILE))\nendif\n\n###############################\n## APK splits\nifdef LOCAL_PACKAGE_SPLITS\n# The splits should have been built in the same command building the base apk.\n# This rule just runs signing.\n# Note that we explicily check the existence of the split apk and remove the\n# built base apk if the split apk isn't there.\n# That way the build system will rerun the aapt after the user changes the splitting parameters.\n$(built_apk_splits): PRIVATE_PRIVATE_KEY := $(private_key)\n$(built_apk_splits): PRIVATE_CERTIFICATE := $(certificate)\n$(built_apk_splits) : $(intermediates)/%.apk : $(LOCAL_BUILT_MODULE)\n\t$(hide) if [ ! -f $@ ]; then \\\n\t  echo 'No $@ generated, check your apk splitting parameters.' 1>&2; \\\n\t  rm $<; exit 1; \\\n\tfi\n\t$(sign-package)\n\n# Rules to install the splits\ninstalled_apk_splits := $(foreach s,$(my_split_suffixes),$(my_module_path)/$(LOCAL_MODULE)_$(s).apk)\n$(installed_apk_splits) : $(my_module_path)/$(LOCAL_MODULE)_%.apk : $(intermediates)/package_%.apk\n\t@echo \"Install: $@\"\n\t$(copy-file-to-new-target)\n\n# Register the additional built and installed files.\nALL_MODULES.$(my_register_name).INSTALLED += $(installed_apk_splits)\nALL_MODULES.$(my_register_name).BUILT_INSTALLED += \\\n  $(foreach s,$(my_split_suffixes),$(intermediates)/package_$(s).apk:$(my_module_path)/$(LOCAL_MODULE)_$(s).apk)\n\n# Make sure to install the splits when you run \"make <module_name>\".\n$(my_all_targets): $(installed_apk_splits)\n\nifdef LOCAL_COMPATIBILITY_SUITE\n\n$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n  $(eval my_compat_dist_$(suite) := $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \\\n    $(foreach s,$(my_split_suffixes),\\\n      $(call compat-copy-pair,$(intermediates)/package_$(s).apk,$(dir)/$(LOCAL_MODULE)_$(s).apk)))))\n\n$(call create-suite-dependencies)\n\nendif # LOCAL_COMPATIBILITY_SUITE\nendif # LOCAL_PACKAGE_SPLITS\n\n# Save information about this package\nPACKAGES.$(LOCAL_PACKAGE_NAME).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES))\nPACKAGES.$(LOCAL_PACKAGE_NAME).RESOURCE_FILES := $(all_resources)\n\nifneq ($(LOCAL_MODULE_STEM),)\n  PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE_STEM)\nelse\n  PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE)\nendif\n\nPACKAGES := $(PACKAGES) $(LOCAL_PACKAGE_NAME)\n\n# Reset internal variables.\nall_res_assets :=\n\nifneq (,$(runtime_resource_overlays_product)$(runtime_resource_overlays_vendor))\n  ifdef LOCAL_EXPORT_PACKAGE_RESOURCES\n    enforce_rro_use_res_lib := true\n  else\n    enforce_rro_use_res_lib := false\n  endif\n\n  ifdef LOCAL_MANIFEST_PACKAGE_NAME\n    enforce_rro_is_manifest_package_name := true\n    enforce_rro_manifest_package_info := $(LOCAL_MANIFEST_PACKAGE_NAME)\n  else\n    enforce_rro_is_manifest_package_name := false\n    enforce_rro_manifest_package_info := $(full_android_manifest)\n  endif\n\n  ifdef runtime_resource_overlays_product\n    $(call append_enforce_rro_sources, \\\n        $(my_register_name), \\\n        $(enforce_rro_is_manifest_package_name), \\\n        $(enforce_rro_manifest_package_info), \\\n        $(enforce_rro_use_res_lib), \\\n        $(runtime_resource_overlays_product), \\\n        product \\\n    )\n  endif\n  ifdef runtime_resource_overlays_vendor\n    $(call append_enforce_rro_sources, \\\n        $(my_register_name), \\\n        $(enforce_rro_is_manifest_package_name), \\\n        $(enforce_rro_manifest_package_info), \\\n        $(enforce_rro_use_res_lib), \\\n        $(runtime_resource_overlays_vendor), \\\n        vendor \\\n    )\n  endif\nendif\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=PACKAGE))"
  },
  {
    "path": "core/packaging/flags.mk",
    "content": "# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n#\n# This file is included by build/make/core/Makefile, and contains the logic for\n# the combined flags files.\n#\n\n# TODO: Should we do all of the images?\n_FLAG_PARTITIONS := product system system_ext vendor\n\n# -----------------------------------------------------------------\n# Aconfig Flags\n\n# Create a summary file of build flags for a single partition\n# $(1): built aconfig flags file (out)\n# $(2): installed aconfig flags file (out)\n# $(3): the partition (in)\n# $(4): input aconfig files for the partition (in)\ndefine generate-partition-aconfig-flag-file\n$(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))\n$(eval $(strip $(1)): PRIVATE_IN := $(strip $(4)))\n$(strip $(1)): $(ACONFIG) $(strip $(4))\n\tmkdir -p $$(dir $$(PRIVATE_OUT))\n\t$$(if $$(PRIVATE_IN), \\\n\t\t$$(ACONFIG) dump --dedup --format protobuf --out $$(PRIVATE_OUT) \\\n\t\t\t--filter container:$(strip $(3))+state:ENABLED \\\n\t\t\t--filter container:$(strip $(3))+permission:READ_WRITE \\\n\t\t\t$$(addprefix --cache ,$$(PRIVATE_IN)), \\\n\t\techo -n > $$(PRIVATE_OUT) \\\n\t)\n$(call copy-one-file, $(1), $(2))\nendef\n\n\n# Create a summary file of build flags for each partition\n# $(1): built aconfig flags file (out)\n# $(2): installed aconfig flags file (out)\n# $(3): input aconfig files for the partition (in)\ndefine generate-global-aconfig-flag-file\n$(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))\n$(eval $(strip $(1)): PRIVATE_IN := $(strip $(3)))\n$(strip $(1)): $(ACONFIG) $(strip $(3))\n\tmkdir -p $$(dir $$(PRIVATE_OUT))\n\t$$(if $$(PRIVATE_IN), \\\n\t\t$$(ACONFIG) dump --dedup --format protobuf --out $$(PRIVATE_OUT) \\\n\t\t\t$$(addprefix --cache ,$$(PRIVATE_IN)), \\\n\t\techo -n > $$(PRIVATE_OUT) \\\n\t)\n$(call copy-one-file, $(1), $(2))\nendef\n\ndefine out-dir-for-partition\n$(TARGET_COPY_OUT_$(call to-upper,$(1)))\nendef\n\n# Get the module names suitable for ALL_MODULES.* variables that are installed\n# for a given container\n# $(1): container\ndefine register-names-for-container\n$(sort $(foreach m,$(product_MODULES),\\\n\t$(if $(filter $(PRODUCT_OUT)/$(call out-dir-for-partition,$(strip $(1)))/%, $(ALL_MODULES.$(m).INSTALLED)), \\\n\t\t$(m)\n\t) \\\n))\nendef\n\n$(foreach partition, $(_FLAG_PARTITIONS), \\\n\t$(eval aconfig_flag_summaries_protobuf.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig_flags.pb) \\\n\t$(eval $(call generate-partition-aconfig-flag-file, \\\n\t\t\t$(TARGET_OUT_FLAGS)/$(partition)/aconfig_flags.pb, \\\n\t\t\t$(aconfig_flag_summaries_protobuf.$(partition)), \\\n\t\t\t$(partition), \\\n\t\t\t$(sort \\\n\t\t\t\t$(foreach m, $(call register-names-for-container, $(partition)), \\\n\t\t\t\t\t$(ALL_MODULES.$(m).ACONFIG_FILES) \\\n\t\t\t\t) \\\n\t\t\t) \\\n\t)) \\\n)\n\n# Collect the on-device flags into a single file, similar to all_aconfig_declarations.\nrequired_aconfig_flags_files := \\\n\t\t$(sort $(foreach partition, $(_FLAG_PARTITIONS), \\\n\t\t\t$(aconfig_flag_summaries_protobuf.$(partition)) \\\n\t\t))\n\n.PHONY: device_aconfig_declarations\ndevice_aconfig_declarations: $(PRODUCT_OUT)/device_aconfig_declarations.pb\n$(eval $(call generate-global-aconfig-flag-file, \\\n\t\t\t$(TARGET_OUT_FLAGS)/device_aconfig_declarations.pb, \\\n\t\t\t$(PRODUCT_OUT)/device_aconfig_declarations.pb, \\\n\t\t\t$(sort $(required_aconfig_flags_files)) \\\n)) \\\n\n# Create a set of storage file for each partition\n# $(1): built aconfig flags storage package map file (out)\n# $(2): built aconfig flags storage flag map file (out)\n# $(3): built aconfig flags storage flag val file (out)\n# $(4): built aconfig flags storage flag info file (out)\n# $(5): installed aconfig flags storage package map file (out)\n# $(6): installed aconfig flags storage flag map file (out)\n# $(7): installed aconfig flags storage flag value file (out)\n# $(8): installed aconfig flags storage flag info file (out)\n# $(9): input aconfig files for the partition (in)\n# $(10): partition name\ndefine generate-partition-aconfig-storage-file\n$(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))\n$(eval $(strip $(1)): PRIVATE_IN := $(strip $(9)))\n\nifneq (,$(RELEASE_FINGERPRINT_ACONFIG_PACKAGES))\nSTORAGE_FILE_VERSION := 2\nelse\nSTORAGE_FILE_VERSION := 1\nendif\n\n$(strip $(1)): $(ACONFIG) $(strip $(9))\n\tmkdir -p $$(dir $$(PRIVATE_OUT))\n\t$$(if $$(PRIVATE_IN), \\\n\t\t$$(ACONFIG) create-storage --container $(10) --file package_map --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \\\n\t\t\t$$(addprefix --cache ,$$(PRIVATE_IN)), \\\n\t)\n\ttouch $$(PRIVATE_OUT)\n$(eval $(strip $(2)): PRIVATE_OUT := $(strip $(2)))\n$(eval $(strip $(2)): PRIVATE_IN := $(strip $(9)))\n$(strip $(2)): $(ACONFIG) $(strip $(9))\n\tmkdir -p $$(dir $$(PRIVATE_OUT))\n\t$$(if $$(PRIVATE_IN), \\\n\t\t$$(ACONFIG) create-storage --container $(10) --file flag_map --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \\\n\t\t\t$$(addprefix --cache ,$$(PRIVATE_IN)), \\\n\t)\n\ttouch $$(PRIVATE_OUT)\n$(eval $(strip $(3)): PRIVATE_OUT := $(strip $(3)))\n$(eval $(strip $(3)): PRIVATE_IN := $(strip $(9)))\n$(strip $(3)): $(ACONFIG) $(strip $(9))\n\tmkdir -p $$(dir $$(PRIVATE_OUT))\n\t$$(if $$(PRIVATE_IN), \\\n\t\t$$(ACONFIG) create-storage --container $(10) --file flag_val --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \\\n\t\t$$(addprefix --cache ,$$(PRIVATE_IN)), \\\n\t)\n\ttouch $$(PRIVATE_OUT)\n$(eval $(strip $(4)): PRIVATE_OUT := $(strip $(4)))\n$(eval $(strip $(4)): PRIVATE_IN := $(strip $(9)))\n$(strip $(4)): $(ACONFIG) $(strip $(9))\n\tmkdir -p $$(dir $$(PRIVATE_OUT))\n\t$$(if $$(PRIVATE_IN), \\\n\t\t$$(ACONFIG) create-storage --container $(10) --file flag_info --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \\\n\t\t$$(addprefix --cache ,$$(PRIVATE_IN)), \\\n\t)\n\ttouch $$(PRIVATE_OUT)\n$(call copy-one-file, $(strip $(1)), $(5))\n$(call copy-one-file, $(strip $(2)), $(6))\n$(call copy-one-file, $(strip $(3)), $(7))\n$(call copy-one-file, $(strip $(4)), $(8))\nendef\n\nifeq ($(RELEASE_CREATE_ACONFIG_STORAGE_FILE),true)\n$(foreach partition, $(_FLAG_PARTITIONS), \\\n\t$(eval aconfig_storage_package_map.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/package.map) \\\n\t$(eval aconfig_storage_flag_map.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/flag.map) \\\n\t$(eval aconfig_storage_flag_val.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/flag.val) \\\n\t$(eval aconfig_storage_flag_info.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/flag.info) \\\n\t$(eval $(call generate-partition-aconfig-storage-file, \\\n\t\t\t\t$(TARGET_OUT_FLAGS)/$(partition)/package.map, \\\n\t\t\t\t$(TARGET_OUT_FLAGS)/$(partition)/flag.map, \\\n\t\t\t\t$(TARGET_OUT_FLAGS)/$(partition)/flag.val, \\\n\t\t\t\t$(TARGET_OUT_FLAGS)/$(partition)/flag.info, \\\n\t\t\t\t$(aconfig_storage_package_map.$(partition)), \\\n\t\t\t\t$(aconfig_storage_flag_map.$(partition)), \\\n\t\t\t\t$(aconfig_storage_flag_val.$(partition)), \\\n\t\t\t\t$(aconfig_storage_flag_info.$(partition)), \\\n\t\t\t\t$(aconfig_flag_summaries_protobuf.$(partition)), \\\n\t\t\t\t$(partition), \\\n\t)) \\\n)\nendif\n\n# -----------------------------------------------------------------\n# Install the ones we need for the configured product\nrequired_flags_files := \\\n\t\t$(sort $(foreach partition, $(_FLAG_PARTITIONS), \\\n\t\t\t$(build_flag_summaries.$(partition)) \\\n\t\t\t$(aconfig_flag_summaries_protobuf.$(partition)) \\\n\t\t\t$(aconfig_storage_package_map.$(partition)) \\\n\t\t\t$(aconfig_storage_flag_map.$(partition)) \\\n\t\t\t$(aconfig_storage_flag_val.$(partition)) \\\n\t\t\t$(aconfig_storage_flag_info.$(partition)) \\\n\t\t))\n\nALL_DEFAULT_INSTALLED_MODULES += $(required_flags_files)\nALL_FLAGS_FILES := $(required_flags_files)\n\n# TODO: Remove\n.PHONY: flag-files\nflag-files: $(required_flags_files)\n\n\n# Clean up\nout-dir-for-partition:=\nregister-names-for-container:=\nrequired_flags_files:=\nrequired_aconfig_flags_files:=\n$(foreach partition, $(_FLAG_PARTITIONS), \\\n\t$(eval build_flag_summaries.$(partition):=) \\\n\t$(eval aconfig_flag_summaries_protobuf.$(partition):=) \\\n\t$(eval aconfig_storage_package_map.$(partition):=) \\\n\t$(eval aconfig_storage_flag_map.$(partition):=) \\\n\t$(eval aconfig_storage_flag_val.$(partition):=) \\\n\t$(eval aconfig_storage_flag_info.$(partition):=) \\\n)\n"
  },
  {
    "path": "core/pathmap.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# A central place to define mappings to paths, to avoid hard-coding\n# them in Android.mk files. Not meant for header file include directories,\n# despite the fact that it was historically used for that!\n#\n# If you want this for a library's header files, use LOCAL_EXPORT_C_INCLUDES\n# instead. Then users of the library don't have to do anything --- they'll\n# have the correct header files added to their include path automatically.\n#\n\n#\n# TODO: Allow each project to define stuff like this before the per-module\n#       Android.mk files are included, so we don't need to have a big central\n#       list.\n#\n\n#\n# A mapping from shorthand names to include directories.\n#\npathmap_INCL := \\\n    camera:system/media/camera/include \\\n    frameworks-base:frameworks/base/include \\\n    frameworks-native:frameworks/native/include \\\n    libhardware:hardware/libhardware/include \\\n    libhardware_legacy:hardware/libhardware_legacy/include \\\n    libril:hardware/ril/include \\\n    system-core:system/core/include \\\n    audio:system/media/audio/include \\\n    audio-effects:system/media/audio_effects/include \\\n    audio-utils:system/media/audio_utils/include \\\n    audio-route:system/media/audio_route/include \\\n    wilhelm:frameworks/wilhelm/include \\\n    wilhelm-ut:frameworks/wilhelm/src/ut \\\n    mediandk:frameworks/av/media/ndk/\n\n#\n# Returns the path to the requested module's include directory,\n# relative to the root of the source tree.  Does not handle external\n# modules.\n#\n# $(1): a list of modules (or other named entities) to find the includes for\n#\ndefine include-path-for\n$(foreach n,$(1),$(patsubst $(n):%,%,$(filter $(n):%,$(pathmap_INCL))))\nendef\n\n#\n# A list of all source roots under frameworks/base, which will be\n# built into the android.jar.\n#\nFRAMEWORKS_BASE_SUBDIRS := \\\n\t$(addsuffix /java, \\\n\t    core \\\n\t    graphics \\\n\t    location \\\n\t    media \\\n\t    media/mca/effect \\\n\t    media/mca/filterfw \\\n\t    media/mca/filterpacks \\\n\t    drm \\\n\t    opengl \\\n\t    sax \\\n\t    telecomm \\\n\t    telephony \\\n\t    wifi \\\n\t    lowpan \\\n\t    keystore \\\n\t    rs \\\n\t )\n\n#\n# A version of FRAMEWORKS_BASE_SUBDIRS that is expanded to full paths from\n# the root of the tree.  This currently needs to be here so that other libraries\n# and apps can find the .aidl files in the framework, though we should really\n# figure out a better way to do this.\n#\nFRAMEWORKS_BASE_JAVA_SRC_DIRS := \\\n\t$(addprefix frameworks/base/,$(FRAMEWORKS_BASE_SUBDIRS))\n"
  },
  {
    "path": "core/phony_package.mk",
    "content": "$(call record-module-type,PHONY_PACKAGE)\nifneq ($(strip $(LOCAL_SRC_FILES)),)\n$(error LOCAL_SRC_FILES are not allowed for phony packages)\nendif\n\nLOCAL_MODULE_CLASS := FAKE\nLOCAL_MODULE_SUFFIX := -timestamp\n\ninclude $(BUILD_SYSTEM)/base_rules.mk\n\n$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)\n\t$(hide) echo \"Fake: $@\"\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) touch $@\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=PHONY_PACKAGE))"
  },
  {
    "path": "core/prebuilt.mk",
    "content": "###########################################################\n## Standard rules for copying files that are prebuilt\n##\n## Additional inputs from base_rules.make:\n## None.\n##\n###########################################################\n$(call record-module-type,PREBUILT)\n\nifdef LOCAL_IS_HOST_MODULE\n  my_prefix := HOST_\n  LOCAL_HOST_PREFIX :=\nelse\n  my_prefix := TARGET_\nendif\n\ninclude $(BUILD_SYSTEM)/multilib.mk\n\nmy_skip_non_preferred_arch :=\n\n# check if first arch is supported\nLOCAL_2ND_ARCH_VAR_PREFIX :=\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\nifeq ($(my_module_arch_supported),true)\n# first arch is supported\ninclude $(BUILD_SYSTEM)/prebuilt_internal.mk\nifneq ($(my_module_multilib),both)\nmy_skip_non_preferred_arch := true\nendif # $(my_module_multilib)\n# For apps, we don't want to set up the prebuilt apk rule twice even if \"LOCAL_MULTILIB := both\".\nifeq (APPS,$(LOCAL_MODULE_CLASS))\nmy_skip_non_preferred_arch := true\nendif\nendif # $(my_module_arch_supported)\n\nifndef my_skip_non_preferred_arch\nifneq (,$($(my_prefix)2ND_ARCH))\n# check if secondary arch is supported\nLOCAL_2ND_ARCH_VAR_PREFIX := $($(my_prefix)2ND_ARCH_VAR_PREFIX)\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\nifeq ($(my_module_arch_supported),true)\n# secondary arch is supported\nLOCAL_BUILT_MODULE :=\nLOCAL_INSTALLED_MODULE :=\nLOCAL_INTERMEDIATE_TARGETS :=\ninclude $(BUILD_SYSTEM)/prebuilt_internal.mk\nendif # $(my_module_arch_supported)\nendif # $($(my_prefix)2ND_ARCH)\nendif # $(my_skip_non_preferred_arch) not true\n\nLOCAL_2ND_ARCH_VAR_PREFIX :=\n\nmy_module_arch_supported :=\n"
  },
  {
    "path": "core/prebuilt_internal.mk",
    "content": "###########################################################\n## Standard rules for copying files that are prebuilt\n##\n## Additional inputs from base_rules.make:\n## None.\n##\n###########################################################\n\ninclude $(BUILD_SYSTEM)/use_lld_setup.mk\n\nifneq ($(LOCAL_PREBUILT_LIBS),)\n$(call pretty-error,dont use LOCAL_PREBUILT_LIBS anymore)\nendif\nifneq ($(LOCAL_PREBUILT_EXECUTABLES),)\n$(call pretty-error,dont use LOCAL_PREBUILT_EXECUTABLES anymore)\nendif\nifneq ($(LOCAL_PREBUILT_JAVA_LIBRARIES),)\n$(call pretty-error,dont use LOCAL_PREBUILT_JAVA_LIBRARIES anymore)\nendif\n\nmy_32_64_bit_suffix := $(if $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT),64,32)\n\nifdef LOCAL_PREBUILT_MODULE_FILE\n  my_prebuilt_src_file := $(LOCAL_PREBUILT_MODULE_FILE)\nelse ifdef LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)\n  my_prebuilt_src_file := $(call clean-path,$(LOCAL_PATH)/$(LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)))\n  LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) :=\nelse ifdef LOCAL_SRC_FILES_$(my_32_64_bit_suffix)\n  my_prebuilt_src_file := $(call clean-path,$(LOCAL_PATH)/$(LOCAL_SRC_FILES_$(my_32_64_bit_suffix)))\n  LOCAL_SRC_FILES_$(my_32_64_bit_suffix) :=\nelse ifdef LOCAL_SRC_FILES\n  my_prebuilt_src_file := $(call clean-path,$(LOCAL_PATH)/$(LOCAL_SRC_FILES))\n  LOCAL_SRC_FILES :=\nelse ifdef LOCAL_REPLACE_PREBUILT_APK_INSTALLED\n  # This is handled specially in app_prebuilt_internal.mk\nelse\n  $(call pretty-error,No source files specified)\nendif\n\nLOCAL_CHECKED_MODULE := $(my_prebuilt_src_file)\n\nifneq (,$(LOCAL_APKCERTS_FILE))\n  PACKAGES := $(PACKAGES) $(LOCAL_MODULE)\n  PACKAGES.$(LOCAL_MODULE).APKCERTS_FILE := $(LOCAL_APKCERTS_FILE)\nendif\n\nifneq (APPS,$(LOCAL_MODULE_CLASS))\nifdef LOCAL_COMPRESSED_MODULE\n$(error $(LOCAL_MODULE) : LOCAL_COMPRESSED_MODULE can only be defined for module class APPS)\nendif  # LOCAL_COMPRESSED_MODULE\nendif  # APPS\n\nifeq (APPS,$(LOCAL_MODULE_CLASS))\n  include $(BUILD_SYSTEM)/app_prebuilt_internal.mk\nelse ifeq (JAVA_LIBRARIES,$(LOCAL_MODULE_CLASS))\n  include $(BUILD_SYSTEM)/java_prebuilt_internal.mk\nelse ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)\n  include $(BUILD_SYSTEM)/cc_prebuilt_internal.mk\nelse ifneq ($(filter SCRIPT ETC DATA RENDERSCRIPT_BITCODE,$(LOCAL_MODULE_CLASS)),)\n  include $(BUILD_SYSTEM)/misc_prebuilt_internal.mk\nelse\n  $(error $(LOCAL_MODULE) : unexpected LOCAL_MODULE_CLASS for prebuilts: $(LOCAL_MODULE_CLASS))\nendif\n\n$(if $(filter-out $(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)), \\\n  $(eval ALL_MODULES.$(my_register_name).IS_PREBUILT_MAKE_MODULE := Y))\n\n$(built_module) : $(LOCAL_ADDITIONAL_DEPENDENCIES)\n\nmy_prebuilt_src_file :=\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=PREBUILT))"
  },
  {
    "path": "core/process_wrapper.sh",
    "content": "#!/bin/sh\n\n# When using a process wrapper, this is the top-level\n# command that is executed instead of the server\n# command.  It starts a new xterm in which the user can\n# interact with the new process.\n#\n# Inside of the xterm is a gdb session, through which\n# the user can debug the new process.\n\n# Save away these variables, since we may loose them\n# when starting in the xterm.\nexport PREV_LD_LIBRARY_PATH=$LD_LIBRARY_PATH\nexport PREV_PATH=$PATH\n\ngnome-terminal -t \"Wrapper: $1\" --disable-factory -x $2/process_wrapper_gdb.sh \"$@\"\n\n"
  },
  {
    "path": "core/process_wrapper_gdb.cmds",
    "content": "run\n"
  },
  {
    "path": "core/process_wrapper_gdb.sh",
    "content": "#!/bin/sh\n\n# This is the command running inside the xterm of our\n# debug wrapper.  It needs to take care of starting\n# the server command, so it can attach to the parent\n# process.  In addition, here we run the command inside\n# of a gdb session to allow for debugging.\n\n# On some systems, running xterm will cause LD_LIBRARY_PATH\n# to be cleared, so restore it and PATH to be safe.\nexport PATH=$PREV_PATH\nexport LD_LIBRARY_PATH=$PREV_LD_LIBRARY_PATH\n\n# Start binderproc (or whatever sub-command is being run)\n# inside of gdb, giving gdb an initial command script to\n# automatically run the process without user intervention.\ngdb -q -x $2/process_wrapper_gdb.cmds --args \"$@\"\n"
  },
  {
    "path": "core/product-graph.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# the sort also acts as a strip to remove the single space entries that creep in because of the evals\ndefine gather-all-makefiles-for-current-product\n$(eval _all_products_visited := )\\\n$(sort $(call gather-all-makefiles-for-current-product-inner,$(INTERNAL_PRODUCT)))\nendef\n\ndefine gather-all-makefiles-for-current-product-inner\n\t$(foreach p,$(1),\\\n\t\t$(if $(filter $(p),$(_all_products_visited)),, \\\n\t\t\t$(p) \\\n\t\t\t$(eval _all_products_visited += $(p)) \\\n\t\t\t$(call gather-all-makefiles-for-current-product-inner, $(PRODUCTS.$(strip $(p)).INHERITS_FROM))\n\t\t) \\\n\t)\nendef\n\nnode_color_target := orange\nnode_color_common := beige\nnode_color_vendor := lavenderblush\nnode_color_default := white\ndefine node-color\n$(if $(filter $(1),$(PRIVATE_TOP_LEVEL_MAKEFILE)),\\\n  $(node_color_target),\\\n  $(if $(filter build/make/target/product/%,$(1)),\\\n    $(node_color_common),\\\n    $(if $(filter vendor/%,$(1)),$(node_color_vendor),$(node_color_default))\\\n  )\\\n)\nendef\n\nopen_parethesis := (\nclose_parenthesis := )\n\n# Emit properties of a product node to a file.\n# $(1) the product\n# $(2) the output file\ndefine emit-product-node-props\n$(hide) echo \\\"$(1)\\\" [ \\\nlabel=\\\"$(dir $(1))\\\\n$(notdir $(1))$(if $(filter $(1),$(PRIVATE_TOP_LEVEL_MAKEFILE)),$(subst $(open_parethesis),,$(subst $(close_parenthesis),,\\\\n\\\\n$(PRODUCT_MODEL)\\\\n$(PRODUCT_DEVICE))))\\\" \\\nstyle=\\\"filled\\\" fillcolor=\\\"$(strip $(call node-color,$(1)))\\\" \\\ncolorscheme=\\\"svg\\\" fontcolor=\\\"darkblue\\\" \\\n] >> $(2)\n\nendef\n\nproducts_graph := $(OUT_DIR)/products.dot\n\n$(products_graph): PRIVATE_ALL_MAKEFILES_FOR_THIS_PRODUCT := $(call gather-all-makefiles-for-current-product)\n$(products_graph): PRIVATE_TOP_LEVEL_MAKEFILE := $(INTERNAL_PRODUCT)\n$(products_graph):\n\t@echo Product graph DOT: $@ for $(PRIVATE_TOP_LEVEL_MAKEFILE)\n\t$(hide) echo 'digraph {' > $@\n\t$(hide) echo 'graph [ ratio=.5 ];' >> $@\n\t$(hide) $(foreach p,$(PRIVATE_ALL_MAKEFILES_FOR_THIS_PRODUCT), \\\n\t  $(foreach d,$(PRODUCTS.$(strip $(p)).INHERITS_FROM), echo \\\"$(d)\\\" -\\> \\\"$(p)\\\" >> $@;))\n\t$(foreach p,$(PRIVATE_ALL_MAKEFILES_FOR_THIS_PRODUCT),$(call emit-product-node-props,$(p),$@))\n\t$(hide) echo '}' >> $@\n\n.PHONY: product-graph\nproduct-graph: $(products_graph)\n\t@echo Product graph .dot file: $(products_graph)\n\t@echo Command to convert to pdf: dot -Tpdf -Nshape=box -o $(OUT_DIR)/products.pdf $(products_graph)\n\t@echo Command to convert to svg: dot -Tsvg -Nshape=box -o $(OUT_DIR)/products.svg $(products_graph)\n"
  },
  {
    "path": "core/product.mk",
    "content": "#\n# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Variables that are meant to hold only a single value.\n# - The value set in the current makefile takes precedence over inherited values\n# - If multiple inherited makefiles set the var, the first-inherited value wins\n_product_single_value_vars :=\n\n# Variables that are lists of values.\n_product_list_vars :=\n\n_product_single_value_vars += PRODUCT_NAME\n_product_single_value_vars += PRODUCT_MODEL\n_product_single_value_vars += PRODUCT_NAME_FOR_ATTESTATION\n_product_single_value_vars += PRODUCT_MODEL_FOR_ATTESTATION\n_product_single_value_vars += PRODUCT_BASE_OS\n\n# Defines the ELF segment alignment for binaries (executables and shared libraries).\n# The ELF segment alignment has to be a PAGE_SIZE multiple. For example, if\n# PRODUCT_MAX_PAGE_SIZE_SUPPORTED=65536, the possible values for PAGE_SIZE could be\n# 4096, 16384 and 65536.\n_product_single_value_vars += PRODUCT_MAX_PAGE_SIZE_SUPPORTED\n_product_single_value_vars += PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE\n\n# Boolean variable determining if AOSP relies on bionic's PAGE_SIZE macro.\n_product_single_value_vars += PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO\n\n# The resource configuration options to use for this product.\n_product_list_vars += PRODUCT_LOCALES\n_product_list_vars += PRODUCT_AAPT_CONFIG\n_product_single_value_vars += PRODUCT_AAPT_PREF_CONFIG\n_product_list_vars += PRODUCT_AAPT_PREBUILT_DPI\n_product_list_vars += PRODUCT_HOST_PACKAGES\n_product_list_vars += PRODUCT_PACKAGES\n_product_list_vars += PRODUCT_PACKAGES_DEBUG\n_product_list_vars += PRODUCT_PACKAGES_DEBUG_ASAN\n_product_list_vars += PRODUCT_PACKAGES_ARM64\n\n# packages that are added to PRODUCT_PACKAGES based on the PRODUCT_SHIPPING_API_LEVEL\n# These are only added if the shipping API level is that level or lower\n_product_list_vars += PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29\n_product_list_vars += PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33\n_product_list_vars += PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34\n\n# Packages included only for eng/userdebug builds, when building with EMMA_INSTRUMENT=true\n_product_list_vars += PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE\n_product_list_vars += PRODUCT_PACKAGES_ENG\n_product_list_vars += PRODUCT_PACKAGES_TESTS\n\n# The device that this product maps to.\n_product_single_value_vars += PRODUCT_DEVICE\n_product_single_value_vars += PRODUCT_MANUFACTURER\n_product_single_value_vars += PRODUCT_BRAND\n_product_single_value_vars += PRODUCT_BRAND_FOR_ATTESTATION\n\n# These PRODUCT_SYSTEM_* flags, if defined, are used in place of the\n# corresponding PRODUCT_* flags for the sysprops on /system.\n_product_single_value_vars += \\\n    PRODUCT_SYSTEM_NAME \\\n    PRODUCT_SYSTEM_MODEL \\\n    PRODUCT_SYSTEM_DEVICE \\\n    PRODUCT_SYSTEM_BRAND \\\n    PRODUCT_SYSTEM_MANUFACTURER \\\n\n# PRODUCT_<PARTITION>_PROPERTIES are lists of property assignments\n# that go to <partition>/build.prop. Each property assignment is\n# \"key = value\" with zero or more whitespace characters on either\n# side of the '='.\n_product_list_vars += \\\n    PRODUCT_SYSTEM_PROPERTIES \\\n    PRODUCT_SYSTEM_EXT_PROPERTIES \\\n    PRODUCT_VENDOR_PROPERTIES \\\n    PRODUCT_ODM_PROPERTIES \\\n    PRODUCT_PRODUCT_PROPERTIES\n\n# TODO(b/117892318) deprecate these:\n# ... in favor or PRODUCT_SYSTEM_PROPERTIES\n_product_list_vars += PRODUCT_SYSTEM_DEFAULT_PROPERTIES\n# ... in favor of PRODUCT_VENDOR_PROPERTIES\n_product_list_vars += PRODUCT_PROPERTY_OVERRIDES\n_product_list_vars += PRODUCT_DEFAULT_PROPERTY_OVERRIDES\n\n# TODO(b/117892318) consider deprecating these too\n_product_list_vars += PRODUCT_SYSTEM_PROPERTY_BLACKLIST\n_product_list_vars += PRODUCT_VENDOR_PROPERTY_BLACKLIST\n\n# The characteristics of the product, which among other things is passed to aapt\n_product_single_value_vars += PRODUCT_CHARACTERISTICS\n\n# A list of words like <source path>:<destination path>[:<owner>].\n# The file at the source path should be copied to the destination path\n# when building  this product.  <destination path> is relative to\n# $(PRODUCT_OUT), so it should look like, e.g., \"system/etc/file.xml\".\n# The rules for these copy steps are defined in build/make/core/Makefile.\n# The optional :<owner> is used to indicate the owner of a vendor file.\n_product_list_vars += PRODUCT_COPY_FILES\n\n# The OTA key(s) specified by the product config, if any.  The names\n# of these keys are stored in the target-files zip so that post-build\n# signing tools can substitute them for the test key embedded by\n# default.\n_product_list_vars += PRODUCT_OTA_PUBLIC_KEYS\n_product_list_vars += PRODUCT_EXTRA_OTA_KEYS\n_product_list_vars += PRODUCT_EXTRA_RECOVERY_KEYS\n\n# Should we use the default resources or add any product specific overlays\n_product_list_vars += PRODUCT_PACKAGE_OVERLAYS\n_product_list_vars += DEVICE_PACKAGE_OVERLAYS\n\n# Resource overlay list which must be excluded from enforcing RRO.\n_product_list_vars += PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS\n\n# Package list to apply enforcing RRO.\n_product_list_vars += PRODUCT_ENFORCE_RRO_TARGETS\n\n_product_list_vars += PRODUCT_SDK_ATREE_FILES\n_product_list_vars += PRODUCT_SDK_ADDON_NAME\n_product_list_vars += PRODUCT_SDK_ADDON_COPY_FILES\n_product_list_vars += PRODUCT_SDK_ADDON_COPY_MODULES\n_product_list_vars += PRODUCT_SDK_ADDON_DOC_MODULES\n_product_list_vars += PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP\n\n# which Soong namespaces to export to Make\n_product_list_vars += PRODUCT_SOONG_NAMESPACES\n\n_product_list_vars += PRODUCT_DEFAULT_WIFI_CHANNELS\n_product_single_value_vars += PRODUCT_DEFAULT_DEV_CERTIFICATE\n_product_list_vars += PRODUCT_MAINLINE_SEPOLICY_DEV_CERTIFICATES\n_product_list_vars += PRODUCT_RESTRICT_VENDOR_FILES\n\n# The list of product-specific kernel header dirs\n_product_list_vars += PRODUCT_VENDOR_KERNEL_HEADERS\n\n# A list of module names in BOOTCLASSPATH (jar files). Each module may be\n# prefixed with \"<apex>:\", which identifies the APEX that provides it. APEXes\n# are identified by their \"variant\" names, i.e. their `apex_name` values in\n# Soong, which default to the `name` values. The prefix can also be \"platform:\"\n# or \"system_ext:\", and defaults to \"platform:\" if left out. See the long\n# comment in build/soong/java/dexprepopt_bootjars.go for details.\n_product_list_vars += PRODUCT_BOOT_JARS\n\n# A list of extra BOOTCLASSPATH jars (to be appended after common jars),\n# following the same format as PRODUCT_BOOT_JARS. Products that include\n# device-specific makefiles before AOSP makefiles should use this instead of\n# PRODUCT_BOOT_JARS, so that device-specific jars go after common jars.\n_product_list_vars += PRODUCT_BOOT_JARS_EXTRA\n\n# List of jars to be included in the ART boot image for testing.\n_product_list_vars += PRODUCT_TEST_ONLY_ART_BOOT_IMAGE_JARS\n\n_product_list_vars += PRODUCT_SYSTEM_SERVER_APPS\n# List of system_server classpath jars on the platform.\n_product_list_vars += PRODUCT_SYSTEM_SERVER_JARS\n# List of system_server classpath jars delivered via apex. Format = <apex name>:<jar name>.\n_product_list_vars += PRODUCT_APEX_SYSTEM_SERVER_JARS\n# List of jars on the platform that system_server loads dynamically using separate classloaders.\n_product_list_vars += PRODUCT_STANDALONE_SYSTEM_SERVER_JARS\n# List of jars delivered via apex that system_server loads dynamically using separate classloaders.\n# Format = <apex name>:<jar name>\n_product_list_vars += PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS\n# If true, then suboptimal order of system server jars does not cause an error.\n_product_single_value_vars += PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS\n# If true, then system server jars defined in Android.mk are supported.\n_product_single_value_vars += PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS\n\n# Additional system server jars to be appended at the end of the common list.\n# This is necessary to avoid jars reordering due to makefile inheritance order.\n_product_list_vars += PRODUCT_SYSTEM_SERVER_JARS_EXTRA\n\n# Set to true to disable <uses-library> checks for a product.\n_product_list_vars += PRODUCT_BROKEN_VERIFY_USES_LIBRARIES\n\n_product_list_vars += PRODUCT_DEXPREOPT_SPEED_APPS\n_product_list_vars += PRODUCT_LOADED_BY_PRIVILEGED_MODULES\n_product_single_value_vars += PRODUCT_VBOOT_SIGNING_KEY\n_product_single_value_vars += PRODUCT_VBOOT_SIGNING_SUBKEY\n_product_single_value_vars += PRODUCT_SYSTEM_VERITY_PARTITION\n_product_single_value_vars += PRODUCT_VENDOR_VERITY_PARTITION\n_product_single_value_vars += PRODUCT_PRODUCT_VERITY_PARTITION\n_product_single_value_vars += PRODUCT_SYSTEM_EXT_VERITY_PARTITION\n_product_single_value_vars += PRODUCT_ODM_VERITY_PARTITION\n_product_single_value_vars += PRODUCT_VENDOR_DLKM_VERITY_PARTITION\n_product_single_value_vars += PRODUCT_ODM_DLKM_VERITY_PARTITION\n_product_single_value_vars += PRODUCT_SYSTEM_DLKM_VERITY_PARTITION\n_product_single_value_vars += PRODUCT_SYSTEM_SERVER_DEBUG_INFO\n_product_single_value_vars += PRODUCT_OTHER_JAVA_DEBUG_INFO\n\n# Per-module dex-preopt configs.\n_product_list_vars += PRODUCT_DEX_PREOPT_MODULE_CONFIGS\n_product_single_value_vars += PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER\n_product_list_vars += PRODUCT_DEX_PREOPT_DEFAULT_FLAGS\n_product_single_value_vars += PRODUCT_DEX_PREOPT_BOOT_FLAGS\n_product_single_value_vars += PRODUCT_DEX_PREOPT_PROFILE_DIR\n_product_single_value_vars += PRODUCT_DEX_PREOPT_GENERATE_DM_FILES\n_product_single_value_vars += PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING\n_product_single_value_vars += PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS\n\n# Boot image options.\n_product_list_vars += PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION\n_product_single_value_vars += \\\n    PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST \\\n    PRODUCT_USE_PROFILE_FOR_BOOT_IMAGE \\\n    PRODUCT_USES_DEFAULT_ART_CONFIG \\\n\n_product_single_value_vars += PRODUCT_SYSTEM_SERVER_COMPILER_FILTER\n# Per-module sanitizer configs\n_product_list_vars += PRODUCT_SANITIZER_MODULE_CONFIGS\n_product_single_value_vars += PRODUCT_SYSTEM_BASE_FS_PATH\n_product_single_value_vars += PRODUCT_VENDOR_BASE_FS_PATH\n_product_single_value_vars += PRODUCT_PRODUCT_BASE_FS_PATH\n_product_single_value_vars += PRODUCT_SYSTEM_EXT_BASE_FS_PATH\n_product_single_value_vars += PRODUCT_ODM_BASE_FS_PATH\n_product_single_value_vars += PRODUCT_VENDOR_DLKM_BASE_FS_PATH\n_product_single_value_vars += PRODUCT_ODM_DLKM_BASE_FS_PATH\n_product_single_value_vars += PRODUCT_SYSTEM_DLKM_BASE_FS_PATH\n\n# The first API level this product shipped with\n_product_single_value_vars += PRODUCT_SHIPPING_API_LEVEL\n\n_product_list_vars += VENDOR_PRODUCT_RESTRICT_VENDOR_FILES\n_product_list_vars += VENDOR_EXCEPTION_MODULES\n_product_list_vars += VENDOR_EXCEPTION_PATHS\n# Whether the product wants to ship libartd. For rules and meaning, see art/Android.mk.\n_product_single_value_vars += PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD\n\n# Make this art variable visible to soong_config.mk.\n_product_single_value_vars += PRODUCT_ART_USE_READ_BARRIER\n\n# Add reserved headroom to a system image.\n_product_single_value_vars += PRODUCT_SYSTEM_HEADROOM\n\n# Whether to save disk space by minimizing java debug info\n_product_single_value_vars += PRODUCT_MINIMIZE_JAVA_DEBUG_INFO\n\n# Whether any paths are excluded from sanitization when SANITIZE_TARGET=integer_overflow\n_product_list_vars += PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS\n\n_product_single_value_vars += PRODUCT_ADB_KEYS\n\n# Whether any paths should have CFI enabled for components\n_product_list_vars += PRODUCT_CFI_INCLUDE_PATHS\n\n# Whether any paths are excluded from sanitization when SANITIZE_TARGET=cfi\n_product_list_vars += PRODUCT_CFI_EXCLUDE_PATHS\n\n# Whether any paths should have HWASan enabled for components\n_product_list_vars += PRODUCT_HWASAN_INCLUDE_PATHS\n\n# Whether any paths are excluded from sanitization when SANITIZE_TARGET=hwaddress\n_product_list_vars += PRODUCT_HWASAN_EXCLUDE_PATHS\n\n# Whether any paths should have Memtag_heap enabled for components\n_product_list_vars += PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS\n_product_list_vars += PRODUCT_MEMTAG_HEAP_ASYNC_DEFAULT_INCLUDE_PATHS\n_product_list_vars += PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS\n_product_list_vars += PRODUCT_MEMTAG_HEAP_SYNC_DEFAULT_INCLUDE_PATHS\n_product_list_vars += PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS\n\n# Whether this product wants to start with an empty list of default memtag_heap include paths\n_product_single_value_vars += PRODUCT_MEMTAG_HEAP_SKIP_DEFAULT_PATHS\n\n# Whether the Scudo hardened allocator is disabled platform-wide\n_product_single_value_vars += PRODUCT_DISABLE_SCUDO\n\n# List of extra VNDK versions to be included\n_product_list_vars += PRODUCT_EXTRA_VNDK_VERSIONS\n\n# Whether APEX should be compressed or not\n_product_single_value_vars += PRODUCT_COMPRESSED_APEX\n\n# Default fs type for APEX payload image (apex_payload.img)\n_product_single_value_vars += PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE\n\n# VNDK version of product partition. It can be 'current' if the product\n# partitions uses PLATFORM_VNDK_VERSION.\n_product_single_value_vars += PRODUCT_PRODUCT_VNDK_VERSION\n\n_product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS\n_product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT\n_product_list_vars += PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST\n_product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT\n_product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST\n\n# List of modules that should be forcefully unmarked from being LOCAL_PRODUCT_MODULE, and hence\n# installed on /system directory by default.\n_product_list_vars += PRODUCT_FORCE_PRODUCT_MODULES_TO_SYSTEM_PARTITION\n\n# When this is true, dynamic partitions is retrofitted on a device that has\n# already been launched without dynamic partitions. Otherwise, the device\n# is launched with dynamic partitions.\n# This flag implies PRODUCT_USE_DYNAMIC_PARTITIONS.\n_product_single_value_vars += PRODUCT_RETROFIT_DYNAMIC_PARTITIONS\n\n# List of directories that will be used to gate blueprint modules from the build graph\n_product_list_vars += PRODUCT_SOURCE_ROOT_DIRS\n\n# When this is true, various build time as well as runtime debugfs restrictions are enabled.\n_product_single_value_vars += PRODUCT_SET_DEBUGFS_RESTRICTIONS\n\n# Other dynamic partition feature flags.PRODUCT_USE_DYNAMIC_PARTITION_SIZE and\n# PRODUCT_BUILD_SUPER_PARTITION default to the value of PRODUCT_USE_DYNAMIC_PARTITIONS.\n_product_single_value_vars += \\\n    PRODUCT_USE_DYNAMIC_PARTITIONS \\\n    PRODUCT_USE_DYNAMIC_PARTITION_SIZE \\\n    PRODUCT_BUILD_SUPER_PARTITION \\\n\n# If set, kernel configuration requirements are present in OTA package (and will be enforced\n# during OTA). Otherwise, kernel configuration requirements are enforced in VTS.\n# Devices that checks the running kernel (instead of the kernel in OTA package) should not\n# set this variable to prevent OTA failures.\n_product_list_vars += PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS\n\n# If set to true, this product forces HIDL to be enabled by declaring android.hidl.manager\n# and android.hidl.token in the framework manifest. The product will also need to add the\n# 'hwservicemanager' service to PRODUCT_PACKAGES if its SHIPPING_API_LEVEL is greater than 34.\n# This should only be used during bringup for devices that are targeting FCM 202404 and still\n# have partner-owned HIDL interfaces that are being converted to AIDL.\n_product_single_value_vars += PRODUCT_HIDL_ENABLED\n\n# If set to true, this product builds a generic OTA package, which installs generic system images\n# onto matching devices. The product may only build a subset of system images (e.g. only\n# system.img), so devices need to install the package in a system-only OTA manner.\n_product_single_value_vars += PRODUCT_BUILD_GENERIC_OTA_PACKAGE\n\n_product_list_vars += PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES\n_product_list_vars += PRODUCT_PACKAGE_NAME_OVERRIDES\n_product_list_vars += PRODUCT_CERTIFICATE_OVERRIDES\n\n# Overrides the (apex, jar) pairs above when determining the on-device location. The format is:\n# <old_apex>:<old_jar>:<new_apex>:<new_jar>\n_product_list_vars += PRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES\n\n# Controls for whether different partitions are built for the current product.\n_product_single_value_vars += PRODUCT_BUILD_SYSTEM_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_SYSTEM_OTHER_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_VENDOR_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_PRODUCT_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_SYSTEM_EXT_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_ODM_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_VENDOR_DLKM_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_ODM_DLKM_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_SYSTEM_DLKM_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_CACHE_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_RAMDISK_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_USERDATA_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_RECOVERY_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_BOOT_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_INIT_BOOT_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_DEBUG_BOOT_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_VENDOR_BOOT_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_VBMETA_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_SUPER_EMPTY_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_PVMFW_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_DESKTOP_RECOVERY_IMAGE\n_product_single_value_vars += PRODUCT_BUILD_DESKTOP_UPDATE_IMAGE\n\n# List of boot jars delivered via updatable APEXes, following the same format as\n# PRODUCT_BOOT_JARS.\n_product_list_vars += PRODUCT_APEX_BOOT_JARS\n\n# If set, device uses virtual A/B.\n_product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA\n\n# If set, device uses virtual A/B Compression.\n_product_single_value_vars += PRODUCT_VIRTUAL_AB_COMPRESSION\n\n# If set, device retrofits virtual A/B.\n_product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA_RETROFIT\n\n# If set, forcefully generate a non-A/B update package.\n# Note: A device configuration should inherit from virtual_ab_ota_plus_non_ab.mk\n# instead of setting this variable directly.\n# Note: Use TARGET_OTA_ALLOW_NON_AB in the build system because\n# TARGET_OTA_ALLOW_NON_AB takes the value of AB_OTA_UPDATER into account.\n_product_single_value_vars += PRODUCT_OTA_FORCE_NON_AB_PACKAGE\n\n# If set, Java module in product partition cannot use hidden APIs.\n_product_single_value_vars += PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE\n\n# Install a copy of the debug policy to the system_ext partition, and allow\n# init-second-stage to load debug policy from system_ext.\n# This option is only meant to be set by compliance GSI targets.\n_product_single_value_vars += PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT\n\n# If set, fsverity metadata files will be generated for each files in the\n# allowlist, plus an manifest APK per partition. For example,\n# /system/framework/service.jar will come with service.jar.fsv_meta in the same\n# directory; the file information will also be included in\n# /system/etc/security/fsverity/BuildManifest.apk\n_product_single_value_vars += PRODUCT_FSVERITY_GENERATE_METADATA\n\n# If true, this builds the mainline modules from source. This overrides any\n# prebuilts selected via RELEASE_APEX_CONTRIBUTIONS_* build flags for the\n# current release config.\n_product_single_value_vars += PRODUCT_MODULE_BUILD_FROM_SOURCE\n\n# If true, installs a full version of com.android.virt APEX.\n_product_single_value_vars += PRODUCT_AVF_ENABLED\n\n# If false, disable the AVF remote attestaton feature.\n_product_single_value_vars += PRODUCT_AVF_REMOTE_ATTESTATION_DISABLED\n\n# If true, kernel with modules will be used for Microdroid VMs.\n_product_single_value_vars += PRODUCT_AVF_KERNEL_MODULES_ENABLED\n\n# If true, the memory controller will be force-enabled in the cgroup v2 hierarchy\n_product_single_value_vars += PRODUCT_MEMCG_V2_FORCE_ENABLED\n\n# If true, the cgroup v2 hierarchy will be split into apps/system subtrees\n_product_single_value_vars += PRODUCT_CGROUP_V2_SYS_APP_ISOLATION_ENABLED\n\n# List of .json files to be merged/compiled into vendor/etc/linker.config.pb and product/etc/linker.config.pb\n_product_list_vars += PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS\n_product_list_vars += PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS\n\n# Whether to use userfaultfd GC.\n# Possible values are:\n# - \"default\" or empty: both the build system and the runtime determine whether to use userfaultfd\n#   GC based on the vendor API level\n# - \"true\": forces the build system to use userfaultfd GC regardless of the vendor API level; the\n#   runtime determines whether to use userfaultfd GC based on the kernel support. Note that the\n#   device may have to re-compile everything on the first boot if the kernel doesn't support\n#   userfaultfd\n# - \"false\": disallows the build system and the runtime to use userfaultfd GC even if the device\n#   supports it. This option is temporary - the plan is to remove it by Aug 2025, at which time\n#   Mainline updates of the ART module will ignore it as well.\n_product_single_value_vars += PRODUCT_ENABLE_UFFD_GC\n\n# Specifies COW version to be used by update_engine and libsnapshot. If this value is not\n# specified we default to COW version 2 in update_engine for backwards compatibility\n_product_single_value_vars += PRODUCT_VIRTUAL_AB_COW_VERSION\n\n# Specifies maximum bytes to be compressed at once during ota. Options: 4096, 8192, 16384, 32768, 65536, 131072, 262144.\n_product_single_value_vars += PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR\n\n# If set, determines whether the build system checks vendor seapp contexts violations.\n_product_single_value_vars += PRODUCT_CHECK_VENDOR_SEAPP_VIOLATIONS\n\n# If set, determines whether the build system checks dev type violations.\n_product_single_value_vars += PRODUCT_CHECK_DEV_TYPE_VIOLATIONS\n\n_product_list_vars += PRODUCT_AFDO_PROFILES\n\n_product_single_value_vars += PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE\n\n_product_list_vars += PRODUCT_RELEASE_CONFIG_MAPS\n\n_product_list_vars += PRODUCT_VALIDATION_CHECKS\n\n_product_single_value_vars += PRODUCT_BUILD_FROM_SOURCE_STUB\n\n_product_single_value_vars += PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS\n\n_product_single_value_vars += PRODUCT_HIDDEN_API_EXPORTABLE_STUBS\n\n_product_single_value_vars += PRODUCT_EXPORT_RUNTIME_APIS\n\n# If set, determines which version of the GKI is used as guest kernel for Microdroid VMs.\n# TODO(b/325991735): link to documentation once it is done.\n_product_single_value_vars += PRODUCT_AVF_MICRODROID_GUEST_GKI_VERSION\n\n# Enables 16KB developer option for device if set.\n_product_single_value_vars += PRODUCT_16K_DEVELOPER_OPTION\n\n# If set, adb root will be disabled (really ro.debuggable=0) in userdebug\n# builds. It's already off disabled in user builds. Eng builds are unaffected\n# by this flag.\n_product_single_value_vars += PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG\n\n# If set, the default value of the versionName of apps will include the build number.\n_product_single_value_vars += PRODUCT_BUILD_APPS_WITH_BUILD_NUMBER\n\n# If set, build would generate system image from Soong-defined module.\n_product_single_value_vars += PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE\n\n# List of stub libraries specific to the product that are already present in the system image and\n# should be included in the system_linker_config.\n_product_list_vars += PRODUCT_EXTRA_STUB_LIBRARIES\n\n# If set to true, all Android.mk files will be ignored.\n_product_single_value_vars += PRODUCT_IGNORE_ALL_ANDROIDMK\n# When PRODUCT_IGNORE_ALL_ANDROIDMK is set to true, this variable will be used to allow some Android.mk files.\n_product_list_vars += PRODUCT_ALLOWED_ANDROIDMK_FILES\n# When PRODUCT_IGNORE_ALL_ANDROIDMK is set to true, path of file that contains a list of allowed Android.mk files\n_product_single_value_vars += PRODUCT_ANDROIDMK_ALLOWLIST_FILE\n# Setting PRODUCT_SOONG_ONLY will cause the build to default to --soong-only mode, and the main\n# kati invocation will not be run.\n_product_single_value_vars += PRODUCT_SOONG_ONLY\n\n# If set to true, use NOTICE.xml.gz generated by soong\n_product_single_value_vars += PRODUCT_USE_SOONG_NOTICE_XML\n\n.KATI_READONLY := _product_single_value_vars _product_list_vars\n_product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars)\n\n#\n# Functions for including product makefiles\n#\n\n#\n# $(1): product to inherit\n#\n# To be called from product makefiles, and is later evaluated during the import-nodes\n# call below. It does the following:\n#  1. Inherits all of the variables from $1.\n#  2. Records the inheritance in the .INHERITS_FROM variable\n#\n# (2) and the PRODUCTS variable can be used together to reconstruct the include hierarchy\n# See e.g. product-graph.mk for an example of this.\n#\ndefine inherit-product\n  $(eval _inherit_product_wildcard := $(wildcard $(1)))\\\n  $(if $(_inherit_product_wildcard),,$(error $(1) does not exist.))\\\n  $(foreach part,$(_inherit_product_wildcard),\\\n    $(if $(findstring ../,$(part)),\\\n      $(eval np := $(call normalize-paths,$(part))),\\\n      $(eval np := $(strip $(part))))\\\n    $(foreach v,$(_product_var_list), \\\n        $(eval $(v) := $($(v)) $(INHERIT_TAG)$(np))) \\\n    $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \\\n    $(eval inherit_var := PRODUCTS.$(current_mk).INHERITS_FROM) \\\n    $(eval $(inherit_var) := $(sort $($(inherit_var)) $(np))) \\\n    $(call dump-inherit,$(current_mk),$(1)) \\\n    $(call dump-config-vals,$(current_mk),inherit))\nendef\n\n# Specifies a number of path prefixes, relative to PRODUCT_OUT, where the\n# product makefile hierarchy rooted in the current node places its artifacts.\n# Creating artifacts outside the specified paths will cause a build-time error.\ndefine require-artifacts-in-path\n  $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \\\n  $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_REQUIREMENTS := $(strip $(1))) \\\n  $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_ALLOWED_LIST := $(strip $(2))) \\\n  $(eval ARTIFACT_PATH_REQUIREMENT_PRODUCTS := \\\n    $(sort $(ARTIFACT_PATH_REQUIREMENT_PRODUCTS) $(current_mk)))\nendef\n\n# Like require-artifacts-in-path, but does not require all allow-list entries to\n# have an effect.\ndefine require-artifacts-in-path-relaxed\n  $(require-artifacts-in-path) \\\n  $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_REQUIREMENT_IS_RELAXED := true)\nendef\n\n# Makes including non-existent modules in PRODUCT_PACKAGES an error.\n# $(1): list of non-existent modules to allow.\ndefine enforce-product-packages-exist\n  $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \\\n  $(eval PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST := true) \\\n  $(eval PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST := $(1)) \\\n  $(eval .KATI_READONLY := PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST) \\\n  $(eval .KATI_READONLY := PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST)\nendef\n\n#\n# Do inherit-product only if $(1) exists\n#\ndefine inherit-product-if-exists\n  $(if $(wildcard $(1)),$(call inherit-product,$(1)),)\nendef\n\n#\n# $(1): product makefile list\n#\n#TODO: check to make sure that products have all the necessary vars defined\ndefine import-products\n$(call import-nodes,PRODUCTS,$(1),$(_product_var_list),$(_product_single_value_vars))\nendef\n\n\n#\n# Does various consistency checks on the current product.\n# Takes no parameters, so $(call ) is not necessary.\n#\ndefine check-current-product\n$(if ,, \\\n  $(if $(call is-c-identifier,$(PRODUCT_NAME)),, \\\n    $(error $(INTERNAL_PRODUCT): PRODUCT_NAME must be a valid C identifier, not \"$(pn)\")) \\\n  $(if $(PRODUCT_BRAND),, \\\n    $(error $(INTERNAL_PRODUCT): PRODUCT_BRAND must be defined.)) \\\n  $(foreach cf,$(strip $(PRODUCT_COPY_FILES)), \\\n    $(if $(filter 2 3,$(words $(subst :,$(space),$(cf)))),, \\\n      $(error $(p): malformed COPY_FILE \"$(cf)\"))))\nendef\n\n# BoardConfig variables that are also inherited in product mks. Should ideally\n# be cleaned up to not be product variables.\n_readonly_late_variables := \\\n  DEVICE_PACKAGE_OVERLAYS \\\n  WITH_DEXPREOPT_ART_BOOT_IMG_ONLY \\\n\n# Modified internally in the build system\n_readonly_late_variables += \\\n  PRODUCT_COPY_FILES \\\n  PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING \\\n  PRODUCT_DEX_PREOPT_BOOT_FLAGS \\\n\n_readonly_early_variables := $(filter-out $(_readonly_late_variables),$(_product_var_list))\n\n#\n# Mark the variables in _product_stash_var_list as readonly\n#\ndefine readonly-variables\n$(foreach v,$(1), \\\n  $(eval $(v) ?=) \\\n  $(eval .KATI_READONLY := $(v)) \\\n )\nendef\ndefine readonly-product-vars\n$(call readonly-variables,$(_readonly_early_variables))\nendef\n\ndefine readonly-final-product-vars\n$(call readonly-variables,$(_readonly_late_variables))\nendef\n\n# Macro re-defined inside strip-product-vars.\nget-product-var = $(PRODUCTS.$(strip $(1)).$(2))\n#\n# Strip the variables in _product_var_list and a few build-system\n# internal variables, and assign the ones for the current product\n# to a shorthand that is more convenient to read from elsewhere.\n#\ndefine strip-product-vars\n$(call dump-phase-start,PRODUCT-EXPAND,,$(_product_var_list),$(_product_single_value_vars), \\\n\t\tbuild/make/core/product.mk) \\\n$(foreach v,\\\n  $(_product_var_list) \\\n    PRODUCT_ENFORCE_PACKAGES_EXIST \\\n    PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST, \\\n  $(eval $(v) := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).$(v)))) \\\n  $(eval get-product-var = $$(if $$(filter $$(1),$$(INTERNAL_PRODUCT)),$$($$(2)),$$(PRODUCTS.$$(strip $$(1)).$$(2)))) \\\n  $(KATI_obsolete_var PRODUCTS.$(INTERNAL_PRODUCT).$(v),Use $(v) instead) \\\n) \\\n$(call dump-phase-end,build/make/core/product.mk)\nendef\n\ndefine add-to-product-copy-files-if-exists\n$(if $(wildcard $(word 1,$(subst :, ,$(1)))),$(1))\nendef\n\n# whitespace placeholder when we record module's dex-preopt config.\n_PDPMC_SP_PLACE_HOLDER := |@SP@|\n# Set up dex-preopt config for a module.\n# $(1) list of module names\n# $(2) the modules' dex-preopt config\ndefine add-product-dex-preopt-module-config\n$(eval _c := $(subst $(space),$(_PDPMC_SP_PLACE_HOLDER),$(strip $(2))))\\\n$(eval PRODUCT_DEX_PREOPT_MODULE_CONFIGS += \\\n  $(foreach m,$(1),$(m)=$(_c)))\nendef\n\n# whitespace placeholder when we record module's sanitizer config.\n_PSMC_SP_PLACE_HOLDER := |@SP@|\n# Set up sanitizer config for a module.\n# $(1) list of module names\n# $(2) the modules' sanitizer config\ndefine add-product-sanitizer-module-config\n$(eval _c := $(subst $(space),$(_PSMC_SP_PLACE_HOLDER),$(strip $(2))))\\\n$(eval PRODUCT_SANITIZER_MODULE_CONFIGS += \\\n  $(foreach m,$(1),$(m)=$(_c)))\nendef\n"
  },
  {
    "path": "core/product_config.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# ---------------------------------------------------------------\n# Generic functions\n# TODO: Move these to definitions.make once we're able to include\n# definitions.make before config.make.\n\n###########################################################\n## Return non-empty if $(1) is a C identifier; i.e., if it\n## matches /^[a-zA-Z_][a-zA-Z0-9_]*$/.  We do this by first\n## making sure that it isn't empty and doesn't start with\n## a digit, then by removing each valid character.  If the\n## final result is empty, then it was a valid C identifier.\n##\n## $(1): word to check\n###########################################################\n\n_ici_digits := 0 1 2 3 4 5 6 7 8 9\n_ici_alphaunderscore := \\\n    a b c d e f g h i j k l m n o p q r s t u v w x y z \\\n    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _\ndefine is-c-identifier\n$(strip \\\n  $(if $(1), \\\n    $(if $(filter $(addsuffix %,$(_ici_digits)),$(1)), \\\n     , \\\n      $(eval w := $(1)) \\\n      $(foreach c,$(_ici_digits) $(_ici_alphaunderscore), \\\n        $(eval w := $(subst $(c),,$(w))) \\\n       ) \\\n      $(if $(w),,TRUE) \\\n      $(eval w :=) \\\n     ) \\\n   ) \\\n )\nendef\n\n# TODO: push this into the combo files; unfortunately, we don't even\n# know HOST_OS at this point.\ntrysed := $(shell echo a | sed -E -e 's/a/b/' 2>/dev/null)\nifeq ($(trysed),b)\n  SED_EXTENDED := sed -E\nelse\n  trysed := $(shell echo c | sed -r -e 's/c/d/' 2>/dev/null)\n  ifeq ($(trysed),d)\n    SED_EXTENDED := sed -r\n  else\n    $(error Unknown sed version)\n  endif\nendif\n\n###########################################################\n## List all of the files in a subdirectory in a format\n## suitable for PRODUCT_COPY_FILES and\n## PRODUCT_SDK_ADDON_COPY_FILES\n##\n## $(1): Glob to match file name\n## $(2): Source directory\n## $(3): Target base directory\n###########################################################\n\ndefine find-copy-subdir-files\n$(shell find $(2) -name \"$(1)\" -type f | $(SED_EXTENDED) \"s:($(2)/?(.*)):\\\\1\\\\:$(3)/\\\\2:\" | sed \"s://:/:g\" | sort)\nendef\n\n#\n# Convert file file to the PRODUCT_COPY_FILES/PRODUCT_SDK_ADDON_COPY_FILES\n# format: for each file F return $(F):$(PREFIX)/$(notdir $(F))\n# $(1): files list\n# $(2): prefix\n\ndefine copy-files\n$(foreach f,$(1),$(f):$(2)/$(notdir $(f)))\nendef\n\n#\n# Convert the list of file names to the list of PRODUCT_COPY_FILES items\n# $(1): from pattern\n# $(2): to pattern\n# $(3): file names\n# E.g., calling product-copy-files-by-pattern with\n#   (from/%, to/%, a b)\n# returns\n#   from/a:to/a from/b:to/b\ndefine product-copy-files-by-pattern\n$(join $(patsubst %,$(1),$(3)),$(patsubst %,:$(2),$(3)))\nendef\n\n# Return empty unless the board matches\ndefine is-board-platform2\n$(filter $(1), $(TARGET_BOARD_PLATFORM))\nendef\n\n# Return empty unless the board is in the list\ndefine is-board-platform-in-list2\n$(filter $(1),$(TARGET_BOARD_PLATFORM))\nendef\n\n# Return empty unless the board is QCOM\ndefine is-vendor-board-qcom\n$(if $(strip $(TARGET_BOARD_PLATFORM) $(QCOM_BOARD_PLATFORMS)),$(filter $(TARGET_BOARD_PLATFORM),$(QCOM_BOARD_PLATFORMS)),\\\n  $(error both TARGET_BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) and QCOM_BOARD_PLATFORMS=$(QCOM_BOARD_PLATFORMS)))\nendef\n\n# ---------------------------------------------------------------\n# Check for obsolete PRODUCT- and APP- goals\nifeq ($(CALLED_FROM_SETUP),true)\nproduct_goals := $(strip $(filter PRODUCT-%,$(MAKECMDGOALS)))\nifdef product_goals\n  $(error The PRODUCT-* goal is no longer supported. Use `TARGET_PRODUCT=<product> m droid` instead)\nendif\nunbundled_goals := $(strip $(filter APP-%,$(MAKECMDGOALS)))\nifdef unbundled_goals\n  $(error The APP-* goal is no longer supported. Use `TARGET_BUILD_APPS=\"<app>\" m droid` instead)\nendif # unbundled_goals\nendif\n\n# Default to building dalvikvm on hosts that support it...\nifeq ($(HOST_OS),linux)\n# ... or if the if the option is already set\nifeq ($(WITH_HOST_DALVIK),)\n  WITH_HOST_DALVIK := true\nendif\nendif\n\n# ---------------------------------------------------------------\n# Include the product definitions.\n# We need to do this to translate TARGET_PRODUCT into its\n# underlying TARGET_DEVICE before we start defining any rules.\n#\ninclude $(BUILD_SYSTEM)/node_fns.mk\ninclude $(BUILD_SYSTEM)/product.mk\n\n# Read all product definitions.\n#\n# Products are defined in AndroidProducts.mk files:\nandroid_products_makefiles := $(file <$(OUT_DIR)/.module_paths/AndroidProducts.mk.list) \\\n  $(SRC_TARGET_DIR)/product/AndroidProducts.mk\n\n# An AndroidProduct.mk file sets the following variables:\n#   PRODUCT_MAKEFILES specifies product makefiles. Each item in this list\n#     is either a <product>:path/to/file.mk, or just path/to/<product.mk>\n#   COMMON_LUNCH_CHOICES specifies <product>-<variant> values to be shown\n#     in the `lunch` menu\n#   STARLARK_OPT_IN_PRODUCTS specifies products to use Starlark-based\n#     product configuration by default\n\n# Builds a list of first/second elements of each pair:\n#   $(call _first,a:A b:B,:) returns 'a b'\n#   $(call _second,a-A b-B,-) returns 'A B'\n_first=$(filter-out $(2)%,$(subst $(2),$(space)$(2),$(1)))\n_second=$(filter-out %$(2),$(subst $(2),$(2)$(space),$(1)))\n\n# Returns <product>:<path> pair from a PRODUCT_MAKEFILE item.\n# If an item is <product>:path/to/file.mk, return it as is,\n# otherwise assume that an item is path/to/<product>.mk and\n# return <product>:path/to/<product>.mk\n_product-spec=$(strip $(if $(findstring :,$(1)),$(1),$(basename $(notdir $(1))):$(1)))\n\n# Reads given AndroidProduct.mk file and sets the following variables:\n#  ap_product_paths -- the list of <product>:<path> pairs\n#  ap_common_lunch_choices -- the list of <product>-<build variant> items\n#  ap_products_using_starlark_config -- the list of products using starlark config\n# In addition, validates COMMON_LUNCH_CHOICES and STARLARK_OPT_IN_PRODUCTS values\ndefine _read-ap-file\n  $(eval PRODUCT_MAKEFILES :=) \\\n  $(eval COMMON_LUNCH_CHOICES :=) \\\n  $(eval STARLARK_OPT_IN_PRODUCTS := ) \\\n  $(eval ap_product_paths :=) \\\n  $(eval LOCAL_DIR := $(patsubst %/,%,$(dir $(f)))) \\\n  $(eval include $(f)) \\\n  $(foreach p, $(PRODUCT_MAKEFILES),$(eval ap_product_paths += $(call _product-spec,$(p)))) \\\n  $(eval ap_common_lunch_choices  := $(COMMON_LUNCH_CHOICES)) \\\n  $(eval ap_products_using_starlark_config := $(STARLARK_OPT_IN_PRODUCTS)) \\\n  $(eval _products := $(call _first,$(ap_product_paths),:)) \\\n  $(eval _bad := $(filter-out $(_products),$(call _first,$(ap_common_lunch_choices),-))) \\\n  $(if $(_bad),$(error COMMON_LUNCH_CHOICES contains products(s) not defined in this file: $(_bad))) \\\n  $(eval _bad := $(filter-out %-eng %-userdebug %-user,$(ap_common_lunch_choices))) \\\n  $(if $(_bad),$(error invalid variant in COMMON_LUNCH_CHOICES: $(_bad)))\n  $(eval _bad := $(filter-out $(_products),$(ap_products_using_starlark_config))) \\\n  $(if $(_bad),$(error STARLARK_OPT_IN_PRODUCTS contains product(s) not defined in this file: $(_bad)))\nendef\n\n# Build cumulative lists of all product specs/lunch choices/Starlark-based products.\nproduct_paths :=\ncommon_lunch_choices :=\nproducts_using_starlark_config :=\n$(foreach f,$(android_products_makefiles), \\\n    $(call _read-ap-file,$(f)) \\\n    $(eval product_paths += $(ap_product_paths)) \\\n    $(eval common_lunch_choices += $(ap_common_lunch_choices)) \\\n    $(eval products_using_starlark_config += $(ap_products_using_starlark_config)) \\\n)\n\n# Dedup, extract product names, etc.\nproduct_paths := $(sort $(product_paths))\nall_named_products := $(sort $(call _first,$(product_paths),:))\ncurrent_product_makefile := $(call _second,$(filter $(TARGET_PRODUCT):%,$(product_paths)),:)\nCOMMON_LUNCH_CHOICES := $(sort $(common_lunch_choices))\n\n# Check that there are no duplicate product names\n$(foreach p,$(all_named_products), \\\n  $(if $(filter 1,$(words $(filter $(p):%,$(product_paths)))),, \\\n    $(error Product name must be unique, \"$(p)\" used by $(call _second,$(filter $(p):%,$(product_paths)),:))))\n\nifneq ($(ALLOW_RULES_IN_PRODUCT_CONFIG),)\n_product_config_saved_KATI_ALLOW_RULES := $(.KATI_ALLOW_RULES)\n.KATI_ALLOW_RULES := $(ALLOW_RULES_IN_PRODUCT_CONFIG)\nendif\n\nifeq (,$(current_product_makefile))\n  $(error Cannot locate config makefile for product \"$(TARGET_PRODUCT)\")\nendif\n\nifneq (,$(filter $(TARGET_PRODUCT),$(products_using_starlark_config)))\n  RBC_PRODUCT_CONFIG := true\nendif\n\nifndef RBC_PRODUCT_CONFIG\n$(call import-products, $(current_product_makefile))\nelse\n  $(shell mkdir -p $(OUT_DIR)/rbc)\n  $(call dump-variables-rbc, $(OUT_DIR)/rbc/make_vars_pre_product_config.mk)\n\n  $(shell $(OUT_DIR)/mk2rbc \\\n    --mode=write -r --outdir $(OUT_DIR)/rbc \\\n    --launcher=$(OUT_DIR)/rbc/launcher.rbc \\\n    --input_variables=$(OUT_DIR)/rbc/make_vars_pre_product_config.mk \\\n    --makefile_list=$(OUT_DIR)/.module_paths/configuration.list \\\n    $(current_product_makefile))\n  ifneq ($(.SHELLSTATUS),0)\n    $(error product configuration converter failed: $(.SHELLSTATUS))\n  endif\n\n  $(shell build/soong/scripts/update_out $(OUT_DIR)/rbc/rbc_product_config_results.mk \\\n    $(OUT_DIR)/rbcrun --mode=rbc $(OUT_DIR)/rbc/launcher.rbc)\n  ifneq ($(.SHELLSTATUS),0)\n    $(error product configuration runner failed: $(.SHELLSTATUS))\n  endif\n\n  include $(OUT_DIR)/rbc/rbc_product_config_results.mk\nendif\n\n# This step was already handled in the RBC product configuration.\nifeq ($(RBC_PRODUCT_CONFIG)$(SKIP_ARTIFACT_PATH_REQUIREMENT_PRODUCTS_CHECK),)\n# Import all the products that have made artifact path requirements, so that we can verify\n# the artifacts they produce. They might be intermediate makefiles instead of real products.\n$(foreach makefile,$(ARTIFACT_PATH_REQUIREMENT_PRODUCTS),\\\n  $(if $(filter-out $(makefile),$(PRODUCTS)),$(eval $(call import-products,$(makefile))))\\\n)\nendif\n\nINTERNAL_PRODUCT := $(current_product_makefile)\n# Strip and assign the PRODUCT_ variables.\n$(call strip-product-vars)\n\n# Quick check\n$(check-current-product)\n\nifneq ($(ALLOW_RULES_IN_PRODUCT_CONFIG),)\n.KATI_ALLOW_RULES := $(_saved_KATI_ALLOW_RULES)\n_product_config_saved_KATI_ALLOW_RULES :=\nendif\n\n############################################################################\n\ncurrent_product_makefile :=\n\n# AOSP and Google products currently share the same `apex_contributions` in next.\n# This causes issues when building <aosp_product>-next-userdebug in main.\n# Create a temporary allowlist to ignore the google apexes listed in `contents` of apex_contributions of `next`\n# *for aosp products*.\n# TODO(b/308187268): Remove this denylist mechanism\n# Use PRODUCT_PACKAGES to determine if this is an aosp product. aosp products do not use google signed apexes.\nignore_apex_contributions :=\nifeq (,$(findstring com.google.android.conscrypt,$(PRODUCT_PACKAGES))$(findstring com.google.android.go.conscrypt,$(PRODUCT_PACKAGES)))\n  ignore_apex_contributions := true\nendif\nifeq (true,$(PRODUCT_MODULE_BUILD_FROM_SOURCE))\n  ignore_apex_contributions := true\nendif\nifneq ($(EMMA_INSTRUMENT)$(EMMA_INSTRUMENT_STATIC)$(EMMA_INSTRUMENT_FRAMEWORK)$(CLANG_COVERAGE)$(NATIVE_COVERAGE_PATHS),)\n# Coverage builds for TARGET_RELEASE=foo should always build from source,\n# even if TARGET_RELEASE=foo uses prebuilt mainline modules.\n# This is necessary because the checked-in prebuilts were generated with\n# instrumentation turned off.\n  ignore_apex_contributions := true\nendif\n\nifeq (true, $(ignore_apex_contributions))\nPRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS := true\nendif\n\n#############################################################################\n\n# Quick check and assign default values\n\nTARGET_DEVICE := $(PRODUCT_DEVICE)\n\n# Allow overriding PLATFORM_BASE_OS when PRODUCT_BASE_OS is defined\nifdef PRODUCT_BASE_OS\n  PLATFORM_BASE_OS := $(PRODUCT_BASE_OS)\nelse\n  PLATFORM_BASE_OS := $(PLATFORM_BASE_OS_ENV_INPUT)\nendif\n.KATI_READONLY := PLATFORM_BASE_OS\n\n# TODO: also keep track of things like \"port\", \"land\" in product files.\n\n# Figure out which resoure configuration options to use for this\n# product.\n# If CUSTOM_LOCALES contains any locales not already included\n# in PRODUCT_LOCALES, add them to PRODUCT_LOCALES.\nextra_locales := $(filter-out $(PRODUCT_LOCALES),$(CUSTOM_LOCALES))\nifneq (,$(extra_locales))\n  ifneq ($(CALLED_FROM_SETUP),true)\n    # Don't spam stdout, because envsetup.sh may be scraping values from it.\n    $(info Adding CUSTOM_LOCALES [$(extra_locales)] to PRODUCT_LOCALES [$(PRODUCT_LOCALES)])\n  endif\n  PRODUCT_LOCALES += $(extra_locales)\n  extra_locales :=\nendif\n\n# Add PRODUCT_LOCALES to PRODUCT_AAPT_CONFIG\nPRODUCT_AAPT_CONFIG := $(PRODUCT_LOCALES) $(PRODUCT_AAPT_CONFIG)\n\n# Keep a copy of the space-separated config\nPRODUCT_AAPT_CONFIG_SP := $(PRODUCT_AAPT_CONFIG)\nPRODUCT_AAPT_CONFIG := $(subst $(space),$(comma),$(PRODUCT_AAPT_CONFIG))\n\n###########################################################\n## Add 'platform:' prefix to jars not in <apex>:<module> format.\n##\n## This makes sure that a jar corresponds to ConfigureJarList format of <apex> and <module> pairs\n## where needed.\n##\n## $(1): a list of jars either in <module> or <apex>:<module> format\n###########################################################\n\ndefine qualify-platform-jars\n  $(foreach jar,$(1),$(if $(findstring :,$(jar)),,platform:)$(jar))\nendef\n\n# Extra boot jars must be appended at the end after common boot jars.\nPRODUCT_BOOT_JARS += $(PRODUCT_BOOT_JARS_EXTRA)\n\nPRODUCT_BOOT_JARS := $(call qualify-platform-jars,$(PRODUCT_BOOT_JARS))\n\n# b/191127295: force core-icu4j onto boot image. It comes from a non-updatable APEX jar, but has\n# historically been part of the boot image; even though APEX jars are not meant to be part of the\n# boot image.\n# TODO(b/191686720): remove PRODUCT_APEX_BOOT_JARS to avoid a special handling of core-icu4j\n# in make rules.\nPRODUCT_APEX_BOOT_JARS := $(filter-out com.android.i18n:core-icu4j,$(PRODUCT_APEX_BOOT_JARS))\n# All APEX jars come after /system and /system_ext jars, so adding core-icu4j at the end of the list\nPRODUCT_BOOT_JARS += com.android.i18n:core-icu4j\n\n# The extra system server jars must be appended at the end after common system server jars.\nPRODUCT_SYSTEM_SERVER_JARS += $(PRODUCT_SYSTEM_SERVER_JARS_EXTRA)\n\nPRODUCT_SYSTEM_SERVER_JARS := $(call qualify-platform-jars,$(PRODUCT_SYSTEM_SERVER_JARS))\n\n# Sort APEX boot and system server jars. We use deterministic alphabetical order\n# when constructing BOOTCLASSPATH and SYSTEMSERVERCLASSPATH definition on device\n# after an update. Enforce it in the build system as well to avoid recompiling\n# everything after an update due a change in the order.\nPRODUCT_APEX_BOOT_JARS := $(sort $(PRODUCT_APEX_BOOT_JARS))\nPRODUCT_APEX_SYSTEM_SERVER_JARS := $(sort $(PRODUCT_APEX_SYSTEM_SERVER_JARS))\n\nPRODUCT_STANDALONE_SYSTEM_SERVER_JARS := \\\n  $(call qualify-platform-jars,$(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS))\n\nifndef PRODUCT_SYSTEM_NAME\n  PRODUCT_SYSTEM_NAME := $(PRODUCT_NAME)\nendif\nifndef PRODUCT_SYSTEM_DEVICE\n  PRODUCT_SYSTEM_DEVICE := $(PRODUCT_DEVICE)\nendif\nifndef PRODUCT_SYSTEM_BRAND\n  PRODUCT_SYSTEM_BRAND := $(PRODUCT_BRAND)\nendif\nifndef PRODUCT_MODEL\n  PRODUCT_MODEL := $(PRODUCT_NAME)\nendif\nifndef PRODUCT_SYSTEM_MODEL\n  PRODUCT_SYSTEM_MODEL := $(PRODUCT_MODEL)\nendif\n\nifndef PRODUCT_MANUFACTURER\n  PRODUCT_MANUFACTURER := unknown\nendif\nifndef PRODUCT_SYSTEM_MANUFACTURER\n  PRODUCT_SYSTEM_MANUFACTURER := $(PRODUCT_MANUFACTURER)\nendif\n\nifndef PRODUCT_CHARACTERISTICS\n  TARGET_AAPT_CHARACTERISTICS := default\nelse\n  TARGET_AAPT_CHARACTERISTICS := $(PRODUCT_CHARACTERISTICS)\nendif\n\nifndef PRODUCT_SHIPPING_API_LEVEL\n  PRODUCT_SHIPPING_API_LEVEL := 10000\nendif\n\nifdef PRODUCT_DEFAULT_DEV_CERTIFICATE\n  ifneq (1,$(words $(PRODUCT_DEFAULT_DEV_CERTIFICATE)))\n    $(error PRODUCT_DEFAULT_DEV_CERTIFICATE='$(PRODUCT_DEFAULT_DEV_CERTIFICATE)', \\\n      only 1 certificate is allowed.)\n  endif\nendif\n\n$(foreach apexpair,$(PRODUCT_APEX_BOOT_JARS), \\\n  $(foreach platformpair,$(PRODUCT_BOOT_JARS), \\\n    $(eval apexjar := $(call word-colon,2,$(apexpair))) \\\n    $(eval platformjar := $(call word-colon,2,$(platformpair))) \\\n    $(if $(filter $(apexjar), $(platformjar)), \\\n      $(error A jar in PRODUCT_APEX_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(apexjar) is))))\n\nENFORCE_SYSTEM_CERTIFICATE := $(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT)\nENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST := $(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)\n\nPRODUCT_OTA_PUBLIC_KEYS := $(sort $(PRODUCT_OTA_PUBLIC_KEYS))\nPRODUCT_EXTRA_OTA_KEYS := $(sort $(PRODUCT_EXTRA_OTA_KEYS))\nPRODUCT_EXTRA_RECOVERY_KEYS := $(sort $(PRODUCT_EXTRA_RECOVERY_KEYS))\n\nPRODUCT_VALIDATION_CHECKS := $(sort $(PRODUCT_VALIDATION_CHECKS))\n\n# Resolve and setup per-module dex-preopt configs.\nDEXPREOPT_DISABLED_MODULES :=\n# If a module has multiple setups, the first takes precedence.\n_pdpmc_modules :=\n$(foreach c,$(PRODUCT_DEX_PREOPT_MODULE_CONFIGS),\\\n  $(eval m := $(firstword $(subst =,$(space),$(c))))\\\n  $(if $(filter $(_pdpmc_modules),$(m)),,\\\n    $(eval _pdpmc_modules += $(m))\\\n    $(eval cf := $(patsubst $(m)=%,%,$(c)))\\\n    $(eval cf := $(subst $(_PDPMC_SP_PLACE_HOLDER),$(space),$(cf)))\\\n    $(if $(filter disable,$(cf)),\\\n      $(eval DEXPREOPT_DISABLED_MODULES += $(m)),\\\n      $(eval DEXPREOPT.$(TARGET_PRODUCT).$(m).CONFIG := $(cf)))))\n_pdpmc_modules :=\n\n\n# Resolve and setup per-module sanitizer configs.\n# If a module has multiple setups, the first takes precedence.\n_psmc_modules :=\n$(foreach c,$(PRODUCT_SANITIZER_MODULE_CONFIGS),\\\n  $(eval m := $(firstword $(subst =,$(space),$(c))))\\\n  $(if $(filter $(_psmc_modules),$(m)),,\\\n    $(eval _psmc_modules += $(m))\\\n    $(eval cf := $(patsubst $(m)=%,%,$(c)))\\\n    $(eval cf := $(subst $(_PSMC_SP_PLACE_HOLDER),$(space),$(cf)))\\\n    $(eval SANITIZER.$(TARGET_PRODUCT).$(m).CONFIG := $(cf))))\n_psmc_modules :=\n\n# Reset ADB keys. If RELEASE_BUILD_USE_VARIANT_FLAGS is set look for\n# the value of a dedicated flag. Otherwise check if build variant is\n# non-debuggable.\nifneq (,$(RELEASE_BUILD_USE_VARIANT_FLAGS))\nifneq (,$(RELEASE_BUILD_PURGE_PRODUCT_ADB_KEYS))\n  PRODUCT_ADB_KEYS :=\nendif\nelse ifeq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT)))\n  PRODUCT_ADB_KEYS :=\nendif\n\nifneq ($(filter-out 0 1,$(words $(PRODUCT_ADB_KEYS))),)\n  $(error Only one file may be in PRODUCT_ADB_KEYS: $(PRODUCT_ADB_KEYS))\nendif\n\n# Show a warning wall of text if non-compliance-GSI products set this option.\nifdef PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT\n  ifeq (,$(filter gsi_arm gsi_arm64 gsi_x86 gsi_x86_64 gsi_car_arm64 gsi_car_x86_64 gsi_tv_arm gsi_tv_arm64 clockwork_gsi_google_arm,$(PRODUCT_NAME)))\n    $(warning PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT is set but \\\n      PRODUCT_NAME ($(PRODUCT_NAME)) doesn't look like a GSI for compliance \\\n      testing. This is a special configuration for compliance GSI, so do make \\\n      sure you understand the security implications before setting this \\\n      option. If you don't know what this option does, then you probably \\\n      shouldn't set this.)\n  endif\nendif\n\nifndef PRODUCT_USE_DYNAMIC_PARTITIONS\n  PRODUCT_USE_DYNAMIC_PARTITIONS := $(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)\nendif\n\n# All requirements of PRODUCT_USE_DYNAMIC_PARTITIONS falls back to\n# PRODUCT_USE_DYNAMIC_PARTITIONS if not defined.\nifndef PRODUCT_USE_DYNAMIC_PARTITION_SIZE\n  PRODUCT_USE_DYNAMIC_PARTITION_SIZE := $(PRODUCT_USE_DYNAMIC_PARTITIONS)\nendif\n\nifndef PRODUCT_BUILD_SUPER_PARTITION\n  PRODUCT_BUILD_SUPER_PARTITION := $(PRODUCT_USE_DYNAMIC_PARTITIONS)\nendif\n\nifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),)\n  ifdef PRODUCT_SHIPPING_API_LEVEL\n    ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29))\n      PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS := true\n    endif\n  endif\nendif\n\nifeq ($(PRODUCT_SET_DEBUGFS_RESTRICTIONS),)\n  ifdef PRODUCT_SHIPPING_API_LEVEL\n    ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),31))\n      PRODUCT_SET_DEBUGFS_RESTRICTIONS := true\n    endif\n  endif\nendif\n\n# If build command defines OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS,\n# override PRODUCT_EXTRA_VNDK_VERSIONS with it.\nifdef OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS\n  PRODUCT_EXTRA_VNDK_VERSIONS := $(OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS)\nendif\n\n###########################################\n# APEXes are by default not compressed\n#\n# APEX compression can be forcibly enabled (resp. disabled) by\n# setting OVERRIDE_PRODUCT_COMPRESSED_APEX to true (resp. false), e.g. by\n# setting the OVERRIDE_PRODUCT_COMPRESSED_APEX environment variable.\nifdef OVERRIDE_PRODUCT_COMPRESSED_APEX\n  PRODUCT_COMPRESSED_APEX := $(OVERRIDE_PRODUCT_COMPRESSED_APEX)\nendif\n\nifdef OVERRIDE_PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE\n  PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE := $(OVERRIDE_PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE)\nelse ifeq ($(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE),)\n  # Use ext4 as a default payload fs type\n  PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE := ext4\nendif\nifeq ($(filter ext4 erofs,$(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE)),)\n  $(error PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE should be either erofs or ext4,\\\n    not $(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE).)\nendif\n\n$(KATI_obsolete_var OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS \\\n    ,Use PRODUCT_EXTRA_VNDK_VERSIONS instead)\n\n# If build command defines OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE,\n# override PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE with it unless it is\n# defined as `false`. If the value is `false` clear\n# PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE\n# OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE can be used for\n# testing only.\nifdef OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE\n  ifeq (false,$(OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE))\n    PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE :=\n  else\n    PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := $(OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE)\n  endif\nelse ifeq ($(PRODUCT_SHIPPING_API_LEVEL),)\n  # No shipping level defined. Enforce the product interface by default.\n  PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true\nelse ifeq ($(call math_gt,$(PRODUCT_SHIPPING_API_LEVEL),29),true)\n  # Enforce product interface if PRODUCT_SHIPPING_API_LEVEL is greater than 29.\n  PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true\nendif\n\n$(KATI_obsolete_var OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE,Use PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE instead)\n\n# From Android V, Define PRODUCT_PRODUCT_VNDK_VERSION as current by default.\n# This is required to make all devices have product variants.\nifndef PRODUCT_PRODUCT_VNDK_VERSION\n  PRODUCT_PRODUCT_VNDK_VERSION := current\nendif\n\nifdef PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS\n    $(error PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS is deprecated, consider using RRO for \\\n      $(PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS))\nendif\n\n# This table maps sdk version 35 to vendor api level 202404 and assumes yearly\n# release for the same month. If 10000 API level or more is used, which usually\n# represents 'current' or 'future' API levels, several zeros are added to\n# preserve ordering. Specifically API level 10,000 is converted to 10,000,000\n# which importantly is greater than 202404 = 202,404. This convention will break\n# in 100,000 CE, which is the closest multiple of 10 that doesn't break earlier\n# than 10,000 as an API level breaks.\ndefine sdk-to-vendor-api-level\n$(if $(call math_lt_or_eq,$(1),34),$(1),$(if $(call math_lt,$(1),10000),20$(call int_subtract,$(1),11)04,$(1)000))\nendef\n\nifneq ($(call sdk-to-vendor-api-level,34),34)\n$(error sdk-to-vendor-api-level is broken for pre-Trunk-Stable SDKs)\nendif\nifneq ($(call sdk-to-vendor-api-level,35),202404)\n$(error sdk-to-vendor-api-level is broken for post-Trunk-Stable SDKs)\nendif\nifneq ($(call sdk-to-vendor-api-level,10000),10000000)\n$(error sdk-to-vendor-api-level is broken for current $(call sdk-to-vendor-api-level,10000))\nendif\n\n# VSR API level is the vendor api level of the product shipping API level.\nVSR_VENDOR_API_LEVEL := $(call sdk-to-vendor-api-level,$(PLATFORM_SDK_VERSION))\nifdef PRODUCT_SHIPPING_API_LEVEL\n  VSR_VENDOR_API_LEVEL := $(call sdk-to-vendor-api-level,$(PRODUCT_SHIPPING_API_LEVEL))\nendif\nifdef BOARD_SHIPPING_API_LEVEL\n  # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level.\n  # In this case, the VSR API level is the minimum of the PRODUCT_SHIPPING_API_LEVEL\n  # and RELEASE_BOARD_API_LEVEL\n  board_api_level := $(RELEASE_BOARD_API_LEVEL)\n  ifdef BOARD_API_LEVEL_PROP_OVERRIDE\n    # This must be used only for testing purpose. Product must not be released\n    # with the modified api level value.\n    board_api_level := $(BOARD_API_LEVEL_PROP_OVERRIDE)\n  endif\n  VSR_VENDOR_API_LEVEL := $(call math_min,$(VSR_VENDOR_API_LEVEL),$(board_api_level))\n  board_api_level :=\nendif\n.KATI_READONLY := VSR_VENDOR_API_LEVEL\n\n# Boolean variable determining if vendor seapp contexts is enforced\nCHECK_VENDOR_SEAPP_VIOLATIONS := false\nifneq ($(call math_gt,$(VSR_VENDOR_API_LEVEL),34),)\n  CHECK_VENDOR_SEAPP_VIOLATIONS := true\nelse ifneq ($(PRODUCT_CHECK_VENDOR_SEAPP_VIOLATIONS),)\n  CHECK_VENDOR_SEAPP_VIOLATIONS := $(PRODUCT_CHECK_VENDOR_SEAPP_VIOLATIONS)\nendif\n.KATI_READONLY := CHECK_VENDOR_SEAPP_VIOLATIONS\n\n# Boolean variable determining if selinux labels of /dev are enforced\nCHECK_DEV_TYPE_VIOLATIONS := false\nifneq ($(call math_gt,$(VSR_VENDOR_API_LEVEL),202404),)\n  CHECK_DEV_TYPE_VIOLATIONS := true\nelse ifneq ($(PRODUCT_CHECK_DEV_TYPE_VIOLATIONS),)\n  CHECK_DEV_TYPE_VIOLATIONS := $(PRODUCT_CHECK_DEV_TYPE_VIOLATIONS)\nendif\n.KATI_READONLY := CHECK_DEV_TYPE_VIOLATIONS\n\ndefine product-overrides-config\n$$(foreach rule,$$(PRODUCT_$(1)_OVERRIDES),\\\n    $$(if $$(filter 2,$$(words $$(subst :,$$(space),$$(rule)))),,\\\n        $$(error Rule \"$$(rule)\" in PRODUCT_$(1)_OVERRIDE is not <module_name>:<new_value>)))\nendef\n\n$(foreach var, \\\n    MANIFEST_PACKAGE_NAME \\\n    PACKAGE_NAME \\\n    CERTIFICATE, \\\n  $(eval $(call product-overrides-config,$(var))))\n\n# Macro to use below. $(1) is the name of the partition\ndefine product-build-image-config\nifneq ($$(filter-out true false,$$(PRODUCT_BUILD_$(1)_IMAGE)),)\n    $$(error Invalid PRODUCT_BUILD_$(1)_IMAGE: $$(PRODUCT_BUILD_$(1)_IMAGE) -- true false and empty are supported)\nendif\nendef\n\nifndef PRODUCT_VIRTUAL_AB_COW_VERSION\n  PRODUCT_VIRTUAL_AB_COW_VERSION := 2\n  ifdef PRODUCT_SHIPPING_API_LEVEL\n    ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),34))\n      PRODUCT_VIRTUAL_AB_COW_VERSION := 3\n    endif\n  endif\nendif\n\n# Copy and check the value of each PRODUCT_BUILD_*_IMAGE variable\n$(foreach image, \\\n    PVMFW \\\n    SYSTEM \\\n    SYSTEM_OTHER \\\n    VENDOR \\\n    PRODUCT \\\n    SYSTEM_EXT \\\n    ODM \\\n    VENDOR_DLKM \\\n    ODM_DLKM \\\n    SYSTEM_DLKM \\\n    CACHE \\\n    RAMDISK \\\n    USERDATA \\\n    BOOT \\\n    RECOVERY, \\\n  $(eval $(call product-build-image-config,$(image))))\n\nproduct-build-image-config :=\n\nifdef PRODUCT_SOONG_ONLY\n  ifneq ($(PRODUCT_SOONG_ONLY),true)\n    ifneq ($(PRODUCT_SOONG_ONLY),false)\n      $(error PRODUCT_SOONG_ONLY can only be true, false or unset)\n    endif\n  endif\nendif\n\n$(call readonly-product-vars)\n"
  },
  {
    "path": "core/product_config.rbc",
    "content": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Runtime functions.\"\"\"\n\n_soong_config_namespaces_key = \"$SOONG_CONFIG_NAMESPACES\"\n_dist_for_goals_key = \"$dist_for_goals\"\ndef _init_globals(input_variables_init):\n    \"\"\"Initializes dictionaries of global variables.\n\n    This function runs the given input_variables_init function,\n    passing it a globals dictionary and a handle as if it\n    were a regular product. It then returns 2 copies of\n    the globals dictionary, so that one can be kept around\n    to diff changes made to the other later.\n    \"\"\"\n    globals_base = {\"PRODUCT_SOONG_NAMESPACES\": []}\n    input_variables_init(globals_base, __h_new())\n\n    # Rerun input_variables_init to produce a copy\n    # of globals_base, because starlark doesn't support\n    # deep copying objects.\n    globals = {\"PRODUCT_SOONG_NAMESPACES\": []}\n    input_variables_init(globals, __h_new())\n\n    # Variables that should be defined.\n    mandatory_vars = [\n        \"PLATFORM_VERSION_CODENAME\",\n        \"PLATFORM_VERSION\",\n        \"PRODUCT_SOONG_NAMESPACES\",\n        # TODO(asmundak): do we need TARGET_ARCH? AOSP does not reference it\n        \"TARGET_BUILD_VARIANT\",\n        \"TARGET_PRODUCT\",\n    ]\n    for bv in mandatory_vars:\n        if not bv in globals:\n            fail(bv, \" is not defined\")\n\n    return (globals, globals_base)\n\ndef __print_attr(attr, value):\n    # Allow using empty strings to clear variables, but not None values\n    if value == None:\n        return\n    if type(value) == \"list\":\n        value = list(value)\n        for i, x in enumerate(value):\n            if type(x) == \"tuple\" and len(x) == 1:\n                value[i] = \"@inherit:\" + x[0] + \".mk\"\n            elif type(x) != \"string\":\n                fail(\"Wasn't a list of strings:\", attr, \" value:\", value)\n        print(attr, \":=\", \" \".join(value))\n    else:\n        # Trim all spacing to a single space\n        print(attr, \":=\", _mkstrip(value))\n\ndef _printvars(state):\n    \"\"\"Prints configuration and global variables.\"\"\"\n    (globals, globals_base) = state\n    for attr, val in sorted(globals.items()):\n        if attr == _soong_config_namespaces_key:\n            __print_attr(\"SOONG_CONFIG_NAMESPACES\", val.keys())\n            for nsname, nsvars in sorted(val.items()):\n                # Define SOONG_CONFIG_<ns> for Make, othewise\n                # it cannot be added to .KATI_READONLY list\n                print(\"SOONG_CONFIG_\" + nsname, \":=\", \" \".join(nsvars.keys()))\n                for var, val in sorted(nsvars.items()):\n                    if val:\n                        __print_attr(\"SOONG_CONFIG_%s_%s\" % (nsname, var), val)\n                    else:\n                        print(\"SOONG_CONFIG_%s_%s :=\" % (nsname, var))\n        elif attr == _dist_for_goals_key:\n            goals = []\n            src_dst_list = []\n            goal_dst_list = []\n            for goal_name, goal_src_dst_list in sorted(val.items()):\n                goals.append(goal_name)\n                for sd in sorted(goal_src_dst_list):\n                    src_dst_list.append(\":\".join(sd))\n                    goal_dst_list.append(\":\".join((goal_name, sd[1])))\n            print(\"_all_dist_goal_output_pairs:=\", \" \".join(goal_dst_list))\n            print(\"_all_dist_goals:=\", \" \".join(goals))\n            print(\"_all_dist_src_dst_pairs:=\", \" \".join(src_dst_list))\n        elif attr not in globals_base or globals_base[attr] != val:\n            __print_attr(attr, val)\n\ndef __sort_pcm_names(pcm_names):\n    # We have to add an extension back onto the pcm names when sorting,\n    # or else the sort order could be wrong when one is a prefix of another.\n    return [x[:-3] for x in sorted([y + \".mk\" for y in pcm_names], reverse=True)]\n\ndef _product_configuration(top_pcm_name, top_pcm, input_variables_init):\n    \"\"\"Creates configuration.\"\"\"\n\n    # Product configuration is created by traversing product's inheritance\n    # tree. It is traversed twice.\n    # First, beginning with top-level module we execute a module and find\n    # its ancestors, repeating this recursively. At the end of this phase\n    # we get the full inheritance tree.\n    # Second, we traverse the tree in the postfix order (i.e., visiting a\n    # node after its ancestors) to calculate the product configuration.\n    #\n    # PCM means \"Product Configuration Module\", i.e., a Starlark file\n    # whose body consists of a single init function.\n\n    globals, globals_base = _init_globals(input_variables_init)\n\n    # Each PCM is represented by a quadruple of function, config, children names\n    # and readyness (that is, the configurations from inherited PCMs have been\n    # substituted).\n    configs = {top_pcm_name: (top_pcm, None, [], False)}  # All known PCMs\n\n    # Stack containing PCMs to be processed\n    pcm_stack = [top_pcm_name]\n\n    # Run it until pcm_stack is exhausted, but no more than N times\n    for n in range(1000):\n        if not pcm_stack:\n            break\n        name = pcm_stack.pop()\n        pcm, cfg, c, _ = configs[name]\n\n        # cfg is set only after PCM has been called, leverage this\n        # to prevent calling the same PCM twice\n        if cfg != None:\n            continue\n\n        # Run this one, obtaining its configuration and child PCMs.\n        if _options.trace_modules:\n            rblf_log(\"%d: %s\" % (n, name))\n\n        # Run PCM.\n        handle = __h_new()\n        pcm(globals, handle)\n\n        if handle.artifact_path_requirements:\n            globals[\"PRODUCTS.\"+name+\".mk.ARTIFACT_PATH_REQUIREMENTS\"] = handle.artifact_path_requirements\n            globals[\"PRODUCTS.\"+name+\".mk.ARTIFACT_PATH_ALLOWED_LIST\"] = handle.artifact_path_allowed_list\n            globals[\"PRODUCTS.\"+name+\".mk.ARTIFACT_PATH_REQUIREMENT_IS_RELAXED\"] = \"true\" if handle.artifact_path_requirement_is_relaxed[0] else \"\"\n            globals.setdefault(\"ARTIFACT_PATH_REQUIREMENT_PRODUCTS\", [])\n            globals[\"ARTIFACT_PATH_REQUIREMENT_PRODUCTS\"] = sorted(globals[\"ARTIFACT_PATH_REQUIREMENT_PRODUCTS\"] + [name+\".mk\"])\n\n        if handle.product_enforce_packages_exist[0]:\n            globals[\"PRODUCTS.\"+name+\".mk.PRODUCT_ENFORCE_PACKAGES_EXIST\"] = \"true\"\n            globals[\"PRODUCTS.\"+name+\".mk.PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST\"] = handle.product_enforce_packages_exist_allow_list\n\n        # Now we know everything about this PCM, record it in 'configs'.\n        children = handle.inherited_modules\n        if _options.trace_modules:\n            rblf_log(\"   \", \"    \".join(children.keys()))\n        # Starlark dictionaries are guaranteed to iterate through in insertion order,\n        # so children.keys() will be ordered by the inherit() calls\n        configs[name] = (pcm, handle.cfg, children.keys(), False)\n\n        for child_name in __sort_pcm_names(children.keys()):\n            if child_name not in configs:\n                configs[child_name] = (children[child_name], None, [], False)\n            pcm_stack.append(child_name)\n    if pcm_stack:\n        fail(\"Inheritance processing took too many iterations\")\n\n    for pcm_name in globals.get(\"ARTIFACT_PATH_REQUIREMENT_PRODUCTS\", []):\n        for var, val in evaluate_finalized_product_variables(configs, pcm_name[:-3]).items():\n            globals[\"PRODUCTS.\"+pcm_name+\".\"+var] = val\n\n    # Copy product config variables from the cfg dictionary to the\n    # PRODUCTS.<top_level_makefile_name>.<var_name> global variables.\n    for var, val in evaluate_finalized_product_variables(configs, top_pcm_name, _options.trace_modules).items():\n        globals[\"PRODUCTS.\"+top_pcm_name+\".mk.\"+var] = val\n\n    # Record inheritance hierarchy in PRODUCTS.<file>.INHERITS_FROM variables.\n    # This is required for m product-graph.\n    for config in configs:\n        if len(configs[config][2]) > 0:\n            globals[\"PRODUCTS.\"+config+\".mk.INHERITS_FROM\"] = sorted([x + \".mk\" for x in configs[config][2]])\n    globals[\"PRODUCTS\"] = __words(globals.get(\"PRODUCTS\", [])) + [top_pcm_name + \".mk\"]\n\n    return (globals, globals_base)\n\ndef evaluate_finalized_product_variables(configs, top_level_pcm_name, trace=False):\n    configs_postfix = []\n    pcm_stack = [(top_level_pcm_name, True)]\n    for i in range(1000):\n        if not pcm_stack:\n            break\n\n        pcm_name, before = pcm_stack.pop()\n        if before:\n            pcm_stack.append((pcm_name, False))\n            for child in __sort_pcm_names(configs[pcm_name][2]):\n                pcm_stack.append((child, True))\n        else:\n            configs_postfix.append(pcm_name)\n    if pcm_stack:\n        fail(\"Inheritance processing took too many iterations\")\n\n    # clone the configs, because in the process of evaluating the\n    # final cfg dictionary we will remove values from the intermediate\n    # cfg dictionaries. We need to be able to call evaluate_finalized_product_variables()\n    # multiple times, so we can't change the origional configs object.\n    cloned_configs = {}\n    for pcm_name in configs:\n        # skip unneeded pcms\n        if pcm_name not in configs_postfix:\n            continue\n        pcm, cfg, children_names, ready = configs[pcm_name]\n        cloned_cfg = {}\n        for var, val in cfg.items():\n            if type(val) == 'list':\n                cloned_cfg[var] = list(val)\n            else:\n                cloned_cfg[var] = val\n        cloned_configs[pcm_name] = (pcm, cloned_cfg, children_names, ready)\n    configs = cloned_configs\n\n    if trace:\n        rblf_log(\"\\n---Postfix---\")\n        for x in configs_postfix:\n            rblf_log(\"   \", x)\n\n    # Traverse the tree from the bottom, evaluating inherited values\n    for pcm_name in configs_postfix:\n        pcm, cfg, children_names, ready = configs[pcm_name]\n\n        # Should run\n        if cfg == None:\n            fail(\"%s: has not been run\" % pcm_name)\n\n        # Ready once\n        if ready:\n            continue\n\n        # Children should be ready\n        for child_name in children_names:\n            if not configs[child_name][3]:\n                fail(\"%s: child is not ready\" % child_name)\n\n        _substitute_inherited(configs, pcm_name, cfg)\n        _percolate_inherited(configs, pcm_name, cfg, children_names)\n        configs[pcm_name] = pcm, cfg, children_names, True\n    return configs[top_level_pcm_name][1]\n\ndef _dictionary_difference(a, b):\n    result = {}\n    for attr, val in a.items():\n        if attr not in b or b[attr] != val:\n            result[attr] = val\n    return result\n\ndef _board_configuration(board_config_init, input_variables_init):\n    globals_base = {}\n    h_base = __h_new()\n    globals = {}\n    h = __h_new()\n\n    input_variables_init(globals_base, h_base)\n    input_variables_init(globals, h)\n    board_config_init(globals, h)\n\n    # Board configuration files aren't really supposed to change\n    # product configuration variables, but some do. You lose the\n    # inheritance features of the product config variables if you do.\n    for var, value in _dictionary_difference(h.cfg, h_base.cfg).items():\n        globals[var] = value\n\n    return (globals, globals_base)\n\n\ndef _substitute_inherited(configs, pcm_name, cfg):\n    \"\"\"Substitutes inherited values in all the attributes.\n\n    When a value of an attribute is a list, some of its items may be\n    references to a value of a same attribute in an inherited product,\n    e.g., for a given module PRODUCT_PACKAGES can be\n      [\"foo\", (submodule), \"bar\"]\n    and for 'submodule' PRODUCT_PACKAGES may be [\"baz\"]\n    (we use a tuple to distinguish submodule references).\n    After the substitution the value of PRODUCT_PACKAGES for the module\n    will become [\"foo\", \"baz\", \"bar\"]\n    \"\"\"\n    for attr, val in cfg.items():\n        # TODO(asmundak): should we handle single vars?\n        if type(val) != \"list\":\n            continue\n\n        if attr not in _options.trace_variables:\n            cfg[attr] = _value_expand(configs, attr, val)\n        else:\n            old_val = val\n            new_val = _value_expand(configs, attr, val)\n            if new_val != old_val:\n                rblf_log(\"%s(i): %s=%s (was %s)\" % (pcm_name, attr, new_val, old_val))\n            cfg[attr] = new_val\n\ndef _value_expand(configs, attr, values_list):\n    \"\"\"Expands references to inherited values in a given list.\"\"\"\n    result = []\n    expanded = {}\n    for item in values_list:\n        # Inherited values are 1-tuples\n        if type(item) != \"tuple\":\n            result.append(item)\n            continue\n        child_name = item[0]\n        if child_name in expanded:\n            continue\n        expanded[child_name] = True\n        child = configs[child_name]\n        if not child[3]:\n            fail(\"%s should be ready\" % child_name)\n        __move_items(result, child[1], attr)\n\n    return result\n\ndef _percolate_inherited(configs, cfg_name, cfg, children_names):\n    \"\"\"Percolates the settings that are present only in children.\"\"\"\n    percolated_attrs = {}\n    for child_name in children_names:\n        child_cfg = configs[child_name][1]\n        for attr, value in child_cfg.items():\n            if type(value) != \"list\":\n                continue\n            if attr in percolated_attrs:\n                # We already are percolating this one, just add this list\n                __move_items(cfg[attr], child_cfg, attr)\n            elif not attr in cfg:\n                percolated_attrs[attr] = True\n                cfg[attr] = []\n                __move_items(cfg[attr], child_cfg, attr)\n\n    # single value variables need to be inherited in alphabetical order,\n    # not in the order of inherit() calls.\n    for child_name in sorted(children_names):\n        child_cfg = configs[child_name][1]\n        for attr, value in child_cfg.items():\n            if type(value) != \"list\":\n                # Single value variables take the first value available from the leftmost\n                # branch of the tree. If we also had \"or attr in percolated_attrs\" in this\n                # if statement, it would take the value from the rightmost branch.\n                if cfg.get(attr, \"\") == \"\":\n                    cfg[attr] = value\n                    percolated_attrs[attr] = True\n                    child_cfg.pop(attr)\n\n    for attr in _options.trace_variables:\n        if attr in percolated_attrs:\n            rblf_log(\"%s: %s^=%s\" % (cfg_name, attr, cfg[attr]))\n\ndef __move_items(to_list, from_cfg, attr):\n    value = from_cfg.get(attr, [])\n    if value:\n        to_list.extend(value)\n        from_cfg.pop(attr)\n\ndef _indirect(pcm_name):\n    \"\"\"Returns configuration item for the inherited module.\"\"\"\n    return (pcm_name,)\n\ndef _soong_config_namespace(g, nsname):\n    \"\"\"Adds given namespace if it does not exist.\"\"\"\n\n    old = g.get(_soong_config_namespaces_key, {})\n    if old.get(nsname):\n        return\n\n    # A value cannot be updated, so we need to create a new dictionary\n    g[_soong_config_namespaces_key] = dict([(k,v) for k,v in old.items()] + [(nsname, {})])\n\ndef _soong_config_set(g, nsname, var, value):\n    \"\"\"Assigns the value to the variable in the namespace.\"\"\"\n    _soong_config_namespace(g, nsname)\n    g[_soong_config_namespaces_key][nsname][var]=_mkstrip(value)\n\ndef _soong_config_set_bool(g, nsname, var, value):\n    \"\"\"Assigns the value to the variable in the namespace, and marks it as a boolean.\"\"\"\n    _soong_config_set(g, nsname, var, _filter(\"true\", value))\n    g[\"SOONG_CONFIG_TYPE_%s_%s\" % (nsname, var)] = \"bool\"\n\ndef _soong_config_append(g, nsname, var, value):\n    \"\"\"Appends to the value of the variable in the namespace.\"\"\"\n    _soong_config_namespace(g, nsname)\n    ns = g[_soong_config_namespaces_key][nsname]\n    oldv = ns.get(var)\n    if oldv == None:\n        ns[var] = _mkstrip(value)\n    else:\n        ns[var] += \" \" + _mkstrip(value)\n\n\ndef _soong_config_get(g, nsname, var):\n    \"\"\"Gets to the value of the variable in the namespace.\"\"\"\n    return g.get(_soong_config_namespaces_key, {}).get(nsname, {}).get(var, None)\n\ndef _abspath(paths):\n    \"\"\"Provided for compatibility, to be removed later.\"\"\"\n    cwd = rblf_shell('pwd')\n    results = []\n    for path in __words(paths):\n        if path[0] != \"/\":\n            path = cwd + \"/\" + path\n\n        resultparts = []\n        for part in path.split('/'):\n            if part == \".\" or part == \"\":\n                continue\n            elif part == \"..\":\n                if resultparts:\n                    resultparts.pop()\n            else:\n                resultparts.append(part)\n        results.append(\"/\" + \"/\".join(resultparts))\n\n    return \" \".join(results)\n\n\ndef _addprefix(prefix, string_or_list):\n    \"\"\"Adds prefix and returns a list.\n\n    If string_or_list is a list, prepends prefix to each element.\n    Otherwise, string_or_list is considered to be a string which\n    is split into words and then prefix is prepended to each one.\n\n    Args:\n        prefix\n        string_or_list\n\n    \"\"\"\n    return [prefix + x for x in __words(string_or_list)]\n\ndef _addsuffix(suffix, string_or_list):\n    \"\"\"Adds suffix and returns a list.\n\n    If string_or_list is a list, appends suffix to each element.\n    Otherwise, string_or_list is considered to be a string which\n    is split into words and then suffix is appended to each one.\n\n    Args:\n      suffix\n      string_or_list\n    \"\"\"\n    return [x + suffix for x in __words(string_or_list)]\n\ndef __words(string_or_list):\n    if type(string_or_list) == \"list\":\n        for x in string_or_list:\n            if type(x) != \"string\":\n                return string_or_list\n        string_or_list = \" \".join(string_or_list)\n    return _mkstrip(string_or_list).split()\n\n# Handle manipulation functions.\n# A handle passed to a PCM consists of:\n#   product attributes dict (\"cfg\")\n#   inherited modules dict (maps module name to PCM)\n#   default value list (initially empty, modified by inheriting)\ndef __h_new():\n    \"\"\"Constructs a handle which is passed to PCM.\"\"\"\n    return struct(\n        cfg = dict(),\n        inherited_modules = dict(),\n        default_list_value = list(),\n        artifact_path_requirements = list(),\n        artifact_path_allowed_list = list(),\n        artifact_path_requirement_is_relaxed = [False], # as a list so that we can reassign it\n        product_enforce_packages_exist = [False],\n        product_enforce_packages_exist_allow_list = [],\n    )\n\ndef __h_cfg(handle):\n    \"\"\"Returns PCM's product configuration attributes dict.\n\n    This function is also exported as rblf.cfg, and every PCM\n    calls it at the beginning.\n    \"\"\"\n    return handle.cfg\n\ndef _setdefault(handle, attr):\n    \"\"\"If attribute has not been set, assigns default value to it.\n\n    This function is exported as rblf.setdefault().\n    Only list attributes are initialized this way. The default\n    value is kept in the PCM's handle. Calling inherit() updates it.\n    \"\"\"\n    cfg = handle.cfg\n    if cfg.get(attr) == None:\n        cfg[attr] = list(handle.default_list_value)\n    return cfg[attr]\n\ndef _inherit(handle, pcm_name, pcm):\n    \"\"\"Records inheritance.\n\n    This function is exported as rblf.inherit, PCM calls it when\n    a module is inherited.\n    \"\"\"\n    handle.inherited_modules[pcm_name] = pcm\n    handle.default_list_value.append(_indirect(pcm_name))\n\n    # Add inherited module reference to all configuration values\n    for attr, val in handle.cfg.items():\n        if type(val) == \"list\":\n            val.append(_indirect(pcm_name))\n\ndef __base(path):\n    \"\"\"Returns basename.\"\"\"\n    return path.rsplit(\"/\",1)[-1]\n\ndef _board_platform_in(g, string_or_list):\n    \"\"\"Returns true if board is in the list.\"\"\"\n    board = g.get(\"TARGET_BOARD_PLATFORM\",\"\")\n    if not board:\n        return False\n    return board in __words(string_or_list)\n\n\ndef _board_platform_is(g, s):\n    \"\"\"True if board is the same as argument.\"\"\"\n    return g.get(\"TARGET_BOARD_PLATFORM\",\"\") == s\n\n\ndef _copy_files(l, outdir):\n    \"\"\"Generate <item>:<outdir>/item for each item.\"\"\"\n    return [\"%s:%s/%s\" % (path, outdir, __base(path)) for path in __words(l)]\n\ndef _copy_if_exists(path_pair):\n    \"\"\"If from file exists, returns [from:to] pair.\"\"\"\n    value = path_pair.split(\":\", 2)\n\n    if value[0].find('*') != -1:\n        fail(\"copy_if_exists: input file cannot contain *\")\n\n    # Check that l[0] exists\n    return [\":\".join(value)] if rblf_wildcard(value[0]) else []\n\ndef _enforce_product_packages_exist(handle, pkg_string_or_list=[]):\n    \"\"\"Makes including non-existent modules in PRODUCT_PACKAGES an error.\"\"\"\n    handle.product_enforce_packages_exist[0] = True\n    handle.product_enforce_packages_exist_allow_list.clear()\n    handle.product_enforce_packages_exist_allow_list.extend(__words(pkg_string_or_list))\n\ndef _add_product_dex_preopt_module_config(handle, modules, config):\n    \"\"\"Equivalent to add-product-dex-preopt-module-config from build/make/core/product.mk.\"\"\"\n    modules = __words(modules)\n    config = _mkstrip(config).replace(\" \", \"|@SP@|\")\n    _setdefault(handle, \"PRODUCT_DEX_PREOPT_MODULE_CONFIGS\")\n    handle.cfg[\"PRODUCT_DEX_PREOPT_MODULE_CONFIGS\"] += [m + \"=\" + config for m in modules]\n\ndef _find_and_copy(pattern, from_dir, to_dir):\n    \"\"\"Return a copy list for the files matching the pattern.\"\"\"\n    return sorted([(\"%s/%s:%s/%s\" % (from_dir, f, to_dir, f))\n        .replace(\"//\", \"/\") for f in rblf_find_files(from_dir, pattern, only_files=1)])\n\ndef _findstring(needle, haystack):\n    \"\"\"Equivalent to GNU make's $(findstring).\"\"\"\n    if haystack.find(needle) < 0:\n        return \"\"\n    return needle\n\ndef _filter_out(pattern, text):\n    \"\"\"Return all the words from `text' that do not match any word in `pattern'.\n\n    Args:\n        pattern: string or list of words. '%' stands for wildcard (in regex terms, '.*')\n        text: string or list of words\n    Return:\n        list of words\n    \"\"\"\n    patterns = [__mkparse_pattern(x) for x in __words(pattern)]\n    res = []\n    for w in __words(text):\n        match = False\n        for p in patterns:\n            if __mkpattern_matches(p, w):\n                match = True\n                break\n        if not match:\n            res.append(w)\n    return res\n\ndef _filter(pattern, text):\n    \"\"\"Return all the words in `text` that match `pattern`.\n\n    Args:\n        pattern: strings of words or a list. A word can contain '%',\n         which stands for any sequence of characters.\n        text: string or list of words.\n    \"\"\"\n    patterns = [__mkparse_pattern(x) for x in __words(pattern)]\n    res = []\n    for w in __words(text):\n        for p in patterns:\n            if __mkpattern_matches(p, w):\n                res.append(w)\n                break\n    return res\n\ndef _first_word(input):\n    \"\"\"Equivalent to the GNU make function $(firstword).\"\"\"\n    input = __words(input)\n    if len(input) == 0:\n        return \"\"\n    return input[0]\n\ndef _last_word(input):\n    \"\"\"Equivalent to the GNU make function $(lastword).\"\"\"\n    input = __words(input)\n    l = len(input)\n    if l == 0:\n        return \"\"\n    return input[l-1]\n\ndef _flatten_2d_list(list):\n    result = []\n    for x in list:\n        result += x\n    return result\n\ndef _dir(paths):\n    \"\"\"Equivalent to the GNU make function $(dir).\n\n    Returns the folder of the file for each path in paths.\n    \"\"\"\n    return \" \".join([w.rsplit(\"/\",1)[0] for w in __words(paths)])\n\ndef _notdir(paths):\n    \"\"\"Equivalent to the GNU make function $(notdir).\n\n    Returns the name of the file at the end of each path in paths.\n    \"\"\"\n    return \" \".join([__base(w) for w in __words(paths)])\n\ndef _require_artifacts_in_path(handle, paths, allowed_paths):\n    \"\"\"Equivalent to require-artifacts-in-path in Make.\"\"\"\n    handle.artifact_path_requirements.clear()\n    handle.artifact_path_requirements.extend(__words(paths))\n    handle.artifact_path_allowed_list.clear()\n    handle.artifact_path_allowed_list.extend(__words(allowed_paths))\n\ndef _require_artifacts_in_path_relaxed(handle, paths, allowed_paths):\n    \"\"\"Equivalent to require-artifacts-in-path-relaxed in Make.\"\"\"\n    _require_artifacts_in_path(handle, paths, allowed_paths)\n    handle.artifact_path_requirement_is_relaxed[0] = True\n\ndef _expand_wildcard(pattern):\n    \"\"\"Expands shell wildcard pattern.\"\"\"\n    result = []\n    for word in __words(pattern):\n        result.extend(rblf_wildcard(word))\n    return result\n\ndef _mkdist_for_goals(g, goal, src_dst_list):\n    \"\"\"Implements dist-for-goals macro.\"\"\"\n    goals_map = g.get(_dist_for_goals_key, {})\n    pairs = goals_map.get(goal)\n    if pairs == None:\n        pairs = []\n        g[_dist_for_goals_key] = dict([(k,v) for k,v in goals_map.items()] + [(goal, pairs)])\n    for src_dst in __words(src_dst_list):\n        pair=src_dst.split(\":\")\n        if len(pair) > 2:\n            fail(src_dst + \" should be a :-separated pair\")\n        pairs.append((pair[0],pair[1] if len(pair) == 2 and pair[1] else __base(pair[0])))\n    g[_dist_for_goals_key][goal] = pairs\n\n\ndef _mkerror(file, message = \"\"):\n    \"\"\"Prints error and stops.\"\"\"\n    fail(\"%s: %s. Stop\" % (file, message))\n\ndef _mkwarning(file, message = \"\"):\n    \"\"\"Prints warning.\"\"\"\n    rblf_log(file, \"warning\", message, sep = ':')\n\ndef _mk2rbc_error(loc, message):\n    \"\"\"Prints a message about conversion error and stops.\"\"\"\n    _mkerror(loc, message)\n\ndef _mkinfo(file, message = \"\"):\n    \"\"\"Prints info.\"\"\"\n    rblf_log(message)\n\n\ndef __mkparse_pattern(pattern):\n    \"\"\"Parses Make's patsubst pattern.\n\n    This is equivalent to pattern.split('%', 1), except it\n    also takes into account escaping the % symbols.\n    \"\"\"\n    in_escape = False\n    res = []\n    acc = \"\"\n    for c in pattern.elems():\n        if in_escape:\n            in_escape = False\n            acc += c\n        elif c == '\\\\':\n            in_escape = True\n        elif c == '%' and not res:\n            res.append(acc)\n            acc = ''\n        else:\n            acc += c\n    if in_escape:\n        acc += '\\\\'\n    res.append(acc)\n    return res\n\ndef __mkpattern_matches(pattern, word):\n    \"\"\"Returns if a pattern matches a given word.\n\n    The pattern must be a list of strings of length at most 2.\n    This checks if word is either equal to the pattern or\n    starts/ends with the two parts of the pattern.\n    \"\"\"\n    if len(pattern) > 2:\n        fail(\"Pattern can have at most 2 components\")\n    elif len(pattern) == 1:\n        return pattern[0]==word\n    else:\n        return ((len(word) >= len(pattern[0])+len(pattern[1]))\n            and word.startswith(pattern[0])\n            and word.endswith(pattern[1]))\n\ndef __mkpatsubst_word(parsed_pattern,parsed_subst, word):\n    (before, after) = parsed_pattern\n    if not word.startswith(before):\n        return word\n    if not word.endswith(after):\n        return word\n    if len(parsed_subst) < 2:\n        return parsed_subst[0]\n    return parsed_subst[0] + word[len(before):len(word) - len(after)] + parsed_subst[1]\n\n\ndef _mkpatsubst(pattern, replacement, s):\n    \"\"\"Emulates Make's patsubst.\n\n    Tokenizes `s` (unless it is already a list), and then performs a simple\n    wildcard substitution (in other words, `foo%bar` pattern is equivalent to\n    the regular expression `^foo(.*)bar$, and the first `%` in replacement is\n    $1 in regex terms).\n    \"\"\"\n    parsed_pattern = __mkparse_pattern(pattern)\n    if len(parsed_pattern) == 1:\n        out_words = [ replacement if x == pattern else x for x in __words(s)]\n    else:\n        parsed_replacement = __mkparse_pattern(replacement)\n        out_words = [__mkpatsubst_word(parsed_pattern, parsed_replacement, x) for x in __words(s)]\n    return out_words if type(s) == \"list\" else \" \".join(out_words)\n\n\ndef _mksort(input):\n    \"\"\"Emulate Make's sort.\n\n    This is unique from a regular sort in that it also strips\n    the input, and removes duplicate words from the input.\n    \"\"\"\n    input = sorted(__words(input))\n    result = []\n    for w in input:\n        if len(result) == 0 or result[-1] != w:\n            result.append(w)\n    return result\n\n\ndef _mkstrip(s):\n    \"\"\"Emulates Make's strip.\n\n    That is, removes string's leading and trailing whitespace characters and\n    replaces any sequence of whitespace characters with with a single space.\n    \"\"\"\n    t = type(s)\n    if t == \"list\":\n        s = \" \".join(s)\n    elif t != \"string\":\n        fail(\"Argument to mkstrip must be a string or list, got: \"+t)\n    result = \"\"\n    was_space = False\n    for ch in s.strip().elems():\n        is_space = ch.isspace()\n        if not is_space:\n            if was_space:\n                result += \" \"\n            result += ch\n        was_space = is_space\n    return result\n\ndef _mksubst(old, new, s):\n    \"\"\"Emulates Make's subst.\n\n    Replaces each occurence of 'old' with 'new'.\n    If 's' is a list, applies substitution to each item.\n    \"\"\"\n    if type(s) == \"list\":\n        return [e.replace(old, new) for e in s]\n    return s.replace(old, new)\n\n\ndef _product_copy_files_by_pattern(src, dest, s):\n    \"\"\"Creates a copy list.\n\n    For each item in a given list, create <from>:<to> pair, where <from> and\n    <to> are the results of applying Make-style patsubst of <src> and <dest>\n    respectively. E.g. the result of calling this function with\n    (\"foo/%\", \"bar/%\", [\"a\", \"b\"])  will be\n    [\"foo/a:bar/a\", \"foo/b:bar/b\"].\n    \"\"\"\n    parsed_src = __mkparse_pattern(src)\n    parsed_dest = __mkparse_pattern(dest)\n    parsed_percent = [\"\", \"\"]\n    words = s if type(s) == \"list\" else _mkstrip(s).split(\" \")\n    return [ __mkpatsubst_word(parsed_percent, parsed_src, x) + \":\" + __mkpatsubst_word(parsed_percent, parsed_dest, x) for x in words]\n\n\n__zero_values = {\n    \"string\": \"\",\n    \"list\": [],\n    \"int\": 0,\n    \"float\": 0,\n    \"bool\": False,\n    \"dict\": {},\n    \"NoneType\": None,\n    \"tuple\": (),\n}\ndef __zero_value(x):\n    t = type(x)\n    if t in __zero_values:\n        return __zero_values[t]\n    else:\n        fail(\"Unknown type: \"+t)\n\n\ndef _clear_var_list(g, h, var_list):\n    cfg = __h_cfg(h)\n    for v in __words(var_list):\n        # Set these variables to their zero values rather than None\n        # or removing them from the dictionary because if they were\n        # removed entirely, ?= would set their value, when it would not\n        # after a make-based clear_var_list call.\n        if v in g:\n            g[v] = __zero_value(g[v])\n        if v in cfg:\n            cfg[v] = __zero_value(cfg[v])\n\n        if v not in cfg and v not in g:\n            # Cause the variable to appear set like the make version does\n            g[v] = \"\"\n\n# Settings used during debugging.\n_options = struct(\n    trace_modules = False,\n    trace_variables = [],\n)\n\nrblf = struct(\n    soong_config_namespace = _soong_config_namespace,\n    soong_config_append = _soong_config_append,\n    soong_config_set = _soong_config_set,\n    soong_config_set_bool = _soong_config_set_bool,\n    soong_config_get = _soong_config_get,\n    abspath = _abspath,\n    add_product_dex_preopt_module_config = _add_product_dex_preopt_module_config,\n    addprefix = _addprefix,\n    addsuffix = _addsuffix,\n    board_platform_in = _board_platform_in,\n    board_platform_is = _board_platform_is,\n    clear_var_list = _clear_var_list,\n    copy_files = _copy_files,\n    copy_if_exists = _copy_if_exists,\n    cfg = __h_cfg,\n    dir = _dir,\n    enforce_product_packages_exist = _enforce_product_packages_exist,\n    expand_wildcard = _expand_wildcard,\n    filter = _filter,\n    filter_out = _filter_out,\n    find_and_copy = _find_and_copy,\n    findstring = _findstring,\n    first_word = _first_word,\n    last_word = _last_word,\n    flatten_2d_list = _flatten_2d_list,\n    inherit = _inherit,\n    indirect = _indirect,\n    mk2rbc_error = _mk2rbc_error,\n    mkdist_for_goals = _mkdist_for_goals,\n    mkinfo = _mkinfo,\n    mkerror = _mkerror,\n    mkpatsubst = _mkpatsubst,\n    mkwarning = _mkwarning,\n    mksort = _mksort,\n    mkstrip = _mkstrip,\n    mksubst = _mksubst,\n    notdir = _notdir,\n    printvars = _printvars,\n    product_configuration = _product_configuration,\n    board_configuration = _board_configuration,\n    product_copy_files_by_pattern = _product_copy_files_by_pattern,\n    require_artifacts_in_path = _require_artifacts_in_path,\n    require_artifacts_in_path_relaxed = _require_artifacts_in_path_relaxed,\n    setdefault = _setdefault,\n    shell = rblf_shell,\n    warning = _mkwarning,\n    words = __words,\n)\n"
  },
  {
    "path": "core/product_validation_checks.mk",
    "content": "# PRODUCT_VALIDATION_CHECKS allows you to enforce that your product config variables follow some\n# rules. To use it, add the paths to starlark configuration language (scl) files in\n# PRODUCT_VALIDATION_CHECKS. A validate_product_variables function in those files will be called\n# with a single \"context\" object.\n#\n# The context object currently 2 attributes:\n#   - product_variables: This has all the product variables. All the variables are either of type\n#                        string or list, more accurate typing (like bool) isn't known.\n#   - board_variables: This only has a small subset of the board variables, because there isn't a\n#                      known list of board variables. Feel free to expand the subset if you need a\n#                      new variable.\n#\n# You can then inspect (but not modify) these variables and fail() if they don't meet your\n# requirements. Example:\n#\n# In a product config file: PRODUCT_VALIDATION_CHECKS += //path/to/my_validations.scl\n# In my_validations.scl:\n# def validate_product_variables(ctx):\n#     for dir in ctx.board_variables.BOARD_SEPOLICY_DIRS:\n#         if not dir.startswith('system/sepolicy/'):\n#             fail('Only sepolicies in system/seplicy are allowed, found: ' + dir)\n\nifdef PRODUCT_VALIDATION_CHECKS\n\n$(if $(filter-out //%.scl,$(PRODUCT_VALIDATION_CHECKS)), \\\n\t$(error All PRODUCT_VALIDATION_CHECKS files must start with // and end with .scl, exceptions: $(filter-out //%.scl,$(PRODUCT_VALIDATION_CHECKS))))\n\nknown_board_variables := \\\n  BOARD_VENDOR_SEPOLICY_DIRS BOARD_SEPOLICY_DIRS \\\n  SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS \\\n  SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS \\\n  PRODUCT_PUBLIC_SEPOLICY_DIRS \\\n  PRODUCT_PRIVATE_SEPOLICY_DIRS\n\nknown_board_list_variables := \\\n  BOARD_VENDOR_SEPOLICY_DIRS BOARD_SEPOLICY_DIRS \\\n  SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS \\\n  SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS \\\n  PRODUCT_PUBLIC_SEPOLICY_DIRS \\\n  PRODUCT_PRIVATE_SEPOLICY_DIRS\n\nescape_starlark_string=$(subst \",\\\",$(subst \\,\\\\,$(1)))\nproduct_variable_starlark_value=$(if $(filter $(1),$(_product_list_vars) $(known_board_list_variables)),[$(foreach w,$($(1)),\"$(call escape_starlark_string,$(w))\", )],\"$(call escape_starlark_string,$(1))\")\nfilename_to_starlark=$(subst -,_,$(subst /,_,$(subst .,_,$(1))))\n_c:=$(foreach f,$(PRODUCT_VALIDATION_CHECKS),$(newline)load(\"$(f)\", validate_product_variables_$(call filename_to_starlark,$(f)) = \"validate_product_variables\"))\n# TODO: we should freeze the context because it contains mutable lists, so that validation checks can't affect each other\n_c+=$(newline)_ctx = struct(\n_c+=$(newline)product_variables = struct(\n_c+=$(foreach v,$(_product_var_list),$(newline)  $(v) = $(call product_variable_starlark_value,$(v)),)\n_c+=$(newline)),\n_c+=$(newline)board_variables = struct(\n_c+=$(foreach v,$(known_board_variables),$(newline)  $(v) = $(call product_variable_starlark_value,$(v)),)\n_c+=$(newline))\n_c+=$(newline))\n_c+=$(foreach f,$(PRODUCT_VALIDATION_CHECKS),$(newline)validate_product_variables_$(call filename_to_starlark,$(f))(_ctx))\n_c+=$(newline)variables_to_export_to_make = {}\n$(KATI_file_no_rerun >$(OUT_DIR)/product_validation_checks_entrypoint.scl,$(_c))\nfilename_to_starlark:=\nescape_starlark_string:=\nproduct_variable_starlark_value:=\nknown_board_variables :=\nknown_board_list_variables :=\n\n# Exclude the entrypoint file as a dependency (by passing it as the 2nd argument) so that we don't\n# rerun kati every build. Even though we're using KATI_file_no_rerun, product config is run every\n# build, so the file will still be rewritten.\n#\n# We also need to pass --allow_external_entrypoint to rbcrun in case the OUT_DIR is set to something\n# outside of the source tree.\n$(call run-starlark,$(OUT_DIR)/product_validation_checks_entrypoint.scl,$(OUT_DIR)/product_validation_checks_entrypoint.scl,--allow_external_entrypoint)\n\nendif # ifdef PRODUCT_VALIDATION_CHECKS\n"
  },
  {
    "path": "core/proguard/checknotnull.flags",
    "content": "# Tell R8 that the following methods are check not null methods, and to\n# replace invocations to them with a more concise nullness check that produces\n# (slightly) less informative error messages\n\n-convertchecknotnull class com.google.common.base.Preconditions {\n  ** checkNotNull(...);\n}\n\n-convertchecknotnull class java.util.Objects {\n  ** requireNonNull(...);\n}\n\n-convertchecknotnull class kotlin.jvm.internal.Intrinsics {\n  void checkNotNull(...);\n  void checkExpressionValueIsNotNull(...);\n  void checkNotNullExpressionValue(...);\n  void checkReturnedValueIsNotNull(...);\n  void checkFieldIsNotNull(...);\n  void checkParameterIsNotNull(...);\n  void checkNotNullParameter(...);\n}\n\n-convertchecknotnull class dagger.internal.Preconditions {\n  ** checkNotNull*(...);\n}\n"
  },
  {
    "path": "core/proguard/kotlin.flags",
    "content": "# Ignore missing Kotlin meta-annotations so that Java-only projects can depend\n# on projects that happen to be written in Kotlin but do not have a run-time\n# dependency on the Kotlin standard library. Note these annotations are RUNTIME\n# retention, but we won't need them available in Java-only projects.\n-dontwarn kotlin.Metadata\n-dontwarn kotlin.annotation.AnnotationRetention\n-dontwarn kotlin.annotation.AnnotationTarget\n-dontwarn kotlin.annotation.Retention\n-dontwarn kotlin.annotation.Target\n\n# Kotlin DebugMetadata has no value in release builds, these two rules, will\n# allow AppReduce to strip out DebutMetadata.\n# TODO(b/302383328): Restore the below checkdiscard after resolving transitive\n# inclusion of kotlin-stdlib from androidx.annotation library deps.\n# -checkdiscard interface kotlin.coroutines.jvm.internal.DebugMetadata\n-assumenosideeffects class kotlin.coroutines.jvm.internal.DebugMetadataKt {\n  *** getDebugMetadataAnnotation(...);\n}\n-assumevalues class kotlin.coroutines.jvm.internal.DebugMetadataKt {\n  *** getDebugMetadataAnnotation(...) return null;\n}\n"
  },
  {
    "path": "core/proguard.flags",
    "content": "# Keep classes and members with the platform-defined @VisibleForTesting annotation.\n-keep @com.android.internal.annotations.VisibleForTesting class *\n-keepclassmembers class * {\n    @com.android.internal.annotations.VisibleForTesting *;\n}\n\n# Keep classes and members with platform @TestApi annotations, similar to\n# @VisibleForTesting.\n-keep @android.annotation.TestApi class *\n-keepclassmembers class * {\n    @android.annotation.TestApi *;\n}\n\n# Keep classes and members with non-platform @VisibleForTesting annotations, but\n# only within platform-defined packages. This avoids keeping external, library-specific\n# test code that isn't actually needed for platform testing.\n# TODO(b/239961360): Migrate away from androidx.annotation.VisibleForTesting\n# and com.google.common.annotations.VisibleForTesting use in platform code.\n-keep @**.VisibleForTesting class android.**,com.android.**,com.google.android.**\n-keepclassmembers class android.**,com.android.**,com.google.android.** {\n    @**.VisibleForTesting *;\n}\n\n# Keep rule for members that are needed solely to keep alive downstream weak\n# references, and could otherwise be removed after tree shaking optimizations.\n-keepclassmembers,allowaccessmodification,allowobfuscation,allowshrinking class * {\n  @com.android.internal.annotations.KeepForWeakReference <fields>;\n}\n\n# Needed to ensure callback field references are kept in their respective\n# owning classes when the downstream callback registrars only store weak refs.\n-if @com.android.internal.annotations.WeaklyReferencedCallback class *\n-keepclassmembers,allowaccessmodification,allowobfuscation,allowshrinking class * {\n  !synthetic <1> *;\n}\n-if class * extends @com.android.internal.annotations.WeaklyReferencedCallback **\n-keepclassmembers,allowaccessmodification,allowobfuscation,allowshrinking class * {\n  !synthetic <1> *;\n}\n\n# Understand the common @Keep annotation from various Android packages:\n#  * android.support.annotation\n#  * androidx.annotation\n#  * com.android.internal.annotations\n-keep class **android**.annotation*.Keep\n\n-keep @**android**.annotation*.Keep class * { *; }\n\n-keepclasseswithmembers class * {\n    @**android**.annotation*.Keep <methods>;\n}\n\n-keepclasseswithmembers class * {\n    @**android**.annotation*.Keep <fields>;\n}\n\n-keepclasseswithmembers class * {\n    @**android**.annotation*.Keep <init>(...);\n}\n\n# Keep Dalvik optimization annotations. These annotations are special in that\n# 1) we want them preserved for visibility with ART, but 2) they don't have\n# RUNTIME retention. These minimal keep rules ensure they're not stripped by R8.\n# TODO(b/215417388): Export this rule from the owning library, core-libart,\n# via export_proguard_flags_files.\n-keepclassmembers,allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification class * {\n    @dalvik.annotation.optimization.** *;\n}\n\n-include proguard_basic_keeps.flags\n-include proguard/kotlin.flags\n"
  },
  {
    "path": "core/proguard.jacoco.flags",
    "content": "# Keep everything for the emma classes\n-keep class com.vladium.** {\n  *;\n}\n# Keep everything for the jacoco classes\n-keep class org.jacoco.** {\n  *;\n}\n"
  },
  {
    "path": "core/proguard_basic_keeps.flags",
    "content": "# Preserve line number information for debugging stack traces.\n-keepattributes SourceFile,LineNumberTable\n\n# Annotations are implemented as attributes, so we have to explicitly keep them.\n# Keep all runtime-visible annotations like RuntimeVisibleParameterAnnotations\n# and RuntimeVisibleTypeAnnotations, as well as associated defaults.\n-keepattributes RuntimeVisible*Annotation*,AnnotationDefault\n\n# With R8 full mode, certain attributes are only kept when matched with an\n# explicit keep rule for that target, even with a global -keepattributes rule.\n# As such, we can add the global keep rule here with minimal cost while\n# simplifying incremental development.\n-keepattributes Exceptions\n\n# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations\n-keepclassmembers enum * {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n}\n\n# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native\n-keepclasseswithmembernames,includedescriptorclasses class * {\n    native <methods>;\n}\n\n# class$ methods are inserted by some compilers to implement .class construct,\n# see http://proguard.sourceforge.net/manual/examples.html#library\n-keepclassmembernames class * {\n    java.lang.Class class$(java.lang.String);\n    java.lang.Class class$(java.lang.String, boolean);\n}\n\n# Keep serializable classes and necessary members for serializable classes\n# Copied from the ProGuard manual at http://proguard.sourceforge.net.\n-keepnames class * implements java.io.Serializable\n-keepclassmembers class * implements java.io.Serializable {\n    static final long serialVersionUID;\n    private static final java.io.ObjectStreamField[] serialPersistentFields;\n    !static !transient <fields>;\n    private void writeObject(java.io.ObjectOutputStream);\n    private void readObject(java.io.ObjectInputStream);\n    java.lang.Object writeReplace();\n    java.lang.Object readResolve();\n}\n\n# Keep all Javascript API methods\n-keepclassmembers class * {\n    @android.webkit.JavascriptInterface <methods>;\n}\n\n# Keep Throwable's constructor that takes a String argument.\n-keepclassmembers class * extends java.lang.Throwable {\n  <init>(java.lang.String);\n}\n\n# Please specify classes to be kept explicitly in your package's configuration.\n# -keep class * extends android.app.Activity\n# -keep class * extends android.view.View\n# -keep class * extends android.app.Service\n# -keep class * extends android.content.BroadcastReceiver\n# -keep class * extends android.content.ContentProvider\n# -keep class * extends android.preference.Preference\n# -keep class * extends android.app.BackupAgent\n\n# Parcelable CREATORs must be kept for Parcelable functionality\n-keepclassmembers class * implements android.os.Parcelable {\n  public static final ** CREATOR;\n}\n\n# The support library contains references to newer platform versions.\n# Don't warn about those in case this app is linking against an older\n# platform version.  We know about them, and they are safe.\n# See proguard-android.txt in the SDK package.\n#\n# DO NOT USE THIS: We figured it's dangerous to blindly ignore all support library warnings.\n# ProGuard may strip members of subclass of unknown super classes, in case an app is linking against\n# LOCAL_SDK_VERSION lower than the support library's LOCAL_SDK_VERSION.\n# See bug/20658265.\n# -dontwarn android.support.**\n\n# From https://github.com/google/guava/wiki/UsingProGuardWithGuava\n# Striped64, LittleEndianByteArray, UnsignedBytes, AbstractFuture\n-dontwarn sun.misc.Unsafe\n# Futures.getChecked (which often won't work with Proguard anyway) uses this. It\n# has a fallback, but again, don't use Futures.getChecked on Android regardless.\n-dontwarn java.lang.ClassValue\n\n# Ignore missing annotation references for various support libraries.\n# While this is not ideal, it should be relatively safe given that\n# 1) runtime-visible annotations will still be kept, and 2) compile-time\n# annotations are stripped by R8 anyway.\n# Note: The ** prefix is used to accommodate jarjar repackaging.\n# TODO(b/242088131): Remove these exemptions after resolving transitive libs\n# dependencies that are provided to R8.\n-dontwarn **android**.annotation*.**\n-dontwarn **com.google.errorprone.annotations.**\n-dontwarn javax.annotation.**\n-dontwarn org.checkerframework.**\n-dontwarn org.jetbrains.annotations.**\n\n# Less spammy.\n-dontnote\n\n# The lite proto runtime uses reflection to access fields based on the names in\n# the schema, keep all the fields. Wildcard is used to apply the rule to classes\n# that have been renamed with jarjar.\n-keepclassmembers class * extends **.protobuf.MessageLite { <fields>; }\n"
  },
  {
    "path": "core/project_definitions.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# Allow projects to define their own globally-available variables.\n#\n\n#\n# Include definitions for prebuilt SDK, if present.\n#\n-include prebuilts/sdk/current/definitions.mk\n"
  },
  {
    "path": "core/python_binary_host_mobly_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2023 The Android Open Source Project\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n          http://www.apache.org/licenses/LICENSE-2.0\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Config for {MODULE} mobly test\">\n    {EXTRA_CONFIGS}\n\n    <device name=\"AndroidRealDevice\"></device>\n    <device name=\"AndroidRealDevice\"></device>\n\n    <option name=\"mobly_pkg\" key=\"file\" value=\"{MODULE}\" />\n    <test class=\"MoblyAospPackageTest\" />\n</configuration>\n"
  },
  {
    "path": "core/python_binary_host_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2018 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Config to run {MODULE} unittests\">\n    <test class=\"com.android.tradefed.testtype.python.PythonBinaryHostTest\" >\n        <option name=\"par-file-name\" value=\"{MODULE}\" />\n        <option name=\"test-timeout\" value=\"5m\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/ravenwood_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2023 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs {MODULE}\">\n    <option name=\"test-suite-tag\" value=\"ravenwood\" />\n    <option name=\"test-suite-tag\" value=\"ravenwood-tests\" />\n\n    <option name=\"java-folder\" value=\"prebuilts/jdk/jdk21/linux-x86/\" />\n    <option name=\"use-ravenwood-resources\" value=\"true\" />\n    <option name=\"exclude-paths\" value=\"java\" />\n    <option name=\"null-device\" value=\"true\" />\n    <option name=\"do-not-swallow-runner-errors\" value=\"true\" />\n\n    {EXTRA_CONFIGS}\n\n    <test class=\"com.android.tradefed.testtype.IsolatedHostTest\" >\n        <option name=\"jar\" value=\"{MODULE}.jar\" />\n        <option name=\"java-flags\" value=\"--add-modules=jdk.compiler\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED\"/>\n\n        <!-- Needed for supporting ParcelFileDescriptor internals -->\n        <option name=\"java-flags\" value=\"--add-exports=java.base/jdk.internal.access=ALL-UNNAMED\"/>\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/rbe.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Notice: this works only with Google's RBE service.\nifneq ($(filter-out false,$(USE_RBE)),)\n  ifdef RBE_DIR\n    rbe_dir := $(RBE_DIR)\n  else\n    rbe_dir := prebuilts/remoteexecution-client/live/\n  endif\n\n  ifdef RBE_CXX_POOL\n    cxx_pool := $(RBE_CXX_POOL)\n  else\n    cxx_pool := default\n  endif\n\n  ifdef RBE_JAVA_POOL\n    java_pool := $(RBE_JAVA_POOL)\n  else\n    java_pool := java16\n  endif\n\n  ifdef RBE_CXX_EXEC_STRATEGY\n    cxx_rbe_exec_strategy := $(RBE_CXX_EXEC_STRATEGY)\n  else\n    cxx_rbe_exec_strategy := local\n  endif\n\n  ifdef RBE_CXX_COMPARE\n    cxx_compare := $(RBE_CXX_COMPARE)\n  else\n    cxx_compare := false\n  endif\n\n  ifdef RBE_JAVAC_EXEC_STRATEGY\n    javac_exec_strategy := $(RBE_JAVAC_EXEC_STRATEGY)\n  else\n    javac_exec_strategy := remote_local_fallback\n  endif\n\n  ifdef RBE_R8_EXEC_STRATEGY\n    r8_exec_strategy := $(RBE_R8_EXEC_STRATEGY)\n  else\n    r8_exec_strategy := remote_local_fallback\n  endif\n\n  ifdef RBE_D8_EXEC_STRATEGY\n    d8_exec_strategy := $(RBE_D8_EXEC_STRATEGY)\n  else\n    d8_exec_strategy := remote_local_fallback\n  endif\n\n  platform := container-image=docker://gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:1eb7f64b9e17102b970bd7a1af7daaebdb01c3fb777715899ef462d6c6d01a45\n  cxx_platform := $(platform),Pool=$(cxx_pool)\n  java_r8_d8_platform := $(platform),Pool=$(java_pool)\n\n  RBE_WRAPPER := $(rbe_dir)/rewrapper\n  RBE_CXX := --labels=type=compile,lang=cpp,compiler=clang --env_var_allowlist=PWD --exec_strategy=$(cxx_rbe_exec_strategy) --platform=$(cxx_platform) --compare=$(cxx_compare)\n\n  # Append rewrapper to existing *_WRAPPER variables so it's possible to\n  # use both ccache and rewrapper.\n  CC_WRAPPER := $(strip $(CC_WRAPPER) $(RBE_WRAPPER) $(RBE_CXX))\n  CXX_WRAPPER := $(strip $(CXX_WRAPPER) $(RBE_WRAPPER) $(RBE_CXX))\n\n  ifdef RBE_JAVAC\n    JAVAC_WRAPPER := $(strip $(JAVAC_WRAPPER) $(RBE_WRAPPER) --labels=type=compile,lang=java,compiler=javac --exec_strategy=$(javac_exec_strategy) --platform=$(java_r8_d8_platform))\n  endif\n\n  ifdef RBE_R8\n    R8_WRAPPER := $(strip $(RBE_WRAPPER) --labels=type=compile,compiler=r8 --exec_strategy=$(r8_exec_strategy) --platform=$(java_r8_d8_platform) --inputs=$(OUT_DIR)/host/linux-x86/framework/r8.jar,build/make/core/proguard_basic_keeps.flags --toolchain_inputs=$(firstword $(JAVA)))\n  endif\n\n  ifdef RBE_D8\n    D8_WRAPPER := $(strip $(RBE_WRAPPER) --labels=type=compile,compiler=d8 --exec_strategy=$(d8_exec_strategy) --platform=$(java_r8_d8_platform) --inputs=$(OUT_DIR)/host/linux-x86/framework/d8.jar --toolchain_inputs=$(firstword $(JAVA)))\n  endif\n\n  rbe_dir :=\nendif\n\n"
  },
  {
    "path": "core/release_config.mk",
    "content": "# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\n# -----------------------------------------------------------------\n# Determine which pass this is.\n# -----------------------------------------------------------------\n# On the first pass, we are asked for only PRODUCT_RELEASE_CONFIG_MAPS,\n# on the second pass, we are asked for whatever else is wanted.\n_final_product_config_pass:=\nifneq (PRODUCT_RELEASE_CONFIG_MAPS,$(DUMP_MANY_VARS))\n    _final_product_config_pass:=true\nendif\n\n# -----------------------------------------------------------------\n# Choose the flag files\n# -----------------------------------------------------------------\n# Release configs are defined in reflease_config_map files, which map\n# the short name (e.g. -next) used in lunch to the starlark files\n# defining the build flag values.\n#\n# (If you're thinking about aconfig flags, there is one build flag,\n# RELEASE_ACONFIG_VALUE_SETS, that sets which aconfig_value_set\n# module to use to set the aconfig flag values.)\n#\n# The short release config names *can* appear multiple times, to allow\n# for AOSP and vendor specific flags under the same name, but the\n# individual flag values must appear in exactly one config.  Vendor\n# does not override AOSP, or anything like that.  This is because\n# vendor code usually includes prebuilts, and having vendor compile\n# with different flags from AOSP increases the likelihood of flag\n# mismatch.\n\n# Do this first, because we're going to unset TARGET_RELEASE before\n# including anyone, so they don't start making conditionals based on it.\n# This logic is in make because starlark doesn't understand optional\n# vendor files.\n\n# If this is a google source tree, restrict it to only the one file\n# which has OWNERS control.  If it isn't let others define their own.\nconfig_map_files := $(wildcard build/release/release_config_map.mk) \\\n    $(wildcard vendor/google_shared/build/release/release_config_map.mk) \\\n    $(if $(wildcard vendor/google/release/release_config_map.mk), \\\n        vendor/google/release/release_config_map.mk, \\\n        $(sort \\\n            $(wildcard device/*/release/release_config_map.mk) \\\n            $(wildcard device/*/*/release/release_config_map.mk) \\\n            $(wildcard vendor/*/release/release_config_map.mk) \\\n            $(wildcard vendor/*/*/release/release_config_map.mk) \\\n        ) \\\n    )\n\nprotobuf_map_files := build/release/release_config_map.textproto \\\n    $(wildcard vendor/google_shared/build/release/release_config_map.textproto) \\\n    $(if $(wildcard vendor/google/release/release_config_map.textproto), \\\n        vendor/google/release/release_config_map.textproto, \\\n        $(sort \\\n            $(wildcard device/*/release/release_config_map.textproto) \\\n            $(wildcard device/*/*/release/release_config_map.textproto) \\\n            $(wildcard vendor/*/release/release_config_map.textproto) \\\n            $(wildcard vendor/*/*/release/release_config_map.textproto) \\\n        ) \\\n    )\n\n# Remove support for the legacy approach.\n_must_protobuf := true\n\n# PRODUCT_RELEASE_CONFIG_MAPS is set by Soong using an initial run of product\n# config to capture only the list of config maps needed by the build.\n# Keep them in the order provided, but remove duplicates.\n# Treat .mk and .textproto as equal for duplicate elimination, but force\n# protobuf if any PRODUCT_RELEASE_CONFIG_MAPS specify .textproto.\n$(foreach map,$(PRODUCT_RELEASE_CONFIG_MAPS), \\\n    $(if $(filter $(basename $(map)),$(basename $(config_map_files))),, \\\n        $(eval config_map_files += $(map))) \\\n    $(if $(filter $(basename $(map)).textproto,$(map)),$(eval _must_protobuf := true)) \\\n)\n\n\n# If we are missing the textproto version of any of $(config_map_files), we cannot use protobuf.\n_can_protobuf := true\n$(foreach map,$(config_map_files), \\\n    $(if $(wildcard $(basename $(map)).textproto),,$(eval _can_protobuf :=)) \\\n)\n# If we are missing the mk version of any of $(protobuf_map_files), we must use protobuf.\n$(foreach map,$(protobuf_map_files), \\\n    $(if $(wildcard $(basename $(map)).mk),,$(eval _must_protobuf := true)) \\\n)\n\nifneq (,$(_must_protobuf))\n    ifeq (,$(_can_protobuf))\n        # We must use protobuf, but we cannot use protobuf.\n        $(error release config is a mixture of .scl and .textproto)\n    endif\nendif\n\n_use_protobuf :=\nifneq (,$(_must_protobuf))\n    _use_protobuf := true\nelse\n    ifneq ($(_can_protobuf),)\n        # Determine the default\n        $(foreach map,$(config_map_files), \\\n            $(if $(wildcard $(dir $(map))/build_config/DEFAULT=proto),$(eval _use_protobuf := true)) \\\n            $(if $(wildcard $(dir $(map))/build_config/DEFAULT=make),$(eval _use_protobuf := )) \\\n        )\n        # Update for this specific release config only (no inheritance).\n        $(foreach map,$(config_map_files), \\\n            $(if $(wildcard $(dir $(map))/build_config/$(TARGET_RELEASE)=proto),$(eval _use_protobuf := true)) \\\n            $(if $(wildcard $(dir $(map))/build_config/$(TARGET_RELEASE)=make),$(eval _use_protobuf := )) \\\n        )\n    endif\nendif\n\nifneq (,$(_use_protobuf))\n    # The .textproto files are the canonical source of truth.\n    _args := $(foreach map,$(config_map_files), --map $(map) )\n    ifneq (,$(_must_protobuf))\n        # Disable the build flag in release-config.\n        _args += --guard=false\n    endif\n    _args += --allow-missing=true\n    ifneq (,$(TARGET_PRODUCT))\n        _args += --product $(TARGET_PRODUCT)\n    endif\n    _flags_dir:=$(OUT_DIR)/soong/release-config\n    _flags_file:=$(_flags_dir)/release_config-$(TARGET_PRODUCT)-$(TARGET_RELEASE).vars\n    # release-config generates $(_flags_varmk)\n    _flags_varmk:=$(_flags_file:.vars=.varmk)\n    $(shell $(OUT_DIR)/release-config $(_args) >$(OUT_DIR)/release-config.out && touch -t 200001010000 $(_flags_varmk))\n    $(if $(filter-out 0,$(.SHELLSTATUS)),$(error release-config failed to run))\n    ifneq (,$(_final_product_config_pass))\n        # Save the final version of the config.\n        $(shell if ! cmp --quiet $(_flags_varmk) $(_flags_file); then cp $(_flags_varmk) $(_flags_file); fi)\n        # This will also set ALL_RELEASE_CONFIGS_FOR_PRODUCT and _used_files for us.\n        $(eval include $(_flags_file))\n        $(KATI_extra_file_deps $(OUT_DIR)/release-config $(protobuf_map_files) $(_flags_file))\n        ifneq (,$(_disallow_lunch_use))\n            $(error Release config ${TARGET_RELEASE} is disallowed for build.  Please use one of: $(ALL_RELEASE_CONFIGS_FOR_PRODUCT))\n        endif\n    else\n        # This is the first pass of product config.\n        $(eval include $(_flags_varmk))\n    endif\n    _used_files :=\n    ifeq (,$(_must_protobuf)$(RELEASE_BUILD_FLAGS_IN_PROTOBUF))\n        _use_protobuf :=\n    endif\n    _flags_dir:=\n    _flags_file:=\n    _flags_varmk:=\nendif\nifeq (,$(_use_protobuf))\n    # The .mk files are the canonical source of truth.\n\n\n# Declare an alias release-config\n#\n# This should be used to declare a release as an alias of another, meaning no\n# release config files should be present.\n#\n# $1 config name\n# $2 release config for which it is an alias\ndefine alias-release-config\n    $(call _declare-release-config,$(1),,$(2),true)\nendef\n\n# Declare or extend a release-config.\n#\n# The order of processing is:\n# 1. Recursively apply any overridden release configs.  Only apply each config\n#    the first time we reach it.\n# 2. Apply any files for this release config, in the order they were added to\n#    the declaration.\n#\n# Example:\n#   With these declarations:\n#     $(declare-release-config foo, foo.scl)\n#     $(declare-release-config bar, bar.scl, foo)\n#     $(declare-release-config baz, baz.scl, bar)\n#     $(declare-release-config bif, bif.scl, foo baz)\n#     $(declare-release-config bop, bop.scl, bar baz)\n#\n#   TARGET_RELEASE:\n#     - bar will use: foo.scl bar.scl\n#     - baz will use: foo.scl bar.scl baz.scl\n#     - bif will use: foo.scl bar.scl baz.scl bif.scl\n#     - bop will use: foo.scl bar.scl baz.scl bop.scl\n#\n# $1 config name\n# $2 release config files\n# $3 overridden release config\ndefine declare-release-config\n    $(call _declare-release-config,$(1),$(2),$(3),)\nendef\n\ndefine _declare-release-config\n    $(if $(strip $(2)$(3)),,  \\\n        $(error declare-release-config: config $(strip $(1)) must have release config files, override another release config, or both) \\\n    )\n    $(if $(strip $(4)),$(eval _all_release_configs.$(strip $(1)).ALIAS := true))\n    $(eval ALL_RELEASE_CONFIGS_FOR_PRODUCT := $(sort $(ALL_RELEASE_CONFIGS_FOR_PRODUCT) $(strip $(1))))\n    $(if $(strip $(3)), \\\n      $(if $(filter $(ALL_RELEASE_CONFIGS_FOR_PRODUCT), $(strip $(3))),\n        $(if $(filter $(_all_release_configs.$(strip $(1)).OVERRIDES),$(strip $(3))),,\n          $(eval _all_release_configs.$(strip $(1)).OVERRIDES := $(_all_release_configs.$(strip $(1)).OVERRIDES) $(strip $(3)))), \\\n        $(error No release config $(strip $(3))) \\\n      ) \\\n    )\n    $(eval _all_release_configs.$(strip $(1)).DECLARED_IN := $(_included) $(_all_release_configs.$(strip $(1)).DECLARED_IN))\n    $(eval _all_release_configs.$(strip $(1)).FILES := $(_all_release_configs.$(strip $(1)).FILES) $(strip $(2)))\nendef\n\n# Include the config map files and populate _flag_declaration_files.\n# If the file is found more than once, only include it the first time.\n_flag_declaration_files :=\n_included_config_map_files :=\n$(foreach f, $(config_map_files), \\\n    $(eval FLAG_DECLARATION_FILES:= ) \\\n    $(if $(filter $(_included_config_map_files),$(f)),,\\\n        $(eval _included := $(f)) \\\n        $(eval include $(f)) \\\n        $(eval _flag_declaration_files += $(FLAG_DECLARATION_FILES)) \\\n        $(eval _included_config_map_files += $(f)) \\\n    ) \\\n)\nFLAG_DECLARATION_FILES :=\n\n# Verify that all inherited/overridden release configs are declared.\n$(foreach config,$(ALL_RELEASE_CONFIGS_FOR_PRODUCT),\\\n  $(foreach r,$(all_release_configs.$(r).OVERRIDES),\\\n    $(if $(strip $(_all_release_configs.$(r).FILES)$(_all_release_configs.$(r).OVERRIDES)),,\\\n    $(error Release config $(config) [declared in: $(_all_release_configs.$(r).DECLARED_IN)] inherits from non-existent $(r).)\\\n)))\n# Verify that alias configs do not have config files.\n$(foreach r,$(ALL_RELEASE_CONFIGS_FOR_PRODUCT),\\\n  $(if $(_all_release_configs.$(r).ALIAS),$(if $(_all_release_configs.$(r).FILES),\\\n    $(error Alias release config \"$(r)\" may not specify release config files $(_all_release_configs.$(r).FILES))\\\n)))\n\n# Use makefiles\nendif\n\nifeq ($(TARGET_RELEASE),)\n    # We allow some internal paths to explicitly set TARGET_RELEASE to the\n    # empty string.  For the most part, 'make' treats unset and empty string as\n    # the same.  But the following line differentiates, and will only assign\n    # if the variable was completely unset.\n    TARGET_RELEASE ?= was_unset\n    ifeq ($(TARGET_RELEASE),was_unset)\n        $(error No release config set for target; please set TARGET_RELEASE, or if building on the command line use 'lunch <target>-<release>-<build_type>', where release is one of: $(ALL_RELEASE_CONFIGS_FOR_PRODUCT))\n    endif\n    # Instead of leaving this string empty, we want to default to a valid\n    # setting.  Full builds coming through this path is a bug, but in case\n    # of such a bug, we want to at least get consistent, valid results.\n    TARGET_RELEASE = trunk_staging\nendif\n\n# During pass 1 of product config, using a non-existent release config is not an error.\n# We can safely assume that we are doing pass 1 if DUMP_MANY_VARS==\"PRODUCT_RELEASE_CONFIG_MAPS\".\nifneq (,$(_final_product_config_pass))\n    ifeq ($(filter $(ALL_RELEASE_CONFIGS_FOR_PRODUCT), $(TARGET_RELEASE)),)\n        $(error No release config found for TARGET_RELEASE: $(TARGET_RELEASE). Available releases are: $(ALL_RELEASE_CONFIGS_FOR_PRODUCT))\n    endif\nendif\n\nifeq (,$(_use_protobuf))\n# Choose flag files\n# Don't sort this, use it in the order they gave us.\n# Do allow duplicate entries, retaining only the first usage.\nflag_value_files :=\n\n# Apply overrides recursively\n#\n# $1 release config that we override\napplied_releases :=\ndefine _apply-release-config-overrides\n$(foreach r,$(1), \\\n  $(if $(filter $(r),$(applied_releases)),, \\\n    $(foreach o,$(_all_release_configs.$(r).OVERRIDES),$(call _apply-release-config-overrides,$(o)))\\\n    $(eval applied_releases += $(r))\\\n    $(foreach f,$(_all_release_configs.$(r).FILES), \\\n      $(if $(filter $(f),$(flag_value_files)),,$(eval flag_value_files += $(f)))\\\n    )\\\n  )\\\n)\nendef\n$(call _apply-release-config-overrides,$(TARGET_RELEASE))\n# Unset variables so they can't use them\ndefine declare-release-config\n$(error declare-release-config can only be called from inside release_config_map.mk files)\nendef\ndefine _apply-release-config-overrides\n$(error invalid use of apply-release-config-overrides)\nendef\n\n# use makefiles\nendif\n\n# TODO: Remove this check after enough people have sourced lunch that we don't\n# need to worry about it trying to do get_build_vars TARGET_RELEASE. Maybe after ~9/2023\nifneq ($(CALLED_FROM_SETUP),true)\ndefine TARGET_RELEASE\n$(error TARGET_RELEASE may not be accessed directly. Use individual flags.)\nendef\nelse\nTARGET_RELEASE:=\nendif\n.KATI_READONLY := TARGET_RELEASE\n\nifeq (,$(_use_protobuf))\n$(foreach config, $(ALL_RELEASE_CONFIGS_FOR_PRODUCT), \\\n    $(eval _all_release_configs.$(config).DECLARED_IN:= ) \\\n    $(eval _all_release_configs.$(config).FILES:= ) \\\n)\napplied_releases:=\n# use makefiles\nendif\nconfig_map_files:=\nprotobuf_map_files:=\n\n\nifeq (,$(_use_protobuf))\n# -----------------------------------------------------------------\n# Flag declarations and values\n# -----------------------------------------------------------------\n# This part is in starlark.  We generate a root starlark file that loads\n# all of the flags declaration files that we found, and the flag_value_files\n# that we chose from the config map above.  Then we run that, and load the\n# results of that into the make environment.\n\n# _flag_declaration_files is the combined list of FLAG_DECLARATION_FILES set by\n# release_config_map.mk files above.\n\n# Because starlark can't find files with $(wildcard), write an entrypoint starlark script that\n# contains the result of the above wildcards for the starlark code to use.\nfilename_to_starlark=$(subst /,_,$(subst .,_,$(1)))\n_c:=load(\"//build/make/core/release_config.scl\", \"release_config\")\n_c+=$(newline)def add(d, k, v):\n_c+=$(newline)$(space)d = dict(d)\n_c+=$(newline)$(space)d[k] = v\n_c+=$(newline)$(space)return d\n_c+=$(foreach f,$(_flag_declaration_files),$(newline)load(\"$(f)\", flags_$(call filename_to_starlark,$(f)) = \"flags\"))\n_c+=$(newline)all_flags = [] $(foreach f,$(_flag_declaration_files),+ [add(x, \"declared_in\", \"$(f)\") for x in flags_$(call filename_to_starlark,$(f))])\n_c+=$(foreach f,$(flag_value_files),$(newline)load(\"//$(f)\", values_$(call filename_to_starlark,$(f)) = \"values\"))\n_c+=$(newline)all_values = [] $(foreach f,$(flag_value_files),+ [add(x, \"set_in\", \"$(f)\") for x in values_$(call filename_to_starlark,$(f))])\n_c+=$(newline)variables_to_export_to_make = release_config(all_flags, all_values)\n$(file >$(OUT_DIR)/release_config_entrypoint.scl,$(_c))\n_c:=\nfilename_to_starlark:=\n\n# Exclude the entrypoint file as a dependency (by passing it as the 2nd argument) so that we don't\n# rerun kati every build. Kati will replay the $(file) command that generates it every build,\n# updating its timestamp.\n#\n# We also need to pass --allow_external_entrypoint to rbcrun in case the OUT_DIR is set to something\n# outside of the source tree.\n$(call run-starlark,$(OUT_DIR)/release_config_entrypoint.scl,$(OUT_DIR)/release_config_entrypoint.scl,--allow_external_entrypoint)\n\n# use makefiles\nendif\n_can_protobuf :=\n_must_protobuf :=\n_use_protobuf :=\n\n"
  },
  {
    "path": "core/release_config.scl",
    "content": "# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"\nExport build flags (with values) to make.\n\"\"\"\n\nload(\"//build/bazel/utils:schema_validation.scl\", \"validate\")\n\n# Partitions that get build system flag summaries\n_flag_partitions = [\n    \"product\",\n    \"system\",\n    \"system_ext\",\n    \"vendor\",\n]\n\nALL = [\"all\"]\nPRODUCT = [\"product\"]\nSYSTEM = [\"system\"]\nSYSTEM_EXT = [\"system_ext\"]\nVENDOR = [\"vendor\"]\n\n_valid_types = [\"NoneType\", \"bool\", \"list\", \"string\", \"int\"]\n\n_all_flags_schema = {\n    \"type\": \"list\",\n    \"of\": {\n        \"type\": \"dict\",\n        \"required_keys\": {\n            \"name\": {\"type\": \"string\"},\n            \"partitions\": {\n                \"type\": \"list\",\n                \"of\": {\n                    \"type\": \"string\",\n                    \"choices\": _flag_partitions + [\"all\"],\n                },\n                \"unique\": True,\n            },\n            \"default\": {\n                \"or\": [\n                    {\"type\": t}\n                    for t in _valid_types\n                ],\n            },\n            \"origin\": {\"type\": \"string\"},\n            \"declared_in\": {\"type\": \"string\"},\n        },\n        \"optional_keys\": {\n            \"appends\": {\n                \"type\": \"bool\",\n            },\n        },\n    },\n}\n\n_all_values_schema = {\n    \"type\": \"list\",\n    \"of\": {\n        \"type\": \"dict\",\n        \"required_keys\": {\n            \"name\": {\"type\": \"string\"},\n            \"value\": {\n                \"or\": [\n                    {\"type\": t}\n                    for t in _valid_types\n                ],\n            },\n            \"set_in\": {\"type\": \"string\"},\n        },\n    },\n}\n\ndef flag(name, partitions, default, *, origin = \"Unknown\", appends = False):\n    \"\"\"Declare a flag.\n\n    Args:\n      name: name of the flag\n      partitions: the partitions where this should be recorded.\n      default: the default value of the flag.\n      origin: The origin of this flag.\n      appends: Whether new values should be append (not replace) the old.\n\n    Returns:\n      A dictionary containing the flag declaration.\n    \"\"\"\n    if not partitions:\n        fail(\"At least 1 partition is required\")\n    if not name.startswith(\"RELEASE_\"):\n        fail(\"Release flag names must start with RELEASE_\")\n    if \" \" in name or \"\\t\" in name or \"\\n\" in name:\n        fail(\"Flag names must not contain whitespace: \\\"\" + name + \"\\\"\")\n    for partition in partitions:\n        if partition == \"all\":\n            if len(partitions) > 1:\n                fail(\"\\\"all\\\" can't be combined with other partitions: \" + str(partitions))\n        elif partition not in _flag_partitions:\n            fail(\"Invalid partition: \" + partition + \", allowed partitions: \" +\n                 str(_flag_partitions))\n    if type(default) not in _valid_types:\n        fail(\"Invalid type of default for flag \\\"\" + name + \"\\\" (\" + type(default) + \")\")\n    return {\n        \"name\": name,\n        \"partitions\": partitions,\n        \"default\": default,\n        \"appends\": appends,\n        \"origin\": origin,\n    }\n\ndef value(name, value):\n    \"\"\"Define the flag value for a particular configuration.\n\n    Args:\n      name: The name of the flag.\n      value: The value for the flag.\n\n    Returns:\n      A dictionary containing the name and value to be used.\n    \"\"\"\n    return {\n        \"name\": name,\n        \"value\": value,\n    }\n\ndef _format_value(val):\n    \"\"\"Format the starlark type correctly for make.\n\n    Args:\n      val: The value to format\n\n    Returns:\n      The value, formatted correctly for make.\n    \"\"\"\n    if type(val) == \"NoneType\":\n        return \"\"\n    elif type(val) == \"bool\":\n        return \"true\" if val else \"\"\n    else:\n        return val\n\ndef equal_flag_declaration(flag, other):\n    \"\"\"Return true if the flag declarations are equal.\n\n    Args:\n      flag: This flag declaration.\n      other: Another flag declaration.\n\n    Returns:\n      Whether the declarations are the same.\n    \"\"\"\n    for key in \"name\", \"partitions\", \"default\", \"appends\":\n        if flag[key] != other[key]:\n            return False\n    # For now, allow Unknown to match any other origin.\n    if flag[\"origin\"] == \"Unknown\" or other[\"origin\"] == \"Unknown\":\n        return True\n    return flag[\"origin\"] == other[\"origin\"]\n\ndef release_config(all_flags, all_values):\n    \"\"\"Return the make variables that should be set for this release config.\n\n    Args:\n      all_flags: A list of flag objects (from flag() calls).\n      all_values: A list of value objects (from value() calls).\n\n    Returns:\n      A dictionary of {name: value} variables for make.\n    \"\"\"\n    validate(all_flags, _all_flags_schema)\n    validate(all_values, _all_values_schema)\n\n    # Final values.\n    values = {}\n    # Validate flags\n    flag_names = []\n    flags_dict = {}\n    for flag in all_flags:\n        name = flag[\"name\"]\n        if name in flag_names:\n            if equal_flag_declaration(flag, flags_dict[name]):\n                continue\n            else:\n                fail(flag[\"declared_in\"] + \": Duplicate declaration of flag \" + name +\n                     \" (declared first in \" + flags_dict[name][\"declared_in\"] + \")\")\n        flag_names.append(name)\n        flags_dict[name] = flag\n        # Set the flag value to the default value.\n        values[name] = {\"name\": name, \"value\": _format_value(flag[\"default\"]), \"set_in\": flag[\"declared_in\"]}\n\n    # Record which flags go on which partition\n    partitions = {}\n    for flag in all_flags:\n        for partition in flag[\"partitions\"]:\n            if partition == \"all\":\n                if len(flag[\"partitions\"]) > 1:\n                    fail(\"\\\"all\\\" can't be combined with other partitions: \" + str(flag[\"partitions\"]))\n                for partition in _flag_partitions:\n                    partitions.setdefault(partition, []).append(flag[\"name\"])\n            else:\n                partitions.setdefault(partition, []).append(flag[\"name\"])\n\n    # Generate final values.\n    # Only declared flags may have a value.\n    for value in all_values:\n        name = value[\"name\"]\n        if name not in flag_names:\n            fail(value[\"set_in\"] + \": Value set for undeclared build flag: \" + name)\n        if flags_dict[name][\"appends\"]:\n            if name in values:\n                values[name][\"value\"] += \" \" + value[\"value\"]\n                values[name][\"set_in\"] += \" \" + value[\"set_in\"]\n            else:\n                values[name] = value\n        else:\n            values[name] = value\n\n    # Collect values\n    result = {\n        \"_ALL_RELEASE_FLAGS\": sorted(flag_names),\n    }\n    for partition, names in partitions.items():\n        result[\"_ALL_RELEASE_FLAGS.PARTITIONS.\" + partition] = names\n    for flag in all_flags:\n        val = _format_value(values[flag[\"name\"]][\"value\"])\n        result[flag[\"name\"]] = val\n        result[\"_ALL_RELEASE_FLAGS.\" + flag[\"name\"] + \".PARTITIONS\"] = flag[\"partitions\"]\n        result[\"_ALL_RELEASE_FLAGS.\" + flag[\"name\"] + \".DEFAULT\"] = _format_value(flag[\"default\"])\n        result[\"_ALL_RELEASE_FLAGS.\" + flag[\"name\"] + \".VALUE\"] = val\n        result[\"_ALL_RELEASE_FLAGS.\" + flag[\"name\"] + \".DECLARED_IN\"] = flag[\"declared_in\"]\n        result[\"_ALL_RELEASE_FLAGS.\" + flag[\"name\"] + \".SET_IN\"] = values[flag[\"name\"]][\"set_in\"]\n        result[\"_ALL_RELEASE_FLAGS.\" + flag[\"name\"] + \".ORIGIN\"] = flag[\"origin\"]\n\n    return result\n"
  },
  {
    "path": "core/robolectric_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2020 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs {MODULE}\">\n    <option name=\"test-suite-tag\" value=\"robolectric\" />\n    <option name=\"test-suite-tag\" value=\"robolectric-tests\" />\n\n    <option name=\"exclude-paths\" value=\"java\" />\n    <option name=\"use-robolectric-resources\" value=\"true\" />\n\n    <!-- attempt to always show Tradefed errors -->\n    <option name=\"do-not-swallow-runner-errors\" value=\"true\" />\n\n    <!-- prevent Tradefed from hanging indefinitely in CI -->\n    <option name=\"socket-timeout\" value=\"600000\" />\n    <option name=\"test-case-timeout\" value=\"2m\" />\n\n    {EXTRA_CONFIGS}\n\n    <test class=\"com.android.tradefed.testtype.IsolatedHostTest\" >\n\n        {EXTRA_TEST_RUNNER_CONFIGS}\n\n        <option name=\"jar\" value=\"{MODULE}.jar\" />\n        <option name=\"java-flags\" value=\"--add-modules=jdk.compiler\"/>\n        <option name=\"java-flags\" value=\"--add-opens=java.base/java.lang=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-opens=java.base/java.lang.reflect=ALL-UNNAMED\"/>\n        <!-- b/238100560 -->\n        <option name=\"java-flags\" value=\"--add-opens=java.base/jdk.internal.util.random=ALL-UNNAMED\"/>\n        <!-- b/251387255 -->\n        <option name=\"java-flags\" value=\"--add-opens=java.base/java.io=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-opens=java.base/java.net=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-opens=java.base/java.nio=ALL-UNNAMED\"/> <!-- required for ShadowVMRuntime -->\n        <option name=\"java-flags\" value=\"--add-opens=java.base/java.security=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-opens=java.base/java.text=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-opens=java.base/java.util=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-opens=java.base/jdk.internal.access=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-opens=java.desktop/java.awt.font=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED\"/>\n        <option name=\"java-flags\" value=\"--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED\"/>\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/root.mk",
    "content": "### DO NOT EDIT THIS FILE ###\ninclude build/make/core/main.mk\n### DO NOT EDIT THIS FILE ###\n"
  },
  {
    "path": "core/rust_device_benchmark_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2021 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Config to run {MODULE} rust benchmark tests.\">\n    <target_preparer class=\"com.android.tradefed.targetprep.PushFilePreparer\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"{MODULE}->/data/local/tmp/{MODULE}\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.rust.RustBinaryTest\" >\n        <option name=\"test-device-path\" value=\"/data/local/tmp\" />\n        <option name=\"module-name\" value=\"{MODULE}\" />\n        <option name=\"is-benchmark\" value=\"true\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/rust_device_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2019 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Config to run {MODULE} device tests.\">\n\n    {EXTRA_CONFIGS}\n\n    <target_preparer class=\"com.android.tradefed.targetprep.PushFilePreparer\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"{MODULE}->{TEST_INSTALL_BASE}/{MODULE}\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.rust.RustBinaryTest\" >\n        <option name=\"test-device-path\" value=\"{TEST_INSTALL_BASE}\" />\n        <option name=\"module-name\" value=\"{MODULE}\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/rust_host_benchmark_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2021 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Config to run {MODULE} rust benchmark host tests\">\n    <test class=\"com.android.tradefed.testtype.rust.RustBinaryHostTest\" >\n        <option name=\"test-file\" value=\"{MODULE}\" />\n        <option name=\"test-timeout\" value=\"5m\" />\n        <option name=\"is-benchmark\" value=\"true\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/rust_host_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2019 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Config to run {MODULE} host tests\">\n    <test class=\"com.android.tradefed.testtype.rust.RustBinaryHostTest\" >\n        <option name=\"test-file\" value=\"{MODULE}\" />\n        <option name=\"test-timeout\" value=\"5m\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "core/sbom.mk",
    "content": "# For SBOM generation\n# This is included by base_rules.mk and is not necessary to be included in other .mk files\n# unless a .mk file changes its installed file after including base_rules.mk.\n\nifdef my_register_name\n  # ALL_INSTALLED_FILES.$(installed_file).STATIC_LIBRARIES: list of module name of static libraries, e.g. libc++demangle libclang_rt.builtins, for primary arch\n  # ALL_INSTALLED_FILES.$(installed_file).WHOLE_STATIC_LIBRARIES: list of module name of static libraries, e.g. libc++demangle_32 libclang_rt.builtins_32, for 2nd arch.\n  ifneq (, $(strip $(ALL_MODULES.$(my_register_name).INSTALLED)))\n    $(foreach installed_file,$(ALL_MODULES.$(my_register_name).INSTALLED),\\\n      $(eval ALL_INSTALLED_FILES.$(installed_file) := $(my_register_name))\\\n      $(eval ALL_INSTALLED_FILES.$(installed_file).STATIC_LIBRARIES := $(foreach l,$(strip $(sort $(LOCAL_STATIC_LIBRARIES))),$l$(if $(LOCAL_2ND_ARCH_VAR_PREFIX),$($(my_prefix)2ND_ARCH_MODULE_SUFFIX))))\\\n      $(eval ALL_INSTALLED_FILES.$(installed_file).WHOLE_STATIC_LIBRARIES := $(foreach l,$(strip $(sort $(LOCAL_WHOLE_STATIC_LIBRARIES))),$l$(if $(LOCAL_2ND_ARCH_VAR_PREFIX),$($(my_prefix)2ND_ARCH_MODULE_SUFFIX))))\\\n    )\n  endif\n  ifeq (STATIC_LIBRARIES,$(LOCAL_MODULE_CLASS))\n  ALL_STATIC_LIBRARIES.$(my_register_name).STATIC_LIBRARIES := $(foreach l,$(strip $(sort $(LOCAL_STATIC_LIBRARIES))),$l$($(my_prefix)2ND_ARCH_MODULE_SUFFIX))\n  ALL_STATIC_LIBRARIES.$(my_register_name).WHOLE_STATIC_LIBRARIES := $(foreach l,$(strip $(sort $(LOCAL_WHOLE_STATIC_LIBRARIES))),$l$($(my_prefix)2ND_ARCH_MODULE_SUFFIX))\n  ifdef LOCAL_SOONG_MODULE_TYPE\n    ALL_STATIC_LIBRARIES.$(my_register_name).BUILT_FILE := $(LOCAL_PREBUILT_MODULE_FILE)\n  endif\n  endif\nendif"
  },
  {
    "path": "core/sdk_check.mk",
    "content": "\n# Enforcement checks that LOCAL_SDK_VERSION and LOCAL_PRIVATE_PLATFORM_APIS are\n# set correctly.\n# Should be included by java targets that allow specifying LOCAL_SDK_VERSION.\n# The JAVA_SDK_ENFORCEMENT_WARNING and JAVA_SDK_ENFORCEMENT_ERROR variables may\n# be set to a particular module class to enable warnings and errors for that\n# subtype.\n\nallowed_modules := framework-res__auto_generated_rro\n\n\nifeq (,$(JAVA_SDK_ENFORCEMENT_ERROR))\n  JAVA_SDK_ENFORCEMENT_ERROR := APPS\nendif\n\nifeq ($(LOCAL_SDK_VERSION)$(LOCAL_PRIVATE_PLATFORM_APIS),)\n  ifeq (,$(filter $(LOCAL_MODULE),$(allowed_modules)))\n    ifneq ($(JAVA_SDK_ENFORCEMENT_WARNING)$(JAVA_SDK_ENFORCEMENT_ERROR),)\n      my_message := Must specify LOCAL_SDK_VERSION or LOCAL_PRIVATE_PLATFORM_APIS,\n      ifeq ($(LOCAL_MODULE_CLASS),$(JAVA_SDK_ENFORCEMENT_ERROR))\n        $(call pretty-error,$(my_message))\n      endif\n      ifeq ($(LOCAL_MODULE_CLASS),$(JAVA_SDK_ENFORCEMENT_WARNING))\n        $(call pretty-warning,$(my_message))\n      endif\n      my_message :=\n    endif\n  endif\nelse ifneq ($(LOCAL_SDK_VERSION),)\n  ifneq ($(LOCAL_PRIVATE_PLATFORM_APIS),)\n    my_message := Specifies both LOCAL_SDK_VERSION ($(LOCAL_SDK_VERSION)) and\n    my_message += LOCAL_PRIVATE_PLATFORM_APIS ($(LOCAL_PRIVATE_PLATFORM_APIS))\n    my_message += but should specify only one\n    $(call pretty-error,$(my_message))\n    my_message :=\n  endif\nendif\n"
  },
  {
    "path": "core/shared_library.mk",
    "content": "$(call record-module-type,SHARED_LIBRARY)\nifdef LOCAL_IS_HOST_MODULE\n  $(call pretty-error,BUILD_SHARED_LIBRARY is incompatible with LOCAL_IS_HOST_MODULE. Use BUILD_HOST_SHARED_LIBRARY instead.)\nendif\nmy_prefix := TARGET_\ninclude $(BUILD_SYSTEM)/multilib.mk\n\nifndef my_module_multilib\n# libraries default to building for both architecturess\nmy_module_multilib := both\nendif\n\nifeq ($(my_module_multilib),both)\nifneq ($(LOCAL_MODULE_PATH),)\nifneq ($(TARGET_2ND_ARCH),)\n$(error $(LOCAL_MODULE): LOCAL_MODULE_PATH for shared libraries is unsupported in multiarch builds, use LOCAL_MODULE_RELATIVE_PATH instead)\nendif\nendif\n\nifneq ($(LOCAL_UNSTRIPPED_PATH),)\nifneq ($(TARGET_2ND_ARCH),)\n$(error $(LOCAL_MODULE): LOCAL_UNSTRIPPED_PATH for shared libraries is unsupported in multiarch builds)\nendif\nendif\nendif # my_module_multilib == both\n\n\nLOCAL_2ND_ARCH_VAR_PREFIX :=\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\n\nifeq ($(my_module_arch_supported),true)\ninclude $(BUILD_SYSTEM)/shared_library_internal.mk\nendif\n\nifdef TARGET_2ND_ARCH\n\nLOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX)\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\n\nifeq ($(my_module_arch_supported),true)\n# Build for TARGET_2ND_ARCH\nLOCAL_BUILT_MODULE :=\nLOCAL_INSTALLED_MODULE :=\nLOCAL_INTERMEDIATE_TARGETS :=\n\ninclude $(BUILD_SYSTEM)/shared_library_internal.mk\n\nendif\n\nLOCAL_2ND_ARCH_VAR_PREFIX :=\n\nendif # TARGET_2ND_ARCH\n\nmy_module_arch_supported :=\n\n###########################################################\n## Copy headers to the install tree\n###########################################################\nifdef LOCAL_COPY_HEADERS\n$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\\\n  $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\\\n  $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers))\ninclude $(BUILD_SYSTEM)/copy_headers.mk\nendif\n"
  },
  {
    "path": "core/shared_library_internal.mk",
    "content": "###########################################################\n## Standard rules for building a normal shared library.\n##\n## Additional inputs from base_rules.make:\n## None.\n##\n## LOCAL_MODULE_SUFFIX will be set for you.\n###########################################################\n\nifeq ($(strip $(LOCAL_MODULE_CLASS)),)\nLOCAL_MODULE_CLASS := SHARED_LIBRARIES\nendif\nifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)\nLOCAL_MODULE_SUFFIX := $(TARGET_SHLIB_SUFFIX)\nendif\nifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)$(LOCAL_MODULE_STEM_32)$(LOCAL_MODULE_STEM_64)),)\n$(error $(LOCAL_PATH): Cannot set module stem for a library)\nendif\n\nifdef target-shared-library-hook\n$(call target-shared-library-hook)\nendif\n\nskip_build_from_source :=\nifdef LOCAL_PREBUILT_MODULE_FILE\nifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH)))\ninclude $(BUILD_SYSTEM)/prebuilt_internal.mk\nskip_build_from_source := true\nendif\nendif\n\nifndef skip_build_from_source\n\ninclude $(BUILD_SYSTEM)/dynamic_binary.mk\n\n# Define PRIVATE_ variables from global vars\nifeq ($(LOCAL_NO_LIBCRT_BUILTINS),true)\nmy_target_libcrt_builtins :=\nelse\nmy_target_libcrt_builtins := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBCRT_BUILTINS)\nendif\nifeq ($(LOCAL_NO_CRT),true)\nmy_target_crtbegin_so_o :=\nmy_target_crtend_so_o :=\nelse ifeq ($(call module-in-vendor-or-product),true)\nmy_target_crtbegin_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_so.vendor)\nmy_target_crtend_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_so.vendor)\nelse\nmy_target_crtbegin_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_so)\nmy_target_crtend_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_so)\nendif\nifneq ($(LOCAL_SDK_VERSION),)\nmy_target_crtbegin_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_so.sdk.$(my_ndk_crt_version))\nmy_target_crtend_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_so.sdk.$(my_ndk_crt_version))\nendif\n$(linked_module): PRIVATE_TARGET_LIBCRT_BUILTINS := $(my_target_libcrt_builtins)\n$(linked_module): PRIVATE_TARGET_CRTBEGIN_SO_O := $(my_target_crtbegin_so_o)\n$(linked_module): PRIVATE_TARGET_CRTEND_SO_O := $(my_target_crtend_so_o)\n\n$(linked_module): \\\n        $(all_objects) \\\n        $(all_libraries) \\\n        $(my_target_crtbegin_so_o) \\\n        $(my_target_crtend_so_o) \\\n        $(my_target_libcrt_builtins) \\\n        $(LOCAL_ADDITIONAL_DEPENDENCIES) $(CLANG_CXX)\n\t$(transform-o-to-shared-lib)\n\nifeq ($(my_native_coverage),true)\ngcno_suffix := .zip\n\nbuilt_whole_gcno_libraries := \\\n    $(foreach lib,$(my_whole_static_libraries), \\\n      $(call intermediates-dir-for, \\\n        STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \\\n        $(my_host_cross))/$(lib)$(gcno_suffix))\n\nbuilt_static_gcno_libraries := \\\n    $(foreach lib,$(my_static_libraries), \\\n      $(call intermediates-dir-for, \\\n        STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \\\n        $(my_host_cross))/$(lib)$(gcno_suffix))\n\nifdef LOCAL_IS_HOST_MODULE\nmy_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path))\nelse\nmy_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path))\nendif\n\nGCNO_ARCHIVE := $(basename $(my_installed_module_stem))$(gcno_suffix)\n\n$(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS)\n$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(strip $(LOCAL_GCNO_FILES))\n$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(strip $(built_whole_gcno_libraries)) $(strip $(built_static_gcno_libraries))\n$(intermediates)/$(GCNO_ARCHIVE) : $(LOCAL_GCNO_FILES) $(built_whole_gcno_libraries) $(built_static_gcno_libraries)\n\t$(package-coverage-files)\n\n$(my_coverage_path)/$(GCNO_ARCHIVE) : $(intermediates)/$(GCNO_ARCHIVE)\n\t$(copy-file-to-target)\n\n$(LOCAL_BUILT_MODULE): $(my_coverage_path)/$(GCNO_ARCHIVE)\nendif\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=SHARED_LIBRARY))\n\nendif  # skip_build_from_source\n"
  },
  {
    "path": "core/shell_test_config_template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2020 The Android Open Source Project\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Config for running {MODULE} through Atest or in Infra\">\n    <option name=\"test-suite-tag\" value=\"{MODULE}\" />\n\n    {EXTRA_CONFIGS}\n\n    <!-- This test requires a device, so it's not annotated with a null-device. -->\n    <test class=\"com.android.tradefed.testtype.binary.ExecutableHostTest\" >\n        <option name=\"binary\" value=\"{OUTPUT_FILENAME}\" />\n        <!-- Test script assumes a relative path with the tests/ folders. -->\n        <option name=\"relative-path-execution\" value=\"true\" />\n        <!-- Tests shouldn't be that long but set 15m to be safe. -->\n        <option name=\"per-binary-timeout\" value=\"15m\" />\n    </test>\n</configuration>"
  },
  {
    "path": "core/soong_android_app_set.mk",
    "content": "# App prebuilt coming from Soong.\n# Extra inputs:\n# LOCAL_APK_SET_INSTALL_FILE\n\nifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n  $(call pretty-error,soong_apk_set.mk may only be used from Soong)\nendif\n\nLOCAL_BUILT_MODULE_STEM := package.apk\nLOCAL_INSTALLED_MODULE_STEM := $(notdir $(LOCAL_PREBUILT_MODULE_FILE))\n\n#######################################\ninclude $(BUILD_SYSTEM)/base_rules.mk\n#######################################\n\n$(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE)))\n\nPACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES))\n\nPACKAGES := $(PACKAGES) $(LOCAL_MODULE)\n# We can't know exactly what apk files would be outputted yet.\n# Let extract_apks generate apkcerts.txt and merge it later.\nPACKAGES.$(LOCAL_MODULE).APKCERTS_FILE := $(LOCAL_APKCERTS_FILE)\n\nSOONG_ALREADY_CONV += $(LOCAL_MODULE)\n"
  },
  {
    "path": "core/soong_app_prebuilt.mk",
    "content": "# App prebuilt coming from Soong.\n# Extra inputs:\n# LOCAL_SOONG_BUILT_INSTALLED\n# LOCAL_SOONG_BUNDLE\n# LOCAL_SOONG_CLASSES_JAR\n# LOCAL_SOONG_DEX_JAR\n# LOCAL_SOONG_HEADER_JAR\n# LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR\n# LOCAL_SOONG_PROGUARD_DICT\n# LOCAL_SOONG_PROGUARD_USAGE_ZIP\n# LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE\n# LOCAL_SOONG_RRO_DIRS\n# LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH)\n# LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH)\n# LOCAL_SOONG_JNI_LIBS_SYMBOLS\n# LOCAL_SOONG_DEXPREOPT_CONFIG\n\nifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n  $(call pretty-error,soong_app_prebuilt.mk may only be used from Soong)\nendif\n\nLOCAL_MODULE_SUFFIX := .apk\nLOCAL_BUILT_MODULE_STEM := package.apk\n\nintermediates.COMMON := $(call local-intermediates-dir,COMMON)\n\nfull_classes_jar := $(intermediates.COMMON)/classes.jar\nfull_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar\nfull_classes_header_jar := $(intermediates.COMMON)/classes-header.jar\n\n\n#######################################\ninclude $(BUILD_SYSTEM)/base_rules.mk\n#######################################\n\nifdef LOCAL_SOONG_CLASSES_JAR\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_jar)))\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_pre_proguard_jar)))\n  $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(full_classes_jar)))\n\n  ifneq ($(TURBINE_ENABLED),false)\n    ifdef LOCAL_SOONG_HEADER_JAR\n      $(eval $(call copy-one-file,$(LOCAL_SOONG_HEADER_JAR),$(full_classes_header_jar)))\n    else\n      $(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_header_jar)))\n    endif\n  endif # TURBINE_ENABLED != false\n\n  javac-check : $(full_classes_jar)\n  javac-check-$(LOCAL_MODULE) : $(full_classes_jar)\n  .PHONY: javac-check-$(LOCAL_MODULE)\nendif\n\nifdef LOCAL_SOONG_DEXPREOPT_CONFIG\n  my_dexpreopt_config := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_DEXPREOPT_CONFIG), $(my_dexpreopt_config)))\n  $(LOCAL_BUILT_MODULE): $(my_dexpreopt_config)\nendif\n\n\n\n# Run veridex on product, system_ext and vendor modules.\n# We skip it for unbundled app builds where we cannot build veridex.\nmodule_run_appcompat :=\nifeq (true,$(non_system_module))\nifeq (,$(TARGET_BUILD_APPS))  # not unbundled app build\nifeq (,$(filter sdk,$(MAKECMDGOALS))) # not sdk build (which is another form of unbundled build)\nifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)\n  module_run_appcompat := true\nendif\nendif\nendif\nendif\n\nifeq ($(module_run_appcompat),true)\n  $(LOCAL_BUILT_MODULE): $(appcompat-files)\n  $(LOCAL_BUILT_MODULE): PRIVATE_INSTALLED_MODULE := $(LOCAL_INSTALLED_MODULE)\n  $(LOCAL_BUILT_MODULE): $(LOCAL_PREBUILT_MODULE_FILE)\n\t@echo \"Copy: $@\"\n\t$(copy-file-to-target)\n\t$(appcompat-header)\n\t$(run-appcompat)\nelse\n  $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE)))\nendif\n\nifdef LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR),\\\n    $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar))\n  $(call add-dependency,$(LOCAL_BUILT_MODULE),\\\n    $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar)\nendif\n\nifdef LOCAL_SOONG_PROGUARD_DICT\n  $(eval $(call copy-r8-dictionary-file-with-mapping,\\\n    $(LOCAL_SOONG_PROGUARD_DICT),\\\n    $(intermediates.COMMON)/proguard_dictionary,\\\n    $(intermediates.COMMON)/proguard_dictionary.textproto))\n\n  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_FILES := \\\n    $(intermediates.COMMON)/proguard_dictionary \\\n    $(LOCAL_SOONG_CLASSES_JAR)\n  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS := \\\n    -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/proguard_dictionary \\\n    -f $(intermediates.COMMON)/proguard_dictionary \\\n    -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/classes.jar \\\n    -f $(LOCAL_SOONG_CLASSES_JAR)\n  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_MAPPING := $(intermediates.COMMON)/proguard_dictionary.textproto\nendif\n\nifdef LOCAL_SOONG_PROGUARD_USAGE_ZIP\n  ALL_MODULES.$(my_register_name).PROGUARD_USAGE_ZIP := $(LOCAL_SOONG_PROGUARD_USAGE_ZIP)\nendif\n\nifdef LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE\nresource_export_package := $(intermediates.COMMON)/package-export.apk\nresource_export_stamp := $(intermediates.COMMON)/src/R.stamp\n\n$(resource_export_package): PRIVATE_STAMP := $(resource_export_stamp)\n$(resource_export_package): .KATI_IMPLICIT_OUTPUTS := $(resource_export_stamp)\n$(resource_export_package): $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE)\n\t@echo \"Copy: $@\"\n\t$(copy-file-to-target)\n\ttouch $(PRIVATE_STAMP)\n$(call add-dependency,$(LOCAL_BUILT_MODULE),$(resource_export_package))\n\nendif # LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE\n\njava-dex: $(LOCAL_SOONG_DEX_JAR)\n\n\n# Copy test suite files.\nifdef LOCAL_COMPATIBILITY_SUITE\nmy_apks_to_install := $(foreach f,$(filter %.apk %.idsig,$(LOCAL_SOONG_BUILT_INSTALLED)),$(call word-colon,1,$(f)))\n$(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \\\n  $(eval my_compat_dist_$(suite) := $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \\\n    $(foreach a,$(my_apks_to_install),\\\n      $(call compat-copy-pair,$(a),$(dir)/$(notdir $(a)))))))\n$(call create-suite-dependencies)\nendif\n\n# install symbol files of JNI libraries\nmy_jni_lib_symbols_copy_files := $(foreach f,$(LOCAL_SOONG_JNI_LIBS_SYMBOLS),\\\n  $(call word-colon,1,$(f)):$(patsubst $(PRODUCT_OUT)/%,$(TARGET_OUT_UNSTRIPPED)/%,$(call word-colon,2,$(f))))\n\n$(foreach f, $(my_jni_lib_symbols_copy_files), \\\n  $(eval $(call copy-unstripped-elf-file-with-mapping, \\\n    $(call word-colon,1,$(f)), \\\n    $(call word-colon,2,$(f)), \\\n    $(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(call word-colon,2,$(f)).textproto)\\\n  ))\\\n)\n\nsymbolic_outputs := $(foreach f,$(my_jni_lib_symbols_copy_files),$(call word-colon,2,$(f)))\nsymbolic_mappings := $(foreach f,$(symbolic_outputs),$(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(f).textproto))\nALL_MODULES.$(my_register_name).SYMBOLIC_OUTPUT_PATH := $(symbolic_outputs)\nALL_MODULES.$(my_register_name).ELF_SYMBOL_MAPPING_PATH := $(symbolic_mappings)\n\n$(LOCAL_BUILT_MODULE): | $(symbolic_outputs)\n\n# embedded JNI will already have been handled by soong\nmy_embed_jni :=\nmy_prebuilt_jni_libs :=\nifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))\n  ifdef LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH)\n    my_2nd_arch_prefix :=\n    LOCAL_JNI_SHARED_LIBRARIES := $(LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH))\n    partition_lib_pairs :=  $(LOCAL_SOONG_JNI_LIBS_PARTITION_$(TARGET_ARCH))\n    include $(BUILD_SYSTEM)/install_jni_libs_internal.mk\n  endif\n  ifdef TARGET_2ND_ARCH\n    ifdef LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH)\n      my_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX)\n      LOCAL_JNI_SHARED_LIBRARIES := $(LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH))\n      partition_lib_pairs :=  $(LOCAL_SOONG_JNI_LIBS_PARTITION_$(TARGET_2ND_ARCH))\n      include $(BUILD_SYSTEM)/install_jni_libs_internal.mk\n    endif\n  endif\nendif\nLOCAL_SHARED_JNI_LIBRARIES :=\nmy_embed_jni :=\nmy_prebuilt_jni_libs :=\nmy_2nd_arch_prefix :=\npartition_lib_pairs :=\n\nPACKAGES := $(PACKAGES) $(LOCAL_MODULE)\nifndef LOCAL_CERTIFICATE\n  $(call pretty-error,LOCAL_CERTIFICATE must be set for soong_app_prebuilt.mk)\nendif\nifeq ($(LOCAL_CERTIFICATE),PRESIGNED)\n  # The magic string \"PRESIGNED\" means this package is already checked\n  # signed with its release key.\n  #\n  # By setting .CERTIFICATE but not .PRIVATE_KEY, this package will be\n  # mentioned in apkcerts.txt (with certificate set to \"PRESIGNED\")\n  # but the dexpreopt process will not try to re-sign the app.\n  PACKAGES.$(LOCAL_MODULE).CERTIFICATE := PRESIGNED\nelse ifneq ($(LOCAL_CERTIFICATE),)\n  PACKAGES.$(LOCAL_MODULE).CERTIFICATE := $(LOCAL_CERTIFICATE)\n  PACKAGES.$(LOCAL_MODULE).PRIVATE_KEY := $(patsubst %.x509.pem,%.pk8,$(LOCAL_CERTIFICATE))\nendif\ninclude $(BUILD_SYSTEM)/app_certificate_validate.mk\nPACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES))\n\nifneq ($(LOCAL_MODULE_STEM),)\n  PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE_STEM)\nelse\n  PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE)\nendif\n\n# Set a actual_partition_tag (calculated in base_rules.mk) for the package.\nPACKAGES.$(LOCAL_MODULE).PARTITION := $(actual_partition_tag)\n\nifdef LOCAL_SOONG_BUNDLE\n  ALL_MODULES.$(my_register_name).BUNDLE := $(LOCAL_SOONG_BUNDLE)\nendif\n\nifdef LOCAL_SOONG_LINT_REPORTS\n  ALL_MODULES.$(my_register_name).LINT_REPORTS := $(LOCAL_SOONG_LINT_REPORTS)\nendif\n\nifndef LOCAL_IS_HOST_MODULE\nifeq ($(LOCAL_SDK_VERSION),system_current)\nmy_link_type := java:system\nelse ifneq ($(LOCAL_SDK_VERSION),)\nmy_link_type := java:sdk\nelse\nmy_link_type := java:platform\nendif\n# warn/allowed types are both empty because Soong modules can't depend on\n# make-defined modules.\nmy_warn_types :=\nmy_allowed_types :=\n\nmy_link_deps :=\nmy_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX)\nmy_common := COMMON\ninclude $(BUILD_SYSTEM)/link_type.mk\nendif # !LOCAL_IS_HOST_MODULE\n\nifdef LOCAL_PREBUILT_COVERAGE_ARCHIVE\n  my_coverage_dir := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path))\n  my_coverage_copy_pairs := $(foreach f,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(f):$(my_coverage_dir)/$(notdir  $(f)))\n  my_coverage_files := $(call copy-many-files,$(my_coverage_copy_pairs))\n  $(LOCAL_INSTALLED_MODULE): $(my_coverage_files)\nendif\n\nSOONG_ALREADY_CONV += $(LOCAL_MODULE)\n\n###########################################################\n## SBOM generation\n###########################################################\ninclude $(BUILD_SBOM_GEN)\n"
  },
  {
    "path": "core/soong_cc_rust_prebuilt.mk",
    "content": "# Native prebuilt coming from Soong.\n# Extra inputs:\n# LOCAL_SOONG_LINK_TYPE\n# LOCAL_SOONG_TOC\n# LOCAL_SOONG_UNSTRIPPED_BINARY\n# LOCAL_SOONG_VNDK_VERSION : means the version of VNDK where this module belongs\n\nifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n  $(call pretty-error,soong_cc_rust_prebuilt.mk may only be used from Soong)\nendif\n\nifdef LOCAL_IS_HOST_MODULE\n  ifneq ($(HOST_OS),$(LOCAL_MODULE_HOST_OS))\n    my_prefix := HOST_CROSS_\n    LOCAL_HOST_PREFIX := $(my_prefix)\n  else\n    my_prefix := HOST_\n    LOCAL_HOST_PREFIX :=\n  endif\nelse\n  my_prefix := TARGET_\nendif\n\nifeq ($($(my_prefix)ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH))\n  # primary arch\n  LOCAL_2ND_ARCH_VAR_PREFIX :=\nelse ifeq ($($(my_prefix)2ND_ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH))\n  # secondary arch\n  LOCAL_2ND_ARCH_VAR_PREFIX := $($(my_prefix)2ND_ARCH_VAR_PREFIX)\nelse\n  $(call pretty-error,Unsupported LOCAL_MODULE_$(my_prefix)ARCH=$(LOCAL_MODULE_$(my_prefix)ARCH))\nendif\n\n# Don't install static/rlib/proc_macro libraries.\nifndef LOCAL_UNINSTALLABLE_MODULE\n  ifneq ($(filter STATIC_LIBRARIES RLIB_LIBRARIES PROC_MACRO_LIBRARIES,$(LOCAL_MODULE_CLASS)),)\n    LOCAL_UNINSTALLABLE_MODULE := true\n  endif\nendif\n\nmy_check_same_vndk_variants :=\nsame_vndk_variants_stamp :=\nifeq ($(LOCAL_CHECK_SAME_VNDK_VARIANTS),true)\n  ifeq ($(filter hwaddress address, $(SANITIZE_TARGET)),)\n    ifneq ($(CLANG_COVERAGE),true)\n      # Do not compare VNDK variant for special cases e.g. coverage builds.\n      ifneq ($(SKIP_VNDK_VARIANTS_CHECK),true)\n        my_check_same_vndk_variants := true\n        same_vndk_variants_stamp := $(call local-intermediates-dir,,$(LOCAL_2ND_ARCH_VAR_PREFIX))/same_vndk_variants.timestamp\n      endif\n    endif\n  endif\nendif\n\nifeq ($(my_check_same_vndk_variants),true)\n  # Add the timestamp to the CHECKED list so that `checkbuild` can run it.\n  # Note that because `checkbuild` doesn't check LOCAL_BUILT_MODULE for soong-built modules adding\n  # the timestamp to LOCAL_BUILT_MODULE isn't enough. It is skipped when the vendor variant\n  # isn't used at all and it may break in the downstream trees.\n  LOCAL_ADDITIONAL_CHECKED_MODULE += $(same_vndk_variants_stamp)\nendif\n\n#######################################\ninclude $(BUILD_SYSTEM)/base_rules.mk\n#######################################\n\nifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES RLIB_LIBRARIES DYLIB_LIBRARIES HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)),)\n  # Soong module is a static or shared library\n  EXPORTS_LIST += $(intermediates)\n  EXPORTS.$(intermediates).FLAGS := $(LOCAL_EXPORT_CFLAGS)\n  EXPORTS.$(intermediates).DEPS := $(LOCAL_EXPORT_C_INCLUDE_DEPS)\n\n  ifdef LOCAL_SOONG_TOC\n    $(eval $(call copy-one-file,$(LOCAL_SOONG_TOC),$(LOCAL_BUILT_MODULE).toc))\n    $(call add-dependency,$(LOCAL_BUILT_MODULE).toc,$(LOCAL_BUILT_MODULE))\n    $(my_all_targets): $(LOCAL_BUILT_MODULE).toc\n  endif\n\n  SOONG_ALREADY_CONV += $(LOCAL_MODULE)\n\n  my_link_type := $(LOCAL_SOONG_LINK_TYPE)\n  my_warn_types :=\n  my_allowed_types :=\n  my_link_deps :=\n  my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX)\n  my_common :=\n  include $(BUILD_SYSTEM)/link_type.mk\nendif\n\nifeq ($(call module-in-vendor-or-product),true)\n  ifneq ($(LOCAL_VNDK_DEPEND_ON_CORE_VARIANT),true)\n    name_without_suffix := $(patsubst %.vendor,%,$(LOCAL_MODULE))\n    ifneq ($(name_without_suffix),$(LOCAL_MODULE))\n      SPLIT_VENDOR.$(LOCAL_MODULE_CLASS).$(name_without_suffix) := 1\n    else\n      name_without_suffix := $(patsubst %.product,%,$(LOCAL_MODULE))\n      ifneq ($(name_without_suffix),$(LOCAL_MODULE))\n        SPLIT_PRODUCT.$(LOCAL_MODULE_CLASS).$(name_without_suffix) := 1\n      endif\n    endif\n    name_without_suffix :=\n  endif\nendif\n\n# Check prebuilt ELF binaries.\nifdef LOCAL_INSTALLED_MODULE\n  ifneq ($(LOCAL_CHECK_ELF_FILES),)\n    my_prebuilt_src_file := $(LOCAL_PREBUILT_MODULE_FILE)\n    my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES)\n    include $(BUILD_SYSTEM)/check_elf_file.mk\n  endif\nendif\n\n# The real dependency will be added after all Android.mks are loaded and the install paths\n# of the shared libraries are determined.\nifdef LOCAL_INSTALLED_MODULE\n  ifdef LOCAL_SHARED_LIBRARIES\n    my_shared_libraries := $(LOCAL_SHARED_LIBRARIES)\n    ifeq ($(call module-in-vendor-or-product),true)\n      ifdef LOCAL_IN_PRODUCT\n        my_shared_libraries := $(foreach l,$(my_shared_libraries),\\\n          $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l)))\n      else\n        my_shared_libraries := $(foreach l,$(my_shared_libraries),\\\n          $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l)))\n      endif\n    endif\n    $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \\\n      $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_shared_libraries))\n  endif\n  ifdef LOCAL_DYLIB_LIBRARIES\n    my_dylibs := $(LOCAL_DYLIB_LIBRARIES)\n    # Treat these as shared library dependencies for installation purposes.\n    ifeq ($(call module-in-vendor-or-product),true)\n      ifdef LOCAL_IN_PRODUCT\n        my_dylibs := $(foreach l,$(my_dylibs),\\\n          $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l)))\n      else\n        my_dylibs := $(foreach l,$(my_dylibs),\\\n          $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l)))\n      endif\n    endif\n    $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \\\n      $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_dylibs))\n  endif\nendif\n\nifeq ($(my_check_same_vndk_variants),true)\n  my_core_register_name := $(subst .vendor,,$(subst .product,,$(my_register_name)))\n  my_core_variant_files := $(call module-target-built-files,$(my_core_register_name))\n  my_core_shared_lib := $(sort $(filter %.so,$(my_core_variant_files)))\n\n  $(same_vndk_variants_stamp): PRIVATE_CORE_VARIANT := $(my_core_shared_lib)\n  $(same_vndk_variants_stamp): PRIVATE_VENDOR_VARIANT := $(LOCAL_PREBUILT_MODULE_FILE)\n  $(same_vndk_variants_stamp): PRIVATE_TOOLS_PREFIX := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)TOOLS_PREFIX)\n\n  $(same_vndk_variants_stamp): $(my_core_shared_lib) $(LOCAL_PREBUILT_MODULE_FILE)\n\t$(call verify-vndk-libs-identical,\\\n\t    $(PRIVATE_CORE_VARIANT),\\\n\t    $(PRIVATE_VENDOR_VARIANT),\\\n\t    $(PRIVATE_TOOLS_PREFIX))\n\ttouch $@\n\n  $(LOCAL_BUILT_MODULE): $(same_vndk_variants_stamp)\nendif\n\n# Use copy-or-link-prebuilt-to-target for host executables and shared libraries,\n# to preserve symlinks to the source trees. They can then run directly from the\n# prebuilt directories where the linker can load their dependencies using\n# relative RUNPATHs.\n$(LOCAL_BUILT_MODULE): $(LOCAL_PREBUILT_MODULE_FILE)\nifeq ($(LOCAL_IS_HOST_MODULE) $(if $(filter EXECUTABLES SHARED_LIBRARIES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),true,),true true)\n\t$(copy-or-link-prebuilt-to-target)\n  ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)\n\t[ -x $@ ] || ( $(call echo-error,$@,Target of symlink is not executable); false )\n  endif\nelse\n\t$(transform-prebuilt-to-target)\n  ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)\n\t$(hide) chmod +x $@\n  endif\nendif\n\nifndef LOCAL_IS_HOST_MODULE\n  ifdef LOCAL_SOONG_UNSTRIPPED_BINARY\n    ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true)\n      my_symbol_path := $(if $(LOCAL_SOONG_SYMBOL_PATH),$(LOCAL_SOONG_SYMBOL_PATH),$(my_module_path))\n      # Store a copy with symbols for symbolic debugging\n      my_unstripped_path := $(TARGET_OUT_UNSTRIPPED)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_symbol_path))\n      # drop /root as /root is mounted as /\n      my_unstripped_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/root/%,$(TARGET_OUT_UNSTRIPPED)/%, $(my_unstripped_path))\n      symbolic_output := $(my_unstripped_path)/$(my_installed_module_stem)\n      elf_symbol_mapping_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(symbolic_output).textproto)\n\n      ALL_MODULES.$(my_register_name).SYMBOLIC_OUTPUT_PATH := $(symbolic_output)\n      ALL_MODULES.$(my_register_name).ELF_SYMBOL_MAPPING_PATH := $(elf_symbol_mapping_path)\n\n      $(eval $(call copy-unstripped-elf-file-with-mapping,$(LOCAL_SOONG_UNSTRIPPED_BINARY),$(symbolic_output),$(elf_symbol_mapping_path)))\n      $(LOCAL_BUILT_MODULE): | $(symbolic_output)\n\n      ifeq ($(BREAKPAD_GENERATE_SYMBOLS),true)\n        my_breakpad_path := $(TARGET_OUT_BREAKPAD)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_symbol_path))\n        breakpad_output := $(my_breakpad_path)/$(my_installed_module_stem).sym\n        $(breakpad_output) : $(LOCAL_SOONG_UNSTRIPPED_BINARY) | $(BREAKPAD_DUMP_SYMS) $(PRIVATE_READELF)\n\t@echo \"target breakpad: $(PRIVATE_MODULE) ($@)\"\n\t@mkdir -p $(dir $@)\n\t$(hide) if $(PRIVATE_READELF) -S $< > /dev/null 2>&1 ; then \\\n\t  $(BREAKPAD_DUMP_SYMS) -c $< > $@ ; \\\n\telse \\\n\t  echo \"skipped for non-elf file.\"; \\\n\t  touch $@; \\\n\tfi\n        $(call add-dependency,$(LOCAL_BUILT_MODULE),$(breakpad_output))\n      endif\n    endif\n  endif\nendif\n\nifeq ($(NATIVE_COVERAGE),true)\n  ifneq (,$(strip $(LOCAL_PREBUILT_COVERAGE_ARCHIVE)))\n    $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(intermediates)/$(LOCAL_MODULE).zip))\n    ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true)\n      ifdef LOCAL_IS_HOST_MODULE\n        my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path))\n      else\n        my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path))\n      endif\n      my_coverage_path := $(my_coverage_path)/$(patsubst %.so,%,$(my_installed_module_stem)).zip\n      $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(my_coverage_path)))\n      $(LOCAL_BUILT_MODULE): $(my_coverage_path)\n    endif\n  else\n    # Coverage information is needed when static lib is a dependency of another\n    # coverage-enabled module.\n    ifeq (STATIC_LIBRARIES, $(LOCAL_MODULE_CLASS))\n      GCNO_ARCHIVE := $(LOCAL_MODULE).zip\n      $(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS)\n      $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS :=\n      $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES :=\n      $(intermediates)/$(GCNO_ARCHIVE) :\n\t$(package-coverage-files)\n    endif\n  endif\nendif\n\n# A product may be configured to strip everything in some build variants.\n# We do the stripping as a post-install command so that LOCAL_BUILT_MODULE\n# is still with the symbols and we don't need to clean it (and relink) when\n# you switch build variant.\nifneq ($(filter $(STRIP_EVERYTHING_BUILD_VARIANTS),$(TARGET_BUILD_VARIANT)),)\n$(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := \\\n  $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_STRIP) --strip-all $(LOCAL_INSTALLED_MODULE)\nendif\n\n$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)\n\n# Reinstall shared library dependencies of fuzz targets to /data/fuzz/ (for\n# target) or /data/ (for host).\nifdef LOCAL_IS_FUZZ_TARGET\n$(LOCAL_INSTALLED_MODULE): $(LOCAL_FUZZ_INSTALLED_SHARED_DEPS)\nendif\n"
  },
  {
    "path": "core/soong_config.mk",
    "content": "SOONG_MAKEVARS_MK := $(SOONG_OUT_DIR)/make_vars-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk\nSOONG_ANDROID_MK := $(SOONG_OUT_DIR)/Android-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk\n\ninclude $(BUILD_SYSTEM)/art_config.mk\ninclude $(BUILD_SYSTEM)/dex_preopt_config.mk\n\nifndef AFDO_PROFILES\n# Set AFDO_PROFILES\n-include vendor/google_data/pgo_profile/sampling/afdo_profiles.mk\ninclude toolchain/pgo-profiles/sampling/afdo_profiles.mk\nelse\n$(error AFDO_PROFILES can only be set from soong_config.mk. For product-specific fdo_profiles, please use PRODUCT_AFDO_PROFILES)\nendif\n\n# PRODUCT_AFDO_PROFILES takes precedence over product-agnostic profiles in AFDO_PROFILES\nALL_AFDO_PROFILES := $(PRODUCT_AFDO_PROFILES) $(AFDO_PROFILES)\n\nifneq (,$(filter-out environment undefined,$(origin GENRULE_SANDBOXING)))\n  $(error GENRULE_SANDBOXING can only be provided via an environment variable, use BUILD_BROKEN_GENRULE_SANDBOXING to disable genrule sandboxing in board config)\nendif\n\nifeq ($(WRITE_SOONG_VARIABLES),true)\n\n# Create soong.variables with copies of makefile settings.  Runs every build,\n# but only updates soong.variables if it changes\n$(shell mkdir -p $(dir $(SOONG_VARIABLES)))\n$(call json_start)\n\n$(call add_json_str,  Make_suffix, -$(TARGET_PRODUCT)$(COVERAGE_SUFFIX))\n\n$(call add_json_str,  BuildId,                           $(BUILD_ID))\n$(call add_json_str,  BuildFingerprintFile,              build_fingerprint.txt)\n$(call add_json_str,  BuildNumberFile,                   build_number.txt)\n$(call add_json_str,  BuildHostnameFile,                 build_hostname.txt)\n$(call add_json_str,  BuildThumbprintFile,               build_thumbprint.txt)\n$(call add_json_bool, DisplayBuildNumber,                $(filter true,$(DISPLAY_BUILD_NUMBER)))\n\n$(call add_json_str,  Platform_display_version_name,     $(PLATFORM_DISPLAY_VERSION))\n$(call add_json_str,  Platform_version_name,             $(PLATFORM_VERSION))\n$(call add_json_val,  Platform_sdk_version,              $(PLATFORM_SDK_VERSION))\n$(call add_json_str,  Platform_sdk_codename,             $(PLATFORM_VERSION_CODENAME))\n$(call add_json_bool, Platform_sdk_final,                $(filter REL,$(PLATFORM_VERSION_CODENAME)))\n$(call add_json_val,  Platform_sdk_extension_version,    $(PLATFORM_SDK_EXTENSION_VERSION))\n$(call add_json_val,  Platform_base_sdk_extension_version, $(PLATFORM_BASE_SDK_EXTENSION_VERSION))\n$(call add_json_csv,  Platform_version_active_codenames, $(PLATFORM_VERSION_ALL_CODENAMES))\n$(call add_json_csv,  Platform_version_all_preview_codenames, $(PLATFORM_VERSION_ALL_PREVIEW_CODENAMES))\n$(call add_json_str,  Platform_security_patch,           $(PLATFORM_SECURITY_PATCH))\n$(call add_json_str,  Platform_preview_sdk_version,      $(PLATFORM_PREVIEW_SDK_VERSION))\n$(call add_json_str,  Platform_base_os,                  $(PLATFORM_BASE_OS))\n$(call add_json_str,  Platform_version_last_stable,      $(PLATFORM_VERSION_LAST_STABLE))\n$(call add_json_str,  Platform_version_known_codenames,  $(PLATFORM_VERSION_KNOWN_CODENAMES))\n\n$(call add_json_bool, Release_aidl_use_unfrozen,         $(RELEASE_AIDL_USE_UNFROZEN))\n\n$(call add_json_bool, Allow_missing_dependencies,        $(filter true,$(ALLOW_MISSING_DEPENDENCIES)))\n$(call add_json_bool, Unbundled_build,                   $(TARGET_BUILD_UNBUNDLED))\n$(call add_json_list, Unbundled_build_apps,              $(TARGET_BUILD_APPS))\n$(call add_json_bool, Unbundled_build_image,             $(TARGET_BUILD_UNBUNDLED_IMAGE))\n$(call add_json_bool, Always_use_prebuilt_sdks,          $(TARGET_BUILD_USE_PREBUILT_SDKS))\n\n$(call add_json_bool, Debuggable,                        $(filter userdebug eng,$(TARGET_BUILD_VARIANT)))\n$(call add_json_bool, Eng,                               $(filter eng,$(TARGET_BUILD_VARIANT)))\n$(call add_json_str,  BuildType,                         $(TARGET_BUILD_TYPE))\n\n$(call add_json_str,  DeviceName,                        $(TARGET_DEVICE))\n$(call add_json_str,  DeviceProduct,                     $(TARGET_PRODUCT))\n$(call add_json_str,  DeviceArch,                        $(TARGET_ARCH))\n$(call add_json_str,  DeviceArchVariant,                 $(TARGET_ARCH_VARIANT))\n$(call add_json_str,  DeviceCpuVariant,                  $(TARGET_CPU_VARIANT))\n$(call add_json_list, DeviceAbi,                         $(TARGET_CPU_ABI) $(TARGET_CPU_ABI2))\n\n$(call add_json_str,  DeviceSecondaryArch,               $(TARGET_2ND_ARCH))\n$(call add_json_str,  DeviceSecondaryArchVariant,        $(TARGET_2ND_ARCH_VARIANT))\n$(call add_json_str,  DeviceSecondaryCpuVariant,         $(TARGET_2ND_CPU_VARIANT))\n$(call add_json_list, DeviceSecondaryAbi,                $(TARGET_2ND_CPU_ABI) $(TARGET_2ND_CPU_ABI2))\n\n$(call add_json_bool, Aml_abis,                          $(if $(filter mainline_sdk,$(TARGET_ARCH_SUITE)),true))\n$(call add_json_bool, Ndk_abis,                          $(if $(filter ndk,         $(TARGET_ARCH_SUITE)),true))\n\n$(call add_json_str,  NativeBridgeArch,                  $(TARGET_NATIVE_BRIDGE_ARCH))\n$(call add_json_str,  NativeBridgeArchVariant,           $(TARGET_NATIVE_BRIDGE_ARCH_VARIANT))\n$(call add_json_str,  NativeBridgeCpuVariant,            $(TARGET_NATIVE_BRIDGE_CPU_VARIANT))\n$(call add_json_list, NativeBridgeAbi,                   $(TARGET_NATIVE_BRIDGE_ABI))\n$(call add_json_str,  NativeBridgeRelativePath,          $(TARGET_NATIVE_BRIDGE_RELATIVE_PATH))\n\n$(call add_json_str,  NativeBridgeSecondaryArch,         $(TARGET_NATIVE_BRIDGE_2ND_ARCH))\n$(call add_json_str,  NativeBridgeSecondaryArchVariant,  $(TARGET_NATIVE_BRIDGE_2ND_ARCH_VARIANT))\n$(call add_json_str,  NativeBridgeSecondaryCpuVariant,   $(TARGET_NATIVE_BRIDGE_2ND_CPU_VARIANT))\n$(call add_json_list, NativeBridgeSecondaryAbi,          $(TARGET_NATIVE_BRIDGE_2ND_ABI))\n$(call add_json_str,  NativeBridgeSecondaryRelativePath, $(TARGET_NATIVE_BRIDGE_2ND_RELATIVE_PATH))\n\n$(call add_json_str,  HostArch,                          $(HOST_ARCH))\n$(call add_json_str,  HostSecondaryArch,                 $(HOST_2ND_ARCH))\n$(call add_json_bool, HostStaticBinaries,                $(BUILD_HOST_static))\n$(call add_json_bool, HostMusl,                          $(USE_HOST_MUSL))\n\n$(call add_json_str,  CrossHost,                         $(HOST_CROSS_OS))\n$(call add_json_str,  CrossHostArch,                     $(HOST_CROSS_ARCH))\n$(call add_json_str,  CrossHostSecondaryArch,            $(HOST_CROSS_2ND_ARCH))\n\n$(call add_json_list, DeviceResourceOverlays,            $(DEVICE_PACKAGE_OVERLAYS))\n$(call add_json_list, ProductResourceOverlays,           $(PRODUCT_PACKAGE_OVERLAYS))\n$(call add_json_list, EnforceRROTargets,                 $(PRODUCT_ENFORCE_RRO_TARGETS))\n$(call add_json_list, EnforceRROExcludedOverlays,        $(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS))\n\n$(call add_json_str,  AAPTCharacteristics,               $(TARGET_AAPT_CHARACTERISTICS))\n$(call add_json_list, AAPTConfig,                        $(PRODUCT_AAPT_CONFIG))\n$(call add_json_str,  AAPTPreferredConfig,               $(PRODUCT_AAPT_PREF_CONFIG))\n$(call add_json_list, AAPTPrebuiltDPI,                   $(PRODUCT_AAPT_PREBUILT_DPI))\n\n$(call add_json_str,  DefaultAppCertificate,             $(PRODUCT_DEFAULT_DEV_CERTIFICATE))\n$(call add_json_list, ExtraOtaKeys,                      $(PRODUCT_EXTRA_OTA_KEYS))\n$(call add_json_list, ExtraOtaRecoveryKeys,              $(PRODUCT_EXTRA_RECOVERY_KEYS))\n$(call add_json_str,  MainlineSepolicyDevCertificates,   $(MAINLINE_SEPOLICY_DEV_CERTIFICATES))\n\n$(call add_json_str,  AppsDefaultVersionName,            $(APPS_DEFAULT_VERSION_NAME))\n\n$(call add_json_list, SanitizeHost,                      $(SANITIZE_HOST))\n$(call add_json_list, SanitizeDevice,                    $(SANITIZE_TARGET))\n$(call add_json_list, SanitizeDeviceDiag,                $(SANITIZE_TARGET_DIAG))\n$(call add_json_list, SanitizeDeviceArch,                $(SANITIZE_TARGET_ARCH))\n\n$(call add_json_bool, Safestack,                         $(filter true,$(USE_SAFESTACK)))\n$(call add_json_bool, EnableCFI,                         $(call invert_bool,$(filter false,$(ENABLE_CFI))))\n$(call add_json_list, CFIExcludePaths,                   $(CFI_EXCLUDE_PATHS) $(PRODUCT_CFI_EXCLUDE_PATHS))\n$(call add_json_list, CFIIncludePaths,                   $(CFI_INCLUDE_PATHS) $(PRODUCT_CFI_INCLUDE_PATHS))\n$(call add_json_list, IntegerOverflowExcludePaths,       $(INTEGER_OVERFLOW_EXCLUDE_PATHS) $(PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS))\n$(call add_json_list, HWASanIncludePaths,                $(HWASAN_INCLUDE_PATHS) $(PRODUCT_HWASAN_INCLUDE_PATHS))\n$(call add_json_list, HWASanExcludePaths,                $(HWASAN_EXCLUDE_PATHS) $(PRODUCT_HWASAN_EXCLUDE_PATHS))\n\n$(call add_json_list, MemtagHeapExcludePaths,            $(MEMTAG_HEAP_EXCLUDE_PATHS) $(PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS))\n$(call add_json_list, MemtagHeapAsyncIncludePaths,       $(MEMTAG_HEAP_ASYNC_INCLUDE_PATHS) $(PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS) $(if $(filter true,$(PRODUCT_MEMTAG_HEAP_SKIP_DEFAULT_PATHS)),,$(PRODUCT_MEMTAG_HEAP_ASYNC_DEFAULT_INCLUDE_PATHS)))\n$(call add_json_list, MemtagHeapSyncIncludePaths,       $(MEMTAG_HEAP_SYNC_INCLUDE_PATHS) $(PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS) $(if $(filter true,$(PRODUCT_MEMTAG_HEAP_SKIP_DEFAULT_PATHS)),,$(PRODUCT_MEMTAG_HEAP_SYNC_DEFAULT_INCLUDE_PATHS)))\n\n$(call add_json_bool, DisableScudo,                      $(filter true,$(PRODUCT_DISABLE_SCUDO)))\n\n$(call add_json_bool, ClangTidy,                         $(filter 1 true,$(WITH_TIDY)))\n$(call add_json_str,  TidyChecks,                        $(WITH_TIDY_CHECKS))\n\n$(call add_json_list, JavaCoveragePaths,                 $(JAVA_COVERAGE_PATHS))\n$(call add_json_list, JavaCoverageExcludePaths,          $(JAVA_COVERAGE_EXCLUDE_PATHS))\n\n$(call add_json_bool, GcovCoverage,                      $(filter true,$(NATIVE_COVERAGE)))\n$(call add_json_bool, ClangCoverage,                     $(filter true,$(CLANG_COVERAGE)))\n$(call add_json_bool, ClangCoverageContinuousMode,       $(filter true,$(CLANG_COVERAGE_CONTINUOUS_MODE)))\n$(call add_json_list, NativeCoveragePaths,               $(NATIVE_COVERAGE_PATHS))\n$(call add_json_list, NativeCoverageExcludePaths,        $(NATIVE_COVERAGE_EXCLUDE_PATHS))\n\n$(call add_json_bool, ArtUseReadBarrier,                 $(call invert_bool,$(filter false,$(PRODUCT_ART_USE_READ_BARRIER))))\n$(call add_json_str,  BtConfigIncludeDir,                $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR))\n$(call add_json_list, DeviceKernelHeaders,               $(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) $(TARGET_PRODUCT_KERNEL_HEADERS))\n$(call add_json_str,  VendorApiLevel,                    $(BOARD_API_LEVEL))\n$(call add_json_str,  VendorApiLevelPropOverride,        $(BOARD_API_LEVEL_PROP_OVERRIDE))\n$(call add_json_list, ExtraVndkVersions,                 $(PRODUCT_EXTRA_VNDK_VERSIONS))\n$(call add_json_list, DeviceSystemSdkVersions,           $(BOARD_SYSTEMSDK_VERSIONS))\n$(call add_json_list, Platform_systemsdk_versions,       $(PLATFORM_SYSTEMSDK_VERSIONS))\n$(call add_json_bool, Malloc_low_memory,                 $(findstring true,$(MALLOC_SVELTE) $(MALLOC_LOW_MEMORY)))\n$(call add_json_bool, Malloc_zero_contents,              $(call invert_bool,$(filter false,$(MALLOC_ZERO_CONTENTS))))\n$(call add_json_bool, Malloc_pattern_fill_contents,      $(MALLOC_PATTERN_FILL_CONTENTS))\n$(call add_json_str,  Override_rs_driver,                $(OVERRIDE_RS_DRIVER))\n$(call add_json_str,  DeviceMaxPageSizeSupported,        $(TARGET_MAX_PAGE_SIZE_SUPPORTED))\n$(call add_json_bool, DeviceNoBionicPageSizeMacro,       $(filter true,$(TARGET_NO_BIONIC_PAGE_SIZE_MACRO)))\n\n$(call add_json_bool, UncompressPrivAppDex,              $(call invert_bool,$(filter true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS))))\n$(call add_json_list, ModulesLoadedByPrivilegedModules,  $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))\n\n$(call add_json_list, BootJars,                          $(PRODUCT_BOOT_JARS))\n$(call add_json_list, ApexBootJars,                      $(filter-out $(APEX_BOOT_JARS_EXCLUDED), $(PRODUCT_APEX_BOOT_JARS)))\n\n$(call add_json_map,  BuildFlags)\n$(foreach flag,$(_ALL_RELEASE_FLAGS),\\\n  $(call add_json_str,$(flag),$(_ALL_RELEASE_FLAGS.$(flag).VALUE)))\n$(call end_json_map)\n$(call add_json_map,  BuildFlagTypes)\n$(foreach flag,$(_ALL_RELEASE_FLAGS),\\\n  $(call add_json_str,$(flag),$(_ALL_RELEASE_FLAGS.$(flag).TYPE)))\n$(call end_json_map)\n\n$(call add_json_bool, MultitreeUpdateMeta,               $(filter true,$(TARGET_MULTITREE_UPDATE_META)))\n\n$(call add_json_bool, Treble_linker_namespaces,          $(filter true,$(PRODUCT_TREBLE_LINKER_NAMESPACES)))\n$(call add_json_bool, Enforce_vintf_manifest,            $(filter true,$(PRODUCT_ENFORCE_VINTF_MANIFEST)))\n\n$(call add_json_bool, Uml,                               $(filter true,$(TARGET_USER_MODE_LINUX)))\n$(call add_json_str,  VendorPath,                        $(TARGET_COPY_OUT_VENDOR))\n$(call add_json_str,  VendorDlkmPath,                    $(TARGET_COPY_OUT_VENDOR_DLKM))\n$(call add_json_bool, BuildingVendorImage,               $(BUILDING_VENDOR_IMAGE))\n$(call add_json_str,  OdmPath,                           $(TARGET_COPY_OUT_ODM))\n$(call add_json_bool, BuildingOdmImage,                  $(BUILDING_ODM_IMAGE))\n$(call add_json_str,  OdmDlkmPath,                       $(TARGET_COPY_OUT_ODM_DLKM))\n$(call add_json_str,  ProductPath,                       $(TARGET_COPY_OUT_PRODUCT))\n$(call add_json_bool, BuildingProductImage,              $(BUILDING_PRODUCT_IMAGE))\n$(call add_json_str,  SystemExtPath,                     $(TARGET_COPY_OUT_SYSTEM_EXT))\n$(call add_json_str,  SystemDlkmPath,                    $(TARGET_COPY_OUT_SYSTEM_DLKM))\n$(call add_json_str,  OemPath,                           $(TARGET_COPY_OUT_OEM))\n$(call add_json_bool, MinimizeJavaDebugInfo,             $(filter true,$(PRODUCT_MINIMIZE_JAVA_DEBUG_INFO)))\n$(call add_json_str,  RecoveryPath,                      $(TARGET_COPY_OUT_RECOVERY))\n$(call add_json_bool, BuildingRecoveryImage,             $(BUILDING_RECOVERY_IMAGE))\n$(call add_json_str,  UserdataPath,                      $(TARGET_COPY_OUT_DATA))\n$(call add_json_bool, BuildingUserdataImage,             $(BUILDING_USERDATA_IMAGE))\n\n$(call add_json_bool, UseGoma,                           $(filter-out false,$(USE_GOMA)))\n$(call add_json_bool, UseRBE,                            $(filter-out false,$(USE_RBE)))\n$(call add_json_bool, UseRBEJAVAC,                       $(filter-out false,$(RBE_JAVAC)))\n$(call add_json_bool, UseRBER8,                          $(filter-out false,$(RBE_R8)))\n$(call add_json_bool, UseRBED8,                          $(filter-out false,$(RBE_D8)))\n$(call add_json_bool, Arc,                               $(filter true,$(TARGET_ARC)))\n\n$(call add_json_list, NamespacesToExport,                $(PRODUCT_SOONG_NAMESPACES))\n\n$(call add_json_list, PgoAdditionalProfileDirs,          $(PGO_ADDITIONAL_PROFILE_DIRS))\n\n$(call add_json_list, BoardVendorSepolicyDirs,           $(BOARD_VENDOR_SEPOLICY_DIRS) $(BOARD_SEPOLICY_DIRS))\n$(call add_json_list, BoardOdmSepolicyDirs,              $(BOARD_ODM_SEPOLICY_DIRS))\n$(call add_json_list, SystemExtPublicSepolicyDirs,       $(SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS))\n$(call add_json_list, SystemExtPrivateSepolicyDirs,      $(SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS))\n$(call add_json_list, BoardSepolicyM4Defs,               $(BOARD_SEPOLICY_M4DEFS))\n$(call add_json_str,  BoardSepolicyVers,                 $(BOARD_SEPOLICY_VERS))\n$(call add_json_str,  SystemExtSepolicyPrebuiltApiDir,   $(BOARD_SYSTEM_EXT_PREBUILT_DIR))\n$(call add_json_str,  ProductSepolicyPrebuiltApiDir,     $(BOARD_PRODUCT_PREBUILT_DIR))\n$(call add_json_str,  BoardPlatform,                     $(TARGET_BOARD_PLATFORM))\n\n$(call add_json_str,  PlatformSepolicyVersion,           $(PLATFORM_SEPOLICY_VERSION))\n$(call add_json_list, PlatformSepolicyCompatVersions,    $(PLATFORM_SEPOLICY_COMPAT_VERSIONS))\n\n$(call add_json_bool, ForceApexSymlinkOptimization,      $(filter true,$(TARGET_FORCE_APEX_SYMLINK_OPTIMIZATION)))\n\n$(call add_json_str,  DexpreoptGlobalConfig,             $(DEX_PREOPT_CONFIG))\n\n$(call add_json_bool, WithDexpreopt,                     $(filter true,$(WITH_DEXPREOPT)))\n\n$(call add_json_list, ManifestPackageNameOverrides,      $(PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES))\n$(call add_json_list, PackageNameOverrides,              $(PRODUCT_PACKAGE_NAME_OVERRIDES))\n$(call add_json_list, CertificateOverrides,              $(PRODUCT_CERTIFICATE_OVERRIDES))\n$(call add_json_list, ConfiguredJarLocationOverrides,    $(PRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES))\n\n$(call add_json_str, ApexGlobalMinSdkVersionOverride,    $(APEX_GLOBAL_MIN_SDK_VERSION_OVERRIDE))\n\n$(call add_json_bool, EnforceSystemCertificate,          $(filter true,$(ENFORCE_SYSTEM_CERTIFICATE)))\n$(call add_json_list, EnforceSystemCertificateAllowList, $(ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST))\n\n$(call add_json_list, ProductHiddenAPIStubs,             $(PRODUCT_HIDDENAPI_STUBS))\n$(call add_json_list, ProductHiddenAPIStubsSystem,       $(PRODUCT_HIDDENAPI_STUBS_SYSTEM))\n$(call add_json_list, ProductHiddenAPIStubsTest,         $(PRODUCT_HIDDENAPI_STUBS_TEST))\n\n$(call add_json_list, ProductPublicSepolicyDirs,         $(PRODUCT_PUBLIC_SEPOLICY_DIRS))\n$(call add_json_list, ProductPrivateSepolicyDirs,        $(PRODUCT_PRIVATE_SEPOLICY_DIRS))\n\n$(call add_json_list, TargetFSConfigGen,                 $(TARGET_FS_CONFIG_GEN))\n\n# Although USE_SOONG_DEFINED_SYSTEM_IMAGE determines whether to use the system image specified by\n# PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE, PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE is still used to compare\n# installed files between make and soong, regardless of the USE_SOONG_DEFINED_SYSTEM_IMAGE setting.\n$(call add_json_bool, UseSoongSystemImage,               $(filter true,$(USE_SOONG_DEFINED_SYSTEM_IMAGE)))\n$(call add_json_str,  ProductSoongDefinedSystemImage,    $(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE))\n\n$(call add_json_bool, UseSoongNoticeXML, $(filter true,$(PRODUCT_USE_SOONG_NOTICE_XML)))\n\n$(call add_json_map, VendorVars)\n$(foreach namespace,$(sort $(SOONG_CONFIG_NAMESPACES)),\\\n  $(call add_json_map, $(namespace))\\\n  $(foreach key,$(sort $(SOONG_CONFIG_$(namespace))),\\\n    $(call add_json_str,$(key),$(subst \",\\\",$(SOONG_CONFIG_$(namespace)_$(key)))))\\\n  $(call end_json_map))\n$(call end_json_map)\n\n# Add the types of the variables in VendorVars. Since this is much newer\n# than VendorVars, which has a history of just using string values for everything,\n# variables are assumed to be strings by default. For strings, SOONG_CONFIG_TYPE_*\n# will not be set, and they will not have an entry in the VendorVarTypes map.\n$(call add_json_map, VendorVarTypes)\n$(foreach namespace,$(sort $(SOONG_CONFIG_NAMESPACES)),\\\n  $(call add_json_map, $(namespace))\\\n  $(foreach key,$(sort $(SOONG_CONFIG_$(namespace))),\\\n    $(if $(SOONG_CONFIG_TYPE_$(namespace)_$(key)),$(call add_json_str,$(key),$(subst \",\\\",$(SOONG_CONFIG_TYPE_$(namespace)_$(key))))))\\\n  $(call end_json_map))\n$(call end_json_map)\n\n$(call add_json_bool, EnforceProductPartitionInterface,  $(filter true,$(PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE)))\n$(call add_json_str,  DeviceCurrentApiLevelForVendorModules,  $(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES))\n\n$(call add_json_bool, CompressedApex, $(filter true,$(PRODUCT_COMPRESSED_APEX)))\n$(call add_json_str, DefaultApexPayloadType, $(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE))\n\n$(call add_json_bool, BoardUsesRecoveryAsBoot, $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)))\n\n$(call add_json_list, BoardKernelBinaries, $(BOARD_KERNEL_BINARIES))\n$(call add_json_list, BoardKernelModuleInterfaceVersions, $(BOARD_KERNEL_MODULE_INTERFACE_VERSIONS))\n\n$(call add_json_bool, BoardMoveRecoveryResourcesToVendorBoot, $(filter true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)))\n$(call add_json_str,  PrebuiltHiddenApiDir, $(BOARD_PREBUILT_HIDDENAPI_DIR))\n\n$(call add_json_str,  Shipping_api_level, $(PRODUCT_SHIPPING_API_LEVEL))\n\n$(call add_json_list, BuildBrokenPluginValidation,         $(BUILD_BROKEN_PLUGIN_VALIDATION))\n$(call add_json_bool, BuildBrokenClangProperty,            $(filter true,$(BUILD_BROKEN_CLANG_PROPERTY)))\n$(call add_json_bool, BuildBrokenClangAsFlags,             $(filter true,$(BUILD_BROKEN_CLANG_ASFLAGS)))\n$(call add_json_bool, BuildBrokenClangCFlags,              $(filter true,$(BUILD_BROKEN_CLANG_CFLAGS)))\n# Use the value of GENRULE_SANDBOXING if set, otherwise use the inverse of BUILD_BROKEN_GENRULE_SANDBOXING\n$(call add_json_bool, GenruleSandboxing,                   $(if $(GENRULE_SANDBOXING),$(filter true,$(GENRULE_SANDBOXING)),$(if $(filter true,$(BUILD_BROKEN_GENRULE_SANDBOXING)),,true)))\n$(call add_json_bool, BuildBrokenEnforceSyspropOwner,      $(filter true,$(BUILD_BROKEN_ENFORCE_SYSPROP_OWNER)))\n$(call add_json_bool, BuildBrokenTrebleSyspropNeverallow,  $(filter true,$(BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW)))\n$(call add_json_bool, BuildBrokenVendorPropertyNamespace,  $(filter true,$(BUILD_BROKEN_VENDOR_PROPERTY_NAMESPACE)))\n$(call add_json_bool, BuildBrokenIncorrectPartitionImages, $(filter true,$(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES)))\n$(call add_json_list, BuildBrokenInputDirModules,          $(BUILD_BROKEN_INPUT_DIR_MODULES))\n$(call add_json_bool, BuildBrokenDontCheckSystemSdk,       $(filter true,$(BUILD_BROKEN_DONT_CHECK_SYSTEMSDK)))\n$(call add_json_bool, BuildBrokenDupSysprop,               $(filter true,$(BUILD_BROKEN_DUP_SYSPROP)))\n\n$(call add_json_list, BuildWarningBadOptionalUsesLibsAllowlist,    $(BUILD_WARNING_BAD_OPTIONAL_USES_LIBS_ALLOWLIST))\n\n$(call add_json_bool, BuildDebugfsRestrictionsEnabled, $(filter true,$(PRODUCT_SET_DEBUGFS_RESTRICTIONS)))\n\n$(call add_json_bool, RequiresInsecureExecmemForSwiftshader, $(filter true,$(PRODUCT_REQUIRES_INSECURE_EXECMEM_FOR_SWIFTSHADER)))\n\n$(call add_json_bool, SelinuxIgnoreNeverallows, $(filter true,$(SELINUX_IGNORE_NEVERALLOWS)))\n\n$(call add_json_list, SepolicyFreezeTestExtraDirs,         $(SEPOLICY_FREEZE_TEST_EXTRA_DIRS))\n$(call add_json_list, SepolicyFreezeTestExtraPrebuiltDirs, $(SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS))\n\n$(call add_json_bool, GenerateAidlNdkPlatformBackend, $(filter true,$(NEED_AIDL_NDK_PLATFORM_BACKEND)))\n\n$(call add_json_bool, IgnorePrefer32OnDevice, $(filter true,$(IGNORE_PREFER32_ON_DEVICE)))\n\n$(call add_json_list, SourceRootDirs,             $(PRODUCT_SOURCE_ROOT_DIRS))\n\n$(call add_json_list, AfdoProfiles,                $(ALL_AFDO_PROFILES))\n\n$(call add_json_str,  ProductManufacturer, $(PRODUCT_MANUFACTURER))\n$(call add_json_str,  ProductBrand,        $(PRODUCT_BRAND))\n$(call add_json_str,  ProductDevice,       $(PRODUCT_DEVICE))\n$(call add_json_str,  ProductModel,        $(PRODUCT_MODEL))\n\n$(call add_json_str, ReleaseVersion,    $(_RELEASE_VERSION))\n$(call add_json_list, ReleaseAconfigValueSets,    $(RELEASE_ACONFIG_VALUE_SETS))\n$(call add_json_str, ReleaseAconfigFlagDefaultPermission,    $(RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION))\n\n$(call add_json_bool, ReleaseDefaultModuleBuildFromSource,   $(RELEASE_DEFAULT_MODULE_BUILD_FROM_SOURCE))\n\n$(call add_json_bool, CheckVendorSeappViolations, $(filter true,$(CHECK_VENDOR_SEAPP_VIOLATIONS)))\n\n$(call add_json_bool, BuildIgnoreApexContributionContents, $(PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS))\n\n$(call add_json_bool, BuildFromSourceStub, $(findstring true,$(PRODUCT_BUILD_FROM_SOURCE_STUB) $(BUILD_FROM_SOURCE_STUB)))\n\n$(call add_json_bool, HiddenapiExportableStubs, $(filter true,$(PRODUCT_HIDDEN_API_EXPORTABLE_STUBS)))\n\n$(call add_json_bool, ExportRuntimeApis, $(filter true,$(PRODUCT_EXPORT_RUNTIME_APIS)))\n\n$(call add_json_str, AconfigContainerValidation, $(ACONFIG_CONTAINER_VALIDATION))\n\n$(call add_json_list, ProductLocales, $(subst _,-,$(PRODUCT_LOCALES)))\n\n$(call add_json_list, ProductDefaultWifiChannels, $(PRODUCT_DEFAULT_WIFI_CHANNELS))\n\n$(call add_json_bool, BoardUseVbmetaDigestInFingerprint, $(filter true,$(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT)))\n\n$(call add_json_list, OemProperties, $(PRODUCT_OEM_PROPERTIES))\n\n$(call add_json_list, SystemPropFiles, $(TARGET_SYSTEM_PROP))\n$(call add_json_list, SystemExtPropFiles, $(TARGET_SYSTEM_EXT_PROP))\n$(call add_json_list, ProductPropFiles, $(TARGET_PRODUCT_PROP))\n$(call add_json_list, OdmPropFiles, $(TARGET_ODM_PROP))\n$(call add_json_list, VendorPropFiles, $(TARGET_VENDOR_PROP))\n\n# Do not set ArtTargetIncludeDebugBuild into any value if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD is not set,\n# to have the same behavior from runtime_libart.mk.\nifneq ($(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD),)\n$(call add_json_bool, ArtTargetIncludeDebugBuild, $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD))\nendif\n\n_config_enable_uffd_gc := \\\n  $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default)\n$(call add_json_str, EnableUffdGc, $(_config_enable_uffd_gc))\n_config_enable_uffd_gc :=\n$(call add_json_str, BoardKernelVersion, $(BOARD_KERNEL_VERSION))\n\n$(call add_json_list, DeviceFrameworkCompatibilityMatrixFile, $(DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE))\n$(call add_json_list, DeviceProductCompatibilityMatrixFile, $(DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE))\n$(call add_json_list, BoardAvbSystemAddHashtreeFooterArgs, $(BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS))\n$(call add_json_bool, BoardAvbEnable, $(filter true,$(BOARD_AVB_ENABLE)))\n\n$(call add_json_str, AdbKeys, $(PRODUCT_ADB_KEYS))\n\n$(call add_json_map, PartitionVarsForSoongMigrationOnlyDoNotUse)\n  $(call add_json_str,  ProductDirectory,    $(dir $(INTERNAL_PRODUCT)))\n\n  $(call add_json_map,PartitionQualifiedVariables)\n  $(foreach image_type,INIT_BOOT BOOT VENDOR_BOOT SYSTEM VENDOR CACHE USERDATA PRODUCT SYSTEM_EXT OEM ODM VENDOR_DLKM ODM_DLKM SYSTEM_DLKM VBMETA VBMETA_SYSTEM VBMETA_SYSTEM_DLKM VBMETA_VENDOR_DLKM, \\\n    $(call add_json_map,$(call to-lower,$(image_type))) \\\n    $(call add_json_bool, BuildingImage, $(filter true,$(BUILDING_$(image_type)_IMAGE))) \\\n    $(call add_json_bool, PrebuiltImage, $(filter true,$(BOARD_PREBUILT_$(image_type)IMAGE))) \\\n    $(call add_json_str, BoardErofsCompressor, $(BOARD_$(image_type)IMAGE_EROFS_COMPRESSOR)) \\\n    $(call add_json_str, BoardErofsCompressHints, $(BOARD_$(image_type)IMAGE_EROFS_COMPRESS_HINTS)) \\\n    $(call add_json_str, BoardErofsPclusterSize, $(BOARD_$(image_type)IMAGE_EROFS_PCLUSTER_SIZE)) \\\n    $(call add_json_str, BoardExtfsInodeCount, $(BOARD_$(image_type)IMAGE_EXTFS_INODE_COUNT)) \\\n    $(call add_json_str, BoardExtfsRsvPct, $(BOARD_$(image_type)IMAGE_EXTFS_RSV_PCT)) \\\n    $(call add_json_str, BoardF2fsSloadCompressFlags, $(BOARD_$(image_type)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS)) \\\n    $(call add_json_str, BoardFileSystemCompress, $(BOARD_$(image_type)IMAGE_FILE_SYSTEM_COMPRESS)) \\\n    $(call add_json_str, BoardFileSystemType, $(BOARD_$(image_type)IMAGE_FILE_SYSTEM_TYPE)) \\\n    $(call add_json_str, BoardJournalSize, $(BOARD_$(image_type)IMAGE_JOURNAL_SIZE)) \\\n    $(call add_json_str, BoardPartitionReservedSize, $(BOARD_$(image_type)IMAGE_PARTITION_RESERVED_SIZE)) \\\n    $(call add_json_str, BoardPartitionSize, $(BOARD_$(image_type)IMAGE_PARTITION_SIZE)) \\\n    $(call add_json_str, BoardSquashfsBlockSize, $(BOARD_$(image_type)IMAGE_SQUASHFS_BLOCK_SIZE)) \\\n    $(call add_json_str, BoardSquashfsCompressor, $(BOARD_$(image_type)IMAGE_SQUASHFS_COMPRESSOR)) \\\n    $(call add_json_str, BoardSquashfsCompressorOpt, $(BOARD_$(image_type)IMAGE_SQUASHFS_COMPRESSOR_OPT)) \\\n    $(call add_json_str, BoardSquashfsDisable4kAlign, $(BOARD_$(image_type)IMAGE_SQUASHFS_DISABLE_4K_ALIGN)) \\\n    $(call add_json_str, BoardAvbKeyPath, $(BOARD_AVB_$(image_type)_KEY_PATH)) \\\n    $(call add_json_str, BoardAvbAlgorithm, $(BOARD_AVB_$(image_type)_ALGORITHM)) \\\n    $(call add_json_str, BoardAvbRollbackIndex, $(BOARD_AVB_$(image_type)_ROLLBACK_INDEX)) \\\n    $(call add_json_str, BoardAvbRollbackIndexLocation, $(BOARD_AVB_$(image_type)_ROLLBACK_INDEX_LOCATION)) \\\n    $(call add_json_str, BoardAvbAddHashtreeFooterArgs, $(BOARD_AVB_$(image_type)_ADD_HASHTREE_FOOTER_ARGS)) \\\n    $(call add_json_str, ProductBaseFsPath, $(PRODUCT_$(image_type)_BASE_FS_PATH)) \\\n    $(call add_json_str, ProductHeadroom, $(PRODUCT_$(image_type)_HEADROOM)) \\\n    $(call add_json_str, ProductVerityPartition, $(PRODUCT_$(image_type)_VERITY_PARTITION)) \\\n    $(call end_json_map) \\\n  )\n  $(call end_json_map)\n\n  $(call add_json_bool, TargetUserimagesUseExt2, $(filter true,$(TARGET_USERIMAGES_USE_EXT2)))\n  $(call add_json_bool, TargetUserimagesUseExt3, $(filter true,$(TARGET_USERIMAGES_USE_EXT3)))\n  $(call add_json_bool, TargetUserimagesUseExt4, $(filter true,$(TARGET_USERIMAGES_USE_EXT4)))\n\n  $(call add_json_bool, TargetUserimagesSparseExtDisabled, $(filter true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED)))\n  $(call add_json_bool, TargetUserimagesSparseErofsDisabled, $(filter true,$(TARGET_USERIMAGES_SPARSE_EROFS_DISABLED)))\n  $(call add_json_bool, TargetUserimagesSparseSquashfsDisabled, $(filter true,$(TARGET_USERIMAGES_SPARSE_SQUASHFS_DISABLED)))\n  $(call add_json_bool, TargetUserimagesSparseF2fsDisabled, $(filter true,$(TARGET_USERIMAGES_SPARSE_F2FS_DISABLED)))\n\n  $(call add_json_str, BoardErofsCompressor, $(BOARD_EROFS_COMPRESSOR))\n  $(call add_json_str, BoardErofsCompressorHints, $(BOARD_EROFS_COMPRESS_HINTS))\n  $(call add_json_str, BoardErofsPclusterSize, $(BOARD_EROFS_PCLUSTER_SIZE))\n  $(call add_json_str, BoardErofsShareDupBlocks, $(BOARD_EROFS_SHARE_DUP_BLOCKS))\n  $(call add_json_str, BoardErofsUseLegacyCompression, $(BOARD_EROFS_USE_LEGACY_COMPRESSION))\n  $(call add_json_str, BoardExt4ShareDupBlocks, $(BOARD_EXT4_SHARE_DUP_BLOCKS))\n  $(call add_json_str, BoardFlashLogicalBlockSize, $(BOARD_FLASH_LOGICAL_BLOCK_SIZE))\n  $(call add_json_str, BoardFlashEraseBlockSize, $(BOARD_FLASH_ERASE_BLOCK_SIZE))\n  $(call add_json_bool, BuildingVbmetaImage, $(BUILDING_VBMETA_IMAGE))\n\n  # boot image stuff\n  $(call add_json_bool, BuildingRamdiskImage, $(filter true,$(BUILDING_RAMDISK_IMAGE)))\n  $(call add_json_bool, ProductBuildBootImage, $(filter true,$(PRODUCT_BUILD_BOOT_IMAGE)))\n  $(call add_json_str, ProductBuildVendorBootImage, $(PRODUCT_BUILD_VENDOR_BOOT_IMAGE))\n  $(call add_json_bool, ProductBuildInitBootImage, $(filter true,$(PRODUCT_BUILD_INIT_BOOT_IMAGE)))\n  $(call add_json_bool, BoardUsesRecoveryAsBoot, $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)))\n  $(call add_json_str, BoardPrebuiltBootimage, $(BOARD_PREBUILT_BOOT_IMAGE))\n  $(call add_json_str, BoardPrebuiltInitBootimage, $(BOARD_PREBUILT_INIT_BOOT_IMAGE))\n  $(call add_json_str, BoardBootimagePartitionSize, $(BOARD_BOOTIMAGE_PARTITION_SIZE))\n  $(call add_json_str, BoardVendorBootimagePartitionSize, $(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))\n  $(call add_json_str, BoardInitBootimagePartitionSize, $(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE))\n  $(call add_json_str, BoardBootHeaderVersion, $(BOARD_BOOT_HEADER_VERSION))\n  $(call add_json_str, TargetKernelPath, $(TARGET_KERNEL_PATH))\n  $(call add_json_bool, BoardUsesGenericKernelImage, $(BOARD_USES_GENERIC_KERNEL_IMAGE))\n  $(call add_json_str, BootSecurityPatch, $(BOOT_SECURITY_PATCH))\n  $(call add_json_str, InitBootSecurityPatch, $(INIT_BOOT_SECURITY_PATCH))\n  $(call add_json_str, VendorSecurityPatch, $(VENDOR_SECURITY_PATCH))\n  $(call add_json_str, OdmSecurityPatch, $(ODM_SECURITY_PATCH))\n  $(call add_json_str, SystemDlkmSecurityPatch, $(SYSTEM_DLKM_SECURITY_PATCH))\n  $(call add_json_str, VendorDlkmSecurityPatch, $(VENDOR_DLKM_SECURITY_PATCH))\n  $(call add_json_str, OdmDlkmSecurityPatch, $(ODM_DLKM_SECURITY_PATCH))\n  $(call add_json_bool, BoardIncludeDtbInBootimg, $(BOARD_INCLUDE_DTB_IN_BOOTIMG))\n  $(call add_json_list, InternalKernelCmdline, $(INTERNAL_KERNEL_CMDLINE))\n  $(call add_json_list, InternalBootconfig, $(INTERNAL_BOOTCONFIG))\n  $(call add_json_str, InternalBootconfigFile, $(INTERNAL_BOOTCONFIG_FILE))\n\n  $(call add_json_bool, BuildingSystemOtherImage, $(BUILDING_SYSTEM_OTHER_IMAGE))\n\n  # super image stuff\n  $(call add_json_bool, ProductUseDynamicPartitions, $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)))\n  $(call add_json_bool, ProductRetrofitDynamicPartitions, $(filter true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)))\n  $(call add_json_bool, ProductBuildSuperPartition, $(filter true,$(PRODUCT_BUILD_SUPER_PARTITION)))\n  $(call add_json_bool, BuildingSuperEmptyImage, $(filter true,$(BUILDING_SUPER_EMPTY_IMAGE)))\n  $(call add_json_str, BoardSuperPartitionSize, $(BOARD_SUPER_PARTITION_SIZE))\n  $(call add_json_str, BoardSuperPartitionMetadataDevice, $(BOARD_SUPER_PARTITION_METADATA_DEVICE))\n  $(call add_json_list, BoardSuperPartitionBlockDevices, $(BOARD_SUPER_PARTITION_BLOCK_DEVICES))\n  $(call add_json_map, BoardSuperPartitionGroups)\n    $(foreach group, $(BOARD_SUPER_PARTITION_GROUPS), \\\n      $(call add_json_map, $(group)) \\\n        $(call add_json_str, GroupSize, $(BOARD_$(call to-upper,$(group))_SIZE)) \\\n        $(if $(BOARD_$(call to-upper,$(group))_PARTITION_LIST), \\\n          $(call add_json_list, PartitionList, $(BOARD_$(call to-upper,$(group))_PARTITION_LIST))) \\\n      $(call end_json_map))\n    $(call end_json_map)\n  $(call add_json_bool, ProductVirtualAbOta, $(filter true,$(PRODUCT_VIRTUAL_AB_OTA)))\n  $(call add_json_bool, ProductVirtualAbOtaRetrofit, $(filter true,$(PRODUCT_VIRTUAL_AB_OTA_RETROFIT)))\n  $(call add_json_bool, ProductVirtualAbCompression, $(filter true,$(PRODUCT_VIRTUAL_AB_COMPRESSION)))\n  $(call add_json_str, ProductVirtualAbCompressionMethod, $(PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD))\n  $(call add_json_str, ProductVirtualAbCompressionFactor, $(PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR))\n  $(call add_json_str, ProductVirtualAbCowVersion, $(PRODUCT_VIRTUAL_AB_COW_VERSION))\n  $(call add_json_bool, AbOtaUpdater, $(filter true,$(AB_OTA_UPDATER)))\n  $(call add_json_list, AbOtaPartitions, $(AB_OTA_PARTITIONS))\n  $(call add_json_list, AbOtaKeys, $(PRODUCT_OTA_PUBLIC_KEYS))\n  $(call add_json_list, AbOtaPostInstallConfig, $(AB_OTA_POSTINSTALL_CONFIG))\n  $(call add_json_bool, BoardSuperImageInUpdatePackage, $(filter true,$(BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE)))\n\n  # Avb (android verified boot) stuff\n  $(call add_json_bool, BoardAvbEnable, $(filter true,$(BOARD_AVB_ENABLE)))\n  $(call add_json_str, BoardAvbAlgorithm, $(BOARD_AVB_ALGORITHM))\n  $(call add_json_str, BoardAvbKeyPath, $(BOARD_AVB_KEY_PATH))\n  $(call add_json_str, BoardAvbRollbackIndex, $(BOARD_AVB_ROLLBACK_INDEX))\n  $(call add_json_map, ChainedVbmetaPartitions)\n  $(foreach partition,system vendor $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),\\\n    $(call add_json_map, $(partition)) \\\n      $(call add_json_list,Partitions,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition)))) \\\n      $(call add_json_str,Key,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_KEY_PATH)) \\\n      $(call add_json_str,Algorithm,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ALGORITHM)) \\\n      $(call add_json_str,RollbackIndex,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX)) \\\n      $(call add_json_str,RollbackIndexLocation,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)) \\\n    $(call end_json_map))\n  $(call end_json_map)\n\n  $(call add_json_bool, ProductUseDynamicPartitionSize, $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITION_SIZE)))\n  $(call add_json_bool, CopyImagesForTargetFilesZip, $(filter true,$(COPY_IMAGES_FOR_TARGET_FILES_ZIP)))\n\n  $(call add_json_list, ProductPackages, $(PRODUCT_PACKAGES))\n  $(call add_json_list, ProductPackagesDebug, $(PRODUCT_PACKAGES_DEBUG))\n\n  # Used to generate /vendor/linker.config.pb\n  $(call add_json_list, VendorLinkerConfigSrcs, $(PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS))\n  $(call add_json_list, ProductLinkerConfigSrcs, $(PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS))\n\n  # Used to generate _dlkm partitions\n  $(call add_json_bool, BuildingSystemDlkmImage,               $(BUILDING_SYSTEM_DLKM_IMAGE))\n  $(call add_json_list, SystemKernelModules, $(BOARD_SYSTEM_KERNEL_MODULES))\n  $(call add_json_str, SystemKernelBlocklistFile, $(BOARD_SYSTEM_KERNEL_MODULES_BLOCKLIST_FILE))\n  $(call add_json_list, SystemKernelLoadModules, $(BOARD_SYSTEM_KERNEL_MODULES_LOAD))\n  $(call add_json_bool, BuildingVendorDlkmImage,               $(BUILDING_VENDOR_DLKM_IMAGE))\n  $(call add_json_list, VendorKernelModules, $(BOARD_VENDOR_KERNEL_MODULES))\n  $(call add_json_str, VendorKernelBlocklistFile, $(BOARD_VENDOR_KERNEL_MODULES_BLOCKLIST_FILE))\n  $(call add_json_bool, BuildingOdmDlkmImage,               $(BUILDING_ODM_DLKM_IMAGE))\n  $(call add_json_list, OdmKernelModules, $(BOARD_ODM_KERNEL_MODULES))\n  $(call add_json_str, OdmKernelBlocklistFile, $(BOARD_ODM_KERNEL_MODULES_BLOCKLIST_FILE))\n  $(call add_json_list, VendorRamdiskKernelModules, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES))\n  $(call add_json_str, VendorRamdiskKernelBlocklistFile, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_BLOCKLIST_FILE))\n  $(call add_json_list, VendorRamdiskKernelLoadModules, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD))\n  $(call add_json_str, VendorRamdiskKernelOptionsFile, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_OPTIONS_FILE))\n\n  # Used to generate /vendor/build.prop\n  $(call add_json_list, BoardInfoFiles, $(if $(TARGET_BOARD_INFO_FILES),$(TARGET_BOARD_INFO_FILES),$(firstword $(TARGET_BOARD_INFO_FILE) $(wildcard $(TARGET_DEVICE_DIR)/board-info.txt))))\n  $(call add_json_str, BootLoaderBoardName, $(TARGET_BOOTLOADER_BOARD_NAME))\n\n  $(call add_json_list, ProductCopyFiles, $(PRODUCT_COPY_FILES))\n\n  # Used to generate fsv meta\n  $(call add_json_bool, ProductFsverityGenerateMetadata,               $(PRODUCT_FSVERITY_GENERATE_METADATA))\n\n  # Used to generate recovery partition\n  $(call add_json_str, TargetScreenDensity, $(TARGET_SCREEN_DENSITY))\n\n  # Used to generate /recovery/root/build.prop\n  $(call add_json_map, PrivateRecoveryUiProperties)\n    $(call add_json_str, animation_fps, $(TARGET_RECOVERY_UI_ANIMATION_FPS))\n    $(call add_json_str, margin_height, $(TARGET_RECOVERY_UI_MARGIN_HEIGHT))\n    $(call add_json_str, margin_width, $(TARGET_RECOVERY_UI_MARGIN_WIDTH))\n    $(call add_json_str, menu_unusable_rows, $(TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS))\n    $(call add_json_str, progress_bar_baseline, $(TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE))\n    $(call add_json_str, touch_low_threshold, $(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD))\n    $(call add_json_str, touch_high_threshold, $(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD))\n    $(call add_json_str, vr_stereo_offset, $(TARGET_RECOVERY_UI_VR_STEREO_OFFSET))\n    $(call add_json_str, brightness_file, $(TARGET_RECOVERY_UI_BRIGHTNESS_FILE))\n    $(call add_json_str, max_brightness_file, $(TARGET_RECOVERY_UI_MAX_BRIGHTNESS_FILE))\n    $(call add_json_str, brightness_normal_percent, $(TARGET_RECOVERY_UI_BRIGHTNESS_NORMAL))\n    $(call add_json_str, brightness_dimmed_percent, $(TARGET_RECOVERY_UI_BRIGHTNESS_DIMMED))\n  $(call end_json_map)\n\n  $(call add_json_str, PrebuiltBootloader, $(BOARD_PREBUILT_BOOTLOADER))\n\n  # Used to generate userdata partition\n  $(call add_json_str, ProductFsCasefold, $(PRODUCT_FS_CASEFOLD))\n  $(call add_json_str, ProductQuotaProjid, $(PRODUCT_QUOTA_PROJID))\n  $(call add_json_str, ProductFsCompression, $(PRODUCT_FS_COMPRESSION))\n\n  $(call add_json_str, ReleaseToolsExtensionDir, $(firstword $(TARGET_RELEASETOOLS_EXTENSIONS) $($(TARGET_DEVICE_DIR)/../common)))\n\n  $(call add_json_list, BoardPartialOtaUpdatePartitionsList, $(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST))\n  $(call add_json_str, BoardFlashBlockSize, $(BOARD_FLASH_BLOCK_SIZE))\n  $(call add_json_bool, BootloaderInUpdatePackage, $(BOARD_BOOTLOADER_IN_UPDATE_PACKAGE))\n\n  # Fastboot\n  $(call add_json_str, BoardFastbootInfoFile, $(TARGET_BOARD_FASTBOOT_INFO_FILE))\n\n$(call end_json_map)\n\n# For converting vintf_data\n$(call add_json_list, DeviceMatrixFile, $(DEVICE_MATRIX_FILE))\n$(call add_json_list, ProductManifestFiles, $(PRODUCT_MANIFEST_FILES))\n$(call add_json_list, SystemManifestFile, $(DEVICE_FRAMEWORK_MANIFEST_FILE))\nSYSTEM_EXT_HWSERVICE_FILES :=\nifeq ($(PRODUCT_HIDL_ENABLED),true)\n  ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES)),)\n    SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager_no_max.xml\n  else\n    $(error If PRODUCT_HIDL_ENABLED is set, hwservicemanager must be added to PRODUCT_PACKAGES explicitly)\n  endif\nelse\n  ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES)),)\n    SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager.xml\n  else ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)),)\n    SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager.xml\n  endif\nendif\n$(call add_json_list, SystemExtManifestFiles, $(SYSTEM_EXT_MANIFEST_FILES) $(SYSTEM_EXT_HWSERVICE_FILES))\n$(call add_json_list, DeviceManifestFiles, $(DEVICE_MANIFEST_FILE))\n$(call add_json_list, OdmManifestFiles, $(ODM_MANIFEST_FILES))\n\n$(call json_end)\n\n$(file >$(SOONG_VARIABLES).tmp,$(json_contents))\n\n$(shell if ! cmp -s $(SOONG_VARIABLES).tmp $(SOONG_VARIABLES); then \\\n\t  mv $(SOONG_VARIABLES).tmp $(SOONG_VARIABLES); \\\n\telse \\\n\t  rm $(SOONG_VARIABLES).tmp; \\\n\tfi)\n\ninclude $(BUILD_SYSTEM)/soong_extra_config.mk\n\nendif # CONFIGURE_SOONG\n"
  },
  {
    "path": "core/soong_droiddoc_prebuilt.mk",
    "content": "# Droiddoc prebuilt coming from Soong.\n\nifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n  $(call pretty-error,soong_droiddoc_prebuilt.mk may only be used from Soong)\nendif\n\nifdef LOCAL_DROIDDOC_STUBS_SRCJAR\n$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_STUBS_SRCJAR),$(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar))\n$(eval ALL_TARGETS.$(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA))\nALL_DOCS += $(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar\n\n.PHONY: $(LOCAL_MODULE)\n$(LOCAL_MODULE) : $(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar\nendif\n\nifdef LOCAL_DROIDDOC_DOC_ZIP\n$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_DOC_ZIP),$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip))\n$(eval ALL_TARGETS.$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA))\n$(call dist-for-goals,docs,$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip)\n\n.PHONY: $(LOCAL_MODULE) $(LOCAL_MODULE)-docs.zip\n$(LOCAL_MODULE) $(LOCAL_MODULE)-docs.zip : $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip\nALL_DOCS += $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip\nendif\n\nifdef LOCAL_DROIDDOC_ANNOTATIONS_ZIP\n$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_ANNOTATIONS_ZIP),$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_annotations.zip))\n$(eval ALL_TARGETS.$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_annotations.zip.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA))\nendif\n\nifdef LOCAL_DROIDDOC_API_VERSIONS_XML\n$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_API_VERSIONS_XML),$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_generated-api-versions.xml))\n$(eval ALL_TARGETS.$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_generated-api-versions.xml.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA))\nendif\n\nifdef LOCAL_DROIDDOC_METADATA_ZIP\n$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_METADATA_ZIP),$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)-metadata.zip))\n$(eval ALL_TARGETS.$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)-metadata.zip.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA))\nendif\n"
  },
  {
    "path": "core/soong_extra_config.mk",
    "content": "$(call json_start)\n\n$(call add_json_str, DeviceCpuVariantRuntime,           $(TARGET_CPU_VARIANT_RUNTIME))\n$(call add_json_str, DeviceAbiList,                     $(TARGET_CPU_ABI_LIST))\n$(call add_json_str, DeviceAbiList32,                   $(TARGET_CPU_ABI_LIST_32_BIT))\n$(call add_json_str, DeviceAbiList64,                   $(TARGET_CPU_ABI_LIST_64_BIT))\n$(call add_json_str, DeviceSecondaryCpuVariantRuntime,  $(TARGET_2ND_CPU_VARIANT_RUNTIME))\n\n$(call add_json_str, Dex2oatTargetCpuVariantRuntime,         $(DEX2OAT_TARGET_CPU_VARIANT_RUNTIME))\n$(call add_json_str, Dex2oatTargetInstructionSetFeatures,    $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES))\n$(call add_json_str, SecondaryDex2oatCpuVariantRuntime,      $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT_RUNTIME))\n$(call add_json_str, SecondaryDex2oatInstructionSetFeatures, $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES))\n\n$(call add_json_str, BoardPlatform,          $(TARGET_BOARD_PLATFORM))\n$(call add_json_str, BoardShippingApiLevel,  $(BOARD_SHIPPING_API_LEVEL))\n$(call add_json_str, ShippingApiLevel,       $(PRODUCT_SHIPPING_API_LEVEL))\n\n$(call add_json_str, ProductModel,                      $(PRODUCT_MODEL))\n$(call add_json_str, ProductModelForAttestation,        $(PRODUCT_MODEL_FOR_ATTESTATION))\n$(call add_json_str, ProductBrandForAttestation,        $(PRODUCT_BRAND_FOR_ATTESTATION))\n$(call add_json_str, ProductNameForAttestation,         $(PRODUCT_NAME_FOR_ATTESTATION))\n$(call add_json_str, ProductDeviceForAttestation,       $(PRODUCT_DEVICE_FOR_ATTESTATION))\n$(call add_json_str, ProductManufacturerForAttestation, $(PRODUCT_MANUFACTURER_FOR_ATTESTATION))\n\n$(call add_json_str, SystemBrand, $(PRODUCT_SYSTEM_BRAND))\n$(call add_json_str, SystemDevice, $(PRODUCT_SYSTEM_DEVICE))\n$(call add_json_str, SystemManufacturer, $(PRODUCT_SYSTEM_MANUFACTURER))\n$(call add_json_str, SystemModel, $(PRODUCT_SYSTEM_MODEL))\n$(call add_json_str, SystemName, $(PRODUCT_SYSTEM_NAME))\n\n# Collapses ?= and = operators for system property variables. Also removes double quotes to prevent\n# malformed JSON. This change aligns with the existing behavior of sysprop.mk, which passes property\n# variables to the echo command, effectively discarding surrounding double quotes.\ndefine collapse-prop-pairs\n$(subst \",,$(call collapse-pairs,$(call collapse-pairs,$$($(1)),?=),=))\nendef\n\n$(call add_json_list, PRODUCT_SYSTEM_PROPERTIES,         $(call collapse-prop-pairs,PRODUCT_SYSTEM_PROPERTIES))\n$(call add_json_list, PRODUCT_SYSTEM_DEFAULT_PROPERTIES, $(call collapse-prop-pairs,PRODUCT_SYSTEM_DEFAULT_PROPERTIES))\n$(call add_json_list, PRODUCT_SYSTEM_EXT_PROPERTIES,     $(call collapse-prop-pairs,PRODUCT_SYSTEM_EXT_PROPERTIES))\n$(call add_json_list, PRODUCT_VENDOR_PROPERTIES,         $(call collapse-prop-pairs,PRODUCT_VENDOR_PROPERTIES))\n$(call add_json_list, PRODUCT_PRODUCT_PROPERTIES,        $(call collapse-prop-pairs,PRODUCT_PRODUCT_PROPERTIES))\n$(call add_json_list, PRODUCT_ODM_PROPERTIES,            $(call collapse-prop-pairs,PRODUCT_ODM_PROPERTIES))\n$(call add_json_list, PRODUCT_PROPERTY_OVERRIDES,        $(call collapse-prop-pairs,PRODUCT_PROPERTY_OVERRIDES))\n$(call add_json_list, PRODUCT_DEFAULT_PROPERTY_OVERRIDES,        $(call collapse-prop-pairs,PRODUCT_DEFAULT_PROPERTY_OVERRIDES))\n\n$(call add_json_str, BootloaderBoardName, $(TARGET_BOOTLOADER_BOARD_NAME))\n\n$(call add_json_bool, SdkBuild, $(filter sdk sdk_addon,$(MAKECMDGOALS)))\n\n$(call add_json_str, SystemServerCompilerFilter, $(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER))\n\n$(call add_json_bool, Product16KDeveloperOption, $(filter true,$(PRODUCT_16K_DEVELOPER_OPTION)))\n\n$(call add_json_str, RecoveryDefaultRotation, $(TARGET_RECOVERY_DEFAULT_ROTATION))\n$(call add_json_str, RecoveryOverscanPercent, $(TARGET_RECOVERY_OVERSCAN_PERCENT))\n$(call add_json_str, RecoveryPixelFormat, $(TARGET_RECOVERY_PIXEL_FORMAT))\n\nifdef AB_OTA_UPDATER\n$(call add_json_bool, AbOtaUpdater, $(filter true,$(AB_OTA_UPDATER)))\n$(call add_json_str, AbOtaPartitions, $(subst $(space),$(comma),$(sort $(AB_OTA_PARTITIONS))))\nendif\n\nifdef PRODUCT_USE_DYNAMIC_PARTITIONS\n$(call add_json_bool, UseDynamicPartitions, $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)))\nendif\n\nifdef PRODUCT_RETROFIT_DYNAMIC_PARTITIONS\n$(call add_json_bool, RetrofitDynamicPartitions, $(filter true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)))\nendif\n\n$(call add_json_bool, DontUseVabcOta, $(filter true,$(BOARD_DONT_USE_VABC_OTA)))\n\n$(call add_json_bool, FullTreble, $(filter true,$(PRODUCT_FULL_TREBLE)))\n\n$(call add_json_bool, NoBionicPageSizeMacro, $(filter true,$(PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO)))\n\n$(call add_json_bool, PropertySplitEnabled, $(filter true,$(BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED)))\n\n$(call add_json_str, ScreenDensity, $(TARGET_SCREEN_DENSITY))\n\n$(call add_json_str, UsesVulkan, $(TARGET_USES_VULKAN))\n\n$(call add_json_bool, ZygoteForce64, $(filter true,$(ZYGOTE_FORCE_64)))\n\n$(call add_json_str, VendorSecurityPatch,       $(VENDOR_SECURITY_PATCH))\n$(call add_json_str, VendorImageFileSystemType, $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE))\n\n$(call add_json_list, BuildVersionTags, $(BUILD_VERSION_TAGS))\n\n$(call add_json_bool, ProductNotDebuggableInUserdebug, $(PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG))\n\n$(call add_json_bool, UsesProductImage, $(filter true,$(BOARD_USES_PRODUCTIMAGE)))\n\n$(call add_json_bool, TargetBoots16K, $(filter true,$(TARGET_BOOTS_16K)))\n\n$(call json_end)\n\n$(shell mkdir -p $(dir $(SOONG_EXTRA_VARIABLES)))\n$(file >$(SOONG_EXTRA_VARIABLES).tmp,$(json_contents))\n\n$(shell if ! cmp -s $(SOONG_EXTRA_VARIABLES).tmp $(SOONG_EXTRA_VARIABLES); then \\\n\t  mv $(SOONG_EXTRA_VARIABLES).tmp $(SOONG_EXTRA_VARIABLES); \\\n\telse \\\n\t  rm $(SOONG_EXTRA_VARIABLES).tmp; \\\n\tfi)\n"
  },
  {
    "path": "core/soong_java_prebuilt.mk",
    "content": "# Java prebuilt coming from Soong.\n# Extra inputs:\n# LOCAL_SOONG_BUILT_INSTALLED\n# LOCAL_SOONG_CLASSES_JAR\n# LOCAL_SOONG_HEADER_JAR\n# LOCAL_SOONG_DEX_JAR\n# LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR\n# LOCAL_SOONG_DEXPREOPT_CONFIG\n\nifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))\n  $(call pretty-error,soong_java_prebuilt.mk may only be used from Soong)\nendif\n\nLOCAL_MODULE_SUFFIX := .jar\nLOCAL_BUILT_MODULE_STEM := javalib.jar\n\nintermediates.COMMON := $(call local-intermediates-dir,COMMON)\n\nfull_classes_jar := $(intermediates.COMMON)/classes.jar\nfull_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar\nfull_classes_header_jar := $(intermediates.COMMON)/classes-header.jar\ncommon_javalib.jar := $(intermediates.COMMON)/javalib.jar\n\n#######################################\ninclude $(BUILD_SYSTEM)/base_rules.mk\n#######################################\n\nifdef LOCAL_SOONG_CLASSES_JAR\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_jar)))\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_pre_proguard_jar)))\n  $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(full_classes_jar)))\n\n  ifneq ($(TURBINE_ENABLED),false)\n    ifdef LOCAL_SOONG_HEADER_JAR\n      $(eval $(call copy-one-file,$(LOCAL_SOONG_HEADER_JAR),$(full_classes_header_jar)))\n    else\n      $(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_header_jar)))\n    endif\n  endif # TURBINE_ENABLED != false\nendif\n\n$(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE)))\n\nifdef LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR),\\\n    $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar))\n  $(call add-dependency,$(common_javalib.jar),\\\n    $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar)\nendif\n\nifdef LOCAL_SOONG_PROGUARD_DICT\n  $(eval $(call copy-r8-dictionary-file-with-mapping,\\\n    $(LOCAL_SOONG_PROGUARD_DICT),\\\n    $(intermediates.COMMON)/proguard_dictionary,\\\n    $(intermediates.COMMON)/proguard_dictionary.textproto))\n\n  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_FILES := \\\n    $(intermediates.COMMON)/proguard_dictionary \\\n    $(LOCAL_SOONG_CLASSES_JAR)\n  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS := \\\n    -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/proguard_dictionary \\\n    -f $(intermediates.COMMON)/proguard_dictionary \\\n    -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/classes.jar \\\n    -f $(LOCAL_SOONG_CLASSES_JAR)\n  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_MAPPING := $(intermediates.COMMON)/proguard_dictionary.textproto\nendif\n\nifdef LOCAL_SOONG_PROGUARD_USAGE_ZIP\n  ALL_MODULES.$(my_register_name).PROGUARD_USAGE_ZIP := $(LOCAL_SOONG_PROGUARD_USAGE_ZIP)\nendif\n\n\nifdef LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE\n  my_res_package := $(intermediates.COMMON)/package-res.apk\n\n  $(my_res_package): $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE)\n\t@echo \"Copy: $@\"\n\t$(copy-file-to-target)\n\n  $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_res_package))\n\n  my_transitive_res_packages := $(intermediates.COMMON)/transitive-res-packages\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_TRANSITIVE_RES_PACKAGES),$(my_transitive_res_packages)))\n  $(call add-dependency,$(my_res_package),$(my_transitive_res_packages))\n\n  my_proguard_flags := $(intermediates.COMMON)/export_proguard_flags\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_EXPORT_PROGUARD_FLAGS),$(my_proguard_flags)))\n  $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_proguard_flags))\n\n  my_static_library_extra_packages := $(intermediates.COMMON)/extra_packages\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES),$(my_static_library_extra_packages)))\n  $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_static_library_extra_packages))\n\n  my_static_library_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml\n  $(eval $(call copy-one-file,$(LOCAL_FULL_MANIFEST_FILE),$(my_static_library_android_manifest)))\n  $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_static_library_android_manifest))\nendif # LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE\n\n\nifdef LOCAL_SOONG_DEX_JAR\n  ifndef LOCAL_IS_HOST_MODULE\n    boot_jars := $(foreach pair,$(PRODUCT_BOOT_JARS), $(call word-colon,2,$(pair)))\n    ifneq ($(filter $(LOCAL_MODULE),$(boot_jars)),) # is_boot_jar\n      ifeq (true,$(WITH_DEXPREOPT))\n        # dex_bootjars singleton installs all of bootjars' dexpreopt files (.art, .oat, .vdex, ...)\n        # This includes both the primary and secondary arches.\n        # Add them to the required list so they are installed alongside this module.\n        ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET += dex_bootjars\n        # Copy $(LOCAL_BUILT_MODULE) and its dependencies when installing boot.art\n        # so that dependencies of $(LOCAL_BUILT_MODULE) (which may include\n        # jacoco-report-classes.jar) are copied for every build.\n        $(foreach m,dex_bootjars, \\\n          $(eval $(call add-dependency,$(firstword $(call module-installed-files,$(m))),$(LOCAL_BUILT_MODULE))) \\\n        )\n      endif\n    endif # is_boot_jar\n\n    $(eval $(call copy-one-file,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar)))\n    $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(common_javalib.jar)))\n    ifdef LOCAL_SOONG_CLASSES_JAR\n      $(eval $(call add-dependency,$(common_javalib.jar),$(full_classes_jar)))\n      ifneq ($(TURBINE_ENABLED),false)\n        $(eval $(call add-dependency,$(common_javalib.jar),$(full_classes_header_jar)))\n      endif\n    endif\n  endif\n\n  java-dex : $(LOCAL_BUILT_MODULE)\nelse  # LOCAL_SOONG_DEX_JAR\n  ifndef LOCAL_UNINSTALLABLE_MODULE\n    ifndef LOCAL_IS_HOST_MODULE\n      $(call pretty-error,Installable device module must have LOCAL_SOONG_DEX_JAR set)\n    endif\n  endif\nendif  # LOCAL_SOONG_DEX_JAR\n\nALL_MODULES.$(my_register_name).CLASSES_JAR := $(full_classes_jar)\n\nifdef LOCAL_SOONG_AAR\n  ALL_MODULES.$(my_register_name).AAR := $(LOCAL_SOONG_AAR)\nendif\n\n# Copy dexpreopt.config files from Soong libraries to the location where Make\n# modules can find them.\nifdef LOCAL_SOONG_DEXPREOPT_CONFIG\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_DEXPREOPT_CONFIG), $(call local-intermediates-dir,)/dexpreopt.config))\n  my_dexpreopt_config := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config\n  $(eval $(call copy-one-file,$(LOCAL_SOONG_DEXPREOPT_CONFIG), $(my_dexpreopt_config)))\n  $(LOCAL_BUILT_MODULE): $(my_dexpreopt_config)\nendif\n\nifdef LOCAL_SOONG_CLASSES_JAR\njavac-check : $(full_classes_jar)\njavac-check-$(LOCAL_MODULE) : $(full_classes_jar)\nendif\n.PHONY: javac-check-$(LOCAL_MODULE)\n\nifndef LOCAL_IS_HOST_MODULE\nifeq ($(LOCAL_SDK_VERSION),system_current)\nmy_link_type := java:system\nelse ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION)))\nmy_link_type := java:system\nelse ifeq ($(LOCAL_SDK_VERSION),core_current)\nmy_link_type := java:core\nelse ifneq ($(LOCAL_SDK_VERSION),)\nmy_link_type := java:sdk\nelse\nmy_link_type := java:platform\nendif\n# warn/allowed types are both empty because Soong modules can't depend on\n# make-defined modules.\nmy_warn_types :=\nmy_allowed_types :=\n\nmy_link_deps :=\nmy_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX)\nmy_common := COMMON\ninclude $(BUILD_SYSTEM)/link_type.mk\nendif # !LOCAL_IS_HOST_MODULE\n\n# LOCAL_EXPORT_SDK_LIBRARIES set by soong is written to exported-sdk-libs file\nmy_exported_sdk_libs_file := $(intermediates.COMMON)/exported-sdk-libs\n$(my_exported_sdk_libs_file): PRIVATE_EXPORTED_SDK_LIBS := $(LOCAL_EXPORT_SDK_LIBRARIES)\n$(my_exported_sdk_libs_file):\n\t@echo \"Export SDK libs $@\"\n\t$(hide) mkdir -p $(dir $@) && rm -f $@\n\t$(if $(PRIVATE_EXPORTED_SDK_LIBS),\\\n\t\t$(hide) echo $(PRIVATE_EXPORTED_SDK_LIBS) | tr ' ' '\\n' > $@,\\\n\t\t$(hide) touch $@)\n\nSOONG_ALREADY_CONV += $(LOCAL_MODULE)\n"
  },
  {
    "path": "core/static_java_library.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Standard rules for building a \"static\" java library.\n# Static java libraries are not installed, nor listed on any\n# classpaths.  They can, however, be included wholesale in\n# other java modules.\n\n$(call record-module-type,STATIC_JAVA_LIBRARY)\nLOCAL_UNINSTALLABLE_MODULE := true\nLOCAL_IS_STATIC_JAVA_LIBRARY := true\nLOCAL_MODULE_CLASS := JAVA_LIBRARIES\n\nintermediates.COMMON := $(call local-intermediates-dir,COMMON)\n\nmy_res_package :=\n\n# Process Support Library dependencies.\ninclude $(BUILD_SYSTEM)/support_libraries.mk\n\ninclude $(BUILD_SYSTEM)/force_aapt2.mk\n\n# Hack to build static Java library with Android resource\n# See bug 5714516\nall_resources :=\nneed_compile_res :=\n# A static Java library needs to explicily set LOCAL_RESOURCE_DIR.\nifdef LOCAL_RESOURCE_DIR\nneed_compile_res := true\nLOCAL_RESOURCE_DIR := $(foreach d,$(LOCAL_RESOURCE_DIR),$(call clean-path,$(d)))\nendif\nifneq ($(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),)\nneed_compile_res := true\nendif\n\nifeq ($(need_compile_res),true)\nall_resources := $(strip \\\n    $(foreach dir, $(LOCAL_RESOURCE_DIR), \\\n      $(addprefix $(dir)/, \\\n        $(patsubst res/%,%, \\\n          $(call find-subdir-assets,$(dir)) \\\n        ) \\\n      ) \\\n    ))\n\n# By default we should remove the R/Manifest classes from a static Java library,\n# because they will be regenerated in the app that uses it.\n# But if the static Java library will be used by a library, then we may need to\n# keep the generated classes with \"LOCAL_JAR_EXCLUDE_FILES := none\".\nifndef LOCAL_JAR_EXCLUDE_FILES\nLOCAL_JAR_EXCLUDE_FILES := $(ANDROID_RESOURCE_GENERATED_CLASSES)\nendif\nifeq (none,$(LOCAL_JAR_EXCLUDE_FILES))\nLOCAL_JAR_EXCLUDE_FILES :=\nendif\n\nproguard_options_file :=\n\nifneq ($(filter custom,$(LOCAL_PROGUARD_ENABLED)),custom)\n  proguard_options_file := $(intermediates.COMMON)/proguard_options\nendif\n\nLOCAL_PROGUARD_FLAGS := $(addprefix -include ,$(proguard_options_file)) $(LOCAL_PROGUARD_FLAGS)\nLOCAL_PROGUARD_FLAGS_DEPS += $(proguard_options_file)\n\nR_file_stamp := $(intermediates.COMMON)/src/R.stamp\nLOCAL_INTERMEDIATE_TARGETS += $(R_file_stamp)\n\nifneq ($(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),)\n  # If we are using static android libraries, every source file becomes an overlay.\n  # This is to emulate old AAPT behavior which simulated library support.\n  my_res_resources :=\n  my_overlay_resources := $(all_resources)\nelse\n  # Otherwise, for a library we treat all the resource equal with no overlay.\n  my_res_resources := $(all_resources)\n  my_overlay_resources :=\nendif\n# For libraries put everything in the COMMON intermediate directory.\nmy_res_package := $(intermediates.COMMON)/package-res.apk\n\nLOCAL_INTERMEDIATE_TARGETS += $(my_res_package)\n\nendif  # need_compile_res\n\nall_res_assets := $(all_resources)\n\ninclude $(BUILD_SYSTEM)/java_renderscript.mk\n\nifeq (true,$(need_compile_res))\n# work around missing manifests by creating a default one\nifeq (,$(strip $(LOCAL_MANIFEST_FILE)$(LOCAL_FULL_MANIFEST_FILE)))\n  ifeq (,$(wildcard $(LOCAL_PATH)/AndroidManifest.xml))\n    LOCAL_FULL_MANIFEST_FILE := $(call local-intermediates-dir,COMMON)/DefaultManifest.xml\n    $(call create-default-manifest-file,$(LOCAL_FULL_MANIFEST_FILE),$(call module-min-sdk-version))\n  endif\nendif\ninclude $(BUILD_SYSTEM)/android_manifest.mk\n\nLOCAL_SDK_RES_VERSION:=$(strip $(LOCAL_SDK_RES_VERSION))\nifeq ($(LOCAL_SDK_RES_VERSION),)\n  LOCAL_SDK_RES_VERSION:=$(LOCAL_SDK_VERSION)\nendif\n\nframework_res_package_export :=\n# Please refer to package.mk\nifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true)\nifneq ($(filter-out current system_current test_current,$(LOCAL_SDK_RES_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_RES_VERSION))),)\nframework_res_package_export := \\\n    $(call resolve-prebuilt-sdk-jar-path,$(LOCAL_SDK_RES_VERSION))\nelse\nframework_res_package_export := \\\n    $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk\nendif\nendif\n\n# transitive-res-packages is only populated for Soong modules for now, but needs\n# to exist so that other Make modules can depend on it.  Create an empty file.\nmy_transitive_res_packages := $(intermediates.COMMON)/transitive-res-packages\n$(my_transitive_res_packages):\n\ttouch $@\n\nimport_proguard_flag_files := $(strip $(foreach l,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES),\\\n    $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/export_proguard_flags))\n$(intermediates.COMMON)/export_proguard_flags: $(import_proguard_flag_files) $(addprefix $(LOCAL_PATH)/,$(LOCAL_EXPORT_PROGUARD_FLAG_FILES))\n\t@echo \"Export proguard flags: $@\"\n\trm -f $@\n\ttouch $@\n\tfor f in $+; do \\\n\t\techo -e \"\\n# including $$f\" >>$@; \\\n\t\tcat $$f >>$@; \\\n\tdone\nimport_proguard_flag_files :=\n\ninclude $(BUILD_SYSTEM)/aapt_flags.mk\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_FLAGS := $(LOCAL_AAPT_FLAGS)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_AAPT_CHARACTERISTICS := $(TARGET_AAPT_CHARACTERISTICS)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_PACKAGE_NAME := $(LOCAL_MANIFEST_PACKAGE_NAME)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_INSTRUMENTATION_FOR := $(LOCAL_MANIFEST_INSTRUMENTATION_FOR)\n\n# add --non-constant-id to prevent inlining constants.\n# AAR needs text symbol file R.txt.\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_FLAGS := $(LOCAL_AAPT_FLAGS) --static-lib --output-text-symbols $(intermediates.COMMON)/R.txt\nifndef LOCAL_AAPT_NAMESPACES\n  $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_FLAGS += --no-static-lib-packages\nendif\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PRODUCT_AAPT_CONFIG :=\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PRODUCT_AAPT_PREF_CONFIG :=\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_AAPT_CHARACTERISTICS :=\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ANDROID_MANIFEST := $(full_android_manifest)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_PUBLICS_OUTPUT := $(intermediates.COMMON)/public_resources.xml\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_INCLUDES := $(framework_res_package_export)\n\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASSET_DIR :=\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PROGUARD_OPTIONS_FILE := $(proguard_options_file)\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_PACKAGE_NAME :=\n$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_INSTRUMENTATION_FOR :=\n\n# One more level with name res so we can zip up the flat resources that can be linked by apps.\nmy_compiled_res_base_dir := $(intermediates.COMMON)/flat-res/res\nifneq (,$(filter-out current,$(renderscript_target_api)))\n  ifneq ($(call math_gt_or_eq,$(renderscript_target_api),21),true)\n    my_generated_res_zips := $(rs_generated_res_zip)\n  endif  # renderscript_target_api < 21\nendif  # renderscript_target_api is set\ninclude $(BUILD_SYSTEM)/aapt2.mk\n$(my_res_package) : $(framework_res_package_export)\n$(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(intermediates.COMMON)/R.txt\n\nendif # need_compile_res\n\ninclude $(BUILD_SYSTEM)/java_library.mk\n\nifeq (true,$(need_compile_res))\n\n$(LOCAL_BUILT_MODULE): $(R_file_stamp)\n$(java_source_list_file): $(R_file_stamp)\n$(full_classes_compiled_jar): $(R_file_stamp)\n$(full_classes_turbine_jar): $(R_file_stamp)\n\n\n# if we have custom proguarding done use the proguarded classes jar instead of the normal classes jar\nifeq ($(filter custom,$(LOCAL_PROGUARD_ENABLED)),custom)\naar_classes_jar = $(full_classes_jar)\nelse\naar_classes_jar = $(full_classes_pre_proguard_jar)\nendif\n\n# Rule to build AAR, archive including classes.jar, resource, etc.\nbuilt_aar := $(intermediates.COMMON)/javalib.aar\n$(built_aar): PRIVATE_MODULE := $(LOCAL_MODULE)\n$(built_aar): PRIVATE_ANDROID_MANIFEST := $(full_android_manifest)\n$(built_aar): PRIVATE_CLASSES_JAR := $(aar_classes_jar)\n$(built_aar): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR)\n$(built_aar): PRIVATE_R_TXT := $(intermediates.COMMON)/R.txt\n$(built_aar): $(JAR_ARGS)\n$(built_aar) : $(aar_classes_jar) $(full_android_manifest) $(intermediates.COMMON)/R.txt\n\t@echo \"target AAR:  $(PRIVATE_MODULE) ($@)\"\n\t$(hide) rm -rf $(dir $@)aar && mkdir -p $(dir $@)aar/res\n\t$(hide) cp $(PRIVATE_ANDROID_MANIFEST) $(dir $@)aar/AndroidManifest.xml\n\t$(hide) cp $(PRIVATE_CLASSES_JAR) $(dir $@)aar/classes.jar\n\t# Note: Use \"cp -n\" to honor the resource overlay rules, if multiple res dirs exist.\n\t$(hide) $(foreach res,$(PRIVATE_RESOURCE_DIR),cp -Rfn $(res)/* $(dir $@)aar/res;)\n\t$(hide) cp $(PRIVATE_R_TXT) $(dir $@)aar/R.txt\n\t$(hide) $(JAR) -cMf $@ \\\n\t  $(call jar-args-sorted-files-in-directory,$(dir $@)aar)\n\n# Register the aar file.\nALL_MODULES.$(my_register_name).AAR := $(built_aar)\nendif  # need_compile_res\n\n# Reset internal variables.\naar_classes_jar :=\nall_res_assets :=\nLOCAL_IS_STATIC_JAVA_LIBRARY :=\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=STATIC_JAVA_LIBRARY))"
  },
  {
    "path": "core/static_library.mk",
    "content": "$(call record-module-type,STATIC_LIBRARY)\nifdef LOCAL_IS_HOST_MODULE\n  $(call pretty-error,BUILD_STATIC_LIBRARY is incompatible with LOCAL_IS_HOST_MODULE. Use BUILD_HOST_STATIC_LIBRARY instead)\nendif\nmy_prefix := TARGET_\ninclude $(BUILD_SYSTEM)/multilib.mk\n\nifndef my_module_multilib\n# libraries default to building for both architecturess\nmy_module_multilib := both\nendif\n\nLOCAL_2ND_ARCH_VAR_PREFIX :=\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\n\nifeq ($(my_module_arch_supported),true)\ninclude $(BUILD_SYSTEM)/static_library_internal.mk\nendif\n\nifdef TARGET_2ND_ARCH\n\nLOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX)\ninclude $(BUILD_SYSTEM)/module_arch_supported.mk\n\nifeq ($(my_module_arch_supported),true)\n# Build for TARGET_2ND_ARCH\nLOCAL_BUILT_MODULE :=\nLOCAL_INSTALLED_MODULE :=\nLOCAL_INTERMEDIATE_TARGETS :=\n\ninclude $(BUILD_SYSTEM)/static_library_internal.mk\n\nendif\n\nLOCAL_2ND_ARCH_VAR_PREFIX :=\n\nendif # TARGET_2ND_ARCH\n\nmy_module_arch_supported :=\n\n###########################################################\n## Copy headers to the install tree\n###########################################################\nifdef LOCAL_COPY_HEADERS\n$(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\\\n  $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\\\n  $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers))\ninclude $(BUILD_SYSTEM)/copy_headers.mk\nendif\n"
  },
  {
    "path": "core/static_library_internal.mk",
    "content": "###########################################################\n## Standard rules for building a static library.\n##\n## Additional inputs from base_rules.make:\n## None.\n##\n## LOCAL_MODULE_SUFFIX will be set for you.\n###########################################################\n\nifeq ($(strip $(LOCAL_MODULE_CLASS)),)\nLOCAL_MODULE_CLASS := STATIC_LIBRARIES\nendif\nifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)\nLOCAL_MODULE_SUFFIX := .a\nendif\nLOCAL_UNINSTALLABLE_MODULE := true\nifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),)\n$(error $(LOCAL_PATH): Cannot set module stem for a library)\nendif\n\ninclude $(BUILD_SYSTEM)/binary.mk\n\n$(LOCAL_BUILT_MODULE) : $(built_whole_libraries)\n$(LOCAL_BUILT_MODULE) : $(all_objects) $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_AR)\n\t$(transform-o-to-static-lib)\n\nifeq ($(NATIVE_COVERAGE),true)\ngcno_suffix := .zip\n\nbuilt_whole_gcno_libraries := \\\n    $(foreach lib,$(my_whole_static_libraries), \\\n      $(call intermediates-dir-for, \\\n        STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \\\n        $(my_host_cross))/$(lib)$(gcno_suffix))\n\nGCNO_ARCHIVE := $(LOCAL_MODULE)$(gcno_suffix)\n\n$(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS)\n$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(strip $(LOCAL_GCNO_FILES))\n$(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(strip $(built_whole_gcno_libraries))\n$(intermediates)/$(GCNO_ARCHIVE) : $(LOCAL_GCNO_FILES) $(built_whole_gcno_libraries)\n\t$(package-coverage-files)\nendif\n\n$(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=STATIC_LIBRARY))"
  },
  {
    "path": "core/suite_host_config.mk",
    "content": "#\n# Copyright (C) 2016 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nLOCAL_MODULE_CLASS := FAKE\nLOCAL_IS_HOST_MODULE := true\n\ninclude $(BUILD_SYSTEM)/base_rules.mk\n\n$(LOCAL_BUILT_MODULE):\n\t@echo \"$(LOCAL_COMPATIBILITY_SUITE) host-driven test target: $(PRIVATE_MODULE)\"\n\t$(hide) touch $@\n"
  },
  {
    "path": "core/support_libraries.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n###########################################################\n## Rules for resolving Support Library dependencies.\n##\n## The following variables may be modified:\n## - LOCAL_JAVA_LIBRARIES\n## - LOCAL_STATIC_JAVA_LIBRARIES\n## - LOCAL_SHARED_ANDROID_LIBRARIES\n## - LOCAL_STATIC_ANDROID_LIBRARIES\n###########################################################\n\n# Some projects don't work correctly yet. Allow them to skip resolution.\nifndef LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES\n\n# Aggregate all requested Support Library modules.\nrequested_support_libs := $(filter $(SUPPORT_LIBRARIES_JARS) $(SUPPORT_LIBRARIES_AARS), \\\n    $(LOCAL_JAVA_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES) \\\n    $(LOCAL_SHARED_ANDROID_LIBRARIES) $(LOCAL_STATIC_ANDROID_LIBRARIES))\n\n# Filter the Support Library modules out of the library variables. We don't\n# trust developers to get these right, so they will be added back by the\n# build system based on the output of this file and the type of build.\nLOCAL_JAVA_LIBRARIES := $(filter-out $(requested_support_libs), \\\n    $(LOCAL_JAVA_LIBRARIES))\nLOCAL_STATIC_JAVA_LIBRARIES := $(filter-out $(requested_support_libs), \\\n    $(LOCAL_STATIC_JAVA_LIBRARIES))\nLOCAL_SHARED_ANDROID_LIBRARIES := $(filter-out $(requested_support_libs), \\\n    $(LOCAL_SHARED_ANDROID_LIBRARIES))\nLOCAL_STATIC_ANDROID_LIBRARIES := $(filter-out $(requested_support_libs), \\\n    $(LOCAL_STATIC_ANDROID_LIBRARIES))\n\nLOCAL_STATIC_ANDROID_LIBRARIES := $(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) \\\n    $(filter $(SUPPORT_LIBRARIES_AARS),$(requested_support_libs)))\nLOCAL_STATIC_JAVA_LIBRARIES := $(strip $(LOCAL_STATIC_JAVA_LIBRARIES) \\\n    $(filter $(SUPPORT_LIBRARIES_JARS),$(requested_support_libs)))\n\nendif #LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES\nLOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES :=\n"
  },
  {
    "path": "core/sysprop.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# sysprop.mk defines rules for generating <partition>/[etc/]build.prop files\n\n# -----------------------------------------------------------------\n# property_overrides_split_enabled\nproperty_overrides_split_enabled :=\nifeq ($(BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED), true)\n  property_overrides_split_enabled := true\nendif\n\nPOST_PROCESS_PROPS := $(HOST_OUT_EXECUTABLES)/post_process_props$(HOST_EXECUTABLE_SUFFIX)\n\n# Emits a set of sysprops common to all partitions to a file.\n# $(1): Partition name\n# $(2): Output file name\ndefine generate-common-build-props\n    echo \"####################################\" >> $(2);\\\n    echo \"# from generate-common-build-props\" >> $(2);\\\n    echo \"# These properties identify this partition image.\" >> $(2);\\\n    echo \"####################################\" >> $(2);\\\n    echo \"ro.product.$(1).brand=$(PRODUCT_BRAND)\" >> $(2);\\\n    echo \"ro.product.$(1).device=$(TARGET_DEVICE)\" >> $(2);\\\n    echo \"ro.product.$(1).manufacturer=$(PRODUCT_MANUFACTURER)\" >> $(2);\\\n    echo \"ro.product.$(1).model=$(PRODUCT_MODEL)\" >> $(2);\\\n    echo \"ro.product.$(1).name=$(TARGET_PRODUCT)\" >> $(2);\\\n    if [ -n \"$(strip $(PRODUCT_MODEL_FOR_ATTESTATION))\" ]; then \\\n        echo \"ro.product.model_for_attestation=$(PRODUCT_MODEL_FOR_ATTESTATION)\" >> $(2);\\\n    fi; \\\n    if [ -n \"$(strip $(PRODUCT_BRAND_FOR_ATTESTATION))\" ]; then \\\n        echo \"ro.product.brand_for_attestation=$(PRODUCT_BRAND_FOR_ATTESTATION)\" >> $(2);\\\n    fi; \\\n    if [ -n \"$(strip $(PRODUCT_NAME_FOR_ATTESTATION))\" ]; then \\\n        echo \"ro.product.name_for_attestation=$(PRODUCT_NAME_FOR_ATTESTATION)\" >> $(2);\\\n    fi; \\\n    if [ -n \"$(strip $(PRODUCT_DEVICE_FOR_ATTESTATION))\" ]; then \\\n        echo \"ro.product.device_for_attestation=$(PRODUCT_DEVICE_FOR_ATTESTATION)\" >> $(2);\\\n    fi; \\\n    if [ -n \"$(strip $(PRODUCT_MANUFACTURER_FOR_ATTESTATION))\" ]; then \\\n        echo \"ro.product.manufacturer_for_attestation=$(PRODUCT_MANUFACTURER_FOR_ATTESTATION)\" >> $(2);\\\n    fi; \\\n    $(if $(filter true,$(ZYGOTE_FORCE_64)),\\\n        $(if $(filter vendor,$(1)),\\\n            echo \"ro.$(1).product.cpu.abilist=$(TARGET_CPU_ABI_LIST_64_BIT)\" >> $(2);\\\n            echo \"ro.$(1).product.cpu.abilist32=\" >> $(2);\\\n            echo \"ro.$(1).product.cpu.abilist64=$(TARGET_CPU_ABI_LIST_64_BIT)\" >> $(2);\\\n        )\\\n    ,\\\n        $(if $(filter system vendor odm,$(1)),\\\n            echo \"ro.$(1).product.cpu.abilist=$(TARGET_CPU_ABI_LIST)\" >> $(2);\\\n            echo \"ro.$(1).product.cpu.abilist32=$(TARGET_CPU_ABI_LIST_32_BIT)\" >> $(2);\\\n            echo \"ro.$(1).product.cpu.abilist64=$(TARGET_CPU_ABI_LIST_64_BIT)\" >> $(2);\\\n        )\\\n    )\\\n    echo \"ro.$(1).build.date=`$(DATE_FROM_FILE)`\" >> $(2);\\\n    echo \"ro.$(1).build.date.utc=`$(DATE_FROM_FILE) +%s`\" >> $(2);\\\n    # Allow optional assignments for ARC forward-declarations (b/249168657)\n    # TODO: Remove any tag-related inconsistencies once the goals from\n    # go/arc-android-sigprop-changes have been achieved.\n    echo \"ro.$(1).build.fingerprint?=$(BUILD_FINGERPRINT_FROM_FILE)\" >> $(2);\\\n    echo \"ro.$(1).build.id?=$(BUILD_ID)\" >> $(2);\\\n    echo \"ro.$(1).build.tags?=$(BUILD_VERSION_TAGS)\" >> $(2);\\\n    echo \"ro.$(1).build.type=$(TARGET_BUILD_VARIANT)\" >> $(2);\\\n    echo \"ro.$(1).build.version.incremental=$(BUILD_NUMBER_FROM_FILE)\" >> $(2);\\\n    echo \"ro.$(1).build.version.release=$(PLATFORM_VERSION_LAST_STABLE)\" >> $(2);\\\n    echo \"ro.$(1).build.version.release_or_codename=$(PLATFORM_VERSION)\" >> $(2);\\\n    echo \"ro.$(1).build.version.sdk=$(PLATFORM_SDK_VERSION)\" >> $(2);\\\n    echo \"ro.$(1).build.version.sdk_minor=$(PLATFORM_SDK_MINOR_VERSION)\" >> $(2);\\\n\nendef\n\n# Rule for generating <partition>/[etc/]build.prop file\n#\n# $(1): partition name\n# $(2): path to the output\n# $(3): path to the input *.prop files. The contents of the files are directly\n#       emitted to the output\n# $(4): list of variable names each of which contains name=value pairs\n# $(5): optional list of prop names to force remove from the output. Properties from both\n#       $(3) and (4) are affected\n# $(6): optional list of files to append at the end. The content of each file is emitted\n#       to the output\n# $(7): optional flag to skip common properties generation\ndefine build-properties\nALL_DEFAULT_INSTALLED_MODULES += $(2)\n\n$(eval # Properties can be assigned using `prop ?= value` or `prop = value` syntax.)\n$(eval # Eliminate spaces around the ?= and = separators.)\n$(foreach name,$(strip $(4)),\\\n    $(eval _temp := $$(call collapse-pairs,$$($(name)),?=))\\\n    $(eval _resolved_$(name) := $$(call collapse-pairs,$$(_temp),=))\\\n)\n\n$(eval # Implement the legacy behavior when BUILD_BROKEN_DUP_SYSPROP is on.)\n$(eval # Optional assignments are all converted to normal assignments and)\n$(eval # when their duplicates the first one wins)\n$(if $(filter true,$(BUILD_BROKEN_DUP_SYSPROP)),\\\n    $(foreach name,$(strip $(4)),\\\n        $(eval _temp := $$(subst ?=,=,$$(_resolved_$(name))))\\\n        $(eval _resolved_$(name) := $$(call uniq-pairs-by-first-component,$$(_resolved_$(name)),=))\\\n    )\\\n    $(eval _option := --allow-dup)\\\n)\n\n$(2): $(POST_PROCESS_PROPS) $(INTERNAL_BUILD_ID_MAKEFILE) $(3) $(6) $(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC)\n\t$(hide) echo Building $$@\n\t$(hide) mkdir -p $$(dir $$@)\n\t$(hide) rm -f $$@ && touch $$@\nifneq ($(strip $(7)), true)\n\t$(hide) $$(call generate-common-build-props,$(call to-lower,$(strip $(1))),$$@)\nendif\n        # Make and Soong use different intermediate files to build vendor/build.prop.\n        # Although the sysprop contents are same, the absolute paths of android-info.prop are different.\n        # Print the filename for the intermediate files (files in OUT_DIR).\n        # This helps with validating mk->soong migration of android partitions.\n\t$(hide) $(foreach file,$(strip $(3)),\\\n\t    if [ -f \"$(file)\" ]; then\\\n\t        echo \"\" >> $$@;\\\n\t        echo \"####################################\" >> $$@;\\\n\t        $(if $(filter $(OUT_DIR)/%,$(file)), \\\n\t\techo \"# from $(notdir $(file))\" >> $$@;\\\n\t\t,\\\n\t\techo \"# from $(file)\" >> $$@;\\\n\t\t)\\\n\t        echo \"####################################\" >> $$@;\\\n\t        cat $(file) >> $$@;\\\n\t    fi;)\n\t$(hide) $(foreach name,$(strip $(4)),\\\n\t    echo \"\" >> $$@;\\\n\t    echo \"####################################\" >> $$@;\\\n\t    echo \"# from variable $(name)\" >> $$@;\\\n\t    echo \"####################################\" >> $$@;\\\n\t    $$(foreach line,$$(_resolved_$(name)),\\\n\t        echo \"$$(line)\" >> $$@;\\\n\t    )\\\n\t)\n\t$(hide) $(POST_PROCESS_PROPS) $$(_option) \\\n\t  --sdk-version $(PLATFORM_SDK_VERSION) \\\n\t  --kernel-version-file-for-uffd-gc \"$(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC)\" \\\n\t  $$@ $(5)\n\t$(hide) $(foreach file,$(strip $(6)),\\\n\t    if [ -f \"$(file)\" ]; then\\\n\t        cat $(file) >> $$@;\\\n\t    fi;)\n\t$(hide) echo \"# end of file\" >> $$@\n\n$(call declare-1p-target,$(2))\nendef\n\nKNOWN_OEM_THUMBPRINT_PROPERTIES := \\\n    ro.product.brand \\\n    ro.product.name \\\n    ro.product.device\nOEM_THUMBPRINT_PROPERTIES := $(filter $(KNOWN_OEM_THUMBPRINT_PROPERTIES),\\\n    $(PRODUCT_OEM_PROPERTIES))\nKNOWN_OEM_THUMBPRINT_PROPERTIES:=\n\n# -----------------------------------------------------------------\n# system/build.prop\n#\n# system/build.prop is built by Soong. See system-build.prop module in\n# build/soong/Android.bp.\n\nINSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop\n\n# -----------------------------------------------------------------\n# vendor/build.prop\n#\n_prop_files_ := $(if $(TARGET_VENDOR_PROP),\\\n    $(TARGET_VENDOR_PROP),\\\n    $(wildcard $(TARGET_DEVICE_DIR)/vendor.prop))\n\nandroid_info_prop := $(call intermediates-dir-for,ETC,android_info_prop)/android-info.prop\n$(android_info_prop): $(INSTALLED_ANDROID_INFO_TXT_TARGET)\n\tcat $< | grep 'require version-' | sed -e 's/require version-/ro.build.expect./g' > $@\n\n_prop_files_ += $(android_info_prop)\n\nifdef property_overrides_split_enabled\n# Order matters here. When there are duplicates, the last one wins.\n# TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter\n_prop_vars_ := \\\n    ADDITIONAL_VENDOR_PROPERTIES \\\n    PRODUCT_VENDOR_PROPERTIES\n\n# TODO(b/117892318): deprecate this\n_prop_vars_ += \\\n    PRODUCT_DEFAULT_PROPERTY_OVERRIDES \\\n    PRODUCT_PROPERTY_OVERRIDES\nelse\n_prop_vars_ :=\nendif\n\nINSTALLED_VENDOR_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR)/build.prop\n$(eval $(call build-properties,\\\n    vendor,\\\n    $(INSTALLED_VENDOR_BUILD_PROP_TARGET),\\\n    $(_prop_files_),\\\n    $(_prop_vars_),\\\n    $(PRODUCT_VENDOR_PROPERTY_BLACKLIST),\\\n    $(empty),\\\n    $(empty)))\n\n$(eval $(call declare-1p-target,$(INSTALLED_VENDOR_BUILD_PROP_TARGET)))\n\n# -----------------------------------------------------------------\n# product/etc/build.prop\n#\n# product/etc/build.prop is built by Soong. See product-build.prop module in\n# build/soong/Android.bp.\n\nINSTALLED_PRODUCT_BUILD_PROP_TARGET := $(TARGET_OUT_PRODUCT)/etc/build.prop\n\n# ----------------------------------------------------------------\n# odm/etc/build.prop\n#\n# odm/etc/build.prop is built by Soong. See odm-build.prop module in\n# build/soong/Android.bp.\n\nINSTALLED_ODM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM)/etc/build.prop\n\n# ----------------------------------------------------------------\n# vendor_dlkm/etc/build.prop\n# odm_dlkm/etc/build.prop\n# system_dlkm/build.prop\n# These are built by Soong. See build/soong/Android.bp\n\nINSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR_DLKM)/etc/build.prop\nINSTALLED_ODM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM_DLKM)/etc/build.prop\nINSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_SYSTEM_DLKM)/etc/build.prop\nALL_DEFAULT_INSTALLED_MODULES += \\\n  $(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET) \\\n  $(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET) \\\n  $(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET) \\\n\n# -----------------------------------------------------------------\n# system_ext/etc/build.prop\n#\n# system_ext/etc/build.prop is built by Soong. See system-build.prop module in\n# build/soong/Android.bp.\n\nINSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET := $(TARGET_OUT_SYSTEM_EXT)/etc/build.prop\n\nRAMDISK_BUILD_PROP_REL_PATH := system/etc/ramdisk/build.prop\nifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT))\nINSTALLED_RAMDISK_BUILD_PROP_TARGET := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/$(RAMDISK_BUILD_PROP_REL_PATH)\nelse\nINSTALLED_RAMDISK_BUILD_PROP_TARGET := $(TARGET_RAMDISK_OUT)/$(RAMDISK_BUILD_PROP_REL_PATH)\nendif\n\nALL_INSTALLED_BUILD_PROP_FILES := \\\n  $(INSTALLED_BUILD_PROP_TARGET) \\\n  $(INSTALLED_VENDOR_BUILD_PROP_TARGET) \\\n  $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) \\\n  $(INSTALLED_ODM_BUILD_PROP_TARGET) \\\n  $(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET) \\\n  $(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET) \\\n  $(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET) \\\n  $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET) \\\n  $(INSTALLED_RAMDISK_BUILD_PROP_TARGET)\n\n# $1 installed file path, e.g. out/target/product/vsoc_x86_64/system/build.prop\ndefine is-build-prop\n$(if $(findstring $1,$(ALL_INSTALLED_BUILD_PROP_FILES)),Y)\nendef\n"
  },
  {
    "path": "core/sysprop_config.mk",
    "content": "# ADDITIONAL_<partition>_PROPERTIES are properties that are determined by the\n# build system itself. Don't let it be defined from outside of the core build\n# system like Android.mk or <product>.mk files.\n_additional_prop_var_names := \\\n    ADDITIONAL_SYSTEM_PROPERTIES \\\n    ADDITIONAL_VENDOR_PROPERTIES \\\n    ADDITIONAL_ODM_PROPERTIES \\\n    ADDITIONAL_PRODUCT_PROPERTIES\n\n$(foreach name, $(_additional_prop_var_names),\\\n  $(if $($(name)),\\\n    $(error $(name) must not set before here. $($(name)))\\\n  ,)\\\n  $(eval $(name) :=)\\\n)\n_additional_prop_var_names :=\n\n$(KATI_obsolete_var ADDITIONAL_SYSTEM_PROPERTIES,Use build/soong/scripts/gen_build_prop.py instead)\n$(KATI_obsolete_var ADDITIONAL_ODM_PROPERTIES,Use build/soong/scripts/gen_build_prop.py instead)\n$(KATI_obsolete_var ADDITIONAL_PRODUCT_PROPERTIES,Use build/soong/scripts/gen_build_prop.py instead)\n\n# Add cpu properties for bionic and ART.\nADDITIONAL_VENDOR_PROPERTIES += ro.bionic.arch=$(TARGET_ARCH)\nADDITIONAL_VENDOR_PROPERTIES += ro.bionic.cpu_variant=$(TARGET_CPU_VARIANT_RUNTIME)\nADDITIONAL_VENDOR_PROPERTIES += ro.bionic.2nd_arch=$(TARGET_2ND_ARCH)\nADDITIONAL_VENDOR_PROPERTIES += ro.bionic.2nd_cpu_variant=$(TARGET_2ND_CPU_VARIANT_RUNTIME)\n\nADDITIONAL_VENDOR_PROPERTIES += persist.sys.dalvik.vm.lib.2=libart.so\nADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_ARCH).variant=$(DEX2OAT_TARGET_CPU_VARIANT_RUNTIME)\nifneq ($(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),)\n  ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_ARCH).features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)\nendif\n\nifdef TARGET_2ND_ARCH\n  ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_2ND_ARCH).variant=$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT_RUNTIME)\n  ifneq ($($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),)\n    ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_2ND_ARCH).features=$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)\n  endif\nendif\n\n# Although these variables are prefixed with TARGET_RECOVERY_, they are also needed under charger\n# mode (via libminui).\nifdef TARGET_RECOVERY_DEFAULT_ROTATION\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.minui.default_rotation=$(TARGET_RECOVERY_DEFAULT_ROTATION)\nendif\nifdef TARGET_RECOVERY_OVERSCAN_PERCENT\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.minui.overscan_percent=$(TARGET_RECOVERY_OVERSCAN_PERCENT)\nendif\nifdef TARGET_RECOVERY_PIXEL_FORMAT\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.minui.pixel_format=$(TARGET_RECOVERY_PIXEL_FORMAT)\nendif\n\nifdef PRODUCT_USE_DYNAMIC_PARTITIONS\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.boot.dynamic_partitions=$(PRODUCT_USE_DYNAMIC_PARTITIONS)\nendif\n\nifdef PRODUCT_RETROFIT_DYNAMIC_PARTITIONS\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.boot.dynamic_partitions_retrofit=$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)\nendif\n\nifdef PRODUCT_SHIPPING_API_LEVEL\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.product.first_api_level=$(PRODUCT_SHIPPING_API_LEVEL)\nendif\n\nifdef PRODUCT_SHIPPING_VENDOR_API_LEVEL\n# PRODUCT_SHIPPING_VENDOR_API_LEVEL was used to set ro.vendor.api_level\n# manually for testing. To prevent using this variable for product release,\n# remove this variable and show an error message.\n$(error PRODUCT_SHIPPING_VENDOR_API_LEVEL is not available. ro.vendor.api_level\\\n  property must not be set manually)\nendif\n\nifneq ($(TARGET_BUILD_VARIANT),user)\n  ifdef PRODUCT_SET_DEBUGFS_RESTRICTIONS\n    ADDITIONAL_VENDOR_PROPERTIES += \\\n      ro.product.debugfs_restrictions.enabled=$(PRODUCT_SET_DEBUGFS_RESTRICTIONS)\n  endif\nendif\n\n# Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level.\n# This must not be defined for the non-GRF devices.\n# The values of the GRF properties will be verified by post_process_props.py\nifdef BOARD_SHIPPING_API_LEVEL\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.board.first_api_level=$(BOARD_SHIPPING_API_LEVEL)\nendif\n\n# Build system set BOARD_API_LEVEL to show the api level of the vendor API surface.\n# This must not be altered outside of build system.\nifdef BOARD_API_LEVEL\n  ADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.board.api_level?=$(BOARD_API_LEVEL)\n  ifdef BOARD_API_LEVEL_PROP_OVERRIDE\n    # This must be used only for testing purpose. Product must not be released\n    # with the modified api level value.\n    $(warning BOARD_API_LEVEL_PROP_OVERRIDE can be defined only for testing purpose)\n    ADDITIONAL_VENDOR_PROPERTIES += \\\n      ro.board.api_level=$(BOARD_API_LEVEL_PROP_OVERRIDE)\n  endif\nendif\n# RELEASE_BOARD_API_LEVEL_FROZEN is true when the vendor API surface is frozen.\nifdef RELEASE_BOARD_API_LEVEL_FROZEN\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.board.api_frozen=$(RELEASE_BOARD_API_LEVEL_FROZEN)\nendif\n\n# Set build prop. This prop is read by ota_from_target_files when generating OTA,\n# to decide if VABC should be disabled.\nifeq ($(BOARD_DONT_USE_VABC_OTA),true)\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.vendor.build.dont_use_vabc=true\nendif\n\n# Set the flag in vendor. So VTS would know if the new fingerprint format is in use when\n# the system images are replaced by GSI.\nifeq ($(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT),true)\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.vendor.build.fingerprint_has_digest=1\nendif\n\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.vendor.build.security_patch=$(VENDOR_SECURITY_PATCH) \\\n    ro.product.board=$(TARGET_BOOTLOADER_BOARD_NAME) \\\n    ro.board.platform=$(TARGET_BOARD_PLATFORM) \\\n    ro.hwui.use_vulkan=$(TARGET_USES_VULKAN)\n\nifdef TARGET_SCREEN_DENSITY\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.sf.lcd_density=$(TARGET_SCREEN_DENSITY)\nendif\n\nifdef AB_OTA_UPDATER\nADDITIONAL_VENDOR_PROPERTIES += \\\n    ro.build.ab_update=$(AB_OTA_UPDATER)\nendif\n\nifeq ($(AB_OTA_UPDATER),true)\nADDITIONAL_VENDOR_PROPERTIES += ro.vendor.build.ab_ota_partitions=$(subst $(space),$(comma),$(sort $(AB_OTA_PARTITIONS)))\nendif\n\nuser_variant := $(filter user userdebug,$(TARGET_BUILD_VARIANT))\n\nconfig_enable_uffd_gc := \\\n  $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default)\n\nADDITIONAL_VENDOR_PROPERTIES := $(strip $(ADDITIONAL_VENDOR_PROPERTIES))\n\n.KATI_READONLY += \\\n    ADDITIONAL_VENDOR_PROPERTIES\n"
  },
  {
    "path": "core/target_test_internal.mk",
    "content": "#######################################################\n## Shared definitions for all target test compilations.\n#######################################################\n\nifeq ($(LOCAL_GTEST),true)\n  LOCAL_CFLAGS += -DGTEST_OS_LINUX_ANDROID -DGTEST_HAS_STD_STRING\n\n  ifndef LOCAL_SDK_VERSION\n    LOCAL_STATIC_LIBRARIES += libgtest_main libgtest\n  else\n    # TODO(danalbert): Remove the suffix from the module since we only need the\n    # one variant now.\n    my_ndk_gtest_suffix := _c++\n    LOCAL_STATIC_LIBRARIES += \\\n        libgtest_main_ndk$(my_ndk_gtest_suffix) \\\n        libgtest_ndk$(my_ndk_gtest_suffix)\n  endif\nendif\n\nifdef LOCAL_MODULE_PATH\n$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH when building test $(LOCAL_MODULE))\nendif\n\nifdef LOCAL_MODULE_PATH_32\n$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_32 when building test $(LOCAL_MODULE))\nendif\n\nifdef LOCAL_MODULE_PATH_64\n$(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_64 when building test $(LOCAL_MODULE))\nendif\n\nuse_testcase_folder := false\nifneq ($(LOCAL_MODULE),$(filter $(LOCAL_MODULE),$(DEFAULT_DATA_OUT_MODULES)))\n  use_testcase_folder := true\nendif\n\nifneq ($(use_testcase_folder),true)\nifndef LOCAL_MODULE_RELATIVE_PATH\nLOCAL_MODULE_RELATIVE_PATH := $(LOCAL_MODULE)\nendif\nendif\n\n# Implicitly run this test under MTE SYNC for aarch64 binaries. This is a no-op\n# on non-MTE hardware.\nifneq (,$(filter arm64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)))\n  LOCAL_WHOLE_STATIC_LIBRARIES += note_memtag_heap_sync\nendif\n"
  },
  {
    "path": "core/tasks/README.dex_preopt_check.md",
    "content": "# `dex_preopt_check`\n\n`dex_preopt_check` is a build-time check to make sure that all system server\njars are dexpreopted. When the check fails, you will see the following error\nmessage:\n\n```\nFAILED:\nbuild/make/core/tasks/dex_preopt_check.mk:13: warning:  Missing compilation artifacts. Dexpreopting is not working for some system server jars\nOffending entries:\n```\n\nPossible causes are:\n\n1.  There is an APEX/SDK mismatch. (E.g., the APEX is built from source while\n    the SDK is built from prebuilt.)\n\n1.  The `systemserverclasspath_fragment` is not added as\n    `systemserverclasspath_fragments` of the corresponding `apex` module, or not\n    added as `exported_systemserverclasspath_fragments` of the corresponding\n    `prebuilt_apex`/`apex_set` module when building from prebuilt.\n\n1.  The expected version of the system server java library is not preferred.\n    (E.g., the `java_import` module has `prefer: false` when building from\n    prebuilt.)\n\n1.  Dexpreopting is disabled for the system server java library. This can be due\n    to various reasons including but not limited to:\n\n    - The java library has `dex_preopt: { enabled: false }` in the Android.bp\n      file.\n\n    - The java library is listed in `DEXPREOPT_DISABLED_MODULES` in a Makefile.\n\n    - The java library is missing `installable: true` in the Android.bp\n      file when building from source.\n\n    - Sanitizer is enabled.\n\n1.  `PRODUCT_SYSTEM_SERVER_JARS`, `PRODUCT_APEX_SYSTEM_SERVER_JARS`,\n    `PRODUCT_STANDALONE_SYSTEM_SERVER_JARS`, or\n    `PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS` has an extra entry that is not\n    needed by the product.\n"
  },
  {
    "path": "core/tasks/art-host-tests.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n.PHONY: art-host-tests\n\nintermediates_dir := $(call intermediates-dir-for,PACKAGING,art-host-tests)\nart_host_tests_zip := $(PRODUCT_OUT)/art-host-tests.zip\n# Get the hostside libraries to be packaged in the test zip. Unlike\n# device-tests.mk or general-tests.mk, the files are not copied to the\n# testcases directory.\nmy_host_shared_lib_for_art_host_tests := $(foreach f,$(COMPATIBILITY.art-host-tests.HOST_SHARED_LIBRARY.FILES),$(strip \\\n    $(eval _cmf_tuple := $(subst :, ,$(f))) \\\n    $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \\\n    $(_cmf_src)))\n\n# Create an artifact to include a list of test config files in art-host-tests.\nart_host_tests_list_zip := $(PRODUCT_OUT)/art-host-tests_list.zip\n# Create an artifact to include all test config files in art-host-tests.\nart_host_tests_configs_zip := $(PRODUCT_OUT)/art-host-tests_configs.zip\n# Create an artifact to include all shared library files in art-host-tests.\nart_host_tests_host_shared_libs_zip := $(PRODUCT_OUT)/art-host-tests_host-shared-libs.zip\n\n$(art_host_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_art_host_tests)\n$(art_host_tests_zip) : PRIVATE_art_host_tests_list_zip := $(art_host_tests_list_zip)\n$(art_host_tests_zip) : PRIVATE_art_host_tests_configs_zip := $(art_host_tests_configs_zip)\n$(art_host_tests_zip) : PRIVATE_art_host_tests_host_shared_libs_zip := $(art_host_tests_host_shared_libs_zip)\n$(art_host_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(art_host_tests_list_zip) $(art_host_tests_configs_zip) $(art_host_tests_host_shared_libs_zip)\n$(art_host_tests_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)\n$(art_host_tests_zip) : $(COMPATIBILITY.art-host-tests.FILES) $(my_host_shared_lib_for_art_host_tests) $(SOONG_ZIP)\n\trm -rf $(PRIVATE_INTERMEDIATES_DIR)\n\trm -f $@ $(PRIVATE_art_host_tests_list_zip)\n\tmkdir -p $(PRIVATE_INTERMEDIATES_DIR)\n\techo $(sort $(COMPATIBILITY.art-host-tests.FILES)) | tr \" \" \"\\n\" > $(PRIVATE_INTERMEDIATES_DIR)/list\n\tgrep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/host.list || true\n\t$(hide) touch $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list\n\t$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \\\n\t  echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \\\n\tdone\n\t$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host.list \\\n\t  -P host/testcases -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list \\\n\t  -sha256\n\tgrep -e .*\\\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list > $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list || true\n\t$(hide) $(SOONG_ZIP) -d -o $(PRIVATE_art_host_tests_configs_zip) \\\n\t  -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list\n\tgrep $(HOST_OUT) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true\n\t$(hide) $(SOONG_ZIP) -d -o $(PRIVATE_art_host_tests_host_shared_libs_zip) \\\n\t  -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list\n\tgrep -e .*\\\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_INTERMEDIATES_DIR)/art-host-tests_list\n\t$(hide) $(SOONG_ZIP) -d -o $(PRIVATE_art_host_tests_list_zip) -C $(PRIVATE_INTERMEDIATES_DIR) -f $(PRIVATE_INTERMEDIATES_DIR)/art-host-tests_list\n\nart-host-tests: $(art_host_tests_zip)\n$(call dist-for-goals, art-host-tests, $(art_host_tests_zip) $(art_host_tests_list_zip) $(art_host_tests_configs_zip) $(art_host_tests_host_shared_libs_zip))\n\n$(call declare-1p-container,$(art_host_tests_zip),)\n$(call declare-container-license-deps,$(art_host_tests_zip),$(COMPATIBILITY.art-host-tests.FILES) $(my_host_shared_lib_for_art_host_tests),$(PRODUCT_OUT)/:/)\n\ntests: art-host-tests\n\nintermediates_dir :=\nart_host_tests_zip :=\nart_host_tests_list_zip :=\nart_host_tests_configs_zip :=\nart_host_tests_host_shared_libs_zip :=\n"
  },
  {
    "path": "core/tasks/art.mk",
    "content": "# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n########################################################################\n# clean-oat rules\n#\n\n.PHONY: clean-oat\nclean-oat: clean-oat-host clean-oat-target\n\n.PHONY: clean-oat-host\nclean-oat-host:\n\tfind $(OUT_DIR) '(' -name '*.oat' -o -name '*.odex' -o -name '*.art' -o -name '*.vdex' ')' -a -type f | xargs rm -f\n\trm -rf $(TMPDIR)/*/test-*/dalvik-cache/*\n\trm -rf $(TMPDIR)/android-data/dalvik-cache/*\n"
  },
  {
    "path": "core/tasks/automotive-general-tests.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n.PHONY: automotive-general-tests\n\nautomotive_general_tests_tools := \\\n    $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar \\\n    $(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util.jar \\\n    $(HOST_OUT_JAVA_LIBRARIES)/vts-tradefed.jar \\\n\nintermediates_dir := $(call intermediates-dir-for,PACKAGING,automotive-general-tests)\nautomotive_general_tests_zip := $(PRODUCT_OUT)/automotive-general-tests.zip\n# Create an artifact to include a list of test config files in automotive-general-tests.\nautomotive_general_tests_list_zip := $(PRODUCT_OUT)/automotive-general-tests_list.zip\n\n# Filter shared entries between automotive-general-tests and automotive-tests's HOST_SHARED_LIBRARY.FILES,\n# to avoid warning about overriding commands.\nmy_host_shared_lib_for_automotive_general_tests := \\\n  $(foreach m,$(filter $(COMPATIBILITY.automotive-tests.HOST_SHARED_LIBRARY.FILES),\\\n\t   $(COMPATIBILITY.automotive-general-tests.HOST_SHARED_LIBRARY.FILES)),$(call word-colon,2,$(m)))\nmy_automotive_general_tests_shared_lib_files := \\\n  $(filter-out $(COMPATIBILITY.automotive-tests.HOST_SHARED_LIBRARY.FILES),\\\n\t $(COMPATIBILITY.automotive-general-tests.HOST_SHARED_LIBRARY.FILES))\n\nmy_host_shared_lib_for_automotive_general_tests += $(call copy-many-files,$(my_automotive_general_tests_shared_lib_files))\n\n# Create an artifact to include all test config files in automotive-general-tests.\nautomotive_general_tests_configs_zip := $(PRODUCT_OUT)/automotive-general-tests_configs.zip\n# Create an artifact to include all shared librariy files in automotive-general-tests.\nautomotive_general_tests_host_shared_libs_zip := $(PRODUCT_OUT)/automotive-general-tests_host-shared-libs.zip\n\n$(automotive_general_tests_zip) : PRIVATE_automotive_general_tests_list_zip := $(automotive_general_tests_list_zip)\n$(automotive_general_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(automotive_general_tests_list_zip) $(automotive_general_tests_configs_zip) $(automotive_general_tests_host_shared_libs_zip)\n$(automotive_general_tests_zip) : PRIVATE_TOOLS := $(automotive_general_tests_tools)\n$(automotive_general_tests_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)\n$(automotive_general_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_automotive_general_tests)\n$(automotive_general_tests_zip) : PRIVATE_automotive_general_tests_configs_zip := $(automotive_general_tests_configs_zip)\n$(automotive_general_tests_zip) : PRIVATE_general_host_shared_libs_zip := $(automotive_general_tests_host_shared_libs_zip)\n$(automotive_general_tests_zip) : $(COMPATIBILITY.automotive-general-tests.FILES) $(automotive_general_tests_tools) $(my_host_shared_lib_for_automotive_general_tests) $(SOONG_ZIP)\n\trm -rf $(PRIVATE_INTERMEDIATES_DIR)\n\trm -f $@ $(PRIVATE_automotive_general_tests_list_zip)\n\tmkdir -p $(PRIVATE_INTERMEDIATES_DIR) $(PRIVATE_INTERMEDIATES_DIR)/tools\n\techo $(sort $(COMPATIBILITY.automotive-general-tests.FILES)) | tr \" \" \"\\n\" > $(PRIVATE_INTERMEDIATES_DIR)/list\n\tgrep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/host.list || true\n\tgrep $(TARGET_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/target.list || true\n\tgrep -e .*\\\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list > $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list || true\n\tgrep -e .*\\\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list > $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list || true\n\t$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \\\n\t  echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/host.list; \\\n\t  echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \\\n\tdone\n\tgrep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true\n\tcp -fp $(PRIVATE_TOOLS) $(PRIVATE_INTERMEDIATES_DIR)/tools/\n\t$(SOONG_ZIP) -d -o $@ \\\n\t  -P host -C $(PRIVATE_INTERMEDIATES_DIR) -D $(PRIVATE_INTERMEDIATES_DIR)/tools \\\n\t  -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host.list \\\n\t  -P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target.list\n\t$(SOONG_ZIP) -d -o $(PRIVATE_automotive_general_tests_configs_zip) \\\n\t  -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list \\\n\t  -P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list\n\t$(SOONG_ZIP) -d -o $(PRIVATE_general_host_shared_libs_zip) \\\n\t  -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list\n\tgrep -e .*\\\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_INTERMEDIATES_DIR)/automotive-general-tests_list\n\tgrep -e .*\\\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_INTERMEDIATES_DIR)/automotive-general-tests_list\n\t$(SOONG_ZIP) -d -o $(PRIVATE_automotive_general_tests_list_zip) -C $(PRIVATE_INTERMEDIATES_DIR) -f $(PRIVATE_INTERMEDIATES_DIR)/automotive-general-tests_list\n\nautomotive-general-tests: $(automotive_general_tests_zip)\n$(call dist-for-goals, automotive-general-tests, $(automotive_general_tests_zip) $(automotive_general_tests_list_zip) $(automotive_general_tests_configs_zip) $(automotive_general_tests_host_shared_libs_zip))\n\n$(call declare-1p-container,$(automotive_general_tests_zip),)\n$(call declare-container-license-deps,$(automotive_general_tests_zip),$(COMPATIBILITY.automotive-general-tests.FILES) $(automotive_general_tests_tools) $(my_host_shared_lib_for_automotive_general_tests),$(PRODUCT_OUT)/:/)\n\nintermediates_dir :=\nautomotive_general_tests_tools :=\nautomotive_general_tests_zip :=\nautomotive_general_tests_list_zip :=\nautomotive_general_tests_configs_zip :=\nautomotive_general_tests_host_shared_libs_zip :=\n"
  },
  {
    "path": "core/tasks/automotive-sdv-tests.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\n.PHONY: automotive-sdv-tests\n\nautomotive-sdv-tests-zip := $(PRODUCT_OUT)/automotive-sdv-tests.zip\n# Create an artifact to include a list of test config files in automotive-sdv-tests.\nautomotive-sdv-tests-list-zip := $(PRODUCT_OUT)/automotive-sdv-tests_list.zip\n# Create an artifact to include all test config files in automotive-sdv-tests.\nautomotive-sdv-tests-configs-zip := $(PRODUCT_OUT)/automotive-sdv-tests_configs.zip\nmy_host_shared_lib_for_automotive_sdv_tests := $(call copy-many-files,$(COMPATIBILITY.automotive-sdv-tests.HOST_SHARED_LIBRARY.FILES))\nautomotive_sdv_tests_host_shared_libs_zip := $(PRODUCT_OUT)/automotive-sdv-tests_host-shared-libs.zip\n\n$(automotive-sdv-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(automotive-sdv-tests-list-zip) $(automotive-sdv-tests-configs-zip) $(automotive_sdv_tests_host_shared_libs_zip)\n$(automotive-sdv-tests-zip) : PRIVATE_automotive_sdv_tests_list := $(PRODUCT_OUT)/automotive-sdv-tests_list\n$(automotive-sdv-tests-zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_automotive_sdv_tests)\n$(automotive-sdv-tests-zip) : PRIVATE_automotive_host_shared_libs_zip := $(automotive_sdv_tests_host_shared_libs_zip)\n$(automotive-sdv-tests-zip) : $(COMPATIBILITY.automotive-sdv-tests.FILES) $(my_host_shared_lib_for_automotive_sdv_tests) $(SOONG_ZIP)\n\trm -f $@-shared-libs.list\n\techo $(sort $(COMPATIBILITY.automotive-sdv-tests.FILES)) | tr \" \" \"\\n\" > $@.list\n\tgrep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true\n\tgrep -e .*\\\\.config$$ $@-host.list > $@-host-test-configs.list || true\n\t$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \\\n\t  echo $$shared_lib >> $@-host.list; \\\n\t  echo $$shared_lib >> $@-shared-libs.list; \\\n\tdone\n\tgrep $(HOST_OUT_TESTCASES) $@-shared-libs.list > $@-host-shared-libs.list || true\n\tgrep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true\n\tgrep -e .*\\\\.config$$ $@-target.list > $@-target-test-configs.list || true\n\t$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list\n\t$(hide) $(SOONG_ZIP) -d -o $(automotive-sdv-tests-configs-zip) \\\n\t  -P host -C $(HOST_OUT) -l $@-host-test-configs.list \\\n\t  -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list\n\t$(SOONG_ZIP) -d -o $(PRIVATE_automotive_host_shared_libs_zip) \\\n\t  -P host -C $(HOST_OUT) -l $@-host-shared-libs.list\n\trm -f $(PRIVATE_automotive_sdv_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_automotive_sdv_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_automotive_sdv_tests_list)\n\t$(hide) $(SOONG_ZIP) -d -o $(automotive-sdv-tests-list-zip) -C $(dir $@) -f $(PRIVATE_automotive_sdv_tests_list)\n\trm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \\\n\t  $@-shared-libs.list $@-host-shared-libs.list $(PRIVATE_automotive_sdv_tests_list)\n\nautomotive-sdv-tests: $(automotive-sdv-tests-zip)\n$(call dist-for-goals, automotive-sdv-tests, $(automotive-sdv-tests-zip) $(automotive-sdv-tests-list-zip) $(automotive-sdv-tests-configs-zip) $(automotive_sdv_tests_host_shared_libs_zip))\n\n$(call declare-1p-container,$(automotive-sdv-tests-zip),)\n$(call declare-container-license-deps,$(automotive-sdv-tests-zip),$(COMPATIBILITY.automotive-sdv-tests.FILES) $(my_host_shared_lib_for_automotive_sdv_tests),$(PRODUCT_OUT)/:/)\n\ntests: automotive-sdv-tests\n"
  },
  {
    "path": "core/tasks/automotive-tests.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\n.PHONY: automotive-tests\n\nautomotive-tests-zip := $(PRODUCT_OUT)/automotive-tests.zip\n# Create an artifact to include a list of test config files in automotive-tests.\nautomotive-tests-list-zip := $(PRODUCT_OUT)/automotive-tests_list.zip\n# Create an artifact to include all test config files in automotive-tests.\nautomotive-tests-configs-zip := $(PRODUCT_OUT)/automotive-tests_configs.zip\nmy_host_shared_lib_for_automotive_tests := $(call copy-many-files,$(COMPATIBILITY.automotive-tests.HOST_SHARED_LIBRARY.FILES))\nautomotive_tests_host_shared_libs_zip := $(PRODUCT_OUT)/automotive-tests_host-shared-libs.zip\n\n$(automotive-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(automotive-tests-list-zip) $(automotive-tests-configs-zip) $(automotive_tests_host_shared_libs_zip)\n$(automotive-tests-zip) : PRIVATE_automotive_tests_list := $(PRODUCT_OUT)/automotive-tests_list\n$(automotive-tests-zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_automotive_tests)\n$(automotive-tests-zip) : PRIVATE_automotive_host_shared_libs_zip := $(automotive_tests_host_shared_libs_zip)\n$(automotive-tests-zip) : $(COMPATIBILITY.automotive-tests.FILES) $(my_host_shared_lib_for_automotive_tests) $(SOONG_ZIP)\n\trm -f $@-shared-libs.list\n\techo $(sort $(COMPATIBILITY.automotive-tests.FILES)) | tr \" \" \"\\n\" > $@.list\n\tgrep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true\n\tgrep -e .*\\\\.config$$ $@-host.list > $@-host-test-configs.list || true\n\t$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \\\n\t  echo $$shared_lib >> $@-host.list; \\\n\t  echo $$shared_lib >> $@-shared-libs.list; \\\n\tdone\n\tgrep $(HOST_OUT_TESTCASES) $@-shared-libs.list > $@-host-shared-libs.list || true\n\tgrep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true\n\tgrep -e .*\\\\.config$$ $@-target.list > $@-target-test-configs.list || true\n\t$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list\n\t$(hide) $(SOONG_ZIP) -d -o $(automotive-tests-configs-zip) \\\n\t  -P host -C $(HOST_OUT) -l $@-host-test-configs.list \\\n\t  -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list\n\t$(SOONG_ZIP) -d -o $(PRIVATE_automotive_host_shared_libs_zip) \\\n\t  -P host -C $(HOST_OUT) -l $@-host-shared-libs.list\n\trm -f $(PRIVATE_automotive_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_automotive_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_automotive_tests_list)\n\t$(hide) $(SOONG_ZIP) -d -o $(automotive-tests-list-zip) -C $(dir $@) -f $(PRIVATE_automotive_tests_list)\n\trm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \\\n\t  $@-shared-libs.list $@-host-shared-libs.list $(PRIVATE_automotive_tests_list)\n\nautomotive-tests: $(automotive-tests-zip)\n$(call dist-for-goals, automotive-tests, $(automotive-tests-zip) $(automotive-tests-list-zip) $(automotive-tests-configs-zip) $(automotive_tests_host_shared_libs_zip))\n\n$(call declare-1p-container,$(automotive-tests-zip),)\n$(call declare-container-license-deps,$(automotive-tests-zip),$(COMPATIBILITY.automotive-tests.FILES) $(my_host_shared_lib_for_automotive_tests),$(PRODUCT_OUT)/:/)\n\ntests: automotive-tests\n"
  },
  {
    "path": "core/tasks/autorepro.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nifneq ($(wildcard test/sts/README-autorepro.md),)\ntest_suite_name := autorepro\ntest_suite_tradefed := sts-tradefed\ntest_suite_readme := test/sts/README-autorepro.md\nautorepro_zip := $(HOST_OUT)/$(test_suite_name)/autorepro.zip\n\ninclude $(BUILD_SYSTEM)/tasks/tools/compatibility.mk\n\nautorepro_plugin_skel := $(call intermediates-dir-for,ETC,autorepro-plugin-skel.zip)/autorepro-plugin-skel.zip\n\n$(autorepro_zip): AUTOREPRO_ZIP := $(compatibility_zip)\n$(autorepro_zip): AUTOREPRO_PLUGIN_SKEL := $(autorepro_plugin_skel)\n$(autorepro_zip): $(MERGE_ZIPS) $(ZIP2ZIP) $(compatibility_zip) $(autorepro_plugin_skel)\n\trm -f $@ $(AUTOREPRO_ZIP)_filtered\n\t$(ZIP2ZIP) -i $(AUTOREPRO_ZIP) -o $(AUTOREPRO_ZIP)_filtered \\\n\t\t-x android-autorepro/tools/sts-tradefed-tests.jar \\\n\t\t'android-autorepro/tools/*:autorepro/src/main/resources/sts-tradefed-tools/'\n\t$(MERGE_ZIPS) $@ $(AUTOREPRO_ZIP)_filtered $(AUTOREPRO_PLUGIN_SKEL)\n\trm -f $(AUTOREPRO_ZIP)_filtered\n\n.PHONY: autorepro\nautorepro: $(autorepro_zip)\n$(call dist-for-goals, autorepro, $(autorepro_zip))\n\nendif\n"
  },
  {
    "path": "core/tasks/berberis_test.mk",
    "content": "#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nBERBERIS_DIR := frameworks/libs/binary_translation\n\n# Berberis includes some components which may conflict with other packages.\n# Only build it when requested explicitly.\nifeq ($(BUILD_BERBERIS),true)\n\ninclude $(BERBERIS_DIR)/tests/run_host_tests.mk\n\nendif  # BUILD_BERBERIS\n"
  },
  {
    "path": "core/tasks/build_custom_images.mk",
    "content": "#\n# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Build additional images requested by the product makefile.\n# This script gives the ability to build multiple additional images and you can\n# configure what modules/files to include in each image.\n# 1. Define PRODUCT_CUSTOM_IMAGE_MAKEFILES in your product makefile.\n#    PRODUCT_CUSTOM_IMAGE_MAKEFILES is a list of makefiles.\n#    Each makefile configures an image.\n#    For image configuration makefile foo/bar/xyz.mk, the built image file name\n#    will be xyz.img. So make sure they won't conflict.\n# 2. In each image's configuration makefile, you can define variables:\n#   - CUSTOM_IMAGE_MOUNT_POINT, the mount point, such as \"oem\", \"odm\" etc.\n#   - CUSTOM_IMAGE_PARTITION_SIZE\n#   - CUSTOM_IMAGE_FILE_SYSTEM_TYPE\n#   - CUSTOM_IMAGE_DICT_FILE, a text file defines a dictionary accepted by\n#     BuildImage() in tools/releasetools/build_image.py.\n#   - CUSTOM_IMAGE_MODULES, a list of module names you want to include in\n#     the image; Not only the module itself will be installed to proper path in\n#     the image, you can also piggyback additional files/directories with the\n#     module's LOCAL_PICKUP_FILES.\n#   - CUSTOM_IMAGE_COPY_FILES, a list of \"<src>:<dest>\" to be copied to the\n#     image. <dest> is relativ to the root of the image.\n#   - CUSTOM_IMAGE_SELINUX, set to \"true\" if the image supports selinux.\n#   - CUSTOM_IMAGE_SUPPORT_VERITY, set to \"true\" if the product supports verity.\n#   - CUSTOM_IMAGE_SUPPORT_VERITY_FEC, set to \"true\" if the product supports\n#     verity FEC (forward error correction).\n#   - CUSTOM_IMAGE_VERITY_BLOCK_DEVICE\n#   - CUSTOM_IMAGE_AVB_HASH_ENABLE, set to \"true\" to add AVB HASH footer.\n#   - CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS, additional args of AVB HASH footer.\n#   - CUSTOM_IMAGE_AVB_HASHTREE_ENABLE, set to \"true\" to add AVB HASHTREE\n#     footer.\n#   - CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS, additional args of AVB\n#     HASHTREE footer.\n#   - CUSTOM_IMAGE_AVB_KEY_PATH, custom AVB signing key.\n#   - CUSTOM_IMAGE_AVB_ALGORITHM, custom AVB signing algorithm.\n#\n# To build all those images, run \"make custom_images\".\n\nifneq ($(filter $(MAKECMDGOALS),custom_images),)\n\n.PHONY: custom_images\n\ncustom_image_parameter_variables := \\\n  CUSTOM_IMAGE_MOUNT_POINT \\\n  CUSTOM_IMAGE_PARTITION_SIZE \\\n  CUSTOM_IMAGE_FILE_SYSTEM_TYPE \\\n  CUSTOM_IMAGE_DICT_FILE \\\n  CUSTOM_IMAGE_MODULES \\\n  CUSTOM_IMAGE_COPY_FILES \\\n  CUSTOM_IMAGE_SELINUX \\\n  CUSTOM_IMAGE_VERITY_BLOCK_DEVICE \\\n  CUSTOM_IMAGE_AVB_HASH_ENABLE \\\n  CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS \\\n  CUSTOM_IMAGE_AVB_HASHTREE_ENABLE \\\n  CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS \\\n  CUSTOM_IMAGE_AVB_KEY_PATH \\\n  CUSTOM_IMAGE_AVB_ALGORITHM \\\n\n# We don't expect product makefile to inherit/override PRODUCT_CUSTOM_IMAGE_MAKEFILES,\n# so we don't put it in the _product_var_list.\n$(foreach mk, $(PRODUCT_CUSTOM_IMAGE_MAKEFILES),\\\n  $(eval my_custom_imag_makefile := $(mk))\\\n  $(eval include $(BUILD_SYSTEM)/tasks/tools/build_custom_image.mk))\n\nendif\n"
  },
  {
    "path": "core/tasks/catbox.mk",
    "content": "# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ntest_suite_name := catbox\ntest_suite_tradefed := catbox-tradefed\ntest_suite_readme := test/catbox/tools/catbox-tradefed/README\ntest_suite_tools := $(HOST_OUT_JAVA_LIBRARIES)/catbox-report-lib.jar\n\ninclude $(BUILD_SYSTEM)/tasks/tools/compatibility.mk\n\n.PHONY: catbox\ncatbox: $(compatibility_zip)\n$(call dist-for-goals, catbox, $(compatibility_zip))"
  },
  {
    "path": "core/tasks/check-abi-dump-list.mk",
    "content": "# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n#####################################################################\n# Check the generate list against the latest list stored in the\n# source tree\n.PHONY: check-abi-dump-list\n\n# Check if vndk list is changed\ndroidcore: check-abi-dump-list\n\ncheck-abi-dump-list-timestamp := $(call intermediates-dir-for,PACKAGING,vndk)/check-abi-dump-list-timestamp\n\n# The ABI tool does not support sanitizer and coverage builds.\nifeq (,$(filter true,$(SKIP_ABI_CHECKS) $(CLANG_COVERAGE)))\nifeq (,$(SANITIZE_TARGET))\ncheck-abi-dump-list: $(check-abi-dump-list-timestamp)\nendif\nendif\n\n#####################################################################\n# ABI reference dumps.\n\n# LSDUMP_PATHS is a list of tag:path. They are written to LSDUMP_PATHS_FILE.\nLSDUMP_PATHS_FILE := $(PRODUCT_OUT)/lsdump_paths.txt\n\n$(LSDUMP_PATHS_FILE): PRIVATE_LSDUMP_PATHS := $(LSDUMP_PATHS)\n$(LSDUMP_PATHS_FILE):\n\t@echo \"Generate $@\"\n\t@rm -rf $@ && echo -e \"$(subst :,:$(space),$(subst $(space),\\n,$(PRIVATE_LSDUMP_PATHS)))\" > $@\n\n# $(1): A list of tags.\n# $(2): A list of tag:path.\n# Return the file paths of the ABI dumps that match the tags.\ndefine filter-abi-dump-paths\n$(eval tag_patterns := $(addsuffix :%,$(1)))\n$(patsubst $(tag_patterns),%,$(filter $(tag_patterns),$(2)))\nendef\n\n# Subsets of LSDUMP_PATHS.\n.PHONY: findlsdumps_APEX\nfindlsdumps_APEX: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,APEX,$(LSDUMP_PATHS))\n\n.PHONY: findlsdumps_LLNDK\nfindlsdumps_LLNDK: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,LLNDK,$(LSDUMP_PATHS))\n\n.PHONY: findlsdumps_NDK\nfindlsdumps_NDK: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,NDK,$(LSDUMP_PATHS))\n\n.PHONY: findlsdumps_PLATFORM\nfindlsdumps_PLATFORM: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,PLATFORM,$(LSDUMP_PATHS))\n\n.PHONY: findlsdumps\nfindlsdumps: $(LSDUMP_PATHS_FILE) $(foreach p,$(LSDUMP_PATHS),$(call word-colon,2,$(p)))\n\n#####################################################################\n# Check that all ABI reference dumps have corresponding\n# APEX/LLNDK/PLATFORM libraries.\n\n# $(1): The directory containing ABI dumps.\n# Return a list of ABI dump paths ending with .so.lsdump.\ndefine find-abi-dump-paths\n$(if $(wildcard $(1)), \\\n  $(addprefix $(1)/, \\\n    $(call find-files-in-subdirs,$(1),\"*.so.lsdump\" -and -type f,.)))\nendef\n\n# $(1): A list of tags.\n# $(2): A list of tag:path.\n# Return the file names of the ABI dumps that match the tags, and replace the\n# file name extensions with .so.lsdump.\ndefine filter-abi-dump-names\n$(patsubst %.so.llndk.lsdump,%.so.lsdump, \\\n  $(patsubst %.so.apex.lsdump,%.so.lsdump, \\\n    $(notdir $(call filter-abi-dump-paths,$(1),$(2)))))\nendef\n\nVNDK_ABI_DUMP_DIR := prebuilts/abi-dumps/vndk/$(RELEASE_BOARD_API_LEVEL)\nifeq (REL,$(PLATFORM_VERSION_CODENAME))\n    PLATFORM_ABI_DUMP_DIR := prebuilts/abi-dumps/platform/$(PLATFORM_SDK_VERSION)\nelse\n    PLATFORM_ABI_DUMP_DIR := prebuilts/abi-dumps/platform/current\nendif\nVNDK_ABI_DUMPS := $(call find-abi-dump-paths,$(VNDK_ABI_DUMP_DIR))\nPLATFORM_ABI_DUMPS := $(call find-abi-dump-paths,$(PLATFORM_ABI_DUMP_DIR))\n\n# Check for superfluous lsdump files. Since LSDUMP_PATHS only covers the\n# libraries that can be built from source in the current build, and prebuilts of\n# Mainline modules may be in use, we also allow the libs in STUB_LIBRARIES for\n# platform ABIs.\n# In addition, libRS is allowed because it's disabled for RISC-V.\n\n$(check-abi-dump-list-timestamp): PRIVATE_LSDUMP_PATHS := $(LSDUMP_PATHS)\n$(check-abi-dump-list-timestamp): PRIVATE_STUB_LIBRARIES := $(STUB_LIBRARIES)\n$(check-abi-dump-list-timestamp):\n\t$(eval added_vndk_abi_dumps := $(strip $(sort $(filter-out \\\n\t  $(call filter-abi-dump-names,LLNDK,$(PRIVATE_LSDUMP_PATHS)) libRS.so.lsdump, \\\n\t  $(notdir $(VNDK_ABI_DUMPS))))))\n\t$(if $(added_vndk_abi_dumps), \\\n\t  echo -e \"Found unexpected ABI reference dump files under $(VNDK_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \\`find \\$${ANDROID_BUILD_TOP}/$(VNDK_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_vndk_abi_dumps)) ')' -delete\\` to delete the dump files.\")\n\n\t# TODO(b/314010764): Remove LLNDK tag after PLATFORM_SDK_VERSION is upgraded to 35.\n\t$(eval added_platform_abi_dumps := $(strip $(sort $(filter-out \\\n\t  $(call filter-abi-dump-names,APEX LLNDK PLATFORM,$(PRIVATE_LSDUMP_PATHS)) \\\n\t  $(addsuffix .lsdump,$(PRIVATE_STUB_LIBRARIES)) libRS.so.lsdump, \\\n\t  $(notdir $(PLATFORM_ABI_DUMPS))))))\n\t$(if $(added_platform_abi_dumps), \\\n\t  echo -e \"Found unexpected ABI reference dump files under $(PLATFORM_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \\`find \\$${ANDROID_BUILD_TOP}/$(PLATFORM_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_platform_abi_dumps)) ')' -delete\\` to delete the dump files.\")\n\n\t$(if $(added_vndk_abi_dumps)$(added_platform_abi_dumps),exit 1)\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) touch $@\n"
  },
  {
    "path": "core/tasks/csuite.mk",
    "content": "# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ntest_suite_name := csuite\ntest_suite_tradefed := csuite-tradefed\ntest_suite_readme := test/app_compat/csuite/README.md\n\ninclude $(BUILD_SYSTEM)/tasks/tools/compatibility.mk\n\n.PHONY: csuite\ncsuite: $(compatibility_zip)\n$(call dist-for-goals, csuite, $(compatibility_zip))\n"
  },
  {
    "path": "core/tasks/cts.mk",
    "content": "# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ntest_suite_name := cts\ntest_suite_tradefed := cts-tradefed\ntest_suite_dynamic_config := cts/tools/cts-tradefed/DynamicConfig.xml\ntest_suite_readme := cts/tools/cts-tradefed/README\ntest_suite_tools := $(HOST_OUT_JAVA_LIBRARIES)/ats_console_deploy.jar \\\n  $(HOST_OUT_JAVA_LIBRARIES)/ats_olc_server_local_mode_deploy.jar\n\n$(call declare-1p-target,$(test_suite_dynamic_config),cts)\n$(call declare-1p-target,$(test_suite_readme),cts)\n\ninclude $(BUILD_SYSTEM)/tasks/tools/compatibility.mk\n\n.PHONY: cts\ncts: $(compatibility_zip) $(compatibility_tests_list_zip)\n$(call dist-for-goals, cts, $(compatibility_zip) $(compatibility_tests_list_zip))\n\n.PHONY: cts_v2\ncts_v2: cts\n\n# platform version check (b/32056228)\n# ============================================================\nifneq (,$(wildcard cts/))\n  cts_platform_version_path := cts/tests/tests/os/assets/platform_versions.txt\n  cts_platform_version_string := $(shell cat $(cts_platform_version_path))\n  cts_platform_release_path := cts/tests/tests/os/assets/platform_releases.txt\n  cts_platform_release_string := $(shell cat $(cts_platform_release_path))\n\n  ifneq (REL,$(PLATFORM_VERSION_CODENAME))\n    ifeq (,$(findstring $(PLATFORM_VERSION),$(cts_platform_version_string)))\n      define error_msg\n        ============================================================\n        Could not find version \"$(PLATFORM_VERSION)\" in CTS platform version file:\n        $(cts_platform_version_path)\n        Most likely PLATFORM_VERSION in build/core/version_defaults.mk\n        has changed and a new version must be added to this CTS file.\n        ============================================================\n      endef\n      $(error $(error_msg))\n    endif\n  endif\n  ifeq (,$(findstring $(PLATFORM_VERSION_LAST_STABLE),$(cts_platform_release_string)))\n    define error_msg\n      ============================================================\n      Could not find version \"$(PLATFORM_VERSION_LAST_STABLE)\" in CTS platform release file:\n      $(cts_platform_release_path)\n      Most likely PLATFORM_VERSION_LAST_STABLE in build/core/version_defaults.mk\n      has changed and a new version must be added to this CTS file.\n      ============================================================\n    endef\n    $(error $(error_msg))\n  endif\nendif\n\n# Creates a \"cts-verifier\" directory that will contain:\n#\n# 1. Out directory with a \"android-cts-verifier\" containing the CTS Verifier\n#    and other binaries it needs.\n#\n# 2. Zipped version of the android-cts-verifier directory to be included with\n#    the build distribution.\n##\ncts-dir := $(HOST_OUT)/cts-verifier\nverifier-dir-name := android-cts-verifier\nverifier-dir := $(cts-dir)/$(verifier-dir-name)\nverifier-zip-name := $(verifier-dir-name).zip\nverifier-zip := $(cts-dir)/$(verifier-zip-name)\n\ncts : $(verifier-zip)\n$(verifier-zip): PRIVATE_DIR := $(cts-dir)\n$(verifier-zip): $(SOONG_ANDROID_CTS_VERIFIER_ZIP)\n\trm -rf $(PRIVATE_DIR)\n\tmkdir -p $(PRIVATE_DIR)\n\tunzip -q -d $(PRIVATE_DIR) $<\n\t$(copy-file-to-target)\n\n# For producing CTS coverage reports.\n# Run \"make cts-test-coverage\" in the $ANDROID_BUILD_TOP directory.\n\ncts_api_coverage_exe := $(HOST_OUT_EXECUTABLES)/cts-api-coverage\ndexdeps_exe := $(HOST_OUT_EXECUTABLES)/dexdeps\ncts_api_map_exe := $(HOST_OUT_EXECUTABLES)/cts-api-map\n\ncoverage_out := $(HOST_OUT)/cts-api-coverage\napi_map_out := $(HOST_OUT)/cts-api-map\n\ncts_jar_files := $(api_map_out)/cts_jar_files.txt\ncts_v_host_jar_files := $(api_map_out)/cts_v_host_jar_files.txt\ncts_all_jar_files := $(api_map_out)/cts_all_jar_files.txt\n\n$(cts_jar_files): PRIVATE_API_MAP_FILES := $(sort $(COMPATIBILITY.cts.API_MAP_FILES))\n$(cts_jar_files):\n\tmkdir -p $(dir $@)\n\techo $(PRIVATE_API_MAP_FILES) > $@\n\n$(cts_v_host_jar_files): PRIVATE_API_MAP_FILES := $(sort $(COMPATIBILITY.cts-v-host.API_MAP_FILES))\n$(cts_v_host_jar_files): $(SOONG_ANDROID_CTS_VERIFIER_APP_LIST)\n\tmkdir -p $(dir $@)\n\tcp $< $@\n\techo $(PRIVATE_API_MAP_FILES) >> $@\n\n$(cts_all_jar_files): PRIVATE_API_MAP_FILES := $(sort $(COMPATIBILITY.cts.API_MAP_FILES) \\\n                                                      $(COMPATIBILITY.cts-v-host.API_MAP_FILES))\n$(cts_all_jar_files): $(SOONG_ANDROID_CTS_VERIFIER_APP_LIST)\n\tmkdir -p $(dir $@)\n\tcp $< $@\n\techo $(PRIVATE_API_MAP_FILES) >> $@\n\napi_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/api.xml\n\nnapi_text_description := cts/tools/cts-api-coverage/etc/ndk-api.xml\nnapi_xml_description := $(coverage_out)/ndk-api.xml\n$(napi_xml_description) : $(napi_text_description) $(ACP)\n\t\t$(hide) echo \"Preparing NDK API XML: $@\"\n\t\t$(hide) mkdir -p $(dir $@)\n\t\t$(hide) $(ACP)  $< $@\n\nsystem_api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/system-api.xml\nmodule_lib_api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/module-lib-api.xml\nsystem_service_api_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/system-server-api.xml\n\ncombined_api_xml_description := $(api_xml_description) \\\n  $(system_api_xml_description) \\\n  $(module_lib_api_xml_description) \\\n  $(system_service_api_description)\n\ncts-test-coverage-report := $(coverage_out)/test-coverage.html\ncts-system-api-coverage-report := $(coverage_out)/system-api-coverage.html\ncts-system-api-xml-coverage-report := $(coverage_out)/system-api-coverage.xml\ncts-verifier-coverage-report := $(coverage_out)/verifier-coverage.html\ncts-combined-coverage-report := $(coverage_out)/combined-coverage.html\ncts-combined-xml-coverage-report := $(coverage_out)/combined-coverage.xml\n\ncts_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(api_xml_description) $(napi_xml_description)\ncts_system_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(system_api_xml_description)\n\ncts-api-map-xml-report := $(api_map_out)/cts-api-map.xml\ncts-v-host-api-map-xml-report := $(api_map_out)/cts-v-host-api-map.xml\ncts-combined-api-map-xml-report := $(api_map_out)/cts-combined-api-map.xml\ncts-combined-api-map-html-report := $(api_map_out)/cts-combined-api-map.html\ncts-combined-api-inherit-xml-report := $(api_map_out)/cts-combined-api-inherit.xml\n\ncts_api_map_dependencies := $(cts_api_map_exe) $(combined_api_xml_description) $(cts_jar_files)\ncts_v_host_api_map_dependencies := $(cts_api_map_exe) $(combined_api_xml_description) $(cts_v_host_jar_files)\ncts_combined_api_map_dependencies := $(cts_api_map_exe) $(combined_api_xml_description) $(cts_all_jar_files)\n\nandroid_cts_zip := $(HOST_OUT)/cts/android-cts.zip\ncts_verifier_apk := $(call intermediates-dir-for,APPS,CtsVerifier)/package.apk\n\n$(cts-test-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)\n$(cts-test-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)\n$(cts-test-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)\n$(cts-test-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)\n$(cts-test-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)\n$(cts-test-coverage-report) : $(android_cts_zip) $(cts_api_coverage_dependencies) | $(ACP)\n\t$(call generate-coverage-report-cts,\"CTS Tests API-NDK Coverage Report\",\\\n\t\t\t$(PRIVATE_TEST_CASES),html)\n\n$(cts-system-api-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)\n$(cts-system-api-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)\n$(cts-system-api-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)\n$(cts-system-api-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)\n$(cts-system-api-coverage-report): PRIVATE_NAPI_XML_DESC := \"\"\n$(cts-system-api-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP)\n\t$(call generate-coverage-report-cts,\"CTS System API Coverage Report\",\\\n\t\t\t$(PRIVATE_TEST_CASES),html)\n\n$(cts-system-api-xml-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)\n$(cts-system-api-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)\n$(cts-system-api-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)\n$(cts-system-api-xml-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)\n$(cts-system-api-xml-coverage-report): PRIVATE_NAPI_XML_DESC := \"\"\n$(cts-system-api-xml-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP)\n\t$(call generate-coverage-report-cts,\"CTS System API Coverage Report - XML\",\\\n\t\t\t$(PRIVATE_TEST_CASES),xml)\n\n$(cts-verifier-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(verifier-dir), $(c))\n$(cts-verifier-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)\n$(cts-verifier-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)\n$(cts-verifier-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)\n$(cts-verifier-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)\n$(cts-verifier-coverage-report) : $(cts_verifier_apk) $(verifier-zip) $(cts_api_coverage_dependencies) | $(ACP)\n\t$(call generate-coverage-report-cts,\"CTS Verifier API Coverage Report\",\\\n\t\t\t$(PRIVATE_TEST_CASES),html)\n\n$(cts-combined-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts) $(verifier-dir), $(c))\n$(cts-combined-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)\n$(cts-combined-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)\n$(cts-combined-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)\n$(cts-combined-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)\n$(cts-combined-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(verifier-zip) $(cts_api_coverage_dependencies) | $(ACP)\n\t$(call generate-coverage-report-cts,\"CTS Combined API Coverage Report\",\\\n\t\t\t$(PRIVATE_TEST_CASES),html)\n\n$(cts-combined-xml-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts) $(verifier-dir), $(c))\n$(cts-combined-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)\n$(cts-combined-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)\n$(cts-combined-xml-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)\n$(cts-combined-xml-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)\n$(cts-combined-xml-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(verifier-zip) $(cts_api_coverage_dependencies) | $(ACP)\n\t$(call generate-coverage-report-cts,\"CTS Combined API Coverage Report - XML\",\\\n\t\t\t$(PRIVATE_TEST_CASES),xml)\n\n.PHONY: cts-test-coverage\ncts-test-coverage : $(cts-test-coverage-report)\n\n.PHONY: cts-system-api-coverage\ncts-system-api-coverage : $(cts-system-api-coverage-report)\n\n.PHONY: cts-system-api-xml-coverage\ncts-system-api-xml-coverage : $(cts-system-api-xml-coverage-report)\n\n.PHONY: cts-verifier-coverage\ncts-verifier-coverage : $(cts-verifier-coverage-report)\n\n.PHONY: cts-combined-coverage\ncts-combined-coverage : $(cts-combined-coverage-report)\n\n.PHONY: cts-combined-xml-coverage\ncts-combined-xml-coverage : $(cts-combined-xml-coverage-report)\n\n.PHONY: cts-coverage-report-all cts-api-coverage\ncts-coverage-report-all: cts-test-coverage cts-verifier-coverage cts-combined-coverage cts-combined-xml-coverage\n\n$(cts-api-map-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)\n$(cts-api-map-xml-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description)\n$(cts-api-map-xml-report): PRIVATE_JAR_FILES := $(cts_jar_files)\n$(cts-api-map-xml-report) : $(android_cts_zip) $(cts_api_map_dependencies) | $(ACP)\n\t$(call generate-api-map-report-cts,\"CTS API MAP Report - XML\",\\\n\t\t\t$(PRIVATE_JAR_FILES),xml)\n\n$(cts-v-host-api-map-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)\n$(cts-v-host-api-map-xml-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description)\n$(cts-v-host-api-map-xml-report): PRIVATE_JAR_FILES := $(cts_v_host_jar_files)\n$(cts-v-host-api-map-xml-report) : $(verifier_zip) $(cts_v_host_api_map_dependencies) | $(ACP)\n\t$(call generate-api-map-report-cts,\"CTS-V-HOST API MAP Report - XML\",\\\n\t\t\t$(PRIVATE_JAR_FILES),xml)\n\n$(cts-combined-api-map-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)\n$(cts-combined-api-map-xml-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description)\n$(cts-combined-api-map-xml-report): PRIVATE_JAR_FILES := $(cts_all_jar_files)\n$(cts-combined-api-map-xml-report) : $(verifier_zip) $(android_cts_zip) $(cts_combined_api_map_dependencies) | $(ACP)\n\t$(call generate-api-map-report-cts,\"CTS Combined API MAP Report - XML\",\\\n\t\t\t$(PRIVATE_JAR_FILES),xml)\n\n$(cts-combined-api-map-html-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)\n$(cts-combined-api-map-html-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description)\n$(cts-combined-api-map-html-report): PRIVATE_JAR_FILES := $(cts_all_jar_files)\n$(cts-combined-api-map-html-report) : $(verifier_zip) $(android_cts_zip) $(cts_combined_api_map_dependencies) | $(ACP)\n\t$(call generate-api-map-report-cts,\"CTS Combined API MAP Report - HTML\",\\\n\t\t\t$(PRIVATE_JAR_FILES),html)\n\n$(cts-combined-api-inherit-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)\n$(cts-combined-api-inherit-xml-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description)\n$(cts-combined-api-inherit-xml-report): PRIVATE_JAR_FILES := $(cts_all_jar_files)\n$(cts-combined-api-inherit-xml-report) : $(verifier_zip) $(android_cts_zip) $(cts_combined_api_map_dependencies) | $(ACP)\n\t$(call generate-api-inherit-report-cts,\"CTS Combined API Inherit Report - XML\",\\\n\t\t\t$(PRIVATE_JAR_FILES),xml)\n\n.PHONY: cts-api-map-xml\ncts-api-map-xml : $(cts-api-map-xml-report)\n\n.PHONY: cts-v-host-api-map-xml\ncts-v-host-api-map-xml: $(cts-v-host-api-map-xml-report)\n\n.PHONY: cts-combined-api-map-xml\ncts-combined-api-map-xml : $(cts-combined-api-map-xml-report)\n\n.PHONY: cts-combined-api-inherit-xml\ncts-combined-api-inherit-xml : $(cts-combined-api-inherit-xml-report)\n\n.PHONY: cts-api-map-all\n\n# Put the test coverage report in the dist dir if \"cts-api-coverage\" is among the build goals.\n$(call dist-for-goals, cts-api-coverage, $(cts-system-api-xml-coverage-report):cts-system-api-coverage-report.xml)\n$(call dist-for-goals, cts-api-coverage, $(cts-combined-xml-coverage-report):cts-combined-coverage-report.xml)\n\nALL_TARGETS.$(cts-test-coverage-report).META_LIC:=$(module_license_metadata)\nALL_TARGETS.$(cts-system-api-coverage-report).META_LIC:=$(module_license_metadata)\nALL_TARGETS.$(cts-system-api-xml-coverage-report).META_LIC:=$(module_license_metadata)\nALL_TARGETS.$(cts-verifier-coverage-report).META_LIC:=$(module_license_metadata)\nALL_TARGETS.$(cts-combined-coverage-report).META_LIC:=$(module_license_metadata)\nALL_TARGETS.$(cts-combined-xml-coverage-report).META_LIC:=$(module_license_metadata)\n\n# Put the test api map report in the dist dir if \"cts-api-map-all\" is among the build goals.\n$(call dist-for-goals, cts-api-map-all, $(cts-combined-api-map-xml-report):cts-api-map-report.xml)\n$(call dist-for-goals, cts-api-map-all, $(cts-combined-api-inherit-xml-report):cts-api-inherit-report.xml)\n\nALL_TARGETS.$(cts-api-map-xml-report).META_LIC:=$(module_license_metadata)\nALL_TARGETS.$(cts-v-host-api-map-xml-report).META_LIC:=$(module_license_metadata)\nALL_TARGETS.$(cts-combined-api-map-xml-report).META_LIC:=$(module_license_metadata)\nALL_TARGETS.$(cts-combined-api-map-html-report).META_LIC:=$(module_license_metadata)\nALL_TARGETS.$(cts-combined-api-map-inherit-report).META_LIC:=$(module_license_metadata)\n\n# Arguments;\n#  1 - Name of the report printed out on the screen\n#  2 - List of apk files that will be scanned to generate the report\n#  3 - Format of the report\ndefine generate-coverage-report-cts\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) $(PRIVATE_CTS_API_COVERAGE_EXE) -j 8 -d $(PRIVATE_DEXDEPS_EXE) -a $(PRIVATE_API_XML_DESC) -n $(PRIVATE_NAPI_XML_DESC) -f $(3) -o $@ $(2)\n\t@ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@)\nendef\n\n# Arguments;\n#  1 - Name of the report printed out on the screen\n#  2 - A file containing list of files that to be analyzed\n#  3 - Format of the report\ndefine generate-api-map-report-cts\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) $(PRIVATE_CTS_API_MAP_EXE) -j 8 -m api_map -m xts_annotation -a $(shell echo \"$(PRIVATE_API_XML_DESC)\" | tr ' ' ',') -i $(2) -f $(3) -o $@\n\t@ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@)\nendef\n\n\n# Arguments;\n#  1 - Name of the report printed out on the screen\n#  2 - A file containing list of files that to be analyzed\n#  3 - Format of the report\ndefine generate-api-inherit-report-cts\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) $(PRIVATE_CTS_API_MAP_EXE) -j 8 -m xts_api_inherit -a $(shell echo \"$(PRIVATE_API_XML_DESC)\" | tr ' ' ',') -i $(2) -f $(3) -o $@\n\t@ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@)\nendef\n\n# Reset temp vars\ncts_api_coverage_dependencies :=\ncts_system_api_coverage_dependencies :=\ncts_api_map_dependencies :=\ncts_v_host_api_map_dependencies :=\ncts_combined_api_map_dependencies :=\ncts-combined-coverage-report :=\ncts-combined-xml-coverage-report :=\ncts-verifier-coverage-report :=\ncts-test-coverage-report :=\ncts-system-api-coverage-report :=\ncts-system-api-xml-coverage-report :=\ncts-api-map-xml-report :=\ncts-v-host-api-map-xml-report :=\ncts-combined-api-map-xml-report :=\ncts-combined-api-map-html-report :=\ncts-combined-api-map-inherit-report :=\napi_xml_description :=\napi_text_description :=\nsystem_api_xml_description :=\ncombined_api_xml_description :=\nnapi_xml_description :=\nnapi_text_description :=\ncoverage_out :=\napi_map_out :=\ncts_jar_files :=\ndexdeps_exe :=\ncts_api_coverage_exe :=\ncts_api_map_exe :=\ncts_verifier_apk :=\nandroid_cts_zip :=\ncts-dir :=\nverifier-dir-name :=\nverifier-dir :=\nverifier-zip-name :=\nverifier-zip :=\n"
  },
  {
    "path": "core/tasks/cts_root.mk",
    "content": "# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nifneq ($(wildcard test/cts-root/README.md),)\ntest_suite_name := cts_root\ntest_suite_tradefed := cts-root-tradefed\ntest_suite_readme := test/cts-root/README.md\n\ninclude $(BUILD_SYSTEM)/tasks/tools/compatibility.mk\n\n.PHONY: cts_root\ncts_root: $(compatibility_zip)\n$(call dist-for-goals, cts_root, $(compatibility_zip))\nendif\n"
  },
  {
    "path": "core/tasks/device-platinum-tests.mk",
    "content": "# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\n.PHONY: device-platinum-tests\n\ndevice_platinum_tests_zip := $(PRODUCT_OUT)/device-platinum-tests.zip\n# Create an artifact to include a list of test config files in device-platinum-tests.\ndevice_platinum_tests_list_zip := $(PRODUCT_OUT)/device-platinum-tests_list.zip\n# Create an artifact to include all test config files in device-platinum-tests.\ndevice_platinum_tests_configs_zip := $(PRODUCT_OUT)/device-platinum-tests_configs.zip\nmy_host_shared_lib_for_device_platinum_tests := $(call copy-many-files,$(COMPATIBILITY.device-platinum-tests.HOST_SHARED_LIBRARY.FILES))\ndevice_platinum_tests_host_shared_libs_zip := $(PRODUCT_OUT)/device-platinum-tests_host-shared-libs.zip\n\n$(device_platinum_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(device_platinum_tests_list_zip) $(device_platinum_tests_configs_zip) $(device_platinum_tests_host_shared_libs_zip)\n$(device_platinum_tests_zip) : PRIVATE_device_platinum_tests_list_zip := $(device_platinum_tests_list_zip)\n$(device_platinum_tests_zip) : PRIVATE_device_platinum_tests_configs_zip := $(device_platinum_tests_configs_zip)\n$(device_platinum_tests_zip) : PRIVATE_device_platinum_tests_list := $(PRODUCT_OUT)/device-platinum-tests_list\n$(device_platinum_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_device_platinum_tests)\n$(device_platinum_tests_zip) : PRIVATE_device_host_shared_libs_zip := $(device_platinum_tests_host_shared_libs_zip)\n$(device_platinum_tests_zip) : $(COMPATIBILITY.device-platinum-tests.FILES) $(my_host_shared_lib_for_device_platinum_tests) $(SOONG_ZIP)\n\trm -f $@-shared-libs.list\n\trm -f $(PRIVATE_device_platinum_tests_list_zip)\n\techo $(sort $(COMPATIBILITY.device-platinum-tests.FILES)) | tr \" \" \"\\n\" > $@.list\n\tgrep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true\n\tgrep -e .*\\\\.config$$ $@-host.list > $@-host-test-configs.list || true\n\t$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \\\n\t  echo $$shared_lib >> $@-host.list; \\\n\t  echo $$shared_lib >> $@-shared-libs.list; \\\n\tdone\n\tgrep $(HOST_OUT_TESTCASES) $@-shared-libs.list > $@-host-shared-libs.list || true\n\tgrep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true\n\tgrep -e .*\\\\.config$$ $@-target.list > $@-target-test-configs.list || true\n\t$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list -sha256\n\t$(hide) $(SOONG_ZIP) -d -o $(PRIVATE_device_platinum_tests_configs_zip) \\\n\t  -P host -C $(HOST_OUT) -l $@-host-test-configs.list \\\n\t  -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list\n\t$(SOONG_ZIP) -d -o $(PRIVATE_device_host_shared_libs_zip) \\\n\t  -P host -C $(HOST_OUT) -l $@-host-shared-libs.list\n\trm -f $(PRIVATE_device_platinum_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_device_platinum_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_device_platinum_tests_list)\n\t$(hide) $(SOONG_ZIP) -d -o $(PRIVATE_device_platinum_tests_list_zip) -C $(dir $@) -f $(PRIVATE_device_platinum_tests_list)\n\trm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \\\n\t  $@-shared-libs.list $@-host-shared-libs.list $(PRIVATE_device_platinum_tests_list)\n\ndevice-platinum-tests: $(device_platinum_tests_zip)\n$(call dist-for-goals, device-platinum-tests, $(device_platinum_tests_zip) $(device_platinum_tests_list_zip) $(device_platinum_tests_configs_zip) $(device_platinum_tests_host_shared_libs_zip))\n\n$(call declare-1p-container,$(device_platinum_tests_zip),)\n$(call declare-container-license-deps,$(device_platinum_tests_zip),$(COMPATIBILITY.device-platinum-tests.FILES) $(my_host_shared_lib_for_device_platinum_tests),$(PRODUCT_OUT)/:/)\n\ntests: device-platinum-tests\n\n# Reset temp vars\ndevice_platinum_tests_zip :=\ndevice_platinum_tests_list_zip :=\ndevice_platinum_tests_configs_zip :=\nmy_host_shared_lib_for_device_platinum_tests :=\ndevice_platinum_tests_host_shared_libs_zip :=\n"
  },
  {
    "path": "core/tasks/device-tests.mk",
    "content": "# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\n.PHONY: device-tests\n.PHONY: device-tests-files-list\n\ndevice-tests-zip := $(PRODUCT_OUT)/device-tests.zip\n# Create an artifact to include a list of test config files in device-tests.\ndevice-tests-list-zip := $(PRODUCT_OUT)/device-tests_list.zip\n# Create an artifact to include all test config files in device-tests.\ndevice-tests-configs-zip := $(PRODUCT_OUT)/device-tests_configs.zip\nmy_host_shared_lib_for_device_tests := $(call copy-many-files,$(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES))\ndevice_tests_files_list := $(PRODUCT_OUT)/device-tests_files\n\n$(device-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(device-tests-list-zip) $(device-tests-configs-zip)\n$(device-tests-zip) : PRIVATE_device_tests_list := $(PRODUCT_OUT)/device-tests_list\n$(device-tests-zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_device_tests)\n$(device-tests-zip) : $(COMPATIBILITY.device-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES) $(my_host_shared_lib_for_device_tests) $(SOONG_ZIP)\n\techo $(sort $(COMPATIBILITY.device-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr \" \" \"\\n\" > $@.list\n\tgrep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true\n\tgrep -e .*\\\\.config$$ $@-host.list > $@-host-test-configs.list || true\n\t$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \\\n\t  echo $$shared_lib >> $@-host.list; \\\n\tdone\n\tgrep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true\n\tgrep -e .*\\\\.config$$ $@-target.list > $@-target-test-configs.list || true\n\t$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list -sha256\n\t$(hide) $(SOONG_ZIP) -d -o $(device-tests-configs-zip) \\\n\t  -P host -C $(HOST_OUT) -l $@-host-test-configs.list \\\n\t  -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list\n\trm -f $(PRIVATE_device_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_device_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_device_tests_list)\n\t$(hide) $(SOONG_ZIP) -d -o $(device-tests-list-zip) -C $(dir $@) -f $(PRIVATE_device_tests_list)\n\trm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \\\n\t\t$(PRIVATE_device_tests_list)\n\n$(device_tests_files_list) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_device_tests)\n$(device_tests_files_list) :\n\techo $(sort $(COMPATIBILITY.device-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr \" \" \"\\n\" > $@.full_list\n\tgrep $(HOST_OUT_TESTCASES) $@.full_list > $@ || true\n\tgrep $(TARGET_OUT_TESTCASES) $@.full_list >> $@ || true\n\ndevice-tests: $(device-tests-zip)\ndevice-tests-files-list: $(device_tests_files_list)\n\n$(call dist-for-goals, device-tests, $(device-tests-zip) $(device-tests-list-zip) $(device-tests-configs-zip))\n\n$(call declare-1p-container,$(device-tests-zip),)\n$(call declare-container-license-deps,$(device-tests-zip),$(COMPATIBILITY.device-tests.FILES) $(my_host_shared_lib_for_device_tests),$(PRODUCT_OUT)/:/)\n\ntests: device-tests\n"
  },
  {
    "path": "core/tasks/dex_preopt_check.mk",
    "content": "# Checks that some critical dexpreopt output files are installed.\n\n# Inputs:\n# DISABLE_DEXPREOPT_CHECK: True if the check should be disabled.\n# PRODUCT_PACKAGES: The list of packages to be installed for the product.\n# ALL_DEFAULT_INSTALLED_MODULES: The full list of modules going to be installed.\n# DEXPREOPT_SYSTEMSERVER_ARTIFACTS: The list of compilation artifacts of system server jars, which\n# \tis generated by Soong in dexpreopt_check.go.\n\nifneq (true,$(DISABLE_DEXPREOPT_CHECK))\n  # Skip the check if the system server is not installed for the product.\n  ifneq (,$(filter services,$(PRODUCT_PACKAGES)))\n    $(call maybe-print-list-and-error,\\\n      $(filter-out $(ALL_DEFAULT_INSTALLED_MODULES),$(DEXPREOPT_SYSTEMSERVER_ARTIFACTS)),\\\n      Missing compilation artifacts. Dexpreopting is not working for some system server jars. See \\\n      https://cs.android.com/android/platform/superproject/+/master:build/make/core/tasks/README.dex_preopt_check.md \\\n    )\n  endif\nendif\n"
  },
  {
    "path": "core/tasks/dts.mk",
    "content": "# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Desktop test suite\nifneq ($(wildcard test/dts/tools/dts-tradefed/README),)\ntest_suite_name := dts\ntest_suite_tradefed := dts-tradefed\ntest_suite_readme := test/dts/tools/dts-tradefed/README\ntest_suite_tools := $(HOST_OUT_JAVA_LIBRARIES)/ats_console_deploy.jar \\\n  $(HOST_OUT_JAVA_LIBRARIES)/ats_olc_server_local_mode_deploy.jar\n\ninclude $(BUILD_SYSTEM)/tasks/tools/compatibility.mk\n\n.PHONY: dts\ndts: $(compatibility_zip) $(compatibility_tests_list_zip)\n$(call dist-for-goals, dts, $(compatibility_zip) $(compatibility_tests_list_zip))\nendif\n"
  },
  {
    "path": "core/tasks/find-shareduid-violation.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nshareduid_violation_modules_filename := $(PRODUCT_OUT)/shareduid_violation_modules.json\n\n$(shareduid_violation_modules_filename): $(INSTALLED_SYSTEMIMAGE_TARGET) \\\n    $(INSTALLED_RAMDISK_TARGET) \\\n    $(INSTALLED_BOOTIMAGE_TARGET) \\\n    $(INSTALLED_USERDATAIMAGE_TARGET) \\\n    $(INSTALLED_VENDORIMAGE_TARGET) \\\n    $(INSTALLED_PRODUCTIMAGE_TARGET) \\\n    $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)\n\n$(shareduid_violation_modules_filename): $(HOST_OUT_EXECUTABLES)/find_shareduid_violation\n$(shareduid_violation_modules_filename): $(AAPT2)\n\t$(HOST_OUT_EXECUTABLES)/find_shareduid_violation \\\n\t\t--product_out $(PRODUCT_OUT) \\\n\t\t--aapt $(AAPT2) \\\n\t\t--copy_out_system $(TARGET_COPY_OUT_SYSTEM) \\\n\t\t--copy_out_vendor $(TARGET_COPY_OUT_VENDOR) \\\n\t\t--copy_out_product $(TARGET_COPY_OUT_PRODUCT) \\\n\t\t--copy_out_system_ext $(TARGET_COPY_OUT_SYSTEM_EXT) \\\n\t\t> $@\n\n$(call declare-0p-target,$(shareduid_violation_modules_filename))\n$(call dist-for-goals,droidcore,$(shareduid_violation_modules_filename))\n"
  },
  {
    "path": "core/tasks/fontchain_lint.mk",
    "content": "# Copyright (C) 2011 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Run sanity tests on fonts on checkbuild\ncheckbuild: fontchain_lint\n\nFONTCHAIN_LINTER := $(HOST_OUT_EXECUTABLES)/fontchain_linter\nifeq ($(MINIMAL_FONT_FOOTPRINT),true)\nCHECK_EMOJI := false\nelse\nCHECK_EMOJI := true\nendif\n\nfontchain_lint_timestamp := $(call intermediates-dir-for,PACKAGING,fontchain_lint)/stamp\n\n.PHONY: fontchain_lint\nfontchain_lint: $(fontchain_lint_timestamp)\n\nfontchain_lint_deps := \\\n    external/unicode/DerivedAge.txt \\\n    external/unicode/emoji-data.txt \\\n    external/unicode/emoji-sequences.txt \\\n    external/unicode/emoji-variation-sequences.txt \\\n    external/unicode/emoji-zwj-sequences.txt \\\n    external/unicode/additions/emoji-data.txt \\\n    external/unicode/additions/emoji-sequences.txt \\\n    external/unicode/additions/emoji-zwj-sequences.txt \\\n\n$(fontchain_lint_timestamp): $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img $(fontchain_lint_deps)\n\t@echo Running fontchain lint\n\t$(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode\n\ttouch $@\n"
  },
  {
    "path": "core/tasks/general-tests.mk",
    "content": "# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n.PHONY: general-tests\n.PHONY: general-tests-files-list\n\ngeneral_tests_tools := \\\n    $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar \\\n    $(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util.jar \\\n    $(HOST_OUT_JAVA_LIBRARIES)/vts-tradefed.jar \\\n\nintermediates_dir := $(call intermediates-dir-for,PACKAGING,general-tests)\ngeneral_tests_zip := $(PRODUCT_OUT)/general-tests.zip\n# Create an artifact to include a list of test config files in general-tests.\ngeneral_tests_list_zip := $(PRODUCT_OUT)/general-tests_list.zip\n\n# Create an artifact to include all test config files in general-tests.\ngeneral_tests_configs_zip := $(PRODUCT_OUT)/general-tests_configs.zip\n\n# Filter shared entries between general-tests and device-tests's HOST_SHARED_LIBRARY.FILES,\n# to avoid warning about overriding commands.\nmy_host_shared_lib_for_general_tests := \\\n  $(foreach m,$(filter $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\\\n\t   $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES)),$(call word-colon,2,$(m)))\nmy_general_tests_shared_lib_files := \\\n  $(filter-out $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\\\n\t $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES))\n\nmy_host_shared_lib_for_general_tests += $(call copy-many-files,$(my_general_tests_shared_lib_files))\n\nmy_host_shared_lib_symlinks := \\\n    $(filter $(COMPATIBILITY.host-unit-tests.SYMLINKS),\\\n\t$(COMPATIBILITY.general-tests.SYMLINKS))\n\nmy_general_tests_symlinks := \\\n    $(filter-out $(COMPATIBILITY.camera-hal-tests.SYMLINKS),\\\n    $(filter-out $(COMPATIBILITY.host-unit-tests.SYMLINKS),\\\n\t $(COMPATIBILITY.general-tests.SYMLINKS)))\n\nmy_symlinks_for_general_tests := $(foreach f,$(my_general_tests_symlinks),\\\n\t$(strip $(eval _cmf_tuple := $(subst :, ,$(f))) \\\n\t$(eval _cmf_dep := $(word 1,$(_cmf_tuple))) \\\n\t$(eval _cmf_src := $(word 2,$(_cmf_tuple))) \\\n\t$(eval _cmf_dest := $(word 3,$(_cmf_tuple))) \\\n\t$(call symlink-file,$(_cmf_dep),$(_cmf_src),$(_cmf_dest)) \\\n\t$(_cmf_dest)))\n\n# In this one directly take the overlap into the zip since we can't rewrite rules\nmy_symlinks_for_general_tests += $(foreach f,$(my_host_shared_lib_symlinks),\\\n        $(strip $(eval _cmf_tuple := $(subst :, ,$(f))) \\\n        $(eval _cmf_dep := $(word 1,$(_cmf_tuple))) \\\n        $(eval _cmf_src := $(word 2,$(_cmf_tuple))) \\\n        $(eval _cmf_dest := $(word 3,$(_cmf_tuple))) \\\n        $(_cmf_dest)))\n\ngeneral_tests_files_list := $(PRODUCT_OUT)/general-tests_files\ngeneral_tests_host_files_list := $(PRODUCT_OUT)/general-tests_host_files\ngeneral_tests_target_files_list := $(PRODUCT_OUT)/general-tests_target_files\n\n$(general_tests_zip) : PRIVATE_general_tests_list_zip := $(general_tests_list_zip)\n$(general_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(general_tests_list_zip) $(general_tests_configs_zip)\n$(general_tests_zip) : PRIVATE_TOOLS := $(general_tests_tools)\n$(general_tests_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)\n$(general_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_general_tests)\n$(general_tests_zip) : PRIVATE_SYMLINKS := $(my_symlinks_for_general_tests)\n$(general_tests_zip) : PRIVATE_general_tests_configs_zip := $(general_tests_configs_zip)\n$(general_tests_zip) : $(COMPATIBILITY.general-tests.FILES) $(my_host_shared_lib_for_general_tests) $(COMPATIBILITY.general-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES) $(general_tests_tools) $(my_symlinks_for_general_tests) $(SOONG_ZIP)\n\trm -rf $(PRIVATE_INTERMEDIATES_DIR)\n\trm -f $@ $(PRIVATE_general_tests_list_zip)\n\tmkdir -p $(PRIVATE_INTERMEDIATES_DIR) $(PRIVATE_INTERMEDIATES_DIR)/tools\n\techo $(sort $(COMPATIBILITY.general-tests.FILES) $(COMPATIBILITY.general-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr \" \" \"\\n\" > $(PRIVATE_INTERMEDIATES_DIR)/list\n\tfor symlink in $(PRIVATE_SYMLINKS); do \\\n\t  echo $$symlink >> $(PRIVATE_INTERMEDIATES_DIR)/list; \\\n\tdone\n\t$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \\\n\t  echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \\\n\tdone\n\tgrep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true\n\tgrep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/host.list || true\n\tgrep $(TARGET_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/target.list || true\n\tgrep -e .*\\\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list > $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list || true\n\tgrep -e .*\\\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list > $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list || true\n\tcp -fp $(PRIVATE_TOOLS) $(PRIVATE_INTERMEDIATES_DIR)/tools/\n\t$(SOONG_ZIP) -d -o $@ \\\n\t  -P host -C $(PRIVATE_INTERMEDIATES_DIR) -D $(PRIVATE_INTERMEDIATES_DIR)/tools \\\n\t  -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host.list \\\n\t  -P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target.list \\\n\t  -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list \\\n\t  -sha256\n\t$(SOONG_ZIP) -d -o $(PRIVATE_general_tests_configs_zip) \\\n\t  -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list \\\n\t  -P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list\n\tgrep -e .*\\\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list\n\tgrep -e .*\\\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list\n\t$(SOONG_ZIP) -d -o $(PRIVATE_general_tests_list_zip) -C $(PRIVATE_INTERMEDIATES_DIR) -f $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list\n\n$(general_tests_files_list) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)\n$(general_tests_files_list) : PRIVATE_general_tests_host_files_list := $(general_tests_host_files_list)\n$(general_tests_files_list) : PRIVATE_general_tests_target_files_list := $(general_tests_target_files_list)\n$(general_tests_files_list) :\n\techo $(sort $(COMPATIBILITY.general-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr \" \" \"\\n\" > $@\n\tgrep $(HOST_OUT_TESTCASES) $@ > $(PRIVATE_general_tests_host_files_list) || true\n\tgrep $(TARGET_OUT_TESTCASES) $@ >> $(PRIVATE_general_tests_target_files_list) || true\n\ngeneral-tests: $(general_tests_zip)\ngeneral-tests-files-list: $(general_tests_files_list)\n$(call dist-for-goals, general-tests, $(general_tests_zip) $(general_tests_list_zip) $(general_tests_configs_zip) $(general_tests_shared_libs_zip))\n\n$(call declare-1p-container,$(general_tests_zip),)\n$(call declare-container-license-deps,$(general_tests_zip),$(COMPATIBILITY.general-tests.FILES) $(general_tests_tools),$(PRODUCT_OUT)/:/)\n\nintermediates_dir :=\ngeneral_tests_tools :=\ngeneral_tests_zip :=\ngeneral_tests_list_zip :=\ngeneral_tests_configs_zip :=\ngeneral_tests_shared_libs_zip :=\nmy_host_shared_lib_for_general_tests :=\nmy_symlinks_for_general_tests :=\nmy_general_tests_shared_lib_files :=\nmy_general_tests_symlinks :=\nmy_host_shared_lib_symlinks :=\n"
  },
  {
    "path": "core/tasks/host-unit-tests.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# `host-unit-tests` shall only include hostside unittest that don't require a device to run. Tests\n# included will be run as part of presubmit check.\n# To add tests to the suite, do one of the following:\n# * For test modules configured with Android.bp, set attribute `test_options: { unit_test: true }`\n# * For test modules configured with mk, set `LOCAL_IS_UNIT_TEST := true`\n.PHONY: host-unit-tests\n\nintermediates_dir := $(call intermediates-dir-for,PACKAGING,host-unit-tests)\nhost_unit_tests_zip := $(PRODUCT_OUT)/host-unit-tests.zip\n# Get the hostside libraries to be packaged in the test zip. Unlike\n# device-tests.mk or general-tests.mk, the files are not copied to the\n# testcases directory.\nmy_host_shared_lib_for_host_unit_tests := $(foreach f,$(COMPATIBILITY.host-unit-tests.HOST_SHARED_LIBRARY.FILES),$(strip \\\n    $(eval _cmf_tuple := $(subst :, ,$(f))) \\\n    $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \\\n    $(_cmf_src)))\n\nmy_symlinks_for_host_unit_tests := $(foreach f,$(COMPATIBILITY.host-unit-tests.SYMLINKS),\\\n\t$(strip $(eval _cmf_tuple := $(subst :, ,$(f))) \\\n\t$(eval _cmf_dep := $(word 1,$(_cmf_tuple))) \\\n\t$(eval _cmf_src := $(word 2,$(_cmf_tuple))) \\\n\t$(eval _cmf_dest := $(word 3,$(_cmf_tuple))) \\\n\t$(call symlink-file,$(_cmf_dep),$(_cmf_src),$(_cmf_dest)) \\\n\t$(_cmf_dest)))\n\n$(host_unit_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_host_unit_tests)\n\n$(host_unit_tests_zip) : PRIVATE_SYMLINKS := $(my_symlinks_for_host_unit_tests)\n\n$(host_unit_tests_zip) : $(COMPATIBILITY.host-unit-tests.FILES) $(my_host_shared_lib_for_host_unit_tests) $(my_symlinks_for_host_unit_tests) $(SOONG_ZIP)\n\techo $(sort $(COMPATIBILITY.host-unit-tests.FILES)) | tr \" \" \"\\n\" > $@.list\n\tgrep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true\n\techo \"\" >> $@-host-libs.list\n\t$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \\\n\t  echo $$shared_lib >> $@-host-libs.list; \\\n\tdone\n\t$(hide) for symlink in $(PRIVATE_SYMLINKS); do \\\n\t  echo $$symlink >> $@-host.list; \\\n\tdone\n\tgrep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true\n\t$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list \\\n\t  -P target -C $(PRODUCT_OUT) -l $@-target.list \\\n\t  -P host/testcases -C $(HOST_OUT) -l $@-host-libs.list -sha256\n\trm -f $@.list $@-host.list $@-target.list $@-host-libs.list\n\nhost-unit-tests: $(host_unit_tests_zip)\n$(call dist-for-goals, host-unit-tests, $(host_unit_tests_zip))\n\n$(call declare-1p-container,$(host_unit_tests_zip),)\n$(call declare-container-license-deps,$(host_unit_tests_zip),$(COMPATIBILITY.host-unit-tests.FILES) $(my_host_shared_lib_for_host_unit_tests),$(PRODUCT_OUT)/:/)\n\ntests: host-unit-tests\n"
  },
  {
    "path": "core/tasks/host_init_verifier.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nhost_init_verifier_output := $(PRODUCT_OUT)/host_init_verifier_output.txt\n\n$(host_init_verifier_output): \\\n    $(INSTALLED_SYSTEMIMAGE_TARGET) \\\n    $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \\\n    $(INSTALLED_VENDORIMAGE_TARGET) \\\n    $(INSTALLED_ODMIMAGE_TARGET) \\\n    $(INSTALLED_PRODUCTIMAGE_TARGET) \\\n    $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \\\n    $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \\\n    $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \\\n    $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \\\n    $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \\\n    $(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \\\n    $(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \\\n    $(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \\\n    $(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \\\n    $(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts\n\n# Run host_init_verifier on the partition staging directories.\n$(host_init_verifier_output): $(HOST_INIT_VERIFIER)\n\t$(HOST_INIT_VERIFIER) \\\n\t\t-p $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \\\n\t\t-p $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \\\n\t\t-p $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \\\n\t\t-p $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \\\n\t\t-p $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \\\n\t\t--property-contexts=$(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \\\n\t\t--property-contexts=$(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \\\n\t\t--property-contexts=$(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \\\n\t\t--property-contexts=$(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \\\n\t\t--property-contexts=$(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts \\\n\t\t--out_system $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM) \\\n\t\t--out_system_ext $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_EXT) \\\n\t\t--out_vendor $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR) \\\n\t\t--out_odm $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ODM) \\\n\t\t--out_product $(PRODUCT_OUT)/$(TARGET_COPY_OUT_PRODUCT) \\\n\t\t> $@\n\n$(call dist-for-goals,droidcore-unbundled,$(host_init_verifier_output))\n"
  },
  {
    "path": "core/tasks/mcts.mk",
    "content": "# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nifneq ($(wildcard test/mts/README.md),)\n\nmcts_test_suites :=\nmcts_all_test_suites :=\nmcts_all_test_suites += mcts\n\n$(foreach module, $(mts_modules), $(eval mcts_test_suites += mcts-$(module)))\n\n$(foreach suite, $(mcts_test_suites), \\\n\t$(eval test_suite_name := $(suite)) \\\n\t$(eval test_suite_tradefed := mts-tradefed) \\\n\t$(eval test_suite_readme := test/mts/README.md) \\\n\t$(eval include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk) \\\n\t$(eval .PHONY: $(suite)) \\\n\t$(eval $(suite): $(compatibility_zip)) \\\n\t$(eval $(call dist-for-goals, $(suite), $(compatibility_zip))) \\\n)\n\n$(foreach suite, $(mcts_all_test_suites), \\\n\t$(eval test_suite_name := $(suite)) \\\n\t$(eval test_suite_tradefed := mcts-tradefed) \\\n\t$(eval test_suite_readme := test/mts/README.md) \\\n\t$(eval include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk) \\\n\t$(eval .PHONY: $(suite)) \\\n\t$(eval $(suite): $(compatibility_zip)) \\\n\t$(eval $(call dist-for-goals, $(suite), $(compatibility_zip))) \\\n)\n\nendif\n"
  },
  {
    "path": "core/tasks/meta-lic.mk",
    "content": "# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Declare license metadata for non-module files released with products.\n\n# Moved here from device/generic/car/Android.mk\n$(eval $(call declare-1p-copy-files,device/generic/car,))\n\n# Moved here from device/generic/trusty/Android.mk\n$(eval $(call declare-1p-copy-files,device/generic/trusty,))\n\n# Moved here from device/generic/uml/Android.mk\n$(eval $(call declare-1p-copy-files,device/generic/uml,))\n\n# Moved here from device/google_car/common/Android.mk\n$(eval $(call declare-1p-copy-files,device/google_car/common,))\n\n# Moved here from device/google/atv/Android.mk\n$(eval $(call declare-1p-copy-files,device/google/atv,atv-component-overrides.xml))\n$(eval $(call declare-1p-copy-files,device/google/atv,tv_core_hardware.xml))\n\n# Moved here from device/google/cuttlefish/Android.mk\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,.idc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,init.cutf_cvm.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.f2fs.hctr2,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.f2fs.cts,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.ext4.hctr2,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.ext4.cts,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,init.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,audio_policy.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n\n$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish/shared/config,pci.ids,SPDX-license-identifier-BSD-3-Clause,notice,device/google/cuttlefish/shared/config/LICENSE_BSD,))\n\n$(eval $(call declare-1p-copy-files,device/google/cuttlefish,privapp-permissions-cuttlefish.xml))\n$(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_profiles_V1_0.xml))\n$(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_codecs_performance.xml))\n$(eval $(call declare-1p-copy-files,device/google/cuttlefish,cuttlefish_excluded_hardware.xml))\n$(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_codecs.xml))\n$(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_codecs_google_video.xml))\n$(eval $(call declare-1p-copy-files,device/google/cuttlefish,car_audio_configuration.xml))\n$(eval $(call declare-1p-copy-files,device/google/cuttlefish,audio_policy_configuration.xml))\n$(eval $(call declare-1p-copy-files,device/google/cuttlefish,preinstalled-packages-product-car-cuttlefish.xml))\n$(eval $(call declare-1p-copy-files,hardware/google/camera/devices,.json))\n\n# Moved here from device/google/gs101/Android.mk\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,p2p_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n\n$(eval $(call declare-1p-copy-files,device/google/gs101,audio_policy_configuration.xml))\n\n# Move here from device/google/raviole/Android.mk\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,libnfc-nci-raven.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n$(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))\n\n$(eval $(call declare-1p-copy-files,device/google/raviole,audio_policy_configuration.xml))\n\n# Moved here from device/sample/Android.mk\n$(eval $(call declare-1p-copy-files,device/sample,))\n\n# Moved here from device/google/trout/Android.mk\n$(eval $(call declare-1p-copy-files,device/google/trout,))\n\n# Moved here from frameworks/av/media/Android.mk\n$(eval $(call declare-1p-copy-files,frameworks/av/media/libeffects,audio_effects.conf))\n$(eval $(call declare-1p-copy-files,frameworks/av/media/libeffects,audio_effects.xml))\n$(eval $(call declare-1p-copy-files,frameworks/av/media/libstagefright,))\n\n# Moved here from frameworks/av/services/Android.mk\n$(eval $(call declare-1p-copy-files,frameworks/av/services/audiopolicy,))\n\n# Moved here from frameworks/base/Android.mk\n$(eval $(call declare-1p-copy-files,frameworks/base,.ogg))\n$(eval $(call declare-1p-copy-files,frameworks/base,.kl))\n$(eval $(call declare-1p-copy-files,frameworks/base,.kcm))\n$(eval $(call declare-1p-copy-files,frameworks/base,.idc))\n$(eval $(call declare-1p-copy-files,frameworks/base,dirty-image-objects))\n$(eval $(call declare-1p-copy-files,frameworks/base/config,))\n$(eval $(call declare-1p-copy-files,frameworks/native/data,))\n\n# Moved here from hardware/google/camera/Android.mk\n$(eval $(call declare-1p-copy-files,hardware/google/camera,))\n\n# Moved here from hardware/interfaces/tv/Android.mk\n$(eval $(call declare-1p-copy-files,hardware/interfaces/tv,tuner_vts_config_1_0.xml))\n$(eval $(call declare-1p-copy-files,hardware/interfaces/tv,tuner_vts_config_1_1.xml))\n\n# Moved here from device/generic/goldfish/Android.mk\n$(eval $(call declare-1p-copy-files,device/generic/goldfish/data,))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish/input,))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish/wifi,))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish/camera,))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,hals.conf))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,init.qemu-adb-keys.sh))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,init.system_ext.rc))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,.json))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,ueventd.rc))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,wpa_supplicant.conf))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,media_profiles_V1_0.xml))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,init.ranchu.rc))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,fstab.ranchu))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,display_settings.xml))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,display_settings_freeform.xml))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,device_state_configuration.xml))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,init.ranchu-core.sh))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,init.ranchu-net.sh))\n$(eval $(call declare-1p-copy-files,device/generic/goldfish,audio_policy_configuration.xml))\n\n# Moved here from packages/services/Car/Android.mk\n$(eval $(call declare-1p-copy-files,packages/services/Car,))\n\n# Moved here from hardware/libhardware_legacy/Android.mk\n$(eval $(call declare-1p-copy-files,hardware/libhardware_legacy,))\n\n# Moved here from system/core/rootdir/Android.mk\n$(eval $(call declare-1p-copy-files,system/core/rootdir,))\n"
  },
  {
    "path": "core/tasks/module-info.mk",
    "content": "# Print a list of the modules that could be built\n# Currently runtime_dependencies only include the runtime libs information for cc binaries.\n\nMODULE_INFO_JSON := $(PRODUCT_OUT)/module-info.json\nCOMMA := ,\n_NEWLINE := '\\n'\n\ndefine write-optional-json-list\n$(if $(strip $(2)),'$(COMMA)$(strip $(1)): [$(KATI_foreach_sep w,$(COMMA) ,$(2),\"$(w)\")]')\nendef\n\ndefine write-optional-json-bool\n$(if $(strip $(2)),'$(COMMA)$(strip $(1)): \"$(strip $(2))\"')\nendef\n\nSOONG_MODULE_INFO := $(SOONG_OUT_DIR)/module-info-$(TARGET_PRODUCT)${COVERAGE_SUFFIX}.json\n\n$(MODULE_INFO_JSON): PRIVATE_SOONG_MODULE_INFO := $(SOONG_MODULE_INFO)\n$(MODULE_INFO_JSON): PRIVATE_MERGE_JSON_OBJECTS := $(HOST_OUT_EXECUTABLES)/merge_module_info_json\n$(MODULE_INFO_JSON): $(HOST_OUT_EXECUTABLES)/merge_module_info_json\n$(MODULE_INFO_JSON): $(SOONG_MODULE_INFO)\n\t@echo Generating $@\n\t$(hide) echo -ne '{\\n ' > $@.tmp\n\t$(hide) echo -ne $(KATI_foreach_sep m,$(COMMA)$(_NEWLINE), $(sort $(ALL_MAKE_MODULE_INFO_JSON_MODULES)),\\\n\t\t'\"$(m)\": {' \\\n\t\t\t'\"module_name\": \"$(ALL_MODULES.$(m).MODULE_NAME)\"' \\\n\t\t\t$(call write-optional-json-list, \"class\", $(sort $(ALL_MODULES.$(m).CLASS))) \\\n\t\t\t$(call write-optional-json-list, \"path\", $(sort $(ALL_MODULES.$(m).PATH))) \\\n\t\t\t$(call write-optional-json-list, \"tags\", $(sort $(ALL_MODULES.$(m).TAGS))) \\\n\t\t\t$(call write-optional-json-list, \"installed\", $(sort $(ALL_MODULES.$(m).INSTALLED))) \\\n\t\t\t$(call write-optional-json-list, \"compatibility_suites\", $(sort $(ALL_MODULES.$(m).COMPATIBILITY_SUITES))) \\\n\t\t\t$(call write-optional-json-list, \"auto_test_config\", $(sort $(ALL_MODULES.$(m).auto_test_config))) \\\n\t\t\t$(call write-optional-json-list, \"test_config\", $(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS))) \\\n\t\t\t$(call write-optional-json-list, \"dependencies\", $(sort $(ALL_MODULES.$(m).ALL_DEPS))) \\\n\t\t\t$(call write-optional-json-list, \"required\", $(sort $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET))) \\\n\t\t\t$(call write-optional-json-list, \"shared_libs\", $(sort $(ALL_MODULES.$(m).SHARED_LIBS))) \\\n\t\t\t$(call write-optional-json-list, \"static_libs\", $(sort $(ALL_MODULES.$(m).STATIC_LIBS))) \\\n\t\t\t$(call write-optional-json-list, \"system_shared_libs\", $(sort $(ALL_MODULES.$(m).SYSTEM_SHARED_LIBS))) \\\n\t\t\t$(call write-optional-json-list, \"srcs\", $(sort $(ALL_MODULES.$(m).SRCS))) \\\n\t\t\t$(call write-optional-json-list, \"srcjars\", $(sort $(ALL_MODULES.$(m).SRCJARS))) \\\n\t\t\t$(call write-optional-json-list, \"classes_jar\", $(sort $(ALL_MODULES.$(m).CLASSES_JAR))) \\\n\t\t\t$(call write-optional-json-list, \"test_mainline_modules\", $(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES))) \\\n\t\t\t$(call write-optional-json-bool, \"is_unit_test\", $(ALL_MODULES.$(m).IS_UNIT_TEST)) \\\n\t\t\t$(call write-optional-json-list, \"test_options_tags\", $(sort $(ALL_MODULES.$(m).TEST_OPTIONS_TAGS))) \\\n\t\t\t$(call write-optional-json-list, \"data\", $(sort $(ALL_MODULES.$(m).TEST_DATA))) \\\n\t\t\t$(call write-optional-json-list, \"runtime_dependencies\", $(sort $(ALL_MODULES.$(m).LOCAL_RUNTIME_LIBRARIES))) \\\n\t\t\t$(call write-optional-json-list, \"static_dependencies\", $(sort $(ALL_MODULES.$(m).LOCAL_STATIC_LIBRARIES))) \\\n\t\t\t$(call write-optional-json-list, \"data_dependencies\", $(sort $(ALL_MODULES.$(m).TEST_DATA_BINS))) \\\n\t\t\t$(call write-optional-json-list, \"supported_variants\", $(sort $(ALL_MODULES.$(m).SUPPORTED_VARIANTS))) \\\n\t\t\t$(call write-optional-json-list, \"host_dependencies\", $(sort $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET))) \\\n\t\t\t$(call write-optional-json-list, \"target_dependencies\", $(sort $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST))) \\\n\t\t\t$(call write-optional-json-bool, \"test_module_config_base\", $(ALL_MODULES.$(m).TEST_MODULE_CONFIG_BASE)) \\\n\t\t\t$(call write-optional-json-bool, \"make\", $(if $(ALL_MODULES.$(m).IS_SOONG_MODULE),,true)) \\\n\t\t\t$(call write-optional-json-bool, \"make_generated_module_info\", true) \\\n\t\t'}')'\\n}\\n' >> $@.tmp\n\t$(PRIVATE_MERGE_JSON_OBJECTS) -o $@ $(PRIVATE_SOONG_MODULE_INFO) $@.tmp\n\trm $@.tmp\n\n.PHONY: module-info\nmodule-info: $(MODULE_INFO_JSON)\n\ndroidcore-unbundled: $(MODULE_INFO_JSON)\n\n$(call dist-for-goals, general-tests, $(MODULE_INFO_JSON))\n$(call dist-for-goals, droidcore-unbundled, $(MODULE_INFO_JSON))\n\n# On every build, generate an all_modules.txt file to be used for autocompleting\n# the m command. After timing this using $(shell date +\"%s.%3N\"), it only adds\n# 0.01 seconds to the internal master build, and will only rerun on builds that\n# rerun kati.\n$(file >$(PRODUCT_OUT)/all_modules.txt,$(subst $(space),$(newline),$(ALL_MODULES)))\n"
  },
  {
    "path": "core/tasks/mts.mk",
    "content": "# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nifneq ($(wildcard test/mts/README.md),)\n\nmts_test_suites :=\nmts_test_suites += mts\n\n$(foreach module, $(mts_modules), $(eval mts_test_suites += mts-$(module)))\n\n$(foreach suite, $(mts_test_suites), \\\n\t$(eval test_suite_name := $(suite)) \\\n\t$(eval test_suite_tradefed := mts-tradefed) \\\n\t$(eval test_suite_readme := test/mts/README.md) \\\n\t$(eval include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk) \\\n\t$(eval .PHONY: $(suite)) \\\n\t$(eval $(suite): $(compatibility_zip)) \\\n\t$(eval $(call dist-for-goals, $(suite), $(compatibility_zip))) \\\n)\n\nendif\n"
  },
  {
    "path": "core/tasks/multitree.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n.PHONY: update-meta\nupdate-meta: $(SOONG_MULTITREE_METADATA)\n"
  },
  {
    "path": "core/tasks/oem_image.mk",
    "content": "#\n# Copyright (C) 2014 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# We build oem.img only if it's asked for.\nifneq ($(filter $(MAKECMDGOALS),oem_image),)\nifndef BOARD_OEMIMAGE_PARTITION_SIZE\n$(error BOARD_OEMIMAGE_PARTITION_SIZE is not set.)\nendif\n\nINTERNAL_OEMIMAGE_FILES := \\\n    $(filter $(TARGET_OUT_OEM)/%,$(ALL_DEFAULT_INSTALLED_MODULES))\n\noemimage_intermediates := \\\n    $(call intermediates-dir-for,PACKAGING,oem)\nBUILT_OEMIMAGE_TARGET := $(PRODUCT_OUT)/oem.img\n# We just build this directly to the install location.\nINSTALLED_OEMIMAGE_TARGET := $(BUILT_OEMIMAGE_TARGET)\n\n$(INSTALLED_OEMIMAGE_TARGET) : $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_OEMIMAGE_FILES)\n\t$(call pretty,\"Target oem fs image: $@\")\n\t@mkdir -p $(TARGET_OUT_OEM)\n\t@mkdir -p $(oemimage_intermediates) && rm -rf $(oemimage_intermediates)/oem_image_info.txt\n\t$(call generate-image-prop-dictionary, $(oemimage_intermediates)/oem_image_info.txt,oem,skip_fsck=true)\n\tPATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n\t    $(BUILD_IMAGE) \\\n\t        $(TARGET_OUT_OEM) $(oemimage_intermediates)/oem_image_info.txt $@ $(TARGET_OUT)\n\t$(call assert-max-image-size,$@,$(BOARD_OEMIMAGE_PARTITION_SIZE))\n\n.PHONY: oem_image\noem_image : $(INSTALLED_OEMIMAGE_TARGET)\n$(call dist-for-goals, oem_image, $(INSTALLED_OEMIMAGE_TARGET))\n\n$(call declare-1p-container,$(INSTALLED_OEMIMAGE_TARGET),)\n$(call declare-container-license-deps,$(INSTALLED_OEMIMAGE_TARGET),$(INTERNAL_USERIMAGE_DEPS) $(INTERNAL_OEMIMAGE_FILES),$(INSTALLED_OEMIMAGE_TARGET):)\n\nendif  # oem_image in $(MAKECMDGOALS)\n"
  },
  {
    "path": "core/tasks/offline-sdk-docs.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.\n# So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to\n# $(OUT_DOCS)/offline-sdk.\n$(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip\n\t$(hide) rm -rf $(OUT_DOCS)/offline-sdk\n\t$(hide) mkdir -p $(OUT_DOCS)/offline-sdk\n\t( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1\n\n.PHONY: docs offline-sdk-docs\ndocs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp\n"
  },
  {
    "path": "core/tasks/owners.mk",
    "content": "# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# Create an artifact to include OWNERS files in source tree.\n\n.PHONY: owners\n\nintermediates := $(call intermediates-dir-for,PACKAGING,owners)\nowners_zip := $(intermediates)/owners.zip\nowners_list := $(OUT_DIR)/.module_paths/OWNERS.list\nowners := $(file <$(owners_list))\n$(owners_zip) : PRIVATE_owners := $(subst $(newline),\\n,$(owners))\n\n$(owners_zip) : $(owners) $(SOONG_ZIP)\n\t@echo \"Building artifact to include OWNERS files.\"\n\trm -rf $@\n\techo -e \"$(PRIVATE_owners)\" > $@.list\n\t$(SOONG_ZIP) -o $@ -C . -l $@.list\n\trm -f $@.list\n\nowners : $(owners_zip)\n\n$(call dist-for-goals, general-tests, $(owners_zip))\n\n$(call declare-0p-target,$(owners_zip))\n"
  },
  {
    "path": "core/tasks/performance-tests.mk",
    "content": "# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\n.PHONY: performance-tests\n\nperformance_tests_zip := $(PRODUCT_OUT)/performance-tests.zip\n# Create an artifact to include a list of test config files in performance-tests.\nperformance_tests_list_zip := $(PRODUCT_OUT)/performance-tests_list.zip\n# Create an artifact to include all test config files in performance-tests.\nperformance_tests_configs_zip := $(PRODUCT_OUT)/performance-tests_configs.zip\n\n$(performance_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(performance_tests_list_zip) $(performance_tests_configs_zip)\n$(performance_tests_zip) : PRIVATE_performance_tests_list_zip := $(performance_tests_list_zip)\n$(performance_tests_zip) : PRIVATE_performance_tests_configs_zip := $(performance_tests_configs_zip)\n$(performance_tests_zip) : PRIVATE_performance_tests_list := $(PRODUCT_OUT)/performance-tests_list\n$(performance_tests_zip) : $(COMPATIBILITY.performance-tests.FILES) $(SOONG_ZIP)\n\techo $(sort $(COMPATIBILITY.performance-tests.FILES)) | tr \" \" \"\\n\" > $@.list\n\tgrep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true\n\tgrep -e .*\\\\.config$$ $@-host.list > $@-host-test-configs.list || true\n\tgrep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true\n\tgrep -e .*\\\\.config$$ $@-target.list > $@-target-test-configs.list || true\n\t$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list -sha256\n\t$(hide) $(SOONG_ZIP) -d -o $(PRIVATE_performance_tests_configs_zip) \\\n\t  -P host -C $(HOST_OUT) -l $@-host-test-configs.list \\\n\t  -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list\n\trm -f $(PRIVATE_performance_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_performance_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_performance_tests_list)\n\t$(hide) $(SOONG_ZIP) -d -o $(PRIVATE_performance_tests_list_zip) -C $(dir $@) -f $(PRIVATE_performance_tests_list)\n\trm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \\\n\t  $(PRIVATE_performance_tests_list)\n\nperformance-tests: $(performance_tests_zip)\n$(call dist-for-goals, performance-tests, $(performance_tests_zip) $(performance_tests_list_zip) $(performance_tests_configs_zip))\n\n$(call declare-1p-container,$(performance_tests_zip),)\n$(call declare-container-license-deps,$(performance_tests_zip),$(COMPATIBILITY.performance-tests.FILES),$(PRODUCT_OUT)/:/)\n\ntests: performance-tests\n\n# Reset temp vars\nperformance_tests_zip :=\nperformance_tests_list_zip :=\nperformance_tests_configs_zip :=\n"
  },
  {
    "path": "core/tasks/platform_availability_check.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Check whether there is any module that isn't available for platform\n# is installed to the platform.\n\n# Skip for unbundled builds that don't produce a platform image.\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n\n# Filter FAKE and NON_INSTALLABLE modules out and then collect those are not\n# available for platform\n_modules_not_available_for_platform := \\\n$(strip $(foreach m,$(product_MODULES),\\\n  $(if $(filter-out FAKE,$(ALL_MODULES.$(m).CLASS)),\\\n    $(if $(ALL_MODULES.$(m).INSTALLED),\\\n      $(if $(filter true,$(ALL_MODULES.$(m).NOT_AVAILABLE_FOR_PLATFORM)),\\\n        $(m))))))\n\nifndef ALLOW_MISSING_DEPENDENCIES\n  _violators_with_path := $(foreach m,$(sort $(_modules_not_available_for_platform)),\\\n    $(m):$(word 1,$(ALL_MODULES.$(m).PATH))\\\n  )\n\n  $(call maybe-print-list-and-error,$(_violators_with_path),\\\nFollowing modules are requested to be installed. But are not available \\\nfor platform because they do not have \"//apex_available:platform\" or \\\nthey depend on other modules that are not available for platform)\n\nelse\n\n# Don't error out immediately when ALLOW_MISSING_DEPENDENCIES is set.\n# Instead, add a dependency on a rule that prints the error message.\n  define not_available_for_platform_rule\n    not_installable_file := $(patsubst $(OUT_DIR)/%,$(OUT_DIR)/NOT_AVAILABLE_FOR_PLATFORM/%,$(1))\n    $(1): $$(not_installable_file)\n    $$(not_installable_file):\n\t$(call echo-error,$(2),Module is requested to be installed but is not \\\navailable for platform because it does not have \"//apex_available:platform\" or \\\nit depends on other modules that are not available for platform.)\n\texit 1\n  endef\n\n  $(foreach m,$(_modules_not_available_for_platform),\\\n    $(foreach i,$(filter-out $(HOST_OUT)/%,$(ALL_MODULES.$(m).INSTALLED)),\\\n      $(eval $(call not_available_for_platform_rule,$(i),$(m)))))\nendif\n\nendif\n"
  },
  {
    "path": "core/tasks/prebuilt_tradefed.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nifeq (,$(wildcard tools/tradefederation/core))\n.PHONY: tradefed-core\ntradefed-core: tradefed atest_tradefed.sh\n.PHONY: tradefed-all\ntradefed-all: tradefed atest_tradefed.sh\n\n$(call dist-for-goals, tradefed, $(HOST_OUT)/etc/tradefed.zip)\nendif\n"
  },
  {
    "path": "core/tasks/sdk-addon.mk",
    "content": "# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n.PHONY: sdk_addon\n\n# If they didn't define PRODUCT_SDK_ADDON_NAME, then we won't define\n# any of these rules.\naddon_name := $(PRODUCT_SDK_ADDON_NAME)\nifneq ($(addon_name),)\n\naddon_dir_leaf        := $(addon_name)-$(INTERNAL_SDK_HOST_OS_NAME)\naddon_dir_img         := $(addon_dir_leaf)-img\nintermediates         := $(HOST_OUT_INTERMEDIATES)/SDK_ADDON/$(addon_name)_intermediates\nfull_target           := $(HOST_OUT_SDK_ADDON)/$(addon_dir_leaf).zip\nfull_target_dist_name := $(addon_name)-FILE_NAME_TAG_PLACEHOLDER-$(INTERNAL_SDK_HOST_OS_NAME)\nfull_target_img       := $(HOST_OUT_SDK_ADDON)/$(addon_dir_img).zip\nstaging               := $(intermediates)\n\nsdk_addon_deps :=\nfiles_to_copy :=\n\ndefine stub-addon-jar-file\n$(subst .jar,_stub-addon.jar,$(1))\nendef\n\ndefine stub-addon-jar\n$(call stub-addon-jar-file,$(1)): $(1) | mkstubs\n\t$(info Stubbing addon jar using $(PRODUCT_SDK_ADDON_STUB_DEFS))\n\t$(hide) $(JAVA) -jar $(call module-installed-files,mkstubs) $(if $(hide),,--v) \\\n\t\t\"$$<\" \"$$@\" @$(PRODUCT_SDK_ADDON_STUB_DEFS)\nendef\n\n# Files that are built and then copied into the sdk-addon\nifneq ($(PRODUCT_SDK_ADDON_COPY_MODULES),)\n$(foreach cf,$(PRODUCT_SDK_ADDON_COPY_MODULES), \\\n  $(eval _src := $(call module-stubs-files,$(call word-colon,1,$(cf)))) \\\n  $(eval $(call stub-addon-jar,$(_src))) \\\n  $(eval _src := $(call stub-addon-jar-file,$(_src))) \\\n  $(if $(_src),,$(eval $(error Unknown or unlinkable module: $(call word-colon,1,$(cf)). Requested by $(INTERNAL_PRODUCT)))) \\\n  $(eval _dest := $(call word-colon,2,$(cf))) \\\n  $(eval files_to_copy += $(addon_dir_leaf):$(_src):$(_dest)) \\\n )\nendif\n\n# Files that are copied directly into the sdk-addon\nifneq ($(PRODUCT_SDK_ADDON_COPY_FILES),)\n$(foreach cf,$(PRODUCT_SDK_ADDON_COPY_FILES), \\\n  $(eval _src  := $(call word-colon,1,$(cf))) \\\n  $(eval _dest := $(call word-colon,2,$(cf))) \\\n  $(if $(findstring images/,$(_dest)), $(eval _root := $(addon_dir_img)), $(eval _root := $(addon_dir_leaf))) \\\n  $(eval files_to_copy += $(_root):$(_src):$(_dest)) \\\n )\nendif\n\n# Files copied in the system-image directory\nfiles_to_copy += \\\n\t$(addon_dir_img):$(INSTALLED_QEMU_SYSTEMIMAGE):images/$(TARGET_CPU_ABI)/system.img \\\n\t$(addon_dir_img):$(INSTALLED_QEMU_VENDORIMAGE):images/$(TARGET_CPU_ABI)/vendor.img \\\n\t$(addon_dir_img):$(INSTALLED_QEMU_RAMDISKIMAGE):images/$(TARGET_CPU_ABI)/ramdisk.img \\\n\t$(addon_dir_img):$(PRODUCT_OUT)/system/build.prop:images/$(TARGET_CPU_ABI)/build.prop \\\n\t$(addon_dir_img):device/generic/goldfish/data/etc/userdata.img:images/$(TARGET_CPU_ABI)/userdata.img \\\n\t$(addon_dir_img):$(target_notice_file_txt):images/$(TARGET_CPU_ABI)/NOTICE.txt \\\n\t$(addon_dir_img):$(PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP):images/source.properties\n\n\nifeq ($(BOARD_AVB_ENABLE),true)\nfiles_to_copy += \\\n\t$(addon_dir_img):$(QEMU_VERIFIED_BOOT_PARAMS):images/$(TARGET_CPU_ABI)/VerifiedBootParams.textproto\nendif\n\n# Generate rules to copy the requested files\n$(foreach cf,$(files_to_copy), \\\n  $(eval _root := $(call word-colon,1,$(cf))) \\\n  $(eval _src  := $(call word-colon,2,$(cf))) \\\n  $(eval _dest := $(call append-path,$(call append-path,$(staging),$(_root)),$(call word-colon,3,$(cf)))) \\\n  $(eval $(call copy-one-file,$(_src),$(_dest))) \\\n  $(eval sdk_addon_deps += $(_dest)) \\\n )\n\n# The system-image source.properties is a template that we directly expand in-place\naddon_img_source_prop := $(call append-path,$(staging),$(addon_dir_img))/images/$(TARGET_CPU_ABI)/source.properties\nsdk_addon_deps += $(addon_img_source_prop)\n\n$(addon_img_source_prop): $(PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP)\n\t@echo Generate $@\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) sed \\\n\t\t-e 's/$${PLATFORM_VERSION}/$(PLATFORM_VERSION)/' \\\n\t\t-e 's/$${PLATFORM_SDK_VERSION}/$(PLATFORM_SDK_VERSION)/' \\\n\t\t-e 's/$${PLATFORM_VERSION_CODENAME}/$(subst REL,,$(PLATFORM_VERSION_CODENAME))/' \\\n\t\t-e 's/$${TARGET_ARCH}/$(TARGET_ARCH)/' \\\n\t\t-e 's/$${TARGET_CPU_ABI}/$(TARGET_CPU_ABI)/' \\\n\t\t$< > $@ && sed -i -e '/^AndroidVersion.CodeName=\\s*$$/d' $@\n\n\n# We don't know about all of the docs files, so depend on the timestamps for\n# them, and record the directories, and the packaging rule will just copy the\n# whole thing.\ndoc_modules := $(PRODUCT_SDK_ADDON_DOC_MODULES)\nsdk_addon_deps += $(foreach dm, $(doc_modules), $(call doc-timestamp-for, $(dm)))\n$(full_target): PRIVATE_DOCS_DIRS := $(addprefix $(OUT_DOCS)/, $(doc_modules))\n\n$(full_target): PRIVATE_STAGING_DIR := $(call append-path,$(staging),$(addon_dir_leaf))\n\n$(full_target): $(sdk_addon_deps) | $(SOONG_ZIP)\n\t@echo Packaging SDK Addon: $@\n\t$(hide) mkdir -p $(PRIVATE_STAGING_DIR)/docs\n\t$(hide) for d in $(PRIVATE_DOCS_DIRS); do \\\n\t    cp -R $$d $(PRIVATE_STAGING_DIR)/docs ;\\\n\t  done\n\t$(hide) mkdir -p $(dir $@)\n\t$(hide) $(SOONG_ZIP) -o $@ -C $(dir $(PRIVATE_STAGING_DIR)) -D $(PRIVATE_STAGING_DIR)\n\n$(full_target_img): PRIVATE_STAGING_DIR := $(call append-path,$(staging),$(addon_dir_img))/images/$(TARGET_CPU_ABI)\n$(full_target_img): $(full_target) $(addon_img_source_prop) | $(SOONG_ZIP)\n\t@echo Packaging SDK Addon System-Image: $@\n\t$(hide) mkdir -p $(dir $@)\n\tcp -R $(PRODUCT_OUT)/data $(PRIVATE_STAGING_DIR)\n\t$(hide) $(SOONG_ZIP) -o $@ -C $(dir $(PRIVATE_STAGING_DIR)) -D $(PRIVATE_STAGING_DIR)\n\n\nsdk_addon: $(full_target) $(full_target_img)\n\nifneq ($(sdk_repo_goal),)\n# If we're building the sdk_repo, keep the name of the addon zip\n# around so that development/build/tools/sdk_repo.mk can dist it\n# at the appropriate location.\nADDON_SDK_ZIP        := $(full_target)\nADDON_SDK_IMG_ZIP    := $(full_target_img)\nelse\n# When not building an sdk_repo, just dist the addon zip file\n# as-is.\n$(call dist-for-goals, sdk_addon, $(full_target):$(full_target_dist_name))\nendif\n\nelse # addon_name\nifneq ($(filter sdk_addon,$(MAKECMDGOALS)),)\n$(error Trying to build sdk_addon, but product '$(INTERNAL_PRODUCT)' does not define one)\nendif\nendif # addon_name\n"
  },
  {
    "path": "core/tasks/sts.mk",
    "content": "# Copyright (C) 2016 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nifneq ($(wildcard test/sts/README.md),)\ntest_suite_name := sts\ntest_suite_tradefed := sts-tradefed\ntest_suite_readme := test/sts/README.md\n\ninclude $(BUILD_SYSTEM)/tasks/tools/compatibility.mk\n\n.PHONY: sts\nsts: $(compatibility_zip)\n$(call dist-for-goals, sts, $(compatibility_zip))\nendif\n"
  },
  {
    "path": "core/tasks/tools/build_custom_image.mk",
    "content": "#\n# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n\n# Define rule to build one custom image.\n# Input variables: my_custom_imag_makefile\n\n$(call clear-var-list, $(custom_image_parameter_variables))\n\ninclude $(my_custom_imag_makefile)\n\nmy_custom_image_name := $(basename $(notdir $(my_custom_imag_makefile)))\n\nintermediates := $(call intermediates-dir-for,PACKAGING,$(my_custom_image_name))\nmy_built_custom_image := $(intermediates)/$(my_custom_image_name).img\nmy_staging_dir := $(intermediates)/$(CUSTOM_IMAGE_MOUNT_POINT)\n\n# Collect CUSTOM_IMAGE_MODULES's installd files and their PICKUP_FILES.\nmy_built_modules :=\nmy_copy_pairs :=\nmy_pickup_files :=\n\n$(foreach m,$(CUSTOM_IMAGE_MODULES),\\\n  $(eval _pickup_files := $(strip $(ALL_MODULES.$(m).PICKUP_FILES)\\\n    $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).PICKUP_FILES)))\\\n  $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\\\n    $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\\\n  $(if $(_pickup_files)$(_built_files),,\\\n    $(warning Unknown installed file for module '$(m)'))\\\n  $(eval my_pickup_files += $(_pickup_files))\\\n  $(foreach i, $(_built_files),\\\n    $(eval bui_ins := $(subst :,$(space),$(i)))\\\n    $(eval ins := $(word 2,$(bui_ins)))\\\n    $(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\\\n      $(eval bui := $(word 1,$(bui_ins)))\\\n      $(eval my_built_modules += $(bui))\\\n      $(eval my_copy_dest := $(patsubst $(PRODUCT_OUT)/%,%,$(ins)))\\\n      $(eval my_copy_dest := $(subst /,$(space),$(my_copy_dest)))\\\n      $(eval my_copy_dest := $(wordlist 2,999,$(my_copy_dest)))\\\n      $(eval my_copy_dest := $(subst $(space),/,$(my_copy_dest)))\\\n      $(eval my_copy_pairs += $(bui):$(my_staging_dir)/$(my_copy_dest)))\\\n  ))\n\nmy_kernel_module_copy_files :=\nmy_custom_image_modules_var := BOARD_$(strip $(call to-upper,$(my_custom_image_name)))_KERNEL_MODULES\nifdef $(my_custom_image_modules_var)\n$(foreach kmod,\\\n  $(call build-image-kernel-modules,$($(my_custom_image_modules_var)),$(my_staging_dir),$(CUSTOM_IMAGE_MOUNT_POINT),$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)),$($(my_custom_image_modules_var)),modules.load,,$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)_stripped)),\\\n  $(eval pair := $(subst :,$(space),$(kmod)))\\\n  $(eval my_kernel_module_copy_files += $(word 1,$(pair)):$(subst $(my_staging_dir)/,,$(word 2,$(pair)))))\nendif\n\n# Collect CUSTOM_IMAGE_COPY_FILES.\nmy_image_copy_files :=\n$(foreach f,$(CUSTOM_IMAGE_COPY_FILES) $(my_kernel_module_copy_files),\\\n  $(eval pair := $(subst :,$(space),$(f)))\\\n  $(eval src := $(word 1,$(pair)))\\\n  $(eval my_image_copy_files += $(src))\\\n  $(eval my_copy_pairs += $(src):$(my_staging_dir)/$(word 2,$(pair))))\n\nifdef CUSTOM_IMAGE_AVB_KEY_PATH\nifndef CUSTOM_IMAGE_AVB_ALGORITHM\n  $(error CUSTOM_IMAGE_AVB_ALGORITHM is not defined)\nendif\nifndef CUSTOM_IMAGE_AVB_ROLLBACK_INDEX\n  $(error CUSTOM_IMAGE_AVB_ROLLBACK_INDEX is not defined)\nendif\n# set rollback_index via footer args\nCUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS += --rollback_index $(CUSTOM_IMAGE_AVB_ROLLBACK_INDEX)\nCUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS += --rollback_index $(CUSTOM_IMAGE_AVB_ROLLBACK_INDEX)\nendif\n\n$(my_built_custom_image): PRIVATE_INTERMEDIATES := $(intermediates)\n$(my_built_custom_image): PRIVATE_MOUNT_POINT := $(CUSTOM_IMAGE_MOUNT_POINT)\n$(my_built_custom_image): PRIVATE_PARTITION_SIZE := $(CUSTOM_IMAGE_PARTITION_SIZE)\n$(my_built_custom_image): PRIVATE_FILE_SYSTEM_TYPE := $(CUSTOM_IMAGE_FILE_SYSTEM_TYPE)\n$(my_built_custom_image): PRIVATE_STAGING_DIR := $(my_staging_dir)\n$(my_built_custom_image): PRIVATE_COPY_PAIRS := $(my_copy_pairs)\n$(my_built_custom_image): PRIVATE_PICKUP_FILES := $(my_pickup_files)\n$(my_built_custom_image): PRIVATE_SELINUX := $(CUSTOM_IMAGE_SELINUX)\n$(my_built_custom_image): PRIVATE_VERITY_BLOCK_DEVICE := $(CUSTOM_IMAGE_VERITY_BLOCK_DEVICE)\n$(my_built_custom_image): PRIVATE_DICT_FILE := $(CUSTOM_IMAGE_DICT_FILE)\n$(my_built_custom_image): PRIVATE_AVB_AVBTOOL := $(AVBTOOL)\n$(my_built_custom_image): PRIVATE_AVB_KEY_PATH := $(CUSTOM_IMAGE_AVB_KEY_PATH)\n$(my_built_custom_image): PRIVATE_AVB_ALGORITHM:= $(CUSTOM_IMAGE_AVB_ALGORITHM)\n$(my_built_custom_image): PRIVATE_AVB_HASH_ENABLE := $(CUSTOM_IMAGE_AVB_HASH_ENABLE)\n$(my_built_custom_image): PRIVATE_AVB_ADD_HASH_FOOTER_ARGS := $(CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS)\n$(my_built_custom_image): PRIVATE_AVB_HASHTREE_ENABLE := $(CUSTOM_IMAGE_AVB_HASHTREE_ENABLE)\n$(my_built_custom_image): PRIVATE_AVB_ADD_HASHTREE_FOOTER_ARGS := $(CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS)\nifeq (true,$(filter true, $(CUSTOM_IMAGE_AVB_HASH_ENABLE) $(CUSTOM_IMAGE_AVB_HASHTREE_ENABLE)))\n  $(my_built_custom_image): $(AVBTOOL)\nelse ifneq (,$(filter true, $(CUSTOM_IMAGE_AVB_HASH_ENABLE) $(CUSTOM_IMAGE_AVB_HASHTREE_ENABLE)))\n  $(error Cannot set both CUSTOM_IMAGE_AVB_HASH_ENABLE and CUSTOM_IMAGE_AVB_HASHTREE_ENABLE to true)\nendif\nifeq ($(strip $(HAS_BUILD_NUMBER)),true)\n$(my_built_custom_image): $(BUILD_NUMBER_FILE)\nendif\n$(my_built_custom_image): $(INTERNAL_USERIMAGES_DEPS) $(my_built_modules) $(my_image_copy_files) $(my_custom_image_modules_dep) \\\n  $(CUSTOM_IMAGE_DICT_FILE)\n\t@echo \"Build image $@\"\n\t$(hide) rm -rf $(PRIVATE_INTERMEDIATES) && mkdir -p $(PRIVATE_INTERMEDIATES)\n\t$(hide) rm -rf $(PRIVATE_STAGING_DIR) && mkdir -p $(PRIVATE_STAGING_DIR)\n\t# Copy all the files.\n\t$(hide) $(foreach p,$(PRIVATE_COPY_PAIRS),\\\n\t          $(eval pair := $(subst :,$(space),$(p)))\\\n\t          mkdir -p $(dir $(word 2,$(pair)));\\\n\t          cp -Rf $(word 1,$(pair)) $(word 2,$(pair));)\n\t$(if $($(PRIVATE_PICKUP_FILES)),$(hide) cp -Rf $(PRIVATE_PICKUP_FILES) $(PRIVATE_STAGING_DIR))\n\t# Generate the dict.\n\t$(hide) echo \"# For all accepted properties, see BuildImage() in tools/releasetools/build_image.py\" > $(PRIVATE_INTERMEDIATES)/image_info.txt\n\t$(hide) echo \"mount_point=$(PRIVATE_MOUNT_POINT)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt\n\t$(hide) echo \"partition_name=$(PRIVATE_MOUNT_POINT)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt\n\t$(hide) echo \"fs_type=$(PRIVATE_FILE_SYSTEM_TYPE)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt\n\t$(hide) echo \"partition_size=$(PRIVATE_PARTITION_SIZE)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt\n\t$(hide) echo \"ext_mkuserimg=$(notdir $(MKEXTUSERIMG))\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt\n\t$(if $(PRIVATE_SELINUX),$(hide) echo \"selinux_fc=$(SELINUX_FC)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt)\n\t$(if $(filter eng, $(TARGET_BUILD_VARIANT)),$(hide) echo \"verity_disable=true\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt)\n\t$(hide) echo \"avb_avbtool=$(PRIVATE_AVB_AVBTOOL)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt\n\t$(if $(PRIVATE_AVB_KEY_PATH),\\\n\t  $(hide) echo \"avb_key_path=$(PRIVATE_AVB_KEY_PATH)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\\\n\t    echo \"avb_algorithm=$(PRIVATE_AVB_ALGORITHM)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt)\n\t$(if $(PRIVATE_AVB_HASH_ENABLE),\\\n\t  $(hide) echo \"avb_hash_enable=$(PRIVATE_AVB_HASH_ENABLE)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\\\n\t    echo \"avb_add_hash_footer_args=$(PRIVATE_AVB_ADD_HASH_FOOTER_ARGS)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt)\n\t$(if $(PRIVATE_AVB_HASHTREE_ENABLE),\\\n\t  $(hide) echo \"avb_hashtree_enable=$(PRIVATE_AVB_HASHTREE_ENABLE)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\\\n\t    echo \"avb_add_hashtree_footer_args=$(PRIVATE_AVB_ADD_HASHTREE_FOOTER_ARGS)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt)\n\t$(if $(PRIVATE_DICT_FILE),\\\n\t  $(hide) echo \"# Properties from $(PRIVATE_DICT_FILE)\" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\\\n\t    cat $(PRIVATE_DICT_FILE) >> $(PRIVATE_INTERMEDIATES)/image_info.txt)\n\t# Generate the image.\n\t$(if $(filter oem,$(PRIVATE_MOUNT_POINT)), \\\n\t  $(hide) echo \"oem.buildnumber=$(BUILD_NUMBER_FROM_FILE)\" >> $(PRIVATE_STAGING_DIR)/oem.prop)\n\t$(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \\\n\t    $(BUILD_IMAGE) \\\n\t        $(PRIVATE_STAGING_DIR) $(PRIVATE_INTERMEDIATES)/image_info.txt $@ $(TARGET_OUT)\n\nmy_installed_custom_image := $(PRODUCT_OUT)/$(notdir $(my_built_custom_image))\n$(my_installed_custom_image) : $(my_built_custom_image)\n\t$(call copy-file-to-new-target-with-cp)\n\n.PHONY: $(my_custom_image_name)\ncustom_images $(my_custom_image_name) : $(my_installed_custom_image)\n\n# Archive the built image.\n$(call dist-for-goals, $(my_custom_image_name) custom_images,$(my_installed_custom_image))\n\nmy_staging_dir :=\nmy_built_modules :=\nmy_copy_dest :=\nmy_copy_pairs :=\nmy_pickup_files :=\n"
  },
  {
    "path": "core/tasks/tools/compatibility.mk",
    "content": "# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Package up a compatibility test suite in a zip file.\n#\n# Input variables:\n#   test_suite_name: the name of this test suite eg. cts\n#   test_suite_tradefed: the name of this test suite's tradefed wrapper\n#   test_suite_dynamic_config: the path to this test suite's dynamic configuration file\n#   test_suite_readme: the path to a README file for this test suite\n#   test_suite_prebuilt_tools: the set of prebuilt tools to be included directly\n#                         in the 'tools' subdirectory of the test suite.\n#   test_suite_tools: the set of tools for this test suite\n#\n# Output variables:\n#   compatibility_zip: the path to the output zip file.\n\nspecial_mts_test_suites :=\nspecial_mts_test_suites += $(mts_modules)\nifneq ($(filter $(special_mts_test_suites),$(patsubst mcts-%,%,$(test_suite_name))),)\n\ttest_suite_subdir := android-mts\nelse ifneq ($(filter $(special_mts_test_suites),$(patsubst mts-%,%,$(test_suite_name))),)\n\ttest_suite_subdir := android-mts\nelse\n\ttest_suite_subdir := android-$(test_suite_name)\nendif\n\nout_dir := $(HOST_OUT)/$(test_suite_name)/$(test_suite_subdir)\ntest_artifacts := $(COMPATIBILITY.$(test_suite_name).FILES)\ntest_tools := $(HOST_OUT_JAVA_LIBRARIES)/tradefed.jar \\\n  $(HOST_OUT_JAVA_LIBRARIES)/loganalysis.jar \\\n  $(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util.jar \\\n  $(HOST_OUT_JAVA_LIBRARIES)/compatibility-tradefed.jar \\\n  $(HOST_OUT_JAVA_LIBRARIES)/$(test_suite_tradefed).jar \\\n  $(HOST_OUT_JAVA_LIBRARIES)/$(test_suite_tradefed)-tests.jar \\\n  $(HOST_OUT_EXECUTABLES)/$(test_suite_tradefed) \\\n  $(HOST_OUT_EXECUTABLES)/test-utils-script \\\n  $(test_suite_readme)\n\n$(foreach f,$(test_suite_readme),$(if $(strip $(ALL_TARGETS.$(f).META_LIC)),,$(eval ALL_TARGETS.$(f).META_LIC := $(module_license_metadata))))\n\ntest_tools += $(test_suite_tools)\n\n# The JDK to package into the test suite zip file.  Always package the linux JDK.\ntest_suite_jdk_dir := $(ANDROID_JAVA_HOME)/../linux-x86\nifndef test_suite_jdk_files\n  # This file gets included many times, so make sure we only run the $(shell) once.\n  # Otherwise it will slow down every build due to all copies of it being rerun when kati\n  # checks the stamp file.\n  test_suite_jdk_files :=$= $(shell find $(test_suite_jdk_dir) -type f | sort)\nendif\ntest_suite_jdk := $(call intermediates-dir-for,PACKAGING,$(test_suite_name)_jdk,HOST)/jdk.zip\n$(test_suite_jdk): PRIVATE_JDK_DIR := $(test_suite_jdk_dir)\n$(test_suite_jdk): PRIVATE_SUBDIR := $(test_suite_subdir)\n$(test_suite_jdk): $(test_suite_jdk_files)\n$(test_suite_jdk): $(SOONG_ZIP)\n\t$(SOONG_ZIP) -o $@ -P $(PRIVATE_SUBDIR)/jdk -C $(PRIVATE_JDK_DIR) -D $(PRIVATE_JDK_DIR) -sha256\n\n$(call declare-license-metadata,$(test_suite_jdk),SPDX-license-identifier-GPL-2.0-with-classpath-exception,permissive,\\\n  $(test_suite_jdk_dir)/legal/java.base/LICENSE,JDK,prebuilts/jdk/$(notdir $(patsubst %/,%,$(dir $(test_suite_jdk_dir)))))\n\n# Copy license metadata\n$(call declare-copy-target-license-metadata,$(out_dir)/$(notdir $(test_suite_jdk)),$(test_suite_jdk))\n$(foreach t,$(test_tools) $(test_suite_prebuilt_tools),\\\n  $(eval _dst := $(out_dir)/tools/$(notdir $(t)))\\\n  $(if $(strip $(ALL_TARGETS.$(t).META_LIC)),\\\n    $(call declare-copy-target-license-metadata,$(_dst),$(t)),\\\n    $(warning $(t) has no license metadata)\\\n  )\\\n)\ntest_copied_tools := $(foreach t,$(test_tools) $(test_suite_prebuilt_tools), $(out_dir)/tools/$(notdir $(t))) $(out_dir)/$(notdir $(test_suite_jdk))\n\n\n# Include host shared libraries\nhost_shared_libs := $(call copy-many-files, $(COMPATIBILITY.$(test_suite_name).HOST_SHARED_LIBRARY.FILES))\n\n$(if $(strip $(host_shared_libs)),\\\n  $(foreach p,$(COMPATIBILITY.$(test_suite_name).HOST_SHARED_LIBRARY.FILES),\\\n    $(eval _src := $(call word-colon,1,$(p)))\\\n    $(eval _dst := $(call word-colon,2,$(p)))\\\n    $(if $(strip $(ALL_TARGETS.$(_src).META_LIC)),\\\n      $(call declare-copy-target-license-metadata,$(_dst),$(_src)),\\\n      $(warning $(_src) has no license metadata for $(_dst))\\\n    )\\\n  )\\\n)\n\ncompatibility_zip_deps := \\\n  $(test_artifacts) \\\n  $(test_tools) \\\n  $(test_suite_prebuilt_tools) \\\n  $(test_suite_dynamic_config) \\\n  $(test_suite_jdk) \\\n  $(MERGE_ZIPS) \\\n  $(SOONG_ZIP) \\\n  $(host_shared_libs) \\\n  $(test_suite_extra_deps) \\\n\ncompatibility_zip_resources := $(out_dir)/tools $(out_dir)/testcases $(out_dir)/lib $(out_dir)/lib64\n\n# Test Suite NOTICE files\ntest_suite_notice_txt := $(out_dir)/NOTICE.txt\ntest_suite_notice_html := $(out_dir)/NOTICE.html\n\ncompatibility_zip_deps += $(test_suite_notice_txt)\ncompatibility_zip_resources += $(test_suite_notice_txt)\n\ncompatibility_tests_list_zip := $(HOST_OUT)/$(test_suite_name)/android-$(test_suite_name)-tests_list.zip\n\ncompatibility_zip := $(HOST_OUT)/$(test_suite_name)/android-$(test_suite_name).zip\n$(compatibility_zip) : .KATI_IMPLICIT_OUTPUTS := $(compatibility_tests_list_zip)\n$(compatibility_zip): PRIVATE_OUT_DIR := $(out_dir)\n$(compatibility_zip): PRIVATE_TOOLS := $(test_tools) $(test_suite_prebuilt_tools)\n$(compatibility_zip): PRIVATE_SUITE_NAME := $(test_suite_name)\n$(compatibility_zip): PRIVATE_DYNAMIC_CONFIG := $(test_suite_dynamic_config)\n$(compatibility_zip): PRIVATE_RESOURCES := $(compatibility_zip_resources)\n$(compatibility_zip): PRIVATE_JDK := $(test_suite_jdk)\n$(compatibility_zip): PRIVATE_tests_list := $(out_dir)-tests_list\n$(compatibility_zip): PRIVATE_tests_list_zip := $(compatibility_tests_list_zip)\nifeq ($(strip $(HAS_BUILD_NUMBER)),true)\n$(compatibility_zip): $(BUILD_NUMBER_FILE)\nendif\n$(compatibility_zip): $(compatibility_zip_deps) | $(ADB) $(ACP)\n# Make dir structure\n\tmkdir -p $(PRIVATE_OUT_DIR)/tools $(PRIVATE_OUT_DIR)/testcases\n\trm -f $@ $@.tmp $@.jdk\n\techo $(BUILD_NUMBER_FROM_FILE) > $(PRIVATE_OUT_DIR)/tools/version.txt\n# Copy tools\n\tcp $(PRIVATE_TOOLS) $(PRIVATE_OUT_DIR)/tools\n\t$(if $(PRIVATE_DYNAMIC_CONFIG),$(hide) cp $(PRIVATE_DYNAMIC_CONFIG) $(PRIVATE_OUT_DIR)/testcases/$(PRIVATE_SUITE_NAME).dynamic)\n\tfind $(PRIVATE_RESOURCES) | sort >$@.list\n\t$(SOONG_ZIP) -d -o $@.tmp -C $(dir $@) -l $@.list -sha256\n\t$(MERGE_ZIPS) $@ $@.tmp $(PRIVATE_JDK)\n\trm -f $@.tmp\n# Build a list of tests\n\trm -f $(PRIVATE_tests_list)\n\t$(hide) grep -e .*\\\\.config$$ $@.list | sed s%$(PRIVATE_OUT_DIR)/testcases/%%g > $(PRIVATE_tests_list)\n\t$(SOONG_ZIP) -d -o $(PRIVATE_tests_list_zip) -j -f $(PRIVATE_tests_list)\n\trm -f $(PRIVATE_tests_list)\n\n$(call declare-0p-target,$(compatibility_tests_list_zip),)\n\n$(call declare-1p-container,$(compatibility_zip),)\n$(call declare-container-license-deps,$(compatibility_zip),$(compatibility_zip_deps) $(test_copied_tools), $(out_dir)/:/)\n\n$(eval $(call html-notice-rule,$(test_suite_notice_html),\"Test suites\",\"Notices for files contained in the test suites filesystem image:\",$(compatibility_zip),$(compatibility_zip)))\n$(eval $(call text-notice-rule,$(test_suite_notice_txt),\"Test suites\",\"Notices for files contained in the test suites filesystem image:\",$(compatibility_zip),$(compatibility_zip)))\n\n$(call declare-0p-target,$(test_suite_notice_html))\n$(call declare-0p-target,$(test_suite_notice_txt))\n\n$(call declare-1p-copy-files,$(test_suite_dynamic_config),)\n$(call declare-1p-copy-files,$(test_suite_prebuilt_tools),)\n\n# Reset all input variables\ntest_suite_name :=\ntest_suite_tradefed :=\ntest_suite_dynamic_config :=\ntest_suite_readme :=\ntest_suite_prebuilt_tools :=\ntest_suite_tools :=\ntest_suite_jdk :=\ntest_suite_jdk_dir :=\nhost_shared_libs :=\ntest_suite_extra_deps :=\n"
  },
  {
    "path": "core/tasks/tools/package-modules.mk",
    "content": "# Package up modules to a zip file.\n# It preserves the install path of the modules' installed files.\n#\n# Input variables:\n#   my_modules: a list of module names\n#   my_package_name: the name of the output zip file.\n#   my_copy_pairs: a list of extra files to install (in src:dest format)\n# Optional input variables:\n#   my_modules_strict: what happens when a module from my_modules does not exist\n#     \"true\": error out when a module is missing\n#     \"false\": print a warning when a module is missing\n#     \"\": defaults to false currently\n# Output variables:\n#   my_package_zip: the path to the output zip file.\n#\n#\n\nmy_makefile := $(lastword $(filter-out $(lastword $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := $(my_package_name)\nLOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0\nLOCAL_LICENSE_CONDITIONS := notice\nLOCAL_LICENSE_PACKAGE_NAME := Android\nLOCAL_NOTICE_FILE := build/soong/licenses/LICENSE\nLOCAL_MODULE_CLASS := PACKAGING\nLOCAL_MODULE_STEM := $(my_package_name).zip\nLOCAL_UNINSTALLABLE_MODULE := true\ninclude $(BUILD_SYSTEM)/base_rules.mk\nmy_staging_dir := $(intermediates)/staging\nmy_package_zip := $(LOCAL_BUILT_MODULE)\n\nmy_built_modules := $(foreach p,$(my_copy_pairs),$(call word-colon,1,$(p)))\nmy_copy_pairs := $(foreach p,$(my_copy_pairs),$(call word-colon,1,$(p)):$(my_staging_dir)/$(call word-colon,2,$(p)))\nmy_pickup_files :=\nmy_missing_error :=\n\n# Iterate over the modules and include their direct dependencies stated in the\n# LOCAL_REQUIRED_MODULES.\nmy_modules_and_deps := $(my_modules)\n$(foreach m,$(my_modules),\\\n  $(eval _explicitly_required := \\\n    $(strip $(ALL_MODULES.$(m).EXPLICITLY_REQUIRED_FROM_TARGET)\\\n    $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).EXPLICITLY_REQUIRED_FROM_TARGET)))\\\n  $(eval my_modules_and_deps += $(_explicitly_required))\\\n)\n\nifneq ($(filter-out true false,$(my_modules_strict)),)\n  $(shell $(call echo-error,$(my_makefile),$(my_package_name): Invalid value for 'my_module_strict' = '$(my_modules_strict)'. Valid values: 'true', 'false', ''))\n  $(error done)\nendif\n\nmy_missing_files = $(shell $(call echo-warning,$(my_makefile),$(my_package_name): Unknown installed file for module '$(1)'))$(shell$(call echo-warning,$(my_makefile),$(my_package_name): Some necessary modules may have been skipped by Soong. Check if PRODUCT_SOURCE_ROOT_DIRS is pruning necessary Android.bp files.))\nifeq ($(ALLOW_MISSING_DEPENDENCIES),true)\n  # Ignore unknown installed files on partial builds\n  my_missing_files =\nelse ifneq ($(my_modules_strict),false)\n  my_missing_files = $(shell $(call echo-error,$(my_makefile),$(my_package_name): Unknown installed file for module '$(1)'))$(shell$(call echo-warning,$(my_makefile),$(my_package_name): Some necessary modules may have been skipped by Soong. Check if PRODUCT_SOURCE_ROOT_DIRS is pruning necessary Android.bp files.))$(eval my_missing_error := true)\nendif\n\n# Iterate over modules' built files and installed files;\n# Calculate the dest files in the output zip file.\n\n$(foreach m,$(my_modules_and_deps),\\\n  $(eval _pickup_files := $(strip $(ALL_MODULES.$(m).PICKUP_FILES)\\\n    $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).PICKUP_FILES)))\\\n  $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\\\n    $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\\\n  $(eval _module_class_folder := $($(strip MODULE_CLASS_$(word 1, $(strip $(ALL_MODULES.$(m).CLASS)\\\n    $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).CLASS))))))\\\n  $(if $(_pickup_files)$(_built_files),,\\\n    $(call my_missing_files,$(m)))\\\n  $(eval my_pickup_files += $(_pickup_files))\\\n  $(foreach i, $(_built_files),\\\n    $(eval bui_ins := $(subst :,$(space),$(i)))\\\n    $(eval ins := $(word 2,$(bui_ins)))\\\n    $(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\\\n      $(eval bui := $(word 1,$(bui_ins)))\\\n      $(eval my_built_modules += $(bui))\\\n      $(if $(filter $(_module_class_folder), nativetest benchmarktest),\\\n        $(eval module_class_folder_stem := $(_module_class_folder)$(findstring 64, $(patsubst $(PRODUCT_OUT)/%,%,$(ins)))),\\\n        $(eval module_class_folder_stem := $(_module_class_folder)))\\\n      $(eval my_copy_dest := $(patsubst data/%,DATA/%,\\\n                               $(patsubst testcases/%,DATA/$(module_class_folder_stem)/%,\\\n                                 $(patsubst testcases/$(m)/$(TARGET_ARCH)/%,DATA/$(module_class_folder_stem)/$(m)/%,\\\n                                   $(patsubst testcases/$(m)/$(TARGET_2ND_ARCH)/%,DATA/$(module_class_folder_stem)/$(m)/%,\\\n                                     $(patsubst system/%,DATA/%,\\\n                                       $(patsubst $(PRODUCT_OUT)/%,%,$(ins))))))))\\\n      $(eval my_copy_pairs += $(bui):$(my_staging_dir)/$(my_copy_dest)))\\\n  ))\n\nifneq ($(my_missing_error),)\n  $(error done)\nendif\n\n$(my_package_zip): PRIVATE_COPY_PAIRS := $(my_copy_pairs)\n$(my_package_zip): PRIVATE_STAGING_DIR := $(my_staging_dir)\n$(my_package_zip): PRIVATE_PICKUP_FILES := $(my_pickup_files)\n$(my_package_zip) : $(my_built_modules) $(SOONG_ZIP)\n\t@echo \"Package $@\"\n\t@rm -rf $(PRIVATE_STAGING_DIR) && mkdir -p $(PRIVATE_STAGING_DIR)\n\t$(foreach p, $(PRIVATE_COPY_PAIRS),\\\n\t  $(eval pair := $(subst :,$(space),$(p)))\\\n\t  mkdir -p $(dir $(word 2,$(pair))) && \\\n\t  cp -Rf $(word 1,$(pair)) $(word 2,$(pair)) && ) true\n\t$(hide) $(foreach f, $(PRIVATE_PICKUP_FILES),\\\n\t  cp -RfL $(f) $(PRIVATE_STAGING_DIR) && ) true\n\t$(hide) $(SOONG_ZIP) -o $@ -C $(PRIVATE_STAGING_DIR) -D $(PRIVATE_STAGING_DIR)\n\trm -rf $(PRIVATE_STAGING_DIR)\n\nmy_makefile :=\nmy_staging_dir :=\nmy_built_modules :=\nmy_copy_dest :=\nmy_copy_pairs :=\nmy_pickup_files :=\nmy_missing_files :=\nmy_missing_error :=\nmy_modules_and_deps :=\nmy_modules_strict :=\n"
  },
  {
    "path": "core/tasks/tradefed-tests-list.mk",
    "content": "# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# List all TradeFed tests from COMPATIBILITY.tradefed_tests_dir\n.PHONY: tradefed-tests-list\n\nCOMPATIBILITY.tradefed_tests_dir := \\\n  $(COMPATIBILITY.tradefed_tests_dir) \\\n  tools/tradefederation/core/res/config \\\n  tools/tradefederation/core/javatests/res/config \\\n  vendor/google_tradefederation/contrib/res/config \\\n  vendor/google_tradefederation/core/res/config \\\n  vendor/google_tradefederation/core/javatests/res/config \\\n  vendor/google_tradefederation/core/prod_tests/res/config\n\ntradefed_tests :=\n$(foreach dir, $(COMPATIBILITY.tradefed_tests_dir), \\\n  $(if $(wildcard $(dir)/*), \\\n    $(eval tradefed_tests += $(shell find $(dir) -type f -name \"*.xml\")) \\\n  ) \\\n)\n\ntradefed_tests_list_intermediates := $(call intermediates-dir-for,PACKAGING,tradefed_tests_list,HOST,COMMON)\ntradefed_tests_list_zip := $(tradefed_tests_list_intermediates)/tradefed-tests_list.zip\nall_tests :=\n$(foreach test, $(tradefed_tests), \\\n  $(eval all_tests += $(word 2,$(subst /res/config/,$(space),$(test)))))\n$(tradefed_tests_list_zip) : PRIVATE_tradefed_tests := $(subst .xml,,$(subst $(space),\\n,$(sort $(all_tests))))\n$(tradefed_tests_list_zip) : PRIVATE_tradefed_tests_list := $(tradefed_tests_list_intermediates)/tradefed-tests_list\n\n$(tradefed_tests_list_zip) : $(tradefed_tests) $(SOONG_ZIP)\n\t@echo \"Package: $@\"\n\t$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)\n\t$(hide) echo -e \"$(PRIVATE_tradefed_tests)\" > $(PRIVATE_tradefed_tests_list)\n\t$(hide) $(SOONG_ZIP) -d -o $@ -C $(dir $@) -f $(PRIVATE_tradefed_tests_list)\n\ntradefed-tests-list : $(tradefed_tests_list_zip)\n$(call dist-for-goals, tradefed-tests-list, $(tradefed_tests_list_zip))\n\n$(call declare-1p-target,$(tradefed_tests_list_zip),)\n\ntests: tradefed-tests-list\n"
  },
  {
    "path": "core/tasks/vendor_module_check.mk",
    "content": "#\n# Copyright (C) 2011 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Restrict the vendor module owners here.\n_vendor_owner_allowed_list := \\\n        asus \\\n        audience \\\n        atmel \\\n        broadcom \\\n        csr \\\n        elan \\\n        fpc \\\n        google \\\n        htc \\\n        huawei \\\n        imgtec \\\n        invensense \\\n        intel \\\n        lge \\\n        moto \\\n        mtk \\\n        nvidia \\\n        nxp \\\n        nxpsw \\\n        qcom \\\n        qti \\\n        samsung \\\n        samsung_arm \\\n        sony \\\n        synaptics \\\n        ti \\\n        trusted_logic \\\n        verizon \\\n        waves \\\n        widevine\n\n\n_restrictions := $(PRODUCT_RESTRICT_VENDOR_FILES)\n\nifneq (,$(_restrictions))\nifneq (,$(VENDOR_PRODUCT_RESTRICT_VENDOR_FILES))\n$(error Error: cannot set both PRODUCT_RESTRICT_VENDOR_FILES and VENDOR_PRODUCT_RESTRICT_VENDOR_FILES)\nendif\n_vendor_exception_path_prefix :=\n_vendor_exception_modules :=\nelse\n_restrictions := $(VENDOR_PRODUCT_RESTRICT_VENDOR_FILES)\n_vendor_exception_path_prefix := $(patsubst %, vendor/%/%, $(VENDOR_EXCEPTION_PATHS))\n_vendor_exception_modules := $(VENDOR_EXCEPTION_MODULES)\nendif\n\n\nifneq (,$(_restrictions))\n\n_vendor_check_modules := \\\n$(foreach m, $(filter-out $(_vendor_exception_modules), $(product_MODULES)), \\\n  $(if $(filter-out FAKE, $(ALL_MODULES.$(m).CLASS)),\\\n    $(if $(filter vendor/%, $(ALL_MODULES.$(m).PATH)),\\\n      $(if $(filter-out $(_vendor_exception_path_prefix), $(ALL_MODULES.$(m).PATH)),\\\n        $(m)))))\n\n_vendor_module_owner_info :=\n# Restrict owners\nifneq (,$(filter true owner all, $(_restrictions)))\n\n_vendor_package_overlays := $(filter-out $(_vendor_exception_path_prefix),\\\n    $(filter vendor/%, $(PRODUCT_PACKAGE_OVERLAYS) $(DEVICE_PACKAGE_OVERLAYS)))\nifneq (,$(_vendor_package_overlays))\n$(error Error: Product \"$(TARGET_PRODUCT)\" cannot have overlay in vendor tree: $(_vendor_package_overlays))\nendif\n_vendor_package_overlays :=\n\n_vendor_check_copy_files := $(filter-out $(_vendor_exception_path_prefix),\\\n    $(filter vendor/%, $(PRODUCT_COPY_FILES)))\nifneq (,$(_vendor_check_copy_files))\n$(foreach c, $(_vendor_check_copy_files), \\\n  $(if $(filter $(_vendor_owner_allowed_list), $(call word-colon,3,$(c))),,\\\n    $(error Error: vendor PRODUCT_COPY_FILES file \"$(c)\" has unknown owner))\\\n  $(eval _vendor_module_owner_info += $(call word-colon,2,$(c)):$(call word-colon,3,$(c))))\nendif\n_vendor_check_copy_files :=\n\n$(foreach m, $(_vendor_check_modules), \\\n  $(if $(filter $(_vendor_owner_allowed_list), $(ALL_MODULES.$(m).OWNER)),,\\\n    $(error Error: vendor module \"$(m)\" in $(ALL_MODULES.$(m).PATH) with unknown owner \\\n      \"$(ALL_MODULES.$(m).OWNER)\" in product \"$(TARGET_PRODUCT)\"))\\\n  $(if $(ALL_MODULES.$(m).INSTALLED),\\\n    $(eval _vendor_module_owner_info += $(patsubst $(PRODUCT_OUT)/%,%,$(ALL_MODULES.$(m).INSTALLED)):$(ALL_MODULES.$(m).OWNER))))\n\nendif\n\n\n# Restrict paths\nifneq (,$(filter path all, $(_restrictions)))\n\n$(foreach m, $(_vendor_check_modules), \\\n  $(if $(filter-out ,$(ALL_MODULES.$(m).INSTALLED)),\\\n    $(if $(filter $(TARGET_OUT_VENDOR)/% $(TARGET_OUT_ODM)/% $(TARGET_OUT_VENDOR_DLKM)/% $(TARGET_OUT_ODM_DLKM)/% $(HOST_OUT)/%, $(ALL_MODULES.$(m).INSTALLED)),,\\\n      $(error Error: vendor module \"$(m)\" in $(ALL_MODULES.$(m).PATH) \\\n        in product \"$(TARGET_PRODUCT)\" being installed to \\\n        $(ALL_MODULES.$(m).INSTALLED) which is not in the vendor, odm, vendor_dlkm or odm_dlkm tree))))\n\nendif\n\n_vendor_module_owner_info_txt := $(call intermediates-dir-for,PACKAGING,vendor_owner_info)/vendor_owner_info.txt\n$(_vendor_module_owner_info_txt): PRIVATE_INFO := $(_vendor_module_owner_info)\n$(_vendor_module_owner_info_txt):\n\t@echo \"Write vendor module owner info $@\"\n\t@mkdir -p $(dir $@) && rm -f $@\nifdef _vendor_module_owner_info\n\t@for w in $(PRIVATE_INFO); \\\n\t  do \\\n\t    echo $$w >> $@; \\\n\tdone\nelse\n\t@echo \"No vendor module owner info.\" > $@\nendif\n\n$(call dist-for-goals, droidcore, $(_vendor_module_owner_info_txt))\n\n_vendor_module_owner_info_txt :=\n_vendor_module_owner_info :=\n_vendor_check_modules :=\n_vendor_exception_path_prefix :=\n_vendor_exception_modules :=\n_restrictions :=\nendif\n"
  },
  {
    "path": "core/tasks/vts-core-tests.mk",
    "content": "# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ntest_suite_name := vts\ntest_suite_tradefed := vts-tradefed\ntest_suite_readme := test/vts/tools/vts-core-tradefed/README\n\ninclude $(BUILD_SYSTEM)/tasks/tools/compatibility.mk\n\n.PHONY: vts\nvts: $(compatibility_zip) $(compatibility_tests_list_zip)\n$(call dist-for-goals, vts, $(compatibility_zip) $(compatibility_tests_list_zip))\n\ntests: vts\n"
  },
  {
    "path": "core/tasks/with-license.mk",
    "content": "# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\n.PHONY: with-license\n\nname := $(TARGET_PRODUCT)\nifeq ($(TARGET_BUILD_TYPE),debug)\n\tname := $(name)_debug\nendif\n\ndist_name := $(name)-flashable-FILE_NAME_TAG_PLACEHOLDER-with-license\nname := $(name)-flashable-with-license\n\nwith_license_intermediates := \\\n\t$(call intermediates-dir-for,PACKAGING,with_license)\n\n# Create a with-license artifact target\nlicense_image_input_zip := $(with_license_intermediates)/$(name).zip\n$(license_image_input_zip) : $(BUILT_TARGET_FILES_PACKAGE) $(ZIP2ZIP)\n# DO NOT PROCEED without a license file.\nifndef VENDOR_BLOBS_LICENSE\n\t@echo \"with-license requires VENDOR_BLOBS_LICENSE to be set.\"\n\texit 1\nelse\n\t$(ZIP2ZIP) -i $(BUILT_TARGET_FILES_PACKAGE) -o $@ \\\n\t\tRADIO/bootloader.img:bootloader.img RADIO/radio.img:radio.img \\\n\t\tIMAGES/*.img:. OTA/android-info.txt:android-info.txt\nendif\n\n$(call declare-1p-container,$(license_image_input_zip),build)\n$(call declare-container-deps,$(license_image_input_zip),$(BUILT_TARGET_FILES_PACKAGE))\n\nwith_license_zip := $(PRODUCT_OUT)/$(name).sh\ndist_name := $(dist_name).sh\n$(with_license_zip): PRIVATE_NAME := $(name)\n$(with_license_zip): PRIVATE_INPUT_ZIP := $(license_image_input_zip)\n$(with_license_zip): PRIVATE_VENDOR_BLOBS_LICENSE := $(VENDOR_BLOBS_LICENSE)\n$(with_license_zip): $(license_image_input_zip) $(VENDOR_BLOBS_LICENSE)\n$(with_license_zip): $(HOST_OUT_EXECUTABLES)/generate-self-extracting-archive\n\t# Args: <output> <input archive> <comment> <license file>\n\t$(HOST_OUT_EXECUTABLES)/generate-self-extracting-archive $@ \\\n\t\t$(PRIVATE_INPUT_ZIP) $(PRIVATE_NAME) $(PRIVATE_VENDOR_BLOBS_LICENSE)\nwith-license : $(with_license_zip)\n$(call dist-for-goals, with-license, $(with_license_zip):$(dist_name))\n\n$(call declare-1p-container,$(with_license_zip),)\n$(call declare-container-license-deps,$(with_license_zip),$(license_image_input_zip),$(with_license_zip):)\n\n"
  },
  {
    "path": "core/tasks/wvts.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Widevine test suite for non-GMS partners: go/android-wvts\nifneq ($(wildcard test/wvts/tools/wvts-tradefed/README),)\ntest_suite_name := wvts\ntest_suite_tradefed := wvts-tradefed\ntest_suite_dynamic_config := test/wvts/tools/wvts-tradefed/DynamicConfig.xml\ntest_suite_readme := test/wvts/tools/wvts-tradefed/README\n\n$(call declare-1p-target,$(test_suite_dynamic_config),wvts)\n$(call declare-1p-target,$(test_suite_readme),wvts)\n\ninclude $(BUILD_SYSTEM)/tasks/tools/compatibility.mk\n\n.PHONY: wvts\nwvts: $(compatibility_zip) $(compatibility_tests_list_zip)\n$(call dist-for-goals, wvts, $(compatibility_zip) $(compatibility_tests_list_zip))\nendif\n"
  },
  {
    "path": "core/use_lld_setup.mk",
    "content": "#############################################################\n## Set up flags based on LOCAL_USE_CLANG_LLD.\n## Input variables: LOCAL_USE_CLANG_LLD\n## Output variables: my_use_clang_lld\n#############################################################\n\n# Use LLD by default.\n# Do not use LLD if LOCAL_USE_CLANG_LLD is false or 0\nmy_use_clang_lld := true\nifneq (,$(LOCAL_USE_CLANG_LLD))\n  ifneq (,$(filter 0 false,$(LOCAL_USE_CLANG_LLD)))\n    my_use_clang_lld := false\n  endif\nendif\n\n# Do not use LLD for Darwin host executables or shared libraries.  See\n# https://lld.llvm.org/AtomLLD.html for status of lld for Mach-O.\nifeq ($($(my_prefix)OS),darwin)\nmy_use_clang_lld := false\nendif\n"
  },
  {
    "path": "core/version_util.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n\n#\n# Handle various build version information.\n#\n# Guarantees that the following are defined:\n#     PLATFORM_VERSION\n#     PLATFORM_DISPLAY_VERSION\n#     PLATFORM_SDK_VERSION\n#     PLATFORM_SDK_EXTENSION_VERSION\n#     PLATFORM_BASE_SDK_EXTENSION_VERSION\n#     PLATFORM_VERSION_CODENAME\n#     DEFAULT_APP_TARGET_SDK\n#     BUILD_ID\n#     BUILD_NUMBER\n#     PLATFORM_SECURITY_PATCH\n#     PLATFORM_SYSTEMSDK_VERSIONS\n#     PLATFORM_VERSION_LAST_STABLE\n#     PLATFORM_VERSION_KNOWN_CODENAMES\n#\n\n# Look for an optional file containing overrides of the defaults,\n# but don't cry if we don't find it.  We could just use -include, but\n# the build.prop target also wants INTERNAL_BUILD_ID_MAKEFILE to be set\n# if the file exists.\n#\nINTERNAL_BUILD_ID_MAKEFILE := $(wildcard $(BUILD_SYSTEM)/build_id.mk)\nifdef INTERNAL_BUILD_ID_MAKEFILE\n  include $(INTERNAL_BUILD_ID_MAKEFILE)\nendif\n\nifdef TARGET_PLATFORM_VERSION\n  $(error Do not set TARGET_PLATFORM_VERSION directly. Use RELEASE_PLATFORM_VERSION. value: $(TARGET_PLATFORM_VERSION))\nendif\nTARGET_PLATFORM_VERSION := $(RELEASE_PLATFORM_VERSION)\n.KATI_READONLY := TARGET_PLATFORM_VERSION\n\nifdef PLATFORM_SECURITY_PATCH\n  $(error Do not set PLATFORM_SECURITY_PATCH directly. Use RELEASE_PLATFORM_SECURITY_PATCH. value: $(PLATFORM_SECURITY_PATCH))\nendif\nPLATFORM_SECURITY_PATCH := $(RELEASE_PLATFORM_SECURITY_PATCH)\n.KATI_READONLY := PLATFORM_SECURITY_PATCH\n\nifdef PLATFORM_SDK_VERSION\n  $(error Do not set PLATFORM_SDK_VERSION directly. Use RELEASE_PLATFORM_SDK_VERSION. value: $(PLATFORM_SDK_VERSION))\nendif\nPLATFORM_SDK_VERSION := $(RELEASE_PLATFORM_SDK_VERSION)\n.KATI_READONLY := PLATFORM_SDK_VERSION\n\nifdef PLATFORM_SDK_MINOR_VERSION\n  $(error Do not set PLATFORM_SDK_MINOR_VERSION directly. Use RELEASE_PLATFORM_SDK_MINOR_VERSION. value: $(PLATFORM_SDK_MINOR_VERSION))\nendif\nPLATFORM_SDK_MINOR_VERSION := $(RELEASE_PLATFORM_SDK_MINOR_VERSION)\n.KATI_READONLY := PLATFORM_SDK_MINOR_VERSION\n\nifdef PLATFORM_SDK_EXTENSION_VERSION\n  $(error Do not set PLATFORM_SDK_EXTENSION_VERSION directly. Use RELEASE_PLATFORM_SDK_EXTENSION_VERSION. value: $(PLATFORM_SDK_EXTENSION_VERSION))\nendif\nPLATFORM_SDK_EXTENSION_VERSION := $(RELEASE_PLATFORM_SDK_EXTENSION_VERSION)\n.KATI_READONLY := PLATFORM_SDK_EXTENSION_VERSION\n\nifdef PLATFORM_BASE_SDK_EXTENSION_VERSION\n  $(error Do not set PLATFORM_BASE_SDK_EXTENSION_VERSION directly. Use RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION. value: $(PLATFORM_BASE_SDK_EXTENSION_VERSION))\nendif\nifdef RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION\n  # This is the sdk extension version that PLATFORM_SDK_VERSION ships with.\n  PLATFORM_BASE_SDK_EXTENSION_VERSION := $(RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION)\nelse\n  # Fallback to PLATFORM_SDK_EXTENSION_VERSION if RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION is undefined.\n  PLATFORM_BASE_SDK_EXTENSION_VERSION := $(PLATFORM_SDK_EXTENSION_VERSION)\nendif\n.KATI_READONLY := PLATFORM_BASE_SDK_EXTENSION_VERSION\n\nifdef PLATFORM_VERSION_CODENAME\n  $(error Do not set PLATFORM_VERSION_CODENAME directly. Use RELEASE_PLATFORM_VERSION. value: $(PLATFORM_VERSION_CODENAME))\nendif\nPLATFORM_VERSION_CODENAME := $(RELEASE_PLATFORM_VERSION_CODENAME)\n.KATI_READONLY := PLATFORM_VERSION_CODENAME\n\nifdef PLATFORM_VERSION_ALL_CODENAMES\n  $(error Do not set PLATFORM_VERSION_ALL_CODENAMES directly. Use RELEASE_PLATFORM_VERSION_ALL_CODENAMES. value: $(PLATFORM_VERSION_ALL_CODENAMES))\nendif\nPLATFORM_VERSION_ALL_CODENAMES := $(RELEASE_PLATFORM_VERSION_ALL_CODENAMES)\n.KATI_READONLY := PLATFORM_VERSION_ALL_CODENAMES\n\nifdef PLATFORM_VERSION_ALL_PREVIEW_CODENAMES\n  $(error Do not set PLATFORM_VERSION_ALL_PREVIEW_CODENAMES directly. Use RELEASE_PLATFORM_VERSION_ALL_PREVIEW_CODENAMES. value: $(PLATFORM_VERSION_ALL_PREVIEW_CODENAMES))\nendif\nPLATFORM_VERSION_ALL_PREVIEW_CODENAMES := $(RELEASE_PLATFORM_VERSION_ALL_PREVIEW_CODENAMES)\n.KATI_READONLY := PLATFORM_VERSION_ALL_PREVIEW_CODENAMES\n\nifdef PLATFORM_VERSION_LAST_STABLE\n  $(error Do not set PLATFORM_VERSION_LAST_STABLE directly. Use RELEASE_PLATFORM_VERSION_LAST_STABLE. value: $(PLATFORM_VERSION_CODENAME))\nendif\nPLATFORM_VERSION_LAST_STABLE := $(RELEASE_PLATFORM_VERSION_LAST_STABLE)\n.KATI_READONLY := PLATFORM_VERSION_LAST_STABLE\n\nifdef PLATFORM_VERSION_KNOWN_CODENAMES\n  $(error Do not set PLATFORM_VERSION_KNOWN_CODENAMES directly. Use RELEASE_PLATFORM_VERSION_KNOWN_CODENAMES. value: $(PLATFORM_VERSION_KNOWN_CODENAMES))\nendif\nPLATFORM_VERSION_KNOWN_CODENAMES := $(RELEASE_PLATFORM_VERSION_KNOWN_CODENAMES)\n.KATI_READONLY := PLATFORM_VERSION_KNOWN_CODENAMES\n\nifndef PLATFORM_VERSION\n  ifeq (REL,$(PLATFORM_VERSION_CODENAME))\n      PLATFORM_VERSION := $(PLATFORM_VERSION_LAST_STABLE)\n  else\n      PLATFORM_VERSION := $(PLATFORM_VERSION_CODENAME)\n  endif\nendif\n.KATI_READONLY := PLATFORM_VERSION\n\nifndef PLATFORM_DISPLAY_VERSION\n  PLATFORM_DISPLAY_VERSION := $(PLATFORM_VERSION)\nendif\n.KATI_READONLY := PLATFORM_DISPLAY_VERSION\n\nifeq (REL,$(PLATFORM_VERSION_CODENAME))\n  PLATFORM_PREVIEW_SDK_VERSION := 0\nelse\n  ifndef PLATFORM_PREVIEW_SDK_VERSION\n    # This is the definition of a preview SDK version over and above the current\n    # platform SDK version. Unlike the platform SDK version, a higher value\n    # for preview SDK version does NOT mean that all prior preview APIs are\n    # included. Packages reading this value to determine compatibility with\n    # known APIs should check that this value is precisely equal to the preview\n    # SDK version the package was built for, otherwise it should fall back to\n    # assuming the device can only support APIs as of the previous official\n    # public release.\n    # This value will always be forced to 0 for release builds by the logic\n    # in the \"ifeq\" block above, so the value below will be used on any\n    # non-release builds, and it should always be at least 1, to indicate that\n    # APIs may have changed since the claimed PLATFORM_SDK_VERSION.\n    PLATFORM_PREVIEW_SDK_VERSION := 1\n  endif\nendif\n.KATI_READONLY := PLATFORM_PREVIEW_SDK_VERSION\n\nifndef DEFAULT_APP_TARGET_SDK\n  # This is the default minSdkVersion and targetSdkVersion to use for\n  # all .apks created by the build system.  It can be overridden by explicitly\n  # setting these in the .apk's AndroidManifest.xml.  It is either the code\n  # name of the development build or, if this is a release build, the official\n  # SDK version of this release.\n  ifeq (REL,$(PLATFORM_VERSION_CODENAME))\n    DEFAULT_APP_TARGET_SDK := $(PLATFORM_SDK_VERSION)\n  else\n    DEFAULT_APP_TARGET_SDK := $(PLATFORM_VERSION_CODENAME)\n  endif\nendif\n.KATI_READONLY := DEFAULT_APP_TARGET_SDK\n\nifndef PLATFORM_SYSTEMSDK_MIN_VERSION\n  # This is the oldest version of system SDK that the platform supports. Contrary\n  # to the public SDK where platform essentially supports all previous SDK versions,\n  # platform supports only a few number of recent system SDK versions as some of\n  # old system APIs are gradually deprecated, removed and then deleted.\n  PLATFORM_SYSTEMSDK_MIN_VERSION := 29\nendif\n.KATI_READONLY := PLATFORM_SYSTEMSDK_MIN_VERSION\n\n# This is the list of system SDK versions that the current platform supports.\nPLATFORM_SYSTEMSDK_VERSIONS :=\nifneq (,$(PLATFORM_SYSTEMSDK_MIN_VERSION))\n  $(if $(call math_is_number,$(PLATFORM_SYSTEMSDK_MIN_VERSION)),,\\\n    $(error PLATFORM_SYSTEMSDK_MIN_VERSION must be a number, but was $(PLATFORM_SYSTEMSDK_MIN_VERSION)))\n  PLATFORM_SYSTEMSDK_VERSIONS := $(call int_range_list,$(PLATFORM_SYSTEMSDK_MIN_VERSION),$(PLATFORM_SDK_VERSION))\nendif\n# Platform always supports the current version\nifeq (REL,$(PLATFORM_VERSION_CODENAME))\n  PLATFORM_SYSTEMSDK_VERSIONS += $(PLATFORM_SDK_VERSION)\nelse\n  PLATFORM_SYSTEMSDK_VERSIONS += $(subst $(comma),$(space),$(PLATFORM_VERSION_ALL_CODENAMES))\nendif\nPLATFORM_SYSTEMSDK_VERSIONS := $(strip $(sort $(PLATFORM_SYSTEMSDK_VERSIONS)))\n.KATI_READONLY := PLATFORM_SYSTEMSDK_VERSIONS\n\n.KATI_READONLY := PLATFORM_SECURITY_PATCH\n\nifndef PLATFORM_SECURITY_PATCH_TIMESTAMP\n  # Used to indicate the matching timestamp for the security patch string in PLATFORM_SECURITY_PATCH.\n  PLATFORM_SECURITY_PATCH_TIMESTAMP := $(shell date -d 'TZ=\"GMT\" $(PLATFORM_SECURITY_PATCH)' +%s)\nendif\n.KATI_READONLY := PLATFORM_SECURITY_PATCH_TIMESTAMP\n\n# PLATFORM_BASE_OS is used to indicate the base os applied\n# to the device. Can be an arbitrary string, but must be a\n# single word.\n#\n# If there is no $PLATFORM_BASE_OS set, keep it empty.\n#\n# PLATFORM_BASE_OS can either be set via an enviornment\n# variable, or set via the PRODUCT_BASE_OS product variable.\nPLATFORM_BASE_OS_ENV_INPUT := $(PLATFORM_BASE_OS)\n.KATI_READONLY := PLATFORM_BASE_OS_ENV_INPUT\nPLATFORM_BASE_OS :=\n\nifndef BUILD_ID\n  # Used to signify special builds.  E.g., branches and/or releases,\n  # like \"M5-RC7\".  Can be an arbitrary string, but must be a single\n  # word and a valid file name.\n  #\n  # If there is no BUILD_ID set, make it obvious.\n  BUILD_ID := UNKNOWN\nendif\n.KATI_READONLY := BUILD_ID\n\nifndef BUILD_DATETIME\n  # Used to reproduce builds by setting the same time. Must be the number\n  # of seconds since the Epoch.\n  BUILD_DATETIME := $(shell date +%s)\nendif\n\nDATE := date -d @$(BUILD_DATETIME)\n.KATI_READONLY := DATE\n\n# Everything should be using BUILD_DATETIME_FROM_FILE instead.\n# BUILD_DATETIME and DATE can be removed once BUILD_NUMBER moves\n# to soong_ui.\n$(KATI_obsolete_var BUILD_DATETIME,Use BUILD_DATETIME_FROM_FILE)\n\nifndef HAS_BUILD_NUMBER\n  HAS_BUILD_NUMBER := false\nendif\n.KATI_READONLY := HAS_BUILD_NUMBER\n\nifdef PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION\n  $(error Do not set PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION directly. Use RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION. value: $(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION))\nendif\nPLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION := $(RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION)\n.KATI_READONLY := PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION\n"
  },
  {
    "path": "envsetup.sh",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# gettop is duplicated here and in shell_utils.mk, because it's difficult\n# to find shell_utils.make without it for all the novel ways this file can be\n# sourced.  Other common functions should only be in one place or the other.\nfunction _gettop_once\n{\n    local TOPFILE=build/make/core/envsetup.mk\n    if [ -n \"$TOP\" -a -f \"$TOP/$TOPFILE\" ] ; then\n        # The following circumlocution ensures we remove symlinks from TOP.\n        (cd \"$TOP\"; PWD= /bin/pwd)\n    else\n        if [ -f $TOPFILE ] ; then\n            # The following circumlocution (repeated below as well) ensures\n            # that we record the true directory name and not one that is\n            # faked up with symlink names.\n            PWD= /bin/pwd\n        else\n            local HERE=$PWD\n            local T=\n            while [ \\( ! \\( -f $TOPFILE \\) \\) -a \\( \"$PWD\" != \"/\" \\) ]; do\n                \\cd ..\n                T=`PWD= /bin/pwd -P`\n            done\n            \\cd \"$HERE\"\n            if [ -f \"$T/$TOPFILE\" ]; then\n                echo \"$T\"\n            fi\n        fi\n    fi\n}\nT=$(_gettop_once)\nif [ ! \"$T\" ]; then\n    echo \"Couldn't locate the top of the tree. Always source build/envsetup.sh from the root of the tree.\" >&2\n    return 1\nfi\nIMPORTING_ENVSETUP=true source $T/build/make/shell_utils.sh\n\n# Get all the build variables needed by this script in a single call to the build system.\nfunction build_build_var_cache()\n{\n    local T=$(gettop)\n    # Grep out the variable names from the script.\n    cached_vars=(`cat $T/build/envsetup.sh | tr '()' '  ' | awk '{for(i=1;i<=NF;i++) if($i~/_get_build_var_cached/) print $(i+1)}' | sort -u | tr '\\n' ' '`)\n    cached_abs_vars=(`cat $T/build/envsetup.sh | tr '()' '  ' | awk '{for(i=1;i<=NF;i++) if($i~/_get_abs_build_var_cached/) print $(i+1)}' | sort -u | tr '\\n' ' '`)\n    # Call the build system to dump the \"<val>=<value>\" pairs as a shell script.\n    build_dicts_script=`\\builtin cd $T; build/soong/soong_ui.bash --dumpvars-mode \\\n                        --vars=\"${cached_vars[*]}\" \\\n                        --abs-vars=\"${cached_abs_vars[*]}\" \\\n                        --var-prefix=var_cache_ \\\n                        --abs-var-prefix=abs_var_cache_`\n    local ret=$?\n    if [ $ret -ne 0 ]\n    then\n        unset build_dicts_script\n        return $ret\n    fi\n    # Execute the script to store the \"<val>=<value>\" pairs as shell variables.\n    eval \"$build_dicts_script\"\n    ret=$?\n    unset build_dicts_script\n    if [ $ret -ne 0 ]\n    then\n        return $ret\n    fi\n    BUILD_VAR_CACHE_READY=\"true\"\n}\n\n# Delete the build var cache, so that we can still call into the build system\n# to get build variables not listed in this script.\nfunction destroy_build_var_cache()\n{\n    unset BUILD_VAR_CACHE_READY\n    local v\n    for v in $cached_vars; do\n      unset var_cache_$v\n    done\n    unset cached_vars\n    for v in $cached_abs_vars; do\n      unset abs_var_cache_$v\n    done\n    unset cached_abs_vars\n}\n\n# Get the value of a build variable as an absolute path.\nfunction _get_abs_build_var_cached()\n{\n    if [ \"$BUILD_VAR_CACHE_READY\" = \"true\" ]\n    then\n        eval \"echo \\\"\\${abs_var_cache_$1}\\\"\"\n        return\n    fi\n\n    local T=$(gettop)\n    if [ ! \"$T\" ]; then\n        echo \"Couldn't locate the top of the tree.  Try setting TOP.\" >&2\n        return\n    fi\n    (\\cd $T; build/soong/soong_ui.bash --dumpvar-mode --abs $1)\n}\n\n# Get the exact value of a build variable.\nfunction _get_build_var_cached()\n{\n    if [ \"$BUILD_VAR_CACHE_READY\" = \"true\" ]\n    then\n        eval \"echo \\\"\\${var_cache_$1}\\\"\"\n        return 0\n    fi\n\n    local T=$(gettop)\n    if [ ! \"$T\" ]; then\n        echo \"Couldn't locate the top of the tree.  Try setting TOP.\" >&2\n        return 1\n    fi\n    (\\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)\n}\n\n# This logic matches envsetup.mk\nfunction get_host_prebuilt_prefix\n{\n  local un=$(uname)\n  if [[ $un == \"Linux\" ]] ; then\n    echo linux-x86\n  elif [[ $un == \"Darwin\" ]] ; then\n    echo darwin-x86\n  else\n    echo \"Error: Invalid host operating system: $un\" 1>&2\n  fi\n}\n\n# Add directories to PATH that are dependent on the lunch target.\n# For directories that are not lunch-specific, add them in set_global_paths\nfunction set_lunch_paths()\n{\n    local T=$(gettop)\n    if [ ! \"$T\" ]; then\n        echo \"Couldn't locate the top of the tree.  Try setting TOP.\"\n        return\n    fi\n\n    ##################################################################\n    #                                                                #\n    #              Read me before you modify this code               #\n    #                                                                #\n    #   This function sets ANDROID_LUNCH_BUILD_PATHS to what it is   #\n    #   adding to PATH, and the next time it is run, it removes that #\n    #   from PATH.  This is required so lunch can be run more than   #\n    #   once and still have working paths.                           #\n    #                                                                #\n    ##################################################################\n\n    # Note: on windows/cygwin, ANDROID_LUNCH_BUILD_PATHS will contain spaces\n    # due to \"C:\\Program Files\" being in the path.\n\n    # Handle compat with the old ANDROID_BUILD_PATHS variable.\n    # TODO: Remove this after we think everyone has lunched again.\n    if [ -z \"$ANDROID_LUNCH_BUILD_PATHS\" -a -n \"$ANDROID_BUILD_PATHS\" ] ; then\n      ANDROID_LUNCH_BUILD_PATHS=\"$ANDROID_BUILD_PATHS\"\n      ANDROID_BUILD_PATHS=\n    fi\n    if [ -n \"$ANDROID_PRE_BUILD_PATHS\" ] ; then\n        export PATH=${PATH/$ANDROID_PRE_BUILD_PATHS/}\n        # strip leading ':', if any\n        export PATH=${PATH/:%/}\n        ANDROID_PRE_BUILD_PATHS=\n    fi\n\n    # Out with the old...\n    if [ -n \"$ANDROID_LUNCH_BUILD_PATHS\" ] ; then\n        export PATH=${PATH/$ANDROID_LUNCH_BUILD_PATHS/}\n    fi\n\n    # And in with the new...\n    ANDROID_LUNCH_BUILD_PATHS=$(_get_abs_build_var_cached SOONG_HOST_OUT_EXECUTABLES)\n    ANDROID_LUNCH_BUILD_PATHS+=:$(_get_abs_build_var_cached HOST_OUT_EXECUTABLES)\n\n    # Append llvm binutils prebuilts path to ANDROID_LUNCH_BUILD_PATHS.\n    local ANDROID_LLVM_BINUTILS=$(_get_abs_build_var_cached ANDROID_CLANG_PREBUILTS)/llvm-binutils-stable\n    ANDROID_LUNCH_BUILD_PATHS+=:$ANDROID_LLVM_BINUTILS\n\n    # Set up ASAN_SYMBOLIZER_PATH for SANITIZE_HOST=address builds.\n    export ASAN_SYMBOLIZER_PATH=$ANDROID_LLVM_BINUTILS/llvm-symbolizer\n\n    # Append asuite prebuilts path to ANDROID_LUNCH_BUILD_PATHS.\n    local os_arch=$(_get_build_var_cached HOST_PREBUILT_TAG)\n    ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/acloud/$os_arch\n    ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/aidegen/$os_arch\n    ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/atest/$os_arch\n\n    export ANDROID_JAVA_HOME=$(_get_abs_build_var_cached ANDROID_JAVA_HOME)\n    export JAVA_HOME=$ANDROID_JAVA_HOME\n    export ANDROID_JAVA_TOOLCHAIN=$(_get_abs_build_var_cached ANDROID_JAVA_TOOLCHAIN)\n    ANDROID_LUNCH_BUILD_PATHS+=:$ANDROID_JAVA_TOOLCHAIN\n\n    # Fix up PYTHONPATH\n    if [ -n $ANDROID_PYTHONPATH ]; then\n        export PYTHONPATH=${PYTHONPATH//$ANDROID_PYTHONPATH/}\n    fi\n    # //development/python-packages contains both a pseudo-PYTHONPATH which\n    # mimics an already assembled venv, but also contains real Python packages\n    # that are not in that layout until they are installed. We can fake it for\n    # the latter type by adding the package source directories to the PYTHONPATH\n    # directly. For the former group, we only need to add the python-packages\n    # directory itself.\n    #\n    # This could be cleaned up by converting the remaining packages that are in\n    # the first category into a typical python source layout (that is, another\n    # layer of directory nesting) and automatically adding all subdirectories of\n    # python-packages to the PYTHONPATH instead of manually curating this. We\n    # can't convert the packages like adb to the other style because doing so\n    # would prevent exporting type info from those packages.\n    #\n    # http://b/266688086\n    export ANDROID_PYTHONPATH=$T/development/python-packages/adb:$T/development/python-packages/gdbrunner:$T/development/python-packages:\n    if [ -n $VENDOR_PYTHONPATH ]; then\n        ANDROID_PYTHONPATH=$ANDROID_PYTHONPATH$VENDOR_PYTHONPATH\n    fi\n    export PYTHONPATH=$ANDROID_PYTHONPATH$PYTHONPATH\n\n    unset ANDROID_PRODUCT_OUT\n    export ANDROID_PRODUCT_OUT=$(_get_abs_build_var_cached PRODUCT_OUT)\n    export OUT=$ANDROID_PRODUCT_OUT\n\n    unset ANDROID_HOST_OUT\n    export ANDROID_HOST_OUT=$(_get_abs_build_var_cached HOST_OUT)\n\n    unset ANDROID_SOONG_HOST_OUT\n    export ANDROID_SOONG_HOST_OUT=$(_get_abs_build_var_cached SOONG_HOST_OUT)\n\n    unset ANDROID_HOST_OUT_TESTCASES\n    export ANDROID_HOST_OUT_TESTCASES=$(_get_abs_build_var_cached HOST_OUT_TESTCASES)\n\n    unset ANDROID_TARGET_OUT_TESTCASES\n    export ANDROID_TARGET_OUT_TESTCASES=$(_get_abs_build_var_cached TARGET_OUT_TESTCASES)\n\n    # Finally, set PATH\n    export PATH=$ANDROID_LUNCH_BUILD_PATHS:$PATH\n}\n\n# Add directories to PATH that are NOT dependent on the lunch target.\n# For directories that are lunch-specific, add them in set_lunch_paths\nfunction set_global_paths()\n{\n    local T=$(gettop)\n    if [ ! \"$T\" ]; then\n        echo \"Couldn't locate the top of the tree.  Try setting TOP.\"\n        return\n    fi\n\n    ##################################################################\n    #                                                                #\n    #              Read me before you modify this code               #\n    #                                                                #\n    #   This function sets ANDROID_GLOBAL_BUILD_PATHS to what it is  #\n    #   adding to PATH, and the next time it is run, it removes that #\n    #   from PATH.  This is required so envsetup.sh can be sourced   #\n    #   more than once and still have working paths.                 #\n    #                                                                #\n    ##################################################################\n\n    # Out with the old...\n    if [ -n \"$ANDROID_GLOBAL_BUILD_PATHS\" ] ; then\n        export PATH=${PATH/$ANDROID_GLOBAL_BUILD_PATHS/}\n    fi\n\n    # And in with the new...\n    ANDROID_GLOBAL_BUILD_PATHS=$T/build/soong/bin\n    ANDROID_GLOBAL_BUILD_PATHS+=:$T/build/bazel/bin\n    ANDROID_GLOBAL_BUILD_PATHS+=:$T/development/scripts\n    ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/devtools/tools\n\n    # add kernel specific binaries\n    if [ $(uname -s) = Linux ] ; then\n        ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/misc/linux-x86/dtc\n        ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/misc/linux-x86/libufdt\n    fi\n\n    # If prebuilts/android-emulator/<system>/ exists, prepend it to our PATH\n    # to ensure that the corresponding 'emulator' binaries are used.\n    case $(uname -s) in\n        Darwin)\n            ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/darwin-x86_64\n            ;;\n        Linux)\n            ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/linux-x86_64\n            ;;\n        *)\n            ANDROID_EMULATOR_PREBUILTS=\n            ;;\n    esac\n    if [ -n \"$ANDROID_EMULATOR_PREBUILTS\" -a -d \"$ANDROID_EMULATOR_PREBUILTS\" ]; then\n        ANDROID_GLOBAL_BUILD_PATHS+=:$ANDROID_EMULATOR_PREBUILTS\n        export ANDROID_EMULATOR_PREBUILTS\n    fi\n\n    # Finally, set PATH\n    export PATH=$ANDROID_GLOBAL_BUILD_PATHS:$PATH\n}\n\nfunction printconfig()\n{\n    local T=$(gettop)\n    if [ ! \"$T\" ]; then\n        echo \"Couldn't locate the top of the tree.  Try setting TOP.\" >&2\n        return\n    fi\n    _get_build_var_cached report_config\n}\n\nfunction set_stuff_for_environment()\n{\n    set_lunch_paths\n    set_sequence_number\n\n    export ANDROID_BUILD_TOP=$(gettop)\n}\n\nfunction set_sequence_number()\n{\n    export BUILD_ENV_SEQUENCE_NUMBER=13\n}\n\n# Takes a command name, and check if it's in ENVSETUP_NO_COMPLETION or not.\nfunction should_add_completion() {\n    local cmd=\"$(basename $1| sed 's/_completion//' |sed 's/\\.\\(.*\\)*sh$//')\"\n    case :\"$ENVSETUP_NO_COMPLETION\": in\n        *:\"$cmd\":*)\n            return 1\n            ;;\n    esac\n    return 0\n}\n\nfunction addcompletions()\n{\n    local f=\n\n    # Keep us from trying to run in something that's neither bash nor zsh.\n    if [ -z \"$BASH_VERSION\" -a -z \"$ZSH_VERSION\" ]; then\n        return\n    fi\n\n    # Keep us from trying to run in bash that's too old.\n    if [ -n \"$BASH_VERSION\" -a ${BASH_VERSINFO[0]} -lt 3 ]; then\n        return\n    fi\n\n    local completion_files=(\n      packages/modules/adb/adb.bash\n      system/core/fastboot/fastboot.bash\n      tools/asuite/asuite.sh\n    )\n    # Completion can be disabled selectively to allow users to use non-standard completion.\n    # e.g.\n    # ENVSETUP_NO_COMPLETION=adb # -> disable adb completion\n    # ENVSETUP_NO_COMPLETION=adb:bit # -> disable adb and bit completion\n    local T=$(gettop)\n    for f in ${completion_files[*]}; do\n        f=\"$T/$f\"\n        if [ ! -f \"$f\" ]; then\n          echo \"Warning: completion file $f not found\"\n        elif should_add_completion \"$f\"; then\n            . $f\n        fi\n    done\n\n    if [ -z \"$ZSH_VERSION\" ]; then\n        # Doesn't work in zsh.\n        complete -o nospace -F _croot croot\n        # TODO(b/244559459): Support b autocompletion for zsh\n        complete -F _bazel__complete -o nospace b\n    fi\n    complete -F _lunch lunch\n    complete -F _lunch_completion lunch2\n\n    complete -F _complete_android_module_names pathmod\n    complete -F _complete_android_module_names gomod\n    complete -F _complete_android_module_names outmod\n    complete -F _complete_android_module_names installmod\n    complete -F _complete_android_module_names m\n}\n\nfunction add_lunch_combo()\n{\n    if [ -n \"$ZSH_VERSION\" ]; then\n        echo -n \"${funcfiletrace[1]}: \"\n    else\n        echo -n \"${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \"\n    fi\n    echo \"add_lunch_combo is obsolete. Use COMMON_LUNCH_CHOICES in your AndroidProducts.mk instead.\"\n}\n\nfunction print_lunch_menu()\n{\n    local uname=$(uname)\n    local choices\n    choices=$(TARGET_BUILD_APPS= TARGET_PRODUCT= TARGET_RELEASE= TARGET_BUILD_VARIANT= _get_build_var_cached COMMON_LUNCH_CHOICES 2>/dev/null)\n    local ret=$?\n\n    echo\n    echo \"You're building on\" $uname\n    echo\n\n    if [ $ret -ne 0 ]\n    then\n        echo \"Warning: Cannot display lunch menu.\"\n        echo\n        echo \"Note: You can invoke lunch with an explicit target:\"\n        echo\n        echo \"  usage: lunch [target]\" >&2\n        echo\n        return\n    fi\n\n    echo \"Lunch menu .. Here are the common combinations:\"\n\n    local i=1\n    local choice\n    for choice in $(echo $choices)\n    do\n        echo \"     $i. $choice\"\n        i=$(($i+1))\n    done\n\n    echo\n}\n\nfunction _lunch_meat()\n{\n    local product=$1\n    local release=$2\n    local variant=$3\n\n    TARGET_PRODUCT=$product \\\n    TARGET_RELEASE=$release \\\n    TARGET_BUILD_VARIANT=$variant \\\n    build_build_var_cache\n    if [ $? -ne 0 ]\n    then\n        if [[ \"$product\" =~ .*_(eng|user|userdebug) ]]\n        then\n            echo \"Did you mean -${product/*_/}? (dash instead of underscore)\"\n        fi\n        return 1\n    fi\n    export TARGET_PRODUCT=$(_get_build_var_cached TARGET_PRODUCT)\n    export TARGET_BUILD_VARIANT=$(_get_build_var_cached TARGET_BUILD_VARIANT)\n    export TARGET_RELEASE=$release\n    # Note this is the string \"release\", not the value of the variable.\n    export TARGET_BUILD_TYPE=release\n\n    [[ -n \"${ANDROID_QUIET_BUILD:-}\" ]] || echo\n\n    set_stuff_for_environment\n    [[ -n \"${ANDROID_QUIET_BUILD:-}\" ]] || printconfig\n\n    if [[ -z \"${ANDROID_QUIET_BUILD}\" ]]; then\n        local spam_for_lunch=$(gettop)/build/make/tools/envsetup/spam_for_lunch\n        if [[ -x $spam_for_lunch ]]; then\n            $spam_for_lunch\n        fi\n    fi\n\n    destroy_build_var_cache\n\n    if [[ -n \"${CHECK_MU_CONFIG:-}\" ]]; then\n      check_mu_config\n    fi\n}\n\nunset COMMON_LUNCH_CHOICES_CACHE\n# Tab completion for lunch.\nfunction _lunch()\n{\n    local cur prev opts\n    COMPREPLY=()\n    cur=\"${COMP_WORDS[COMP_CWORD]}\"\n    prev=\"${COMP_WORDS[COMP_CWORD-1]}\"\n\n    if [ -z \"$COMMON_LUNCH_CHOICES_CACHE\" ]; then\n        COMMON_LUNCH_CHOICES_CACHE=$(TARGET_BUILD_APPS= _get_build_var_cached COMMON_LUNCH_CHOICES)\n    fi\n\n    COMPREPLY=( $(compgen -W \"${COMMON_LUNCH_CHOICES_CACHE}\" -- ${cur}) )\n    return 0\n}\n\nfunction _lunch_usage()\n{\n    (\n        echo \"The lunch command selects the configuration to use for subsequent\"\n        echo \"Android builds.\"\n        echo\n        echo \"Usage: lunch TARGET_PRODUCT [TARGET_RELEASE [TARGET_BUILD_VARIANT]]\"\n        echo\n        echo \"  Choose the product, release and variant to use. If not\"\n        echo \"  supplied, TARGET_RELEASE will be 'trunk_staging' and\"\n        echo \"  TARGET_BUILD_VARIANT will be 'eng'\"\n        echo\n        echo\n        echo \"Usage: lunch TARGET_PRODUCT-TARGET_RELEASE-TARGET_BUILD_VARIANT\"\n        echo\n        echo \"  Chose the product, release and variant to use. This\"\n        echo \"  legacy format is maintained for compatibility.\"\n        echo\n        echo\n        echo \"Note that the previous interactive menu and list of hard-coded\"\n        echo \"list of curated targets has been removed. If you would like the\"\n        echo \"list of products, release configs for a particular product, or\"\n        echo \"variants, run the following as individual commands:\"\n        echo \"list_products, list_releases, or list_variants\"\n        echo \"respectively.\"\n        echo\n    ) 1>&2\n}\n\nfunction lunch()\n{\n    if [[ $# -eq 1 && $1 = \"--help\" ]]; then\n        _lunch_usage\n        return 0\n    fi\n    if [[ $# -eq 0 ]]; then\n        echo \"No target specified. See lunch --help\" 1>&2\n        return 1\n    fi\n    if [[ $# -gt 3 ]]; then\n        echo \"Too many parameters given. See lunch --help\" 1>&2\n        return 1\n    fi\n\n    local product release variant\n\n    # Handle the legacy format\n    local legacy=$(echo $1 | grep \"-\")\n    if [[ $# -eq 1 && -n $legacy ]]; then\n        IFS=\"-\" read -r product release variant <<< \"$1\"\n        if [[ -z \"$product\" ]] || [[ -z \"$release\" ]] || [[ -z \"$variant\" ]]; then\n            echo \"Invalid lunch combo: $1\" 1>&2\n            echo \"Valid combos must be of the form <product>-<release>-<variant> when using\" 1>&2\n            echo \"the legacy format.  Run 'lunch --help' for usage.\" 1>&2\n            return 1\n        fi\n    fi\n\n    # Handle the new format.\n    if [[ -z $legacy ]]; then\n        product=$1\n        release=$2\n        if [[ -z $release ]]; then\n            release=trunk_staging\n        fi\n        variant=$3\n        if [[ -z $variant ]]; then\n            variant=eng\n        fi\n    fi\n\n    # Validate the selection and set all the environment stuff\n    _lunch_meat $product $release $variant\n}\n\nunset ANDROID_LUNCH_COMPLETION_PRODUCT_CACHE\nunset ANDROID_LUNCH_COMPLETION_CHOSEN_PRODUCT\nunset ANDROID_LUNCH_COMPLETION_RELEASE_CACHE\n# Tab completion for lunch.\nfunction _lunch_completion()\n{\n    # Available products\n    if [[ $COMP_CWORD -eq 1 ]] ; then\n        if [[ -z $ANDROID_LUNCH_COMPLETION_PRODUCT_CACHE ]]; then\n            ANDROID_LUNCH_COMPLETION_PRODUCT_CACHE=$(list_products)\n        fi\n        COMPREPLY=( $(compgen -W \"${ANDROID_LUNCH_COMPLETION_PRODUCT_CACHE}\" -- \"${COMP_WORDS[COMP_CWORD]}\") )\n    fi\n\n    # Available release configs\n    if [[ $COMP_CWORD -eq 2 ]] ; then\n        if [[ -z $ANDROID_LUNCH_COMPLETION_RELEASE_CACHE || $ANDROID_LUNCH_COMPLETION_CHOSEN_PRODUCT != ${COMP_WORDS[1]} ]] ; then\n            ANDROID_LUNCH_COMPLETION_RELEASE_CACHE=$(list_releases ${COMP_WORDS[1]})\n            ANDROID_LUNCH_COMPLETION_CHOSEN_PRODUCT=${COMP_WORDS[1]}\n        fi\n        COMPREPLY=( $(compgen -W \"${ANDROID_LUNCH_COMPLETION_RELEASE_CACHE}\" -- \"${COMP_WORDS[COMP_CWORD]}\") )\n    fi\n\n    # Available variants\n    if [[ $COMP_CWORD -eq 3 ]] ; then\n        COMPREPLY=(user userdebug eng)\n    fi\n\n    return 0\n}\n\n\n# Configures the build to build unbundled apps.\n# Run tapas with one or more app names (from LOCAL_PACKAGE_NAME)\nfunction tapas()\n{\n    local showHelp=\"$(echo $* | xargs -n 1 echo | \\grep -E '^(help)$' | xargs)\"\n    local arch=\"$(echo $* | xargs -n 1 echo | \\grep -E '^(arm|x86|arm64|x86_64)$' | xargs)\"\n    # TODO(b/307975293): Expand tapas to take release arguments (and update hmm() usage).\n    local release=\"trunk_staging\"\n    local variant=\"$(echo $* | xargs -n 1 echo | \\grep -E '^(user|userdebug|eng)$' | xargs)\"\n    local density=\"$(echo $* | xargs -n 1 echo | \\grep -E '^(ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|alldpi)$' | xargs)\"\n    local keys=\"$(echo $* | xargs -n 1 echo | \\grep -E '^(devkeys)$' | xargs)\"\n    local apps=\"$(echo $* | xargs -n 1 echo | \\grep -E -v '^(user|userdebug|eng|arm|x86|arm64|x86_64|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|alldpi|devkeys)$' | xargs)\"\n\n\n    if [ \"$showHelp\" != \"\" ]; then\n      $(gettop)/build/make/tapasHelp.sh\n      return\n    fi\n\n    if [ $(echo $arch | wc -w) -gt 1 ]; then\n        echo \"tapas: Error: Multiple build archs supplied: $arch\"\n        return\n    fi\n    if [ $(echo $release | wc -w) -gt 1 ]; then\n        echo \"tapas: Error: Multiple build releases supplied: $release\"\n        return\n    fi\n    if [ $(echo $variant | wc -w) -gt 1 ]; then\n        echo \"tapas: Error: Multiple build variants supplied: $variant\"\n        return\n    fi\n    if [ $(echo $density | wc -w) -gt 1 ]; then\n        echo \"tapas: Error: Multiple densities supplied: $density\"\n        return\n    fi\n    if [ $(echo $keys | wc -w) -gt 1 ]; then\n        echo \"tapas: Error: Multiple keys supplied: $keys\"\n        return\n    fi\n\n    local product=aosp_arm\n    case $arch in\n      x86)    product=aosp_x86;;\n      arm64)  product=aosp_arm64;;\n      x86_64) product=aosp_x86_64;;\n    esac\n    if [ -n \"$keys\" ]; then\n        product=${product/aosp_/aosp_${keys}_}\n    fi;\n\n    if [ -z \"$variant\" ]; then\n        variant=eng\n    fi\n    if [ -z \"$apps\" ]; then\n        apps=all\n    fi\n    if [ -z \"$density\" ]; then\n        density=alldpi\n    fi\n\n    export TARGET_PRODUCT=$product\n    export TARGET_RELEASE=$release\n    export TARGET_BUILD_VARIANT=$variant\n    export TARGET_BUILD_DENSITY=$density\n    export TARGET_BUILD_TYPE=release\n    export TARGET_BUILD_APPS=$apps\n\n    build_build_var_cache\n    set_stuff_for_environment\n    printconfig\n    destroy_build_var_cache\n}\n\n# Configures the build to build unbundled Android modules (APEXes).\n# Run banchan with one or more module names (from apex{} modules).\nfunction banchan()\n{\n    local showHelp=\"$(echo $* | xargs -n 1 echo | \\grep -E '^(help)$' | xargs)\"\n    local product=\"$(echo $* | xargs -n 1 echo | \\grep -E '^(.*_)?(arm|x86|arm64|riscv64|x86_64|arm64only|x86_64only)$' | xargs)\"\n    # TODO: Expand banchan to take release arguments (and update hmm() usage).\n    local release=\"trunk_staging\"\n    local variant=\"$(echo $* | xargs -n 1 echo | \\grep -E '^(user|userdebug|eng)$' | xargs)\"\n    local apps=\"$(echo $* | xargs -n 1 echo | \\grep -E -v '^(user|userdebug|eng|(.*_)?(arm|x86|arm64|riscv64|x86_64))$' | xargs)\"\n\n    if [ \"$showHelp\" != \"\" ]; then\n      $(gettop)/build/make/banchanHelp.sh\n      return\n    fi\n\n    if [ -z \"$product\" ]; then\n        product=arm64\n    elif [ $(echo $product | wc -w) -gt 1 ]; then\n        echo \"banchan: Error: Multiple build archs or products supplied: $products\"\n        return\n    fi\n    if [ $(echo $release | wc -w) -gt 1 ]; then\n        echo \"banchan: Error: Multiple build releases supplied: $release\"\n        return\n    fi\n    if [ $(echo $variant | wc -w) -gt 1 ]; then\n        echo \"banchan: Error: Multiple build variants supplied: $variant\"\n        return\n    fi\n    if [ -z \"$apps\" ]; then\n        echo \"banchan: Error: No modules supplied\"\n        return\n    fi\n\n    case $product in\n      arm)    product=module_arm;;\n      x86)    product=module_x86;;\n      arm64)  product=module_arm64;;\n      riscv64) product=module_riscv64;;\n      x86_64) product=module_x86_64;;\n      arm64only)  product=module_arm64only;;\n      x86_64only) product=module_x86_64only;;\n    esac\n    if [ -z \"$variant\" ]; then\n        variant=eng\n    fi\n\n    export TARGET_PRODUCT=$product\n    export TARGET_RELEASE=$release\n    export TARGET_BUILD_VARIANT=$variant\n    export TARGET_BUILD_DENSITY=alldpi\n    export TARGET_BUILD_TYPE=release\n\n    # This setup currently uses TARGET_BUILD_APPS just like tapas, but the use\n    # case is different and it may diverge in the future.\n    export TARGET_BUILD_APPS=$apps\n\n    build_build_var_cache\n    set_stuff_for_environment\n    printconfig\n    destroy_build_var_cache\n}\n\nfunction croot()\n{\n    local T=$(gettop)\n    if [ \"$T\" ]; then\n        if [ \"$1\" ]; then\n            \\cd $(gettop)/$1\n        else\n            \\cd $(gettop)\n        fi\n    else\n        echo \"Couldn't locate the top of the tree.  Try setting TOP.\"\n    fi\n}\n\nfunction _croot()\n{\n    local T=$(gettop)\n    if [ \"$T\" ]; then\n        local cur=\"${COMP_WORDS[COMP_CWORD]}\"\n        k=0\n        for c in $(compgen -d ${T}/${cur}); do\n            COMPREPLY[k++]=${c#${T}/}/\n        done\n    fi\n}\n\nfunction cproj()\n{\n    local TOPFILE=build/make/core/envsetup.mk\n    local HERE=$PWD\n    local T=\n    while [ \\( ! \\( -f $TOPFILE \\) \\) -a \\( $PWD != \"/\" \\) ]; do\n        T=$PWD\n        if [ -f \"$T/Android.mk\" ]; then\n            \\cd $T\n            return\n        fi\n        \\cd ..\n    done\n    \\cd $HERE\n    echo \"can't find Android.mk\"\n}\n\n# Ensure that we're always using the adb in the tree. This works around the fact\n# that bash caches $PATH lookups, so if you use adb before lunching/building the\n# one in your tree, you'll continue to get /usr/bin/adb or whatever even after\n# you have the one from your current tree on your path. Historically this would\n# cause confusion because glinux had adb in /usr/bin/ by default, though that\n# doesn't appear to be the case on my rodete hosts; it is however still the case\n# that my Mac has /usr/local/bin/adb installed by default and on the default\n# path.\nfunction adb() {\n    # We need `command which` because zsh has a built-in `which` that's more\n    # like `type`.\n    local ADB=$(command which adb)\n    if [ -z \"$ADB\" ]; then\n        echo \"Command adb not found; try lunch (and building) first?\"\n        return 1\n    fi\n    run_tool_with_logging \"ADB\" $ADB \"${@}\"\n}\n\nfunction fastboot() {\n    local FASTBOOT=$(command which fastboot)\n    if [ -z \"$FASTBOOT\" ]; then\n        echo \"Command fastboot not found; try lunch (and building) first?\"\n        return 1\n    fi\n    # Support tool event logging for fastboot command.\n    run_tool_with_logging \"FASTBOOT\" $FASTBOOT \"${@}\"\n}\n\n# communicate with a running device or emulator, set up necessary state,\n# and run the hat command.\nfunction runhat()\n{\n    # process standard adb options\n    local adbTarget=\"\"\n    if [ \"$1\" = \"-d\" -o \"$1\" = \"-e\" ]; then\n        adbTarget=$1\n        shift 1\n    elif [ \"$1\" = \"-s\" ]; then\n        adbTarget=\"$1 $2\"\n        shift 2\n    fi\n    local adbOptions=${adbTarget}\n    #echo adbOptions = ${adbOptions}\n\n    # runhat options\n    local targetPid=$1\n\n    if [ \"$targetPid\" = \"\" ]; then\n        echo \"Usage: runhat [ -d | -e | -s serial ] target-pid\"\n        return\n    fi\n\n    # confirm hat is available\n    if [ -z $(which hat) ]; then\n        echo \"hat is not available in this configuration.\"\n        return\n    fi\n\n    # issue \"am\" command to cause the hprof dump\n    local devFile=/data/local/tmp/hprof-$targetPid\n    echo \"Poking $targetPid and waiting for data...\"\n    echo \"Storing data at $devFile\"\n    adb ${adbOptions} shell am dumpheap $targetPid $devFile\n    echo \"Press enter when logcat shows \\\"hprof: heap dump completed\\\"\"\n    echo -n \"> \"\n    read\n\n    local localFile=/tmp/$$-hprof\n\n    echo \"Retrieving file $devFile...\"\n    adb ${adbOptions} pull $devFile $localFile\n\n    adb ${adbOptions} shell rm $devFile\n\n    echo \"Running hat on $localFile\"\n    echo \"View the output by pointing your browser at http://localhost:7000/\"\n    echo \"\"\n    hat -JXmx512m $localFile\n}\n\nfunction godir () {\n    if [[ -z \"$1\" ]]; then\n        echo \"Usage: godir <regex>\"\n        return\n    fi\n    local T=$(gettop)\n    local FILELIST\n    if [ ! \"$OUT_DIR\" = \"\" ]; then\n        mkdir -p $OUT_DIR\n        FILELIST=$OUT_DIR/filelist\n    else\n        FILELIST=$T/filelist\n    fi\n    if [[ ! -f $FILELIST ]]; then\n        echo -n \"Creating index...\"\n        (\\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > $FILELIST)\n        echo \" Done\"\n        echo \"\"\n    fi\n    local lines\n    lines=($(\\grep \"$1\" $FILELIST | sed -e 's/\\/[^/]*$//' | sort | uniq))\n    if [[ ${#lines[@]} = 0 ]]; then\n        echo \"Not found\"\n        return\n    fi\n    local pathname\n    local choice\n    if [[ ${#lines[@]} > 1 ]]; then\n        while [[ -z \"$pathname\" ]]; do\n            local index=1\n            local line\n            for line in ${lines[@]}; do\n                printf \"%6s %s\\n\" \"[$index]\" $line\n                index=$(($index + 1))\n            done\n            echo\n            echo -n \"Select one: \"\n            unset choice\n            read choice\n            if [[ $choice -gt ${#lines[@]} || $choice -lt 1 ]]; then\n                echo \"Invalid choice\"\n                continue\n            fi\n            pathname=${lines[@]:$(($choice-1)):1}\n        done\n    else\n        pathname=${lines[@]:0:1}\n    fi\n    \\cd $T/$pathname\n}\n\n# Go to a specific module in the android tree, as cached in module-info.json. If any build change\n# is made, and it should be reflected in the output, you should run 'refreshmod' first.\n# Note: This function is in envsetup because changing the directory needs to happen in the current\n# shell. All other functions that use module-info.json should be in build/soong/bin.\nfunction gomod() {\n    if [[ $# -ne 1 ]]; then\n        echo \"usage: gomod <module>\" >&2\n        return 1\n    fi\n\n    local path=\"$(pathmod $@)\"\n    if [ -z \"$path\" ]; then\n        return 1\n    fi\n    cd $path\n}\n\nfunction _complete_android_module_names() {\n    local word=${COMP_WORDS[COMP_CWORD]}\n    COMPREPLY=( $(allmod | grep -E \"^$word\") )\n}\n\nfunction get_make_command()\n{\n    # If we're in the top of an Android tree, use soong_ui.bash instead of make\n    if [ -f build/soong/soong_ui.bash ]; then\n        # Always use the real make if -C is passed in\n        for arg in \"$@\"; do\n            if [[ $arg == -C* ]]; then\n                echo command make\n                return\n            fi\n        done\n        echo build/soong/soong_ui.bash --make-mode\n    else\n        echo command make\n    fi\n}\n\nfunction make()\n{\n    _wrap_build $(get_make_command \"$@\") \"$@\"\n}\n\n# Zsh needs bashcompinit called to support bash-style completion.\nfunction enable_zsh_completion() {\n    # Don't override user's options if bash-style completion is already enabled.\n    if ! declare -f complete >/dev/null; then\n        autoload -U compinit && compinit\n        autoload -U bashcompinit && bashcompinit\n    fi\n}\n\nfunction validate_current_shell() {\n    local current_sh=\"$(ps -o command -p $$)\"\n    case \"$current_sh\" in\n        *bash*)\n            function check_type() { type -t \"$1\"; }\n            ;;\n        *zsh*)\n            function check_type() { type \"$1\"; }\n            enable_zsh_completion ;;\n        *)\n            echo -e \"WARNING: Only bash and zsh are supported.\\nUse of other shell would lead to erroneous results.\"\n            ;;\n    esac\n}\n\n# Execute the contents of any vendorsetup.sh files we can find.\n# Unless we find an allowed-vendorsetup_sh-files file, in which case we'll only\n# load those.\n#\n# This allows loading only approved vendorsetup.sh files\nfunction source_vendorsetup() {\n    unset VENDOR_PYTHONPATH\n    local T=\"$(gettop)\"\n    allowed=\n    for f in $(cd \"$T\" && find -L device vendor product -maxdepth 4 -name 'allowed-vendorsetup_sh-files' 2>/dev/null | sort); do\n        if [ -n \"$allowed\" ]; then\n            echo \"More than one 'allowed_vendorsetup_sh-files' file found, not including any vendorsetup.sh files:\"\n            echo \"  $allowed\"\n            echo \"  $f\"\n            return\n        fi\n        allowed=\"$T/$f\"\n    done\n\n    allowed_files=\n    [ -n \"$allowed\" ] && allowed_files=$(cat \"$allowed\")\n    for dir in device vendor product; do\n        for f in $(cd \"$T\" && test -d $dir && \\\n            find -L $dir -maxdepth 4 -name 'vendorsetup.sh' 2>/dev/null | sort); do\n\n            if [[ -z \"$allowed\" || \"$allowed_files\" =~ $f ]]; then\n                echo \"including $f\"; . \"$T/$f\"\n            else\n                echo \"ignoring $f, not in $allowed\"\n            fi\n        done\n    done\n\n    setup_cog_env_if_needed\n}\n\nfunction showcommands() {\n    local T=$(gettop)\n    if [[ -z \"$TARGET_PRODUCT\" ]]; then\n        >&2 echo \"TARGET_PRODUCT not set. Run lunch.\"\n        return\n    fi\n    case $(uname -s) in\n        Darwin)\n            PREBUILT_NAME=darwin-x86\n            ;;\n        Linux)\n            PREBUILT_NAME=linux-x86\n            ;;\n        *)\n            >&2 echo Unknown host $(uname -s)\n            return\n            ;;\n    esac\n    OUT_DIR=\"$(_get_abs_build_var_cached OUT_DIR)\"\n    if [[ \"$1\" == \"--regenerate\" ]]; then\n      shift 1\n      NINJA_ARGS=\"-t commands $@\" m\n    else\n      (cd $T && prebuilts/build-tools/$PREBUILT_NAME/bin/ninja \\\n          -f $OUT_DIR/combined-${TARGET_PRODUCT}.ninja \\\n          -t commands \"$@\")\n    fi\n}\n\n# These functions used to be here but are now standalone scripts\n# in build/soong/bin.  Unset these for the time being so the real\n# script is picked up.\n# TODO: Remove this some time after a suitable delay (maybe 2025?)\nunset allmod\nunset aninja\nunset cgrep\nunset core\nunset coredump_enable\nunset coredump_setup\nunset dirmods\nunset get_build_var\nunset get_abs_build_var\nunset getlastscreenshot\nunset getprebuilt\nunset getscreenshotpath\nunset getsdcardpath\nunset gettargetarch\nunset ggrep\nunset gogrep\nunset hmm\nunset installmod\nunset is64bit\nunset isviewserverstarted\nunset jgrep\nunset jsongrep\nunset key_back\nunset key_home\nunset key_menu\nunset ktgrep\nunset m\nunset mangrep\nunset mgrep\nunset mm\nunset mma\nunset mmm\nunset mmma\nunset outmod\nunset overrideflags\nunset owngrep\nunset pathmod\nunset pez\nunset pygrep\nunset qpid\nunset rcgrep\nunset refreshmod\nunset resgrep\nunset rsgrep\nunset run_tool_with_logging\nunset sepgrep\nunset sgrep\nunset startviewserver\nunset stopviewserver\nunset systemstack\nunset syswrite\nunset tomlgrep\nunset treegrep\n\n\nvalidate_current_shell\nset_global_paths\nsource_vendorsetup\naddcompletions\n\n\n"
  },
  {
    "path": "help.sh",
    "content": "#!/bin/bash\n\n# locate some directories\ncd \"$(dirname $0)\"\nSCRIPT_DIR=\"${PWD}\"\ncd ../..\nTOP=\"${PWD}\"\n\nmessage='The basic Android build process is:\n\ncd '\"${TOP}\"'\nsource build/envsetup.sh    # Add \"lunch\" (and other utilities and variables)\n                            # to the shell environment.\nlunch [<product>-<variant>] # Choose the device to target.\nm [<goals>]                 # Execute the configured build.\n\nUsage of \"m\" imitates usage of the program \"make\".\nSee '\"${SCRIPT_DIR}\"'/Usage.txt for more info about build usage and concepts.\n\nThe parallelism of the build can be set with a -jN argument to \"m\".  If you\ndon'\\''t provide a -j argument, the build system automatically selects a parallel\ntask count that it thinks is optimal for your system.\n\nCommon goals are:\n\n    clean                   (aka clobber) equivalent to rm -rf out/\n    checkbuild              Build every module defined in the source tree\n    droid                   Default target\n    sync                    Build everything in the default target except the images,\n                            for use with adb sync.\n    nothing                 Do not build anything, just parse and validate the build structure\n\n    java                    Build all the java code in the source tree\n    native                  Build all the native code in the source tree\n\n    host                    Build all the host code (not to be run on a device) in the source tree\n    target                  Build all the target code (to be run on the device) in the source tree\n\n    (java|native)-(host|target)\n    (host|target)-(java|native)\n                            Build the intersection of the two given arguments\n\n    snod                    Quickly rebuild the system image from built packages\n                            Stands for \"System, NO Dependencies\"\n    vnod                    Quickly rebuild the vendor image from built packages\n                            Stands for \"Vendor, NO Dependencies\"\n    pnod                    Quickly rebuild the product image from built packages\n                            Stands for \"Product, NO Dependencies\"\n    senod                   Quickly rebuild the system_ext image from built packages\n                            Stands for \"SystemExt, NO Dependencies\"\n    onod                    Quickly rebuild the odm image from built packages\n                            Stands for \"Odm, NO Dependencies\"\n    vdnod                   Quickly rebuild the vendor_dlkm image from built packages\n                            Stands for \"VendorDlkm, NO Dependencies\"\n    odnod                   Quickly rebuild the odm_dlkm image from built packages\n                            Stands for \"OdmDlkm, NO Dependencies\"\n    sdnod                   Quickly rebuild the system_dlkm image from built packages\n                            Stands for \"SystemDlkm, NO Dependencies\"\n\n\nSo, for example, you could run:\n\ncd '\"${TOP}\"'\nsource build/envsetup.sh\nlunch aosp_arm-userdebug\nm -j java\n\nto build all of the java code for the userdebug variant of the aosp_arm device.\n'\n\necho \"$message\"\n"
  },
  {
    "path": "navbar.md",
    "content": "* [Home](/README.md)\n* [Usage](/Usage.txt)\n* [Changes](/Changes.md)\n* [Outdated Reference](/core/build-system.html)\n"
  },
  {
    "path": "packaging/distdir.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# From the Android.mk pass:\nDIST_GOAL_OUTPUT_PAIRS :=\nDIST_SRC_DST_PAIRS :=\ninclude $(KATI_PACKAGE_MK_DIR)/dist.mk\nFILE_NAME_TAG := $(file <$(OUT_DIR)/file_name_tag.txt)\n.KATI_READONLY := FILE_NAME_TAG\n\n$(foreach pair,$(DIST_GOAL_OUTPUT_PAIRS), \\\n  $(eval goal := $(call word-colon,1,$(pair))) \\\n  $(eval output := $(subst FILE_NAME_TAG_PLACEHOLDER,$(FILE_NAME_TAG),$(call word-colon,2,$(pair)))) \\\n  $(eval .PHONY: _dist_$$(goal)) \\\n  $(if $(call streq,$(DIST),true),\\\n    $(eval _dist_$$(goal): $$(DIST_DIR)/$$(output)), \\\n    $(eval _dist_$$(goal):)))\n\ndefine copy-one-dist-file\n$(2): .KATI_TAGS += ;rule_name=dist-cp\n$(2): $(1)\n\t@echo \"Dist: $$@\"\n\trm -f $$@\n\tcp $$< $$@\nendef\n\nifeq ($(DIST),true)\n  $(foreach pair,$(DIST_SRC_DST_PAIRS), \\\n    $(eval src := $(call word-colon,1,$(pair))) \\\n    $(eval dst := $(subst FILE_NAME_TAG_PLACEHOLDER,$(FILE_NAME_TAG),$(DIST_DIR)/$(call word-colon,2,$(pair)))) \\\n    $(eval $(call copy-one-dist-file,$(src),$(dst))))\nendif\n\ncopy-one-dist-file :=\n"
  },
  {
    "path": "packaging/main.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Create a default rule. This is unused currently, as the real default rule is\n# still in the Kati build step.\n.PHONY: _packaging_default_rule_\n_packaging_default_rule_:\n\nifndef KATI\n$(error Only Kati is supported.)\nendif\n\n$(info [1/3] initializing packaging system ...)\n\n.KATI_READONLY := KATI_PACKAGE_MK_DIR\n\ninclude build/make/common/core.mk\ninclude build/make/common/strings.mk\n\n$(info [2/3] including distdir.mk ...)\n\ninclude build/make/packaging/distdir.mk\n\n$(info [3/3] writing packaging rules ...)\n"
  },
  {
    "path": "packaging/main_soong_only.mk",
    "content": "# Copyright (C) 2025 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nifndef KATI\n$(error Only Kati is supported.)\nendif\n\n$(info [1/4] initializing packaging system ...)\n\n.KATI_READONLY := KATI_PACKAGE_MK_DIR\n\ninclude build/make/common/core.mk\ninclude build/make/common/strings.mk\n\n# Define well-known goals and their dependency graph that they've\n# traditionally had in make builds. Also it's important to define\n# droid first so that it's built by default.\n\n.PHONY: droid\ndroid: droid_targets\n\n.PHONY: droid_targets\ndroid_targets: droidcore dist_files\n\n.PHONY: dist_files\ndist_files:\n\n.PHONY: droidcore\ndroidcore: droidcore-unbundled\n\n.PHONY: droidcore-unbundled\ndroidcore-unbundled:\n\n$(info [2/4] including distdir.mk ...)\n\ninclude build/make/packaging/distdir.mk\n\n$(info [3/4] defining phony modules ...)\n\ninclude $(OUT_DIR)/soong/soong_phony_targets.mk\n\ngoals := $(sort $(foreach pair,$(DIST_GOAL_OUTPUT_PAIRS),$(call word-colon,1,$(pair))))\n$(foreach goal,$(goals), \\\n  $(eval .PHONY: $$(goal)) \\\n  $(eval $$(goal):) \\\n  $(if $(call streq,$(DIST),true),\\\n    $(eval $$(goal): _dist_$$(goal))))\n\n$(info [4/4] writing packaging rules ...)\n"
  },
  {
    "path": "rbesetup.sh",
    "content": "function _source_env_setup_script() {\n  local -r ENV_SETUP_SCRIPT=\"build/make/envsetup.sh\"\n  local -r TOP_DIR=$(\n    while [[ ! -f \"${ENV_SETUP_SCRIPT}\" ]] && [[ \"${PWD}\" != \"/\" ]]; do\n      \\cd ..\n    done\n    if [[ -f \"${ENV_SETUP_SCRIPT}\" ]]; then\n      echo \"$(PWD= /bin/pwd -P)\"\n    fi\n  )\n\n  local -r FULL_PATH_ENV_SETUP_SCRIPT=\"${TOP_DIR}/${ENV_SETUP_SCRIPT}\"\n  if [[ ! -f \"${FULL_PATH_ENV_SETUP_SCRIPT}\" ]]; then\n    echo \"ERROR: Unable to source ${ENV_SETUP_SCRIPT}\"\n    return 1\n  fi\n\n  # Need to change directory to the repo root so vendor scripts can be sourced\n  # as well.\n  local -r CUR_DIR=$PWD\n  \\cd \"${TOP_DIR}\"\n  source \"${FULL_PATH_ENV_SETUP_SCRIPT}\"\n  \\cd \"${CUR_DIR}\"\n}\n\n# This function needs to run first as the remaining defining functions may be\n# using the envsetup.sh defined functions. Skip this part if this script is already\n# being invoked from envsetup.sh.\nif [[ \"$1\" != \"--skip-envsetup\" ]]; then\n  _source_env_setup_script || return\nfi\n\n# This function prefixes the given command with appropriate variables needed\n# for the build to be executed with RBE.\nfunction use_rbe() {\n  local RBE_BINARIES_DIR=\"prebuilts/remoteexecution-client/latest\"\n  local DOCKER_IMAGE=\"gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:1eb7f64b9e17102b970bd7a1af7daaebdb01c3fb777715899ef462d6c6d01a45\"\n\n  # Do not set an invocation-ID and let reproxy auto-generate one.\n  USE_RBE=\"true\" \\\n  FLAG_exec_root=\"$(gettop)\" \\\n  FLAG_platform=\"container-image=docker://${DOCKER_IMAGE}\" \\\n  RBE_use_application_default_credentials=\"true\" \\\n  RBE_reproxy_wait_seconds=\"20\" \\\n  RBE_CXX_EXEC_STRATEGY=\"remote_local_fallback\" \\\n  RBE_cpp_dependency_scanner_plugin=\"${RBE_BINARIES_DIR}/dependency_scanner_go_plugin.so\" \\\n  RBE_DIR=${RBE_BINARIES_DIR} \\\n  RBE_re_proxy=\"${RBE_BINARIES_DIR}/reproxy\" \\\n  $@\n}\n\n# This function detects if the uploader is available and sets the path of it to\n# ANDROID_ENABLE_METRICS_UPLOAD.\nfunction _export_metrics_uploader() {\n  local uploader_path=\"$(gettop)/vendor/google/misc/metrics_uploader_prebuilt/metrics_uploader.sh\"\n  if [[ -x \"${uploader_path}\" ]]; then\n    export ANDROID_ENABLE_METRICS_UPLOAD=\"${uploader_path}\"\n  fi\n}\n\n# This function sets RBE specific environment variables needed for the build to\n# executed by RBE. This file should be sourced once per checkout of Android code.\nfunction _set_rbe_vars() {\n  unset USE_GOMA\n  export USE_RBE=\"true\"\n  export RBE_CXX_EXEC_STRATEGY=\"racing\"\n  export RBE_JAVAC_EXEC_STRATEGY=\"racing\"\n  export RBE_R8_EXEC_STRATEGY=\"racing\"\n  export RBE_D8_EXEC_STRATEGY=\"racing\"\n  export RBE_use_unified_cas_ops=\"true\"\n  export RBE_JAVAC=1\n  export RBE_R8=1\n  export RBE_D8=1\n}\n\n_export_metrics_uploader\n_set_rbe_vars\n"
  },
  {
    "path": "shell_utils.sh",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfunction gettop\n{\n    local TOPFILE=build/make/core/envsetup.mk\n    # The ${TOP-} expansion allows this to work even with set -u\n    if [ -n \"${TOP:-}\" -a -f \"${TOP:-}/$TOPFILE\" ] ; then\n        # The following circumlocution ensures we remove symlinks from TOP.\n        (cd \"$TOP\"; PWD= /bin/pwd)\n    else\n        if [ -f $TOPFILE ] ; then\n            # The following circumlocution (repeated below as well) ensures\n            # that we record the true directory name and not one that is\n            # faked up with symlink names.\n            PWD= /bin/pwd\n        else\n            local HERE=$PWD\n            local T=\n            while [ \\( ! \\( -f $TOPFILE \\) \\) -a \\( \"$PWD\" != \"/\" \\) ]; do\n                \\cd ..\n                T=`PWD= /bin/pwd -P`\n            done\n            \\cd \"$HERE\"\n            if [ -f \"$T/$TOPFILE\" ]; then\n                echo \"$T\"\n            fi\n        fi\n    fi\n}\n\n# Asserts that the root of the tree can be found.\nif [ -z \"${IMPORTING_ENVSETUP:-}\" ] ; then\nfunction require_top\n{\n    TOP=$(gettop)\n    if [[ ! $TOP ]] ; then\n        echo \"Can not locate root of source tree. $(basename $0) must be run from within the Android source tree or TOP must be set.\" >&2\n        exit 1\n    fi\n}\nfi\n\n# Asserts that the lunch variables have been set\nif [ -z \"${IMPORTING_ENVSETUP:-}\" ] ; then\nfunction require_lunch\n{\n    if [[ ! $TARGET_PRODUCT || ! $TARGET_RELEASE || ! $TARGET_BUILD_VARIANT  ]] ; then\n        echo \"Please run lunch and try again.\" >&2\n        exit 1\n    fi\n}\nfi\n\n# This function sets up the build environment to be appropriate for Cog.\nfunction setup_cog_env_if_needed() {\n  local top=$(gettop)\n\n  # return early if not in a cog workspace\n  if [[ ! \"$top\" =~ ^/google/cog ]]; then\n    return 0\n  fi\n\n  setup_cog_symlink\n\n  export ANDROID_BUILD_ENVIRONMENT_CONFIG=\"googler-cog\"\n\n  # Running repo command within Cog workspaces is not supported, so override\n  # it with this function. If the user is running repo within a Cog workspace,\n  # we'll fail with an error, otherwise, we run the original repo command with\n  # the given args.\n  if ! ORIG_REPO_PATH=`which repo`; then\n    return 0\n  fi\n  function repo {\n    if [[ \"${PWD}\" == /google/cog/* ]]; then\n      echo -e \"\\e[01;31mERROR:\\e[0mrepo command is disallowed within Cog workspaces.\"\n      kill -INT $$ # exits the script without exiting the user's shell\n    fi\n    ${ORIG_REPO_PATH} \"$@\"\n  }\n}\n\n# creates a symlink for the out/ dir when inside a cog workspace.\nfunction setup_cog_symlink() {\n  local out_dir=$(getoutdir)\n  local top=$(gettop)\n\n  # return early if out dir is already a symlink.\n  if [[ -L \"$out_dir\" ]]; then\n    destination=$(readlink \"$out_dir\")\n    # ensure the destination exists.\n    mkdir -p \"$destination\"\n    return 0\n  fi\n\n  # return early if out dir is not in the workspace\n  if [[ ! \"$out_dir\" =~ ^$top/ ]]; then\n    return 0\n  fi\n\n  local link_destination=\"${HOME}/.cog/android-build-out\"\n\n  # remove existing out/ dir if it exists\n  if [[ -d \"$out_dir\" ]]; then\n    echo \"Detected existing out/ directory in the Cog workspace which is not supported. Repairing workspace by removing it and creating the symlink to ~/.cog/android-build-out\"\n    if ! rm -rf \"$out_dir\"; then\n      echo \"Failed to remove existing out/ directory: $out_dir\" >&2\n      kill -INT $$ # exits the script without exiting the user's shell\n    fi\n  fi\n\n  # create symlink\n  echo \"Creating symlink: $out_dir -> $link_destination\"\n  mkdir -p ${link_destination}\n  if ! ln -s \"$link_destination\" \"$out_dir\"; then\n    echo \"Failed to create cog symlink: $out_dir -> $link_destination\" >&2\n    kill -INT $$ # exits the script without exiting the user's shell\n  fi\n}\n\nfunction getoutdir\n{\n    local top=$(gettop)\n    local out_dir=\"${OUT_DIR:-}\"\n    if [[ -z \"${out_dir}\" ]]; then\n        if [[ -n \"${OUT_DIR_COMMON_BASE:-}\" && -n \"${top}\" ]]; then\n            out_dir=\"${OUT_DIR_COMMON_BASE}/$(basename ${top})\"\n        else\n            out_dir=\"out\"\n        fi\n    fi\n    if [[ \"${out_dir}\" != /* ]]; then\n        out_dir=\"${top}/${out_dir}\"\n    fi\n    echo \"${out_dir}\"\n}\n\n# Pretty print the build status and duration\nfunction _wrap_build()\n{\n    if [[ \"${ANDROID_QUIET_BUILD:-}\" == true ]]; then\n      \"$@\"\n      return $?\n    fi\n    local start_time=$(date +\"%s\")\n    \"$@\"\n    local ret=$?\n    local end_time=$(date +\"%s\")\n    local tdiff=$(($end_time-$start_time))\n    local hours=$(($tdiff / 3600 ))\n    local mins=$((($tdiff % 3600) / 60))\n    local secs=$(($tdiff % 60))\n    local ncolors=$(tput colors 2>/dev/null)\n    if [ -n \"$ncolors\" ] && [ $ncolors -ge 8 ]; then\n        color_failed=$'\\E'\"[0;31m\"\n        color_success=$'\\E'\"[0;32m\"\n        color_warning=$'\\E'\"[0;33m\"\n        color_reset=$'\\E'\"[00m\"\n    else\n        color_failed=\"\"\n        color_success=\"\"\n        color_reset=\"\"\n    fi\n\n    echo\n    if [ $ret -eq 0 ] ; then\n        echo -n \"${color_success}#### build completed successfully \"\n    else\n        echo -n \"${color_failed}#### failed to build some targets \"\n    fi\n    if [ $hours -gt 0 ] ; then\n        printf \"(%02d:%02d:%02d (hh:mm:ss))\" $hours $mins $secs\n    elif [ $mins -gt 0 ] ; then\n        printf \"(%02d:%02d (mm:ss))\" $mins $secs\n    elif [ $secs -gt 0 ] ; then\n        printf \"(%d seconds)\" $secs\n    fi\n    echo \" ####${color_reset}\"\n    echo\n    return $ret\n}\n\n\nfunction log_tool_invocation()\n{\n    if [[ -z $ANDROID_TOOL_LOGGER ]]; then\n        return\n    fi\n\n    LOG_TOOL_TAG=$1\n    LOG_START_TIME=$(date +%s.%N)\n    trap '\n        exit_code=$?;\n        # Remove the trap to prevent duplicate log.\n        trap - EXIT;\n        $ANDROID_TOOL_LOGGER \\\n                --tool_tag=\"${LOG_TOOL_TAG}\" \\\n                --start_timestamp=\"${LOG_START_TIME}\" \\\n                --end_timestamp=\"$(date +%s.%N)\" \\\n                --tool_args=\"$*\" \\\n                --exit_code=\"${exit_code}\" \\\n                ${ANDROID_TOOL_LOGGER_EXTRA_ARGS} \\\n           > /dev/null 2>&1 &\n        exit ${exit_code}\n    ' SIGINT SIGTERM SIGQUIT EXIT\n}\n\n# Import the build variables supplied as arguments into this shell's environment.\n# For absolute variables, prefix the variable name with a '/'. For example:\n#    import_build_vars OUT_DIR DIST_DIR /HOST_OUT_EXECUTABLES\n# Returns nonzero if the build command failed. Stderr is passed through.\nfunction import_build_vars()\n{\n    require_top\n    local script\n    script=$(cd $TOP && build/soong/bin/get_build_vars \"$@\")\n    local ret=$?\n    if [ $ret -ne 0 ] ; then\n        return $ret\n    fi\n    eval \"$script\"\n    return $?\n}\n"
  },
  {
    "path": "tapasHelp.sh",
    "content": "#!/bin/bash\n\n# locate some directories\ncd \"$(dirname $0)\"\nSCRIPT_DIR=\"${PWD}\"\ncd ../..\nTOP=\"${PWD}\"\n\nmessage='usage: tapas [<App1> <App2> ...] [arm|x86|arm64|x86_64] [eng|userdebug|user] [devkeys]\n\ntapas selects individual apps to be built by the Android build system. Unlike\n\"lunch\", \"tapas\" does not request the building of images for a device.\nAdditionally, an app built with \"tapas\" will have its dex file inside its apk,\nwhich should cause it to be suitable for installing on any api-compatible\ndevice. In other words, \"tapas\" configures the build of unbundled apps.\n\nThe names <App1> <App2> ... should match LOCAL_PACKAGE_NAME as defined in an\nAndroid.mk\n\nThe usage of the other arguments matches that of the rest of the platform\nbuild system and can be found by running `m help`'\n\necho \"$message\"\n"
  },
  {
    "path": "target/board/BoardConfigGsiCommon.mk",
    "content": "# BoardConfigGsiCommon.mk\n#\n# Common compile-time definitions for GSI\n# Builds upon the mainline config.\n#\n# See device/generic/common/README.md for more details.\n#\n\ninclude build/make/target/board/BoardConfigMainlineCommon.mk\n\nTARGET_NO_KERNEL := true\n\n# This flag is set by mainline but isn't desired for GSI.\nBOARD_USES_SYSTEM_OTHER_ODEX :=\n\n# system.img is ext4/erofs and non-sparsed.\nGSI_FILE_SYSTEM_TYPE ?= ext4\nBOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE := $(GSI_FILE_SYSTEM_TYPE)\nTARGET_USERIMAGES_SPARSE_EXT_DISABLED := true\nTARGET_USERIMAGES_SPARSE_EROFS_DISABLED := true\n\n# Enable system_dlkm image for creating a symlink in GSI to support\n# the devices with system_dlkm partition\nBOARD_USES_SYSTEM_DLKMIMAGE := true\nBOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE := ext4\nTARGET_COPY_OUT_SYSTEM_DLKM := system_dlkm\n\n# GSI also includes make_f2fs to support userdata parition in f2fs\n# for some devices\nTARGET_USERIMAGES_USE_F2FS := true\n\n# Enable dynamic system image size and reserved 64MB in it.\nBOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE := 67108864\n\n# GSI forces product and system_ext packages to /system for now.\nTARGET_COPY_OUT_PRODUCT := system/product\nTARGET_COPY_OUT_SYSTEM_EXT := system/system_ext\nBOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE :=\nBOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE :=\n\n# Creates metadata partition mount point under root for\n# the devices with metadata parition\nBOARD_USES_METADATA_PARTITION := true\n\n# Android Verified Boot (AVB):\n#   Set the rollback index to zero, to prevent the device bootloader from\n#   updating the last seen rollback index in the tamper-evident storage.\nBOARD_AVB_ROLLBACK_INDEX := 0\n\n# The chained vbmeta settings for boot images.\nBOARD_AVB_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem\nBOARD_AVB_BOOT_ALGORITHM := SHA256_RSA4096\nBOARD_AVB_BOOT_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)\nBOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION := 2\n\n# Enable AVB chained partition for system.\n# https://android.googlesource.com/platform/external/avb/+/master/README.md\nBOARD_AVB_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem\nBOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048\nBOARD_AVB_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)\nBOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION := 1\n\n# Using sha256 for dm-verity partitions. b/156162446\nBOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256\n\nifdef BUILDING_GSI\n# super.img spec for GSI targets\nBOARD_SUPER_PARTITION_SIZE := 3229614080\nBOARD_SUPER_PARTITION_GROUPS := gsi_dynamic_partitions\nBOARD_GSI_DYNAMIC_PARTITIONS_PARTITION_LIST := system\nBOARD_GSI_DYNAMIC_PARTITIONS_SIZE := 3221225472\n\n# Build pvmfw with GSI: b/376363989\nifeq (true,$(PRODUCT_BUILD_PVMFW_IMAGE))\nBOARD_PVMFWIMAGE_PARTITION_SIZE := 0x00100000\nendif\nendif\n\n# TODO(b/123695868, b/146149698):\n#     This flag is set by mainline but isn't desired for GSI\nBOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR :=\n\n# GSI specific System Properties\nifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))\nTARGET_SYSTEM_EXT_PROP := build/make/target/board/gsi_system_ext.prop\nelse\nTARGET_SYSTEM_EXT_PROP := build/make/target/board/gsi_system_ext_user.prop\nendif\n\n# Set this to create /cache mount point for non-A/B devices that mounts /cache.\n# The partition size doesn't matter, just to make build pass.\nBOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4\nBOARD_CACHEIMAGE_PARTITION_SIZE := 16777216\n\n# Setup a vendor image to let PRODUCT_VENDOR_PROPERTIES does not affect GSI\nBOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4\n"
  },
  {
    "path": "target/board/BoardConfigMainlineCommon.mk",
    "content": "# BoardConfigMainlineCommon.mk\n#\n# Common compile-time definitions for mainline images.\n\n# Ensure all trunk-stable flags are available.\ninclude build/make/target/product/build_variables.mk\n\n# The generic product target doesn't have any hardware-specific pieces.\nTARGET_NO_BOOTLOADER := true\nTARGET_NO_RECOVERY := true\n\nBOARD_EXT4_SHARE_DUP_BLOCKS := true\n\nTARGET_USERIMAGES_USE_EXT4 := true\n\n# Mainline devices must have /system_ext, /vendor and /product partitions.\nTARGET_COPY_OUT_SYSTEM_EXT := system_ext\nTARGET_COPY_OUT_VENDOR := vendor\nTARGET_COPY_OUT_PRODUCT := product\nBOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4\nBOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4\n\n# Creates metadata partition mount point under root for\n# the devices with metadata parition\nBOARD_USES_METADATA_PARTITION := true\n\n# 64 bit mediadrmserver\nTARGET_ENABLE_MEDIADRM_64 := true\n\n# Puts odex files on system_other, as well as causing dex files not to get\n# stripped from APKs.\nBOARD_USES_SYSTEM_OTHER_ODEX := true\n\n# Audio: must using XML format for Treblized devices\nUSE_XML_AUDIO_POLICY_CONF := 1\n\n# Bluetooth defines\n# TODO(b/123695868): Remove the need for this\nBOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := build/make/target/board/mainline_arm64/bluetooth\n\nBOARD_AVB_ENABLE := true\nBOARD_AVB_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)\n\nBOARD_CHARGER_ENABLE_SUSPEND := true\n\n# Enable system property split for Treble\nBOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED := true\n\n# Include stats logging code in LMKD\nTARGET_LMKD_STATS_LOG := true\n"
  },
  {
    "path": "target/board/BoardConfigPixelCommon.mk",
    "content": "# BoardConfigPixelCommon.mk\n#\n# Common compile-time definitions for Pixel devices.\n\n# Using sha256 for dm-verity partitions. b/156162446\n# system, system_other, system_ext and product.\nBOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256\nBOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256\nBOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256\nBOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256\nBOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256\n\n# vendor and odm.\nBOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256\nBOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256\n\n# vendor_dlkm and odm_dlkm.\nBOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256\nBOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256\n"
  },
  {
    "path": "target/board/android-info.mk",
    "content": "#\n# Set up product-global definitions and include product-specific rules.\n#\n\nLOCAL_PATH := $(call my-dir)\n\n-include $(TARGET_DEVICE_DIR)/AndroidBoard.mk\n\n# Generate a file that contains various information about the\n# device we're building for.  This file is typically packaged up\n# with everything else.\n#\n# The following logic is used to find the contents of the info file:\n#   1. TARGET_BOARD_INFO_FILES (can be set in BoardConfig.mk) will be combined.\n#   2. TARGET_BOARD_INFO_FILE (can be set in BoardConfig.mk) will be used.\n#   3. $(TARGET_DEVICE_DIR)/board-info.txt will be used if present.\n#\n# Specifying both TARGET_BOARD_INFO_FILES and TARGET_BOARD_INFO_FILE is an\n# error.\n#\nINSTALLED_ANDROID_INFO_TXT_TARGET := $(PRODUCT_OUT)/android-info.txt\nifdef TARGET_BOARD_INFO_FILES\n  ifdef TARGET_BOARD_INFO_FILE\n    $(warning Both TARGET_BOARD_INFO_FILES and TARGET_BOARD_INFO_FILE are defined.)\n    $(warning Using $(TARGET_BOARD_INFO_FILES) rather than $(TARGET_BOARD_INFO_FILE) for android-info.txt)\n  endif\n  board_info_txt := $(call intermediates-dir-for,PACKAGING,board-info)/board-info.txt\n$(board_info_txt): $(TARGET_BOARD_INFO_FILES)\n\t$(hide) cat $(TARGET_BOARD_INFO_FILES) > $@\nelse ifdef TARGET_BOARD_INFO_FILE\n  board_info_txt := $(TARGET_BOARD_INFO_FILE)\nelse\n  board_info_txt := $(wildcard $(TARGET_DEVICE_DIR)/board-info.txt)\nendif\n\nCHECK_RADIO_VERSIONS := $(HOST_OUT_EXECUTABLES)/check_radio_versions$(HOST_EXECUTABLE_SUFFIX)\n$(INSTALLED_ANDROID_INFO_TXT_TARGET): $(board_info_txt) $(CHECK_RADIO_VERSIONS)\n\t$(hide) $(CHECK_RADIO_VERSIONS) \\\n\t\t--board_info_txt $(board_info_txt) \\\n\t\t--board_info_check $(BOARD_INFO_CHECK)\n\t$(call pretty,\"Generated: ($@)\")\nifdef board_info_txt\n\t$(hide) grep -v '#' $< > $@\nelse ifdef TARGET_BOOTLOADER_BOARD_NAME\n\t$(hide) echo \"board=$(TARGET_BOOTLOADER_BOARD_NAME)\" > $@\nelse\n\t$(hide) echo \"\" > $@\nendif\n\n$(call declare-0p-target,$(INSTALLED_ANDROID_INFO_TXT_TARGET))\n\n# Copy compatibility metadata to the device.\n\n# DEVICE_MANIFEST_SKUS: a list of SKUS where DEVICE_MANIFEST_<sku>_FILES is defined.\nifdef DEVICE_MANIFEST_SKUS\n\n# Install /vendor/etc/vintf/manifest_$(sku).xml\n# $(1): sku\ndefine _add_device_sku_manifest\nmy_fragment_files_var := DEVICE_MANIFEST_$$(call to-upper,$(1))_FILES\nifndef $$(my_fragment_files_var)\n$$(error $(1) is in DEVICE_MANIFEST_SKUS but $$(my_fragment_files_var) is not defined)\nendif\nmy_fragment_files := $$($$(my_fragment_files_var))\ninclude $$(CLEAR_VARS)\nLOCAL_MODULE := vendor_manifest_$(1).xml\nLOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution\nLOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice\nLOCAL_MODULE_STEM := manifest_$(1).xml\nLOCAL_MODULE_CLASS := ETC\nLOCAL_MODULE_PATH   := $(TARGET_OUT_VENDOR)/etc/vintf\n\nGEN := $$(local-generated-sources-dir)/manifest_$(1).xml\n$$(GEN): PRIVATE_SRC_FILES := $$(my_fragment_files)\n$$(GEN): $$(my_fragment_files) $$(HOST_OUT_EXECUTABLES)/assemble_vintf\n\tBOARD_SEPOLICY_VERS=$$(BOARD_SEPOLICY_VERS) \\\n\tPRODUCT_ENFORCE_VINTF_MANIFEST=$$(PRODUCT_ENFORCE_VINTF_MANIFEST) \\\n\t$$(HOST_OUT_EXECUTABLES)/assemble_vintf -o $$@ \\\n\t\t-i $$(call normalize-path-list,$$(PRIVATE_SRC_FILES))\n\nLOCAL_PREBUILT_MODULE_FILE := $$(GEN)\ninclude $$(BUILD_PREBUILT)\nmy_fragment_files_var :=\nmy_fragment_files :=\nendef\n\n$(foreach sku, $(DEVICE_MANIFEST_SKUS), $(eval $(call _add_device_sku_manifest,$(sku))))\n_add_device_sku_manifest :=\n\nendif # DEVICE_MANIFEST_SKUS\n\n# ODM_MANIFEST_SKUS: a list of SKUS where ODM_MANIFEST_<sku>_FILES are defined.\nifdef ODM_MANIFEST_SKUS\n\n# Install /odm/etc/vintf/manifest_$(sku).xml\n# $(1): sku\ndefine _add_odm_sku_manifest\nmy_fragment_files_var := ODM_MANIFEST_$$(call to-upper,$(1))_FILES\nifndef $$(my_fragment_files_var)\n$$(error $(1) is in ODM_MANIFEST_SKUS but $$(my_fragment_files_var) is not defined)\nendif\nmy_fragment_files := $$($$(my_fragment_files_var))\ninclude $$(CLEAR_VARS)\nLOCAL_MODULE := odm_manifest_$(1).xml\nLOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution\nLOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice\nLOCAL_MODULE_STEM := manifest_$(1).xml\nLOCAL_MODULE_CLASS := ETC\nLOCAL_MODULE_RELATIVE_PATH := vintf\nLOCAL_ODM_MODULE := true\nGEN := $$(local-generated-sources-dir)/manifest_$(1).xml\n$$(GEN): PRIVATE_SRC_FILES := $$(my_fragment_files)\n$$(GEN): $$(my_fragment_files) $$(HOST_OUT_EXECUTABLES)/assemble_vintf\n\tVINTF_IGNORE_TARGET_FCM_VERSION=true \\\n\t$$(HOST_OUT_EXECUTABLES)/assemble_vintf -o $$@ \\\n\t\t-i $$(call normalize-path-list,$$(PRIVATE_SRC_FILES))\nLOCAL_PREBUILT_MODULE_FILE := $$(GEN)\ninclude $$(BUILD_PREBUILT)\nmy_fragment_files_var :=\nmy_fragment_files :=\nendef\n\n$(foreach sku, $(ODM_MANIFEST_SKUS), $(eval $(call _add_odm_sku_manifest,$(sku))))\n_add_odm_sku_manifest :=\n\nendif # ODM_MANIFEST_SKUS\n"
  },
  {
    "path": "target/board/generic/AndroidBoard.mk",
    "content": "LOCAL_PATH := $(call my-dir)\n"
  },
  {
    "path": "target/board/generic/BoardConfig.mk",
    "content": "# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# arm emulator specific definitions\nTARGET_ARCH := arm\n\n# Note: Before P, we built the platform images for ARMv7-A _without_ NEON.\n# Note: Before Q, we built the CTS and SDK images for ARMv7-A _without_ NEON.\n# Note: Before Q, we built unbundled apps for ARMv7-A _without_ NEON.\n#\n# Starting from Pi, System image of aosp_arm products is the new GSI\n# for real devices newly launched for Pi. These devices are usualy not\n# as performant as the mainstream 64-bit devices and the performance\n# provided by NEON is important for them to pass related CTS tests.\nTARGET_ARCH_VARIANT := armv7-a-neon\nTARGET_CPU_VARIANT := generic\nTARGET_CPU_ABI := armeabi-v7a\nTARGET_CPU_ABI2 := armeabi\n\ninclude build/make/target/board/BoardConfigGsiCommon.mk\n"
  },
  {
    "path": "target/board/generic/README.txt",
    "content": "The \"generic\" product defines a non-hardware-specific target\nwithout a kernel or bootloader.\n\nIt can be used to build the entire user-level system, and\nwill work with the emulator, though sound will not work\n(see the \"emulator\" product for that).\n\nIt is not a product \"base class\"; no other products inherit\nfrom it or use it in any way.\n"
  },
  {
    "path": "target/board/generic/device.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n"
  },
  {
    "path": "target/board/generic/system_ext.prop",
    "content": "#\n# system.prop for generic sdk \n#\n\nrild.libpath=/vendor/lib/libreference-ril.so\n"
  },
  {
    "path": "target/board/generic_64bitonly_x86_64/BoardConfig.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# x86_64 emulator specific definitions\nTARGET_CPU_ABI := x86_64\nTARGET_ARCH := x86_64\nTARGET_ARCH_VARIANT := x86_64\n\n# Keep the following for 32-bit native code support\n# There are a few native services still on 32-bit modes, e.g. media & audio.\n# Remove them in S.\nTARGET_2ND_CPU_ABI := x86\nTARGET_2ND_ARCH := x86\nTARGET_2ND_ARCH_VARIANT := x86_64\n\nTARGET_PRELINK_MODULE := false\n\ninclude build/make/target/board/BoardConfigGsiCommon.mk\n"
  },
  {
    "path": "target/board/generic_64bitonly_x86_64/README.txt",
    "content": "The \"generic_x86_64_app\" product defines a non-hardware-specific IA target\nwithout a kernel or bootloader.\n\nIt can be used to build the entire user-level system, and\nwill work with the IA version of the emulator,\n\nThis supports 64-bit apps only.\n"
  },
  {
    "path": "target/board/generic_64bitonly_x86_64/device.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nifdef NET_ETH0_STARTONBOOT\n  PRODUCT_PROPERTY_OVERRIDES += net.eth0.startonboot=1\nendif\n"
  },
  {
    "path": "target/board/generic_64bitonly_x86_64/system.prop",
    "content": "#\n# system.prop for generic sdk\n#\n\nrild.libpath=/vendor/lib64/libreference-ril.so\n"
  },
  {
    "path": "target/board/generic_arm64/BoardConfig.mk",
    "content": "# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# arm64 emulator specific definitions\nTARGET_ARCH := arm64\nTARGET_ARCH_VARIANT := armv8-a\nTARGET_CPU_VARIANT := generic\nTARGET_CPU_ABI := arm64-v8a\n\nTARGET_2ND_ARCH := arm\nTARGET_2ND_CPU_ABI := armeabi-v7a\nTARGET_2ND_CPU_ABI2 := armeabi\n\nifneq ($(TARGET_BUILD_APPS)$(filter sdk,$(MAKECMDGOALS)),)\n# DO NOT USE\n# DO NOT USE\n#\n# This architecture / CPU variant must NOT be used for any 64 bit\n# platform builds. It is the lowest common denominator required\n# to build an unbundled application or cts for all supported 32 and 64 bit\n# platforms. It now recommended to use generic_arm64_plus_armv7 to achieve this.\n#\n# If you're building a 64 bit platform (and not an application) the\n# ARM-v8 specification allows you to assume all the features available in an\n# armv7-a-neon CPU. You should set the following as 2nd arch/cpu variant:\n#\n# TARGET_2ND_ARCH_VARIANT := armv8-a\n# TARGET_2ND_CPU_VARIANT := generic\n#\n# DO NOT USE\n# DO NOT USE\nTARGET_2ND_ARCH_VARIANT := armv7-a-neon\n# DO NOT USE\n# DO NOT USE\nTARGET_2ND_CPU_VARIANT := generic\n# DO NOT USE\n# DO NOT USE\nelse\nTARGET_2ND_ARCH_VARIANT := armv8-a\nTARGET_2ND_CPU_VARIANT := generic\nendif\n\n# Include 64-bit mediaserver to support 64-bit only devices\nTARGET_DYNAMIC_64_32_MEDIASERVER := true\n# Include 64-bit drmserver to support 64-bit only devices\nTARGET_DYNAMIC_64_32_DRMSERVER := true\n\ninclude build/make/target/board/BoardConfigGsiCommon.mk\n\n# Some vendors still haven't cleaned up all device specific directories under\n# root!\n\n# TODO(b/111434759, b/111287060) SoC specific hacks\nBOARD_ROOT_EXTRA_SYMLINKS += /vendor/lib/dsp:/dsp\nBOARD_ROOT_EXTRA_SYMLINKS += /mnt/vendor/persist:/persist\nBOARD_ROOT_EXTRA_SYMLINKS += /vendor/firmware_mnt:/firmware\n# for Android.bp\nTARGET_ADD_ROOT_EXTRA_VENDOR_SYMLINKS := true\n\n# TODO(b/36764215): remove this setting when the generic system image\n# no longer has QCOM-specific directories under /.\nBOARD_SEPOLICY_DIRS += build/make/target/board/generic_arm64/sepolicy\n"
  },
  {
    "path": "target/board/generic_arm64/README.txt",
    "content": "The \"generic_arm64\" product defines a non-hardware-specific arm64 target\nwithout a bootloader.\n\nIt is also the target to build the generic kernel image (GKI).\n\nIt is not a product \"base class\"; no other products inherit\nfrom it or use it in any way.\n"
  },
  {
    "path": "target/board/generic_arm64/device.mk",
    "content": "#\n# Copyright (C) 2013 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n"
  },
  {
    "path": "target/board/generic_arm64/sepolicy/OWNERS",
    "content": "include platform/system/sepolicy:/OWNERS\n"
  },
  {
    "path": "target/board/generic_arm64/sepolicy/file.te",
    "content": "# TODO(b/36764215): remove this file when the generic system image\n# no longer has these directories\ntype persist_file, file_type;\n\n# Default type for anything under /firmware.\ntype firmware_file, fs_type, contextmount_type;\n"
  },
  {
    "path": "target/board/generic_arm64/sepolicy/file_contexts",
    "content": "# TODO(b/36764215): remove this file when the generic system image\n# no longer has these directories. They are specific to QCOM.\n\n# /\n/tombstones             u:object_r:rootfs:s0\n/dsp                    u:object_r:rootfs:s0\n\n# /persist\n/persist(/.*)?          u:object_r:persist_file:s0\n\n# files in firmware\n/firmware(/.*)?         u:object_r:firmware_file:s0\n"
  },
  {
    "path": "target/board/generic_arm64/system_ext.prop",
    "content": "#\n# system.prop for generic arm64 sdk\n#\n\nrild.libpath=/vendor/lib64/libreference-ril.so\n"
  },
  {
    "path": "target/board/generic_arm64_plus_armv7/BoardConfig.mk",
    "content": "# Copyright (C) 2025 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# arm64 emulator specific definitions\nTARGET_ARCH := arm64\nTARGET_ARCH_VARIANT := armv8-a\nTARGET_CPU_VARIANT := generic\nTARGET_CPU_ABI := arm64-v8a\n\nTARGET_2ND_ARCH := arm\nTARGET_2ND_CPU_ABI := armeabi-v7a\nTARGET_2ND_CPU_ABI2 := armeabi\n\n# DO NOT USE\n# DO NOT USE\n#\n# This architecture / CPU variant must NOT be used for any 64 bit\n# platform builds. It is the lowest common denominator required\n# to build an unbundled application or cts for all supported 32 and 64 bit\n# platforms.\n#\n# If you're building a 64 bit platform (and not an application) the\n# ARM-v8 specification allows you to assume all the features available in an\n# armv7-a-neon CPU. You should set the following as 2nd arch/cpu variant:\n#\n# TARGET_2ND_ARCH_VARIANT := armv8-a\n# TARGET_2ND_CPU_VARIANT := generic\n#\n# DO NOT USE\n# DO NOT USE\nTARGET_2ND_ARCH_VARIANT := armv7-a-neon\n# DO NOT USE\n# DO NOT USE\nTARGET_2ND_CPU_VARIANT := generic\n# DO NOT USE\n# DO NOT USE\n\n# Include 64-bit mediaserver to support 64-bit only devices\nTARGET_DYNAMIC_64_32_MEDIASERVER := true\n# Include 64-bit drmserver to support 64-bit only devices\nTARGET_DYNAMIC_64_32_DRMSERVER := true\n\ninclude build/make/target/board/BoardConfigGsiCommon.mk\n"
  },
  {
    "path": "target/board/generic_arm64_plus_armv7/README.txt",
    "content": "The \"generic_arm64_plus_armv7\" product defines a non-hardware-specific arm64\ntarget with armv7 compatible arm32.  It is used for building CTS and other\ntest suites for which the 32-bit binaries may be run on older devices with\narmv7 CPUs.\n\nIt is not a product \"base class\"; no other products inherit\nfrom it or use it in any way.\n"
  },
  {
    "path": "target/board/generic_arm64_plus_armv7/device.mk",
    "content": "#\n# Copyright (C) 2025 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n"
  },
  {
    "path": "target/board/generic_riscv64/BoardConfig.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# riscv64 emulator specific definitions\nTARGET_ARCH := riscv64\nTARGET_ARCH_VARIANT :=\nTARGET_CPU_VARIANT := generic\nTARGET_CPU_ABI := riscv64\n\n# Include 64-bit mediaserver to support 64-bit only devices\nTARGET_DYNAMIC_64_32_MEDIASERVER := true\n\ninclude build/make/target/board/BoardConfigGsiCommon.mk\n\n# Temporary hack while prebuilt modules are missing riscv64.\nALLOW_MISSING_DEPENDENCIES := true\n"
  },
  {
    "path": "target/board/generic_riscv64/README.txt",
    "content": "The \"generic_riscv64\" product defines a non-hardware-specific riscv64 target\nwithout a bootloader.\n\nIt is also the target to build the generic kernel image (GKI).\n\nIt is not a product \"base class\"; no other products inherit\nfrom it or use it in any way.\n"
  },
  {
    "path": "target/board/generic_riscv64/device.mk",
    "content": "#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n"
  },
  {
    "path": "target/board/generic_riscv64/system_ext.prop",
    "content": "#\n# system.prop for generic riscv64 sdk\n#\n\nrild.libpath=/vendor/lib64/libreference-ril.so\n"
  },
  {
    "path": "target/board/generic_x86/BoardConfig.mk",
    "content": "# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# x86 emulator specific definitions\nTARGET_CPU_ABI := x86\nTARGET_ARCH := x86\nTARGET_ARCH_VARIANT := x86\n\ninclude build/make/target/board/BoardConfigGsiCommon.mk\n"
  },
  {
    "path": "target/board/generic_x86/README.txt",
    "content": "The \"generic_x86\" product defines a non-hardware-specific IA target\nwithout a kernel or bootloader.\n\nIt can be used to build the entire user-level system, and\nwill work with the IA version of the emulator,\n\nIt is not a product \"base class\"; no other products inherit\nfrom it or use it in any way.\n"
  },
  {
    "path": "target/board/generic_x86/device.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nifdef NET_ETH0_STARTONBOOT\n  PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1\nendif\n"
  },
  {
    "path": "target/board/generic_x86/system_ext.prop",
    "content": "#\n# system.prop for generic sdk\n#\n\nrild.libpath=/vendor/lib/libreference-ril.so\n"
  },
  {
    "path": "target/board/generic_x86_64/BoardConfig.mk",
    "content": "# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# x86_64 emulator specific definitions\nTARGET_CPU_ABI := x86_64\nTARGET_ARCH := x86_64\nTARGET_ARCH_VARIANT := x86_64\n\nTARGET_2ND_CPU_ABI := x86\nTARGET_2ND_ARCH := x86\nTARGET_2ND_ARCH_VARIANT := x86_64\n\n# Include 64-bit mediaserver to support 64-bit only devices\nTARGET_DYNAMIC_64_32_MEDIASERVER := true\n# Include 64-bit drmserver to support 64-bit only devices\nTARGET_DYNAMIC_64_32_DRMSERVER := true\n\ninclude build/make/target/board/BoardConfigGsiCommon.mk\n"
  },
  {
    "path": "target/board/generic_x86_64/README.txt",
    "content": "The \"generic_x86_64\" product defines a non-hardware-specific x86_64 target\nwithout a bootloader.\n\nIt is also the target to build the generic kernel image (GKI).\n\nIt is not a product \"base class\"; no other products inherit\nfrom it or use it in any way.\n"
  },
  {
    "path": "target/board/generic_x86_64/device.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n"
  },
  {
    "path": "target/board/generic_x86_64/system_ext.prop",
    "content": "#\n# system.prop for generic sdk\n#\n\nrild.libpath=/vendor/lib64/libreference-ril.so\n"
  },
  {
    "path": "target/board/generic_x86_64_arm64/BoardConfig.mk",
    "content": "# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_CPU_ABI := x86_64\nTARGET_ARCH := x86_64\nTARGET_ARCH_VARIANT := x86_64\n\nTARGET_2ND_CPU_ABI := x86\nTARGET_2ND_ARCH := x86\nTARGET_2ND_ARCH_VARIANT := x86_64\n\nTARGET_NATIVE_BRIDGE_ARCH := arm64\nTARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv8-a\nTARGET_NATIVE_BRIDGE_CPU_VARIANT := generic\nTARGET_NATIVE_BRIDGE_ABI := arm64-v8a\n\nTARGET_NATIVE_BRIDGE_2ND_ARCH := arm\nTARGET_NATIVE_BRIDGE_2ND_ARCH_VARIANT := armv7-a-neon\nTARGET_NATIVE_BRIDGE_2ND_CPU_VARIANT := generic\nTARGET_NATIVE_BRIDGE_2ND_ABI := armeabi-v7a armeabi\n\nBUILD_BROKEN_DUP_RULES := true\n\nTARGET_PRELINK_MODULE := false\n\ninclude build/make/target/board/BoardConfigMainlineCommon.mk\n\n# the settings differ from BoardConfigMainlineCommon.mk\nBOARD_USES_SYSTEM_OTHER_ODEX :=\n\n# Resize to 4G to accommodate ASAN and CTS\nBOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296\n"
  },
  {
    "path": "target/board/generic_x86_64_arm64/README.txt",
    "content": "The \"generic_x86_64\" product defines a non-hardware-specific IA target\nwithout a kernel or bootloader.\n\nIt can be used to build the entire user-level system, and\nwill work with the IA version of the emulator,\n\nIt is not a product \"base class\"; no other products inherit\nfrom it or use it in any way.\n\nThird party arm64 to x86_64 translator has to be installed as well\n"
  },
  {
    "path": "target/board/generic_x86_64_arm64/device.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n"
  },
  {
    "path": "target/board/generic_x86_64_arm64/system_ext.prop",
    "content": "#\n# system.prop for generic sdk\n#\n\nrild.libpath=/vendor/lib64/libreference-ril.so\n"
  },
  {
    "path": "target/board/generic_x86_arm/BoardConfig.mk",
    "content": "# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_CPU_ABI := x86\nTARGET_ARCH := x86\nTARGET_ARCH_VARIANT := x86\n\nTARGET_NATIVE_BRIDGE_ARCH := arm\nTARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv7-a-neon\nTARGET_NATIVE_BRIDGE_CPU_VARIANT := generic\nTARGET_NATIVE_BRIDGE_ABI := armeabi-v7a armeabi\n\nBUILD_BROKEN_DUP_RULES := true\n\n#\n# The inclusion order below is important.\n# The settings in latter makefiles overwrite those in the former.\n#\ninclude build/make/target/board/BoardConfigMainlineCommon.mk\n\n# the settings differ from BoardConfigMainlineCommon.mk\nBOARD_USES_SYSTEM_OTHER_ODEX :=\n\n# Resize to 4G to accomodate ASAN and CTS\nBOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296\n"
  },
  {
    "path": "target/board/generic_x86_arm/README.txt",
    "content": "The \"generic_x86_arm\" product defines a non-hardware-specific IA target\nwithout a kernel or bootloader.\n\nIt can be used to build the entire user-level system, and\nwill work with the IA version of the emulator,\n\nIt is not a product \"base class\"; no other products inherit\nfrom it or use it in any way.\n\nThird party arm to x86 translator has to be installed as well\n"
  },
  {
    "path": "target/board/generic_x86_arm/device.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n"
  },
  {
    "path": "target/board/generic_x86_arm/system_ext.prop",
    "content": "#\n# system.prop for generic sdk\n#\n\nrild.libpath=/vendor/lib/libreference-ril.so\n"
  },
  {
    "path": "target/board/go_defaults.prop",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n"
  },
  {
    "path": "target/board/go_defaults_512.prop",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# 512MB specific properties.\n\n# lmkd can kill more now.\nro.lmk.medium=700\n\n# madvise random in ART to reduce page cache thrashing.\ndalvik.vm.madvise-random=true\n"
  },
  {
    "path": "target/board/go_defaults_common.prop",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Sets Android Go recommended default values for propreties.\n\n# Set lowram options\nro.lmk.critical_upgrade=true\nro.lmk.upgrade_pressure=40\nro.lmk.downgrade_pressure=60\nro.lmk.kill_heaviest_task=false\n\n# set threshold to filter unused apps\npm.dexopt.downgrade_after_inactive_days=10\n\n# set the compiler filter for shared apks to quicken.\n# Rationale: speed has a lot of dex code expansion, it uses more ram and space\n# compared to quicken. Using quicken for shared APKs on Go devices may save RAM.\n# Note that this is a trade-off: here we trade clean pages for dirty pages,\n# extra cpu and battery. That's because the quicken files will be jit-ed in all\n# the processes that load of shared apk and the code cache is not shared.\n# Some notable apps that will be affected by this are gms and chrome.\n# b/65591595.\npm.dexopt.shared=quicken\n\n# Default heap sizes. Allow up to 256m for large heaps to make sure a single app\n# doesn't take all of the RAM.\ndalvik.vm.heapgrowthlimit=128m\ndalvik.vm.heapsize=256m\n"
  },
  {
    "path": "target/board/gsi_arm64/BoardConfig.mk",
    "content": "# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\ninclude build/make/target/board/BoardConfigGsiCommon.mk\n\nTARGET_ARCH := arm64\nTARGET_ARCH_VARIANT := armv8-a\nTARGET_CPU_ABI := arm64-v8a\nTARGET_CPU_ABI2 :=\nTARGET_CPU_VARIANT := generic\n\nTARGET_2ND_ARCH := arm\nTARGET_2ND_ARCH_VARIANT := armv8-a\nTARGET_2ND_CPU_ABI := armeabi-v7a\nTARGET_2ND_CPU_ABI2 := armeabi\nTARGET_2ND_CPU_VARIANT := generic\n\n# Include 64-bit mediaserver to support 64-bit only devices\nTARGET_DYNAMIC_64_32_MEDIASERVER := true\n# Include 64-bit drmserver to support 64-bit only devices\nTARGET_DYNAMIC_64_32_DRMSERVER := true\n\n# TODO(b/111434759, b/111287060) SoC specific hacks\nBOARD_ROOT_EXTRA_SYMLINKS += /vendor/lib/dsp:/dsp\nBOARD_ROOT_EXTRA_SYMLINKS += /mnt/vendor/persist:/persist\nBOARD_ROOT_EXTRA_SYMLINKS += /vendor/firmware_mnt:/firmware\n\n# TODO(b/36764215): remove this setting when the generic system image\n# no longer has QCOM-specific directories under /.\nBOARD_SEPOLICY_DIRS += build/make/target/board/generic_arm64/sepolicy\n"
  },
  {
    "path": "target/board/gsi_system_ext.prop",
    "content": "# GSI always generate dex pre-opt in system image\nro.cp_system_other_odex=0\n\n# GSI always disables adb authentication\nro.adb.secure=0\n\n# GSI disables non-AOSP nnapi extensions on product partition\nro.nnapi.extensions.deny_on_product=true\n\n# TODO(b/120679683): disable RescueParty before all problem apps solved\npersist.sys.disable_rescue=true\n\n# TODO(b/78105955): disable privapp_permissions checking before the bug solved\nro.control_privapp_permissions=disable\n"
  },
  {
    "path": "target/board/gsi_system_ext_user.prop",
    "content": "# GSI always generate dex pre-opt in system image\nro.cp_system_other_odex=0\n\n# GSI disables non-AOSP nnapi extensions on product partition\nro.nnapi.extensions.deny_on_product=true\n\n# TODO(b/120679683): disable RescueParty before all problem apps solved\npersist.sys.disable_rescue=true\n\n# TODO(b/78105955): disable privapp_permissions checking before the bug solved\nro.control_privapp_permissions=disable\n"
  },
  {
    "path": "target/board/linux_bionic/BoardConfig.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This \"device\" is only intended to be used for host Bionic build targets, so\n# (device) target architectures are irrelevant. However, the build system isn't\n# prepared to handle no target architectures at all, so pick something\n# arbitrarily.\nTARGET_ARCH := arm\nTARGET_ARCH_VARIANT := armv7-a-neon\nTARGET_CPU_VARIANT := generic\nTARGET_CPU_ABI := armeabi-v7a\nTARGET_CPU_ABI2 := armeabi\n\nHOST_CROSS_OS := linux_bionic\nHOST_CROSS_ARCH := x86_64\nHOST_CROSS_2ND_ARCH :=\n"
  },
  {
    "path": "target/board/linux_bionic/README.md",
    "content": "This \"device\" is suitable for Soong-only builds to create Bionic binaries for\nLinux hosts:\n\n```\nbuild/soong/soong_ui.bash --make-mode --soong-only TARGET_PRODUCT=linux_bionic ...\n```\n"
  },
  {
    "path": "target/board/mainline_arm64/BoardConfig.mk",
    "content": "# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_ARCH := arm64\nTARGET_ARCH_VARIANT := armv8-a\nTARGET_CPU_VARIANT := generic\nTARGET_CPU_ABI := arm64-v8a\n\nTARGET_2ND_ARCH := arm\nTARGET_2ND_ARCH_VARIANT := armv8-a\nTARGET_2ND_CPU_ABI := armeabi-v7a\nTARGET_2ND_CPU_ABI2 := armeabi\nTARGET_2ND_CPU_VARIANT := generic\n\ninclude build/make/target/board/BoardConfigMainlineCommon.mk\n\n# TODO(b/143732851): Remove this after replacing /persit with\n# /mnt/vendor/persist\nBOARD_ROOT_EXTRA_SYMLINKS += /mnt/vendor/persist:/persist\nBOARD_SEPOLICY_DIRS += build/make/target/board/mainline_arm64/sepolicy\n\nTARGET_NO_KERNEL := true\n\n# Build generic A/B format system-only OTA.\nAB_OTA_UPDATER := true\nAB_OTA_PARTITIONS := system\n\nBOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4\nBOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4\nBOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4\n"
  },
  {
    "path": "target/board/mainline_arm64/bluetooth/bdroid_buildcfg.h",
    "content": "/*\n *\n *  Copyright (c) 2013, The Linux Foundation. All rights reserved.\n *  Not a Contribution, Apache license notifications and license are retained\n *  for attribution purposes only.\n *\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _BDROID_BUILDCFG_H\n#define _BDROID_BUILDCFG_H\n\n// VSC spec support\n#define BLE_VND_INCLUDED TRUE\n\n#endif\n"
  },
  {
    "path": "target/board/mainline_arm64/sepolicy/OWNERS",
    "content": "include platform/system/sepolicy:/OWNERS\n"
  },
  {
    "path": "target/board/mainline_arm64/sepolicy/file.te",
    "content": "# TODO(b/143732851): remove this file when the mainline system image\n# no longer need these SoC specific directory\ntype persist_file, file_type;\n"
  },
  {
    "path": "target/board/mainline_arm64/sepolicy/file_contexts",
    "content": "# TODO(b/143732851): remove this file when the mainline system image\n# no longer need these SoC specific directory\n\n# /persist\n/persist(/.*)?          u:object_r:persist_file:s0\n"
  },
  {
    "path": "target/board/mainline_sdk/BoardConfig.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Ensure all trunk-stable flags are available.\ninclude build/make/target/product/build_variables.mk\n\nTARGET_ARCH_SUITE := mainline_sdk\n\nHOST_CROSS_OS := linux_bionic\nHOST_CROSS_ARCH := x86_64\nHOST_CROSS_2ND_ARCH :=\n"
  },
  {
    "path": "target/board/mainline_sdk/README.md",
    "content": "This device is suitable for a soong-only build that builds for all the architectures\nneeded for mainline modules sdk prebuilts.\n"
  },
  {
    "path": "target/board/mainline_x86/BoardConfig.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_ARCH := x86\nTARGET_ARCH_VARIANT := x86\nTARGET_CPU_ABI := x86\n\ninclude build/make/target/board/BoardConfigMainlineCommon.mk\n\nTARGET_NO_KERNEL := true\n\n# Build generic A/B format system-only OTA.\nAB_OTA_UPDATER := true\nAB_OTA_PARTITIONS := system\n\nBOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4\nBOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4\nBOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4\n"
  },
  {
    "path": "target/board/mainline_x86_64/BoardConfig.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_ARCH := x86_64\nTARGET_ARCH_VARIANT := x86_64\nTARGET_CPU_ABI := x86_64\n\nTARGET_2ND_ARCH := x86\nTARGET_2ND_ARCH_VARIANT := x86_64\nTARGET_2ND_CPU_ABI := x86\n\ninclude build/make/target/board/BoardConfigMainlineCommon.mk\n\nTARGET_NO_KERNEL := true\n\n# Build generic A/B format system-only OTA.\nAB_OTA_UPDATER := true\nAB_OTA_PARTITIONS := system\n\nBOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4\nBOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4\nBOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4\n"
  },
  {
    "path": "target/board/mainline_x86_arm/BoardConfig.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_ARCH := x86\nTARGET_ARCH_VARIANT := x86\nTARGET_CPU_ABI := x86\n\nTARGET_NATIVE_BRIDGE_ARCH := arm\nTARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv7-a-neon\nTARGET_NATIVE_BRIDGE_CPU_VARIANT := generic\nTARGET_NATIVE_BRIDGE_ABI := armeabi-v7a armeabi\n\ninclude build/make/target/board/BoardConfigMainlineCommon.mk\n\nTARGET_NO_KERNEL := true\n\n# Build generic A/B format system-only OTA.\nAB_OTA_UPDATER := true\nAB_OTA_PARTITIONS := system\n\nBOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4\nBOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4\nBOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4\n"
  },
  {
    "path": "target/board/module_arm/BoardConfig.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_ARCH := arm\nTARGET_ARCH_VARIANT := armv7-a-neon\nTARGET_CPU_VARIANT := generic\nTARGET_CPU_ABI := armeabi-v7a\nTARGET_CPU_ABI2 := armeabi\n"
  },
  {
    "path": "target/board/module_arm/README.md",
    "content": "This device is suitable for an unbundled module targeted specifically to an arm\ndevice.\n"
  },
  {
    "path": "target/board/module_arm64/BoardConfig.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_ARCH := arm64\nTARGET_ARCH_VARIANT := armv8-a\nTARGET_CPU_VARIANT := generic\nTARGET_CPU_ABI := arm64-v8a\n\nTARGET_2ND_ARCH := arm\nTARGET_2ND_ARCH_VARIANT := armv8-a\nTARGET_2ND_CPU_ABI := armeabi-v7a\nTARGET_2ND_CPU_ABI2 := armeabi\nTARGET_2ND_CPU_VARIANT := generic\n"
  },
  {
    "path": "target/board/module_arm64/README.md",
    "content": "This device is suitable for an unbundled module targeted specifically to an\narm64 device. 32 bit binaries built with this product will not be suitable for a\n32-bit arm device.\n"
  },
  {
    "path": "target/board/module_arm64only/BoardConfig.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_ARCH := arm64\nTARGET_ARCH_VARIANT := armv8-a\nTARGET_CPU_VARIANT := generic\nTARGET_CPU_ABI := arm64-v8a\n"
  },
  {
    "path": "target/board/module_arm64only/README.md",
    "content": "This device is suitable for an unbundled module targeted specifically to an\narm64 device. 32 bit binaries will not be built.\n"
  },
  {
    "path": "target/board/module_riscv64/BoardConfig.mk",
    "content": "# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_ARCH := riscv64\nTARGET_ARCH_VARIANT :=\nTARGET_CPU_VARIANT := generic\nTARGET_CPU_ABI := riscv64\n\n# Temporary hack while prebuilt modules are missing riscv64.\nALLOW_MISSING_DEPENDENCIES := true\n"
  },
  {
    "path": "target/board/module_riscv64/README.md",
    "content": "This device is suitable for an unbundled module targeted specifically to a\nriscv64 device. This is a 64-bit only device (no 32-bit support).\n"
  },
  {
    "path": "target/board/module_x86/BoardConfig.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_CPU_ABI := x86\nTARGET_ARCH := x86\nTARGET_ARCH_VARIANT := x86\n"
  },
  {
    "path": "target/board/module_x86/README.md",
    "content": "This device is suitable for an unbundled module targeted specifically to an\nx86 device.\n"
  },
  {
    "path": "target/board/module_x86_64/BoardConfig.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_CPU_ABI := x86_64\nTARGET_ARCH := x86_64\nTARGET_ARCH_VARIANT := x86_64\n\nTARGET_2ND_CPU_ABI := x86\nTARGET_2ND_ARCH := x86\nTARGET_2ND_ARCH_VARIANT := x86_64\n"
  },
  {
    "path": "target/board/module_x86_64/README.md",
    "content": "This device is suitable for an unbundled module targeted specifically to an\nx86_64 device. 32 bit binaries built with this product will not be suitable for\na 32-bit x86 device.\n"
  },
  {
    "path": "target/board/module_x86_64only/BoardConfig.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_CPU_ABI := x86_64\nTARGET_ARCH := x86_64\nTARGET_ARCH_VARIANT := x86_64\n"
  },
  {
    "path": "target/board/module_x86_64only/README.md",
    "content": "This device is suitable for an unbundled module targeted specifically to an\nx86_64 device. 32 bit binaries will not be built.\n"
  },
  {
    "path": "target/board/ndk/BoardConfig.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nTARGET_ARCH_SUITE := ndk\n"
  },
  {
    "path": "target/board/ndk/README.md",
    "content": "This device is suitable for a soong-only build that builds for all the architectures\nneeded for the ndk.\n"
  },
  {
    "path": "target/product/AndroidProducts.mk",
    "content": "#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# This file should set PRODUCT_MAKEFILES to a list of product makefiles\n# to expose to the build system.  LOCAL_DIR will already be set to\n# the directory containing this file.\n# PRODUCT_MAKEFILES is set up in AndroidProducts.mks.\n# Format of PRODUCT_MAKEFILES:\n# <product_name>:<path_to_the_product_makefile>\n# If the <product_name> is the same as the base file name (without dir\n# and the .mk suffix) of the product makefile, \"<product_name>:\" can be\n# omitted.\n#\n# This file may not rely on the value of any variable other than\n# LOCAL_DIR; do not use any conditionals, and do not look up the\n# value of any variable that isn't set in this file or in a file that\n# it includes.\n#\n\n# Unbundled apps will be built with the most generic product config.\nifneq ($(TARGET_BUILD_APPS),)\nPRODUCT_MAKEFILES := \\\n    $(LOCAL_DIR)/aosp_arm64.mk \\\n    $(LOCAL_DIR)/aosp_arm64_fullmte.mk \\\n    $(LOCAL_DIR)/aosp_arm64_plus_armv7.mk \\\n    $(LOCAL_DIR)/aosp_arm.mk \\\n    $(LOCAL_DIR)/aosp_riscv64.mk \\\n    $(LOCAL_DIR)/aosp_x86_64.mk \\\n    $(LOCAL_DIR)/aosp_x86.mk \\\n    $(LOCAL_DIR)/full.mk \\\n    $(LOCAL_DIR)/full_x86.mk \\\n\nelse\nPRODUCT_MAKEFILES := \\\n    $(LOCAL_DIR)/aosp_64bitonly_x86_64.mk \\\n    $(LOCAL_DIR)/aosp_arm64.mk \\\n    $(LOCAL_DIR)/aosp_arm64_fullmte.mk \\\n    $(LOCAL_DIR)/aosp_arm64_plus_armv7.mk \\\n    $(LOCAL_DIR)/aosp_arm.mk \\\n    $(LOCAL_DIR)/aosp_riscv64.mk \\\n    $(LOCAL_DIR)/aosp_x86_64.mk \\\n    $(LOCAL_DIR)/aosp_x86_arm.mk \\\n    $(LOCAL_DIR)/aosp_x86.mk \\\n    $(LOCAL_DIR)/full.mk \\\n    $(LOCAL_DIR)/full_x86.mk \\\n    $(LOCAL_DIR)/generic.mk \\\n    $(LOCAL_DIR)/generic_system_arm64.mk \\\n    $(LOCAL_DIR)/generic_system_x86.mk \\\n    $(LOCAL_DIR)/generic_system_x86_64.mk \\\n    $(LOCAL_DIR)/generic_system_x86_arm.mk \\\n    $(LOCAL_DIR)/generic_x86.mk \\\n    $(LOCAL_DIR)/mainline_system_arm64.mk \\\n    $(LOCAL_DIR)/mainline_system_x86.mk \\\n    $(LOCAL_DIR)/mainline_system_x86_64.mk \\\n    $(LOCAL_DIR)/mainline_system_x86_arm.mk \\\n    $(LOCAL_DIR)/ndk.mk \\\n    $(LOCAL_DIR)/sdk.mk \\\n    $(LOCAL_DIR)/sdk_with_runtime_apis.mk \\\n\nendif\n\nPRODUCT_MAKEFILES += \\\n    $(LOCAL_DIR)/linux_bionic.mk \\\n    $(LOCAL_DIR)/mainline_sdk.mk \\\n    $(LOCAL_DIR)/module_arm.mk \\\n    $(LOCAL_DIR)/module_arm64.mk \\\n    $(LOCAL_DIR)/module_arm64only.mk \\\n    $(LOCAL_DIR)/module_riscv64.mk \\\n    $(LOCAL_DIR)/module_x86.mk \\\n    $(LOCAL_DIR)/module_x86_64.mk \\\n    $(LOCAL_DIR)/module_x86_64only.mk \\\n\nCOMMON_LUNCH_CHOICES := \\\n    aosp_arm64-trunk_staging-eng \\\n    aosp_arm-trunk_staging-eng \\\n    aosp_x86_64-trunk_staging-eng \\\n    aosp_x86-trunk_staging-eng \\\n"
  },
  {
    "path": "target/product/OWNERS",
    "content": "per-file runtime_libart.mk = mast@google.com, ngeoffray@google.com, rpl@google.com, vmarko@google.com\n\n# GSI\nper-file gsi_release.mk = file:/target/product/gsi/OWNERS\nper-file developer_gsi_keys.mk = file:/target/product/gsi/OWNERS\n\n# Android Go\nper-file go_defaults.mk = gkaiser@google.com, kushg@google.com, rajekumar@google.com\nper-file go_defaults_512.mk = gkaiser@google.com, kushg@google.com, rajekumar@google.com\nper-file go_defaults_common.mk = gkaiser@google.com, kushg@google.com, rajekumar@google.com\n\n# Translation\nper-file languages_default.mk = aapple@google.com\n"
  },
  {
    "path": "target/product/angle_default.mk",
    "content": "#\n# Copyright 2023 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# To enable ANGLE as the default system GLES drivers, add\n# $(call inherit-product, $(SRC_TARGET_DIR)/product/angle_default.mk) to the Makefile.\n\nPRODUCT_SYSTEM_PROPERTIES += \\\n    persist.graphics.egl=angle\n"
  },
  {
    "path": "target/product/aosp_64bitonly_x86_64.mk",
    "content": "#\n# Copyright 2020 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_USE_DYNAMIC_PARTITIONS := true\n\n# The system image of aosp_x86_64_app-userdebug is a GSI for the devices with:\n# - x86 64 bits user space\n# - 64 bits binder interface\n# - system-as-root\n# - VNDK enforcement\n# - compatible property override enabled\n\n# This is a build configuration for a full-featured build of the\n# Open-Source part of the tree. It's geared toward a US-centric\n# build quite specifically for the emulator, and might not be\n# entirely appropriate to inherit from for on-device configurations.\n\n# GSI for system/product & support 64-bit apps only\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/mainline_system.mk)\n\n# Enable mainline checking for excat this product name\nifeq (aosp_64bitonly_x86_64,$(TARGET_PRODUCT))\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed\nendif\n\n#\n# All components inherited here go to system_ext image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)\n\n#\n# All components inherited here go to product image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)\n\n#\n# All components inherited here go to vendor image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk)\n\n#\n# Special settings for GSI releasing\n#\nifeq (aosp_64bitonly_x86_64,$(TARGET_PRODUCT))\n# Build modules from source if this has not been pre-configured\nMODULE_BUILD_FROM_SOURCE ?= true\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)\nendif\n\nPRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \\\n    root/init.zygote64.rc\n\n# This build configuration supports 64-bit apps only\nPRODUCT_NAME := aosp_64bitonly_x86_64\nPRODUCT_DEVICE := generic_64bitonly_x86_64\nPRODUCT_BRAND := Android\nPRODUCT_MODEL := AOSP on x86_64 App\n"
  },
  {
    "path": "target/product/aosp_arm.mk",
    "content": "#\n# Copyright 2017 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_USE_DYNAMIC_PARTITIONS := true\n\n# The system image of aosp_arm-userdebug is a GSI for the devices with:\n# - ARM 32 bits user space\n# - 64 bits binder interface\n# - system-as-root\n# - VNDK enforcement\n# - compatible property override enabled\n\n#\n# All components inherited here go to system image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)\n\n# Enable mainline checking for excat this product name\nifeq (aosp_arm,$(TARGET_PRODUCT))\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed\nendif\n\nPRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \\\n\n#\n# All components inherited here go to system_ext image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)\n\n#\n# All components inherited here go to product image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)\n\n#\n# All components inherited here go to vendor image\n#\n$(call inherit-product-if-exists, build/make/target/product/ramdisk_stub.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk)\n\n#\n# Special settings for GSI releasing\n#\nifeq (aosp_arm,$(TARGET_PRODUCT))\n# Build modules from source if this has not been pre-configured\nMODULE_BUILD_FROM_SOURCE ?= true\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)\n\nPRODUCT_SOONG_DEFINED_SYSTEM_IMAGE := aosp_system_image\nUSE_SOONG_DEFINED_SYSTEM_IMAGE := true\nPRODUCT_USE_SOONG_NOTICE_XML := true\n\nendif\n\nPRODUCT_NAME := aosp_arm\nPRODUCT_DEVICE := generic\nPRODUCT_BRAND := Android\nPRODUCT_MODEL := AOSP on ARM32\n"
  },
  {
    "path": "target/product/aosp_arm64.mk",
    "content": "#\n# Copyright (C) 2017 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# The system image of aosp_arm64-userdebug is a GSI for the devices with:\n# - ARM 64 bits user space\n# - 64 bits binder interface\n# - system-as-root\n# - VNDK enforcement\n# - compatible property override enabled\n\n# This is a build configuration for a full-featured build of the\n# Open-Source part of the tree. It's geared toward a US-centric\n# build quite specifically for the emulator, and might not be\n# entirely appropriate to inherit from for on-device configurations.\n\n#\n# All components inherited here go to system image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)\n\n# Enable mainline checking for excat this product name\nifeq (aosp_arm64,$(TARGET_PRODUCT))\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed\nendif\n\n#\n# All components inherited here go to system_ext image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)\n\n# pKVM\n$(call inherit-product-if-exists, packages/modules/Virtualization/apex/product_packages.mk)\n\n#\n# All components inherited here go to product image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)\n\n#\n# All components inherited here go to vendor or vendor_boot image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk)\nAB_OTA_UPDATER := true\nAB_OTA_PARTITIONS ?= system\n\n#\n# Special settings for GSI releasing\n#\nifeq (aosp_arm64,$(TARGET_PRODUCT))\n# Build modules from source if this has not been pre-configured\nMODULE_BUILD_FROM_SOURCE ?= true\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)\n\nPRODUCT_SOONG_DEFINED_SYSTEM_IMAGE := aosp_system_image\nUSE_SOONG_DEFINED_SYSTEM_IMAGE := true\nPRODUCT_USE_SOONG_NOTICE_XML := true\n\nendif\n\nPRODUCT_NAME := aosp_arm64\nPRODUCT_DEVICE := generic_arm64\nPRODUCT_BRAND := Android\nPRODUCT_MODEL := AOSP on ARM64\n\nPRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true\n"
  },
  {
    "path": "target/product/aosp_arm64_fullmte.mk",
    "content": "# Copyright (C) 2023 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\ninclude $(SRC_TARGET_DIR)/product/fullmte.mk\n\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_arm64.mk)\n\n# Build modules from source if this has not been pre-configured\nMODULE_BUILD_FROM_SOURCE ?= true\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)\n\nPRODUCT_NAME := aosp_arm64_fullmte\n"
  },
  {
    "path": "target/product/aosp_arm64_plus_armv7.mk",
    "content": "#\n# Copyright (C) 2025 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# aosp_arm64_plus_armv7 is for building CTS and other test suites with\n# arm64 as the primary architecture and armv7 arm32 as the secondary\n# architecture.\n\n#\n# All components inherited here go to system image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)\n\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed\n\n#\n# All components inherited here go to system_ext image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)\n\n# pKVM\n$(call inherit-product-if-exists, packages/modules/Virtualization/apex/product_packages.mk)\n\n#\n# All components inherited here go to product image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)\n\n#\n# All components inherited here go to vendor or vendor_boot image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk)\nAB_OTA_UPDATER := true\nAB_OTA_PARTITIONS ?= system\n\n#\n# Special settings for GSI releasing\n#\n# Build modules from source if this has not been pre-configured\nMODULE_BUILD_FROM_SOURCE ?= true\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)\n\n\nPRODUCT_NAME := aosp_arm64_plus_armv7\nPRODUCT_DEVICE := generic_arm64_plus_armv7\nPRODUCT_BRAND := Android\nPRODUCT_MODEL := AOSP on ARM64 with ARMV7\n\nPRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true\n"
  },
  {
    "path": "target/product/aosp_base.mk",
    "content": "#\n# Copyright 2013 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/full_base.mk)\n"
  },
  {
    "path": "target/product/aosp_base_telephony.mk",
    "content": "#\n# Copyright 2013 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/full_base_telephony.mk)\n\nPRODUCT_PACKAGES += \\\n    messaging\n"
  },
  {
    "path": "target/product/aosp_product.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Includes all AOSP product packages\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_product.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_product.mk)\n\n# Default AOSP sounds\n$(call inherit-product-if-exists, frameworks/base/data/sounds/AllAudio.mk)\n\n# Additional settings used in all AOSP builds\nPRODUCT_PRODUCT_PROPERTIES += \\\n    ro.config.ringtone?=Ring_Synth_04.ogg \\\n    ro.config.notification_sound?=pixiedust.ogg \\\n    ro.com.android.dataroaming?=true \\\n\n# More AOSP packages\nPRODUCT_PACKAGES += \\\n    initial-package-stopped-states-aosp.xml \\\n    messaging \\\n    PhotoTable \\\n    preinstalled-packages-platform-aosp-product.xml \\\n    ThemePicker \\\n\n# Telephony:\n#   Provide a APN configuration to GSI product\nPRODUCT_COPY_FILES += \\\n    device/sample/etc/apns-full-conf.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/apns-conf.xml\n"
  },
  {
    "path": "target/product/aosp_riscv64.mk",
    "content": "#\n# Copyright 2022 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_USE_DYNAMIC_PARTITIONS := true\n\n# The system image of aosp_riscv64-userdebug is a GSI for the devices with:\n# - riscv64 user space\n# - 64 bits binder interface\n# - system-as-root\n# - VNDK enforcement\n# - compatible property override enabled\n\n# This is a build configuration for a full-featured build of the\n# Open-Source part of the tree. It's geared toward a US-centric\n# build quite specifically for the emulator, and might not be\n# entirely appropriate to inherit from for on-device configurations.\n\n# GSI for system/product & support 64-bit apps only\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/mainline_system.mk)\n\n#\n# All components inherited here go to system_ext image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)\n\n#\n# All components inherited here go to product image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)\n\n#\n# All components inherited here go to vendor image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_riscv64/device.mk)\n\n#\n# Special settings for GSI releasing\n#\nifeq (aosp_riscv64,$(TARGET_PRODUCT))\n# Build modules from source if this has not been pre-configured\nMODULE_BUILD_FROM_SOURCE ?= true\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)\nendif\n\nPRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \\\n    root/init.zygote64.rc\n\n# TODO(b/206676167): This property can be removed when renderscript is removed.\n# Prevents framework from attempting to load renderscript libraries, which are\n# not supported on this architecture.\nPRODUCT_SYSTEM_PROPERTIES += \\\n    config.disable_renderscript=1 \\\n\n# This build configuration supports 64-bit apps only\nPRODUCT_NAME := aosp_riscv64\nPRODUCT_DEVICE := generic_riscv64\nPRODUCT_BRAND := Android\nPRODUCT_MODEL := AOSP on Riscv64\n"
  },
  {
    "path": "target/product/aosp_x86.mk",
    "content": "#\n# Copyright 2013 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_USE_DYNAMIC_PARTITIONS := true\n\n# The system image of aosp_x86-userdebug is a GSI for the devices with:\n# - x86 32 bits user space\n# - 64 bits binder interface\n# - system-as-root\n# - VNDK enforcement\n# - compatible property override enabled\n\n#\n# All components inherited here go to system image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)\n\n# Enable mainline checking for excat this product name\nifeq (aosp_x86,$(TARGET_PRODUCT))\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed\nendif\n\n#\n# All components inherited here go to system_ext image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)\n\n#\n# All components inherited here go to product image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)\n\n#\n# All components inherited here go to vendor image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk)\n\n\n#\n# Special settings for GSI releasing\n#\nifeq (aosp_x86,$(TARGET_PRODUCT))\n# Build modules from source if this has not been pre-configured\nMODULE_BUILD_FROM_SOURCE ?= true\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)\n\nPRODUCT_SOONG_DEFINED_SYSTEM_IMAGE := aosp_system_image\nUSE_SOONG_DEFINED_SYSTEM_IMAGE := true\nPRODUCT_USE_SOONG_NOTICE_XML := true\n\nendif\n\nPRODUCT_NAME := aosp_x86\nPRODUCT_DEVICE := generic_x86\nPRODUCT_BRAND := Android\nPRODUCT_MODEL := AOSP on x86\n"
  },
  {
    "path": "target/product/aosp_x86_64.mk",
    "content": "#\n# Copyright 2013 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_USE_DYNAMIC_PARTITIONS := true\n\n# The system image of aosp_x86_64-userdebug is a GSI for the devices with:\n# - x86 64 bits user space\n# - 64 bits binder interface\n# - system-as-root\n# - VNDK enforcement\n# - compatible property override enabled\n\n# This is a build configuration for a full-featured build of the\n# Open-Source part of the tree. It's geared toward a US-centric\n# build quite specifically for the emulator, and might not be\n# entirely appropriate to inherit from for on-device configurations.\n\n#\n# All components inherited here go to system image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)\n\n# Enable mainline checking for excat this product name\nifeq (aosp_x86_64,$(TARGET_PRODUCT))\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed\nendif\n\n#\n# All components inherited here go to system_ext image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)\n\n# pKVM\n$(call inherit-product, packages/modules/Virtualization/apex/product_packages.mk)\n\n#\n# All components inherited here go to product image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)\n\n#\n# All components inherited here go to vendor image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk)\nAB_OTA_UPDATER := true\nAB_OTA_PARTITIONS ?= system\n\n#\n# Special settings for GSI releasing\n#\nifeq (aosp_x86_64,$(TARGET_PRODUCT))\n# Build modules from source if this has not been pre-configured\nMODULE_BUILD_FROM_SOURCE ?= true\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)\n\nPRODUCT_SOONG_DEFINED_SYSTEM_IMAGE := aosp_system_image\nUSE_SOONG_DEFINED_SYSTEM_IMAGE := true\nPRODUCT_USE_SOONG_NOTICE_XML := true\n\nendif\n\nPRODUCT_NAME := aosp_x86_64\nPRODUCT_DEVICE := generic_x86_64\nPRODUCT_BRAND := Android\nPRODUCT_MODEL := AOSP on x86_64\n\nPRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true\n"
  },
  {
    "path": "target/product/aosp_x86_arm.mk",
    "content": "#\n# Copyright 2016 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_USE_DYNAMIC_PARTITIONS := true\n\n#\n# All components inherited here go to system image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)\n\n# Enable mainline checking\nifeq (aosp_x86_arm,$(TARGET_PRODUCT))\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed\nendif\n\n# TODO (b/138382074): remove following setting after enable product/system_ext\nPRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \\\n    system/product/% \\\n    system/system_ext/%\n\n#\n# All components inherited here go to system_ext image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)\n\n#\n# All components inherited here go to product image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)\n\n#\n# All components inherited here go to vendor image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_arm/device.mk)\n\n\nPRODUCT_NAME := aosp_x86_arm\nPRODUCT_DEVICE := generic_x86_arm\nPRODUCT_BRAND := Android\nPRODUCT_MODEL := AOSP on IA Emulator\n"
  },
  {
    "path": "target/product/app_function_extensions.mk",
    "content": "#\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# The app function sidecar extensions\n\n# /system_ext packages\nPRODUCT_PACKAGES += \\\n    com.android.extensions.appfunctions \\\n    appfunctions.extension.xml\n"
  },
  {
    "path": "target/product/base.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile is suitable to inherit by products that don't need to be split\n# up by partition.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/base_system.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/base_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/base_vendor.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/base_product.mk)\n"
  },
  {
    "path": "target/product/base_product.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Base modules and settings for the product partition.\nPRODUCT_PACKAGES += \\\n    build_flag_product \\\n    fs_config_dirs_product \\\n    fs_config_files_product \\\n    group_product \\\n    ModuleMetadata \\\n    passwd_product \\\n    product_compatibility_matrix.xml \\\n    product_manifest.xml \\\n    selinux_policy_product \\\n    product-build.prop \\\n\n# Packages included only for eng or userdebug builds, previously debug tagged\nPRODUCT_PACKAGES_DEBUG += \\\n    adb_keys \\\n"
  },
  {
    "path": "target/product/base_system.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Base modules and settings for the system partition.\nPRODUCT_PACKAGES += \\\n    abx \\\n    aconfigd-system \\\n    adbd_system_api \\\n    aflags \\\n    am \\\n    android.hidl.base-V1.0-java \\\n    android.hidl.manager-V1.0-java \\\n    android.system.suspend-service \\\n    android.test.base \\\n    android.test.mock \\\n    android.test.runner \\\n    apexd \\\n    appops \\\n    app_process \\\n    appwidget \\\n    atrace \\\n    audioserver \\\n    BackupRestoreConfirmation \\\n    bcc \\\n    blank_screen \\\n    blkid \\\n    bmgr \\\n    bootanimation \\\n    bootstat \\\n    boringssl_self_test \\\n    bpfloader \\\n    bu \\\n    bugreport \\\n    bugreportz \\\n    build_flag_system \\\n    cgroups.json \\\n    charger \\\n    cmd \\\n    com.android.adbd \\\n    com.android.adservices \\\n    com.android.appsearch \\\n    com.android.bt \\\n    com.android.configinfrastructure \\\n    com.android.conscrypt \\\n    com.android.devicelock \\\n    com.android.extservices \\\n    com.android.healthfitness \\\n    com.android.i18n \\\n    com.android.ipsec \\\n    com.android.location.provider \\\n    com.android.media \\\n    com.android.media.swcodec \\\n    com.android.mediaprovider \\\n    com.android.ondevicepersonalization \\\n    com.android.os.statsd \\\n    com.android.permission \\\n    com.android.resolv \\\n    com.android.rkpd \\\n    com.android.neuralnetworks \\\n    com.android.scheduling \\\n    com.android.sdkext \\\n    com.android.tethering \\\n    $(RELEASE_PACKAGE_TZDATA_MODULE) \\\n    com.android.uwb \\\n    com.android.virt \\\n    com.android.wifi \\\n    ContactsProvider \\\n    content \\\n    CtsShimPrebuilt \\\n    CtsShimPrivPrebuilt \\\n    debuggerd\\\n    device_config \\\n    dmctl \\\n    dnsmasq \\\n    dmesgd \\\n    DownloadProvider \\\n    dpm \\\n    dump.erofs \\\n    dumpstate \\\n    dumpsys \\\n    E2eeContactKeysProvider \\\n    e2fsck \\\n    enhanced-confirmation.xml \\\n    ExtShared \\\n    flags_health_check \\\n    framework-graphics \\\n    framework-location \\\n    framework-minus-apex \\\n    framework-minus-apex-install-dependencies \\\n    framework-sysconfig.xml \\\n    fsck.erofs \\\n    fsck_msdos \\\n    fsverity-release-cert-der \\\n    fs_config_files_system \\\n    fs_config_dirs_system \\\n    gpu_counter_producer \\\n    group_system \\\n    gsid \\\n    gsi_tool \\\n    heapprofd \\\n    heapprofd_client \\\n    gatekeeperd \\\n    gpuservice \\\n    hid \\\n    idmap2 \\\n    idmap2d \\\n    ime \\\n    ims-common \\\n    incident \\\n    incidentd \\\n    incident_helper \\\n    incident-helper-cmd \\\n    init.environ.rc \\\n    init_system \\\n    initial-package-stopped-states.xml \\\n    input \\\n    installd \\\n    IntentResolver \\\n    ip \\\n    iptables \\\n    javax.obex \\\n    kcmdlinectrl \\\n    keystore2 \\\n    credstore \\\n    ld.mc \\\n    libaaudio \\\n    libalarm_jni \\\n    libamidi \\\n    libandroid \\\n    libandroidfw \\\n    libandroid_runtime \\\n    libandroid_servers \\\n    libartpalette-system \\\n    libaudioeffect_jni \\\n    libbinder \\\n    libbinder_ndk \\\n    libbinder_rpc_unstable \\\n    libc.bootstrap \\\n    libcamera2ndk \\\n    libcutils \\\n    libdl.bootstrap \\\n    libdl_android.bootstrap \\\n    libdrmframework \\\n    libdrmframework_jni \\\n    libEGL \\\n    libETC1 \\\n    libfdtrack \\\n    libFFTEm \\\n    libfilterfw \\\n    libgatekeeper \\\n    libGLESv1_CM \\\n    libGLESv2 \\\n    libGLESv3 \\\n    libgui \\\n    libhardware \\\n    libhardware_legacy \\\n    libincident \\\n    libinput \\\n    libinputflinger \\\n    libiprouteutil \\\n    libjnigraphics \\\n    libjpeg \\\n    liblog \\\n    libm.bootstrap \\\n    libmedia \\\n    libmedia_jni \\\n    libmediandk \\\n    libmonkey_jni \\\n    libmtp \\\n    libnetd_client \\\n    libnetlink \\\n    libnetutils \\\n    libneuralnetworks_packageinfo \\\n    libOpenMAXAL \\\n    libOpenSLES \\\n    libpdfium \\\n    libpower \\\n    libpowermanager \\\n    libradio_metadata \\\n    librtp_jni \\\n    libsensorservice \\\n    libsfplugin_ccodec \\\n    libskia \\\n    libsonic \\\n    libsonivox \\\n    libsoundpool \\\n    libspeexresampler \\\n    libsqlite \\\n    libstagefright \\\n    libstagefright_foundation \\\n    libstagefright_omx \\\n    libstdc++ \\\n    libsysutils \\\n    libui \\\n    libusbhost \\\n    libutils \\\n    libvintf_jni \\\n    libvulkan \\\n    libwilhelm \\\n    linker \\\n    llkd \\\n    llndk_libs \\\n    lmkd \\\n    LocalTransport \\\n    locksettings \\\n    logcat \\\n    logd \\\n    lpdump \\\n    lshal \\\n    mdnsd \\\n    mediacodec.policy \\\n    mediaextractor \\\n    mediametrics \\\n    media_profiles_V1_0.dtd \\\n    MediaProviderLegacy \\\n    mediaserver \\\n    mke2fs \\\n    mkfs.erofs \\\n    monkey \\\n    misctrl \\\n    mtectrl \\\n    ndc \\\n    netd \\\n    NetworkStack \\\n    odsign \\\n    org.apache.http.legacy \\\n    otacerts \\\n    PackageInstaller \\\n    package-shareduid-allowlist.xml \\\n    passwd_system \\\n    pbtombstone \\\n    perfetto \\\n    perfetto-extras \\\n    ping \\\n    ping6 \\\n    pintool \\\n    platform.xml \\\n    pm \\\n    prefetch \\\n    preinstalled-packages-asl-files.xml \\\n    preinstalled-packages-platform.xml \\\n    preinstalled-packages-strict-signature.xml \\\n    privapp-permissions-platform.xml \\\n    prng_seeder \\\n    recovery-persist \\\n    resize2fs \\\n    rss_hwm_reset \\\n    run-as \\\n    sanitizer.libraries.txt \\\n    schedtest \\\n    screencap \\\n    sdcard \\\n    secdiscard \\\n    SecureElement \\\n    selinux_policy_system \\\n    sensorservice \\\n    service \\\n    servicemanager \\\n    services \\\n    settings \\\n    SettingsProvider \\\n    sfdo \\\n    sgdisk \\\n    Shell \\\n    shell_and_utilities_system \\\n    sm \\\n    snapuserd \\\n    storaged \\\n    surfaceflinger \\\n    svc \\\n    system-build.prop \\\n    task_profiles.json \\\n    tc \\\n    telecom \\\n    telephony-common \\\n    tombstoned \\\n    traced \\\n    traced_probes \\\n    tradeinmode \\\n    tune2fs \\\n    uiautomator \\\n    uinput \\\n    uncrypt \\\n    usbd \\\n    vdc \\\n    vintf \\\n    voip-common \\\n    vold \\\n    watchdogd \\\n    wificond \\\n    wifi.rc \\\n    wm \\\n\n# When we release crashrecovery module\nifeq ($(RELEASE_CRASHRECOVERY_MODULE),true)\n  PRODUCT_PACKAGES += \\\n        com.android.crashrecovery \\\n\nelse\n  PRODUCT_PACKAGES += \\\n    framework-platformcrashrecovery \\\n\nendif\n\n# When we release ondeviceintelligence in neuralnetworks module\nifneq ($(RELEASE_ONDEVICE_INTELLIGENCE_MODULE),true)\n  PRODUCT_PACKAGES += \\\n        framework-ondeviceintelligence-platform\n\nendif\n\n\n# When we release uprobestats module\nifeq ($(RELEASE_UPROBESTATS_MODULE),true)\n    PRODUCT_PACKAGES += \\\n        com.android.uprobestats \\\n\nelse\n    PRODUCT_PACKAGES += \\\n        uprobestats \\\n        libuprobestats_client \\\n\nendif\n\n# These packages are not used on Android TV\nifneq ($(PRODUCT_IS_ATV),true)\n  PRODUCT_PACKAGES += \\\n      $(RELEASE_PACKAGE_SOUND_PICKER) \\\n\nendif\n\n# Product does not support Dynamic System Update\nifneq ($(PRODUCT_NO_DYNAMIC_SYSTEM_UPDATE),true)\n    PRODUCT_PACKAGES += \\\n        DynamicSystemInstallationService \\\n\nendif\n\n# Check if the build supports NFC apex or not\nifeq ($(RELEASE_PACKAGE_NFC_STACK),NfcNci)\n    PRODUCT_PACKAGES += \\\n        framework-nfc \\\n        NfcNci\nelse\n    PRODUCT_PACKAGES += \\\n        com.android.nfcservices\nendif\n\n# Check if the build supports Profiling module\nifeq ($(RELEASE_PACKAGE_PROFILING_MODULE),true)\n    PRODUCT_PACKAGES += \\\n       com.android.profiling\nendif\n\nifeq ($(RELEASE_USE_WEBVIEW_BOOTSTRAP_MODULE),true)\n    PRODUCT_PACKAGES += \\\n        com.android.webview.bootstrap\nendif\n\n# Only add the jar when it is not in the Tethering module. Otherwise,\n# it will be added via com.android.tethering\nifneq ($(RELEASE_MOVE_VCN_TO_MAINLINE),true)\n    PRODUCT_PACKAGES += \\\n        framework-connectivity-b\nendif\n\nifneq (,$(RELEASE_RANGING_STACK))\n    PRODUCT_PACKAGES += \\\n        com.android.ranging\nendif\n\nifeq ($(RELEASE_MEMORY_MANAGEMENT_DAEMON),true)\n  PRODUCT_PACKAGES += \\\n        mm_daemon\nelse\n  PRODUCT_PACKAGES += \\\n        init-mmd-prop.rc\nendif\n\n# VINTF data for system image\nPRODUCT_PACKAGES += \\\n    system_manifest.xml \\\n    system_compatibility_matrix.xml \\\n\n# hwservicemanager is now installed on system_ext, but apexes might be using\n# old libraries that are expecting it to be installed on system. This allows\n# those apexes to continue working. The symlink can be removed once we are sure\n# there are no devices using hwservicemanager (when Android V launching devices\n# are no longer supported for dessert upgrades).\nPRODUCT_PACKAGES += \\\n    hwservicemanager_compat_symlink_module \\\n\nPRODUCT_PACKAGES_ARM64 := libclang_rt.hwasan \\\n libclang_rt.hwasan.bootstrap \\\n libc_hwasan \\\n\n# Jacoco agent JARS to be built and installed, if any.\nifeq ($(EMMA_INSTRUMENT),true)\n  ifneq ($(EMMA_INSTRUMENT_STATIC),true)\n    # For instrumented build, if Jacoco is not being included statically\n    # in instrumented packages then include Jacoco classes in the product\n    # packages.\n    PRODUCT_PACKAGES += jacocoagent\n    ifneq ($(EMMA_INSTRUMENT_FRAMEWORK),true)\n      # For instrumented build, if Jacoco is not being included statically\n      # in instrumented packages and has not already been included in the\n      # bootclasspath via ART_APEX_JARS then include Jacoco classes into the\n      # bootclasspath.\n      PRODUCT_BOOT_JARS += jacocoagent\n    endif # EMMA_INSTRUMENT_FRAMEWORK\n  endif # EMMA_INSTRUMENT_STATIC\nendif # EMMA_INSTRUMENT\n\nifeq (,$(DISABLE_WALLPAPER_BACKUP))\n  PRODUCT_PACKAGES += \\\n    WallpaperBackup\nendif\n\nPRODUCT_PACKAGES += \\\n    libEGL_angle \\\n    libGLESv1_CM_angle \\\n    libGLESv2_angle\n\n# For testing purposes\nifeq ($(FORCE_AUDIO_SILENT), true)\n    PRODUCT_SYSTEM_PROPERTIES += ro.audio.silent=1\nendif\n\n# Host tools to install\nPRODUCT_HOST_PACKAGES += \\\n    BugReport \\\n    adb \\\n    adevice \\\n    atest \\\n    bcc \\\n    bit \\\n    dump.erofs \\\n    e2fsck \\\n    fastboot \\\n    flags_health_check \\\n    fsck.erofs \\\n    icu-data_host_i18n_apex \\\n    tzdata_icu_res_files_host_prebuilts \\\n    idmap2 \\\n    incident_report \\\n    ld.mc \\\n    lpdump \\\n    mke2fs \\\n    mkfs.erofs \\\n    pbtombstone \\\n    resize2fs \\\n    sgdisk \\\n    sqlite3 \\\n    tinyplay \\\n    tune2fs \\\n    unwind_info \\\n    unwind_reg_info \\\n    unwind_symbols \\\n    tzdata_host \\\n    tzdata_host_tzdata_apex \\\n    tzlookup.xml_host_tzdata_apex \\\n    tz_version_host \\\n    tz_version_host_tzdata_apex \\\n\n# For art-tools, if the dependencies have changed, please sync them to art/Android.bp as well.\nPRODUCT_HOST_PACKAGES += \\\n    ahat \\\n    dexdump \\\n    hprof-conv\n# A subset of the tools are disabled when HOST_PREFER_32_BIT is defined as make reports that\n# they are not supported on host (b/129323791). This is likely due to art_apex disabling host\n# APEX builds when HOST_PREFER_32_BIT is set (b/120617876).\nifneq ($(HOST_PREFER_32_BIT),true)\nPRODUCT_HOST_PACKAGES += \\\n    dexlist \\\n    oatdump\nendif\n\n\nPRODUCT_PACKAGES += init.usb.rc init.usb.configfs.rc\n\nPRODUCT_PACKAGES += etc_hosts\n\nPRODUCT_PACKAGES += init.zygote32.rc\nPRODUCT_VENDOR_PROPERTIES += ro.zygote?=zygote32\n\nPRODUCT_SYSTEM_PROPERTIES += debug.atrace.tags.enableflags=0\nPRODUCT_SYSTEM_PROPERTIES += persist.traced.enable=1\nPRODUCT_SYSTEM_PROPERTIES += ro.surface_flinger.game_default_frame_rate_override=60\n\n# Include kernel configs.\nPRODUCT_PACKAGES += \\\n    approved-ogki-builds.xml \\\n    kernel-lifetimes.xml\n\n# Packages included only for eng or userdebug builds, previously debug tagged\nPRODUCT_PACKAGES_DEBUG := \\\n    adevice_fingerprint \\\n    arping \\\n    dmuserd \\\n    evemu-record \\\n    idlcli \\\n    init-debug.rc \\\n    iotop \\\n    iperf3 \\\n    iw \\\n    layertracegenerator \\\n    libclang_rt.ubsan_standalone \\\n    logpersist.start \\\n    logtagd.rc \\\n    ot-cli-ftd \\\n    ot-ctl \\\n    overlay_remounter \\\n    procrank \\\n    profcollectd \\\n    profcollectctl \\\n    record_binder \\\n    servicedispatcher \\\n    showmap \\\n    snapshotctl \\\n    sqlite3 \\\n    ss \\\n    start_with_lockagent \\\n    strace \\\n    su \\\n    sanitizer-status \\\n    tracepath \\\n    tracepath6 \\\n    traceroute6 \\\n    unwind_info \\\n    unwind_reg_info \\\n    unwind_symbols \\\n\n# The set of packages whose code can be loaded by the system server.\nPRODUCT_SYSTEM_SERVER_APPS += \\\n    SettingsProvider \\\n\nifeq (,$(DISABLE_WALLPAPER_BACKUP))\n  PRODUCT_SYSTEM_SERVER_APPS += \\\n    WallpaperBackup\nendif\n\nPRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE := \\\n    libdumpcoverage\n\nPRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\\\n    frameworks/base/config/preloaded-classes:system/etc/preloaded-classes)\n\n# Enable dirty image object binning to reduce dirty pages in the image.\nPRODUCT_PACKAGES += dirty-image-objects\n\n# Enable go/perfetto-persistent-tracing for eng builds\nifneq (,$(filter eng, $(TARGET_BUILD_VARIANT)))\n    PRODUCT_PRODUCT_PROPERTIES += persist.debug.perfetto.persistent_sysui_tracing_for_bugreport=1\nendif\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/runtime_libart.mk)\n\n# Ensure all trunk-stable flags are available.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/build_variables.mk)\n\n# Use \"image\" APEXes always.\n$(call inherit-product,$(SRC_TARGET_DIR)/product/updatable_apex.mk)\n\n$(call soong_config_set, bionic, large_system_property_node, $(RELEASE_LARGE_SYSTEM_PROPERTY_NODE))\n$(call soong_config_set, Aconfig, read_from_new_storage, $(RELEASE_READ_FROM_NEW_STORAGE))\n$(call soong_config_set, SettingsLib, legacy_avatar_picker_app_enabled, $(if $(RELEASE_AVATAR_PICKER_APP),,true))\n"
  },
  {
    "path": "target/product/base_system_ext.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Base modules and settings for the system_ext partition.\nPRODUCT_PACKAGES += \\\n    build_flag_system_ext \\\n    fs_config_dirs_system_ext \\\n    fs_config_files_system_ext \\\n    group_system_ext \\\n    passwd_system_ext \\\n    SatelliteClient \\\n    selinux_policy_system_ext \\\n    system_ext_manifest.xml \\\n    system_ext-build.prop \\\n\n# Base modules when shipping api level is less than or equal to 34\nPRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \\\n    hwservicemanager \\\n    android.hidl.allocator@1.0-service \\\n    android.hidl.memory@1.0-impl \\\n\n# AppFunction Extensions\nifneq (,$(RELEASE_APPFUNCTION_SIDECAR))\n    $(call inherit-product, $(SRC_TARGET_DIR)/product/app_function_extensions.mk)\nendif"
  },
  {
    "path": "target/product/base_vendor.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Base modules and settings for recovery.\nPRODUCT_PACKAGES += \\\n    adbd.recovery \\\n    build_flag_vendor \\\n    cgroups.recovery.json \\\n    charger.recovery \\\n    init_second_stage.recovery \\\n    ld.config.recovery.txt \\\n    linker.recovery \\\n    otacerts.recovery \\\n    recovery \\\n    servicemanager.recovery \\\n    shell_and_utilities_recovery \\\n    watchdogd.recovery \\\n\nPRODUCT_VENDOR_PROPERTIES += \\\n    ro.recovery.usb.vid?=18D1 \\\n    ro.recovery.usb.adb.pid?=D001 \\\n    ro.recovery.usb.fastboot.pid?=4EE0 \\\n\n# These had been pulled in via init_second_stage.recovery, but may not be needed.\nPRODUCT_HOST_PACKAGES += \\\n    e2fsdroid \\\n    mke2fs \\\n    sload_f2fs \\\n    make_f2fs \\\n\nPRODUCT_HOST_PACKAGES += \\\n    icu-data_host_i18n_apex\n\n# Base modules and settings for the vendor partition.\nPRODUCT_PACKAGES += \\\n    com.android.hardware.cas \\\n    boringssl_self_test_vendor \\\n    dumpsys_vendor \\\n    fs_config_files_nonsystem \\\n    fs_config_dirs_nonsystem \\\n    gralloc.default \\\n    group_odm \\\n    group_vendor \\\n    init_vendor \\\n    libbundlewrapper \\\n    libclearkeycasplugin \\\n    libdownmix \\\n    libdrmclearkeyplugin \\\n    libdynproc \\\n    libeffectproxy \\\n    libeffects \\\n    libhapticgenerator \\\n    libldnhncr \\\n    libreference-ril \\\n    libreverbwrapper \\\n    libril \\\n    libvisualizer \\\n    passwd_odm \\\n    passwd_vendor \\\n    selinux_policy_nonsystem \\\n    selinux_policy_vendor \\\n    selinux_policy_odm \\\n    shell_and_utilities_vendor \\\n    odm-build.prop \\\n\n# libhealthloop BPF filter. This is in base_vendor.mk because libhealthloop must\n# be a static library and because the Android build system ignores 'required'\n# sections for static libraries.\nPRODUCT_PACKAGES += filterPowerSupplyEvents.o\n\n# Base modules when shipping api level is less than or equal to 34\nPRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \\\n     android.hidl.memory@1.0-impl.vendor \\\n\n# OMX not supported for 64bit_only builds\n# Only supported when SHIPPING_API_LEVEL is less than or equal to 33\nifneq ($(TARGET_SUPPORTS_OMX_SERVICE),false)\n    PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33 += \\\n        android.hardware.media.omx@1.0-service \\\n\nendif\n\n# Base modules when shipping api level is less than or equal to 33\nPRODUCT_PACKAGES_SHIPPING_API_LEVEL_33 += \\\n    android.hardware.cas@1.2-service \\\n\n# Base modules when shipping api level is less than or equal to 29\nPRODUCT_PACKAGES_SHIPPING_API_LEVEL_29 += \\\n    android.hardware.configstore@1.1-service \\\n    vndservice \\\n    vndservicemanager \\\n\n# VINTF data for vendor image\nPRODUCT_PACKAGES += \\\n    vendor_compatibility_matrix.xml \\\n\n# Base modules and settings for the debug ramdisk, which is then packed\n# into a boot-debug.img and a vendor_boot-debug.img.\nPRODUCT_PACKAGES += \\\n    adb_debug.prop \\\n    userdebug_plat_sepolicy.cil\n\n# On eng or userdebug builds, build in perf-setup-sh by default.\nifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))\nPRODUCT_PACKAGES += \\\n    perf-setup-sh\nendif\n"
  },
  {
    "path": "target/product/build_variables.mk",
    "content": "#\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This file contains the trunk-stable flags that should be exported to all\n# Android targets.\n\n# Control libbinder client caching\n$(call soong_config_set, libbinder, release_libbinder_client_cache, $(RELEASE_LIBBINDER_CLIENT_CACHE))\n\n# Control caching while adding service in libbinder cache\n$(call soong_config_set, libbinder, release_libbinder_addservice_cache, $(RELEASE_LIBBINDER_ADDSERVICE_CACHE))\n\n# Remove static list in libbinder cache\n$(call soong_config_set, libbinder, release_libbinder_remove_cache_static_list, $(RELEASE_LIBBINDER_REMOVE_CACHE_STATIC_LIST))\n\n# Use the configured release of sqlite\n$(call soong_config_set, libsqlite3, release_package_libsqlite3, $(RELEASE_PACKAGE_LIBSQLITE3))\n\n# Use the configured MessageQueue implementation\n$(call soong_config_set, messagequeue, release_package_messagequeue_implementation, $(RELEASE_PACKAGE_MESSAGEQUEUE_IMPLEMENTATION))\n\n# Use the configured version of WebView\n$(call soong_config_set, webview, release_package_webview_version, $(RELEASE_PACKAGE_WEBVIEW_VERSION))\n\n# Use the configured version of Cronet\n$(call soong_config_set,cronet,enable_cronet_tot,$(RELEASE_ENABLE_TOT_CRONET))\n"
  },
  {
    "path": "target/product/cfi-common.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a set of common components to enable CFI for (across\n# compatible product configs)\nPRODUCT_CFI_INCLUDE_PATHS :=  \\\n    device/generic/goldfish/wifi/wpa_supplicant_8_lib \\\n    device/google/cuttlefish/guest/libs/wpa_supplicant_8_lib \\\n    external/tinyxml2 \\\n    external/wpa_supplicant_8 \\\n    frameworks/av/camera \\\n    frameworks/av/media \\\n    frameworks/av/services \\\n    frameworks/minikin \\\n    hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib \\\n    hardware/synaptics/wlan/synadhd/wpa_supplicant_8_lib \\\n    hardware/interfaces/nfc \\\n    hardware/qcom/wlan/qcwcn/wpa_supplicant_8_lib \\\n    hardware/qcom/wlan/legacy/qcwcn/wpa_supplicant_8_lib \\\n    hardware/qcom/wlan/wcn6740/qcwcn/wpa_supplicant_8_lib \\\n    hardware/interfaces/keymaster \\\n    hardware/interfaces/security \\\n    packages/modules/Bluetooth/system \\\n    system/chre \\\n    system/core/libnetutils \\\n    system/libziparchive \\\n    system/gatekeeper \\\n    system/keymaster \\\n    system/nfc \\\n    system/security \\\n"
  },
  {
    "path": "target/product/core_64_bit.mk",
    "content": "#\n# Copyright (C) 2014 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Inherit from this product for devices that support 64-bit apps using:\n# $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)\n# The inheritance for this must come before the inheritance chain that leads\n# to core_minimal.mk\n\n# For now this will allow 64-bit apps, but still compile all apps with JNI\n# for 32-bit only.\n\n# Copy the 64-bit primary, 32-bit secondary zygote startup script\nPRODUCT_PACKAGES += init.zygote64.rc init.zygote64_32.rc\n\n# Set the zygote property to select the 64-bit primary, 32-bit secondary script\n# This line must be parsed before the one in core_minimal.mk\nifeq ($(ZYGOTE_FORCE_64),true)\nPRODUCT_VENDOR_PROPERTIES += ro.zygote=zygote64\nelse\nPRODUCT_VENDOR_PROPERTIES += ro.zygote=zygote64_32\nendif\n\nTARGET_SUPPORTS_32_BIT_APPS := true\nTARGET_SUPPORTS_64_BIT_APPS := true\n"
  },
  {
    "path": "target/product/core_64_bit_only.mk",
    "content": "#\n# Copyright (C) 2014 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Inherit from this product for devices that support only 64-bit apps using:\n# $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk)\n# The inheritance for this must come before the inheritance chain that leads\n# to core_minimal.mk.\n\n# Copy the 64-bit zygote startup script\nPRODUCT_PACKAGES += init.zygote64.rc\n\n# Set the zygote property to select the 64-bit script.\n# This line must be parsed before the one in core_minimal.mk\nPRODUCT_VENDOR_PROPERTIES += ro.zygote=zygote64\n# A 64-bit-only platform does not have dex2oat32, so make sure dex2oat64 is\n# used for dexopt.\nPRODUCT_VENDOR_PROPERTIES += dalvik.vm.dex2oat64.enabled=true\n\nTARGET_SUPPORTS_32_BIT_APPS := false\nTARGET_SUPPORTS_64_BIT_APPS := true\nTARGET_SUPPORTS_OMX_SERVICE := false\n"
  },
  {
    "path": "target/product/core_minimal.mk",
    "content": "#\n# Copyright (C) 2013 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This product is the base of a generic media-capable device, which\n# means most android products, but excludes wearables.\n#\n# Note: Do not add any contents directly to this file. Choose either\n# media_<x> depending on partition also consider base_<x>.mk or\n# handheld_<x>.mk.\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/media_system.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/media_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/media_vendor.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk)\n\nPRODUCT_BRAND := generic\nPRODUCT_DEVICE := generic\nPRODUCT_NAME := core\n"
  },
  {
    "path": "target/product/core_no_zygote.mk",
    "content": "#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Inherit from this product for devices that do not include a zygote using:\n# $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk)\n# The inheritance for this must come before the inheritance chain that leads\n# to core_minimal.mk.\n\n# Copy the no-zygote startup script\nPRODUCT_COPY_FILES += system/core/rootdir/init.no_zygote.rc:system/etc/init/hw/init.no_zygote.rc\n\n# Set the zygote property to select the no-zygote script.\n# This line must be parsed before the one in core_minimal.mk\nPRODUCT_VENDOR_PROPERTIES += ro.zygote=no_zygote\n\nTARGET_SUPPORTS_32_BIT_APPS := false\nTARGET_SUPPORTS_64_BIT_APPS := false\n"
  },
  {
    "path": "target/product/default_art_config.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This file contains product config for the ART module that is common for\n# platform and unbundled builds.\n\nifeq ($(ART_APEX_JARS),)\n  $(error ART_APEX_JARS is empty; cannot initialize PRODUCT_BOOT_JARS variable)\nendif\n\n# Order of the jars on BOOTCLASSPATH follows:\n# 1. ART APEX jars\n# 2. System jars\n# 3. System_ext jars\n# 4. Non-updatable APEX jars\n# 5. Updatable APEX jars\n#\n# ART APEX jars (1) are defined in ART_APEX_JARS. System and system_ext boot jars are defined below\n# in PRODUCT_BOOT_JARS. All other non-art APEX boot jars are part of the PRODUCT_APEX_BOOT_JARS.\n#\n# The actual runtime ordering matching above is determined by derive_classpath service at runtime.\n# See packages/modules/SdkExtensions/README.md for more details.\n\n# The order of PRODUCT_BOOT_JARS matters for runtime class lookup performance.\nPRODUCT_BOOT_JARS := \\\n    $(ART_APEX_JARS)\n\n# List of jars to be included in the ART boot image for testing.\n# DO NOT reorder this list. The order must match the one described above.\n# Note: We use the host variant of \"core-icu4j\" and \"conscrypt\" for testing.\nPRODUCT_TEST_ONLY_ART_BOOT_IMAGE_JARS := \\\n    $(ART_APEX_JARS) \\\n    platform:core-icu4j-host \\\n    platform:conscrypt-host \\\n\n# /system and /system_ext boot jars.\nPRODUCT_BOOT_JARS += \\\n    framework-minus-apex \\\n    framework-graphics \\\n    framework-location \\\n    ext \\\n    telephony-common \\\n    voip-common \\\n    ims-common\n\n# APEX boot jars. Keep the list sorted by module names and then library names.\n# Note: If the existing apex introduces the new jar, also add it to\n# PRODUCT_APEX_BOOT_JARS_FOR_SOURCE_BUILD_ONLY below.\n# Note: core-icu4j is moved back to PRODUCT_BOOT_JARS in product_config.mk at a later stage.\n# Note: For modules available in Q, DO NOT add new entries here.\nPRODUCT_APEX_BOOT_JARS := \\\n    com.android.adservices:framework-adservices \\\n    com.android.adservices:framework-sdksandbox \\\n    com.android.appsearch:framework-appsearch \\\n    com.android.bt:framework-bluetooth \\\n    com.android.configinfrastructure:framework-configinfrastructure \\\n    com.android.conscrypt:conscrypt \\\n    com.android.devicelock:framework-devicelock \\\n    com.android.healthfitness:framework-healthfitness \\\n    com.android.i18n:core-icu4j \\\n    com.android.ipsec:android.net.ipsec.ike \\\n    com.android.media:updatable-media \\\n    com.android.mediaprovider:framework-mediaprovider \\\n    com.android.mediaprovider:framework-pdf \\\n    com.android.mediaprovider:framework-pdf-v \\\n    com.android.mediaprovider:framework-photopicker \\\n    com.android.ondevicepersonalization:framework-ondevicepersonalization \\\n    com.android.os.statsd:framework-statsd \\\n    com.android.permission:framework-permission \\\n    com.android.permission:framework-permission-s \\\n    com.android.scheduling:framework-scheduling \\\n    com.android.sdkext:framework-sdkextensions \\\n    com.android.tethering:framework-connectivity \\\n    com.android.tethering:framework-connectivity-t \\\n    com.android.tethering:framework-tethering \\\n    com.android.uwb:framework-uwb \\\n    com.android.virt:framework-virtualization \\\n    com.android.wifi:framework-wifi \\\n\n# When crashrecovery module is ready use apex jar\n# else put the platform jar in system\nifeq ($(RELEASE_CRASHRECOVERY_MODULE),true)\n    PRODUCT_APEX_BOOT_JARS += \\\n        com.android.crashrecovery:framework-crashrecovery \\\n\nelse\n    PRODUCT_BOOT_JARS += \\\n        framework-platformcrashrecovery \\\n\nendif\n\n# When we release ondeviceintelligence in NeuralNetworks module\nifeq ($(RELEASE_ONDEVICE_INTELLIGENCE_MODULE),true)\n    PRODUCT_APEX_BOOT_JARS += \\\n    com.android.neuralnetworks:framework-ondeviceintelligence \\\n\nelse\n    PRODUCT_BOOT_JARS += \\\n        framework-ondeviceintelligence-platform \\\n\nendif\n\n# Check if the build supports NFC apex or not\nifeq ($(RELEASE_PACKAGE_NFC_STACK),NfcNci)\n    PRODUCT_BOOT_JARS += \\\n        framework-nfc\nelse\n    PRODUCT_APEX_BOOT_JARS += \\\n        com.android.nfcservices:framework-nfc\n    $(call soong_config_set,bootclasspath,nfc_apex_bootclasspath_fragment,true)\nendif\n\n# Check if build supports Profiling module.\nifeq ($(RELEASE_PACKAGE_PROFILING_MODULE),true)\n    PRODUCT_APEX_BOOT_JARS += \\\n        com.android.profiling:framework-profiling \\\n\nendif\n\nifneq (,$(RELEASE_RANGING_STACK))\n    PRODUCT_APEX_BOOT_JARS += \\\n        com.android.uwb:framework-ranging \\\n    $(call soong_config_set,bootclasspath,release_ranging_stack,true)\nendif\n\n# Check if VCN should be built into the tethering module or not\nifeq ($(RELEASE_MOVE_VCN_TO_MAINLINE),true)\n    PRODUCT_APEX_BOOT_JARS += \\\n        com.android.tethering:framework-connectivity-b \\\n\nelse\n    PRODUCT_BOOT_JARS += \\\n        framework-connectivity-b \\\n\nendif\n\n# List of system_server classpath jars delivered via apex.\n# Keep the list sorted by module names and then library names.\n# Note: For modules available in Q, DO NOT add new entries here.\nPRODUCT_APEX_SYSTEM_SERVER_JARS := \\\n    com.android.adservices:service-adservices \\\n    com.android.adservices:service-sdksandbox \\\n    com.android.appsearch:service-appsearch \\\n    com.android.art:service-art \\\n    com.android.configinfrastructure:service-configinfrastructure \\\n    com.android.healthfitness:service-healthfitness \\\n    com.android.media:service-media-s \\\n    com.android.ondevicepersonalization:service-ondevicepersonalization \\\n    com.android.permission:service-permission \\\n    com.android.rkpd:service-rkp \\\n\n# When we release crashrecovery module\nifeq ($(RELEASE_CRASHRECOVERY_MODULE),true)\n  PRODUCT_APEX_SYSTEM_SERVER_JARS += \\\n        com.android.crashrecovery:service-crashrecovery \\\n\nendif\n\n# When we release ondeviceintelligence in NeuralNetworks module\nifeq ($(RELEASE_ONDEVICE_INTELLIGENCE_MODULE),true)\n    PRODUCT_APEX_SYSTEM_SERVER_JARS += \\\n        com.android.neuralnetworks:service-ondeviceintelligence\n\nendif\n\nifeq ($(RELEASE_AVF_ENABLE_LLPVM_CHANGES),true)\n  PRODUCT_APEX_SYSTEM_SERVER_JARS += com.android.virt:service-virtualization\nendif\n\n# Use $(wildcard) to avoid referencing the profile in thin manifests that don't have the\n# art project.\nifneq (,$(wildcard art))\n  PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION += art/build/boot/boot-image-profile.txt\nendif\n\n# List of jars on the platform that system_server loads dynamically using separate classloaders.\n# Keep the list sorted library names.\nPRODUCT_STANDALONE_SYSTEM_SERVER_JARS := \\\n\n# List of jars delivered via apex that system_server loads dynamically using separate classloaders.\n# Keep the list sorted by module names and then library names.\n# Note: For modules available in Q, DO NOT add new entries here.\nPRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS := \\\n    com.android.bt:service-bluetooth \\\n    com.android.devicelock:service-devicelock \\\n    com.android.os.statsd:service-statsd \\\n    com.android.scheduling:service-scheduling \\\n    com.android.tethering:service-connectivity \\\n    com.android.uwb:service-uwb \\\n    com.android.wifi:service-wifi \\\n\n# Check if build supports Profiling module.\nifeq ($(RELEASE_PACKAGE_PROFILING_MODULE),true)\n    PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS += \\\n        com.android.profiling:service-profiling \\\n\nendif\n\nifneq (,$(RELEASE_RANGING_STACK))\n    PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS += \\\n        com.android.uwb:service-ranging\nendif\n\n# Overrides the (apex, jar) pairs above when determining the on-device location. The format is:\n# <old_apex>:<old_jar>:<new_apex>:<new_jar>\nPRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES := \\\n    platform:framework-minus-apex:platform:framework \\\n    platform:core-icu4j-host:com.android.i18n:core-icu4j \\\n    platform:conscrypt-host:com.android.conscrypt:conscrypt \\\n\n# Minimal configuration for running dex2oat (default argument values).\n# PRODUCT_USES_DEFAULT_ART_CONFIG must be true to enable boot image compilation.\nPRODUCT_USES_DEFAULT_ART_CONFIG := true\nPRODUCT_SYSTEM_PROPERTIES += \\\n    dalvik.vm.image-dex2oat-Xms=64m \\\n    dalvik.vm.image-dex2oat-Xmx=64m \\\n    dalvik.vm.dex2oat-Xms=64m \\\n    dalvik.vm.dex2oat-Xmx=512m \\\n\nPRODUCT_ENABLE_UFFD_GC := default\n"
  },
  {
    "path": "target/product/developer_gsi_keys.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Device makers who are willing to support booting the public Developer-GSI\n# in locked state can add the following line into a device.mk to inherit this\n# makefile. This file will then install the up-to-date GSI public keys into\n# the first-stage ramdisk to pass verified boot.\n#\n# In device/<company>/<board>/device.mk:\n#   $(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk)\n#\n# Currently, the developer GSI images can be downloaded from the following URL:\n#   https://developer.android.com/topic/generic-system-image/releases\n#\nPRODUCT_PACKAGES += \\\n    q-developer-gsi.avbpubkey \\\n    r-developer-gsi.avbpubkey \\\n    s-developer-gsi.avbpubkey \\\n"
  },
  {
    "path": "target/product/empty-preloaded-classes",
    "content": "# Empty preloaded-classes file for automated testing.\n"
  },
  {
    "path": "target/product/empty-profile",
    "content": "# Empty preloaded-classes file for automated testing.\n"
  },
  {
    "path": "target/product/emulated_storage.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_QUOTA_PROJID := 1\nPRODUCT_VENDOR_PROPERTIES += external_storage.projid.enabled=1\n\nPRODUCT_FS_CASEFOLD := 1\nPRODUCT_VENDOR_PROPERTIES += external_storage.casefold.enabled=1\n\nPRODUCT_VENDOR_PROPERTIES += external_storage.sdcardfs.enabled=0\n"
  },
  {
    "path": "target/product/full.manifest.xml",
    "content": "<manifest version=\"1.0\" type=\"device\" target-level=\"7\">\n</manifest>\n"
  },
  {
    "path": "target/product/full.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a build configuration for a full-featured build of the\n# Open-Source part of the tree. It's geared toward a US-centric\n# build quite specifically for the emulator, and might not be\n# entirely appropriate to inherit from for on-device configurations.\n\n$(call inherit-product-if-exists, build/make/target/product/ramdisk_stub.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/board/generic/device.mk)\n\nDEVICE_MANIFEST_FILE += build/make/target/product/full.manifest.xml\n\n# Enable dynamic partition size\nPRODUCT_USE_DYNAMIC_PARTITION_SIZE := true\n\n# Overrides\nPRODUCT_NAME := full\nPRODUCT_DEVICE := generic\nPRODUCT_BRAND := Android\nPRODUCT_MODEL := AOSP on ARM Emulator\n"
  },
  {
    "path": "target/product/full_base.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a build configuration for a full-featured build of the\n# Open-Source part of the tree. It's geared toward a US-centric\n# build of the emulator, but all those aspects can be overridden\n# in inherited configurations.\n\nPRODUCT_PACKAGES := \\\n    libfwdlockengine \\\n    WAPPushManager\n\nPRODUCT_PACKAGES += \\\n    LiveWallpapersPicker \\\n    PhotoTable \\\n    preinstalled-packages-platform-full-base.xml\n\n# Net:\n#   Vendors can use the platform-provided network configuration utilities (ip,\n#   iptable, etc.) to configure the Linux networking stack, but these utilities\n#   do not yet include a HIDL interface wrapper. This is a solution on\n#   Android O.\nPRODUCT_PACKAGES += \\\n    netutils-wrapper-1.0\n\n# Additional settings used in all AOSP builds\nPRODUCT_VENDOR_PROPERTIES := \\\n    ro.config.ringtone?=Ring_Synth_04.ogg \\\n    ro.config.notification_sound?=pixiedust.ogg\n\n# Put en_US first in the list, so make it default.\nPRODUCT_LOCALES := en_US\n\n# Get some sounds\n$(call inherit-product-if-exists, frameworks/base/data/sounds/AllAudio.mk)\n\n# Get a list of languages.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_full.mk)\n\n# Get everything else from the parent package\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_no_telephony.mk)\n\n# Add adb keys to debuggable AOSP builds (if they exist)\n$(call inherit-product-if-exists, vendor/google/security/adb/vendor_key.mk)\n"
  },
  {
    "path": "target/product/full_base_telephony.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a build configuration for a full-featured build of the\n# Open-Source part of the tree. It's geared toward a US-centric\n# build quite specifically for the emulator, and might not be\n# entirely appropriate to inherit from for on-device configurations.\n\nPRODUCT_VENDOR_PROPERTIES := \\\n    keyguard.no_require_sim?=true \\\n    ro.com.android.dataroaming?=true\n\nPRODUCT_COPY_FILES := \\\n    device/sample/etc/apns-full-conf.xml:system/etc/apns-conf.xml \\\n    frameworks/native/data/etc/handheld_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/handheld_core_hardware.xml\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony.mk)\n"
  },
  {
    "path": "target/product/full_x86.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a build configuration for a full-featured build of the\n# Open-Source part of the tree. It's geared toward a US-centric\n# build quite specifically for the emulator, and might not be\n# entirely appropriate to inherit from for on-device configurations.\n\n# If running on an emulator or some other device that has a LAN connection\n# that isn't a wifi connection. This will instruct init.rc to enable the\n# network connection so that you can use it with ADB\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk)\n\nDEVICE_MANIFEST_FILE += build/make/target/product/full.manifest.xml\n\nifdef NET_ETH0_STARTONBOOT\n  PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1\nendif\n\n# Enable dynamic partition size\nPRODUCT_USE_DYNAMIC_PARTITION_SIZE := true\n\n# Overrides\nPRODUCT_NAME := full_x86\nPRODUCT_DEVICE := generic_x86\nPRODUCT_BRAND := Android\nPRODUCT_MODEL := AOSP on IA Emulator\n"
  },
  {
    "path": "target/product/fullmte.mk",
    "content": "#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Enables more comprehensive detection of memory errors on hardware that\n# supports the ARM Memory Tagging Extension (MTE), by building the image with\n# MTE stack instrumentation and forcing MTE on in SYNC mode in all processes.\n# For more details, see:\n# https://source.android.com/docs/security/test/memory-safety/arm-mte\nifeq ($(filter memtag_heap,$(SANITIZE_TARGET)),)\n  SANITIZE_TARGET := $(strip $(SANITIZE_TARGET) memtag_heap memtag_stack memtag_globals)\n  SANITIZE_TARGET_DIAG := $(strip $(SANITIZE_TARGET_DIAG) memtag_heap)\nendif\nPRODUCT_PRODUCT_PROPERTIES += persist.arm64.memtag.default=sync\nPRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE := 131072\n"
  },
  {
    "path": "target/product/generic/Android.bp",
    "content": "generic_rootdirs = [\n    \"apex\",\n    \"bootstrap-apex\",\n    \"config\",\n    \"data\",\n    \"data_mirror\",\n    \"debug_ramdisk\",\n    \"dev\",\n    \"linkerconfig\",\n    \"metadata\",\n    \"mnt\",\n    \"odm\",\n    \"odm_dlkm\",\n    \"oem\",\n    \"postinstall\",\n    \"proc\",\n    \"second_stage_resources\",\n    \"storage\",\n    \"sys\",\n    \"system\",\n    \"system_dlkm\",\n    \"tmp\",\n    \"vendor\",\n    \"vendor_dlkm\",\n]\n\nandroid_rootdirs = [\n    \"system_ext\",\n    \"product\",\n]\n\ngeneric_symlinks = [\n    {\n        target: \"/system/bin/init\",\n        name: \"init\",\n    },\n    {\n        target: \"/system/etc\",\n        name: \"etc\",\n    },\n    {\n        target: \"/system/bin\",\n        name: \"bin\",\n    },\n    {\n        target: \"/vendor\",\n        name: \"system/vendor\",\n    },\n    {\n        target: \"/system_dlkm/lib/modules\",\n        name: \"system/lib/modules\",\n    },\n    {\n        target: \"/data/user_de/0/com.android.shell/files/bugreports\",\n        name: \"bugreports\",\n    },\n    {\n        target: \"/sys/kernel/debug\",\n        name: \"d\",\n    },\n    {\n        target: \"/storage/self/primary\",\n        name: \"sdcard\",\n    },\n    {\n        target: \"/product/etc/security/adb_keys\",\n        name: \"adb_keys\",\n    },\n    // For Treble Generic System Image (GSI), system-as-root GSI needs to work on both devices with\n    // and without /odm partition. Those symlinks are for devices without /odm partition. For\n    // devices with /odm partition, mount odm.img under /odm will hide those symlinks.\n    {\n        target: \"/vendor/odm/app\",\n        name: \"odm/app\",\n    },\n    {\n        target: \"/vendor/odm/bin\",\n        name: \"odm/bin\",\n    },\n    {\n        target: \"/vendor/odm/etc\",\n        name: \"odm/etc\",\n    },\n    {\n        target: \"/vendor/odm/firmware\",\n        name: \"odm/firmware\",\n    },\n    {\n        target: \"/vendor/odm/framework\",\n        name: \"odm/framework\",\n    },\n    {\n        target: \"/vendor/odm/lib\",\n        name: \"odm/lib\",\n    },\n    {\n        target: \"/vendor/odm/lib64\",\n        name: \"odm/lib64\",\n    },\n    {\n        target: \"/vendor/odm/overlay\",\n        name: \"odm/overlay\",\n    },\n    {\n        target: \"/vendor/odm/priv-app\",\n        name: \"odm/priv-app\",\n    },\n    {\n        target: \"/vendor/odm/usr\",\n        name: \"odm/usr\",\n    },\n]\n\nandroid_symlinks = [\n    {\n        target: \"/product\",\n        name: \"system/product\",\n    },\n    {\n        target: \"/system_ext\",\n        name: \"system/system_ext\",\n    },\n    {\n        target: \"/data/cache\",\n        name: \"cache\",\n    },\n    {\n        target: \"/odm/odm_dlkm/etc\",\n        name: \"odm_dlkm/etc\",\n    },\n    {\n        target: \"/vendor/vendor_dlkm/etc\",\n        name: \"vendor_dlkm/etc\",\n    },\n]\n\nextra_vendor_symlinks = [\n    // Some vendors still haven't cleaned up all device specific directories under root!\n    // TODO(b/111434759, b/111287060) SoC specific hacks\n    {\n        target: \"/vendor/lib/dsp\",\n        name: \"dsp\",\n    },\n    {\n        target: \"/mnt/vendor/persist\",\n        name: \"persist\",\n    },\n    {\n        target: \"/vendor/firmware_mnt\",\n        name: \"firmware\",\n    },\n]\n\nfilegroup {\n    name: \"generic_system_sign_key\",\n    srcs: [\":avb_testkey_rsa4096\"],\n}\n\nphony {\n    name: \"generic_system_fonts\",\n    required: [\n        \"AndroidClock.ttf\",\n        \"CarroisGothicSC-Regular.ttf\",\n        \"ComingSoon.ttf\",\n        \"CutiveMono.ttf\",\n        \"DancingScript-Regular.ttf\",\n        \"DroidSansMono.ttf\",\n        \"NotoColorEmoji.ttf\",\n        \"NotoColorEmojiFlags.ttf\",\n        \"NotoNaskhArabic-Bold.ttf\",\n        \"NotoNaskhArabic-Regular.ttf\",\n        \"NotoNaskhArabicUI-Bold.ttf\",\n        \"NotoNaskhArabicUI-Regular.ttf\",\n        \"NotoSansAdlam-VF.ttf\",\n        \"NotoSansAhom-Regular.otf\",\n        \"NotoSansAnatolianHieroglyphs-Regular.otf\",\n        \"NotoSansArmenian-VF.ttf\",\n        \"NotoSansAvestan-Regular.ttf\",\n        \"NotoSansBalinese-Regular.ttf\",\n        \"NotoSansBamum-Regular.ttf\",\n        \"NotoSansBassaVah-Regular.otf\",\n        \"NotoSansBatak-Regular.ttf\",\n        \"NotoSansBengali-VF.ttf\",\n        \"NotoSansBengaliUI-VF.ttf\",\n        \"NotoSansBhaiksuki-Regular.otf\",\n        \"NotoSansBrahmi-Regular.ttf\",\n        \"NotoSansBuginese-Regular.ttf\",\n        \"NotoSansBuhid-Regular.ttf\",\n        \"NotoSansCJK-Regular.ttc\",\n        \"NotoSansCanadianAboriginal-Regular.ttf\",\n        \"NotoSansCarian-Regular.ttf\",\n        \"NotoSansChakma-Regular.otf\",\n        \"NotoSansCham-Bold.ttf\",\n        \"NotoSansCham-Regular.ttf\",\n        \"NotoSansCherokee-Regular.ttf\",\n        \"NotoSansCoptic-Regular.ttf\",\n        \"NotoSansCuneiform-Regular.ttf\",\n        \"NotoSansCypriot-Regular.ttf\",\n        \"NotoSansDeseret-Regular.ttf\",\n        \"NotoSansDevanagari-VF.ttf\",\n        \"NotoSansDevanagariUI-VF.ttf\",\n        \"NotoSansEgyptianHieroglyphs-Regular.ttf\",\n        \"NotoSansElbasan-Regular.otf\",\n        \"NotoSansEthiopic-VF.ttf\",\n        \"NotoSansGeorgian-VF.ttf\",\n        \"NotoSansGlagolitic-Regular.ttf\",\n        \"NotoSansGothic-Regular.ttf\",\n        \"NotoSansGrantha-Regular.ttf\",\n        \"NotoSansGujarati-Bold.ttf\",\n        \"NotoSansGujarati-Regular.ttf\",\n        \"NotoSansGujaratiUI-Bold.ttf\",\n        \"NotoSansGujaratiUI-Regular.ttf\",\n        \"NotoSansGunjalaGondi-Regular.otf\",\n        \"NotoSansGurmukhi-VF.ttf\",\n        \"NotoSansGurmukhiUI-VF.ttf\",\n        \"NotoSansHanifiRohingya-Regular.otf\",\n        \"NotoSansHanunoo-Regular.ttf\",\n        \"NotoSansHatran-Regular.otf\",\n        \"NotoSansHebrew-Bold.ttf\",\n        \"NotoSansHebrew-Regular.ttf\",\n        \"NotoSansImperialAramaic-Regular.ttf\",\n        \"NotoSansInscriptionalPahlavi-Regular.ttf\",\n        \"NotoSansInscriptionalParthian-Regular.ttf\",\n        \"NotoSansJavanese-Regular.otf\",\n        \"NotoSansKaithi-Regular.ttf\",\n        \"NotoSansKannada-VF.ttf\",\n        \"NotoSansKannadaUI-VF.ttf\",\n        \"NotoSansKayahLi-Regular.ttf\",\n        \"NotoSansKharoshthi-Regular.ttf\",\n        \"NotoSansKhmer-VF.ttf\",\n        \"NotoSansKhmerUI-Bold.ttf\",\n        \"NotoSansKhmerUI-Regular.ttf\",\n        \"NotoSansKhojki-Regular.otf\",\n        \"NotoSansLao-Bold.ttf\",\n        \"NotoSansLao-Regular.ttf\",\n        \"NotoSansLaoUI-Bold.ttf\",\n        \"NotoSansLaoUI-Regular.ttf\",\n        \"NotoSansLepcha-Regular.ttf\",\n        \"NotoSansLimbu-Regular.ttf\",\n        \"NotoSansLinearA-Regular.otf\",\n        \"NotoSansLinearB-Regular.ttf\",\n        \"NotoSansLisu-Regular.ttf\",\n        \"NotoSansLycian-Regular.ttf\",\n        \"NotoSansLydian-Regular.ttf\",\n        \"NotoSansMalayalam-VF.ttf\",\n        \"NotoSansMalayalamUI-VF.ttf\",\n        \"NotoSansMandaic-Regular.ttf\",\n        \"NotoSansManichaean-Regular.otf\",\n        \"NotoSansMarchen-Regular.otf\",\n        \"NotoSansMasaramGondi-Regular.otf\",\n        \"NotoSansMedefaidrin-VF.ttf\",\n        \"NotoSansMeeteiMayek-Regular.ttf\",\n        \"NotoSansMeroitic-Regular.otf\",\n        \"NotoSansMiao-Regular.otf\",\n        \"NotoSansModi-Regular.ttf\",\n        \"NotoSansMongolian-Regular.ttf\",\n        \"NotoSansMro-Regular.otf\",\n        \"NotoSansMultani-Regular.otf\",\n        \"NotoSansMyanmar-Bold.otf\",\n        \"NotoSansMyanmar-Medium.otf\",\n        \"NotoSansMyanmar-Regular.otf\",\n        \"NotoSansMyanmarUI-Bold.otf\",\n        \"NotoSansMyanmarUI-Medium.otf\",\n        \"NotoSansMyanmarUI-Regular.otf\",\n        \"NotoSansNKo-Regular.ttf\",\n        \"NotoSansNabataean-Regular.otf\",\n        \"NotoSansNewTaiLue-Regular.ttf\",\n        \"NotoSansNewa-Regular.otf\",\n        \"NotoSansOgham-Regular.ttf\",\n        \"NotoSansOlChiki-Regular.ttf\",\n        \"NotoSansOldItalic-Regular.ttf\",\n        \"NotoSansOldNorthArabian-Regular.otf\",\n        \"NotoSansOldPermic-Regular.otf\",\n        \"NotoSansOldPersian-Regular.ttf\",\n        \"NotoSansOldSouthArabian-Regular.ttf\",\n        \"NotoSansOldTurkic-Regular.ttf\",\n        \"NotoSansOriya-Bold.ttf\",\n        \"NotoSansOriya-Regular.ttf\",\n        \"NotoSansOriyaUI-Bold.ttf\",\n        \"NotoSansOriyaUI-Regular.ttf\",\n        \"NotoSansOsage-Regular.ttf\",\n        \"NotoSansOsmanya-Regular.ttf\",\n        \"NotoSansPahawhHmong-Regular.otf\",\n        \"NotoSansPalmyrene-Regular.otf\",\n        \"NotoSansPauCinHau-Regular.otf\",\n        \"NotoSansPhagsPa-Regular.ttf\",\n        \"NotoSansPhoenician-Regular.ttf\",\n        \"NotoSansRejang-Regular.ttf\",\n        \"NotoSansRunic-Regular.ttf\",\n        \"NotoSansSamaritan-Regular.ttf\",\n        \"NotoSansSaurashtra-Regular.ttf\",\n        \"NotoSansSharada-Regular.otf\",\n        \"NotoSansShavian-Regular.ttf\",\n        \"NotoSansSinhala-VF.ttf\",\n        \"NotoSansSinhalaUI-VF.ttf\",\n        \"NotoSansSoraSompeng-Regular.otf\",\n        \"NotoSansSoyombo-VF.ttf\",\n        \"NotoSansSundanese-Regular.ttf\",\n        \"NotoSansSylotiNagri-Regular.ttf\",\n        \"NotoSansSymbols-Regular-Subsetted.ttf\",\n        \"NotoSansSymbols-Regular-Subsetted2.ttf\",\n        \"NotoSansSyriacEastern-Regular.ttf\",\n        \"NotoSansSyriacEstrangela-Regular.ttf\",\n        \"NotoSansSyriacWestern-Regular.ttf\",\n        \"NotoSansTagalog-Regular.ttf\",\n        \"NotoSansTagbanwa-Regular.ttf\",\n        \"NotoSansTaiLe-Regular.ttf\",\n        \"NotoSansTaiTham-Regular.ttf\",\n        \"NotoSansTaiViet-Regular.ttf\",\n        \"NotoSansTakri-VF.ttf\",\n        \"NotoSansTamil-VF.ttf\",\n        \"NotoSansTamilUI-VF.ttf\",\n        \"NotoSansTelugu-VF.ttf\",\n        \"NotoSansTeluguUI-VF.ttf\",\n        \"NotoSansThaana-Bold.ttf\",\n        \"NotoSansThaana-Regular.ttf\",\n        \"NotoSansThai-Bold.ttf\",\n        \"NotoSansThai-Regular.ttf\",\n        \"NotoSansThaiUI-Bold.ttf\",\n        \"NotoSansThaiUI-Regular.ttf\",\n        \"NotoSansTifinagh-Regular.otf\",\n        \"NotoSansUgaritic-Regular.ttf\",\n        \"NotoSansVai-Regular.ttf\",\n        \"NotoSansWancho-Regular.otf\",\n        \"NotoSansWarangCiti-Regular.otf\",\n        \"NotoSansYi-Regular.ttf\",\n        \"NotoSerif-Bold.ttf\",\n        \"NotoSerif-BoldItalic.ttf\",\n        \"NotoSerif-Italic.ttf\",\n        \"NotoSerif-Regular.ttf\",\n        \"NotoSerifArmenian-VF.ttf\",\n        \"NotoSerifBengali-VF.ttf\",\n        \"NotoSerifCJK-Regular.ttc\",\n        \"NotoSerifDevanagari-VF.ttf\",\n        \"NotoSerifDogra-Regular.ttf\",\n        \"NotoSerifEthiopic-VF.ttf\",\n        \"NotoSerifGeorgian-VF.ttf\",\n        \"NotoSerifGujarati-VF.ttf\",\n        \"NotoSerifGurmukhi-VF.ttf\",\n        \"NotoSerifHebrew-Bold.ttf\",\n        \"NotoSerifHebrew-Regular.ttf\",\n        \"NotoSerifHentaigana.ttf\",\n        \"NotoSerifKannada-VF.ttf\",\n        \"NotoSerifKhmer-Bold.otf\",\n        \"NotoSerifKhmer-Regular.otf\",\n        \"NotoSerifLao-Bold.ttf\",\n        \"NotoSerifLao-Regular.ttf\",\n        \"NotoSerifMalayalam-VF.ttf\",\n        \"NotoSerifMyanmar-Bold.otf\",\n        \"NotoSerifMyanmar-Regular.otf\",\n        \"NotoSerifNyiakengPuachueHmong-VF.ttf\",\n        \"NotoSerifSinhala-VF.ttf\",\n        \"NotoSerifTamil-VF.ttf\",\n        \"NotoSerifTelugu-VF.ttf\",\n        \"NotoSerifThai-Bold.ttf\",\n        \"NotoSerifThai-Regular.ttf\",\n        \"NotoSerifTibetan-VF.ttf\",\n        \"NotoSerifYezidi-VF.ttf\",\n        \"Roboto-Regular.ttf\",\n        \"RobotoFlex-Regular.ttf\",\n        \"RobotoStatic-Regular.ttf\",\n        \"SourceSansPro-Bold.ttf\",\n        \"SourceSansPro-BoldItalic.ttf\",\n        \"SourceSansPro-Italic.ttf\",\n        \"SourceSansPro-Regular.ttf\",\n        \"SourceSansPro-SemiBold.ttf\",\n        \"SourceSansPro-SemiBoldItalic.ttf\",\n        \"font_fallback.xml\",\n        \"fonts.xml\",\n    ],\n}\n\nandroid_filesystem_defaults {\n    name: \"system_ext_image_defaults\",\n    deps: [\n        ///////////////////////////////////////////\n        // base_system_ext\n        ///////////////////////////////////////////\n        \"build_flag_system_ext\",\n        \"fs_config_dirs_system_ext\",\n        \"fs_config_files_system_ext\",\n        \"group_system_ext\",\n        \"passwd_system_ext\",\n        \"SatelliteClient\",\n        \"selinux_policy_system_ext\",\n        \"system_ext_manifest.xml\",\n        \"system_ext-build.prop\",\n        // Base modules when shipping api level is less than or equal to 34\n        \"hwservicemanager\",\n        \"android.hidl.allocator@1.0-service\",\n\n        ///////////////////////////////////////////\n        // media_system_ext\n        ///////////////////////////////////////////\n        \"StatementService\",\n\n        ///////////////////////////////////////////\n        // window_extensions_base\n        ///////////////////////////////////////////\n        \"androidx.window.extensions\",\n        \"androidx.window.sidecar\",\n\n        ///////////////////////////////////////////\n        // base_system\n        ///////////////////////////////////////////\n        \"charger\",\n    ] + select(release_flag(\"RELEASE_APPFUNCTION_SIDECAR\"), {\n        true: [\n            \"com.android.extensions.appfunctions\",\n            \"appfunctions.extension.xml\",\n        ],\n        default: [],\n    }),\n}\n\nandroid_filesystem_defaults {\n    name: \"product_image_defaults\",\n    deps: [\n        ///////////////////////////////////////////\n        // media_product\n        ///////////////////////////////////////////\n        \"webview\",\n\n        ///////////////////////////////////////////\n        // base_product\n        ///////////////////////////////////////////\n\n        // Base modules and settings for the product partition.\n        \"build_flag_product\",\n        \"fs_config_dirs_product\",\n        \"fs_config_files_product\",\n        \"group_product\",\n        \"ModuleMetadata\",\n        \"passwd_product\",\n        \"product_compatibility_matrix.xml\",\n        \"product_manifest.xml\",\n        \"selinux_policy_product\",\n        \"product-build.prop\",\n\n        // AUDIO\n        \"frameworks_sounds\",\n    ] + select(product_variable(\"debuggable\"), {\n        // Packages included only for eng or userdebug builds, previously debug tagged\n        true: [\"adb_keys\"],\n        default: [],\n    }),\n}\n\nsystem_image_fsverity_default = {\n    inputs: [\n        \"etc/boot-image.prof\",\n        \"etc/classpaths/*.pb\",\n        \"etc/dirty-image-objects\",\n        \"etc/preloaded-classes\",\n        \"framework/*\",\n        \"framework/*/*\", // framework/{arch}\n        \"framework/oat/*/*\", // framework/oat/{arch}\n    ],\n    libs: [\":framework-res{.export-package.apk}\"],\n}\n\nsoong_config_module_type {\n    name: \"system_image_defaults\",\n    module_type: \"android_filesystem_defaults\",\n    config_namespace: \"ANDROID\",\n    bool_variables: [\"TARGET_ADD_ROOT_EXTRA_VENDOR_SYMLINKS\"],\n    properties: [\"symlinks\"],\n}\n\ngenrule {\n    name: \"plat_and_vendor_file_contexts\",\n    device_common_srcs: [\n        \":plat_file_contexts\",\n        \":vendor_file_contexts\",\n    ],\n    out: [\"file_contexts\"],\n    cmd: \"cat $(in) > $(out)\",\n}\n\nsystem_image_defaults {\n    name: \"system_image_defaults\",\n    partition_name: \"system\",\n    base_dir: \"system\",\n    stem: \"system.img\",\n    no_full_install: true,\n    dirs: generic_rootdirs,\n    soong_config_variables: {\n        TARGET_ADD_ROOT_EXTRA_VENDOR_SYMLINKS: {\n            symlinks: generic_symlinks + extra_vendor_symlinks,\n            conditions_default: {\n                symlinks: generic_symlinks,\n            },\n        },\n    },\n    file_contexts: \":plat_and_vendor_file_contexts\",\n    linker_config: {\n        gen_linker_config: true,\n        linker_config_srcs: [\":system_linker_config_json_file\"],\n    },\n    fsverity: {\n        inputs: select(soong_config_variable(\"ANDROID\", \"PRODUCT_FSVERITY_GENERATE_METADATA\"), {\n            true: [\n                \"etc/boot-image.prof\",\n                \"etc/classpaths/*.pb\",\n                \"etc/dirty-image-objects\",\n                \"etc/preloaded-classes\",\n                \"framework/*\",\n                \"framework/*/*\", // framework/{arch}\n                \"framework/oat/*/*\", // framework/oat/{arch}\n            ],\n            default: [],\n        }),\n        libs: select(soong_config_variable(\"ANDROID\", \"PRODUCT_FSVERITY_GENERATE_METADATA\"), {\n            true: [\":framework-res{.export-package.apk}\"],\n            default: [],\n        }),\n    },\n    build_logtags: true,\n    gen_aconfig_flags_pb: true,\n\n    compile_multilib: \"both\",\n\n    use_avb: true,\n    avb_private_key: \":generic_system_sign_key\",\n    avb_algorithm: \"SHA256_RSA4096\",\n    avb_hash_algorithm: \"sha256\",\n    rollback_index_location: 1,\n\n    deps: [\n        \"abx\",\n        \"aconfigd-system\",\n        \"aflags\",\n        \"am\",\n        \"android.software.credentials.prebuilt.xml\", // generic_system\n        \"android.software.webview.prebuilt.xml\", // media_system\n        \"android.software.window_magnification.prebuilt.xml\", // handheld_system\n        \"android.system.suspend-service\",\n        \"apexd\",\n        \"appops\",\n        \"approved-ogki-builds.xml\", // base_system\n        \"appwidget\",\n        \"atrace\",\n        \"audioserver\",\n        \"bcc\",\n        \"blank_screen\",\n        \"blkid\",\n        \"bmgr\",\n        \"bootanimation\",\n        \"bootstat\",\n        \"bpfloader\",\n        \"bu\",\n        \"bugreport\",\n        \"bugreportz\",\n        \"cameraserver\",\n        \"cgroups.json\",\n        \"cmd\",\n        \"content\",\n        \"cppreopts.sh\", // generic_system\n        \"credstore\",\n        \"debuggerd\",\n        \"device_config\",\n        \"dirty-image-objects\",\n        \"dmctl\",\n        \"dmesgd\",\n        \"dnsmasq\",\n        \"dpm\",\n        \"dump.erofs\",\n        \"dumpstate\",\n        \"dumpsys\",\n        \"e2fsck\",\n        \"enhanced-confirmation.xml\", // base_system\n        \"etc_hosts\",\n        \"flags_health_check\",\n        \"framework-audio_effects.xml\", // for handheld // handheld_system\n        \"framework-sysconfig.xml\",\n        \"fs_config_dirs_system\",\n        \"fs_config_files_system\",\n        \"fsck.erofs\",\n        \"fsck.f2fs\", // for media_system\n        \"fsck_msdos\",\n        \"fsverity-release-cert-der\",\n        \"gatekeeperd\",\n        \"gpu_counter_producer\",\n        \"gpuservice\",\n        \"group_system\",\n        \"gsi_tool\",\n        \"gsid\",\n        \"heapprofd\",\n        \"hid\",\n        \"hiddenapi-package-whitelist.xml\", // from runtime_libart\n        \"idc_data\",\n        \"idmap2\",\n        \"idmap2d\",\n        \"ime\",\n        \"incident\",\n        \"incident-helper-cmd\",\n        \"incident_helper\",\n        \"incidentd\",\n        \"init.environ.rc-soong\",\n        \"init.usb.configfs.rc\",\n        \"init.usb.rc\",\n        \"init.zygote32.rc\",\n        \"init.zygote64.rc\",\n        \"init.zygote64_32.rc\",\n        \"initial-package-stopped-states.xml\",\n        \"input\",\n        \"installd\",\n        \"ip\", // base_system\n        \"iptables\",\n        \"kcmdlinectrl\",\n        \"kernel-lifetimes.xml\", // base_system\n        \"keychars_data\",\n        \"keylayout_data\",\n        \"keystore2\",\n        \"ld.mc\",\n        \"llkd\", // base_system\n        \"lmkd\", // base_system\n        \"locksettings\", // base_system\n        \"logcat\", // base_system\n        \"logd\", // base_system\n        \"lpdump\", // base_system\n        \"lshal\", // base_system\n        \"make_f2fs\", // media_system\n        \"mdnsd\", // base_system\n        \"media_profiles_V1_0.dtd\", // base_system\n        \"mediacodec.policy\", // base_system\n        \"mediaextractor\", // base_system\n        \"mediametrics\", // base_system\n        \"misctrl\", // from base_system\n        \"mke2fs\", // base_system\n        \"mkfs.erofs\", // base_system\n        \"monkey\", // base_system\n        \"mtectrl\", // base_system\n        \"ndc\", // base_system\n        \"netd\", // base_system\n        \"netutils-wrapper-1.0\", // full_base\n        \"notice_xml_system\",\n        \"odsign\", // base_system\n        \"otapreopt_script\", // generic_system\n        \"package-shareduid-allowlist.xml\", // base_system\n        \"passwd_system\", // base_system\n        \"pbtombstone\", // base_system\n        \"perfetto\", // base_system\n        \"ping\", // base_system\n        \"ping6\", // base_system\n        \"pintool\", // base_system\n        \"platform.xml\", // base_system\n        \"pm\", // base_system\n        \"prefetch\", //base_system\n        \"preinstalled-packages-asl-files.xml\", // base_system\n        \"preinstalled-packages-platform-generic-system.xml\", // generic_system\n        \"preinstalled-packages-platform-handheld-system.xml\", // handheld_system\n        \"preinstalled-packages-platform.xml\", // base_system\n        \"preinstalled-packages-strict-signature.xml\", // base_system\n        \"preloaded-classes\", // ok\n        \"privapp-permissions-platform.xml\", // base_system\n        \"prng_seeder\", // base_system\n        \"public.libraries.android.txt\",\n        \"recovery-persist\", // base_system\n        \"recovery-refresh\", // generic_system\n        \"requestsync\", // media_system\n        \"resize2fs\", // base_system\n        \"rss_hwm_reset\", // base_system\n        \"run-as\", // base_system\n        \"schedtest\", // base_system\n        \"screencap\", // base_system\n        \"screenrecord\", // handheld_system\n        \"sdcard\", // base_system\n        \"secdiscard\", // base_system\n        \"sensorservice\", // base_system\n        \"service\", // base_system\n        \"servicemanager\", // base_system\n        \"settings\", // base_system\n        \"sfdo\", // base_system\n        \"sgdisk\", // base_system\n        \"sm\", // base_system\n        \"snapuserd\", // base_system\n        \"storaged\", // base_system\n        \"surfaceflinger\", // base_system\n        \"svc\", // base_system\n        \"system_manifest.xml\", // base_system\n        \"task_profiles.json\", // base_system\n        \"tc\", // base_system\n        \"telecom\", // base_system\n        \"tombstoned\", // base_system\n        \"traced\", // base_system\n        \"traced_probes\", // base_system\n        \"tradeinmode\", // base_system\n        \"tune2fs\", // base_system\n        \"uiautomator\", // base_system\n        \"uinput\", // base_system\n        \"uncrypt\", // base_system\n        \"update_engine\", // generic_system\n        \"update_engine_sideload\", // recovery\n        \"update_verifier\", // generic_system\n        \"usbd\", // base_system\n        \"vdc\", // base_system\n        \"virtual_camera\", // handheld_system // release_package_virtual_camera\n        \"vold\", // base_system\n        \"vr\", // handheld_system\n        \"watchdogd\", // base_system\n        \"wifi.rc\", // base_system\n        \"wificond\", // base_system\n        \"wm\", // base_system\n    ] + select(release_flag(\"RELEASE_PLATFORM_VERSION_CODENAME\"), {\n        \"REL\": [],\n        default: [\n            \"android.software.preview_sdk.prebuilt.xml\", // media_system\n        ],\n    }) + select(release_flag(\"RELEASE_MEMORY_MANAGEMENT_DAEMON\"), {\n        true: [\n            \"mm_daemon\", // base_system (RELEASE_MEMORY_MANAGEMENT_DAEMON)\n        ],\n        default: [\n            \"init-mmd-prop.rc\", // base_system\n        ],\n    }) + select(product_variable(\"debuggable\"), {\n        true: [\n            \"alloctop\",\n            \"adevice_fingerprint\",\n            \"arping\",\n            \"avbctl\",\n            \"bootctl\",\n            \"dmuserd\",\n            \"evemu-record\",\n            \"idlcli\",\n            \"init-debug.rc\",\n            \"iotop\",\n            \"iperf3\",\n            \"iw\",\n            \"layertracegenerator\",\n            \"logpersist.start\",\n            \"logtagd.rc\",\n            \"ot-cli-ftd\",\n            \"ot-ctl\",\n            \"overlay_remounter\",\n            \"procrank\",\n            \"profcollectctl\",\n            \"profcollectd\",\n            \"record_binder\",\n            \"sanitizer-status\",\n            \"servicedispatcher\",\n            \"showmap\",\n            \"snapshotctl\",\n            \"sqlite3\",\n            \"ss\",\n            \"start_with_lockagent\",\n            \"strace\",\n            \"su\",\n            \"tinycap\",\n            \"tinyhostless\",\n            \"tinymix\",\n            \"tinypcminfo\",\n            \"tinyplay\", // host\n            \"tracepath\",\n            \"tracepath6\",\n            \"traceroute6\",\n            \"unwind_info\",\n            \"unwind_reg_info\",\n            \"unwind_symbols\",\n            \"update_engine_client\",\n        ],\n        default: [],\n    }) + select(release_flag(\"RELEASE_UPROBESTATS_MODULE\"), {\n        true: [],\n        default: [\n            \"uprobestats\", // base_system internal\n        ],\n    }),\n    multilib: {\n        common: {\n            deps: [\n                \"BackupRestoreConfirmation\", // base_system\n                \"BasicDreams\", // handheld_system\n                \"BlockedNumberProvider\", // handheld_system\n                \"BluetoothMidiService\", // handheld_system\n                \"BookmarkProvider\", // handheld_system\n                \"BuiltInPrintService\", // handheld_system\n                \"CalendarProvider\", // handheld_system\n                \"CallLogBackup\", // telephony_system\n                \"CameraExtensionsProxy\", // handheld_system\n                \"CaptivePortalLogin\", // handheld_system\n                \"CarrierDefaultApp\", // telephony_system\n                \"CellBroadcastLegacyApp\", // telephony_system\n                \"CertInstaller\", // handheld_system\n                \"CompanionDeviceManager\", // media_system\n                \"ContactsProvider\", // base_system\n                \"CredentialManager\", // handheld_system\n                \"DeviceAsWebcam\", // handheld_system\n                \"DeviceDiagnostics\", // handheld_system - internal\n                \"DocumentsUI\", // handheld_system\n                \"DownloadProvider\", // base_system\n                \"DownloadProviderUi\", // handheld_system\n                \"DynamicSystemInstallationService\", // base_system\n                \"E2eeContactKeysProvider\", // base_system\n                \"EasterEgg\", // handheld_system\n                \"ExtShared\", // base_system\n                \"ExternalStorageProvider\", // handheld_system\n                \"FusedLocation\", // handheld_system\n                \"HTMLViewer\", // media_system\n                \"InputDevices\", // handheld_system\n                \"IntentResolver\", // base_system\n                \"KeyChain\", // handheld_system\n                \"LiveWallpapersPicker\", // generic_system, full_base\n                \"LocalTransport\", // base_system\n                \"ManagedProvisioning\", // handheld_system\n                \"MediaProviderLegacy\", // base_system\n                \"MmsService\", // handheld_system\n                \"MtpService\", // handheld_system\n                \"MusicFX\", // handheld_system\n                \"NetworkStack\", // base_system\n                \"ONS\", // telephony_system\n                \"PacProcessor\", // handheld_system\n                \"PackageInstaller\", // base_system\n                \"PartnerBookmarksProvider\", // generic_system\n                \"PrintRecommendationService\", // handheld_system\n                \"PrintSpooler\", // handheld_system\n                \"ProxyHandler\", // handheld_system\n                \"SecureElement\", // handheld_system\n                \"SettingsProvider\", // base_system\n                \"SharedStorageBackup\", // handheld_system\n                \"Shell\", // base_system\n                \"SimAppDialog\", // handheld_system\n                \"SoundPicker\", // not installed by anyone\n                \"Stk\", // generic_system\n                \"Tag\", // generic_system\n                \"TeleService\", // handheld_system\n                \"Telecom\", // handheld_system\n                \"TelephonyProvider\", // handheld_system\n                \"Traceur\", // handheld_system\n                \"UserDictionaryProvider\", // handheld_system\n                \"VpnDialogs\", // handheld_system\n                \"WallpaperBackup\", // base_system\n                \"adbd_system_api\", // base_system\n                \"android.hidl.base-V1.0-java\", // base_system\n                \"android.hidl.manager-V1.0-java\", // base_system\n                \"android.test.base\", // from runtime_libart\n                \"android.test.mock\", // base_system\n                \"android.test.runner\", // base_system\n                \"aosp_mainline_modules\", // ok\n                \"build_flag_system\", // base_system\n                \"charger_res_images\", // generic_system\n                \"com.android.apex.cts.shim.v1_prebuilt\", // ok\n                \"com.android.cellbroadcast\", // telephony_system\n                \"com.android.future.usb.accessory\", // media_system\n                \"com.android.location.provider\", // base_system\n                \"com.android.media.remotedisplay\", // media_system\n                \"com.android.media.remotedisplay.xml\", // media_system\n                \"com.android.mediadrm.signer\", // media_system\n                \"com.android.nfc_extras\", // ok\n                \"com.android.nfcservices\", // base_system (RELEASE_PACKAGE_NFC_STACK != NfcNci)\n                \"com.android.runtime\", // ok\n                \"dex_bootjars\",\n                \"ext\", // from runtime_libart\n                \"framework-graphics\", // base_system\n                \"framework-location\", // base_system\n                \"framework-minus-apex-install-dependencies\", // base_system\n                \"framework_compatibility_matrix.device.xml\",\n                \"generic_system_fonts\", // ok\n                \"hwservicemanager_compat_symlink_module\", // base_system\n                \"hyph-data\",\n                \"ims-common\", // base_system\n                \"init_system\", // base_system\n                \"javax.obex\", // base_system\n                \"llndk.libraries.txt\", //ok\n                \"org.apache.http.legacy\", // base_system\n                \"perfetto-extras\", // system\n                \"sanitizer.libraries.txt\", // base_system\n                \"selinux_policy_system_soong\", // ok\n                \"services\", // base_system\n                \"shell_and_utilities_system\", // ok\n                \"system-build.prop\",\n                \"system_compatibility_matrix.xml\", //base_system\n                \"telephony-common\", // libs from TeleService\n                \"voip-common\", // base_system\n            ] + select(soong_config_variable(\"ANDROID\", \"release_crashrecovery_module\"), {\n                \"true\": [\n                    \"com.android.crashrecovery\", // base_system (RELEASE_CRASHRECOVERY_MODULE)\n                ],\n                default: [\n                    \"framework-platformcrashrecovery\", // base_system\n                ],\n            }) + select(release_flag(\"RELEASE_ONDEVICE_INTELLIGENCE_MODULE\"), {\n                true: [\n                    \"com.android.neuralnetworks\", // base_system (RELEASE_ONDEVICE_INTELLIGENCE_MODULE)\n                ],\n                default: [\n                    \"framework-ondeviceintelligence-platform\", // base_system\n                ],\n            }) + select(soong_config_variable(\"ANDROID\", \"release_package_profiling_module\"), {\n                \"true\": [\n                    \"com.android.profiling\", // base_system (RELEASE_PACKAGE_PROFILING_MODULE)\n                ],\n                default: [],\n            }) + select(release_flag(\"RELEASE_MOVE_VCN_TO_MAINLINE\"), {\n                true: [],\n                default: [\n                    \"framework-connectivity-b\", // base_system\n                ],\n            }) + select(release_flag(\"RELEASE_UPROBESTATS_MODULE\"), {\n                true: [\n                    \"com.android.uprobestats\", // base_system (RELEASE_UPROBESTATS_MODULE)\n                ],\n                default: [],\n            }),\n        },\n        prefer32: {\n            deps: [\n                \"drmserver\", // media_system\n                \"mediaserver\", // base_system\n            ],\n        },\n        lib64: {\n            deps: [\n                \"android.system.virtualizationcommon-ndk\",\n                \"android.system.virtualizationservice-ndk\",\n                \"libgsi\",\n                \"servicemanager\",\n            ],\n        },\n        both: {\n            deps: [\n                \"android.hardware.biometrics.fingerprint@2.1\", // generic_system\n                \"android.hardware.radio.config@1.0\", // generic_system\n                \"android.hardware.radio.deprecated@1.0\", // generic_system\n                \"android.hardware.radio@1.0\", // generic_system\n                \"android.hardware.radio@1.1\", // generic_system\n                \"android.hardware.radio@1.2\", // generic_system\n                \"android.hardware.radio@1.3\", // generic_system\n                \"android.hardware.radio@1.4\", // generic_system\n                \"android.hardware.secure_element@1.0\", // generic_system\n                \"app_process\", // base_system\n                \"boringssl_self_test\", // base_system\n                \"heapprofd_client\", // base_system\n                \"libEGL\", // base_system\n                \"libEGL_angle\", // base_system\n                \"libETC1\", // base_system\n                \"libFFTEm\", // base_system\n                \"libGLESv1_CM\", // base_system\n                \"libGLESv1_CM_angle\", // base_system\n                \"libGLESv2\", // base_system\n                \"libGLESv2_angle\", // base_system\n                \"libGLESv3\", // base_system\n                \"libOpenMAXAL\", // base_system\n                \"libOpenSLES\", // base_system\n                \"libaaudio\", // base_system\n                \"libalarm_jni\", // base_system\n                \"libamidi\", // base_system\n                \"libandroid\",\n                \"libandroid_runtime\",\n                \"libandroid_servers\",\n                \"libandroidfw\",\n                \"libartpalette-system\",\n                \"libaudio-resampler\", // generic-system\n                \"libaudioeffect_jni\",\n                \"libaudiohal\", // generic-system\n                \"libaudiopolicyengineconfigurable\", // generic-system\n                \"libbinder\",\n                \"libbinder_ndk\",\n                \"libbinder_rpc_unstable\",\n                \"libcamera2ndk\",\n                \"libcgrouprc\", // llndk library\n                \"libclang_rt.asan\",\n                \"libcompiler_rt\",\n                \"libcutils\", // used by many libs\n                \"libdmabufheap\", // used by many libs\n                \"libdrm\", // used by many libs // generic_system\n                \"libdrmframework\", // base_system\n                \"libdrmframework_jni\", // base_system\n                \"libfdtrack\", // base_system\n                \"libfilterfw\", // base_system\n                \"libfilterpack_imageproc\", // media_system\n                \"libfwdlockengine\", // generic_system\n                \"libgatekeeper\", // base_system\n                \"libgui\", // base_system\n                \"libhardware\", // base_system\n                \"libhardware_legacy\", // base_system\n                \"libhidltransport\", // generic_system\n                \"libhwbinder\", // generic_system\n                \"libinput\", // base_system\n                \"libinputflinger\", // base_system\n                \"libiprouteutil\", // base_system\n                \"libjnigraphics\", // base_system\n                \"libjpeg\", // base_system\n                \"liblog\", // base_system\n                \"liblogwrap\", // generic_system\n                \"liblz4\", // generic_system\n                \"libmedia\", // base_system\n                \"libmedia_jni\", // base_system\n                \"libmediandk\", // base_system\n                \"libminui\", // generic_system\n                \"libmonkey_jni\", // base_system - internal\n                \"libmtp\", // base_system\n                \"libnetd_client\", // base_system\n                \"libnetlink\", // base_system\n                \"libnetutils\", // base_system\n                \"libneuralnetworks_packageinfo\", // base_system\n                \"libnl\", // generic_system\n                \"libpdfium\", // base_system\n                \"libpolicy-subsystem\", // generic_system\n                \"libpower\", // base_system\n                \"libpowermanager\", // base_system\n                \"libprotobuf-cpp-full\", // generic_system\n                \"libradio_metadata\", // base_system\n                \"librs_jni\", // handheld_system\n                \"librtp_jni\", // base_system\n                \"libsensorservice\", // base_system\n                \"libsfplugin_ccodec\", // base_system\n                \"libskia\", // base_system\n                \"libsonic\", // base_system\n                \"libsonivox\", // base_system\n                \"libsoundpool\", // base_system\n                \"libspeexresampler\", // base_system\n                \"libsqlite\", // base_system\n                \"libstagefright\", // base_system\n                \"libstagefright_foundation\", // base_system\n                \"libstagefright_omx\", // base_system\n                \"libstdc++\", // base_system\n                \"libsysutils\", // base_system\n                \"libui\", // base_system\n                \"libusbhost\", // base_system\n                \"libutils\", // base_system\n                \"libvendorsupport\", // llndk library\n                \"libvintf_jni\", // base_system\n                \"libvulkan\", // base_system\n                \"libwebviewchromium_loader\", // media_system\n                \"libwebviewchromium_plat_support\", // media_system\n                \"libwilhelm\", // base_system\n                \"linker\", // base_system\n            ] + select(soong_config_variable(\"ANDROID\", \"TARGET_DYNAMIC_64_32_DRMSERVER\"), {\n                \"true\": [\"drmserver\"],\n                default: [],\n            }) + select(soong_config_variable(\"ANDROID\", \"TARGET_DYNAMIC_64_32_MEDIASERVER\"), {\n                \"true\": [\"mediaserver\"],\n                default: [],\n            }) + select(release_flag(\"RELEASE_UPROBESTATS_MODULE\"), {\n                true: [],\n                default: [\n                    \"libuprobestats_client\", // base_system internal\n                ],\n            }),\n        },\n    },\n    arch: {\n        arm64: {\n            deps: [\n                \"libclang_rt.hwasan\",\n                \"libc_hwasan\",\n            ],\n        },\n    },\n}\n\nandroid_system_image {\n    name: \"aosp_shared_system_image\",\n    defaults: [\"system_image_defaults\"],\n    dirs: android_rootdirs,\n    symlinks: android_symlinks,\n    type: \"erofs\",\n    erofs: {\n        compressor: \"lz4hc,9\",\n        compress_hints: \"erofs_compress_hints.txt\",\n    },\n    deps: [\n        // DO NOT update this list. Instead, update the system_image_defaults to\n        // sync with the base_system.mk\n        \"logpersist.start\", // cf only\n    ],\n}\n"
  },
  {
    "path": "target/product/generic/OWNERS",
    "content": "# Bug component: 1322713\ninseob@google.com\njeongik@google.com\njiyong@google.com\njustinyun@google.com\nkiyoungkim@google.com\n"
  },
  {
    "path": "target/product/generic/erofs_compress_hints.txt",
    "content": "0 .*\\.apex$"
  },
  {
    "path": "target/product/generic.mk",
    "content": "#\n# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a generic phone product that isn't specialized for a specific device.\n# It includes the base Android platform.\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_no_telephony.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony.mk)\n\n# Overrides\nPRODUCT_BRAND := generic\nPRODUCT_DEVICE := generic\nPRODUCT_NAME := generic\n\nallowed_list := product_manifest.xml\n\n# TODO(b/182105280): When ART prebuilts are used in this product, Soong doesn't\n# produce any Android.mk entries for them. Exclude them until that problem is\n# fixed.\nallowed_list += com.android.art com.android.art.debug\n\n$(call enforce-product-packages-exist,$(allowed_list))\n"
  },
  {
    "path": "target/product/generic_no_telephony.mk",
    "content": "#\n# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This product is a generic phone or tablet, that doesn't have telephony.\n#\n# Note: Do not add any contents directly to this file. Choose either\n# handheld_system or handheld_vendor depending on partition (also consider\n# base_<x>.mk or media_<x>.mk.\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_vendor.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_product.mk)\n\nPRODUCT_BRAND := generic\nPRODUCT_DEVICE := generic\nPRODUCT_NAME := generic_no_telephony\n"
  },
  {
    "path": "target/product/generic_ramdisk.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile installs contents of the generic ramdisk.\n# Inherit from this makefile to declare that this product uses generic ramdisk.\n# This makefile checks that other makefiles must not install things to the\n# ramdisk.\n\n# Ramdisk\nPRODUCT_PACKAGES += \\\n    init_first_stage \\\n    snapuserd_ramdisk \\\n    ramdisk-build.prop \\\n    toolbox_ramdisk \\\n\n# Debug ramdisk\nPRODUCT_PACKAGES += \\\n    adb_debug.prop \\\n    userdebug_plat_sepolicy.cil \\\n\n\n# For targets using dedicated recovery partition, generic ramdisk\n# might be relocated to recovery partition\n_my_paths := \\\n    $(TARGET_COPY_OUT_RAMDISK)/ \\\n    $(TARGET_COPY_OUT_DEBUG_RAMDISK)/ \\\n    $(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/system \\\n\n\n# We use the \"relaxed\" version here because tzdata / tz_version is only produced\n# by this makefile on a subset of devices.\n# TODO: remove this\n$(call require-artifacts-in-path-relaxed, $(_my_paths), )\n"
  },
  {
    "path": "target/product/generic_system.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile is the basis of a generic system image for a handheld device.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk)\n# Add adb keys to debuggable AOSP builds (if they exist)\n$(call inherit-product-if-exists, vendor/google/security/adb/vendor_key.mk)\n\n# Enable updating of APEXes\n$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)\n\n# Shared java libs\nPRODUCT_PACKAGES += \\\n    com.android.nfc_extras \\\n\n# Applications\nPRODUCT_PACKAGES += \\\n    LiveWallpapersPicker \\\n    PartnerBookmarksProvider \\\n    preinstalled-packages-platform-generic-system.xml \\\n    Stk \\\n    Tag \\\n\n# OTA support\nPRODUCT_PACKAGES += \\\n    recovery-refresh \\\n    update_engine \\\n    update_verifier \\\n\n# Wrapped net utils for /vendor access.\nPRODUCT_PACKAGES += netutils-wrapper-1.0\n\n# Charger images\nPRODUCT_PACKAGES += charger_res_images\n\n# system_other support\nPRODUCT_PACKAGES += \\\n    cppreopts.sh \\\n    otapreopt_script \\\n\n# For ringtones that rely on forward lock encryption\nPRODUCT_PACKAGES += libfwdlockengine\n\n# System libraries commonly depended on by things on the system_ext or product partitions.\n# These lists will be pruned periodically.\nPRODUCT_PACKAGES += \\\n    android.hardware.biometrics.fingerprint@2.1 \\\n    android.hardware.radio@1.0 \\\n    android.hardware.radio@1.1 \\\n    android.hardware.radio@1.2 \\\n    android.hardware.radio@1.3 \\\n    android.hardware.radio@1.4 \\\n    android.hardware.radio.config@1.0 \\\n    android.hardware.radio.deprecated@1.0 \\\n    android.hardware.secure_element@1.0 \\\n    libaudio-resampler \\\n    libaudiohal \\\n    libdrm \\\n    liblogwrap \\\n    liblz4 \\\n    libminui \\\n    libnl \\\n    libprotobuf-cpp-full \\\n\n# These libraries are empty and have been combined into libhidlbase, but are still depended\n# on by things off /system.\n# TODO(b/135686713): remove these\nPRODUCT_PACKAGES += \\\n    libhidltransport \\\n    libhwbinder \\\n\nPRODUCT_PACKAGES_DEBUG += \\\n    avbctl \\\n    bootctl \\\n    tinycap \\\n    tinyhostless \\\n    tinymix \\\n    tinypcminfo \\\n    tinyplay \\\n    update_engine_client \\\n\nPRODUCT_HOST_PACKAGES += \\\n    tinyplay\n\n# Enable configurable audio policy\nPRODUCT_PACKAGES += \\\n    libaudiopolicyengineconfigurable \\\n    libpolicy-subsystem\n\n# Add all of the packages used to support older/upgrading devices\n# These can be removed as we drop support for the older API levels\nPRODUCT_PACKAGES += \\\n    $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29) \\\n    $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33) \\\n    $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)\n\n# Include all zygote init scripts. \"ro.zygote\" will select one of them.\nPRODUCT_PACKAGES += \\\n    init.zygote32.rc \\\n    init.zygote64.rc \\\n    init.zygote64_32.rc\n\n# Support Credential Manager\nPRODUCT_PACKAGES += \\\n    android.software.credentials.prebuilt.xml\n\n# Enable dynamic partition size\nPRODUCT_USE_DYNAMIC_PARTITION_SIZE := true\n\nPRODUCT_ENFORCE_RRO_TARGETS := *\n\nPRODUCT_NAME := generic_system\nPRODUCT_BRAND := generic\n\n# Define /system partition-specific product properties to identify that /system\n# partition is generic_system.\nPRODUCT_SYSTEM_NAME := mainline\nPRODUCT_SYSTEM_BRAND := Android\nPRODUCT_SYSTEM_MANUFACTURER := Android\nPRODUCT_SYSTEM_MODEL := mainline\nPRODUCT_SYSTEM_DEVICE := generic\n\n_base_mk_allowed_list :=\n\n_my_allowed_list := $(_base_mk_allowed_list)\n\n# For mainline, system.img should be mounted at /, so we include ROOT here.\n_my_paths := \\\n  $(TARGET_COPY_OUT_ROOT)/ \\\n  $(TARGET_COPY_OUT_SYSTEM)/ \\\n\n$(call require-artifacts-in-path, $(_my_paths), $(_my_allowed_list))\n\n# Product config map to toggle between sources and prebuilts of required mainline modules\nPRODUCT_RELEASE_CONFIG_MAPS += $(wildcard build/release/gms_mainline/required/release_config_map.textproto)\nPRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/build/release/gms_mainline/required/release_config_map.textproto)\n"
  },
  {
    "path": "target/product/generic_system_arm64.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# All components inherited here go to system image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)\n$(call enforce-product-packages-exist,)\n\n# Enable mainline checking\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true\n\nPRODUCT_BUILD_CACHE_IMAGE := false\nPRODUCT_BUILD_ODM_IMAGE := false\nPRODUCT_BUILD_VENDOR_DLKM_IMAGE := false\nPRODUCT_BUILD_ODM_DLKM_IMAGE := false\nPRODUCT_BUILD_PRODUCT_IMAGE  := false\nPRODUCT_BUILD_RAMDISK_IMAGE := false\nPRODUCT_BUILD_SYSTEM_IMAGE := true\nPRODUCT_BUILD_SYSTEM_EXT_IMAGE := false\nPRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false\nPRODUCT_BUILD_USERDATA_IMAGE := false\nPRODUCT_BUILD_VENDOR_IMAGE := false\n\nPRODUCT_SHIPPING_API_LEVEL := 29\n\nPRODUCT_RESTRICT_VENDOR_FILES := all\n\nPRODUCT_NAME := generic_system_arm64\nPRODUCT_DEVICE := mainline_arm64\nPRODUCT_BRAND := generic\n"
  },
  {
    "path": "target/product/generic_system_x86.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# All components inherited here go to system image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)\n$(call enforce-product-packages-exist,)\n\n# Enable mainline checking\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true\n\nPRODUCT_BUILD_CACHE_IMAGE := false\nPRODUCT_BUILD_ODM_IMAGE := false\nPRODUCT_BUILD_VENDOR_DLKM_IMAGE := false\nPRODUCT_BUILD_ODM_DLKM_IMAGE := false\nPRODUCT_BUILD_PRODUCT_IMAGE  := false\nPRODUCT_BUILD_RAMDISK_IMAGE := false\nPRODUCT_BUILD_SYSTEM_IMAGE := true\nPRODUCT_BUILD_SYSTEM_EXT_IMAGE := false\nPRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false\nPRODUCT_BUILD_USERDATA_IMAGE := false\nPRODUCT_BUILD_VENDOR_IMAGE := false\n\nPRODUCT_SHIPPING_API_LEVEL := 29\n\nPRODUCT_RESTRICT_VENDOR_FILES := all\n\nPRODUCT_NAME := generic_system_x86\nPRODUCT_DEVICE := mainline_x86\nPRODUCT_BRAND := generic\n"
  },
  {
    "path": "target/product/generic_system_x86_64.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# All components inherited here go to system image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)\n$(call enforce-product-packages-exist,)\n\n# Enable mainline checking\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true\n\nPRODUCT_BUILD_CACHE_IMAGE := false\nPRODUCT_BUILD_ODM_IMAGE := false\nPRODUCT_BUILD_VENDOR_DLKM_IMAGE := false\nPRODUCT_BUILD_ODM_DLKM_IMAGE := false\nPRODUCT_BUILD_PRODUCT_IMAGE  := false\nPRODUCT_BUILD_RAMDISK_IMAGE := false\nPRODUCT_BUILD_SYSTEM_IMAGE := true\nPRODUCT_BUILD_SYSTEM_EXT_IMAGE := false\nPRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false\nPRODUCT_BUILD_USERDATA_IMAGE := false\nPRODUCT_BUILD_VENDOR_IMAGE := false\n\nPRODUCT_SHIPPING_API_LEVEL := 29\n\nPRODUCT_RESTRICT_VENDOR_FILES := all\n\nPRODUCT_NAME := generic_system_x86_64\nPRODUCT_DEVICE := mainline_x86_64\nPRODUCT_BRAND := generic\n"
  },
  {
    "path": "target/product/generic_system_x86_arm.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# All components inherited here go to system image\n#\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)\n$(call enforce-product-packages-exist,)\n\n# Enable mainline checking\nPRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true\n\nPRODUCT_BUILD_CACHE_IMAGE := false\nPRODUCT_BUILD_ODM_IMAGE := false\nPRODUCT_BUILD_VENDOR_DLKM_IMAGE := false\nPRODUCT_BUILD_ODM_DLKM_IMAGE := false\nPRODUCT_BUILD_PRODUCT_IMAGE  := false\nPRODUCT_BUILD_RAMDISK_IMAGE := false\nPRODUCT_BUILD_SYSTEM_IMAGE := true\nPRODUCT_BUILD_SYSTEM_EXT_IMAGE := false\nPRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false\nPRODUCT_BUILD_USERDATA_IMAGE := false\nPRODUCT_BUILD_VENDOR_IMAGE := false\n\nPRODUCT_SHIPPING_API_LEVEL := 29\n\nPRODUCT_RESTRICT_VENDOR_FILES := all\n\nPRODUCT_NAME := generic_system_x86_arm\nPRODUCT_DEVICE := mainline_x86_arm\nPRODUCT_BRAND := generic\n"
  },
  {
    "path": "target/product/generic_x86.mk",
    "content": "#\n# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a generic phone product that isn't specialized for a specific device.\n# It includes the base Android platform.\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic.mk)\n\n# Overrides\nPRODUCT_BRAND := generic_x86\nPRODUCT_DEVICE := generic_x86\nPRODUCT_NAME := generic_x86\n"
  },
  {
    "path": "target/product/go_defaults.mk",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Inherit common Android Go defaults.\n$(call inherit-product, build/make/target/product/go_defaults_common.mk)\n\n# Product config map to toggle between sources and prebuilts of required mainline modules\nPRODUCT_RELEASE_CONFIG_MAPS += $(wildcard build/release/gms_mainline_go/required/release_config_map.textproto)\nPRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/build/release/gms_mainline_go/required/release_config_map.textproto)\n\n# Add the system properties.\nTARGET_SYSTEM_PROP += \\\n    build/make/target/board/go_defaults.prop\n"
  },
  {
    "path": "target/product/go_defaults_512.mk",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Inherit common Android Go defaults.\n$(call inherit-product, build/make/target/product/go_defaults_common.mk)\n\n# Add the system properties.\nTARGET_SYSTEM_PROP += \\\n    build/make/target/board/go_defaults_512.prop\n"
  },
  {
    "path": "target/product/go_defaults_common.mk",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Sets Android Go recommended default product options..\n\n\n# Set lowram options and enable traced by default\nPRODUCT_VENDOR_PROPERTIES += \\\n     ro.config.low_ram=true \\\n\n# Speed profile services and wifi-service to reduce RAM and storage.\nPRODUCT_SYSTEM_SERVER_COMPILER_FILTER := speed-profile\n\n# Do not generate libartd.\nPRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD := false\n\n# Strip the local variable table and the local variable type table to reduce\n# the size of the system image. This has no bearing on stack traces, but will\n# leave less information available via JDWP.\nPRODUCT_MINIMIZE_JAVA_DEBUG_INFO := true\n\n# Use the low memory allocator outside of eng builds to save RSS.\nifneq (,$(filter eng, $(TARGET_BUILD_VARIANT)))\n  MALLOC_LOW_MEMORY := true\nendif\n\n# Add the system properties.\nTARGET_SYSTEM_PROP += \\\n    build/make/target/board/go_defaults_common.prop\n\n# use the go specific handheld_core_hardware.xml from frameworks\nPRODUCT_COPY_FILES += \\\n    frameworks/native/data/etc/go_handheld_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/handheld_core_hardware.xml\n"
  },
  {
    "path": "target/product/gsi/28.txt",
    "content": "LLNDK: libEGL.so\nLLNDK: libGLESv1_CM.so\nLLNDK: libGLESv2.so\nLLNDK: libGLESv3.so\nLLNDK: libRS.so\nLLNDK: libandroid_net.so\nLLNDK: libc.so\nLLNDK: libdl.so\nLLNDK: libft2.so\nLLNDK: liblog.so\nLLNDK: libm.so\nLLNDK: libmediandk.so\nLLNDK: libnativewindow.so\nLLNDK: libneuralnetworks.so\nLLNDK: libsync.so\nLLNDK: libvndksupport.so\nLLNDK: libvulkan.so\nVNDK-SP: android.hardware.graphics.common@1.0.so\nVNDK-SP: android.hardware.graphics.common@1.1.so\nVNDK-SP: android.hardware.graphics.mapper@2.0.so\nVNDK-SP: android.hardware.graphics.mapper@2.1.so\nVNDK-SP: android.hardware.renderscript@1.0.so\nVNDK-SP: android.hidl.memory.token@1.0.so\nVNDK-SP: android.hidl.memory@1.0.so\nVNDK-SP: android.hidl.memory@1.0-impl.so\nVNDK-SP: libRSCpuRef.so\nVNDK-SP: libRSDriver.so\nVNDK-SP: libRS_internal.so\nVNDK-SP: libbacktrace.so\nVNDK-SP: libbase.so\nVNDK-SP: libbcinfo.so\nVNDK-SP: libblas.so\nVNDK-SP: libc++.so\nVNDK-SP: libcompiler_rt.so\nVNDK-SP: libcutils.so\nVNDK-SP: libhardware.so\nVNDK-SP: libhidlbase.so\nVNDK-SP: libhidlmemory.so\nVNDK-SP: libhidltransport.so\nVNDK-SP: libhwbinder.so\nVNDK-SP: libhwbinder_noltopgo.so\nVNDK-SP: libion.so\nVNDK-SP: liblzma.so\nVNDK-SP: libunwind.so\nVNDK-SP: libunwindstack.so\nVNDK-SP: libutils.so\nVNDK-SP: libutilscallstack.so\nVNDK-SP: libz.so\nVNDK-core: android.frameworks.displayservice@1.0.so\nVNDK-core: android.frameworks.schedulerservice@1.0.so\nVNDK-core: android.frameworks.sensorservice@1.0.so\nVNDK-core: android.frameworks.vr.composer@1.0.so\nVNDK-core: android.hardware.audio.common-util.so\nVNDK-core: android.hardware.audio.common@2.0.so\nVNDK-core: android.hardware.audio.common@2.0-util.so\nVNDK-core: android.hardware.audio.common@4.0.so\nVNDK-core: android.hardware.audio.common@4.0-util.so\nVNDK-core: android.hardware.audio.effect@2.0.so\nVNDK-core: android.hardware.audio.effect@4.0.so\nVNDK-core: android.hardware.audio@2.0.so\nVNDK-core: android.hardware.audio@4.0.so\nVNDK-core: android.hardware.authsecret@1.0.so\nVNDK-core: android.hardware.automotive.audiocontrol@1.0.so\nVNDK-core: android.hardware.automotive.evs@1.0.so\nVNDK-core: android.hardware.automotive.vehicle@2.0.so\nVNDK-core: android.hardware.biometrics.fingerprint@2.1.so\nVNDK-core: android.hardware.bluetooth.a2dp@1.0.so\nVNDK-core: android.hardware.bluetooth@1.0.so\nVNDK-core: android.hardware.boot@1.0.so\nVNDK-core: android.hardware.broadcastradio@1.0.so\nVNDK-core: android.hardware.broadcastradio@1.1.so\nVNDK-core: android.hardware.broadcastradio@2.0.so\nVNDK-core: android.hardware.camera.common@1.0.so\nVNDK-core: android.hardware.camera.device@1.0.so\nVNDK-core: android.hardware.camera.device@3.2.so\nVNDK-core: android.hardware.camera.device@3.3.so\nVNDK-core: android.hardware.camera.device@3.4.so\nVNDK-core: android.hardware.camera.metadata@3.2.so\nVNDK-core: android.hardware.camera.metadata@3.3.so\nVNDK-core: android.hardware.camera.provider@2.4.so\nVNDK-core: android.hardware.cas.native@1.0.so\nVNDK-core: android.hardware.cas@1.0.so\nVNDK-core: android.hardware.configstore-utils.so\nVNDK-core: android.hardware.configstore@1.0.so\nVNDK-core: android.hardware.configstore@1.1.so\nVNDK-core: android.hardware.confirmationui-support-lib.so\nVNDK-core: android.hardware.confirmationui@1.0.so\nVNDK-core: android.hardware.contexthub@1.0.so\nVNDK-core: android.hardware.drm@1.0.so\nVNDK-core: android.hardware.drm@1.1.so\nVNDK-core: android.hardware.dumpstate@1.0.so\nVNDK-core: android.hardware.gatekeeper@1.0.so\nVNDK-core: android.hardware.gnss@1.0.so\nVNDK-core: android.hardware.gnss@1.1.so\nVNDK-core: android.hardware.graphics.allocator@2.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-core: android.hardware.graphics.composer@2.1.so\nVNDK-core: android.hardware.graphics.composer@2.2.so\nVNDK-core: android.hardware.health@1.0.so\nVNDK-core: android.hardware.health@2.0.so\nVNDK-core: android.hardware.ir@1.0.so\nVNDK-core: android.hardware.keymaster@3.0.so\nVNDK-core: android.hardware.keymaster@4.0.so\nVNDK-core: android.hardware.light@2.0.so\nVNDK-core: android.hardware.media.bufferpool@1.0.so\nVNDK-core: android.hardware.media.omx@1.0.so\nVNDK-core: android.hardware.media@1.0.so\nVNDK-core: android.hardware.memtrack@1.0.so\nVNDK-core: android.hardware.neuralnetworks@1.0.so\nVNDK-core: android.hardware.neuralnetworks@1.1.so\nVNDK-core: android.hardware.nfc@1.0.so\nVNDK-core: android.hardware.nfc@1.1.so\nVNDK-core: android.hardware.oemlock@1.0.so\nVNDK-core: android.hardware.power@1.0.so\nVNDK-core: android.hardware.power@1.1.so\nVNDK-core: android.hardware.power@1.2.so\nVNDK-core: android.hardware.radio.config@1.0.so\nVNDK-core: android.hardware.radio.deprecated@1.0.so\nVNDK-core: android.hardware.radio@1.0.so\nVNDK-core: android.hardware.radio@1.1.so\nVNDK-core: android.hardware.radio@1.2.so\nVNDK-core: android.hardware.secure_element@1.0.so\nVNDK-core: android.hardware.sensors@1.0.so\nVNDK-core: android.hardware.soundtrigger@2.0.so\nVNDK-core: android.hardware.soundtrigger@2.0-core.so\nVNDK-core: android.hardware.soundtrigger@2.1.so\nVNDK-core: android.hardware.tetheroffload.config@1.0.so\nVNDK-core: android.hardware.tetheroffload.control@1.0.so\nVNDK-core: android.hardware.thermal@1.0.so\nVNDK-core: android.hardware.thermal@1.1.so\nVNDK-core: android.hardware.tv.cec@1.0.so\nVNDK-core: android.hardware.tv.input@1.0.so\nVNDK-core: android.hardware.usb.gadget@1.0.so\nVNDK-core: android.hardware.usb@1.0.so\nVNDK-core: android.hardware.usb@1.1.so\nVNDK-core: android.hardware.vibrator@1.0.so\nVNDK-core: android.hardware.vibrator@1.1.so\nVNDK-core: android.hardware.vibrator@1.2.so\nVNDK-core: android.hardware.vr@1.0.so\nVNDK-core: android.hardware.weaver@1.0.so\nVNDK-core: android.hardware.wifi.hostapd@1.0.so\nVNDK-core: android.hardware.wifi.offload@1.0.so\nVNDK-core: android.hardware.wifi.supplicant@1.0.so\nVNDK-core: android.hardware.wifi.supplicant@1.1.so\nVNDK-core: android.hardware.wifi@1.0.so\nVNDK-core: android.hardware.wifi@1.1.so\nVNDK-core: android.hardware.wifi@1.2.so\nVNDK-core: android.hidl.allocator@1.0.so\nVNDK-core: android.hidl.memory.block@1.0.so\nVNDK-core: android.hidl.token@1.0.so\nVNDK-core: android.hidl.token@1.0-utils.so\nVNDK-core: android.system.net.netd@1.0.so\nVNDK-core: android.system.net.netd@1.1.so\nVNDK-core: android.system.wifi.keystore@1.0.so\nVNDK-core: libadf.so\nVNDK-core: libaudioroute.so\nVNDK-core: libaudioutils.so\nVNDK-core: libbinder.so\nVNDK-core: libcamera_metadata.so\nVNDK-core: libcap.so\nVNDK-core: libcn-cbor.so\nVNDK-core: libcrypto.so\nVNDK-core: libcrypto_utils.so\nVNDK-core: libcurl.so\nVNDK-core: libdiskconfig.so\nVNDK-core: libdumpstateutil.so\nVNDK-core: libevent.so\nVNDK-core: libexif.so\nVNDK-core: libexpat.so\nVNDK-core: libfmq.so\nVNDK-core: libgatekeeper.so\nVNDK-core: libgui.so\nVNDK-core: libhardware_legacy.so\nVNDK-core: libhidlallocatorutils.so\nVNDK-core: libhidlcache.so\nVNDK-core: libjpeg.so\nVNDK-core: libkeymaster_messages.so\nVNDK-core: libkeymaster_portable.so\nVNDK-core: libldacBT_abr.so\nVNDK-core: libldacBT_enc.so\nVNDK-core: liblz4.so\nVNDK-core: libmedia_helper.so\nVNDK-core: libmedia_omx.so\nVNDK-core: libmemtrack.so\nVNDK-core: libminijail.so\nVNDK-core: libmkbootimg_abi_check.so\nVNDK-core: libnetutils.so\nVNDK-core: libnl.so\nVNDK-core: libopus.so\nVNDK-core: libpagemap.so\nVNDK-core: libpcre2.so\nVNDK-core: libpiex.so\nVNDK-core: libpng.so\nVNDK-core: libpower.so\nVNDK-core: libprocinfo.so\nVNDK-core: libprotobuf-cpp-full.so\nVNDK-core: libprotobuf-cpp-lite.so\nVNDK-core: libpuresoftkeymasterdevice.so\nVNDK-core: libradio_metadata.so\nVNDK-core: libselinux.so\nVNDK-core: libsoftkeymasterdevice.so\nVNDK-core: libspeexresampler.so\nVNDK-core: libsqlite.so\nVNDK-core: libssl.so\nVNDK-core: libstagefright_amrnb_common.so\nVNDK-core: libstagefright_bufferqueue_helper.so\nVNDK-core: libstagefright_enc_common.so\nVNDK-core: libstagefright_flacdec.so\nVNDK-core: libstagefright_foundation.so\nVNDK-core: libstagefright_omx.so\nVNDK-core: libstagefright_omx_utils.so\nVNDK-core: libstagefright_soft_aacdec.so\nVNDK-core: libstagefright_soft_aacenc.so\nVNDK-core: libstagefright_soft_amrdec.so\nVNDK-core: libstagefright_soft_amrnbenc.so\nVNDK-core: libstagefright_soft_amrwbenc.so\nVNDK-core: libstagefright_soft_avcdec.so\nVNDK-core: libstagefright_soft_avcenc.so\nVNDK-core: libstagefright_soft_flacdec.so\nVNDK-core: libstagefright_soft_flacenc.so\nVNDK-core: libstagefright_soft_g711dec.so\nVNDK-core: libstagefright_soft_gsmdec.so\nVNDK-core: libstagefright_soft_hevcdec.so\nVNDK-core: libstagefright_soft_mp3dec.so\nVNDK-core: libstagefright_soft_mpeg2dec.so\nVNDK-core: libstagefright_soft_mpeg4dec.so\nVNDK-core: libstagefright_soft_mpeg4enc.so\nVNDK-core: libstagefright_soft_opusdec.so\nVNDK-core: libstagefright_soft_rawdec.so\nVNDK-core: libstagefright_soft_vorbisdec.so\nVNDK-core: libstagefright_soft_vpxdec.so\nVNDK-core: libstagefright_soft_vpxenc.so\nVNDK-core: libstagefright_xmlparser.so\nVNDK-core: libsuspend.so\nVNDK-core: libsysutils.so\nVNDK-core: libtinyalsa.so\nVNDK-core: libtinyxml2.so\nVNDK-core: libui.so\nVNDK-core: libusbhost.so\nVNDK-core: libvixl-arm.so\nVNDK-core: libvixl-arm64.so\nVNDK-core: libvorbisidec.so\nVNDK-core: libwifi-system-iface.so\nVNDK-core: libxml2.so\nVNDK-core: libyuv.so\nVNDK-core: libziparchive.so\nVNDK-private: libbacktrace.so\nVNDK-private: libblas.so\nVNDK-private: libcompiler_rt.so\nVNDK-private: libft2.so\nVNDK-private: libgui.so\nVNDK-private: libunwind.so\n"
  },
  {
    "path": "target/product/gsi/29.txt",
    "content": "LLNDK: libEGL.so\nLLNDK: libGLESv1_CM.so\nLLNDK: libGLESv2.so\nLLNDK: libGLESv3.so\nLLNDK: libRS.so\nLLNDK: libandroid_net.so\nLLNDK: libc.so\nLLNDK: libcgrouprc.so\nLLNDK: libdl.so\nLLNDK: libft2.so\nLLNDK: liblog.so\nLLNDK: libm.so\nLLNDK: libmediandk.so\nLLNDK: libnativewindow.so\nLLNDK: libneuralnetworks.so\nLLNDK: libsync.so\nLLNDK: libvndksupport.so\nLLNDK: libvulkan.so\nVNDK-SP: android.hardware.graphics.common@1.0.so\nVNDK-SP: android.hardware.graphics.common@1.1.so\nVNDK-SP: android.hardware.graphics.common@1.2.so\nVNDK-SP: android.hardware.graphics.mapper@2.0.so\nVNDK-SP: android.hardware.graphics.mapper@2.1.so\nVNDK-SP: android.hardware.graphics.mapper@3.0.so\nVNDK-SP: android.hardware.renderscript@1.0.so\nVNDK-SP: android.hidl.memory.token@1.0.so\nVNDK-SP: android.hidl.memory@1.0.so\nVNDK-SP: android.hidl.memory@1.0-impl.so\nVNDK-SP: android.hidl.safe_union@1.0.so\nVNDK-SP: libRSCpuRef.so\nVNDK-SP: libRSDriver.so\nVNDK-SP: libRS_internal.so\nVNDK-SP: libbacktrace.so\nVNDK-SP: libbase.so\nVNDK-SP: libbcinfo.so\nVNDK-SP: libbinderthreadstate.so\nVNDK-SP: libblas.so\nVNDK-SP: libc++.so\nVNDK-SP: libcompiler_rt.so\nVNDK-SP: libcutils.so\nVNDK-SP: libhardware.so\nVNDK-SP: libhidlbase.so\nVNDK-SP: libhidlmemory.so\nVNDK-SP: libhidltransport.so\nVNDK-SP: libhwbinder.so\nVNDK-SP: libhwbinder_noltopgo.so\nVNDK-SP: libion.so\nVNDK-SP: libjsoncpp.so\nVNDK-SP: liblzma.so\nVNDK-SP: libprocessgroup.so\nVNDK-SP: libunwindstack.so\nVNDK-SP: libutils.so\nVNDK-SP: libutilscallstack.so\nVNDK-SP: libz.so\nVNDK-core: android.frameworks.cameraservice.common@2.0.so\nVNDK-core: android.frameworks.cameraservice.device@2.0.so\nVNDK-core: android.frameworks.cameraservice.service@2.0.so\nVNDK-core: android.frameworks.displayservice@1.0.so\nVNDK-core: android.frameworks.schedulerservice@1.0.so\nVNDK-core: android.frameworks.sensorservice@1.0.so\nVNDK-core: android.frameworks.stats@1.0.so\nVNDK-core: android.frameworks.vr.composer@1.0.so\nVNDK-core: android.hardware.atrace@1.0.so\nVNDK-core: android.hardware.audio.common@2.0.so\nVNDK-core: android.hardware.audio.common@4.0.so\nVNDK-core: android.hardware.audio.common@5.0.so\nVNDK-core: android.hardware.audio.effect@2.0.so\nVNDK-core: android.hardware.audio.effect@4.0.so\nVNDK-core: android.hardware.audio.effect@5.0.so\nVNDK-core: android.hardware.audio@2.0.so\nVNDK-core: android.hardware.audio@4.0.so\nVNDK-core: android.hardware.audio@5.0.so\nVNDK-core: android.hardware.authsecret@1.0.so\nVNDK-core: android.hardware.automotive.audiocontrol@1.0.so\nVNDK-core: android.hardware.automotive.evs@1.0.so\nVNDK-core: android.hardware.automotive.vehicle@2.0.so\nVNDK-core: android.hardware.biometrics.face@1.0.so\nVNDK-core: android.hardware.biometrics.fingerprint@2.1.so\nVNDK-core: android.hardware.bluetooth.a2dp@1.0.so\nVNDK-core: android.hardware.bluetooth.audio@2.0.so\nVNDK-core: android.hardware.bluetooth@1.0.so\nVNDK-core: android.hardware.boot@1.0.so\nVNDK-core: android.hardware.broadcastradio@1.0.so\nVNDK-core: android.hardware.broadcastradio@1.1.so\nVNDK-core: android.hardware.broadcastradio@2.0.so\nVNDK-core: android.hardware.camera.common@1.0.so\nVNDK-core: android.hardware.camera.device@1.0.so\nVNDK-core: android.hardware.camera.device@3.2.so\nVNDK-core: android.hardware.camera.device@3.3.so\nVNDK-core: android.hardware.camera.device@3.4.so\nVNDK-core: android.hardware.camera.device@3.5.so\nVNDK-core: android.hardware.camera.metadata@3.2.so\nVNDK-core: android.hardware.camera.metadata@3.3.so\nVNDK-core: android.hardware.camera.metadata@3.4.so\nVNDK-core: android.hardware.camera.provider@2.4.so\nVNDK-core: android.hardware.camera.provider@2.5.so\nVNDK-core: android.hardware.cas.native@1.0.so\nVNDK-core: android.hardware.cas@1.0.so\nVNDK-core: android.hardware.cas@1.1.so\nVNDK-core: android.hardware.configstore-utils.so\nVNDK-core: android.hardware.configstore@1.0.so\nVNDK-core: android.hardware.configstore@1.1.so\nVNDK-core: android.hardware.confirmationui-support-lib.so\nVNDK-core: android.hardware.confirmationui@1.0.so\nVNDK-core: android.hardware.contexthub@1.0.so\nVNDK-core: android.hardware.drm@1.0.so\nVNDK-core: android.hardware.drm@1.1.so\nVNDK-core: android.hardware.drm@1.2.so\nVNDK-core: android.hardware.dumpstate@1.0.so\nVNDK-core: android.hardware.fastboot@1.0.so\nVNDK-core: android.hardware.gatekeeper@1.0.so\nVNDK-core: android.hardware.gnss.measurement_corrections@1.0.so\nVNDK-core: android.hardware.gnss.visibility_control@1.0.so\nVNDK-core: android.hardware.gnss@1.0.so\nVNDK-core: android.hardware.gnss@1.1.so\nVNDK-core: android.hardware.gnss@2.0.so\nVNDK-core: android.hardware.graphics.allocator@2.0.so\nVNDK-core: android.hardware.graphics.allocator@3.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-core: android.hardware.graphics.composer@2.1.so\nVNDK-core: android.hardware.graphics.composer@2.2.so\nVNDK-core: android.hardware.graphics.composer@2.3.so\nVNDK-core: android.hardware.health.storage@1.0.so\nVNDK-core: android.hardware.health@1.0.so\nVNDK-core: android.hardware.health@2.0.so\nVNDK-core: android.hardware.input.classifier@1.0.so\nVNDK-core: android.hardware.input.common@1.0.so\nVNDK-core: android.hardware.ir@1.0.so\nVNDK-core: android.hardware.keymaster@3.0.so\nVNDK-core: android.hardware.keymaster@4.0.so\nVNDK-core: android.hardware.light@2.0.so\nVNDK-core: android.hardware.media.bufferpool@1.0.so\nVNDK-core: android.hardware.media.bufferpool@2.0.so\nVNDK-core: android.hardware.media.c2@1.0.so\nVNDK-core: android.hardware.media.omx@1.0.so\nVNDK-core: android.hardware.media@1.0.so\nVNDK-core: android.hardware.memtrack@1.0.so\nVNDK-core: android.hardware.neuralnetworks@1.0.so\nVNDK-core: android.hardware.neuralnetworks@1.1.so\nVNDK-core: android.hardware.neuralnetworks@1.2.so\nVNDK-core: android.hardware.nfc@1.0.so\nVNDK-core: android.hardware.nfc@1.1.so\nVNDK-core: android.hardware.nfc@1.2.so\nVNDK-core: android.hardware.oemlock@1.0.so\nVNDK-core: android.hardware.power.stats@1.0.so\nVNDK-core: android.hardware.power@1.0.so\nVNDK-core: android.hardware.power@1.1.so\nVNDK-core: android.hardware.power@1.2.so\nVNDK-core: android.hardware.power@1.3.so\nVNDK-core: android.hardware.radio.config@1.0.so\nVNDK-core: android.hardware.radio.config@1.1.so\nVNDK-core: android.hardware.radio.config@1.2.so\nVNDK-core: android.hardware.radio.deprecated@1.0.so\nVNDK-core: android.hardware.radio@1.0.so\nVNDK-core: android.hardware.radio@1.1.so\nVNDK-core: android.hardware.radio@1.2.so\nVNDK-core: android.hardware.radio@1.3.so\nVNDK-core: android.hardware.radio@1.4.so\nVNDK-core: android.hardware.secure_element@1.0.so\nVNDK-core: android.hardware.secure_element@1.1.so\nVNDK-core: android.hardware.sensors@1.0.so\nVNDK-core: android.hardware.sensors@2.0.so\nVNDK-core: android.hardware.soundtrigger@2.0.so\nVNDK-core: android.hardware.soundtrigger@2.0-core.so\nVNDK-core: android.hardware.soundtrigger@2.1.so\nVNDK-core: android.hardware.soundtrigger@2.2.so\nVNDK-core: android.hardware.tetheroffload.config@1.0.so\nVNDK-core: android.hardware.tetheroffload.control@1.0.so\nVNDK-core: android.hardware.thermal@1.0.so\nVNDK-core: android.hardware.thermal@1.1.so\nVNDK-core: android.hardware.thermal@2.0.so\nVNDK-core: android.hardware.tv.cec@1.0.so\nVNDK-core: android.hardware.tv.cec@2.0.so\nVNDK-core: android.hardware.tv.input@1.0.so\nVNDK-core: android.hardware.usb.gadget@1.0.so\nVNDK-core: android.hardware.usb@1.0.so\nVNDK-core: android.hardware.usb@1.1.so\nVNDK-core: android.hardware.usb@1.2.so\nVNDK-core: android.hardware.vibrator@1.0.so\nVNDK-core: android.hardware.vibrator@1.1.so\nVNDK-core: android.hardware.vibrator@1.2.so\nVNDK-core: android.hardware.vibrator@1.3.so\nVNDK-core: android.hardware.vr@1.0.so\nVNDK-core: android.hardware.weaver@1.0.so\nVNDK-core: android.hardware.wifi.hostapd@1.0.so\nVNDK-core: android.hardware.wifi.hostapd@1.1.so\nVNDK-core: android.hardware.wifi.offload@1.0.so\nVNDK-core: android.hardware.wifi.supplicant@1.0.so\nVNDK-core: android.hardware.wifi.supplicant@1.1.so\nVNDK-core: android.hardware.wifi.supplicant@1.2.so\nVNDK-core: android.hardware.wifi@1.0.so\nVNDK-core: android.hardware.wifi@1.1.so\nVNDK-core: android.hardware.wifi@1.2.so\nVNDK-core: android.hardware.wifi@1.3.so\nVNDK-core: android.hidl.allocator@1.0.so\nVNDK-core: android.hidl.memory.block@1.0.so\nVNDK-core: android.hidl.token@1.0.so\nVNDK-core: android.hidl.token@1.0-utils.so\nVNDK-core: android.system.net.netd@1.0.so\nVNDK-core: android.system.net.netd@1.1.so\nVNDK-core: android.system.suspend@1.0.so\nVNDK-core: android.system.wifi.keystore@1.0.so\nVNDK-core: libadf.so\nVNDK-core: libaudioroute.so\nVNDK-core: libaudioutils.so\nVNDK-core: libbinder.so\nVNDK-core: libcamera_metadata.so\nVNDK-core: libcap.so\nVNDK-core: libcn-cbor.so\nVNDK-core: libcodec2.so\nVNDK-core: libcrypto.so\nVNDK-core: libcrypto_utils.so\nVNDK-core: libcurl.so\nVNDK-core: libdiskconfig.so\nVNDK-core: libdumpstateutil.so\nVNDK-core: libevent.so\nVNDK-core: libexif.so\nVNDK-core: libexpat.so\nVNDK-core: libfmq.so\nVNDK-core: libgatekeeper.so\nVNDK-core: libgui.so\nVNDK-core: libhardware_legacy.so\nVNDK-core: libhidlallocatorutils.so\nVNDK-core: libhidlcache.so\nVNDK-core: libjpeg.so\nVNDK-core: libkeymaster_messages.so\nVNDK-core: libkeymaster_portable.so\nVNDK-core: libldacBT_abr.so\nVNDK-core: libldacBT_enc.so\nVNDK-core: liblz4.so\nVNDK-core: libmedia_helper.so\nVNDK-core: libmedia_omx.so\nVNDK-core: libmemtrack.so\nVNDK-core: libminijail.so\nVNDK-core: libmkbootimg_abi_check.so\nVNDK-core: libnetutils.so\nVNDK-core: libnl.so\nVNDK-core: libpcre2.so\nVNDK-core: libpiex.so\nVNDK-core: libpng.so\nVNDK-core: libpower.so\nVNDK-core: libprocinfo.so\nVNDK-core: libprotobuf-cpp-full.so\nVNDK-core: libprotobuf-cpp-lite.so\nVNDK-core: libpuresoftkeymasterdevice.so\nVNDK-core: libradio_metadata.so\nVNDK-core: libselinux.so\nVNDK-core: libsoftkeymasterdevice.so\nVNDK-core: libspeexresampler.so\nVNDK-core: libsqlite.so\nVNDK-core: libssl.so\nVNDK-core: libstagefright_bufferpool@2.0.so\nVNDK-core: libstagefright_bufferqueue_helper.so\nVNDK-core: libstagefright_foundation.so\nVNDK-core: libstagefright_omx.so\nVNDK-core: libstagefright_omx_utils.so\nVNDK-core: libstagefright_xmlparser.so\nVNDK-core: libsysutils.so\nVNDK-core: libtinyalsa.so\nVNDK-core: libtinyxml2.so\nVNDK-core: libui.so\nVNDK-core: libusbhost.so\nVNDK-core: libwifi-system-iface.so\nVNDK-core: libxml2.so\nVNDK-core: libyuv.so\nVNDK-core: libziparchive.so\nVNDK-private: libbacktrace.so\nVNDK-private: libbinderthreadstate.so\nVNDK-private: libblas.so\nVNDK-private: libcompiler_rt.so\nVNDK-private: libft2.so\nVNDK-private: libgui.so\n"
  },
  {
    "path": "target/product/gsi/30.txt",
    "content": "LLNDK: libEGL.so\nLLNDK: libGLESv1_CM.so\nLLNDK: libGLESv2.so\nLLNDK: libGLESv3.so\nLLNDK: libRS.so\nLLNDK: libandroid_net.so\nLLNDK: libbinder_ndk.so\nLLNDK: libc.so\nLLNDK: libcgrouprc.so\nLLNDK: libdl.so\nLLNDK: libft2.so\nLLNDK: liblog.so\nLLNDK: libm.so\nLLNDK: libmediandk.so\nLLNDK: libnativewindow.so\nLLNDK: libneuralnetworks.so\nLLNDK: libselinux.so\nLLNDK: libsync.so\nLLNDK: libvndksupport.so\nLLNDK: libvulkan.so\nVNDK-SP: android.hardware.common-V1-ndk_platform.so\nVNDK-SP: android.hardware.graphics.common-V1-ndk_platform.so\nVNDK-SP: android.hardware.graphics.common@1.0.so\nVNDK-SP: android.hardware.graphics.common@1.1.so\nVNDK-SP: android.hardware.graphics.common@1.2.so\nVNDK-SP: android.hardware.graphics.mapper@2.0.so\nVNDK-SP: android.hardware.graphics.mapper@2.1.so\nVNDK-SP: android.hardware.graphics.mapper@3.0.so\nVNDK-SP: android.hardware.graphics.mapper@4.0.so\nVNDK-SP: android.hardware.renderscript@1.0.so\nVNDK-SP: android.hidl.memory.token@1.0.so\nVNDK-SP: android.hidl.memory@1.0-impl.so\nVNDK-SP: android.hidl.memory@1.0.so\nVNDK-SP: android.hidl.safe_union@1.0.so\nVNDK-SP: libRSCpuRef.so\nVNDK-SP: libRSDriver.so\nVNDK-SP: libRS_internal.so\nVNDK-SP: libbacktrace.so\nVNDK-SP: libbase.so\nVNDK-SP: libbcinfo.so\nVNDK-SP: libblas.so\nVNDK-SP: libc++.so\nVNDK-SP: libcompiler_rt.so\nVNDK-SP: libcutils.so\nVNDK-SP: libgralloctypes.so\nVNDK-SP: libhardware.so\nVNDK-SP: libhidlbase.so\nVNDK-SP: libhidlmemory.so\nVNDK-SP: libion.so\nVNDK-SP: libjsoncpp.so\nVNDK-SP: liblzma.so\nVNDK-SP: libprocessgroup.so\nVNDK-SP: libunwindstack.so\nVNDK-SP: libutils.so\nVNDK-SP: libutilscallstack.so\nVNDK-SP: libz.so\nVNDK-core: android.frameworks.automotive.display@1.0.so\nVNDK-core: android.frameworks.cameraservice.common@2.0.so\nVNDK-core: android.frameworks.cameraservice.device@2.0.so\nVNDK-core: android.frameworks.cameraservice.service@2.0.so\nVNDK-core: android.frameworks.cameraservice.service@2.1.so\nVNDK-core: android.frameworks.displayservice@1.0.so\nVNDK-core: android.frameworks.schedulerservice@1.0.so\nVNDK-core: android.frameworks.sensorservice@1.0.so\nVNDK-core: android.frameworks.stats@1.0.so\nVNDK-core: android.hardware.atrace@1.0.so\nVNDK-core: android.hardware.audio.common@2.0.so\nVNDK-core: android.hardware.audio.common@4.0.so\nVNDK-core: android.hardware.audio.common@5.0.so\nVNDK-core: android.hardware.audio.common@6.0.so\nVNDK-core: android.hardware.audio.effect@2.0.so\nVNDK-core: android.hardware.audio.effect@4.0.so\nVNDK-core: android.hardware.audio.effect@5.0.so\nVNDK-core: android.hardware.audio.effect@6.0.so\nVNDK-core: android.hardware.audio@2.0.so\nVNDK-core: android.hardware.audio@4.0.so\nVNDK-core: android.hardware.audio@5.0.so\nVNDK-core: android.hardware.audio@6.0.so\nVNDK-core: android.hardware.authsecret@1.0.so\nVNDK-core: android.hardware.automotive.audiocontrol@1.0.so\nVNDK-core: android.hardware.automotive.audiocontrol@2.0.so\nVNDK-core: android.hardware.automotive.can@1.0.so\nVNDK-core: android.hardware.automotive.evs@1.0.so\nVNDK-core: android.hardware.automotive.evs@1.1.so\nVNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so\nVNDK-core: android.hardware.automotive.sv@1.0.so\nVNDK-core: android.hardware.automotive.vehicle@2.0.so\nVNDK-core: android.hardware.biometrics.face@1.0.so\nVNDK-core: android.hardware.biometrics.fingerprint@2.1.so\nVNDK-core: android.hardware.biometrics.fingerprint@2.2.so\nVNDK-core: android.hardware.bluetooth.a2dp@1.0.so\nVNDK-core: android.hardware.bluetooth.audio@2.0.so\nVNDK-core: android.hardware.bluetooth@1.0.so\nVNDK-core: android.hardware.bluetooth@1.1.so\nVNDK-core: android.hardware.boot@1.0.so\nVNDK-core: android.hardware.boot@1.1.so\nVNDK-core: android.hardware.broadcastradio@1.0.so\nVNDK-core: android.hardware.broadcastradio@1.1.so\nVNDK-core: android.hardware.broadcastradio@2.0.so\nVNDK-core: android.hardware.camera.common@1.0.so\nVNDK-core: android.hardware.camera.device@1.0.so\nVNDK-core: android.hardware.camera.device@3.2.so\nVNDK-core: android.hardware.camera.device@3.3.so\nVNDK-core: android.hardware.camera.device@3.4.so\nVNDK-core: android.hardware.camera.device@3.5.so\nVNDK-core: android.hardware.camera.device@3.6.so\nVNDK-core: android.hardware.camera.metadata@3.2.so\nVNDK-core: android.hardware.camera.metadata@3.3.so\nVNDK-core: android.hardware.camera.metadata@3.4.so\nVNDK-core: android.hardware.camera.metadata@3.5.so\nVNDK-core: android.hardware.camera.provider@2.4.so\nVNDK-core: android.hardware.camera.provider@2.5.so\nVNDK-core: android.hardware.camera.provider@2.6.so\nVNDK-core: android.hardware.cas.native@1.0.so\nVNDK-core: android.hardware.cas@1.0.so\nVNDK-core: android.hardware.cas@1.1.so\nVNDK-core: android.hardware.cas@1.2.so\nVNDK-core: android.hardware.configstore-utils.so\nVNDK-core: android.hardware.configstore@1.0.so\nVNDK-core: android.hardware.configstore@1.1.so\nVNDK-core: android.hardware.confirmationui-support-lib.so\nVNDK-core: android.hardware.confirmationui@1.0.so\nVNDK-core: android.hardware.contexthub@1.0.so\nVNDK-core: android.hardware.contexthub@1.1.so\nVNDK-core: android.hardware.drm@1.0.so\nVNDK-core: android.hardware.drm@1.1.so\nVNDK-core: android.hardware.drm@1.2.so\nVNDK-core: android.hardware.drm@1.3.so\nVNDK-core: android.hardware.dumpstate@1.0.so\nVNDK-core: android.hardware.dumpstate@1.1.so\nVNDK-core: android.hardware.fastboot@1.0.so\nVNDK-core: android.hardware.gatekeeper@1.0.so\nVNDK-core: android.hardware.gnss.measurement_corrections@1.0.so\nVNDK-core: android.hardware.gnss.measurement_corrections@1.1.so\nVNDK-core: android.hardware.gnss.visibility_control@1.0.so\nVNDK-core: android.hardware.gnss@1.0.so\nVNDK-core: android.hardware.gnss@1.1.so\nVNDK-core: android.hardware.gnss@2.0.so\nVNDK-core: android.hardware.gnss@2.1.so\nVNDK-core: android.hardware.graphics.allocator@2.0.so\nVNDK-core: android.hardware.graphics.allocator@3.0.so\nVNDK-core: android.hardware.graphics.allocator@4.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-core: android.hardware.graphics.composer@2.1.so\nVNDK-core: android.hardware.graphics.composer@2.2.so\nVNDK-core: android.hardware.graphics.composer@2.3.so\nVNDK-core: android.hardware.graphics.composer@2.4.so\nVNDK-core: android.hardware.health.storage@1.0.so\nVNDK-core: android.hardware.health@1.0.so\nVNDK-core: android.hardware.health@2.0.so\nVNDK-core: android.hardware.health@2.1.so\nVNDK-core: android.hardware.identity-V2-ndk_platform.so\nVNDK-core: android.hardware.input.classifier@1.0.so\nVNDK-core: android.hardware.input.common@1.0.so\nVNDK-core: android.hardware.ir@1.0.so\nVNDK-core: android.hardware.keymaster-V2-ndk_platform.so\nVNDK-core: android.hardware.keymaster@3.0.so\nVNDK-core: android.hardware.keymaster@4.0.so\nVNDK-core: android.hardware.keymaster@4.1.so\nVNDK-core: android.hardware.light-V1-ndk_platform.so\nVNDK-core: android.hardware.light@2.0.so\nVNDK-core: android.hardware.media.bufferpool@1.0.so\nVNDK-core: android.hardware.media.bufferpool@2.0.so\nVNDK-core: android.hardware.media.c2@1.0.so\nVNDK-core: android.hardware.media.c2@1.1.so\nVNDK-core: android.hardware.media.omx@1.0.so\nVNDK-core: android.hardware.media@1.0.so\nVNDK-core: android.hardware.memtrack@1.0.so\nVNDK-core: android.hardware.neuralnetworks@1.0.so\nVNDK-core: android.hardware.neuralnetworks@1.1.so\nVNDK-core: android.hardware.neuralnetworks@1.2.so\nVNDK-core: android.hardware.neuralnetworks@1.3.so\nVNDK-core: android.hardware.nfc@1.0.so\nVNDK-core: android.hardware.nfc@1.1.so\nVNDK-core: android.hardware.nfc@1.2.so\nVNDK-core: android.hardware.oemlock@1.0.so\nVNDK-core: android.hardware.power-V1-ndk_platform.so\nVNDK-core: android.hardware.power.stats@1.0.so\nVNDK-core: android.hardware.power@1.0.so\nVNDK-core: android.hardware.power@1.1.so\nVNDK-core: android.hardware.power@1.2.so\nVNDK-core: android.hardware.power@1.3.so\nVNDK-core: android.hardware.radio.config@1.0.so\nVNDK-core: android.hardware.radio.config@1.1.so\nVNDK-core: android.hardware.radio.config@1.2.so\nVNDK-core: android.hardware.radio.deprecated@1.0.so\nVNDK-core: android.hardware.radio@1.0.so\nVNDK-core: android.hardware.radio@1.1.so\nVNDK-core: android.hardware.radio@1.2.so\nVNDK-core: android.hardware.radio@1.3.so\nVNDK-core: android.hardware.radio@1.4.so\nVNDK-core: android.hardware.radio@1.5.so\nVNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so\nVNDK-core: android.hardware.secure_element@1.0.so\nVNDK-core: android.hardware.secure_element@1.1.so\nVNDK-core: android.hardware.secure_element@1.2.so\nVNDK-core: android.hardware.sensors@1.0.so\nVNDK-core: android.hardware.sensors@2.0.so\nVNDK-core: android.hardware.sensors@2.1.so\nVNDK-core: android.hardware.soundtrigger@2.0-core.so\nVNDK-core: android.hardware.soundtrigger@2.0.so\nVNDK-core: android.hardware.soundtrigger@2.1.so\nVNDK-core: android.hardware.soundtrigger@2.2.so\nVNDK-core: android.hardware.soundtrigger@2.3.so\nVNDK-core: android.hardware.tetheroffload.config@1.0.so\nVNDK-core: android.hardware.tetheroffload.control@1.0.so\nVNDK-core: android.hardware.thermal@1.0.so\nVNDK-core: android.hardware.thermal@1.1.so\nVNDK-core: android.hardware.thermal@2.0.so\nVNDK-core: android.hardware.tv.cec@1.0.so\nVNDK-core: android.hardware.tv.cec@2.0.so\nVNDK-core: android.hardware.tv.input@1.0.so\nVNDK-core: android.hardware.tv.tuner@1.0.so\nVNDK-core: android.hardware.usb.gadget@1.0.so\nVNDK-core: android.hardware.usb.gadget@1.1.so\nVNDK-core: android.hardware.usb@1.0.so\nVNDK-core: android.hardware.usb@1.1.so\nVNDK-core: android.hardware.usb@1.2.so\nVNDK-core: android.hardware.vibrator-V1-ndk_platform.so\nVNDK-core: android.hardware.vibrator@1.0.so\nVNDK-core: android.hardware.vibrator@1.1.so\nVNDK-core: android.hardware.vibrator@1.2.so\nVNDK-core: android.hardware.vibrator@1.3.so\nVNDK-core: android.hardware.vr@1.0.so\nVNDK-core: android.hardware.weaver@1.0.so\nVNDK-core: android.hardware.wifi.hostapd@1.0.so\nVNDK-core: android.hardware.wifi.hostapd@1.1.so\nVNDK-core: android.hardware.wifi.hostapd@1.2.so\nVNDK-core: android.hardware.wifi.offload@1.0.so\nVNDK-core: android.hardware.wifi.supplicant@1.0.so\nVNDK-core: android.hardware.wifi.supplicant@1.1.so\nVNDK-core: android.hardware.wifi.supplicant@1.2.so\nVNDK-core: android.hardware.wifi.supplicant@1.3.so\nVNDK-core: android.hardware.wifi@1.0.so\nVNDK-core: android.hardware.wifi@1.1.so\nVNDK-core: android.hardware.wifi@1.2.so\nVNDK-core: android.hardware.wifi@1.3.so\nVNDK-core: android.hardware.wifi@1.4.so\nVNDK-core: android.hidl.allocator@1.0.so\nVNDK-core: android.hidl.memory.block@1.0.so\nVNDK-core: android.hidl.token@1.0-utils.so\nVNDK-core: android.hidl.token@1.0.so\nVNDK-core: android.system.net.netd@1.0.so\nVNDK-core: android.system.net.netd@1.1.so\nVNDK-core: android.system.suspend@1.0.so\nVNDK-core: android.system.wifi.keystore@1.0.so\nVNDK-core: libadf.so\nVNDK-core: libaudioroute.so\nVNDK-core: libaudioutils.so\nVNDK-core: libbinder.so\nVNDK-core: libbufferqueueconverter.so\nVNDK-core: libcamera_metadata.so\nVNDK-core: libcap.so\nVNDK-core: libcn-cbor.so\nVNDK-core: libcodec2.so\nVNDK-core: libcrypto.so\nVNDK-core: libcrypto_utils.so\nVNDK-core: libcurl.so\nVNDK-core: libdiskconfig.so\nVNDK-core: libdumpstateutil.so\nVNDK-core: libevent.so\nVNDK-core: libexif.so\nVNDK-core: libexpat.so\nVNDK-core: libfmq.so\nVNDK-core: libgatekeeper.so\nVNDK-core: libgui.so\nVNDK-core: libhardware_legacy.so\nVNDK-core: libhidlallocatorutils.so\nVNDK-core: libjpeg.so\nVNDK-core: libldacBT_abr.so\nVNDK-core: libldacBT_enc.so\nVNDK-core: liblz4.so\nVNDK-core: libmedia_helper.so\nVNDK-core: libmedia_omx.so\nVNDK-core: libmemtrack.so\nVNDK-core: libminijail.so\nVNDK-core: libmkbootimg_abi_check.so\nVNDK-core: libnetutils.so\nVNDK-core: libnl.so\nVNDK-core: libpcre2.so\nVNDK-core: libpiex.so\nVNDK-core: libpng.so\nVNDK-core: libpower.so\nVNDK-core: libprocinfo.so\nVNDK-core: libradio_metadata.so\nVNDK-core: libspeexresampler.so\nVNDK-core: libsqlite.so\nVNDK-core: libssl.so\nVNDK-core: libstagefright_bufferpool@2.0.so\nVNDK-core: libstagefright_bufferqueue_helper.so\nVNDK-core: libstagefright_foundation.so\nVNDK-core: libstagefright_omx.so\nVNDK-core: libstagefright_omx_utils.so\nVNDK-core: libstagefright_xmlparser.so\nVNDK-core: libsysutils.so\nVNDK-core: libtinyalsa.so\nVNDK-core: libtinyxml2.so\nVNDK-core: libui.so\nVNDK-core: libusbhost.so\nVNDK-core: libwifi-system-iface.so\nVNDK-core: libxml2.so\nVNDK-core: libyuv.so\nVNDK-core: libziparchive.so\nVNDK-private: libbacktrace.so\nVNDK-private: libblas.so\nVNDK-private: libcompiler_rt.so\nVNDK-private: libft2.so\nVNDK-private: libgui.so\n"
  },
  {
    "path": "target/product/gsi/31.txt",
    "content": "LLNDK: libEGL.so\nLLNDK: libGLESv1_CM.so\nLLNDK: libGLESv2.so\nLLNDK: libGLESv3.so\nLLNDK: libRS.so\nLLNDK: libandroid_net.so\nLLNDK: libbinder_ndk.so\nLLNDK: libc.so\nLLNDK: libcgrouprc.so\nLLNDK: libdl.so\nLLNDK: libft2.so\nLLNDK: liblog.so\nLLNDK: libm.so\nLLNDK: libmediandk.so\nLLNDK: libnativewindow.so\nLLNDK: libneuralnetworks.so\nLLNDK: libselinux.so\nLLNDK: libsync.so\nLLNDK: libvndksupport.so\nLLNDK: libvulkan.so\nVNDK-SP: android.hardware.common-V2-ndk_platform.so\nVNDK-SP: android.hardware.common.fmq-V1-ndk_platform.so\nVNDK-SP: android.hardware.graphics.common-V2-ndk_platform.so\nVNDK-SP: android.hardware.graphics.common@1.0.so\nVNDK-SP: android.hardware.graphics.common@1.1.so\nVNDK-SP: android.hardware.graphics.common@1.2.so\nVNDK-SP: android.hardware.graphics.mapper@2.0.so\nVNDK-SP: android.hardware.graphics.mapper@2.1.so\nVNDK-SP: android.hardware.graphics.mapper@3.0.so\nVNDK-SP: android.hardware.graphics.mapper@4.0.so\nVNDK-SP: android.hardware.renderscript@1.0.so\nVNDK-SP: android.hidl.memory.token@1.0.so\nVNDK-SP: android.hidl.memory@1.0-impl.so\nVNDK-SP: android.hidl.memory@1.0.so\nVNDK-SP: android.hidl.safe_union@1.0.so\nVNDK-SP: libRSCpuRef.so\nVNDK-SP: libRSDriver.so\nVNDK-SP: libRS_internal.so\nVNDK-SP: libbacktrace.so\nVNDK-SP: libbase.so\nVNDK-SP: libbcinfo.so\nVNDK-SP: libblas.so\nVNDK-SP: libc++.so\nVNDK-SP: libcompiler_rt.so\nVNDK-SP: libcutils.so\nVNDK-SP: libdmabufheap.so\nVNDK-SP: libgralloctypes.so\nVNDK-SP: libhardware.so\nVNDK-SP: libhidlbase.so\nVNDK-SP: libhidlmemory.so\nVNDK-SP: libion.so\nVNDK-SP: libjsoncpp.so\nVNDK-SP: liblzma.so\nVNDK-SP: libprocessgroup.so\nVNDK-SP: libunwindstack.so\nVNDK-SP: libutils.so\nVNDK-SP: libutilscallstack.so\nVNDK-SP: libz.so\nVNDK-core: android.hardware.audio.common@2.0.so\nVNDK-core: android.hardware.authsecret-V1-ndk_platform.so\nVNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so\nVNDK-core: android.hardware.configstore-utils.so\nVNDK-core: android.hardware.configstore@1.0.so\nVNDK-core: android.hardware.configstore@1.1.so\nVNDK-core: android.hardware.confirmationui-support-lib.so\nVNDK-core: android.hardware.gnss-V1-ndk_platform.so\nVNDK-core: android.hardware.graphics.allocator@2.0.so\nVNDK-core: android.hardware.graphics.allocator@3.0.so\nVNDK-core: android.hardware.graphics.allocator@4.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-core: android.hardware.health.storage-V1-ndk_platform.so\nVNDK-core: android.hardware.identity-V3-ndk_platform.so\nVNDK-core: android.hardware.keymaster-V3-ndk_platform.so\nVNDK-core: android.hardware.light-V1-ndk_platform.so\nVNDK-core: android.hardware.media.bufferpool@2.0.so\nVNDK-core: android.hardware.media.omx@1.0.so\nVNDK-core: android.hardware.media@1.0.so\nVNDK-core: android.hardware.memtrack-V1-ndk_platform.so\nVNDK-core: android.hardware.memtrack@1.0.so\nVNDK-core: android.hardware.oemlock-V1-ndk_platform.so\nVNDK-core: android.hardware.power-V2-ndk_platform.so\nVNDK-core: android.hardware.power.stats-V1-ndk_platform.so\nVNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so\nVNDK-core: android.hardware.security.keymint-V1-ndk_platform.so\nVNDK-core: android.hardware.security.secureclock-V1-ndk_platform.so\nVNDK-core: android.hardware.security.sharedsecret-V1-ndk_platform.so\nVNDK-core: android.hardware.soundtrigger@2.0-core.so\nVNDK-core: android.hardware.soundtrigger@2.0.so\nVNDK-core: android.hardware.vibrator-V2-ndk_platform.so\nVNDK-core: android.hardware.weaver-V1-ndk_platform.so\nVNDK-core: android.hidl.token@1.0-utils.so\nVNDK-core: android.hidl.token@1.0.so\nVNDK-core: android.system.keystore2-V1-ndk_platform.so\nVNDK-core: android.system.suspend@1.0.so\nVNDK-core: libaudioroute.so\nVNDK-core: libaudioutils.so\nVNDK-core: libbinder.so\nVNDK-core: libbufferqueueconverter.so\nVNDK-core: libcamera_metadata.so\nVNDK-core: libcap.so\nVNDK-core: libcn-cbor.so\nVNDK-core: libcodec2.so\nVNDK-core: libcrypto.so\nVNDK-core: libcrypto_utils.so\nVNDK-core: libcurl.so\nVNDK-core: libdiskconfig.so\nVNDK-core: libdumpstateutil.so\nVNDK-core: libevent.so\nVNDK-core: libexif.so\nVNDK-core: libexpat.so\nVNDK-core: libfmq.so\nVNDK-core: libgatekeeper.so\nVNDK-core: libgui.so\nVNDK-core: libhardware_legacy.so\nVNDK-core: libhidlallocatorutils.so\nVNDK-core: libjpeg.so\nVNDK-core: libldacBT_abr.so\nVNDK-core: libldacBT_enc.so\nVNDK-core: liblz4.so\nVNDK-core: libmedia_helper.so\nVNDK-core: libmedia_omx.so\nVNDK-core: libmemtrack.so\nVNDK-core: libminijail.so\nVNDK-core: libmkbootimg_abi_check.so\nVNDK-core: libnetutils.so\nVNDK-core: libnl.so\nVNDK-core: libpcre2.so\nVNDK-core: libpiex.so\nVNDK-core: libpng.so\nVNDK-core: libpower.so\nVNDK-core: libprocinfo.so\nVNDK-core: libradio_metadata.so\nVNDK-core: libspeexresampler.so\nVNDK-core: libsqlite.so\nVNDK-core: libssl.so\nVNDK-core: libstagefright_bufferpool@2.0.so\nVNDK-core: libstagefright_bufferqueue_helper.so\nVNDK-core: libstagefright_foundation.so\nVNDK-core: libstagefright_omx.so\nVNDK-core: libstagefright_omx_utils.so\nVNDK-core: libstagefright_xmlparser.so\nVNDK-core: libsysutils.so\nVNDK-core: libtinyalsa.so\nVNDK-core: libtinyxml2.so\nVNDK-core: libui.so\nVNDK-core: libusbhost.so\nVNDK-core: libwifi-system-iface.so\nVNDK-core: libxml2.so\nVNDK-core: libyuv.so\nVNDK-core: libziparchive.so\nVNDK-private: libbacktrace.so\nVNDK-private: libblas.so\nVNDK-private: libcompiler_rt.so\nVNDK-private: libft2.so\nVNDK-private: libgui.so\nVNDK-product: android.hardware.audio.common@2.0.so\nVNDK-product: android.hardware.configstore@1.0.so\nVNDK-product: android.hardware.configstore@1.1.so\nVNDK-product: android.hardware.graphics.allocator@2.0.so\nVNDK-product: android.hardware.graphics.allocator@3.0.so\nVNDK-product: android.hardware.graphics.allocator@4.0.so\nVNDK-product: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-product: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-product: android.hardware.graphics.common@1.0.so\nVNDK-product: android.hardware.graphics.common@1.1.so\nVNDK-product: android.hardware.graphics.common@1.2.so\nVNDK-product: android.hardware.graphics.mapper@2.0.so\nVNDK-product: android.hardware.graphics.mapper@2.1.so\nVNDK-product: android.hardware.graphics.mapper@3.0.so\nVNDK-product: android.hardware.graphics.mapper@4.0.so\nVNDK-product: android.hardware.media.bufferpool@2.0.so\nVNDK-product: android.hardware.media.omx@1.0.so\nVNDK-product: android.hardware.media@1.0.so\nVNDK-product: android.hardware.memtrack@1.0.so\nVNDK-product: android.hardware.renderscript@1.0.so\nVNDK-product: android.hardware.soundtrigger@2.0.so\nVNDK-product: android.hidl.memory.token@1.0.so\nVNDK-product: android.hidl.memory@1.0.so\nVNDK-product: android.hidl.safe_union@1.0.so\nVNDK-product: android.hidl.token@1.0.so\nVNDK-product: android.system.suspend@1.0.so\nVNDK-product: libaudioutils.so\nVNDK-product: libbacktrace.so\nVNDK-product: libbase.so\nVNDK-product: libc++.so\nVNDK-product: libcamera_metadata.so\nVNDK-product: libcap.so\nVNDK-product: libcompiler_rt.so\nVNDK-product: libcrypto.so\nVNDK-product: libcurl.so\nVNDK-product: libcutils.so\nVNDK-product: libevent.so\nVNDK-product: libexpat.so\nVNDK-product: libfmq.so\nVNDK-product: libhidlbase.so\nVNDK-product: libhidlmemory.so\nVNDK-product: libion.so\nVNDK-product: libjpeg.so\nVNDK-product: libjsoncpp.so\nVNDK-product: libldacBT_abr.so\nVNDK-product: libldacBT_enc.so\nVNDK-product: liblz4.so\nVNDK-product: liblzma.so\nVNDK-product: libminijail.so\nVNDK-product: libnl.so\nVNDK-product: libpcre2.so\nVNDK-product: libpiex.so\nVNDK-product: libpng.so\nVNDK-product: libprocessgroup.so\nVNDK-product: libprocinfo.so\nVNDK-product: libspeexresampler.so\nVNDK-product: libssl.so\nVNDK-product: libtinyalsa.so\nVNDK-product: libtinyxml2.so\nVNDK-product: libunwindstack.so\nVNDK-product: libutils.so\nVNDK-product: libutilscallstack.so\nVNDK-product: libwifi-system-iface.so\nVNDK-product: libxml2.so\nVNDK-product: libyuv.so\nVNDK-product: libz.so\nVNDK-product: libziparchive.so\n"
  },
  {
    "path": "target/product/gsi/32.txt",
    "content": "LLNDK: libEGL.so\nLLNDK: libGLESv1_CM.so\nLLNDK: libGLESv2.so\nLLNDK: libGLESv3.so\nLLNDK: libRS.so\nLLNDK: libandroid_net.so\nLLNDK: libbinder_ndk.so\nLLNDK: libc.so\nLLNDK: libcgrouprc.so\nLLNDK: libdl.so\nLLNDK: libft2.so\nLLNDK: liblog.so\nLLNDK: libm.so\nLLNDK: libmediandk.so\nLLNDK: libnativewindow.so\nLLNDK: libneuralnetworks.so\nLLNDK: libselinux.so\nLLNDK: libsync.so\nLLNDK: libvndksupport.so\nLLNDK: libvulkan.so\nVNDK-SP: android.hardware.common-V2-ndk_platform.so\nVNDK-SP: android.hardware.common.fmq-V1-ndk_platform.so\nVNDK-SP: android.hardware.graphics.common-V2-ndk_platform.so\nVNDK-SP: android.hardware.graphics.common@1.0.so\nVNDK-SP: android.hardware.graphics.common@1.1.so\nVNDK-SP: android.hardware.graphics.common@1.2.so\nVNDK-SP: android.hardware.graphics.mapper@2.0.so\nVNDK-SP: android.hardware.graphics.mapper@2.1.so\nVNDK-SP: android.hardware.graphics.mapper@3.0.so\nVNDK-SP: android.hardware.graphics.mapper@4.0.so\nVNDK-SP: android.hardware.renderscript@1.0.so\nVNDK-SP: android.hidl.memory.token@1.0.so\nVNDK-SP: android.hidl.memory@1.0-impl.so\nVNDK-SP: android.hidl.memory@1.0.so\nVNDK-SP: android.hidl.safe_union@1.0.so\nVNDK-SP: libRSCpuRef.so\nVNDK-SP: libRSDriver.so\nVNDK-SP: libRS_internal.so\nVNDK-SP: libbacktrace.so\nVNDK-SP: libbase.so\nVNDK-SP: libbcinfo.so\nVNDK-SP: libblas.so\nVNDK-SP: libc++.so\nVNDK-SP: libcompiler_rt.so\nVNDK-SP: libcutils.so\nVNDK-SP: libdmabufheap.so\nVNDK-SP: libgralloctypes.so\nVNDK-SP: libhardware.so\nVNDK-SP: libhidlbase.so\nVNDK-SP: libhidlmemory.so\nVNDK-SP: libion.so\nVNDK-SP: libjsoncpp.so\nVNDK-SP: liblzma.so\nVNDK-SP: libprocessgroup.so\nVNDK-SP: libunwindstack.so\nVNDK-SP: libutils.so\nVNDK-SP: libutilscallstack.so\nVNDK-SP: libz.so\nVNDK-core: android.hardware.audio.common@2.0.so\nVNDK-core: android.hardware.authsecret-V1-ndk_platform.so\nVNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so\nVNDK-core: android.hardware.configstore-utils.so\nVNDK-core: android.hardware.configstore@1.0.so\nVNDK-core: android.hardware.configstore@1.1.so\nVNDK-core: android.hardware.confirmationui-support-lib.so\nVNDK-core: android.hardware.gnss-V1-ndk_platform.so\nVNDK-core: android.hardware.graphics.allocator@2.0.so\nVNDK-core: android.hardware.graphics.allocator@3.0.so\nVNDK-core: android.hardware.graphics.allocator@4.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-core: android.hardware.health.storage-V1-ndk_platform.so\nVNDK-core: android.hardware.identity-V3-ndk_platform.so\nVNDK-core: android.hardware.keymaster-V3-ndk_platform.so\nVNDK-core: android.hardware.light-V1-ndk_platform.so\nVNDK-core: android.hardware.media.bufferpool@2.0.so\nVNDK-core: android.hardware.media.omx@1.0.so\nVNDK-core: android.hardware.media@1.0.so\nVNDK-core: android.hardware.memtrack-V1-ndk_platform.so\nVNDK-core: android.hardware.memtrack@1.0.so\nVNDK-core: android.hardware.oemlock-V1-ndk_platform.so\nVNDK-core: android.hardware.power-V2-ndk_platform.so\nVNDK-core: android.hardware.power.stats-V1-ndk_platform.so\nVNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so\nVNDK-core: android.hardware.security.keymint-V1-ndk_platform.so\nVNDK-core: android.hardware.security.secureclock-V1-ndk_platform.so\nVNDK-core: android.hardware.security.sharedsecret-V1-ndk_platform.so\nVNDK-core: android.hardware.soundtrigger@2.0-core.so\nVNDK-core: android.hardware.soundtrigger@2.0.so\nVNDK-core: android.hardware.vibrator-V2-ndk_platform.so\nVNDK-core: android.hardware.weaver-V1-ndk_platform.so\nVNDK-core: android.hidl.token@1.0-utils.so\nVNDK-core: android.hidl.token@1.0.so\nVNDK-core: android.system.keystore2-V1-ndk_platform.so\nVNDK-core: android.system.suspend@1.0.so\nVNDK-core: libaudioroute.so\nVNDK-core: libaudioutils.so\nVNDK-core: libbinder.so\nVNDK-core: libbufferqueueconverter.so\nVNDK-core: libcamera_metadata.so\nVNDK-core: libcap.so\nVNDK-core: libcn-cbor.so\nVNDK-core: libcodec2.so\nVNDK-core: libcrypto.so\nVNDK-core: libcrypto_utils.so\nVNDK-core: libcurl.so\nVNDK-core: libdiskconfig.so\nVNDK-core: libdumpstateutil.so\nVNDK-core: libevent.so\nVNDK-core: libexif.so\nVNDK-core: libexpat.so\nVNDK-core: libfmq.so\nVNDK-core: libgatekeeper.so\nVNDK-core: libgui.so\nVNDK-core: libhardware_legacy.so\nVNDK-core: libhidlallocatorutils.so\nVNDK-core: libjpeg.so\nVNDK-core: libldacBT_abr.so\nVNDK-core: libldacBT_enc.so\nVNDK-core: liblz4.so\nVNDK-core: libmedia_helper.so\nVNDK-core: libmedia_omx.so\nVNDK-core: libmemtrack.so\nVNDK-core: libminijail.so\nVNDK-core: libmkbootimg_abi_check.so\nVNDK-core: libnetutils.so\nVNDK-core: libnl.so\nVNDK-core: libpcre2.so\nVNDK-core: libpiex.so\nVNDK-core: libpng.so\nVNDK-core: libpower.so\nVNDK-core: libprocinfo.so\nVNDK-core: libradio_metadata.so\nVNDK-core: libspeexresampler.so\nVNDK-core: libsqlite.so\nVNDK-core: libssl.so\nVNDK-core: libstagefright_bufferpool@2.0.so\nVNDK-core: libstagefright_bufferqueue_helper.so\nVNDK-core: libstagefright_foundation.so\nVNDK-core: libstagefright_omx.so\nVNDK-core: libstagefright_omx_utils.so\nVNDK-core: libstagefright_xmlparser.so\nVNDK-core: libsysutils.so\nVNDK-core: libtinyalsa.so\nVNDK-core: libtinyxml2.so\nVNDK-core: libui.so\nVNDK-core: libusbhost.so\nVNDK-core: libwifi-system-iface.so\nVNDK-core: libxml2.so\nVNDK-core: libyuv.so\nVNDK-core: libziparchive.so\nVNDK-private: libbacktrace.so\nVNDK-private: libblas.so\nVNDK-private: libcompiler_rt.so\nVNDK-private: libft2.so\nVNDK-private: libgui.so\nVNDK-product: android.hardware.audio.common@2.0.so\nVNDK-product: android.hardware.configstore@1.0.so\nVNDK-product: android.hardware.configstore@1.1.so\nVNDK-product: android.hardware.graphics.allocator@2.0.so\nVNDK-product: android.hardware.graphics.allocator@3.0.so\nVNDK-product: android.hardware.graphics.allocator@4.0.so\nVNDK-product: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-product: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-product: android.hardware.graphics.common@1.0.so\nVNDK-product: android.hardware.graphics.common@1.1.so\nVNDK-product: android.hardware.graphics.common@1.2.so\nVNDK-product: android.hardware.graphics.mapper@2.0.so\nVNDK-product: android.hardware.graphics.mapper@2.1.so\nVNDK-product: android.hardware.graphics.mapper@3.0.so\nVNDK-product: android.hardware.graphics.mapper@4.0.so\nVNDK-product: android.hardware.media.bufferpool@2.0.so\nVNDK-product: android.hardware.media.omx@1.0.so\nVNDK-product: android.hardware.media@1.0.so\nVNDK-product: android.hardware.memtrack@1.0.so\nVNDK-product: android.hardware.renderscript@1.0.so\nVNDK-product: android.hardware.soundtrigger@2.0.so\nVNDK-product: android.hidl.memory.token@1.0.so\nVNDK-product: android.hidl.memory@1.0.so\nVNDK-product: android.hidl.safe_union@1.0.so\nVNDK-product: android.hidl.token@1.0.so\nVNDK-product: android.system.suspend@1.0.so\nVNDK-product: libaudioutils.so\nVNDK-product: libbacktrace.so\nVNDK-product: libbase.so\nVNDK-product: libc++.so\nVNDK-product: libcamera_metadata.so\nVNDK-product: libcap.so\nVNDK-product: libcompiler_rt.so\nVNDK-product: libcrypto.so\nVNDK-product: libcurl.so\nVNDK-product: libcutils.so\nVNDK-product: libevent.so\nVNDK-product: libexpat.so\nVNDK-product: libfmq.so\nVNDK-product: libhidlbase.so\nVNDK-product: libhidlmemory.so\nVNDK-product: libion.so\nVNDK-product: libjpeg.so\nVNDK-product: libjsoncpp.so\nVNDK-product: libldacBT_abr.so\nVNDK-product: libldacBT_enc.so\nVNDK-product: liblz4.so\nVNDK-product: liblzma.so\nVNDK-product: libminijail.so\nVNDK-product: libnl.so\nVNDK-product: libpcre2.so\nVNDK-product: libpiex.so\nVNDK-product: libpng.so\nVNDK-product: libprocessgroup.so\nVNDK-product: libprocinfo.so\nVNDK-product: libspeexresampler.so\nVNDK-product: libssl.so\nVNDK-product: libtinyalsa.so\nVNDK-product: libtinyxml2.so\nVNDK-product: libunwindstack.so\nVNDK-product: libutils.so\nVNDK-product: libutilscallstack.so\nVNDK-product: libwifi-system-iface.so\nVNDK-product: libxml2.so\nVNDK-product: libyuv.so\nVNDK-product: libz.so\nVNDK-product: libziparchive.so\n"
  },
  {
    "path": "target/product/gsi/33.txt",
    "content": "LLNDK: libEGL.so\nLLNDK: libGLESv1_CM.so\nLLNDK: libGLESv2.so\nLLNDK: libGLESv3.so\nLLNDK: libRS.so\nLLNDK: libandroid_net.so\nLLNDK: libbinder_ndk.so\nLLNDK: libc.so\nLLNDK: libcgrouprc.so\nLLNDK: libdl.so\nLLNDK: libft2.so\nLLNDK: liblog.so\nLLNDK: libm.so\nLLNDK: libmediandk.so\nLLNDK: libnativewindow.so\nLLNDK: libneuralnetworks.so\nLLNDK: libselinux.so\nLLNDK: libsync.so\nLLNDK: libvndksupport.so\nLLNDK: libvulkan.so\nVNDK-SP: android.hardware.common-V2-ndk.so\nVNDK-SP: android.hardware.common.fmq-V1-ndk.so\nVNDK-SP: android.hardware.graphics.allocator-V1-ndk.so\nVNDK-SP: android.hardware.graphics.common-V3-ndk.so\nVNDK-SP: android.hardware.graphics.common@1.0.so\nVNDK-SP: android.hardware.graphics.common@1.1.so\nVNDK-SP: android.hardware.graphics.common@1.2.so\nVNDK-SP: android.hardware.graphics.composer3-V1-ndk.so\nVNDK-SP: android.hardware.graphics.mapper@2.0.so\nVNDK-SP: android.hardware.graphics.mapper@2.1.so\nVNDK-SP: android.hardware.graphics.mapper@3.0.so\nVNDK-SP: android.hardware.graphics.mapper@4.0.so\nVNDK-SP: android.hardware.renderscript@1.0.so\nVNDK-SP: android.hidl.memory.token@1.0.so\nVNDK-SP: android.hidl.memory@1.0-impl.so\nVNDK-SP: android.hidl.memory@1.0.so\nVNDK-SP: android.hidl.safe_union@1.0.so\nVNDK-SP: libRSCpuRef.so\nVNDK-SP: libRSDriver.so\nVNDK-SP: libRS_internal.so\nVNDK-SP: libbacktrace.so\nVNDK-SP: libbase.so\nVNDK-SP: libbcinfo.so\nVNDK-SP: libblas.so\nVNDK-SP: libc++.so\nVNDK-SP: libcompiler_rt.so\nVNDK-SP: libcutils.so\nVNDK-SP: libdmabufheap.so\nVNDK-SP: libgralloctypes.so\nVNDK-SP: libhardware.so\nVNDK-SP: libhidlbase.so\nVNDK-SP: libhidlmemory.so\nVNDK-SP: libion.so\nVNDK-SP: libjsoncpp.so\nVNDK-SP: liblzma.so\nVNDK-SP: libprocessgroup.so\nVNDK-SP: libunwindstack.so\nVNDK-SP: libutils.so\nVNDK-SP: libutilscallstack.so\nVNDK-SP: libz.so\nVNDK-core: android.hardware.audio.common-V1-ndk.so\nVNDK-core: android.hardware.audio.common@2.0.so\nVNDK-core: android.hardware.authsecret-V1-ndk.so\nVNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk.so\nVNDK-core: android.hardware.bluetooth.audio-V2-ndk.so\nVNDK-core: android.hardware.camera.common-V1-ndk.so\nVNDK-core: android.hardware.camera.device-V1-ndk.so\nVNDK-core: android.hardware.camera.metadata-V1-ndk.so\nVNDK-core: android.hardware.camera.provider-V1-ndk.so\nVNDK-core: android.hardware.configstore-utils.so\nVNDK-core: android.hardware.configstore@1.0.so\nVNDK-core: android.hardware.configstore@1.1.so\nVNDK-core: android.hardware.confirmationui-support-lib.so\nVNDK-core: android.hardware.drm-V1-ndk.so\nVNDK-core: android.hardware.dumpstate-V1-ndk.so\nVNDK-core: android.hardware.gnss-V2-ndk.so\nVNDK-core: android.hardware.graphics.allocator@2.0.so\nVNDK-core: android.hardware.graphics.allocator@3.0.so\nVNDK-core: android.hardware.graphics.allocator@4.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-core: android.hardware.health-V1-ndk.so\nVNDK-core: android.hardware.health.storage-V1-ndk.so\nVNDK-core: android.hardware.identity-V4-ndk.so\nVNDK-core: android.hardware.ir-V1-ndk.so\nVNDK-core: android.hardware.keymaster-V3-ndk.so\nVNDK-core: android.hardware.light-V2-ndk.so\nVNDK-core: android.hardware.media.bufferpool@2.0.so\nVNDK-core: android.hardware.media.omx@1.0.so\nVNDK-core: android.hardware.media@1.0.so\nVNDK-core: android.hardware.memtrack-V1-ndk.so\nVNDK-core: android.hardware.memtrack@1.0.so\nVNDK-core: android.hardware.nfc-V1-ndk.so\nVNDK-core: android.hardware.oemlock-V1-ndk.so\nVNDK-core: android.hardware.power-V3-ndk.so\nVNDK-core: android.hardware.power.stats-V1-ndk.so\nVNDK-core: android.hardware.radio-V1-ndk.so\nVNDK-core: android.hardware.radio.config-V1-ndk.so\nVNDK-core: android.hardware.radio.data-V1-ndk.so\nVNDK-core: android.hardware.radio.messaging-V1-ndk.so\nVNDK-core: android.hardware.radio.modem-V1-ndk.so\nVNDK-core: android.hardware.radio.network-V1-ndk.so\nVNDK-core: android.hardware.radio.sim-V1-ndk.so\nVNDK-core: android.hardware.radio.voice-V1-ndk.so\nVNDK-core: android.hardware.rebootescrow-V1-ndk.so\nVNDK-core: android.hardware.security.dice-V1-ndk.so\nVNDK-core: android.hardware.security.keymint-V2-ndk.so\nVNDK-core: android.hardware.security.secureclock-V1-ndk.so\nVNDK-core: android.hardware.security.sharedsecret-V1-ndk.so\nVNDK-core: android.hardware.sensors-V1-ndk.so\nVNDK-core: android.hardware.soundtrigger3-V1-ndk.so\nVNDK-core: android.hardware.soundtrigger@2.0-core.so\nVNDK-core: android.hardware.soundtrigger@2.0.so\nVNDK-core: android.hardware.usb-V1-ndk.so\nVNDK-core: android.hardware.uwb-V1-ndk.so\nVNDK-core: android.hardware.vibrator-V2-ndk.so\nVNDK-core: android.hardware.weaver-V1-ndk.so\nVNDK-core: android.hardware.wifi.hostapd-V1-ndk.so\nVNDK-core: android.hardware.wifi.supplicant-V1-ndk.so\nVNDK-core: android.hidl.token@1.0-utils.so\nVNDK-core: android.hidl.token@1.0.so\nVNDK-core: android.media.audio.common.types-V1-ndk.so\nVNDK-core: android.media.soundtrigger.types-V1-ndk.so\nVNDK-core: android.system.keystore2-V2-ndk.so\nVNDK-core: android.system.suspend-V1-ndk.so\nVNDK-core: android.system.suspend@1.0.so\nVNDK-core: libaudioroute.so\nVNDK-core: libaudioutils.so\nVNDK-core: libbinder.so\nVNDK-core: libbufferqueueconverter.so\nVNDK-core: libcamera_metadata.so\nVNDK-core: libcap.so\nVNDK-core: libcn-cbor.so\nVNDK-core: libcodec2.so\nVNDK-core: libcrypto.so\nVNDK-core: libcrypto_utils.so\nVNDK-core: libcurl.so\nVNDK-core: libdiskconfig.so\nVNDK-core: libdumpstateutil.so\nVNDK-core: libevent.so\nVNDK-core: libexif.so\nVNDK-core: libexpat.so\nVNDK-core: libfmq.so\nVNDK-core: libgatekeeper.so\nVNDK-core: libgui.so\nVNDK-core: libhardware_legacy.so\nVNDK-core: libhidlallocatorutils.so\nVNDK-core: libjpeg.so\nVNDK-core: libldacBT_abr.so\nVNDK-core: libldacBT_enc.so\nVNDK-core: liblz4.so\nVNDK-core: libmedia_helper.so\nVNDK-core: libmedia_omx.so\nVNDK-core: libmemtrack.so\nVNDK-core: libminijail.so\nVNDK-core: libmkbootimg_abi_check.so\nVNDK-core: libnetutils.so\nVNDK-core: libnl.so\nVNDK-core: libpcre2.so\nVNDK-core: libpiex.so\nVNDK-core: libpng.so\nVNDK-core: libpower.so\nVNDK-core: libprocinfo.so\nVNDK-core: libradio_metadata.so\nVNDK-core: libspeexresampler.so\nVNDK-core: libsqlite.so\nVNDK-core: libssl.so\nVNDK-core: libstagefright_bufferpool@2.0.so\nVNDK-core: libstagefright_bufferqueue_helper.so\nVNDK-core: libstagefright_foundation.so\nVNDK-core: libstagefright_omx.so\nVNDK-core: libstagefright_omx_utils.so\nVNDK-core: libstagefright_xmlparser.so\nVNDK-core: libsysutils.so\nVNDK-core: libtinyalsa.so\nVNDK-core: libtinyxml2.so\nVNDK-core: libui.so\nVNDK-core: libusbhost.so\nVNDK-core: libwifi-system-iface.so\nVNDK-core: libxml2.so\nVNDK-core: libyuv.so\nVNDK-core: libziparchive.so\nVNDK-private: libbacktrace.so\nVNDK-private: libblas.so\nVNDK-private: libcompiler_rt.so\nVNDK-private: libft2.so\nVNDK-private: libgui.so\nVNDK-product: android.hardware.audio.common@2.0.so\nVNDK-product: android.hardware.configstore@1.0.so\nVNDK-product: android.hardware.configstore@1.1.so\nVNDK-product: android.hardware.graphics.allocator@2.0.so\nVNDK-product: android.hardware.graphics.allocator@3.0.so\nVNDK-product: android.hardware.graphics.allocator@4.0.so\nVNDK-product: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-product: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-product: android.hardware.graphics.common@1.0.so\nVNDK-product: android.hardware.graphics.common@1.1.so\nVNDK-product: android.hardware.graphics.common@1.2.so\nVNDK-product: android.hardware.graphics.mapper@2.0.so\nVNDK-product: android.hardware.graphics.mapper@2.1.so\nVNDK-product: android.hardware.graphics.mapper@3.0.so\nVNDK-product: android.hardware.graphics.mapper@4.0.so\nVNDK-product: android.hardware.media.bufferpool@2.0.so\nVNDK-product: android.hardware.media.omx@1.0.so\nVNDK-product: android.hardware.media@1.0.so\nVNDK-product: android.hardware.memtrack@1.0.so\nVNDK-product: android.hardware.renderscript@1.0.so\nVNDK-product: android.hardware.soundtrigger@2.0.so\nVNDK-product: android.hidl.memory.token@1.0.so\nVNDK-product: android.hidl.memory@1.0.so\nVNDK-product: android.hidl.safe_union@1.0.so\nVNDK-product: android.hidl.token@1.0.so\nVNDK-product: android.system.suspend@1.0.so\nVNDK-product: libaudioutils.so\nVNDK-product: libbacktrace.so\nVNDK-product: libbase.so\nVNDK-product: libc++.so\nVNDK-product: libcamera_metadata.so\nVNDK-product: libcap.so\nVNDK-product: libcompiler_rt.so\nVNDK-product: libcrypto.so\nVNDK-product: libcurl.so\nVNDK-product: libcutils.so\nVNDK-product: libevent.so\nVNDK-product: libexpat.so\nVNDK-product: libfmq.so\nVNDK-product: libhidlbase.so\nVNDK-product: libhidlmemory.so\nVNDK-product: libion.so\nVNDK-product: libjpeg.so\nVNDK-product: libjsoncpp.so\nVNDK-product: libldacBT_abr.so\nVNDK-product: libldacBT_enc.so\nVNDK-product: liblz4.so\nVNDK-product: liblzma.so\nVNDK-product: libminijail.so\nVNDK-product: libnl.so\nVNDK-product: libpcre2.so\nVNDK-product: libpiex.so\nVNDK-product: libpng.so\nVNDK-product: libprocessgroup.so\nVNDK-product: libprocinfo.so\nVNDK-product: libspeexresampler.so\nVNDK-product: libssl.so\nVNDK-product: libtinyalsa.so\nVNDK-product: libtinyxml2.so\nVNDK-product: libunwindstack.so\nVNDK-product: libutils.so\nVNDK-product: libutilscallstack.so\nVNDK-product: libwifi-system-iface.so\nVNDK-product: libxml2.so\nVNDK-product: libyuv.so\nVNDK-product: libz.so\nVNDK-product: libziparchive.so\n"
  },
  {
    "path": "target/product/gsi/34.txt",
    "content": "LLNDK: libEGL.so\nLLNDK: libGLESv1_CM.so\nLLNDK: libGLESv2.so\nLLNDK: libGLESv3.so\nLLNDK: libRS.so\nLLNDK: libandroid_net.so\nLLNDK: libbinder_ndk.so\nLLNDK: libc.so\nLLNDK: libcgrouprc.so\nLLNDK: libcom.android.tethering.connectivity_native.so\nLLNDK: libdl.so\nLLNDK: libft2.so\nLLNDK: liblog.so\nLLNDK: libm.so\nLLNDK: libmediandk.so\nLLNDK: libnativewindow.so\nLLNDK: libneuralnetworks.so\nLLNDK: libselinux.so\nLLNDK: libsync.so\nLLNDK: libvndksupport.so\nLLNDK: libvulkan.so\nVNDK-SP: android.hardware.common-V2-ndk.so\nVNDK-SP: android.hardware.common.fmq-V1-ndk.so\nVNDK-SP: android.hardware.graphics.common-V4-ndk.so\nVNDK-SP: android.hardware.graphics.common@1.0.so\nVNDK-SP: android.hardware.graphics.common@1.1.so\nVNDK-SP: android.hardware.graphics.common@1.2.so\nVNDK-SP: android.hardware.graphics.composer3-V1-ndk.so\nVNDK-SP: android.hardware.graphics.mapper@2.0.so\nVNDK-SP: android.hardware.graphics.mapper@2.1.so\nVNDK-SP: android.hardware.graphics.mapper@3.0.so\nVNDK-SP: android.hardware.graphics.mapper@4.0.so\nVNDK-SP: android.hardware.graphics.allocator-V2-ndk.so\nVNDK-SP: android.hardware.renderscript@1.0.so\nVNDK-SP: android.hidl.memory.token@1.0.so\nVNDK-SP: android.hidl.memory@1.0-impl.so\nVNDK-SP: android.hidl.memory@1.0.so\nVNDK-SP: android.hidl.safe_union@1.0.so\nVNDK-SP: libRSCpuRef.so\nVNDK-SP: libRSDriver.so\nVNDK-SP: libRS_internal.so\nVNDK-SP: libbase.so\nVNDK-SP: libbcinfo.so\nVNDK-SP: libblas.so\nVNDK-SP: libc++.so\nVNDK-SP: libcompiler_rt.so\nVNDK-SP: libcutils.so\nVNDK-SP: libdmabufheap.so\nVNDK-SP: libgralloctypes.so\nVNDK-SP: libhardware.so\nVNDK-SP: libhidlbase.so\nVNDK-SP: libhidlmemory.so\nVNDK-SP: libion.so\nVNDK-SP: libjsoncpp.so\nVNDK-SP: liblzma.so\nVNDK-SP: libprocessgroup.so\nVNDK-SP: libunwindstack.so\nVNDK-SP: libutils.so\nVNDK-SP: libutilscallstack.so\nVNDK-SP: libz.so\nVNDK-core: android.frameworks.cameraservice.common-V1-ndk.so\nVNDK-core: android.frameworks.cameraservice.device-V1-ndk.so\nVNDK-core: android.frameworks.cameraservice.service-V1-ndk.so\nVNDK-core: android.hardware.audio.common@2.0.so\nVNDK-core: android.hardware.configstore-utils.so\nVNDK-core: android.hardware.configstore@1.0.so\nVNDK-core: android.hardware.configstore@1.1.so\nVNDK-core: android.hardware.confirmationui-support-lib.so\nVNDK-core: android.hardware.graphics.allocator@2.0.so\nVNDK-core: android.hardware.graphics.allocator@3.0.so\nVNDK-core: android.hardware.graphics.allocator@4.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-core: android.hardware.media.bufferpool@2.0.so\nVNDK-core: android.hardware.media.omx@1.0.so\nVNDK-core: android.hardware.media@1.0.so\nVNDK-core: android.hardware.memtrack-V1-ndk.so\nVNDK-core: android.hardware.memtrack@1.0.so\nVNDK-core: android.hardware.soundtrigger@2.0-core.so\nVNDK-core: android.hardware.soundtrigger@2.0.so\nVNDK-core: android.hidl.token@1.0-utils.so\nVNDK-core: android.hidl.token@1.0.so\nVNDK-core: android.system.suspend-V1-ndk.so\nVNDK-core: android.system.suspend@1.0.so\nVNDK-core: libaudioroute.so\nVNDK-core: libaudioutils.so\nVNDK-core: libbinder.so\nVNDK-core: libbufferqueueconverter.so\nVNDK-core: libcamera_metadata.so\nVNDK-core: libcap.so\nVNDK-core: libcn-cbor.so\nVNDK-core: libcodec2.so\nVNDK-core: libcrypto.so\nVNDK-core: libcrypto_utils.so\nVNDK-core: libcurl.so\nVNDK-core: libdiskconfig.so\nVNDK-core: libdumpstateutil.so\nVNDK-core: libevent.so\nVNDK-core: libexif.so\nVNDK-core: libexpat.so\nVNDK-core: libfmq.so\nVNDK-core: libgatekeeper.so\nVNDK-core: libgui.so\nVNDK-core: libhardware_legacy.so\nVNDK-core: libhidlallocatorutils.so\nVNDK-core: libjpeg.so\nVNDK-core: libldacBT_abr.so\nVNDK-core: libldacBT_enc.so\nVNDK-core: liblz4.so\nVNDK-core: libmedia_helper.so\nVNDK-core: libmedia_omx.so\nVNDK-core: libmemtrack.so\nVNDK-core: libminijail.so\nVNDK-core: libmkbootimg_abi_check.so\nVNDK-core: libnetutils.so\nVNDK-core: libnl.so\nVNDK-core: libpcre2.so\nVNDK-core: libpiex.so\nVNDK-core: libpng.so\nVNDK-core: libpower.so\nVNDK-core: libprocinfo.so\nVNDK-core: libradio_metadata.so\nVNDK-core: libspeexresampler.so\nVNDK-core: libsqlite.so\nVNDK-core: libssl.so\nVNDK-core: libstagefright_bufferpool@2.0.so\nVNDK-core: libstagefright_bufferqueue_helper.so\nVNDK-core: libstagefright_foundation.so\nVNDK-core: libstagefright_omx.so\nVNDK-core: libstagefright_omx_utils.so\nVNDK-core: libstagefright_xmlparser.so\nVNDK-core: libsysutils.so\nVNDK-core: libtinyalsa.so\nVNDK-core: libtinyxml2.so\nVNDK-core: libui.so\nVNDK-core: libusbhost.so\nVNDK-core: libwifi-system-iface.so\nVNDK-core: libxml2.so\nVNDK-core: libyuv.so\nVNDK-core: libziparchive.so\nVNDK-private: libblas.so\nVNDK-private: libcompiler_rt.so\nVNDK-private: libft2.so\nVNDK-private: libgui.so\nVNDK-product: android.hardware.audio.common@2.0.so\nVNDK-product: android.hardware.configstore@1.0.so\nVNDK-product: android.hardware.configstore@1.1.so\nVNDK-product: android.hardware.graphics.allocator@2.0.so\nVNDK-product: android.hardware.graphics.allocator@3.0.so\nVNDK-product: android.hardware.graphics.allocator@4.0.so\nVNDK-product: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-product: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-product: android.hardware.graphics.common@1.0.so\nVNDK-product: android.hardware.graphics.common@1.1.so\nVNDK-product: android.hardware.graphics.common@1.2.so\nVNDK-product: android.hardware.graphics.mapper@2.0.so\nVNDK-product: android.hardware.graphics.mapper@2.1.so\nVNDK-product: android.hardware.graphics.mapper@3.0.so\nVNDK-product: android.hardware.graphics.mapper@4.0.so\nVNDK-product: android.hardware.media.bufferpool@2.0.so\nVNDK-product: android.hardware.media.omx@1.0.so\nVNDK-product: android.hardware.media@1.0.so\nVNDK-product: android.hardware.memtrack@1.0.so\nVNDK-product: android.hardware.renderscript@1.0.so\nVNDK-product: android.hardware.soundtrigger@2.0.so\nVNDK-product: android.hidl.memory.token@1.0.so\nVNDK-product: android.hidl.memory@1.0.so\nVNDK-product: android.hidl.safe_union@1.0.so\nVNDK-product: android.hidl.token@1.0.so\nVNDK-product: android.system.suspend@1.0.so\nVNDK-product: libaudioutils.so\nVNDK-product: libbase.so\nVNDK-product: libc++.so\nVNDK-product: libcamera_metadata.so\nVNDK-product: libcap.so\nVNDK-product: libcompiler_rt.so\nVNDK-product: libcrypto.so\nVNDK-product: libcurl.so\nVNDK-product: libcutils.so\nVNDK-product: libevent.so\nVNDK-product: libexpat.so\nVNDK-product: libfmq.so\nVNDK-product: libhidlbase.so\nVNDK-product: libhidlmemory.so\nVNDK-product: libion.so\nVNDK-product: libjpeg.so\nVNDK-product: libjsoncpp.so\nVNDK-product: libldacBT_abr.so\nVNDK-product: libldacBT_enc.so\nVNDK-product: liblz4.so\nVNDK-product: liblzma.so\nVNDK-product: libminijail.so\nVNDK-product: libnl.so\nVNDK-product: libpcre2.so\nVNDK-product: libpiex.so\nVNDK-product: libpng.so\nVNDK-product: libprocessgroup.so\nVNDK-product: libprocinfo.so\nVNDK-product: libspeexresampler.so\nVNDK-product: libssl.so\nVNDK-product: libtinyalsa.so\nVNDK-product: libtinyxml2.so\nVNDK-product: libunwindstack.so\nVNDK-product: libutils.so\nVNDK-product: libutilscallstack.so\nVNDK-product: libwifi-system-iface.so\nVNDK-product: libxml2.so\nVNDK-product: libyuv.so\nVNDK-product: libz.so\nVNDK-product: libziparchive.so\n"
  },
  {
    "path": "target/product/gsi/Android.bp",
    "content": "// Copyright 2020 Google Inc. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nfilegroup {\n    name: \"vndk_lib_lists\",\n    srcs: [\n        \"*.txt\",\n    ],\n}\n\nprebuilt_etc {\n    name: \"gsi_skip_mount.cfg\",\n    filename: \"skip_mount.cfg\",\n    src: \"gsi_skip_mount.cfg\",\n\n    system_ext_specific: true,\n    relative_install_path: \"init/config\",\n\n    required: [\"gsi_skip_mount_compat_symlink\"],\n}\n\n// Adds a symlink under /system/etc/init/config pointing to /system/system_ext/etc/init/config\n// because first-stage init in Android 10.0 will read the skip_mount.cfg from /system/etc/* after\n// chroot /system.\n// TODO: remove this symlink when no need to support new GSI on Android 10.\n// The actual file needs to be under /system/system_ext because it's GSI-specific and does not\n// belong to core CSI.\ninstall_symlink {\n    name: \"gsi_skip_mount_compat_symlink\",\n    installed_location: \"etc/init/config\",\n    symlink_target: \"/system/system_ext/etc/init/config\",\n}\n\n// init.gsi.rc, GSI-specific init script.\nprebuilt_etc {\n    name: \"init.gsi.rc\",\n    src: \"init.gsi.rc\",\n    system_ext_specific: true,\n    relative_install_path: \"init\",\n}\n\nprebuilt_etc {\n    name: \"init.vndk-nodef.rc\",\n    src: \"init.vndk-nodef.rc\",\n    system_ext_specific: true,\n    relative_install_path: \"gsi\",\n}\n\ngsi_symlinks = [\n    {\n        target: \"/system/system_ext\",\n        name: \"system_ext\",\n    },\n    {\n        target: \"/system/product\",\n        name: \"product\",\n    },\n    {\n        target: \"/odm/odm_dlkm/etc\",\n        name: \"odm_dlkm/etc\",\n    },\n    {\n        target: \"/vendor/vendor_dlkm/etc\",\n        name: \"vendor_dlkm/etc\",\n    },\n]\n\nandroid_filesystem_defaults {\n    name: \"android_gsi_defaults\",\n    defaults: [\n        \"system_image_defaults\",\n        \"system_ext_image_defaults\",\n        \"product_image_defaults\",\n    ],\n    symlinks: gsi_symlinks,\n    dirs: [\"cache\"],\n    deps: [\n        ///////////////////////////////////////////\n        // gsi_system_ext\n        ///////////////////////////////////////////\n\n        // handheld packages\n        \"Launcher3QuickStep\",\n        \"Provision\",\n        \"Settings\",\n        \"StorageManager\",\n        \"SystemUI\",\n\n        // telephony packages\n        \"CarrierConfig\",\n\n        ///////////////////////////////////////////\n        // gsi_release\n        ///////////////////////////////////////////\n        \"gsi_skip_mount.cfg\",\n        \"init.gsi.rc\",\n        \"init.vndk-nodef.rc\",\n        // Overlay the GSI specific setting for framework and SystemUI\n        \"gsi_overlay_framework\",\n        \"gsi_overlay_systemui\",\n\n        ///////////////////////////////////////////\n        // VNDK\n        ///////////////////////////////////////////\n        \"com.android.vndk.v30\",\n        \"com.android.vndk.v31\",\n        \"com.android.vndk.v32\",\n        \"com.android.vndk.v33\",\n        \"com.android.vndk.v34\",\n\n        ///////////////////////////////////////////\n        // gsi_product\n        ///////////////////////////////////////////\n        \"Browser2\",\n        \"Camera2\",\n        \"Dialer\",\n        \"LatinIME\",\n        \"apns-full-conf.xml\",\n        \"frameworks-base-overlays\",\n    ],\n    multilib: {\n        lib64: {\n            deps: [\n                ///////////////////////////////////////////\n                // AVF\n                ///////////////////////////////////////////\n                \"com.android.compos\",\n                \"features_com.android.virt.xml\",\n            ],\n        },\n        both: {\n            // PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34\n            deps: [\"android.hidl.memory@1.0-impl\"],\n        },\n    },\n    type: \"ext4\",\n}\n\n// system.img for gsi_{arch} targets\nandroid_system_image {\n    name: \"android_gsi\",\n    defaults: [\"android_gsi_defaults\"],\n    enabled: select(soong_config_variable(\"ANDROID\", \"PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT\"), {\n        \"true\": true,\n        default: false,\n    }),\n    deps: [\n        // Install a copy of the debug policy to the system_ext partition, and allow\n        // init-second-stage to load debug policy from system_ext.\n        // This option is only meant to be set by compliance GSI targets.\n        \"system_ext_userdebug_plat_sepolicy.cil\",\n    ],\n}\n\n// system.img for aosp_{arch} targets\nandroid_system_image {\n    name: \"aosp_system_image\",\n    defaults: [\"android_gsi_defaults\"],\n    deps: [\n        // handheld_system_ext\n        \"AccessibilityMenu\",\n        \"WallpaperCropper\",\n\n        // telephony_system_ext\n        \"EmergencyInfo\",\n\n        // handheld_product\n        \"Calendar\",\n        \"Contacts\",\n        \"DeskClock\",\n        \"Gallery2\",\n        \"Music\",\n        \"preinstalled-packages-platform-handheld-product.xml\",\n        \"QuickSearchBox\",\n        \"SettingsIntelligence\",\n        \"frameworks-base-overlays\",\n\n        // telephony_product\n        \"ImsServiceEntitlement\",\n        \"preinstalled-packages-platform-telephony-product.xml\",\n\n        // more AOSP packages\n        \"initial-package-stopped-states-aosp.xml\",\n        \"messaging\",\n        \"PhotoTable\",\n        \"preinstalled-packages-platform-aosp-product.xml\",\n        \"ThemePicker\",\n    ] + select(product_variable(\"debuggable\"), {\n        true: [\"frameworks-base-overlays-debug\"],\n        default: [],\n    }),\n    enabled: select(soong_config_variable(\"gsi\", \"building_gsi\"), {\n        true: true,\n        default: false,\n    }),\n    multilib: {\n        common: {\n            deps: select(release_flag(\"RELEASE_AVATAR_PICKER_APP\"), {\n                true: [\n                    \"AvatarPicker\", // handheld_system_ext (RELEASE_AVATAR_PICKER_APP)\n                ],\n                default: [],\n            }),\n        },\n    },\n}\n"
  },
  {
    "path": "target/product/gsi/OWNERS",
    "content": "bowgotsai@google.com\njiyong@google.com\njustinyun@google.com\nsmoreland@google.com\nszuweilin@google.com\nyochiang@google.com\n"
  },
  {
    "path": "target/product/gsi/current.txt",
    "content": "LLNDK: libEGL.so\nLLNDK: libGLESv1_CM.so\nLLNDK: libGLESv2.so\nLLNDK: libGLESv3.so\nLLNDK: libRS.so\nLLNDK: libandroid_net.so\nLLNDK: libapexsupport.so\nLLNDK: libbinder_ndk.so\nLLNDK: libc.so\nLLNDK: libcgrouprc.so\nLLNDK: libcom.android.tethering.connectivity_native.so\nLLNDK: libdl.so\nLLNDK: libft2.so\nLLNDK: liblog.so\nLLNDK: libm.so\nLLNDK: libmediandk.so\nLLNDK: libnativewindow.so\nLLNDK: libneuralnetworks.so\nLLNDK: libselinux.so\nLLNDK: libsync.so\nLLNDK: libvendorsupport.so\nLLNDK: libvndksupport.so\nLLNDK: libvulkan.so\nVNDK-SP: android.hardware.common-V2-ndk.so\nVNDK-SP: android.hardware.common.fmq-V1-ndk.so\nVNDK-SP: android.hardware.graphics.allocator-V2-ndk.so\nVNDK-SP: android.hardware.graphics.common-V6-ndk.so\nVNDK-SP: android.hardware.graphics.common@1.0.so\nVNDK-SP: android.hardware.graphics.common@1.1.so\nVNDK-SP: android.hardware.graphics.common@1.2.so\nVNDK-SP: android.hardware.graphics.composer3-V1-ndk.so\nVNDK-SP: android.hardware.graphics.mapper@2.0.so\nVNDK-SP: android.hardware.graphics.mapper@2.1.so\nVNDK-SP: android.hardware.graphics.mapper@3.0.so\nVNDK-SP: android.hardware.graphics.mapper@4.0.so\nVNDK-SP: android.hardware.renderscript@1.0.so\nVNDK-SP: android.hidl.memory.token@1.0.so\nVNDK-SP: android.hidl.memory@1.0.so\nVNDK-SP: android.hidl.safe_union@1.0.so\nVNDK-SP: libRSCpuRef.so\nVNDK-SP: libRSDriver.so\nVNDK-SP: libRS_internal.so\nVNDK-SP: libbase.so\nVNDK-SP: libbcinfo.so\nVNDK-SP: libblas.so\nVNDK-SP: libc++.so\nVNDK-SP: libcompiler_rt.so\nVNDK-SP: libcutils.so\nVNDK-SP: libdmabufheap.so\nVNDK-SP: libgralloctypes.so\nVNDK-SP: libhardware.so\nVNDK-SP: libhidlbase.so\nVNDK-SP: libhidlmemory.so\nVNDK-SP: libion.so\nVNDK-SP: libjsoncpp.so\nVNDK-SP: liblzma.so\nVNDK-SP: libprocessgroup.so\nVNDK-SP: libunwindstack.so\nVNDK-SP: libutils.so\nVNDK-SP: libutilscallstack.so\nVNDK-SP: libz.so\nVNDK-core: android.frameworks.cameraservice.common-V1-ndk.so\nVNDK-core: android.frameworks.cameraservice.device-V2-ndk.so\nVNDK-core: android.frameworks.cameraservice.service-V2-ndk.so\nVNDK-core: android.hardware.audio.common@2.0.so\nVNDK-core: android.hardware.configstore-utils.so\nVNDK-core: android.hardware.configstore@1.0.so\nVNDK-core: android.hardware.configstore@1.1.so\nVNDK-core: android.hardware.confirmationui-support-lib.so\nVNDK-core: android.hardware.graphics.allocator@2.0.so\nVNDK-core: android.hardware.graphics.allocator@3.0.so\nVNDK-core: android.hardware.graphics.allocator@4.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-core: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-core: android.hardware.media.bufferpool@2.0.so\nVNDK-core: android.hardware.media.omx@1.0.so\nVNDK-core: android.hardware.media@1.0.so\nVNDK-core: android.hardware.memtrack-V1-ndk.so\nVNDK-core: android.hardware.memtrack@1.0.so\nVNDK-core: android.hardware.soundtrigger@2.0-core.so\nVNDK-core: android.hardware.soundtrigger@2.0.so\nVNDK-core: android.hidl.token@1.0-utils.so\nVNDK-core: android.hidl.token@1.0.so\nVNDK-core: android.system.suspend-V1-ndk.so\nVNDK-core: android.system.suspend@1.0.so\nVNDK-core: libaudioroute.so\nVNDK-core: libaudioutils.so\nVNDK-core: libbinder.so\nVNDK-core: libbufferqueueconverter.so\nVNDK-core: libcamera_metadata.so\nVNDK-core: libcap.so\nVNDK-core: libcn-cbor.so\nVNDK-core: libcodec2.so\nVNDK-core: libcrypto.so\nVNDK-core: libcrypto_utils.so\nVNDK-core: libcurl.so\nVNDK-core: libdumpstateutil.so\nVNDK-core: libevent.so\nVNDK-core: libexif.so\nVNDK-core: libexpat.so\nVNDK-core: libfmq.so\nVNDK-core: libgatekeeper.so\nVNDK-core: libgui.so\nVNDK-core: libhardware_legacy.so\nVNDK-core: libhidlallocatorutils.so\nVNDK-core: libjpeg.so\nVNDK-core: libldacBT_abr.so\nVNDK-core: libldacBT_enc.so\nVNDK-core: liblz4.so\nVNDK-core: libmedia_helper.so\nVNDK-core: libmedia_omx.so\nVNDK-core: libmemtrack.so\nVNDK-core: libminijail.so\nVNDK-core: libmkbootimg_abi_check.so\nVNDK-core: libnetutils.so\nVNDK-core: libnl.so\nVNDK-core: libpcre2.so\nVNDK-core: libpiex.so\nVNDK-core: libpng.so\nVNDK-core: libpower.so\nVNDK-core: libprocinfo.so\nVNDK-core: libradio_metadata.so\nVNDK-core: libspeexresampler.so\nVNDK-core: libsqlite.so\nVNDK-core: libssl.so\nVNDK-core: libstagefright_bufferpool@2.0.so\nVNDK-core: libstagefright_bufferqueue_helper.so\nVNDK-core: libstagefright_foundation.so\nVNDK-core: libstagefright_omx.so\nVNDK-core: libstagefright_omx_utils.so\nVNDK-core: libstagefright_xmlparser.so\nVNDK-core: libsysutils.so\nVNDK-core: libtinyalsa.so\nVNDK-core: libtinyxml2.so\nVNDK-core: libui.so\nVNDK-core: libusbhost.so\nVNDK-core: libwifi-system-iface.so\nVNDK-core: libxml2.so\nVNDK-core: libyuv.so\nVNDK-core: libziparchive.so\nVNDK-core: server_configurable_flags.so\nVNDK-private: libblas.so\nVNDK-private: libcompiler_rt.so\nVNDK-private: libft2.so\nVNDK-private: libgui.so\nVNDK-product: android.hardware.audio.common@2.0.so\nVNDK-product: android.hardware.configstore@1.0.so\nVNDK-product: android.hardware.configstore@1.1.so\nVNDK-product: android.hardware.graphics.allocator@2.0.so\nVNDK-product: android.hardware.graphics.allocator@3.0.so\nVNDK-product: android.hardware.graphics.allocator@4.0.so\nVNDK-product: android.hardware.graphics.bufferqueue@1.0.so\nVNDK-product: android.hardware.graphics.bufferqueue@2.0.so\nVNDK-product: android.hardware.graphics.common@1.0.so\nVNDK-product: android.hardware.graphics.common@1.1.so\nVNDK-product: android.hardware.graphics.common@1.2.so\nVNDK-product: android.hardware.graphics.mapper@2.0.so\nVNDK-product: android.hardware.graphics.mapper@2.1.so\nVNDK-product: android.hardware.graphics.mapper@3.0.so\nVNDK-product: android.hardware.graphics.mapper@4.0.so\nVNDK-product: android.hardware.media.bufferpool@2.0.so\nVNDK-product: android.hardware.media.omx@1.0.so\nVNDK-product: android.hardware.media@1.0.so\nVNDK-product: android.hardware.memtrack@1.0.so\nVNDK-product: android.hardware.renderscript@1.0.so\nVNDK-product: android.hardware.soundtrigger@2.0.so\nVNDK-product: android.hidl.memory.token@1.0.so\nVNDK-product: android.hidl.memory@1.0.so\nVNDK-product: android.hidl.safe_union@1.0.so\nVNDK-product: android.hidl.token@1.0.so\nVNDK-product: android.system.suspend@1.0.so\nVNDK-product: libaudioutils.so\nVNDK-product: libbase.so\nVNDK-product: libc++.so\nVNDK-product: libcamera_metadata.so\nVNDK-product: libcap.so\nVNDK-product: libcompiler_rt.so\nVNDK-product: libcrypto.so\nVNDK-product: libcurl.so\nVNDK-product: libcutils.so\nVNDK-product: libevent.so\nVNDK-product: libexpat.so\nVNDK-product: libfmq.so\nVNDK-product: libhidlbase.so\nVNDK-product: libhidlmemory.so\nVNDK-product: libion.so\nVNDK-product: libjpeg.so\nVNDK-product: libjsoncpp.so\nVNDK-product: libldacBT_abr.so\nVNDK-product: libldacBT_enc.so\nVNDK-product: liblz4.so\nVNDK-product: liblzma.so\nVNDK-product: libminijail.so\nVNDK-product: libnl.so\nVNDK-product: libpcre2.so\nVNDK-product: libpiex.so\nVNDK-product: libpng.so\nVNDK-product: libprocessgroup.so\nVNDK-product: libprocinfo.so\nVNDK-product: libspeexresampler.so\nVNDK-product: libssl.so\nVNDK-product: libtinyalsa.so\nVNDK-product: libtinyxml2.so\nVNDK-product: libunwindstack.so\nVNDK-product: libutils.so\nVNDK-product: libutilscallstack.so\nVNDK-product: libwifi-system-iface.so\nVNDK-product: libxml2.so\nVNDK-product: libyuv.so\nVNDK-product: libz.so\nVNDK-product: libziparchive.so\nVNDK-product: server_configurable_flags.so\n"
  },
  {
    "path": "target/product/gsi/gsi_skip_mount.cfg",
    "content": "# Skip \"system\" mountpoints.\n/oem\n/product\n/system_ext\n# Skip sub-mountpoints of system mountpoints.\n/oem/*\n/product/*\n/system_ext/*\n/system/*\n"
  },
  {
    "path": "target/product/gsi/init.gsi.rc",
    "content": "#\n# Android init script for GSI required initialization\n#\n\nimport /system/system_ext/etc/gsi/init.vndk-${ro.vndk.version:-nodef}.rc\n"
  },
  {
    "path": "target/product/gsi/init.vndk-nodef.rc",
    "content": "on early-init\n    # Reboot if BOARD_VNDK_VERSION is not defined\n    exec - root -- /system/bin/reboot bootloader\n"
  },
  {
    "path": "target/product/gsi/testkey_rsa2048.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA3fDgwU4JKVRHhAfofi/g8daTNplB2mTJCX9fIMy9FnZDXNij\n1zijRQ8HKbt3bAGImQvb3GxSV4M5eIdiLDUF7RsUpE7K+s939i/AaTtcuyqimQbJ\nQjP9emTsgngHzuKWMg1mwlRZYDfdv62zIQmZcbM9a0CZE36hAYvEBiDB8qT4ob++\ngodGAx3rpF2Wi7mhIYDINvkCw8/16Qi9CZgvOUrEolt3mz8Sps41z9j7YAsPbAa8\nfg7dUu61s6NkZEykl4G67loOaf7h+SyP//LpFZ0gV+STZ+EMGofL0SXb8A+hdIYE\nQxsnKUYo8e+GaQg92FLxVZqcfyG3AZuMB04R1QIDAQABAoIBAQDGj3/1UaSepjlJ\nZW3an2lH1Cpm2ZxyEGNQLPVluead1vaTdXq3zYM9AKHu8zp3lbOpAVQVk4/jnZJo\nQ+9QD6waonTIP3oYBE+WIMirHSHsjctkzw52PV9VBkAWxd5ueIfZheXejGpdy/2H\nRJcTQqxWbf7QGr4ZE9xmLq4UsW/zbXwy8qGEp9eMQIIaWBua43FkqmWYLSnVFVJI\nGl8mfVJctLNSZHhS3tKiV8up6NxZlDjO8o7kYVFCkv0xJ9yzQNBc3P2MEmvfZ06D\nQnimHBqSxr0M9X6hqP43CnqtCbpsHS8A12Dm4l6fkXfkrAY0UNrEaCSDb8aN7TEc\n7bc1MB4NAoGBAPK7xSuvQE9CH05Iy+G6mEQTtNmpfcQosqhi6dF60h4bqlkeGzUu\ngF/PKHwwffHAxQSv4V831P3A/IoJFa9IFkg218mYPNfzpj4vJA4aNCDp+SYZAIYm\nh6hMOmuByI97wds2yCBGt4mP0eow5B3A1b3UQeqW6LVSuobZ22QVlSk/AoGBAOoS\nL82yda9hUa7vuXtqTraf9EGjSXhyjoPqWxa+a1ooI9l24f7mokS5Iof+a/SLfPUj\npwj8eOeOZksjAaWJIdrRb3TaYLaqhDkWQeV5N5XxYbn3+TvVJQyR+OSBfGoEpVP/\nIS6fusvpT3eULJDax10By+gDcoLT5M1FNs4rBIvrAoGBAM8yJP5DHDwLjzl9vjsy\n0iLaR3e8zBQTQV2nATvFAXKd3u0vW74rsX0XEdHgesFP8V0s3M4wlGj+wRL66j2y\n5QJDfjMg9l7IJlHSX46CI5ks33X7xYy9evLYDs4R/Kct1q5OtsmGU8jisSadETus\njUb61kFvC7krovjVIgbuvWJ1AoGAVikzp4gVgeVU6AwePqu3JcpjYvX0SX4Br9VI\nimq1oY49BAOa1PWYratoZp7kpjPiX2osRkaJStNEHExagtCjwaRuXpk0GIlT+p+S\nyiGAsJUV4BrDh57B8IqbD6IKZgwnv2+ei0cIv562PdIxRXEDCd1rbZA3SqktA9KC\nhgmXttkCgYBPU1lqRpwoHP9lpOBTDa6/Xi6WaDEWrG/tUF/wMlvrZ4hEVMDJRs1d\n9JCXBxL/O4TMvpmyVKBZW15iZOcLM3EpiZ00UD+ChcAaFstup+oYKrs8gL9hgyTd\ncvWMxGQm13KwSj2CLzEQpPAN5xG14njXaee5ksshxkzBz9z3MVWiiw==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "target/product/gsi_release.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# The makefile contains the special settings for GSI releasing.\n# This makefile is used for the build targets which used for releasing GSI.\n#\n# For example:\n# - Released GSI contains skip_mount.cfg to skip mounting prodcut paritition\n# - Released GSI contains more VNDK packages to support old version vendors\n# - etc.\n#\n# See device/generic/common/README.md for more details.\n#\n\nBUILDING_GSI := true\n\nPRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \\\n    system/etc/init/config \\\n    system/product/% \\\n    system/system_ext/%\n\n# GSI should always support up-to-date platform features.\n# Keep this value at the latest API level to ensure latest build system\n# default configs are applied.\nPRODUCT_SHIPPING_API_LEVEL := 34\n\n# Enable dynamic partitions to facilitate mixing onto Cuttlefish\nPRODUCT_USE_DYNAMIC_PARTITIONS := true\n\n# Enable dynamic partition size\nPRODUCT_USE_DYNAMIC_PARTITION_SIZE := true\n\n# GSI specific tasks on boot\nPRODUCT_PACKAGES += \\\n    gsi_skip_mount.cfg \\\n    init.gsi.rc \\\n    init.vndk-nodef.rc \\\n\n\n# Overlay the GSI specific setting for framework and SystemUI\nifneq ($(PRODUCT_IS_AUTOMOTIVE),true)\n    PRODUCT_PACKAGES += \\\n        gsi_overlay_framework \\\n        gsi_overlay_systemui \\\n    PRODUCT_COPY_FILES += \\\n        device/generic/common/overlays/overlay-config.xml:$(TARGET_COPY_OUT_SYSTEM_EXT)/overlay/config/config.xml\nendif\n\n# Support additional VNDK snapshots\nPRODUCT_EXTRA_VNDK_VERSIONS := \\\n    30 \\\n    31 \\\n    32 \\\n    33 \\\n    34 \\\n\n# Do not build non-GSI partition images.\nPRODUCT_BUILD_CACHE_IMAGE := false\nPRODUCT_BUILD_DEBUG_BOOT_IMAGE := false\nPRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE := false\nPRODUCT_BUILD_USERDATA_IMAGE := false\nPRODUCT_BUILD_VENDOR_IMAGE := false\nPRODUCT_BUILD_SUPER_PARTITION := false\nPRODUCT_BUILD_SUPER_EMPTY_IMAGE := false\nPRODUCT_BUILD_SYSTEM_DLKM_IMAGE := false\nPRODUCT_EXPORT_BOOT_IMAGE_TO_DIST := true\n\n# Build pvmfw with GSI: b/376363989, pvmfw currently only supports AArch64\nifneq (,$(filter %_arm64,$(TARGET_PRODUCT)))\nPRODUCT_BUILD_PVMFW_IMAGE := true\nendif\n\n# Additional settings used in all GSI builds\nPRODUCT_PRODUCT_PROPERTIES += \\\n    ro.crypto.metadata_init_delete_all_keys.enabled=false \\\n    debug.codec2.bqpool_dealloc_after_stop=1 \\\n\n# Window Extensions\nifneq ($(PRODUCT_IS_ATV),true)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)\nendif\n\n# A GSI is to be mixed with different boot images. That means we can't determine\n# the kernel version when building a GSI.\n# Assume the device supports UFFD. If it doesn't, the ART runtime will fall back\n# to CC, and odrefresh will regenerate core dexopt artifacts on the first boot,\n# so this is okay.\nPRODUCT_ENABLE_UFFD_GC := true\n"
  },
  {
    "path": "target/product/handheld_product.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile contains the product partition contents for\n# a generic phone or tablet device. Only add something here if\n# it definitely doesn't belong on other types of devices (if it\n# does, use base_product.mk).\n$(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk)\n\n# /product packages\nPRODUCT_PACKAGES += \\\n    Browser2 \\\n    Calendar \\\n    Camera2 \\\n    Contacts \\\n    DeskClock \\\n    Gallery2 \\\n    LatinIME \\\n    Music \\\n    preinstalled-packages-platform-handheld-product.xml \\\n    QuickSearchBox \\\n    SettingsIntelligence \\\n    frameworks-base-overlays\n\nPRODUCT_PACKAGES_DEBUG += \\\n    frameworks-base-overlays-debug\n"
  },
  {
    "path": "target/product/handheld_system.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile contains the system partition contents for\n# a generic phone or tablet device. Only add something here if\n# it definitely doesn't belong on other types of devices (if it\n# does, use base_vendor.mk).\n$(call inherit-product, $(SRC_TARGET_DIR)/product/media_system.mk)\n$(call inherit-product-if-exists, frameworks/base/data/fonts/fonts.mk)\n$(call inherit-product-if-exists, external/google-fonts/dancing-script/fonts.mk)\n$(call inherit-product-if-exists, external/google-fonts/carrois-gothic-sc/fonts.mk)\n$(call inherit-product-if-exists, external/google-fonts/coming-soon/fonts.mk)\n$(call inherit-product-if-exists, external/google-fonts/cutive-mono/fonts.mk)\n$(call inherit-product-if-exists, external/google-fonts/source-sans-pro/fonts.mk)\n$(call inherit-product-if-exists, external/noto-fonts/fonts.mk)\n$(call inherit-product-if-exists, external/roboto-fonts/fonts.mk)\n$(call inherit-product-if-exists, external/roboto-flex-fonts/fonts.mk)\n$(call inherit-product-if-exists, external/hyphenation-patterns/patterns.mk)\n$(call inherit-product-if-exists, frameworks/base/data/keyboards/keyboards.mk)\n$(call inherit-product-if-exists, frameworks/webview/chromium/chromium.mk)\n\nPRODUCT_PACKAGES += \\\n    android.software.window_magnification.prebuilt.xml \\\n    BasicDreams \\\n    BlockedNumberProvider \\\n    BluetoothMidiService \\\n    BookmarkProvider \\\n    BuiltInPrintService \\\n    CalendarProvider \\\n    cameraserver \\\n    CameraExtensionsProxy \\\n    CaptivePortalLogin \\\n    CertInstaller \\\n    CredentialManager \\\n    DeviceAsWebcam \\\n    DeviceDiagnostics \\\n    DocumentsUI \\\n    DownloadProviderUi \\\n    EasterEgg \\\n    ExternalStorageProvider \\\n    FusedLocation \\\n    InputDevices \\\n    KeyChain \\\n    librs_jni \\\n    ManagedProvisioning \\\n    MmsService \\\n    MtpService \\\n    MusicFX \\\n    PacProcessor \\\n    preinstalled-packages-platform-handheld-system.xml \\\n    PrintRecommendationService \\\n    PrintSpooler \\\n    ProxyHandler \\\n    screenrecord \\\n    SecureElement \\\n    SharedStorageBackup \\\n    SimAppDialog \\\n    Telecom \\\n    TeleService \\\n    Traceur \\\n    UserDictionaryProvider \\\n    VpnDialogs \\\n    vr \\\n\n# Choose the correct products based on HSUM status\nifeq ($(PRODUCT_USE_HSUM),true)\n  PRODUCT_PACKAGES += TelephonyProviderHsum\nelse\n  PRODUCT_PACKAGES += TelephonyProvider\nendif\n\nPRODUCT_PACKAGES += $(RELEASE_PACKAGE_VIRTUAL_CAMERA)\n# Set virtual_camera_service_enabled soong config variable based on the\n# RELEASE_PACKAGE_VIRTUAL_CAMERA build. virtual_camera_service_enabled soong config\n# variable is used to prevent accessing the service when it's not present in the build.\n$(call soong_config_set,vdm,virtual_camera_service_enabled,$(if $(RELEASE_PACKAGE_VIRTUAL_CAMERA),true,false))\n\nPRODUCT_SYSTEM_SERVER_APPS += \\\n    FusedLocation \\\n    InputDevices \\\n    KeyChain \\\n    Telecom \\\n\nPRODUCT_PACKAGES += framework-audio_effects.xml\n\nPRODUCT_VENDOR_PROPERTIES += \\\n    ro.carrier?=unknown \\\n    ro.config.notification_sound?=OnTheHunt.ogg \\\n    ro.config.alarm_alert?=Alarm_Classic.ogg\n"
  },
  {
    "path": "target/product/handheld_system_ext.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile contains the system_ext partition contents for\n# a generic phone or tablet device. Only add something here if\n# it definitely doesn't belong on other types of devices (if it\n# does, use base_system_ext.mk).\n$(call inherit-product, $(SRC_TARGET_DIR)/product/media_system_ext.mk)\n\n# /system_ext packages\nPRODUCT_PACKAGES += \\\n    AccessibilityMenu \\\n    $(if $(RELEASE_AVATAR_PICKER_APP), AvatarPicker,) \\\n    Launcher3QuickStep \\\n    Provision \\\n    Settings \\\n    StorageManager \\\n    SystemUI \\\n    WallpaperCropper \\\n"
  },
  {
    "path": "target/product/handheld_vendor.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile contains the non-system partition contents for\n# a generic phone or tablet device. Only add something here if\n# it definitely doesn't belong on other types of devices (if it\n# does, use base_vendor.mk).\n$(call inherit-product, $(SRC_TARGET_DIR)/product/media_vendor.mk)\n\n# /vendor packages\nPRODUCT_PACKAGES += \\\n    audio.primary.default \\\n    local_time.default \\\n    power.default \\\n    vibrator.default \\\n\n"
  },
  {
    "path": "target/product/hsum_common.mk",
    "content": "#\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Contains common default elements for devices running in Headless System User Mode.\n\n# Should generally be inherited first as using an HSUM configuration can affect downstream choices\n# (such as ensuring that the HSUM-variants of packages are selected).\n\nPRODUCT_SYSTEM_DEFAULT_PROPERTIES += \\\n    ro.fw.mu.headless_system_user=true\n\n# Variable for elsewhere choosing the appropriate products based on HSUM status.\nPRODUCT_USE_HSUM := true\n\nPRODUCT_PACKAGES += \\\n    HsumDefaultConfigOverlay\n"
  },
  {
    "path": "target/product/languages_default.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a build configuration that just contains a list of languages, with\n# en_US set as the default language.\nPRODUCT_LOCALES := \\\n        en_US \\\n        af_ZA \\\n        am_ET \\\n        ar_EG \\\n        ar_XB \\\n        as_IN \\\n        az_AZ \\\n        be_BY \\\n        bg_BG \\\n        bn_BD \\\n        bs_BA \\\n        ca_ES \\\n        cs_CZ \\\n        da_DK \\\n        de_DE \\\n        el_GR \\\n        en_AU \\\n        en_CA \\\n        en_GB \\\n        en_IN \\\n        en_XA \\\n        es_ES \\\n        es_US \\\n        et_EE \\\n        eu_ES \\\n        fa_IR \\\n        fi_FI \\\n        fr_CA \\\n        fr_FR \\\n        gl_ES \\\n        gu_IN \\\n        hi_IN \\\n        hr_HR \\\n        hu_HU \\\n        hy_AM \\\n        in_ID \\\n        is_IS \\\n        it_IT \\\n        iw_IL \\\n        ja_JP \\\n        ka_GE \\\n        kk_KZ \\\n        km_KH \\\n        kn_IN \\\n        ko_KR \\\n        ky_KG \\\n        lo_LA \\\n        lt_LT \\\n        lv_LV \\\n        mk_MK \\\n        ml_IN \\\n        mn_MN \\\n        mr_IN \\\n        ms_MY \\\n        my_MM \\\n        nb_NO \\\n        ne_NP \\\n        nl_NL \\\n        or_IN \\\n        pa_IN \\\n        pl_PL \\\n        pt_BR \\\n        pt_PT \\\n        ro_RO \\\n        ru_RU \\\n        si_LK \\\n        sk_SK \\\n        sl_SI \\\n        sq_AL \\\n        sr_Latn_RS \\\n        sr_RS \\\n        sv_SE \\\n        sw_TZ \\\n        ta_IN \\\n        te_IN \\\n        th_TH \\\n        tl_PH \\\n        tr_TR \\\n        uk_UA \\\n        ur_PK \\\n        uz_UZ \\\n        vi_VN \\\n        zh_CN \\\n        zh_HK \\\n        zh_TW \\\n        zu_ZA \\\n"
  },
  {
    "path": "target/product/languages_full.mk",
    "content": "#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a build configuration that contains the default list of languages,\n# as well as the en_XC pseudo-locale, which is useful for localization test\n# builds.\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk)\nPRODUCT_LOCALES += en_XC\n"
  },
  {
    "path": "target/product/large_screen_common.mk",
    "content": "# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Window Extensions\n$(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)\n\n# Enable Settings 2-pane optimization for large-screen\nPRODUCT_SYSTEM_PROPERTIES += \\\n    persist.settings.large_screen_opt.enabled=true\n"
  },
  {
    "path": "target/product/linux_bionic.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_NAME := linux_bionic\nPRODUCT_BRAND := Android\nPRODUCT_DEVICE := linux_bionic\n"
  },
  {
    "path": "target/product/mainline_sdk.mk",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_NAME := mainline_sdk\nPRODUCT_BRAND := Android\nPRODUCT_DEVICE := mainline_sdk\n\nPRODUCT_BUILD_FROM_SOURCE_STUB := true"
  },
  {
    "path": "target/product/mainline_system_arm64.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Do not modify this file. It's just alias of generic_system_arm64.mk\n# Will be removed when renaming from mainline_system to generic_system\n# complete\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_arm64.mk)\n\nPRODUCT_NAME := mainline_system_arm64\n"
  },
  {
    "path": "target/product/mainline_system_x86.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Do not modify this file. It's just alias of generic_system_x86.mk\n# Will be removed when renaming from mainline_system to generic_system\n# complete\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_x86.mk)\n\nPRODUCT_NAME := mainline_system_x86\n"
  },
  {
    "path": "target/product/mainline_system_x86_64.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Do not modify this file. It's just alias of generic_system_x86_64.mk\n# Will be removed when renaming from mainline_system to generic_system\n# complete\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_x86_64.mk)\n\nPRODUCT_NAME := mainline_system_x86_64\n"
  },
  {
    "path": "target/product/mainline_system_x86_arm.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Do not modify this file. It's just alias of generic_system_x86_arm.mk\n# Will be removed when renaming from mainline_system to generic_system\n# complete\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_x86_arm.mk)\n\nPRODUCT_NAME := mainline_system_x86_arm\n"
  },
  {
    "path": "target/product/media_product.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile contains the product partition contents for\n# media-capable devices (non-wearables). Only add something here\n# if it definitely doesn't belong on wearables. Otherwise, choose\n# base_product.mk.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/base_product.mk)\n\n# /product packages\nPRODUCT_PACKAGES += \\\n    webview \\\n"
  },
  {
    "path": "target/product/media_system.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile contains the system partition contents for\n# media-capable devices (non-wearables). Only add something\n# here if it definitely doesn't belong on wearables. Otherwise,\n# choose base_system.mk.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/base_system.mk)\n\nPRODUCT_PACKAGES += \\\n    android.software.webview.prebuilt.xml \\\n    com.android.future.usb.accessory \\\n    com.android.mediadrm.signer \\\n    com.android.media.remotedisplay \\\n    com.android.media.remotedisplay.xml \\\n    CompanionDeviceManager \\\n    drmserver \\\n    fsck.f2fs \\\n    HTMLViewer \\\n    libfilterpack_imageproc \\\n    libwebviewchromium_loader \\\n    libwebviewchromium_plat_support \\\n    make_f2fs \\\n    requestsync \\\n\nPRODUCT_HOST_PACKAGES += \\\n    fsck.f2fs \\\n\nifneq (REL,$(PLATFORM_VERSION_CODENAME))\nPRODUCT_PACKAGES += \\\n    android.software.preview_sdk.prebuilt.xml\nendif\n\n# The order here is the same order they end up on the classpath, so it matters.\nPRODUCT_SYSTEM_SERVER_JARS := \\\n    com.android.location.provider \\\n    services\n\nPRODUCT_COPY_FILES += \\\n    system/core/rootdir/etc/public.libraries.android.txt:system/etc/public.libraries.txt\n\n# Enable boot.oat filtering of compiled classes to reduce boot.oat size. b/28026683\nPRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\\\n    frameworks/base/config/compiled-classes-phone:system/etc/compiled-classes)\n\n# On userdebug builds, collect more tombstones by default.\nifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))\nPRODUCT_VENDOR_PROPERTIES += \\\n    tombstoned.max_tombstone_count?=50\nendif\n\nPRODUCT_VENDOR_PROPERTIES += \\\n    ro.logd.size.stats=64K \\\n    log.tag.stats_log=I\n\n# Enable CFI for security-sensitive components\n$(call inherit-product, $(SRC_TARGET_DIR)/product/cfi-common.mk)\n$(call inherit-product-if-exists, vendor/google/products/cfi-vendor.mk)\n\n# Enable MTE for security-sensitive components\n$(call inherit-product, $(SRC_TARGET_DIR)/product/memtag-common.mk)\n$(call inherit-product-if-exists, vendor/google/products/memtag-vendor.mk)\n"
  },
  {
    "path": "target/product/media_system_ext.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile contains the system_ext partition contents for\n# media-capable devices (non-wearables). Only add something here\n# if it definitely doesn't belong on wearables. Otherwise, choose\n# base_system_ext.mk.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/base_system_ext.mk)\n\nPRODUCT_PACKAGES += \\\n    StatementService \\\n\n# Window Extensions\n$(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions_base.mk)\n"
  },
  {
    "path": "target/product/media_vendor.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This makefile contains the non-system partition contents for\n# media-capable devices (non-wearables). Only add something here\n# if it definitely doesn't belong on wearables. Otherwise, choose\n# base_vendor.mk.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/base_vendor.mk)\n\n# /vendor packages\nPRODUCT_PACKAGES += \\\n    libaudiopreprocessing \\\n"
  },
  {
    "path": "target/product/memtag-common.mk",
    "content": "# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a recommended set of common components to enable MTE for.\n\nPRODUCT_MEMTAG_HEAP_ASYNC_DEFAULT_INCLUDE_PATHS := \\\n    external/android-clat \\\n    external/iproute2 \\\n    external/iptables \\\n    external/mtpd \\\n    external/ppp \\\n    hardware/st/nfc \\\n    hardware/st/secure_element \\\n    hardware/st/secure_element2 \\\n    packages/modules/StatsD \\\n    system/bpf \\\n    system/netd/netutil_wrappers \\\n    system/netd/server\n"
  },
  {
    "path": "target/product/module_arm.mk",
    "content": "#\n# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk)\n\nPRODUCT_NAME := module_arm\nPRODUCT_DEVICE := module_arm\n"
  },
  {
    "path": "target/product/module_arm64.mk",
    "content": "#\n# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)\n\nPRODUCT_NAME := module_arm64\nPRODUCT_DEVICE := module_arm64\n\nPRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true\nPRODUCT_MAX_PAGE_SIZE_SUPPORTED := 16384\n"
  },
  {
    "path": "target/product/module_arm64only.mk",
    "content": "#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk)\n\nPRODUCT_NAME := module_arm64only\nPRODUCT_DEVICE := module_arm64only\n\nPRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true\nPRODUCT_MAX_PAGE_SIZE_SUPPORTED := 16384\n"
  },
  {
    "path": "target/product/module_common.mk",
    "content": "#\n# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/build_variables.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/default_art_config.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/cfi-common.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/memtag-common.mk)\n\n# Enables treble, which enabled certain -D compilation flags. In particular, libhidlbase\n# uses -DENFORCE_VINTF_MANIFEST. See b/185759877\nPRODUCT_SHIPPING_API_LEVEL := 29\n\n# If true, this builds the mainline modules from source. This overrides any\n# prebuilts selected via RELEASE_APEX_CONTRIBUTIONS_* build flags for the\n# current release config.\nPRODUCT_MODULE_BUILD_FROM_SOURCE := true\n\n# Build sdk from source if the branch is not using slim manifests.\nifneq (,$(strip $(wildcard frameworks/base/Android.bp)))\n  UNBUNDLED_BUILD_SDKS_FROM_SOURCE := true\nendif\n\nPRODUCT_BRAND := Android\n"
  },
  {
    "path": "target/product/module_riscv64.mk",
    "content": "#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk)\n\nPRODUCT_NAME := module_riscv64\nPRODUCT_DEVICE := module_riscv64\n"
  },
  {
    "path": "target/product/module_x86.mk",
    "content": "#\n# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk)\n\nPRODUCT_NAME := module_x86\nPRODUCT_DEVICE := module_x86\n"
  },
  {
    "path": "target/product/module_x86_64.mk",
    "content": "#\n# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)\n\nPRODUCT_NAME := module_x86_64\nPRODUCT_DEVICE := module_x86_64\n\nPRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true\nPRODUCT_MAX_PAGE_SIZE_SUPPORTED :=  16384\n"
  },
  {
    "path": "target/product/module_x86_64only.mk",
    "content": "#\n# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk)\n\nPRODUCT_NAME := module_x86_64only\nPRODUCT_DEVICE := module_x86_64only\n\nPRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true\nPRODUCT_MAX_PAGE_SIZE_SUPPORTED := 16384\n"
  },
  {
    "path": "target/product/ndk.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This device is suitable for soong-only build that builds for all the architectures\n# needed for the ndk. It is not going to work for normal `lunch <foo> && m` workflows.\n\nPRODUCT_NAME := ndk\nPRODUCT_BRAND := Android\nPRODUCT_DEVICE := ndk\n\nPRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true\n"
  },
  {
    "path": "target/product/non_ab_device.mk",
    "content": "# Packages to update the recovery partition, which will be installed on\n# /vendor. Don't install these unless they're needed.\nPRODUCT_PACKAGES += \\\n    applypatch\n\n"
  },
  {
    "path": "target/product/product_launched_with_k.mk",
    "content": "#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on.\nPRODUCT_SHIPPING_API_LEVEL := 19\n"
  },
  {
    "path": "target/product/product_launched_with_l.mk",
    "content": "#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on.\nPRODUCT_SHIPPING_API_LEVEL := 21\n"
  },
  {
    "path": "target/product/product_launched_with_l_mr1.mk",
    "content": "#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on.\nPRODUCT_SHIPPING_API_LEVEL := 22\n"
  },
  {
    "path": "target/product/product_launched_with_m.mk",
    "content": "#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on.\nPRODUCT_SHIPPING_API_LEVEL := 23\n"
  },
  {
    "path": "target/product/product_launched_with_n.mk",
    "content": "#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on.\nPRODUCT_SHIPPING_API_LEVEL := 24\n"
  },
  {
    "path": "target/product/product_launched_with_n_mr1.mk",
    "content": "#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on.\nPRODUCT_SHIPPING_API_LEVEL := 25\n"
  },
  {
    "path": "target/product/product_launched_with_o.mk",
    "content": "#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on.\nPRODUCT_SHIPPING_API_LEVEL := 26\n"
  },
  {
    "path": "target/product/product_launched_with_o_mr1.mk",
    "content": "#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on.\nPRODUCT_SHIPPING_API_LEVEL := 27\n"
  },
  {
    "path": "target/product/product_launched_with_p.mk",
    "content": "#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on.\nPRODUCT_SHIPPING_API_LEVEL := 28"
  },
  {
    "path": "target/product/profile_boot_common.mk",
    "content": "#\n# Copyright 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Use an empty profile to make non of the boot image be AOT compiled (for now).\n# Note that we could use a previous profile but we will miss the opportunity to\n# remove classes that are no longer in use.\n# Ideally we would just generate an empty boot.art but we don't have the build\n# support to separate the image from the compile code.\nPRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION := build/make/target/product/empty-profile\nDEX_PREOPT_DEFAULT := nostripping\n\n# Boot image property overrides.\nPRODUCT_VENDOR_PROPERTIES += \\\n    dalvik.vm.profilesystemserver=true \\\n    dalvik.vm.profilebootclasspath=true\n\nPRODUCT_DIST_BOOT_AND_SYSTEM_JARS := true\n"
  },
  {
    "path": "target/product/ramdisk_stub.mk",
    "content": "#\n# Copyright 2022 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_COPY_FILES += \\\n    build/make/target/product/ramdisk_stub.mk:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/nonempty\n"
  },
  {
    "path": "target/product/runtime_libart.mk",
    "content": "#\n# Copyright (C) 2013 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Provides a functioning ART environment without Android frameworks\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/default_art_config.mk)\n\n# Additional mixins to the boot classpath.\nPRODUCT_PACKAGES += \\\n    android.test.base \\\n\n# Why are we pulling in ext, which is frameworks/base, depending on tagsoup and nist-sip?\nPRODUCT_PACKAGES += \\\n    ext \\\n\n# Runtime (Bionic) APEX module.\nPRODUCT_PACKAGES += com.android.runtime\n\n# ART APEX module.\n#\n# Select either release (com.android.art) or debug (com.android.art.debug)\n# variant of the ART APEX. By default, \"user\" build variants contain the release\n# module, while the \"eng\" build variant contain the debug module. However, if\n# `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is defined, it overrides the previous\n# logic:\n# - if `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is set to `false`, the\n#   build will include the release module (whatever the build\n#   variant);\n# - if `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is set to `true`, the\n#   build will include the debug module (whatever the build variant).\n#\n# Note that the ART APEX package includes the minimal boot classpath JARs\n# (listed in ART_APEX_JARS), which should no longer be added directly to\n# PRODUCT_PACKAGES.\n\nart_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD)\nifneq (false,$(art_target_include_debug_build))\n  ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))\n    art_target_include_debug_build := true\n  endif\nendif\n\nifeq (true,$(art_target_include_debug_build))\n  PRODUCT_PACKAGES += com.android.art.debug\n  apex_test_module := art-check-debug-apex-gen-fakebin\nelse\n  PRODUCT_PACKAGES += com.android.art\n  apex_test_module := art-check-release-apex-gen-fakebin\nendif\n\nifeq (true,$(call soong_config_get,art_module,source_build))\n  PRODUCT_HOST_PACKAGES += $(apex_test_module)\nendif\n\nart_target_include_debug_build :=\napex_test_module :=\n\n# Certificates.\nPRODUCT_PACKAGES += \\\n    cacerts \\\n\nPRODUCT_PACKAGES += \\\n    hiddenapi-package-whitelist.xml \\\n\nifeq (,$(TARGET_BUILD_UNBUNDLED))\n  # Don't depend on the framework boot image profile in unbundled builds where\n  # frameworks/base may not be present.\n  # TODO(b/179900989): We may not need this check once we stop using full\n  # platform products on the thin ART manifest branch.\n  PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION += frameworks/base/boot/boot-image-profile.txt\nendif\n\n# The dalvik.vm.dexopt.thermal-cutoff property must contain one of the values\n# listed here:\n#\n# https://source.android.com/devices/architecture/hidl/thermal-mitigation#thermal-api\n#\n# If the thermal status of the device reaches or exceeds the value set here\n# background dexopt will be terminated and rescheduled using an exponential\n# backoff polcy.\n#\n# The thermal cutoff value is currently set to THERMAL_STATUS_MODERATE.\nPRODUCT_SYSTEM_PROPERTIES += \\\n    dalvik.vm.usejit=true \\\n    dalvik.vm.dexopt.secondary=true \\\n    dalvik.vm.dexopt.thermal-cutoff=2 \\\n    dalvik.vm.appimageformat=lz4\n\nPRODUCT_SYSTEM_PROPERTIES += \\\n    ro.dalvik.vm.native.bridge?=0\n\n# The install filter is speed-profile in order to enable the use of\n# profiles from the dex metadata files. Note that if a profile is not provided\n# or if it is empty speed-profile is equivalent to (quicken + empty app image).\n# Note that `cmdline` is not strictly needed but it simplifies the management\n# of compilation reason in the platform (as we have a unified, single path,\n# without exceptions).\n# TODO(b/243646876): Remove `pm.dexopt.post-boot`.\nPRODUCT_SYSTEM_PROPERTIES += \\\n    pm.dexopt.post-boot?=verify \\\n    pm.dexopt.first-boot?=verify \\\n    pm.dexopt.boot-after-ota?=verify \\\n    pm.dexopt.boot-after-mainline-update?=verify \\\n    pm.dexopt.install?=speed-profile \\\n    pm.dexopt.install-fast?=skip \\\n    pm.dexopt.install-bulk?=speed-profile \\\n    pm.dexopt.install-bulk-secondary?=verify \\\n    pm.dexopt.install-bulk-downgraded?=verify \\\n    pm.dexopt.install-bulk-secondary-downgraded?=verify \\\n    pm.dexopt.bg-dexopt?=speed-profile \\\n    pm.dexopt.ab-ota?=speed-profile \\\n    pm.dexopt.inactive?=verify \\\n    pm.dexopt.cmdline?=verify \\\n    pm.dexopt.shared?=speed\n\nifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))\n    OVERRIDE_DISABLE_DEXOPT_ALL ?= true\nendif\n\n# OVERRIDE_DISABLE_DEXOPT_ALL disables all dexpreopt (build-time) and dexopt (on-device) activities.\n# This option is for faster iteration during development and should never be enabled for production.\nifneq (,$(filter true,$(OVERRIDE_DISABLE_DEXOPT_ALL)))\n  PRODUCT_SYSTEM_PROPERTIES += \\\n    dalvik.vm.disable-art-service-dexopt=true \\\n    dalvik.vm.disable-odrefresh=true\n\n  # Disable all dexpreopt activities except for the ART boot image.\n  # We have to dexpreopt the ART boot image because they are used by ART tests. This should not\n  # be too much of a problem for platform developers because a change to framework code should not\n  # trigger dexpreopt for the ART boot image.\n  WITH_DEXPREOPT_ART_BOOT_IMG_ONLY := true\n  $(call soong_config_set_bool,PrebuiltGmsCore,ExcludeExtractApk,true)\nendif\n\n# Enable resolution of startup const strings.\nPRODUCT_SYSTEM_PROPERTIES += \\\n    dalvik.vm.dex2oat-resolve-startup-strings=true\n\n# Specify default block size of 512K to enable parallel image decompression.\nPRODUCT_SYSTEM_PROPERTIES += \\\n    dalvik.vm.dex2oat-max-image-block-size=524288\n\n# Enable minidebuginfo generation unless overridden.\nPRODUCT_SYSTEM_PROPERTIES += \\\n    dalvik.vm.minidebuginfo=true \\\n    dalvik.vm.dex2oat-minidebuginfo=true\n\n# Enable Madvising of the whole odex and vdex files to MADV_WILLNEED.\n# The size specified here is the size limit of how much of the file\n# (in bytes) is madvised.\n# For odex and vdex files, we limit madvising to 100MB.\n# For art files, we defer to the runtime for default behavior.\nPRODUCT_SYSTEM_PROPERTIES += \\\n    dalvik.vm.madvise.vdexfile.size=104857600 \\\n    dalvik.vm.madvise.odexfile.size=104857600\n\n# Properties for the Unspecialized App Process Pool\nPRODUCT_SYSTEM_PROPERTIES += \\\n    dalvik.vm.usap_pool_enabled?=false \\\n    dalvik.vm.usap_refill_threshold?=1 \\\n    dalvik.vm.usap_pool_size_max?=3 \\\n    dalvik.vm.usap_pool_size_min?=1 \\\n    dalvik.vm.usap_pool_refill_delay_ms?=3000\n\nPRODUCT_SYSTEM_PROPERTIES += \\\n    dalvik.vm.useartservice=true \\\n    dalvik.vm.enable_pr_dexopt=true\n\n# Copy preopted files from system_b on first boot.\nPRODUCT_SYSTEM_PROPERTIES += ro.cp_system_other_odex=1\nPRODUCT_PACKAGES += \\\n  cppreopts.sh\n"
  },
  {
    "path": "target/product/sdk.mk",
    "content": "#\n# Copyright (C) 2014 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is a simple product that uses configures the minimum amount\n# needed to build the SDK (without the emulator).\n\n# Ensure all trunk-stable flags are available.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/build_variables.mk)\n\n# In order to build the bootclasspath sources, the bootclasspath needs to\n# be setup via default_art_config.mk. The sources only really make sense\n# together with a device (e.g. the emulator). So if the SDK sources change\n# to be built with the device, this could be removed.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/default_art_config.mk)\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk)\n\nPRODUCT_NAME := sdk\nPRODUCT_BRAND := Android\nPRODUCT_DEVICE := mainline_x86\n\nPRODUCT_BUILD_FROM_SOURCE_STUB := true\n\n# Use sources of mainline modules\nPRODUCT_MODULE_BUILD_FROM_SOURCE := true\n\nifeq ($(WITHOUT_CHECK_API),true)\n  $(error WITHOUT_CHECK_API cannot be set to true for SDK product builds)\nendif\n\n# Include Wear flag values so that Wear-related APIs are build in sdks.\nPRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/wear/release/release_config_map.textproto)\n"
  },
  {
    "path": "target/product/sdk_with_runtime_apis.mk",
    "content": "#\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk)\n\nPRODUCT_NAME := sdk_with_runtime_apis\n\nPRODUCT_HIDDEN_API_EXPORTABLE_STUBS := true\nPRODUCT_EXPORT_RUNTIME_APIS := true"
  },
  {
    "path": "target/product/security/Android.bp",
    "content": "// AOSP test certificate\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nandroid_app_certificate {\n    name: \"aosp-testkey\",\n    certificate: \"testkey\",\n}\n\n// Certificate for CTS tests that rely on UICC hardware conforming to the\n// updated CTS UICC card specification introduced in 2021. See\n// //cts/tests/tests/carrierapi/Android.bp for more details.\nandroid_app_certificate {\n    name: \"cts-uicc-2021-testkey\",\n    certificate: \"cts_uicc_2021\",\n}\n\n// Google-owned certificate for CTS testing, since we can't trust arbitrary keys\n// on release devices.\nprebuilt_etc {\n    name: \"fsverity-release-cert-der\",\n    src: \"fsverity-release.x509.der\",\n    sub_dir: \"security/fsverity\",\n    filename_from_src: true,\n}\n\n// otacerts: A keystore with the authorized keys in it, which is used to verify\n// the authenticity of downloaded OTA packages.\n// This module zips files defined in PRODUCT_DEFAULT_DEV_CERTIFICATE and\n// PRODUCT_EXTRA_OTA_KEYS for system or PRODUCT_EXTRA_RECOVERY_KEYS for recovery\n// image\notacerts_zip {\n    name: \"otacerts\",\n    relative_install_path: \"security\",\n    filename: \"otacerts.zip\",\n}\n\notacerts_zip {\n    name: \"otacerts.recovery\",\n    recovery: true,\n    relative_install_path: \"security\",\n    filename: \"otacerts.zip\",\n}\n\nadb_keys {\n    name: \"adb_keys\",\n    product_specific: true,\n}\n"
  },
  {
    "path": "target/product/security/README",
    "content": "For detailed information on key types and image signing, please see:\n\nhttps://source.android.com/devices/tech/ota/sign_builds.html\n\nThe test keys in this directory are used in development only and should\nNEVER be used to sign packages in publicly released images (as that would\nopen a major security hole).\n\nkey generation\n--------------\n\nThe following commands were used to generate the test key pairs:\n\n  development/tools/make_key testkey       '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'\n  development/tools/make_key platform      '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'\n  development/tools/make_key shared        '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'\n  development/tools/make_key media         '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'\n  development/tools/make_key cts_uicc_2021 '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'\n\nsigning using the openssl commandline (for boot/system images)\n--------------------------------------------------------------\n\n1. convert pk8 format key to pem format\n   % openssl pkcs8 -inform DER -nocrypt -in testkey.pk8 -out testkey.pem\n\n2. create a signature using the pem format key\n   % openssl dgst -binary -sha1 -sign testkey.pem FILE > FILE.sig\n\nextracting public keys for embedding\n------------------------------------\n\ndumpkey.jar is a Java tool that takes an x.509 certificate in PEM format as\ninput and prints a C structure to standard output:\n\n    $ java -jar out/host/linux-x86/framework/dumpkey.jar build/make/target/product/security/testkey.x509.pem\n    {64,0xc926ad21,{1795090719,2141396315,950055447,2581568430,4268923165,1920809988,546586521,3498997798,1776797858,3740060814,1805317999,1429410244,129622599,1422441418,1783893377,1222374759,2563319927,323993566,28517732,609753416,1826472888,215237850,4261642700,4049082591,3228462402,774857746,154822455,2497198897,2758199418,3019015328,2794777644,87251430,2534927978,120774784,571297800,3695899472,2479925187,3811625450,3401832990,2394869647,3267246207,950095497,555058928,414729973,1136544882,3044590084,465547824,4058146728,2731796054,1689838846,3890756939,1048029507,895090649,247140249,178744550,3547885223,3165179243,109881576,3944604415,1044303212,3772373029,2985150306,3737520932,3599964420},{3437017481,3784475129,2800224972,3086222688,251333580,2131931323,512774938,325948880,2657486437,2102694287,3820568226,792812816,1026422502,2053275343,2800889200,3113586810,165549746,4273519969,4065247892,1902789247,772932719,3941848426,3652744109,216871947,3164400649,1942378755,3996765851,1055777370,964047799,629391717,2232744317,3910558992,191868569,2758883837,3682816752,2997714732,2702529250,3570700455,3776873832,3924067546,3555689545,2758825434,1323144535,61311905,1997411085,376844204,213777604,4077323584,9135381,1625809335,2804742137,2952293945,1117190829,4237312782,1825108855,3013147971,1111251351,2568837572,1684324211,2520978805,367251975,810756730,2353784344,1175080310}}\n\nThis is called by build/make/core/Makefile to incorporate the OTA signing keys\ninto the recovery image.\n\nconverting to java keystore for Android Studio\n----------------------------------------------\n\nSuppose we want to convert shared.pk8 and shared.x509.pem to shared.keystore.\n\n $ openssl pkcs8 -inform DER -nocrypt                      \\\n   -in build/make/target/product/security/shared.pk8       \\\n   -out shared.pem\n $ openssl pkcs12 -export                                  \\\n   -in build/make/target/product/security/shared.x509.pem  \\\n   -inkey shared.pem -out shared.p12                       \\\n   -password pass:android -name AndroidDebugKey\n $ keytool -importkeystore -deststorepass android          \\\n   -destkeystore shared.keystore -srckeystore shared.p12   \\\n   -srcstoretype PKCS12 -srcstorepass android\n\nThe keystore can be used in build.gradle as follows.\n\nsigningConfigs {\n    shared {\n        storeFile file(\"shared.keystore\")\n        storePassword \"android\"\n        keyPassword \"android\"\n        keyAlias \"AndroidDebugKey\"\n    }\n}\n\n"
  },
  {
    "path": "target/product/security/bluetooth.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIGOzCCBCOgAwIBAgIUEiZapaWZVSter06CJMf2kHi8PIswDQYJKoZIhvcNAQEL\nBQAwgasxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH\nDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy\nb2lkMScwJQYDVQQDDB5jb20uYW5kcm9pZC5ibHVldG9vdGguc2VydmljZXMxIjAg\nBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMjIwMzE1MDAzNjAz\nWhgPNDc2MDAyMDkwMDM2MDNaMIGrMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2Fs\naWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9p\nZDEQMA4GA1UECwwHQW5kcm9pZDEnMCUGA1UEAwweY29tLmFuZHJvaWQuYmx1ZXRv\nb3RoLnNlcnZpY2VzMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsVlq9pozUREGlb8u8Y0A\nfYwPs5OuavNx/EsX03aTjmAXUfSOMAewqzUXDIRjw8UQvOW63utaZ0go9osDPzNf\nVEftmGxW/AUC+HWGaLDQfCYO3ficPPOS7xpEhGZERNbnhvh5qX0NBt6mJygsfpOm\nRPThbi6Ig2Brxh1eqVYqRkTjhNFKD6gCd1PdMmUSF88xEYaZWvTkET89Zh38lLza\n2x/wfNZmCSAVurNw1Kf9NQfYsaGHwMsjrvTyhG93TTYXzRBFzAO2WlBiw6R0tQr8\nZW5XCM9Yo6AS0KXiU0ZWwOXxhGdr38rNd7j9nZtpFwWmN1kgeb/vpEfq0Ylua9By\nuURnfJZu2K4TbFamuyjihItra2ZKOtFNPDeuggKMCkuZz6WU8FCoMEpnq5P2agxN\nOGAa7ynXdNzek98N3TGX8qtfEgCv6vyuM0gakJ6D9nM43nsCm1LkB/JA0CacWyRz\nljaLL1C4S43azEOYyOOb94ITnkZCQGtH33kxzamyPLIZ37VF4+v6yTXySLBzOnhe\nOs5uBIDohVJuI838bLhZf8e5mIrnjiKwsmExXiQvgidbwvZKCz9n8YT4iUhWPx4F\nW+GPcivZsvsECcnJ2QURK1zhir5QuLS7ZbAth4kiEUxJ6ujF5jftE+L/ClK2LiY0\n2IXWRCct8J1hfJZZx8lm3PUCAwEAAaNTMFEwHQYDVR0OBBYEFO5CgtQzKbTEd/Q9\nrxK14a9BBwFZMB8GA1UdIwQYMBaAFO5CgtQzKbTEd/Q9rxK14a9BBwFZMA8GA1Ud\nEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAGrGS1zmaoARVq7qhoY+xzSc\n1I/Tzf6vG6aHBC+CcIoSM2oqr6TGH+ADHAY6jhu/qzv1ij3gtoInAkBtkWvYsCIV\neISPj8Qomcd8EIeW77p+ArKzS4HY5m1c/O4D/5rkl6c0exFq4Pdw9V8xyM98QtLd\noj4xzzXUTPOIwkROHkj8otcML28m/MC0l/4b+flHnPqKFuLBjhxi9b/ZfwaXfjkx\nTcXpM3nPH8zN7kaJpS1fPW1IJyxJYvT022uK+afpezTmyS/50aOncUGjDJRw8CcO\nB88O8lpizDD3tD7P6jVOpRRJS4SnkVErbIn1xdWER6ubhnnycH7UmDVIx+vNd/t6\nYDa377au8Za+LnbDPfV1+Og+RaJSEIjJgfYyqnjBxGdRGN21VbqJdRzo/eO4ZFd2\nmGVtMosVr0jw4O8r60o9oMMWBTbFpxOI929QdcV+X1Lz8A8BZz0faXfZ2Z9usctu\nW2FtZge3tsJ07z7kuhNdbnm2yQVfd0FqiJsapUjlhgcdFVoDWPuqOfWAoG31ble6\neiNnxfjiCckPWyciIE6lw97nvavGjlUacH5qVG86hOWU7xyBgeQ0PH4e+Nxr50yU\nA0GMxni1gefZFG8qEPdNRuDT1QdqDGh/8Ea11GEUMXdAxk0UzqyAtLDr6MbwK6lV\nmqmeueFdogdjvQ3mXe94\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "target/product/security/cts_uicc_2021.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIECzCCAvOgAwIBAgIUHYLIIL60vWPD6aOBwZUcdbsae+cwDQYJKoZIhvcNAQEL\nBQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH\nDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy\nb2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu\nZHJvaWQuY29tMB4XDTIxMDEyNjAwMjAyMVoXDTQ4MDYxMzAwMjAyMVowgZQxCzAJ\nBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp\nbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD\nVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlOMSHqBu0ihUDfFgwMfO\npJtpyxHe0KKfHRndUQcYU/1v6/auy2YqkgKv+AraoukuU3gJeOiWoaqaWFNcm6md\nWfGRNT4oABhhNS43n5PI4NlLjI4yeUJJppZn5LPpc/8vZ0P8ZFE9CJmtckCh+hES\nBzqnxkCnq1PoxlcF3S/f8lOtd6ymaMDf3sYcePaoU8yTWFksl7EWRVwhBUIf7/r8\nepbNiV14/aH2cQfHVfpf54TIdk7s0/ehVA70A5gQp7Utn6mY2zEJlMrTKWRqA/a5\noYiob3y+v2JWNcljHY6twwDOGwW7G0NWJVtaWj76Z3o9RpIhAglivhOrHTflIU3+\n2QIDAQABo1MwUTAdBgNVHQ4EFgQUZJ1oGb33n/OY+Mm8ykci4I6c9OcwHwYDVR0j\nBBgwFoAUZJ1oGb33n/OY+Mm8ykci4I6c9OcwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQsFAAOCAQEASajvU0KCN2kfATPV95LQVE3N/URPi/lX9MfQptE54E+R\n6dHwHQIwU/fBFapAHfGgrpwUZftJO+Bad2iu5s1IhTJ0Q5v0yHdvWfo4EzVeMzPV\n+/DWU786pPEomFkb9ZKhgVkFNPcbXlkUm/9HxRHPRTm8x+BE/75PKI+kh+pDmM+P\n5v4W0qDKPgFzIY/D4F++gVyPZ3O+/GhunjsJozO+dvN+50FH6o/kBHm2+QqQNYPW\nf232F3CYtH4uWI0TkbwmSvVGW8iOqh330Cef5zqwSdOkzybUirXFsHUu1Zad1aLT\nt0mu6RgNEmX8efOQCcz2Z/on8lkIAxCBwLX7wkH5JA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "target/product/security/media.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEqDCCA5CgAwIBAgIJAPK5jmEjVyxOMA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD\nVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g\nVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE\nAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe\nFw0wODA0MTUyMzQwNTdaFw0zNTA5MDEyMzQwNTdaMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G\nA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p\nZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI\nhvcNAQEBBQADggENADCCAQgCggEBAK4lDFoW75f8KGmsZRsyF8w2ug6GlkFo1YoE\nn0DOhYZxI6P/tPbZScM88to6BcI+rKpX2AOImxdZvPWefG8hiQriUIW37VaqYmwJ\nie+czTY2LKDo0blgP9TYModnkmzMCQxot3Wuf/MJNMw2nvKFWiZn3wxmf9DHz12O\numVYBnNzA7tiRybquu37cvB+16dqs8uaOBxLfc2AmxQNiR8AITvkAfWNagamHq3D\nqcLxxlZyhbCa4JNCpm+kIer5Ot91c6AowzHXBgGrOvfMhAM+znx3KjpbhrDb6dd3\nw6SKqYAe3O4ngVifRNnkETl5YAV2qZQQuoEJElna2YxsaP94S48CAQOjgfwwgfkw\nHQYDVR0OBBYEFMopPKqLwO0+VC7vQgWiv/K1fk11MIHJBgNVHSMEgcEwgb6AFMop\nPKqLwO0+VC7vQgWiv/K1fk11oYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE\nCBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH\nQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG\nCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAPK5jmEjVyxOMAwGA1Ud\nEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAITelRbV5KhyF6c9qEhwSPUzc6X3\nM/OQ1hvfPMnlJRYlv8qnwxWcriddFyqa4eh21UWBJ6xUL2gpDdUQwAKdj1Hg7hVr\ne3tazbOUJBuOx4t05cQsXK+uFWyvW9GZojonUk2gct6743hGSlM2MLDk0P+34I7L\ncB+ttjecdEZ/bgDG7YiFlTgHkgOHVgB4csjjAHr0I6V6LKs6KChptkxLe9X8GH0K\nfiQVll1ark4Hpt91G0p16Xk8kYphK4HNC2KK7gFo3ETkexDTWTJghJ1q321yfcJE\nRMIh0/nsw2jK0HmZ8rgQW8HyDTjUEGbMFBHCV6lupDSfV0ZWVQfk6AIKGoE=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "target/product/security/networkstack.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIF3DCCA8SgAwIBAgIJAPxssNim/dFoMA0GCSqGSIb3DQEBCwUAMIGBMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g\nVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEhMB8GA1UE\nAwwYY29tLmFuZHJvaWQubmV0d29ya3N0YWNrMCAXDTE5MDIxMjAxNDYyMFoYDzQ3\nNTcwMTA4MDE0NjIwWjCBgTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju\naWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAO\nBgNVBAsMB0FuZHJvaWQxITAfBgNVBAMMGGNvbS5hbmRyb2lkLm5ldHdvcmtzdGFj\nazCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALtx9RN/8LLXV6zCyj03\njg+N4RCQ1crz1J4xTTXCg7d4sC15LY66RANkypcJhUQWYPC8AK+8Y91hGxv1GtKK\nHt0h4ASPVIuA+L0RPiVoKCL1fauCc6+vEsZNGaDGviOPPmbdx5sQ/ZJpMePuYKe/\nYYZE2jwsT8QoE51F0nvtp/5F4wB1tJPq1uwBzdVdkxwKZX4uWXQspjK23DhCot63\n0iRDyAkpHXpUkgOuauNWWCpMoj8w8FScTshAinUnjpXGnoOQrVKAvO+u9vEwmkG9\nnzv7XRLcp+eexv1oSBk/qatygiSIe0+T6YXsfL9kAbDoY6S5HAXQRvBA/pVABLFk\nWVT8tBFM7h6LZLR9cZoZ70wAHLGD9/PhZuQ/VtaAR8NEDaNP31KdRCdLiy9q+zRQ\nka2K1Lk71cVdUihqXTwVdGXbjd9i5822sQ+xiIgEav3SY65vISXZBldZx+QvhhCm\ndG7b3FR9QwFhLu7Dw8vRJN7OzI04sg5zsT8k7nyhOpjF9h8MgbB9K1GXSbwry54J\nSa72wRij6BJearV/zka7CRpmdA4Qsxx0C4kZAMDs2pzGnstPM2mZixdRBt0KT/1w\nJOt+df7dGlsTHQuytAxjSR48+GuJV7IVIbOpbtE3alGmrGl4ZrAlbe4bzZq5oYi/\nTO2AtZpfJMLamlXrew5QIRbjAgMBAAGjUzBRMB0GA1UdDgQWBBSTg8ks+/CZ1cR7\nDDZX2GIqCEty4TAfBgNVHSMEGDAWgBSTg8ks+/CZ1cR7DDZX2GIqCEty4TAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBqBQE4L94qa49wxgzRuO5P\neIcYwoixcCWO86liMLZQBWUNakxCpZqXst3sUCQT57Q4+9BgNj10t0ojI4Kn93/T\n2jTjj3n60DWotHLFz/NlgYoBGNh/oeMcx+1L79J2KHYMKQmAw8w7f/DP0Bt1/x/M\ng+mBtbJaVNhbaKgEJKwmAV+zpMdUlppxF0wLwoP2yIGR3O1gniRfWTj/0K15kZji\n0L9jQiIcGwpdMy7S//xmiYLKu8t9O2MP+EduXISsCtN635IkA1IAA5+V7B+pW/g3\nlsDomGE1zuLcrvGQskmFWn5zl9SgvxfqY9l4WJxrSBGKOB//vXkMRNgCM+LjUpKj\ntVM8o/LMFz+Fz5BK3+Lk4hg9weug664HuDmoH/G8kuKSVQlXyFma8h6cBJe5I0zj\nRfP1CLHMhyqlXdtedzxcfdZXe5qLba7SCuH/S4IG/Z9cj1oiuhmAvvAa5vyyZZuX\nrVuYX6gcAZ/+AI3dnIEwwG/GAyshScIgn8Q4p+jDsgzgNlCtMcTuSPFpd3oK4YK3\nLKMbgVQPYfFn2Net9Pa7IzD/XCQDckUADYFywSq11apYkLixLbDw5yliZOtm5/lx\nTDEARkn7S4ZABfnEPIDbP23lL9RNbiA2v+f1gHFW7Vq1kdBv1ruTukM06ic5r4tB\n7SaGRU5gtmbRBzi7e6iAAQ==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "target/product/security/nfc.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\r\nMIIF2DCCA8CgAwIBAgIUC94q348hFaPm2jow3R84ZjNFc3EwDQYJKoZIhvcNAQELBQAwfDELMAkG\r\nA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDAS\r\nBgNVBAoTC0dvb2dsZSBJbmMuMRAwDgYDVQQLEwdBbmRyb2lkMRgwFgYDVQQDDA9jb21fYW5kcm9p\r\nZF9uZmMwIBcNMjMxMTAxMjEzNzE1WhgPMjA1MzExMDEyMTM3MTVaMHwxCzAJBgNVBAYTAlVTMRMw\r\nEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29n\r\nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEYMBYGA1UEAwwPY29tX2FuZHJvaWRfbmZjMIICIjAN\r\nBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArpgwKyLDl8M3KRb1Fxs3P2mnt81sB3uGZs44R6C6\r\nCwFhiiACOmEQBcm79BUKBMrE9dUbyOL/GKluNzD026UsDE+N2wDQ8siTxaljDAwhZBpurhOu4uH8\r\nBKJOzoczAlJFMHpFIMCKQXwotMjT93BuhlSo024Q5QDd2j7Gajk21TkkyQNlBOiyEpKkrRPBuArw\r\n2knqhuX+nLYkJ5roANaJVDsiKMDG/mKnjwAndrgVbBiKaOdfuRd+pJleN3LUkAfYHHBqlOJnPGSI\r\njfYK+9TjsIEYVEOb4SMI3CbWwHfOdEIBgz3IPqMtamEnbZHNlfVWURTNGAF2co+DF3TDGDEjraK4\r\nR5pXDk/W+4Ex77wQPCIT+d981zkbTpgsPXvZmsBzCYMw6tYksPj86fSVJUrJhlibDk4YHVFsF7OK\r\narNf044yPhZ+WUIDqWJ6GB0GU8LWGbbe8iaP0ro9Q1DYgYc6buYWIcX81XZO+hHgWtUb2rNqIUsp\r\n/4DmT1vgz7TiMWcY7pjrHlNHtVf4jC+OU2c+p8u4XUGQxdIKGgZSoHldtAcnwqGuIpat9lS+gtVl\r\nvJUp8w3Z2gv4q/bBVZ3NNasA1d3HXVQUWiwszcjiVvoSRa/AlMVUGureGRbsiKsyHisYp9rxk1DB\r\ndPS9h7tMs/5rV6RM2nZfdfQr71zX9ieSoz0CAwEAAaNQME4wDAYDVR0TBAUwAwEB/zAdBgNVHQ4E\r\nFgQU9v9SL0QIU9fq7aB70/jqVBLmZJUwHwYDVR0jBBgwFoAU9v9SL0QIU9fq7aB70/jqVBLmZJUw\r\nDQYJKoZIhvcNAQELBQADggIBAExt2/NFt+2IhC5+/cgi8hzvwZKQyml1MQ9pjrkfQy0JGzGTOPDr\r\n+NPuT5jh/SOfGzdBsGVs3hvK7hFvXsFEWwVQaDOkKhcruks+g7FxldhXC2z9iKgjNrOeUXoE7SiE\r\nzXA/p1KiBcRL3MSMbg/iQjQVxlJky4BDo39SoEgDeL9+i7L4cBwZQ2LBBLPIOdE7G/cG1Q6UE+KN\r\n/mnz0kk3+FIg0q4szXDxH+o2V4ZYHSOy8P6TBW8gEQ71RGOnh0wtaTIx2RD/zqJAi+BzWMLsC636\r\nTNMmqKassG4MH445ul2w0ZzOClJ4gkl1e7dtK7/Kes4kcLOI/i4JHLOcydEqum+t8gQtMYyGM1Kv\r\nmVpC3hEv2pYwFfhg8hg31MljZmLD761kLOLfw98N350h6dNdQ0jci/3rqbbjVinVQoQSVEzJcA9Q\r\ngQhRLKHiO7oRmht6ilRLFtGZd/PwIMWMNqksTfVM5frMIIZXdfew+efHIJ7X+ZgJu3tGWcbFYFte\r\nK/BbmPLnp3aAGg/wwU1dqwCANf53oUc4ZzqRm9eovlVsrFiRM/DGt2/t4ujorU6Uwwt2+n05QU7b\r\n7PXhc7bTP6adUWMNMxSNIPo6wHmsTb2pCg+K5LuNMFJzXcoI3uBW9Qu4M/tLRv4kRKZzphqUbX+e\r\n/5hW2myw2BvbdwWFrz6XBgkz\r\n-----END CERTIFICATE-----\r\n"
  },
  {
    "path": "target/product/security/platform.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEqDCCA5CgAwIBAgIJALOZgIbQVs/6MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD\nVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g\nVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE\nAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe\nFw0wODA0MTUyMjQwNTBaFw0zNTA5MDEyMjQwNTBaMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G\nA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p\nZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI\nhvcNAQEBBQADggENADCCAQgCggEBAJx4BZKsDV04HN6qZezIpgBuNkgMbXIHsSAR\nvlCGOqvitV0Amt9xRtbyICKAx81Ne9smJDuKgGwms0sTdSOkkmgiSQTcAUk+fArP\nGgXIdPabA3tgMJ2QdNJCgOFrrSqHNDYZUer3KkgtCbIEsYdeEqyYwap3PWgAuer9\n5W1Yvtjo2hb5o2AJnDeoNKbf7be2tEoEngeiafzPLFSW8s821k35CjuNjzSjuqtM\n9TNxqydxmzulh1StDFP8FOHbRdUeI0+76TybpO35zlQmE1DsU1YHv2mi/0qgfbX3\n6iANCabBtJ4hQC+J7RGQiTqrWpGA8VLoL4WkV1PPX8GQccXuyCcCAQOjgfwwgfkw\nHQYDVR0OBBYEFE/koLPdnLop9x1yh8Tnw48ghsKZMIHJBgNVHSMEgcEwgb6AFE/k\noLPdnLop9x1yh8Tnw48ghsKZoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE\nCBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH\nQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG\nCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJALOZgIbQVs/6MAwGA1Ud\nEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAFclUbjZOh9z3g9tRp+G2tZwFAAp\nPIigzXzXeLc9r8wZf6t25iEuVsHHYc/EL9cz3lLFCuCIFM78CjtaGkNGBU2Cnx2C\ntCsgSL+ItdFJKe+F9g7dEtctVWV+IuPoXQTIMdYT0Zk4u4mCJH+jISVroS0dao+S\n6h2xw3Mxe6DAN/DRr/ZFrvIkl5+6bnoUvAJccbmBOM7z3fwFlhfPJIRc97QNY4L3\nJ17XOElatuWTG5QhdlxJG3L7aOCA29tYwgKdNHyLMozkPvaosVUz7fvpib1qSN1L\nIC7alMarjdW4OZID2q4u1EYjLk/pvZYTlMYwDlE448/Shebk5INTjLixs1c=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "target/product/security/sdk_sandbox.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIECzCCAvOgAwIBAgIUMWJGQnrJU7zBEpPqv63u2HOlib0wDQYJKoZIhvcNAQEL\nBQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH\nDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy\nb2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu\nZHJvaWQuY29tMB4XDTIxMTEwMjE3MDIxNFoXDTQ5MDMyMDE3MDIxNFowgZQxCzAJ\nBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp\nbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD\nVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA09j3dyTxv8ojb4sXjrWX\nsmXTYEez/u6X6po8+mWXp1xl1Y9xjYrxZROIE1MJL8aay8iYJihqx7RBWTPJYtYZ\nTLElA3dyQuMgDIKtlQR3QAMRoc2IKrkfcIboEs71xl78EnTSQfRJTUEFvNigzjfB\ne3JVtNDC9BR/33Iv9oNED84qW9C54h4TWHLyvo75unzPQUGS6uEIhhHa/8ynZZQW\nYEd0NwAQNqbcMdbN8Bn6sRRCidEOIPd8Uu8DtIofLi7/YMo4CH1Q5f5UQbtPtqU2\nm8fjQN9WYzMazvWltRE+HYDH9YnXCLAsVicNdmFhAlXri15nG2AiRnSrHu/panAc\n6wIDAQABo1MwUTAdBgNVHQ4EFgQU3F5r2DhJbRfkJKuqs1hjP/0dCUEwHwYDVR0j\nBBgwFoAU3F5r2DhJbRfkJKuqs1hjP/0dCUEwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQsFAAOCAQEAwQQ8/D3f/WS5cwqcsFpT+Qzik9yTu53nsXz/pBDSbeM3\nzX1RCejXsmXhPjN7cu0uJYlrIuArOagHSC5pDci6GzcwunnnkRazSAmTpHLSRgeb\ncLgKHLCph9sulI1r82x9upF47zLlbfkTrtGJryej+yWJ2Ne8irJIPeNR0z0sTBWJ\n2Ngg55ezFWj3mihzw4Z6YU9txJB7Gj9eNYXdcubjoNs2mSU/6dR+HwJtD64FuH3x\nQLGMZscizCN8N6b5xayjwPsszQhaHI4iR4oGJ9prbDd0JoylwWr2LrQhYuWQCn20\ncG5YhrtZshj6f1eGV1TDYd8xziapilqwzrchARvP8g==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "target/product/security/shared.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEqDCCA5CgAwIBAgIJAPKnM5a9OHZ6MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD\nVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g\nVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE\nAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe\nFw0wODA3MjMyMTU3NTlaFw0zNTEyMDkyMTU3NTlaMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G\nA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p\nZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI\nhvcNAQEBBQADggENADCCAQgCggEBAMjC2/0JSi30XD/xoy7SGAXscvxY0BeXG9D2\ntSwmLXCBnRkZZ+FY39Oix/Gz4OgM5UXXnShIIgIR64bw/YMS03tCDBE3UMyUYYro\ncvSIZGO9xGJ8qgwEg8hkk+NRVXEXAzi/3MTNat3RwKLzX1zyTtPkBDo+WOKwXmZM\nzeEry2dzX9bfEknDaeYlQrwKRynlORf1w4/6UtF7c8nHN5jdsY7UgVkIdVR+Zr/F\n2spMJabrlg7ZaSNwnaMCumRstJazJehsXIsuejN3srvkx88zJUKRFj9okVKsCIVQ\nyDxQj0v1rfCu1aLcoFg/mrCtF2UNt+6ksj/bRYhVR9D+q3IYOIkCAQOjgfwwgfkw\nHQYDVR0OBBYEFMtMfizbs/CtqY2reZaNFy6dux7RMIHJBgNVHSMEgcEwgb6AFMtM\nfizbs/CtqY2reZaNFy6dux7RoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE\nCBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH\nQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG\nCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAPKnM5a9OHZ6MAwGA1Ud\nEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAECo0JaZeVnpF6NsRCRra6wrrgVD\nfs2JeUEY94NHIDUtHG+KObCGmUL02mWYH6opUdM5cRKewZIdeVZxxSfW4knyUoKf\nr1tZExAxHi3gllANVorUEUplbcNKjG9hBFOvwep5ktukqns/hUOm41wHKN53/pfu\nrIN3H9DskPjkRJQ07gtgRXg+cMei5GAkkmDgA892CNw1Kkye9wbe9LJgUOl4ri//\n16MyN4cBSRXrPMh0/MeprpMId8XIx9HC4qjuhjyJGA0YVc7bpADnukPMyqckPTl+\nfA6Ojk19T5K2u+rUnAzwGAae3coufi+0Zo2J2715UNDNJUGA+h6q/CpVb4Q=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "target/product/security/testkey.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD\nVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g\nVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE\nAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe\nFw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G\nA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p\nZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI\nhvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM\nqOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4\nwgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy\n4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU\nRNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s\nzI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw\nHQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ\nAFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE\nCBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH\nQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG\nCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud\nEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZa\nJ6cVosK0TyIUFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4Y\nLCBOsVMR9FXYJLZW2+TcIkCRLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe\n+ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn9gP+pWA7LFQNvXwBnDa6sppCccEX\n31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqGCHzzTy3sIeJFymwr\nsBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "target/product/sysconfig/Android.bp",
    "content": "// Copyright (C} 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"};\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nprebuilt_etc {\n    name: \"preinstalled-packages-platform-aosp-product.xml\",\n    product_specific: true,\n    sub_dir: \"sysconfig\",\n    src: \"preinstalled-packages-platform-aosp-product.xml\",\n}\n\nprebuilt_etc {\n    name: \"preinstalled-packages-platform-full-base.xml\",\n    sub_dir: \"sysconfig\",\n    src: \"preinstalled-packages-platform-full-base.xml\",\n}\n\nprebuilt_etc {\n    name: \"preinstalled-packages-platform-generic-system.xml\",\n    sub_dir: \"sysconfig\",\n    src: \"preinstalled-packages-platform-generic-system.xml\",\n}\n\nprebuilt_etc {\n    name: \"preinstalled-packages-platform-handheld-product.xml\",\n    product_specific: true,\n    sub_dir: \"sysconfig\",\n    src: \"preinstalled-packages-platform-handheld-product.xml\",\n}\n\nprebuilt_etc {\n    name: \"preinstalled-packages-platform-handheld-system.xml\",\n    sub_dir: \"sysconfig\",\n    src: \"preinstalled-packages-platform-handheld-system.xml\",\n}\n\nprebuilt_etc {\n    name: \"preinstalled-packages-platform-telephony-product.xml\",\n    product_specific: true,\n    sub_dir: \"sysconfig\",\n    src: \"preinstalled-packages-platform-telephony-product.xml\",\n}\n\nprebuilt_etc {\n    name: \"initial-package-stopped-states-aosp.xml\",\n    product_specific: true,\n    sub_dir: \"sysconfig\",\n    src: \"initial-package-stopped-states-aosp.xml\",\n}\n"
  },
  {
    "path": "target/product/sysconfig/initial-package-stopped-states-aosp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright (C) 2023 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n -->\n\n<!--\nThis XML defines an allowlist for packages that should not be scanned in a \"stopped\" state.\nWhen this feature is turned on (indicated by the config config_stopSystemPackagesByDefault in\ncore/res/res/values/config.xml) packages on the system partition that are encountered by\nthe PackageManagerService for the first time are scanned in the \"stopped\" state. This allowlist\nis also considered while creating new users on the device. Stopped state is not set during\nsubsequent reboots.\n\nExample usage\n    1. <initial-package-state package=\"com.example.app\" stopped=\"false\"/>\n        Indicates that a system package - com.example.app's initial stopped state should not be set\n        by the Package Manager. By default, system apps are marked as stopped.\n    2. <initial-package-state package=\"com.example.app\" stopped=\"true\"/>\n        Indicates that a system package - com.example.app's initial state should be set by the\n        Package Manager to \"stopped=true\". It will have the same effect on the\n        package's stopped state even if this package was not included in the allow list.\n    3. <initial-package-state package=\"com.example.app\"/>\n        Invalid usage.\n-->\n\n<config>\n\t<initial-package-state package=\"com.android.calendar\" stopped=\"false\"/>\n\t<initial-package-state package=\"com.android.camera2\" stopped=\"false\"/>\n\t<initial-package-state package=\"com.android.contacts\" stopped=\"false\"/>\n\t<initial-package-state package=\"com.android.documentsui\" stopped=\"false\"/>\n\t<initial-package-state package=\"com.android.messaging\" stopped=\"false\"/>\n\t<initial-package-state package=\"com.android.quicksearchbox\" stopped=\"false\"/>\n\t<initial-package-state package=\"com.android.settings\" stopped=\"false\"/>\n\t<initial-package-state package=\"com.android.stk\" stopped=\"false\"/>\n</config>\n"
  },
  {
    "path": "target/product/sysconfig/preinstalled-packages-platform-aosp-product.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2019 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- System packages to preinstall on all devices with aosp_product, per user type.\n     Documentation at frameworks/base/data/etc/preinstalled-packages-platform.xml\n-->\n<config>\n    <install-in-user-type package=\"com.android.wallpaperpicker\">\n        <install-in user-type=\"FULL\" />\n    </install-in-user-type>\n\n    <!-- System packages that should not be pre-installed on the CLONE profile. -->\n    <!-- Messages -->\n    <install-in-user-type package=\"com.android.messaging\">\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.PRIVATE\" />\n    </install-in-user-type>\n</config>\n"
  },
  {
    "path": "target/product/sysconfig/preinstalled-packages-platform-full-base.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2019 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- System packages to preinstall on all devices with full_base, per user type.\n     Documentation at frameworks/base/data/etc/preinstalled-packages-platform.xml\n-->\n<config>\n    <install-in-user-type package=\"com.android.wallpaper.livepicker\">\n        <install-in user-type=\"FULL\" />\n    </install-in-user-type>\n</config>\n"
  },
  {
    "path": "target/product/sysconfig/preinstalled-packages-platform-generic-system.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2022 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- System packages to preinstall on all devices with generic_system, per user type.\n     Documentation at frameworks/base/data/etc/preinstalled-packages-platform.xml\n-->\n<config>\n    <!--  Stk (SIM ToolKit)\n    TODO(b/258055479): Check if this should be preinstalled on SYSTEM user -->\n    <install-in-user-type package=\"com.android.stk\">\n        <install-in user-type=\"SYSTEM\" />\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.PRIVATE\" />\n    </install-in-user-type>\n</config>\n\n"
  },
  {
    "path": "target/product/sysconfig/preinstalled-packages-platform-handheld-product.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2019 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- System packages to preinstall on all devices with handheld_product, per user type.\n     Documentation at frameworks/base/data/etc/preinstalled-packages-platform.xml\n-->\n<config>\n    <!--  Android Keyboard (AOSP) (LatinIME) TODO(b/258055479) -->\n    <install-in-user-type package=\"com.android.inputmethod.latin\">\n        <install-in user-type=\"SYSTEM\" />\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n    </install-in-user-type>\n\n    <!--  Calendar  -->\n    <install-in-user-type package=\"com.android.calendar\">\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.PRIVATE\" />\n    </install-in-user-type>\n\n    <!--  Camera (Camera2) -->\n    <install-in-user-type package=\"com.android.camera2\">\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n    </install-in-user-type>\n\n    <!--  Clock (DeskClock) -->\n    <install-in-user-type package=\"com.android.deskclock\">\n        <install-in user-type=\"FULL\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.PRIVATE\" />\n    </install-in-user-type>\n\n    <!--  Contacts -->\n    <install-in-user-type package=\"com.android.contacts\">\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n    </install-in-user-type>\n\n    <!--  Gallery (Gallery2) -->\n    <install-in-user-type package=\"com.android.gallery3d\">\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.PRIVATE\" />\n    </install-in-user-type>\n\n    <!--  Search (QuickSearchBox) TODO(b/258055479) -->\n    <install-in-user-type package=\"com.android.quicksearchbox\">\n        <install-in user-type=\"SYSTEM\" />\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.PRIVATE\" />\n    </install-in-user-type>\n\n    <!-- WallpaperCropper -->\n    <install-in-user-type package=\"com.android.wallpapercropper\">\n        <install-in user-type=\"FULL\" />\n    </install-in-user-type>\n</config>\n"
  },
  {
    "path": "target/product/sysconfig/preinstalled-packages-platform-handheld-system.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2022 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- System packages to preinstall on all devices with handheld_system, per user type.\n     Documentation at frameworks/base/data/etc/preinstalled-packages-platform.xml\n-->\n<config>\n    <!-- Files (DocumentsUI) TODO(b/258055479) -->\n    <install-in-user-type package=\"com.android.documentsui\">\n        <install-in user-type=\"SYSTEM\" />\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n    </install-in-user-type>\n\n    <!--  Printer (BuiltInPrintService) (Does not show on launcher but shows on the share sheet) -->\n    <install-in-user-type package=\"com.android.bips\">\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n    </install-in-user-type>\n</config>\n"
  },
  {
    "path": "target/product/sysconfig/preinstalled-packages-platform-telephony-product.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2022 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- System packages to preinstall on all devices with telephony_product, per user type.\n     Documentation at frameworks/base/data/etc/preinstalled-packages-platform.xml\n-->\n<config>\n    <!--  Phone\n    TODO(b/258055373): Check if this should be preinstalled on SYSTEM user -->\n    <install-in-user-type package=\"com.android.dialer\">\n        <install-in user-type=\"SYSTEM\" />\n        <install-in user-type=\"FULL\" />\n        <install-in user-type=\"PROFILE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.CLONE\" />\n        <do-not-install-in user-type=\"android.os.usertype.profile.PRIVATE\" />\n    </install-in-user-type>\n</config>\n\n"
  },
  {
    "path": "target/product/telephony.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# All modules for telephony\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_vendor.mk)\n$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_product.mk)\n"
  },
  {
    "path": "target/product/telephony_product.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is the list of modules that are specific to products that have telephony\n# hardware, and install to the product partition.\n\n# /product packages\nPRODUCT_PACKAGES += \\\n    Dialer \\\n    ImsServiceEntitlement \\\n    preinstalled-packages-platform-telephony-product.xml\n"
  },
  {
    "path": "target/product/telephony_system.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is the list of modules that are specific to products that have telephony\n# hardware, and install on the system partition.\n\nPRODUCT_PACKAGES := \\\n    ONS \\\n    CarrierDefaultApp \\\n    CallLogBackup \\\n    com.android.cellbroadcast \\\n    CellBroadcastLegacyApp \\\n\nPRODUCT_COPY_FILES := \\\n"
  },
  {
    "path": "target/product/telephony_system_ext.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is the list of modules that are specific to products that have telephony\n# hardware, and install to the system_ext partition.\n\n# /system_ext packages\nPRODUCT_PACKAGES += \\\n    CarrierConfig \\\n    EmergencyInfo \\\n\nPRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \\\n    hwservicemanager \\\n    android.hidl.allocator@1.0-service \\\n"
  },
  {
    "path": "target/product/telephony_vendor.mk",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This is the list of modules that are specific to products that have telephony\n# hardware, and install outside the system partition.\n\n# /vendor packages\nPRODUCT_PACKAGES := \\\n    rild \\\n"
  },
  {
    "path": "target/product/updatable_apex.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# com.android.apex.cts.shim.v1_prebuilt overrides CtsShimPrebuilt\n# and CtsShimPrivPrebuilt since they are packaged inside the APEX.\nPRODUCT_PACKAGES += com.android.apex.cts.shim.v1_prebuilt\nPRODUCT_SYSTEM_PROPERTIES := ro.apex.updatable=true\n\n# Use compressed apexes in pre-installed partitions.\n# Note: this doesn't mean that all pre-installed apexes will be compressed.\n#  Whether an apex is compressed or not is controlled at apex Soong module\n#  via compresible property.\nPRODUCT_COMPRESSED_APEX := true\n"
  },
  {
    "path": "target/product/userspace_reboot.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# DEPRECATED! Do not inherit this.\n"
  },
  {
    "path": "target/product/virtual_ab_ota/OWNERS",
    "content": "zhangkelvin@google.com\ndvander@google.com\nakailash@google.com\n\n"
  },
  {
    "path": "target/product/virtual_ab_ota/README.md",
    "content": "# Virtual A/B makefiles\n\nDevices that uses Virtual A/B must inherit from one of the makefiles in this directory.\n\n## Structure\n\n```\nlaunch.mk\n  |- retrofit.mk\n  |- plus_non_ab.mk\n\nlaunch_with_vendor_ramdisk.mk\n  |- compression.mk\n\ncompression_retrofit.mk\n```\n"
  },
  {
    "path": "target/product/virtual_ab_ota/android_t_baseline.mk",
    "content": "#\n# Copyright (C) 2022 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This file should be used only for T launching devices. We maintain\n# this file just for backward compatibility for T launch devices\n# so that build doesn't break.\n#\n# All U+ launching devices should instead use vabc_features.mk.\n$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/vabc_features.mk)\n"
  },
  {
    "path": "target/product/virtual_ab_ota/compression.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk)\n\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.batch_writes=true\n\n# Optional assignment. On low memory devices, disabling io_uring can relieve cpu and memory\n# pressure during an OTA.\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled?=true\n\n# Enabling this property, will improve OTA install time\n# but will use an additional CPU core\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.threads=true\n\nPRODUCT_VIRTUAL_AB_COMPRESSION := true\nPRODUCT_PACKAGES += \\\n    snapuserd.vendor_ramdisk \\\n    snapuserd \\\n    snapuserd.recovery\n"
  },
  {
    "path": "target/product/virtual_ab_ota/compression_retrofit.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true\nPRODUCT_VIRTUAL_AB_COMPRESSION := true\n\n# For devices that are not GKI-capable (eg do not have vendor_boot),\n# snapuserd.ramdisk is included rather than snapuserd.vendor_ramdisk.\n# When using virtual_ab_ota_compression_retrofit.mk, either\n# virtual_ab_ota.mk or virtual_ab_ota_retrofit.mk must be inherited\n# as well.\nPRODUCT_PACKAGES += \\\n    snapuserd.ramdisk \\\n    snapuserd \\\n    snapuserd.recovery\n"
  },
  {
    "path": "target/product/virtual_ab_ota/compression_with_xor.mk",
    "content": "#\n# Copyright (C) 2021 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression.mk)\n\n\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true\n"
  },
  {
    "path": "target/product/virtual_ab_ota/launch.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPRODUCT_VIRTUAL_AB_OTA := true\n\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true\n\nPRODUCT_PACKAGES += e2fsck_ramdisk\n"
  },
  {
    "path": "target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Devices launching with Virtual A/B and has a vendor_boot partition is\n# preferred to inherit from this makefile instead of launch.mk.\n\nPRODUCT_VIRTUAL_AB_OTA := true\n\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true\n\nPRODUCT_PACKAGES += \\\n    linker.vendor_ramdisk \\\n    e2fsck.vendor_ramdisk \\\n    fsck.f2fs.vendor_ramdisk \\\n"
  },
  {
    "path": "target/product/virtual_ab_ota/plus_non_ab.mk",
    "content": "#\n# Copyright (C) 2020 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch.mk)\n\nPRODUCT_OTA_FORCE_NON_AB_PACKAGE := true\n\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.allow_non_ab=true\n"
  },
  {
    "path": "target/product/virtual_ab_ota/retrofit.mk",
    "content": "#\n# Copyright (C) 2019 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch.mk)\n\nPRODUCT_VIRTUAL_AB_OTA_RETROFIT := true\n\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.retrofit=true\n"
  },
  {
    "path": "target/product/virtual_ab_ota/vabc_features.mk",
    "content": "#\n# Copyright (C) 2022 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This file enables baseline features, such as io_uring,\n# userspace merge, etc. But sets compression method to none.\n# This .mk file also removes snapuserd from vendor ramdisk,\n# as T launching devices will have init_boot which has snapuserd\n# in generic ramdisk.\n#\n# T and U launching devices should include this .mk file, and configure\n# compression algorithm by setting\n# PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD to lz4, gz or brotli. Complete\n# set of supported algorithms can be found in\n# system/core/fs_mgr/libsnapshot/cow_writer.cpp\n\nPRODUCT_VIRTUAL_AB_OTA := true\n\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true\n\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.batch_writes=true\n\n# Optional assignments, low memory devices may benefit from overriding these.\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled?=true\nPRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled?=true\n\n# Low memory device configurations. If memory usage and cpu utilization is\n# a bottleneck during OTA, the below configurations can be added to a\n# device's .mk file improve performance for low mem devices.\n#\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.read_ahead_size=16\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.o_direct.enabled=true\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.merge_thread_priority=19\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.worker_thread_priority=0\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.num_worker_threads=3\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.num_merge_threads=1\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.num_verify_threads=1\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.cow_op_merge_size=16\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.verify_threshold_size=1073741824\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.verify_block_size=1048576\n\n# Enabling this property, will improve OTA install time\n# but will use an additional CPU core\n# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.threads=true\nifndef PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR\n    PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 65536\nendif\n\nPRODUCT_VIRTUAL_AB_COMPRESSION := true\nPRODUCT_VIRTUAL_AB_COMPRESSION_METHOD ?= none\nPRODUCT_PACKAGES += \\\n    snapuserd \\\n\n"
  },
  {
    "path": "target/product/window_extensions.mk",
    "content": "#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Extension of window_extensions_base.mk to enable the activity embedding\n# feature for all apps by default. All large screen devices must inherit\n# this in build. Optional for other form factors.\n#\n# Indicated whether the Activity Embedding feature should be guarded by\n# Android 15 to avoid app compat impact.\n# If true (or not set), the feature is only enabled for apps with target\n# SDK of Android 15 or above.\n# If false, the feature is enabled for all apps.\nPRODUCT_PRODUCT_PROPERTIES += \\\n    persist.wm.extensions.activity_embedding_guard_with_android_15=false\n"
  },
  {
    "path": "target/product/window_extensions_base.mk",
    "content": "#\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# The base version of window_extensions.mk to be included on all non-wearable\n# devices. Devices that don't support multi-window can choose to drop this.\n#\n# Note: by default the Activity Embedding feature is guarded by app's\n# targetSDK on Android 15 to avoid app compat impact.\n#\n# Large screen devices must inherit window_extensions.mk to enable the\n# Activity Embedding feature for all apps.\n\n# /system_ext packages\nPRODUCT_PACKAGES += \\\n    androidx.window.extensions \\\n    androidx.window.sidecar\n\n# properties\nPRODUCT_PRODUCT_PROPERTIES += \\\n    persist.wm.extensions.enabled=true\n"
  },
  {
    "path": "teams/Android.bp",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// DON'T ADD NEW RULES HERE. For more details refer to\n// go/new-android-ownership-model\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nteam {\n    name: \"trendy_team_qmc_pss\",\n\n    // go/trendy/manage/engineers/6342544841375744\n    trendy_team_id: \"6342544841375744\",\n}\n\nteam {\n    name: \"trendy_team_cpu_team\",\n\n    // go/trendy/manage/engineers/5119059747307520\n    trendy_team_id: \"5119059747307520\",\n}\n\nteam {\n    name: \"trendy_team_pwg_mobile\",\n\n    // go/trendy/manage/engineers/4869274588315648\n    trendy_team_id: \"4869274588315648\",\n}\n\nteam {\n    name: \"trendy_team_pce_weu\",\n\n    // go/trendy/manage/engineers/5205725968891904\n    trendy_team_id: \"5205725968891904\",\n}\n\nteam {\n    name: \"trendy_team_peeps_t_pgm_android_engprod\",\n\n    // go/trendy/manage/engineers/6288284960358400\n    trendy_team_id: \"6288284960358400\",\n}\n\nteam {\n    name: \"trendy_team_appsearch\",\n\n    // go/trendy/manage/engineers/5075661716815872\n    trendy_team_id: \"5075661716815872\",\n}\n\nteam {\n    name: \"trendy_team_shayba_team\",\n\n    // go/trendy/manage/engineers/6213135020228608\n    trendy_team_id: \"6213135020228608\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_software\",\n\n    // go/trendy/manage/engineers/4856005120622592\n    trendy_team_id: \"4856005120622592\",\n}\n\nteam {\n    name: \"trendy_team_platform_enabler_framework_make_pixel_\",\n\n    // go/trendy/manage/engineers/5893944097243136\n    trendy_team_id: \"5893944097243136\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_power_sw\",\n\n    // go/trendy/manage/engineers/6703184655286272\n    trendy_team_id: \"6703184655286272\",\n}\n\nteam {\n    name: \"trendy_team_marvinpaul_team\",\n\n    // go/trendy/manage/engineers/4800689692901376\n    trendy_team_id: \"4800689692901376\",\n}\n\nteam {\n    name: \"trendy_team_interactive_tv\",\n\n    // go/trendy/manage/engineers/6150577853661184\n    trendy_team_id: \"6150577853661184\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_sensor_framework\",\n\n    // go/trendy/manage/engineers/5005310567284736\n    trendy_team_id: \"5005310567284736\",\n}\n\nteam {\n    name: \"trendy_team_wsd_arch\",\n\n    // go/trendy/manage/engineers/6173769806512128\n    trendy_team_id: \"6173769806512128\",\n}\n\nteam {\n    name: \"trendy_team_lanechr_team\",\n\n    // go/trendy/manage/engineers/5674594204811264\n    trendy_team_id: \"5674594204811264\",\n}\n\nteam {\n    name: \"trendy_team_test_eng_android_for_work\",\n\n    // go/trendy/manage/engineers/5909887015845888\n    trendy_team_id: \"5909887015845888\",\n}\n\nteam {\n    name: \"trendy_team_camera_app\",\n\n    // go/trendy/manage/engineers/5216644934533120\n    trendy_team_id: \"5216644934533120\",\n}\n\nteam {\n    name: \"trendy_team_vamaraju_team\",\n\n    // go/trendy/manage/engineers/5150510960771072\n    trendy_team_id: \"5150510960771072\",\n}\n\nteam {\n    name: \"trendy_team_android_media_audio_framework\",\n\n    // go/trendy/manage/engineers/5823575353065472\n    trendy_team_id: \"5823575353065472\",\n}\n\nteam {\n    name: \"trendy_team_superglue\",\n\n    // go/trendy/manage/engineers/5211667882999808\n    trendy_team_id: \"5211667882999808\",\n}\n\nteam {\n    name: \"trendy_team_display_framework\",\n\n    // go/trendy/manage/engineers/6035600925163520\n    trendy_team_id: \"6035600925163520\",\n}\n\nteam {\n    name: \"trendy_team_ananthak_team\",\n\n    // go/trendy/manage/engineers/6706043301298176\n    trendy_team_id: \"6706043301298176\",\n}\n\nteam {\n    name: \"trendy_team_qmc_pqm\",\n\n    // go/trendy/manage/engineers/4715267632267264\n    trendy_team_id: \"4715267632267264\",\n}\n\nteam {\n    name: \"trendy_team_search_allapps\",\n\n    // go/trendy/manage/engineers/4926160670195712\n    trendy_team_id: \"4926160670195712\",\n}\n\nteam {\n    name: \"trendy_team_communal\",\n\n    // go/trendy/manage/engineers/6380669942530048\n    trendy_team_id: \"6380669942530048\",\n}\n\nteam {\n    name: \"trendy_team_nova\",\n\n    // go/trendy/manage/engineers/5418955074043904\n    trendy_team_id: \"5418955074043904\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_framework_o_o\",\n\n    // go/trendy/manage/engineers/5999497213509632\n    trendy_team_id: \"5999497213509632\",\n}\n\nteam {\n    name: \"trendy_team_android_go\",\n\n    // go/trendy/manage/engineers/6543205713444864\n    trendy_team_id: \"6543205713444864\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_frameworks\",\n\n    // go/trendy/manage/engineers/5138392408817664\n    trendy_team_id: \"5138392408817664\",\n}\n\nteam {\n    name: \"trendy_team_ssd_sensor\",\n\n    // go/trendy/manage/engineers/5084703539200000\n    trendy_team_id: \"5084703539200000\",\n}\n\nteam {\n    name: \"trendy_team_dontek_team\",\n\n    // go/trendy/manage/engineers/5746076285042688\n    trendy_team_id: \"5746076285042688\",\n}\n\nteam {\n    name: \"trendy_team_carrier_field_test\",\n\n    // go/trendy/manage/engineers/6409766640975872\n    trendy_team_id: \"6409766640975872\",\n}\n\nteam {\n    name: \"trendy_team_pmw_standards\",\n\n    // go/trendy/manage/engineers/6428806822526976\n    trendy_team_id: \"6428806822526976\",\n}\n\nteam {\n    name: \"trendy_team_build_infra\",\n\n    // go/trendy/manage/engineers/4516184164433920\n    trendy_team_id: \"4516184164433920\",\n}\n\nteam {\n    name: \"trendy_team_qmc_gft\",\n\n    // go/trendy/manage/engineers/5454139446132736\n    trendy_team_id: \"5454139446132736\",\n}\n\nteam {\n    name: \"trendy_team_android_storage\",\n\n    // go/trendy/manage/engineers/6301594936049664\n    trendy_team_id: \"6301594936049664\",\n}\n\nteam {\n    name: \"trendy_team_pixel_mobile_wireless\",\n\n    // go/trendy/manage/engineers/4821918175887360\n    trendy_team_id: \"4821918175887360\",\n}\n\nteam {\n    name: \"trendy_team_camera_from_google\",\n\n    // go/trendy/manage/engineers/4799694104854528\n    trendy_team_id: \"4799694104854528\",\n}\n\nteam {\n    name: \"trendy_team_pixel_connectivity_settings\",\n\n    // go/trendy/manage/engineers/5622496450871296\n    trendy_team_id: \"5622496450871296\",\n}\n\nteam {\n    name: \"trendy_team_androidbugtool_abt_\",\n\n    // go/trendy/manage/engineers/6531817781493760\n    trendy_team_id: \"6531817781493760\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_security\",\n\n    // go/trendy/manage/engineers/6325699325362176\n    trendy_team_id: \"6325699325362176\",\n}\n\nteam {\n    name: \"trendy_team_pascallouis_team\",\n\n    // go/trendy/manage/engineers/5111238276317184\n    trendy_team_id: \"5111238276317184\",\n}\n\nteam {\n    name: \"trendy_team_android_camera_ecosystem_enabling\",\n\n    // go/trendy/manage/engineers/4529290269327360\n    trendy_team_id: \"4529290269327360\",\n}\n\nteam {\n    name: \"trendy_team_calendar\",\n\n    // go/trendy/manage/engineers/6719127573889024\n    trendy_team_id: \"6719127573889024\",\n}\n\nteam {\n    name: \"trendy_team_cgc\",\n\n    // go/trendy/manage/engineers/4590315499061248\n    trendy_team_id: \"4590315499061248\",\n}\n\nteam {\n    name: \"trendy_team_diagnostic_tool\",\n\n    // go/trendy/manage/engineers/4689924564746240\n    trendy_team_id: \"4689924564746240\",\n}\n\nteam {\n    name: \"trendy_team_pixel_camera_system_software\",\n\n    // go/trendy/manage/engineers/6386525306486784\n    trendy_team_id: \"6386525306486784\",\n}\n\nteam {\n    name: \"trendy_team_credential_manager\",\n\n    // go/trendy/manage/engineers/5276403428655104\n    trendy_team_id: \"5276403428655104\",\n}\n\nteam {\n    name: \"trendy_team_wear_wti_wear_tools_and_infra_\",\n\n    // go/trendy/manage/engineers/6225571306438656\n    trendy_team_id: \"6225571306438656\",\n}\n\nteam {\n    name: \"trendy_team_pixel_biometrics_face\",\n\n    // go/trendy/manage/engineers/5028705926742016\n    trendy_team_id: \"5028705926742016\",\n}\n\nteam {\n    name: \"trendy_team_location_time\",\n\n    // go/trendy/manage/engineers/4883807600017408\n    trendy_team_id: \"4883807600017408\",\n}\n\nteam {\n    name: \"trendy_team_android_hardware_backed_security\",\n\n    // go/trendy/manage/engineers/6398595556343808\n    trendy_team_id: \"6398595556343808\",\n}\n\nteam {\n    name: \"trendy_team_play_newsstand\",\n\n    // go/trendy/manage/engineers/5171015201980416\n    trendy_team_id: \"5171015201980416\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_framework_jaggies\",\n\n    // go/trendy/manage/engineers/5753206608887808\n    trendy_team_id: \"5753206608887808\",\n}\n\nteam {\n    name: \"trendy_team_make_pixel_tpgm\",\n\n    // go/trendy/manage/engineers/6061069864665088\n    trendy_team_id: \"6061069864665088\",\n}\n\nteam {\n    name: \"trendy_team_make_transformer\",\n\n    // go/trendy/manage/engineers/6224539427438592\n    trendy_team_id: \"6224539427438592\",\n}\n\nteam {\n    name: \"trendy_team_wittrock_team\",\n\n    // go/trendy/manage/engineers/5707412083474432\n    trendy_team_id: \"5707412083474432\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_android_companion_sdk\",\n\n    // go/trendy/manage/engineers/4864923637022720\n    trendy_team_id: \"4864923637022720\",\n}\n\nteam {\n    name: \"trendy_team_assistant_sysui_integration\",\n\n    // go/trendy/manage/engineers/4884282575060992\n    trendy_team_id: \"4884282575060992\",\n}\n\nteam {\n    name: \"trendy_team_things\",\n\n    // go/trendy/manage/engineers/5206199574069248\n    trendy_team_id: \"5206199574069248\",\n}\n\nteam {\n    name: \"trendy_team_wsd_w13\",\n\n    // go/trendy/manage/engineers/5612469120532480\n    trendy_team_id: \"5612469120532480\",\n}\n\nteam {\n    name: \"trendy_team_iqbalasif_team\",\n\n    // go/trendy/manage/engineers/4912049813094400\n    trendy_team_id: \"4912049813094400\",\n}\n\nteam {\n    name: \"trendy_team_biometric_security\",\n\n    // go/trendy/manage/engineers/5797911960649728\n    trendy_team_id: \"5797911960649728\",\n}\n\nteam {\n    name: \"trendy_team_silberst_team\",\n\n    // go/trendy/manage/engineers/5710892584042496\n    trendy_team_id: \"5710892584042496\",\n}\n\nteam {\n    name: \"trendy_team_pmw_telephony\",\n\n    // go/trendy/manage/engineers/6029121444151296\n    trendy_team_id: \"6029121444151296\",\n}\n\nteam {\n    name: \"trendy_team_zzz\",\n\n    // go/trendy/manage/engineers/6351340934397952\n    trendy_team_id: \"6351340934397952\",\n}\n\nteam {\n    name: \"trendy_team_lite_team\",\n\n    // go/trendy/manage/engineers/5647925813346304\n    trendy_team_id: \"5647925813346304\",\n}\n\nteam {\n    name: \"trendy_team_gms_core\",\n\n    // go/trendy/manage/engineers/5735614422843392\n    trendy_team_id: \"5735614422843392\",\n}\n\nteam {\n    name: \"trendy_team_dialer_make_pixel_\",\n\n    // go/trendy/manage/engineers/5126396509978624\n    trendy_team_id: \"5126396509978624\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_color\",\n\n    // go/trendy/manage/engineers/5489236125581312\n    trendy_team_id: \"5489236125581312\",\n}\n\nteam {\n    name: \"trendy_team_fwk_nfc\",\n\n    // go/trendy/manage/engineers/5962312512864256\n    trendy_team_id: \"5962312512864256\",\n}\n\nteam {\n    name: \"trendy_team_srajkumar_team\",\n\n    // go/trendy/manage/engineers/5170053894012928\n    trendy_team_id: \"5170053894012928\",\n}\n\nteam {\n    name: \"trendy_team_in_market_tpm\",\n\n    // go/trendy/manage/engineers/5352549888196608\n    trendy_team_id: \"5352549888196608\",\n}\n\nteam {\n    name: \"trendy_team_leannogasawara_team\",\n\n    // go/trendy/manage/engineers/4905467198472192\n    trendy_team_id: \"4905467198472192\",\n}\n\nteam {\n    name: \"trendy_team_zurikemp_team\",\n\n    // go/trendy/manage/engineers/4559796603879424\n    trendy_team_id: \"4559796603879424\",\n}\n\nteam {\n    name: \"trendy_team_android_telemetry_infra\",\n\n    // go/trendy/manage/engineers/5295809771732992\n    trendy_team_id: \"5295809771732992\",\n}\n\nteam {\n    name: \"trendy_team_system_ui_sensors\",\n\n    // go/trendy/manage/engineers/5647653492621312\n    trendy_team_id: \"5647653492621312\",\n}\n\nteam {\n    name: \"trendy_team_windowing_animations_transitions\",\n\n    // go/trendy/manage/engineers/4803040337362944\n    trendy_team_id: \"4803040337362944\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_framework_jjaggi\",\n\n    // go/trendy/manage/engineers/6471742270898176\n    trendy_team_id: \"6471742270898176\",\n}\n\nteam {\n    name: \"trendy_team_accessibility_hearing_aids\",\n\n    // go/trendy/manage/engineers/4661226340253696\n    trendy_team_id: \"4661226340253696\",\n}\n\nteam {\n    name: \"trendy_team_performance\",\n\n    // go/trendy/manage/engineers/5842000521625600\n    trendy_team_id: \"5842000521625600\",\n}\n\nteam {\n    name: \"trendy_team_cloud_android\",\n\n    // go/trendy/manage/engineers/5980255760023552\n    trendy_team_id: \"5980255760023552\",\n}\n\nteam {\n    name: \"trendy_team_visual_design\",\n\n    // go/trendy/manage/engineers/4504161399734272\n    trendy_team_id: \"4504161399734272\",\n}\n\nteam {\n    name: \"trendy_team_wilkinsonclay_team\",\n\n    // go/trendy/manage/engineers/5680997128634368\n    trendy_team_id: \"5680997128634368\",\n}\n\nteam {\n    name: \"trendy_team_tccyp_nadiae\",\n\n    // go/trendy/manage/engineers/6556518831652864\n    trendy_team_id: \"6556518831652864\",\n}\n\nteam {\n    name: \"trendy_team_accessibility_settings\",\n\n    // go/trendy/manage/engineers/5641806510587904\n    trendy_team_id: \"5641806510587904\",\n}\n\nteam {\n    name: \"trendy_team_hansmuller_team\",\n\n    // go/trendy/manage/engineers/5069192257765376\n    trendy_team_id: \"5069192257765376\",\n}\n\nteam {\n    name: \"trendy_team_retail_demo_mode\",\n\n    // go/trendy/manage/engineers/6520787531235328\n    trendy_team_id: \"6520787531235328\",\n}\n\nteam {\n    name: \"trendy_team_lse_dreams\",\n\n    // go/trendy/manage/engineers/6317558842097664\n    trendy_team_id: \"6317558842097664\",\n}\n\nteam {\n    name: \"trendy_team_android_usb\",\n\n    // go/trendy/manage/engineers/5090707854426112\n    trendy_team_id: \"5090707854426112\",\n}\n\nteam {\n    name: \"trendy_team_curtisgalloway_team\",\n\n    // go/trendy/manage/engineers/5706857730703360\n    trendy_team_id: \"5706857730703360\",\n}\n\nteam {\n    name: \"trendy_team_camera_algorithms\",\n\n    // go/trendy/manage/engineers/6544854980886528\n    trendy_team_id: \"6544854980886528\",\n}\n\nteam {\n    name: \"trendy_team_cast_3p\",\n\n    // go/trendy/manage/engineers/6585564972875776\n    trendy_team_id: \"6585564972875776\",\n}\n\nteam {\n    name: \"trendy_team_mesch_team\",\n\n    // go/trendy/manage/engineers/5205465899368448\n    trendy_team_id: \"5205465899368448\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_audio_arch\",\n\n    // go/trendy/manage/engineers/5560501377073152\n    trendy_team_id: \"5560501377073152\",\n}\n\nteam {\n    name: \"trendy_team_defunct_use_controls_quick_settings\",\n\n    // go/trendy/manage/engineers/4667861043412992\n    trendy_team_id: \"4667861043412992\",\n}\n\nteam {\n    name: \"trendy_team_android_rust\",\n\n    // go/trendy/manage/engineers/4844600586305536\n    trendy_team_id: \"4844600586305536\",\n}\n\nteam {\n    name: \"trendy_team_ailabs\",\n\n    // go/trendy/manage/engineers/6673470538285056\n    trendy_team_id: \"6673470538285056\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_connectivity\",\n\n    // go/trendy/manage/engineers/6245149466263552\n    trendy_team_id: \"6245149466263552\",\n}\n\nteam {\n    name: \"trendy_team_android_core_experiments\",\n\n    // go/trendy/manage/engineers/5709654965780480\n    trendy_team_id: \"5709654965780480\",\n}\n\nteam {\n    name: \"trendy_team_native_tools_libraries\",\n\n    // go/trendy/manage/engineers/5920332376309760\n    trendy_team_id: \"5920332376309760\",\n}\n\nteam {\n    name: \"trendy_team_app_compat\",\n\n    // go/trendy/manage/engineers/4907132411314176\n    trendy_team_id: \"4907132411314176\",\n}\n\nteam {\n    name: \"trendy_team_zra_team\",\n\n    // go/trendy/manage/engineers/6227615267586048\n    trendy_team_id: \"6227615267586048\",\n}\n\nteam {\n    name: \"trendy_team_pixel_watch_system_software\",\n\n    // go/trendy/manage/engineers/5295994500972544\n    trendy_team_id: \"5295994500972544\",\n}\n\nteam {\n    name: \"trendy_team_surfaces_engprod\",\n\n    // go/trendy/manage/engineers/6154478176600064\n    trendy_team_id: \"6154478176600064\",\n}\n\nteam {\n    name: \"trendy_team_android_permissions\",\n\n    // go/trendy/manage/engineers/5533977340313600\n    trendy_team_id: \"5533977340313600\",\n}\n\nteam {\n    name: \"trendy_team_platform_program_mgrs\",\n\n    // go/trendy/manage/engineers/4766394922958848\n    trendy_team_id: \"4766394922958848\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_system_health\",\n\n    // go/trendy/manage/engineers/4864801213644800\n    trendy_team_id: \"4864801213644800\",\n}\n\nteam {\n    name: \"trendy_team_messages\",\n\n    // go/trendy/manage/engineers/5137480097333248\n    trendy_team_id: \"5137480097333248\",\n}\n\nteam {\n    name: \"trendy_team_palmer_team\",\n\n    // go/trendy/manage/engineers/5643570052235264\n    trendy_team_id: \"5643570052235264\",\n}\n\nteam {\n    name: \"trendy_team_android_video_image_codecs\",\n\n    // go/trendy/manage/engineers/5733246110433280\n    trendy_team_id: \"5733246110433280\",\n}\n\nteam {\n    name: \"trendy_team_play_music\",\n\n    // go/trendy/manage/engineers/6015440132112384\n    trendy_team_id: \"6015440132112384\",\n}\n\nteam {\n    name: \"trendy_team_system_clockwork_internal_\",\n\n    // go/trendy/manage/engineers/6509670608797696\n    trendy_team_id: \"6509670608797696\",\n}\n\nteam {\n    name: \"trendy_team_multitasking_windowing\",\n\n    // go/trendy/manage/engineers/5149185436975104\n    trendy_team_id: \"5149185436975104\",\n}\n\nteam {\n    name: \"trendy_team_vr\",\n\n    // go/trendy/manage/engineers/4854355853180928\n    trendy_team_id: \"4854355853180928\",\n}\n\nteam {\n    name: \"trendy_team_maruel_team\",\n\n    // go/trendy/manage/engineers/6302551810146304\n    trendy_team_id: \"6302551810146304\",\n}\n\nteam {\n    name: \"trendy_team_tv_os\",\n\n    // go/trendy/manage/engineers/4662491074134016\n    trendy_team_id: \"4662491074134016\",\n}\n\nteam {\n    name: \"trendy_team_auto_engprod\",\n\n    // go/trendy/manage/engineers/6199949475479552\n    trendy_team_id: \"6199949475479552\",\n}\n\nteam {\n    name: \"trendy_team_sarahcobb_team\",\n\n    // go/trendy/manage/engineers/5755692179947520\n    trendy_team_id: \"5755692179947520\",\n}\n\nteam {\n    name: \"trendy_team_accessibility_services\",\n\n    // go/trendy/manage/engineers/6367283853000704\n    trendy_team_id: \"6367283853000704\",\n}\n\nteam {\n    name: \"trendy_team_documentsui\",\n\n    // go/trendy/manage/engineers/5805983167021056\n    trendy_team_id: \"5805983167021056\",\n}\n\nteam {\n    name: \"trendy_team_carrier_cert_follow_up\",\n\n    // go/trendy/manage/engineers/6751912099741696\n    trendy_team_id: \"6751912099741696\",\n}\n\nteam {\n    name: \"trendy_team_mobile_device_partners\",\n\n    // go/trendy/manage/engineers/5833057717092352\n    trendy_team_id: \"5833057717092352\",\n}\n\nteam {\n    name: \"trendy_team_activity_recognition\",\n\n    // go/trendy/manage/engineers/6304701268000768\n    trendy_team_id: \"6304701268000768\",\n}\n\nteam {\n    name: \"trendy_team_jasoncampbell_team\",\n\n    // go/trendy/manage/engineers/4834972524511232\n    trendy_team_id: \"4834972524511232\",\n}\n\nteam {\n    name: \"trendy_team_glanceables\",\n\n    // go/trendy/manage/engineers/4658222004600832\n    trendy_team_id: \"4658222004600832\",\n}\n\nteam {\n    name: \"trendy_team_android_safe_browsing\",\n\n    // go/trendy/manage/engineers/6685713244782592\n    trendy_team_id: \"6685713244782592\",\n}\n\nteam {\n    name: \"trendy_team_android_input\",\n\n    // go/trendy/manage/engineers/5141994775805952\n    trendy_team_id: \"5141994775805952\",\n}\n\nteam {\n    name: \"trendy_team_android_rust_toolchain\",\n\n    // go/trendy/manage/engineers/6530590989975552\n    trendy_team_id: \"6530590989975552\",\n}\n\nteam {\n    name: \"trendy_team_exo\",\n\n    // go/trendy/manage/engineers/5631545248088064\n    trendy_team_id: \"5631545248088064\",\n}\n\nteam {\n    name: \"trendy_team_android_camera_innovation_team\",\n\n    // go/trendy/manage/engineers/5272590669479936\n    trendy_team_id: \"5272590669479936\",\n}\n\nteam {\n    name: \"trendy_team_accessibility_sound_amplifier\",\n\n    // go/trendy/manage/engineers/5674840312020992\n    trendy_team_id: \"5674840312020992\",\n}\n\nteam {\n    name: \"trendy_team_android_printing\",\n\n    // go/trendy/manage/engineers/6257528146067456\n    trendy_team_id: \"6257528146067456\",\n}\n\nteam {\n    name: \"trendy_team_dtiselice_team\",\n\n    // go/trendy/manage/engineers/5177934253031424\n    trendy_team_id: \"5177934253031424\",\n}\n\nteam {\n    name: \"trendy_team_personal_safety\",\n\n    // go/trendy/manage/engineers/6222285147111424\n    trendy_team_id: \"6222285147111424\",\n}\n\nteam {\n    name: \"trendy_team_notifications\",\n\n    // go/trendy/manage/engineers/5993521355587584\n    trendy_team_id: \"5993521355587584\",\n}\n\nteam {\n    name: \"trendy_team_java_core_libraries\",\n\n    // go/trendy/manage/engineers/4768044190400512\n    trendy_team_id: \"4768044190400512\",\n}\n\nteam {\n    name: \"trendy_team_updatable_sdk_apis\",\n\n    // go/trendy/manage/engineers/4840215139483648\n    trendy_team_id: \"4840215139483648\",\n}\n\nteam {\n    name: \"trendy_team_wear_low_power_mcu_experiences\",\n\n    // go/trendy/manage/engineers/6172878013628416\n    trendy_team_id: \"6172878013628416\",\n}\n\nteam {\n    name: \"trendy_team_biometrics_framework\",\n\n    // go/trendy/manage/engineers/6205415425998848\n    trendy_team_id: \"6205415425998848\",\n}\n\nteam {\n    name: \"trendy_team_pesto\",\n\n    // go/trendy/manage/engineers/5551098528825344\n    trendy_team_id: \"5551098528825344\",\n}\n\nteam {\n    name: \"trendy_team_wear_engineering_foundations\",\n\n    // go/trendy/manage/engineers/5366936275681280\n    trendy_team_id: \"5366936275681280\",\n}\n\nteam {\n    name: \"trendy_team_wear_wcs_developer\",\n\n    // go/trendy/manage/engineers/5114199579459584\n    trendy_team_id: \"5114199579459584\",\n}\n\nteam {\n    name: \"trendy_team_aaos_framework\",\n\n    // go/trendy/manage/engineers/6547794223333376\n    trendy_team_id: \"6547794223333376\",\n}\n\nteam {\n    name: \"trendy_team_clockwork\",\n\n    // go/trendy/manage/engineers/4908781678755840\n    trendy_team_id: \"4908781678755840\",\n}\n\nteam {\n    name: \"trendy_team_pixel_connectivity_wifi_drivers_firmware\",\n\n    // go/trendy/manage/engineers/4583326236934144\n    trendy_team_id: \"4583326236934144\",\n}\n\nteam {\n    name: \"trendy_team_games\",\n\n    // go/trendy/manage/engineers/6736719759933440\n    trendy_team_id: \"6736719759933440\",\n}\n\nteam {\n    name: \"trendy_team_systems_n4_\",\n\n    // go/trendy/manage/engineers/6474486236708864\n    trendy_team_id: \"6474486236708864\",\n}\n\nteam {\n    name: \"trendy_team_android_kvm\",\n\n    // go/trendy/manage/engineers/6529318184714240\n    trendy_team_id: \"6529318184714240\",\n}\n\nteam {\n    name: \"trendy_team_wsd_w22\",\n\n    // go/trendy/manage/engineers/6580039352975360\n    trendy_team_id: \"6580039352975360\",\n}\n\nteam {\n    name: \"trendy_team_android_sudo\",\n\n    // go/trendy/manage/engineers/5329344876380160\n    trendy_team_id: \"5329344876380160\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_system_health_and_power\",\n\n    // go/trendy/manage/engineers/5219147457658880\n    trendy_team_id: \"5219147457658880\",\n}\n\nteam {\n    name: \"trendy_team_android_build_release_tools\",\n\n    // go/trendy/manage/engineers/6260558107803648\n    trendy_team_id: \"6260558107803648\",\n}\n\nteam {\n    name: \"trendy_team_fused_presence_provider\",\n\n    // go/trendy/manage/engineers/6536344753307648\n    trendy_team_id: \"6536344753307648\",\n}\n\nteam {\n    name: \"trendy_team_agsa\",\n\n    // go/trendy/manage/engineers/6157826887909376\n    trendy_team_id: \"6157826887909376\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_developer_tiles\",\n\n    // go/trendy/manage/engineers/6633777396613120\n    trendy_team_id: \"6633777396613120\",\n}\n\nteam {\n    name: \"trendy_team_essential_applications\",\n\n    // go/trendy/manage/engineers/4926373864800256\n    trendy_team_id: \"4926373864800256\",\n}\n\nteam {\n    name: \"trendy_team_pixel_mobile_data\",\n\n    // go/trendy/manage/engineers/4996742608977920\n    trendy_team_id: \"4996742608977920\",\n}\n\nteam {\n    name: \"trendy_team_wsd_w52\",\n\n    // go/trendy/manage/engineers/6280972190220288\n    trendy_team_id: \"6280972190220288\",\n}\n\nteam {\n    name: \"trendy_team_pixel_mobile_connectivity\",\n\n    // go/trendy/manage/engineers/6754311945977856\n    trendy_team_id: \"6754311945977856\",\n}\n\nteam {\n    name: \"trendy_team_essentialapps_clock_calculator\",\n\n    // go/trendy/manage/engineers/5270363728674816\n    trendy_team_id: \"5270363728674816\",\n}\n\nteam {\n    name: \"trendy_team_ssd_system_health\",\n\n    // go/trendy/manage/engineers/6456894050664448\n    trendy_team_id: \"6456894050664448\",\n}\n\nteam {\n    name: \"trendy_team_pixel_continuity\",\n\n    // go/trendy/manage/engineers/4786635551309824\n    trendy_team_id: \"4786635551309824\",\n}\n\nteam {\n    name: \"trendy_team_machine_learning\",\n\n    // go/trendy/manage/engineers/5276568318246912\n    trendy_team_id: \"5276568318246912\",\n}\n\nteam {\n    name: \"trendy_team_pixel_ml\",\n\n    // go/trendy/manage/engineers/5339883108990976\n    trendy_team_id: \"5339883108990976\",\n}\n\nteam {\n    name: \"trendy_team_ex_enterprise\",\n\n    // go/trendy/manage/engineers/6738369027375104\n    trendy_team_id: \"6738369027375104\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_aoc\",\n\n    // go/trendy/manage/engineers/4712464983425024\n    trendy_team_id: \"4712464983425024\",\n}\n\nteam {\n    name: \"trendy_team_android_platform_communications\",\n\n    // go/trendy/manage/engineers/6577505415102464\n    trendy_team_id: \"6577505415102464\",\n}\n\nteam {\n    name: \"trendy_team_sunshine\",\n\n    // go/trendy/manage/engineers/6105050329776128\n    trendy_team_id: \"6105050329776128\",\n}\n\nteam {\n    name: \"trendy_team_qmc_iqt_tao\",\n\n    // go/trendy/manage/engineers/5065462085713920\n    trendy_team_id: \"5065462085713920\",\n}\n\nteam {\n    name: \"trendy_team_mckillop_team\",\n\n    // go/trendy/manage/engineers/5926589599744000\n    trendy_team_id: \"5926589599744000\",\n}\n\nteam {\n    name: \"trendy_team_pixel_process_experience\",\n\n    // go/trendy/manage/engineers/5745436633235456\n    trendy_team_id: \"5745436633235456\",\n}\n\nteam {\n    name: \"trendy_team_wsd_l1\",\n\n    // go/trendy/manage/engineers/5119887911288832\n    trendy_team_id: \"5119887911288832\",\n}\n\nteam {\n    name: \"trendy_team_foldables\",\n\n    // go/trendy/manage/engineers/5149421392920576\n    trendy_team_id: \"5149421392920576\",\n}\n\nteam {\n    name: \"trendy_team_arc_next\",\n\n    // go/trendy/manage/engineers/6238917659361280\n    trendy_team_id: \"6238917659361280\",\n}\n\nteam {\n    name: \"trendy_team_android_rubidium\",\n\n    // go/trendy/manage/engineers/5098012529295360\n    trendy_team_id: \"5098012529295360\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_framework_svetoslavganov\",\n\n    // go/trendy/manage/engineers/6404117492531200\n    trendy_team_id: \"6404117492531200\",\n}\n\nteam {\n    name: \"trendy_team_gregsimon_team\",\n\n    // go/trendy/manage/engineers/5702018510520320\n    trendy_team_id: \"5702018510520320\",\n}\n\nteam {\n    name: \"trendy_team_text_to_speech\",\n\n    // go/trendy/manage/engineers/6368933120442368\n    trendy_team_id: \"6368933120442368\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_audio\",\n\n    // go/trendy/manage/engineers/6492078422753280\n    trendy_team_id: \"6492078422753280\",\n}\n\nteam {\n    name: \"trendy_team_transformer\",\n\n    // go/trendy/manage/engineers/5964312841420800\n    trendy_team_id: \"5964312841420800\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_video\",\n\n    // go/trendy/manage/engineers/6442361728696320\n    trendy_team_id: \"6442361728696320\",\n}\n\nteam {\n    name: \"trendy_team_lse_app_compat\",\n\n    // go/trendy/manage/engineers/5180827749154816\n    trendy_team_id: \"5180827749154816\",\n}\n\nteam {\n    name: \"trendy_team_android_media_leads\",\n\n    // go/trendy/manage/engineers/5487674550779904\n    trendy_team_id: \"5487674550779904\",\n}\n\nteam {\n    name: \"trendy_team_kousha_team\",\n\n    // go/trendy/manage/engineers/5157338676887552\n    trendy_team_id: \"5157338676887552\",\n}\n\nteam {\n    name: \"trendy_team_security\",\n\n    // go/trendy/manage/engineers/5241383946158080\n    trendy_team_id: \"5241383946158080\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_battery_life_system_power_\",\n\n    // go/trendy/manage/engineers/4512957492756480\n    trendy_team_id: \"4512957492756480\",\n}\n\nteam {\n    name: \"trendy_team_eggs\",\n\n    // go/trendy/manage/engineers/4568929198309376\n    trendy_team_id: \"4568929198309376\",\n}\n\nteam {\n    name: \"trendy_team_jeremymanson_team\",\n\n    // go/trendy/manage/engineers/5095869749297152\n    trendy_team_id: \"5095869749297152\",\n}\n\nteam {\n    name: \"trendy_team_exchange_active_sync_in_gmail\",\n\n    // go/trendy/manage/engineers/5382121434513408\n    trendy_team_id: \"5382121434513408\",\n}\n\nteam {\n    name: \"trendy_team_ios_backup_restore_make_pixel_\",\n\n    // go/trendy/manage/engineers/5752160863420416\n    trendy_team_id: \"5752160863420416\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_location\",\n\n    // go/trendy/manage/engineers/6228195632087040\n    trendy_team_id: \"6228195632087040\",\n}\n\nteam {\n    name: \"trendy_team_input_framework\",\n\n    // go/trendy/manage/engineers/4999436357238784\n    trendy_team_id: \"4999436357238784\",\n}\n\nteam {\n    name: \"trendy_team_tpm_tvc\",\n\n    // go/trendy/manage/engineers/5390683333230592\n    trendy_team_id: \"5390683333230592\",\n}\n\nteam {\n    name: \"trendy_team_lse_desktop_os_experience\",\n\n    // go/trendy/manage/engineers/5125234900434944\n    trendy_team_id: \"5125234900434944\",\n}\n\nteam {\n    name: \"trendy_team_android_for_india_device_experiences\",\n\n    // go/trendy/manage/engineers/5395413652111360\n    trendy_team_id: \"5395413652111360\",\n}\n\nteam {\n    name: \"trendy_team_pixel_zombie\",\n\n    // go/trendy/manage/engineers/5074646910533632\n    trendy_team_id: \"5074646910533632\",\n}\n\nteam {\n    name: \"trendy_team_android_onboarding\",\n\n    // go/trendy/manage/engineers/5152271974367232\n    trendy_team_id: \"5152271974367232\",\n}\n\nteam {\n    name: \"trendy_team_pixel_audio\",\n\n    // go/trendy/manage/engineers/5436547260088320\n    trendy_team_id: \"5436547260088320\",\n}\n\nteam {\n    name: \"trendy_team_pixel_connectivity_bt\",\n\n    // go/trendy/manage/engineers/6328035423453184\n    trendy_team_id: \"6328035423453184\",\n}\n\nteam {\n    name: \"trendy_team_wsd_w12\",\n\n    // go/trendy/manage/engineers/6333748748353536\n    trendy_team_id: \"6333748748353536\",\n}\n\nteam {\n    name: \"trendy_team_qmc_mvt\",\n\n    // go/trendy/manage/engineers/4572880876470272\n    trendy_team_id: \"4572880876470272\",\n}\n\nteam {\n    name: \"trendy_team_switch_access_voice_access\",\n\n    // go/trendy/manage/engineers/4794432469467136\n    trendy_team_id: \"4794432469467136\",\n}\n\nteam {\n    name: \"trendy_team_mainline_modularization\",\n\n    // go/trendy/manage/engineers/5845084143386624\n    trendy_team_id: \"5845084143386624\",\n}\n\nteam {\n    name: \"trendy_team_fwk_telecom\",\n\n    // go/trendy/manage/engineers/5330994143821824\n    trendy_team_id: \"5330994143821824\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_framework_akulian\",\n\n    // go/trendy/manage/engineers/5323210872750080\n    trendy_team_id: \"5323210872750080\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_identity\",\n\n    // go/trendy/manage/engineers/6017732386390016\n    trendy_team_id: \"6017732386390016\",\n}\n\nteam {\n    name: \"trendy_team_android_pdf\",\n\n    // go/trendy/manage/engineers/5175136433045504\n    trendy_team_id: \"5175136433045504\",\n}\n\nteam {\n    name: \"trendy_team_developer_relations\",\n\n    // go/trendy/manage/engineers/5709226143776768\n    trendy_team_id: \"5709226143776768\",\n}\n\nteam {\n    name: \"trendy_team_system_intelligence\",\n\n    // go/trendy/manage/engineers/5849675995709440\n    trendy_team_id: \"5849675995709440\",\n}\n\nteam {\n    name: \"trendy_team_mainline_updates\",\n\n    // go/trendy/manage/engineers/4845810809995264\n    trendy_team_id: \"4845810809995264\",\n}\n\nteam {\n    name: \"trendy_team_n_a_1\",\n\n    // go/trendy/manage/engineers/5946720655376384\n    trendy_team_id: \"5946720655376384\",\n}\n\nteam {\n    name: \"trendy_team_google_drive_docs_sheets_and_slides\",\n\n    // go/trendy/manage/engineers/6613574457622528\n    trendy_team_id: \"6613574457622528\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_awareness_health_experiences\",\n\n    // go/trendy/manage/engineers/6627866395967488\n    trendy_team_id: \"6627866395967488\",\n}\n\nteam {\n    name: \"trendy_team_context_infrastructure\",\n\n    // go/trendy/manage/engineers/4701268040646656\n    trendy_team_id: \"4701268040646656\",\n}\n\nteam {\n    name: \"trendy_team_android_media_solutions\",\n\n    // go/trendy/manage/engineers/4750452004356096\n    trendy_team_id: \"4750452004356096\",\n}\n\nteam {\n    name: \"trendy_team_wear_device_and_infrastructure\",\n\n    // go/trendy/manage/engineers/6358069369798656\n    trendy_team_id: \"6358069369798656\",\n}\n\nteam {\n    name: \"trendy_team_pixel_biometrics\",\n\n    // go/trendy/manage/engineers/5780875748737024\n    trendy_team_id: \"5780875748737024\",\n}\n\nteam {\n    name: \"trendy_team_app_knowledge_platform\",\n\n    // go/trendy/manage/engineers/6272266390634496\n    trendy_team_id: \"6272266390634496\",\n}\n\nteam {\n    name: \"trendy_team_wsd\",\n\n    // go/trendy/manage/engineers/4680083260178432\n    trendy_team_id: \"4680083260178432\",\n}\n\nteam {\n    name: \"trendy_team_seg\",\n\n    // go/trendy/manage/engineers/5067111353155584\n    trendy_team_id: \"5067111353155584\",\n}\n\nteam {\n    name: \"trendy_team_devinlawson_team\",\n\n    // go/trendy/manage/engineers/4805900971442176\n    trendy_team_id: \"4805900971442176\",\n}\n\nteam {\n    name: \"trendy_team_camera_hardware\",\n\n    // go/trendy/manage/engineers/6087458143731712\n    trendy_team_id: \"6087458143731712\",\n}\n\nteam {\n    name: \"trendy_team_camera_image_quality\",\n\n    // go/trendy/manage/engineers/5401362887999488\n    trendy_team_id: \"5401362887999488\",\n}\n\nteam {\n    name: \"trendy_team_android_power_and_comms_infra\",\n\n    // go/trendy/manage/engineers/5325547653332992\n    trendy_team_id: \"5325547653332992\",\n}\n\nteam {\n    name: \"trendy_team_pmw_pmo\",\n\n    // go/trendy/manage/engineers/4656299270504448\n    trendy_team_id: \"4656299270504448\",\n}\n\nteam {\n    name: \"trendy_team_filament\",\n\n    // go/trendy/manage/engineers/6031425915486208\n    trendy_team_id: \"6031425915486208\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_bspcore\",\n\n    // go/trendy/manage/engineers/6508021341356032\n    trendy_team_id: \"6508021341356032\",\n}\n\nteam {\n    name: \"trendy_team_powermanager_framework\",\n\n    // go/trendy/manage/engineers/5116162121564160\n    trendy_team_id: \"5116162121564160\",\n}\n\nteam {\n    name: \"trendy_team_wear_romanesco\",\n\n    // go/trendy/manage/engineers/5112520062697472\n    trendy_team_id: \"5112520062697472\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_theming\",\n\n    // go/trendy/manage/engineers/5179308179881984\n    trendy_team_id: \"5179308179881984\",\n}\n\nteam {\n    name: \"trendy_team_recorder\",\n\n    // go/trendy/manage/engineers/5085035337383936\n    trendy_team_id: \"5085035337383936\",\n}\n\nteam {\n    name: \"trendy_team_framework_accessibility\",\n\n    // go/trendy/manage/engineers/5474751170019328\n    trendy_team_id: \"5474751170019328\",\n}\n\nteam {\n    name: \"trendy_team_windowing_infra_\",\n\n    // go/trendy/manage/engineers/4578440609431552\n    trendy_team_id: \"4578440609431552\",\n}\n\nteam {\n    name: \"trendy_team_pmw_mcs\",\n\n    // go/trendy/manage/engineers/5864733550608384\n    trendy_team_id: \"5864733550608384\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_sysui_ctrl_carousel_tiles_recents_launcher_\",\n\n    // go/trendy/manage/engineers/4820131976740864\n    trendy_team_id: \"4820131976740864\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_accessibility_compliance\",\n\n    // go/trendy/manage/engineers/5381719553114112\n    trendy_team_id: \"5381719553114112\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_performance_thermal\",\n\n    // go/trendy/manage/engineers/5146276190355456\n    trendy_team_id: \"5146276190355456\",\n}\n\nteam {\n    name: \"trendy_team_neelsa_team\",\n\n    // go/trendy/manage/engineers/5736750978334720\n    trendy_team_id: \"5736750978334720\",\n}\n\nteam {\n    name: \"trendy_team_pixel_camera_engineering_experience\",\n\n    // go/trendy/manage/engineers/5190256655466496\n    trendy_team_id: \"5190256655466496\",\n}\n\nteam {\n    name: \"trendy_team_embedded_web_on_android\",\n\n    // go/trendy/manage/engineers/630061306576896\n    trendy_team_id: \"630061306576896\",\n}\n\nteam {\n    name: \"trendy_team_gantry\",\n\n    // go/trendy/manage/engineers/5677019153858560\n    trendy_team_id: \"5677019153858560\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_battery\",\n\n    // go/trendy/manage/engineers/6052273771642880\n    trendy_team_id: \"6052273771642880\",\n}\n\nteam {\n    name: \"trendy_team_enigma\",\n\n    // go/trendy/manage/engineers/5396338361597952\n    trendy_team_id: \"5396338361597952\",\n}\n\nteam {\n    name: \"trendy_team_pixel_gps_power\",\n\n    // go/trendy/manage/engineers/5075907446177792\n    trendy_team_id: \"5075907446177792\",\n}\n\nteam {\n    name: \"trendy_team_tool_frank\",\n\n    // go/trendy/manage/engineers/6200209976360960\n    trendy_team_id: \"6200209976360960\",\n}\n\nteam {\n    name: \"trendy_team_rginda_team\",\n\n    // go/trendy/manage/engineers/6031367105314816\n    trendy_team_id: \"6031367105314816\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_soc_power\",\n\n    // go/trendy/manage/engineers/5400771358785536\n    trendy_team_id: \"5400771358785536\",\n}\n\nteam {\n    name: \"trendy_team_android_crumpet\",\n\n    // go/trendy/manage/engineers/5199704478351360\n    trendy_team_id: \"5199704478351360\",\n}\n\nteam {\n    name: \"trendy_team_wallpapers\",\n\n    // go/trendy/manage/engineers/5125411306373120\n    trendy_team_id: \"5125411306373120\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_volta\",\n\n    // go/trendy/manage/engineers/6316156562309120\n    trendy_team_id: \"6316156562309120\",\n}\n\nteam {\n    name: \"trendy_team_camera_machine_intelligence\",\n\n    // go/trendy/manage/engineers/6578390085533696\n    trendy_team_id: \"6578390085533696\",\n}\n\nteam {\n    name: \"trendy_team_sheepo_team\",\n\n    // go/trendy/manage/engineers/5068061372743680\n    trendy_team_id: \"5068061372743680\",\n}\n\nteam {\n    name: \"trendy_team_android_profile_experiences\",\n\n    // go/trendy/manage/engineers/5914919462404096\n    trendy_team_id: \"5914919462404096\",\n}\n\nteam {\n    name: \"trendy_team_review_platform\",\n\n    // go/trendy/manage/engineers/5952905574514688\n    trendy_team_id: \"5952905574514688\",\n}\n\nteam {\n    name: \"trendy_team_abarth_team\",\n\n    // go/trendy/manage/engineers/4857528786780160\n    trendy_team_id: \"4857528786780160\",\n}\n\nteam {\n    name: \"trendy_team_treble\",\n\n    // go/trendy/manage/engineers/5452490178691072\n    trendy_team_id: \"5452490178691072\",\n}\n\nteam {\n    name: \"trendy_team_jsasinowski_team\",\n\n    // go/trendy/manage/engineers/6239259762786304\n    trendy_team_id: \"6239259762786304\",\n}\n\nteam {\n    name: \"trendy_team_vaas_team\",\n\n    // go/trendy/manage/engineers/5106754296905728\n    trendy_team_id: \"5106754296905728\",\n}\n\nteam {\n    name: \"trendy_team_internationalization\",\n\n    // go/trendy/manage/engineers/5911536283287552\n    trendy_team_id: \"5911536283287552\",\n}\n\nteam {\n    name: \"trendy_team_android_safer_apps\",\n\n    // go/trendy/manage/engineers/5943179005034496\n    trendy_team_id: \"5943179005034496\",\n}\n\nteam {\n    name: \"trendy_team_connectivity_telemetry\",\n\n    // go/trendy/manage/engineers/5084491349393408\n    trendy_team_id: \"5084491349393408\",\n}\n\nteam {\n    name: \"trendy_team_eseidel_team\",\n\n    // go/trendy/manage/engineers/5453997738721280\n    trendy_team_id: \"5453997738721280\",\n}\n\nteam {\n    name: \"trendy_team_test_eng_infrastructure\",\n\n    // go/trendy/manage/engineers/5981905027465216\n    trendy_team_id: \"5981905027465216\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_wcs_notification\",\n\n    // go/trendy/manage/engineers/4805871527690240\n    trendy_team_id: \"4805871527690240\",\n}\n\nteam {\n    name: \"trendy_team_konkers_team\",\n\n    // go/trendy/manage/engineers/5751147701895168\n    trendy_team_id: \"5751147701895168\",\n}\n\nteam {\n    name: \"trendy_team_mkearney_team\",\n\n    // go/trendy/manage/engineers/5082590844452864\n    trendy_team_id: \"5082590844452864\",\n}\n\nteam {\n    name: \"trendy_team_android_kernel\",\n\n    // go/trendy/manage/engineers/5014334795022336\n    trendy_team_id: \"5014334795022336\",\n}\n\nteam {\n    name: \"trendy_team_chrome\",\n\n    // go/trendy/manage/engineers/6439301864620032\n    trendy_team_id: \"6439301864620032\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_dialer_messages\",\n\n    // go/trendy/manage/engineers/4906732878725120\n    trendy_team_id: \"4906732878725120\",\n}\n\nteam {\n    name: \"trendy_team_android_tv_engprod\",\n\n    // go/trendy/manage/engineers/5538081157185536\n    trendy_team_id: \"5538081157185536\",\n}\n\nteam {\n    name: \"trendy_team_nicoh_team\",\n\n    // go/trendy/manage/engineers/5662292009189376\n    trendy_team_id: \"5662292009189376\",\n}\n\nteam {\n    name: \"trendy_team_wear_apps_ecosystem\",\n\n    // go/trendy/manage/engineers/4908413871882240\n    trendy_team_id: \"4908413871882240\",\n}\n\nteam {\n    name: \"trendy_team_cellular_security\",\n\n    // go/trendy/manage/engineers/6529004011683840\n    trendy_team_id: \"6529004011683840\",\n}\n\nteam {\n    name: \"trendy_team_dhaloni_team\",\n\n    // go/trendy/manage/engineers/6213556497448960\n    trendy_team_id: \"6213556497448960\",\n}\n\nteam {\n    name: \"trendy_team_applications_google_wide_\",\n\n    // go/trendy/manage/engineers/6120993248378880\n    trendy_team_id: \"6120993248378880\",\n}\n\nteam {\n    name: \"trendy_team_defunct_system_ui_intelligence_dfeng\",\n\n    // go/trendy/manage/engineers/5577284748443648\n    trendy_team_id: \"5577284748443648\",\n}\n\nteam {\n    name: \"trendy_team_oslo\",\n\n    // go/trendy/manage/engineers/5779594887954432\n    trendy_team_id: \"5779594887954432\",\n}\n\nteam {\n    name: \"trendy_team_bluetooth\",\n\n    // go/trendy/manage/engineers/6226546364645376\n    trendy_team_id: \"6226546364645376\",\n}\n\nteam {\n    name: \"trendy_team_localization\",\n\n    // go/trendy/manage/engineers/5751557341446144\n    trendy_team_id: \"5751557341446144\",\n}\n\nteam {\n    name: \"trendy_team_ssd_security\",\n\n    // go/trendy/manage/engineers/4608065248559104\n    trendy_team_id: \"4608065248559104\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_usb\",\n\n    // go/trendy/manage/engineers/5100646457802752\n    trendy_team_id: \"5100646457802752\",\n}\n\nteam {\n    name: \"trendy_team_hiroshi_team\",\n\n    // go/trendy/manage/engineers/5756564662288384\n    trendy_team_id: \"5756564662288384\",\n}\n\nteam {\n    name: \"trendy_team_contacts\",\n\n    // go/trendy/manage/engineers/4732859818311680\n    trendy_team_id: \"4732859818311680\",\n}\n\nteam {\n    name: \"trendy_team_wsd_w2\",\n\n    // go/trendy/manage/engineers/5945071387934720\n    trendy_team_id: \"5945071387934720\",\n}\n\nteam {\n    name: \"trendy_team_wsd_w51\",\n\n    // go/trendy/manage/engineers/5698780783312896\n    trendy_team_id: \"5698780783312896\",\n}\n\nteam {\n    name: \"trendy_team_erahm_team\",\n\n    // go/trendy/manage/engineers/5666347807309824\n    trendy_team_id: \"5666347807309824\",\n}\n\nteam {\n    name: \"trendy_team_wsd_w53\",\n\n    // go/trendy/manage/engineers/5841167539109888\n    trendy_team_id: \"5841167539109888\",\n}\n\nteam {\n    name: \"trendy_team_framework_overground\",\n\n    // go/trendy/manage/engineers/5135830829891584\n    trendy_team_id: \"5135830829891584\",\n}\n\nteam {\n    name: \"trendy_team_android_performance_console\",\n\n    // go/trendy/manage/engineers/5761662355963904\n    trendy_team_id: \"5761662355963904\",\n}\n\nteam {\n    name: \"trendy_team_partner_modem\",\n\n    // go/trendy/manage/engineers/6502710329376768\n    trendy_team_id: \"6502710329376768\",\n}\n\nteam {\n    name: \"trendy_team_scd_tool\",\n\n    // go/trendy/manage/engineers/5225441027555328\n    trendy_team_id: \"5225441027555328\",\n}\n\nteam {\n    name: \"trendy_team_gtw_sw\",\n\n    // go/trendy/manage/engineers/6069865957687296\n    trendy_team_id: \"6069865957687296\",\n}\n\nteam {\n    name: \"trendy_team_wearables\",\n\n    // go/trendy/manage/engineers/6122642515820544\n    trendy_team_id: \"6122642515820544\",\n}\n\nteam {\n    name: \"trendy_team_android_text\",\n\n    // go/trendy/manage/engineers/5194085585289216\n    trendy_team_id: \"5194085585289216\",\n}\n\nteam {\n    name: \"trendy_team_android_health\",\n\n    // go/trendy/manage/engineers/5177772706004992\n    trendy_team_id: \"5177772706004992\",\n}\n\nteam {\n    name: \"trendy_team_wsd_w23\",\n\n    // go/trendy/manage/engineers/6191361992556544\n    trendy_team_id: \"6191361992556544\",\n}\n\nteam {\n    name: \"trendy_team_pixel_connectivity_networking\",\n\n    // go/trendy/manage/engineers/6685592469241856\n    trendy_team_id: \"6685592469241856\",\n}\n\nteam {\n    name: \"trendy_team_ppi_team\",\n\n    // go/trendy/manage/engineers/5171933646848000\n    trendy_team_id: \"5171933646848000\",\n}\n\nteam {\n    name: \"trendy_team_ssd_mm_peripheral\",\n\n    // go/trendy/manage/engineers/6624019818086400\n    trendy_team_id: \"6624019818086400\",\n}\n\nteam {\n    name: \"trendy_team_n_a\",\n\n    // go/trendy/manage/engineers/4891189492711424\n    trendy_team_id: \"4891189492711424\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_hid_driver\",\n\n    // go/trendy/manage/engineers/4534130425102336\n    trendy_team_id: \"4534130425102336\",\n}\n\nteam {\n    name: \"trendy_team_wear_wearable_motion_algorithms\",\n\n    // go/trendy/manage/engineers/5397550198587392\n    trendy_team_id: \"5397550198587392\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_sysui_notifications\",\n\n    // go/trendy/manage/engineers/5256257183055872\n    trendy_team_id: \"5256257183055872\",\n}\n\nteam {\n    name: \"trendy_team_android_camera_engprod\",\n\n    // go/trendy/manage/engineers/5594382843281408\n    trendy_team_id: \"5594382843281408\",\n}\n\nteam {\n    name: \"trendy_team_lockscreen_aod\",\n\n    // go/trendy/manage/engineers/5503979641012224\n    trendy_team_id: \"5503979641012224\",\n}\n\nteam {\n    name: \"trendy_team_windowing_sdk\",\n\n    // go/trendy/manage/engineers/5683037008723968\n    trendy_team_id: \"5683037008723968\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_inmarket_power\",\n\n    // go/trendy/manage/engineers/6675891331170304\n    trendy_team_id: \"6675891331170304\",\n}\n\nteam {\n    name: \"trendy_team_betterbug\",\n\n    // go/trendy/manage/engineers/4910400652607488\n    trendy_team_id: \"4910400652607488\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_security\",\n\n    // go/trendy/manage/engineers/5030277713625088\n    trendy_team_id: \"5030277713625088\",\n}\n\nteam {\n    name: \"trendy_team_pixel_energizer\",\n\n    // go/trendy/manage/engineers/4970605270302720\n    trendy_team_id: \"4970605270302720\",\n}\n\nteam {\n    name: \"trendy_team_fwk_core_networking\",\n\n    // go/trendy/manage/engineers/5559692562399232\n    trendy_team_id: \"5559692562399232\",\n}\n\nteam {\n    name: \"trendy_team_chromium_webview\",\n\n    // go/trendy/manage/engineers/5630061306576896\n    trendy_team_id: \"5630061306576896\",\n}\n\nteam {\n    name: \"trendy_team_framework_cdm\",\n\n    // go/trendy/manage/engineers/4793721887031296\n    trendy_team_id: \"4793721887031296\",\n}\n\nteam {\n    name: \"trendy_team_system_walleye_\",\n\n    // go/trendy/manage/engineers/5665245678665728\n    trendy_team_id: \"5665245678665728\",\n}\n\nteam {\n    name: \"trendy_team_system_marlin_sailfish_\",\n\n    // go/trendy/manage/engineers/4713618364825600\n    trendy_team_id: \"4713618364825600\",\n}\n\nteam {\n    name: \"trendy_team_qmc\",\n\n    // go/trendy/manage/engineers/5207848841510912\n    trendy_team_id: \"5207848841510912\",\n}\n\nteam {\n    name: \"trendy_team_android_wallet_integration\",\n\n    // go/trendy/manage/engineers/5785777995153408\n    trendy_team_id: \"5785777995153408\",\n}\n\nteam {\n    name: \"trendy_team_noreent_team\",\n\n    // go/trendy/manage/engineers/5766299843198976\n    trendy_team_id: \"5766299843198976\",\n}\n\nteam {\n    name: \"trendy_team_ink\",\n\n    // go/trendy/manage/engineers/6620225162608640\n    trendy_team_id: \"6620225162608640\",\n}\n\nteam {\n    name: \"trendy_team_make_pixel\",\n\n    // go/trendy/manage/engineers/6140234701864960\n    trendy_team_id: \"6140234701864960\",\n}\n\nteam {\n    name: \"trendy_team_chillers_team\",\n\n    // go/trendy/manage/engineers/5631647887294464\n    trendy_team_id: \"5631647887294464\",\n}\n\nteam {\n    name: \"trendy_team_system_experience\",\n\n    // go/trendy/manage/engineers/5083633521950720\n    trendy_team_id: \"5083633521950720\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_framework_roosa\",\n\n    // go/trendy/manage/engineers/6708067074998272\n    trendy_team_id: \"6708067074998272\",\n}\n\nteam {\n    name: \"trendy_team_build\",\n\n    // go/trendy/manage/engineers/5542100376354816\n    trendy_team_id: \"5542100376354816\",\n}\n\nteam {\n    name: \"trendy_team_play_store\",\n\n    // go/trendy/manage/engineers/4803228562489344\n    trendy_team_id: \"4803228562489344\",\n}\n\nteam {\n    name: \"trendy_team_clocks\",\n\n    // go/trendy/manage/engineers/6327058391007232\n    trendy_team_id: \"6327058391007232\",\n}\n\nteam {\n    name: \"trendy_team_asafi_team\",\n\n    // go/trendy/manage/engineers/6217735399964672\n    trendy_team_id: \"6217735399964672\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_storage\",\n\n    // go/trendy/manage/engineers/4644898888089600\n    trendy_team_id: \"4644898888089600\",\n}\n\nteam {\n    name: \"trendy_team_play_movies\",\n\n    // go/trendy/manage/engineers/4838412934578176\n    trendy_team_id: \"4838412934578176\",\n}\n\nteam {\n    name: \"trendy_team_system_hammerhead_camera_\",\n\n    // go/trendy/manage/engineers/6597631539019776\n    trendy_team_id: \"6597631539019776\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_sysui_applications\",\n\n    // go/trendy/manage/engineers/4929833494544384\n    trendy_team_id: \"4929833494544384\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_tpm\",\n\n    // go/trendy/manage/engineers/4612922981122048\n    trendy_team_id: \"4612922981122048\",\n}\n\nteam {\n    name: \"trendy_team_qmc_script_automation\",\n\n    // go/trendy/manage/engineers/5047869899669504\n    trendy_team_id: \"5047869899669504\",\n}\n\nteam {\n    name: \"trendy_team_pixel_sw_tpm\",\n\n    // go/trendy/manage/engineers/5506916004265984\n    trendy_team_id: \"5506916004265984\",\n}\n\nteam {\n    name: \"trendy_team_device_and_factory_tpm\",\n\n    // go/trendy/manage/engineers/4574530143911936\n    trendy_team_id: \"4574530143911936\",\n}\n\nteam {\n    name: \"trendy_team_pmw_mss\",\n\n    // go/trendy/manage/engineers/4525262032896000\n    trendy_team_id: \"4525262032896000\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_esim_and_carriers\",\n\n    // go/trendy/manage/engineers/5045168113614848\n    trendy_team_id: \"5045168113614848\",\n}\n\nteam {\n    name: \"trendy_team_android_pixel_context_hub\",\n\n    // go/trendy/manage/engineers/5375970200944640\n    trendy_team_id: \"5375970200944640\",\n}\n\nteam {\n    name: \"trendy_team_pixel_setting_exp\",\n\n    // go/trendy/manage/engineers/5758010936295424\n    trendy_team_id: \"5758010936295424\",\n}\n\nteam {\n    name: \"trendy_team_defunct_system_ui_intelligence_praveenj\",\n\n    // go/trendy/manage/engineers/6648758829711360\n    trendy_team_id: \"6648758829711360\",\n}\n\nteam {\n    name: \"trendy_team_system_bullhead_\",\n\n    // go/trendy/manage/engineers/4592122329956352\n    trendy_team_id: \"4592122329956352\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_fingerprint\",\n\n    // go/trendy/manage/engineers/5380181285568512\n    trendy_team_id: \"5380181285568512\",\n}\n\nteam {\n    name: \"trendy_team_android_sdlc\",\n\n    // go/trendy/manage/engineers/6492896504152064\n    trendy_team_id: \"6492896504152064\",\n}\n\nteam {\n    name: \"trendy_team_android_core_graphics_stack\",\n\n    // go/trendy/manage/engineers/5260625399644160\n    trendy_team_id: \"5260625399644160\",\n}\n\nteam {\n    name: \"trendy_team_accessibility_switch_access\",\n\n    // go/trendy/manage/engineers/6026869039857664\n    trendy_team_id: \"6026869039857664\",\n}\n\nteam {\n    name: \"trendy_team_sqa_make_pixel_\",\n\n    // go/trendy/manage/engineers/5610819853090816\n    trendy_team_id: \"5610819853090816\",\n}\n\nteam {\n    name: \"trendy_team_controls\",\n\n    // go/trendy/manage/engineers/5005994102259712\n    trendy_team_id: \"5005994102259712\",\n}\n\nteam {\n    name: \"trendy_team_renderscript_nnapi\",\n\n    // go/trendy/manage/engineers/6527262794842112\n    trendy_team_id: \"6527262794842112\",\n}\n\nteam {\n    name: \"trendy_team_test_infrastructure\",\n\n    // go/trendy/manage/engineers/5130189115654144\n    trendy_team_id: \"5130189115654144\",\n}\n\nteam {\n    name: \"trendy_team_ssd_peripheral\",\n\n    // go/trendy/manage/engineers/6314507294867456\n    trendy_team_id: \"6314507294867456\",\n}\n\nteam {\n    name: \"trendy_team_device_connectivity_experiences_make_pixel_\",\n\n    // go/trendy/manage/engineers/5348586329866240\n    trendy_team_id: \"5348586329866240\",\n}\n\nteam {\n    name: \"trendy_team_nandunair_team\",\n\n    // go/trendy/manage/engineers/4874500384129024\n    trendy_team_id: \"4874500384129024\",\n}\n\nteam {\n    name: \"trendy_team_godofredoc_team\",\n\n    // go/trendy/manage/engineers/4892528710156288\n    trendy_team_id: \"4892528710156288\",\n}\n\nteam {\n    name: \"trendy_team_gtw_misc\",\n\n    // go/trendy/manage/engineers/6437652597178368\n    trendy_team_id: \"6437652597178368\",\n}\n\nteam {\n    name: \"trendy_team_perception_virtualization\",\n\n    // go/trendy/manage/engineers/5133931925897216\n    trendy_team_id: \"5133931925897216\",\n}\n\nteam {\n    name: \"trendy_team_safety_els_earthquake\",\n\n    // go/trendy/manage/engineers/6508498165071872\n    trendy_team_id: \"6508498165071872\",\n}\n\nteam {\n    name: \"trendy_team_virtual_device_framework\",\n\n    // go/trendy/manage/engineers/4798040542445568\n    trendy_team_id: \"4798040542445568\",\n}\n\nteam {\n    name: \"trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_\",\n\n    // go/trendy/manage/engineers/6402468225089536\n    trendy_team_id: \"6402468225089536\",\n}\n\nteam {\n    name: \"trendy_team_android_media_reliability\",\n\n    // go/trendy/manage/engineers/5489323818221568\n    trendy_team_id: \"5489323818221568\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_services\",\n\n    // go/trendy/manage/engineers/5140566757179392\n    trendy_team_id: \"5140566757179392\",\n}\n\nteam {\n    name: \"trendy_team_qmc_pda\",\n\n    // go/trendy/manage/engineers/5155072283377664\n    trendy_team_id: \"5155072283377664\",\n}\n\nteam {\n    name: \"trendy_team_vsl\",\n\n    // go/trendy/manage/engineers/6562447166930944\n    trendy_team_id: \"6562447166930944\",\n}\n\nteam {\n    name: \"trendy_team_android_release_metrics\",\n\n    // go/trendy/manage/engineers/6018925759201280\n    trendy_team_id: \"6018925759201280\",\n}\n\nteam {\n    name: \"trendy_team_testing\",\n\n    // go/trendy/manage/engineers/5892294829801472\n    trendy_team_id: \"5892294829801472\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_wallet_integration\",\n\n    // go/trendy/manage/engineers/5286726042288128\n    trendy_team_id: \"5286726042288128\",\n}\n\nteam {\n    name: \"trendy_team_leonardchan_team\",\n\n    // go/trendy/manage/engineers/6260994579005440\n    trendy_team_id: \"6260994579005440\",\n}\n\nteam {\n    name: \"trendy_team_system_performance\",\n\n    // go/trendy/manage/engineers/5188607388024832\n    trendy_team_id: \"5188607388024832\",\n}\n\nteam {\n    name: \"trendy_team_system_power\",\n\n    // go/trendy/manage/engineers/4820820748533760\n    trendy_team_id: \"4820820748533760\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_gdm_location_ads_marketplaces\",\n\n    // go/trendy/manage/engineers/5261636812570624\n    trendy_team_id: \"5261636812570624\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_digital_key\",\n\n    // go/trendy/manage/engineers/6444896766033920\n    trendy_team_id: \"6444896766033920\",\n}\n\nteam {\n    name: \"trendy_team_aosp\",\n\n    // go/trendy/manage/engineers/4860855378018304\n    trendy_team_id: \"4860855378018304\",\n}\n\nteam {\n    name: \"trendy_team_launcher\",\n\n    // go/trendy/manage/engineers/5102295725244416\n    trendy_team_id: \"5102295725244416\",\n}\n\nteam {\n    name: \"trendy_team_ime\",\n\n    // go/trendy/manage/engineers/6085808876290048\n    trendy_team_id: \"6085808876290048\",\n}\n\nteam {\n    name: \"trendy_team_jyotiraju_team\",\n\n    // go/trendy/manage/engineers/5720977167253504\n    trendy_team_id: \"5720977167253504\",\n}\n\nteam {\n    name: \"trendy_team_camera\",\n\n    // go/trendy/manage/engineers/5718022236798976\n    trendy_team_id: \"5718022236798976\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_backup_restore\",\n\n    // go/trendy/manage/engineers/4875982171176960\n    trendy_team_id: \"4875982171176960\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_developer_watch_faces_complications\",\n\n    // go/trendy/manage/engineers/5638213037096960\n    trendy_team_id: \"5638213037096960\",\n}\n\nteam {\n    name: \"trendy_team_mainline_reach\",\n\n    // go/trendy/manage/engineers/5701386012098560\n    trendy_team_id: \"5701386012098560\",\n}\n\nteam {\n    name: \"trendy_team_ssd_bsp\",\n\n    // go/trendy/manage/engineers/5876351911198720\n    trendy_team_id: \"5876351911198720\",\n}\n\nteam {\n    name: \"trendy_team_ux_design\",\n\n    // go/trendy/manage/engineers/4678433992736768\n    trendy_team_id: \"4678433992736768\",\n}\n\nteam {\n    name: \"trendy_team_accessibility_fw_make_pixel\",\n\n    // go/trendy/manage/engineers/5522858922868736\n    trendy_team_id: \"5522858922868736\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_media\",\n\n    // go/trendy/manage/engineers/5365411545513984\n    trendy_team_id: \"5365411545513984\",\n}\n\nteam {\n    name: \"trendy_team_system_angler_\",\n\n    // go/trendy/manage/engineers/5593227667046400\n    trendy_team_id: \"5593227667046400\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_tools\",\n\n    // go/trendy/manage/engineers/6460475572912128\n    trendy_team_id: \"6460475572912128\",\n}\n\nteam {\n    name: \"trendy_team_platform_build\",\n\n    // go/trendy/manage/engineers/5774403578920960\n    trendy_team_id: \"5774403578920960\",\n}\n\nteam {\n    name: \"trendy_team_pchitoor_team\",\n\n    // go/trendy/manage/engineers/5962577315266560\n    trendy_team_id: \"5962577315266560\",\n}\n\nteam {\n    name: \"trendy_team_multi_device_platform\",\n\n    // go/trendy/manage/engineers/5850153090711552\n    trendy_team_id: \"5850153090711552\",\n}\n\nteam {\n    name: \"trendy_team_safetynet\",\n\n    // go/trendy/manage/engineers/4748802736914432\n    trendy_team_id: \"4748802736914432\",\n}\n\nteam {\n    name: \"trendy_team_android_resources\",\n\n    // go/trendy/manage/engineers/4678767020703744\n    trendy_team_id: \"4678767020703744\",\n}\n\nteam {\n    name: \"trendy_team_joshconner_team\",\n\n    // go/trendy/manage/engineers/6226828248383488\n    trendy_team_id: \"6226828248383488\",\n}\n\nteam {\n    name: \"trendy_team_qmc_ait\",\n\n    // go/trendy/manage/engineers/6175419073953792\n    trendy_team_id: \"6175419073953792\",\n}\n\nteam {\n    name: \"trendy_team_pixel_global\",\n\n    // go/trendy/manage/engineers/4609714516000768\n    trendy_team_id: \"4609714516000768\",\n}\n\nteam {\n    name: \"trendy_team_qa_automation\",\n\n    // go/trendy/manage/engineers/6159878303678464\n    trendy_team_id: \"6159878303678464\",\n}\n\nteam {\n    name: \"trendy_team_android_gpu\",\n\n    // go/trendy/manage/engineers/6105848565104640\n    trendy_team_id: \"6105848565104640\",\n}\n\nteam {\n    name: \"trendy_team_qmc_ate\",\n\n    // go/trendy/manage/engineers/4819171481092096\n    trendy_team_id: \"4819171481092096\",\n}\n\nteam {\n    name: \"trendy_team_hangouts\",\n\n    // go/trendy/manage/engineers/6263380004175872\n    trendy_team_id: \"6263380004175872\",\n}\n\nteam {\n    name: \"trendy_team_cross_device_control\",\n\n    // go/trendy/manage/engineers/5888607197757440\n    trendy_team_id: \"5888607197757440\",\n}\n\nteam {\n    name: \"trendy_team_interactions_frameworks\",\n\n    // go/trendy/manage/engineers/4795124029489152\n    trendy_team_id: \"4795124029489152\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_framework_santoscordon\",\n\n    // go/trendy/manage/engineers/6049242537951232\n    trendy_team_id: \"6049242537951232\",\n}\n\nteam {\n    name: \"trendy_team_authentication\",\n\n    // go/trendy/manage/engineers/5901909380988928\n    trendy_team_id: \"5901909380988928\",\n}\n\nteam {\n    name: \"trendy_team_stylus\",\n\n    // go/trendy/manage/engineers/5685003218747392\n    trendy_team_id: \"5685003218747392\",\n}\n\nteam {\n    name: \"trendy_team_linus_team\",\n\n    // go/trendy/manage/engineers/6210035100844032\n    trendy_team_id: \"6210035100844032\",\n}\n\nteam {\n    name: \"trendy_team_virtualization\",\n\n    // go/trendy/manage/engineers/5117131519787008\n    trendy_team_id: \"5117131519787008\",\n}\n\nteam {\n    name: \"trendy_team_billstevenson_team\",\n\n    // go/trendy/manage/engineers/5631064744820736\n    trendy_team_id: \"5631064744820736\",\n}\n\nteam {\n    name: \"trendy_team_android_smartos\",\n\n    // go/trendy/manage/engineers/5637973325414400\n    trendy_team_id: \"5637973325414400\",\n}\n\nteam {\n    name: \"trendy_team_art_performance\",\n\n    // go/trendy/manage/engineers/6210603446042624\n    trendy_team_id: \"6210603446042624\",\n}\n\nteam {\n    name: \"trendy_team_ssd\",\n\n    // go/trendy/manage/engineers/5858759725154304\n    trendy_team_id: \"5858759725154304\",\n}\n\nteam {\n    name: \"trendy_team_abc_engops\",\n\n    // go/trendy/manage/engineers/5273578928504832\n    trendy_team_id: \"5273578928504832\",\n}\n\nteam {\n    name: \"trendy_team_wear_weather_android_app\",\n\n    // go/trendy/manage/engineers/6496415568003072\n    trendy_team_id: \"6496415568003072\",\n}\n\nteam {\n    name: \"trendy_team_rubidium_sdk_runtime\",\n\n    // go/trendy/manage/engineers/6286508355911680\n    trendy_team_id: \"6286508355911680\",\n}\n\nteam {\n    name: \"trendy_team_borthakur_team\",\n\n    // go/trendy/manage/engineers/4962243059023872\n    trendy_team_id: \"4962243059023872\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_graphics\",\n\n    // go/trendy/manage/engineers/5791644907110400\n    trendy_team_id: \"5791644907110400\",\n}\n\nteam {\n    name: \"trendy_team_make_creative_android_key_experience\",\n\n    // go/trendy/manage/engineers/5716738515173376\n    trendy_team_id: \"5716738515173376\",\n}\n\nteam {\n    name: \"trendy_team_pmw_l1rf\",\n\n    // go/trendy/manage/engineers/5302906915684352\n    trendy_team_id: \"5302906915684352\",\n}\n\nteam {\n    name: \"trendy_team_test_eng_comms_power\",\n\n    // go/trendy/manage/engineers/6632815911108608\n    trendy_team_id: \"6632815911108608\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_architecture_group\",\n\n    // go/trendy/manage/engineers/5609928060207104\n    trendy_team_id: \"5609928060207104\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_esim_carriers\",\n\n    // go/trendy/manage/engineers/5928361498935296\n    trendy_team_id: \"5928361498935296\",\n}\n\nteam {\n    name: \"trendy_team_pixel_connectivity_gps\",\n\n    // go/trendy/manage/engineers/4920660539899904\n    trendy_team_id: \"4920660539899904\",\n}\n\nteam {\n    name: \"trendy_team_adversarial_code_ai_arc_ai_\",\n\n    // go/trendy/manage/engineers/4850213657673728\n    trendy_team_id: \"4850213657673728\",\n}\n\nteam {\n    name: \"trendy_team_android_binary_transparency\",\n\n    // go/trendy/manage/engineers/6585365243002880\n    trendy_team_id: \"6585365243002880\",\n}\n\nteam {\n    name: \"trendy_team_test_eng_automotive_tv\",\n\n    // go/trendy/manage/engineers/6156177620467712\n    trendy_team_id: \"6156177620467712\",\n}\n\nteam {\n    name: \"trendy_team_tgosselaar_team\",\n\n    // go/trendy/manage/engineers/4897638077071360\n    trendy_team_id: \"4897638077071360\",\n}\n\nteam {\n    name: \"trendy_team_pixel_connectivity_wifi\",\n\n    // go/trendy/manage/engineers/4776219313340416\n    trendy_team_id: \"4776219313340416\",\n}\n\nteam {\n    name: \"trendy_team_setup_wizard\",\n\n    // go/trendy/manage/engineers/5417305806602240\n    trendy_team_id: \"5417305806602240\",\n}\n\nteam {\n    name: \"trendy_team_security_validation_engineering_sve_\",\n\n    // go/trendy/manage/engineers/5850943050907648\n    trendy_team_id: \"5850943050907648\",\n}\n\nteam {\n    name: \"trendy_team_wsd_function\",\n\n    // go/trendy/manage/engineers/6650408097153024\n    trendy_team_id: \"6650408097153024\",\n}\n\nteam {\n    name: \"trendy_team_platform_product_mgrs\",\n\n    // go/trendy/manage/engineers/6483282329731072\n    trendy_team_id: \"6483282329731072\",\n}\n\nteam {\n    name: \"trendy_team_partner_telephony\",\n\n    // go/trendy/manage/engineers/5767882120265728\n    trendy_team_id: \"5767882120265728\",\n}\n\nteam {\n    name: \"trendy_team_crjohns_team\",\n\n    // go/trendy/manage/engineers/4804101473992704\n    trendy_team_id: \"4804101473992704\",\n}\n\nteam {\n    name: \"trendy_team_wsd_ims\",\n\n    // go/trendy/manage/engineers/6050624504201216\n    trendy_team_id: \"6050624504201216\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_weather\",\n\n    // go/trendy/manage/engineers/5464164419928064\n    trendy_team_id: \"5464164419928064\",\n}\n\nteam {\n    name: \"trendy_team_guptaritu_team\",\n\n    // go/trendy/manage/engineers/5142679624777728\n    trendy_team_id: \"5142679624777728\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_developer_tools\",\n\n    // go/trendy/manage/engineers/6228915878002688\n    trendy_team_id: \"6228915878002688\",\n}\n\nteam {\n    name: \"trendy_team_play_books\",\n\n    // go/trendy/manage/engineers/5769149527490560\n    trendy_team_id: \"5769149527490560\",\n}\n\nteam {\n    name: \"trendy_team_melissadaniels_team\",\n\n    // go/trendy/manage/engineers/5715112926281728\n    trendy_team_id: \"5715112926281728\",\n}\n\nteam {\n    name: \"trendy_team_wear_shared_context_state\",\n\n    // go/trendy/manage/engineers/5329107344588800\n    trendy_team_id: \"5329107344588800\",\n}\n\nteam {\n    name: \"trendy_team_motion\",\n\n    // go/trendy/manage/engineers/6331351269277696\n    trendy_team_id: \"6331351269277696\",\n}\n\nteam {\n    name: \"trendy_team_gpp_on_device\",\n\n    // go/trendy/manage/engineers/5181961504980992\n    trendy_team_id: \"5181961504980992\",\n}\n\nteam {\n    name: \"trendy_team_android_settings_app\",\n\n    // go/trendy/manage/engineers/6204400884154368\n    trendy_team_id: \"6204400884154368\",\n}\n\nteam {\n    name: \"trendy_team_l1_inmarket\",\n\n    // go/trendy/manage/engineers/5172846450737152\n    trendy_team_id: \"5172846450737152\",\n}\n\nteam {\n    name: \"trendy_team_wear_wearflow\",\n\n    // go/trendy/manage/engineers/5947250429558784\n    trendy_team_id: \"5947250429558784\",\n}\n\nteam {\n    name: \"trendy_team_enterprise\",\n\n    // go/trendy/manage/engineers/5366178515910656\n    trendy_team_id: \"5366178515910656\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_framework_michaelwr\",\n\n    // go/trendy/manage/engineers/4574277124915200\n    trendy_team_id: \"4574277124915200\",\n}\n\nteam {\n    name: \"trendy_team_color\",\n\n    // go/trendy/manage/engineers/6305208086724608\n    trendy_team_id: \"6305208086724608\",\n}\n\nteam {\n    name: \"trendy_team_osbornc_team\",\n\n    // go/trendy/manage/engineers/6319504650043392\n    trendy_team_id: \"6319504650043392\",\n}\n\nteam {\n    name: \"trendy_team_dialer\",\n\n    // go/trendy/manage/engineers/6595982271578112\n    trendy_team_id: \"6595982271578112\",\n}\n\nteam {\n    name: \"trendy_team_framework_bpm\",\n\n    // go/trendy/manage/engineers/5498119911243776\n    trendy_team_id: \"5498119911243776\",\n}\n\nteam {\n    name: \"trendy_team_wsd_core_team\",\n\n    // go/trendy/manage/engineers/6683943201800192\n    trendy_team_id: \"6683943201800192\",\n}\n\nteam {\n    name: \"trendy_team_pixel_connectivity_ril\",\n\n    // go/trendy/manage/engineers/4995093341536256\n    trendy_team_id: \"4995093341536256\",\n}\n\nteam {\n    name: \"trendy_team_perfetto\",\n\n    // go/trendy/manage/engineers/4783987109003264\n    trendy_team_id: \"4783987109003264\",\n}\n\nteam {\n    name: \"trendy_team_partner_devrel\",\n\n    // go/trendy/manage/engineers/4537696504381440\n    trendy_team_id: \"4537696504381440\",\n}\n\nteam {\n    name: \"trendy_team_fwk_thread_network\",\n\n    // go/trendy/manage/engineers/5094685775134720\n    trendy_team_id: \"5094685775134720\",\n}\n\nteam {\n    name: \"trendy_team_thatguy_team\",\n\n    // go/trendy/manage/engineers/5688369775542272\n    trendy_team_id: \"5688369775542272\",\n}\n\nteam {\n    name: \"trendy_team_finder\",\n\n    // go/trendy/manage/engineers/6492831025364992\n    trendy_team_id: \"6492831025364992\",\n}\n\nteam {\n    name: \"trendy_team_boot_time\",\n\n    // go/trendy/manage/engineers/6017089399554048\n    trendy_team_id: \"6017089399554048\",\n}\n\nteam {\n    name: \"trendy_team_wear_wcs_beto\",\n\n    // go/trendy/manage/engineers/4910945436336128\n    trendy_team_id: \"4910945436336128\",\n}\n\nteam {\n    name: \"trendy_team_responsible_apis\",\n\n    // go/trendy/manage/engineers/5651962854178816\n    trendy_team_id: \"5651962854178816\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_corebsp\",\n\n    // go/trendy/manage/engineers/4626673699553280\n    trendy_team_id: \"4626673699553280\",\n}\n\nteam {\n    name: \"trendy_team_education\",\n\n    // go/trendy/manage/engineers/4889540225269760\n    trendy_team_id: \"4889540225269760\",\n}\n\nteam {\n    name: \"trendy_team_alarm_clock\",\n\n    // go/trendy/manage/engineers/6193011259998208\n    trendy_team_id: \"6193011259998208\",\n}\n\nteam {\n    name: \"trendy_team_pmw_tvc\",\n\n    // go/trendy/manage/engineers/4526288764960768\n    trendy_team_id: \"4526288764960768\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_pixel_wifi\",\n\n    // go/trendy/manage/engineers/5741405413900288\n    trendy_team_id: \"5741405413900288\",\n}\n\nteam {\n    name: \"trendy_team_abdulla_team\",\n\n    // go/trendy/manage/engineers/6223585145421824\n    trendy_team_id: \"6223585145421824\",\n}\n\nteam {\n    name: \"trendy_team_hardware\",\n\n    // go/trendy/manage/engineers/5357382422888448\n    trendy_team_id: \"5357382422888448\",\n}\n\nteam {\n    name: \"trendy_team_status_bar\",\n\n    // go/trendy/manage/engineers/6329516043173888\n    trendy_team_id: \"6329516043173888\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_sysui_big_picture\",\n\n    // go/trendy/manage/engineers/5081356719521792\n    trendy_team_id: \"5081356719521792\",\n}\n\nteam {\n    name: \"trendy_team_wear_material_design_for_wearos\",\n\n    // go/trendy/manage/engineers/4792942333952000\n    trendy_team_id: \"4792942333952000\",\n}\n\nteam {\n    name: \"trendy_team_platform_security\",\n\n    // go/trendy/manage/engineers/5243033213599744\n    trendy_team_id: \"5243033213599744\",\n}\n\nteam {\n    name: \"trendy_team_llvm_and_toolchains\",\n\n    // go/trendy/manage/engineers/5990701120487424\n    trendy_team_id: \"5990701120487424\",\n}\n\nteam {\n    name: \"trendy_team_jmccandless_team\",\n\n    // go/trendy/manage/engineers/5227794226380800\n    trendy_team_id: \"5227794226380800\",\n}\n\nteam {\n    name: \"trendy_team_safety_center\",\n\n    // go/trendy/manage/engineers/5930273843609600\n    trendy_team_id: \"5930273843609600\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_systems_engineering_and_devices\",\n\n    // go/trendy/manage/engineers/5407847298793472\n    trendy_team_id: \"5407847298793472\",\n}\n\nteam {\n    name: \"trendy_team_framework_android_multiuser\",\n\n    // go/trendy/manage/engineers/5981525732392960\n    trendy_team_id: \"5981525732392960\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_test2\",\n\n    // go/trendy/manage/engineers/4590958690074624\n    trendy_team_id: \"4590958690074624\",\n}\n\nteam {\n    name: \"trendy_team_qmc_iqt_tpe\",\n\n    // go/trendy/manage/engineers/6668000283197440\n    trendy_team_id: \"6668000283197440\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_factory\",\n\n    // go/trendy/manage/engineers/6267032573739008\n    trendy_team_id: \"6267032573739008\",\n}\n\nteam {\n    name: \"trendy_team_automotive\",\n\n    // go/trendy/manage/engineers/5770798794932224\n    trendy_team_id: \"5770798794932224\",\n}\n\nteam {\n    name: \"trendy_team_aaos_display_safety_triage\",\n\n    // go/trendy/manage/engineers/6522093663780864\n    trendy_team_id: \"6522093663780864\",\n}\n\nteam {\n    name: \"trendy_team_camera_htc_lg_qualcomm\",\n\n    // go/trendy/manage/engineers/6332099480911872\n    trendy_team_id: \"6332099480911872\",\n}\n\nteam {\n    name: \"trendy_team_rkp_keystore\",\n\n    // go/trendy/manage/engineers/5634304374505472\n    trendy_team_id: \"5634304374505472\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_watch_faces\",\n\n    // go/trendy/manage/engineers/5885708195495936\n    trendy_team_id: \"5885708195495936\",\n}\n\nteam {\n    name: \"trendy_team_arc_app_compat\",\n\n    // go/trendy/manage/engineers/4811894441279488\n    trendy_team_id: \"4811894441279488\",\n}\n\nteam {\n    name: \"trendy_team_psohn_team\",\n\n    // go/trendy/manage/engineers/4852673947009024\n    trendy_team_id: \"4852673947009024\",\n}\n\nteam {\n    name: \"trendy_team_hollande_team\",\n\n    // go/trendy/manage/engineers/5356533186723840\n    trendy_team_id: \"5356533186723840\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_partner_programs_and_engineering_team\",\n\n    // go/trendy/manage/engineers/4934997571960832\n    trendy_team_id: \"4934997571960832\",\n}\n\nteam {\n    name: \"trendy_team_pixel_connectivity_nfc\",\n\n    // go/trendy/manage/engineers/5631272051965952\n    trendy_team_id: \"5631272051965952\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_ios_connectivity\",\n\n    // go/trendy/manage/engineers/4702455644192768\n    trendy_team_id: \"4702455644192768\",\n}\n\nteam {\n    name: \"trendy_team_arc_\",\n\n    // go/trendy/manage/engineers/4556937957867520\n    trendy_team_id: \"4556937957867520\",\n}\n\nteam {\n    name: \"trendy_team_android_one\",\n\n    // go/trendy/manage/engineers/6272176097198080\n    trendy_team_id: \"6272176097198080\",\n}\n\nteam {\n    name: \"trendy_team_ccherubino_team\",\n\n    // go/trendy/manage/engineers/4846471192150016\n    trendy_team_id: \"4846471192150016\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_bluetooth_and_nfc\",\n\n    // go/trendy/manage/engineers/5259280851435520\n    trendy_team_id: \"5259280851435520\",\n}\n\nteam {\n    name: \"trendy_team_test_eng_afw_auth_location_camera_media\",\n\n    // go/trendy/manage/engineers/6298564376264704\n    trendy_team_id: \"6298564376264704\",\n}\n\nteam {\n    name: \"trendy_team_frousseau_team\",\n\n    // go/trendy/manage/engineers/5718296436572160\n    trendy_team_id: \"5718296436572160\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_touch_haptic\",\n\n    // go/trendy/manage/engineers/6469264330096640\n    trendy_team_id: \"6469264330096640\",\n}\n\nteam {\n    name: \"trendy_team_partner_eng\",\n\n    // go/trendy/manage/engineers/5172664469422080\n    trendy_team_id: \"5172664469422080\",\n}\n\nteam {\n    name: \"trendy_team_dogfooders\",\n\n    // go/trendy/manage/engineers/4643249620647936\n    trendy_team_id: \"4643249620647936\",\n}\n\nteam {\n    name: \"trendy_team_system_external_\",\n\n    // go/trendy/manage/engineers/5558043294957568\n    trendy_team_id: \"5558043294957568\",\n}\n\nteam {\n    name: \"trendy_team_foundations\",\n\n    // go/trendy/manage/engineers/6216250952679424\n    trendy_team_id: \"6216250952679424\",\n}\n\nteam {\n    name: \"trendy_team_camera_framework\",\n\n    // go/trendy/manage/engineers/6455244783222784\n    trendy_team_id: \"6455244783222784\",\n}\n\nteam {\n    name: \"trendy_team_bugjuggler\",\n\n    // go/trendy/manage/engineers/6472836969267200\n    trendy_team_id: \"6472836969267200\",\n}\n\nteam {\n    name: \"trendy_team_cligh_team\",\n\n    // go/trendy/manage/engineers/6273778455314432\n    trendy_team_id: \"6273778455314432\",\n}\n\nteam {\n    name: \"trendy_team_android_testing_experiences\",\n\n    // go/trendy/manage/engineers/5653137056366592\n    trendy_team_id: \"5653137056366592\",\n}\n\nteam {\n    name: \"trendy_team_dx\",\n\n    // go/trendy/manage/engineers/5677977168281600\n    trendy_team_id: \"5677977168281600\",\n}\n\nteam {\n    name: \"trendy_team_framework_backstage_power\",\n\n    // go/trendy/manage/engineers/6314066964283392\n    trendy_team_id: \"6314066964283392\",\n}\n\nteam {\n    name: \"trendy_team_rlb_team\",\n\n    // go/trendy/manage/engineers/5206858878058496\n    trendy_team_id: \"5206858878058496\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_pts\",\n\n    // go/trendy/manage/engineers/5553725663510528\n    trendy_team_id: \"5553725663510528\",\n}\n\nteam {\n    name: \"trendy_team_keir_team\",\n\n    // go/trendy/manage/engineers/5731700089978880\n    trendy_team_id: \"5731700089978880\",\n}\n\nteam {\n    name: \"trendy_team_system_taimen_\",\n\n    // go/trendy/manage/engineers/6421709678575616\n    trendy_team_id: \"6421709678575616\",\n}\n\nteam {\n    name: \"trendy_team_security_response\",\n\n    // go/trendy/manage/engineers/5031926981066752\n    trendy_team_id: \"5031926981066752\",\n}\n\nteam {\n    name: \"trendy_team_preload_safety\",\n\n    // go/trendy/manage/engineers/4584609580744704\n    trendy_team_id: \"4584609580744704\",\n}\n\nteam {\n    name: \"trendy_team_pixel_customizations_make_\",\n\n    // go/trendy/manage/engineers/5155643836366848\n    trendy_team_id: \"5155643836366848\",\n}\n\nteam {\n    name: \"trendy_team_tooltopia\",\n\n    // go/trendy/manage/engineers/5456472883101696\n    trendy_team_id: \"5456472883101696\",\n}\n\nteam {\n    name: \"trendy_team_accessibility_live_transcribe\",\n\n    // go/trendy/manage/engineers/6299695642345472\n    trendy_team_id: \"6299695642345472\",\n}\n\nteam {\n    name: \"trendy_team_trusty\",\n\n    // go/trendy/manage/engineers/5109319549616128\n    trendy_team_id: \"5109319549616128\",\n}\n\nteam {\n    name: \"trendy_team_amathes_team\",\n\n    // go/trendy/manage/engineers/5157715862257664\n    trendy_team_id: \"5157715862257664\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_developer_android_devrel_\",\n\n    // go/trendy/manage/engineers/5861820594028544\n    trendy_team_id: \"5861820594028544\",\n}\n\nteam {\n    name: \"trendy_team_overview\",\n\n    // go/trendy/manage/engineers/5071790575550464\n    trendy_team_id: \"5071790575550464\",\n}\n\nteam {\n    name: \"trendy_team_android_sensors\",\n\n    // go/trendy/manage/engineers/4776371090259968\n    trendy_team_id: \"4776371090259968\",\n}\n\nteam {\n    name: \"trendy_team_ui_toolkit\",\n\n    // go/trendy/manage/engineers/5638857399599104\n    trendy_team_id: \"5638857399599104\",\n}\n\nteam {\n    name: \"trendy_team_gesture_nav\",\n\n    // go/trendy/manage/engineers/6304405391310848\n    trendy_team_id: \"6304405391310848\",\n}\n\nteam {\n    name: \"trendy_team_qmc_wifi_storage\",\n\n    // go/trendy/manage/engineers/4924724597358592\n    trendy_team_id: \"4924724597358592\",\n}\n\nteam {\n    name: \"trendy_team_wsd_w11\",\n\n    // go/trendy/manage/engineers/5929128469331968\n    trendy_team_id: \"5929128469331968\",\n}\n\nteam {\n    name: \"trendy_team_fsamuel_team\",\n\n    // go/trendy/manage/engineers/5753514497310720\n    trendy_team_id: \"5753514497310720\",\n}\n\nteam {\n    name: \"trendy_team_pixel_haptic\",\n\n    // go/trendy/manage/engineers/5919013003493376\n    trendy_team_id: \"5919013003493376\",\n}\n\nteam {\n    name: \"trendy_team_pixel_retention\",\n\n    // go/trendy/manage/engineers/5647985290805248\n    trendy_team_id: \"5647985290805248\",\n}\n\nteam {\n    name: \"trendy_team_pixel_onboarding\",\n\n    // go/trendy/manage/engineers/5531340811960320\n    trendy_team_id: \"5531340811960320\",\n}\n\nteam {\n    name: \"trendy_team_wsd_standard\",\n\n    // go/trendy/manage/engineers/6296915108823040\n    trendy_team_id: \"6296915108823040\",\n}\n\nteam {\n    name: \"trendy_team_art_mainline\",\n\n    // go/trendy/manage/engineers/5733965155401728\n    trendy_team_id: \"5733965155401728\",\n}\n\nteam {\n    name: \"trendy_team_shade\",\n\n    // go/trendy/manage/engineers/5646715170226176\n    trendy_team_id: \"5646715170226176\",\n}\n\nteam {\n    name: \"trendy_team_gchips_compute_sw\",\n\n    // go/trendy/manage/engineers/6245787818131456\n    trendy_team_id: \"6245787818131456\",\n}\n\nteam {\n    name: \"trendy_team_haptics_framework\",\n\n    // go/trendy/manage/engineers/5895438509441024\n    trendy_team_id: \"5895438509441024\",\n}\n\nteam {\n    name: \"trendy_team_accessibility_braille\",\n\n    // go/trendy/manage/engineers/4992530205933568\n    trendy_team_id: \"4992530205933568\",\n}\n\nteam {\n    name: \"trendy_team_qmc_utd\",\n\n    // go/trendy/manage/engineers/5524508190310400\n    trendy_team_id: \"5524508190310400\",\n}\n\nteam {\n    name: \"trendy_team_android_on\",\n\n    // go/trendy/manage/engineers/4539345771823104\n    trendy_team_id: \"4539345771823104\",\n}\n\nteam {\n    name: \"trendy_team_fit\",\n\n    // go/trendy/manage/engineers/5628412039135232\n    trendy_team_id: \"5628412039135232\",\n}\n\nteam {\n    name: \"trendy_team_fwk_uwb\",\n\n    // go/trendy/manage/engineers/5983733408235520\n    trendy_team_id: \"5983733408235520\",\n}\n\nteam {\n    name: \"trendy_team_aidroid\",\n\n    // go/trendy/manage/engineers/4697675446222848\n    trendy_team_id: \"4697675446222848\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_test1\",\n\n    // go/trendy/manage/engineers/6212752467460096\n    trendy_team_id: \"6212752467460096\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_releases_telemetry_and_analytics\",\n\n    // go/trendy/manage/engineers/5892057298010112\n    trendy_team_id: \"5892057298010112\",\n}\n\nteam {\n    name: \"trendy_team_context_hub\",\n\n    // go/trendy/manage/engineers/5080704467501056\n    trendy_team_id: \"5080704467501056\",\n}\n\nteam {\n    name: \"trendy_team_carrier_follow_up\",\n\n    // go/trendy/manage/engineers/6615223725064192\n    trendy_team_id: \"6615223725064192\",\n}\n\nteam {\n    name: \"trendy_team_accessibility_live_caption\",\n\n    // go/trendy/manage/engineers/4764529665409024\n    trendy_team_id: \"4764529665409024\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_display\",\n\n    // go/trendy/manage/engineers/6261730736734208\n    trendy_team_id: \"6261730736734208\",\n}\n\nteam {\n    name: \"trendy_team_foundation_security_rust_pkvm_\",\n\n    // go/trendy/manage/engineers/5071354421084160\n    trendy_team_id: \"5071354421084160\",\n}\n\nteam {\n    name: \"trendy_team_pixel_repair_mode\",\n\n    // go/trendy/manage/engineers/6083775867813888\n    trendy_team_id: \"6083775867813888\",\n}\n\nteam {\n    name: \"trendy_team_capture_and_share\",\n\n    // go/trendy/manage/engineers/5644523746787328\n    trendy_team_id: \"5644523746787328\",\n}\n\nteam {\n    name: \"trendy_team_keep\",\n\n    // go/trendy/manage/engineers/5839518271668224\n    trendy_team_id: \"5839518271668224\",\n}\n\nteam {\n    name: \"trendy_team_aaos_security\",\n\n    // go/trendy/manage/engineers/6264394363076608\n    trendy_team_id: \"6264394363076608\",\n}\n\nteam {\n    name: \"trendy_team_zero_jank\",\n\n    // go/trendy/manage/engineers/4764874133897216\n    trendy_team_id: \"4764874133897216\",\n}\n\nteam {\n    name: \"trendy_team_android_unified_core_infrastructure\",\n\n    // go/trendy/manage/engineers/5842172961914880\n    trendy_team_id: \"5842172961914880\",\n}\n\nteam {\n    name: \"trendy_team_android_media_codec_framework\",\n\n    // go/trendy/manage/engineers/4943966050844672\n    trendy_team_id: \"4943966050844672\",\n}\n\nteam {\n    name: \"trendy_team_system_gn_\",\n\n    // go/trendy/manage/engineers/4785636376444928\n    trendy_team_id: \"4785636376444928\",\n}\n\nteam {\n    name: \"trendy_team_deprecated_android_auth_client\",\n\n    // go/trendy/manage/engineers/5471731632177152\n    trendy_team_id: \"5471731632177152\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_ios_companion_sdk\",\n\n    // go/trendy/manage/engineers/5737044865089536\n    trendy_team_id: \"5737044865089536\",\n}\n\nteam {\n    name: \"trendy_team_nearby\",\n\n    // go/trendy/manage/engineers/4959908969447424\n    trendy_team_id: \"4959908969447424\",\n}\n\nteam {\n    name: \"trendy_team_camerax_make_pixel_\",\n\n    // go/trendy/manage/engineers/4521753585778688\n    trendy_team_id: \"4521753585778688\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_health_services\",\n\n    // go/trendy/manage/engineers/6526182686097408\n    trendy_team_id: \"6526182686097408\",\n}\n\nteam {\n    name: \"trendy_team_media_volume\",\n\n    // go/trendy/manage/engineers/6360142070841344\n    trendy_team_id: \"6360142070841344\",\n}\n\nteam {\n    name: \"trendy_team_large_screen_experiences_sysui\",\n\n    // go/trendy/manage/engineers/5855214130069504\n    trendy_team_id: \"5855214130069504\",\n}\n\nteam {\n    name: \"trendy_team_mainline_engprod\",\n\n    // go/trendy/manage/engineers/5474634789847040\n    trendy_team_id: \"5474634789847040\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_power_foundations\",\n\n    // go/trendy/manage/engineers/6292909196214272\n    trendy_team_id: \"6292909196214272\",\n}\n\nteam {\n    name: \"trendy_team_windowing_tools\",\n\n    // go/trendy/manage/engineers/6382778382188544\n    trendy_team_id: \"6382778382188544\",\n}\n\nteam {\n    name: \"trendy_team_android_framework_appcompat\",\n\n    // go/trendy/manage/engineers/5383770701955072\n    trendy_team_id: \"5383770701955072\",\n}\n\nteam {\n    name: \"trendy_team_fitbit\",\n\n    // go/trendy/manage/engineers/6497885327360000\n    trendy_team_id: \"6497885327360000\",\n}\n\nteam {\n    name: \"trendy_team_overdrive\",\n\n    // go/trendy/manage/engineers/4961558236889088\n    trendy_team_id: \"4961558236889088\",\n}\n\nteam {\n    name: \"trendy_team_framework_android_packages\",\n\n    // go/trendy/manage/engineers/5989762407104512\n    trendy_team_id: \"5989762407104512\",\n}\n\nteam {\n    name: \"trendy_team_tkilbourn_team\",\n\n    // go/trendy/manage/engineers/4856646707871744\n    trendy_team_id: \"4856646707871744\",\n}\n\nteam {\n    name: \"trendy_team_large_screen_experiences_platform\",\n\n    // go/trendy/manage/engineers/4826462937317376\n    trendy_team_id: \"4826462937317376\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_service\",\n\n    // go/trendy/manage/engineers/5802643790135296\n    trendy_team_id: \"5802643790135296\",\n}\n\nteam {\n    name: \"trendy_team_android_developer_tools\",\n\n    // go/trendy/manage/engineers/6201807353020416\n    trendy_team_id: \"6201807353020416\",\n}\n\nteam {\n    name: \"trendy_team_autofill\",\n\n    // go/trendy/manage/engineers/4676203460329472\n    trendy_team_id: \"4676203460329472\",\n}\n\nteam {\n    name: \"trendy_team_wsd_w5\",\n\n    // go/trendy/manage/engineers/4627306702045184\n    trendy_team_id: \"4627306702045184\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_xfood_xwear_and_logistics\",\n\n    // go/trendy/manage/engineers/6599089311350784\n    trendy_team_id: \"6599089311350784\",\n}\n\nteam {\n    name: \"trendy_team_android_media_drm\",\n\n    // go/trendy/manage/engineers/5311752690335744\n    trendy_team_id: \"5311752690335744\",\n}\n\nteam {\n    name: \"trendy_team_nsylvain_team\",\n\n    // go/trendy/manage/engineers/5129062893912064\n    trendy_team_id: \"5129062893912064\",\n}\n\nteam {\n    name: \"trendy_team_fwk_wifi_hal\",\n\n    // go/trendy/manage/engineers/5470082364735488\n    trendy_team_id: \"5470082364735488\",\n}\n\nteam {\n    name: \"trendy_team_tombergan_team\",\n\n    // go/trendy/manage/engineers/5764031194497024\n    trendy_team_id: \"5764031194497024\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_platform_program_partner_eng_\",\n\n    // go/trendy/manage/engineers/5472261070815232\n    trendy_team_id: \"5472261070815232\",\n}\n\nteam {\n    name: \"trendy_team_masd_pixel_key_experiences\",\n\n    // go/trendy/manage/engineers/4873597306667008\n    trendy_team_id: \"4873597306667008\",\n}\n\nteam {\n    name: \"trendy_team_external\",\n\n    // go/trendy/manage/engineers/6033032318156800\n    trendy_team_id: \"6033032318156800\",\n}\n\nteam {\n    name: \"trendy_team_fwk_telephony\",\n\n    // go/trendy/manage/engineers/5663596411224064\n    trendy_team_id: \"5663596411224064\",\n}\n\nteam {\n    name: \"trendy_team_customization_picker\",\n\n    // go/trendy/manage/engineers/6311142173081600\n    trendy_team_id: \"6311142173081600\",\n}\n\nteam {\n    name: \"trendy_team_android_test_surfaces\",\n\n    // go/trendy/manage/engineers/4879149651099648\n    trendy_team_id: \"4879149651099648\",\n}\n\nteam {\n    name: \"trendy_team_instant_apps\",\n\n    // go/trendy/manage/engineers/6720776841330688\n    trendy_team_id: \"6720776841330688\",\n}\n\nteam {\n    name: \"trendy_team_accessibility_aas\",\n\n    // go/trendy/manage/engineers/6043198228299776\n    trendy_team_id: \"6043198228299776\",\n}\n\nteam {\n    name: \"trendy_team_android_engprod_lon\",\n\n    // go/trendy/manage/engineers/5432243163791360\n    trendy_team_id: \"5432243163791360\",\n}\n\nteam {\n    name: \"trendy_team_pmw_mce\",\n\n    // go/trendy/manage/engineers/6424723868909568\n    trendy_team_id: \"6424723868909568\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_kernel\",\n\n    // go/trendy/manage/engineers/5436802711846912\n    trendy_team_id: \"5436802711846912\",\n}\n\nteam {\n    name: \"trendy_team_nga\",\n\n    // go/trendy/manage/engineers/5594876934488064\n    trendy_team_id: \"5594876934488064\",\n}\n\nteam {\n    name: \"trendy_team_web_on_android_performance\",\n\n    // go/trendy/manage/engineers/5864851748847616\n    trendy_team_id: \"5864851748847616\",\n}\n\nteam {\n    name: \"trendy_team_reveman_team\",\n\n    // go/trendy/manage/engineers/5113274057261056\n    trendy_team_id: \"5113274057261056\",\n}\n\nteam {\n    name: \"trendy_team_test_eng_ota_framework_sysui_suw_abvt_cts\",\n\n    // go/trendy/manage/engineers/4653694981111808\n    trendy_team_id: \"4653694981111808\",\n}\n\nteam {\n    name: \"trendy_team_backup_restore\",\n\n    // go/trendy/manage/engineers/5049519167111168\n    trendy_team_id: \"5049519167111168\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_bringup_and_factory\",\n\n    // go/trendy/manage/engineers/5999752665268224\n    trendy_team_id: \"5999752665268224\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_calling_messaging\",\n\n    // go/trendy/manage/engineers/5401274807648256\n    trendy_team_id: \"5401274807648256\",\n}\n\nteam {\n    name: \"trendy_team_android_imaging\",\n\n    // go/trendy/manage/engineers/5838538113384448\n    trendy_team_id: \"5838538113384448\",\n}\n\nteam {\n    name: \"trendy_team_pixel_system_sw_sensor\",\n\n    // go/trendy/manage/engineers/4904417557643264\n    trendy_team_id: \"4904417557643264\",\n}\n\nteam {\n    name: \"trendy_team_dogfood_triage\",\n\n    // go/trendy/manage/engineers/5130823981236224\n    trendy_team_id: \"5130823981236224\",\n}\n\nteam {\n    name: \"trendy_team_input_method_framework\",\n\n    // go/trendy/manage/engineers/6394201770459136\n    trendy_team_id: \"6394201770459136\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_platform_dev_lead_device_program_\",\n\n    // go/trendy/manage/engineers/4642393194135552\n    trendy_team_id: \"4642393194135552\",\n}\n\nteam {\n    name: \"trendy_team_wear_bona_companion\",\n\n    // go/trendy/manage/engineers/4721932784009216\n    trendy_team_id: \"4721932784009216\",\n}\n\nteam {\n    name: \"trendy_team_wear_wear_compose\",\n\n    // go/trendy/manage/engineers/4958404271308800\n    trendy_team_id: \"4958404271308800\",\n}\n\nteam {\n    name: \"trendy_team_system_fugu_\",\n\n    // go/trendy/manage/engineers/5682837864710144\n    trendy_team_id: \"5682837864710144\",\n}\n\nteam {\n    name: \"trendy_team_tvolkert_team\",\n\n    // go/trendy/manage/engineers/5093014696525824\n    trendy_team_id: \"5093014696525824\",\n}\n\nteam {\n    name: \"trendy_team_media_framework_drm\",\n\n    // go/trendy/manage/engineers/5311752690335744\n    trendy_team_id: \"5311752690335744\",\n}\n\nteam {\n    name: \"trendy_team_media_framework_audio\",\n\n    // go/trendy/manage/engineers/5823575353065472\n    trendy_team_id: \"5823575353065472\",\n}\n\nteam {\n    name: \"trendy_team_pixel_pearl\",\n\n    // go/trendy/manage/engineers/6326219602231296\n    trendy_team_id: \"6326219602231296\",\n}\n\nteam {\n    name: \"trendy_team_ar_sensors_context_hub\",\n\n    // go/trendy/manage/engineers/4776371090259968\n    trendy_team_id: \"4776371090259968\",\n}\n\nteam {\n    name: \"trendy_team_media_codec_framework\",\n\n    // go/trendy/manage/engineers/4943966050844672\n    trendy_team_id: \"4943966050844672\",\n}\n\nteam {\n    name: \"trendy_team_android_platform_performance_testing\",\n\n    // go/trendy/manage/engineers/5810097836621824\n    trendy_team_id: \"5810097836621824\",\n}\n\nteam {\n    name: \"trendy_team_adte\",\n\n    // go/trendy/manage/engineers/5551098528825344\n    trendy_team_id: \"5551098528825344\",\n}\n\nteam {\n    name: \"trendy_team_incremental\",\n\n    // go/trendy/manage/engineers/5955405559201792\n    trendy_team_id: \"5955405559201792\",\n}\n\nteam {\n    name: \"trendy_team_android_media_better_together\",\n\n    // go/trendy/manage/engineers/5617300451721216\n    trendy_team_id: \"5617300451721216\",\n}\n\nteam {\n    name: \"trendy_team_attack_tools\",\n\n    // go/trendy/manage/engineers/4705629185081344\n    trendy_team_id: \"4705629185081344\",\n}\n\nteam {\n    name: \"trendy_team_android_media_solutions_editing\",\n\n    // go/trendy/manage/engineers/5350750192762880\n    trendy_team_id: \"5350750192762880\",\n}\n\nteam {\n    name: \"trendy_team_android_media_solutions_playback\",\n\n    // go/trendy/manage/engineers/6742515252559872\n    trendy_team_id: \"6742515252559872\",\n}\n\nteam {\n    name: \"trendy_team_android_telemetry_client_infra\",\n\n    // go/trendy/manage/engineers/5403245077430272\n    trendy_team_id: \"5403245077430272\",\n}\n\nteam {\n    name: \"trendy_team_pte_sysui\",\n\n    // go/trendy/manage/engineers/5185897463382016\n    trendy_team_id: \"5185897463382016\",\n}\n\nteam {\n    name: \"trendy_team_pixel_troubleshooting_app\",\n\n    // go/trendy/manage/engineers/5097003746426880\n    trendy_team_id: \"5097003746426880\",\n}\n\nteam {\n    name: \"trendy_team_desktop_firmware\",\n\n    // go/trendy/manage/engineers/5787938454863872\n    trendy_team_id: \"5787938454863872\",\n}\n\nteam {\n    name: \"trendy_team_art_cloud\",\n\n    // go/trendy/manage/engineers/5121440647577600\n    trendy_team_id: \"5121440647577600\",\n}\n\nteam {\n    name: \"trendy_team_ravenwood\",\n\n    // go/trendy/manage/engineers/6027181500497920\n    trendy_team_id: \"6027181500497920\",\n}\n\nteam {\n    name: \"trendy_team_automotive_cast\",\n\n    // go/trendy/manage/engineers/5293683026264064\n    trendy_team_id: \"5293683026264064\",\n}\n\nteam {\n    name: \"trendy_team_wear_standalone_kids\",\n\n    // go/trendy/manage/engineers/6303298703949824\n    trendy_team_id: \"6303298703949824\",\n}\n\nteam {\n    name: \"trendy_team_desktop_stats\",\n\n    // go/trendy/manage/engineers/5440764114206720\n    trendy_team_id: \"5440764114206720\",\n}\n\nteam {\n    name: \"trendy_team_desktop_wifi\",\n\n    // go/trendy/manage/engineers/6463689697099776\n    trendy_team_id: \"6463689697099776\",\n}\n\n// DON'T ADD NEW RULES HERE. For more details refer to\n// go/new-android-ownership-model\n"
  },
  {
    "path": "teams/OWNERS",
    "content": "dariofreni@google.com\nronish@google.com\n"
  },
  {
    "path": "tests/artifact_path_requirements/inherit1.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":inherit3.rbc\", _inherit3_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  rblf.inherit(handle, \"test/inherit3\", _inherit3_init)\n"
  },
  {
    "path": "tests/artifact_path_requirements/inherit2.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":inherit4.rbc\", _inherit4_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  rblf.inherit(handle, \"test/inherit4\", _inherit4_init)\n  rblf.require_artifacts_in_path(handle, \"vendor/\", \"\")\n"
  },
  {
    "path": "tests/artifact_path_requirements/inherit3.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":inherit4.rbc\", _inherit4_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  rblf.inherit(handle, \"test/inherit4\", _inherit4_init)\n  rblf.require_artifacts_in_path(handle, \"vendor/\", \"\")\n"
  },
  {
    "path": "tests/artifact_path_requirements/inherit4.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  rblf.setdefault(handle, \"PRODUCT_COPY_FILES\")\n  cfg[\"PRODUCT_COPY_FILES\"] += [\"foo/bar/baz.txt:vendor/etc/baz.txt\"]\n"
  },
  {
    "path": "tests/artifact_path_requirements/product.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":inherit1.rbc\", _inherit1_init = \"init\")\nload(\":inherit2.rbc\", _inherit2_init = \"init\")\nload(\":inherit3.rbc\", _inherit3_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n  rblf.inherit(handle, \"test/inherit1\", _inherit1_init)\n  rblf.inherit(handle, \"test/inherit2\", _inherit2_init)\n  rblf.inherit(handle, \"test/inherit3\", _inherit3_init)\n"
  },
  {
    "path": "tests/artifact_path_requirements/test.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\"//build/make/tests/input_variables.rbc\", input_variables_init = \"init\")\nload(\":product.rbc\", \"init\")\n\ndef assert_eq(expected, actual):\n    if expected != actual:\n        fail(\"Expected '%s', got '%s'\" % (expected, actual))\n\ndef test():\n    (globals, globals_base) = rblf.product_configuration(\"test/product\", init, input_variables_init)\n    assert_eq([\"foo/bar/baz.txt:vendor/etc/baz.txt\"], globals[\"PRODUCTS.test/product.mk.PRODUCT_COPY_FILES\"])\n    assert_eq([\"foo/bar/baz.txt:vendor/etc/baz.txt\"], globals[\"PRODUCTS.test/inherit2.mk.PRODUCT_COPY_FILES\"])\n    assert_eq([\"foo/bar/baz.txt:vendor/etc/baz.txt\"], globals[\"PRODUCTS.test/inherit3.mk.PRODUCT_COPY_FILES\"])\n"
  },
  {
    "path": "tests/b_tests.sh",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# These commands are expected to always return successfully\n\ntrap 'exit 1' ERR\n\nsource $(dirname $0)/../envsetup.sh\n\n# lunch required to set up PATH to use b\nlunch aosp_arm64\n\ntest_target=//build/bazel/scripts/difftool:difftool\n\nif b build //build/bazel:nonexistent_module &>/dev/null ; then\n    echo \"b did not fail when building a nonexistent module\" >&2\n    exit 1\nfi\nb build \"$test_target\"\nb build -- \"$test_target\"\nb build \"$test_target\" --run-soong-tests\nb build --run-soong-tests \"$test_target\"\nb --run-soong-tests build \"$test_target\"\n# Test that the bazel server can be restarted once shut down. If run in a\n# docker container, you need to run the docker container with --init or\n# have some other process as PID 1 that can reap zombies.\nb shutdown\nb cquery 'kind(test, //build/bazel/examples/android_app/...)' --config=android\nb run $test_target -- --help >/dev/null\n\n# Workflow tests for bmod\nbmod libm\nb run $(bmod fastboot) -- help\nb build $(bmod libm) $(bmod libcutils) --config=android\n"
  },
  {
    "path": "tests/board.rbc",
    "content": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n  g[\"A_LIST_VARIABLE\"] += [\"bar\"]\n"
  },
  {
    "path": "tests/board_input_vars.rbc",
    "content": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n  g[\"A_LIST_VARIABLE\"] = [\"foo\"]\n"
  },
  {
    "path": "tests/conversion_error.rbc",
    "content": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\n# Run test configuration and verify its result.\n# The main configuration file is device.rbc.\n# It inherits part1.rbc and also includes include1.rbc\n# TODO(asmundak): more tests are needed to verify that:\n#  * multi-level inheritance works as expected\n#  * all runtime functions (wildcard, regex, etc.) work\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":version_defaults.rbc\", \"version_defaults\")\nload(\":device.rbc\", \"init\")\n\nrblf.mk2rbc_error(\"file.mk:123\", \"cannot convert\")\n"
  },
  {
    "path": "tests/envsetup_tests.sh",
    "content": "#!/bin/bash -e\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ntests=(\n $(dirname $0)/lunch_tests.sh\n)\n\nfor test in $tests; do\n  bash -x $test\ndone\n"
  },
  {
    "path": "tests/include1.rbc",
    "content": "\n# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Included file (not inherited)\n# Converted from makefile\n### PRODUCT_PACKAGES += inc\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n  rblf.setdefault(handle, \"PRODUCT_PACKAGES\")\n  cfg[\"PRODUCT_PACKAGES\"] += [\"inc\"]\n"
  },
  {
    "path": "tests/inherits_in_regular_variables/inherit1.rbc",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  cfg.setdefault(\"PRODUCT_PACKAGES\", [])\n  cfg[\"PRODUCT_PACKAGES\"] += [\"bar\"]\n"
  },
  {
    "path": "tests/inherits_in_regular_variables/product.rbc",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":inherit1.rbc\", _inherit1_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  cfg.setdefault(\"PRODUCT_PACKAGES\", [])\n  cfg[\"PRODUCT_PACKAGES\"] += [\"foo\"]\n\n  g[\"PRODUCT_PACKAGES_COPY\"] = cfg[\"PRODUCT_PACKAGES\"]\n\n  rblf.inherit(handle, \"test/inherit1\", _inherit1_init)\n\n"
  },
  {
    "path": "tests/inherits_in_regular_variables/test.rbc",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\"//build/make/tests/input_variables.rbc\", input_variables_init = \"init\")\nload(\":product.rbc\", \"init\")\n\n\ndef assert_eq(expected, actual):\n    if expected != actual:\n        fail(\"Expected '%s', got '%s'\" % (expected, actual))\n\ndef test():\n    (globals, globals_base) = rblf.product_configuration(\"test/device\", init, input_variables_init)\n    assert_eq([\"foo\", \"bar\"], globals[\"PRODUCTS.test/device.mk.PRODUCT_PACKAGES\"])\n    assert_eq([\"foo\", (\"test/inherit1\",)], globals[\"PRODUCT_PACKAGES_COPY\"])\n\n    # Ideally we would check that rblf.printvars returns the correct result, but we don't have\n    # a good way to intercept its output or mock rblf_cli\n"
  },
  {
    "path": "tests/input_variables.rbc",
    "content": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This file was generated by running `m RBC_PRODUCT_CONFIG=1 nothing`\n# and then copying it from out/rbc/out/rbc/make_vars_pre_product_config.rbc.\n# It was manually trimmed down afterwards to just the variables we need.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n  g[\"PLATFORM_VERSION_CODENAME\"] = \"Tiramisu\"\n  g[\"PLATFORM_VERSION\"] = \"Tiramisu\"\n  g[\"TARGET_BUILD_VARIANT\"] = \"userdebug\"\n  g[\"TARGET_BUILD_TYPE\"] = \"release\"\n  g[\"TARGET_PRODUCT\"] = \"aosp_arm64\"\n  g[\"PLATFORM_SDK_VERSION\"] = \"31\"\n"
  },
  {
    "path": "tests/lunch_tests.sh",
    "content": "#!/usr/bin/env bash\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nsource $(dirname $0)/../envsetup.sh\n\nunset TARGET_PRODUCT TARGET_BUILD_VARIANT TARGET_PLATFORM_VERSION\n\nfunction check_lunch\n(\n    echo lunch $1\n    set +e\n    lunch $1 > /dev/null 2> /dev/null\n    set -e\n    [ \"$TARGET_PRODUCT\" = \"$2\" ] || ( echo \"lunch $1: expected TARGET_PRODUCT='$2', got '$TARGET_PRODUCT'\" && exit 1 )\n    [ \"$TARGET_BUILD_VARIANT\" = \"$3\" ] || ( echo \"lunch $1: expected TARGET_BUILD_VARIANT='$3', got '$TARGET_BUILD_VARIANT'\" && exit 1 )\n    [ \"$TARGET_PLATFORM_VERSION\" = \"$4\" ] || ( echo \"lunch $1: expected TARGET_PLATFORM_VERSION='$4', got '$TARGET_PLATFORM_VERSION'\" && exit 1 )\n)\n\ndefault_version=$(get_build_var RELEASE_PLATFORM_VERSION)\n\n# lunch tests\ncheck_lunch \"aosp_arm64\"                                \"aosp_arm64\" \"eng\"       \"\"\ncheck_lunch \"aosp_arm64-userdebug\"                      \"aosp_arm64\" \"userdebug\" \"\"\ncheck_lunch \"aosp_arm64-userdebug-$default_version\"     \"aosp_arm64\" \"userdebug\" \"$default_version\"\ncheck_lunch \"abc\"                                       \"\" \"\" \"\"\ncheck_lunch \"aosp_arm64-abc\"                            \"\" \"\" \"\"\ncheck_lunch \"aosp_arm64-userdebug-abc\"                  \"\" \"\" \"\"\ncheck_lunch \"aosp_arm64-abc-$default_version\"             \"\" \"\" \"\"\ncheck_lunch \"abc-userdebug-$default_version\"              \"\" \"\" \"\"\ncheck_lunch \"-\"                                         \"\" \"\" \"\"\ncheck_lunch \"--\"                                        \"\" \"\" \"\"\ncheck_lunch \"-userdebug\"                                \"\" \"\" \"\"\ncheck_lunch \"-userdebug-\"                               \"\" \"\" \"\"\ncheck_lunch \"-userdebug-$default_version\"                 \"\" \"\" \"\"\ncheck_lunch \"aosp_arm64-userdebug-$default_version-\"      \"\" \"\" \"\"\ncheck_lunch \"aosp_arm64-userdebug-$default_version-abc\"   \"\" \"\" \"\"\n"
  },
  {
    "path": "tests/part1.rbc",
    "content": "\n# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Part configuration\n# Converted from\n### PRODUCT_COPY_FILES += part_from:part_to\n### PRODUCT_PRODUCT_PROPERTIES += part_properties\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n  rblf.setdefault(handle, \"PRODUCT_COPY_FILES\")\n  cfg[\"PRODUCT_COPY_FILES\"] += [\"part_from:part_to\"]\n  rblf.setdefault(handle, \"PRODUCT_PRODUCT_PROPERTIES\")\n  cfg[\"PRODUCT_PRODUCT_PROPERTIES\"] += [\"part_properties\"]\n  rblf.soong_config_namespace(g, \"NS1\")\n  rblf.soong_config_append(g, \"NS1\", \"v1\", \"abc_part1\")\n"
  },
  {
    "path": "tests/prefixed_sort_order/base-secondary.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  g.setdefault(\"MY_VAR\", [])\n  g[\"MY_VAR\"] += [\"foo\"]\n"
  },
  {
    "path": "tests/prefixed_sort_order/base.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  g.setdefault(\"MY_VAR\", [])\n  g[\"MY_VAR\"] += [\"bar\"]\n"
  },
  {
    "path": "tests/prefixed_sort_order/product.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":base.rbc\", _base_init = \"init\")\nload(\":base-secondary.rbc\", _base_secondary_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  # It's important that base-secondary uses a dash, an underscore won't expose the sort order issue:\n  # >>> sorted([\"base\", \"base-secondary\"])\n  # ['base', 'base-secondary']\n  # >>> sorted([\"base.mk\", \"base-secondary.mk\"])\n  # ['base-secondary.mk', 'base.mk']\n\n  rblf.inherit(handle, \"base\", _base_init)\n  rblf.inherit(handle, \"base-secondary\", _base_secondary_init)\n"
  },
  {
    "path": "tests/prefixed_sort_order/test.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\"//build/make/tests/input_variables.rbc\", input_variables_init = \"init\")\nload(\":product.rbc\", \"init\")\n\n\ndef assert_eq(expected, actual):\n    if expected != actual:\n        fail(\"Expected '%s', got '%s'\" % (expected, actual))\n\ndef test():\n    (globals, globals_base) = rblf.product_configuration(\"test/device\", init, input_variables_init)\n    assert_eq([\"foo\", \"bar\"], globals[\"MY_VAR\"])\n"
  },
  {
    "path": "tests/product.rbc",
    "content": "\n# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Top-level test configuration.\n# Converted from the following makefile\n### PRODUCT_PACKAGES += dev\n### PRODUCT_HOST_PACKAGES += host\n### $(call inherit-product, $(LOCAL_PATH)/part1.mk)\n### PRODUCT_COPY_FILES += device_from:device_to\n### include $(LOCAL_PATH)/include1.mk\n### PRODUCT_PACKAGES += dev_after\n### PRODUCT_COPY_FILES += $(call find-copy-subdir-files,audio_platform_info*.xml,device/google/redfin/audio,$(TARGET_COPY_OUT_VENDOR)/etc) xyz:/etc/xyz\n### PRODUCT_COPY_FILES += $(call copy-files,x.xml y.xml,/etc)\n### $(call add_soong_config_namespace,NS1)\n### $(call soong_config_append,NS1,v1,abc)\n### $(call soong_config_append,NS1,v2,def)\n### $(call add_soong_config_var_value,NS2,v3,abc)\n### $(call soong_config_set,NS2,v3,xyz)\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":part1.rbc\", _part1_init = \"init\")\nload(\":include1.rbc\", _include1_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n  rblf.setdefault(handle, \"PRODUCT_PACKAGES\")\n  cfg[\"PRODUCT_PACKAGES\"] += [\"dev\"]\n  rblf.setdefault(handle, \"PRODUCT_HOST_PACKAGES\")\n  cfg[\"PRODUCT_HOST_PACKAGES\"] += [\"host\"]\n  rblf.inherit(handle, \"test/part1\", _part1_init)\n  rblf.setdefault(handle, \"PRODUCT_COPY_FILES\")\n  cfg[\"PRODUCT_COPY_FILES\"] += [\"device_from:device_to\"]\n  _include1_init(g, handle)\n  cfg[\"PRODUCT_PACKAGES\"] += [\"dev_after\"]\n  cfg[\"PRODUCT_COPY_FILES\"] += (rblf.find_and_copy(\"audio_platform_info*.xml\", \"device/google/redfin\", \"||VENDOR-PATH-PH||/etc\") +\n      [\"xyz:/etc/xyz\"])\n  cfg[\"PRODUCT_COPY_FILES\"] += rblf.copy_files(\"x.xml y.xml\", \"/etc\")\n  cfg[\"PRODUCT_COPY_FILES\"] += rblf.copy_files([\"from/sub/x\", \"from/sub/y\"], \"to\")\n\n  rblf.soong_config_namespace(g, \"NS1\")\n  rblf.soong_config_append(g, \"NS1\", \"v1\", \"abc\")\n  rblf.soong_config_append(g, \"NS1\", \"v2\", \"def\")\n  rblf.soong_config_set(g, \"NS2\", \"v3\", \"abc\")\n  rblf.soong_config_set(g, \"NS2\", \"v3\", \"xyz\")\n  rblf.soong_config_set(g, \"NS2\", \"v4\", \"xyz   \")\n\n  rblf.mkdist_for_goals(g, \"goal\", \"dir1/file1:out1 dir1/file2:out2\")\n  rblf.mkdist_for_goals(g, \"goal\", \"dir2/file2:\")\n\n  if rblf.board_platform_in(g, \"board1 board2\"):\n    cfg[\"PRODUCT_PACKAGES\"] += [\"bad_package\"]\n  g[\"TARGET_BOARD_PLATFORM\"] = \"board1\"\n  if rblf.board_platform_in(g, \"board1 board2\"):\n    cfg[\"PRODUCT_PACKAGES\"] += [\"board1_in\"]\n  if rblf.board_platform_in(g, [\"board3\",\"board2\"]):\n    cfg[\"PRODUCT_PACKAGES\"] += [\"bad_board_in\"]\n  if rblf.board_platform_is(g, \"board1\"):\n    cfg[\"PRODUCT_PACKAGES\"] += [\"board1_is\"]\n  if rblf.board_platform_is(g, \"board2\"):\n    cfg[\"PRODUCT_PACKAGES\"] += [\"bad_board1_is\"]\n"
  },
  {
    "path": "tests/roboleaf_tests.sh",
    "content": "#!/bin/bash -e\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ntests=(\n $(dirname $0)/b_tests.sh\n)\n\nfor test in $tests; do\n  bash -x $test\ndone\n"
  },
  {
    "path": "tests/run.rbc",
    "content": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\n# Run test product configuration and verify its result.\n# The main configuration file is product.rbc.\n# It inherits part1.rbc and also includes include1.rbc\n# TODO(asmundak): more tests are needed to verify that:\n#  * multi-level inheritance works as expected\n#  * all runtime functions (wildcard, regex, etc.) work\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":input_variables.rbc\", input_variables_init = \"init\")\nload(\":product.rbc\", \"init\")\nload(\":board.rbc\", board_init = \"init\")\nload(\":board_input_vars.rbc\", board_input_vars_init = \"init\")\nload(\"//build/make/tests/single_value_inheritance:test.rbc\", test_single_value_inheritance = \"test\")\nload(\"//build/make/tests/single_value_inheritance_2:test.rbc\", test_single_value_inheritance_2 = \"test\")\nload(\"//build/make/tests/artifact_path_requirements:test.rbc\", test_artifact_path_requirements = \"test\")\nload(\"//build/make/tests/prefixed_sort_order:test.rbc\", test_prefixed_sort_order = \"test\")\nload(\"//build/make/tests/inherits_in_regular_variables:test.rbc\", test_inherits_in_regular_variables = \"test\")\n\ndef assert_eq(expected, actual):\n    if expected != actual:\n        fail(\"Expected '%s', got '%s'\" % (expected, actual))\n\ndef assert_dict_subset(expected, actual):\n    for key, val in expected.items():\n        assert_eq(val, actual[key])\n\n# Unit tests for non-trivial runtime functions\nassert_eq([\"a\", \"b\", \"c\"], rblf.mksort(\"b a    c c\"))\nassert_eq([\"a\", \"b\", \"c\"], rblf.mksort([\"b\", \"a\", \"c\", \"c\"]))\n\nassert_eq(\"\", rblf.mkstrip(\" \\n \\t    \"))\nassert_eq(\"a b c\", rblf.mkstrip(\"  a b   \\n  c \\t\"))\nassert_eq(\"1\", rblf.mkstrip(\"1 \"))\n\nassert_eq([\"a\", \"b\"], rblf.words(\"a b\"))\nassert_eq([\"a\", \"b\", \"c\"], rblf.words([\"a b\", \"c\"]))\n# 1-tuple like we use in product variables\nassert_eq([\"a b\", (\"c\",)], rblf.words([\"a b\", (\"c\",)]))\n\nassert_eq(\"b1 b2\", rblf.mksubst(\"a\", \"b\", \"a1 a2\"))\nassert_eq([\"b1\", \"x2\"], rblf.mksubst(\"a\", \"b\", [\"a1\", \"x2\"]))\n\nassert_eq(\"ABcdYZ\", rblf.mkpatsubst(\"ab%yz\", \"AB%YZ\", \"abcdyz\"))\nassert_eq(\"bcz\", rblf.mkpatsubst(\"a%z\", \"A%Z\", \"bcz\"))\nassert_eq([\"Ay\", \"Az\"], rblf.mkpatsubst(\"a%\", \"A%\", [\"ay\", \"az\"]))\nassert_eq(\"AcZ bcz\", rblf.mkpatsubst(\"a%z\", \"A%Z\", \"acz  bcz\"))\nassert_eq(\"Abcd\", rblf.mkpatsubst(\"a%\", \"A%\", \"abcd\"))\nassert_eq(\"abcZ\", rblf.mkpatsubst(\"%z\", \"%Z\", \"abcz\"))\nassert_eq(\"azx b\", rblf.mkpatsubst(\"az\", \"AZ\", \"azx  b\"))\nassert_eq([\"azx\", \"b\"], rblf.mkpatsubst(\"az\", \"AZ\", [\"azx\", \"b\"]))\nassert_eq(\"ABC\", rblf.mkpatsubst(\"abc\", \"ABC\", \"abc\"))\nassert_eq([\"%/foo\"], rblf.mkpatsubst(\"%\", \"\\\\%/%\", [\"foo\"]))\nassert_eq([\"foo/%\"], rblf.mkpatsubst(\"%\", \"%/%\", [\"foo\"]))\nassert_eq([\"from/a:to/a\", \"from/b:to/b\"], rblf.product_copy_files_by_pattern(\"from/%\", \"to/%\", \"a b\"))\n\nassert_eq([], rblf.filter([\"a\", \"\", \"b\"], \"f\"))\nassert_eq([\"ab%c\", \"axyzb%c\"], rblf.filter([\"a%b%c\"], [\"ab%c\", \"axyzb%c\", \"axyzb%cd\", \"axyzbwc\"]))\nassert_eq([\"abc\", \"bcd\"], rblf.filter([\"a%\", \"b%\"], [\"abc\", \"def\", \"bcd\", \"xabc\"]))\nassert_eq([\"b\", \"ab\"], rblf.filter_out([\"a\", \"\" ], [\"a\", \"\", \"b\", \"ab\"]))\nassert_eq([\"c\"], rblf.filter_out([\"a\", \"b\" ], [\"a\", \"b\", \"c\"]))\nassert_eq([\"c\"], rblf.filter_out([\"a%\", \"b\" ], [\"abc\", \"b\", \"c\"]))\n\nassert_eq(\"foo.c no_folder\", rblf.notdir([\"src/foo.c\", \"no_folder\"]))\nassert_eq(\"foo.c no_folder\", rblf.notdir(\"src/foo.c no_folder\"))\nassert_eq(\"\", rblf.notdir(\"/\"))\nassert_eq(\"\", rblf.notdir(\"\"))\n\ncwd = rblf_shell('pwd')\nassert_eq(cwd+\"/foo/bar\", rblf.abspath(\"foo/bar\"))\nassert_eq(cwd+\"/bar\", rblf.abspath(\"foo/.././bar\"))\nassert_eq(cwd+\"/bar\", rblf.abspath(\"foo/..////bar//\"))\nassert_eq(\"/foo/baz\", rblf.abspath(\"/foo/bar/../baz\"))\nassert_eq(cwd+\"/foo/bar \"+cwd+\"/foo/baz\", rblf.abspath(\"foo/bar foo/baz\"))\nassert_eq(\"/baz\", rblf.abspath(\"/../../../../../../../../../../../../../../../../baz\"))\n\nassert_eq(\"foo\", rblf.first_word(\"foo bar\"))\nassert_eq(\"foo\", rblf.first_word([\"foo\", \"bar\"]))\nassert_eq(\"\", rblf.first_word(\"\"))\nassert_eq(\"\", rblf.first_word([]))\nassert_eq(\"bar\", rblf.last_word(\"foo bar\"))\nassert_eq(\"bar\", rblf.last_word([\"foo\", \"bar\"]))\nassert_eq(\"\", rblf.last_word(\"\"))\nassert_eq(\"\", rblf.last_word([]))\n\nassert_eq([\"foo\", \"bar\"], rblf.flatten_2d_list([[\"foo\", \"bar\"]]))\nassert_eq([\"foo\", \"bar\"], rblf.flatten_2d_list([[\"foo\"], [\"bar\"]]))\nassert_eq([], rblf.flatten_2d_list([]))\n\nassert_eq(\n    [\"build/make/tests/board.rbc\", \"build/make/tests/board_input_vars.rbc\"],\n    rblf.expand_wildcard(\"build/make/tests/board*.rbc\")\n)\nassert_eq(\n    [\"build/make/tests/run.rbc\", \"build/make/tests/product.rbc\"],\n    rblf.expand_wildcard(\"build/make/tests/run.rbc build/make/tests/product.rbc\")\n)\nassert_eq(\n    [\"build/make/tests/run.rbc\"],\n    rblf.expand_wildcard(\"build/make/tests/run.rbc build/make/tests/nonexistent.rbc\")\n)\n\n(globals, globals_base) = rblf.product_configuration(\"test/device\", init, input_variables_init)\nassert_dict_subset({\n    \"PRODUCTS.test/device.mk.PRODUCT_COPY_FILES\": [\n        \"part_from:part_to\",\n        \"device_from:device_to\",\n        \"device/google/redfin/audio/audio_platform_info_noextcodec_snd.xml:||VENDOR-PATH-PH||/etc/audio/audio_platform_info_noextcodec_snd.xml\",\n        \"xyz:/etc/xyz\",\n        \"x.xml:/etc/x.xml\",\n        \"y.xml:/etc/y.xml\",\n        \"from/sub/x:to/x\",\n        \"from/sub/y:to/y\",\n    ],\n    \"PRODUCTS.test/device.mk.PRODUCT_HOST_PACKAGES\": [\"host\"],\n    \"PRODUCTS.test/device.mk.PRODUCT_PACKAGES\": [\n        \"dev\",\n        \"inc\",\n        \"dev_after\",\n        \"board1_in\",\n        \"board1_is\",\n    ],\n    \"PRODUCTS.test/device.mk.PRODUCT_PRODUCT_PROPERTIES\": [\"part_properties\"]\n}, globals)\n\nns = globals[\"$SOONG_CONFIG_NAMESPACES\"]\nassert_eq(\n    {\n        \"NS1\": {\n            \"v1\": \"abc abc_part1\",\n            \"v2\": \"def\"\n        },\n        \"NS2\": {\n            \"v3\": \"xyz\",\n            \"v4\": \"xyz\"\n        }\n    },\n    {k:v for k, v in sorted(ns.items()) }\n)\n\nassert_eq(\"Tiramisu\", globals[\"PLATFORM_VERSION\"])\nassert_eq(\"31\", globals[\"PLATFORM_SDK_VERSION\"])\n\nassert_eq(\"xyz\", rblf.soong_config_get(globals, \"NS2\", \"v3\"))\nassert_eq(None, rblf.soong_config_get(globals, \"NS2\", \"nonexistant_var\"))\n\ngoals = globals[\"$dist_for_goals\"]\nassert_eq(\n    {\n        \"goal\": [(\"dir1/file1\", \"out1\"), (\"dir1/file2\", \"out2\"), (\"dir2/file2\", \"file2\")]\n    },\n    { k:v for k,v in sorted(goals.items()) }\n)\n\n(board_globals, board_globals_base) = rblf.board_configuration(board_init, board_input_vars_init)\nassert_eq({\"A_LIST_VARIABLE\": [\"foo\", \"bar\"]}, board_globals)\nassert_eq({\"A_LIST_VARIABLE\": [\"foo\"]}, board_globals_base)\n\ng = {\"FOO\": \"a\", \"BAR\": \"c\", \"BAZ\": \"e\"}\ncfg = {\"FOO\": \"b\", \"BAR\": \"d\", \"BAZ\": \"f\"}\nrblf.clear_var_list(g, struct(cfg=cfg), \"FOO BAR NEWVAR\")\nassert_eq(\"\", g[\"FOO\"])\nassert_eq(\"\", cfg[\"FOO\"])\nassert_eq(\"\", g[\"BAR\"])\nassert_eq(\"\", cfg[\"BAR\"])\nassert_eq(\"e\", g[\"BAZ\"])\nassert_eq(\"f\", cfg[\"BAZ\"])\nassert_eq(\"\", g.get(\"NEWVAR\"))\n\ntest_single_value_inheritance()\ntest_single_value_inheritance_2()\ntest_artifact_path_requirements()\ntest_prefixed_sort_order()\ntest_inherits_in_regular_variables()\n"
  },
  {
    "path": "tests/single_value_inheritance/inherit1.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  cfg[\"PRODUCT_CHARACTERISTICS\"] = \"tablet\"\n  cfg[\"PRODUCT_DEFAULT_DEV_CERTIFICATE\"] = \"vendor/myvendor/certs/devkeys/devkey\"\n  cfg.setdefault(\"PRODUCT_PACKAGES\", [])\n  cfg[\"PRODUCT_PACKAGES\"] += [\"bar\"]\n"
  },
  {
    "path": "tests/single_value_inheritance/inherit2.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  cfg[\"PRODUCT_CHARACTERISTICS\"] = \"nosdcard\"\n  cfg.setdefault(\"PRODUCT_PACKAGES\", [])\n  cfg[\"PRODUCT_PACKAGES\"] += [\"foo\"]\n"
  },
  {
    "path": "tests/single_value_inheritance/product.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":inherit1.rbc\", _inherit1_init = \"init\")\nload(\":inherit2.rbc\", _inherit2_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n  rblf.inherit(handle, \"test/inherit2\", _inherit2_init)\n  rblf.inherit(handle, \"test/inherit1\", _inherit1_init)\n\n  cfg[\"PRODUCT_DEFAULT_DEV_CERTIFICATE\"] = \"\"\n"
  },
  {
    "path": "tests/single_value_inheritance/test.rbc",
    "content": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\"//build/make/tests/input_variables.rbc\", input_variables_init = \"init\")\nload(\":product.rbc\", \"init\")\n\n\ndef assert_eq(expected, actual):\n    if expected != actual:\n        fail(\"Expected '%s', got '%s'\" % (expected, actual))\n\ndef test():\n    (globals, globals_base) = rblf.product_configuration(\"test/device\", init, input_variables_init)\n    assert_eq(\"tablet\", globals[\"PRODUCTS.test/device.mk.PRODUCT_CHARACTERISTICS\"])\n    assert_eq(\"vendor/myvendor/certs/devkeys/devkey\", globals[\"PRODUCTS.test/device.mk.PRODUCT_DEFAULT_DEV_CERTIFICATE\"])\n    assert_eq([\"foo\", \"bar\"], globals[\"PRODUCTS.test/device.mk.PRODUCT_PACKAGES\"])\n"
  },
  {
    "path": "tests/single_value_inheritance_2/a.rbc",
    "content": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  cfg[\"PRODUCT_ENABLE_UFFD_GC\"] = \"true\"\n"
  },
  {
    "path": "tests/single_value_inheritance_2/b.rbc",
    "content": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  cfg[\"PRODUCT_ENABLE_UFFD_GC\"] = \"default\"\n"
  },
  {
    "path": "tests/single_value_inheritance_2/c.rbc",
    "content": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":b.rbc\", _b_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  rblf.inherit(handle, \"test/b\", _b_init)\n"
  },
  {
    "path": "tests/single_value_inheritance_2/d.rbc",
    "content": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":c.rbc\", _c_init = \"init\")\nload(\":a.rbc\", _a_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  rblf.inherit(handle, \"test/a\", _a_init)\n  rblf.inherit(handle, \"test/c\", _c_init)\n"
  },
  {
    "path": "tests/single_value_inheritance_2/product.rbc",
    "content": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\":b.rbc\", _b_init = \"init\")\nload(\":d.rbc\", _d_init = \"init\")\n\ndef init(g, handle):\n  cfg = rblf.cfg(handle)\n\n  rblf.inherit(handle, \"test/b\", _b_init)\n  rblf.inherit(handle, \"test/d\", _d_init)\n"
  },
  {
    "path": "tests/single_value_inheritance_2/test.rbc",
    "content": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"//build/make/core:product_config.rbc\", \"rblf\")\nload(\"//build/make/tests/input_variables.rbc\", input_variables_init = \"init\")\nload(\":product.rbc\", \"init\")\n\n\ndef assert_eq(expected, actual):\n    if expected != actual:\n        fail(\"Expected '%s', got '%s'\" % (expected, actual))\n\n# This test is testing that single value variables are \"stolen\" when processing the inheritance\n# graph. i.e. if you have a graph like this:\n#\n#   B   A\n#   |\\  |\n#   | C |\n#    \\ \\|\n#     \\ D\n#      \\|\n#       E\n#\n# The same variable is defined in both A and B. In D, the value from A is chosen because it comes\n# alphabetically before C. But then in E, the value from D is chosen instead of the value from B,\n# because the value of B was \"stolen\" and sucked into C, leaving B with no value set.\ndef test():\n    (globals, globals_base) = rblf.product_configuration(\"test/device\", init, input_variables_init)\n    assert_eq(\"true\", globals[\"PRODUCTS.test/device.mk.PRODUCT_ENABLE_UFFD_GC\"])\n"
  },
  {
    "path": "tests/version_defaults.rbc",
    "content": "version_defaults = struct(\n    codenames = { \"SP1A\" : \"S\" },\n    default_platform_version = \"SP1A\",\n    max_platform_version = \"SP1A\",\n    min_platform_version = \"SP1A\",\n    platform_base_sdk_extension_version = 0,\n    platform_sdk_extension_version = 1,\n    platform_sdk_version = 30,\n    platform_security_patch = \"2021-08-05\",\n    platform_version_last_stable = 11,\n)\n"
  },
  {
    "path": "tools/Android.bp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\npython_binary_host {\n    name: \"generate-self-extracting-archive\",\n    srcs: [\"generate-self-extracting-archive.py\"],\n}\n\npython_binary_host {\n    name: \"post_process_props\",\n    srcs: [\"post_process_props.py\"],\n    libs: [\n        \"uffd_gc_utils\",\n    ],\n}\n\npython_test_host {\n    name: \"post_process_props_unittest\",\n    main: \"test_post_process_props.py\",\n    srcs: [\n        \"post_process_props.py\",\n        \"test_post_process_props.py\",\n    ],\n    libs: [\n        \"uffd_gc_utils\",\n    ],\n    test_config: \"post_process_props_unittest.xml\",\n    test_suites: [\"general-tests\"],\n}\n\npython_binary_host {\n    name: \"extract_kernel\",\n    srcs: [\"extract_kernel.py\"],\n}\n\ngenrule_defaults {\n    name: \"extract_kernel_release_defaults\",\n    tools: [\n        \"extract_kernel\",\n        \"lz4\",\n    ],\n    out: [\"kernel_release.txt\"],\n    cmd: \"$(location) --tools lz4:$(location lz4) --input $(in) --output-release > $(out)\",\n}\n\ncc_binary_host {\n    name: \"build-runfiles\",\n    srcs: [\"build-runfiles.cc\"],\n}\n\npython_binary_host {\n    name: \"check_radio_versions\",\n    srcs: [\"check_radio_versions.py\"],\n}\n\npython_binary_host {\n    name: \"check_elf_file\",\n    srcs: [\"check_elf_file.py\"],\n}\n\npython_binary_host {\n    name: \"generate_gts_shared_report\",\n    srcs: [\"generate_gts_shared_report.py\"],\n}\n\npython_binary_host {\n    name: \"list_files\",\n    main: \"list_files.py\",\n    srcs: [\n        \"list_files.py\",\n    ],\n}\n\npython_test_host {\n    name: \"auto_gen_test_config_test\",\n    main: \"auto_gen_test_config_test.py\",\n    srcs: [\n        \"auto_gen_test_config.py\",\n        \"auto_gen_test_config_test.py\",\n    ],\n    auto_gen_config: true,\n    test_suites: [\"general-tests\"],\n    test_options: {\n        unit_test: true,\n    },\n}\n\npython_binary_host {\n    name: \"characteristics_rro_generator\",\n    srcs: [\"characteristics_rro_generator.py\"],\n}\n\npython_binary_host {\n    name: \"merge-event-log-tags\",\n    srcs: [\n        \"event_log_tags.py\",\n        \"merge-event-log-tags.py\",\n    ],\n}\n\npython_binary_host {\n    name: \"java-event-log-tags\",\n    srcs: [\n        \"event_log_tags.py\",\n        \"java-event-log-tags.py\",\n    ],\n}\n"
  },
  {
    "path": "tools/aconfig/.editorconfig",
    "content": "# EditorConfig is awesome: https://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n[*.java]\nindent_style = tab\nindent_size = 4\n\n"
  },
  {
    "path": "tools/aconfig/.gitignore",
    "content": "/Cargo.lock\n/target\n"
  },
  {
    "path": "tools/aconfig/Cargo.toml",
    "content": "[workspace]\n\nmembers = [\n    \"aconfig\",\n    \"aconfig_device_paths\",\n    \"aconfig_protos\",\n    \"aconfig_storage_file\",\n    \"aconfig_storage_read_api\",\n    \"aconfig_storage_write_api\",\n    \"aflags\",\n    \"convert_finalized_flags\",\n    \"exported_flag_check\",\n]\n\nresolver = \"2\"\n"
  },
  {
    "path": "tools/aconfig/MODULE_LICENSE_APACHE2",
    "content": ""
  },
  {
    "path": "tools/aconfig/OWNERS",
    "content": "dzshen@google.com\nopg@google.com\nzhidou@google.com\n\namhk@google.com  #{LAST_RESORT_SUGGESTION}\njham@google.com  #{LAST_RESORT_SUGGESTION}\njoeo@google.com  #{LAST_RESORT_SUGGESTION}\n"
  },
  {
    "path": "tools/aconfig/PREUPLOAD.cfg",
    "content": "[Builtin Hooks]\nrustfmt = true\n\n[Builtin Hooks Options]\nrustfmt = --config-path=rustfmt.toml\n"
  },
  {
    "path": "tools/aconfig/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      // aconfig unit tests\n      \"name\": \"aconfig.test\"\n    },\n    {\n      // aconfig Java integration tests (host)\n      \"name\": \"AconfigJavaHostTest\"\n    },\n    {\n      // aconfig Java integration tests\n      \"name\": \"aconfig.test.java\"\n    },\n    {\n      // aconfig C++ integration tests (production mode auto-generated code)\n      \"name\": \"aconfig.test.cpp\"\n    },\n    {\n      // aconfig C++ integration tests (test mode auto-generated code)\n      \"name\": \"aconfig.test.cpp.test_mode\"\n    },\n    // TODO(b/327420679): Enable export mode for native flag library\n    // {\n    //   // aconfig C++ integration tests (exported mode auto-generated code)\n    //   \"name\": \"aconfig.test.cpp.exported_mode\"\n    // },\n    {\n      // aconfig Rust integration tests (production mode auto-generated code)\n      \"name\": \"aconfig.prod_mode.test.rust\"\n    },\n    {\n      // aconfig Rust integration tests (test mode auto-generated code)\n      \"name\": \"aconfig.test_mode.test.rust\"\n    },\n    // TODO(b/327420679): Enable export mode for native flag library\n    // {\n    //   // aconfig Rust integration tests (exported mode auto-generated code)\n    //   \"name\": \"aconfig.exported_mode.test.rust\"\n    // },\n    {\n      // aflags CLI unit tests\n      \"name\": \"aflags.test\"\n    },\n    {\n      // aconfig_protos unit tests\n      \"name\": \"aconfig_protos.test\"\n    },\n    {\n      // aconfig_storage_file unit tests\n      \"name\": \"aconfig_storage_file.test\"\n    },\n    {\n      // Ensure changes on aconfig auto generated library is compatible with\n      // test testing filtering logic. Breakage on this test means all tests\n      // that using the flag annotations to do filtering will get affected.\n      \"name\": \"FlagAnnotationTests\",\n      \"options\": [\n        {\n          \"include-filter\": \"android.cts.flags.tests.FlagAnnotationTest\"\n        }\n      ]\n    },\n    {\n      // Ensure changes on aconfig auto generated library is compatible with\n      // test testing filtering logic. Breakage on this test means all tests\n      // that using the flag macros to do filtering will get affected.\n      \"name\": \"FlagMacrosTests\"\n    },\n    {\n      // aconfig_storage_write_api unit tests\n      \"name\": \"aconfig_storage_write_api.test\"\n    },\n    {\n      // aconfig_storage_read_api unit tests\n      \"name\": \"aconfig_storage_read_api.test\"\n    },\n    {\n      // aconfig_storage write api rust integration tests\n      \"name\": \"aconfig_storage_write_api.test.rust\"\n    },\n    {\n      // aconfig_storage write api cpp integration tests\n      \"name\": \"aconfig_storage_write_api.test.cpp\"\n    },\n    {\n      // aconfig_storage read api rust integration tests\n      \"name\": \"aconfig_storage_read_api.test.rust\"\n    },\n    {\n      // aconfig_storage read api cpp integration tests\n      \"name\": \"aconfig_storage_read_api.test.cpp\"\n    },\n    {\n      // aconfig_storage file cpp integration tests\n      \"name\": \"aconfig_storage_file.test.cpp\"\n    },\n    {\n      // aconfig_storage file java integration tests\n      \"name\": \"aconfig_storage_file.test.java\"\n    },\n    {\n      // aconfig_storage read functional test\n      \"name\": \"aconfig_storage_read_functional\"\n    }\n  ]\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_defaults {\n    name: \"aconfig.defaults\",\n    edition: \"2021\",\n    clippy_lints: \"android\",\n    lints: \"android\",\n    srcs: [\n        \"src/main.rs\",\n        \":finalized_flags_record.json\",\n    ],\n    rustlibs: [\n        \"libaconfig_protos\",\n        \"libaconfig_storage_file\",\n        \"libanyhow\",\n        \"libclap\",\n        \"libitertools\",\n        \"libprotobuf\",\n        \"libserde\",\n        \"libserde_json\",\n        \"libtinytemplate\",\n        \"libconvert_finalized_flags\",\n    ],\n    cfgs: select(release_flag(\"RELEASE_ACONFIG_FINGERPRINT_RUST\"), {\n        true: [\"enable_fingerprint_rust\"],\n        default: [],\n    }),\n}\n\nrust_binary_host {\n    name: \"aconfig\",\n    defaults: [\"aconfig.defaults\"],\n}\n\nrust_test_host {\n    name: \"aconfig.test\",\n    defaults: [\"aconfig.defaults\"],\n    rustlibs: [\n        \"libitertools\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\n// integration tests: general\n\naconfig_declarations {\n    name: \"aconfig.test.flags\",\n    package: \"com.android.aconfig.test\",\n    container: \"system\",\n    srcs: [\"tests/test.aconfig\"],\n}\n\naconfig_declarations {\n    name: \"aconfig.test.exported.flags\",\n    package: \"com.android.aconfig.test.exported\",\n    exportable: true,\n    container: \"system\",\n    srcs: [\"tests/test_exported.aconfig\"],\n}\n\naconfig_declarations {\n    name: \"aconfig.test.forcereadonly.flags\",\n    package: \"com.android.aconfig.test.forcereadonly\",\n    container: \"system\",\n    srcs: [\"tests/test_force_read_only.aconfig\"],\n}\n\naconfig_values {\n    name: \"aconfig.test.flag.values\",\n    package: \"com.android.aconfig.test\",\n    srcs: [\n        \"tests/first.values\",\n        \"tests/second.values\",\n    ],\n}\n\naconfig_values {\n    name: \"aconfig.test.flag.second_values\",\n    package: \"com.android.aconfig.test\",\n    srcs: [\n        \"tests/third.values\",\n    ],\n}\n\naconfig_value_set {\n    name: \"aconfig.test.flag.value_set\",\n    values: [\n        \"aconfig.test.flag.values\",\n    ],\n}\n\n// integration tests: java\n\njava_aconfig_library {\n    name: \"aconfig_test_java_library\",\n    aconfig_declarations: \"aconfig.test.flags\",\n}\n\njava_aconfig_library {\n    name: \"aconfig_test_java_library_exported\",\n    aconfig_declarations: \"aconfig.test.exported.flags\",\n    mode: \"exported\",\n}\n\njava_aconfig_library {\n    name: \"aconfig_test_java_library_forcereadonly\",\n    aconfig_declarations: \"aconfig.test.forcereadonly.flags\",\n    mode: \"force-read-only\",\n}\n\nandroid_test {\n    name: \"aconfig.test.java\",\n    srcs: [\n        \"tests/AconfigTest.java\",\n    ],\n    manifest: \"tests/AndroidManifest.xml\",\n    certificate: \"platform\",\n    static_libs: [\n        \"aconfig_test_java_library\",\n        \"aconfig_test_java_library_exported\",\n        \"aconfig_test_java_library_forcereadonly\",\n        \"androidx.test.rules\",\n        \"testng\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\njava_aconfig_library {\n    name: \"aconfig_host_test_java_library\",\n    aconfig_declarations: \"aconfig.test.flags\",\n    host_supported: true,\n    mode: \"test\",\n}\n\njava_test_host {\n    name: \"AconfigJavaHostTest\",\n    srcs: [\n        \"tests/AconfigHostTest.java\",\n    ],\n    static_libs: [\n        \"aconfig_host_test_java_library\",\n        \"junit\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\n// integration tests: C++\n\ncc_aconfig_library {\n    name: \"aconfig_test_cpp_library\",\n    aconfig_declarations: \"aconfig.test.flags\",\n}\n\ncc_aconfig_library {\n    name: \"aconfig_test_cpp_library_test_variant\",\n    aconfig_declarations: \"aconfig.test.flags\",\n    mode: \"test\",\n}\n\ncc_aconfig_library {\n    name: \"aconfig_test_cpp_library_force_read_only_variant\",\n    aconfig_declarations: \"aconfig.test.flags\",\n    mode: \"force-read-only\",\n}\n\ncc_test {\n    name: \"aconfig.test.cpp\",\n    srcs: [\n        \"tests/aconfig_test.cpp\",\n    ],\n    static_libs: [\n        \"aconfig_test_cpp_library\",\n        \"libgmock\",\n    ],\n    shared_libs: [\n        \"server_configurable_flags\",\n    ],\n    defaults: [\n        \"aconfig_lib_cc_static_link.defaults\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\ncc_test {\n    name: \"aconfig.test.cpp.test_mode\",\n    srcs: [\n        \"tests/aconfig_test_test_variant.cpp\",\n    ],\n    static_libs: [\n        \"aconfig_test_cpp_library_test_variant\",\n        \"libgmock\",\n    ],\n    shared_libs: [\n        \"server_configurable_flags\",\n    ],\n    defaults: [\n        \"aconfig_lib_cc_static_link.defaults\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\n// TODO(327420679): Enable export mode for native flag library\n/*\ncc_aconfig_library {\n    name: \"aconfig_test_cpp_library_exported_variant\",\n    aconfig_declarations: \"aconfig.test.flags\",\n    mode: \"exported\",\n}\n\ncc_test {\n    name: \"aconfig.test.cpp.exported_mode\",\n    srcs: [\n        \"tests/aconfig_exported_mode_test.cpp\",\n    ],\n    static_libs: [\n        \"aconfig_test_cpp_library_exported_variant\",\n        \"libgmock\",\n    ],\n    shared_libs: [\n        \"server_configurable_flags\",\n    ],\n    defaults: [\n        \"aconfig_lib_cc_static_link.defaults\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n*/\n\ncc_test {\n    name: \"aconfig.test.cpp.force_read_only_mode\",\n    srcs: [\n        \"tests/aconfig_force_read_only_mode_test.cpp\",\n    ],\n    static_libs: [\n        \"aconfig_test_cpp_library_force_read_only_variant\",\n        \"libgmock\",\n    ],\n    shared_libs: [\n        \"server_configurable_flags\",\n    ],\n    defaults: [\n        \"aconfig_lib_cc_static_link.defaults\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\nrust_aconfig_library {\n    name: \"libaconfig_test_rust_library\",\n    crate_name: \"aconfig_test_rust_library\",\n    aconfig_declarations: \"aconfig.test.flags\",\n    host_supported: true,\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.configinfrastructure\",\n    ],\n    min_sdk_version: \"34\",\n}\n\nrust_test {\n    name: \"aconfig.prod_mode.test.rust\",\n    srcs: [\n        \"tests/aconfig_prod_mode_test.rs\",\n    ],\n    rustlibs: [\n        \"libaconfig_test_rust_library\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\nrust_aconfig_library {\n    name: \"libaconfig_test_rust_library_with_test_mode\",\n    crate_name: \"aconfig_test_rust_library\",\n    aconfig_declarations: \"aconfig.test.flags\",\n    mode: \"test\",\n}\n\nrust_test {\n    name: \"aconfig.test_mode.test.rust\",\n    srcs: [\n        \"tests/aconfig_test_mode_test.rs\",\n    ],\n    rustlibs: [\n        \"libaconfig_test_rust_library_with_test_mode\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\n// TODO(327420679): Enable export mode for native flag library\n/*\nrust_aconfig_library {\n    name: \"libaconfig_test_rust_library_with_exported_mode\",\n    crate_name: \"aconfig_test_rust_library\",\n    aconfig_declarations: \"aconfig.test.flags\",\n    mode: \"exported\",\n}\n\nrust_test {\n    name: \"aconfig.exported_mode.test.rust\",\n    srcs: [\n        \"tests/aconfig_exported_mode_test.rs\",\n    ],\n    rustlibs: [\n        \"libaconfig_test_rust_library_with_exported_mode\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n*/\n\nrust_aconfig_library {\n    name: \"libaconfig_test_rust_library_with_force_read_only_mode\",\n    crate_name: \"aconfig_test_rust_library\",\n    aconfig_declarations: \"aconfig.test.flags\",\n    mode: \"force-read-only\",\n}\n\nrust_test {\n    name: \"aconfig.force_read_only_mode.test.rust\",\n    srcs: [\n        \"tests/aconfig_force_read_only_mode_test.rs\",\n    ],\n    rustlibs: [\n        \"libaconfig_test_rust_library_with_force_read_only_mode\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/Cargo.toml",
    "content": "[package]\nname = \"aconfig\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[features]\ndefault = [\"cargo\"]\ncargo = []\n\n[dependencies]\nanyhow = \"1.0.69\"\nclap = { version = \"4.1.8\", features = [\"derive\"] }\nitertools = \"0.10.5\"\nprotobuf = \"3.2.0\"\nserde = { version = \"1.0.152\", features = [\"derive\"] }\nserde_json = \"1.0.93\"\ntinytemplate = \"1.2.1\"\naconfig_protos = { path = \"../aconfig_protos\" }\naconfig_storage_file = { path = \"../aconfig_storage_file\" }\nconvert_finalized_flags = { path = \"../convert_finalized_flags\" }\n\n[build-dependencies]\nanyhow = \"1.0.69\"\nitertools = \"0.10.5\"\nserde = { version = \"1.0.152\", features = [\"derive\"] }\nserde_json = \"1.0.93\"\nconvert_finalized_flags = { path = \"../convert_finalized_flags\" }\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(enable_fingerprint_rust)'] }\n"
  },
  {
    "path": "tools/aconfig/aconfig/build.rs",
    "content": "use anyhow::{anyhow, Result};\nuse std::env;\nuse std::fs;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::{Path, PathBuf};\n\nuse convert_finalized_flags::read_files_to_map_using_path;\nuse convert_finalized_flags::FinalizedFlagMap;\n\n// This fn makes assumptions about the working directory which we should not rely\n// on for actual (Soong) builds. It is reasonable to assume that this is being\n// called from the aconfig directory as cargo is used for local development and\n// the cargo workspace for our project is build/make/tools/aconfig.\n// This is meant to get the list of finalized flag\n// files provided by the filegroup + \"locations\" in soong.\n// Cargo-only usage is asserted via implementation of\n// read_files_to_map_using_env, the only public cargo-only fn.\nfn read_files_to_map_using_env() -> Result<FinalizedFlagMap> {\n    let mut current_dir = std::env::current_dir()?;\n\n    // Path of aconfig from the top of tree.\n    let aconfig_path = PathBuf::from(\"build/make/tools/aconfig\");\n\n    // Path of SDK files from the top of tree.\n    let sdk_dir_path = PathBuf::from(\"prebuilts/sdk\");\n\n    // Iterate up the directory structure until we have the base aconfig dir.\n    while !current_dir.canonicalize()?.ends_with(&aconfig_path) {\n        if let Some(parent) = current_dir.parent() {\n            current_dir = parent.to_path_buf();\n        } else {\n            return Err(anyhow!(\"Cannot execute outside of aconfig.\"));\n        }\n    }\n\n    // Remove the aconfig path, leaving the top of the tree.\n    for _ in 0..aconfig_path.components().count() {\n        current_dir.pop();\n    }\n\n    // Get the absolute path of the sdk files.\n    current_dir.push(sdk_dir_path);\n\n    let mut flag_files = Vec::new();\n\n    // Search all sub-dirs in prebuilts/sdk for finalized-flags.txt files.\n    // The files are in prebuilts/sdk/<api level>/finalized-flags.txt.\n    let api_level_dirs = fs::read_dir(current_dir)?;\n    for api_level_dir in api_level_dirs {\n        if api_level_dir.is_err() {\n            eprintln!(\"Error opening directory: {}\", api_level_dir.err().unwrap());\n            continue;\n        }\n\n        // Skip non-directories.\n        let api_level_dir_path = api_level_dir.unwrap().path();\n        if !api_level_dir_path.is_dir() {\n            continue;\n        }\n\n        // Some directories were created before trunk stable and don't have\n        // flags, or aren't api level directories at all.\n        let flag_file_path = api_level_dir_path.join(\"finalized-flags.txt\");\n        if !flag_file_path.exists() {\n            continue;\n        }\n\n        if let Some(path) = flag_file_path.to_str() {\n            flag_files.push(path.to_string());\n        } else {\n            eprintln!(\"Error converting path to string: {:?}\", flag_file_path);\n        }\n    }\n\n    read_files_to_map_using_path(flag_files)\n}\n\nfn main() {\n    let out_dir = env::var_os(\"OUT_DIR\").unwrap();\n    let dest_path = Path::new(&out_dir).join(\"finalized_flags_record.json\");\n\n    let finalized_flags_map: Result<FinalizedFlagMap> = read_files_to_map_using_env();\n    if finalized_flags_map.is_err() {\n        return;\n    }\n    let json_str = serde_json::to_string(&finalized_flags_map.unwrap()).unwrap();\n\n    let mut f = File::create(&dest_path).unwrap();\n    f.write_all(json_str.as_bytes()).unwrap();\n\n    //println!(\"cargo:rerun-if-changed=input.txt\");\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/codegen/cpp.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse anyhow::{ensure, Result};\nuse serde::Serialize;\nuse std::collections::HashMap;\nuse std::path::PathBuf;\nuse tinytemplate::TinyTemplate;\n\nuse aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};\n\nuse crate::codegen;\nuse crate::codegen::CodegenMode;\nuse crate::commands::{should_include_flag, OutputFile};\n\npub fn generate_cpp_code<I>(\n    package: &str,\n    parsed_flags_iter: I,\n    codegen_mode: CodegenMode,\n    flag_ids: HashMap<String, u16>,\n) -> Result<Vec<OutputFile>>\nwhere\n    I: Iterator<Item = ProtoParsedFlag>,\n{\n    let mut readwrite_count = 0;\n    let class_elements: Vec<ClassElement> = parsed_flags_iter\n        .map(|pf| create_class_element(package, &pf, flag_ids.clone(), &mut readwrite_count))\n        .collect();\n    let readwrite = readwrite_count > 0;\n    let has_fixed_read_only = class_elements.iter().any(|item| item.is_fixed_read_only);\n    let header = package.replace('.', \"_\");\n    let package_macro = header.to_uppercase();\n    let cpp_namespace = package.replace('.', \"::\");\n    ensure!(class_elements.len() > 0);\n    let container = class_elements[0].container.clone();\n    ensure!(codegen::is_valid_name_ident(&header));\n    let context = Context {\n        header: &header,\n        package_macro: &package_macro,\n        cpp_namespace: &cpp_namespace,\n        package,\n        has_fixed_read_only,\n        readwrite,\n        readwrite_count,\n        is_test_mode: codegen_mode == CodegenMode::Test,\n        class_elements,\n        container,\n    };\n\n    let files = [\n        FileSpec {\n            name: &format!(\"{}.h\", header),\n            template: include_str!(\"../../templates/cpp_exported_header.template\"),\n            dir: \"include\",\n        },\n        FileSpec {\n            name: &format!(\"{}.cc\", header),\n            template: include_str!(\"../../templates/cpp_source_file.template\"),\n            dir: \"\",\n        },\n    ];\n    files.iter().map(|file| generate_file(file, &context)).collect()\n}\n\npub fn generate_file(file: &FileSpec, context: &Context) -> Result<OutputFile> {\n    let mut template = TinyTemplate::new();\n    template.add_template(file.name, file.template)?;\n    let contents = template.render(file.name, &context)?;\n    let path: PathBuf = [&file.dir, &file.name].iter().collect();\n    Ok(OutputFile { contents: contents.into(), path })\n}\n\n#[derive(Serialize)]\npub struct FileSpec<'a> {\n    pub name: &'a str,\n    pub template: &'a str,\n    pub dir: &'a str,\n}\n\n#[derive(Serialize)]\npub struct Context<'a> {\n    pub header: &'a str,\n    pub package_macro: &'a str,\n    pub cpp_namespace: &'a str,\n    pub package: &'a str,\n    pub has_fixed_read_only: bool,\n    pub readwrite: bool,\n    pub readwrite_count: i32,\n    pub is_test_mode: bool,\n    pub class_elements: Vec<ClassElement>,\n    pub container: String,\n}\n\n#[derive(Serialize)]\npub struct ClassElement {\n    pub readwrite_idx: i32,\n    pub readwrite: bool,\n    pub is_fixed_read_only: bool,\n    pub default_value: String,\n    pub flag_name: String,\n    pub flag_macro: String,\n    pub flag_offset: u16,\n    pub device_config_namespace: String,\n    pub device_config_flag: String,\n    pub container: String,\n}\n\nfn create_class_element(\n    package: &str,\n    pf: &ProtoParsedFlag,\n    flag_ids: HashMap<String, u16>,\n    rw_count: &mut i32,\n) -> ClassElement {\n    let no_assigned_offset = !should_include_flag(pf);\n\n    let flag_offset = match flag_ids.get(pf.name()) {\n        Some(offset) => offset,\n        None => {\n            // System/vendor/product RO+disabled flags have no offset in storage files.\n            // Assign placeholder value.\n            if no_assigned_offset {\n                &0\n            }\n            // All other flags _must_ have an offset.\n            else {\n                panic!(\"{}\", format!(\"missing flag offset for {}\", pf.name()));\n            }\n        }\n    };\n\n    ClassElement {\n        readwrite_idx: if pf.permission() == ProtoFlagPermission::READ_WRITE {\n            let index = *rw_count;\n            *rw_count += 1;\n            index\n        } else {\n            -1\n        },\n        readwrite: pf.permission() == ProtoFlagPermission::READ_WRITE,\n        is_fixed_read_only: pf.is_fixed_read_only(),\n        default_value: if pf.state() == ProtoFlagState::ENABLED {\n            \"true\".to_string()\n        } else {\n            \"false\".to_string()\n        },\n        flag_name: pf.name().to_string(),\n        flag_macro: pf.name().to_uppercase(),\n        flag_offset: *flag_offset,\n        device_config_namespace: pf.namespace().to_string(),\n        device_config_flag: codegen::create_device_config_ident(package, pf.name())\n            .expect(\"values checked at flag parse time\"),\n        container: pf.container().to_string(),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use aconfig_protos::ProtoParsedFlags;\n    use std::collections::HashMap;\n\n    const EXPORTED_PROD_HEADER_EXPECTED: &str = r#\"\n#pragma once\n\n#ifndef COM_ANDROID_ACONFIG_TEST\n#define COM_ANDROID_ACONFIG_TEST(FLAG) COM_ANDROID_ACONFIG_TEST_##FLAG\n#endif\n\n#ifndef COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO\n#define COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO true\n#endif\n\n#ifndef COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO_EXPORTED\n#define COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO_EXPORTED true\n#endif\n\n#ifdef __cplusplus\n\n#include <memory>\n\nnamespace com::android::aconfig::test {\n\nclass flag_provider_interface {\npublic:\n    virtual ~flag_provider_interface() = default;\n\n    virtual bool disabled_ro() = 0;\n\n    virtual bool disabled_rw() = 0;\n\n    virtual bool disabled_rw_exported() = 0;\n\n    virtual bool disabled_rw_in_other_namespace() = 0;\n\n    virtual bool enabled_fixed_ro() = 0;\n\n    virtual bool enabled_fixed_ro_exported() = 0;\n\n    virtual bool enabled_ro() = 0;\n\n    virtual bool enabled_ro_exported() = 0;\n\n    virtual bool enabled_rw() = 0;\n};\n\nextern std::unique_ptr<flag_provider_interface> provider_;\n\ninline bool disabled_ro() {\n    return false;\n}\n\ninline bool disabled_rw() {\n    return provider_->disabled_rw();\n}\n\ninline bool disabled_rw_exported() {\n    return provider_->disabled_rw_exported();\n}\n\ninline bool disabled_rw_in_other_namespace() {\n    return provider_->disabled_rw_in_other_namespace();\n}\n\nconstexpr inline bool enabled_fixed_ro() {\n    return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO;\n}\n\nconstexpr inline bool enabled_fixed_ro_exported() {\n    return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO_EXPORTED;\n}\n\ninline bool enabled_ro() {\n    return true;\n}\n\ninline bool enabled_ro_exported() {\n    return true;\n}\n\ninline bool enabled_rw() {\n    return provider_->enabled_rw();\n}\n\n}\n\nextern \"C\" {\n#endif // __cplusplus\n\nbool com_android_aconfig_test_disabled_ro();\n\nbool com_android_aconfig_test_disabled_rw();\n\nbool com_android_aconfig_test_disabled_rw_exported();\n\nbool com_android_aconfig_test_disabled_rw_in_other_namespace();\n\nbool com_android_aconfig_test_enabled_fixed_ro();\n\nbool com_android_aconfig_test_enabled_fixed_ro_exported();\n\nbool com_android_aconfig_test_enabled_ro();\n\nbool com_android_aconfig_test_enabled_ro_exported();\n\nbool com_android_aconfig_test_enabled_rw();\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\"#;\n\n    const EXPORTED_TEST_HEADER_EXPECTED: &str = r#\"\n#pragma once\n\n#ifdef __cplusplus\n\n#include <memory>\n\nnamespace com::android::aconfig::test {\n\nclass flag_provider_interface {\npublic:\n\n    virtual ~flag_provider_interface() = default;\n\n    virtual bool disabled_ro() = 0;\n    virtual bool disabled_rw() = 0;\n    virtual bool disabled_rw_exported() = 0;\n    virtual bool disabled_rw_in_other_namespace() = 0;\n    virtual bool enabled_fixed_ro() = 0;\n    virtual bool enabled_fixed_ro_exported() = 0;\n    virtual bool enabled_ro() = 0;\n    virtual bool enabled_ro_exported() = 0;\n    virtual bool enabled_rw() = 0;\n\n    virtual void disabled_ro(bool val) = 0;\n    virtual void disabled_rw(bool val) = 0;\n    virtual void disabled_rw_exported(bool val) = 0;\n    virtual void disabled_rw_in_other_namespace(bool val) = 0;\n    virtual void enabled_fixed_ro(bool val) = 0;\n    virtual void enabled_fixed_ro_exported(bool val) = 0;\n    virtual void enabled_ro(bool val) = 0;\n    virtual void enabled_ro_exported(bool val) = 0;\n    virtual void enabled_rw(bool val) = 0;\n\n    virtual void reset_flags() {}\n};\n\nextern std::unique_ptr<flag_provider_interface> provider_;\n\ninline bool disabled_ro() {\n    return provider_->disabled_ro();\n}\n\ninline void disabled_ro(bool val) {\n    provider_->disabled_ro(val);\n}\n\ninline bool disabled_rw() {\n    return provider_->disabled_rw();\n}\n\ninline void disabled_rw(bool val) {\n    provider_->disabled_rw(val);\n}\n\ninline bool disabled_rw_exported() {\n    return provider_->disabled_rw_exported();\n}\n\ninline void disabled_rw_exported(bool val) {\n    provider_->disabled_rw_exported(val);\n}\n\ninline bool disabled_rw_in_other_namespace() {\n    return provider_->disabled_rw_in_other_namespace();\n}\n\ninline void disabled_rw_in_other_namespace(bool val) {\n    provider_->disabled_rw_in_other_namespace(val);\n}\n\ninline bool enabled_fixed_ro() {\n    return provider_->enabled_fixed_ro();\n}\n\ninline void enabled_fixed_ro(bool val) {\n    provider_->enabled_fixed_ro(val);\n}\n\ninline bool enabled_fixed_ro_exported() {\n    return provider_->enabled_fixed_ro_exported();\n}\n\ninline void enabled_fixed_ro_exported(bool val) {\n    provider_->enabled_fixed_ro_exported(val);\n}\n\ninline bool enabled_ro() {\n    return provider_->enabled_ro();\n}\n\ninline void enabled_ro(bool val) {\n    provider_->enabled_ro(val);\n}\n\ninline bool enabled_ro_exported() {\n    return provider_->enabled_ro_exported();\n}\n\ninline void enabled_ro_exported(bool val) {\n    provider_->enabled_ro_exported(val);\n}\n\ninline bool enabled_rw() {\n    return provider_->enabled_rw();\n}\n\ninline void enabled_rw(bool val) {\n    provider_->enabled_rw(val);\n}\n\ninline void reset_flags() {\n    return provider_->reset_flags();\n}\n\n}\n\nextern \"C\" {\n#endif // __cplusplus\n\nbool com_android_aconfig_test_disabled_ro();\n\nvoid set_com_android_aconfig_test_disabled_ro(bool val);\n\nbool com_android_aconfig_test_disabled_rw();\n\nvoid set_com_android_aconfig_test_disabled_rw(bool val);\n\nbool com_android_aconfig_test_disabled_rw_exported();\n\nvoid set_com_android_aconfig_test_disabled_rw_exported(bool val);\n\nbool com_android_aconfig_test_disabled_rw_in_other_namespace();\n\nvoid set_com_android_aconfig_test_disabled_rw_in_other_namespace(bool val);\n\nbool com_android_aconfig_test_enabled_fixed_ro();\n\nvoid set_com_android_aconfig_test_enabled_fixed_ro(bool val);\n\nbool com_android_aconfig_test_enabled_fixed_ro_exported();\n\nvoid set_com_android_aconfig_test_enabled_fixed_ro_exported(bool val);\n\nbool com_android_aconfig_test_enabled_ro();\n\nvoid set_com_android_aconfig_test_enabled_ro(bool val);\n\nbool com_android_aconfig_test_enabled_ro_exported();\n\nvoid set_com_android_aconfig_test_enabled_ro_exported(bool val);\n\nbool com_android_aconfig_test_enabled_rw();\n\nvoid set_com_android_aconfig_test_enabled_rw(bool val);\n\nvoid com_android_aconfig_test_reset_flags();\n\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n\n\"#;\n\n    const EXPORTED_FORCE_READ_ONLY_HEADER_EXPECTED: &str = r#\"\n#pragma once\n\n#ifndef COM_ANDROID_ACONFIG_TEST\n#define COM_ANDROID_ACONFIG_TEST(FLAG) COM_ANDROID_ACONFIG_TEST_##FLAG\n#endif\n\n#ifndef COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO\n#define COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO true\n#endif\n\n#ifdef __cplusplus\n\n#include <memory>\n\nnamespace com::android::aconfig::test {\n\nclass flag_provider_interface {\npublic:\n    virtual ~flag_provider_interface() = default;\n\n    virtual bool disabled_ro() = 0;\n\n    virtual bool disabled_rw() = 0;\n\n    virtual bool disabled_rw_in_other_namespace() = 0;\n\n    virtual bool enabled_fixed_ro() = 0;\n\n    virtual bool enabled_ro() = 0;\n\n    virtual bool enabled_rw() = 0;\n};\n\nextern std::unique_ptr<flag_provider_interface> provider_;\n\ninline bool disabled_ro() {\n    return false;\n}\n\ninline bool disabled_rw() {\n    return false;\n}\n\ninline bool disabled_rw_in_other_namespace() {\n    return false;\n}\n\nconstexpr inline bool enabled_fixed_ro() {\n    return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO;\n}\n\ninline bool enabled_ro() {\n    return true;\n}\n\ninline bool enabled_rw() {\n    return true;\n}\n\n}\n\nextern \"C\" {\n#endif // __cplusplus\n\nbool com_android_aconfig_test_disabled_ro();\n\nbool com_android_aconfig_test_disabled_rw();\n\nbool com_android_aconfig_test_disabled_rw_in_other_namespace();\n\nbool com_android_aconfig_test_enabled_fixed_ro();\n\nbool com_android_aconfig_test_enabled_ro();\n\nbool com_android_aconfig_test_enabled_rw();\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\"#;\n\n    const PROD_SOURCE_FILE_EXPECTED: &str = r#\"\n#include \"com_android_aconfig_test.h\"\n\n#include <unistd.h>\n#include \"aconfig_storage/aconfig_storage_read_api.hpp\"\n#include <android/log.h>\n#define LOG_TAG \"aconfig_cpp_codegen\"\n#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)\n\n#include <vector>\n\nnamespace com::android::aconfig::test {\n\n    class flag_provider : public flag_provider_interface {\n        public:\n\n            flag_provider()\n                : cache_(4, -1)\n                , boolean_start_index_()\n                , flag_value_file_(nullptr)\n                , package_exists_in_storage_(true) {\n\n                auto package_map_file = aconfig_storage::get_mapped_file(\n                    \"system\",\n                    aconfig_storage::StorageFileType::package_map);\n                if (!package_map_file.ok()) {\n                    ALOGE(\"error: failed to get package map file: %s\", package_map_file.error().c_str());\n                    package_exists_in_storage_ = false;\n                    return;\n                }\n\n                auto context = aconfig_storage::get_package_read_context(\n                    **package_map_file, \"com.android.aconfig.test\");\n                if (!context.ok()) {\n                    ALOGE(\"error: failed to get package read context: %s\", context.error().c_str());\n                    package_exists_in_storage_ = false;\n                    return;\n                }\n\n                if (!(context->package_exists)) {\n                    package_exists_in_storage_ = false;\n                    return;\n                }\n\n                // cache package boolean flag start index\n                boolean_start_index_ = context->boolean_start_index;\n\n                // unmap package map file and free memory\n                delete *package_map_file;\n\n                auto flag_value_file = aconfig_storage::get_mapped_file(\n                    \"system\",\n                    aconfig_storage::StorageFileType::flag_val);\n                if (!flag_value_file.ok()) {\n                    ALOGE(\"error: failed to get flag value file: %s\", flag_value_file.error().c_str());\n                    package_exists_in_storage_ = false;\n                    return;\n                }\n\n                // cache flag value file\n                flag_value_file_ = std::unique_ptr<aconfig_storage::MappedStorageFile>(\n                    *flag_value_file);\n\n            }\n\n\n            virtual bool disabled_ro() override {\n                return false;\n            }\n\n            virtual bool disabled_rw() override {\n                if (cache_[0] == -1) {\n                    if (!package_exists_in_storage_) {\n                        return false;\n                    }\n\n                    auto value = aconfig_storage::get_boolean_flag_value(\n                        *flag_value_file_,\n                        boolean_start_index_ + 0);\n\n                    if (!value.ok()) {\n                        ALOGE(\"error: failed to read flag value: %s\", value.error().c_str());\n                        return false;\n                    }\n\n                    cache_[0] = *value;\n                }\n                return cache_[0];\n            }\n\n            virtual bool disabled_rw_exported() override {\n                if (cache_[1] == -1) {\n                    if (!package_exists_in_storage_) {\n                        return false;\n                    }\n\n                    auto value = aconfig_storage::get_boolean_flag_value(\n                        *flag_value_file_,\n                        boolean_start_index_ + 1);\n\n                    if (!value.ok()) {\n                        ALOGE(\"error: failed to read flag value: %s\", value.error().c_str());\n                        return false;\n                    }\n\n                    cache_[1] = *value;\n                }\n                return cache_[1];\n            }\n\n            virtual bool disabled_rw_in_other_namespace() override {\n                if (cache_[2] == -1) {\n                    if (!package_exists_in_storage_) {\n                        return false;\n                    }\n\n                    auto value = aconfig_storage::get_boolean_flag_value(\n                        *flag_value_file_,\n                        boolean_start_index_ + 2);\n\n                    if (!value.ok()) {\n                        ALOGE(\"error: failed to read flag value: %s\", value.error().c_str());\n                        return false;\n                    }\n\n                    cache_[2] = *value;\n                }\n                return cache_[2];\n            }\n\n            virtual bool enabled_fixed_ro() override {\n                return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO;\n            }\n\n            virtual bool enabled_fixed_ro_exported() override {\n                return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO_EXPORTED;\n            }\n\n            virtual bool enabled_ro() override {\n                return true;\n            }\n\n            virtual bool enabled_ro_exported() override {\n                return true;\n            }\n\n            virtual bool enabled_rw() override {\n                if (cache_[3] == -1) {\n                    if (!package_exists_in_storage_) {\n                        return true;\n                    }\n\n                    auto value = aconfig_storage::get_boolean_flag_value(\n                        *flag_value_file_,\n                        boolean_start_index_ + 7);\n\n                    if (!value.ok()) {\n                        ALOGE(\"error: failed to read flag value: %s\", value.error().c_str());\n                        return true;\n                    }\n\n                    cache_[3] = *value;\n                }\n                return cache_[3];\n            }\n\n    private:\n        std::vector<int8_t> cache_ = std::vector<int8_t>(4, -1);\n\n        uint32_t boolean_start_index_;\n\n        std::unique_ptr<aconfig_storage::MappedStorageFile> flag_value_file_;\n\n        bool package_exists_in_storage_;\n    };\n\n    std::unique_ptr<flag_provider_interface> provider_ =\n        std::make_unique<flag_provider>();\n}\n\nbool com_android_aconfig_test_disabled_ro() {\n    return false;\n}\n\nbool com_android_aconfig_test_disabled_rw() {\n    return com::android::aconfig::test::disabled_rw();\n}\n\nbool com_android_aconfig_test_disabled_rw_exported() {\n    return com::android::aconfig::test::disabled_rw_exported();\n}\n\nbool com_android_aconfig_test_disabled_rw_in_other_namespace() {\n    return com::android::aconfig::test::disabled_rw_in_other_namespace();\n}\n\nbool com_android_aconfig_test_enabled_fixed_ro() {\n    return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO;\n}\n\nbool com_android_aconfig_test_enabled_fixed_ro_exported() {\n    return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO_EXPORTED;\n}\n\nbool com_android_aconfig_test_enabled_ro() {\n    return true;\n}\n\nbool com_android_aconfig_test_enabled_ro_exported() {\n    return true;\n}\n\nbool com_android_aconfig_test_enabled_rw() {\n    return com::android::aconfig::test::enabled_rw();\n}\n\n\"#;\n\n    const TEST_SOURCE_FILE_EXPECTED: &str = r#\"\n#include \"com_android_aconfig_test.h\"\n\n#include <unistd.h>\n#include \"aconfig_storage/aconfig_storage_read_api.hpp\"\n#include <android/log.h>\n#define LOG_TAG \"aconfig_cpp_codegen\"\n#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)\n\n#include <unordered_map>\n#include <string>\n\nnamespace com::android::aconfig::test {\n\n    class flag_provider : public flag_provider_interface {\n        private:\n            std::unordered_map<std::string, bool> overrides_;\n\n            uint32_t boolean_start_index_;\n\n            std::unique_ptr<aconfig_storage::MappedStorageFile> flag_value_file_;\n\n            bool package_exists_in_storage_;\n\n        public:\n            flag_provider()\n                : overrides_()\n                , boolean_start_index_()\n                , flag_value_file_(nullptr)\n                , package_exists_in_storage_(true) {\n\n                auto package_map_file = aconfig_storage::get_mapped_file(\n                     \"system\",\n                    aconfig_storage::StorageFileType::package_map);\n\n                if (!package_map_file.ok()) {\n                    ALOGE(\"error: failed to get package map file: %s\", package_map_file.error().c_str());\n                    package_exists_in_storage_ = false;\n                    return;\n                }\n\n                auto context = aconfig_storage::get_package_read_context(\n                    **package_map_file, \"com.android.aconfig.test\");\n\n                if (!context.ok()) {\n                    ALOGE(\"error: failed to get package read context: %s\", context.error().c_str());\n                    package_exists_in_storage_ = false;\n                    return;\n                }\n\n                if (!(context->package_exists)) {\n                    package_exists_in_storage_ = false;\n                    return;\n                }\n\n                // cache package boolean flag start index\n                boolean_start_index_ = context->boolean_start_index;\n\n                // unmap package map file and free memory\n                delete *package_map_file;\n\n                auto flag_value_file = aconfig_storage::get_mapped_file(\n                    \"system\",\n                aconfig_storage::StorageFileType::flag_val);\n                if (!flag_value_file.ok()) {\n                    ALOGE(\"error: failed to get flag value file: %s\", flag_value_file.error().c_str());\n                    package_exists_in_storage_ = false;\n                    return;\n                }\n\n                // cache flag value file\n                flag_value_file_ = std::unique_ptr<aconfig_storage::MappedStorageFile>(\n                *flag_value_file);\n\n            }\n\n            virtual bool disabled_ro() override {\n                auto it = overrides_.find(\"disabled_ro\");\n                  if (it != overrides_.end()) {\n                      return it->second;\n                } else {\n                  return false;\n                }\n            }\n\n            virtual void disabled_ro(bool val) override {\n                overrides_[\"disabled_ro\"] = val;\n            }\n\n            virtual bool disabled_rw() override {\n                auto it = overrides_.find(\"disabled_rw\");\n                  if (it != overrides_.end()) {\n                      return it->second;\n                } else {\n                    if (!package_exists_in_storage_) {\n                        return false;\n                    }\n\n                    auto value = aconfig_storage::get_boolean_flag_value(\n                        *flag_value_file_,\n                        boolean_start_index_ + 0);\n\n                    if (!value.ok()) {\n                        ALOGE(\"error: failed to read flag value: %s\", value.error().c_str());\n                        return false;\n                    } else {\n                        return *value;\n                    }\n                }\n            }\n\n            virtual void disabled_rw(bool val) override {\n                overrides_[\"disabled_rw\"] = val;\n            }\n\n            virtual bool disabled_rw_exported() override {\n                auto it = overrides_.find(\"disabled_rw_exported\");\n                  if (it != overrides_.end()) {\n                      return it->second;\n                } else {\n                    if (!package_exists_in_storage_) {\n                        return false;\n                    }\n\n                    auto value = aconfig_storage::get_boolean_flag_value(\n                        *flag_value_file_,\n                        boolean_start_index_ + 1);\n\n                    if (!value.ok()) {\n                        ALOGE(\"error: failed to read flag value: %s\", value.error().c_str());\n                        return false;\n                    } else {\n                        return *value;\n                    }\n                }\n            }\n\n            virtual void disabled_rw_exported(bool val) override {\n                overrides_[\"disabled_rw_exported\"] = val;\n            }\n\n            virtual bool disabled_rw_in_other_namespace() override {\n                auto it = overrides_.find(\"disabled_rw_in_other_namespace\");\n                  if (it != overrides_.end()) {\n                      return it->second;\n                } else {\n                    if (!package_exists_in_storage_) {\n                        return false;\n                    }\n\n                    auto value = aconfig_storage::get_boolean_flag_value(\n                        *flag_value_file_,\n                        boolean_start_index_ + 2);\n\n                    if (!value.ok()) {\n                        ALOGE(\"error: failed to read flag value: %s\", value.error().c_str());\n                        return false;\n                    } else {\n                        return *value;\n                    }\n                }\n            }\n\n            virtual void disabled_rw_in_other_namespace(bool val) override {\n                overrides_[\"disabled_rw_in_other_namespace\"] = val;\n            }\n\n            virtual bool enabled_fixed_ro() override {\n                auto it = overrides_.find(\"enabled_fixed_ro\");\n                  if (it != overrides_.end()) {\n                      return it->second;\n                } else {\n                  return true;\n                }\n            }\n\n            virtual void enabled_fixed_ro(bool val) override {\n                overrides_[\"enabled_fixed_ro\"] = val;\n            }\n\n            virtual bool enabled_fixed_ro_exported() override {\n                auto it = overrides_.find(\"enabled_fixed_ro_exported\");\n                  if (it != overrides_.end()) {\n                      return it->second;\n                } else {\n                  return true;\n                }\n            }\n\n            virtual void enabled_fixed_ro_exported(bool val) override {\n                overrides_[\"enabled_fixed_ro_exported\"] = val;\n            }\n\n            virtual bool enabled_ro() override {\n                auto it = overrides_.find(\"enabled_ro\");\n                  if (it != overrides_.end()) {\n                      return it->second;\n                } else {\n                  return true;\n                }\n            }\n\n            virtual void enabled_ro(bool val) override {\n                overrides_[\"enabled_ro\"] = val;\n            }\n\n            virtual bool enabled_ro_exported() override {\n                auto it = overrides_.find(\"enabled_ro_exported\");\n                  if (it != overrides_.end()) {\n                      return it->second;\n                } else {\n                  return true;\n                }\n            }\n\n            virtual void enabled_ro_exported(bool val) override {\n                overrides_[\"enabled_ro_exported\"] = val;\n            }\n\n            virtual bool enabled_rw() override {\n                auto it = overrides_.find(\"enabled_rw\");\n                  if (it != overrides_.end()) {\n                      return it->second;\n                } else {\n                    if (!package_exists_in_storage_) {\n                        return true;\n                    }\n\n                    auto value = aconfig_storage::get_boolean_flag_value(\n                        *flag_value_file_,\n                        boolean_start_index_ + 7);\n\n                    if (!value.ok()) {\n                        ALOGE(\"error: failed to read flag value: %s\", value.error().c_str());\n                        return true;\n                    } else {\n                        return *value;\n                    }\n                }\n            }\n\n            virtual void enabled_rw(bool val) override {\n                overrides_[\"enabled_rw\"] = val;\n            }\n\n            virtual void reset_flags() override {\n                overrides_.clear();\n            }\n    };\n\n    std::unique_ptr<flag_provider_interface> provider_ =\n        std::make_unique<flag_provider>();\n}\n\nbool com_android_aconfig_test_disabled_ro() {\n    return com::android::aconfig::test::disabled_ro();\n}\n\n\nvoid set_com_android_aconfig_test_disabled_ro(bool val) {\n    com::android::aconfig::test::disabled_ro(val);\n}\n\nbool com_android_aconfig_test_disabled_rw() {\n    return com::android::aconfig::test::disabled_rw();\n}\n\n\nvoid set_com_android_aconfig_test_disabled_rw(bool val) {\n    com::android::aconfig::test::disabled_rw(val);\n}\n\n\nbool com_android_aconfig_test_disabled_rw_exported() {\n    return com::android::aconfig::test::disabled_rw_exported();\n}\n\nvoid set_com_android_aconfig_test_disabled_rw_exported(bool val) {\n    com::android::aconfig::test::disabled_rw_exported(val);\n}\n\n\nbool com_android_aconfig_test_disabled_rw_in_other_namespace() {\n    return com::android::aconfig::test::disabled_rw_in_other_namespace();\n}\n\nvoid set_com_android_aconfig_test_disabled_rw_in_other_namespace(bool val) {\n    com::android::aconfig::test::disabled_rw_in_other_namespace(val);\n}\n\n\nbool com_android_aconfig_test_enabled_fixed_ro() {\n    return com::android::aconfig::test::enabled_fixed_ro();\n}\n\nvoid set_com_android_aconfig_test_enabled_fixed_ro(bool val) {\n    com::android::aconfig::test::enabled_fixed_ro(val);\n}\n\nbool com_android_aconfig_test_enabled_fixed_ro_exported() {\n    return com::android::aconfig::test::enabled_fixed_ro_exported();\n}\n\nvoid set_com_android_aconfig_test_enabled_fixed_ro_exported(bool val) {\n    com::android::aconfig::test::enabled_fixed_ro_exported(val);\n}\n\nbool com_android_aconfig_test_enabled_ro() {\n    return com::android::aconfig::test::enabled_ro();\n}\n\n\nvoid set_com_android_aconfig_test_enabled_ro(bool val) {\n    com::android::aconfig::test::enabled_ro(val);\n}\n\n\nbool com_android_aconfig_test_enabled_ro_exported() {\n    return com::android::aconfig::test::enabled_ro_exported();\n}\n\n\nvoid set_com_android_aconfig_test_enabled_ro_exported(bool val) {\n    com::android::aconfig::test::enabled_ro_exported(val);\n}\n\n\nbool com_android_aconfig_test_enabled_rw() {\n    return com::android::aconfig::test::enabled_rw();\n}\n\n\nvoid set_com_android_aconfig_test_enabled_rw(bool val) {\n    com::android::aconfig::test::enabled_rw(val);\n}\n\nvoid com_android_aconfig_test_reset_flags() {\n     com::android::aconfig::test::reset_flags();\n}\n\n\"#;\n\n    const FORCE_READ_ONLY_SOURCE_FILE_EXPECTED: &str = r#\"\n#include \"com_android_aconfig_test.h\"\n\nnamespace com::android::aconfig::test {\n\n    class flag_provider : public flag_provider_interface {\n        public:\n\n            virtual bool disabled_ro() override {\n                return false;\n            }\n\n            virtual bool disabled_rw() override {\n                return false;\n            }\n\n            virtual bool disabled_rw_in_other_namespace() override {\n                return false;\n            }\n\n            virtual bool enabled_fixed_ro() override {\n                return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO;\n            }\n\n            virtual bool enabled_ro() override {\n                return true;\n            }\n\n            virtual bool enabled_rw() override {\n                return true;\n            }\n    };\n\n    std::unique_ptr<flag_provider_interface> provider_ =\n        std::make_unique<flag_provider>();\n}\n\nbool com_android_aconfig_test_disabled_ro() {\n    return false;\n}\n\nbool com_android_aconfig_test_disabled_rw() {\n    return false;\n}\n\nbool com_android_aconfig_test_disabled_rw_in_other_namespace() {\n    return false;\n}\n\nbool com_android_aconfig_test_enabled_fixed_ro() {\n    return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO;\n}\n\nbool com_android_aconfig_test_enabled_ro() {\n    return true;\n}\n\nbool com_android_aconfig_test_enabled_rw() {\n    return true;\n}\n\n\"#;\n\n    const READ_ONLY_EXPORTED_PROD_HEADER_EXPECTED: &str = r#\"\n#pragma once\n\n#ifndef COM_ANDROID_ACONFIG_TEST\n#define COM_ANDROID_ACONFIG_TEST(FLAG) COM_ANDROID_ACONFIG_TEST_##FLAG\n#endif\n\n#ifndef COM_ANDROID_ACONFIG_TEST_DISABLED_FIXED_RO\n#define COM_ANDROID_ACONFIG_TEST_DISABLED_FIXED_RO false\n#endif\n\n#ifndef COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO\n#define COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO true\n#endif\n\n#ifdef __cplusplus\n\n#include <memory>\n\nnamespace com::android::aconfig::test {\n\nclass flag_provider_interface {\npublic:\n    virtual ~flag_provider_interface() = default;\n\n    virtual bool disabled_fixed_ro() = 0;\n\n    virtual bool disabled_ro() = 0;\n\n    virtual bool enabled_fixed_ro() = 0;\n\n    virtual bool enabled_ro() = 0;\n};\n\nextern std::unique_ptr<flag_provider_interface> provider_;\n\nconstexpr inline bool disabled_fixed_ro() {\n    return COM_ANDROID_ACONFIG_TEST_DISABLED_FIXED_RO;\n}\n\ninline bool disabled_ro() {\n    return false;\n}\n\nconstexpr inline bool enabled_fixed_ro() {\n    return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO;\n}\n\ninline bool enabled_ro() {\n    return true;\n}\n}\n\nextern \"C\" {\n#endif // __cplusplus\n\nbool com_android_aconfig_test_disabled_fixed_ro();\n\nbool com_android_aconfig_test_disabled_ro();\n\nbool com_android_aconfig_test_enabled_fixed_ro();\n\nbool com_android_aconfig_test_enabled_ro();\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\"#;\n\n    const READ_ONLY_PROD_SOURCE_FILE_EXPECTED: &str = r#\"\n#include \"com_android_aconfig_test.h\"\n\nnamespace com::android::aconfig::test {\n\n    class flag_provider : public flag_provider_interface {\n        public:\n\n            virtual bool disabled_fixed_ro() override {\n                return COM_ANDROID_ACONFIG_TEST_DISABLED_FIXED_RO;\n            }\n\n            virtual bool disabled_ro() override {\n                return false;\n            }\n\n            virtual bool enabled_fixed_ro() override {\n                return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO;\n            }\n\n            virtual bool enabled_ro() override {\n                return true;\n            }\n    };\n\n    std::unique_ptr<flag_provider_interface> provider_ =\n        std::make_unique<flag_provider>();\n}\n\nbool com_android_aconfig_test_disabled_fixed_ro() {\n    return COM_ANDROID_ACONFIG_TEST_DISABLED_FIXED_RO;\n}\n\nbool com_android_aconfig_test_disabled_ro() {\n    return false;\n}\n\nbool com_android_aconfig_test_enabled_fixed_ro() {\n    return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO;\n}\n\nbool com_android_aconfig_test_enabled_ro() {\n    return true;\n}\n\"#;\n    use crate::commands::assign_flag_ids;\n\n    fn test_generate_cpp_code(\n        parsed_flags: ProtoParsedFlags,\n        mode: CodegenMode,\n        expected_header: &str,\n        expected_src: &str,\n    ) {\n        let modified_parsed_flags =\n            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();\n        let flag_ids =\n            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();\n        let generated = generate_cpp_code(\n            crate::test::TEST_PACKAGE,\n            modified_parsed_flags.into_iter(),\n            mode,\n            flag_ids,\n        )\n        .unwrap();\n        let mut generated_files_map = HashMap::new();\n        for file in generated {\n            generated_files_map.insert(\n                String::from(file.path.to_str().unwrap()),\n                String::from_utf8(file.contents).unwrap(),\n            );\n        }\n\n        let mut target_file_path = String::from(\"include/com_android_aconfig_test.h\");\n        assert!(generated_files_map.contains_key(&target_file_path));\n        assert_eq!(\n            None,\n            crate::test::first_significant_code_diff(\n                expected_header,\n                generated_files_map.get(&target_file_path).unwrap()\n            )\n        );\n\n        target_file_path = String::from(\"com_android_aconfig_test.cc\");\n        assert!(generated_files_map.contains_key(&target_file_path));\n        assert_eq!(\n            None,\n            crate::test::first_significant_code_diff(\n                expected_src,\n                generated_files_map.get(&target_file_path).unwrap()\n            )\n        );\n    }\n\n    #[test]\n    fn test_generate_cpp_code_for_prod() {\n        let parsed_flags = crate::test::parse_test_flags();\n        test_generate_cpp_code(\n            parsed_flags,\n            CodegenMode::Production,\n            EXPORTED_PROD_HEADER_EXPECTED,\n            PROD_SOURCE_FILE_EXPECTED,\n        );\n    }\n\n    #[test]\n    fn test_generate_cpp_code_for_test() {\n        let parsed_flags = crate::test::parse_test_flags();\n        test_generate_cpp_code(\n            parsed_flags,\n            CodegenMode::Test,\n            EXPORTED_TEST_HEADER_EXPECTED,\n            TEST_SOURCE_FILE_EXPECTED,\n        );\n    }\n\n    #[test]\n    fn test_generate_cpp_code_for_force_read_only() {\n        let parsed_flags = crate::test::parse_test_flags();\n        test_generate_cpp_code(\n            parsed_flags,\n            CodegenMode::ForceReadOnly,\n            EXPORTED_FORCE_READ_ONLY_HEADER_EXPECTED,\n            FORCE_READ_ONLY_SOURCE_FILE_EXPECTED,\n        );\n    }\n\n    #[test]\n    fn test_generate_cpp_code_for_read_only_prod() {\n        let parsed_flags = crate::test::parse_read_only_test_flags();\n        test_generate_cpp_code(\n            parsed_flags,\n            CodegenMode::Production,\n            READ_ONLY_EXPORTED_PROD_HEADER_EXPECTED,\n            READ_ONLY_PROD_SOURCE_FILE_EXPECTED,\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/codegen/java.rs",
    "content": "/*\n* Copyright (C) 2023 The Android Open Source Project\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*      http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\nuse anyhow::Result;\nuse serde::Serialize;\nuse std::collections::{BTreeMap, BTreeSet};\nuse std::path::PathBuf;\nuse tinytemplate::TinyTemplate;\n\nuse crate::codegen;\nuse crate::codegen::CodegenMode;\nuse crate::commands::{should_include_flag, OutputFile};\nuse aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};\nuse convert_finalized_flags::{FinalizedFlag, FinalizedFlagMap};\nuse std::collections::HashMap;\n\n// Arguments to configure codegen for generate_java_code.\npub struct JavaCodegenConfig {\n    pub codegen_mode: CodegenMode,\n    pub flag_ids: HashMap<String, u16>,\n    pub allow_instrumentation: bool,\n    pub package_fingerprint: u64,\n    pub new_exported: bool,\n    pub single_exported_file: bool,\n    pub finalized_flags: FinalizedFlagMap,\n}\n\npub fn generate_java_code<I>(\n    package: &str,\n    parsed_flags_iter: I,\n    config: JavaCodegenConfig,\n) -> Result<Vec<OutputFile>>\nwhere\n    I: Iterator<Item = ProtoParsedFlag>,\n{\n    let flag_elements: Vec<FlagElement> = parsed_flags_iter\n        .map(|pf| {\n            create_flag_element(package, &pf, config.flag_ids.clone(), &config.finalized_flags)\n        })\n        .collect();\n    let namespace_flags = gen_flags_by_namespace(&flag_elements);\n    let properties_set: BTreeSet<String> =\n        flag_elements.iter().map(|fe| format_property_name(&fe.device_config_namespace)).collect();\n    let is_test_mode = config.codegen_mode == CodegenMode::Test;\n    let library_exported = config.codegen_mode == CodegenMode::Exported;\n    let runtime_lookup_required =\n        flag_elements.iter().any(|elem| elem.is_read_write) || library_exported;\n    let container = (flag_elements.first().expect(\"zero template flags\").container).to_string();\n    let is_platform_container =\n        matches!(container.as_str(), \"system\" | \"system_ext\" | \"product\" | \"vendor\");\n    let context = Context {\n        flag_elements,\n        namespace_flags,\n        is_test_mode,\n        runtime_lookup_required,\n        properties_set,\n        package_name: package.to_string(),\n        library_exported,\n        allow_instrumentation: config.allow_instrumentation,\n        container,\n        is_platform_container,\n        package_fingerprint: format!(\"0x{:X}L\", config.package_fingerprint),\n        new_exported: config.new_exported,\n        single_exported_file: config.single_exported_file,\n    };\n    let mut template = TinyTemplate::new();\n    if library_exported && config.single_exported_file {\n        template.add_template(\n            \"ExportedFlags.java\",\n            include_str!(\"../../templates/ExportedFlags.java.template\"),\n        )?;\n    }\n    template.add_template(\"Flags.java\", include_str!(\"../../templates/Flags.java.template\"))?;\n    add_feature_flags_impl_template(&context, &mut template)?;\n    template.add_template(\n        \"FeatureFlags.java\",\n        include_str!(\"../../templates/FeatureFlags.java.template\"),\n    )?;\n    template.add_template(\n        \"CustomFeatureFlags.java\",\n        include_str!(\"../../templates/CustomFeatureFlags.java.template\"),\n    )?;\n    template.add_template(\n        \"FakeFeatureFlagsImpl.java\",\n        include_str!(\"../../templates/FakeFeatureFlagsImpl.java.template\"),\n    )?;\n\n    let path: PathBuf = package.split('.').collect();\n    let mut files = vec![\n        \"Flags.java\",\n        \"FeatureFlags.java\",\n        \"FeatureFlagsImpl.java\",\n        \"CustomFeatureFlags.java\",\n        \"FakeFeatureFlagsImpl.java\",\n    ];\n    if library_exported && config.single_exported_file {\n        files.push(\"ExportedFlags.java\");\n    }\n    files\n        .iter()\n        .map(|file| {\n            Ok(OutputFile {\n                contents: template.render(file, &context)?.into(),\n                path: path.join(file),\n            })\n        })\n        .collect::<Result<Vec<OutputFile>>>()\n}\n\nfn gen_flags_by_namespace(flags: &[FlagElement]) -> Vec<NamespaceFlags> {\n    let mut namespace_to_flag: BTreeMap<String, Vec<FlagElement>> = BTreeMap::new();\n\n    for flag in flags {\n        match namespace_to_flag.get_mut(&flag.device_config_namespace) {\n            Some(flag_list) => flag_list.push(flag.clone()),\n            None => {\n                namespace_to_flag.insert(flag.device_config_namespace.clone(), vec![flag.clone()]);\n            }\n        }\n    }\n\n    namespace_to_flag\n        .iter()\n        .map(|(namespace, flags)| NamespaceFlags {\n            namespace: namespace.to_string(),\n            flags: flags.clone(),\n        })\n        .collect()\n}\n\n#[derive(Serialize)]\nstruct Context {\n    pub flag_elements: Vec<FlagElement>,\n    pub namespace_flags: Vec<NamespaceFlags>,\n    pub is_test_mode: bool,\n    pub runtime_lookup_required: bool,\n    pub properties_set: BTreeSet<String>,\n    pub package_name: String,\n    pub library_exported: bool,\n    pub allow_instrumentation: bool,\n    pub container: String,\n    pub is_platform_container: bool,\n    pub package_fingerprint: String,\n    pub new_exported: bool,\n    pub single_exported_file: bool,\n}\n\n#[derive(Serialize, Debug)]\nstruct NamespaceFlags {\n    pub namespace: String,\n    pub flags: Vec<FlagElement>,\n}\n\n#[derive(Serialize, Clone, Debug)]\nstruct FlagElement {\n    pub container: String,\n    pub default_value: bool,\n    pub device_config_namespace: String,\n    pub device_config_flag: String,\n    pub flag_name: String,\n    pub flag_name_constant_suffix: String,\n    pub flag_offset: u16,\n    pub is_read_write: bool,\n    pub method_name: String,\n    pub properties: String,\n    pub finalized_sdk_present: bool,\n    pub finalized_sdk_value: i32,\n}\n\nfn create_flag_element(\n    package: &str,\n    pf: &ProtoParsedFlag,\n    flag_offsets: HashMap<String, u16>,\n    finalized_flags: &FinalizedFlagMap,\n) -> FlagElement {\n    let device_config_flag = codegen::create_device_config_ident(package, pf.name())\n        .expect(\"values checked at flag parse time\");\n\n    let no_assigned_offset = !should_include_flag(pf);\n\n    let flag_offset = match flag_offsets.get(pf.name()) {\n        Some(offset) => offset,\n        None => {\n            // System/vendor/product RO+disabled flags have no offset in storage files.\n            // Assign placeholder value.\n            if no_assigned_offset {\n                &0\n            }\n            // All other flags _must_ have an offset.\n            else {\n                panic!(\"{}\", format!(\"missing flag offset for {}\", pf.name()));\n            }\n        }\n    };\n\n    // An empty map is provided if check_api_level is disabled.\n    let mut finalized_sdk_present: bool = false;\n    let mut finalized_sdk_value: i32 = 0;\n    if !finalized_flags.is_empty() {\n        let finalized_sdk = finalized_flags.get_finalized_level(&FinalizedFlag {\n            flag_name: pf.name().to_string(),\n            package_name: package.to_string(),\n        });\n        finalized_sdk_present = finalized_sdk.is_some();\n        finalized_sdk_value = finalized_sdk.map(|f| f.0).unwrap_or_default();\n    }\n\n    FlagElement {\n        container: pf.container().to_string(),\n        default_value: pf.state() == ProtoFlagState::ENABLED,\n        device_config_namespace: pf.namespace().to_string(),\n        device_config_flag,\n        flag_name: pf.name().to_string(),\n        flag_name_constant_suffix: pf.name().to_ascii_uppercase(),\n        flag_offset: *flag_offset,\n        is_read_write: pf.permission() == ProtoFlagPermission::READ_WRITE,\n        method_name: format_java_method_name(pf.name()),\n        properties: format_property_name(pf.namespace()),\n        finalized_sdk_present,\n        finalized_sdk_value,\n    }\n}\n\nfn format_java_method_name(flag_name: &str) -> String {\n    let splits: Vec<&str> = flag_name.split('_').filter(|&word| !word.is_empty()).collect();\n    if splits.len() == 1 {\n        let name = splits[0];\n        name[0..1].to_ascii_lowercase() + &name[1..]\n    } else {\n        splits\n            .iter()\n            .enumerate()\n            .map(|(index, word)| {\n                if index == 0 {\n                    word.to_ascii_lowercase()\n                } else {\n                    word[0..1].to_ascii_uppercase() + &word[1..].to_ascii_lowercase()\n                }\n            })\n            .collect::<Vec<String>>()\n            .join(\"\")\n    }\n}\n\nfn format_property_name(property_name: &str) -> String {\n    let name = format_java_method_name(property_name);\n    format!(\"mProperties{}{}\", &name[0..1].to_ascii_uppercase(), &name[1..])\n}\n\nfn add_feature_flags_impl_template(\n    context: &Context,\n    template: &mut TinyTemplate,\n) -> Result<(), tinytemplate::error::Error> {\n    if context.is_test_mode {\n        // Test mode has its own template, so use regardless of any other settings.\n        template.add_template(\n            \"FeatureFlagsImpl.java\",\n            include_str!(\"../../templates/FeatureFlagsImpl.test_mode.java.template\"),\n        )?;\n        return Ok(());\n    }\n\n    match (context.library_exported, context.new_exported, context.allow_instrumentation) {\n        // Exported library with new_exported enabled, use new storage exported template.\n        (true, true, _) => {\n            template.add_template(\n                \"FeatureFlagsImpl.java\",\n                include_str!(\"../../templates/FeatureFlagsImpl.exported.java.template\"),\n            )?;\n        }\n\n        // Exported library with new_exported NOT enabled, use legacy (device\n        // config) template, because regardless of allow_instrumentation, we use\n        // device config for exported libs if new_exported isn't enabled.\n        // Remove once new_exported is fully rolled out.\n        (true, false, _) => {\n            template.add_template(\n                \"FeatureFlagsImpl.java\",\n                include_str!(\"../../templates/FeatureFlagsImpl.deviceConfig.java.template\"),\n            )?;\n        }\n\n        // New storage internal mode.\n        (false, _, true) => {\n            template.add_template(\n                \"FeatureFlagsImpl.java\",\n                include_str!(\"../../templates/FeatureFlagsImpl.new_storage.java.template\"),\n            )?;\n        }\n\n        // Device config internal mode. Use legacy (device config) template.\n        (false, _, false) => {\n            template.add_template(\n                \"FeatureFlagsImpl.java\",\n                include_str!(\"../../templates/FeatureFlagsImpl.deviceConfig.java.template\"),\n            )?;\n        }\n    };\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use convert_finalized_flags::ApiLevel;\n\n    use super::*;\n    use crate::commands::assign_flag_ids;\n    use std::collections::HashMap;\n\n    const EXPECTED_FEATUREFLAGS_COMMON_CONTENT: &str = r#\"\n    package com.android.aconfig.test;\n    // TODO(b/303773055): Remove the annotation after access issue is resolved.\n    import android.compat.annotation.UnsupportedAppUsage;\n    /** @hide */\n    public interface FeatureFlags {\n        @com.android.aconfig.annotations.AssumeFalseForR8\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        boolean disabledRo();\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        boolean disabledRw();\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        boolean disabledRwExported();\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        boolean disabledRwInOtherNamespace();\n        @com.android.aconfig.annotations.AssumeTrueForR8\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        boolean enabledFixedRo();\n        @com.android.aconfig.annotations.AssumeTrueForR8\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        boolean enabledFixedRoExported();\n        @com.android.aconfig.annotations.AssumeTrueForR8\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        boolean enabledRo();\n        @com.android.aconfig.annotations.AssumeTrueForR8\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        boolean enabledRoExported();\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        boolean enabledRw();\n    }\n    \"#;\n\n    const EXPECTED_FLAG_COMMON_CONTENT: &str = r#\"\n    package com.android.aconfig.test;\n    // TODO(b/303773055): Remove the annotation after access issue is resolved.\n    import android.compat.annotation.UnsupportedAppUsage;\n    /** @hide */\n    public final class Flags {\n        /** @hide */\n        public static final String FLAG_DISABLED_RO = \"com.android.aconfig.test.disabled_ro\";\n        /** @hide */\n        public static final String FLAG_DISABLED_RW = \"com.android.aconfig.test.disabled_rw\";\n        /** @hide */\n        public static final String FLAG_DISABLED_RW_EXPORTED = \"com.android.aconfig.test.disabled_rw_exported\";\n        /** @hide */\n        public static final String FLAG_DISABLED_RW_IN_OTHER_NAMESPACE = \"com.android.aconfig.test.disabled_rw_in_other_namespace\";\n        /** @hide */\n        public static final String FLAG_ENABLED_FIXED_RO = \"com.android.aconfig.test.enabled_fixed_ro\";\n        /** @hide */\n        public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = \"com.android.aconfig.test.enabled_fixed_ro_exported\";\n        /** @hide */\n        public static final String FLAG_ENABLED_RO = \"com.android.aconfig.test.enabled_ro\";\n        /** @hide */\n        public static final String FLAG_ENABLED_RO_EXPORTED = \"com.android.aconfig.test.enabled_ro_exported\";\n        /** @hide */\n        public static final String FLAG_ENABLED_RW = \"com.android.aconfig.test.enabled_rw\";\n\n        @com.android.aconfig.annotations.AssumeFalseForR8\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        public static boolean disabledRo() {\n            return FEATURE_FLAGS.disabledRo();\n        }\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        public static boolean disabledRw() {\n            return FEATURE_FLAGS.disabledRw();\n        }\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        public static boolean disabledRwExported() {\n            return FEATURE_FLAGS.disabledRwExported();\n        }\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        public static boolean disabledRwInOtherNamespace() {\n            return FEATURE_FLAGS.disabledRwInOtherNamespace();\n        }\n        @com.android.aconfig.annotations.AssumeTrueForR8\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        public static boolean enabledFixedRo() {\n            return FEATURE_FLAGS.enabledFixedRo();\n        }\n        @com.android.aconfig.annotations.AssumeTrueForR8\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        public static boolean enabledFixedRoExported() {\n            return FEATURE_FLAGS.enabledFixedRoExported();\n        }\n        @com.android.aconfig.annotations.AssumeTrueForR8\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        public static boolean enabledRo() {\n            return FEATURE_FLAGS.enabledRo();\n        }\n        @com.android.aconfig.annotations.AssumeTrueForR8\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        public static boolean enabledRoExported() {\n            return FEATURE_FLAGS.enabledRoExported();\n        }\n        @com.android.aconfig.annotations.AconfigFlagAccessor\n        @UnsupportedAppUsage\n        public static boolean enabledRw() {\n            return FEATURE_FLAGS.enabledRw();\n        }\n    \"#;\n\n    const EXPECTED_CUSTOMFEATUREFLAGS_CONTENT: &str = r#\"\n    package com.android.aconfig.test;\n\n    // TODO(b/303773055): Remove the annotation after access issue is resolved.\n    import android.compat.annotation.UnsupportedAppUsage;\n    import java.util.Arrays;\n    import java.util.HashSet;\n    import java.util.List;\n    import java.util.Set;\n    import java.util.function.BiPredicate;\n    import java.util.function.Predicate;\n\n    /** @hide */\n    public class CustomFeatureFlags implements FeatureFlags {\n\n        private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;\n\n        public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {\n            mGetValueImpl = getValueImpl;\n        }\n\n        @Override\n        @UnsupportedAppUsage\n        public boolean disabledRo() {\n            return getValue(Flags.FLAG_DISABLED_RO,\n                    FeatureFlags::disabledRo);\n        }\n        @Override\n        @UnsupportedAppUsage\n        public boolean disabledRw() {\n            return getValue(Flags.FLAG_DISABLED_RW,\n                FeatureFlags::disabledRw);\n        }\n        @Override\n        @UnsupportedAppUsage\n        public boolean disabledRwExported() {\n            return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,\n                FeatureFlags::disabledRwExported);\n        }\n        @Override\n        @UnsupportedAppUsage\n        public boolean disabledRwInOtherNamespace() {\n            return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,\n                FeatureFlags::disabledRwInOtherNamespace);\n        }\n        @Override\n        @UnsupportedAppUsage\n        public boolean enabledFixedRo() {\n            return getValue(Flags.FLAG_ENABLED_FIXED_RO,\n                FeatureFlags::enabledFixedRo);\n        }\n        @Override\n        @UnsupportedAppUsage\n        public boolean enabledFixedRoExported() {\n            return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,\n                FeatureFlags::enabledFixedRoExported);\n        }\n        @Override\n        @UnsupportedAppUsage\n        public boolean enabledRo() {\n            return getValue(Flags.FLAG_ENABLED_RO,\n                FeatureFlags::enabledRo);\n        }\n        @Override\n        @UnsupportedAppUsage\n        public boolean enabledRoExported() {\n            return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,\n                FeatureFlags::enabledRoExported);\n        }\n        @Override\n        @UnsupportedAppUsage\n        public boolean enabledRw() {\n            return getValue(Flags.FLAG_ENABLED_RW,\n                FeatureFlags::enabledRw);\n        }\n\n        public boolean isFlagReadOnlyOptimized(String flagName) {\n            if (mReadOnlyFlagsSet.contains(flagName) &&\n                isOptimizationEnabled()) {\n                    return true;\n            }\n            return false;\n        }\n\n        @com.android.aconfig.annotations.AssumeTrueForR8\n        private boolean isOptimizationEnabled() {\n            return false;\n        }\n\n        protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {\n            return mGetValueImpl.test(flagName, getter);\n        }\n\n        public List<String> getFlagNames() {\n            return Arrays.asList(\n                Flags.FLAG_DISABLED_RO,\n                Flags.FLAG_DISABLED_RW,\n                Flags.FLAG_DISABLED_RW_EXPORTED,\n                Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,\n                Flags.FLAG_ENABLED_FIXED_RO,\n                Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,\n                Flags.FLAG_ENABLED_RO,\n                Flags.FLAG_ENABLED_RO_EXPORTED,\n                Flags.FLAG_ENABLED_RW\n            );\n        }\n\n        private Set<String> mReadOnlyFlagsSet = new HashSet<>(\n            Arrays.asList(\n                Flags.FLAG_DISABLED_RO,\n                Flags.FLAG_ENABLED_FIXED_RO,\n                Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,\n                Flags.FLAG_ENABLED_RO,\n                Flags.FLAG_ENABLED_RO_EXPORTED,\n                \"\"\n            )\n        );\n    }\n    \"#;\n\n    const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#\"\n    package com.android.aconfig.test;\n\n    import java.util.HashMap;\n    import java.util.Map;\n    import java.util.function.Predicate;\n\n    /** @hide */\n    public class FakeFeatureFlagsImpl extends CustomFeatureFlags {\n        private final Map<String, Boolean> mFlagMap = new HashMap<>();\n        private final FeatureFlags mDefaults;\n\n        public FakeFeatureFlagsImpl() {\n            this(null);\n        }\n\n        public FakeFeatureFlagsImpl(FeatureFlags defaults) {\n            super(null);\n            mDefaults = defaults;\n            // Initialize the map with null values\n            for (String flagName : getFlagNames()) {\n                mFlagMap.put(flagName, null);\n            }\n        }\n\n        @Override\n        protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {\n            Boolean value = this.mFlagMap.get(flagName);\n            if (value != null) {\n                return value;\n            }\n            if (mDefaults != null) {\n                return getter.test(mDefaults);\n            }\n            throw new IllegalArgumentException(flagName + \" is not set\");\n        }\n\n        public void setFlag(String flagName, boolean value) {\n            if (!this.mFlagMap.containsKey(flagName)) {\n                throw new IllegalArgumentException(\"no such flag \" + flagName);\n            }\n            this.mFlagMap.put(flagName, value);\n        }\n\n        public void resetAll() {\n            for (Map.Entry entry : mFlagMap.entrySet()) {\n                entry.setValue(null);\n            }\n        }\n    }\n    \"#;\n\n    #[test]\n    fn test_generate_java_code_production() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let mode = CodegenMode::Production;\n        let modified_parsed_flags =\n            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();\n        let flag_ids =\n            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();\n        let config = JavaCodegenConfig {\n            codegen_mode: mode,\n            flag_ids,\n            allow_instrumentation: true,\n            package_fingerprint: 5801144784618221668,\n            new_exported: false,\n            single_exported_file: false,\n            finalized_flags: FinalizedFlagMap::new(),\n        };\n        let generated_files = generate_java_code(\n            crate::test::TEST_PACKAGE,\n            modified_parsed_flags.into_iter(),\n            config,\n        )\n        .unwrap();\n        let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()\n            + r#\"\n            private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();\n        }\"#;\n\n        let expected_featureflagsmpl_content = r#\"\n        package com.android.aconfig.test;\n        // TODO(b/303773055): Remove the annotation after access issue is resolved.\n        import android.compat.annotation.UnsupportedAppUsage;\n        import android.os.flagging.PlatformAconfigPackageInternal;\n        import android.util.Log;\n        /** @hide */\n        public final class FeatureFlagsImpl implements FeatureFlags {\n            private static final String TAG = \"FeatureFlagsImpl\";\n            private static volatile boolean isCached = false;\n            private static boolean disabledRw = false;\n            private static boolean disabledRwExported = false;\n            private static boolean disabledRwInOtherNamespace = false;\n            private static boolean enabledRw = true;\n            private void init() {\n                try {\n                    PlatformAconfigPackageInternal reader = PlatformAconfigPackageInternal.load(\"com.android.aconfig.test\", 0x5081CE7221C77064L);\n                    disabledRw = reader.getBooleanFlagValue(0);\n                    disabledRwExported = reader.getBooleanFlagValue(1);\n                    enabledRw = reader.getBooleanFlagValue(7);\n                    disabledRwInOtherNamespace = reader.getBooleanFlagValue(2);\n                } catch (Exception e) {\n                    Log.e(TAG, e.toString());\n                } catch (LinkageError e) {\n                    // for mainline module running on older devices.\n                    // This should be replaces to version check, after the version bump.\n                    Log.e(TAG, e.toString());\n                }\n                isCached = true;\n            }\n\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean disabledRo() {\n                return false;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean disabledRw() {\n                if (!isCached) {\n                    init();\n                }\n                return disabledRw;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean disabledRwExported() {\n                if (!isCached) {\n                    init();\n                }\n                return disabledRwExported;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean disabledRwInOtherNamespace() {\n                if (!isCached) {\n                    init();\n                }\n                return disabledRwInOtherNamespace;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean enabledFixedRo() {\n                return true;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean enabledFixedRoExported() {\n                return true;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean enabledRo() {\n                return true;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean enabledRoExported() {\n                return true;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean enabledRw() {\n                if (!isCached) {\n                    init();\n                }\n                return enabledRw;\n            }\n        }\n        \"#;\n\n        let mut file_set = HashMap::from([\n            (\"com/android/aconfig/test/Flags.java\", expect_flags_content.as_str()),\n            (\"com/android/aconfig/test/FeatureFlagsImpl.java\", expected_featureflagsmpl_content),\n            (\"com/android/aconfig/test/FeatureFlags.java\", EXPECTED_FEATUREFLAGS_COMMON_CONTENT),\n            (\n                \"com/android/aconfig/test/CustomFeatureFlags.java\",\n                EXPECTED_CUSTOMFEATUREFLAGS_CONTENT,\n            ),\n            (\n                \"com/android/aconfig/test/FakeFeatureFlagsImpl.java\",\n                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,\n            ),\n        ]);\n\n        for file in generated_files {\n            let file_path = file.path.to_str().unwrap();\n            assert!(file_set.contains_key(file_path), \"Cannot find {}\", file_path);\n            assert_eq!(\n                None,\n                crate::test::first_significant_code_diff(\n                    file_set.get(file_path).unwrap(),\n                    &String::from_utf8(file.contents).unwrap()\n                ),\n                \"File {} content is not correct\",\n                file_path\n            );\n            file_set.remove(file_path);\n        }\n\n        assert!(file_set.is_empty());\n    }\n\n    #[test]\n    fn test_generate_java_code_exported() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let mode = CodegenMode::Exported;\n        let modified_parsed_flags =\n            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();\n        let flag_ids =\n            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();\n        let config = JavaCodegenConfig {\n            codegen_mode: mode,\n            flag_ids,\n            allow_instrumentation: true,\n            package_fingerprint: 5801144784618221668,\n            new_exported: false,\n            single_exported_file: false,\n            finalized_flags: FinalizedFlagMap::new(),\n        };\n        let generated_files = generate_java_code(\n            crate::test::TEST_PACKAGE,\n            modified_parsed_flags.into_iter(),\n            config,\n        )\n        .unwrap();\n\n        let expect_flags_content = r#\"\n        package com.android.aconfig.test;\n        import android.os.Build;\n        /** @hide */\n        public final class Flags {\n            /** @hide */\n            public static final String FLAG_DISABLED_RW_EXPORTED = \"com.android.aconfig.test.disabled_rw_exported\";\n            /** @hide */\n            public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = \"com.android.aconfig.test.enabled_fixed_ro_exported\";\n            /** @hide */\n            public static final String FLAG_ENABLED_RO_EXPORTED = \"com.android.aconfig.test.enabled_ro_exported\";\n            public static boolean disabledRwExported() {\n                return FEATURE_FLAGS.disabledRwExported();\n            }\n            public static boolean enabledFixedRoExported() {\n                return FEATURE_FLAGS.enabledFixedRoExported();\n            }\n            public static boolean enabledRoExported() {\n                return FEATURE_FLAGS.enabledRoExported();\n            }\n            private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();\n        }\n        \"#;\n\n        let expect_feature_flags_content = r#\"\n        package com.android.aconfig.test;\n        /** @hide */\n        public interface FeatureFlags {\n            boolean disabledRwExported();\n            boolean enabledFixedRoExported();\n            boolean enabledRoExported();\n        }\n        \"#;\n\n        let expect_feature_flags_impl_content = r#\"\n        package com.android.aconfig.test;\n        import android.os.Binder;\n        import android.provider.DeviceConfig;\n        import android.provider.DeviceConfig.Properties;\n        /** @hide */\n        public final class FeatureFlagsImpl implements FeatureFlags {\n            private static volatile boolean aconfig_test_is_cached = false;\n            private static boolean disabledRwExported = false;\n            private static boolean enabledFixedRoExported = false;\n            private static boolean enabledRoExported = false;\n\n            private void load_overrides_aconfig_test() {\n                final long ident = Binder.clearCallingIdentity();\n                try {\n                    Properties properties = DeviceConfig.getProperties(\"aconfig_test\");\n                    disabledRwExported =\n                        properties.getBoolean(Flags.FLAG_DISABLED_RW_EXPORTED, false);\n                    enabledFixedRoExported =\n                        properties.getBoolean(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false);\n                    enabledRoExported =\n                        properties.getBoolean(Flags.FLAG_ENABLED_RO_EXPORTED, false);\n                } catch (NullPointerException e) {\n                    throw new RuntimeException(\n                        \"Cannot read value from namespace aconfig_test \"\n                        + \"from DeviceConfig. It could be that the code using flag \"\n                        + \"executed before SettingsProvider initialization. Please use \"\n                        + \"fixed read-only flag by adding is_fixed_read_only: true in \"\n                        + \"flag declaration.\",\n                        e\n                    );\n                } catch (SecurityException e) {\n                    // for isolated process case, skip loading flag value from the storage, use the default\n                } finally {\n                    Binder.restoreCallingIdentity(ident);\n                }\n                aconfig_test_is_cached = true;\n            }\n            @Override\n            public boolean disabledRwExported() {\n                if (!aconfig_test_is_cached) {\n                        load_overrides_aconfig_test();\n                }\n                return disabledRwExported;\n            }\n            @Override\n            public boolean enabledFixedRoExported() {\n                if (!aconfig_test_is_cached) {\n                        load_overrides_aconfig_test();\n                }\n                return enabledFixedRoExported;\n            }\n            @Override\n            public boolean enabledRoExported() {\n                if (!aconfig_test_is_cached) {\n                        load_overrides_aconfig_test();\n                }\n                return enabledRoExported;\n            }\n        }\"#;\n\n        let expect_custom_feature_flags_content = r#\"\n        package com.android.aconfig.test;\n\n        import java.util.Arrays;\n        import java.util.HashMap;\n        import java.util.Map;\n        import java.util.HashSet;\n        import java.util.List;\n        import java.util.Set;\n        import java.util.function.BiPredicate;\n        import java.util.function.Predicate;\n\n        import android.os.Build;\n\n        /** @hide */\n        public class CustomFeatureFlags implements FeatureFlags {\n\n            private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;\n\n            public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {\n                mGetValueImpl = getValueImpl;\n            }\n\n            @Override\n            public boolean disabledRwExported() {\n                return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,\n                    FeatureFlags::disabledRwExported);\n            }\n            @Override\n            public boolean enabledFixedRoExported() {\n                return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,\n                    FeatureFlags::enabledFixedRoExported);\n            }\n            @Override\n            public boolean enabledRoExported() {\n                return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,\n                    FeatureFlags::enabledRoExported);\n            }\n\n            protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {\n                return mGetValueImpl.test(flagName, getter);\n            }\n\n            public List<String> getFlagNames() {\n                return Arrays.asList(\n                    Flags.FLAG_DISABLED_RW_EXPORTED,\n                    Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,\n                    Flags.FLAG_ENABLED_RO_EXPORTED\n                );\n            }\n\n            private Set<String> mReadOnlyFlagsSet = new HashSet<>(\n                Arrays.asList(\n                    \"\"\n                )\n            );\n\n            private Map<String, Integer> mFinalizedFlags = new HashMap<>(\n                Map.ofEntries(\n                    Map.entry(\"\", Integer.MAX_VALUE)\n                )\n            );\n\n            public boolean isFlagFinalized(String flagName) {\n                if (!mFinalizedFlags.containsKey(flagName)) {\n                    return false;\n                }\n                return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName);\n            }\n        }\n    \"#;\n\n        let mut file_set = HashMap::from([\n            (\"com/android/aconfig/test/Flags.java\", expect_flags_content),\n            (\"com/android/aconfig/test/FeatureFlags.java\", expect_feature_flags_content),\n            (\"com/android/aconfig/test/FeatureFlagsImpl.java\", expect_feature_flags_impl_content),\n            (\n                \"com/android/aconfig/test/CustomFeatureFlags.java\",\n                expect_custom_feature_flags_content,\n            ),\n            (\n                \"com/android/aconfig/test/FakeFeatureFlagsImpl.java\",\n                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,\n            ),\n        ]);\n\n        for file in generated_files {\n            let file_path = file.path.to_str().unwrap();\n            assert!(file_set.contains_key(file_path), \"Cannot find {}\", file_path);\n            assert_eq!(\n                None,\n                crate::test::first_significant_code_diff(\n                    file_set.get(file_path).unwrap(),\n                    &String::from_utf8(file.contents).unwrap()\n                ),\n                \"File {} content is not correct\",\n                file_path\n            );\n            file_set.remove(file_path);\n        }\n\n        assert!(file_set.is_empty());\n    }\n\n    #[test]\n    fn test_generate_java_code_new_exported() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let mode = CodegenMode::Exported;\n        let modified_parsed_flags =\n            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();\n        let flag_ids =\n            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();\n        let config = JavaCodegenConfig {\n            codegen_mode: mode,\n            flag_ids,\n            allow_instrumentation: true,\n            package_fingerprint: 5801144784618221668,\n            new_exported: true,\n            single_exported_file: false,\n            finalized_flags: FinalizedFlagMap::new(),\n        };\n        let generated_files = generate_java_code(\n            crate::test::TEST_PACKAGE,\n            modified_parsed_flags.into_iter(),\n            config,\n        )\n        .unwrap();\n\n        let expect_flags_content = r#\"\n        package com.android.aconfig.test;\n        import android.os.Build;\n        /** @hide */\n        public final class Flags {\n            /** @hide */\n            public static final String FLAG_DISABLED_RW_EXPORTED = \"com.android.aconfig.test.disabled_rw_exported\";\n            /** @hide */\n            public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = \"com.android.aconfig.test.enabled_fixed_ro_exported\";\n            /** @hide */\n            public static final String FLAG_ENABLED_RO_EXPORTED = \"com.android.aconfig.test.enabled_ro_exported\";\n            public static boolean disabledRwExported() {\n                return FEATURE_FLAGS.disabledRwExported();\n            }\n            public static boolean enabledFixedRoExported() {\n                return FEATURE_FLAGS.enabledFixedRoExported();\n            }\n            public static boolean enabledRoExported() {\n                return FEATURE_FLAGS.enabledRoExported();\n            }\n            private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();\n        }\n        \"#;\n\n        let expect_feature_flags_content = r#\"\n        package com.android.aconfig.test;\n        /** @hide */\n        public interface FeatureFlags {\n            boolean disabledRwExported();\n            boolean enabledFixedRoExported();\n            boolean enabledRoExported();\n        }\n        \"#;\n\n        let expect_feature_flags_impl_content = r#\"\n        package com.android.aconfig.test;\n        import android.os.Build;\n        import android.os.flagging.AconfigPackage;\n        import android.util.Log;\n        /** @hide */\n        public final class FeatureFlagsImpl implements FeatureFlags {\n            private static final String TAG = \"FeatureFlagsImplExport\";\n            private static volatile boolean isCached = false;\n            private static boolean disabledRwExported = false;\n            private static boolean enabledFixedRoExported = false;\n            private static boolean enabledRoExported = false;\n            private void init() {\n                try {\n                    AconfigPackage reader = AconfigPackage.load(\"com.android.aconfig.test\");\n                    disabledRwExported = reader.getBooleanFlagValue(\"disabled_rw_exported\", false);\n                    enabledFixedRoExported = reader.getBooleanFlagValue(\"enabled_fixed_ro_exported\", false);\n                    enabledRoExported = reader.getBooleanFlagValue(\"enabled_ro_exported\", false);\n                } catch (Exception e) {\n                    // pass\n                    Log.e(TAG, e.toString());\n                } catch (LinkageError e) {\n                    // for mainline module running on older devices.\n                    // This should be replaces to version check, after the version bump.\n                    Log.w(TAG, e.toString());\n                }\n                isCached = true;\n            }\n            @Override\n            public boolean disabledRwExported() {\n                if (!isCached) {\n                    init();\n                }\n                return disabledRwExported;\n            }\n            @Override\n            public boolean enabledFixedRoExported() {\n                if (!isCached) {\n                    init();\n                }\n                return enabledFixedRoExported;\n            }\n            @Override\n            public boolean enabledRoExported() {\n                if (!isCached) {\n                    init();\n                }\n                return enabledRoExported;\n            }\n        }\"#;\n\n        let expect_custom_feature_flags_content = r#\"\n        package com.android.aconfig.test;\n\n        import java.util.Arrays;\n        import java.util.HashMap;\n        import java.util.Map;\n        import java.util.HashSet;\n        import java.util.List;\n        import java.util.Set;\n        import java.util.function.BiPredicate;\n        import java.util.function.Predicate;\n        import android.os.Build;\n\n        /** @hide */\n        public class CustomFeatureFlags implements FeatureFlags {\n\n            private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;\n\n            public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {\n                mGetValueImpl = getValueImpl;\n            }\n\n            @Override\n            public boolean disabledRwExported() {\n                return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,\n                    FeatureFlags::disabledRwExported);\n            }\n            @Override\n            public boolean enabledFixedRoExported() {\n                return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,\n                    FeatureFlags::enabledFixedRoExported);\n            }\n            @Override\n            public boolean enabledRoExported() {\n                return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,\n                    FeatureFlags::enabledRoExported);\n            }\n\n            protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {\n                return mGetValueImpl.test(flagName, getter);\n            }\n\n            public List<String> getFlagNames() {\n                return Arrays.asList(\n                    Flags.FLAG_DISABLED_RW_EXPORTED,\n                    Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,\n                    Flags.FLAG_ENABLED_RO_EXPORTED\n                );\n            }\n\n            private Set<String> mReadOnlyFlagsSet = new HashSet<>(\n                Arrays.asList(\n                    \"\"\n                )\n            );\n\n            private Map<String, Integer> mFinalizedFlags = new HashMap<>(\n                Map.ofEntries(\n                    Map.entry(\"\", Integer.MAX_VALUE)\n                )\n            );\n\n            public boolean isFlagFinalized(String flagName) {\n                if (!mFinalizedFlags.containsKey(flagName)) {\n                    return false;\n                }\n                return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName);\n            }\n        }\n    \"#;\n\n        let mut file_set = HashMap::from([\n            (\"com/android/aconfig/test/Flags.java\", expect_flags_content),\n            (\"com/android/aconfig/test/FeatureFlags.java\", expect_feature_flags_content),\n            (\"com/android/aconfig/test/FeatureFlagsImpl.java\", expect_feature_flags_impl_content),\n            (\n                \"com/android/aconfig/test/CustomFeatureFlags.java\",\n                expect_custom_feature_flags_content,\n            ),\n            (\n                \"com/android/aconfig/test/FakeFeatureFlagsImpl.java\",\n                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,\n            ),\n        ]);\n\n        for file in generated_files {\n            let file_path = file.path.to_str().unwrap();\n            assert!(file_set.contains_key(file_path), \"Cannot find {}\", file_path);\n            assert_eq!(\n                None,\n                crate::test::first_significant_code_diff(\n                    file_set.get(file_path).unwrap(),\n                    &String::from_utf8(file.contents).unwrap()\n                ),\n                \"File {} content is not correct\",\n                file_path\n            );\n            file_set.remove(file_path);\n        }\n\n        assert!(file_set.is_empty());\n    }\n\n    #[test]\n    fn test_generate_java_code_new_exported_with_sdk_check() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let mode = CodegenMode::Exported;\n        let modified_parsed_flags =\n            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();\n        let flag_ids =\n            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();\n        let mut finalized_flags = FinalizedFlagMap::new();\n        finalized_flags.insert_if_new(\n            ApiLevel(36),\n            FinalizedFlag {\n                flag_name: \"disabled_rw_exported\".to_string(),\n                package_name: \"com.android.aconfig.test\".to_string(),\n            },\n        );\n        let config = JavaCodegenConfig {\n            codegen_mode: mode,\n            flag_ids,\n            allow_instrumentation: true,\n            package_fingerprint: 5801144784618221668,\n            new_exported: true,\n            single_exported_file: false,\n            finalized_flags,\n        };\n        let generated_files = generate_java_code(\n            crate::test::TEST_PACKAGE,\n            modified_parsed_flags.into_iter(),\n            config,\n        )\n        .unwrap();\n\n        let expect_flags_content = r#\"\n        package com.android.aconfig.test;\n        import android.os.Build;\n        /** @hide */\n        public final class Flags {\n            /** @hide */\n            public static final String FLAG_DISABLED_RW_EXPORTED = \"com.android.aconfig.test.disabled_rw_exported\";\n            /** @hide */\n            public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = \"com.android.aconfig.test.enabled_fixed_ro_exported\";\n            /** @hide */\n            public static final String FLAG_ENABLED_RO_EXPORTED = \"com.android.aconfig.test.enabled_ro_exported\";\n            public static boolean disabledRwExported() {\n                if (Build.VERSION.SDK_INT >= 36) {\n                  return true;\n                }\n                return FEATURE_FLAGS.disabledRwExported();\n            }\n            public static boolean enabledFixedRoExported() {\n                return FEATURE_FLAGS.enabledFixedRoExported();\n            }\n            public static boolean enabledRoExported() {\n                return FEATURE_FLAGS.enabledRoExported();\n            }\n            private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();\n        }\n        \"#;\n\n        let expect_feature_flags_content = r#\"\n        package com.android.aconfig.test;\n        /** @hide */\n        public interface FeatureFlags {\n            boolean disabledRwExported();\n            boolean enabledFixedRoExported();\n            boolean enabledRoExported();\n        }\n        \"#;\n\n        let expect_feature_flags_impl_content = r#\"\n        package com.android.aconfig.test;\n        import android.os.Build;\n        import android.os.flagging.AconfigPackage;\n        import android.util.Log;\n        /** @hide */\n        public final class FeatureFlagsImpl implements FeatureFlags {\n            private static final String TAG = \"FeatureFlagsImplExport\";\n            private static volatile boolean isCached = false;\n            private static boolean disabledRwExported = false;\n            private static boolean enabledFixedRoExported = false;\n            private static boolean enabledRoExported = false;\n            private void init() {\n                try {\n                    AconfigPackage reader = AconfigPackage.load(\"com.android.aconfig.test\");\n                    disabledRwExported = Build.VERSION.SDK_INT >= 36 ? true : reader.getBooleanFlagValue(\"disabled_rw_exported\", false);\n                    enabledFixedRoExported = reader.getBooleanFlagValue(\"enabled_fixed_ro_exported\", false);\n                    enabledRoExported = reader.getBooleanFlagValue(\"enabled_ro_exported\", false);\n                } catch (Exception e) {\n                    // pass\n                    Log.e(TAG, e.toString());\n                } catch (LinkageError e) {\n                    // for mainline module running on older devices.\n                    // This should be replaces to version check, after the version bump.\n                    Log.w(TAG, e.toString());\n                }\n                isCached = true;\n            }\n            @Override\n            public boolean disabledRwExported() {\n                if (!isCached) {\n                    init();\n                }\n                return disabledRwExported;\n            }\n            @Override\n            public boolean enabledFixedRoExported() {\n                if (!isCached) {\n                    init();\n                }\n                return enabledFixedRoExported;\n            }\n            @Override\n            public boolean enabledRoExported() {\n                if (!isCached) {\n                    init();\n                }\n                return enabledRoExported;\n            }\n        }\"#;\n\n        let expect_custom_feature_flags_content = r#\"\n        package com.android.aconfig.test;\n\n        import java.util.Arrays;\n        import java.util.HashMap;\n        import java.util.Map;\n        import java.util.HashSet;\n        import java.util.List;\n        import java.util.Set;\n        import java.util.function.BiPredicate;\n        import java.util.function.Predicate;\n        import android.os.Build;\n\n        /** @hide */\n        public class CustomFeatureFlags implements FeatureFlags {\n\n            private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;\n\n            public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {\n                mGetValueImpl = getValueImpl;\n            }\n\n            @Override\n            public boolean disabledRwExported() {\n                return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,\n                    FeatureFlags::disabledRwExported);\n            }\n            @Override\n            public boolean enabledFixedRoExported() {\n                return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,\n                    FeatureFlags::enabledFixedRoExported);\n            }\n            @Override\n            public boolean enabledRoExported() {\n                return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,\n                    FeatureFlags::enabledRoExported);\n            }\n\n            protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {\n                return mGetValueImpl.test(flagName, getter);\n            }\n\n            public List<String> getFlagNames() {\n                return Arrays.asList(\n                    Flags.FLAG_DISABLED_RW_EXPORTED,\n                    Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,\n                    Flags.FLAG_ENABLED_RO_EXPORTED\n                );\n            }\n\n            private Set<String> mReadOnlyFlagsSet = new HashSet<>(\n                Arrays.asList(\n                    \"\"\n                )\n            );\n\n            private Map<String, Integer> mFinalizedFlags = new HashMap<>(\n                Map.ofEntries(\n                    Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, 36),\n                    Map.entry(\"\", Integer.MAX_VALUE)\n                )\n            );\n\n            public boolean isFlagFinalized(String flagName) {\n                if (!mFinalizedFlags.containsKey(flagName)) {\n                    return false;\n                }\n                return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName);\n            }\n        }\n    \"#;\n\n        let mut file_set = HashMap::from([\n            (\"com/android/aconfig/test/Flags.java\", expect_flags_content),\n            (\"com/android/aconfig/test/FeatureFlags.java\", expect_feature_flags_content),\n            (\"com/android/aconfig/test/FeatureFlagsImpl.java\", expect_feature_flags_impl_content),\n            (\n                \"com/android/aconfig/test/CustomFeatureFlags.java\",\n                expect_custom_feature_flags_content,\n            ),\n            (\n                \"com/android/aconfig/test/FakeFeatureFlagsImpl.java\",\n                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,\n            ),\n        ]);\n\n        for file in generated_files {\n            let file_path = file.path.to_str().unwrap();\n            assert!(file_set.contains_key(file_path), \"Cannot find {}\", file_path);\n            assert_eq!(\n                None,\n                crate::test::first_significant_code_diff(\n                    file_set.get(file_path).unwrap(),\n                    &String::from_utf8(file.contents).unwrap()\n                ),\n                \"File {} content is not correct\",\n                file_path\n            );\n            file_set.remove(file_path);\n        }\n\n        assert!(file_set.is_empty());\n    }\n\n    // Test that the SDK check isn't added unless the library is exported (even\n    // if the flag is present in finalized_flags).\n    #[test]\n    fn test_generate_java_code_flags_with_sdk_check() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let mode = CodegenMode::Production;\n        let modified_parsed_flags =\n            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();\n        let flag_ids =\n            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();\n        let mut finalized_flags = FinalizedFlagMap::new();\n        finalized_flags.insert_if_new(\n            ApiLevel(36),\n            FinalizedFlag {\n                flag_name: \"disabled_rw\".to_string(),\n                package_name: \"com.android.aconfig.test\".to_string(),\n            },\n        );\n        let config = JavaCodegenConfig {\n            codegen_mode: mode,\n            flag_ids,\n            allow_instrumentation: true,\n            package_fingerprint: 5801144784618221668,\n            new_exported: true,\n            single_exported_file: false,\n            finalized_flags,\n        };\n        let generated_files = generate_java_code(\n            crate::test::TEST_PACKAGE,\n            modified_parsed_flags.into_iter(),\n            config,\n        )\n        .unwrap();\n\n        let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()\n            + r#\"\n        private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();\n        }\"#;\n\n        let file = generated_files.iter().find(|f| f.path.ends_with(\"Flags.java\")).unwrap();\n        assert_eq!(\n            None,\n            crate::test::first_significant_code_diff(\n                &expect_flags_content,\n                &String::from_utf8(file.contents.clone()).unwrap()\n            ),\n            \"Flags content is not correct\"\n        );\n    }\n\n    #[test]\n    fn test_generate_java_code_test() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let mode = CodegenMode::Test;\n        let modified_parsed_flags =\n            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();\n        let flag_ids =\n            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();\n        let config = JavaCodegenConfig {\n            codegen_mode: mode,\n            flag_ids,\n            allow_instrumentation: true,\n            package_fingerprint: 5801144784618221668,\n            new_exported: false,\n            single_exported_file: false,\n            finalized_flags: FinalizedFlagMap::new(),\n        };\n        let generated_files = generate_java_code(\n            crate::test::TEST_PACKAGE,\n            modified_parsed_flags.into_iter(),\n            config,\n        )\n        .unwrap();\n\n        let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()\n            + r#\"\n            public static void setFeatureFlags(FeatureFlags featureFlags) {\n                Flags.FEATURE_FLAGS = featureFlags;\n            }\n            public static void unsetFeatureFlags() {\n                Flags.FEATURE_FLAGS = null;\n            }\n            private static FeatureFlags FEATURE_FLAGS;\n        }\n        \"#;\n        let expect_featureflagsimpl_content = r#\"\n        package com.android.aconfig.test;\n        /** @hide */\n        public final class FeatureFlagsImpl implements FeatureFlags {\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            public boolean disabledRo() {\n                throw new UnsupportedOperationException(\n                    \"Method is not implemented.\");\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            public boolean disabledRw() {\n                throw new UnsupportedOperationException(\n                    \"Method is not implemented.\");\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            public boolean disabledRwExported() {\n                throw new UnsupportedOperationException(\n                    \"Method is not implemented.\");\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            public boolean disabledRwInOtherNamespace() {\n                throw new UnsupportedOperationException(\n                    \"Method is not implemented.\");\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            public boolean enabledFixedRo() {\n                throw new UnsupportedOperationException(\n                    \"Method is not implemented.\");\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            public boolean enabledFixedRoExported() {\n                throw new UnsupportedOperationException(\n                    \"Method is not implemented.\");\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            public boolean enabledRo() {\n                throw new UnsupportedOperationException(\n                    \"Method is not implemented.\");\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            public boolean enabledRoExported() {\n                throw new UnsupportedOperationException(\n                    \"Method is not implemented.\");\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            public boolean enabledRw() {\n                throw new UnsupportedOperationException(\n                    \"Method is not implemented.\");\n            }\n        }\n        \"#;\n\n        let mut file_set = HashMap::from([\n            (\"com/android/aconfig/test/Flags.java\", expect_flags_content.as_str()),\n            (\"com/android/aconfig/test/FeatureFlags.java\", EXPECTED_FEATUREFLAGS_COMMON_CONTENT),\n            (\"com/android/aconfig/test/FeatureFlagsImpl.java\", expect_featureflagsimpl_content),\n            (\n                \"com/android/aconfig/test/CustomFeatureFlags.java\",\n                EXPECTED_CUSTOMFEATUREFLAGS_CONTENT,\n            ),\n            (\n                \"com/android/aconfig/test/FakeFeatureFlagsImpl.java\",\n                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,\n            ),\n        ]);\n\n        for file in generated_files {\n            let file_path = file.path.to_str().unwrap();\n            assert!(file_set.contains_key(file_path), \"Cannot find {}\", file_path);\n            assert_eq!(\n                None,\n                crate::test::first_significant_code_diff(\n                    file_set.get(file_path).unwrap(),\n                    &String::from_utf8(file.contents).unwrap()\n                ),\n                \"File {} content is not correct\",\n                file_path\n            );\n            file_set.remove(file_path);\n        }\n\n        assert!(file_set.is_empty());\n    }\n\n    #[test]\n    fn test_generate_java_code_force_read_only() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let mode = CodegenMode::ForceReadOnly;\n        let modified_parsed_flags =\n            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();\n        let flag_ids =\n            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();\n        let config = JavaCodegenConfig {\n            codegen_mode: mode,\n            flag_ids,\n            allow_instrumentation: true,\n            package_fingerprint: 5801144784618221668,\n            new_exported: false,\n            single_exported_file: false,\n            finalized_flags: FinalizedFlagMap::new(),\n        };\n        let generated_files = generate_java_code(\n            crate::test::TEST_PACKAGE,\n            modified_parsed_flags.into_iter(),\n            config,\n        )\n        .unwrap();\n        let expect_featureflags_content = r#\"\n        package com.android.aconfig.test;\n        // TODO(b/303773055): Remove the annotation after access issue is resolved.\n        import android.compat.annotation.UnsupportedAppUsage;\n        /** @hide */\n        public interface FeatureFlags {\n            @com.android.aconfig.annotations.AssumeFalseForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            boolean disabledRo();\n            @com.android.aconfig.annotations.AssumeFalseForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            boolean disabledRw();\n            @com.android.aconfig.annotations.AssumeFalseForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            boolean disabledRwInOtherNamespace();\n            @com.android.aconfig.annotations.AssumeTrueForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            boolean enabledFixedRo();\n            @com.android.aconfig.annotations.AssumeTrueForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            boolean enabledRo();\n            @com.android.aconfig.annotations.AssumeTrueForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            boolean enabledRw();\n        }\"#;\n\n        let expect_featureflagsimpl_content = r#\"\n        package com.android.aconfig.test;\n        // TODO(b/303773055): Remove the annotation after access issue is resolved.\n        import android.compat.annotation.UnsupportedAppUsage;\n        /** @hide */\n        public final class FeatureFlagsImpl implements FeatureFlags {\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean disabledRo() {\n                return false;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean disabledRw() {\n                return false;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean disabledRwInOtherNamespace() {\n                return false;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean enabledFixedRo() {\n                return true;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean enabledRo() {\n                return true;\n            }\n            @Override\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public boolean enabledRw() {\n                return true;\n            }\n        }\n        \"#;\n\n        let expect_flags_content = r#\"\n        package com.android.aconfig.test;\n        // TODO(b/303773055): Remove the annotation after access issue is resolved.\n        import android.compat.annotation.UnsupportedAppUsage;\n        /** @hide */\n        public final class Flags {\n            /** @hide */\n            public static final String FLAG_DISABLED_RO = \"com.android.aconfig.test.disabled_ro\";\n            /** @hide */\n            public static final String FLAG_DISABLED_RW = \"com.android.aconfig.test.disabled_rw\";\n            /** @hide */\n            public static final String FLAG_DISABLED_RW_IN_OTHER_NAMESPACE = \"com.android.aconfig.test.disabled_rw_in_other_namespace\";\n            /** @hide */\n            public static final String FLAG_ENABLED_FIXED_RO = \"com.android.aconfig.test.enabled_fixed_ro\";\n            /** @hide */\n            public static final String FLAG_ENABLED_RO = \"com.android.aconfig.test.enabled_ro\";\n            /** @hide */\n            public static final String FLAG_ENABLED_RW = \"com.android.aconfig.test.enabled_rw\";\n            @com.android.aconfig.annotations.AssumeFalseForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public static boolean disabledRo() {\n                return FEATURE_FLAGS.disabledRo();\n            }\n            @com.android.aconfig.annotations.AssumeFalseForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public static boolean disabledRw() {\n                return FEATURE_FLAGS.disabledRw();\n            }\n            @com.android.aconfig.annotations.AssumeFalseForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public static boolean disabledRwInOtherNamespace() {\n                return FEATURE_FLAGS.disabledRwInOtherNamespace();\n            }\n            @com.android.aconfig.annotations.AssumeTrueForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public static boolean enabledFixedRo() {\n                return FEATURE_FLAGS.enabledFixedRo();\n            }\n            @com.android.aconfig.annotations.AssumeTrueForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public static boolean enabledRo() {\n                return FEATURE_FLAGS.enabledRo();\n            }\n            @com.android.aconfig.annotations.AssumeTrueForR8\n            @com.android.aconfig.annotations.AconfigFlagAccessor\n            @UnsupportedAppUsage\n            public static boolean enabledRw() {\n                return FEATURE_FLAGS.enabledRw();\n            }\n            private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();\n        }\"#;\n\n        let expect_customfeatureflags_content = r#\"\n        package com.android.aconfig.test;\n\n        // TODO(b/303773055): Remove the annotation after access issue is resolved.\n        import android.compat.annotation.UnsupportedAppUsage;\n        import java.util.Arrays;\n        import java.util.HashSet;\n        import java.util.List;\n        import java.util.Set;\n        import java.util.function.BiPredicate;\n        import java.util.function.Predicate;\n\n        /** @hide */\n        public class CustomFeatureFlags implements FeatureFlags {\n\n            private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;\n\n            public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {\n                mGetValueImpl = getValueImpl;\n            }\n\n            @Override\n            @UnsupportedAppUsage\n            public boolean disabledRo() {\n                return getValue(Flags.FLAG_DISABLED_RO,\n                        FeatureFlags::disabledRo);\n            }\n            @Override\n            @UnsupportedAppUsage\n            public boolean disabledRw() {\n                return getValue(Flags.FLAG_DISABLED_RW,\n                    FeatureFlags::disabledRw);\n            }\n            @Override\n            @UnsupportedAppUsage\n            public boolean disabledRwInOtherNamespace() {\n                return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,\n                    FeatureFlags::disabledRwInOtherNamespace);\n            }\n            @Override\n            @UnsupportedAppUsage\n            public boolean enabledFixedRo() {\n                return getValue(Flags.FLAG_ENABLED_FIXED_RO,\n                    FeatureFlags::enabledFixedRo);\n            }\n            @Override\n            @UnsupportedAppUsage\n            public boolean enabledRo() {\n                return getValue(Flags.FLAG_ENABLED_RO,\n                    FeatureFlags::enabledRo);\n            }\n            @Override\n            @UnsupportedAppUsage\n            public boolean enabledRw() {\n                return getValue(Flags.FLAG_ENABLED_RW,\n                    FeatureFlags::enabledRw);\n            }\n\n            public boolean isFlagReadOnlyOptimized(String flagName) {\n                if (mReadOnlyFlagsSet.contains(flagName) &&\n                    isOptimizationEnabled()) {\n                        return true;\n                }\n                return false;\n            }\n\n            @com.android.aconfig.annotations.AssumeTrueForR8\n            private boolean isOptimizationEnabled() {\n                return false;\n            }\n\n            protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {\n                return mGetValueImpl.test(flagName, getter);\n            }\n\n            public List<String> getFlagNames() {\n                return Arrays.asList(\n                    Flags.FLAG_DISABLED_RO,\n                    Flags.FLAG_DISABLED_RW,\n                    Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,\n                    Flags.FLAG_ENABLED_FIXED_RO,\n                    Flags.FLAG_ENABLED_RO,\n                    Flags.FLAG_ENABLED_RW\n                );\n            }\n\n            private Set<String> mReadOnlyFlagsSet = new HashSet<>(\n                Arrays.asList(\n                    Flags.FLAG_DISABLED_RO,\n                    Flags.FLAG_DISABLED_RW,\n                    Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,\n                    Flags.FLAG_ENABLED_FIXED_RO,\n                    Flags.FLAG_ENABLED_RO,\n                    Flags.FLAG_ENABLED_RW,\n                    \"\"\n                )\n            );\n        }\n        \"#;\n\n        let mut file_set = HashMap::from([\n            (\"com/android/aconfig/test/Flags.java\", expect_flags_content),\n            (\"com/android/aconfig/test/FeatureFlagsImpl.java\", expect_featureflagsimpl_content),\n            (\"com/android/aconfig/test/FeatureFlags.java\", expect_featureflags_content),\n            (\"com/android/aconfig/test/CustomFeatureFlags.java\", expect_customfeatureflags_content),\n            (\n                \"com/android/aconfig/test/FakeFeatureFlagsImpl.java\",\n                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,\n            ),\n        ]);\n\n        for file in generated_files {\n            let file_path = file.path.to_str().unwrap();\n            assert!(file_set.contains_key(file_path), \"Cannot find {}\", file_path);\n            assert_eq!(\n                None,\n                crate::test::first_significant_code_diff(\n                    file_set.get(file_path).unwrap(),\n                    &String::from_utf8(file.contents).unwrap()\n                ),\n                \"File {} content is not correct\",\n                file_path\n            );\n            file_set.remove(file_path);\n        }\n\n        assert!(file_set.is_empty());\n    }\n\n    #[test]\n    fn test_generate_java_code_exported_flags() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let mode = CodegenMode::Exported;\n        let modified_parsed_flags =\n            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();\n        let flag_ids =\n            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();\n        let mut finalized_flags = FinalizedFlagMap::new();\n        finalized_flags.insert_if_new(\n            ApiLevel(36),\n            FinalizedFlag {\n                flag_name: \"disabled_rw_exported\".to_string(),\n                package_name: \"com.android.aconfig.test\".to_string(),\n            },\n        );\n        let config = JavaCodegenConfig {\n            codegen_mode: mode,\n            flag_ids,\n            allow_instrumentation: true,\n            package_fingerprint: 5801144784618221668,\n            new_exported: true,\n            single_exported_file: true,\n            finalized_flags,\n        };\n        let generated_files = generate_java_code(\n            crate::test::TEST_PACKAGE,\n            modified_parsed_flags.into_iter(),\n            config,\n        )\n        .unwrap();\n\n        let expect_exported_flags_content = r#\"\n        package com.android.aconfig.test;\n\n        import android.os.Build;\n        import android.os.flagging.AconfigPackage;\n        import android.util.Log;\n        public final class ExportedFlags {\n\n            public static final String FLAG_DISABLED_RW_EXPORTED = \"com.android.aconfig.test.disabled_rw_exported\";\n            public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = \"com.android.aconfig.test.enabled_fixed_ro_exported\";\n            public static final String FLAG_ENABLED_RO_EXPORTED = \"com.android.aconfig.test.enabled_ro_exported\";\n            private static final String TAG = \"ExportedFlags\";\n            private static volatile boolean isCached = false;\n\n            private static boolean disabledRwExported = false;\n            private static boolean enabledFixedRoExported = false;\n            private static boolean enabledRoExported = false;\n            private ExportedFlags() {}\n\n            private void init() {\n                try {\n                    AconfigPackage reader = AconfigPackage.load(\"com.android.aconfig.test\");\n                    disabledRwExported = reader.getBooleanFlagValue(\"disabled_rw_exported\", false);\n                    enabledFixedRoExported = reader.getBooleanFlagValue(\"enabled_fixed_ro_exported\", false);\n                    enabledRoExported = reader.getBooleanFlagValue(\"enabled_ro_exported\", false);\n                } catch (Exception e) {\n                    // pass\n                    Log.e(TAG, e.toString());\n                } catch (LinkageError e) {\n                    // for mainline module running on older devices.\n                    // This should be replaces to version check, after the version bump.\n                    Log.w(TAG, e.toString());\n                }\n                isCached = true;\n            }\n            public static boolean disabledRwExported() {\n                if (Build.VERSION.SDK_INT >= 36) {\n                  return true;\n                }\n\n                if (!featureFlags.isCached) {\n                    featureFlags.init();\n                }\n                return featureFlags.disabledRwExported;\n            }\n            public static boolean enabledFixedRoExported() {\n                if (!featureFlags.isCached) {\n                    featureFlags.init();\n                }\n                return featureFlags.enabledFixedRoExported;\n            }\n            public static boolean enabledRoExported() {\n                if (!featureFlags.isCached) {\n                    featureFlags.init();\n                }\n                return featureFlags.enabledRoExported;\n            }\n            private static ExportedFlags featureFlags = new ExportedFlags();\n        }\"#;\n\n        let file = generated_files.iter().find(|f| f.path.ends_with(\"ExportedFlags.java\")).unwrap();\n        assert_eq!(\n            None,\n            crate::test::first_significant_code_diff(\n                expect_exported_flags_content,\n                &String::from_utf8(file.contents.clone()).unwrap()\n            ),\n            \"ExportedFlags content is not correct\"\n        );\n    }\n\n    #[test]\n    fn test_format_java_method_name() {\n        let expected = \"someSnakeName\";\n        let input = \"____some_snake___name____\";\n        let formatted_name = format_java_method_name(input);\n        assert_eq!(expected, formatted_name);\n\n        let input = \"someSnakeName\";\n        let formatted_name = format_java_method_name(input);\n        assert_eq!(expected, formatted_name);\n\n        let input = \"SomeSnakeName\";\n        let formatted_name = format_java_method_name(input);\n        assert_eq!(expected, formatted_name);\n\n        let input = \"SomeSnakeName_\";\n        let formatted_name = format_java_method_name(input);\n        assert_eq!(expected, formatted_name);\n\n        let input = \"_SomeSnakeName\";\n        let formatted_name = format_java_method_name(input);\n        assert_eq!(expected, formatted_name);\n    }\n\n    #[test]\n    fn test_format_property_name() {\n        let expected = \"mPropertiesSomeSnakeName\";\n        let input = \"____some_snake___name____\";\n        let formatted_name = format_property_name(input);\n        assert_eq!(expected, formatted_name);\n\n        let input = \"someSnakeName\";\n        let formatted_name = format_property_name(input);\n        assert_eq!(expected, formatted_name);\n\n        let input = \"SomeSnakeName\";\n        let formatted_name = format_property_name(input);\n        assert_eq!(expected, formatted_name);\n\n        let input = \"SomeSnakeName_\";\n        let formatted_name = format_property_name(input);\n        assert_eq!(expected, formatted_name);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/codegen/mod.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npub mod cpp;\npub mod java;\npub mod rust;\n\nuse aconfig_protos::{is_valid_name_ident, is_valid_package_ident};\nuse anyhow::{ensure, Result};\nuse clap::ValueEnum;\n\npub fn create_device_config_ident(package: &str, flag_name: &str) -> Result<String> {\n    ensure!(is_valid_package_ident(package), \"bad package\");\n    ensure!(is_valid_name_ident(flag_name), \"bad flag name\");\n    Ok(format!(\"{}.{}\", package, flag_name))\n}\n\n#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]\npub enum CodegenMode {\n    Exported,\n    ForceReadOnly,\n    Production,\n    Test,\n}\n\nimpl std::fmt::Display for CodegenMode {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n        match self {\n            CodegenMode::Exported => write!(f, \"exported\"),\n            CodegenMode::ForceReadOnly => write!(f, \"force-read-only\"),\n            CodegenMode::Production => write!(f, \"production\"),\n            CodegenMode::Test => write!(f, \"test\"),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    #[test]\n    fn test_create_device_config_ident() {\n        assert_eq!(\n            \"com.foo.bar.some_flag\",\n            create_device_config_ident(\"com.foo.bar\", \"some_flag\").unwrap()\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/codegen/rust.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse anyhow::Result;\nuse serde::Serialize;\nuse tinytemplate::TinyTemplate;\n\nuse aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};\n\nuse std::collections::HashMap;\n\nuse crate::codegen;\nuse crate::codegen::CodegenMode;\nuse crate::commands::{should_include_flag, OutputFile};\n\npub fn generate_rust_code<I>(\n    package: &str,\n    flag_ids: HashMap<String, u16>,\n    parsed_flags_iter: I,\n    codegen_mode: CodegenMode,\n    package_fingerprint: Option<u64>,\n) -> Result<OutputFile>\nwhere\n    I: Iterator<Item = ProtoParsedFlag>,\n{\n    let template_flags: Vec<TemplateParsedFlag> = parsed_flags_iter\n        .map(|pf| TemplateParsedFlag::new(package, flag_ids.clone(), &pf))\n        .collect();\n    let has_readwrite = template_flags.iter().any(|item| item.readwrite);\n    let container = (template_flags.first().expect(\"zero template flags\").container).to_string();\n    let use_package_fingerprint = package_fingerprint.is_some();\n    let context = TemplateContext {\n        package: package.to_string(),\n        template_flags,\n        modules: package.split('.').map(|s| s.to_string()).collect::<Vec<_>>(),\n        has_readwrite,\n        container,\n        use_package_fingerprint,\n        package_fingerprint: package_fingerprint.unwrap_or_default(),\n    };\n    let mut template = TinyTemplate::new();\n    template.add_template(\n        \"rust_code_gen\",\n        match codegen_mode {\n            CodegenMode::Test => include_str!(\"../../templates/rust_test.template\"),\n            CodegenMode::Exported | CodegenMode::ForceReadOnly | CodegenMode::Production => {\n                include_str!(\"../../templates/rust.template\")\n            }\n        },\n    )?;\n    let contents = template.render(\"rust_code_gen\", &context)?;\n    let path = [\"src\", \"lib.rs\"].iter().collect();\n    Ok(OutputFile { contents: contents.into(), path })\n}\n\n#[derive(Serialize)]\nstruct TemplateContext {\n    pub package: String,\n    pub template_flags: Vec<TemplateParsedFlag>,\n    pub modules: Vec<String>,\n    pub has_readwrite: bool,\n    pub container: String,\n    pub use_package_fingerprint: bool,\n    pub package_fingerprint: u64,\n}\n\n#[derive(Serialize)]\nstruct TemplateParsedFlag {\n    pub readwrite: bool,\n    pub default_value: String,\n    pub name: String,\n    pub container: String,\n    pub flag_offset: u16,\n    pub device_config_namespace: String,\n    pub device_config_flag: String,\n}\n\nimpl TemplateParsedFlag {\n    #[allow(clippy::nonminimal_bool)]\n    fn new(package: &str, flag_offsets: HashMap<String, u16>, pf: &ProtoParsedFlag) -> Self {\n        let flag_offset = match flag_offsets.get(pf.name()) {\n            Some(offset) => offset,\n            None => {\n                // System/vendor/product RO+disabled flags have no offset in storage files.\n                // Assign placeholder value.\n                if !should_include_flag(pf) {\n                    &0\n                }\n                // All other flags _must_ have an offset.\n                else {\n                    panic!(\"{}\", format!(\"missing flag offset for {}\", pf.name()));\n                }\n            }\n        };\n\n        Self {\n            readwrite: pf.permission() == ProtoFlagPermission::READ_WRITE,\n            default_value: match pf.state() {\n                ProtoFlagState::ENABLED => \"true\".to_string(),\n                ProtoFlagState::DISABLED => \"false\".to_string(),\n            },\n            name: pf.name().to_string(),\n            container: pf.container().to_string(),\n            flag_offset: *flag_offset,\n            device_config_namespace: pf.namespace().to_string(),\n            device_config_flag: codegen::create_device_config_ident(package, pf.name())\n                .expect(\"values checked at flag parse time\"),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    const PROD_EXPECTED: &str = r#\"\n//! codegenerated rust flag lib\nuse aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};\nuse std::path::Path;\nuse std::io::Write;\nuse std::sync::LazyLock;\nuse log::{log, LevelFilter, Level};\n\n/// flag provider\npub struct FlagProvider;\n\nstatic PACKAGE_CONTEXT: LazyLock<Result<Option<PackageReadContext>, AconfigStorageError>> = LazyLock::new(|| unsafe {\n    get_mapped_storage_file(\"system\", StorageFileType::PackageMap)\n    .and_then(|package_map| get_package_read_context(&package_map, \"com.android.aconfig.test\"))\n});\n\nstatic FLAG_VAL_MAP: LazyLock<Result<Mmap, AconfigStorageError>> = LazyLock::new(|| unsafe {\n    get_mapped_storage_file(\"system\", StorageFileType::FlagVal)\n});\n\n/// flag value cache for disabled_rw\nstatic CACHED_disabled_rw: LazyLock<bool> = LazyLock::new(|| {\n    // This will be called multiple times. Subsequent calls after the first are noops.\n    logger::init(\n        logger::Config::default()\n            .with_tag_on_device(\"aconfig_rust_codegen\")\n            .with_max_level(LevelFilter::Info));\n\n    let flag_value_result = FLAG_VAL_MAP\n        .as_ref()\n        .map_err(|err| format!(\"failed to get flag val map: {err}\"))\n        .and_then(|flag_val_map| {\n            PACKAGE_CONTEXT\n              .as_ref()\n               .map_err(|err| format!(\"failed to get package read context: {err}\"))\n               .and_then(|package_context| {\n                   match package_context {\n                       Some(context) => {\n                           get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 0)\n                               .map_err(|err| format!(\"failed to get flag: {err}\"))\n                       },\n                       None => {\n                           log!(Level::Error, \"no context found for package com.android.aconfig.test\");\n                           Err(format!(\"failed to flag package com.android.aconfig.test\"))\n                       }\n                    }\n                })\n            });\n\n    match flag_value_result {\n        Ok(flag_value) => {\n            return flag_value;\n        },\n        Err(err) => {\n            log!(Level::Error, \"aconfig_rust_codegen: error: {err}\");\n            return false;\n        }\n    }\n});\n\n/// flag value cache for disabled_rw_exported\nstatic CACHED_disabled_rw_exported: LazyLock<bool> = LazyLock::new(|| {\n        // This will be called multiple times. Subsequent calls after the first are noops.\n        logger::init(\n            logger::Config::default()\n                .with_tag_on_device(\"aconfig_rust_codegen\")\n                .with_max_level(LevelFilter::Info));\n\n        let flag_value_result = FLAG_VAL_MAP\n            .as_ref()\n            .map_err(|err| format!(\"failed to get flag val map: {err}\"))\n            .and_then(|flag_val_map| {\n                PACKAGE_CONTEXT\n                    .as_ref()\n                .map_err(|err| format!(\"failed to get package read context: {err}\"))\n                .and_then(|package_context| {\n                    match package_context {\n                        Some(context) => {\n                           get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 1)\n                                    .map_err(|err| format!(\"failed to get flag: {err}\"))\n                            },\n                            None => {\n                                log!(Level::Error, \"no context found for package com.android.aconfig.test\");\n                                Err(format!(\"failed to flag package com.android.aconfig.test\"))\n                            }\n                        }\n                    })\n                });\n\n        match flag_value_result {\n            Ok(flag_value) => {\n                 return flag_value;\n            },\n            Err(err) => {\n                log!(Level::Error, \"aconfig_rust_codegen: error: {err}\");\n                return false;\n            }\n        }\n});\n\n/// flag value cache for disabled_rw_in_other_namespace\nstatic CACHED_disabled_rw_in_other_namespace: LazyLock<bool> = LazyLock::new(|| {\n        // This will be called multiple times. Subsequent calls after the first are noops.\n        logger::init(\n            logger::Config::default()\n                .with_tag_on_device(\"aconfig_rust_codegen\")\n                .with_max_level(LevelFilter::Info));\n\n        let flag_value_result = FLAG_VAL_MAP\n            .as_ref()\n            .map_err(|err| format!(\"failed to get flag val map: {err}\"))\n            .and_then(|flag_val_map| {\n                PACKAGE_CONTEXT\n                    .as_ref()\n                .map_err(|err| format!(\"failed to get package read context: {err}\"))\n                .and_then(|package_context| {\n                    match package_context {\n                        Some(context) => {\n                           get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 2)\n                                    .map_err(|err| format!(\"failed to get flag: {err}\"))\n                            },\n                            None => {\n                                log!(Level::Error, \"no context found for package com.android.aconfig.test\");\n                                Err(format!(\"failed to flag package com.android.aconfig.test\"))\n                            }\n                        }\n                    })\n                });\n\n        match flag_value_result {\n            Ok(flag_value) => {\n                 return flag_value;\n            },\n            Err(err) => {\n                log!(Level::Error, \"aconfig_rust_codegen: error: {err}\");\n                return false;\n            }\n        }\n});\n\n\n/// flag value cache for enabled_rw\nstatic CACHED_enabled_rw: LazyLock<bool> = LazyLock::new(|| {\n        // This will be called multiple times. Subsequent calls after the first are noops.\n        logger::init(\n            logger::Config::default()\n                .with_tag_on_device(\"aconfig_rust_codegen\")\n                .with_max_level(LevelFilter::Info));\n\n        let flag_value_result = FLAG_VAL_MAP\n            .as_ref()\n            .map_err(|err| format!(\"failed to get flag val map: {err}\"))\n            .and_then(|flag_val_map| {\n                PACKAGE_CONTEXT\n                    .as_ref()\n                    .map_err(|err| format!(\"failed to get package read context: {err}\"))\n                    .and_then(|package_context| {\n                      match package_context {\n                            Some(context) => {\n                              get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 7)\n                                      .map_err(|err| format!(\"failed to get flag: {err}\"))\n                            },\n                            None => {\n                                log!(Level::Error, \"no context found for package com.android.aconfig.test\");\n                                Err(format!(\"failed to flag package com.android.aconfig.test\"))\n                            }\n                        }\n                    })\n                });\n\n        match flag_value_result {\n            Ok(flag_value) => {\n                 return flag_value;\n            },\n            Err(err) => {\n                log!(Level::Error, \"aconfig_rust_codegen: error: {err}\");\n                return true;\n            }\n        }\n});\n\nimpl FlagProvider {\n\n\n    /// query flag disabled_ro\n    pub fn disabled_ro(&self) -> bool {\n        false\n    }\n\n    /// query flag disabled_rw\n    pub fn disabled_rw(&self) -> bool {\n        *CACHED_disabled_rw\n    }\n\n    /// query flag disabled_rw_exported\n    pub fn disabled_rw_exported(&self) -> bool {\n        *CACHED_disabled_rw_exported\n    }\n\n    /// query flag disabled_rw_in_other_namespace\n    pub fn disabled_rw_in_other_namespace(&self) -> bool {\n        *CACHED_disabled_rw_in_other_namespace\n    }\n\n    /// query flag enabled_fixed_ro\n    pub fn enabled_fixed_ro(&self) -> bool {\n        true\n    }\n\n    /// query flag enabled_fixed_ro_exported\n    pub fn enabled_fixed_ro_exported(&self) -> bool {\n        true\n    }\n\n    /// query flag enabled_ro\n    pub fn enabled_ro(&self) -> bool {\n        true\n    }\n\n    /// query flag enabled_ro_exported\n    pub fn enabled_ro_exported(&self) -> bool {\n        true\n    }\n\n    /// query flag enabled_rw\n    pub fn enabled_rw(&self) -> bool {\n        *CACHED_enabled_rw\n    }\n\n\n}\n\n/// flag provider\npub static PROVIDER: FlagProvider = FlagProvider;\n\n\n/// query flag disabled_ro\n#[inline(always)]\npub fn disabled_ro() -> bool {\n   false\n}\n\n/// query flag disabled_rw\n#[inline(always)]\npub fn disabled_rw() -> bool {\n    PROVIDER.disabled_rw()\n}\n\n/// query flag disabled_rw_exported\n#[inline(always)]\npub fn disabled_rw_exported() -> bool {\n    PROVIDER.disabled_rw_exported()\n}\n\n/// query flag disabled_rw_in_other_namespace\n#[inline(always)]\npub fn disabled_rw_in_other_namespace() -> bool {\n    PROVIDER.disabled_rw_in_other_namespace()\n}\n\n/// query flag enabled_fixed_ro\n#[inline(always)]\npub fn enabled_fixed_ro() -> bool {\n    true\n}\n\n/// query flag enabled_fixed_ro_exported\n#[inline(always)]\npub fn enabled_fixed_ro_exported() -> bool {\n    true\n}\n\n/// query flag enabled_ro\n#[inline(always)]\npub fn enabled_ro() -> bool {\n    true\n}\n\n/// query flag enabled_ro_exported\n#[inline(always)]\npub fn enabled_ro_exported() -> bool {\n    true\n}\n\n/// query flag enabled_rw\n#[inline(always)]\npub fn enabled_rw() -> bool {\n    PROVIDER.enabled_rw()\n}\n\"#;\n\n    const TEST_EXPECTED: &str = r#\"\n//! codegenerated rust flag lib\nuse aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};\nuse std::collections::BTreeMap;\nuse std::path::Path;\nuse std::io::Write;\nuse std::sync::{LazyLock, Mutex};\nuse log::{log, LevelFilter, Level};\n\n/// flag provider\npub struct FlagProvider {\n    overrides: BTreeMap<&'static str, bool>,\n}\n\nstatic PACKAGE_CONTEXT: LazyLock<Result<Option<PackageReadContext>, AconfigStorageError>> = LazyLock::new(|| unsafe {\n    get_mapped_storage_file(\"system\", StorageFileType::PackageMap)\n    .and_then(|package_map| get_package_read_context(&package_map, \"com.android.aconfig.test\"))\n});\n\nstatic FLAG_VAL_MAP: LazyLock<Result<Mmap, AconfigStorageError>> = LazyLock::new(|| unsafe {\n    get_mapped_storage_file(\"system\", StorageFileType::FlagVal)\n});\n\n/// flag value cache for disabled_rw\nstatic CACHED_disabled_rw: LazyLock<bool> = LazyLock::new(|| {\n    // This will be called multiple times. Subsequent calls after the first are noops.\n    logger::init(\n        logger::Config::default()\n            .with_tag_on_device(\"aconfig_rust_codegen\")\n            .with_max_level(LevelFilter::Info));\n\n    let flag_value_result = FLAG_VAL_MAP\n        .as_ref()\n        .map_err(|err| format!(\"failed to get flag val map: {err}\"))\n        .and_then(|flag_val_map| {\n            PACKAGE_CONTEXT\n               .as_ref()\n                    .map_err(|err| format!(\"failed to get package read context: {err}\"))\n                    .and_then(|package_context| {\n                      match package_context {\n                            Some(context) => {\n                              get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 0)\n                               .map_err(|err| format!(\"failed to get flag: {err}\"))\n                       },\n                       None => {\n                           log!(Level::Error, \"no context found for package com.android.aconfig.test\");\n                           Err(format!(\"failed to flag package com.android.aconfig.test\"))\n                       }\n                    }\n                })\n            });\n\n    match flag_value_result {\n        Ok(flag_value) => {\n            return flag_value;\n        },\n        Err(err) => {\n            log!(Level::Error, \"aconfig_rust_codegen: error: {err}\");\n            return false;\n        }\n    }\n});\n\n/// flag value cache for disabled_rw_exported\nstatic CACHED_disabled_rw_exported: LazyLock<bool> = LazyLock::new(|| {\n        // This will be called multiple times. Subsequent calls after the first are noops.\n        logger::init(\n            logger::Config::default()\n                .with_tag_on_device(\"aconfig_rust_codegen\")\n                .with_max_level(LevelFilter::Info));\n\n        let flag_value_result = FLAG_VAL_MAP\n            .as_ref()\n            .map_err(|err| format!(\"failed to get flag val map: {err}\"))\n            .and_then(|flag_val_map| {\n                PACKAGE_CONTEXT\n                    .as_ref()\n                    .map_err(|err| format!(\"failed to get package read context: {err}\"))\n                    .and_then(|package_context| {\n                      match package_context {\n                            Some(context) => {\n                              get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 1)\n                                    .map_err(|err| format!(\"failed to get flag: {err}\"))\n                            },\n                            None => {\n                                log!(Level::Error, \"no context found for package com.android.aconfig.test\");\n                                Err(format!(\"failed to flag package com.android.aconfig.test\"))\n                            }\n                        }\n                    })\n                });\n\n        match flag_value_result {\n            Ok(flag_value) => {\n                 return flag_value;\n            },\n            Err(err) => {\n                log!(Level::Error, \"aconfig_rust_codegen: error: {err}\");\n                return false;\n            }\n        }\n});\n\n/// flag value cache for disabled_rw_in_other_namespace\nstatic CACHED_disabled_rw_in_other_namespace: LazyLock<bool> = LazyLock::new(|| {\n        // This will be called multiple times. Subsequent calls after the first are noops.\n        logger::init(\n            logger::Config::default()\n                .with_tag_on_device(\"aconfig_rust_codegen\")\n                .with_max_level(LevelFilter::Info));\n\n        let flag_value_result = FLAG_VAL_MAP\n            .as_ref()\n            .map_err(|err| format!(\"failed to get flag val map: {err}\"))\n            .and_then(|flag_val_map| {\n                PACKAGE_CONTEXT\n                    .as_ref()\n                    .map_err(|err| format!(\"failed to get package read context: {err}\"))\n                    .and_then(|package_context| {\n                      match package_context {\n                            Some(context) => {\n                              get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 2)\n                                    .map_err(|err| format!(\"failed to get flag: {err}\"))\n                            },\n                            None => {\n                                log!(Level::Error, \"no context found for package com.android.aconfig.test\");\n                                Err(format!(\"failed to flag package com.android.aconfig.test\"))\n                            }\n                        }\n                    })\n                });\n\n        match flag_value_result {\n            Ok(flag_value) => {\n                 return flag_value;\n            },\n            Err(err) => {\n                log!(Level::Error, \"aconfig_rust_codegen: error: {err}\");\n                return false;\n            }\n        }\n});\n\n\n/// flag value cache for enabled_rw\nstatic CACHED_enabled_rw: LazyLock<bool> = LazyLock::new(|| {\n        // This will be called multiple times. Subsequent calls after the first are noops.\n        logger::init(\n            logger::Config::default()\n                .with_tag_on_device(\"aconfig_rust_codegen\")\n                .with_max_level(LevelFilter::Info));\n\n        let flag_value_result = FLAG_VAL_MAP\n            .as_ref()\n            .map_err(|err| format!(\"failed to get flag val map: {err}\"))\n            .and_then(|flag_val_map| {\n                PACKAGE_CONTEXT\n                    .as_ref()\n                    .map_err(|err| format!(\"failed to get package read context: {err}\"))\n                    .and_then(|package_context| {\n                      match package_context {\n                            Some(context) => {\n                              get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 7)\n                                    .map_err(|err| format!(\"failed to get flag: {err}\"))\n                            },\n                            None => {\n                                log!(Level::Error, \"no context found for package com.android.aconfig.test\");\n                                Err(format!(\"failed to flag package com.android.aconfig.test\"))\n                            }\n                        }\n                    })\n                });\n\n        match flag_value_result {\n            Ok(flag_value) => {\n                 return flag_value;\n            },\n            Err(err) => {\n                log!(Level::Error, \"aconfig_rust_codegen: error: {err}\");\n                return true;\n            }\n        }\n});\n\nimpl FlagProvider {\n    /// query flag disabled_ro\n    pub fn disabled_ro(&self) -> bool {\n        self.overrides.get(\"disabled_ro\").copied().unwrap_or(\n            false\n        )\n    }\n\n    /// set flag disabled_ro\n    pub fn set_disabled_ro(&mut self, val: bool) {\n        self.overrides.insert(\"disabled_ro\", val);\n    }\n\n    /// query flag disabled_rw\n    pub fn disabled_rw(&self) -> bool {\n        self.overrides.get(\"disabled_rw\").copied().unwrap_or(\n            *CACHED_disabled_rw\n        )\n    }\n\n    /// set flag disabled_rw\n    pub fn set_disabled_rw(&mut self, val: bool) {\n        self.overrides.insert(\"disabled_rw\", val);\n    }\n\n    /// query flag disabled_rw_exported\n    pub fn disabled_rw_exported(&self) -> bool {\n        self.overrides.get(\"disabled_rw_exported\").copied().unwrap_or(\n            *CACHED_disabled_rw_exported\n        )\n    }\n\n    /// set flag disabled_rw_exported\n    pub fn set_disabled_rw_exported(&mut self, val: bool) {\n        self.overrides.insert(\"disabled_rw_exported\", val);\n    }\n\n    /// query flag disabled_rw_in_other_namespace\n    pub fn disabled_rw_in_other_namespace(&self) -> bool {\n        self.overrides.get(\"disabled_rw_in_other_namespace\").copied().unwrap_or(\n            *CACHED_disabled_rw_in_other_namespace\n        )\n    }\n\n    /// set flag disabled_rw_in_other_namespace\n    pub fn set_disabled_rw_in_other_namespace(&mut self, val: bool) {\n        self.overrides.insert(\"disabled_rw_in_other_namespace\", val);\n    }\n\n    /// query flag enabled_fixed_ro\n    pub fn enabled_fixed_ro(&self) -> bool {\n        self.overrides.get(\"enabled_fixed_ro\").copied().unwrap_or(\n            true\n        )\n    }\n\n    /// set flag enabled_fixed_ro\n    pub fn set_enabled_fixed_ro(&mut self, val: bool) {\n        self.overrides.insert(\"enabled_fixed_ro\", val);\n    }\n\n    /// query flag enabled_fixed_ro_exported\n    pub fn enabled_fixed_ro_exported(&self) -> bool {\n        self.overrides.get(\"enabled_fixed_ro_exported\").copied().unwrap_or(\n            true\n        )\n    }\n\n    /// set flag enabled_fixed_ro_exported\n    pub fn set_enabled_fixed_ro_exported(&mut self, val: bool) {\n        self.overrides.insert(\"enabled_fixed_ro_exported\", val);\n    }\n\n    /// query flag enabled_ro\n    pub fn enabled_ro(&self) -> bool {\n        self.overrides.get(\"enabled_ro\").copied().unwrap_or(\n            true\n        )\n    }\n\n    /// set flag enabled_ro\n    pub fn set_enabled_ro(&mut self, val: bool) {\n        self.overrides.insert(\"enabled_ro\", val);\n    }\n\n    /// query flag enabled_ro_exported\n    pub fn enabled_ro_exported(&self) -> bool {\n        self.overrides.get(\"enabled_ro_exported\").copied().unwrap_or(\n            true\n        )\n    }\n\n    /// set flag enabled_ro_exported\n    pub fn set_enabled_ro_exported(&mut self, val: bool) {\n        self.overrides.insert(\"enabled_ro_exported\", val);\n    }\n\n    /// query flag enabled_rw\n    pub fn enabled_rw(&self) -> bool {\n        self.overrides.get(\"enabled_rw\").copied().unwrap_or(\n            *CACHED_enabled_rw\n        )\n    }\n\n    /// set flag enabled_rw\n    pub fn set_enabled_rw(&mut self, val: bool) {\n        self.overrides.insert(\"enabled_rw\", val);\n    }\n\n    /// clear all flag overrides\n    pub fn reset_flags(&mut self) {\n        self.overrides.clear();\n    }\n}\n\n/// flag provider\npub static PROVIDER: Mutex<FlagProvider> = Mutex::new(\n    FlagProvider {overrides: BTreeMap::new()}\n);\n\n/// query flag disabled_ro\n#[inline(always)]\npub fn disabled_ro() -> bool {\n    PROVIDER.lock().unwrap().disabled_ro()\n}\n\n/// set flag disabled_ro\n#[inline(always)]\npub fn set_disabled_ro(val: bool) {\n    PROVIDER.lock().unwrap().set_disabled_ro(val);\n}\n\n/// query flag disabled_rw\n#[inline(always)]\npub fn disabled_rw() -> bool {\n    PROVIDER.lock().unwrap().disabled_rw()\n}\n\n/// set flag disabled_rw\n#[inline(always)]\npub fn set_disabled_rw(val: bool) {\n    PROVIDER.lock().unwrap().set_disabled_rw(val);\n}\n\n/// query flag disabled_rw_exported\n#[inline(always)]\npub fn disabled_rw_exported() -> bool {\n    PROVIDER.lock().unwrap().disabled_rw_exported()\n}\n\n/// set flag disabled_rw_exported\n#[inline(always)]\npub fn set_disabled_rw_exported(val: bool) {\n    PROVIDER.lock().unwrap().set_disabled_rw_exported(val);\n}\n\n/// query flag disabled_rw_in_other_namespace\n#[inline(always)]\npub fn disabled_rw_in_other_namespace() -> bool {\n    PROVIDER.lock().unwrap().disabled_rw_in_other_namespace()\n}\n\n/// set flag disabled_rw_in_other_namespace\n#[inline(always)]\npub fn set_disabled_rw_in_other_namespace(val: bool) {\n    PROVIDER.lock().unwrap().set_disabled_rw_in_other_namespace(val);\n}\n\n/// query flag enabled_fixed_ro\n#[inline(always)]\npub fn enabled_fixed_ro() -> bool {\n    PROVIDER.lock().unwrap().enabled_fixed_ro()\n}\n\n/// set flag enabled_fixed_ro\n#[inline(always)]\npub fn set_enabled_fixed_ro(val: bool) {\n    PROVIDER.lock().unwrap().set_enabled_fixed_ro(val);\n}\n\n/// query flag enabled_fixed_ro_exported\n#[inline(always)]\npub fn enabled_fixed_ro_exported() -> bool {\n    PROVIDER.lock().unwrap().enabled_fixed_ro_exported()\n}\n\n/// set flag enabled_fixed_ro_exported\n#[inline(always)]\npub fn set_enabled_fixed_ro_exported(val: bool) {\n    PROVIDER.lock().unwrap().set_enabled_fixed_ro_exported(val);\n}\n\n/// query flag enabled_ro\n#[inline(always)]\npub fn enabled_ro() -> bool {\n    PROVIDER.lock().unwrap().enabled_ro()\n}\n\n/// set flag enabled_ro\n#[inline(always)]\npub fn set_enabled_ro(val: bool) {\n    PROVIDER.lock().unwrap().set_enabled_ro(val);\n}\n\n/// query flag enabled_ro_exported\n#[inline(always)]\npub fn enabled_ro_exported() -> bool {\n    PROVIDER.lock().unwrap().enabled_ro_exported()\n}\n\n/// set flag enabled_ro_exported\n#[inline(always)]\npub fn set_enabled_ro_exported(val: bool) {\n    PROVIDER.lock().unwrap().set_enabled_ro_exported(val);\n}\n\n/// query flag enabled_rw\n#[inline(always)]\npub fn enabled_rw() -> bool {\n    PROVIDER.lock().unwrap().enabled_rw()\n}\n\n/// set flag enabled_rw\n#[inline(always)]\npub fn set_enabled_rw(val: bool) {\n    PROVIDER.lock().unwrap().set_enabled_rw(val);\n}\n\n/// clear all flag override\npub fn reset_flags() {\n    PROVIDER.lock().unwrap().reset_flags()\n}\n\"#;\n\n    const FORCE_READ_ONLY_EXPECTED: &str = r#\"\n//! codegenerated rust flag lib\nuse aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};\nuse std::path::Path;\nuse std::io::Write;\nuse std::sync::LazyLock;\nuse log::{log, LevelFilter, Level};\n\n/// flag provider\npub struct FlagProvider;\n\nimpl FlagProvider {\n    /// query flag disabled_ro\n    pub fn disabled_ro(&self) -> bool {\n        false\n    }\n\n    /// query flag disabled_rw\n    pub fn disabled_rw(&self) -> bool {\n        false\n    }\n\n    /// query flag disabled_rw_in_other_namespace\n    pub fn disabled_rw_in_other_namespace(&self) -> bool {\n        false\n    }\n\n    /// query flag enabled_fixed_ro\n    pub fn enabled_fixed_ro(&self) -> bool {\n        true\n    }\n\n    /// query flag enabled_ro\n    pub fn enabled_ro(&self) -> bool {\n        true\n    }\n\n    /// query flag enabled_rw\n    pub fn enabled_rw(&self) -> bool {\n        true\n    }\n}\n\n/// flag provider\npub static PROVIDER: FlagProvider = FlagProvider;\n\n/// query flag disabled_ro\n#[inline(always)]\npub fn disabled_ro() -> bool {\n    false\n}\n\n/// query flag disabled_rw\n#[inline(always)]\npub fn disabled_rw() -> bool {\n    false\n}\n\n/// query flag disabled_rw_in_other_namespace\n#[inline(always)]\npub fn disabled_rw_in_other_namespace() -> bool {\n    false\n}\n\n/// query flag enabled_fixed_ro\n#[inline(always)]\npub fn enabled_fixed_ro() -> bool {\n    true\n}\n\n/// query flag enabled_ro\n#[inline(always)]\npub fn enabled_ro() -> bool {\n    true\n}\n\n/// query flag enabled_rw\n#[inline(always)]\npub fn enabled_rw() -> bool {\n    true\n}\n\"#;\n    use crate::commands::assign_flag_ids;\n\n    fn test_generate_rust_code(mode: CodegenMode, expected: &str) {\n        let parsed_flags = crate::test::parse_test_flags();\n        let modified_parsed_flags =\n            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();\n        let flag_ids =\n            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();\n        let generated = generate_rust_code(\n            crate::test::TEST_PACKAGE,\n            flag_ids,\n            modified_parsed_flags.into_iter(),\n            mode,\n            None,\n        )\n        .unwrap();\n        assert_eq!(\"src/lib.rs\", format!(\"{}\", generated.path.display()));\n        crate::test::assert_no_significant_code_diff(\n            expected,\n            &String::from_utf8(generated.contents).unwrap(),\n        );\n    }\n\n    #[test]\n    fn test_generate_rust_code_for_prod() {\n        test_generate_rust_code(CodegenMode::Production, PROD_EXPECTED);\n    }\n\n    #[test]\n    fn test_generate_rust_code_for_test() {\n        test_generate_rust_code(CodegenMode::Test, TEST_EXPECTED);\n    }\n\n    #[test]\n    fn test_generate_rust_code_for_force_read_only() {\n        test_generate_rust_code(CodegenMode::ForceReadOnly, FORCE_READ_ONLY_EXPECTED);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/commands.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse anyhow::{bail, ensure, Context, Result};\nuse convert_finalized_flags::FinalizedFlagMap;\nuse itertools::Itertools;\nuse protobuf::Message;\nuse std::collections::HashMap;\nuse std::hash::Hasher;\nuse std::io::Read;\nuse std::path::PathBuf;\n\nuse crate::codegen::cpp::generate_cpp_code;\nuse crate::codegen::java::{generate_java_code, JavaCodegenConfig};\nuse crate::codegen::rust::generate_rust_code;\nuse crate::codegen::CodegenMode;\nuse crate::dump::{DumpFormat, DumpPredicate};\nuse crate::storage::generate_storage_file;\nuse aconfig_protos::{\n    ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,\n    ProtoParsedFlags, ProtoTracepoint,\n};\nuse aconfig_storage_file::sip_hasher13::SipHasher13;\nuse aconfig_storage_file::StorageFileType;\n\npub struct Input {\n    pub source: String,\n    pub reader: Box<dyn Read>,\n}\n\nimpl Input {\n    fn try_parse_flags(&mut self) -> Result<ProtoParsedFlags> {\n        let mut buffer = Vec::new();\n        self.reader\n            .read_to_end(&mut buffer)\n            .with_context(|| format!(\"failed to read {}\", self.source))?;\n        aconfig_protos::parsed_flags::try_from_binary_proto(&buffer)\n            .with_context(|| self.error_context())\n    }\n\n    fn error_context(&self) -> String {\n        format!(\"failed to parse {}\", self.source)\n    }\n}\n\npub struct OutputFile {\n    pub path: PathBuf, // relative to some root directory only main knows about\n    pub contents: Vec<u8>,\n}\n\npub const DEFAULT_FLAG_STATE: ProtoFlagState = ProtoFlagState::DISABLED;\npub const DEFAULT_FLAG_PERMISSION: ProtoFlagPermission = ProtoFlagPermission::READ_WRITE;\n\npub fn parse_flags(\n    package: &str,\n    container: Option<&str>,\n    declarations: Vec<Input>,\n    values: Vec<Input>,\n    default_permission: ProtoFlagPermission,\n    allow_read_write: bool,\n) -> Result<Vec<u8>> {\n    let mut parsed_flags = ProtoParsedFlags::new();\n\n    for mut input in declarations {\n        let mut contents = String::new();\n        input\n            .reader\n            .read_to_string(&mut contents)\n            .with_context(|| format!(\"failed to read {}\", input.source))?;\n\n        let flag_declarations = aconfig_protos::flag_declarations::try_from_text_proto(&contents)\n            .with_context(|| input.error_context())?;\n        ensure!(\n            package == flag_declarations.package(),\n            \"failed to parse {}: expected package {}, got {}\",\n            input.source,\n            package,\n            flag_declarations.package()\n        );\n        if let Some(c) = container {\n            ensure!(\n                c == flag_declarations.container(),\n                \"failed to parse {}: expected container {}, got {}\",\n                input.source,\n                c,\n                flag_declarations.container()\n            );\n        }\n        for mut flag_declaration in flag_declarations.flag.into_iter() {\n            aconfig_protos::flag_declaration::verify_fields(&flag_declaration)\n                .with_context(|| input.error_context())?;\n\n            // create ParsedFlag using FlagDeclaration and default values\n            let mut parsed_flag = ProtoParsedFlag::new();\n            if let Some(c) = container {\n                parsed_flag.set_container(c.to_string());\n            }\n            parsed_flag.set_package(package.to_string());\n            parsed_flag.set_name(flag_declaration.take_name());\n            parsed_flag.set_namespace(flag_declaration.take_namespace());\n            parsed_flag.set_description(flag_declaration.take_description());\n            parsed_flag.bug.append(&mut flag_declaration.bug);\n            parsed_flag.set_state(DEFAULT_FLAG_STATE);\n            let flag_permission = if flag_declaration.is_fixed_read_only() {\n                ProtoFlagPermission::READ_ONLY\n            } else {\n                default_permission\n            };\n            parsed_flag.set_permission(flag_permission);\n            parsed_flag.set_is_fixed_read_only(flag_declaration.is_fixed_read_only());\n            parsed_flag.set_is_exported(flag_declaration.is_exported());\n            let mut tracepoint = ProtoTracepoint::new();\n            tracepoint.set_source(input.source.clone());\n            tracepoint.set_state(DEFAULT_FLAG_STATE);\n            tracepoint.set_permission(flag_permission);\n            parsed_flag.trace.push(tracepoint);\n\n            let mut metadata = ProtoFlagMetadata::new();\n            let purpose = flag_declaration.metadata.purpose();\n            metadata.set_purpose(purpose);\n            parsed_flag.metadata = Some(metadata).into();\n\n            // verify ParsedFlag looks reasonable\n            aconfig_protos::parsed_flag::verify_fields(&parsed_flag)?;\n\n            // verify ParsedFlag can be added\n            ensure!(\n                parsed_flags.parsed_flag.iter().all(|other| other.name() != parsed_flag.name()),\n                \"failed to declare flag {} from {}: flag already declared\",\n                parsed_flag.name(),\n                input.source\n            );\n\n            // add ParsedFlag to ParsedFlags\n            parsed_flags.parsed_flag.push(parsed_flag);\n        }\n    }\n\n    for mut input in values {\n        let mut contents = String::new();\n        input\n            .reader\n            .read_to_string(&mut contents)\n            .with_context(|| format!(\"failed to read {}\", input.source))?;\n        let flag_values = aconfig_protos::flag_values::try_from_text_proto(&contents)\n            .with_context(|| input.error_context())?;\n        for flag_value in flag_values.flag_value.into_iter() {\n            aconfig_protos::flag_value::verify_fields(&flag_value)\n                .with_context(|| input.error_context())?;\n\n            let Some(parsed_flag) = parsed_flags\n                .parsed_flag\n                .iter_mut()\n                .find(|pf| pf.package() == flag_value.package() && pf.name() == flag_value.name())\n            else {\n                // (silently) skip unknown flags\n                continue;\n            };\n\n            ensure!(\n                !parsed_flag.is_fixed_read_only()\n                    || flag_value.permission() == ProtoFlagPermission::READ_ONLY,\n                \"failed to set permission of flag {}, since this flag is fixed read only flag\",\n                flag_value.name()\n            );\n\n            parsed_flag.set_state(flag_value.state());\n            parsed_flag.set_permission(flag_value.permission());\n            let mut tracepoint = ProtoTracepoint::new();\n            tracepoint.set_source(input.source.clone());\n            tracepoint.set_state(flag_value.state());\n            tracepoint.set_permission(flag_value.permission());\n            parsed_flag.trace.push(tracepoint);\n        }\n    }\n\n    if !allow_read_write {\n        if let Some(pf) = parsed_flags\n            .parsed_flag\n            .iter()\n            .find(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE)\n        {\n            bail!(\"flag {} has permission READ_WRITE, but allow_read_write is false\", pf.name());\n        }\n    }\n\n    // Create a sorted parsed_flags\n    aconfig_protos::parsed_flags::sort_parsed_flags(&mut parsed_flags);\n    aconfig_protos::parsed_flags::verify_fields(&parsed_flags)?;\n    let mut output = Vec::new();\n    parsed_flags.write_to_vec(&mut output)?;\n    Ok(output)\n}\n\npub fn create_java_lib(\n    mut input: Input,\n    codegen_mode: CodegenMode,\n    allow_instrumentation: bool,\n    new_exported: bool,\n    single_exported_file: bool,\n    finalized_flags: FinalizedFlagMap,\n) -> Result<Vec<OutputFile>> {\n    let parsed_flags = input.try_parse_flags()?;\n    let modified_parsed_flags =\n        modify_parsed_flags_based_on_mode(parsed_flags.clone(), codegen_mode)?;\n    let Some(package) = find_unique_package(&modified_parsed_flags) else {\n        bail!(\"no parsed flags, or the parsed flags use different packages\");\n    };\n    let package = package.to_string();\n    let mut flag_names = extract_flag_names(parsed_flags)?;\n    let package_fingerprint = compute_flags_fingerprint(&mut flag_names);\n    let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;\n    let config = JavaCodegenConfig {\n        codegen_mode,\n        flag_ids,\n        allow_instrumentation,\n        package_fingerprint,\n        new_exported,\n        single_exported_file,\n        finalized_flags,\n    };\n    generate_java_code(&package, modified_parsed_flags.into_iter(), config)\n}\n\npub fn create_cpp_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> {\n    // TODO(327420679): Enable export mode for native flag library\n    ensure!(\n        codegen_mode != CodegenMode::Exported,\n        \"Exported mode for generated c/c++ flag library is disabled\"\n    );\n    let parsed_flags = input.try_parse_flags()?;\n    let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?;\n    let Some(package) = find_unique_package(&modified_parsed_flags) else {\n        bail!(\"no parsed flags, or the parsed flags use different packages\");\n    };\n    let package = package.to_string();\n    let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;\n    generate_cpp_code(&package, modified_parsed_flags.into_iter(), codegen_mode, flag_ids)\n}\n\npub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<OutputFile> {\n    // // TODO(327420679): Enable export mode for native flag library\n    ensure!(\n        codegen_mode != CodegenMode::Exported,\n        \"Exported mode for generated rust flag library is disabled\"\n    );\n    let parsed_flags = input.try_parse_flags()?;\n    let modified_parsed_flags =\n        modify_parsed_flags_based_on_mode(parsed_flags.clone(), codegen_mode)?;\n    let Some(package) = find_unique_package(&modified_parsed_flags) else {\n        bail!(\"no parsed flags, or the parsed flags use different packages\");\n    };\n    let package = package.to_string();\n\n    let package_fingerprint: Option<u64> = if cfg!(enable_fingerprint_rust) {\n        let mut flag_names = extract_flag_names(parsed_flags)?;\n        Some(compute_flags_fingerprint(&mut flag_names))\n    } else {\n        None\n    };\n\n    let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;\n    generate_rust_code(\n        &package,\n        flag_ids,\n        modified_parsed_flags.into_iter(),\n        codegen_mode,\n        package_fingerprint,\n    )\n}\n\npub fn create_storage(\n    caches: Vec<Input>,\n    container: &str,\n    file: &StorageFileType,\n    version: u32,\n) -> Result<Vec<u8>> {\n    let parsed_flags_vec: Vec<ProtoParsedFlags> =\n        caches.into_iter().map(|mut input| input.try_parse_flags()).collect::<Result<Vec<_>>>()?;\n    generate_storage_file(container, parsed_flags_vec.iter(), file, version)\n}\n\npub fn create_device_config_defaults(mut input: Input) -> Result<Vec<u8>> {\n    let parsed_flags = input.try_parse_flags()?;\n    let mut output = Vec::new();\n    for parsed_flag in parsed_flags\n        .parsed_flag\n        .into_iter()\n        .filter(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE)\n    {\n        let line = format!(\n            \"{}:{}={}\\n\",\n            parsed_flag.namespace(),\n            parsed_flag.fully_qualified_name(),\n            match parsed_flag.state() {\n                ProtoFlagState::ENABLED => \"enabled\",\n                ProtoFlagState::DISABLED => \"disabled\",\n            }\n        );\n        output.extend_from_slice(line.as_bytes());\n    }\n    Ok(output)\n}\n\npub fn create_device_config_sysprops(mut input: Input) -> Result<Vec<u8>> {\n    let parsed_flags = input.try_parse_flags()?;\n    let mut output = Vec::new();\n    for parsed_flag in parsed_flags\n        .parsed_flag\n        .into_iter()\n        .filter(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE)\n    {\n        let line = format!(\n            \"persist.device_config.{}={}\\n\",\n            parsed_flag.fully_qualified_name(),\n            match parsed_flag.state() {\n                ProtoFlagState::ENABLED => \"true\",\n                ProtoFlagState::DISABLED => \"false\",\n            }\n        );\n        output.extend_from_slice(line.as_bytes());\n    }\n    Ok(output)\n}\n\npub fn dump_parsed_flags(\n    mut input: Vec<Input>,\n    format: DumpFormat,\n    filters: &[&str],\n    dedup: bool,\n) -> Result<Vec<u8>> {\n    let individually_parsed_flags: Result<Vec<ProtoParsedFlags>> =\n        input.iter_mut().map(|i| i.try_parse_flags()).collect();\n    let parsed_flags: ProtoParsedFlags =\n        aconfig_protos::parsed_flags::merge(individually_parsed_flags?, dedup)?;\n    let filters: Vec<Box<DumpPredicate>> = if filters.is_empty() {\n        vec![Box::new(|_| true)]\n    } else {\n        filters\n            .iter()\n            .map(|f| crate::dump::create_filter_predicate(f))\n            .collect::<Result<Vec<_>>>()?\n    };\n    crate::dump::dump_parsed_flags(\n        parsed_flags.parsed_flag.into_iter().filter(|flag| filters.iter().any(|p| p(flag))),\n        format,\n    )\n}\n\nfn find_unique_package(parsed_flags: &[ProtoParsedFlag]) -> Option<&str> {\n    let package = parsed_flags.first().map(|pf| pf.package())?;\n    if parsed_flags.iter().any(|pf| pf.package() != package) {\n        return None;\n    }\n    Some(package)\n}\n\npub fn modify_parsed_flags_based_on_mode(\n    parsed_flags: ProtoParsedFlags,\n    codegen_mode: CodegenMode,\n) -> Result<Vec<ProtoParsedFlag>> {\n    fn exported_mode_flag_modifier(mut parsed_flag: ProtoParsedFlag) -> ProtoParsedFlag {\n        parsed_flag.set_state(ProtoFlagState::DISABLED);\n        parsed_flag.set_permission(ProtoFlagPermission::READ_WRITE);\n        parsed_flag.set_is_fixed_read_only(false);\n        parsed_flag\n    }\n\n    fn force_read_only_mode_flag_modifier(mut parsed_flag: ProtoParsedFlag) -> ProtoParsedFlag {\n        parsed_flag.set_permission(ProtoFlagPermission::READ_ONLY);\n        parsed_flag\n    }\n\n    let modified_parsed_flags: Vec<_> = match codegen_mode {\n        CodegenMode::Exported => parsed_flags\n            .parsed_flag\n            .into_iter()\n            .filter(|pf| pf.is_exported())\n            .map(exported_mode_flag_modifier)\n            .collect(),\n        CodegenMode::ForceReadOnly => parsed_flags\n            .parsed_flag\n            .into_iter()\n            .filter(|pf| !pf.is_exported())\n            .map(force_read_only_mode_flag_modifier)\n            .collect(),\n        CodegenMode::Production | CodegenMode::Test => {\n            parsed_flags.parsed_flag.into_iter().collect()\n        }\n    };\n    if modified_parsed_flags.is_empty() {\n        bail!(\"{codegen_mode} library contains no {codegen_mode} flags\");\n    }\n\n    Ok(modified_parsed_flags)\n}\n\npub fn assign_flag_ids<'a, I>(package: &str, parsed_flags_iter: I) -> Result<HashMap<String, u16>>\nwhere\n    I: Iterator<Item = &'a ProtoParsedFlag> + Clone,\n{\n    assert!(parsed_flags_iter.clone().tuple_windows().all(|(a, b)| a.name() <= b.name()));\n    let mut flag_ids = HashMap::new();\n    let mut flag_idx = 0;\n    for pf in parsed_flags_iter {\n        if package != pf.package() {\n            return Err(anyhow::anyhow!(\"encountered a flag not in current package\"));\n        }\n\n        // put a cap on how many flags a package can contain to 65535\n        if flag_idx > u16::MAX as u32 {\n            return Err(anyhow::anyhow!(\"the number of flags in a package cannot exceed 65535\"));\n        }\n\n        if should_include_flag(pf) {\n            flag_ids.insert(pf.name().to_string(), flag_idx as u16);\n            flag_idx += 1;\n        }\n    }\n    Ok(flag_ids)\n}\n\n// Creates a fingerprint of the flag names (which requires sorting the vector).\n// Fingerprint is used by both codegen and storage files.\npub fn compute_flags_fingerprint(flag_names: &mut Vec<String>) -> u64 {\n    flag_names.sort();\n\n    let mut hasher = SipHasher13::new();\n    for flag in flag_names {\n        hasher.write(flag.as_bytes());\n    }\n    hasher.finish()\n}\n\n// Converts ProtoParsedFlags into a vector of strings containing all of the flag\n// names. Helper fn for creating fingerprint for codegen files. Flags must all\n// belong to the same package.\nfn extract_flag_names(flags: ProtoParsedFlags) -> Result<Vec<String>> {\n    let separated_flags: Vec<ProtoParsedFlag> = flags.parsed_flag.into_iter().collect::<Vec<_>>();\n\n    // All flags must belong to the same package as the fingerprint is per-package.\n    let Some(_package) = find_unique_package(&separated_flags) else {\n        bail!(\"No parsed flags, or the parsed flags use different packages.\");\n    };\n\n    Ok(separated_flags\n        .into_iter()\n        .filter(should_include_flag)\n        .map(|flag| flag.name.unwrap())\n        .collect::<Vec<_>>())\n}\n\n// Exclude system/vendor/product flags that are RO+disabled.\npub fn should_include_flag(pf: &ProtoParsedFlag) -> bool {\n    let should_filter_container = pf.container == Some(\"vendor\".to_string())\n        || pf.container == Some(\"system\".to_string())\n        || pf.container == Some(\"system_ext\".to_string())\n        || pf.container == Some(\"product\".to_string());\n\n    let disabled_ro = pf.state == Some(ProtoFlagState::DISABLED.into())\n        && pf.permission == Some(ProtoFlagPermission::READ_ONLY.into());\n\n    !should_filter_container || !disabled_ro\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use aconfig_protos::ProtoFlagPurpose;\n\n    #[test]\n    fn test_offset_fingerprint() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let expected_fingerprint: u64 = 11551379960324242360;\n\n        let mut extracted_flags = extract_flag_names(parsed_flags).unwrap();\n        let hash_result = compute_flags_fingerprint(&mut extracted_flags);\n\n        assert_eq!(hash_result, expected_fingerprint);\n    }\n\n    #[test]\n    fn test_offset_fingerprint_matches_from_package() {\n        let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags();\n\n        // All test flags are in the same package, so fingerprint from all of them.\n        let mut extracted_flags = extract_flag_names(parsed_flags.clone()).unwrap();\n        let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags);\n\n        let mut flag_names_vec = parsed_flags\n            .parsed_flag\n            .clone()\n            .into_iter()\n            .filter(should_include_flag)\n            .map(|flag| flag.name.unwrap())\n            .map(String::from)\n            .collect::<Vec<_>>();\n        let result_from_names = compute_flags_fingerprint(&mut flag_names_vec);\n\n        // Assert the same hash is generated for each case.\n        assert_eq!(result_from_parsed_flags, result_from_names);\n    }\n\n    #[test]\n    fn test_offset_fingerprint_different_packages_does_not_match() {\n        // Parse flags from two packages.\n        let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags();\n        let second_parsed_flags = crate::test::parse_second_package_flags();\n\n        let mut extracted_flags = extract_flag_names(parsed_flags).unwrap();\n        let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags);\n        let mut second_extracted_flags = extract_flag_names(second_parsed_flags).unwrap();\n        let second_result = compute_flags_fingerprint(&mut second_extracted_flags);\n\n        // Different flags should have a different fingerprint.\n        assert_ne!(result_from_parsed_flags, second_result);\n    }\n\n    #[test]\n    fn test_parse_flags() {\n        let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags\n        aconfig_protos::parsed_flags::verify_fields(&parsed_flags).unwrap();\n\n        let enabled_ro =\n            parsed_flags.parsed_flag.iter().find(|pf| pf.name() == \"enabled_ro\").unwrap();\n        assert!(aconfig_protos::parsed_flag::verify_fields(enabled_ro).is_ok());\n        assert_eq!(\"com.android.aconfig.test\", enabled_ro.package());\n        assert_eq!(\"enabled_ro\", enabled_ro.name());\n        assert_eq!(\"This flag is ENABLED + READ_ONLY\", enabled_ro.description());\n        assert_eq!(ProtoFlagState::ENABLED, enabled_ro.state());\n        assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.permission());\n        assert_eq!(ProtoFlagPurpose::PURPOSE_BUGFIX, enabled_ro.metadata.purpose());\n        assert_eq!(3, enabled_ro.trace.len());\n        assert!(!enabled_ro.is_fixed_read_only());\n        assert_eq!(\"tests/test.aconfig\", enabled_ro.trace[0].source());\n        assert_eq!(ProtoFlagState::DISABLED, enabled_ro.trace[0].state());\n        assert_eq!(ProtoFlagPermission::READ_WRITE, enabled_ro.trace[0].permission());\n        assert_eq!(\"tests/first.values\", enabled_ro.trace[1].source());\n        assert_eq!(ProtoFlagState::DISABLED, enabled_ro.trace[1].state());\n        assert_eq!(ProtoFlagPermission::READ_WRITE, enabled_ro.trace[1].permission());\n        assert_eq!(\"tests/second.values\", enabled_ro.trace[2].source());\n        assert_eq!(ProtoFlagState::ENABLED, enabled_ro.trace[2].state());\n        assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.trace[2].permission());\n\n        assert_eq!(9, parsed_flags.parsed_flag.len());\n        for pf in parsed_flags.parsed_flag.iter() {\n            if pf.name().starts_with(\"enabled_fixed_ro\") {\n                continue;\n            }\n            let first = pf.trace.first().unwrap();\n            assert_eq!(DEFAULT_FLAG_STATE, first.state());\n            assert_eq!(DEFAULT_FLAG_PERMISSION, first.permission());\n\n            let last = pf.trace.last().unwrap();\n            assert_eq!(pf.state(), last.state());\n            assert_eq!(pf.permission(), last.permission());\n        }\n\n        let enabled_fixed_ro =\n            parsed_flags.parsed_flag.iter().find(|pf| pf.name() == \"enabled_fixed_ro\").unwrap();\n        assert!(enabled_fixed_ro.is_fixed_read_only());\n        assert_eq!(ProtoFlagState::ENABLED, enabled_fixed_ro.state());\n        assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.permission());\n        assert_eq!(2, enabled_fixed_ro.trace.len());\n        assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.trace[0].permission());\n        assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.trace[1].permission());\n    }\n\n    #[test]\n    fn test_parse_flags_setting_default() {\n        let first_flag = r#\"\n        package: \"com.first\"\n        flag {\n            name: \"first\"\n            namespace: \"first_ns\"\n            description: \"This is the description of the first flag.\"\n            bug: \"123\"\n        }\n        \"#;\n        let declaration =\n            vec![Input { source: \"momery\".to_string(), reader: Box::new(first_flag.as_bytes()) }];\n        let value: Vec<Input> = vec![];\n\n        let flags_bytes = crate::commands::parse_flags(\n            \"com.first\",\n            None,\n            declaration,\n            value,\n            ProtoFlagPermission::READ_ONLY,\n            true,\n        )\n        .unwrap();\n        let parsed_flags =\n            aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();\n        assert_eq!(1, parsed_flags.parsed_flag.len());\n        let parsed_flag = parsed_flags.parsed_flag.first().unwrap();\n        assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state());\n        assert_eq!(ProtoFlagPermission::READ_ONLY, parsed_flag.permission());\n    }\n\n    #[test]\n    fn test_parse_flags_package_mismatch_between_declaration_and_command_line() {\n        let first_flag = r#\"\n        package: \"com.declaration.package\"\n        container: \"first.container\"\n        flag {\n            name: \"first\"\n            namespace: \"first_ns\"\n            description: \"This is the description of the first flag.\"\n            bug: \"123\"\n        }\n        \"#;\n        let declaration =\n            vec![Input { source: \"memory\".to_string(), reader: Box::new(first_flag.as_bytes()) }];\n\n        let value: Vec<Input> = vec![];\n\n        let error = crate::commands::parse_flags(\n            \"com.argument.package\",\n            Some(\"first.container\"),\n            declaration,\n            value,\n            ProtoFlagPermission::READ_WRITE,\n            true,\n        )\n        .unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            \"failed to parse memory: expected package com.argument.package, got com.declaration.package\"\n        );\n    }\n\n    #[test]\n    fn test_parse_flags_container_mismatch_between_declaration_and_command_line() {\n        let first_flag = r#\"\n        package: \"com.first\"\n        container: \"declaration.container\"\n        flag {\n            name: \"first\"\n            namespace: \"first_ns\"\n            description: \"This is the description of the first flag.\"\n            bug: \"123\"\n        }\n        \"#;\n        let declaration =\n            vec![Input { source: \"memory\".to_string(), reader: Box::new(first_flag.as_bytes()) }];\n\n        let value: Vec<Input> = vec![];\n\n        let error = crate::commands::parse_flags(\n            \"com.first\",\n            Some(\"argument.container\"),\n            declaration,\n            value,\n            ProtoFlagPermission::READ_WRITE,\n            true,\n        )\n        .unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            \"failed to parse memory: expected container argument.container, got declaration.container\"\n        );\n    }\n    #[test]\n    fn test_parse_flags_no_allow_read_write_default_error() {\n        let first_flag = r#\"\n        package: \"com.first\"\n        container: \"com.first.container\"\n        flag {\n            name: \"first\"\n            namespace: \"first_ns\"\n            description: \"This is the description of the first flag.\"\n            bug: \"123\"\n        }\n        \"#;\n        let declaration =\n            vec![Input { source: \"memory\".to_string(), reader: Box::new(first_flag.as_bytes()) }];\n\n        let error = crate::commands::parse_flags(\n            \"com.first\",\n            Some(\"com.first.container\"),\n            declaration,\n            vec![],\n            ProtoFlagPermission::READ_WRITE,\n            false,\n        )\n        .unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            \"flag first has permission READ_WRITE, but allow_read_write is false\"\n        );\n    }\n\n    #[test]\n    fn test_parse_flags_no_allow_read_write_value_error() {\n        let first_flag = r#\"\n        package: \"com.first\"\n        container: \"com.first.container\"\n        flag {\n            name: \"first\"\n            namespace: \"first_ns\"\n            description: \"This is the description of the first flag.\"\n            bug: \"123\"\n        }\n        \"#;\n        let declaration =\n            vec![Input { source: \"memory\".to_string(), reader: Box::new(first_flag.as_bytes()) }];\n\n        let first_flag_value = r#\"\n        flag_value {\n            package: \"com.first\"\n            name: \"first\"\n            state: DISABLED\n            permission: READ_WRITE\n        }\n        \"#;\n        let value = vec![Input {\n            source: \"memory\".to_string(),\n            reader: Box::new(first_flag_value.as_bytes()),\n        }];\n        let error = crate::commands::parse_flags(\n            \"com.first\",\n            Some(\"com.first.container\"),\n            declaration,\n            value,\n            ProtoFlagPermission::READ_ONLY,\n            false,\n        )\n        .unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            \"flag first has permission READ_WRITE, but allow_read_write is false\"\n        );\n    }\n\n    #[test]\n    fn test_parse_flags_no_allow_read_write_success() {\n        let first_flag = r#\"\n        package: \"com.first\"\n        container: \"com.first.container\"\n        flag {\n            name: \"first\"\n            namespace: \"first_ns\"\n            description: \"This is the description of the first flag.\"\n            bug: \"123\"\n        }\n        \"#;\n        let declaration =\n            vec![Input { source: \"memory\".to_string(), reader: Box::new(first_flag.as_bytes()) }];\n\n        let first_flag_value = r#\"\n        flag_value {\n            package: \"com.first\"\n            name: \"first\"\n            state: DISABLED\n            permission: READ_ONLY\n        }\n        \"#;\n        let value = vec![Input {\n            source: \"memory\".to_string(),\n            reader: Box::new(first_flag_value.as_bytes()),\n        }];\n        let flags_bytes = crate::commands::parse_flags(\n            \"com.first\",\n            Some(\"com.first.container\"),\n            declaration,\n            value,\n            ProtoFlagPermission::READ_ONLY,\n            false,\n        )\n        .unwrap();\n        let parsed_flags =\n            aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();\n        assert_eq!(1, parsed_flags.parsed_flag.len());\n        let parsed_flag = parsed_flags.parsed_flag.first().unwrap();\n        assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state());\n        assert_eq!(ProtoFlagPermission::READ_ONLY, parsed_flag.permission());\n    }\n\n    #[test]\n    fn test_parse_flags_override_fixed_read_only() {\n        let first_flag = r#\"\n        package: \"com.first\"\n        container: \"com.first.container\"\n        flag {\n            name: \"first\"\n            namespace: \"first_ns\"\n            description: \"This is the description of the first flag.\"\n            bug: \"123\"\n            is_fixed_read_only: true\n        }\n        \"#;\n        let declaration =\n            vec![Input { source: \"memory\".to_string(), reader: Box::new(first_flag.as_bytes()) }];\n\n        let first_flag_value = r#\"\n        flag_value {\n            package: \"com.first\"\n            name: \"first\"\n            state: DISABLED\n            permission: READ_WRITE\n        }\n        \"#;\n        let value = vec![Input {\n            source: \"memory\".to_string(),\n            reader: Box::new(first_flag_value.as_bytes()),\n        }];\n        let error = crate::commands::parse_flags(\n            \"com.first\",\n            Some(\"com.first.container\"),\n            declaration,\n            value,\n            ProtoFlagPermission::READ_WRITE,\n            true,\n        )\n        .unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            \"failed to set permission of flag first, since this flag is fixed read only flag\"\n        );\n    }\n\n    #[test]\n    fn test_parse_flags_metadata() {\n        let metadata_flag = r#\"\n        package: \"com.first\"\n        flag {\n            name: \"first\"\n            namespace: \"first_ns\"\n            description: \"This is the description of this feature flag.\"\n            bug: \"123\"\n            metadata {\n                purpose: PURPOSE_FEATURE\n            }\n        }\n        \"#;\n        let declaration = vec![Input {\n            source: \"memory\".to_string(),\n            reader: Box::new(metadata_flag.as_bytes()),\n        }];\n        let value: Vec<Input> = vec![];\n\n        let flags_bytes = crate::commands::parse_flags(\n            \"com.first\",\n            None,\n            declaration,\n            value,\n            ProtoFlagPermission::READ_ONLY,\n            true,\n        )\n        .unwrap();\n        let parsed_flags =\n            aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();\n        assert_eq!(1, parsed_flags.parsed_flag.len());\n        let parsed_flag = parsed_flags.parsed_flag.first().unwrap();\n        assert_eq!(ProtoFlagPurpose::PURPOSE_FEATURE, parsed_flag.metadata.purpose());\n    }\n\n    #[test]\n    fn test_create_device_config_defaults() {\n        let input = parse_test_flags_as_input();\n        let bytes = create_device_config_defaults(input).unwrap();\n        let text = std::str::from_utf8(&bytes).unwrap();\n        assert_eq!(\"aconfig_test:com.android.aconfig.test.disabled_rw=disabled\\naconfig_test:com.android.aconfig.test.disabled_rw_exported=disabled\\nother_namespace:com.android.aconfig.test.disabled_rw_in_other_namespace=disabled\\naconfig_test:com.android.aconfig.test.enabled_rw=enabled\\n\", text);\n    }\n\n    #[test]\n    fn test_create_device_config_sysprops() {\n        let input = parse_test_flags_as_input();\n        let bytes = create_device_config_sysprops(input).unwrap();\n        let text = std::str::from_utf8(&bytes).unwrap();\n        assert_eq!(\"persist.device_config.com.android.aconfig.test.disabled_rw=false\\npersist.device_config.com.android.aconfig.test.disabled_rw_exported=false\\npersist.device_config.com.android.aconfig.test.disabled_rw_in_other_namespace=false\\npersist.device_config.com.android.aconfig.test.enabled_rw=true\\n\", text);\n    }\n\n    #[test]\n    fn test_dump() {\n        let input = parse_test_flags_as_input();\n        let bytes = dump_parsed_flags(\n            vec![input],\n            DumpFormat::Custom(\"{fully_qualified_name}\".to_string()),\n            &[],\n            false,\n        )\n        .unwrap();\n        let text = std::str::from_utf8(&bytes).unwrap();\n        assert!(text.contains(\"com.android.aconfig.test.disabled_ro\"));\n    }\n\n    #[test]\n    fn test_dump_multiple_filters() {\n        let input = parse_test_flags_as_input();\n        let bytes = dump_parsed_flags(\n            vec![input],\n            DumpFormat::Custom(\"{fully_qualified_name}\".to_string()),\n            &[\"container:system+state:ENABLED\", \"container:system+permission:READ_WRITE\"],\n            false,\n        )\n        .unwrap();\n        let text = std::str::from_utf8(&bytes).unwrap();\n        let expected_flag_list = &[\n            \"com.android.aconfig.test.disabled_rw\",\n            \"com.android.aconfig.test.disabled_rw_exported\",\n            \"com.android.aconfig.test.disabled_rw_in_other_namespace\",\n            \"com.android.aconfig.test.enabled_fixed_ro\",\n            \"com.android.aconfig.test.enabled_fixed_ro_exported\",\n            \"com.android.aconfig.test.enabled_ro\",\n            \"com.android.aconfig.test.enabled_ro_exported\",\n            \"com.android.aconfig.test.enabled_rw\",\n        ];\n        assert_eq!(expected_flag_list.map(|s| format!(\"{}\\n\", s)).join(\"\"), text);\n    }\n\n    #[test]\n    fn test_dump_textproto_format_dedup() {\n        let input = parse_test_flags_as_input();\n        let input2 = parse_test_flags_as_input();\n        let bytes =\n            dump_parsed_flags(vec![input, input2], DumpFormat::Textproto, &[], true).unwrap();\n        let text = std::str::from_utf8(&bytes).unwrap();\n        assert_eq!(\n            None,\n            crate::test::first_significant_code_diff(\n                crate::test::TEST_FLAGS_TEXTPROTO.trim(),\n                text.trim()\n            )\n        );\n    }\n\n    fn parse_test_flags_as_input() -> Input {\n        let parsed_flags = crate::test::parse_test_flags();\n        let binary_proto = parsed_flags.write_to_bytes().unwrap();\n        let cursor = std::io::Cursor::new(binary_proto);\n        let reader = Box::new(cursor);\n        Input { source: \"test.data\".to_string(), reader }\n    }\n\n    #[test]\n    fn test_modify_parsed_flags_based_on_mode_prod() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let p_parsed_flags =\n            modify_parsed_flags_based_on_mode(parsed_flags.clone(), CodegenMode::Production)\n                .unwrap();\n        assert_eq!(parsed_flags.parsed_flag.len(), p_parsed_flags.len());\n        for (i, item) in p_parsed_flags.iter().enumerate() {\n            assert!(parsed_flags.parsed_flag[i].eq(item));\n        }\n    }\n\n    #[test]\n    fn test_modify_parsed_flags_based_on_mode_exported() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let p_parsed_flags =\n            modify_parsed_flags_based_on_mode(parsed_flags, CodegenMode::Exported).unwrap();\n        assert_eq!(3, p_parsed_flags.len());\n        for flag in p_parsed_flags.iter() {\n            assert_eq!(ProtoFlagState::DISABLED, flag.state());\n            assert_eq!(ProtoFlagPermission::READ_WRITE, flag.permission());\n            assert!(!flag.is_fixed_read_only());\n            assert!(flag.is_exported());\n        }\n\n        let mut parsed_flags = crate::test::parse_test_flags();\n        parsed_flags.parsed_flag.retain(|pf| !pf.is_exported());\n        let error =\n            modify_parsed_flags_based_on_mode(parsed_flags, CodegenMode::Exported).unwrap_err();\n        assert_eq!(\"exported library contains no exported flags\", format!(\"{:?}\", error));\n    }\n\n    #[test]\n    fn test_assign_flag_ids() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let package = find_unique_package(&parsed_flags.parsed_flag).unwrap().to_string();\n        let flag_ids = assign_flag_ids(&package, parsed_flags.parsed_flag.iter()).unwrap();\n        let expected_flag_ids = HashMap::from([\n            (String::from(\"disabled_rw\"), 0_u16),\n            (String::from(\"disabled_rw_exported\"), 1_u16),\n            (String::from(\"disabled_rw_in_other_namespace\"), 2_u16),\n            (String::from(\"enabled_fixed_ro\"), 3_u16),\n            (String::from(\"enabled_fixed_ro_exported\"), 4_u16),\n            (String::from(\"enabled_ro\"), 5_u16),\n            (String::from(\"enabled_ro_exported\"), 6_u16),\n            (String::from(\"enabled_rw\"), 7_u16),\n        ]);\n        assert_eq!(flag_ids, expected_flag_ids);\n    }\n\n    #[test]\n    fn test_modify_parsed_flags_based_on_mode_force_read_only() {\n        let parsed_flags = crate::test::parse_test_flags();\n        let p_parsed_flags =\n            modify_parsed_flags_based_on_mode(parsed_flags.clone(), CodegenMode::ForceReadOnly)\n                .unwrap();\n        assert_eq!(6, p_parsed_flags.len());\n        for pf in p_parsed_flags {\n            assert_eq!(ProtoFlagPermission::READ_ONLY, pf.permission());\n        }\n\n        let mut parsed_flags = crate::test::parse_test_flags();\n        parsed_flags.parsed_flag.retain_mut(|pf| pf.is_exported());\n        let error = modify_parsed_flags_based_on_mode(parsed_flags, CodegenMode::ForceReadOnly)\n            .unwrap_err();\n        assert_eq!(\n            \"force-read-only library contains no force-read-only flags\",\n            format!(\"{:?}\", error)\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/dump.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse aconfig_protos::{\n    ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoTracepoint,\n};\nuse aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};\nuse anyhow::{anyhow, bail, Context, Result};\nuse protobuf::Message;\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum DumpFormat {\n    Protobuf,\n    Textproto,\n    Custom(String),\n}\n\nimpl TryFrom<&str> for DumpFormat {\n    type Error = anyhow::Error;\n\n    fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {\n        match value {\n            // protobuf formats\n            \"protobuf\" => Ok(Self::Protobuf),\n            \"textproto\" => Ok(Self::Textproto),\n            // custom format\n            _ => Ok(Self::Custom(value.to_owned())),\n        }\n    }\n}\n\npub fn dump_parsed_flags<I>(parsed_flags_iter: I, format: DumpFormat) -> Result<Vec<u8>>\nwhere\n    I: Iterator<Item = ProtoParsedFlag>,\n{\n    let mut output = Vec::new();\n    match format {\n        DumpFormat::Protobuf => {\n            let parsed_flags =\n                ProtoParsedFlags { parsed_flag: parsed_flags_iter.collect(), ..Default::default() };\n            parsed_flags.write_to_vec(&mut output)?;\n        }\n        DumpFormat::Textproto => {\n            let parsed_flags =\n                ProtoParsedFlags { parsed_flag: parsed_flags_iter.collect(), ..Default::default() };\n            let s = protobuf::text_format::print_to_string_pretty(&parsed_flags);\n            output.extend_from_slice(s.as_bytes());\n        }\n        DumpFormat::Custom(format) => {\n            for flag in parsed_flags_iter {\n                dump_custom_format(&flag, &format, &mut output);\n            }\n        }\n    }\n    Ok(output)\n}\n\nfn dump_custom_format(flag: &ProtoParsedFlag, format: &str, output: &mut Vec<u8>) {\n    fn format_trace(trace: &[ProtoTracepoint]) -> String {\n        trace\n            .iter()\n            .map(|tracepoint| {\n                format!(\n                    \"{}: {:?} + {:?}\",\n                    tracepoint.source(),\n                    tracepoint.permission(),\n                    tracepoint.state()\n                )\n            })\n            .collect::<Vec<_>>()\n            .join(\", \")\n    }\n\n    fn format_trace_paths(trace: &[ProtoTracepoint]) -> String {\n        trace.iter().map(|tracepoint| tracepoint.source()).collect::<Vec<_>>().join(\", \")\n    }\n\n    fn format_metadata(metadata: &ProtoFlagMetadata) -> String {\n        format!(\"{:?}\", metadata.purpose())\n    }\n\n    let mut str = format\n        // ProtoParsedFlag fields\n        .replace(\"{package}\", flag.package())\n        .replace(\"{name}\", flag.name())\n        .replace(\"{namespace}\", flag.namespace())\n        .replace(\"{description}\", flag.description())\n        .replace(\"{bug}\", &flag.bug.join(\", \"))\n        .replace(\"{state}\", &format!(\"{:?}\", flag.state()))\n        .replace(\"{state:bool}\", &format!(\"{}\", flag.state() == ProtoFlagState::ENABLED))\n        .replace(\"{permission}\", &format!(\"{:?}\", flag.permission()))\n        .replace(\"{trace}\", &format_trace(&flag.trace))\n        .replace(\"{trace:paths}\", &format_trace_paths(&flag.trace))\n        .replace(\"{is_fixed_read_only}\", &format!(\"{}\", flag.is_fixed_read_only()))\n        .replace(\"{is_exported}\", &format!(\"{}\", flag.is_exported()))\n        .replace(\"{container}\", flag.container())\n        .replace(\"{metadata}\", &format_metadata(&flag.metadata))\n        // ParsedFlagExt functions\n        .replace(\"{fully_qualified_name}\", &flag.fully_qualified_name());\n    str.push('\\n');\n    output.extend_from_slice(str.as_bytes());\n}\n\npub type DumpPredicate = dyn Fn(&ProtoParsedFlag) -> bool;\n\npub fn create_filter_predicate(filter: &str) -> Result<Box<DumpPredicate>> {\n    let predicates = filter\n        .split('+')\n        .map(|sub_filter| create_filter_predicate_single(sub_filter))\n        .collect::<Result<Vec<_>>>()?;\n    Ok(Box::new(move |flag| predicates.iter().all(|p| p(flag))))\n}\n\nfn create_filter_predicate_single(filter: &str) -> Result<Box<DumpPredicate>> {\n    fn enum_from_str<T>(expected: &[T], s: &str) -> Result<T>\n    where\n        T: std::fmt::Debug + Copy,\n    {\n        for candidate in expected.iter() {\n            if s == format!(\"{:?}\", candidate) {\n                return Ok(*candidate);\n            }\n        }\n        let expected =\n            expected.iter().map(|state| format!(\"{:?}\", state)).collect::<Vec<_>>().join(\", \");\n        bail!(\"\\\"{s}\\\": not a valid flag state, expected one of {expected}\");\n    }\n\n    let error_msg = format!(\"\\\"{filter}\\\": filter syntax error\");\n    let (what, arg) = filter.split_once(':').ok_or_else(|| anyhow!(error_msg.clone()))?;\n    match what {\n        \"package\" => {\n            let expected = arg.to_owned();\n            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.package() == expected))\n        }\n        \"name\" => {\n            let expected = arg.to_owned();\n            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.name() == expected))\n        }\n        \"namespace\" => {\n            let expected = arg.to_owned();\n            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.namespace() == expected))\n        }\n        // description: not supported yet\n        \"bug\" => {\n            let expected = arg.to_owned();\n            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.bug.join(\", \") == expected))\n        }\n        \"state\" => {\n            let expected = enum_from_str(&[ProtoFlagState::ENABLED, ProtoFlagState::DISABLED], arg)\n                .context(error_msg)?;\n            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.state() == expected))\n        }\n        \"permission\" => {\n            let expected = enum_from_str(\n                &[ProtoFlagPermission::READ_ONLY, ProtoFlagPermission::READ_WRITE],\n                arg,\n            )\n            .context(error_msg)?;\n            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.permission() == expected))\n        }\n        // trace: not supported yet\n        \"is_fixed_read_only\" => {\n            let expected: bool = arg.parse().context(error_msg)?;\n            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.is_fixed_read_only() == expected))\n        }\n        \"is_exported\" => {\n            let expected: bool = arg.parse().context(error_msg)?;\n            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.is_exported() == expected))\n        }\n        \"container\" => {\n            let expected = arg.to_owned();\n            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.container() == expected))\n        }\n        // metadata: not supported yet\n        \"fully_qualified_name\" => {\n            let expected = arg.to_owned();\n            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.fully_qualified_name() == expected))\n        }\n        _ => Err(anyhow!(error_msg)),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::test::parse_test_flags;\n    use aconfig_protos::ProtoParsedFlags;\n    use protobuf::Message;\n\n    fn parse_enabled_ro_flag() -> ProtoParsedFlag {\n        parse_test_flags().parsed_flag.into_iter().find(|pf| pf.name() == \"enabled_ro\").unwrap()\n    }\n\n    #[test]\n    fn test_dumpformat_from_str() {\n        // supported format types\n        assert_eq!(DumpFormat::try_from(\"protobuf\").unwrap(), DumpFormat::Protobuf);\n        assert_eq!(DumpFormat::try_from(\"textproto\").unwrap(), DumpFormat::Textproto);\n        assert_eq!(\n            DumpFormat::try_from(\"foobar\").unwrap(),\n            DumpFormat::Custom(\"foobar\".to_owned())\n        );\n    }\n\n    #[test]\n    fn test_dump_parsed_flags_protobuf_format() {\n        let expected = protobuf::text_format::parse_from_str::<ProtoParsedFlags>(\n            crate::test::TEST_FLAGS_TEXTPROTO,\n        )\n        .unwrap()\n        .write_to_bytes()\n        .unwrap();\n        let parsed_flags = parse_test_flags();\n        let actual =\n            dump_parsed_flags(parsed_flags.parsed_flag.into_iter(), DumpFormat::Protobuf).unwrap();\n        assert_eq!(expected, actual);\n    }\n\n    #[test]\n    fn test_dump_parsed_flags_textproto_format() {\n        let parsed_flags = parse_test_flags();\n        let bytes =\n            dump_parsed_flags(parsed_flags.parsed_flag.into_iter(), DumpFormat::Textproto).unwrap();\n        let text = std::str::from_utf8(&bytes).unwrap();\n        assert_eq!(crate::test::TEST_FLAGS_TEXTPROTO.trim(), text.trim());\n    }\n\n    #[test]\n    fn test_dump_parsed_flags_custom_format() {\n        macro_rules! assert_dump_parsed_flags_custom_format_contains {\n            ($format:expr, $expected:expr) => {\n                let parsed_flags = parse_test_flags();\n                let bytes = dump_parsed_flags(\n                    parsed_flags.parsed_flag.into_iter(),\n                    $format.try_into().unwrap(),\n                )\n                .unwrap();\n                let text = std::str::from_utf8(&bytes).unwrap();\n                assert!(text.contains($expected));\n            };\n        }\n\n        // custom format\n        assert_dump_parsed_flags_custom_format_contains!(\n            \"{fully_qualified_name}={permission} + {state}\",\n            \"com.android.aconfig.test.enabled_ro=READ_ONLY + ENABLED\"\n        );\n    }\n\n    #[test]\n    fn test_dump_custom_format() {\n        macro_rules! assert_custom_format {\n            ($format:expr, $expected:expr) => {\n                let flag = parse_enabled_ro_flag();\n                let mut bytes = vec![];\n                dump_custom_format(&flag, $format, &mut bytes);\n                let text = std::str::from_utf8(&bytes).unwrap();\n                assert_eq!(text, $expected);\n            };\n        }\n\n        assert_custom_format!(\"{package}\", \"com.android.aconfig.test\\n\");\n        assert_custom_format!(\"{name}\", \"enabled_ro\\n\");\n        assert_custom_format!(\"{namespace}\", \"aconfig_test\\n\");\n        assert_custom_format!(\"{description}\", \"This flag is ENABLED + READ_ONLY\\n\");\n        assert_custom_format!(\"{bug}\", \"abc\\n\");\n        assert_custom_format!(\"{state}\", \"ENABLED\\n\");\n        assert_custom_format!(\"{state:bool}\", \"true\\n\");\n        assert_custom_format!(\"{permission}\", \"READ_ONLY\\n\");\n        assert_custom_format!(\"{trace}\", \"tests/test.aconfig: READ_WRITE + DISABLED, tests/first.values: READ_WRITE + DISABLED, tests/second.values: READ_ONLY + ENABLED\\n\");\n        assert_custom_format!(\n            \"{trace:paths}\",\n            \"tests/test.aconfig, tests/first.values, tests/second.values\\n\"\n        );\n        assert_custom_format!(\"{is_fixed_read_only}\", \"false\\n\");\n        assert_custom_format!(\"{is_exported}\", \"false\\n\");\n        assert_custom_format!(\"{container}\", \"system\\n\");\n        assert_custom_format!(\"{metadata}\", \"PURPOSE_BUGFIX\\n\");\n\n        assert_custom_format!(\"name={name}|state={state}\", \"name=enabled_ro|state=ENABLED\\n\");\n        assert_custom_format!(\"{state}{state}{state}\", \"ENABLEDENABLEDENABLED\\n\");\n    }\n\n    #[test]\n    fn test_create_filter_predicate() {\n        macro_rules! assert_create_filter_predicate {\n            ($filter:expr, $expected:expr) => {\n                let parsed_flags = parse_test_flags();\n                let predicate = create_filter_predicate($filter).unwrap();\n                let mut filtered_flags: Vec<String> = parsed_flags\n                    .parsed_flag\n                    .into_iter()\n                    .filter(predicate)\n                    .map(|flag| flag.fully_qualified_name())\n                    .collect();\n                filtered_flags.sort();\n                assert_eq!(&filtered_flags, $expected);\n            };\n        }\n\n        assert_create_filter_predicate!(\n            \"package:com.android.aconfig.test\",\n            &[\n                \"com.android.aconfig.test.disabled_ro\",\n                \"com.android.aconfig.test.disabled_rw\",\n                \"com.android.aconfig.test.disabled_rw_exported\",\n                \"com.android.aconfig.test.disabled_rw_in_other_namespace\",\n                \"com.android.aconfig.test.enabled_fixed_ro\",\n                \"com.android.aconfig.test.enabled_fixed_ro_exported\",\n                \"com.android.aconfig.test.enabled_ro\",\n                \"com.android.aconfig.test.enabled_ro_exported\",\n                \"com.android.aconfig.test.enabled_rw\",\n            ]\n        );\n        assert_create_filter_predicate!(\n            \"name:disabled_rw\",\n            &[\"com.android.aconfig.test.disabled_rw\"]\n        );\n        assert_create_filter_predicate!(\n            \"namespace:other_namespace\",\n            &[\"com.android.aconfig.test.disabled_rw_in_other_namespace\"]\n        );\n        // description: not supported yet\n        assert_create_filter_predicate!(\"bug:123\", &[\"com.android.aconfig.test.disabled_ro\",]);\n        assert_create_filter_predicate!(\n            \"state:ENABLED\",\n            &[\n                \"com.android.aconfig.test.enabled_fixed_ro\",\n                \"com.android.aconfig.test.enabled_fixed_ro_exported\",\n                \"com.android.aconfig.test.enabled_ro\",\n                \"com.android.aconfig.test.enabled_ro_exported\",\n                \"com.android.aconfig.test.enabled_rw\",\n            ]\n        );\n        assert_create_filter_predicate!(\n            \"permission:READ_ONLY\",\n            &[\n                \"com.android.aconfig.test.disabled_ro\",\n                \"com.android.aconfig.test.enabled_fixed_ro\",\n                \"com.android.aconfig.test.enabled_fixed_ro_exported\",\n                \"com.android.aconfig.test.enabled_ro\",\n                \"com.android.aconfig.test.enabled_ro_exported\",\n            ]\n        );\n        // trace: not supported yet\n        assert_create_filter_predicate!(\n            \"is_fixed_read_only:true\",\n            &[\n                \"com.android.aconfig.test.enabled_fixed_ro\",\n                \"com.android.aconfig.test.enabled_fixed_ro_exported\",\n            ]\n        );\n        assert_create_filter_predicate!(\n            \"is_exported:true\",\n            &[\n                \"com.android.aconfig.test.disabled_rw_exported\",\n                \"com.android.aconfig.test.enabled_fixed_ro_exported\",\n                \"com.android.aconfig.test.enabled_ro_exported\",\n            ]\n        );\n        assert_create_filter_predicate!(\n            \"container:system\",\n            &[\n                \"com.android.aconfig.test.disabled_ro\",\n                \"com.android.aconfig.test.disabled_rw\",\n                \"com.android.aconfig.test.disabled_rw_exported\",\n                \"com.android.aconfig.test.disabled_rw_in_other_namespace\",\n                \"com.android.aconfig.test.enabled_fixed_ro\",\n                \"com.android.aconfig.test.enabled_fixed_ro_exported\",\n                \"com.android.aconfig.test.enabled_ro\",\n                \"com.android.aconfig.test.enabled_ro_exported\",\n                \"com.android.aconfig.test.enabled_rw\",\n            ]\n        );\n        // metadata: not supported yet\n\n        // synthesized fields\n        assert_create_filter_predicate!(\n            \"fully_qualified_name:com.android.aconfig.test.disabled_rw\",\n            &[\"com.android.aconfig.test.disabled_rw\"]\n        );\n\n        // multiple sub filters\n        assert_create_filter_predicate!(\n            \"permission:READ_ONLY+state:ENABLED\",\n            &[\n                \"com.android.aconfig.test.enabled_fixed_ro\",\n                \"com.android.aconfig.test.enabled_fixed_ro_exported\",\n                \"com.android.aconfig.test.enabled_ro\",\n                \"com.android.aconfig.test.enabled_ro_exported\",\n            ]\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/main.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! `aconfig` is a build time tool to manage build time configurations, such as feature flags.\n\nuse aconfig_storage_file::DEFAULT_FILE_VERSION;\nuse aconfig_storage_file::MAX_SUPPORTED_FILE_VERSION;\nuse anyhow::{anyhow, bail, Context, Result};\nuse clap::{builder::ArgAction, builder::EnumValueParser, Arg, ArgMatches, Command};\nuse core::any::Any;\nuse std::fs;\nuse std::io;\nuse std::io::Write;\nuse std::path::{Path, PathBuf};\n\nmod codegen;\nmod commands;\nmod dump;\nmod storage;\n\nuse aconfig_storage_file::StorageFileType;\nuse codegen::CodegenMode;\nuse convert_finalized_flags::FinalizedFlagMap;\nuse dump::DumpFormat;\n\n#[cfg(test)]\nmod test;\n\nuse commands::{Input, OutputFile};\n\nconst HELP_DUMP_CACHE: &str = r#\"\nAn aconfig cache file, created via `aconfig create-cache`.\n\"#;\n\nconst HELP_DUMP_FORMAT: &str = r#\"\nChange the output format for each flag.\n\nThe argument to --format is a format string. Each flag will be a copy of this string, with certain\nplaceholders replaced by attributes of the flag. The placeholders are\n\n  {package}\n  {name}\n  {namespace}\n  {description}\n  {bug}\n  {state}\n  {state:bool}\n  {permission}\n  {trace}\n  {trace:paths}\n  {is_fixed_read_only}\n  {is_exported}\n  {container}\n  {metadata}\n  {fully_qualified_name}\n\nNote: the format strings \"textproto\" and \"protobuf\" are handled in a special way: they output all\nflag attributes in text or binary protobuf format.\n\nExamples:\n\n  # See which files were read to determine the value of a flag; the files were read in the order\n  # listed.\n  --format='{fully_qualified_name} {trace}'\n\n  # Trace the files read for a specific flag. Useful during debugging.\n  --filter=fully_qualified_name:com.foo.flag_name --format='{trace}'\n\n  # Print a somewhat human readable description of each flag.\n  --format='The flag {name} in package {package} is {state} and has permission {permission}.'\n\"#;\n\nconst HELP_DUMP_FILTER: &str = r#\"\nLimit which flags to output. If --filter is omitted, all flags will be printed. If multiple\n--filter options are provided, the output will be limited to flags that match any of the filters.\n\nThe argument to --filter is a search query. Multiple queries can be AND-ed together by\nconcatenating them with a plus sign.\n\nValid queries are:\n\n  package:<string>\n  name:<string>\n  namespace:<string>\n  bug:<string>\n  state:ENABLED|DISABLED\n  permission:READ_ONLY|READ_WRITE\n  is_fixed_read_only:true|false\n  is_exported:true|false\n  container:<string>\n  fully_qualified_name:<string>\n\nNote: there is currently no support for filtering based on these flag attributes: description,\ntrace, metadata.\n\nExamples:\n\n  # Print a single flag:\n  --filter=fully_qualified_name:com.foo.flag_name\n\n  # Print all known information about a single flag:\n  --filter=fully_qualified_name:com.foo.flag_name --format=textproto\n\n  # Print all flags in the com.foo package, and all enabled flags in the com.bar package:\n  --filter=package:com.foo --filter=package.com.bar+state:ENABLED\n\"#;\n\nconst HELP_DUMP_DEDUP: &str = r#\"\nAllow the same flag to be present in multiple cache files; if duplicates are found, collapse into\na single instance.\n\"#;\n\nfn cli() -> Command {\n    Command::new(\"aconfig\")\n        .subcommand_required(true)\n        .subcommand(\n            Command::new(\"create-cache\")\n                .arg(Arg::new(\"package\").long(\"package\").required(true))\n                .arg(Arg::new(\"container\").long(\"container\").required(true))\n                .arg(Arg::new(\"declarations\").long(\"declarations\").action(ArgAction::Append))\n                .arg(Arg::new(\"values\").long(\"values\").action(ArgAction::Append))\n                .arg(\n                    Arg::new(\"default-permission\")\n                        .long(\"default-permission\")\n                        .value_parser(aconfig_protos::flag_permission::parse_from_str)\n                        .default_value(aconfig_protos::flag_permission::to_string(\n                            &commands::DEFAULT_FLAG_PERMISSION,\n                        )),\n                )\n                .arg(\n                    Arg::new(\"allow-read-write\")\n                        .long(\"allow-read-write\")\n                        .value_parser(clap::value_parser!(bool))\n                        .default_value(\"true\"),\n                )\n                .arg(Arg::new(\"cache\").long(\"cache\").required(true)),\n        )\n        .subcommand(\n            Command::new(\"create-java-lib\")\n                .arg(Arg::new(\"cache\").long(\"cache\").required(true))\n                .arg(Arg::new(\"out\").long(\"out\").required(true))\n                .arg(\n                    Arg::new(\"mode\")\n                        .long(\"mode\")\n                        .value_parser(EnumValueParser::<CodegenMode>::new())\n                        .default_value(\"production\"),\n                )\n                .arg(\n                    Arg::new(\"single-exported-file\")\n                        .long(\"single-exported-file\")\n                        .value_parser(clap::value_parser!(bool))\n                        .default_value(\"false\"),\n                )\n                // TODO: b/395899938 - clean up flags for switching to new storage\n                .arg(\n                    Arg::new(\"allow-instrumentation\")\n                        .long(\"allow-instrumentation\")\n                        .value_parser(clap::value_parser!(bool))\n                        .default_value(\"false\"),\n                )\n                // TODO: b/395899938 - clean up flags for switching to new storage\n                .arg(\n                    Arg::new(\"new-exported\")\n                        .long(\"new-exported\")\n                        .value_parser(clap::value_parser!(bool))\n                        .default_value(\"false\"),\n                )\n                // Allows build flag toggling of checking API level in exported\n                // flag lib for finalized API flags.\n                // TODO: b/378936061 - Remove once build flag for API level\n                // check is fully enabled.\n                .arg(\n                    Arg::new(\"check-api-level\")\n                        .long(\"check-api-level\")\n                        .value_parser(clap::value_parser!(bool))\n                        .default_value(\"false\"),\n                ),\n        )\n        .subcommand(\n            Command::new(\"create-cpp-lib\")\n                .arg(Arg::new(\"cache\").long(\"cache\").required(true))\n                .arg(Arg::new(\"out\").long(\"out\").required(true))\n                .arg(\n                    Arg::new(\"mode\")\n                        .long(\"mode\")\n                        .value_parser(EnumValueParser::<CodegenMode>::new())\n                        .default_value(\"production\"),\n                )\n                .arg(\n                    Arg::new(\"allow-instrumentation\")\n                        .long(\"allow-instrumentation\")\n                        .value_parser(clap::value_parser!(bool))\n                        .default_value(\"false\"),\n                ),\n        )\n        .subcommand(\n            Command::new(\"create-rust-lib\")\n                .arg(Arg::new(\"cache\").long(\"cache\").required(true))\n                .arg(Arg::new(\"out\").long(\"out\").required(true))\n                .arg(\n                    Arg::new(\"allow-instrumentation\")\n                        .long(\"allow-instrumentation\")\n                        .value_parser(clap::value_parser!(bool))\n                        .default_value(\"false\"),\n                )\n                .arg(\n                    Arg::new(\"mode\")\n                        .long(\"mode\")\n                        .value_parser(EnumValueParser::<CodegenMode>::new())\n                        .default_value(\"production\"),\n                ),\n        )\n        .subcommand(\n            Command::new(\"create-device-config-defaults\")\n                .arg(Arg::new(\"cache\").long(\"cache\").action(ArgAction::Append).required(true))\n                .arg(Arg::new(\"out\").long(\"out\").default_value(\"-\")),\n        )\n        .subcommand(\n            Command::new(\"create-device-config-sysprops\")\n                .arg(Arg::new(\"cache\").long(\"cache\").action(ArgAction::Append).required(true))\n                .arg(Arg::new(\"out\").long(\"out\").default_value(\"-\")),\n        )\n        .subcommand(\n            Command::new(\"dump-cache\")\n                .alias(\"dump\")\n                .arg(\n                    Arg::new(\"cache\")\n                        .long(\"cache\")\n                        .action(ArgAction::Append)\n                        .long_help(HELP_DUMP_CACHE.trim()),\n                )\n                .arg(\n                    Arg::new(\"format\")\n                        .long(\"format\")\n                        .value_parser(|s: &str| DumpFormat::try_from(s))\n                        .default_value(\n                            \"{fully_qualified_name} [{container}]: {permission} + {state}\",\n                        )\n                        .long_help(HELP_DUMP_FORMAT.trim()),\n                )\n                .arg(\n                    Arg::new(\"filter\")\n                        .long(\"filter\")\n                        .action(ArgAction::Append)\n                        .long_help(HELP_DUMP_FILTER.trim()),\n                )\n                .arg(\n                    Arg::new(\"dedup\")\n                        .long(\"dedup\")\n                        .num_args(0)\n                        .action(ArgAction::SetTrue)\n                        .long_help(HELP_DUMP_DEDUP.trim()),\n                )\n                .arg(Arg::new(\"out\").long(\"out\").default_value(\"-\")),\n        )\n        .subcommand(\n            Command::new(\"create-storage\")\n                .arg(\n                    Arg::new(\"container\")\n                        .long(\"container\")\n                        .required(true)\n                        .help(\"The target container for the generated storage file.\"),\n                )\n                .arg(\n                    Arg::new(\"file\")\n                        .long(\"file\")\n                        .value_parser(|s: &str| StorageFileType::try_from(s)),\n                )\n                .arg(Arg::new(\"cache\").long(\"cache\").action(ArgAction::Append).required(true))\n                .arg(Arg::new(\"out\").long(\"out\").required(true))\n                .arg(\n                    Arg::new(\"version\")\n                        .long(\"version\")\n                        .required(false)\n                        .value_parser(|s: &str| s.parse::<u32>()),\n                ),\n        )\n}\n\nfn get_required_arg<'a, T>(matches: &'a ArgMatches, arg_name: &str) -> Result<&'a T>\nwhere\n    T: Any + Clone + Send + Sync + 'static,\n{\n    matches\n        .get_one::<T>(arg_name)\n        .ok_or(anyhow!(\"internal error: required argument '{}' not found\", arg_name))\n}\n\nfn get_optional_arg<'a, T>(matches: &'a ArgMatches, arg_name: &str) -> Option<&'a T>\nwhere\n    T: Any + Clone + Send + Sync + 'static,\n{\n    matches.get_one::<T>(arg_name)\n}\n\nfn open_zero_or_more_files(matches: &ArgMatches, arg_name: &str) -> Result<Vec<Input>> {\n    let mut opened_files = vec![];\n    for path in matches.get_many::<String>(arg_name).unwrap_or_default() {\n        let file = Box::new(fs::File::open(path)?);\n        opened_files.push(Input { source: path.to_string(), reader: file });\n    }\n    Ok(opened_files)\n}\n\nfn open_single_file(matches: &ArgMatches, arg_name: &str) -> Result<Input> {\n    let Some(path) = matches.get_one::<String>(arg_name) else {\n        bail!(\"missing argument {}\", arg_name);\n    };\n    let file = Box::new(fs::File::open(path)?);\n    Ok(Input { source: path.to_string(), reader: file })\n}\n\nfn write_output_file_realtive_to_dir(root: &Path, output_file: &OutputFile) -> Result<()> {\n    let path = root.join(&output_file.path);\n    let parent = path\n        .parent()\n        .ok_or(anyhow!(\"unable to locate parent of output file {}\", path.display()))?;\n    fs::create_dir_all(parent)\n        .with_context(|| format!(\"failed to create directory {}\", parent.display()))?;\n    let mut file =\n        fs::File::create(&path).with_context(|| format!(\"failed to open {}\", path.display()))?;\n    file.write_all(&output_file.contents)\n        .with_context(|| format!(\"failed to write to {}\", path.display()))?;\n    Ok(())\n}\n\nfn write_output_to_file_or_stdout(path: &str, data: &[u8]) -> Result<()> {\n    if path == \"-\" {\n        io::stdout().write_all(data).context(\"failed to write to stdout\")?;\n    } else {\n        fs::File::create(path)\n            .with_context(|| format!(\"failed to open {}\", path))?\n            .write_all(data)\n            .with_context(|| format!(\"failed to write to {}\", path))?;\n    }\n    Ok(())\n}\n\nfn load_finalized_flags() -> Result<FinalizedFlagMap> {\n    let json_str = include_str!(concat!(env!(\"OUT_DIR\"), \"/finalized_flags_record.json\"));\n    let map = serde_json::from_str(json_str)?;\n    Ok(map)\n}\n\nfn main() -> Result<()> {\n    let matches = cli().get_matches();\n    match matches.subcommand() {\n        Some((\"create-cache\", sub_matches)) => {\n            let package = get_required_arg::<String>(sub_matches, \"package\")?;\n            let container =\n                get_optional_arg::<String>(sub_matches, \"container\").map(|c| c.as_str());\n            let declarations = open_zero_or_more_files(sub_matches, \"declarations\")?;\n            let values = open_zero_or_more_files(sub_matches, \"values\")?;\n            let default_permission = get_required_arg::<aconfig_protos::ProtoFlagPermission>(\n                sub_matches,\n                \"default-permission\",\n            )?;\n            let allow_read_write = get_optional_arg::<bool>(sub_matches, \"allow-read-write\")\n                .expect(\"failed to parse allow-read-write\");\n            let output = commands::parse_flags(\n                package,\n                container,\n                declarations,\n                values,\n                *default_permission,\n                *allow_read_write,\n            )\n            .context(\"failed to create cache\")?;\n            let path = get_required_arg::<String>(sub_matches, \"cache\")?;\n            write_output_to_file_or_stdout(path, &output)?;\n        }\n        Some((\"create-java-lib\", sub_matches)) => {\n            let cache = open_single_file(sub_matches, \"cache\")?;\n            let mode = get_required_arg::<CodegenMode>(sub_matches, \"mode\")?;\n            let allow_instrumentation =\n                get_required_arg::<bool>(sub_matches, \"allow-instrumentation\")?;\n            let new_exported = get_required_arg::<bool>(sub_matches, \"new-exported\")?;\n            let single_exported_file =\n                get_required_arg::<bool>(sub_matches, \"single-exported-file\")?;\n\n            let check_api_level = get_required_arg::<bool>(sub_matches, \"check-api-level\")?;\n            let finalized_flags: FinalizedFlagMap =\n                if *check_api_level { load_finalized_flags()? } else { FinalizedFlagMap::new() };\n\n            let generated_files = commands::create_java_lib(\n                cache,\n                *mode,\n                *allow_instrumentation,\n                *new_exported,\n                *single_exported_file,\n                finalized_flags,\n            )\n            .context(\"failed to create java lib\")?;\n            let dir = PathBuf::from(get_required_arg::<String>(sub_matches, \"out\")?);\n            generated_files\n                .iter()\n                .try_for_each(|file| write_output_file_realtive_to_dir(&dir, file))?;\n        }\n        Some((\"create-cpp-lib\", sub_matches)) => {\n            let cache = open_single_file(sub_matches, \"cache\")?;\n            let mode = get_required_arg::<CodegenMode>(sub_matches, \"mode\")?;\n            let generated_files =\n                commands::create_cpp_lib(cache, *mode).context(\"failed to create cpp lib\")?;\n            let dir = PathBuf::from(get_required_arg::<String>(sub_matches, \"out\")?);\n            generated_files\n                .iter()\n                .try_for_each(|file| write_output_file_realtive_to_dir(&dir, file))?;\n        }\n        Some((\"create-rust-lib\", sub_matches)) => {\n            let cache = open_single_file(sub_matches, \"cache\")?;\n            let mode = get_required_arg::<CodegenMode>(sub_matches, \"mode\")?;\n            let generated_file =\n                commands::create_rust_lib(cache, *mode).context(\"failed to create rust lib\")?;\n            let dir = PathBuf::from(get_required_arg::<String>(sub_matches, \"out\")?);\n            write_output_file_realtive_to_dir(&dir, &generated_file)?;\n        }\n        Some((\"create-device-config-defaults\", sub_matches)) => {\n            let cache = open_single_file(sub_matches, \"cache\")?;\n            let output = commands::create_device_config_defaults(cache)\n                .context(\"failed to create device config defaults\")?;\n            let path = get_required_arg::<String>(sub_matches, \"out\")?;\n            write_output_to_file_or_stdout(path, &output)?;\n        }\n        Some((\"create-device-config-sysprops\", sub_matches)) => {\n            let cache = open_single_file(sub_matches, \"cache\")?;\n            let output = commands::create_device_config_sysprops(cache)\n                .context(\"failed to create device config sysprops\")?;\n            let path = get_required_arg::<String>(sub_matches, \"out\")?;\n            write_output_to_file_or_stdout(path, &output)?;\n        }\n        Some((\"dump-cache\", sub_matches)) => {\n            let input = open_zero_or_more_files(sub_matches, \"cache\")?;\n            let format = get_required_arg::<DumpFormat>(sub_matches, \"format\")\n                .context(\"failed to dump previously parsed flags\")?;\n            let filters = sub_matches\n                .get_many::<String>(\"filter\")\n                .unwrap_or_default()\n                .map(String::as_ref)\n                .collect::<Vec<_>>();\n            let dedup = get_required_arg::<bool>(sub_matches, \"dedup\")?;\n            let output = commands::dump_parsed_flags(input, format.clone(), &filters, *dedup)?;\n            let path = get_required_arg::<String>(sub_matches, \"out\")?;\n            write_output_to_file_or_stdout(path, &output)?;\n        }\n        Some((\"create-storage\", sub_matches)) => {\n            let version =\n                get_optional_arg::<u32>(sub_matches, \"version\").unwrap_or(&DEFAULT_FILE_VERSION);\n            if *version > MAX_SUPPORTED_FILE_VERSION {\n                bail!(\"Invalid version selected ({})\", version);\n            }\n            let file = get_required_arg::<StorageFileType>(sub_matches, \"file\")\n                .context(\"Invalid storage file selection\")?;\n            let cache = open_zero_or_more_files(sub_matches, \"cache\")?;\n            let container = get_required_arg::<String>(sub_matches, \"container\")?;\n            let path = get_required_arg::<String>(sub_matches, \"out\")?;\n\n            let output = commands::create_storage(cache, container, file, *version)\n                .context(\"failed to create storage files\")?;\n            write_output_to_file_or_stdout(path, &output)?;\n        }\n        _ => unreachable!(),\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/storage/flag_info.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse crate::commands::assign_flag_ids;\nuse crate::storage::FlagPackage;\nuse aconfig_protos::{ProtoFlagPermission, ProtoFlagState};\nuse aconfig_storage_file::{FlagInfoHeader, FlagInfoList, FlagInfoNode, StorageFileType};\nuse anyhow::{anyhow, Result};\n\nfn new_header(container: &str, num_flags: u32, version: u32) -> FlagInfoHeader {\n    FlagInfoHeader {\n        version,\n        container: String::from(container),\n        file_type: StorageFileType::FlagInfo as u8,\n        file_size: 0,\n        num_flags,\n        boolean_flag_offset: 0,\n    }\n}\n\npub fn create_flag_info(\n    container: &str,\n    packages: &[FlagPackage],\n    version: u32,\n) -> Result<FlagInfoList> {\n    // Exclude system/vendor/product flags that are RO+disabled.\n    let mut filtered_packages = packages.to_vec();\n    if container == \"system\" || container == \"vendor\" || container == \"product\" {\n        for package in filtered_packages.iter_mut() {\n            package.boolean_flags.retain(|b| {\n                !(b.state == Some(ProtoFlagState::DISABLED.into())\n                    && b.permission == Some(ProtoFlagPermission::READ_ONLY.into()))\n            });\n        }\n    }\n\n    let num_flags = filtered_packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();\n\n    let mut is_flag_rw = vec![false; num_flags as usize];\n    for pkg in filtered_packages {\n        let start_index = pkg.boolean_start_index as usize;\n        let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;\n        for pf in pkg.boolean_flags {\n            let fid = flag_ids\n                .get(pf.name())\n                .ok_or(anyhow!(format!(\"missing flag id for {}\", pf.name())))?;\n            is_flag_rw[start_index + (*fid as usize)] =\n                pf.permission() == ProtoFlagPermission::READ_WRITE;\n        }\n    }\n\n    let mut list = FlagInfoList {\n        header: new_header(container, num_flags, version),\n        nodes: is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(),\n    };\n\n    // initialize all header fields\n    list.header.boolean_flag_offset = list.header.into_bytes().len() as u32;\n    let bytes_per_node = FlagInfoNode::create(false).into_bytes().len() as u32;\n    list.header.file_size = list.header.boolean_flag_offset + num_flags * bytes_per_node;\n\n    Ok(list)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};\n    use aconfig_storage_file::DEFAULT_FILE_VERSION;\n\n    pub fn create_test_flag_info_list_from_source() -> Result<FlagInfoList> {\n        let caches = parse_all_test_flags();\n        let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION);\n        create_flag_info(\"mockup\", &packages, DEFAULT_FILE_VERSION)\n    }\n\n    #[test]\n    // this test point locks down the flag info creation and each field\n    fn test_list_contents() {\n        let flag_info_list = create_test_flag_info_list_from_source();\n        assert!(flag_info_list.is_ok());\n        let expected_flag_info_list =\n            aconfig_storage_file::test_utils::create_test_flag_info_list(DEFAULT_FILE_VERSION);\n        assert_eq!(flag_info_list.unwrap(), expected_flag_info_list);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/storage/flag_table.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse crate::commands::{assign_flag_ids, should_include_flag};\nuse crate::storage::FlagPackage;\nuse aconfig_protos::ProtoFlagPermission;\nuse aconfig_storage_file::{\n    get_table_size, FlagTable, FlagTableHeader, FlagTableNode, StorageFileType, StoredFlagType,\n};\nuse anyhow::{anyhow, Result};\n\nfn new_header(container: &str, num_flags: u32, version: u32) -> FlagTableHeader {\n    FlagTableHeader {\n        version,\n        container: String::from(container),\n        file_type: StorageFileType::FlagMap as u8,\n        file_size: 0,\n        num_flags,\n        bucket_offset: 0,\n        node_offset: 0,\n    }\n}\n\n// a struct that contains FlagTableNode and a bunch of other information to help\n// flag table creation\n#[derive(PartialEq, Debug, Clone)]\nstruct FlagTableNodeWrapper {\n    pub node: FlagTableNode,\n    pub bucket_index: u32,\n}\n\nimpl FlagTableNodeWrapper {\n    fn new(\n        package_id: u32,\n        flag_name: &str,\n        flag_type: StoredFlagType,\n        flag_index: u16,\n        num_buckets: u32,\n    ) -> Self {\n        let bucket_index = FlagTableNode::find_bucket_index(package_id, flag_name, num_buckets);\n        let node = FlagTableNode {\n            package_id,\n            flag_name: flag_name.to_string(),\n            flag_type,\n            flag_index,\n            next_offset: None,\n        };\n        Self { node, bucket_index }\n    }\n\n    fn create_nodes(package: &FlagPackage, num_buckets: u32) -> Result<Vec<Self>> {\n        // Exclude system/vendor/product flags that are RO+disabled.\n        let mut filtered_package = package.clone();\n        filtered_package.boolean_flags.retain(|pf| should_include_flag(pf));\n\n        let flag_ids =\n            assign_flag_ids(package.package_name, filtered_package.boolean_flags.iter().copied())?;\n        filtered_package\n            .boolean_flags\n            .iter()\n            .map(|&pf| {\n                let fid = flag_ids\n                    .get(pf.name())\n                    .ok_or(anyhow!(format!(\"missing flag id for {}\", pf.name())))?;\n                let flag_type = if pf.is_fixed_read_only() {\n                    StoredFlagType::FixedReadOnlyBoolean\n                } else {\n                    match pf.permission() {\n                        ProtoFlagPermission::READ_WRITE => StoredFlagType::ReadWriteBoolean,\n                        ProtoFlagPermission::READ_ONLY => StoredFlagType::ReadOnlyBoolean,\n                    }\n                };\n                Ok(Self::new(package.package_id, pf.name(), flag_type, *fid, num_buckets))\n            })\n            .collect::<Result<Vec<_>>>()\n    }\n}\n\npub fn create_flag_table(\n    container: &str,\n    packages: &[FlagPackage],\n    version: u32,\n) -> Result<FlagTable> {\n    // create table\n    let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();\n    let num_buckets = get_table_size(num_flags)?;\n\n    let mut header = new_header(container, num_flags, version);\n    let mut buckets = vec![None; num_buckets as usize];\n    let mut node_wrappers = packages\n        .iter()\n        .map(|pkg| FlagTableNodeWrapper::create_nodes(pkg, num_buckets))\n        .collect::<Result<Vec<_>>>()?\n        .concat();\n\n    // initialize all header fields\n    header.bucket_offset = header.into_bytes().len() as u32;\n    header.node_offset = header.bucket_offset + num_buckets * 4;\n    header.file_size = header.node_offset\n        + node_wrappers.iter().map(|x| x.node.into_bytes().len()).sum::<usize>() as u32;\n\n    // sort nodes by bucket index for efficiency\n    node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));\n\n    // fill all node offset\n    let mut offset = header.node_offset;\n    for i in 0..node_wrappers.len() {\n        let node_bucket_idx = node_wrappers[i].bucket_index;\n        let next_node_bucket_idx = if i + 1 < node_wrappers.len() {\n            Some(node_wrappers[i + 1].bucket_index)\n        } else {\n            None\n        };\n\n        if buckets[node_bucket_idx as usize].is_none() {\n            buckets[node_bucket_idx as usize] = Some(offset);\n        }\n        offset += node_wrappers[i].node.into_bytes().len() as u32;\n\n        if let Some(index) = next_node_bucket_idx {\n            if index == node_bucket_idx {\n                node_wrappers[i].node.next_offset = Some(offset);\n            }\n        }\n    }\n\n    let table =\n        FlagTable { header, buckets, nodes: node_wrappers.into_iter().map(|nw| nw.node).collect() };\n\n    Ok(table)\n}\n\n#[cfg(test)]\nmod tests {\n    use aconfig_storage_file::DEFAULT_FILE_VERSION;\n\n    use super::*;\n    use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};\n\n    fn create_test_flag_table_from_source() -> Result<FlagTable> {\n        let caches = parse_all_test_flags();\n        let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION);\n        create_flag_table(\"mockup\", &packages, DEFAULT_FILE_VERSION)\n    }\n\n    #[test]\n    // this test point locks down the table creation and each field\n    fn test_table_contents() {\n        let flag_table = create_test_flag_table_from_source();\n        assert!(flag_table.is_ok());\n        let expected_flag_table =\n            aconfig_storage_file::test_utils::create_test_flag_table(DEFAULT_FILE_VERSION);\n        assert_eq!(flag_table.unwrap(), expected_flag_table);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/storage/flag_value.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse crate::commands::assign_flag_ids;\nuse crate::storage::FlagPackage;\nuse aconfig_protos::{ProtoFlagPermission, ProtoFlagState};\nuse aconfig_storage_file::{FlagValueHeader, FlagValueList, StorageFileType};\nuse anyhow::{anyhow, Result};\n\nfn new_header(container: &str, num_flags: u32, version: u32) -> FlagValueHeader {\n    FlagValueHeader {\n        version,\n        container: String::from(container),\n        file_type: StorageFileType::FlagVal as u8,\n        file_size: 0,\n        num_flags,\n        boolean_value_offset: 0,\n    }\n}\n\npub fn create_flag_value(\n    container: &str,\n    packages: &[FlagPackage],\n    version: u32,\n) -> Result<FlagValueList> {\n    // Exclude system/vendor/product flags that are RO+disabled.\n    let mut filtered_packages = packages.to_vec();\n    if container == \"system\" || container == \"vendor\" || container == \"product\" {\n        for package in filtered_packages.iter_mut() {\n            package.boolean_flags.retain(|b| {\n                !(b.state == Some(ProtoFlagState::DISABLED.into())\n                    && b.permission == Some(ProtoFlagPermission::READ_ONLY.into()))\n            });\n        }\n    }\n    let num_flags = filtered_packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();\n    let mut list = FlagValueList {\n        header: new_header(container, num_flags, version),\n        booleans: vec![false; num_flags as usize],\n    };\n    for pkg in filtered_packages {\n        let start_index = pkg.boolean_start_index as usize;\n        let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;\n        for pf in pkg.boolean_flags.iter() {\n            let fid = flag_ids\n                .get(pf.name())\n                .ok_or(anyhow!(format!(\"missing flag id for {}\", pf.name())))?;\n\n            list.booleans[start_index + (*fid as usize)] = pf.state() == ProtoFlagState::ENABLED;\n        }\n    }\n\n    // initialize all header fields\n    list.header.boolean_value_offset = list.header.into_bytes().len() as u32;\n    list.header.file_size = list.header.boolean_value_offset + num_flags;\n\n    Ok(list)\n}\n\n#[cfg(test)]\nmod tests {\n    use aconfig_storage_file::DEFAULT_FILE_VERSION;\n\n    use super::*;\n    use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};\n\n    pub fn create_test_flag_value_list_from_source() -> Result<FlagValueList> {\n        let caches = parse_all_test_flags();\n        let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION);\n        create_flag_value(\"mockup\", &packages, DEFAULT_FILE_VERSION)\n    }\n\n    #[test]\n    // this test point locks down the flag value creation and each field\n    fn test_list_contents() {\n        let flag_value_list = create_test_flag_value_list_from_source();\n        assert!(flag_value_list.is_ok());\n        let expected_flag_value_list =\n            aconfig_storage_file::test_utils::create_test_flag_value_list(DEFAULT_FILE_VERSION);\n        assert_eq!(flag_value_list.unwrap(), expected_flag_value_list);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/storage/mod.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npub mod flag_info;\npub mod flag_table;\npub mod flag_value;\npub mod package_table;\n\nuse anyhow::Result;\nuse std::collections::{HashMap, HashSet};\n\nuse crate::commands::compute_flags_fingerprint;\nuse crate::storage::{\n    flag_info::create_flag_info, flag_table::create_flag_table, flag_value::create_flag_value,\n    package_table::create_package_table,\n};\nuse aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag, ProtoParsedFlags};\nuse aconfig_storage_file::StorageFileType;\n\n#[derive(Clone)]\npub struct FlagPackage<'a> {\n    pub package_name: &'a str,\n    pub package_id: u32,\n    pub fingerprint: u64,\n    pub flag_names: HashSet<&'a str>,\n    pub boolean_flags: Vec<&'a ProtoParsedFlag>,\n    // The index of the first boolean flag in this aconfig package among all boolean\n    // flags in this container.\n    pub boolean_start_index: u32,\n}\n\nimpl<'a> FlagPackage<'a> {\n    fn new(package_name: &'a str, package_id: u32) -> Self {\n        FlagPackage {\n            package_name,\n            package_id,\n            fingerprint: 0,\n            flag_names: HashSet::new(),\n            boolean_flags: vec![],\n            boolean_start_index: 0,\n        }\n    }\n\n    fn insert(&mut self, pf: &'a ProtoParsedFlag) {\n        if self.flag_names.insert(pf.name()) {\n            self.boolean_flags.push(pf);\n        }\n    }\n}\n\npub fn group_flags_by_package<'a, I>(parsed_flags_vec_iter: I, version: u32) -> Vec<FlagPackage<'a>>\nwhere\n    I: Iterator<Item = &'a ProtoParsedFlags>,\n{\n    // group flags by package\n    let mut packages: Vec<FlagPackage<'a>> = Vec::new();\n    let mut package_index: HashMap<&str, usize> = HashMap::new();\n    for parsed_flags in parsed_flags_vec_iter {\n        for parsed_flag in parsed_flags.parsed_flag.iter() {\n            let index = *(package_index.entry(parsed_flag.package()).or_insert(packages.len()));\n            if index == packages.len() {\n                packages.push(FlagPackage::new(parsed_flag.package(), index as u32));\n            }\n\n            // Exclude system/vendor/product flags that are RO+disabled.\n            if (parsed_flag.container == Some(\"system\".to_string())\n                || parsed_flag.container == Some(\"vendor\".to_string())\n                || parsed_flag.container == Some(\"product\".to_string()))\n                && parsed_flag.permission == Some(ProtoFlagPermission::READ_ONLY.into())\n                && parsed_flag.state == Some(ProtoFlagState::DISABLED.into())\n            {\n                continue;\n            }\n\n            packages[index].insert(parsed_flag);\n        }\n    }\n\n    // Calculate boolean flag start index for each package\n    let mut boolean_start_index = 0;\n    for p in packages.iter_mut() {\n        p.boolean_start_index = boolean_start_index;\n        boolean_start_index += p.boolean_flags.len() as u32;\n\n        if version >= 2 {\n            let mut flag_names_vec =\n                p.flag_names.clone().into_iter().map(String::from).collect::<Vec<_>>();\n            let fingerprint = compute_flags_fingerprint(&mut flag_names_vec);\n            p.fingerprint = fingerprint;\n        }\n    }\n\n    packages\n}\n\npub fn generate_storage_file<'a, I>(\n    container: &str,\n    parsed_flags_vec_iter: I,\n    file: &StorageFileType,\n    version: u32,\n) -> Result<Vec<u8>>\nwhere\n    I: Iterator<Item = &'a ProtoParsedFlags>,\n{\n    let packages = group_flags_by_package(parsed_flags_vec_iter, version);\n\n    match file {\n        StorageFileType::PackageMap => {\n            let package_table = create_package_table(container, &packages, version)?;\n            Ok(package_table.into_bytes())\n        }\n        StorageFileType::FlagMap => {\n            let flag_table = create_flag_table(container, &packages, version)?;\n            Ok(flag_table.into_bytes())\n        }\n        StorageFileType::FlagVal => {\n            let flag_value = create_flag_value(container, &packages, version)?;\n            Ok(flag_value.into_bytes())\n        }\n        StorageFileType::FlagInfo => {\n            let flag_info = create_flag_info(container, &packages, version)?;\n            Ok(flag_info.into_bytes())\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use aconfig_storage_file::DEFAULT_FILE_VERSION;\n\n    use super::*;\n    use crate::Input;\n\n    pub fn parse_all_test_flags() -> Vec<ProtoParsedFlags> {\n        let aconfig_files = [\n            (\n                \"com.android.aconfig.storage.test_1\",\n                \"storage_test_1.aconfig\",\n                include_bytes!(\"../../tests/storage_test_1.aconfig\").as_slice(),\n                \"storage_test_1.value\",\n                include_bytes!(\"../../tests/storage_test_1.values\").as_slice(),\n            ),\n            (\n                \"com.android.aconfig.storage.test_2\",\n                \"storage_test_2.aconfig\",\n                include_bytes!(\"../../tests/storage_test_2.aconfig\").as_slice(),\n                \"storage_test_2.value\",\n                include_bytes!(\"../../tests/storage_test_2.values\").as_slice(),\n            ),\n            (\n                \"com.android.aconfig.storage.test_4\",\n                \"storage_test_4.aconfig\",\n                include_bytes!(\"../../tests/storage_test_4.aconfig\").as_slice(),\n                \"storage_test_4.value\",\n                include_bytes!(\"../../tests/storage_test_4.values\").as_slice(),\n            ),\n        ];\n        aconfig_files\n            .into_iter()\n            .map(|(pkg, aconfig_file, aconfig_content, value_file, value_content)| {\n                let bytes = crate::commands::parse_flags(\n                    pkg,\n                    Some(\"system\"),\n                    vec![Input {\n                        source: format!(\"tests/{}\", aconfig_file).to_string(),\n                        reader: Box::new(aconfig_content),\n                    }],\n                    vec![Input {\n                        source: format!(\"tests/{}\", value_file).to_string(),\n                        reader: Box::new(value_content),\n                    }],\n                    crate::commands::DEFAULT_FLAG_PERMISSION,\n                    true,\n                )\n                .unwrap();\n                aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()\n            })\n            .collect()\n    }\n\n    #[test]\n    fn test_flag_package() {\n        let caches = parse_all_test_flags();\n        let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION);\n\n        for pkg in packages.iter() {\n            let pkg_name = pkg.package_name;\n            assert_eq!(pkg.flag_names.len(), pkg.boolean_flags.len());\n            for pf in pkg.boolean_flags.iter() {\n                assert!(pkg.flag_names.contains(pf.name()));\n                assert_eq!(pf.package(), pkg_name);\n            }\n        }\n\n        assert_eq!(packages.len(), 3);\n\n        assert_eq!(packages[0].package_name, \"com.android.aconfig.storage.test_1\");\n        assert_eq!(packages[0].package_id, 0);\n        assert_eq!(packages[0].flag_names.len(), 3);\n        assert!(packages[0].flag_names.contains(\"enabled_rw\"));\n        assert!(packages[0].flag_names.contains(\"disabled_rw\"));\n        assert!(packages[0].flag_names.contains(\"enabled_ro\"));\n        assert_eq!(packages[0].boolean_start_index, 0);\n        assert_eq!(packages[0].fingerprint, 0);\n\n        assert_eq!(packages[1].package_name, \"com.android.aconfig.storage.test_2\");\n        assert_eq!(packages[1].package_id, 1);\n        assert_eq!(packages[1].flag_names.len(), 3);\n        assert!(packages[1].flag_names.contains(\"enabled_ro\"));\n        assert!(packages[1].flag_names.contains(\"disabled_rw\"));\n        assert!(packages[1].flag_names.contains(\"enabled_fixed_ro\"));\n        assert_eq!(packages[1].boolean_start_index, 3);\n        assert_eq!(packages[0].fingerprint, 0);\n\n        assert_eq!(packages[2].package_name, \"com.android.aconfig.storage.test_4\");\n        assert_eq!(packages[2].package_id, 2);\n        assert_eq!(packages[2].flag_names.len(), 2);\n        assert!(packages[2].flag_names.contains(\"enabled_rw\"));\n        assert!(packages[2].flag_names.contains(\"enabled_fixed_ro\"));\n        assert_eq!(packages[2].boolean_start_index, 6);\n        assert_eq!(packages[2].fingerprint, 0);\n    }\n\n    #[test]\n    fn test_flag_package_with_fingerprint() {\n        let caches = parse_all_test_flags();\n        let packages = group_flags_by_package(caches.iter(), 2);\n\n        for pkg in packages.iter() {\n            let pkg_name = pkg.package_name;\n            assert_eq!(pkg.flag_names.len(), pkg.boolean_flags.len());\n            for pf in pkg.boolean_flags.iter() {\n                assert!(pkg.flag_names.contains(pf.name()));\n                assert_eq!(pf.package(), pkg_name);\n            }\n        }\n\n        assert_eq!(packages.len(), 3);\n\n        assert_eq!(packages[0].package_name, \"com.android.aconfig.storage.test_1\");\n        assert_eq!(packages[0].package_id, 0);\n        assert_eq!(packages[0].flag_names.len(), 3);\n        assert!(packages[0].flag_names.contains(\"enabled_rw\"));\n        assert!(packages[0].flag_names.contains(\"disabled_rw\"));\n        assert!(packages[0].flag_names.contains(\"enabled_ro\"));\n        assert_eq!(packages[0].boolean_start_index, 0);\n        assert_eq!(packages[0].fingerprint, 15248948510590158086u64);\n\n        assert_eq!(packages[1].package_name, \"com.android.aconfig.storage.test_2\");\n        assert_eq!(packages[1].package_id, 1);\n        assert_eq!(packages[1].flag_names.len(), 3);\n        assert!(packages[1].flag_names.contains(\"enabled_ro\"));\n        assert!(packages[1].flag_names.contains(\"disabled_rw\"));\n        assert!(packages[1].flag_names.contains(\"enabled_fixed_ro\"));\n        assert_eq!(packages[1].boolean_start_index, 3);\n        assert_eq!(packages[1].fingerprint, 4431940502274857964u64);\n\n        assert_eq!(packages[2].package_name, \"com.android.aconfig.storage.test_4\");\n        assert_eq!(packages[2].package_id, 2);\n        assert_eq!(packages[2].flag_names.len(), 2);\n        assert!(packages[2].flag_names.contains(\"enabled_rw\"));\n        assert!(packages[2].flag_names.contains(\"enabled_fixed_ro\"));\n        assert_eq!(packages[2].boolean_start_index, 6);\n        assert_eq!(packages[2].fingerprint, 16233229917711622375u64);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/storage/package_table.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse anyhow::Result;\n\nuse aconfig_storage_file::{\n    get_table_size, PackageTable, PackageTableHeader, PackageTableNode, StorageFileType,\n};\n\nuse crate::storage::FlagPackage;\n\nfn new_header(container: &str, num_packages: u32, version: u32) -> PackageTableHeader {\n    PackageTableHeader {\n        version,\n        container: String::from(container),\n        file_type: StorageFileType::PackageMap as u8,\n        file_size: 0,\n        num_packages,\n        bucket_offset: 0,\n        node_offset: 0,\n    }\n}\n\n// a struct that contains PackageTableNode and a bunch of other information to help\n// package table creation\n#[derive(PartialEq, Debug)]\nstruct PackageTableNodeWrapper {\n    pub node: PackageTableNode,\n    pub bucket_index: u32,\n}\n\nimpl PackageTableNodeWrapper {\n    fn new(package: &FlagPackage, num_buckets: u32) -> Self {\n        let node = PackageTableNode {\n            package_name: String::from(package.package_name),\n            package_id: package.package_id,\n            fingerprint: package.fingerprint,\n            boolean_start_index: package.boolean_start_index,\n            next_offset: None,\n        };\n        let bucket_index = PackageTableNode::find_bucket_index(package.package_name, num_buckets);\n        Self { node, bucket_index }\n    }\n}\n\npub fn create_package_table(\n    container: &str,\n    packages: &[FlagPackage],\n    version: u32,\n) -> Result<PackageTable> {\n    // create table\n    let num_packages = packages.len() as u32;\n    let num_buckets = get_table_size(num_packages)?;\n    let mut header = new_header(container, num_packages, version);\n    let mut buckets = vec![None; num_buckets as usize];\n    let mut node_wrappers: Vec<_> = packages\n        .iter()\n        .map(|pkg: &FlagPackage<'_>| PackageTableNodeWrapper::new(pkg, num_buckets))\n        .collect();\n\n    // initialize all header fields\n    header.bucket_offset = header.into_bytes().len() as u32;\n    header.node_offset = header.bucket_offset + num_buckets * 4;\n    header.file_size = header.node_offset\n        + node_wrappers.iter().map(|x| x.node.into_bytes(version).len()).sum::<usize>() as u32;\n\n    // sort node_wrappers by bucket index for efficiency\n    node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));\n\n    // fill all node offset\n    let mut offset = header.node_offset;\n    for i in 0..node_wrappers.len() {\n        let node_bucket_idx = node_wrappers[i].bucket_index;\n        let next_node_bucket_idx = if i + 1 < node_wrappers.len() {\n            Some(node_wrappers[i + 1].bucket_index)\n        } else {\n            None\n        };\n\n        if buckets[node_bucket_idx as usize].is_none() {\n            buckets[node_bucket_idx as usize] = Some(offset);\n        }\n        offset += node_wrappers[i].node.into_bytes(version).len() as u32;\n\n        if let Some(index) = next_node_bucket_idx {\n            if index == node_bucket_idx {\n                node_wrappers[i].node.next_offset = Some(offset);\n            }\n        }\n    }\n\n    let table = PackageTable {\n        header,\n        buckets,\n        nodes: node_wrappers.into_iter().map(|nw| nw.node).collect(),\n    };\n    Ok(table)\n}\n\n#[cfg(test)]\nmod tests {\n    use aconfig_storage_file::{DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION};\n\n    use super::*;\n    use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};\n\n    pub fn create_test_package_table_from_source(version: u32) -> Result<PackageTable> {\n        let caches = parse_all_test_flags();\n        let packages = group_flags_by_package(caches.iter(), version);\n        create_package_table(\"mockup\", &packages, version)\n    }\n\n    #[test]\n    // this test point locks down the table creation and each field\n    fn test_table_contents_default_version() {\n        let package_table_result = create_test_package_table_from_source(DEFAULT_FILE_VERSION);\n        assert!(package_table_result.is_ok());\n        let package_table = package_table_result.unwrap();\n\n        let expected_package_table =\n            aconfig_storage_file::test_utils::create_test_package_table(DEFAULT_FILE_VERSION);\n\n        assert_eq!(package_table.header, expected_package_table.header);\n        assert_eq!(package_table.buckets, expected_package_table.buckets);\n        for (node, expected_node) in\n            package_table.nodes.iter().zip(expected_package_table.nodes.iter())\n        {\n            assert_eq!(node.package_name, expected_node.package_name);\n            assert_eq!(node.package_id, expected_node.package_id);\n            assert_eq!(node.boolean_start_index, expected_node.boolean_start_index);\n            assert_eq!(node.next_offset, expected_node.next_offset);\n        }\n    }\n\n    #[test]\n    // this test point locks down the table creation and each field\n    fn test_table_contents_max_version() {\n        let package_table_result =\n            create_test_package_table_from_source(MAX_SUPPORTED_FILE_VERSION);\n        assert!(package_table_result.is_ok());\n        let package_table = package_table_result.unwrap();\n\n        let expected_package_table =\n            aconfig_storage_file::test_utils::create_test_package_table(MAX_SUPPORTED_FILE_VERSION);\n\n        assert_eq!(package_table.header, expected_package_table.header);\n        assert_eq!(package_table.buckets, expected_package_table.buckets);\n        for (node, expected_node) in\n            package_table.nodes.iter().zip(expected_package_table.nodes.iter())\n        {\n            assert_eq!(node.package_name, expected_node.package_name);\n            assert_eq!(node.package_id, expected_node.package_id);\n            assert_eq!(node.boolean_start_index, expected_node.boolean_start_index);\n            assert_eq!(node.next_offset, expected_node.next_offset);\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/src/test.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#[cfg(test)]\npub use test_utils::*;\n\n#[cfg(test)]\npub mod test_utils {\n    use crate::commands::Input;\n    use aconfig_protos::ProtoParsedFlags;\n    use itertools;\n\n    pub const TEST_PACKAGE: &str = \"com.android.aconfig.test\";\n\n    pub const TEST_FLAGS_TEXTPROTO: &str = r#\"\nparsed_flag {\n  package: \"com.android.aconfig.test\"\n  name: \"disabled_ro\"\n  namespace: \"aconfig_test\"\n  description: \"This flag is DISABLED + READ_ONLY\"\n  bug: \"123\"\n  state: DISABLED\n  permission: READ_ONLY\n  trace {\n    source: \"tests/test.aconfig\"\n    state: DISABLED\n    permission: READ_WRITE\n  }\n  trace {\n    source: \"tests/first.values\"\n    state: DISABLED\n    permission: READ_ONLY\n  }\n  is_fixed_read_only: false\n  is_exported: false\n  container: \"system\"\n  metadata {\n    purpose: PURPOSE_UNSPECIFIED\n  }\n}\nparsed_flag {\n  package: \"com.android.aconfig.test\"\n  name: \"disabled_rw\"\n  namespace: \"aconfig_test\"\n  description: \"This flag is DISABLED + READ_WRITE\"\n  bug: \"456\"\n  state: DISABLED\n  permission: READ_WRITE\n  trace {\n    source: \"tests/test.aconfig\"\n    state: DISABLED\n    permission: READ_WRITE\n  }\n  is_fixed_read_only: false\n  is_exported: false\n  container: \"system\"\n  metadata {\n    purpose: PURPOSE_UNSPECIFIED\n  }\n}\nparsed_flag {\n  package: \"com.android.aconfig.test\"\n  name: \"disabled_rw_exported\"\n  namespace: \"aconfig_test\"\n  description: \"This flag is DISABLED + READ_WRITE and exported\"\n  bug: \"111\"\n  state: DISABLED\n  permission: READ_WRITE\n  trace {\n    source: \"tests/test.aconfig\"\n    state: DISABLED\n    permission: READ_WRITE\n  }\n  trace {\n    source: \"tests/first.values\"\n    state: DISABLED\n    permission: READ_WRITE\n  }\n  is_fixed_read_only: false\n  is_exported: true\n  container: \"system\"\n  metadata {\n    purpose: PURPOSE_UNSPECIFIED\n  }\n}\nparsed_flag {\n  package: \"com.android.aconfig.test\"\n  name: \"disabled_rw_in_other_namespace\"\n  namespace: \"other_namespace\"\n  description: \"This flag is DISABLED + READ_WRITE, and is defined in another namespace\"\n  bug: \"999\"\n  state: DISABLED\n  permission: READ_WRITE\n  trace {\n    source: \"tests/test.aconfig\"\n    state: DISABLED\n    permission: READ_WRITE\n  }\n  trace {\n    source: \"tests/first.values\"\n    state: DISABLED\n    permission: READ_WRITE\n  }\n  is_fixed_read_only: false\n  is_exported: false\n  container: \"system\"\n  metadata {\n    purpose: PURPOSE_UNSPECIFIED\n  }\n}\nparsed_flag {\n  package: \"com.android.aconfig.test\"\n  name: \"enabled_fixed_ro\"\n  namespace: \"aconfig_test\"\n  description: \"This flag is fixed READ_ONLY + ENABLED\"\n  bug: \"\"\n  state: ENABLED\n  permission: READ_ONLY\n  trace {\n    source: \"tests/test.aconfig\"\n    state: DISABLED\n    permission: READ_ONLY\n  }\n  trace {\n    source: \"tests/first.values\"\n    state: ENABLED\n    permission: READ_ONLY\n  }\n  is_fixed_read_only: true\n  is_exported: false\n  container: \"system\"\n  metadata {\n    purpose: PURPOSE_UNSPECIFIED\n  }\n}\nparsed_flag {\n  package: \"com.android.aconfig.test\"\n  name: \"enabled_fixed_ro_exported\"\n  namespace: \"aconfig_test\"\n  description: \"This flag is fixed ENABLED + READ_ONLY and exported\"\n  bug: \"111\"\n  state: ENABLED\n  permission: READ_ONLY\n  trace {\n    source: \"tests/test.aconfig\"\n    state: DISABLED\n    permission: READ_ONLY\n  }\n  trace {\n    source: \"tests/first.values\"\n    state: ENABLED\n    permission: READ_ONLY\n  }\n  is_fixed_read_only: true\n  is_exported: true\n  container: \"system\"\n  metadata {\n    purpose: PURPOSE_UNSPECIFIED\n  }\n}\nparsed_flag {\n  package: \"com.android.aconfig.test\"\n  name: \"enabled_ro\"\n  namespace: \"aconfig_test\"\n  description: \"This flag is ENABLED + READ_ONLY\"\n  bug: \"abc\"\n  state: ENABLED\n  permission: READ_ONLY\n  trace {\n    source: \"tests/test.aconfig\"\n    state: DISABLED\n    permission: READ_WRITE\n  }\n  trace {\n    source: \"tests/first.values\"\n    state: DISABLED\n    permission: READ_WRITE\n  }\n  trace {\n    source: \"tests/second.values\"\n    state: ENABLED\n    permission: READ_ONLY\n  }\n  is_fixed_read_only: false\n  is_exported: false\n  container: \"system\"\n  metadata {\n    purpose: PURPOSE_BUGFIX\n  }\n}\nparsed_flag {\n  package: \"com.android.aconfig.test\"\n  name: \"enabled_ro_exported\"\n  namespace: \"aconfig_test\"\n  description: \"This flag is ENABLED + READ_ONLY and exported\"\n  bug: \"111\"\n  state: ENABLED\n  permission: READ_ONLY\n  trace {\n    source: \"tests/test.aconfig\"\n    state: DISABLED\n    permission: READ_WRITE\n  }\n  trace {\n    source: \"tests/first.values\"\n    state: ENABLED\n    permission: READ_ONLY\n  }\n  is_fixed_read_only: false\n  is_exported: true\n  container: \"system\"\n  metadata {\n    purpose: PURPOSE_UNSPECIFIED\n  }\n}\nparsed_flag {\n  package: \"com.android.aconfig.test\"\n  name: \"enabled_rw\"\n  namespace: \"aconfig_test\"\n  description: \"This flag is ENABLED + READ_WRITE\"\n  bug: \"\"\n  state: ENABLED\n  permission: READ_WRITE\n  trace {\n    source: \"tests/test.aconfig\"\n    state: DISABLED\n    permission: READ_WRITE\n  }\n  trace {\n    source: \"tests/first.values\"\n    state: ENABLED\n    permission: READ_WRITE\n  }\n  is_fixed_read_only: false\n  is_exported: false\n  container: \"system\"\n  metadata {\n    purpose: PURPOSE_UNSPECIFIED\n  }\n}\n\"#;\n\n    pub fn parse_read_only_test_flags() -> ProtoParsedFlags {\n        let bytes = crate::commands::parse_flags(\n            \"com.android.aconfig.test\",\n            Some(\"system\"),\n            vec![Input {\n                source: \"tests/read_only_test.aconfig\".to_string(),\n                reader: Box::new(include_bytes!(\"../tests/read_only_test.aconfig\").as_slice()),\n            }],\n            vec![Input {\n                source: \"tests/read_only_test.values\".to_string(),\n                reader: Box::new(include_bytes!(\"../tests/read_only_test.values\").as_slice()),\n            }],\n            crate::commands::DEFAULT_FLAG_PERMISSION,\n            true,\n        )\n        .unwrap();\n        aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()\n    }\n\n    pub fn parse_test_flags() -> ProtoParsedFlags {\n        let bytes = crate::commands::parse_flags(\n            \"com.android.aconfig.test\",\n            Some(\"system\"),\n            vec![Input {\n                source: \"tests/test.aconfig\".to_string(),\n                reader: Box::new(include_bytes!(\"../tests/test.aconfig\").as_slice()),\n            }],\n            vec![\n                Input {\n                    source: \"tests/first.values\".to_string(),\n                    reader: Box::new(include_bytes!(\"../tests/first.values\").as_slice()),\n                },\n                Input {\n                    source: \"tests/second.values\".to_string(),\n                    reader: Box::new(include_bytes!(\"../tests/second.values\").as_slice()),\n                },\n            ],\n            crate::commands::DEFAULT_FLAG_PERMISSION,\n            true,\n        )\n        .unwrap();\n        aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()\n    }\n\n    pub fn parse_second_package_flags() -> ProtoParsedFlags {\n        let bytes = crate::commands::parse_flags(\n            \"com.android.aconfig.second_test\",\n            Some(\"system\"),\n            vec![Input {\n                source: \"tests/test_second_package.aconfig\".to_string(),\n                reader: Box::new(include_bytes!(\"../tests/test_second_package.aconfig\").as_slice()),\n            }],\n            vec![Input {\n                source: \"tests/third.values\".to_string(),\n                reader: Box::new(include_bytes!(\"../tests/third.values\").as_slice()),\n            }],\n            crate::commands::DEFAULT_FLAG_PERMISSION,\n            true,\n        )\n        .unwrap();\n        aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()\n    }\n\n    pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> {\n        let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());\n        let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());\n        match itertools::diff_with(a, b, |left, right| left == right) {\n            Some(itertools::Diff::FirstMismatch(_, mut left, mut right)) => {\n                Some(format!(\"'{}' vs '{}'\", left.next().unwrap(), right.next().unwrap()))\n            }\n            Some(itertools::Diff::Shorter(_, mut left)) => {\n                Some(format!(\"LHS trailing data: '{}'\", left.next().unwrap()))\n            }\n            Some(itertools::Diff::Longer(_, mut right)) => {\n                Some(format!(\"RHS trailing data: '{}'\", right.next().unwrap()))\n            }\n            None => None,\n        }\n    }\n\n    /// Asserts that the two strings are equivalent. For use in tests. Fails\n    /// with formatted error message for easier debugging.\n    pub fn assert_no_significant_code_diff(expected: &str, actual: &str) {\n        let expected =\n            expected.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());\n        let actual = actual.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());\n        let fail_message: Option<String> =\n            match itertools::diff_with(expected, actual, |left, right| left == right) {\n                Some(itertools::Diff::FirstMismatch(_, mut left, mut right)) => Some(format!(\n                    \"DOES NOT MATCH: 1) expected, 2) actual:\\n{}\\n{}\",\n                    left.next().unwrap(),\n                    right.next().unwrap()\n                )),\n                Some(itertools::Diff::Shorter(_, mut left)) => {\n                    Some(format!(\"LHS trailing data: '{}'\", left.next().unwrap()))\n                }\n                Some(itertools::Diff::Longer(_, mut right)) => {\n                    Some(format!(\"RHS trailing data: '{}'\", right.next().unwrap()))\n                }\n                None => None,\n            };\n        assert!(fail_message.is_none(), \"{}\", fail_message.unwrap());\n    }\n\n    #[test]\n    fn test_first_significant_code_diff() {\n        assert!(first_significant_code_diff(\"\", \"\").is_none());\n        assert!(first_significant_code_diff(\"   a\", \"\\n\\na\\n\").is_none());\n        let a = r#\"\n        public class A {\n            private static final String FOO = \"FOO\";\n            public static void main(String[] args) {\n                System.out.println(\"FOO=\" + FOO);\n            }\n        }\n        \"#;\n        let b = r#\"\n        public class A {\n            private static final String FOO = \"BAR\";\n            public static void main(String[] args) {\n                System.out.println(\"foo=\" + FOO);\n            }\n        }\n        \"#;\n        assert_eq!(Some(r#\"'private static final String FOO = \"FOO\";' vs 'private static final String FOO = \"BAR\";'\"#.to_string()), first_significant_code_diff(a, b));\n        assert_eq!(\n            Some(\"LHS trailing data: 'b'\".to_string()),\n            first_significant_code_diff(\"a\\nb\", \"a\")\n        );\n        assert_eq!(\n            Some(\"RHS trailing data: 'b'\".to_string()),\n            first_significant_code_diff(\"a\", \"a\\nb\")\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template",
    "content": "package {package_name};\n\n{{ if not library_exported- }}\n// TODO(b/303773055): Remove the annotation after access issue is resolved.\nimport android.compat.annotation.UnsupportedAppUsage;\n{{ -endif }}\nimport java.util.Arrays;\n{{ -if library_exported }}\nimport java.util.HashMap;\nimport java.util.Map;\n{{ -endif }}\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.BiPredicate;\nimport java.util.function.Predicate;\n{{ -if library_exported }}\nimport android.os.Build;\n{{ -endif }}\n\n{{ -if single_exported_file }}\n{{ -if library_exported }}\n@Deprecated {#- PREFER ExportedFlags #}\n{{ -endif }}\n{{ -else }}\n/** @hide */\n{{ -endif }}\npublic class CustomFeatureFlags implements FeatureFlags \\{\n\n    private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;\n\n    public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) \\{\n        mGetValueImpl = getValueImpl;\n    }\n\n{{ -for item in flag_elements}}\n    @Override\n{{ if not library_exported }}    @UnsupportedAppUsage{{ -endif }}\n    public boolean {item.method_name}() \\{\n        return getValue(Flags.FLAG_{item.flag_name_constant_suffix},\n            FeatureFlags::{item.method_name});\n    }\n{{ endfor }}\n\n{{ -if not library_exported }}\n    public boolean isFlagReadOnlyOptimized(String flagName) \\{\n        if (mReadOnlyFlagsSet.contains(flagName) &&\n            isOptimizationEnabled()) \\{\n                return true;\n        }\n        return false;\n    }\n\n    @com.android.aconfig.annotations.AssumeTrueForR8\n    private boolean isOptimizationEnabled() \\{\n        return false;\n    }\n{{ -endif }}\n\n    protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \\{\n        return mGetValueImpl.test(flagName, getter);\n    }\n\n    public List<String> getFlagNames() \\{\n        return Arrays.asList(\n            {{ -for item in flag_elements }}\n            Flags.FLAG_{item.flag_name_constant_suffix}\n            {{ -if not @last }},{{ endif }}\n            {{ -endfor }}\n        );\n    }\n\n    private Set<String> mReadOnlyFlagsSet = new HashSet<>(\n        Arrays.asList(\n            {{ -for item in flag_elements }}\n            {{ -if not item.is_read_write }}\n            Flags.FLAG_{item.flag_name_constant_suffix},\n            {{ -endif }}\n            {{ -endfor }}\n            \"\"{# The empty string here is to resolve the ending comma #}\n        )\n    );\n\n{{ -if library_exported }}\n    private Map<String, Integer> mFinalizedFlags = new HashMap<>(\n        Map.ofEntries(\n            {{ -for item in flag_elements }}\n            {{ -if item.finalized_sdk_present }}\n            Map.entry(Flags.FLAG_{item.flag_name_constant_suffix}, {item.finalized_sdk_value}),\n            {{ -endif }}\n            {{ -endfor }}\n            Map.entry(\"\", Integer.MAX_VALUE){# The empty entry to avoid empty entries #}\n        )\n    );\n\n    public boolean isFlagFinalized(String flagName) \\{\n        if (!mFinalizedFlags.containsKey(flagName)) \\{\n            return false;\n        }\n        return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName);\n    }\n{{ -endif }}\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/ExportedFlags.java.template",
    "content": "package {package_name}; {#- CODEGEN FOR EXPORTED MODE FOR NEW STORAGE SINGLE EXPORTED FILE#}\n\nimport android.os.Build;\nimport android.os.flagging.AconfigPackage;\nimport android.util.Log;\npublic final class ExportedFlags \\{\n{{ -for item in flag_elements}}\n    public static final String FLAG_{item.flag_name_constant_suffix} = \"{item.device_config_flag}\";\n{{- endfor }}\n    private static final String TAG = \"ExportedFlags\";\n    private static volatile boolean isCached = false;\n{{ for flag in flag_elements }}\n    private static boolean {flag.method_name} = false;\n{{ -endfor }} {#- end flag_elements #}\n    private ExportedFlags() \\{}\n\n    private void init() \\{\n        try \\{\n            AconfigPackage reader = AconfigPackage.load(\"{package_name}\");\n            {{ -for namespace_with_flags in namespace_flags }}\n            {{ -for flag in namespace_with_flags.flags }}\n            {flag.method_name} = reader.getBooleanFlagValue(\"{flag.flag_name}\", {flag.default_value});\n\n            {{ -endfor }} {#- end namespace_with_flags.flags #}\n            {{ -endfor }} {#- end namespace_flags #}\n        } catch (Exception e) \\{\n            // pass\n            Log.e(TAG, e.toString());\n        } catch (LinkageError e) \\{\n            // for mainline module running on older devices.\n            // This should be replaces to version check, after the version bump.\n            Log.w(TAG, e.toString());\n        }\n        isCached = true;\n    }\n\n{{ -for flag in flag_elements }}\n    public static boolean {flag.method_name}() \\{\n        {{ -if flag.finalized_sdk_present }}\n        if (Build.VERSION.SDK_INT >= {flag.finalized_sdk_value}) \\{\n          return true;\n        }\n        {{ -endif}}  {#- end finalized_sdk_present#}\n        if (!featureFlags.isCached) \\{\n            featureFlags.init();\n        }\n        return featureFlags.{flag.method_name};\n    }\n{{ -endfor }}\n    private static ExportedFlags featureFlags = new ExportedFlags();\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template",
    "content": "package {package_name};\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\n{{ -if single_exported_file }}\n{{ -if library_exported }}\n@Deprecated {#- PREFER ExportedFlags #}\n{{ -endif }}\n{{ -else }}\n/** @hide */\n{{ -endif }}\npublic class FakeFeatureFlagsImpl extends CustomFeatureFlags \\{\n    private final Map<String, Boolean> mFlagMap = new HashMap<>();\n    private final FeatureFlags mDefaults;\n\n    public FakeFeatureFlagsImpl() \\{\n        this(null);\n    }\n\n    public FakeFeatureFlagsImpl(FeatureFlags defaults) \\{\n        super(null);\n        mDefaults = defaults;\n        // Initialize the map with null values\n        for (String flagName : getFlagNames()) \\{\n            mFlagMap.put(flagName, null);\n        }\n    }\n\n    @Override\n    protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \\{\n        Boolean value = this.mFlagMap.get(flagName);\n        if (value != null) \\{\n            return value;\n        }\n        if (mDefaults != null) \\{\n            return getter.test(mDefaults);\n        }\n        throw new IllegalArgumentException(flagName + \" is not set\");\n    }\n\n    public void setFlag(String flagName, boolean value) \\{\n        if (!this.mFlagMap.containsKey(flagName)) \\{\n            throw new IllegalArgumentException(\"no such flag \" + flagName);\n        }\n        this.mFlagMap.put(flagName, value);\n    }\n\n    public void resetAll() \\{\n        for (Map.Entry entry : mFlagMap.entrySet()) \\{\n            entry.setValue(null);\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/FeatureFlags.java.template",
    "content": "package {package_name};\n{{ if not library_exported- }}\n// TODO(b/303773055): Remove the annotation after access issue is resolved.\nimport android.compat.annotation.UnsupportedAppUsage;\n{{ -endif }}\n{{ -if single_exported_file }}\n{{ -if library_exported }}\n/**\n * @deprecated Use \\{@link ExportedFlags} instead.\n */\n@Deprecated {#- PREFER ExportedFlags #}\n{{ -endif }}\n{{ -else }}\n/** @hide */\n{{ -endif }}\npublic interface FeatureFlags \\{\n{{ for item in flag_elements }}\n{{ -if not item.is_read_write }}\n{{ -if item.default_value }}\n    @com.android.aconfig.annotations.AssumeTrueForR8\n{{ -else }}\n    @com.android.aconfig.annotations.AssumeFalseForR8\n{{ -endif- }}\n{{ -endif }}\n{{ -if not library_exported }}\n    @com.android.aconfig.annotations.AconfigFlagAccessor\n    @UnsupportedAppUsage\n{{ -endif }}\n    boolean {item.method_name}();\n{{ -endfor }}\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/FeatureFlagsImpl.deviceConfig.java.template",
    "content": "package {package_name};\n{{ if not library_exported- }}\n// TODO(b/303773055): Remove the annotation after access issue is resolved.\nimport android.compat.annotation.UnsupportedAppUsage;\n{{ -endif }} {#- end of not library_exported#}\n{{ -if runtime_lookup_required }}\nimport android.os.Binder;\nimport android.provider.DeviceConfig;\nimport android.provider.DeviceConfig.Properties;\n{{ -endif }}  {#- end of runtime_lookup_required#}\n/** @hide */\npublic final class FeatureFlagsImpl implements FeatureFlags \\{\n{{ -if runtime_lookup_required }}\n{{ -for namespace_with_flags in namespace_flags }}\n    private static volatile boolean {namespace_with_flags.namespace}_is_cached = false;\n{{ -endfor- }}\n{{ for flag in flag_elements }}\n{{- if flag.is_read_write }}\n    private static boolean {flag.method_name} = {flag.default_value};\n{{ -endif }} {#- end of is_read_write#}\n{{ -endfor }}\n{{ for namespace_with_flags in namespace_flags }}\n    private void load_overrides_{namespace_with_flags.namespace}() \\{\n        final long ident = Binder.clearCallingIdentity();\n        try \\{\n            Properties properties = DeviceConfig.getProperties(\"{namespace_with_flags.namespace}\");\n{{ -for flag in namespace_with_flags.flags }}\n{{ -if flag.is_read_write }}\n            {flag.method_name} =\n                properties.getBoolean(Flags.FLAG_{flag.flag_name_constant_suffix}, {flag.default_value});\n{{ -endif }} {#- end of is_read_write#}\n{{ -endfor }}\n        } catch (NullPointerException e) \\{\n            throw new RuntimeException(\n                \"Cannot read value from namespace {namespace_with_flags.namespace} \"\n                + \"from DeviceConfig. It could be that the code using flag \"\n                + \"executed before SettingsProvider initialization. Please use \"\n                + \"fixed read-only flag by adding is_fixed_read_only: true in \"\n                + \"flag declaration.\",\n                e\n            );\n        } catch (SecurityException e) \\{\n            // for isolated process case, skip loading flag value from the storage, use the default\n        } finally \\{\n            Binder.restoreCallingIdentity(ident);\n        }\n        {namespace_with_flags.namespace}_is_cached = true;\n}\n{{ endfor- }}\n{{ -endif }}{#- end of runtime_lookup_required #}\n{{ -for flag in flag_elements }}\n    @Override\n{{ -if not library_exported }}\n    @com.android.aconfig.annotations.AconfigFlagAccessor\n    @UnsupportedAppUsage\n{{ -endif }}{#- end of not library_exported #}\n    public boolean {flag.method_name}() \\{\n{{ -if flag.is_read_write }}\n        if (!{flag.device_config_namespace}_is_cached) \\{\n            load_overrides_{flag.device_config_namespace}();\n        }\n        return {flag.method_name};\n{{ -else }} {#- else is_read_write #}\n        return {flag.default_value};\n{{ -endif }}{#- end of is_read_write #}\n    }\n{{ endfor }}\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/FeatureFlagsImpl.exported.java.template",
    "content": "package {package_name}; {#- CODEGEN FOR EXPORTED MODE FOR NEW STORAGE #}\n\nimport android.os.Build;\nimport android.os.flagging.AconfigPackage;\nimport android.util.Log;\n{{ -if single_exported_file }}\n{{ -if library_exported }}\n/**\n * @deprecated Use \\{@link ExportedFlags} instead.\n */\n@Deprecated {#- PREFER ExportedFlags #}\n{{ -endif }}\n{{ -else }}\n/** @hide */\n{{ -endif }}\npublic final class FeatureFlagsImpl implements FeatureFlags \\{\n    private static final String TAG = \"FeatureFlagsImplExport\";\n    private static volatile boolean isCached = false;\n{{ for flag in flag_elements }}\n    private static boolean {flag.method_name} = false;\n{{ -endfor }} {#- end flag_elements #}\n    private void init() \\{\n        try \\{\n            AconfigPackage reader = AconfigPackage.load(\"{package_name}\");\n            {{ -for namespace_with_flags in namespace_flags }}\n            {{ -for flag in namespace_with_flags.flags }}\n            {{ -if flag.finalized_sdk_present }}\n            {flag.method_name} = Build.VERSION.SDK_INT >= {flag.finalized_sdk_value} ? true : reader.getBooleanFlagValue(\"{flag.flag_name}\", {flag.default_value});\n            {{ - else }} {#- else finalized_sdk_present #}\n            {flag.method_name} = reader.getBooleanFlagValue(\"{flag.flag_name}\", {flag.default_value});\n            {{ -endif}}  {#- end finalized_sdk_present#}\n            {{ -endfor }} {#- end namespace_with_flags.flags #}\n            {{ -endfor }} {#- end namespace_flags #}\n        } catch (Exception e) \\{\n            // pass\n            Log.e(TAG, e.toString());\n        } catch (LinkageError e) \\{\n            // for mainline module running on older devices.\n            // This should be replaces to version check, after the version bump.\n            Log.w(TAG, e.toString());\n        }\n        isCached = true;\n    }\n{{ -for flag in flag_elements }}\n    @Override\n    public boolean {flag.method_name}() \\{\n        if (!isCached) \\{\n            init();\n        }\n        return {flag.method_name};\n    }\n{{ endfor }} {#- end flag_elements #}\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/FeatureFlagsImpl.new_storage.java.template",
    "content": "package {package_name}; {#- CODEGEN FOR INTERNAL MODE FOR NEW STORAGE #}\n// TODO(b/303773055): Remove the annotation after access issue is resolved.\nimport android.compat.annotation.UnsupportedAppUsage;\n{{ -if runtime_lookup_required }}\n{{ if is_platform_container }}\nimport android.os.flagging.PlatformAconfigPackageInternal;\n{{ -else }} {#- else is_platform_container #}\nimport android.os.flagging.AconfigPackageInternal;\n{{ -endif }} {#- end of is_platform_container#}\nimport android.util.Log;\n{{ -endif }} {#- end of runtime_lookup_required#}\n/** @hide */\npublic final class FeatureFlagsImpl implements FeatureFlags \\{\n{{ -if runtime_lookup_required }}\n    private static final String TAG = \"FeatureFlagsImpl\";\n    private static volatile boolean isCached = false;\n{{ for flag in flag_elements }}\n{{ -if flag.is_read_write }}\n    private static boolean {flag.method_name} = {flag.default_value};\n{{ -endif }} {#- end of is_read_write#}\n{{ -endfor }} {#- else flag_elements #}\n\n    private void init() \\{\n        try \\{\n{{ if is_platform_container }}\n            PlatformAconfigPackageInternal reader = PlatformAconfigPackageInternal.load(\"{package_name}\", {package_fingerprint});\n{{ -else }} {#- else is_platform_container #}\n            AconfigPackageInternal reader = AconfigPackageInternal.load(\"{package_name}\", {package_fingerprint});\n{{ -endif }} {#- end of is_platform_container#}\n        {{ -for namespace_with_flags in namespace_flags }}\n        {{ -for flag in namespace_with_flags.flags }}\n        {{ -if flag.is_read_write }}\n            {flag.method_name} = reader.getBooleanFlagValue({flag.flag_offset});\n        {{ -endif }} {#- is_read_write#}\n        {{ -endfor }} {#- else namespace_with_flags.flags #}\n        {{ -endfor }}  {#- else namespace_flags #}\n        } catch (Exception e) \\{\n            Log.e(TAG, e.toString());\n        } catch (LinkageError e) \\{\n            // for mainline module running on older devices.\n            // This should be replaces to version check, after the version bump.\n            Log.e(TAG, e.toString());\n        }\n        isCached = true;\n    }\n{{ -endif }}{#- end of runtime_lookup_required #}\n{{ -for flag in flag_elements }}\n    @Override\n    @com.android.aconfig.annotations.AconfigFlagAccessor\n    @UnsupportedAppUsage\n    public boolean {flag.method_name}() \\{\n{{ -if flag.is_read_write }}\n        if (!isCached) \\{\n            init();\n        }\n        return {flag.method_name};\n{{ -else }}{#- else is_read_write #}\n        return {flag.default_value};\n{{ -endif }}  {#- end of is_read_write#}\n    }\n{{ endfor }} {#- else flag_elements #}\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/FeatureFlagsImpl.test_mode.java.template",
    "content": "package {package_name}; {#- CODEGEN FOR TEST MODE #}\n/** @hide */\npublic final class FeatureFlagsImpl implements FeatureFlags \\{\n{{ for flag in flag_elements }}\n    @Override\n{{ -if not library_exported }}\n    @com.android.aconfig.annotations.AconfigFlagAccessor\n{{ -endif }}\n    public boolean {flag.method_name}() \\{\n        throw new UnsupportedOperationException(\n            \"Method is not implemented.\");\n    }\n{{ endfor- }}\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/Flags.java.template",
    "content": "package {package_name};\n{{ if not library_exported- }}\n// TODO(b/303773055): Remove the annotation after access issue is resolved.\nimport android.compat.annotation.UnsupportedAppUsage;\n{{ else }}\nimport android.os.Build;\n{{ -endif }} {#- end not library_exported#}\n{{ -if single_exported_file }}\n{{ -if library_exported }}\n/**\n * @deprecated Use \\{@link ExportedFlags} instead.\n */\n@Deprecated {#- PREFER ExportedFlags #}\n{{ -endif }}\n{{ -else }}\n/** @hide */\n{{ -endif }}\npublic final class Flags \\{\n{{ -for item in flag_elements}}\n    /** @hide */\n    public static final String FLAG_{item.flag_name_constant_suffix} = \"{item.device_config_flag}\";\n{{- endfor }}\n{{ -for item in flag_elements}}\n{{ -if not item.is_read_write }}\n{{ -if item.default_value }}\n    @com.android.aconfig.annotations.AssumeTrueForR8\n{{ -else }}\n    @com.android.aconfig.annotations.AssumeFalseForR8\n{{ -endif }}\n{{ -endif }}\n{{ -if not library_exported }}\n    @com.android.aconfig.annotations.AconfigFlagAccessor\n    @UnsupportedAppUsage\n{{ -endif }}\n    public static boolean {item.method_name}() \\{\n        {{ if library_exported- }}\n        {{ -if item.finalized_sdk_present }}\n        if (Build.VERSION.SDK_INT >= {item.finalized_sdk_value}) \\{\n          return true;\n        }\n        {{ -endif}}  {#- end finalized_sdk_present#}\n        {{ -endif}}  {#- end library_exported#}\n        return FEATURE_FLAGS.{item.method_name}();\n    }\n{{ -endfor }}\n{{ -if is_test_mode }}\n    public static void setFeatureFlags(FeatureFlags featureFlags) \\{\n        Flags.FEATURE_FLAGS = featureFlags;\n    }\n\n    public static void unsetFeatureFlags() \\{\n        Flags.FEATURE_FLAGS = null;\n    }\n{{ -endif }}\n\n    private static FeatureFlags FEATURE_FLAGS{{ -if not is_test_mode }} = new FeatureFlagsImpl(){{ -endif- }};\n\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/cpp_exported_header.template",
    "content": "#pragma once\n\n{{ if not is_test_mode- }}\n{{ if has_fixed_read_only- }}\n#ifndef {package_macro}\n#define {package_macro}(FLAG) {package_macro}_##FLAG\n#endif\n{{ for item in class_elements }}\n{{ -if item.is_fixed_read_only }}\n#ifndef {package_macro}_{item.flag_macro}\n#define {package_macro}_{item.flag_macro} {item.default_value}\n#endif\n{{ -endif }}\n{{ -endfor }}\n{{ -endif }}\n{{ -endif }}\n\n#ifdef __cplusplus\n\n#include <memory>\n\nnamespace {cpp_namespace} \\{\n\nclass flag_provider_interface \\{\npublic:\n    virtual ~flag_provider_interface() = default;\n    {{ -for item in class_elements}}\n    virtual bool {item.flag_name}() = 0;\n\n    {{ -endfor }}\n\n    {{ -if is_test_mode }}\n    {{ -for item in class_elements}}\n    virtual void {item.flag_name}(bool val) = 0;\n    {{ -endfor }}\n\n    virtual void reset_flags() \\{}\n    {{ -endif }}\n};\n\nextern std::unique_ptr<flag_provider_interface> provider_;\n\n{{ for item in class_elements}}\n{{ if not is_test_mode }}{{ if item.is_fixed_read_only }}constexpr {{ endif }}{{ endif -}}\ninline bool {item.flag_name}() \\{\n    {{ -if is_test_mode }}\n    return provider_->{item.flag_name}();\n    {{ -else }}\n    {{ -if item.readwrite }}\n    return provider_->{item.flag_name}();\n    {{ -else }}\n    {{ -if item.is_fixed_read_only }}\n    return {package_macro}_{item.flag_macro};\n    {{ -else }}\n    return {item.default_value};\n    {{ -endif }}\n    {{ -endif }}\n    {{ -endif }}\n}\n\n{{ -if is_test_mode }}\ninline void {item.flag_name}(bool val) \\{\n    provider_->{item.flag_name}(val);\n}\n{{ -endif }}\n{{ -endfor }}\n\n{{ -if is_test_mode }}\ninline void reset_flags() \\{\n    return provider_->reset_flags();\n}\n{{ -endif }}\n\n}\n\nextern \"C\" \\{\n#endif // __cplusplus\n\n{{ for item in class_elements }}\nbool {header}_{item.flag_name}();\n\n{{ -if is_test_mode }}\nvoid set_{header}_{item.flag_name}(bool val);\n{{ -endif }}\n{{ -endfor }}\n\n{{ -if is_test_mode }}\nvoid {header}_reset_flags();\n{{ -endif }}\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/cpp_source_file.template",
    "content": "#include \"{header}.h\"\n\n{{ if readwrite- }}\n#include <unistd.h>\n#include \"aconfig_storage/aconfig_storage_read_api.hpp\"\n#include <android/log.h>\n#define LOG_TAG \"aconfig_cpp_codegen\"\n#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)\n{{ -endif }}\n\n{{ if is_test_mode }}\n#include <unordered_map>\n#include <string>\n{{ -else- }}\n{{ if readwrite- }}\n#include <vector>\n{{ -endif }}\n{{ -endif }}\n\nnamespace {cpp_namespace} \\{\n\n{{ if is_test_mode }}\n    class flag_provider : public flag_provider_interface \\{\n    private:\n        std::unordered_map<std::string, bool> overrides_;\n\n    {{ if readwrite- }}\n        uint32_t boolean_start_index_;\n\n        std::unique_ptr<aconfig_storage::MappedStorageFile> flag_value_file_;\n\n        bool package_exists_in_storage_;\n    {{ -endif }}\n\n    public:\n    {{ if readwrite- }}\n        flag_provider()\n            : overrides_()\n            , boolean_start_index_()\n            , flag_value_file_(nullptr)\n            , package_exists_in_storage_(true) \\{\n\n            auto package_map_file = aconfig_storage::get_mapped_file(\n                 \"{container}\",\n                 aconfig_storage::StorageFileType::package_map);\n\n            if (!package_map_file.ok()) \\{\n                ALOGE(\"error: failed to get package map file: %s\", package_map_file.error().c_str());\n                package_exists_in_storage_ = false;\n                return;\n            }\n\n            auto context = aconfig_storage::get_package_read_context(\n                **package_map_file, \"{package}\");\n\n            if (!context.ok()) \\{\n                ALOGE(\"error: failed to get package read context: %s\", context.error().c_str());\n                package_exists_in_storage_ = false;\n                return;\n            }\n\n            if (!(context->package_exists)) \\{\n                package_exists_in_storage_ = false;\n                return;\n            }\n\n            // cache package boolean flag start index\n            boolean_start_index_ = context->boolean_start_index;\n\n            // unmap package map file and free memory\n            delete *package_map_file;\n\n            auto flag_value_file = aconfig_storage::get_mapped_file(\n                \"{container}\",\n                aconfig_storage::StorageFileType::flag_val);\n            if (!flag_value_file.ok()) \\{\n                ALOGE(\"error: failed to get flag value file: %s\", flag_value_file.error().c_str());\n                package_exists_in_storage_ = false;\n                return;\n            }\n\n            // cache flag value file\n            flag_value_file_ = std::unique_ptr<aconfig_storage::MappedStorageFile>(\n                *flag_value_file);\n\n        }\n    {{ -else }}\n        flag_provider()\n            : overrides_()\n        \\{}\n    {{ -endif }}\n\n    {{ for item in class_elements }}\n        virtual bool {item.flag_name}() override \\{\n            auto it = overrides_.find(\"{item.flag_name}\");\n            if (it != overrides_.end()) \\{\n                return it->second;\n            } else \\{\n                {{ if item.readwrite- }}\n                if (!package_exists_in_storage_) \\{\n                    return {item.default_value};\n                }\n\n                auto value = aconfig_storage::get_boolean_flag_value(\n                    *flag_value_file_,\n                    boolean_start_index_ + {item.flag_offset});\n\n                if (!value.ok()) \\{\n                    ALOGE(\"error: failed to read flag value: %s\", value.error().c_str());\n                    return {item.default_value};\n                } else \\{\n                    return *value;\n                }\n                {{ -else }}\n                return {item.default_value};\n                {{ -endif }}\n            }\n        }\n\n        virtual void {item.flag_name}(bool val) override \\{\n            overrides_[\"{item.flag_name}\"] = val;\n        }\n    {{ endfor }}\n\n        virtual void reset_flags() override \\{\n            overrides_.clear();\n        }\n    };\n\n{{ -else- }}\n\n    class flag_provider : public flag_provider_interface \\{\n    public:\n\n        {{ if readwrite- }}\n        flag_provider()\n            : cache_({readwrite_count}, -1)\n            , boolean_start_index_()\n            , flag_value_file_(nullptr)\n            , package_exists_in_storage_(true) \\{\n\n            auto package_map_file = aconfig_storage::get_mapped_file(\n                 \"{container}\",\n                 aconfig_storage::StorageFileType::package_map);\n            if (!package_map_file.ok()) \\{\n                ALOGE(\"error: failed to get package map file: %s\", package_map_file.error().c_str());\n                package_exists_in_storage_ = false;\n                return;\n            }\n\n            auto context = aconfig_storage::get_package_read_context(\n                **package_map_file, \"{package}\");\n            if (!context.ok()) \\{\n                ALOGE(\"error: failed to get package read context: %s\", context.error().c_str());\n                package_exists_in_storage_ = false;\n                return;\n            }\n\n            if (!(context->package_exists)) \\{\n                package_exists_in_storage_ = false;\n                return;\n            }\n\n            // cache package boolean flag start index\n            boolean_start_index_ = context->boolean_start_index;\n\n            // unmap package map file and free memory\n            delete *package_map_file;\n\n            auto flag_value_file = aconfig_storage::get_mapped_file(\n                \"{container}\",\n                aconfig_storage::StorageFileType::flag_val);\n            if (!flag_value_file.ok()) \\{\n                ALOGE(\"error: failed to get flag value file: %s\", flag_value_file.error().c_str());\n                package_exists_in_storage_ = false;\n                return;\n            }\n\n            // cache flag value file\n            flag_value_file_ = std::unique_ptr<aconfig_storage::MappedStorageFile>(\n                *flag_value_file);\n\n        }\n        {{ -endif }}\n\n        {{ -for item in class_elements }}\n        virtual bool {item.flag_name}() override \\{\n            {{ -if item.readwrite }}\n            if (cache_[{item.readwrite_idx}] == -1) \\{\n                if (!package_exists_in_storage_) \\{\n                    return {item.default_value};\n                }\n\n                auto value = aconfig_storage::get_boolean_flag_value(\n                    *flag_value_file_,\n                    boolean_start_index_ + {item.flag_offset});\n\n                if (!value.ok()) \\{\n                    ALOGE(\"error: failed to read flag value: %s\", value.error().c_str());\n                    return {item.default_value};\n                }\n\n                cache_[{item.readwrite_idx}] = *value;\n            }\n            return cache_[{item.readwrite_idx}];\n            {{ -else }}\n            {{ -if item.is_fixed_read_only }}\n            return {package_macro}_{item.flag_macro};\n            {{ -else }}\n            return {item.default_value};\n            {{ -endif }}\n            {{ -endif }}\n        }\n        {{ -endfor }}\n\n    {{ if readwrite- }}\n    private:\n        std::vector<int8_t> cache_ = std::vector<int8_t>({readwrite_count}, -1);\n\n        uint32_t boolean_start_index_;\n\n        std::unique_ptr<aconfig_storage::MappedStorageFile> flag_value_file_;\n\n        bool package_exists_in_storage_;\n    {{ -endif }}\n\n    };\n\n{{ -endif }}\n\nstd::unique_ptr<flag_provider_interface> provider_ =\n    std::make_unique<flag_provider>();\n}\n\n{{ for item in class_elements }}\nbool {header}_{item.flag_name}() \\{\n    {{ -if is_test_mode }}\n    return {cpp_namespace}::{item.flag_name}();\n    {{ -else }}\n    {{ -if item.readwrite }}\n    return {cpp_namespace}::{item.flag_name}();\n    {{ -else }}\n    {{ -if item.is_fixed_read_only }}\n    return {package_macro}_{item.flag_macro};\n    {{ -else }}\n    return {item.default_value};\n    {{ -endif }}\n    {{ -endif }}\n    {{ -endif }}\n}\n\n{{ -if is_test_mode }}\nvoid set_{header}_{item.flag_name}(bool val) \\{\n    {cpp_namespace}::{item.flag_name}(val);\n}\n{{ -endif }}\n{{ endfor }}\n\n{{ -if is_test_mode }}\nvoid {header}_reset_flags() \\{\n     {cpp_namespace}::reset_flags();\n}\n{{ -endif }}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/rust.template",
    "content": "//! codegenerated rust flag lib\nuse aconfig_storage_read_api::\\{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};\nuse std::path::Path;\nuse std::io::Write;\nuse std::sync::LazyLock;\nuse log::\\{log, LevelFilter, Level};\n\n/// flag provider\npub struct FlagProvider;\n\n{{ if has_readwrite- }}\nstatic PACKAGE_CONTEXT: LazyLock<Result<Option<PackageReadContext>, AconfigStorageError>> = LazyLock::new(|| unsafe \\{\n    get_mapped_storage_file(\"{container}\", StorageFileType::PackageMap)\n    .and_then(|package_map| get_package_read_context(&package_map, \"{package}\"))\n});\n\nstatic FLAG_VAL_MAP: LazyLock<Result<Mmap, AconfigStorageError>> = LazyLock::new(|| unsafe \\{\n    get_mapped_storage_file(\"{container}\", StorageFileType::FlagVal)\n});\n{{ -for flag in template_flags }}\n\n{{ -if flag.readwrite }}\n/// flag value cache for {flag.name}\nstatic CACHED_{flag.name}: LazyLock<bool> = LazyLock::new(|| \\{\n\n    // This will be called multiple times. Subsequent calls after the first are noops.\n    logger::init(\n        logger::Config::default()\n            .with_tag_on_device(\"aconfig_rust_codegen\")\n            .with_max_level(LevelFilter::Info));\n\n    {{ -if use_package_fingerprint }}\n    let fingerprint_check_failed: bool = PACKAGE_CONTEXT\n        .as_ref()\n        .is_ok_and(|package_context| \\{\n              match package_context \\{\n                Some(context) => \\{\n                        return context.fingerprint != {package_fingerprint}\n                    },\n                    None => \\{\n                        log!(Level::Info, \"aconfig_rust_codegen: missing fingerprint; performing lookup.\");\n                        false\n                    }\n                  }\n                });\n\n    if fingerprint_check_failed \\{\n      log!(Level::Error, \"Fingerprint mismatch for package {package}; returning flag default ({flag.default_value}) for {flag.name}.\");\n      return {flag.default_value};\n    }\n    {{ -endif }}\n\n    let flag_value_result = FLAG_VAL_MAP\n        .as_ref()\n        .map_err(|err| format!(\"failed to get flag val map: \\{err}\"))\n        .and_then(|flag_val_map| \\{\n            PACKAGE_CONTEXT\n                .as_ref()\n                .map_err(|err| format!(\"failed to get package read context: \\{err}\"))\n                .and_then(|package_context| \\{\n                    match package_context \\{\n                        Some(context) => \\{\n                            get_boolean_flag_value(&flag_val_map, context.boolean_start_index + {flag.flag_offset})\n                                .map_err(|err| format!(\"failed to get flag: \\{err}\"))\n                        },\n                        None => \\{\n                            log!(Level::Error, \"no context found for package {package}\");\n                            Err(format!(\"failed to flag package {package}\"))\n                        }\n                    }\n                })\n            });\n\n    match flag_value_result \\{\n        Ok(flag_value) => \\{\n            return flag_value;\n        },\n        Err(err) => \\{\n            log!(Level::Error, \"aconfig_rust_codegen: error: \\{err}\");\n            return {flag.default_value};\n        }\n    }\n\n});\n{{ -endif }}\n{{ -endfor }}\n{{ -endif }}\n\nimpl FlagProvider \\{\n\n{{ for flag in template_flags }}\n    /// query flag {flag.name}\n    pub fn {flag.name}(&self) -> bool \\{\n        {{ -if flag.readwrite }}\n        *CACHED_{flag.name}\n        {{ -else }}\n        {flag.default_value}\n        {{ -endif }}\n    }\n{{ endfor }}\n\n}\n\n/// flag provider\npub static PROVIDER: FlagProvider = FlagProvider;\n\n{{ for flag in template_flags }}\n/// query flag {flag.name}\n#[inline(always)]\npub fn {flag.name}() -> bool \\{\n{{ -if flag.readwrite }}\n    PROVIDER.{flag.name}()\n{{ -else }}\n    {flag.default_value}\n{{ -endif }}\n}\n{{ endfor }}\n"
  },
  {
    "path": "tools/aconfig/aconfig/templates/rust_test.template",
    "content": "//! codegenerated rust flag lib\nuse aconfig_storage_read_api::\\{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};\nuse std::collections::BTreeMap;\nuse std::path::Path;\nuse std::io::Write;\nuse std::sync::\\{LazyLock, Mutex};\nuse log::\\{log, LevelFilter, Level};\n\n/// flag provider\npub struct FlagProvider \\{\n    overrides: BTreeMap<&'static str, bool>,\n}\n\n{{ if has_readwrite- }}\nstatic PACKAGE_CONTEXT: LazyLock<Result<Option<PackageReadContext>, AconfigStorageError>> = LazyLock::new(|| unsafe \\{\n    get_mapped_storage_file(\"{container}\", StorageFileType::PackageMap)\n    .and_then(|package_map| get_package_read_context(&package_map, \"{package}\"))\n});\n\nstatic FLAG_VAL_MAP: LazyLock<Result<Mmap, AconfigStorageError>> = LazyLock::new(|| unsafe \\{\n    get_mapped_storage_file(\"{container}\", StorageFileType::FlagVal)\n});\n\n{{ -for flag in template_flags }}\n{{ -if flag.readwrite }}\n/// flag value cache for {flag.name}\nstatic CACHED_{flag.name}: LazyLock<bool> = LazyLock::new(|| \\{\n\n    // This will be called multiple times. Subsequent calls after the first are noops.\n    logger::init(\n        logger::Config::default()\n            .with_tag_on_device(\"aconfig_rust_codegen\")\n            .with_max_level(LevelFilter::Info));\n\n    {{ -if use_package_fingerprint }}\n    let fingerprint_check_failed: bool = PACKAGE_CONTEXT\n        .as_ref()\n        .is_ok_and(|package_context| \\{\n              match package_context \\{\n                Some(context) => \\{\n                        return context.fingerprint != {package_fingerprint}\n                    },\n                    None => \\{\n                        log!(Level::Info, \"aconfig_rust_codegen: missing fingerprint; performing lookup.\");\n                        false\n                    }\n                  }\n                });\n\n    if fingerprint_check_failed \\{\n      log!(Level::Error, \"Fingerprint mismatch for package {package}; returning flag default ({flag.default_value}) for {flag.name}.\");\n      return {flag.default_value};\n    }\n    {{ -endif }}\n\n    let flag_value_result = FLAG_VAL_MAP\n        .as_ref()\n        .map_err(|err| format!(\"failed to get flag val map: \\{err}\"))\n        .and_then(|flag_val_map| \\{\n            PACKAGE_CONTEXT\n                .as_ref()\n                .map_err(|err| format!(\"failed to get package read context: \\{err}\"))\n                .and_then(|package_context| \\{\n                    match package_context \\{\n                        Some(context) => \\{\n                            get_boolean_flag_value(&flag_val_map, context.boolean_start_index + {flag.flag_offset})\n                                .map_err(|err| format!(\"failed to get flag: \\{err}\"))\n                        },\n                        None => \\{\n                            log!(Level::Error, \"no context found for package {package}\");\n                            Err(format!(\"failed to flag package {package}\"))\n                        }\n                    }\n                })\n            });\n\n    match flag_value_result \\{\n        Ok(flag_value) => \\{\n            return flag_value;\n        },\n        Err(err) => \\{\n            log!(Level::Error, \"aconfig_rust_codegen: error: \\{err}\");\n            return {flag.default_value};\n        }\n    }\n\n});\n{{ -endif }}\n{{ -endfor }}\n{{ -endif }}\n\nimpl FlagProvider \\{\n{{ for flag in template_flags }}\n    /// query flag {flag.name}\n    pub fn {flag.name}(&self) -> bool \\{\n        self.overrides.get(\"{flag.name}\").copied().unwrap_or(\n        {{ if flag.readwrite -}}\n           *CACHED_{flag.name}\n        {{ -else- }}\n           {flag.default_value}\n        {{ -endif }}\n        )\n    }\n\n    /// set flag {flag.name}\n    pub fn set_{flag.name}(&mut self, val: bool) \\{\n        self.overrides.insert(\"{flag.name}\", val);\n    }\n{{ endfor }}\n\n    /// clear all flag overrides\n    pub fn reset_flags(&mut self) \\{\n        self.overrides.clear();\n    }\n}\n\n/// flag provider\npub static PROVIDER: Mutex<FlagProvider> = Mutex::new(\n    FlagProvider \\{overrides: BTreeMap::new()}\n);\n\n{{ for flag in template_flags }}\n/// query flag {flag.name}\n#[inline(always)]\npub fn {flag.name}() -> bool \\{\n    PROVIDER.lock().unwrap().{flag.name}()\n}\n\n/// set flag {flag.name}\n#[inline(always)]\npub fn set_{flag.name}(val: bool) \\{\n    PROVIDER.lock().unwrap().set_{flag.name}(val);\n}\n{{ endfor }}\n\n/// clear all flag override\npub fn reset_flags() \\{\n    PROVIDER.lock().unwrap().reset_flags()\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/AconfigHostTest.java",
    "content": "import static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertThrows;\nimport static org.junit.Assert.assertTrue;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\n\nimport com.android.aconfig.test.FakeFeatureFlagsImpl;\nimport com.android.aconfig.test.FeatureFlags;\nimport com.android.aconfig.test.FeatureFlagsImpl;\nimport com.android.aconfig.test.Flags;\n\n@RunWith(JUnit4.class)\npublic final class AconfigHostTest {\n    @Test\n    public void testThrowsExceptionIfFlagNotSet() {\n        assertThrows(NullPointerException.class, () -> Flags.disabledRo());\n        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();\n        assertThrows(IllegalArgumentException.class, () -> featureFlags.disabledRo());\n    }\n\n    @Test\n    public void testSetFlagInFakeFeatureFlagsImpl() {\n        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();\n        featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true);\n        assertTrue(featureFlags.enabledRw());\n        featureFlags.setFlag(Flags.FLAG_ENABLED_RW, false);\n        assertFalse(featureFlags.enabledRw());\n\n        //Set Flags\n        assertThrows(NullPointerException.class, () -> Flags.enabledRw());\n        Flags.setFeatureFlags(featureFlags);\n        featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true);\n        assertTrue(Flags.enabledRw());\n        Flags.unsetFeatureFlags();\n    }\n\n    @Test\n    public void testSetFlagWithRandomName() {\n        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();\n        assertThrows(IllegalArgumentException.class,\n            () -> featureFlags.setFlag(\"Randome_name\", true));\n    }\n\n    @Test\n    public void testResetFlagsInFakeFeatureFlagsImpl() {\n        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();\n        featureFlags.setFlag(Flags.FLAG_ENABLED_RO, true);\n        assertTrue(featureFlags.enabledRo());\n        featureFlags.resetAll();\n        assertThrows(IllegalArgumentException.class, () -> featureFlags.enabledRo());\n\n        // Set value after reset\n        featureFlags.setFlag(Flags.FLAG_ENABLED_RO, false);\n        assertFalse(featureFlags.enabledRo());\n    }\n\n    @Test\n    public void testFlagsSetFeatureFlags() {\n        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();\n        featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true);\n        assertThrows(NullPointerException.class, () -> Flags.enabledRw());\n        Flags.setFeatureFlags(featureFlags);\n        assertTrue(Flags.enabledRw());\n        Flags.unsetFeatureFlags();\n    }\n\n    @Test\n    public void testFlagsUnsetFeatureFlags() {\n        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();\n        featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true);\n        assertThrows(NullPointerException.class, () -> Flags.enabledRw());\n        Flags.setFeatureFlags(featureFlags);\n        assertTrue(Flags.enabledRw());\n\n        Flags.unsetFeatureFlags();\n        assertThrows(NullPointerException.class, () -> Flags.enabledRw());\n    }\n\n    @Test\n    public void testFeatureFlagsImplNotImpl() {\n        FeatureFlags featureFlags = new FeatureFlagsImpl();\n        assertThrows(UnsupportedOperationException.class,\n            () -> featureFlags.enabledRw());\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/AconfigTest.java",
    "content": "import static com.android.aconfig.test.Flags.FLAG_DISABLED_RO;\nimport static com.android.aconfig.test.Flags.FLAG_DISABLED_RW;\nimport static com.android.aconfig.test.Flags.FLAG_ENABLED_FIXED_RO;\nimport static com.android.aconfig.test.Flags.FLAG_ENABLED_RO;\nimport static com.android.aconfig.test.Flags.FLAG_ENABLED_RW;\nimport static com.android.aconfig.test.Flags.disabledRo;\nimport static com.android.aconfig.test.Flags.disabledRw;\nimport static com.android.aconfig.test.Flags.enabledFixedRo;\nimport static com.android.aconfig.test.Flags.enabledRo;\nimport static com.android.aconfig.test.Flags.enabledRw;\nimport static com.android.aconfig.test.exported.Flags.exportedFlag;\nimport static com.android.aconfig.test.exported.Flags.FLAG_EXPORTED_FLAG;\nimport static com.android.aconfig.test.forcereadonly.Flags.froRw;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertThrows;\nimport static org.junit.Assert.assertTrue;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport com.android.aconfig.test.FakeFeatureFlagsImpl;\nimport com.android.aconfig.test.FeatureFlags;\n\n@RunWith(JUnit4.class)\npublic final class AconfigTest {\n    @Test\n    public void testDisabledReadOnlyFlag() {\n        assertEquals(\"com.android.aconfig.test.disabled_ro\", FLAG_DISABLED_RO);\n        assertFalse(disabledRo());\n    }\n\n    @Test\n    public void testEnabledReadOnlyFlag() {\n        assertEquals(\"com.android.aconfig.test.disabled_rw\", FLAG_DISABLED_RW);\n        assertTrue(enabledRo());\n    }\n\n    @Test\n    public void testEnabledFixedReadOnlyFlag() {\n        assertEquals(\"com.android.aconfig.test.enabled_fixed_ro\", FLAG_ENABLED_FIXED_RO);\n        assertTrue(enabledFixedRo());\n    }\n\n    @Test\n    public void testDisabledReadWriteFlag() {\n        assertEquals(\"com.android.aconfig.test.enabled_ro\", FLAG_ENABLED_RO);\n        assertFalse(disabledRw());\n    }\n\n    @Test\n    public void testEnabledReadWriteFlag() {\n        assertEquals(\"com.android.aconfig.test.enabled_rw\", FLAG_ENABLED_RW);\n        assertTrue(enabledRw());\n    }\n\n    @Test\n    public void testFakeFeatureFlagsImplImpled() {\n        FakeFeatureFlagsImpl fakeFeatureFlags = new FakeFeatureFlagsImpl();\n        fakeFeatureFlags.setFlag(FLAG_ENABLED_RW, false);\n        assertFalse(fakeFeatureFlags.enabledRw());\n    }\n\n    @Test\n    public void testExportedFlag() {\n        assertEquals(\"com.android.aconfig.test.exported.exported_flag\", FLAG_EXPORTED_FLAG);\n        assertFalse(exportedFlag());\n    }\n\n    @Test\n    public void testForceReadOnly() {\n        assertFalse(froRw());\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2023 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"aconfig.test.java\">\n\n    <uses-permission android:name=\"android.permission.READ_DEVICE_CONFIG\" />\n\n    <application>\n        <uses-library android:name=\"android.test.runner\"/>\n    </application>\n\n    <instrumentation android:name=\"androidx.test.runner.AndroidJUnitRunner\"\n        android:targetPackage=\"aconfig.test.java\"\n        android:label=\"aconfig integration tests (java)\" />\n</manifest>\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/aconfig_exported_mode_test.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"com_android_aconfig_test.h\"\n#include \"gtest/gtest.h\"\n\nusing namespace com::android::aconfig::test;\n\nTEST(AconfigTest, TestDisabledRwExportedFlag) {\n  ASSERT_FALSE(com_android_aconfig_test_disabled_rw_exported());\n  ASSERT_FALSE(provider_->disabled_rw_exported());\n  ASSERT_FALSE(disabled_rw_exported());\n}\n\nTEST(AconfigTest, TestEnabledFixedRoExportedFlag) {\n  // TODO: change to assertTrue(enabledFixedRoExported()) when the build supports reading tests/*.values\n  ASSERT_FALSE(com_android_aconfig_test_enabled_fixed_ro_exported());\n  ASSERT_FALSE(provider_->enabled_fixed_ro_exported());\n  ASSERT_FALSE(enabled_fixed_ro_exported());\n}\n\nTEST(AconfigTest, TestEnabledRoExportedFlag) {\n  // TODO: change to assertTrue(enabledRoExported()) when the build supports reading tests/*.values\n  ASSERT_FALSE(com_android_aconfig_test_enabled_ro_exported());\n  ASSERT_FALSE(provider_->enabled_ro_exported());\n  ASSERT_FALSE(enabled_ro_exported());\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}"
  },
  {
    "path": "tools/aconfig/aconfig/tests/aconfig_exported_mode_test.rs",
    "content": "#[cfg(not(feature = \"cargo\"))]\n#[test]\nfn test_flags() {\n    assert!(!aconfig_test_rust_library::disabled_rw_exported());\n    assert!(!aconfig_test_rust_library::enabled_fixed_ro_exported());\n    assert!(!aconfig_test_rust_library::enabled_ro_exported());\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"com_android_aconfig_test.h\"\n#include \"gtest/gtest.h\"\n\nusing namespace com::android::aconfig::test;\n\nTEST(AconfigTest, TestDisabledReadOnlyFlag) {\n  ASSERT_FALSE(com_android_aconfig_test_disabled_ro());\n  ASSERT_FALSE(provider_->disabled_ro());\n  ASSERT_FALSE(disabled_ro());\n}\n\nTEST(AconfigTest, TestEnabledReadOnlyFlag) {\n  ASSERT_TRUE(com_android_aconfig_test_enabled_ro());\n  ASSERT_TRUE(provider_->enabled_ro());\n  ASSERT_TRUE(enabled_ro());\n}\n\nTEST(AconfigTest, TestDisabledReadWriteFlag) {\n  ASSERT_FALSE(com_android_aconfig_test_disabled_rw());\n  ASSERT_FALSE(provider_->disabled_rw());\n  ASSERT_FALSE(disabled_rw());\n}\n\nTEST(AconfigTest, TestEnabledReadWriteFlag) {\n  ASSERT_TRUE(com_android_aconfig_test_enabled_rw());\n  ASSERT_TRUE(provider_->enabled_rw());\n  ASSERT_TRUE(enabled_rw());\n}\n\nTEST(AconfigTest, TestEnabledFixedReadOnlyFlag) {\n  ASSERT_TRUE(com_android_aconfig_test_enabled_fixed_ro());\n  ASSERT_TRUE(provider_->enabled_fixed_ro());\n  ASSERT_TRUE(enabled_fixed_ro());\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.rs",
    "content": "#[cfg(not(feature = \"cargo\"))]\n#[test]\nfn test_flags() {\n    assert!(!aconfig_test_rust_library::disabled_ro());\n    assert!(!aconfig_test_rust_library::disabled_rw());\n    assert!(!aconfig_test_rust_library::disabled_rw_in_other_namespace());\n    assert!(aconfig_test_rust_library::enabled_fixed_ro());\n    assert!(aconfig_test_rust_library::enabled_ro());\n    assert!(aconfig_test_rust_library::enabled_rw());\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/aconfig_prod_mode_test.rs",
    "content": "#[cfg(not(feature = \"cargo\"))]\n#[test]\nfn test_flags() {\n    assert!(!aconfig_test_rust_library::disabled_ro());\n    assert!(!aconfig_test_rust_library::disabled_rw());\n    assert!(aconfig_test_rust_library::enabled_ro());\n    assert!(aconfig_test_rust_library::enabled_rw());\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/aconfig_test.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"com_android_aconfig_test.h\"\n#include \"gtest/gtest.h\"\n\nusing namespace com::android::aconfig::test;\n\nTEST(AconfigTest, TestDisabledReadOnlyFlag) {\n  ASSERT_FALSE(com_android_aconfig_test_disabled_ro());\n  ASSERT_FALSE(provider_->disabled_ro());\n  ASSERT_FALSE(disabled_ro());\n}\n\nTEST(AconfigTest, TestEnabledReadOnlyFlag) {\n  ASSERT_TRUE(com_android_aconfig_test_enabled_ro());\n  ASSERT_TRUE(provider_->enabled_ro());\n  ASSERT_TRUE(enabled_ro());\n}\n\nTEST(AconfigTest, TestDisabledReadWriteFlag) {\n  ASSERT_FALSE(com_android_aconfig_test_disabled_rw());\n  ASSERT_FALSE(provider_->disabled_rw());\n  ASSERT_FALSE(disabled_rw());\n}\n\nTEST(AconfigTest, TestEnabledReadWriteFlag) {\n  ASSERT_TRUE(com_android_aconfig_test_enabled_rw());\n  ASSERT_TRUE(provider_->enabled_rw());\n  ASSERT_TRUE(enabled_rw());\n}\n\nTEST(AconfigTest, TestEnabledFixedReadOnlyFlag) {\n  ASSERT_TRUE(com_android_aconfig_test_enabled_fixed_ro());\n  ASSERT_TRUE(provider_->enabled_fixed_ro());\n  ASSERT_TRUE(enabled_fixed_ro());\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/aconfig_test_mode_test.rs",
    "content": "#[cfg(not(feature = \"cargo\"))]\n#[test]\nfn test_flags() {\n    assert!(!aconfig_test_rust_library::disabled_ro());\n    assert!(!aconfig_test_rust_library::disabled_rw());\n    assert!(aconfig_test_rust_library::enabled_ro());\n    assert!(aconfig_test_rust_library::enabled_rw());\n\n    aconfig_test_rust_library::set_disabled_ro(true);\n    assert!(aconfig_test_rust_library::disabled_ro());\n    aconfig_test_rust_library::set_disabled_rw(true);\n    assert!(aconfig_test_rust_library::disabled_rw());\n    aconfig_test_rust_library::set_enabled_ro(false);\n    assert!(!aconfig_test_rust_library::enabled_ro());\n    aconfig_test_rust_library::set_enabled_rw(false);\n    assert!(!aconfig_test_rust_library::enabled_rw());\n\n    aconfig_test_rust_library::reset_flags();\n    assert!(!aconfig_test_rust_library::disabled_ro());\n    assert!(!aconfig_test_rust_library::disabled_rw());\n    assert!(aconfig_test_rust_library::enabled_ro());\n    assert!(aconfig_test_rust_library::enabled_rw());\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/aconfig_test_test_variant.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"com_android_aconfig_test.h\"\n#include \"gtest/gtest.h\"\n\nusing namespace com::android::aconfig::test;\n\nclass AconfigTest : public ::testing::Test {\n protected:\n  void SetUp() override {\n    reset_flags();\n  }\n};\n\nTEST_F(AconfigTest, TestDisabledReadOnlyFlag) {\n  ASSERT_FALSE(com_android_aconfig_test_disabled_ro());\n  ASSERT_FALSE(provider_->disabled_ro());\n  ASSERT_FALSE(disabled_ro());\n}\n\nTEST_F(AconfigTest, TestEnabledReadOnlyFlag) {\n  ASSERT_TRUE(com_android_aconfig_test_enabled_ro());\n  ASSERT_TRUE(provider_->enabled_ro());\n  ASSERT_TRUE(enabled_ro());\n}\n\nTEST_F(AconfigTest, TestDisabledReadWriteFlag) {\n  ASSERT_FALSE(com_android_aconfig_test_disabled_rw());\n  ASSERT_FALSE(provider_->disabled_rw());\n  ASSERT_FALSE(disabled_rw());\n}\n\nTEST_F(AconfigTest, TestEnabledReadWriteFlag) {\n  ASSERT_TRUE(com_android_aconfig_test_enabled_rw());\n  ASSERT_TRUE(provider_->enabled_rw());\n  ASSERT_TRUE(enabled_rw());\n}\n\nTEST_F(AconfigTest, TestEnabledFixedReadOnlyFlag) {\n  ASSERT_TRUE(com_android_aconfig_test_enabled_fixed_ro());\n  ASSERT_TRUE(provider_->enabled_fixed_ro());\n  ASSERT_TRUE(enabled_fixed_ro());\n}\n\nTEST_F(AconfigTest, OverrideFlagValue) {\n  ASSERT_FALSE(disabled_ro());\n  disabled_ro(true);\n  ASSERT_TRUE(disabled_ro());\n}\n\nTEST_F(AconfigTest, ResetFlagValue) {\n  ASSERT_FALSE(disabled_ro());\n  ASSERT_TRUE(enabled_ro());\n  disabled_ro(true);\n  enabled_ro(false);\n  ASSERT_TRUE(disabled_ro());\n  ASSERT_FALSE(enabled_ro());\n  reset_flags();\n  ASSERT_FALSE(disabled_ro());\n  ASSERT_TRUE(enabled_ro());\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/first.values",
    "content": "flag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"disabled_ro\"\n    state: DISABLED\n    permission: READ_ONLY\n}\nflag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"enabled_ro\"\n    state: DISABLED\n    permission: READ_WRITE\n}\nflag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"enabled_rw\"\n    state: ENABLED\n    permission: READ_WRITE\n}\nflag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"disabled_rw_in_other_namespace\"\n    state: DISABLED\n    permission: READ_WRITE\n}\nflag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"enabled_fixed_ro\"\n    state: ENABLED\n    permission: READ_ONLY\n}\nflag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"enabled_ro_exported\"\n    state: ENABLED\n    permission: READ_ONLY\n}\nflag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"disabled_rw_exported\"\n    state: DISABLED\n    permission: READ_WRITE\n}\nflag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"enabled_fixed_ro_exported\"\n    state: ENABLED\n    permission: READ_ONLY\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/read_only_test.aconfig",
    "content": "package: \"com.android.aconfig.test\"\ncontainer: \"system\"\n\nflag {\n    name: \"enabled_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is ENABLED + READ_ONLY\"\n    bug: \"abc\"\n}\n\nflag {\n    name: \"disabled_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is DISABLED + READ_ONLY\"\n    bug: \"123\"\n}\n\nflag {\n    name: \"enabled_fixed_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is fixed READ_ONLY + ENABLED\"\n    bug: \"\"\n    is_fixed_read_only: true\n}\n\nflag {\n    name: \"disabled_fixed_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is fixed READ_ONLY + DISABLED\"\n    bug: \"\"\n    is_fixed_read_only: true\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/read_only_test.values",
    "content": "flag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"disabled_ro\"\n    state: DISABLED\n    permission: READ_ONLY\n}\nflag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"enabled_ro\"\n    state: ENABLED\n    permission: READ_ONLY\n}\nflag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"enabled_fixed_ro\"\n    state: ENABLED\n    permission: READ_ONLY\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/second.values",
    "content": "flag_value {\n    package: \"com.android.aconfig.test\"\n    name: \"enabled_ro\"\n    state: ENABLED\n    permission: READ_ONLY\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/storage_test_1.aconfig",
    "content": "package: \"com.android.aconfig.storage.test_1\"\ncontainer: \"system\"\n\nflag {\n    name: \"enabled_rw\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is ENABLED + READ_WRITE\"\n    bug: \"\"\n}\n\nflag {\n    name: \"disabled_rw\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is DISABLED + READ_WRITE\"\n    bug: \"456\"\n    is_exported: true\n}\n\nflag {\n    name: \"enabled_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is ENABLED + READ_ONLY\"\n    bug: \"abc\"\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/storage_test_1.values",
    "content": "flag_value {\n    package: \"com.android.aconfig.storage.test_1\"\n    name: \"enabled_rw\"\n    state: ENABLED\n    permission: READ_WRITE\n}\nflag_value {\n    package: \"com.android.aconfig.storage.test_1\"\n    name: \"disabled_rw\"\n    state: DISABLED\n    permission: READ_WRITE\n}\nflag_value {\n    package: \"com.android.aconfig.storage.test_1\"\n    name: \"enabled_ro\"\n    state: ENABLED\n    permission: READ_ONLY\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/storage_test_2.aconfig",
    "content": "package: \"com.android.aconfig.storage.test_2\"\ncontainer: \"system\"\n\nflag {\n    name: \"enabled_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is ENABLED + READ_ONLY\"\n    bug: \"abc\"\n}\n\nflag {\n    name: \"disabled_rw\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is DISABLED + READ_ONLY\"\n    bug: \"123\"\n}\n\nflag {\n    name: \"enabled_fixed_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is fixed READ_ONLY + ENABLED\"\n    bug: \"\"\n    is_fixed_read_only: true\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/storage_test_2.values",
    "content": "flag_value {\n    package: \"com.android.aconfig.storage.test_2\"\n    name: \"enabled_ro\"\n    state: ENABLED\n    permission: READ_ONLY\n}\nflag_value {\n    package: \"com.android.aconfig.storage.test_2\"\n    name: \"disabled_rw\"\n    state: DISABLED\n    permission: READ_WRITE\n}\nflag_value {\n    package: \"com.android.aconfig.storage.test_2\"\n    name: \"enabled_fixed_ro\"\n    state: ENABLED\n    permission: READ_ONLY\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/storage_test_4.aconfig",
    "content": "package: \"com.android.aconfig.storage.test_4\"\ncontainer: \"system\"\n\nflag {\n    name: \"enabled_rw\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is ENABLED + READ_ONLY\"\n    bug: \"abc\"\n}\n\nflag {\n    name: \"enabled_fixed_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is fixed READ_ONLY + ENABLED\"\n    bug: \"\"\n    is_fixed_read_only: true\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/storage_test_4.values",
    "content": "flag_value {\n    package: \"com.android.aconfig.storage.test_4\"\n    name: \"enabled_rw\"\n    state: ENABLED\n    permission: READ_WRITE\n}\nflag_value {\n    package: \"com.android.aconfig.storage.test_4\"\n    name: \"enabled_fixed_ro\"\n    state: ENABLED\n    permission: READ_ONLY\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/test.aconfig",
    "content": "package: \"com.android.aconfig.test\"\ncontainer: \"system\"\n\n# This flag's final value is calculated from:\n# - test.aconfig: DISABLED + READ_WRITE (default)\n# - first.values: DISABLED + READ_WRITE\n# - second.values: ENABLED + READ_ONLY\nflag {\n    name: \"enabled_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is ENABLED + READ_ONLY\"\n    bug: \"abc\"\n    metadata {\n        purpose: PURPOSE_BUGFIX\n    }\n}\n\n# This flag's final value is calculated from:\n# - test.aconfig: DISABLED + READ_WRITE (default)\n# - first.values: ENABLED + READ_WRITE\nflag {\n    name: \"enabled_rw\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is ENABLED + READ_WRITE\"\n    # for bug fields, the empty string is a discouraged but valid value\n    bug: \"\"\n}\n\n# This flag's final value is calculated from:\n# - test.aconfig: DISABLED + READ_WRITE (default)\n# - first.values: DISABLED + READ_ONLY\nflag {\n    name: \"disabled_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is DISABLED + READ_ONLY\"\n    bug: \"123\"\n}\n\n# This flag's final value is calculated from:\n# - test.aconfig: DISABLED + READ_WRITE (default)\nflag {\n    name: \"disabled_rw\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is DISABLED + READ_WRITE\"\n    bug: \"456\"\n}\n\n# This flag's final value calculated from:\n# - test.aconfig: DISABLED + READ_ONLY\n# - first.values: ENABLED + READ_ONLY\nflag {\n    name: \"enabled_fixed_ro\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is fixed READ_ONLY + ENABLED\"\n    bug: \"\"\n    is_fixed_read_only: true\n}\n\nflag {\n    name: \"disabled_rw_in_other_namespace\"\n    namespace: \"other_namespace\"\n    description: \"This flag is DISABLED + READ_WRITE, and is defined in another namespace\"\n    bug: \"999\"\n}\n\nflag {\n    name: \"enabled_ro_exported\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is ENABLED + READ_ONLY and exported\"\n    bug: \"111\"\n    is_exported: true\n}\n\nflag {\n    name: \"disabled_rw_exported\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is DISABLED + READ_WRITE and exported\"\n    bug: \"111\"\n    is_exported: true\n}\n\nflag {\n    name: \"enabled_fixed_ro_exported\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is fixed ENABLED + READ_ONLY and exported\"\n    bug: \"111\"\n    is_fixed_read_only: true\n    is_exported: true\n}"
  },
  {
    "path": "tools/aconfig/aconfig/tests/test_exported.aconfig",
    "content": "package: \"com.android.aconfig.test.exported\"\ncontainer: \"system\"\n\nflag {\n    name: \"exported_flag\"\n    namespace: \"aconfig_test\"\n    description: \"This is an exported flag\"\n    is_exported: true\n    bug: \"888\"\n}\n\nflag {\n    name: \"not_exported_flag\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is not exported\"\n    bug: \"777\"\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/test_force_read_only.aconfig",
    "content": "package: \"com.android.aconfig.test.forcereadonly\"\ncontainer: \"system\"\n\nflag {\n    name: \"fro_exported\"\n    namespace: \"aconfig_test\"\n    description: \"This is an exported flag\"\n    is_exported: true\n    bug: \"888\"\n}\n\nflag {\n    name: \"fro_rw\"\n    namespace: \"aconfig_test\"\n    description: \"This flag is not exported\"\n    bug: \"777\"\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/test_second_package.aconfig",
    "content": "package: \"com.android.aconfig.second_test\"\ncontainer: \"system\"\n\nflag {\n    name: \"testing_flag\"\n    namespace: \"another_namespace\"\n    description: \"This is a flag for testing.\"\n    bug: \"123\"\n}\n\n"
  },
  {
    "path": "tools/aconfig/aconfig/tests/third.values",
    "content": "flag_value {\n    package: \"com.android.aconfig.second_test\"\n    name: \"testing_flag\"\n    state: DISABLED\n    permission: READ_WRITE\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/Android.bp",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_defaults {\n    name: \"libaconfig_device_paths.defaults\",\n    edition: \"2021\",\n    clippy_lints: \"android\",\n    lints: \"android\",\n    srcs: [\"src/lib.rs\"],\n    rustlibs: [\n        \"libaconfig_protos\",\n        \"libanyhow\",\n        \"libprotobuf\",\n    ],\n}\n\nrust_library {\n    name: \"libaconfig_device_paths\",\n    crate_name: \"aconfig_device_paths\",\n    host_supported: true,\n    defaults: [\"libaconfig_device_paths.defaults\"],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.configinfrastructure\",\n    ],\n    min_sdk_version: \"34\",\n}\n\ngenrule {\n    name: \"libaconfig_java_device_paths_src\",\n    srcs: [\"src/DeviceProtosTemplate.java\"],\n    out: [\"DeviceProtos.java\"],\n    tool_files: [\"partition_aconfig_flags_paths.txt\"],\n    cmd: \"sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out)\",\n}\n\njava_library {\n    name: \"aconfig_device_paths_java\",\n    srcs: [\":libaconfig_java_device_paths_src\"],\n    static_libs: [\n        \"libaconfig_java_proto_nano\",\n    ],\n    sdk_version: \"core_platform\",\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.configinfrastructure\",\n    ],\n    min_sdk_version: \"34\",\n}\n\ngenrule {\n    name: \"libaconfig_java_host_device_paths_src\",\n    srcs: [\"src/HostDeviceProtosTemplate.java\"],\n    out: [\"HostDeviceProtos.java\"],\n    tool_files: [\n        \"partition_aconfig_flags_paths.txt\",\n        \"mainline_aconfig_flags_paths.txt\",\n    ],\n    cmd: \"sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out).tmp && \" +\n    \"sed -e '/MAINLINE_T/{r$(location mainline_aconfig_flags_paths.txt)' -e 'd}' $(out).tmp > $(out)\",\n}\n\njava_library_host {\n    name: \"aconfig_host_device_paths_java\",\n    srcs: [\":libaconfig_java_host_device_paths_src\"],\n}\n\ngenrule {\n    name: \"java_device_paths_test_util_src\",\n    srcs: [\"src/DeviceProtosTestUtilTemplate.java\"],\n    out: [\"DeviceProtosTestUtil.java\"],\n    tool_files: [\"partition_aconfig_flags_paths.txt\"],\n    cmd: \"sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out)\",\n}\n\njava_library {\n    name: \"aconfig_device_paths_java_util\",\n    srcs: [\":java_device_paths_test_util_src\"],\n    static_libs: [\n        \"libaconfig_java_proto_nano\",\n    ],\n    sdk_version: \"core_platform\",\n    apex_available: [\n        \"//apex_available:platform\",\n    ],\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/Cargo.toml",
    "content": "[package]\nname = \"aconfig_device_paths\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nanyhow = \"1.0.82\"\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/mainline_aconfig_flags_paths.txt",
    "content": "\"/apex/com.android.adservices/etc/aconfig_flags.pb\",\n\"/apex/com.android.appsearch/etc/aconfig_flags.pb\",\n\"/apex/com.android.art/etc/aconfig_flags.pb\",\n\"/apex/com.android.bt/etc/aconfig_flags.pb\",\n\"/apex/com.android.cellbroadcast/etc/aconfig_flags.pb\",\n\"/apex/com.android.configinfrastructure/etc/aconfig_flags.pb\",\n\"/apex/com.android.conscrypt/etc/aconfig_flags.pb\",\n\"/apex/com.android.devicelock/etc/aconfig_flags.pb\",\n\"/apex/com.android.healthfitness/etc/aconfig_flags.pb\",\n\"/apex/com.android.ipsec/etc/aconfig_flags.pb\",\n\"/apex/com.android.media/etc/aconfig_flags.pb\",\n\"/apex/com.android.mediaprovider/etc/aconfig_flags.pb\",\n\"/apex/com.android.ondevicepersonalization/etc/aconfig_flags.pb\",\n\"/apex/com.android.os.statsd/etc/aconfig_flags.pb\",\n\"/apex/com.android.permission/etc/aconfig_flags.pb\",\n\"/apex/com.android.profiling/etc/aconfig_flags.pb\",\n\"/apex/com.android.tethering/etc/aconfig_flags.pb\",\n\"/apex/com.android.uwb/etc/aconfig_flags.pb\",\n\"/apex/com.android.virt/etc/aconfig_flags.pb\",\n\"/apex/com.android.wifi/etc/aconfig_flags.pb\",\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/partition_aconfig_flags_paths.txt",
    "content": "\"/system/etc/aconfig_flags.pb\",\n\"/system_ext/etc/aconfig_flags.pb\",\n\"/product/etc/aconfig_flags.pb\",\n\"/vendor/etc/aconfig_flags.pb\",\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/src/DeviceProtosTemplate.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage android.aconfig;\n\nimport android.aconfig.nano.Aconfig.parsed_flag;\nimport android.aconfig.nano.Aconfig.parsed_flags;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @hide\n */\npublic class DeviceProtos {\n\tpublic static final String[] PATHS = {\n        TEMPLATE\n    };\n\n    private static final String APEX_DIR = \"/apex\";\n    private static final String APEX_ACONFIG_PATH_SUFFIX = \"/etc/aconfig_flags.pb\";\n\n    /**\n     * Returns a list of all on-device aconfig protos.\n     *\n     * May throw an exception if the protos can't be read at the call site. For\n     * example, some of the protos are in the apex/ partition, which is mounted\n     * somewhat late in the boot process.\n     *\n     * @throws IOException if we can't read one of the protos yet\n     * @return a list of all on-device aconfig protos\n     */\n    public static List<parsed_flag> loadAndParseFlagProtos() throws IOException {\n        ArrayList<parsed_flag> result = new ArrayList();\n\n        for (String path : parsedFlagsProtoPaths()) {\n            try (FileInputStream inputStream = new FileInputStream(path)) {\n                parsed_flags parsedFlags = parsed_flags.parseFrom(inputStream.readAllBytes());\n                for (parsed_flag flag : parsedFlags.parsedFlag) {\n                    result.add(flag);\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Returns the list of all on-device aconfig protos paths.\n     * @hide\n     */\n    public static List<String> parsedFlagsProtoPaths() {\n        ArrayList<String> paths = new ArrayList(Arrays.asList(PATHS));\n\n        File apexDirectory = new File(APEX_DIR);\n        if (!apexDirectory.isDirectory()) {\n            return paths;\n        }\n\n        File[] subdirs = apexDirectory.listFiles();\n        if (subdirs == null) {\n            return paths;\n        }\n\n        for (File prefix : subdirs) {\n            // For each mainline modules, there are two directories, one <modulepackage>/,\n            // and one <modulepackage>@<versioncode>/. Just read the former.\n            if (prefix.getAbsolutePath().contains(\"@\")) {\n                continue;\n            }\n\n            File protoPath = new File(prefix + APEX_ACONFIG_PATH_SUFFIX);\n            if (!protoPath.exists()) {\n                continue;\n            }\n\n            paths.add(protoPath.getAbsolutePath());\n        }\n        return paths;\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/src/DeviceProtosTestUtilTemplate.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage android.aconfig;\n\nimport android.aconfig.nano.Aconfig.parsed_flag;\nimport android.aconfig.nano.Aconfig.parsed_flags;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/** @hide */\npublic class DeviceProtosTestUtil {\n    public static final String[] PATHS = {\n        TEMPLATE\n    };\n\n    private static final String APEX_DIR = \"/apex/\";\n    private static final String APEX_ACONFIG_PATH_SUFFIX = \"/etc/aconfig_flags.pb\";\n    private static final String SYSTEM_APEX_DIR = \"/system/apex\";\n\n    /**\n     * Returns a list of all on-device aconfig protos.\n     *\n     * <p>May throw an exception if the protos can't be read at the call site. For example, some of\n     * the protos are in the apex/ partition, which is mounted somewhat late in the boot process.\n     *\n     * @throws IOException if we can't read one of the protos yet\n     * @return a list of all on-device aconfig protos\n     */\n    public static List<parsed_flag> loadAndParseFlagProtos() throws IOException {\n        ArrayList<parsed_flag> result = new ArrayList();\n\n        for (String path : parsedFlagsProtoPaths()) {\n            try (FileInputStream inputStream = new FileInputStream(path)) {\n                parsed_flags parsedFlags = parsed_flags.parseFrom(inputStream.readAllBytes());\n                for (parsed_flag flag : parsedFlags.parsedFlag) {\n                    result.add(flag);\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Returns the list of all on-device aconfig protos paths.\n     *\n     * @hide\n     */\n    public static List<String> parsedFlagsProtoPaths() {\n        ArrayList<String> paths = new ArrayList(Arrays.asList(PATHS));\n\n        File apexDirectory = new File(SYSTEM_APEX_DIR);\n        if (!apexDirectory.isDirectory()) {\n            return paths;\n        }\n\n        File[] subdirs = apexDirectory.listFiles();\n        if (subdirs == null) {\n            return paths;\n        }\n\n        for (File prefix : subdirs) {\n            String apexName = prefix.getName().replace(\"com.google\", \"com\");\n            apexName = apexName.substring(0, apexName.lastIndexOf('.'));\n\n            File protoPath = new File(APEX_DIR + apexName + APEX_ACONFIG_PATH_SUFFIX);\n            if (!protoPath.exists()) {\n                continue;\n            }\n\n            paths.add(protoPath.getAbsolutePath());\n        }\n        return paths;\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/src/HostDeviceProtosTemplate.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage android.aconfig;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * A host lib that can read all aconfig proto file paths on a given device.\n * This lib is only available on device with root access (userdebug/eng).\n */\npublic class HostDeviceProtos {\n    /**\n     * An interface that executes ADB command and return the result.\n     */\n    public static interface AdbCommandExecutor {\n        /** Executes the ADB command. */\n        String executeAdbCommand(String command);\n    }\n\n    static final String[] PATHS = {\n        TEMPLATE\n    };\n\n    static final String[] MAINLINE_PATHS = {\n        MAINLINE_T\n    };\n\n    private static final String APEX_DIR = \"/apex\";\n    private static final String RECURSIVELY_LIST_APEX_DIR_COMMAND =\n        \"shell su 0 find /apex | grep aconfig_flags\";\n    private static final String APEX_ACONFIG_PATH_SUFFIX = \"/etc/aconfig_flags.pb\";\n\n\n    /**\n     * Returns the list of all on-device aconfig proto paths from host side.\n     */\n    public static List<String> parsedFlagsProtoPaths(AdbCommandExecutor adbCommandExecutor) {\n        ArrayList<String> paths = new ArrayList(Arrays.asList(PATHS));\n\n        String adbCommandOutput = adbCommandExecutor.executeAdbCommand(\n            RECURSIVELY_LIST_APEX_DIR_COMMAND);\n\n        if (adbCommandOutput == null || adbCommandOutput.isEmpty()) {\n            paths.addAll(Arrays.asList(MAINLINE_PATHS));\n            return paths;\n        }\n\n        Set<String> allFiles = new HashSet<>(Arrays.asList(adbCommandOutput.split(\"\\n\")));\n\n        Set<String> subdirs = allFiles.stream().map(file -> {\n            String[] filePaths = file.split(\"/\");\n            // The first element is \"\", the second element is \"apex\".\n            return filePaths.length > 2 ? filePaths[2] : \"\";\n        }).collect(Collectors.toSet());\n\n        for (String prefix : subdirs) {\n            // For each mainline modules, there are two directories, one <modulepackage>/,\n            // and one <modulepackage>@<versioncode>/. Just read the former.\n            if (prefix.contains(\"@\")) {\n                continue;\n            }\n\n            String protoPath = APEX_DIR + \"/\" + prefix + APEX_ACONFIG_PATH_SUFFIX;\n            if (allFiles.contains(protoPath)) {\n                paths.add(protoPath);\n            }\n        }\n        return paths;\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/src/lib.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! Library for finding all aconfig on-device protobuf file paths.\n\nuse anyhow::Result;\nuse std::path::PathBuf;\n\nuse std::fs;\n\nfn read_partition_paths() -> Vec<PathBuf> {\n    include_str!(\"../partition_aconfig_flags_paths.txt\")\n        .split(',')\n        .map(|s| s.trim().trim_matches('\"'))\n        .filter(|s| !s.is_empty())\n        .map(|s| PathBuf::from(s.to_string()))\n        .collect()\n}\n\n/// Determines all paths that contain an aconfig protobuf file,\n/// filtering out nonexistent partition protobuf files.\npub fn parsed_flags_proto_paths() -> Result<Vec<PathBuf>> {\n    let mut result: Vec<PathBuf> =\n        read_partition_paths().into_iter().filter(|s| s.exists()).collect();\n\n    for dir in fs::read_dir(\"/apex\")? {\n        let dir = dir?;\n\n        // Only scan the currently active version of each mainline module; skip the @version dirs.\n        if dir.file_name().as_encoded_bytes().iter().any(|&b| b == b'@') {\n            continue;\n        }\n\n        let mut path = PathBuf::from(\"/apex\");\n        path.push(dir.path());\n        path.push(\"etc\");\n        path.push(\"aconfig_flags.pb\");\n        if path.exists() {\n            result.push(path);\n        }\n    }\n\n    Ok(result)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_read_partition_paths() {\n        assert_eq!(read_partition_paths().len(), 4);\n\n        assert_eq!(\n            read_partition_paths(),\n            vec![\n                PathBuf::from(\"/system/etc/aconfig_flags.pb\"),\n                PathBuf::from(\"/system_ext/etc/aconfig_flags.pb\"),\n                PathBuf::from(\"/product/etc/aconfig_flags.pb\"),\n                PathBuf::from(\"/vendor/etc/aconfig_flags.pb\")\n            ]\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/test/Android.bp",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_team: \"trendy_team_android_core_experiments\",\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nandroid_test {\n    name: \"aconfig_device_paths_java_test\",\n    srcs: [\n        \"src/**/*.java\",\n    ],\n    static_libs: [\n        \"androidx.test.runner\",\n        \"junit\",\n        \"aconfig_device_paths_java_util\",\n    ],\n    test_suites: [\n        \"general-tests\",\n    ],\n    platform_apis: true,\n    certificate: \"platform\",\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/test/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright (C) 2024 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"android.aconfig.storage.test\">\n    <application>\n        <uses-library android:name=\"android.test.runner\" />\n    </application>\n\n    <instrumentation android:name=\"androidx.test.runner.AndroidJUnitRunner\"\n                     android:targetPackage=\"android.aconfig.storage.test\" />\n\n</manifest>\n"
  },
  {
    "path": "tools/aconfig/aconfig_device_paths/test/src/DeviceProtosTestUtilTest.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.test;\n\nimport static org.junit.Assert.assertTrue;\n\nimport android.aconfig.DeviceProtosTestUtil;\nimport android.aconfig.nano.Aconfig.parsed_flag;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.util.List;\nimport java.util.Set;\n\n@RunWith(JUnit4.class)\npublic class DeviceProtosTestUtilTest {\n\n    private static final Set<String> PLATFORM_CONTAINERS = Set.of(\"system\", \"vendor\", \"product\");\n\n    @Test\n    public void testDeviceProtos_loadAndParseFlagProtos() throws Exception {\n        List<parsed_flag> flags = DeviceProtosTestUtil.loadAndParseFlagProtos();\n        int platformFlags = 0;\n        int mainlineFlags = 0;\n        for (parsed_flag pf : flags) {\n            if (PLATFORM_CONTAINERS.contains(pf.container)) {\n                platformFlags++;\n            } else {\n                mainlineFlags++;\n            }\n        }\n\n        assertTrue(platformFlags > 3);\n        assertTrue(mainlineFlags > 3);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_flags/Android.bp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nrust_library {\n    name: \"libaconfig_flags\",\n    crate_name: \"aconfig_flags\",\n    srcs: [\n        \"src/lib.rs\",\n    ],\n    rustlibs: [\n        \"libaconfig_flags_rust\",\n    ],\n    host_supported: true,\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.configinfrastructure\",\n    ],\n    min_sdk_version: \"34\",\n}\n\naconfig_declarations {\n    name: \"aconfig_flags\",\n    package: \"com.android.aconfig.flags\",\n    container: \"system\",\n    srcs: [\"flags.aconfig\"],\n}\n\nrust_aconfig_library {\n    name: \"libaconfig_flags_rust\",\n    crate_name: \"aconfig_flags_rust\",\n    aconfig_declarations: \"aconfig_flags\",\n    host_supported: true,\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.configinfrastructure\",\n    ],\n    min_sdk_version: \"34\",\n}\n\ncc_aconfig_library {\n    name: \"libaconfig_flags_cc\",\n    aconfig_declarations: \"aconfig_flags\",\n}\n\njava_aconfig_library {\n    name: \"aconfig_flags_java\",\n    aconfig_declarations: \"aconfig_flags\",\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_flags/Cargo.toml",
    "content": "[package]\nname = \"aconfig_flags\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[features]\ndefault = [\"cargo\"]\ncargo = []\n\n[dependencies]"
  },
  {
    "path": "tools/aconfig/aconfig_flags/flags.aconfig",
    "content": "package: \"com.android.aconfig.flags\"\ncontainer: \"system\"\n\nflag {\n  name: \"enable_only_new_storage\"\n  namespace: \"core_experiments_team_internal\"\n  bug: \"312235596\"\n  description: \"When enabled, aconfig flags are read from the new aconfig storage only.\"\n}\n\nflag {\n  name: \"enable_aconfigd_from_mainline\"\n  namespace: \"core_experiments_team_internal\"\n  bug: \"369808805\"\n  description: \"When enabled, launch aconfigd from config infra module.\"\n}\n\nflag {\n  name: \"tools_read_from_new_storage\"\n  namespace: \"core_experiments_team_internal\"\n  bug: \"370499640\"\n  description: \"When enabled, tools read directly from the new aconfig storage.\"\n}\n\nflag {\n  name: \"tools_read_from_new_storage_bugfix\"\n  namespace: \"core_experiments_team_internal\"\n  bug: \"370499640\"\n  description: \"When enabled, tools read directly from the new aconfig storage.\"\n  metadata {\n    purpose: PURPOSE_BUGFIX\n  }\n}\n\nflag {\n  name: \"invoke_updatable_aflags\"\n  namespace: \"core_experiments_team_internal\"\n  bug: \"385383899\"\n  description: \"When enabled, the system aflags binary invokes the updatable aflags.\"\n  metadata {\n    purpose: PURPOSE_BUGFIX\n  }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_flags/src/lib.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! `aconfig_flags` is a crate for reading aconfig flags from Rust\n// When building with the Android tool-chain\n//\n//   - the flag functions will read from aconfig_flags_inner\n//   - the feature \"cargo\" will be disabled\n//\n// When building with cargo\n//\n//   - the flag functions will all return some trivial value, like true\n//   - the feature \"cargo\" will be enabled\n//\n// This module hides these differences from the rest of aconfig.\n\n/// Module used when building with the Android tool-chain\n#[cfg(not(feature = \"cargo\"))]\npub mod auto_generated {\n    /// Returns the value for the enable_only_new_storage flag.\n    pub fn enable_only_new_storage() -> bool {\n        aconfig_flags_rust::enable_only_new_storage()\n    }\n\n    /// Returns the value for the enable_aconfigd_from_mainline flag.\n    pub fn enable_aconfigd_from_mainline() -> bool {\n        aconfig_flags_rust::enable_only_new_storage()\n    }\n\n    /// Returns the value for the invoke_updatable_aflags flag.\n    pub fn invoke_updatable_aflags() -> bool {\n        aconfig_flags_rust::invoke_updatable_aflags()\n    }\n}\n\n/// Module used when building with cargo\n#[cfg(feature = \"cargo\")]\npub mod auto_generated {\n    /// Returns a placeholder value for the enable_only_new_storage flag.\n    pub fn enable_only_new_storage() -> bool {\n        // Used only to enable typechecking and testing with cargo\n        true\n    }\n\n    /// Returns a placeholder value for the enable_aconfigd_from_mainline flag.\n    pub fn enable_aconfigd_from_mainline() -> bool {\n        // Used only to enable typechecking and testing with cargo\n        true\n    }\n\n    /// Returns the value for the invoke_updatable_aflags flag.\n    pub fn invoke_updatable_aflags() -> bool {\n        // Used only to enable typechecking and testing with cargo\n        true\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_protos/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\n// proto libraries for consumers of `aconfig dump --format=protobuf` output\n\njava_library {\n    name: \"libaconfig_java_proto_lite\",\n    host_supported: true,\n    srcs: [\"protos/aconfig.proto\"],\n    static_libs: [\"libprotobuf-java-lite\"],\n    proto: {\n        type: \"lite\",\n    },\n    sdk_version: \"current\",\n    min_sdk_version: \"UpsideDownCake\",\n    apex_available: [\n        \"com.android.configinfrastructure\",\n        \"//apex_available:platform\",\n    ],\n}\n\njava_library {\n    name: \"libaconfig_java_proto_nano\",\n    srcs: [\"protos/aconfig.proto\"],\n    static_libs: [\"libprotobuf-java-nano\"],\n    proto: {\n        type: \"nano\",\n    },\n    sdk_version: \"current\",\n    min_sdk_version: \"UpsideDownCake\",\n    apex_available: [\n        \"//apex_available:platform\",\n    ],\n    jarjar_rules: \"jarjar-nano-rules.txt\",\n}\n\njava_library_host {\n    name: \"libaconfig_java_proto_full\",\n    srcs: [\"protos/aconfig.proto\"],\n    static_libs: [\"libprotobuf-java-full\"],\n    proto: {\n        type: \"full\",\n    },\n}\n\npython_library_host {\n    name: \"libaconfig_python_proto\",\n    srcs: [\"protos/aconfig.proto\"],\n    proto: {\n        canonical_path_from_root: false,\n    },\n}\n\nrust_protobuf {\n    name: \"libaconfig_rust_proto\",\n    protos: [\"protos/aconfig.proto\"],\n    crate_name: \"aconfig_rust_proto\",\n    source_stem: \"aconfig_rust_proto\",\n    host_supported: true,\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.configinfrastructure\",\n    ],\n    min_sdk_version: \"34\",\n}\n\nrust_defaults {\n    name: \"aconfig_protos.defaults\",\n    edition: \"2021\",\n    clippy_lints: \"android\",\n    lints: \"android\",\n    srcs: [\"src/lib.rs\"],\n    rustlibs: [\n        \"libaconfig_rust_proto\",\n        \"libanyhow\",\n        \"libprotobuf\",\n    ],\n    proc_macros: [\n        \"libpaste\",\n    ],\n}\n\nrust_library {\n    name: \"libaconfig_protos\",\n    crate_name: \"aconfig_protos\",\n    host_supported: true,\n    defaults: [\"aconfig_protos.defaults\"],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.configinfrastructure\",\n    ],\n    min_sdk_version: \"34\",\n}\n\nrust_test_host {\n    name: \"aconfig_protos.test\",\n    test_suites: [\"general-tests\"],\n    defaults: [\"aconfig_protos.defaults\"],\n}\n\n// Internal protos\n\npython_library_host {\n    name: \"aconfig_internal_proto_python\",\n    srcs: [\"protos/aconfig_internal.proto\"],\n    proto: {\n        canonical_path_from_root: false,\n    },\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_protos/Cargo.toml",
    "content": "[package]\nname = \"aconfig_protos\"\nversion = \"0.1.0\"\nedition = \"2021\"\nbuild = \"build.rs\"\n\n[features]\ndefault = [\"cargo\"]\ncargo = []\n\n[dependencies]\nanyhow = \"1.0.69\"\npaste = \"1.0.11\"\nprotobuf = \"3.2.0\"\n\n[build-dependencies]\nprotobuf-codegen = \"3.2.0\"\n"
  },
  {
    "path": "tools/aconfig/aconfig_protos/build.rs",
    "content": "use protobuf_codegen::Codegen;\n\nfn main() {\n    let proto_files = vec![\"protos/aconfig.proto\"];\n\n    // tell cargo to only re-run the build script if any of the proto files has changed\n    for path in &proto_files {\n        println!(\"cargo:rerun-if-changed={}\", path);\n    }\n\n    Codegen::new()\n        .pure()\n        .include(\"protos\")\n        .inputs(proto_files)\n        .cargo_out_dir(\"aconfig_proto\")\n        .run_from_script();\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_protos/jarjar-nano-rules.txt",
    "content": "rule com.google.protobuf.** android.internal.framework.protobuf.@1"
  },
  {
    "path": "tools/aconfig/aconfig_protos/protos/aconfig.proto",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License\n\n// This is the schema definition for aconfig files. Modifications need to be\n// either backwards compatible, or include updates to all aconfig files in the\n// Android tree.\n\nsyntax = \"proto2\";\n\npackage android.aconfig;\n\n// This protobuf file defines messages used to represent and manage flags in the \"aconfig\" system\n// The following format requirements apply across various message fields:\n//\n// # name: name of the flag\n//\n//    format: a lowercase string in snake_case format, no consecutive underscores, and no leading\n//      digit. For example adjust_rate is a valid name, while AdjustRate, adjust__rate, and\n//      adjust_rate are invalid\n//\n// # namespace: namespace the flag belongs to\n//\n//    format: a lowercase string in snake_case format, no consecutive underscores, and no leading\n//      digit. For example android_bar_system\n//\n// # package: package to which the flag belongs\n//\n//    format: lowercase strings in snake_case format, delimited by dots, no consecutive underscores\n//      and no leading digit in each string. For example com.android.mypackage is a valid name\n//      while com.android.myPackage, com.android.1mypackage are invalid\n//\n// # container: container as software built in its entirety using the same build environment and\n//    always installed as a single unit\n//\n//    For example the following are all separate containers:\n//        * the system partition\n//        * the vendor partition\n//        * apexes: each APEX is its own container\n//        * APKs: for APKs which are released independently via Play, each APK is its own container.\n//            If an APK is released as part of a Mainline module, or as part of the system partition\n//            via OTA, then they are part of the apex or the system partition container\n//\n//    format: lowercase strings in snake_case format, delimited by dots if multiple, no consecutive\n//      underscores or leading digits in each string. The recommended container values are the\n//      partition names or the module names\n\n// messages used in both aconfig input and output\n\nenum flag_state {\n  ENABLED = 1;\n  DISABLED = 2;\n}\n\nenum flag_permission {\n  READ_ONLY = 1;\n  READ_WRITE = 2;\n}\n\n// aconfig input messages: flag declarations and values\n\nmessage flag_declaration {\n  // Name of the flag (required)\n  // See # name for format detail\n  optional string name = 1;\n\n  // Namespace the flag belongs to (required)\n  // See # namespace for format detail\n  optional string namespace = 2;\n\n  // Textual description of the flag's purpose (required)\n  optional string description = 3;\n\n  // Single bug id related to the flag (required)\n  repeated string bug = 4;\n\n  // Indicates if the flag is permanently read-only and cannot be changed\n  // via release configs (optional)\n  // Default value false\n  optional bool is_fixed_read_only = 5;\n\n  // Indicates if the flag is exported and accessible beyond its originating container (optional)\n  // Default value false\n  optional bool is_exported = 6;\n\n  // Additional information about the flag, including its purpose and form factors (optional)\n  optional flag_metadata metadata = 7;\n};\n\n// Optional metadata about the flag, such as its purpose and its intended form factors.\n// Can influence the applied policies and testing strategy.\nmessage flag_metadata {\n  enum flag_purpose {\n    PURPOSE_UNSPECIFIED = 0;\n    PURPOSE_FEATURE = 1;\n    PURPOSE_BUGFIX = 2;\n  }\n\n  optional flag_purpose purpose = 1;\n\n  // TODO(b/315025930): Add field to designate intended target device form factor(s), such as phone, watch or other.\n}\n\nmessage flag_declarations {\n  // Package to which the flag belongs (required)\n  // See # package for format detail\n  optional string package = 1;\n\n  // List of flag_declaration objects (required)\n  repeated flag_declaration flag = 2;\n\n  // Container the flag belongs to (optional)\n  // See # container for format detail\n  optional string container = 3;\n};\n\nmessage flag_value {\n  // Package to which the flag belongs (required)\n  // See # package for format detail\n  optional string package = 1;\n\n  // Name of the flag (required)\n  // See # name for format detail\n  optional string name = 2;\n\n  optional flag_state state = 3;\n  optional flag_permission permission = 4;\n};\n\nmessage flag_values {\n  repeated flag_value flag_value = 1;\n};\n\n// aconfig output messages: parsed and verified flag declarations and values\n\nmessage tracepoint {\n  // path to declaration or value file relative to $TOP\n  optional string source = 1;\n  optional flag_state state = 2;\n  optional flag_permission permission = 3;\n}\n\nmessage parsed_flag {\n  // Package to which the flag belongs (required)\n  // See # package for format detail\n  optional string package = 1;\n\n  // Name of the flag (required)\n  // See # name for format detail\n  optional string name = 2;\n\n  // Namespace the flag belongs to (required)\n  // See # namespace for format detail\n  optional string namespace = 3;\n\n  // Textual description of the flag's purpose (required)\n  optional string description = 4;\n\n  // Single bug id related to the flag (required)\n  repeated string bug = 5;\n\n  optional flag_state state = 6;\n  optional flag_permission permission = 7;\n  repeated tracepoint trace = 8;\n\n  // Indicates if the flag is permanently read-only and cannot be changed\n  // via release configs (optional)\n  // Default value false\n  optional bool is_fixed_read_only = 9;\n\n  // Indicates if the flag is exported and accessible beyond its originating container (optional)\n  // Default value false\n  optional bool is_exported = 10;\n\n  // Container the flag belongs to (optional)\n  // See # container for format detail\n  optional string container = 11;\n\n  // Additional information about the flag, including its purpose and form factors (optional)\n  optional flag_metadata metadata = 12;\n}\n\nmessage parsed_flags {\n  repeated parsed_flag parsed_flag = 1;\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_protos/protos/aconfig_internal.proto",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License\n\n// This is the schema definition for protos intended for internal aconfig\n// use ONLY. There are no guarantees regarding backwards compatibility.\n// Do not put protos here intended for storage or communication.\n\nsyntax = \"proto2\";\n\npackage android.aconfig_internal;\n\n\n// This protobuf defines messages used to store data about flags used to guard\n// APIs which are finalized for a given SDK.\nmessage finalized_flag {\n  // Name of the flag (required). Does not include package name.\n  // Must match flag name in the aconfig declaration header.\n  optional string name = 1;\n\n  // Package the flag belongs to (required).  Must match package in the aconfig declaration header.\n  optional string package = 2;\n\n  // SDK level in which the flag was finalized.\n  optional int32 min_sdk = 3;\n\n  // TODO - b/378936061: Add support for minor SDK version & SDK extension.\n};\n\nmessage finalized_flags {\n  repeated finalized_flag finalized_flag = 1;\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_protos/src/lib.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! `aconfig_protos` is a crate for the protos defined for aconfig\n// When building with the Android tool-chain\n//\n//   - an external crate `aconfig_protos` will be generated\n//   - the feature \"cargo\" will be disabled\n//\n// When building with cargo\n//\n//   - a local sub-module will be generated in OUT_DIR and included in this file\n//   - the feature \"cargo\" will be enabled\n//\n// This module hides these differences from the rest of aconfig.\n\n// ---- When building with the Android tool-chain ----\n#[cfg(not(feature = \"cargo\"))]\nmod auto_generated {\n    pub use aconfig_rust_proto::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;\n    pub use aconfig_rust_proto::aconfig::Flag_declaration as ProtoFlagDeclaration;\n    pub use aconfig_rust_proto::aconfig::Flag_declarations as ProtoFlagDeclarations;\n    pub use aconfig_rust_proto::aconfig::Flag_metadata as ProtoFlagMetadata;\n    pub use aconfig_rust_proto::aconfig::Flag_permission as ProtoFlagPermission;\n    pub use aconfig_rust_proto::aconfig::Flag_state as ProtoFlagState;\n    pub use aconfig_rust_proto::aconfig::Flag_value as ProtoFlagValue;\n    pub use aconfig_rust_proto::aconfig::Flag_values as ProtoFlagValues;\n    pub use aconfig_rust_proto::aconfig::Parsed_flag as ProtoParsedFlag;\n    pub use aconfig_rust_proto::aconfig::Parsed_flags as ProtoParsedFlags;\n    pub use aconfig_rust_proto::aconfig::Tracepoint as ProtoTracepoint;\n}\n\n// ---- When building with cargo ----\n#[cfg(feature = \"cargo\")]\nmod auto_generated {\n    // include! statements should be avoided (because they import file contents verbatim), but\n    // because this is only used during local development, and only if using cargo instead of the\n    // Android tool-chain, we allow it\n    include!(concat!(env!(\"OUT_DIR\"), \"/aconfig_proto/mod.rs\"));\n    pub use aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;\n    pub use aconfig::Flag_declaration as ProtoFlagDeclaration;\n    pub use aconfig::Flag_declarations as ProtoFlagDeclarations;\n    pub use aconfig::Flag_metadata as ProtoFlagMetadata;\n    pub use aconfig::Flag_permission as ProtoFlagPermission;\n    pub use aconfig::Flag_state as ProtoFlagState;\n    pub use aconfig::Flag_value as ProtoFlagValue;\n    pub use aconfig::Flag_values as ProtoFlagValues;\n    pub use aconfig::Parsed_flag as ProtoParsedFlag;\n    pub use aconfig::Parsed_flags as ProtoParsedFlags;\n    pub use aconfig::Tracepoint as ProtoTracepoint;\n}\n\n// ---- Common for both the Android tool-chain and cargo ----\npub use auto_generated::*;\n\nuse anyhow::Result;\nuse paste::paste;\n\n/// Path to proto file\nconst ACONFIG_PROTO_PATH: &str = \"//build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto\";\n\n/// Check if the name identifier is valid\npub fn is_valid_name_ident(s: &str) -> bool {\n    // Identifiers must match [a-z][a-z0-9_]*, except consecutive underscores are not allowed\n    if s.contains(\"__\") {\n        return false;\n    }\n    let mut chars = s.chars();\n    let Some(first) = chars.next() else {\n        return false;\n    };\n    if !first.is_ascii_lowercase() {\n        return false;\n    }\n    chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')\n}\n\n/// Check if the package identifier is valid\npub fn is_valid_package_ident(s: &str) -> bool {\n    if !s.contains('.') {\n        return false;\n    }\n    s.split('.').all(is_valid_name_ident)\n}\n\n/// Check if the container identifier is valid\npub fn is_valid_container_ident(s: &str) -> bool {\n    s.split('.').all(is_valid_name_ident)\n}\n\nfn try_from_text_proto<T>(s: &str) -> Result<T>\nwhere\n    T: protobuf::MessageFull,\n{\n    protobuf::text_format::parse_from_str(s).map_err(|e| e.into())\n}\n\nmacro_rules! ensure_required_fields {\n    ($type:expr, $struct:expr, $($field:expr),+) => {\n        $(\n        paste! {\n            ensure!($struct.[<has_ $field>](), \"bad {}: missing {}\", $type, $field);\n        }\n        )+\n    };\n}\n\n/// Utility module for flag_declaration proto\npub mod flag_declaration {\n    use super::*;\n    use anyhow::ensure;\n\n    /// Ensure the proto instance is valid by checking its fields\n    pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {\n        ensure_required_fields!(\"flag declaration\", pdf, \"name\", \"namespace\", \"description\");\n\n        ensure!(\n            is_valid_name_ident(pdf.name()),\n            \"bad flag declaration: bad name {} expected snake_case string; \\\n        see {ACONFIG_PROTO_PATH} for details\",\n            pdf.name()\n        );\n        ensure!(\n            is_valid_name_ident(pdf.namespace()),\n            \"bad flag declaration: bad namespace {} expected snake_case string; \\\n        see {ACONFIG_PROTO_PATH} for details\",\n            pdf.namespace()\n        );\n        ensure!(!pdf.description().is_empty(), \"bad flag declaration: empty description\");\n        ensure!(pdf.bug.len() == 1, \"bad flag declaration: exactly one bug required\");\n\n        Ok(())\n    }\n}\n\n/// Utility module for flag_declarations proto\npub mod flag_declarations {\n    use super::*;\n    use anyhow::ensure;\n\n    /// Construct a proto instance from a textproto string content\n    pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {\n        let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;\n        verify_fields(&pdf)?;\n        Ok(pdf)\n    }\n\n    /// Ensure the proto instance is valid by checking its fields\n    pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {\n        ensure_required_fields!(\"flag declarations\", pdf, \"package\");\n        // TODO(b/312769710): Make the container field required.\n        ensure!(\n            is_valid_package_ident(pdf.package()),\n            \"bad flag declarations: bad package {} expected snake_case strings delimited by dots; \\\n        see {ACONFIG_PROTO_PATH} for details\",\n            pdf.package()\n        );\n        ensure!(\n            !pdf.has_container() || is_valid_container_ident(pdf.container()),\n            \"bad flag declarations: bad container\"\n        );\n        for flag_declaration in pdf.flag.iter() {\n            super::flag_declaration::verify_fields(flag_declaration)?;\n        }\n\n        Ok(())\n    }\n}\n\n/// Utility module for flag_value proto\npub mod flag_value {\n    use super::*;\n    use anyhow::ensure;\n\n    /// Ensure the proto instance is valid by checking its fields\n    pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {\n        ensure_required_fields!(\"flag value\", fv, \"package\", \"name\", \"state\", \"permission\");\n\n        ensure!(\n            is_valid_package_ident(fv.package()),\n            \"bad flag value: bad package {} expected snake_case strings delimited by dots; \\\n        see {ACONFIG_PROTO_PATH} for details\",\n            fv.package()\n        );\n        ensure!(\n            is_valid_name_ident(fv.name()),\n            \"bad flag value: bad name {} expected snake_case string; \\\n        see {ACONFIG_PROTO_PATH} for details\",\n            fv.name()\n        );\n\n        Ok(())\n    }\n}\n\n/// Utility module for flag_values proto\npub mod flag_values {\n    use super::*;\n\n    /// Construct a proto instance from a textproto string content\n    pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {\n        let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;\n        verify_fields(&pfv)?;\n        Ok(pfv)\n    }\n\n    /// Ensure the proto instance is valid by checking its fields\n    pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {\n        for flag_value in pfv.flag_value.iter() {\n            super::flag_value::verify_fields(flag_value)?;\n        }\n        Ok(())\n    }\n}\n\n/// Utility module for flag_permission proto enum\npub mod flag_permission {\n    use super::*;\n    use anyhow::bail;\n\n    /// Construct a flag permission proto enum from string\n    pub fn parse_from_str(permission: &str) -> Result<ProtoFlagPermission> {\n        match permission.to_ascii_lowercase().as_str() {\n            \"read_write\" => Ok(ProtoFlagPermission::READ_WRITE),\n            \"read_only\" => Ok(ProtoFlagPermission::READ_ONLY),\n            _ => bail!(\"Permission needs to be read_only or read_write.\"),\n        }\n    }\n\n    /// Serialize flag permission proto enum to string\n    pub fn to_string(permission: &ProtoFlagPermission) -> &str {\n        match permission {\n            ProtoFlagPermission::READ_WRITE => \"read_write\",\n            ProtoFlagPermission::READ_ONLY => \"read_only\",\n        }\n    }\n}\n\n/// Utility module for tracepoint proto\npub mod tracepoint {\n    use super::*;\n    use anyhow::ensure;\n\n    /// Ensure the proto instance is valid by checking its fields\n    pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {\n        ensure_required_fields!(\"tracepoint\", tp, \"source\", \"state\", \"permission\");\n\n        ensure!(!tp.source().is_empty(), \"bad tracepoint: empty source\");\n\n        Ok(())\n    }\n}\n\n/// Utility module for parsed_flag proto\npub mod parsed_flag {\n    use super::*;\n    use anyhow::ensure;\n\n    /// Ensure the proto instance is valid by checking its fields\n    pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {\n        ensure_required_fields!(\n            \"parsed flag\",\n            pf,\n            \"package\",\n            \"name\",\n            \"namespace\",\n            \"description\",\n            \"state\",\n            \"permission\"\n        );\n\n        ensure!(\n            is_valid_package_ident(pf.package()),\n            \"bad parsed flag: bad package {} expected snake_case strings delimited by dots; \\\n        see {ACONFIG_PROTO_PATH} for details\",\n            pf.package()\n        );\n        ensure!(\n            !pf.has_container() || is_valid_container_ident(pf.container()),\n            \"bad parsed flag: bad container\"\n        );\n        ensure!(\n            is_valid_name_ident(pf.name()),\n            \"bad parsed flag: bad name {} expected snake_case string; \\\n        see {ACONFIG_PROTO_PATH} for details\",\n            pf.name()\n        );\n        ensure!(\n            is_valid_name_ident(pf.namespace()),\n            \"bad parsed flag: bad namespace {} expected snake_case string; \\\n        see {ACONFIG_PROTO_PATH} for details\",\n            pf.namespace()\n        );\n        ensure!(!pf.description().is_empty(), \"bad parsed flag: empty description\");\n        ensure!(!pf.trace.is_empty(), \"bad parsed flag: empty trace\");\n        for tp in pf.trace.iter() {\n            super::tracepoint::verify_fields(tp)?;\n        }\n        ensure!(pf.bug.len() == 1, \"bad flag declaration: exactly one bug required\");\n        if pf.is_fixed_read_only() {\n            ensure!(\n                pf.permission() == ProtoFlagPermission::READ_ONLY,\n                \"bad parsed flag: flag is is_fixed_read_only but permission is not READ_ONLY\"\n            );\n            for tp in pf.trace.iter() {\n                ensure!(tp.permission() == ProtoFlagPermission::READ_ONLY,\n                \"bad parsed flag: flag is is_fixed_read_only but a tracepoint's permission is not READ_ONLY\"\n                );\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Get the file path of the corresponding flag declaration\n    pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {\n        debug_assert!(!pf.trace.is_empty());\n        pf.trace[0].source()\n    }\n}\n\n/// Utility module for parsed_flags proto\npub mod parsed_flags {\n    use super::*;\n    use anyhow::bail;\n    use std::cmp::Ordering;\n\n    /// Construct a proto instance from a binary proto bytes\n    pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {\n        let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;\n        verify_fields(&message)?;\n        Ok(message)\n    }\n\n    /// Ensure the proto instance is valid by checking its fields\n    pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {\n        use crate::parsed_flag::path_to_declaration;\n\n        let mut previous: Option<&ProtoParsedFlag> = None;\n        for parsed_flag in pf.parsed_flag.iter() {\n            if let Some(prev) = previous {\n                let a = create_sorting_key(prev);\n                let b = create_sorting_key(parsed_flag);\n                match a.cmp(&b) {\n                    Ordering::Less => {}\n                    Ordering::Equal => bail!(\n                        \"bad parsed flags: duplicate flag {} (defined in {} and {})\",\n                        a,\n                        path_to_declaration(prev),\n                        path_to_declaration(parsed_flag)\n                    ),\n                    Ordering::Greater => {\n                        bail!(\"bad parsed flags: not sorted: {} comes before {}\", a, b)\n                    }\n                }\n            }\n            super::parsed_flag::verify_fields(parsed_flag)?;\n            previous = Some(parsed_flag);\n        }\n        Ok(())\n    }\n\n    /// Merge multipe parsed_flags proto\n    pub fn merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags> {\n        let mut merged = ProtoParsedFlags::new();\n        for mut pfs in parsed_flags.into_iter() {\n            merged.parsed_flag.append(&mut pfs.parsed_flag);\n        }\n        merged.parsed_flag.sort_by_cached_key(create_sorting_key);\n        if dedup {\n            // Deduplicate identical protobuf messages.  Messages with the same sorting key but\n            // different fields (including the path to the original source file) will not be\n            // deduplicated and trigger an error in verify_fields.\n            merged.parsed_flag.dedup();\n        }\n        verify_fields(&merged)?;\n        Ok(merged)\n    }\n\n    /// Sort parsed flags\n    pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {\n        pf.parsed_flag.sort_by_key(create_sorting_key);\n    }\n\n    fn create_sorting_key(pf: &ProtoParsedFlag) -> String {\n        pf.fully_qualified_name()\n    }\n}\n\n/// ParsedFlagExt trait\npub trait ParsedFlagExt {\n    /// Return the fully qualified name\n    fn fully_qualified_name(&self) -> String;\n}\n\nimpl ParsedFlagExt for ProtoParsedFlag {\n    fn fully_qualified_name(&self) -> String {\n        format!(\"{}.{}\", self.package(), self.name())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_flag_declarations_try_from_text_proto() {\n        // valid input\n        let flag_declarations = flag_declarations::try_from_text_proto(\n            r#\"\npackage: \"com.foo.bar\"\ncontainer: \"system\"\nflag {\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"123\"\n    is_exported: true\n}\nflag {\n    name: \"second\"\n    namespace: \"second_ns\"\n    description: \"This is the description of the second flag.\"\n    bug: \"abc\"\n    is_fixed_read_only: true\n}\n\"#,\n        )\n        .unwrap();\n        assert_eq!(flag_declarations.package(), \"com.foo.bar\");\n        assert_eq!(flag_declarations.container(), \"system\");\n        let first = flag_declarations.flag.iter().find(|pf| pf.name() == \"first\").unwrap();\n        assert_eq!(first.name(), \"first\");\n        assert_eq!(first.namespace(), \"first_ns\");\n        assert_eq!(first.description(), \"This is the description of the first flag.\");\n        assert_eq!(first.bug, vec![\"123\"]);\n        assert!(!first.is_fixed_read_only());\n        assert!(first.is_exported());\n        let second = flag_declarations.flag.iter().find(|pf| pf.name() == \"second\").unwrap();\n        assert_eq!(second.name(), \"second\");\n        assert_eq!(second.namespace(), \"second_ns\");\n        assert_eq!(second.description(), \"This is the description of the second flag.\");\n        assert_eq!(second.bug, vec![\"abc\"]);\n        assert!(second.is_fixed_read_only());\n        assert!(!second.is_exported());\n\n        // valid input: missing container in flag declarations is supported\n        let flag_declarations = flag_declarations::try_from_text_proto(\n            r#\"\npackage: \"com.foo.bar\"\nflag {\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"123\"\n}\n\"#,\n        )\n        .unwrap();\n        assert_eq!(flag_declarations.container(), \"\");\n        assert!(!flag_declarations.has_container());\n\n        // bad input: missing package in flag declarations\n        let error = flag_declarations::try_from_text_proto(\n            r#\"\ncontainer: \"system\"\nflag {\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n}\nflag {\n    name: \"second\"\n    namespace: \"second_ns\"\n    description: \"This is the description of the second flag.\"\n}\n\"#,\n        )\n        .unwrap_err();\n        assert_eq!(format!(\"{:?}\", error), \"bad flag declarations: missing package\");\n\n        // bad input: missing namespace in flag declaration\n        let error = flag_declarations::try_from_text_proto(\n            r#\"\npackage: \"com.foo.bar\"\ncontainer: \"system\"\nflag {\n    name: \"first\"\n    description: \"This is the description of the first flag.\"\n}\nflag {\n    name: \"second\"\n    namespace: \"second_ns\"\n    description: \"This is the description of the second flag.\"\n}\n\"#,\n        )\n        .unwrap_err();\n        assert_eq!(format!(\"{:?}\", error), \"bad flag declaration: missing namespace\");\n\n        // bad input: bad package name in flag declarations\n        let error = flag_declarations::try_from_text_proto(\n            r#\"\npackage: \"_com.FOO__BAR\"\ncontainer: \"system\"\nflag {\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n}\nflag {\n    name: \"second\"\n    namespace: \"second_ns\"\n    description: \"This is the description of the second flag.\"\n}\n\"#,\n        )\n        .unwrap_err();\n        assert!(format!(\"{:?}\", error).contains(\"bad flag declarations: bad package\"));\n\n        // bad input: bad name in flag declaration\n        let error = flag_declarations::try_from_text_proto(\n            r#\"\npackage: \"com.foo.bar\"\ncontainer: \"system\"\nflag {\n    name: \"FIRST\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n}\nflag {\n    name: \"second\"\n    namespace: \"second_ns\"\n    description: \"This is the description of the second flag.\"\n}\n\"#,\n        )\n        .unwrap_err();\n        assert!(format!(\"{:?}\", error).contains(\"bad flag declaration: bad name\"));\n\n        // bad input: no bug entries in flag declaration\n        let error = flag_declarations::try_from_text_proto(\n            r#\"\npackage: \"com.foo.bar\"\ncontainer: \"system\"\nflag {\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n}\n\"#,\n        )\n        .unwrap_err();\n        assert!(format!(\"{:?}\", error).contains(\"bad flag declaration: exactly one bug required\"));\n\n        // bad input: multiple bug entries in flag declaration\n        let error = flag_declarations::try_from_text_proto(\n            r#\"\npackage: \"com.foo.bar\"\ncontainer: \"system\"\nflag {\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"123\"\n    bug: \"abc\"\n}\n\"#,\n        )\n        .unwrap_err();\n        assert!(format!(\"{:?}\", error).contains(\"bad flag declaration: exactly one bug required\"));\n\n        // bad input: invalid container name in flag declaration\n        let error = flag_declarations::try_from_text_proto(\n            r#\"\npackage: \"com.foo.bar\"\ncontainer: \"__bad_bad_container.com\"\nflag {\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"123\"\n    bug: \"abc\"\n}\n\"#,\n        )\n        .unwrap_err();\n        assert!(format!(\"{:?}\", error).contains(\"bad flag declarations: bad container\"));\n\n        // TODO(b/312769710): Verify error when container is missing.\n    }\n\n    #[test]\n    fn test_flag_values_try_from_text_proto() {\n        // valid input\n        let flag_values = flag_values::try_from_text_proto(\n            r#\"\nflag_value {\n    package: \"com.first\"\n    name: \"first\"\n    state: DISABLED\n    permission: READ_ONLY\n}\nflag_value {\n    package: \"com.second\"\n    name: \"second\"\n    state: ENABLED\n    permission: READ_WRITE\n}\n\"#,\n        )\n        .unwrap();\n        let first = flag_values.flag_value.iter().find(|fv| fv.name() == \"first\").unwrap();\n        assert_eq!(first.package(), \"com.first\");\n        assert_eq!(first.name(), \"first\");\n        assert_eq!(first.state(), ProtoFlagState::DISABLED);\n        assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);\n        let second = flag_values.flag_value.iter().find(|fv| fv.name() == \"second\").unwrap();\n        assert_eq!(second.package(), \"com.second\");\n        assert_eq!(second.name(), \"second\");\n        assert_eq!(second.state(), ProtoFlagState::ENABLED);\n        assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);\n\n        // bad input: bad package in flag value\n        let error = flag_values::try_from_text_proto(\n            r#\"\nflag_value {\n    package: \"COM.FIRST\"\n    name: \"first\"\n    state: DISABLED\n    permission: READ_ONLY\n}\n\"#,\n        )\n        .unwrap_err();\n        assert!(format!(\"{:?}\", error).contains(\"bad flag value: bad package\"));\n\n        // bad input: bad name in flag value\n        let error = flag_values::try_from_text_proto(\n            r#\"\nflag_value {\n    package: \"com.first\"\n    name: \"FIRST\"\n    state: DISABLED\n    permission: READ_ONLY\n}\n\"#,\n        )\n        .unwrap_err();\n        assert!(format!(\"{:?}\", error).contains(\"bad flag value: bad name\"));\n\n        // bad input: missing state in flag value\n        let error = flag_values::try_from_text_proto(\n            r#\"\nflag_value {\n    package: \"com.first\"\n    name: \"first\"\n    permission: READ_ONLY\n}\n\"#,\n        )\n        .unwrap_err();\n        assert_eq!(format!(\"{:?}\", error), \"bad flag value: missing state\");\n\n        // bad input: missing permission in flag value\n        let error = flag_values::try_from_text_proto(\n            r#\"\nflag_value {\n    package: \"com.first\"\n    name: \"first\"\n    state: DISABLED\n}\n\"#,\n        )\n        .unwrap_err();\n        assert_eq!(format!(\"{:?}\", error), \"bad flag value: missing permission\");\n    }\n\n    fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {\n        use protobuf::Message;\n\n        let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;\n        let mut binary_proto = Vec::new();\n        parsed_flags.write_to_vec(&mut binary_proto)?;\n        parsed_flags::try_from_binary_proto(&binary_proto)\n    }\n\n    #[test]\n    fn test_parsed_flags_try_from_text_proto() {\n        // valid input\n        let text_proto = r#\"\nparsed_flag {\n    package: \"com.first\"\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"SOME_BUG\"\n    state: DISABLED\n    permission: READ_ONLY\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\nparsed_flag {\n    package: \"com.second\"\n    name: \"second\"\n    namespace: \"second_ns\"\n    description: \"This is the description of the second flag.\"\n    bug: \"SOME_BUG\"\n    state: ENABLED\n    permission: READ_ONLY\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    trace {\n        source: \"flags.values\"\n        state: ENABLED\n        permission: READ_ONLY\n    }\n    is_fixed_read_only: true\n    container: \"system\"\n}\n\"#;\n        let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();\n        assert_eq!(parsed_flags.parsed_flag.len(), 2);\n        let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == \"second\").unwrap();\n        assert_eq!(second.package(), \"com.second\");\n        assert_eq!(second.name(), \"second\");\n        assert_eq!(second.namespace(), \"second_ns\");\n        assert_eq!(second.description(), \"This is the description of the second flag.\");\n        assert_eq!(second.bug, vec![\"SOME_BUG\"]);\n        assert_eq!(second.state(), ProtoFlagState::ENABLED);\n        assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY);\n        assert_eq!(2, second.trace.len());\n        assert_eq!(second.trace[0].source(), \"flags.declarations\");\n        assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);\n        assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);\n        assert_eq!(second.trace[1].source(), \"flags.values\");\n        assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);\n        assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY);\n        assert!(second.is_fixed_read_only());\n\n        // valid input: empty\n        let parsed_flags = try_from_binary_proto_from_text_proto(\"\").unwrap();\n        assert!(parsed_flags.parsed_flag.is_empty());\n\n        // bad input: empty trace\n        let text_proto = r#\"\nparsed_flag {\n    package: \"com.first\"\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    state: DISABLED\n    permission: READ_ONLY\n    container: \"system\"\n}\n\"#;\n        let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();\n        assert_eq!(format!(\"{:?}\", error), \"bad parsed flag: empty trace\");\n\n        // bad input: missing namespace in parsed_flag\n        let text_proto = r#\"\nparsed_flag {\n    package: \"com.first\"\n    name: \"first\"\n    description: \"This is the description of the first flag.\"\n    state: DISABLED\n    permission: READ_ONLY\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\n\"#;\n        let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();\n        assert_eq!(format!(\"{:?}\", error), \"bad parsed flag: missing namespace\");\n\n        // bad input: parsed_flag not sorted by package\n        let text_proto = r#\"\nparsed_flag {\n    package: \"bbb.bbb\"\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"\"\n    state: DISABLED\n    permission: READ_ONLY\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\nparsed_flag {\n    package: \"aaa.aaa\"\n    name: \"second\"\n    namespace: \"second_ns\"\n    description: \"This is the description of the second flag.\"\n    bug: \"\"\n    state: ENABLED\n    permission: READ_WRITE\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\n\"#;\n        let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            \"bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second\"\n        );\n\n        // bad input: parsed_flag not sorted by name\n        let text_proto = r#\"\nparsed_flag {\n    package: \"com.foo\"\n    name: \"bbb\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"\"\n    state: DISABLED\n    permission: READ_ONLY\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\nparsed_flag {\n    package: \"com.foo\"\n    name: \"aaa\"\n    namespace: \"second_ns\"\n    description: \"This is the description of the second flag.\"\n    bug: \"\"\n    state: ENABLED\n    permission: READ_WRITE\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\n\"#;\n        let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            \"bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa\"\n        );\n\n        // bad input: duplicate flags\n        let text_proto = r#\"\nparsed_flag {\n    package: \"com.foo\"\n    name: \"bar\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"\"\n    state: DISABLED\n    permission: READ_ONLY\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\nparsed_flag {\n    package: \"com.foo\"\n    name: \"bar\"\n    namespace: \"second_ns\"\n    description: \"This is the description of the second flag.\"\n    bug: \"\"\n    state: ENABLED\n    permission: READ_WRITE\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\n\"#;\n        let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();\n        assert_eq!(format!(\"{:?}\", error), \"bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)\");\n    }\n\n    #[test]\n    fn test_parsed_flag_path_to_declaration() {\n        let text_proto = r#\"\nparsed_flag {\n    package: \"com.foo\"\n    name: \"bar\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"b/12345678\"\n    state: DISABLED\n    permission: READ_ONLY\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    trace {\n        source: \"flags.values\"\n        state: ENABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\n\"#;\n        let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();\n        let parsed_flag = &parsed_flags.parsed_flag[0];\n        assert_eq!(crate::parsed_flag::path_to_declaration(parsed_flag), \"flags.declarations\");\n    }\n\n    #[test]\n    fn test_parsed_flags_merge() {\n        let text_proto = r#\"\nparsed_flag {\n    package: \"com.first\"\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"a\"\n    state: DISABLED\n    permission: READ_ONLY\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\nparsed_flag {\n    package: \"com.second\"\n    name: \"second\"\n    namespace: \"second_ns\"\n    description: \"This is the description of the second flag.\"\n    bug: \"b\"\n    state: ENABLED\n    permission: READ_WRITE\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\n\"#;\n        let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();\n\n        let text_proto = r#\"\nparsed_flag {\n    package: \"com.first\"\n    name: \"first\"\n    namespace: \"first_ns\"\n    description: \"This is the description of the first flag.\"\n    bug: \"a\"\n    state: DISABLED\n    permission: READ_ONLY\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\n\"#;\n        let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();\n\n        let text_proto = r#\"\nparsed_flag {\n    package: \"com.second\"\n    name: \"second\"\n    namespace: \"second_ns\"\n    bug: \"b\"\n    description: \"This is the description of the second flag.\"\n    state: ENABLED\n    permission: READ_WRITE\n    trace {\n        source: \"flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n    container: \"system\"\n}\n\"#;\n        let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();\n\n        let text_proto = r#\"\nparsed_flag {\n    package: \"com.second\"\n    name: \"second\"\n    namespace: \"second_ns\"\n    bug: \"b\"\n    description: \"This is the description of the second flag.\"\n    state: ENABLED\n    permission: READ_WRITE\n    trace {\n        source: \"duplicate/flags.declarations\"\n        state: DISABLED\n        permission: READ_ONLY\n    }\n}\n\"#;\n        let second_duplicate = try_from_binary_proto_from_text_proto(text_proto).unwrap();\n\n        // bad cases\n\n        // two of the same flag with dedup disabled\n        let error = parsed_flags::merge(vec![first.clone(), first.clone()], false).unwrap_err();\n        assert_eq!(format!(\"{:?}\", error), \"bad parsed flags: duplicate flag com.first.first (defined in flags.declarations and flags.declarations)\");\n\n        // two conflicting flags with dedup disabled\n        let error =\n            parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], false).unwrap_err();\n        assert_eq!(format!(\"{:?}\", error), \"bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)\");\n\n        // two conflicting flags with dedup enabled\n        let error =\n            parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], true).unwrap_err();\n        assert_eq!(format!(\"{:?}\", error), \"bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)\");\n\n        // valid cases\n        assert!(parsed_flags::merge(vec![], false).unwrap().parsed_flag.is_empty());\n        assert!(parsed_flags::merge(vec![], true).unwrap().parsed_flag.is_empty());\n        assert_eq!(first, parsed_flags::merge(vec![first.clone()], false).unwrap());\n        assert_eq!(first, parsed_flags::merge(vec![first.clone()], true).unwrap());\n        assert_eq!(\n            expected,\n            parsed_flags::merge(vec![first.clone(), second.clone()], false).unwrap()\n        );\n        assert_eq!(\n            expected,\n            parsed_flags::merge(vec![first.clone(), second.clone()], true).unwrap()\n        );\n        assert_eq!(\n            expected,\n            parsed_flags::merge(vec![second.clone(), first.clone()], false).unwrap()\n        );\n        assert_eq!(\n            expected,\n            parsed_flags::merge(vec![second.clone(), first.clone()], true).unwrap()\n        );\n\n        // two identical flags with dedup enabled\n        assert_eq!(first, parsed_flags::merge(vec![first.clone(), first.clone()], true).unwrap());\n    }\n\n    #[test]\n    fn test_is_valid_name_ident() {\n        assert!(is_valid_name_ident(\"foo\"));\n        assert!(is_valid_name_ident(\"foo_bar_123\"));\n        assert!(is_valid_name_ident(\"foo_\"));\n\n        assert!(!is_valid_name_ident(\"\"));\n        assert!(!is_valid_name_ident(\"123_foo\"));\n        assert!(!is_valid_name_ident(\"foo-bar\"));\n        assert!(!is_valid_name_ident(\"foo-b\\u{00e5}r\"));\n        assert!(!is_valid_name_ident(\"foo__bar\"));\n        assert!(!is_valid_name_ident(\"_foo\"));\n    }\n\n    #[test]\n    fn test_is_valid_package_ident() {\n        assert!(is_valid_package_ident(\"foo.bar\"));\n        assert!(is_valid_package_ident(\"foo.bar_baz\"));\n        assert!(is_valid_package_ident(\"foo.bar.a123\"));\n\n        assert!(!is_valid_package_ident(\"foo_bar_123\"));\n        assert!(!is_valid_package_ident(\"foo\"));\n        assert!(!is_valid_package_ident(\"foo._bar\"));\n        assert!(!is_valid_package_ident(\"\"));\n        assert!(!is_valid_package_ident(\"123_foo\"));\n        assert!(!is_valid_package_ident(\"foo-bar\"));\n        assert!(!is_valid_package_ident(\"foo-b\\u{00e5}r\"));\n        assert!(!is_valid_package_ident(\"foo.bar.123\"));\n        assert!(!is_valid_package_ident(\".foo.bar\"));\n        assert!(!is_valid_package_ident(\"foo.bar.\"));\n        assert!(!is_valid_package_ident(\".\"));\n        assert!(!is_valid_package_ident(\"..\"));\n        assert!(!is_valid_package_ident(\"foo..bar\"));\n        assert!(!is_valid_package_ident(\"foo.__bar\"));\n    }\n\n    #[test]\n    fn test_is_valid_container_ident() {\n        assert!(is_valid_container_ident(\"foo.bar\"));\n        assert!(is_valid_container_ident(\"foo.bar_baz\"));\n        assert!(is_valid_container_ident(\"foo.bar.a123\"));\n        assert!(is_valid_container_ident(\"foo\"));\n        assert!(is_valid_container_ident(\"foo_bar_123\"));\n\n        assert!(!is_valid_container_ident(\"\"));\n        assert!(!is_valid_container_ident(\"foo._bar\"));\n        assert!(!is_valid_container_ident(\"_foo\"));\n        assert!(!is_valid_container_ident(\"123_foo\"));\n        assert!(!is_valid_container_ident(\"foo-bar\"));\n        assert!(!is_valid_container_ident(\"foo-b\\u{00e5}r\"));\n        assert!(!is_valid_container_ident(\"foo.bar.123\"));\n        assert!(!is_valid_container_ident(\".foo.bar\"));\n        assert!(!is_valid_container_ident(\"foo.bar.\"));\n        assert!(!is_valid_container_ident(\".\"));\n        assert!(!is_valid_container_ident(\"..\"));\n        assert!(!is_valid_container_ident(\"foo..bar\"));\n        assert!(!is_valid_container_ident(\"foo.__bar\"));\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_defaults {\n    name: \"aconfig_storage_file.defaults\",\n    edition: \"2021\",\n    lints: \"none\",\n    rustlibs: [\n        \"libanyhow\",\n        \"libthiserror\",\n        \"libtempfile\",\n        \"libprotobuf\",\n        \"libclap\",\n        \"libcxx\",\n        \"libaconfig_storage_protos\",\n        \"libserde\",\n    ],\n}\n\nrust_library {\n    name: \"libaconfig_storage_file\",\n    crate_name: \"aconfig_storage_file\",\n    host_supported: true,\n    defaults: [\"aconfig_storage_file.defaults\"],\n    srcs: [\"src/lib.rs\"],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"29\",\n    vendor_available: true,\n    product_available: true,\n}\n\nrust_binary_host {\n    name: \"aconfig-storage\",\n    defaults: [\"aconfig_storage_file.defaults\"],\n    srcs: [\"src/main.rs\"],\n    rustlibs: [\n        \"libaconfig_storage_file\",\n        \"libserde_json\",\n    ],\n}\n\nrust_test_host {\n    name: \"aconfig_storage_file.test\",\n    test_suites: [\"general-tests\"],\n    defaults: [\"aconfig_storage_file.defaults\"],\n    srcs: [\"src/lib.rs\"],\n}\n\nrust_protobuf {\n    name: \"libaconfig_storage_protos\",\n    protos: [\"protos/aconfig_storage_metadata.proto\"],\n    crate_name: \"aconfig_storage_protos\",\n    source_stem: \"aconfig_storage_protos\",\n    host_supported: true,\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"29\",\n    vendor_available: true,\n    product_available: true,\n}\n\ncc_library {\n    name: \"libaconfig_storage_protos_cc\",\n    proto: {\n        export_proto_headers: true,\n        type: \"lite\",\n    },\n    srcs: [\"protos/aconfig_storage_metadata.proto\"],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    host_supported: true,\n    min_sdk_version: \"29\",\n    vendor_available: true,\n    product_available: true,\n    double_loadable: true,\n}\n\n// cxx source codegen from rust api\ngenrule {\n    name: \"libcxx_aconfig_storage_file_bridge_code\",\n    tools: [\"cxxbridge\"],\n    cmd: \"$(location cxxbridge) $(in) > $(out)\",\n    srcs: [\"src/lib.rs\"],\n    out: [\"aconfig_storage/lib.rs.cc\"],\n}\n\n// cxx header codegen from rust api\ngenrule {\n    name: \"libcxx_aconfig_storage_file_bridge_header\",\n    tools: [\"cxxbridge\"],\n    cmd: \"$(location cxxbridge) $(in) --header > $(out)\",\n    srcs: [\"src/lib.rs\"],\n    out: [\"aconfig_storage/lib.rs.h\"],\n}\n\n// a static cc lib based on generated code\nrust_ffi_static {\n    name: \"libaconfig_storage_file_cxx_bridge\",\n    crate_name: \"aconfig_storage_file_cxx_bridge\",\n    host_supported: true,\n    vendor_available: true,\n    product_available: true,\n    srcs: [\"src/lib.rs\"],\n    defaults: [\"aconfig_storage_file.defaults\"],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"29\",\n}\n\n// storage file parse api cc interface\ncc_library {\n    name: \"libaconfig_storage_file_cc\",\n    srcs: [\"aconfig_storage_file.cpp\"],\n    generated_headers: [\n        \"cxx-bridge-header\",\n        \"libcxx_aconfig_storage_file_bridge_header\",\n    ],\n    generated_sources: [\"libcxx_aconfig_storage_file_bridge_code\"],\n    whole_static_libs: [\"libaconfig_storage_file_cxx_bridge\"],\n    export_include_dirs: [\"include\"],\n    host_supported: true,\n    vendor_available: true,\n    product_available: true,\n    shared_libs: [\n        \"libbase\",\n    ],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"29\",\n    double_loadable: true,\n}\n\n// storage file parse api java library\njava_library {\n    name: \"aconfig_storage_file_java\",\n    srcs: [\n        \"srcs/**/*.java\",\n    ],\n    sdk_version: \"core_current\",\n    min_sdk_version: \"29\",\n    host_supported: true,\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n}\n\n// storage file parse api java library for core library\njava_library {\n    name: \"aconfig_storage_file_java_none\",\n    srcs: [\n        \"srcs/**/*.java\",\n    ],\n    sdk_version: \"none\",\n    system_modules: \"core-all-system-modules\",\n    host_supported: true,\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/Cargo.toml",
    "content": "[package]\nname = \"aconfig_storage_file\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[features]\ndefault = [\"cargo\"]\ncargo = []\n\n[dependencies]\nanyhow = \"1.0.69\"\nprotobuf = \"3.2.0\"\ntempfile = \"3.9.0\"\nthiserror = \"1.0.56\"\nclap = { version = \"4.1.8\", features = [\"derive\"] }\ncxx = \"1.0\"\nserde = { version = \"1.0.152\", features = [\"derive\"] }\nserde_json = \"1.0.93\"\n\n[[bin]]\nname = \"aconfig-storage\"\npath = \"src/main.rs\"\n\n[build-dependencies]\nprotobuf-codegen = \"3.2.0\"\ncxx-build = \"1.0\"\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp",
    "content": "#include \"rust/cxx.h\"\n#include \"aconfig_storage/lib.rs.h\"\n\n#include \"aconfig_storage/aconfig_storage_file.hpp\"\n\nusing namespace android::base;\n\nnamespace aconfig_storage {\n\nResult<std::vector<FlagValueSummary>> list_flags(\n    const std::string& package_map,\n    const std::string& flag_map,\n    const std::string& flag_val) {\n  auto flag_list_cxx = list_flags_cxx(rust::Str(package_map.c_str()),\n                                      rust::Str(flag_map.c_str()),\n                                      rust::Str(flag_val.c_str()));\n  if (flag_list_cxx.query_success) {\n    auto flag_list = std::vector<FlagValueSummary>();\n    for (const auto& flag_cxx : flag_list_cxx.flags) {\n      auto flag = FlagValueSummary();\n      flag.package_name = std::string(flag_cxx.package_name);\n      flag.flag_name = std::string(flag_cxx.flag_name);\n      flag.flag_value = std::string(flag_cxx.flag_value);\n      flag.value_type = std::string(flag_cxx.value_type);\n      flag_list.push_back(flag);\n    }\n    return flag_list;\n  } else {\n    return Error() << flag_list_cxx.error_message;\n  }\n}\n\nResult<std::vector<FlagValueAndInfoSummary>> list_flags_with_info(\n    const std::string& package_map,\n    const std::string& flag_map,\n    const std::string& flag_val,\n    const std::string& flag_info) {\n  auto flag_list_cxx = list_flags_with_info_cxx(rust::Str(package_map.c_str()),\n                                                rust::Str(flag_map.c_str()),\n                                                rust::Str(flag_val.c_str()),\n                                                rust::Str(flag_info.c_str()));\n  if (flag_list_cxx.query_success) {\n    auto flag_list = std::vector<FlagValueAndInfoSummary>();\n    for (const auto& flag_cxx : flag_list_cxx.flags) {\n      auto flag = FlagValueAndInfoSummary();\n      flag.package_name = std::string(flag_cxx.package_name);\n      flag.flag_name = std::string(flag_cxx.flag_name);\n      flag.flag_value = std::string(flag_cxx.flag_value);\n      flag.value_type = std::string(flag_cxx.value_type);\n      flag.is_readwrite = flag_cxx.is_readwrite;\n      flag.has_server_override = flag_cxx.has_server_override;\n      flag.has_local_override = flag_cxx.has_local_override;\n      flag_list.push_back(flag);\n    }\n    return flag_list;\n  } else {\n    return Error() << flag_list_cxx.error_message;\n  }\n}\n\n} // namespace aconfig_storage\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/build.rs",
    "content": "use protobuf_codegen::Codegen;\n\nfn main() {\n    let proto_files = vec![\"protos/aconfig_storage_metadata.proto\"];\n\n    // tell cargo to only re-run the build script if any of the proto files has changed\n    for path in &proto_files {\n        println!(\"cargo:rerun-if-changed={}\", path);\n    }\n\n    Codegen::new()\n        .pure()\n        .include(\"protos\")\n        .inputs(proto_files)\n        .cargo_out_dir(\"aconfig_storage_protos\")\n        .run_from_script();\n\n    let _ = cxx_build::bridge(\"src/lib.rs\");\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp",
    "content": "#pragma once\n\n#include <vector>\n#include <string>\n#include <android-base/result.h>\n\nnamespace aconfig_storage {\n\n/// Flag value summary for a flag\nstruct FlagValueSummary {\n  std::string package_name;\n  std::string flag_name;\n  std::string flag_value;\n  std::string value_type;\n};\n\n/// List all flag values\n/// \\input package_map: package map file\n/// \\input flag_map: flag map file\n/// \\input flag_val: flag value file\nandroid::base::Result<std::vector<FlagValueSummary>> list_flags(\n    const std::string& package_map,\n    const std::string& flag_map,\n    const std::string& flag_val);\n\n/// Flag value and info summary for a flag\nstruct FlagValueAndInfoSummary {\n  std::string package_name;\n  std::string flag_name;\n  std::string flag_value;\n  std::string value_type;\n  bool is_readwrite;\n  bool has_server_override;\n  bool has_local_override;\n};\n\n/// List all flag values with their flag info\n/// \\input package_map: package map file\n/// \\input flag_map: flag map file\n/// \\input flag_val: flag value file\n/// \\input flag_info: flag info file\nandroid::base::Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info(\n    const std::string& package_map,\n    const std::string& flag_map,\n    const std::string& flag_val,\n    const std::string& flag_info);\n\n}// namespace aconfig_storage\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License\n\n// This is the schema definition for aconfig files. Modifications need to be\n// either backwards compatible, or include updates to all aconfig files in the\n// Android tree.\n\nsyntax = \"proto2\";\n\npackage android.aconfig_storage_metadata;\n\nmessage storage_file_info {\n  optional uint32 version = 1;\n  optional string container = 2;\n  optional string package_map = 3;\n  optional string flag_map = 4;\n  optional string flag_val = 5;\n  optional string flag_info = 6;\n  optional int64 timestamp = 7;\n}\n\nmessage storage_files {\n  repeated storage_file_info files = 1;\n};\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/src/flag_info.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! flag info module defines the flag info file format and methods for serialization\n//! and deserialization\n\nuse crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};\nuse crate::{AconfigStorageError, StorageFileType};\nuse anyhow::anyhow;\nuse serde::{Deserialize, Serialize};\nuse std::fmt;\n\n/// Flag info header struct\n#[derive(PartialEq, Serialize, Deserialize)]\npub struct FlagInfoHeader {\n    pub version: u32,\n    pub container: String,\n    pub file_type: u8,\n    pub file_size: u32,\n    pub num_flags: u32,\n    pub boolean_flag_offset: u32,\n}\n\n/// Implement debug print trait for header\nimpl fmt::Debug for FlagInfoHeader {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(\n            f,\n            \"Version: {}, Container: {}, File Type: {:?}, File Size: {}\",\n            self.version,\n            self.container,\n            StorageFileType::try_from(self.file_type),\n            self.file_size\n        )?;\n        writeln!(\n            f,\n            \"Num of Flags: {}, Boolean Flag Offset:{}\",\n            self.num_flags, self.boolean_flag_offset\n        )?;\n        Ok(())\n    }\n}\n\nimpl FlagInfoHeader {\n    /// Serialize to bytes\n    pub fn into_bytes(&self) -> Vec<u8> {\n        let mut result = Vec::new();\n        result.extend_from_slice(&self.version.to_le_bytes());\n        let container_bytes = self.container.as_bytes();\n        result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());\n        result.extend_from_slice(container_bytes);\n        result.extend_from_slice(&self.file_type.to_le_bytes());\n        result.extend_from_slice(&self.file_size.to_le_bytes());\n        result.extend_from_slice(&self.num_flags.to_le_bytes());\n        result.extend_from_slice(&self.boolean_flag_offset.to_le_bytes());\n        result\n    }\n\n    /// Deserialize from bytes\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let mut head = 0;\n        let list = Self {\n            version: read_u32_from_bytes(bytes, &mut head)?,\n            container: read_str_from_bytes(bytes, &mut head)?,\n            file_type: read_u8_from_bytes(bytes, &mut head)?,\n            file_size: read_u32_from_bytes(bytes, &mut head)?,\n            num_flags: read_u32_from_bytes(bytes, &mut head)?,\n            boolean_flag_offset: read_u32_from_bytes(bytes, &mut head)?,\n        };\n        if list.file_type != StorageFileType::FlagInfo as u8 {\n            return Err(AconfigStorageError::BytesParseFail(anyhow!(\n                \"binary file is not a flag info file\"\n            )));\n        }\n        Ok(list)\n    }\n}\n\n/// bit field for flag info\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub enum FlagInfoBit {\n    HasServerOverride = 1 << 0,\n    IsReadWrite = 1 << 1,\n    HasLocalOverride = 1 << 2,\n}\n\n/// Flag info node struct\n#[derive(PartialEq, Clone, Serialize, Deserialize)]\npub struct FlagInfoNode {\n    pub attributes: u8,\n}\n\n/// Implement debug print trait for node\nimpl fmt::Debug for FlagInfoNode {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(\n            f,\n            \"readwrite: {}, server override: {}, local override: {}\",\n            self.attributes & (FlagInfoBit::IsReadWrite as u8) != 0,\n            self.attributes & (FlagInfoBit::HasServerOverride as u8) != 0,\n            self.attributes & (FlagInfoBit::HasLocalOverride as u8) != 0,\n        )?;\n        Ok(())\n    }\n}\n\nimpl FlagInfoNode {\n    /// Serialize to bytes\n    pub fn into_bytes(&self) -> Vec<u8> {\n        let mut result = Vec::new();\n        result.extend_from_slice(&self.attributes.to_le_bytes());\n        result\n    }\n\n    /// Deserialize from bytes\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let mut head = 0;\n        let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? };\n        Ok(node)\n    }\n\n    /// Create flag info node\n    pub fn create(is_flag_rw: bool) -> Self {\n        Self { attributes: if is_flag_rw { FlagInfoBit::IsReadWrite as u8 } else { 0u8 } }\n    }\n}\n\n/// Flag info list struct\n#[derive(PartialEq, Serialize, Deserialize)]\npub struct FlagInfoList {\n    pub header: FlagInfoHeader,\n    pub nodes: Vec<FlagInfoNode>,\n}\n\n/// Implement debug print trait for flag info list\nimpl fmt::Debug for FlagInfoList {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(f, \"Header:\")?;\n        write!(f, \"{:?}\", self.header)?;\n        writeln!(f, \"Nodes:\")?;\n        for node in self.nodes.iter() {\n            write!(f, \"{:?}\", node)?;\n        }\n        Ok(())\n    }\n}\n\nimpl FlagInfoList {\n    /// Serialize to bytes\n    pub fn into_bytes(&self) -> Vec<u8> {\n        [\n            self.header.into_bytes(),\n            self.nodes.iter().map(|v| v.into_bytes()).collect::<Vec<_>>().concat(),\n        ]\n        .concat()\n    }\n\n    /// Deserialize from bytes\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let header = FlagInfoHeader::from_bytes(bytes)?;\n        let num_flags = header.num_flags;\n        let mut head = header.into_bytes().len();\n        let nodes = (0..num_flags)\n            .map(|_| {\n                let node = FlagInfoNode::from_bytes(&bytes[head..])?;\n                head += node.into_bytes().len();\n                Ok(node)\n            })\n            .collect::<Result<Vec<_>, AconfigStorageError>>()\n            .map_err(|errmsg| {\n                AconfigStorageError::BytesParseFail(anyhow!(\n                    \"fail to parse flag info list: {}\",\n                    errmsg\n                ))\n            })?;\n        let list = Self { header, nodes };\n        Ok(list)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{\n        test_utils::create_test_flag_info_list, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION,\n    };\n\n    // this test point locks down the value list serialization\n    #[test]\n    fn test_serialization() {\n        for file_version in 1..=MAX_SUPPORTED_FILE_VERSION {\n            let flag_info_list = create_test_flag_info_list(file_version);\n\n            let header: &FlagInfoHeader = &flag_info_list.header;\n            let reinterpreted_header = FlagInfoHeader::from_bytes(&header.into_bytes());\n            assert!(reinterpreted_header.is_ok());\n            assert_eq!(header, &reinterpreted_header.unwrap());\n\n            let nodes: &Vec<FlagInfoNode> = &flag_info_list.nodes;\n            for node in nodes.iter() {\n                let reinterpreted_node = FlagInfoNode::from_bytes(&node.into_bytes()).unwrap();\n                assert_eq!(node, &reinterpreted_node);\n            }\n\n            let flag_info_bytes = flag_info_list.into_bytes();\n            let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes);\n            assert!(reinterpreted_info_list.is_ok());\n            assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap());\n            assert_eq!(flag_info_bytes.len() as u32, header.file_size);\n        }\n    }\n\n    // this test point locks down that version number should be at the top of serialized\n    // bytes\n    #[test]\n    fn test_version_number() {\n        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);\n        let bytes = &flag_info_list.into_bytes();\n        let mut head = 0;\n        let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap();\n        assert_eq!(version_from_file, DEFAULT_FILE_VERSION);\n    }\n\n    // this test point locks down file type check\n    #[test]\n    fn test_file_type_check() {\n        let mut flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);\n        flag_info_list.header.file_type = 123u8;\n        let error = FlagInfoList::from_bytes(&flag_info_list.into_bytes()).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            format!(\"BytesParseFail(binary file is not a flag info file)\")\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/src/flag_table.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! flag table module defines the flag table file format and methods for serialization\n//! and deserialization\n\nuse crate::{\n    get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes,\n    read_u8_from_bytes,\n};\nuse crate::{AconfigStorageError, StorageFileType, StoredFlagType};\nuse anyhow::anyhow;\nuse serde::{Deserialize, Serialize};\nuse std::fmt;\n\n/// Flag table header struct\n#[derive(PartialEq, Serialize, Deserialize)]\npub struct FlagTableHeader {\n    pub version: u32,\n    pub container: String,\n    pub file_type: u8,\n    pub file_size: u32,\n    pub num_flags: u32,\n    pub bucket_offset: u32,\n    pub node_offset: u32,\n}\n\n/// Implement debug print trait for header\nimpl fmt::Debug for FlagTableHeader {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(\n            f,\n            \"Version: {}, Container: {}, File Type: {:?}, File Size: {}\",\n            self.version,\n            self.container,\n            StorageFileType::try_from(self.file_type),\n            self.file_size\n        )?;\n        writeln!(\n            f,\n            \"Num of Flags: {}, Bucket Offset:{}, Node Offset: {}\",\n            self.num_flags, self.bucket_offset, self.node_offset\n        )?;\n        Ok(())\n    }\n}\n\nimpl FlagTableHeader {\n    /// Serialize to bytes\n    pub fn into_bytes(&self) -> Vec<u8> {\n        let mut result = Vec::new();\n        result.extend_from_slice(&self.version.to_le_bytes());\n        let container_bytes = self.container.as_bytes();\n        result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());\n        result.extend_from_slice(container_bytes);\n        result.extend_from_slice(&self.file_type.to_le_bytes());\n        result.extend_from_slice(&self.file_size.to_le_bytes());\n        result.extend_from_slice(&self.num_flags.to_le_bytes());\n        result.extend_from_slice(&self.bucket_offset.to_le_bytes());\n        result.extend_from_slice(&self.node_offset.to_le_bytes());\n        result\n    }\n\n    /// Deserialize from bytes\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let mut head = 0;\n        let table = Self {\n            version: read_u32_from_bytes(bytes, &mut head)?,\n            container: read_str_from_bytes(bytes, &mut head)?,\n            file_type: read_u8_from_bytes(bytes, &mut head)?,\n            file_size: read_u32_from_bytes(bytes, &mut head)?,\n            num_flags: read_u32_from_bytes(bytes, &mut head)?,\n            bucket_offset: read_u32_from_bytes(bytes, &mut head)?,\n            node_offset: read_u32_from_bytes(bytes, &mut head)?,\n        };\n        if table.file_type != StorageFileType::FlagMap as u8 {\n            return Err(AconfigStorageError::BytesParseFail(anyhow!(\n                \"binary file is not a flag map\"\n            )));\n        }\n        Ok(table)\n    }\n}\n\n/// Flag table node struct\n#[derive(PartialEq, Clone, Serialize, Deserialize)]\npub struct FlagTableNode {\n    pub package_id: u32,\n    pub flag_name: String,\n    pub flag_type: StoredFlagType,\n    // within package flag index of this flag type\n    pub flag_index: u16,\n    pub next_offset: Option<u32>,\n}\n\n/// Implement debug print trait for node\nimpl fmt::Debug for FlagTableNode {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(\n            f,\n            \"Package Id: {}, Flag: {}, Type: {:?}, Index: {}, Next: {:?}\",\n            self.package_id, self.flag_name, self.flag_type, self.flag_index, self.next_offset\n        )?;\n        Ok(())\n    }\n}\n\nimpl FlagTableNode {\n    /// Serialize to bytes\n    pub fn into_bytes(&self) -> Vec<u8> {\n        let mut result = Vec::new();\n        result.extend_from_slice(&self.package_id.to_le_bytes());\n        let name_bytes = self.flag_name.as_bytes();\n        result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());\n        result.extend_from_slice(name_bytes);\n        result.extend_from_slice(&(self.flag_type as u16).to_le_bytes());\n        result.extend_from_slice(&self.flag_index.to_le_bytes());\n        result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());\n        result\n    }\n\n    /// Deserialize from bytes\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let mut head = 0;\n        let node = Self {\n            package_id: read_u32_from_bytes(bytes, &mut head)?,\n            flag_name: read_str_from_bytes(bytes, &mut head)?,\n            flag_type: StoredFlagType::try_from(read_u16_from_bytes(bytes, &mut head)?)?,\n            flag_index: read_u16_from_bytes(bytes, &mut head)?,\n            next_offset: match read_u32_from_bytes(bytes, &mut head)? {\n                0 => None,\n                val => Some(val),\n            },\n        };\n        Ok(node)\n    }\n\n    /// Calculate node bucket index\n    pub fn find_bucket_index(package_id: u32, flag_name: &str, num_buckets: u32) -> u32 {\n        let full_flag_name = package_id.to_string() + \"/\" + flag_name;\n        get_bucket_index(full_flag_name.as_bytes(), num_buckets)\n    }\n}\n\n#[derive(PartialEq, Serialize, Deserialize)]\npub struct FlagTable {\n    pub header: FlagTableHeader,\n    pub buckets: Vec<Option<u32>>,\n    pub nodes: Vec<FlagTableNode>,\n}\n\n/// Implement debug print trait for flag table\nimpl fmt::Debug for FlagTable {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(f, \"Header:\")?;\n        write!(f, \"{:?}\", self.header)?;\n        writeln!(f, \"Buckets:\")?;\n        writeln!(f, \"{:?}\", self.buckets)?;\n        writeln!(f, \"Nodes:\")?;\n        for node in self.nodes.iter() {\n            write!(f, \"{:?}\", node)?;\n        }\n        Ok(())\n    }\n}\n\n/// Flag table struct\nimpl FlagTable {\n    /// Serialize to bytes\n    pub fn into_bytes(&self) -> Vec<u8> {\n        [\n            self.header.into_bytes(),\n            self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),\n            self.nodes.iter().map(|v| v.into_bytes()).collect::<Vec<_>>().concat(),\n        ]\n        .concat()\n    }\n\n    /// Deserialize from bytes\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let header = FlagTableHeader::from_bytes(bytes)?;\n        let num_flags = header.num_flags;\n        let num_buckets = crate::get_table_size(num_flags)?;\n        let mut head = header.into_bytes().len();\n        let buckets = (0..num_buckets)\n            .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {\n                0 => None,\n                val => Some(val),\n            })\n            .collect();\n        let nodes = (0..num_flags)\n            .map(|_| {\n                let node = FlagTableNode::from_bytes(&bytes[head..])?;\n                head += node.into_bytes().len();\n                Ok(node)\n            })\n            .collect::<Result<Vec<_>, AconfigStorageError>>()\n            .map_err(|errmsg| {\n                AconfigStorageError::BytesParseFail(anyhow!(\"fail to parse flag table: {}\", errmsg))\n            })?;\n\n        let table = Self { header, buckets, nodes };\n        Ok(table)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{\n        test_utils::create_test_flag_table, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION,\n    };\n\n    // this test point locks down the table serialization\n    #[test]\n    fn test_serialization() {\n        for file_version in 1..=MAX_SUPPORTED_FILE_VERSION {\n            let flag_table = create_test_flag_table(file_version);\n\n            let header: &FlagTableHeader = &flag_table.header;\n            let reinterpreted_header = FlagTableHeader::from_bytes(&header.into_bytes());\n            assert!(reinterpreted_header.is_ok());\n            assert_eq!(header, &reinterpreted_header.unwrap());\n\n            let nodes: &Vec<FlagTableNode> = &flag_table.nodes;\n            for node in nodes.iter() {\n                let reinterpreted_node = FlagTableNode::from_bytes(&node.into_bytes()).unwrap();\n                assert_eq!(node, &reinterpreted_node);\n            }\n\n            let flag_table_bytes = flag_table.into_bytes();\n            let reinterpreted_table = FlagTable::from_bytes(&flag_table_bytes);\n            assert!(reinterpreted_table.is_ok());\n            assert_eq!(&flag_table, &reinterpreted_table.unwrap());\n            assert_eq!(flag_table_bytes.len() as u32, header.file_size);\n        }\n    }\n\n    // this test point locks down that version number should be at the top of serialized\n    // bytes\n    #[test]\n    fn test_version_number() {\n        let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION);\n        let bytes = &flag_table.into_bytes();\n        let mut head = 0;\n        let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap();\n        assert_eq!(version_from_file, DEFAULT_FILE_VERSION);\n    }\n\n    // this test point locks down file type check\n    #[test]\n    fn test_file_type_check() {\n        let mut flag_table = create_test_flag_table(DEFAULT_FILE_VERSION);\n        flag_table.header.file_type = 123u8;\n        let error = FlagTable::from_bytes(&flag_table.into_bytes()).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            format!(\"BytesParseFail(binary file is not a flag map)\")\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/src/flag_value.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! flag value module defines the flag value file format and methods for serialization\n//! and deserialization\n\nuse crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};\nuse crate::{AconfigStorageError, StorageFileType};\nuse anyhow::anyhow;\nuse serde::{Deserialize, Serialize};\nuse std::fmt;\n\n/// Flag value header struct\n#[derive(PartialEq, Serialize, Deserialize)]\npub struct FlagValueHeader {\n    pub version: u32,\n    pub container: String,\n    pub file_type: u8,\n    pub file_size: u32,\n    pub num_flags: u32,\n    pub boolean_value_offset: u32,\n}\n\n/// Implement debug print trait for header\nimpl fmt::Debug for FlagValueHeader {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(\n            f,\n            \"Version: {}, Container: {}, File Type: {:?}, File Size: {}\",\n            self.version,\n            self.container,\n            StorageFileType::try_from(self.file_type),\n            self.file_size\n        )?;\n        writeln!(\n            f,\n            \"Num of Flags: {}, Value Offset:{}\",\n            self.num_flags, self.boolean_value_offset\n        )?;\n        Ok(())\n    }\n}\n\nimpl FlagValueHeader {\n    /// Serialize to bytes\n    pub fn into_bytes(&self) -> Vec<u8> {\n        let mut result = Vec::new();\n        result.extend_from_slice(&self.version.to_le_bytes());\n        let container_bytes = self.container.as_bytes();\n        result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());\n        result.extend_from_slice(container_bytes);\n        result.extend_from_slice(&self.file_type.to_le_bytes());\n        result.extend_from_slice(&self.file_size.to_le_bytes());\n        result.extend_from_slice(&self.num_flags.to_le_bytes());\n        result.extend_from_slice(&self.boolean_value_offset.to_le_bytes());\n        result\n    }\n\n    /// Deserialize from bytes\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let mut head = 0;\n        let list = Self {\n            version: read_u32_from_bytes(bytes, &mut head)?,\n            container: read_str_from_bytes(bytes, &mut head)?,\n            file_type: read_u8_from_bytes(bytes, &mut head)?,\n            file_size: read_u32_from_bytes(bytes, &mut head)?,\n            num_flags: read_u32_from_bytes(bytes, &mut head)?,\n            boolean_value_offset: read_u32_from_bytes(bytes, &mut head)?,\n        };\n        if list.file_type != StorageFileType::FlagVal as u8 {\n            return Err(AconfigStorageError::BytesParseFail(anyhow!(\n                \"binary file is not a flag value file\"\n            )));\n        }\n        Ok(list)\n    }\n}\n\n/// Flag value list struct\n#[derive(PartialEq, Serialize, Deserialize)]\npub struct FlagValueList {\n    pub header: FlagValueHeader,\n    pub booleans: Vec<bool>,\n}\n\n/// Implement debug print trait for flag value\nimpl fmt::Debug for FlagValueList {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(f, \"Header:\")?;\n        write!(f, \"{:?}\", self.header)?;\n        writeln!(f, \"Values:\")?;\n        writeln!(f, \"{:?}\", self.booleans)?;\n        Ok(())\n    }\n}\n\nimpl FlagValueList {\n    /// Serialize to bytes\n    pub fn into_bytes(&self) -> Vec<u8> {\n        [\n            self.header.into_bytes(),\n            self.booleans.iter().map(|&v| u8::from(v).to_le_bytes()).collect::<Vec<_>>().concat(),\n        ]\n        .concat()\n    }\n\n    /// Deserialize from bytes\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let header = FlagValueHeader::from_bytes(bytes)?;\n        let num_flags = header.num_flags;\n        let mut head = header.into_bytes().len();\n        let booleans =\n            (0..num_flags).map(|_| read_u8_from_bytes(bytes, &mut head).unwrap() == 1).collect();\n        let list = Self { header, booleans };\n        Ok(list)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{\n        test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION,\n    };\n\n    #[test]\n    // this test point locks down the value list serialization\n    fn test_serialization() {\n        for file_version in 1..=MAX_SUPPORTED_FILE_VERSION {\n            let flag_value_list = create_test_flag_value_list(file_version);\n\n            let header: &FlagValueHeader = &flag_value_list.header;\n            let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes());\n            assert!(reinterpreted_header.is_ok());\n            assert_eq!(header, &reinterpreted_header.unwrap());\n\n            let flag_value_bytes = flag_value_list.into_bytes();\n            let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_bytes);\n            assert!(reinterpreted_value_list.is_ok());\n            assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap());\n            assert_eq!(flag_value_bytes.len() as u32, header.file_size);\n        }\n    }\n\n    #[test]\n    // this test point locks down that version number should be at the top of serialized\n    // bytes\n    fn test_version_number() {\n        let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);\n        let bytes = &flag_value_list.into_bytes();\n        let mut head = 0;\n        let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap();\n        assert_eq!(version_from_file, DEFAULT_FILE_VERSION);\n    }\n\n    #[test]\n    // this test point locks down file type check\n    fn test_file_type_check() {\n        let mut flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);\n        flag_value_list.header.file_type = 123u8;\n        let error = FlagValueList::from_bytes(&flag_value_list.into_bytes()).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            format!(\"BytesParseFail(binary file is not a flag value file)\")\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/src/lib.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! `aconfig_storage_file` is a crate that defines aconfig storage file format, it\n//! also includes apis to read flags from storage files. It provides three apis to\n//! interface with storage files:\n//!\n//! 1, function to get package flag value start offset\n//! pub fn get_package_offset(container: &str, package: &str) -> `Result<Option<PackageOffset>>>`\n//!\n//! 2, function to get flag offset within a specific package\n//! pub fn get_flag_offset(container: &str, package_id: u32, flag: &str) -> `Result<Option<u16>>>`\n//!\n//! 3, function to get the actual flag value given the global offset (combined package and\n//! flag offset).\n//! pub fn get_boolean_flag_value(container: &str, offset: u32) -> `Result<bool>`\n//!\n//! Note these are low level apis that are expected to be only used in auto generated flag\n//! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis\n//! please refer to the g3doc go/android-flags\n\npub mod flag_info;\npub mod flag_table;\npub mod flag_value;\npub mod package_table;\npub mod protos;\npub mod sip_hasher13;\npub mod test_utils;\n\nuse anyhow::anyhow;\nuse serde::{Deserialize, Serialize};\nuse std::cmp::Ordering;\nuse std::fs::File;\nuse std::hash::Hasher;\nuse std::io::Read;\n\npub use crate::flag_info::{FlagInfoBit, FlagInfoHeader, FlagInfoList, FlagInfoNode};\npub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};\npub use crate::flag_value::{FlagValueHeader, FlagValueList};\npub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};\npub use crate::sip_hasher13::SipHasher13;\n\nuse crate::AconfigStorageError::{\n    BytesParseFail, HashTableSizeLimit, InvalidFlagValueType, InvalidStoredFlagType,\n};\n\n/// The max storage file version from which we can safely read/write. May be\n/// experimental.\npub const MAX_SUPPORTED_FILE_VERSION: u32 = 2;\n\n/// The newest fully-released version. Unless otherwise specified, this is the\n/// version we will write.\npub const DEFAULT_FILE_VERSION: u32 = 1;\n\n/// Good hash table prime number\npub(crate) const HASH_PRIMES: [u32; 29] = [\n    7, 17, 29, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241,\n    786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611,\n    402653189, 805306457, 1610612741,\n];\n\n/// Storage file type enum\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum StorageFileType {\n    PackageMap = 0,\n    FlagMap = 1,\n    FlagVal = 2,\n    FlagInfo = 3,\n}\n\nimpl TryFrom<&str> for StorageFileType {\n    type Error = anyhow::Error;\n\n    fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {\n        match value {\n            \"package_map\" => Ok(Self::PackageMap),\n            \"flag_map\" => Ok(Self::FlagMap),\n            \"flag_val\" => Ok(Self::FlagVal),\n            \"flag_info\" => Ok(Self::FlagInfo),\n            _ => Err(anyhow!(\n                \"Invalid storage file type, valid types are package_map|flag_map|flag_val|flag_info\"\n            )),\n        }\n    }\n}\n\nimpl TryFrom<u8> for StorageFileType {\n    type Error = anyhow::Error;\n\n    fn try_from(value: u8) -> Result<Self, Self::Error> {\n        match value {\n            x if x == Self::PackageMap as u8 => Ok(Self::PackageMap),\n            x if x == Self::FlagMap as u8 => Ok(Self::FlagMap),\n            x if x == Self::FlagVal as u8 => Ok(Self::FlagVal),\n            x if x == Self::FlagInfo as u8 => Ok(Self::FlagInfo),\n            _ => Err(anyhow!(\"Invalid storage file type\")),\n        }\n    }\n}\n\n/// Flag type enum as stored by storage file\n/// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub enum StoredFlagType {\n    ReadWriteBoolean = 0,\n    ReadOnlyBoolean = 1,\n    FixedReadOnlyBoolean = 2,\n}\n\nimpl TryFrom<u16> for StoredFlagType {\n    type Error = AconfigStorageError;\n\n    fn try_from(value: u16) -> Result<Self, Self::Error> {\n        match value {\n            x if x == Self::ReadWriteBoolean as u16 => Ok(Self::ReadWriteBoolean),\n            x if x == Self::ReadOnlyBoolean as u16 => Ok(Self::ReadOnlyBoolean),\n            x if x == Self::FixedReadOnlyBoolean as u16 => Ok(Self::FixedReadOnlyBoolean),\n            _ => Err(InvalidStoredFlagType(anyhow!(\"Invalid stored flag type\"))),\n        }\n    }\n}\n\n/// Flag value type enum, one FlagValueType maps to many StoredFlagType\n/// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum FlagValueType {\n    Boolean = 0,\n}\n\nimpl TryFrom<StoredFlagType> for FlagValueType {\n    type Error = AconfigStorageError;\n\n    fn try_from(value: StoredFlagType) -> Result<Self, Self::Error> {\n        match value {\n            StoredFlagType::ReadWriteBoolean => Ok(Self::Boolean),\n            StoredFlagType::ReadOnlyBoolean => Ok(Self::Boolean),\n            StoredFlagType::FixedReadOnlyBoolean => Ok(Self::Boolean),\n        }\n    }\n}\n\nimpl TryFrom<u16> for FlagValueType {\n    type Error = AconfigStorageError;\n\n    fn try_from(value: u16) -> Result<Self, Self::Error> {\n        match value {\n            x if x == Self::Boolean as u16 => Ok(Self::Boolean),\n            _ => Err(InvalidFlagValueType(anyhow!(\"Invalid flag value type\"))),\n        }\n    }\n}\n\n/// Storage query api error\n#[non_exhaustive]\n#[derive(thiserror::Error, Debug)]\npub enum AconfigStorageError {\n    #[error(\"failed to read the file\")]\n    FileReadFail(#[source] anyhow::Error),\n\n    #[error(\"fail to parse protobuf\")]\n    ProtobufParseFail(#[source] anyhow::Error),\n\n    #[error(\"storage files not found for this container\")]\n    StorageFileNotFound(#[source] anyhow::Error),\n\n    #[error(\"fail to map storage file\")]\n    MapFileFail(#[source] anyhow::Error),\n\n    #[error(\"fail to get mapped file\")]\n    ObtainMappedFileFail(#[source] anyhow::Error),\n\n    #[error(\"fail to flush mapped storage file\")]\n    MapFlushFail(#[source] anyhow::Error),\n\n    #[error(\"number of items in hash table exceed limit\")]\n    HashTableSizeLimit(#[source] anyhow::Error),\n\n    #[error(\"failed to parse bytes into data\")]\n    BytesParseFail(#[source] anyhow::Error),\n\n    #[error(\"cannot parse storage files with a higher version\")]\n    HigherStorageFileVersion(#[source] anyhow::Error),\n\n    #[error(\"invalid storage file byte offset\")]\n    InvalidStorageFileOffset(#[source] anyhow::Error),\n\n    #[error(\"failed to create file\")]\n    FileCreationFail(#[source] anyhow::Error),\n\n    #[error(\"invalid stored flag type\")]\n    InvalidStoredFlagType(#[source] anyhow::Error),\n\n    #[error(\"invalid flag value type\")]\n    InvalidFlagValueType(#[source] anyhow::Error),\n}\n\n/// Get the right hash table size given number of entries in the table. Use a\n/// load factor of 0.5 for performance.\npub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> {\n    HASH_PRIMES\n        .iter()\n        .find(|&&num| num >= 2 * entries)\n        .copied()\n        .ok_or(HashTableSizeLimit(anyhow!(\"Number of items in a hash table exceeds limit\")))\n}\n\n/// Get the corresponding bucket index given the key and number of buckets\npub(crate) fn get_bucket_index(val: &[u8], num_buckets: u32) -> u32 {\n    let mut s = SipHasher13::new();\n    s.write(val);\n    s.write_u8(0xff);\n    let ret = (s.finish() % num_buckets as u64) as u32;\n    ret\n}\n\n/// Read and parse bytes as u8\npub fn read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result<u8, AconfigStorageError> {\n    let val =\n        u8::from_le_bytes(buf[*head..*head + 1].try_into().map_err(|errmsg| {\n            BytesParseFail(anyhow!(\"fail to parse u8 from bytes: {}\", errmsg))\n        })?);\n    *head += 1;\n    Ok(val)\n}\n\n/// Read and parse bytes as u16\npub(crate) fn read_u16_from_bytes(\n    buf: &[u8],\n    head: &mut usize,\n) -> Result<u16, AconfigStorageError> {\n    let val =\n        u16::from_le_bytes(buf[*head..*head + 2].try_into().map_err(|errmsg| {\n            BytesParseFail(anyhow!(\"fail to parse u16 from bytes: {}\", errmsg))\n        })?);\n    *head += 2;\n    Ok(val)\n}\n\n/// Read and parse the first 4 bytes of buf as u32.\npub fn read_u32_from_start_of_bytes(buf: &[u8]) -> Result<u32, AconfigStorageError> {\n    read_u32_from_bytes(buf, &mut 0)\n}\n\n/// Read and parse bytes as u32\npub fn read_u32_from_bytes(buf: &[u8], head: &mut usize) -> Result<u32, AconfigStorageError> {\n    let val =\n        u32::from_le_bytes(buf[*head..*head + 4].try_into().map_err(|errmsg| {\n            BytesParseFail(anyhow!(\"fail to parse u32 from bytes: {}\", errmsg))\n        })?);\n    *head += 4;\n    Ok(val)\n}\n\n// Read and parse bytes as u64\npub fn read_u64_from_bytes(buf: &[u8], head: &mut usize) -> Result<u64, AconfigStorageError> {\n    let val =\n        u64::from_le_bytes(buf[*head..*head + 8].try_into().map_err(|errmsg| {\n            BytesParseFail(anyhow!(\"fail to parse u64 from bytes: {}\", errmsg))\n        })?);\n    *head += 8;\n    Ok(val)\n}\n\n/// Read and parse bytes as string\npub(crate) fn read_str_from_bytes(\n    buf: &[u8],\n    head: &mut usize,\n) -> Result<String, AconfigStorageError> {\n    let num_bytes = read_u32_from_bytes(buf, head)? as usize;\n    let val = String::from_utf8(buf[*head..*head + num_bytes].to_vec())\n        .map_err(|errmsg| BytesParseFail(anyhow!(\"fail to parse string from bytes: {}\", errmsg)))?;\n    *head += num_bytes;\n    Ok(val)\n}\n\n/// Read in storage file as bytes\npub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> {\n    let mut file = File::open(file_path).map_err(|errmsg| {\n        AconfigStorageError::FileReadFail(anyhow!(\"Failed to open file {}: {}\", file_path, errmsg))\n    })?;\n    let mut buffer = Vec::new();\n    file.read_to_end(&mut buffer).map_err(|errmsg| {\n        AconfigStorageError::FileReadFail(anyhow!(\n            \"Failed to read bytes from file {}: {}\",\n            file_path,\n            errmsg\n        ))\n    })?;\n    Ok(buffer)\n}\n\n/// Flag value summary\n#[derive(Debug, PartialEq)]\npub struct FlagValueSummary {\n    pub package_name: String,\n    pub flag_name: String,\n    pub flag_value: String,\n    pub value_type: StoredFlagType,\n}\n\n/// List flag values from storage files\npub fn list_flags(\n    package_map: &str,\n    flag_map: &str,\n    flag_val: &str,\n) -> Result<Vec<FlagValueSummary>, AconfigStorageError> {\n    let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;\n    let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;\n    let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;\n\n    let mut package_info = vec![(\"\", 0); package_table.header.num_packages as usize];\n    for node in package_table.nodes.iter() {\n        package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index);\n    }\n\n    let mut flags = Vec::new();\n    for node in flag_table.nodes.iter() {\n        let (package_name, boolean_start_index) = package_info[node.package_id as usize];\n        let flag_index = boolean_start_index + node.flag_index as u32;\n        let flag_value = flag_value_list.booleans[flag_index as usize];\n        flags.push(FlagValueSummary {\n            package_name: String::from(package_name),\n            flag_name: node.flag_name.clone(),\n            flag_value: flag_value.to_string(),\n            value_type: node.flag_type,\n        });\n    }\n\n    flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {\n        Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),\n        other => other,\n    });\n    Ok(flags)\n}\n\n/// Flag value and info summary\n#[derive(Debug, PartialEq)]\npub struct FlagValueAndInfoSummary {\n    pub package_name: String,\n    pub flag_name: String,\n    pub flag_value: String,\n    pub value_type: StoredFlagType,\n    pub is_readwrite: bool,\n    pub has_server_override: bool,\n    pub has_local_override: bool,\n}\n\n/// List flag values and info from storage files\npub fn list_flags_with_info(\n    package_map: &str,\n    flag_map: &str,\n    flag_val: &str,\n    flag_info: &str,\n) -> Result<Vec<FlagValueAndInfoSummary>, AconfigStorageError> {\n    let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;\n    let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;\n    let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;\n    let flag_info = FlagInfoList::from_bytes(&read_file_to_bytes(flag_info)?)?;\n\n    let mut package_info = vec![(\"\", 0); package_table.header.num_packages as usize];\n    for node in package_table.nodes.iter() {\n        package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index);\n    }\n\n    let mut flags = Vec::new();\n    for node in flag_table.nodes.iter() {\n        let (package_name, boolean_start_index) = package_info[node.package_id as usize];\n        let flag_index = boolean_start_index + node.flag_index as u32;\n        let flag_value = flag_value_list.booleans[flag_index as usize];\n        let flag_attribute = flag_info.nodes[flag_index as usize].attributes;\n        flags.push(FlagValueAndInfoSummary {\n            package_name: String::from(package_name),\n            flag_name: node.flag_name.clone(),\n            flag_value: flag_value.to_string(),\n            value_type: node.flag_type,\n            is_readwrite: flag_attribute & (FlagInfoBit::IsReadWrite as u8) != 0,\n            has_server_override: flag_attribute & (FlagInfoBit::HasServerOverride as u8) != 0,\n            has_local_override: flag_attribute & (FlagInfoBit::HasLocalOverride as u8) != 0,\n        });\n    }\n\n    flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {\n        Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),\n        other => other,\n    });\n    Ok(flags)\n}\n\n// *************************************** //\n// CC INTERLOP\n// *************************************** //\n\n// Exported rust data structure and methods, c++ code will be generated\n#[cxx::bridge]\nmod ffi {\n    /// flag value summary cxx return\n    pub struct FlagValueSummaryCXX {\n        pub package_name: String,\n        pub flag_name: String,\n        pub flag_value: String,\n        pub value_type: String,\n    }\n\n    /// flag value and info summary cxx return\n    pub struct FlagValueAndInfoSummaryCXX {\n        pub package_name: String,\n        pub flag_name: String,\n        pub flag_value: String,\n        pub value_type: String,\n        pub is_readwrite: bool,\n        pub has_server_override: bool,\n        pub has_local_override: bool,\n    }\n\n    /// list flag result cxx return\n    pub struct ListFlagValueResultCXX {\n        pub query_success: bool,\n        pub error_message: String,\n        pub flags: Vec<FlagValueSummaryCXX>,\n    }\n\n    /// list flag with info result cxx return\n    pub struct ListFlagValueAndInfoResultCXX {\n        pub query_success: bool,\n        pub error_message: String,\n        pub flags: Vec<FlagValueAndInfoSummaryCXX>,\n    }\n\n    // Rust export to c++\n    extern \"Rust\" {\n        pub fn list_flags_cxx(\n            package_map: &str,\n            flag_map: &str,\n            flag_val: &str,\n        ) -> ListFlagValueResultCXX;\n\n        pub fn list_flags_with_info_cxx(\n            package_map: &str,\n            flag_map: &str,\n            flag_val: &str,\n            flag_info: &str,\n        ) -> ListFlagValueAndInfoResultCXX;\n    }\n}\n\n/// implement flag value summary cxx return type\nimpl ffi::FlagValueSummaryCXX {\n    pub(crate) fn new(summary: FlagValueSummary) -> Self {\n        Self {\n            package_name: summary.package_name,\n            flag_name: summary.flag_name,\n            flag_value: summary.flag_value,\n            value_type: format!(\"{:?}\", summary.value_type),\n        }\n    }\n}\n\n/// implement flag value and info summary cxx return type\nimpl ffi::FlagValueAndInfoSummaryCXX {\n    pub(crate) fn new(summary: FlagValueAndInfoSummary) -> Self {\n        Self {\n            package_name: summary.package_name,\n            flag_name: summary.flag_name,\n            flag_value: summary.flag_value,\n            value_type: format!(\"{:?}\", summary.value_type),\n            is_readwrite: summary.is_readwrite,\n            has_server_override: summary.has_server_override,\n            has_local_override: summary.has_local_override,\n        }\n    }\n}\n\n/// implement list flag cxx interlop\npub fn list_flags_cxx(\n    package_map: &str,\n    flag_map: &str,\n    flag_val: &str,\n) -> ffi::ListFlagValueResultCXX {\n    match list_flags(package_map, flag_map, flag_val) {\n        Ok(summary) => ffi::ListFlagValueResultCXX {\n            query_success: true,\n            error_message: String::new(),\n            flags: summary.into_iter().map(ffi::FlagValueSummaryCXX::new).collect(),\n        },\n        Err(errmsg) => ffi::ListFlagValueResultCXX {\n            query_success: false,\n            error_message: format!(\"{:?}\", errmsg),\n            flags: Vec::new(),\n        },\n    }\n}\n\n/// implement list flag with info cxx interlop\npub fn list_flags_with_info_cxx(\n    package_map: &str,\n    flag_map: &str,\n    flag_val: &str,\n    flag_info: &str,\n) -> ffi::ListFlagValueAndInfoResultCXX {\n    match list_flags_with_info(package_map, flag_map, flag_val, flag_info) {\n        Ok(summary) => ffi::ListFlagValueAndInfoResultCXX {\n            query_success: true,\n            error_message: String::new(),\n            flags: summary.into_iter().map(ffi::FlagValueAndInfoSummaryCXX::new).collect(),\n        },\n        Err(errmsg) => ffi::ListFlagValueAndInfoResultCXX {\n            query_success: false,\n            error_message: format!(\"{:?}\", errmsg),\n            flags: Vec::new(),\n        },\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::test_utils::{\n        create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list,\n        create_test_package_table, write_bytes_to_temp_file,\n    };\n\n    #[test]\n    // this test point locks down the flag list api\n    fn test_list_flag() {\n        let package_table =\n            write_bytes_to_temp_file(&create_test_package_table(DEFAULT_FILE_VERSION).into_bytes())\n                .unwrap();\n        let flag_table =\n            write_bytes_to_temp_file(&create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes())\n                .unwrap();\n        let flag_value_list = write_bytes_to_temp_file(\n            &create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(),\n        )\n        .unwrap();\n\n        let package_table_path = package_table.path().display().to_string();\n        let flag_table_path = flag_table.path().display().to_string();\n        let flag_value_list_path = flag_value_list.path().display().to_string();\n\n        let flags =\n            list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();\n        let expected = [\n            FlagValueSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_1\"),\n                flag_name: String::from(\"disabled_rw\"),\n                value_type: StoredFlagType::ReadWriteBoolean,\n                flag_value: String::from(\"false\"),\n            },\n            FlagValueSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_1\"),\n                flag_name: String::from(\"enabled_ro\"),\n                value_type: StoredFlagType::ReadOnlyBoolean,\n                flag_value: String::from(\"true\"),\n            },\n            FlagValueSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_1\"),\n                flag_name: String::from(\"enabled_rw\"),\n                value_type: StoredFlagType::ReadWriteBoolean,\n                flag_value: String::from(\"true\"),\n            },\n            FlagValueSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_2\"),\n                flag_name: String::from(\"disabled_rw\"),\n                value_type: StoredFlagType::ReadWriteBoolean,\n                flag_value: String::from(\"false\"),\n            },\n            FlagValueSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_2\"),\n                flag_name: String::from(\"enabled_fixed_ro\"),\n                value_type: StoredFlagType::FixedReadOnlyBoolean,\n                flag_value: String::from(\"true\"),\n            },\n            FlagValueSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_2\"),\n                flag_name: String::from(\"enabled_ro\"),\n                value_type: StoredFlagType::ReadOnlyBoolean,\n                flag_value: String::from(\"true\"),\n            },\n            FlagValueSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_4\"),\n                flag_name: String::from(\"enabled_fixed_ro\"),\n                value_type: StoredFlagType::FixedReadOnlyBoolean,\n                flag_value: String::from(\"true\"),\n            },\n            FlagValueSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_4\"),\n                flag_name: String::from(\"enabled_rw\"),\n                value_type: StoredFlagType::ReadWriteBoolean,\n                flag_value: String::from(\"true\"),\n            },\n        ];\n        assert_eq!(flags, expected);\n    }\n\n    #[test]\n    // this test point locks down the flag list with info api\n    fn test_list_flag_with_info() {\n        let package_table =\n            write_bytes_to_temp_file(&create_test_package_table(DEFAULT_FILE_VERSION).into_bytes())\n                .unwrap();\n        let flag_table =\n            write_bytes_to_temp_file(&create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes())\n                .unwrap();\n        let flag_value_list = write_bytes_to_temp_file(\n            &create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(),\n        )\n        .unwrap();\n        let flag_info_list = write_bytes_to_temp_file(\n            &create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(),\n        )\n        .unwrap();\n\n        let package_table_path = package_table.path().display().to_string();\n        let flag_table_path = flag_table.path().display().to_string();\n        let flag_value_list_path = flag_value_list.path().display().to_string();\n        let flag_info_list_path = flag_info_list.path().display().to_string();\n\n        let flags = list_flags_with_info(\n            &package_table_path,\n            &flag_table_path,\n            &flag_value_list_path,\n            &flag_info_list_path,\n        )\n        .unwrap();\n        let expected = [\n            FlagValueAndInfoSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_1\"),\n                flag_name: String::from(\"disabled_rw\"),\n                value_type: StoredFlagType::ReadWriteBoolean,\n                flag_value: String::from(\"false\"),\n                is_readwrite: true,\n                has_server_override: false,\n                has_local_override: false,\n            },\n            FlagValueAndInfoSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_1\"),\n                flag_name: String::from(\"enabled_ro\"),\n                value_type: StoredFlagType::ReadOnlyBoolean,\n                flag_value: String::from(\"true\"),\n                is_readwrite: false,\n                has_server_override: false,\n                has_local_override: false,\n            },\n            FlagValueAndInfoSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_1\"),\n                flag_name: String::from(\"enabled_rw\"),\n                value_type: StoredFlagType::ReadWriteBoolean,\n                flag_value: String::from(\"true\"),\n                is_readwrite: true,\n                has_server_override: false,\n                has_local_override: false,\n            },\n            FlagValueAndInfoSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_2\"),\n                flag_name: String::from(\"disabled_rw\"),\n                value_type: StoredFlagType::ReadWriteBoolean,\n                flag_value: String::from(\"false\"),\n                is_readwrite: true,\n                has_server_override: false,\n                has_local_override: false,\n            },\n            FlagValueAndInfoSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_2\"),\n                flag_name: String::from(\"enabled_fixed_ro\"),\n                value_type: StoredFlagType::FixedReadOnlyBoolean,\n                flag_value: String::from(\"true\"),\n                is_readwrite: false,\n                has_server_override: false,\n                has_local_override: false,\n            },\n            FlagValueAndInfoSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_2\"),\n                flag_name: String::from(\"enabled_ro\"),\n                value_type: StoredFlagType::ReadOnlyBoolean,\n                flag_value: String::from(\"true\"),\n                is_readwrite: false,\n                has_server_override: false,\n                has_local_override: false,\n            },\n            FlagValueAndInfoSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_4\"),\n                flag_name: String::from(\"enabled_fixed_ro\"),\n                value_type: StoredFlagType::FixedReadOnlyBoolean,\n                flag_value: String::from(\"true\"),\n                is_readwrite: false,\n                has_server_override: false,\n                has_local_override: false,\n            },\n            FlagValueAndInfoSummary {\n                package_name: String::from(\"com.android.aconfig.storage.test_4\"),\n                flag_name: String::from(\"enabled_rw\"),\n                value_type: StoredFlagType::ReadWriteBoolean,\n                flag_value: String::from(\"true\"),\n                is_readwrite: true,\n                has_server_override: false,\n                has_local_override: false,\n            },\n        ];\n        assert_eq!(flags, expected);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/src/main.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! `aconfig-storage` is a debugging tool to parse storage files\n\nuse aconfig_storage_file::{\n    list_flags, list_flags_with_info, read_file_to_bytes, AconfigStorageError, FlagInfoList,\n    FlagTable, FlagValueList, PackageTable, StorageFileType,\n};\nuse clap::{builder::ArgAction, Arg, Command};\nuse serde::Serialize;\nuse serde_json;\nuse std::fmt;\nuse std::fs;\nuse std::fs::File;\nuse std::io::Write;\n\n/**\n * Usage Examples\n *\n * Print file:\n * $ aconfig-storage print --file=path/to/flag.map --type=flag_map\n *\n * List flags:\n * $ aconfig-storage list --flag-map=path/to/flag.map \\\n * --flag-val=path/to/flag.val --package-map=path/to/package.map\n *\n * Write binary file for testing:\n * $ aconfig-storage print --file=path/to/flag.map --type=flag_map --format=json > flag_map.json\n * $ vim flag_map.json // Manually make updates\n * $ aconfig-storage write-bytes --input-file=flag_map.json --output-file=path/to/flag.map --type=flag_map\n */\nfn cli() -> Command {\n    Command::new(\"aconfig-storage\")\n        .subcommand_required(true)\n        .subcommand(\n            Command::new(\"print\")\n                .arg(Arg::new(\"file\").long(\"file\").required(true).action(ArgAction::Set))\n                .arg(\n                    Arg::new(\"type\")\n                        .long(\"type\")\n                        .required(true)\n                        .value_parser(|s: &str| StorageFileType::try_from(s)),\n                )\n                .arg(Arg::new(\"format\").long(\"format\").required(false).action(ArgAction::Set)),\n        )\n        .subcommand(\n            Command::new(\"list\")\n                .arg(\n                    Arg::new(\"package-map\")\n                        .long(\"package-map\")\n                        .required(true)\n                        .action(ArgAction::Set),\n                )\n                .arg(Arg::new(\"flag-map\").long(\"flag-map\").required(true).action(ArgAction::Set))\n                .arg(Arg::new(\"flag-val\").long(\"flag-val\").required(true).action(ArgAction::Set))\n                .arg(\n                    Arg::new(\"flag-info\").long(\"flag-info\").required(false).action(ArgAction::Set),\n                ),\n        )\n        .subcommand(\n            Command::new(\"write-bytes\")\n                // Where to write the output bytes. Suggest to use the StorageFileType names (e.g. flag.map).\n                .arg(\n                    Arg::new(\"output-file\")\n                        .long(\"output-file\")\n                        .required(true)\n                        .action(ArgAction::Set),\n                )\n                // Input file should be json.\n                .arg(\n                    Arg::new(\"input-file\").long(\"input-file\").required(true).action(ArgAction::Set),\n                )\n                .arg(\n                    Arg::new(\"type\")\n                        .long(\"type\")\n                        .required(true)\n                        .value_parser(|s: &str| StorageFileType::try_from(s)),\n                ),\n        )\n}\n\nfn print_storage_file(\n    file_path: &str,\n    file_type: &StorageFileType,\n    as_json: bool,\n) -> Result<(), AconfigStorageError> {\n    let bytes = read_file_to_bytes(file_path)?;\n    match file_type {\n        StorageFileType::PackageMap => {\n            let package_table = PackageTable::from_bytes(&bytes)?;\n            println!(\"{}\", to_print_format(package_table, as_json));\n        }\n        StorageFileType::FlagMap => {\n            let flag_table = FlagTable::from_bytes(&bytes)?;\n            println!(\"{}\", to_print_format(flag_table, as_json));\n        }\n        StorageFileType::FlagVal => {\n            let flag_value = FlagValueList::from_bytes(&bytes)?;\n            println!(\"{}\", to_print_format(flag_value, as_json));\n        }\n        StorageFileType::FlagInfo => {\n            let flag_info = FlagInfoList::from_bytes(&bytes)?;\n            println!(\"{}\", to_print_format(flag_info, as_json));\n        }\n    }\n    Ok(())\n}\n\nfn to_print_format<T>(file_contents: T, as_json: bool) -> String\nwhere\n    T: Serialize + fmt::Debug,\n{\n    if as_json {\n        serde_json::to_string(&file_contents).unwrap()\n    } else {\n        format!(\"{:?}\", file_contents)\n    }\n}\n\nfn main() -> Result<(), AconfigStorageError> {\n    let matches = cli().get_matches();\n    match matches.subcommand() {\n        Some((\"print\", sub_matches)) => {\n            let file_path = sub_matches.get_one::<String>(\"file\").unwrap();\n            let file_type = sub_matches.get_one::<StorageFileType>(\"type\").unwrap();\n            let format = sub_matches.get_one::<String>(\"format\");\n            let as_json: bool = format == Some(&\"json\".to_string());\n            print_storage_file(file_path, file_type, as_json)?\n        }\n        Some((\"list\", sub_matches)) => {\n            let package_map = sub_matches.get_one::<String>(\"package-map\").unwrap();\n            let flag_map = sub_matches.get_one::<String>(\"flag-map\").unwrap();\n            let flag_val = sub_matches.get_one::<String>(\"flag-val\").unwrap();\n            let flag_info = sub_matches.get_one::<String>(\"flag-info\");\n            match flag_info {\n                Some(info_file) => {\n                    let flags = list_flags_with_info(package_map, flag_map, flag_val, info_file)?;\n                    for flag in flags.iter() {\n                        println!(\n                          \"{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}\",\n                          flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,\n                          flag.is_readwrite, flag.has_server_override, flag.has_local_override,\n                      );\n                    }\n                }\n                None => {\n                    let flags = list_flags(package_map, flag_map, flag_val)?;\n                    for flag in flags.iter() {\n                        println!(\n                            \"{} {} {} {:?}\",\n                            flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,\n                        );\n                    }\n                }\n            }\n        }\n        // Converts JSON of the file into raw bytes (as is used on-device).\n        // Intended to generate/easily update these files for testing.\n        Some((\"write-bytes\", sub_matches)) => {\n            let input_file_path = sub_matches.get_one::<String>(\"input-file\").unwrap();\n            let input_json = fs::read_to_string(input_file_path).unwrap();\n\n            let file_type = sub_matches.get_one::<StorageFileType>(\"type\").unwrap();\n            let output_bytes: Vec<u8>;\n            match file_type {\n                StorageFileType::FlagVal => {\n                    let list: FlagValueList = serde_json::from_str(&input_json).unwrap();\n                    output_bytes = list.into_bytes();\n                }\n                StorageFileType::FlagInfo => {\n                    let list: FlagInfoList = serde_json::from_str(&input_json).unwrap();\n                    output_bytes = list.into_bytes();\n                }\n                StorageFileType::FlagMap => {\n                    let table: FlagTable = serde_json::from_str(&input_json).unwrap();\n                    output_bytes = table.into_bytes();\n                }\n                StorageFileType::PackageMap => {\n                    let table: PackageTable = serde_json::from_str(&input_json).unwrap();\n                    output_bytes = table.into_bytes();\n                }\n            }\n\n            let output_file_path = sub_matches.get_one::<String>(\"output-file\").unwrap();\n            let file = File::create(output_file_path);\n            if file.is_err() {\n                panic!(\"can't make file\");\n            }\n            let _ = file.unwrap().write_all(&output_bytes);\n        }\n        _ => unreachable!(),\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/src/package_table.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! package table module defines the package table file format and methods for serialization\n//! and deserialization\n\nuse crate::{\n    get_bucket_index, read_str_from_bytes, read_u32_from_bytes, read_u64_from_bytes,\n    read_u8_from_bytes,\n};\nuse crate::{AconfigStorageError, StorageFileType};\nuse anyhow::anyhow;\nuse serde::{Deserialize, Serialize};\nuse std::fmt;\n\n/// Package table header struct\n#[derive(PartialEq, Serialize, Deserialize)]\npub struct PackageTableHeader {\n    pub version: u32,\n    pub container: String,\n    pub file_type: u8,\n    pub file_size: u32,\n    pub num_packages: u32,\n    pub bucket_offset: u32,\n    pub node_offset: u32,\n}\n\n/// Implement debug print trait for header\nimpl fmt::Debug for PackageTableHeader {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(\n            f,\n            \"Version: {}, Container: {}, File Type: {:?}, File Size: {}\",\n            self.version,\n            self.container,\n            StorageFileType::try_from(self.file_type),\n            self.file_size\n        )?;\n        writeln!(\n            f,\n            \"Num of Packages: {}, Bucket Offset:{}, Node Offset: {}\",\n            self.num_packages, self.bucket_offset, self.node_offset\n        )?;\n        Ok(())\n    }\n}\n\nimpl PackageTableHeader {\n    /// Serialize to bytes\n    pub fn into_bytes(&self) -> Vec<u8> {\n        let mut result = Vec::new();\n        result.extend_from_slice(&self.version.to_le_bytes());\n        let container_bytes = self.container.as_bytes();\n        result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());\n        result.extend_from_slice(container_bytes);\n        result.extend_from_slice(&self.file_type.to_le_bytes());\n        result.extend_from_slice(&self.file_size.to_le_bytes());\n        result.extend_from_slice(&self.num_packages.to_le_bytes());\n        result.extend_from_slice(&self.bucket_offset.to_le_bytes());\n        result.extend_from_slice(&self.node_offset.to_le_bytes());\n        result\n    }\n\n    /// Deserialize from bytes\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let mut head = 0;\n        let table = Self {\n            version: read_u32_from_bytes(bytes, &mut head)?,\n            container: read_str_from_bytes(bytes, &mut head)?,\n            file_type: read_u8_from_bytes(bytes, &mut head)?,\n            file_size: read_u32_from_bytes(bytes, &mut head)?,\n            num_packages: read_u32_from_bytes(bytes, &mut head)?,\n            bucket_offset: read_u32_from_bytes(bytes, &mut head)?,\n            node_offset: read_u32_from_bytes(bytes, &mut head)?,\n        };\n        if table.file_type != StorageFileType::PackageMap as u8 {\n            return Err(AconfigStorageError::BytesParseFail(anyhow!(\n                \"binary file is not a package map\"\n            )));\n        }\n        Ok(table)\n    }\n}\n\n/// Package table node struct\n#[derive(PartialEq, Serialize, Deserialize)]\npub struct PackageTableNode {\n    pub package_name: String,\n    pub package_id: u32,\n    pub fingerprint: u64,\n    // The index of the first boolean flag in this aconfig package among all boolean\n    // flags in this container.\n    pub boolean_start_index: u32,\n    pub next_offset: Option<u32>,\n}\n\n/// Implement debug print trait for node\nimpl fmt::Debug for PackageTableNode {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(\n            f,\n            \"Package: {}, Id: {}, Fingerprint: {}, Boolean flag start index: {}, Next: {:?}\",\n            self.package_name,\n            self.package_id,\n            self.fingerprint,\n            self.boolean_start_index,\n            self.next_offset\n        )?;\n        Ok(())\n    }\n}\n\nimpl PackageTableNode {\n    /// Serialize to bytes\n    pub fn into_bytes(&self, version: u32) -> Vec<u8> {\n        match version {\n            1 => Self::into_bytes_v1(self),\n            2 => Self::into_bytes_v2(self),\n            // TODO(b/316357686): into_bytes should return a Result.\n            _ => Self::into_bytes_v2(&self),\n        }\n    }\n\n    fn into_bytes_v1(&self) -> Vec<u8> {\n        let mut result = Vec::new();\n        let name_bytes = self.package_name.as_bytes();\n        result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());\n        result.extend_from_slice(name_bytes);\n        result.extend_from_slice(&self.package_id.to_le_bytes());\n        result.extend_from_slice(&self.boolean_start_index.to_le_bytes());\n        result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());\n        result\n    }\n\n    fn into_bytes_v2(&self) -> Vec<u8> {\n        let mut result = Vec::new();\n        let name_bytes = self.package_name.as_bytes();\n        result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());\n        result.extend_from_slice(name_bytes);\n        result.extend_from_slice(&self.package_id.to_le_bytes());\n        result.extend_from_slice(&self.fingerprint.to_le_bytes());\n        result.extend_from_slice(&self.boolean_start_index.to_le_bytes());\n        result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());\n        result\n    }\n\n    /// Deserialize from bytes based on file version.\n    pub fn from_bytes(bytes: &[u8], version: u32) -> Result<Self, AconfigStorageError> {\n        match version {\n            1 => Self::from_bytes_v1(bytes),\n            2 => Self::from_bytes_v2(bytes),\n            _ => {\n                return Err(AconfigStorageError::BytesParseFail(anyhow!(\n                    \"Binary file is an unsupported version: {}\",\n                    version\n                )))\n            }\n        }\n    }\n\n    fn from_bytes_v1(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let mut head = 0;\n        let package_name = read_str_from_bytes(bytes, &mut head)?;\n        let package_id = read_u32_from_bytes(bytes, &mut head)?;\n        // v1 does not have fingerprint, so just set to 0.\n        let fingerprint: u64 = 0;\n        let boolean_start_index = read_u32_from_bytes(bytes, &mut head)?;\n        let next_offset = match read_u32_from_bytes(bytes, &mut head)? {\n            0 => None,\n            val => Some(val),\n        };\n\n        let node = Self { package_name, package_id, fingerprint, boolean_start_index, next_offset };\n        Ok(node)\n    }\n\n    fn from_bytes_v2(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let mut head = 0;\n        let package_name = read_str_from_bytes(bytes, &mut head)?;\n        let package_id = read_u32_from_bytes(bytes, &mut head)?;\n        let fingerprint = read_u64_from_bytes(bytes, &mut head)?;\n        let boolean_start_index = read_u32_from_bytes(bytes, &mut head)?;\n        let next_offset = match read_u32_from_bytes(bytes, &mut head)? {\n            0 => None,\n            val => Some(val),\n        };\n\n        let node = Self { package_name, package_id, fingerprint, boolean_start_index, next_offset };\n        Ok(node)\n    }\n\n    /// Get the bucket index for a package table node, defined it here so the\n    /// construction side (aconfig binary) and consumption side (flag read lib)\n    /// use the same method of hashing\n    pub fn find_bucket_index(package: &str, num_buckets: u32) -> u32 {\n        get_bucket_index(package.as_bytes(), num_buckets)\n    }\n}\n\n/// Package table struct\n#[derive(PartialEq, Serialize, Deserialize)]\npub struct PackageTable {\n    pub header: PackageTableHeader,\n    pub buckets: Vec<Option<u32>>,\n    pub nodes: Vec<PackageTableNode>,\n}\n\n/// Implement debug print trait for package table\nimpl fmt::Debug for PackageTable {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(f, \"Header:\")?;\n        write!(f, \"{:?}\", self.header)?;\n        writeln!(f, \"Buckets:\")?;\n        writeln!(f, \"{:?}\", self.buckets)?;\n        writeln!(f, \"Nodes:\")?;\n        for node in self.nodes.iter() {\n            write!(f, \"{:?}\", node)?;\n        }\n        Ok(())\n    }\n}\n\nimpl PackageTable {\n    /// Serialize to bytes\n    pub fn into_bytes(&self) -> Vec<u8> {\n        [\n            self.header.into_bytes(),\n            self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),\n            self.nodes\n                .iter()\n                .map(|v| v.into_bytes(self.header.version))\n                .collect::<Vec<_>>()\n                .concat(),\n        ]\n        .concat()\n    }\n\n    /// Deserialize from bytes\n    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {\n        let header = PackageTableHeader::from_bytes(bytes)?;\n        let num_packages = header.num_packages;\n        let num_buckets = crate::get_table_size(num_packages)?;\n        let mut head = header.into_bytes().len();\n        let buckets = (0..num_buckets)\n            .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {\n                0 => None,\n                val => Some(val),\n            })\n            .collect();\n        let nodes = (0..num_packages)\n            .map(|_| {\n                let node = PackageTableNode::from_bytes(&bytes[head..], header.version)?;\n                head += node.into_bytes(header.version).len();\n                Ok(node)\n            })\n            .collect::<Result<Vec<_>, AconfigStorageError>>()\n            .map_err(|errmsg| {\n                AconfigStorageError::BytesParseFail(anyhow!(\n                    \"fail to parse package table: {}\",\n                    errmsg\n                ))\n            })?;\n\n        let table = Self { header, buckets, nodes };\n        Ok(table)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::test_utils::create_test_package_table;\n    use crate::{read_u32_from_start_of_bytes, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION};\n\n    #[test]\n    // this test point locks down the table serialization\n    fn test_serialization() {\n        for file_version in 1..=MAX_SUPPORTED_FILE_VERSION {\n            let package_table = create_test_package_table(file_version);\n            let header: &PackageTableHeader = &package_table.header;\n            let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes());\n            assert!(reinterpreted_header.is_ok());\n            assert_eq!(header, &reinterpreted_header.unwrap());\n\n            let nodes: &Vec<PackageTableNode> = &package_table.nodes;\n            for node in nodes.iter() {\n                let reinterpreted_node =\n                    PackageTableNode::from_bytes(&node.into_bytes(header.version), header.version)\n                        .unwrap();\n                assert_eq!(node, &reinterpreted_node);\n            }\n\n            let package_table_bytes = package_table.into_bytes();\n            let reinterpreted_table = PackageTable::from_bytes(&package_table_bytes);\n            assert!(reinterpreted_table.is_ok());\n            assert_eq!(&package_table, &reinterpreted_table.unwrap());\n            assert_eq!(package_table_bytes.len() as u32, header.file_size);\n        }\n    }\n\n    #[test]\n    // this test point locks down that version number should be at the top of serialized\n    // bytes\n    fn test_version_number() {\n        let package_table = create_test_package_table(DEFAULT_FILE_VERSION);\n        let bytes = &package_table.into_bytes();\n        let unpacked_version = read_u32_from_start_of_bytes(bytes).unwrap();\n        assert_eq!(unpacked_version, DEFAULT_FILE_VERSION);\n    }\n\n    #[test]\n    fn test_round_trip_default() {\n        let table: PackageTable = create_test_package_table(DEFAULT_FILE_VERSION);\n        let table_bytes = table.into_bytes();\n\n        let reinterpreted_table = PackageTable::from_bytes(&table_bytes).unwrap();\n\n        assert_eq!(table, reinterpreted_table);\n    }\n\n    #[test]\n    fn test_round_trip_max() {\n        let table: PackageTable = create_test_package_table(MAX_SUPPORTED_FILE_VERSION);\n        let table_bytes = table.into_bytes();\n\n        let reinterpreted_table = PackageTable::from_bytes(&table_bytes).unwrap();\n\n        assert_eq!(table, reinterpreted_table);\n    }\n\n    #[test]\n    // this test point locks down file type check\n    fn test_file_type_check() {\n        let mut package_table = create_test_package_table(DEFAULT_FILE_VERSION);\n        package_table.header.file_type = 123u8;\n        let error = PackageTable::from_bytes(&package_table.into_bytes()).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            format!(\"BytesParseFail(binary file is not a package map)\")\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/src/protos.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// When building with the Android tool-chain\n//\n//   - an external crate `aconfig_storage_metadata_protos` will be generated\n//   - the feature \"cargo\" will be disabled\n//\n// When building with cargo\n//\n//   - a local sub-module will be generated in OUT_DIR and included in this file\n//   - the feature \"cargo\" will be enabled\n//\n// This module hides these differences from the rest of the codebase.\n\n// ---- When building with the Android tool-chain ----\n#[cfg(not(feature = \"cargo\"))]\nmod auto_generated {\n    pub use aconfig_storage_protos::aconfig_storage_metadata as ProtoStorage;\n    pub use ProtoStorage::Storage_file_info as ProtoStorageFileInfo;\n    pub use ProtoStorage::Storage_files as ProtoStorageFiles;\n}\n\n// ---- When building with cargo ----\n#[cfg(feature = \"cargo\")]\nmod auto_generated {\n    // include! statements should be avoided (because they import file contents verbatim), but\n    // because this is only used during local development, and only if using cargo instead of the\n    // Android tool-chain, we allow it\n    include!(concat!(env!(\"OUT_DIR\"), \"/aconfig_storage_protos/mod.rs\"));\n    pub use aconfig_storage_metadata::Storage_file_info as ProtoStorageFileInfo;\n    pub use aconfig_storage_metadata::Storage_files as ProtoStorageFiles;\n}\n\n// ---- Common for both the Android tool-chain and cargo ----\npub use auto_generated::*;\n\nuse anyhow::Result;\nuse protobuf::Message;\nuse std::io::Write;\nuse tempfile::NamedTempFile;\n\npub mod storage_record_pb {\n    use super::*;\n    use anyhow::ensure;\n\n    pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoStorageFiles> {\n        let message: ProtoStorageFiles = protobuf::Message::parse_from_bytes(bytes)?;\n        verify_fields(&message)?;\n        Ok(message)\n    }\n\n    pub fn verify_fields(storage_files: &ProtoStorageFiles) -> Result<()> {\n        for storage_file_info in storage_files.files.iter() {\n            ensure!(\n                !storage_file_info.package_map().is_empty(),\n                \"invalid storage file record: missing package map file for container {}\",\n                storage_file_info.container()\n            );\n            ensure!(\n                !storage_file_info.flag_map().is_empty(),\n                \"invalid storage file record: missing flag map file for container {}\",\n                storage_file_info.container()\n            );\n            ensure!(\n                !storage_file_info.flag_val().is_empty(),\n                \"invalid storage file record: missing flag val file for container {}\",\n                storage_file_info.container()\n            );\n        }\n        Ok(())\n    }\n\n    pub fn get_binary_proto_from_text_proto(text_proto: &str) -> Result<Vec<u8>> {\n        let storage_files: ProtoStorageFiles = protobuf::text_format::parse_from_str(text_proto)?;\n        let mut binary_proto = Vec::new();\n        storage_files.write_to_vec(&mut binary_proto)?;\n        Ok(binary_proto)\n    }\n\n    pub fn write_proto_to_temp_file(text_proto: &str) -> Result<NamedTempFile> {\n        let bytes = get_binary_proto_from_text_proto(text_proto).unwrap();\n        let mut file = NamedTempFile::new()?;\n        let _ = file.write_all(&bytes);\n        Ok(file)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_parse_storage_record_pb() {\n        let text_proto = r#\"\nfiles {\n    version: 0\n    container: \"system\"\n    package_map: \"/system/etc/package.map\"\n    flag_map: \"/system/etc/flag.map\"\n    flag_val: \"/metadata/aconfig/system.val\"\n    timestamp: 12345\n}\nfiles {\n    version: 1\n    container: \"product\"\n    package_map: \"/product/etc/package.map\"\n    flag_map: \"/product/etc/flag.map\"\n    flag_val: \"/metadata/aconfig/product.val\"\n    timestamp: 54321\n}\n\"#;\n        let binary_proto_bytes =\n            storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();\n        let storage_files = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap();\n        assert_eq!(storage_files.files.len(), 2);\n        let system_file = &storage_files.files[0];\n        assert_eq!(system_file.version(), 0);\n        assert_eq!(system_file.container(), \"system\");\n        assert_eq!(system_file.package_map(), \"/system/etc/package.map\");\n        assert_eq!(system_file.flag_map(), \"/system/etc/flag.map\");\n        assert_eq!(system_file.flag_val(), \"/metadata/aconfig/system.val\");\n        assert_eq!(system_file.timestamp(), 12345);\n        let product_file = &storage_files.files[1];\n        assert_eq!(product_file.version(), 1);\n        assert_eq!(product_file.container(), \"product\");\n        assert_eq!(product_file.package_map(), \"/product/etc/package.map\");\n        assert_eq!(product_file.flag_map(), \"/product/etc/flag.map\");\n        assert_eq!(product_file.flag_val(), \"/metadata/aconfig/product.val\");\n        assert_eq!(product_file.timestamp(), 54321);\n    }\n\n    #[test]\n    fn test_parse_invalid_storage_record_pb() {\n        let text_proto = r#\"\nfiles {\n    version: 0\n    container: \"system\"\n    package_map: \"\"\n    flag_map: \"/system/etc/flag.map\"\n    flag_val: \"/metadata/aconfig/system.val\"\n    timestamp: 12345\n}\n\"#;\n        let binary_proto_bytes =\n            storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();\n        let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", err),\n            \"invalid storage file record: missing package map file for container system\"\n        );\n\n        let text_proto = r#\"\nfiles {\n    version: 0\n    container: \"system\"\n    package_map: \"/system/etc/package.map\"\n    flag_map: \"\"\n    flag_val: \"/metadata/aconfig/system.val\"\n    timestamp: 12345\n}\n\"#;\n        let binary_proto_bytes =\n            storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();\n        let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", err),\n            \"invalid storage file record: missing flag map file for container system\"\n        );\n\n        let text_proto = r#\"\nfiles {\n    version: 0\n    container: \"system\"\n    package_map: \"/system/etc/package.map\"\n    flag_map: \"/system/etc/flag.map\"\n    flag_val: \"\"\n    timestamp: 12345\n}\n\"#;\n        let binary_proto_bytes =\n            storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();\n        let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", err),\n            \"invalid storage file record: missing flag val file for container system\"\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/src/sip_hasher13.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! An implementation of SipHash13\n\nuse std::cmp;\nuse std::mem;\nuse std::ptr;\nuse std::slice;\n\nuse std::hash::Hasher;\n\n/// An implementation of SipHash 2-4.\n///\n#[derive(Debug, Clone, Default)]\npub struct SipHasher13 {\n    k0: u64,\n    k1: u64,\n    length: usize, // how many bytes we've processed\n    state: State,  // hash State\n    tail: u64,     // unprocessed bytes le\n    ntail: usize,  // how many bytes in tail are valid\n}\n\n#[derive(Debug, Clone, Copy, Default)]\n#[repr(C)]\nstruct State {\n    // v0, v2 and v1, v3 show up in pairs in the algorithm,\n    // and simd implementations of SipHash will use vectors\n    // of v02 and v13. By placing them in this order in the struct,\n    // the compiler can pick up on just a few simd optimizations by itself.\n    v0: u64,\n    v2: u64,\n    v1: u64,\n    v3: u64,\n}\n\nmacro_rules! compress {\n    ($state:expr) => {{\n        compress!($state.v0, $state.v1, $state.v2, $state.v3)\n    }};\n    ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{\n        $v0 = $v0.wrapping_add($v1);\n        $v1 = $v1.rotate_left(13);\n        $v1 ^= $v0;\n        $v0 = $v0.rotate_left(32);\n        $v2 = $v2.wrapping_add($v3);\n        $v3 = $v3.rotate_left(16);\n        $v3 ^= $v2;\n        $v0 = $v0.wrapping_add($v3);\n        $v3 = $v3.rotate_left(21);\n        $v3 ^= $v0;\n        $v2 = $v2.wrapping_add($v1);\n        $v1 = $v1.rotate_left(17);\n        $v1 ^= $v2;\n        $v2 = $v2.rotate_left(32);\n    }};\n}\n\n/// Load an integer of the desired type from a byte stream, in LE order. Uses\n/// `copy_nonoverlapping` to let the compiler generate the most efficient way\n/// to load it from a possibly unaligned address.\n///\n/// Unsafe because: unchecked indexing at i..i+size_of(int_ty)\nmacro_rules! load_int_le {\n    ($buf:expr, $i:expr, $int_ty:ident) => {{\n        debug_assert!($i + mem::size_of::<$int_ty>() <= $buf.len());\n        let mut data = 0 as $int_ty;\n        ptr::copy_nonoverlapping(\n            $buf.get_unchecked($i),\n            &mut data as *mut _ as *mut u8,\n            mem::size_of::<$int_ty>(),\n        );\n        data.to_le()\n    }};\n}\n\n/// Load an u64 using up to 7 bytes of a byte slice.\n///\n/// Unsafe because: unchecked indexing at start..start+len\n#[inline]\nunsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 {\n    debug_assert!(len < 8);\n    let mut i = 0; // current byte index (from LSB) in the output u64\n    let mut out = 0;\n    if i + 3 < len {\n        out = load_int_le!(buf, start + i, u32) as u64;\n        i += 4;\n    }\n    if i + 1 < len {\n        out |= (load_int_le!(buf, start + i, u16) as u64) << (i * 8);\n        i += 2\n    }\n    if i < len {\n        out |= (*buf.get_unchecked(start + i) as u64) << (i * 8);\n        i += 1;\n    }\n    debug_assert_eq!(i, len);\n    out\n}\n\nimpl SipHasher13 {\n    /// Creates a new `SipHasher13` with the two initial keys set to 0.\n    #[inline]\n    pub fn new() -> SipHasher13 {\n        SipHasher13::new_with_keys(0, 0)\n    }\n\n    /// Creates a `SipHasher13` that is keyed off the provided keys.\n    #[inline]\n    pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 {\n        let mut sip_hasher = SipHasher13 {\n            k0: key0,\n            k1: key1,\n            length: 0,\n            state: State { v0: 0, v1: 0, v2: 0, v3: 0 },\n            tail: 0,\n            ntail: 0,\n        };\n        sip_hasher.reset();\n        sip_hasher\n    }\n\n    #[inline]\n    fn c_rounds(state: &mut State) {\n        compress!(state);\n    }\n\n    #[inline]\n    fn d_rounds(state: &mut State) {\n        compress!(state);\n        compress!(state);\n        compress!(state);\n    }\n\n    #[inline]\n    fn reset(&mut self) {\n        self.length = 0;\n        self.state.v0 = self.k0 ^ 0x736f6d6570736575;\n        self.state.v1 = self.k1 ^ 0x646f72616e646f6d;\n        self.state.v2 = self.k0 ^ 0x6c7967656e657261;\n        self.state.v3 = self.k1 ^ 0x7465646279746573;\n        self.ntail = 0;\n    }\n\n    // Specialized write function that is only valid for buffers with len <= 8.\n    // It's used to force inlining of write_u8 and write_usize, those would normally be inlined\n    // except for composite types (that includes slices and str hashing because of delimiter).\n    // Without this extra push the compiler is very reluctant to inline delimiter writes,\n    // degrading performance substantially for the most common use cases.\n    #[inline]\n    fn short_write(&mut self, msg: &[u8]) {\n        debug_assert!(msg.len() <= 8);\n        let length = msg.len();\n        self.length += length;\n\n        let needed = 8 - self.ntail;\n        let fill = cmp::min(length, needed);\n        if fill == 8 {\n            // safe to call since msg hasn't been loaded\n            self.tail = unsafe { load_int_le!(msg, 0, u64) };\n        } else {\n            // safe to call since msg hasn't been loaded, and fill <= msg.len()\n            self.tail |= unsafe { u8to64_le(msg, 0, fill) } << (8 * self.ntail);\n            if length < needed {\n                self.ntail += length;\n                return;\n            }\n        }\n        self.state.v3 ^= self.tail;\n        Self::c_rounds(&mut self.state);\n        self.state.v0 ^= self.tail;\n\n        // Buffered tail is now flushed, process new input.\n        self.ntail = length - needed;\n        // safe to call since number of `needed` bytes has been loaded\n        // and self.ntail + needed == msg.len()\n        self.tail = unsafe { u8to64_le(msg, needed, self.ntail) };\n    }\n}\n\nimpl Hasher for SipHasher13 {\n    // see short_write comment for explanation\n    #[inline]\n    fn write_usize(&mut self, i: usize) {\n        // safe to call, since convert the pointer to u8\n        let bytes = unsafe {\n            slice::from_raw_parts(&i as *const usize as *const u8, mem::size_of::<usize>())\n        };\n        self.short_write(bytes);\n    }\n\n    // see short_write comment for explanation\n    #[inline]\n    fn write_u8(&mut self, i: u8) {\n        self.short_write(&[i]);\n    }\n\n    #[inline]\n    fn write(&mut self, msg: &[u8]) {\n        let length = msg.len();\n        self.length += length;\n\n        let mut needed = 0;\n\n        // loading unprocessed byte from last write\n        if self.ntail != 0 {\n            needed = 8 - self.ntail;\n            // safe to call, since msg hasn't been processed\n            // and cmp::min(length, needed) < 8\n            self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << 8 * self.ntail;\n            if length < needed {\n                self.ntail += length;\n                return;\n            } else {\n                self.state.v3 ^= self.tail;\n                Self::c_rounds(&mut self.state);\n                self.state.v0 ^= self.tail;\n                self.ntail = 0;\n            }\n        }\n\n        // Buffered tail is now flushed, process new input.\n        let len = length - needed;\n        let left = len & 0x7;\n\n        let mut i = needed;\n        while i < len - left {\n            // safe to call since if i < len - left, it means msg has at least 1 byte to load\n            let mi = unsafe { load_int_le!(msg, i, u64) };\n\n            self.state.v3 ^= mi;\n            Self::c_rounds(&mut self.state);\n            self.state.v0 ^= mi;\n\n            i += 8;\n        }\n\n        // safe to call since if left == 0, since this call will load nothing\n        // if left > 0, it means there are number of `left` bytes in msg\n        self.tail = unsafe { u8to64_le(msg, i, left) };\n        self.ntail = left;\n    }\n\n    #[inline]\n    fn finish(&self) -> u64 {\n        let mut state = self.state;\n\n        let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail;\n\n        state.v3 ^= b;\n        Self::c_rounds(&mut state);\n        state.v0 ^= b;\n\n        state.v2 ^= 0xff;\n        Self::d_rounds(&mut state);\n\n        state.v0 ^ state.v1 ^ state.v2 ^ state.v3\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    use std::hash::{Hash, Hasher};\n    use std::string::String;\n\n    #[test]\n    // this test point locks down the value list serialization\n    fn test_sip_hash13_string_hash() {\n        let mut sip_hash13 = SipHasher13::new();\n        let test_str1 = String::from(\"com.google.android.test\");\n        test_str1.hash(&mut sip_hash13);\n        assert_eq!(17898838669067067585, sip_hash13.finish());\n\n        let test_str2 = String::from(\"adfadfadf adfafadadf 1231241241\");\n        test_str2.hash(&mut sip_hash13);\n        assert_eq!(13543518987672889310, sip_hash13.finish());\n    }\n\n    #[test]\n    fn test_sip_hash13_write() {\n        let mut sip_hash13 = SipHasher13::new();\n        let test_str1 = String::from(\"com.google.android.test\");\n        sip_hash13.write(test_str1.as_bytes());\n        sip_hash13.write_u8(0xff);\n        assert_eq!(17898838669067067585, sip_hash13.finish());\n\n        let mut sip_hash132 = SipHasher13::new();\n        let test_str1 = String::from(\"com.google.android.test\");\n        sip_hash132.write(test_str1.as_bytes());\n        assert_eq!(9685440969685209025, sip_hash132.finish());\n        sip_hash132.write(test_str1.as_bytes());\n        assert_eq!(6719694176662736568, sip_hash132.finish());\n\n        let mut sip_hash133 = SipHasher13::new();\n        let test_str2 = String::from(\"abcdefg\");\n        test_str2.hash(&mut sip_hash133);\n        assert_eq!(2492161047327640297, sip_hash133.finish());\n\n        let mut sip_hash134 = SipHasher13::new();\n        let test_str3 = String::from(\"abcdefgh\");\n        test_str3.hash(&mut sip_hash134);\n        assert_eq!(6689927370435554326, sip_hash134.finish());\n    }\n\n    #[test]\n    fn test_sip_hash13_write_short() {\n        let mut sip_hash13 = SipHasher13::new();\n        sip_hash13.write_u8(0x61);\n        assert_eq!(4644417185603328019, sip_hash13.finish());\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/src/test_utils.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode};\nuse crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};\nuse crate::flag_value::{FlagValueHeader, FlagValueList};\nuse crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};\nuse crate::{AconfigStorageError, StorageFileType, StoredFlagType};\n\nuse anyhow::anyhow;\nuse std::io::Write;\nuse tempfile::NamedTempFile;\n\npub fn create_test_package_table(version: u32) -> PackageTable {\n    let header = PackageTableHeader {\n        version: version,\n        container: String::from(\"mockup\"),\n        file_type: StorageFileType::PackageMap as u8,\n        file_size: match version {\n            1 => 209,\n            2 => 233,\n            _ => panic!(\"Unsupported version.\"),\n        },\n        num_packages: 3,\n        bucket_offset: 31,\n        node_offset: 59,\n    };\n    let buckets: Vec<Option<u32>> = match version {\n        1 => vec![Some(59), None, None, Some(109), None, None, None],\n        2 => vec![Some(59), None, None, Some(117), None, None, None],\n        _ => panic!(\"Unsupported version.\"),\n    };\n    let first_node = PackageTableNode {\n        package_name: String::from(\"com.android.aconfig.storage.test_2\"),\n        package_id: 1,\n        fingerprint: match version {\n            1 => 0,\n            2 => 4431940502274857964u64,\n            _ => panic!(\"Unsupported version.\"),\n        },\n        boolean_start_index: 3,\n        next_offset: None,\n    };\n    let second_node = PackageTableNode {\n        package_name: String::from(\"com.android.aconfig.storage.test_1\"),\n        package_id: 0,\n        fingerprint: match version {\n            1 => 0,\n            2 => 15248948510590158086u64,\n            _ => panic!(\"Unsupported version.\"),\n        },\n        boolean_start_index: 0,\n        next_offset: match version {\n            1 => Some(159),\n            2 => Some(175),\n            _ => panic!(\"Unsupported version.\"),\n        },\n    };\n    let third_node = PackageTableNode {\n        package_name: String::from(\"com.android.aconfig.storage.test_4\"),\n        package_id: 2,\n        fingerprint: match version {\n            1 => 0,\n            2 => 16233229917711622375u64,\n            _ => panic!(\"Unsupported version.\"),\n        },\n        boolean_start_index: 6,\n        next_offset: None,\n    };\n    let nodes = vec![first_node, second_node, third_node];\n    PackageTable { header, buckets, nodes }\n}\n\nimpl FlagTableNode {\n    // create test baseline, syntactic sugar\n    fn new_expected(\n        package_id: u32,\n        flag_name: &str,\n        flag_type: u16,\n        flag_index: u16,\n        next_offset: Option<u32>,\n    ) -> Self {\n        Self {\n            package_id,\n            flag_name: flag_name.to_string(),\n            flag_type: StoredFlagType::try_from(flag_type).unwrap(),\n            flag_index,\n            next_offset,\n        }\n    }\n}\n\npub fn create_test_flag_table(version: u32) -> FlagTable {\n    let header = FlagTableHeader {\n        version: version,\n        container: String::from(\"mockup\"),\n        file_type: StorageFileType::FlagMap as u8,\n        file_size: 321,\n        num_flags: 8,\n        bucket_offset: 31,\n        node_offset: 99,\n    };\n    let buckets: Vec<Option<u32>> = vec![\n        Some(99),\n        Some(125),\n        None,\n        None,\n        None,\n        None,\n        Some(177),\n        Some(204),\n        None,\n        Some(262),\n        None,\n        None,\n        None,\n        None,\n        None,\n        Some(294),\n        None,\n    ];\n    let nodes = vec![\n        FlagTableNode::new_expected(0, \"enabled_ro\", 1, 1, None),\n        FlagTableNode::new_expected(0, \"enabled_rw\", 0, 2, Some(151)),\n        FlagTableNode::new_expected(2, \"enabled_rw\", 0, 1, None),\n        FlagTableNode::new_expected(1, \"disabled_rw\", 0, 0, None),\n        FlagTableNode::new_expected(1, \"enabled_fixed_ro\", 2, 1, Some(236)),\n        FlagTableNode::new_expected(1, \"enabled_ro\", 1, 2, None),\n        FlagTableNode::new_expected(2, \"enabled_fixed_ro\", 2, 0, None),\n        FlagTableNode::new_expected(0, \"disabled_rw\", 0, 0, None),\n    ];\n    FlagTable { header, buckets, nodes }\n}\n\npub fn create_test_flag_value_list(version: u32) -> FlagValueList {\n    let header = FlagValueHeader {\n        version: version,\n        container: String::from(\"mockup\"),\n        file_type: StorageFileType::FlagVal as u8,\n        file_size: 35,\n        num_flags: 8,\n        boolean_value_offset: 27,\n    };\n    let booleans: Vec<bool> = vec![false, true, true, false, true, true, true, true];\n    FlagValueList { header, booleans }\n}\n\npub fn create_test_flag_info_list(version: u32) -> FlagInfoList {\n    let header = FlagInfoHeader {\n        version: version,\n        container: String::from(\"mockup\"),\n        file_type: StorageFileType::FlagInfo as u8,\n        file_size: 35,\n        num_flags: 8,\n        boolean_flag_offset: 27,\n    };\n    let is_flag_rw = [true, false, true, true, false, false, false, true];\n    let nodes = is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect();\n    FlagInfoList { header, nodes }\n}\n\npub fn write_bytes_to_temp_file(bytes: &[u8]) -> Result<NamedTempFile, AconfigStorageError> {\n    let mut file = NamedTempFile::new().map_err(|_| {\n        AconfigStorageError::FileCreationFail(anyhow!(\"Failed to create temp file\"))\n    })?;\n    let _ = file.write_all(&bytes);\n    Ok(file)\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\n/**\n * Exception thrown when an error occurs while accessing Aconfig Storage.\n *\n * <p>This exception indicates a general problem with Aconfig Storage, such as an inability to read\n * or write data.\n */\npublic class AconfigStorageException extends RuntimeException {\n\n    /** Generic error code indicating an unspecified Aconfig Storage error. */\n    public static final int ERROR_GENERIC = 0;\n\n    /** Error code indicating that the Aconfig Storage system is not found on the device. */\n    public static final int ERROR_STORAGE_SYSTEM_NOT_FOUND = 1;\n\n    /** Error code indicating that the requested configuration package is not found. */\n    public static final int ERROR_PACKAGE_NOT_FOUND = 2;\n\n    /** Error code indicating that the specified container is not found. */\n    public static final int ERROR_CONTAINER_NOT_FOUND = 3;\n\n    /** Error code indicating that there was an error reading the Aconfig Storage file. */\n    public static final int ERROR_CANNOT_READ_STORAGE_FILE = 4;\n\n    public static final int ERROR_FILE_FINGERPRINT_MISMATCH = 5;\n\n    private final int mErrorCode;\n\n    /**\n     * Constructs a new {@code AconfigStorageException} with a generic error code and the specified\n     * detail message.\n     *\n     * @param msg The detail message for this exception.\n     */\n    public AconfigStorageException(String msg) {\n        super(msg);\n        mErrorCode = ERROR_GENERIC;\n    }\n\n    /**\n     * Constructs a new {@code AconfigStorageException} with a generic error code, the specified\n     * detail message, and cause.\n     *\n     * @param msg The detail message for this exception.\n     * @param cause The cause of this exception.\n     */\n    public AconfigStorageException(String msg, Throwable cause) {\n        super(msg, cause);\n        mErrorCode = ERROR_GENERIC;\n    }\n\n    /**\n     * Constructs a new {@code AconfigStorageException} with the specified error code and detail\n     * message.\n     *\n     * @param errorCode The error code for this exception.\n     * @param msg The detail message for this exception.\n     */\n    public AconfigStorageException(int errorCode, String msg) {\n        super(msg);\n        mErrorCode = errorCode;\n    }\n\n    /**\n     * Constructs a new {@code AconfigStorageException} with the specified error code, detail\n     * message, and cause.\n     *\n     * @param errorCode The error code for this exception.\n     * @param msg The detail message for this exception.\n     * @param cause The cause of this exception.\n     */\n    public AconfigStorageException(int errorCode, String msg, Throwable cause) {\n        super(msg, cause);\n        mErrorCode = errorCode;\n    }\n\n    /**\n     * Returns the error code associated with this exception.\n     *\n     * @return The error code.\n     */\n    public int getErrorCode() {\n        return mErrorCode;\n    }\n\n    /**\n     * Returns the error message for this exception, including the error code and the original\n     * message.\n     *\n     * @return The error message.\n     */\n    @Override\n    public String getMessage() {\n        return errorString() + \": \" + super.getMessage();\n    }\n\n    /**\n     * Returns a string representation of the error code.\n     *\n     * @return The error code string.\n     */\n    private String errorString() {\n        switch (mErrorCode) {\n            case ERROR_GENERIC:\n                return \"ERROR_GENERIC\";\n            case ERROR_STORAGE_SYSTEM_NOT_FOUND:\n                return \"ERROR_STORAGE_SYSTEM_NOT_FOUND\";\n            case ERROR_PACKAGE_NOT_FOUND:\n                return \"ERROR_PACKAGE_NOT_FOUND\";\n            case ERROR_CONTAINER_NOT_FOUND:\n                return \"ERROR_CONTAINER_NOT_FOUND\";\n            case ERROR_CANNOT_READ_STORAGE_FILE:\n                return \"ERROR_CANNOT_READ_STORAGE_FILE\";\n            case ERROR_FILE_FINGERPRINT_MISMATCH:\n                return \"ERROR_FILE_FINGERPRINT_MISMATCH\";\n            default:\n                return \"<Unknown error code \" + mErrorCode + \">\";\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Objects;\n\npublic class ByteBufferReader {\n\n    private ByteBuffer mByteBuffer;\n    private int mPosition;\n\n    public ByteBufferReader(ByteBuffer byteBuffer) {\n        this.mByteBuffer = byteBuffer;\n        this.mByteBuffer.order(ByteOrder.LITTLE_ENDIAN);\n    }\n\n    public int readByte() {\n        return Byte.toUnsignedInt(mByteBuffer.get(nextGetIndex(1)));\n    }\n\n    public int readShort() {\n        return Short.toUnsignedInt(mByteBuffer.getShort(nextGetIndex(2)));\n    }\n\n    public int readInt() {\n        return this.mByteBuffer.getInt(nextGetIndex(4));\n    }\n\n    public long readLong() {\n        return this.mByteBuffer.getLong(nextGetIndex(8));\n    }\n\n    public String readString() {\n        int length = readInt();\n        if (length > 1024) {\n            throw new AconfigStorageException(\n                    \"String length exceeds maximum allowed size (1024 bytes): \" + length);\n        }\n        byte[] bytes = new byte[length];\n        getArray(nextGetIndex(length), bytes, 0, length);\n        return new String(bytes, StandardCharsets.UTF_8);\n    }\n\n    public int readByte(int i) {\n        return Byte.toUnsignedInt(mByteBuffer.get(i));\n    }\n\n    public void position(int newPosition) {\n        mPosition = newPosition;\n    }\n\n    public int position() {\n        return mPosition;\n    }\n\n    private int nextGetIndex(int nb) {\n        int p = mPosition;\n        mPosition += nb;\n        return p;\n    }\n\n    private void getArray(int index, byte[] dst, int offset, int length) {\n        Objects.checkFromIndexSize(index, length, mByteBuffer.limit());\n        Objects.checkFromIndexSize(offset, length, dst.length);\n\n        int end = offset + length;\n        for (int i = offset, j = index; i < end; i++, j++) {\n            dst[i] = mByteBuffer.get(j);\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FileType.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\npublic enum FileType {\n    PACKAGE_MAP(0),\n    FLAG_MAP(1),\n    FLAG_VAL(2),\n    FLAG_INFO(3);\n\n    public final int type;\n\n    FileType(int type) {\n        this.type = type;\n    }\n\n    public static FileType fromInt(int index) {\n        switch (index) {\n            case 0:\n                return PACKAGE_MAP;\n            case 1:\n                return FLAG_MAP;\n            case 2:\n                return FLAG_VAL;\n            case 3:\n                return FLAG_INFO;\n            default:\n                return null;\n        }\n    }\n\n    @Override\n    public String toString() {\n        switch (type) {\n            case 0:\n                return \"PACKAGE_MAP\";\n            case 1:\n                return \"FLAG_MAP\";\n            case 2:\n                return \"FLAG_VAL\";\n            case 3:\n                return \"FLAG_INFO\";\n            default:\n                return \"unrecognized type\";\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagTable.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\nimport java.nio.ByteBuffer;\nimport java.util.Objects;\n\npublic class FlagTable {\n\n    private Header mHeader;\n    private ByteBuffer mBuffer;\n\n    public static FlagTable fromBytes(ByteBuffer bytes) {\n        FlagTable flagTable = new FlagTable();\n        flagTable.mBuffer = bytes;\n        flagTable.mHeader = Header.fromBytes(new ByteBufferReader(bytes));\n\n        return flagTable;\n    }\n\n    public Node get(int packageId, String flagName) {\n        int numBuckets = (mHeader.mNodeOffset - mHeader.mBucketOffset) / 4;\n        int bucketIndex = TableUtils.getBucketIndex(makeKey(packageId, flagName), numBuckets);\n        int newPosition = mHeader.mBucketOffset + bucketIndex * 4;\n        if (newPosition >= mHeader.mNodeOffset) {\n            return null;\n        }\n        ByteBufferReader reader = new ByteBufferReader(mBuffer) ;\n        reader.position(newPosition);\n        int nodeIndex = reader.readInt();\n        if (nodeIndex < mHeader.mNodeOffset || nodeIndex >= mHeader.mFileSize) {\n            return null;\n        }\n\n        while (nodeIndex != -1) {\n            reader.position(nodeIndex);\n            Node node = Node.fromBytes(reader);\n            if (Objects.equals(flagName, node.mFlagName) && packageId == node.mPackageId) {\n                return node;\n            }\n            nodeIndex = node.mNextOffset;\n        }\n\n        return null;\n    }\n\n    public Header getHeader() {\n        return mHeader;\n    }\n\n    private static byte[] makeKey(int packageId, String flagName) {\n        StringBuilder ret = new StringBuilder();\n        return ret.append(packageId).append('/').append(flagName).toString().getBytes(UTF_8);\n    }\n\n    public static class Header {\n\n        private int mVersion;\n        private String mContainer;\n        private FileType mFileType;\n        private int mFileSize;\n        private int mNumFlags;\n        private int mBucketOffset;\n        private int mNodeOffset;\n\n        public static Header fromBytes(ByteBufferReader reader) {\n            Header header = new Header();\n            header.mVersion = reader.readInt();\n            header.mContainer = reader.readString();\n            header.mFileType = FileType.fromInt(reader.readByte());\n            header.mFileSize = reader.readInt();\n            header.mNumFlags = reader.readInt();\n            header.mBucketOffset = reader.readInt();\n            header.mNodeOffset = reader.readInt();\n\n            if (header.mFileType != FileType.FLAG_MAP) {\n                throw new AconfigStorageException(\"binary file is not a flag map\");\n            }\n\n            return header;\n        }\n\n        public int getVersion() {\n            return mVersion;\n        }\n\n        public String getContainer() {\n            return mContainer;\n        }\n\n        public FileType getFileType() {\n            return mFileType;\n        }\n\n        public int getFileSize() {\n            return mFileSize;\n        }\n\n        public int getNumFlags() {\n            return mNumFlags;\n        }\n\n        public int getBucketOffset() {\n            return mBucketOffset;\n        }\n\n        public int getNodeOffset() {\n            return mNodeOffset;\n        }\n    }\n\n    public static class Node {\n\n        private String mFlagName;\n        private FlagType mFlagType;\n        private int mPackageId;\n        private int mFlagIndex;\n        private int mNextOffset;\n\n        public static Node fromBytes(ByteBufferReader reader) {\n            Node node = new Node();\n            node.mPackageId = reader.readInt();\n            node.mFlagName = reader.readString();\n            node.mFlagType = FlagType.fromInt(reader.readShort());\n            node.mFlagIndex = reader.readShort();\n            node.mNextOffset = reader.readInt();\n            node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;\n            return node;\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(mFlagName, mFlagType, mPackageId, mFlagIndex, mNextOffset);\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj) {\n                return true;\n            }\n\n            if (obj == null || !(obj instanceof Node)) {\n                return false;\n            }\n\n            Node other = (Node) obj;\n            return Objects.equals(mFlagName, other.mFlagName)\n                    && Objects.equals(mFlagType, other.mFlagType)\n                    && mPackageId == other.mPackageId\n                    && mFlagIndex == other.mFlagIndex\n                    && mNextOffset == other.mNextOffset;\n        }\n\n        public String getFlagName() {\n            return mFlagName;\n        }\n\n        public FlagType getFlagType() {\n            return mFlagType;\n        }\n\n        public int getPackageId() {\n            return mPackageId;\n        }\n\n        public int getFlagIndex() {\n            return mFlagIndex;\n        }\n\n        public int getNextOffset() {\n            return mNextOffset;\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagType.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\npublic enum FlagType {\n    ReadWriteBoolean (0),\n    ReadOnlyBoolean(1),\n    FixedReadOnlyBoolean(2);\n\n    public final int type;\n\n    FlagType(int type) {\n        this.type = type;\n    }\n\n    public static FlagType fromInt(int index) {\n        switch (index) {\n            case 0:\n                return ReadWriteBoolean;\n            case 1:\n                return ReadOnlyBoolean;\n            case 2:\n                return FixedReadOnlyBoolean;\n            default:\n                return null;\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagValueList.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\nimport java.nio.ByteBuffer;\n\npublic class FlagValueList {\n\n    private Header mHeader;\n    private ByteBufferReader mReader;\n\n    public static FlagValueList fromBytes(ByteBuffer bytes) {\n        FlagValueList flagValueList = new FlagValueList();\n        flagValueList.mReader = new ByteBufferReader(bytes);\n        flagValueList.mHeader = Header.fromBytes(flagValueList.mReader);\n        return flagValueList;\n    }\n\n    public boolean getBoolean(int index) {\n        return mReader.readByte(mHeader.mBooleanValueOffset + index) == 1;\n    }\n\n    public Header getHeader() {\n        return mHeader;\n    }\n\n    public int size() {\n        return mHeader.mNumFlags;\n    }\n\n    public static class Header {\n\n        private int mVersion;\n        private String mContainer;\n        private FileType mFileType;\n        private int mFileSize;\n        private int mNumFlags;\n        private int mBooleanValueOffset;\n\n        public static Header fromBytes(ByteBufferReader reader) {\n            Header header = new Header();\n            header.mVersion = reader.readInt();\n            header.mContainer = reader.readString();\n            header.mFileType = FileType.fromInt(reader.readByte());\n            header.mFileSize = reader.readInt();\n            header.mNumFlags = reader.readInt();\n            header.mBooleanValueOffset = reader.readInt();\n\n            if (header.mFileType != FileType.FLAG_VAL) {\n                throw new AconfigStorageException(\"binary file is not a flag value file\");\n            }\n\n            return header;\n        }\n\n        public int getVersion() {\n            return mVersion;\n        }\n\n        public String getContainer() {\n            return mContainer;\n        }\n\n        public FileType getFileType() {\n            return mFileType;\n        }\n\n        public int getFileSize() {\n            return mFileSize;\n        }\n\n        public int getNumFlags() {\n            return mNumFlags;\n        }\n\n        public int getBooleanValueOffset() {\n            return mBooleanValueOffset;\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class PackageTable {\n\n    private static final int FINGERPRINT_BYTES = 8;\n    // int: mPackageId + int: mBooleanStartIndex + int: mNextOffset\n    private static final int NODE_SKIP_BYTES = 12;\n\n    private Header mHeader;\n    private ByteBuffer mBuffer;\n\n    public static PackageTable fromBytes(ByteBuffer bytes) {\n        PackageTable packageTable = new PackageTable();\n        packageTable.mBuffer = bytes;\n        packageTable.mHeader = Header.fromBytes(new ByteBufferReader(bytes));\n\n        return packageTable;\n    }\n\n    public Node get(String packageName) {\n        int numBuckets = (mHeader.mNodeOffset - mHeader.mBucketOffset) / 4;\n        int bucketIndex = TableUtils.getBucketIndex(packageName.getBytes(UTF_8), numBuckets);\n        int newPosition = mHeader.mBucketOffset + bucketIndex * 4;\n        if (newPosition >= mHeader.mNodeOffset) {\n            return null;\n        }\n        ByteBufferReader reader = new ByteBufferReader(mBuffer);\n        reader.position(newPosition);\n        int nodeIndex = reader.readInt();\n\n        if (nodeIndex < mHeader.mNodeOffset || nodeIndex >= mHeader.mFileSize) {\n            return null;\n        }\n\n        while (nodeIndex != -1) {\n            reader.position(nodeIndex);\n            Node node = Node.fromBytes(reader, mHeader.mVersion);\n            if (Objects.equals(packageName, node.mPackageName)) {\n                return node;\n            }\n            nodeIndex = node.mNextOffset;\n        }\n\n        return null;\n    }\n\n    public List<String> getPackageList() {\n        List<String> list = new ArrayList<>(mHeader.mNumPackages);\n        ByteBufferReader reader = new ByteBufferReader(mBuffer);\n        reader.position(mHeader.mNodeOffset);\n        int fingerprintBytes = mHeader.mVersion == 1 ? 0 : FINGERPRINT_BYTES;\n        int skipBytes = fingerprintBytes + NODE_SKIP_BYTES;\n        for (int i = 0; i < mHeader.mNumPackages; i++) {\n            list.add(reader.readString());\n            reader.position(reader.position() + skipBytes);\n        }\n        return list;\n    }\n\n    public Header getHeader() {\n        return mHeader;\n    }\n\n    public static class Header {\n\n        private int mVersion;\n        private String mContainer;\n        private FileType mFileType;\n        private int mFileSize;\n        private int mNumPackages;\n        private int mBucketOffset;\n        private int mNodeOffset;\n\n        private static Header fromBytes(ByteBufferReader reader) {\n            Header header = new Header();\n            header.mVersion = reader.readInt();\n            header.mContainer = reader.readString();\n            header.mFileType = FileType.fromInt(reader.readByte());\n            header.mFileSize = reader.readInt();\n            header.mNumPackages = reader.readInt();\n            header.mBucketOffset = reader.readInt();\n            header.mNodeOffset = reader.readInt();\n\n            if (header.mFileType != FileType.PACKAGE_MAP) {\n                throw new AconfigStorageException(\"binary file is not a package map\");\n            }\n\n            return header;\n        }\n\n        public int getVersion() {\n            return mVersion;\n        }\n\n        public String getContainer() {\n            return mContainer;\n        }\n\n        public FileType getFileType() {\n            return mFileType;\n        }\n\n        public int getFileSize() {\n            return mFileSize;\n        }\n\n        public int getNumPackages() {\n            return mNumPackages;\n        }\n\n        public int getBucketOffset() {\n            return mBucketOffset;\n        }\n\n        public int getNodeOffset() {\n            return mNodeOffset;\n        }\n    }\n\n    public static class Node {\n\n        private String mPackageName;\n        private int mPackageId;\n        private long mPackageFingerprint;\n        private int mBooleanStartIndex;\n        private int mNextOffset;\n        private boolean mHasPackageFingerprint;\n\n        private static Node fromBytes(ByteBufferReader reader, int version) {\n            switch (version) {\n                case 1:\n                    return fromBytesV1(reader);\n                case 2:\n                    return fromBytesV2(reader);\n                default:\n                    // Do we want to throw here?\n                    return new Node();\n            }\n        }\n\n        private static Node fromBytesV1(ByteBufferReader reader) {\n            Node node = new Node();\n            node.mPackageName = reader.readString();\n            node.mPackageId = reader.readInt();\n            node.mBooleanStartIndex = reader.readInt();\n            node.mNextOffset = reader.readInt();\n            node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;\n            return node;\n        }\n\n        private static Node fromBytesV2(ByteBufferReader reader) {\n            Node node = new Node();\n            node.mPackageName = reader.readString();\n            node.mPackageId = reader.readInt();\n            node.mPackageFingerprint = reader.readLong();\n            node.mBooleanStartIndex = reader.readInt();\n            node.mNextOffset = reader.readInt();\n            node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;\n            node.mHasPackageFingerprint = true;\n            return node;\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(mPackageName, mPackageId, mBooleanStartIndex, mNextOffset);\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj) {\n                return true;\n            }\n\n            if (obj == null || !(obj instanceof Node)) {\n                return false;\n            }\n\n            Node other = (Node) obj;\n            return Objects.equals(mPackageName, other.mPackageName)\n                    && mPackageId == other.mPackageId\n                    && mBooleanStartIndex == other.mBooleanStartIndex\n                    && mNextOffset == other.mNextOffset;\n        }\n\n        public String getPackageName() {\n            return mPackageName;\n        }\n\n        public int getPackageId() {\n            return mPackageId;\n        }\n\n        public long getPackageFingerprint() {\n            return mPackageFingerprint;\n        }\n\n        public int getBooleanStartIndex() {\n            return mBooleanStartIndex;\n        }\n\n        public int getNextOffset() {\n            return mNextOffset;\n        }\n\n        public boolean hasPackageFingerprint() {\n            return mHasPackageFingerprint;\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/SipHasher13.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\npublic class SipHasher13 {\n    static class State {\n        private long v0;\n        private long v2;\n        private long v1;\n        private long v3;\n\n        public State(long k0, long k1) {\n            v0 = k0 ^ 0x736f6d6570736575L;\n            v1 = k1 ^ 0x646f72616e646f6dL;\n            v2 = k0 ^ 0x6c7967656e657261L;\n            v3 = k1 ^ 0x7465646279746573L;\n        }\n\n        public void compress(long m) {\n            v3 ^= m;\n            cRounds();\n            v0 ^= m;\n        }\n\n        public long finish() {\n            v2 ^= 0xff;\n            dRounds();\n            return v0 ^ v1 ^ v2 ^ v3;\n        }\n\n        private void cRounds() {\n            v0 += v1;\n            v1 = Long.rotateLeft(v1, 13);\n            v1 ^= v0;\n            v0 = Long.rotateLeft(v0, 32);\n            v2 += v3;\n            v3 = Long.rotateLeft(v3, 16);\n            v3 ^= v2;\n            v0 += v3;\n            v3 = Long.rotateLeft(v3, 21);\n            v3 ^= v0;\n            v2 += v1;\n            v1 = Long.rotateLeft(v1, 17);\n            v1 ^= v2;\n            v2 = Long.rotateLeft(v2, 32);\n        }\n\n        private void dRounds() {\n            for (int i = 0; i < 3; i++) {\n                v0 += v1;\n                v1 = Long.rotateLeft(v1, 13);\n                v1 ^= v0;\n                v0 = Long.rotateLeft(v0, 32);\n                v2 += v3;\n                v3 = Long.rotateLeft(v3, 16);\n                v3 ^= v2;\n                v0 += v3;\n                v3 = Long.rotateLeft(v3, 21);\n                v3 ^= v0;\n                v2 += v1;\n                v1 = Long.rotateLeft(v1, 17);\n                v1 ^= v2;\n                v2 = Long.rotateLeft(v2, 32);\n            }\n        }\n    }\n\n    public static long hash(byte[] data) {\n        State state = new State(0, 0);\n        int len = data.length;\n        int left = len & 0x7;\n        int index = 0;\n\n        while (index < len - left) {\n            long mi = loadLe(data, index, 8);\n            index += 8;\n            state.compress(mi);\n        }\n\n        // padding the end with 0xff to be consistent with rust\n        long m = (0xffL << (left * 8)) | loadLe(data, index, left);\n        if (left == 0x7) {\n            // compress the m w-2\n            state.compress(m);\n            m = 0L;\n        }\n        // len adds 1 since padded 0xff\n        m |= (((len + 1) & 0xffL) << 56);\n        state.compress(m);\n\n        return state.finish();\n    }\n\n    private static long loadLe(byte[] data, int offset, int size) {\n        long m = 0;\n        for (int i = 0; i < size; i++) {\n            m |= (data[i + offset] & 0xffL) << (i * 8);\n        }\n        return m;\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/StorageFileProvider.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\nimport java.io.Closeable;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.nio.file.DirectoryStream;\nimport java.nio.file.Files;\nimport java.nio.file.NoSuchFileException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardOpenOption;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\n/** @hide */\npublic class StorageFileProvider {\n\n    private static final String DEFAULT_MAP_PATH = \"/metadata/aconfig/maps/\";\n    private static final String DEFAULT_BOOT_PATH = \"/metadata/aconfig/boot/\";\n    private static final String PMAP_FILE_EXT = \".package.map\";\n    private static final String FMAP_FILE_EXT = \".flag.map\";\n    private static final String VAL_FILE_EXT = \".val\";\n    private static final StorageFileProvider DEFAULT_INSTANCE =\n            new StorageFileProvider(DEFAULT_MAP_PATH, DEFAULT_BOOT_PATH);\n\n    private final String mMapPath;\n    private final String mBootPath;\n\n    /** @hide */\n    public static StorageFileProvider getDefaultProvider() {\n        return DEFAULT_INSTANCE;\n    }\n\n    /** @hide */\n    public StorageFileProvider(String mapPath, String bootPath) {\n        mMapPath = mapPath;\n        mBootPath = bootPath;\n    }\n\n    /** @hide */\n    public List<String> listContainers(String[] excludes) {\n        List<String> result = new ArrayList<>();\n        Set<String> set = new HashSet<>(Arrays.asList(excludes));\n\n        try {\n            DirectoryStream<Path> stream =\n                    Files.newDirectoryStream(Paths.get(mMapPath), \"*\" + PMAP_FILE_EXT);\n            for (Path entry : stream) {\n                String fileName = entry.getFileName().toString();\n                String container =\n                        fileName.substring(0, fileName.length() - PMAP_FILE_EXT.length());\n                if (!set.contains(container)) {\n                    result.add(container);\n                }\n            }\n        } catch (NoSuchFileException e) {\n            return result;\n        } catch (Exception e) {\n            throw new AconfigStorageException(\n                    String.format(\"Fail to list map files in path %s\", mMapPath), e);\n        }\n\n        return result;\n    }\n\n    /** @hide */\n    public PackageTable getPackageTable(String container) {\n        return PackageTable.fromBytes(\n                mapStorageFile(\n                        Paths.get(mMapPath, container + PMAP_FILE_EXT), FileType.PACKAGE_MAP));\n    }\n\n    /** @hide */\n    public FlagTable getFlagTable(String container) {\n        return FlagTable.fromBytes(\n                mapStorageFile(Paths.get(mMapPath, container + FMAP_FILE_EXT), FileType.FLAG_MAP));\n    }\n\n    /** @hide */\n    public FlagValueList getFlagValueList(String container) {\n        return FlagValueList.fromBytes(\n                mapStorageFile(Paths.get(mBootPath, container + VAL_FILE_EXT), FileType.FLAG_VAL));\n    }\n\n    // Map a storage file given file path\n    private static MappedByteBuffer mapStorageFile(Path file, FileType type) {\n        FileChannel channel = null;\n        try {\n            channel = FileChannel.open(file, StandardOpenOption.READ);\n            return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());\n        } catch (Exception e) {\n            throw new AconfigStorageException(\n                    AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,\n                    String.format(\"Fail to mmap storage %s file %s\", type.toString(), file),\n                    e);\n        } finally {\n            quietlyDispose(channel);\n        }\n    }\n\n    private static void quietlyDispose(Closeable closable) {\n        try {\n            if (closable != null) {\n                closable.close();\n            }\n        } catch (Exception e) {\n            // no need to care, at least as of now\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/TableUtils.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\npublic class TableUtils {\n\n    private static final int[] HASH_PRIMES =\n            new int[] {\n                7,\n                17,\n                29,\n                53,\n                97,\n                193,\n                389,\n                769,\n                1543,\n                3079,\n                6151,\n                12289,\n                24593,\n                49157,\n                98317,\n                196613,\n                393241,\n                786433,\n                1572869,\n                3145739,\n                6291469,\n                12582917,\n                25165843,\n                50331653,\n                100663319,\n                201326611,\n                402653189,\n                805306457,\n                1610612741\n            };\n\n    public static int getTableSize(int numEntries) {\n        for (int i : HASH_PRIMES) {\n            if (i < 2 * numEntries) continue;\n            return i;\n        }\n        throw new AconfigStorageException(\"Number of items in a hash table exceeds limit\");\n    }\n\n    public static int getBucketIndex(byte[] val, int numBuckets) {\n        long hashVal = SipHasher13.hash(val);\n        return (int) Long.remainderUnsigned(hashVal, numBuckets);\n    }\n\n     public static class StorageFilesBundle {\n        public final PackageTable packageTable;\n        public final FlagTable flagTable;\n        public final FlagValueList flagValueList;\n\n        public StorageFilesBundle (PackageTable pTable, FlagTable fTable, FlagValueList fValueList) {\n            this.packageTable = pTable;\n            this.flagTable = fTable;\n            this.flagValueList = fValueList;\n        }\n     }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/Android.bp",
    "content": "cc_test {\n    name: \"aconfig_storage_file.test.cpp\",\n    team: \"trendy_team_android_core_experiments\",\n    srcs: [\n        \"storage_file_test.cpp\",\n    ],\n    static_libs: [\n        \"libgmock\",\n        \"libaconfig_storage_file_cc\",\n        \"libbase\",\n    ],\n    data: [\n        \"data/v1/package_v1.map\",\n        \"data/v1/flag_v1.map\",\n        \"data/v1/flag_v1.val\",\n        \"data/v1/flag_v1.info\",\n        \"data/v2/package_v2.map\",\n        \"data/v2/flag_v2.map\",\n        \"data/v2/flag_v2.val\",\n        \"data/v2/flag_v2.info\",\n    ],\n    test_suites: [\n        \"device-tests\",\n        \"general-tests\",\n    ],\n}\n\nandroid_test {\n    name: \"aconfig_storage_file.test.java\",\n    team: \"trendy_team_android_core_experiments\",\n    srcs: [\n        \"srcs/**/*.java\",\n    ],\n    static_libs: [\n        \"androidx.test.runner\",\n        \"junit\",\n        \"aconfig_storage_file_java\",\n    ],\n    test_config: \"AndroidStorageJaveTest.xml\",\n    sdk_version: \"test_current\",\n    data: [\n        \"data/v1/package_v1.map\",\n        \"data/v1/flag_v1.map\",\n        \"data/v1/flag_v1.val\",\n        \"data/v1/flag_v1.info\",\n        \"data/v2/package_v2.map\",\n        \"data/v2/flag_v2.map\",\n        \"data/v2/flag_v2.val\",\n        \"data/v2/flag_v2.info\",\n    ],\n    test_suites: [\n        \"general-tests\",\n    ],\n    jarjar_rules: \"jarjar.txt\",\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright (C) 2024 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"android.aconfig.storage.test\">\n    <application>\n        <uses-library android:name=\"android.test.runner\" />\n    </application>\n\n    <instrumentation android:name=\"androidx.test.runner.AndroidJUnitRunner\"\n                     android:targetPackage=\"android.aconfig.storage.test\" />\n\n</manifest>\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright (C) 2024 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<configuration description=\"Test aconfig storage java tests\">\n    <target_preparer class=\"com.android.tradefed.targetprep.suite.SuiteApkInstaller\">\n        <option name=\"cleanup-apks\" value=\"true\" />\n        <option name=\"test-file-name\" value=\"aconfig_storage_file.test.java.apk\" />\n    </target_preparer>\n    <target_preparer class=\"com.android.compatibility.common.tradefed.targetprep.FilePusher\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"package_v1.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/mock.v1.package.map\" />\n        <option name=\"push\" value=\"flag_v1.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/mock.v1.flag.map\" />\n        <option name=\"push\" value=\"flag_v1.val->/data/local/tmp/aconfig_storage_file_test_java/testdata/mock.v1.val\" />\n        <option name=\"push\" value=\"flag_v1.info->/data/local/tmp/aconfig_storage_file_test_java/testdata/mock.v1.info\" />\n        <option name=\"push\" value=\"package_v2.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/mock.v2.package.map\" />\n        <option name=\"push\" value=\"flag_v2.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/mock.v2.flag.map\" />\n        <option name=\"push\" value=\"flag_v2.val->/data/local/tmp/aconfig_storage_file_test_java/testdata/mock.v2.val\" />\n        <option name=\"push\" value=\"flag_v2.info->/data/local/tmp/aconfig_storage_file_test_java/testdata/mock.v2.info\" />\n        <option name=\"post-push\" value=\"chmod +r /data/local/tmp/aconfig_storage_file_test_java/testdata/\" />\n    </target_preparer>\n    <test class=\"com.android.tradefed.testtype.AndroidJUnitTest\" >\n        <option name=\"package\" value=\"android.aconfig.storage.test\" />\n        <option name=\"runtime-hint\" value=\"1m\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/jarjar.txt",
    "content": "rule android.aconfig.storage.AconfigStorageException android.aconfig.storage.test.AconfigStorageException\nrule android.aconfig.storage.FlagTable android.aconfig.storage.test.FlagTable\nrule android.aconfig.storage.PackageTable android.aconfig.storage.test.PackageTable\nrule android.aconfig.storage.ByteBufferReader android.aconfig.storage.test.ByteBufferReader\nrule android.aconfig.storage.FlagType android.aconfig.storage.test.FlagType\nrule android.aconfig.storage.SipHasher13 android.aconfig.storage.test.SipHasher13\nrule android.aconfig.storage.FileType android.aconfig.storage.test.FileType\nrule android.aconfig.storage.FlagValueList android.aconfig.storage.test.FlagValueList\nrule android.aconfig.storage.TableUtils android.aconfig.storage.test.TableUtils\nrule android.aconfig.storage.AconfigPackageImpl android.aconfig.storage.test.AconfigPackageImpl\nrule android.aconfig.storage.StorageFileProvider android.aconfig.storage.test.StorageFileProvider\n\n\nrule android.aconfig.storage.FlagTable$* android.aconfig.storage.test.FlagTable$@1\nrule android.aconfig.storage.PackageTable$* android.aconfig.storage.test.PackageTable$@1\nrule android.aconfig.storage.FlagValueList$* android.aconfig.storage.test.FlagValueList@1\nrule android.aconfig.storage.SipHasher13$* android.aconfig.storage.test.SipHasher13@1\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/srcs/ByteBufferReaderTest.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage.test;\n\nimport static org.junit.Assert.assertEquals;\n\nimport android.aconfig.storage.ByteBufferReader;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\n\n@RunWith(JUnit4.class)\npublic class ByteBufferReaderTest {\n\n    @Test\n    public void testReadByte() {\n        ByteBuffer buffer = ByteBuffer.allocate(1);\n        byte expect = 10;\n        buffer.put(expect).rewind();\n\n        ByteBufferReader reader = new ByteBufferReader(buffer);\n        assertEquals(expect, reader.readByte());\n    }\n\n    @Test\n    public void testReadShort() {\n        ByteBuffer buffer = ByteBuffer.allocate(4);\n        buffer.order(ByteOrder.LITTLE_ENDIAN);\n        short expect = Short.MAX_VALUE;\n        buffer.putShort(expect).rewind();\n\n        ByteBufferReader reader = new ByteBufferReader(buffer);\n        assertEquals(expect, reader.readShort());\n    }\n\n    @Test\n    public void testReadInt() {\n        ByteBuffer buffer = ByteBuffer.allocate(4);\n        buffer.order(ByteOrder.LITTLE_ENDIAN);\n        int expect = 10000;\n        buffer.putInt(expect).rewind();\n\n        ByteBufferReader reader = new ByteBufferReader(buffer);\n        assertEquals(expect, reader.readInt());\n    }\n\n    @Test\n    public void testReadString() {\n        String expect = \"test read string\";\n        byte[] bytes = expect.getBytes(StandardCharsets.UTF_8);\n\n        ByteBuffer buffer = ByteBuffer.allocate(expect.length() * 2 + 4);\n        buffer.order(ByteOrder.LITTLE_ENDIAN);\n        buffer.putInt(expect.length()).put(bytes).rewind();\n\n        ByteBufferReader reader = new ByteBufferReader(buffer);\n\n        assertEquals(expect, reader.readString());\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage.test;\n\nimport static org.junit.Assert.assertEquals;\n\nimport android.aconfig.storage.FileType;\nimport android.aconfig.storage.FlagTable;\nimport android.aconfig.storage.FlagType;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.util.Objects;\nimport java.util.concurrent.CyclicBarrier;\n\n@RunWith(JUnit4.class)\npublic class FlagTableTest {\n\n    @Test\n    public void testFlagTable_rightHeader() throws Exception {\n        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1));\n        FlagTable.Header header = flagTable.getHeader();\n        assertEquals(1, header.getVersion());\n        assertEquals(\"mockup\", header.getContainer());\n        assertEquals(FileType.FLAG_MAP, header.getFileType());\n        assertEquals(321, header.getFileSize());\n        assertEquals(8, header.getNumFlags());\n        assertEquals(31, header.getBucketOffset());\n        assertEquals(99, header.getNodeOffset());\n    }\n\n    @Test\n    public void testFlagTable_rightNode() throws Exception {\n        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1));\n\n        FlagTable.Node node1 = flagTable.get(0, \"enabled_ro\");\n        FlagTable.Node node2 = flagTable.get(0, \"enabled_rw\");\n        FlagTable.Node node3 = flagTable.get(2, \"enabled_rw\");\n        FlagTable.Node node4 = flagTable.get(1, \"disabled_rw\");\n        FlagTable.Node node5 = flagTable.get(1, \"enabled_fixed_ro\");\n        FlagTable.Node node6 = flagTable.get(1, \"enabled_ro\");\n        FlagTable.Node node7 = flagTable.get(2, \"enabled_fixed_ro\");\n        FlagTable.Node node8 = flagTable.get(0, \"disabled_rw\");\n\n        assertEquals(\"enabled_ro\", node1.getFlagName());\n        assertEquals(\"enabled_rw\", node2.getFlagName());\n        assertEquals(\"enabled_rw\", node3.getFlagName());\n        assertEquals(\"disabled_rw\", node4.getFlagName());\n        assertEquals(\"enabled_fixed_ro\", node5.getFlagName());\n        assertEquals(\"enabled_ro\", node6.getFlagName());\n        assertEquals(\"enabled_fixed_ro\", node7.getFlagName());\n        assertEquals(\"disabled_rw\", node8.getFlagName());\n\n        assertEquals(0, node1.getPackageId());\n        assertEquals(0, node2.getPackageId());\n        assertEquals(2, node3.getPackageId());\n        assertEquals(1, node4.getPackageId());\n        assertEquals(1, node5.getPackageId());\n        assertEquals(1, node6.getPackageId());\n        assertEquals(2, node7.getPackageId());\n        assertEquals(0, node8.getPackageId());\n\n        assertEquals(FlagType.ReadOnlyBoolean, node1.getFlagType());\n        assertEquals(FlagType.ReadWriteBoolean, node2.getFlagType());\n        assertEquals(FlagType.ReadWriteBoolean, node3.getFlagType());\n        assertEquals(FlagType.ReadWriteBoolean, node4.getFlagType());\n        assertEquals(FlagType.FixedReadOnlyBoolean, node5.getFlagType());\n        assertEquals(FlagType.ReadOnlyBoolean, node6.getFlagType());\n        assertEquals(FlagType.FixedReadOnlyBoolean, node7.getFlagType());\n        assertEquals(FlagType.ReadWriteBoolean, node8.getFlagType());\n\n        assertEquals(1, node1.getFlagIndex());\n        assertEquals(2, node2.getFlagIndex());\n        assertEquals(1, node3.getFlagIndex());\n        assertEquals(0, node4.getFlagIndex());\n        assertEquals(1, node5.getFlagIndex());\n        assertEquals(2, node6.getFlagIndex());\n        assertEquals(0, node7.getFlagIndex());\n        assertEquals(0, node8.getFlagIndex());\n\n        assertEquals(-1, node1.getNextOffset());\n        assertEquals(151, node2.getNextOffset());\n        assertEquals(-1, node3.getNextOffset());\n        assertEquals(-1, node4.getNextOffset());\n        assertEquals(236, node5.getNextOffset());\n        assertEquals(-1, node6.getNextOffset());\n        assertEquals(-1, node7.getNextOffset());\n        assertEquals(-1, node8.getNextOffset());\n    }\n\n    @Test\n    public void testFlagTable_multithreadsRead() throws Exception {\n        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(2));\n\n        int numberOfThreads = 8;\n        Thread[] threads = new Thread[numberOfThreads];\n        final CyclicBarrier gate = new CyclicBarrier(numberOfThreads + 1);\n        String[] expects = {\n            \"enabled_ro\",\n            \"enabled_rw\",\n            \"enabled_rw\",\n            \"disabled_rw\",\n            \"enabled_fixed_ro\",\n            \"enabled_ro\",\n            \"enabled_fixed_ro\",\n            \"disabled_rw\"\n        };\n        int[] packageIds = {0, 0, 2, 1, 1, 1, 2, 0};\n\n        for (int i = 0; i < numberOfThreads; i++) {\n            String expectRet = expects[i];\n            int packageId = packageIds[i];\n            threads[i] =\n                    new Thread() {\n                        @Override\n                        public void run() {\n                            try {\n                                gate.await();\n                            } catch (Exception e) {\n                            }\n                            for (int j = 0; j < 10; j++) {\n                                if (!Objects.equals(\n                                        expectRet,\n                                        flagTable.get(packageId, expectRet).getFlagName())) {\n                                    throw new RuntimeException();\n                                }\n                            }\n                        }\n                    };\n            threads[i].start();\n        }\n\n        gate.await();\n\n        for (int i = 0; i < numberOfThreads; i++) {\n            threads[i].join();\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport android.aconfig.storage.FileType;\nimport android.aconfig.storage.FlagTable;\nimport android.aconfig.storage.FlagValueList;\nimport android.aconfig.storage.PackageTable;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.util.Objects;\nimport java.util.concurrent.CyclicBarrier;\n\n@RunWith(JUnit4.class)\npublic class FlagValueListTest {\n\n    @Test\n    public void testFlagValueList_rightHeader() throws Exception {\n        FlagValueList flagValueList =\n                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1));\n        FlagValueList.Header header = flagValueList.getHeader();\n        assertEquals(1, header.getVersion());\n        assertEquals(\"mockup\", header.getContainer());\n        assertEquals(FileType.FLAG_VAL, header.getFileType());\n        assertEquals(35, header.getFileSize());\n        assertEquals(8, header.getNumFlags());\n        assertEquals(27, header.getBooleanValueOffset());\n    }\n\n    @Test\n    public void testFlagValueList_rightNode() throws Exception {\n        FlagValueList flagValueList =\n                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1));\n\n        boolean[] expected = new boolean[] {false, true, true, false, true, true, true, true};\n        assertEquals(expected.length, flagValueList.size());\n\n        for (int i = 0; i < flagValueList.size(); i++) {\n            assertEquals(expected[i], flagValueList.getBoolean(i));\n        }\n    }\n\n    @Test\n    public void testFlagValueList_getValue() throws Exception {\n        PackageTable packageTable =\n                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1));\n        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1));\n\n        FlagValueList flagValueList =\n                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1));\n\n        PackageTable.Node pNode = packageTable.get(\"com.android.aconfig.storage.test_1\");\n        FlagTable.Node fNode = flagTable.get(pNode.getPackageId(), \"enabled_rw\");\n        assertTrue(flagValueList.getBoolean(pNode.getBooleanStartIndex() + fNode.getFlagIndex()));\n\n        pNode = packageTable.get(\"com.android.aconfig.storage.test_4\");\n        fNode = flagTable.get(pNode.getPackageId(), \"enabled_fixed_ro\");\n        assertTrue(flagValueList.getBoolean(pNode.getBooleanStartIndex() + fNode.getFlagIndex()));\n    }\n\n    @Test\n    public void testFlagValueList_multithreadsRead() throws Exception {\n        FlagValueList flagValueList =\n                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(2));\n\n        int numberOfThreads = 8;\n        Thread[] threads = new Thread[numberOfThreads];\n        final CyclicBarrier gate = new CyclicBarrier(numberOfThreads + 1);\n        boolean[] expects = {false, true, true, false, true, true, true, true};\n\n        for (int i = 0; i < numberOfThreads; i++) {\n            boolean expectRet = expects[i];\n            int position = i;\n            threads[i] =\n                    new Thread() {\n                        @Override\n                        public void run() {\n                            try {\n                                gate.await();\n                            } catch (Exception e) {\n                            }\n                            for (int j = 0; j < 10; j++) {\n                                if (!Objects.equals(\n                                        expectRet, flagValueList.getBoolean(position))) {\n                                    throw new RuntimeException();\n                                }\n                            }\n                        }\n                    };\n            threads[i].start();\n        }\n\n        gate.await();\n\n        for (int i = 0; i < numberOfThreads; i++) {\n            threads[i].join();\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport android.aconfig.storage.FileType;\nimport android.aconfig.storage.PackageTable;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.CyclicBarrier;\n\n@RunWith(JUnit4.class)\npublic class PackageTableTest {\n\n    @Test\n    public void testPackageTable_rightHeader() throws Exception {\n        PackageTable packageTable =\n                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1));\n        PackageTable.Header header = packageTable.getHeader();\n        assertEquals(1, header.getVersion());\n        assertEquals(\"mockup\", header.getContainer());\n        assertEquals(FileType.PACKAGE_MAP, header.getFileType());\n        assertEquals(209, header.getFileSize());\n        assertEquals(3, header.getNumPackages());\n        assertEquals(31, header.getBucketOffset());\n        assertEquals(59, header.getNodeOffset());\n    }\n\n    @Test\n    public void testPackageTable_rightHeader_v2() throws Exception {\n        PackageTable packageTable =\n                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2));\n        PackageTable.Header header = packageTable.getHeader();\n        assertEquals(2, header.getVersion());\n        assertEquals(\"mockup\", header.getContainer());\n        assertEquals(FileType.PACKAGE_MAP, header.getFileType());\n        assertEquals(233, header.getFileSize());\n        assertEquals(3, header.getNumPackages());\n        assertEquals(31, header.getBucketOffset());\n        assertEquals(59, header.getNodeOffset());\n    }\n\n    @Test\n    public void testPackageTable_rightNode() throws Exception {\n        PackageTable packageTable =\n                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1));\n\n        PackageTable.Node node1 = packageTable.get(\"com.android.aconfig.storage.test_1\");\n        PackageTable.Node node2 = packageTable.get(\"com.android.aconfig.storage.test_2\");\n        PackageTable.Node node4 = packageTable.get(\"com.android.aconfig.storage.test_4\");\n\n        assertEquals(\"com.android.aconfig.storage.test_1\", node1.getPackageName());\n        assertEquals(\"com.android.aconfig.storage.test_2\", node2.getPackageName());\n        assertEquals(\"com.android.aconfig.storage.test_4\", node4.getPackageName());\n\n        assertEquals(0, node1.getPackageId());\n        assertEquals(1, node2.getPackageId());\n        assertEquals(2, node4.getPackageId());\n\n        assertEquals(0, node1.getBooleanStartIndex());\n        assertEquals(3, node2.getBooleanStartIndex());\n        assertEquals(6, node4.getBooleanStartIndex());\n\n        assertEquals(159, node1.getNextOffset());\n        assertEquals(-1, node2.getNextOffset());\n        assertEquals(-1, node4.getNextOffset());\n\n        assertFalse(node1.hasPackageFingerprint());\n        assertFalse(node2.hasPackageFingerprint());\n        assertFalse(node4.hasPackageFingerprint());\n    }\n\n    @Test\n    public void testPackageTable_rightNode_v2() throws Exception {\n        PackageTable packageTable =\n                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2));\n\n        PackageTable.Node node1 = packageTable.get(\"com.android.aconfig.storage.test_1\");\n        PackageTable.Node node2 = packageTable.get(\"com.android.aconfig.storage.test_2\");\n        PackageTable.Node node4 = packageTable.get(\"com.android.aconfig.storage.test_4\");\n\n        assertEquals(\"com.android.aconfig.storage.test_1\", node1.getPackageName());\n        assertEquals(\"com.android.aconfig.storage.test_2\", node2.getPackageName());\n        assertEquals(\"com.android.aconfig.storage.test_4\", node4.getPackageName());\n\n        assertEquals(0, node1.getPackageId());\n        assertEquals(1, node2.getPackageId());\n        assertEquals(2, node4.getPackageId());\n\n        assertEquals(0, node1.getBooleanStartIndex());\n        assertEquals(3, node2.getBooleanStartIndex());\n        assertEquals(6, node4.getBooleanStartIndex());\n\n        assertEquals(175, node1.getNextOffset());\n        assertEquals(-1, node2.getNextOffset());\n        assertEquals(-1, node4.getNextOffset());\n\n        assertTrue(node1.hasPackageFingerprint());\n        assertTrue(node2.hasPackageFingerprint());\n        assertTrue(node4.hasPackageFingerprint());\n\n        assertEquals(-3197795563119393530L, node1.getPackageFingerprint());\n        assertEquals(4431940502274857964L, node2.getPackageFingerprint());\n        assertEquals(-2213514155997929241L, node4.getPackageFingerprint());\n    }\n\n    @Test\n    public void testPackageTable_getPackageList() throws Exception {\n        PackageTable packageTable =\n                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2));\n        Set<String> packages = new HashSet<>(packageTable.getPackageList());\n        assertEquals(3, packages.size());\n        assertTrue(packages.contains(\"com.android.aconfig.storage.test_1\"));\n        assertTrue(packages.contains(\"com.android.aconfig.storage.test_2\"));\n        assertTrue(packages.contains(\"com.android.aconfig.storage.test_4\"));\n\n        packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1));\n        packages = new HashSet<>(packageTable.getPackageList());\n        assertEquals(3, packages.size());\n        assertTrue(packages.contains(\"com.android.aconfig.storage.test_1\"));\n        assertTrue(packages.contains(\"com.android.aconfig.storage.test_2\"));\n        assertTrue(packages.contains(\"com.android.aconfig.storage.test_4\"));\n    }\n\n    @Test\n    public void testPackageTable_multithreadsRead() throws Exception {\n        PackageTable packageTable =\n                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2));\n        int numberOfThreads = 3;\n        Thread[] threads = new Thread[numberOfThreads];\n        final CyclicBarrier gate = new CyclicBarrier(numberOfThreads + 1);\n        String[] expects = {\n            \"com.android.aconfig.storage.test_1\",\n            \"com.android.aconfig.storage.test_2\",\n            \"com.android.aconfig.storage.test_4\"\n        };\n\n        for (int i = 0; i < numberOfThreads; i++) {\n            final String packageName = expects[i];\n            threads[i] =\n                    new Thread() {\n                        @Override\n                        public void run() {\n                            try {\n                                gate.await();\n                            } catch (Exception e) {\n                            }\n                            for (int j = 0; j < 10; j++) {\n                                if (!Objects.equals(\n                                        packageName,\n                                        packageTable.get(packageName).getPackageName())) {\n                                    throw new RuntimeException();\n                                }\n                            }\n                        }\n                    };\n            threads[i].start();\n        }\n\n        gate.await();\n\n        for (int i = 0; i < numberOfThreads; i++) {\n            threads[i].join();\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/srcs/SipHasher13Test.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\nimport android.aconfig.storage.SipHasher13;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\n@RunWith(JUnit4.class)\npublic class SipHasher13Test {\n    @Test\n    public void testSipHash_hashString() throws Exception {\n        String testStr = \"com.google.android.test\";\n        long result = SipHasher13.hash(testStr.getBytes(UTF_8));\n        assertEquals(0xF86572EFF9C4A0C1L, result);\n\n        testStr = \"abcdefg\";\n        result = SipHasher13.hash(testStr.getBytes(UTF_8));\n        assertEquals(0x2295EF44BD078AE9L, result);\n\n        testStr = \"abcdefgh\";\n        result = SipHasher13.hash(testStr.getBytes(UTF_8));\n        assertEquals(0x5CD7657FA7F96C16L, result);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/srcs/StorageFileProviderTest.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\nimport android.aconfig.storage.FlagTable;\nimport android.aconfig.storage.FlagValueList;\nimport android.aconfig.storage.PackageTable;\nimport android.aconfig.storage.StorageFileProvider;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.nio.file.Paths;\nimport java.util.List;\n\n@RunWith(JUnit4.class)\npublic class StorageFileProviderTest {\n\n    @Test\n    public void testlistContainers() throws Exception {\n        StorageFileProvider p =\n                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);\n        String[] excludes = {};\n        List<String> containers = p.listContainers(excludes);\n        assertEquals(2, containers.size());\n\n        excludes = new String[] {\"mock.v1\"};\n        containers = p.listContainers(excludes);\n        assertEquals(1, containers.size());\n\n        p = new StorageFileProvider(\"fake/path/\", \"fake/path/\");\n        containers = p.listContainers(excludes);\n        assertTrue(containers.isEmpty());\n    }\n\n    @Test\n    public void testLoadFiles() throws Exception {\n        StorageFileProvider p =\n                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);\n        PackageTable pt = p.getPackageTable(\"mock.v1\");\n        assertNotNull(pt);\n        FlagTable f = p.getFlagTable(\"mock.v1\");\n        assertNotNull(f);\n        FlagValueList v = p.getFlagValueList(\"mock.v1\");\n        assertNotNull(v);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage.test;\n\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\n\npublic final class TestDataUtils {\n    private static final String TEST_PACKAGE_MAP_PATH = \"mock.v%d.package.map\";\n    private static final String TEST_FLAG_MAP_PATH = \"mock.v%d.flag.map\";\n    private static final String TEST_FLAG_VAL_PATH = \"mock.v%d.val\";\n    private static final String TEST_FLAG_INFO_PATH = \"mock.v%d.info\";\n\n    public static final String TESTDATA_PATH = \"/data/local/tmp/aconfig_storage_file_test_java/testdata/\";\n\n    public static ByteBuffer getTestPackageMapByteBuffer(int version) throws Exception {\n        return readFile(TESTDATA_PATH + String.format(TEST_PACKAGE_MAP_PATH, version));\n    }\n\n    public static ByteBuffer getTestFlagMapByteBuffer(int version) throws Exception {\n        return readFile(TESTDATA_PATH + String.format(TEST_FLAG_MAP_PATH, version));\n    }\n\n    public static ByteBuffer getTestFlagValByteBuffer(int version) throws Exception {\n        return readFile(TESTDATA_PATH + String.format(TEST_FLAG_VAL_PATH, version));\n    }\n\n    public static ByteBuffer getTestFlagInfoByteBuffer(int version) throws Exception {\n        return readFile(TESTDATA_PATH + String.format(TEST_FLAG_INFO_PATH, version));\n    }\n\n    private static ByteBuffer readFile(String fileName) throws Exception {\n        InputStream input = new FileInputStream(fileName);\n        return ByteBuffer.wrap(input.readAllBytes());\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <string>\n#include <vector>\n#include <android-base/file.h>\n#include <android-base/result.h>\n#include <gtest/gtest.h>\n#include \"aconfig_storage/aconfig_storage_file.hpp\"\n\nusing namespace android::base;\nusing namespace aconfig_storage;\n\nvoid verify_value(const FlagValueSummary& flag, const std::string& package_name,\n                  const std::string& flag_name, const std::string& flag_val,\n                  const std::string& value_type) {\n  ASSERT_EQ(flag.package_name, package_name);\n  ASSERT_EQ(flag.flag_name, flag_name);\n  ASSERT_EQ(flag.flag_value, flag_val);\n  ASSERT_EQ(flag.value_type, value_type);\n}\n\nvoid verify_value_info(const FlagValueAndInfoSummary& flag,\n                       const std::string& package_name,\n                       const std::string& flag_name,\n                       const std::string& flag_val,\n                       const std::string& value_type, bool is_readwrite,\n                       bool has_server_override, bool has_local_override) {\n  ASSERT_EQ(flag.package_name, package_name);\n  ASSERT_EQ(flag.flag_name, flag_name);\n  ASSERT_EQ(flag.flag_value, flag_val);\n  ASSERT_EQ(flag.value_type, value_type);\n  ASSERT_EQ(flag.is_readwrite, is_readwrite);\n  ASSERT_EQ(flag.has_server_override, has_server_override);\n  ASSERT_EQ(flag.has_local_override, has_local_override);\n}\n\nResult<std::vector<FlagValueSummary>> get_flag_list_result(\n    const std::string version) {\n  auto const test_base_dir = GetExecutableDirectory();\n  auto const test_dir = test_base_dir + \"/data/v\" + version;\n  auto const package_map = test_dir + \"/package_v\" + version + \".map\";\n  auto const flag_map = test_dir + \"/flag_v\" + version + \".map\";\n  auto const flag_val = test_dir + \"/flag_v\" + version + \".val\";\n  return aconfig_storage::list_flags(package_map, flag_map, flag_val);\n}\n\nResult<std::vector<FlagValueAndInfoSummary>> get_flag_list_result_with_info(\n    const std::string version) {\n  auto const test_base_dir = GetExecutableDirectory();\n  auto const test_dir = test_base_dir + \"/data/v\" + version;\n  auto const package_map = test_dir + \"/package_v\" + version + \".map\";\n  auto const flag_map = test_dir + \"/flag_v\" + version + \".map\";\n  auto const flag_val = test_dir + \"/flag_v\" + version + \".val\";\n  auto const flag_info = test_dir + \"/flag_v\" + version + \".info\";\n  return aconfig_storage::list_flags_with_info(package_map, flag_map, flag_val,\n                                               flag_info);\n}\n\nTEST(AconfigStorageFileTest, test_list_flag) {\n  auto flag_list_result = get_flag_list_result(\"1\");\n  ASSERT_TRUE(flag_list_result.ok());\n\n  auto const& flag_list = *flag_list_result;\n  ASSERT_EQ(flag_list.size(), 8);\n  verify_value(flag_list[0], \"com.android.aconfig.storage.test_1\",\n               \"disabled_rw\", \"false\", \"ReadWriteBoolean\");\n  verify_value(flag_list[1], \"com.android.aconfig.storage.test_1\", \"enabled_ro\",\n               \"true\", \"ReadOnlyBoolean\");\n  verify_value(flag_list[2], \"com.android.aconfig.storage.test_1\", \"enabled_rw\",\n               \"true\", \"ReadWriteBoolean\");\n  verify_value(flag_list[3], \"com.android.aconfig.storage.test_2\",\n               \"disabled_rw\", \"false\", \"ReadWriteBoolean\");\n  verify_value(flag_list[4], \"com.android.aconfig.storage.test_2\",\n               \"enabled_fixed_ro\", \"true\", \"FixedReadOnlyBoolean\");\n  verify_value(flag_list[5], \"com.android.aconfig.storage.test_2\", \"enabled_ro\",\n               \"true\", \"ReadOnlyBoolean\");\n  verify_value(flag_list[6], \"com.android.aconfig.storage.test_4\",\n               \"enabled_fixed_ro\", \"true\", \"FixedReadOnlyBoolean\");\n  verify_value(flag_list[7], \"com.android.aconfig.storage.test_4\", \"enabled_rw\",\n               \"true\", \"ReadWriteBoolean\");\n}\n\n// TODO: b/376256472 - Use parameterized tests.\nTEST(AconfigStorageFileTest, test_list_flag_v2) {\n  auto flag_list_result = get_flag_list_result(\"2\");\n  ASSERT_TRUE(flag_list_result.ok());\n\n  auto const& flag_list = *flag_list_result;\n  ASSERT_EQ(flag_list.size(), 8);\n  verify_value(flag_list[0], \"com.android.aconfig.storage.test_1\",\n               \"disabled_rw\", \"false\", \"ReadWriteBoolean\");\n  verify_value(flag_list[1], \"com.android.aconfig.storage.test_1\", \"enabled_ro\",\n               \"true\", \"ReadOnlyBoolean\");\n  verify_value(flag_list[2], \"com.android.aconfig.storage.test_1\", \"enabled_rw\",\n               \"true\", \"ReadWriteBoolean\");\n  verify_value(flag_list[3], \"com.android.aconfig.storage.test_2\",\n               \"disabled_rw\", \"false\", \"ReadWriteBoolean\");\n  verify_value(flag_list[4], \"com.android.aconfig.storage.test_2\",\n               \"enabled_fixed_ro\", \"true\", \"FixedReadOnlyBoolean\");\n  verify_value(flag_list[5], \"com.android.aconfig.storage.test_2\", \"enabled_ro\",\n               \"true\", \"ReadOnlyBoolean\");\n  verify_value(flag_list[6], \"com.android.aconfig.storage.test_4\",\n               \"enabled_fixed_ro\", \"true\", \"FixedReadOnlyBoolean\");\n  verify_value(flag_list[7], \"com.android.aconfig.storage.test_4\", \"enabled_rw\",\n               \"true\", \"ReadWriteBoolean\");\n}\n\nTEST(AconfigStorageFileTest, test_list_flag_with_info) {\n  auto flag_list_result = get_flag_list_result_with_info(\"1\");\n  ASSERT_TRUE(flag_list_result.ok());\n\n  auto const& flag_list = *flag_list_result;\n  ASSERT_EQ(flag_list.size(), 8);\n  verify_value_info(flag_list[0], \"com.android.aconfig.storage.test_1\",\n                    \"disabled_rw\", \"false\", \"ReadWriteBoolean\", true, false,\n                    false);\n  verify_value_info(flag_list[1], \"com.android.aconfig.storage.test_1\",\n                    \"enabled_ro\", \"true\", \"ReadOnlyBoolean\", false, false,\n                    false);\n  verify_value_info(flag_list[2], \"com.android.aconfig.storage.test_1\",\n                    \"enabled_rw\", \"true\", \"ReadWriteBoolean\", true, false,\n                    false);\n  verify_value_info(flag_list[3], \"com.android.aconfig.storage.test_2\",\n                    \"disabled_rw\", \"false\", \"ReadWriteBoolean\", true, false,\n                    false);\n  verify_value_info(flag_list[4], \"com.android.aconfig.storage.test_2\",\n                    \"enabled_fixed_ro\", \"true\", \"FixedReadOnlyBoolean\", false,\n                    false, false);\n  verify_value_info(flag_list[5], \"com.android.aconfig.storage.test_2\",\n                    \"enabled_ro\", \"true\", \"ReadOnlyBoolean\", false, false,\n                    false);\n  verify_value_info(flag_list[6], \"com.android.aconfig.storage.test_4\",\n                    \"enabled_fixed_ro\", \"true\", \"FixedReadOnlyBoolean\", false,\n                    false, false);\n  verify_value_info(flag_list[7], \"com.android.aconfig.storage.test_4\",\n                    \"enabled_rw\", \"true\", \"ReadWriteBoolean\", true, false,\n                    false);\n}\n\nTEST(AconfigStorageFileTest, test_list_flag_with_info_v2) {\n  auto flag_list_result = get_flag_list_result_with_info(\"2\");\n  ASSERT_TRUE(flag_list_result.ok());\n\n  auto const& flag_list = *flag_list_result;\n  ASSERT_EQ(flag_list.size(), 8);\n  verify_value_info(flag_list[0], \"com.android.aconfig.storage.test_1\",\n                    \"disabled_rw\", \"false\", \"ReadWriteBoolean\", true, false,\n                    false);\n  verify_value_info(flag_list[1], \"com.android.aconfig.storage.test_1\",\n                    \"enabled_ro\", \"true\", \"ReadOnlyBoolean\", false, false,\n                    false);\n  verify_value_info(flag_list[2], \"com.android.aconfig.storage.test_1\",\n                    \"enabled_rw\", \"true\", \"ReadWriteBoolean\", true, false,\n                    false);\n  verify_value_info(flag_list[3], \"com.android.aconfig.storage.test_2\",\n                    \"disabled_rw\", \"false\", \"ReadWriteBoolean\", true, false,\n                    false);\n  verify_value_info(flag_list[4], \"com.android.aconfig.storage.test_2\",\n                    \"enabled_fixed_ro\", \"true\", \"FixedReadOnlyBoolean\", false,\n                    false, false);\n  verify_value_info(flag_list[5], \"com.android.aconfig.storage.test_2\",\n                    \"enabled_ro\", \"true\", \"ReadOnlyBoolean\", false, false,\n                    false);\n  verify_value_info(flag_list[6], \"com.android.aconfig.storage.test_4\",\n                    \"enabled_fixed_ro\", \"true\", \"FixedReadOnlyBoolean\", false,\n                    false, false);\n  verify_value_info(flag_list[7], \"com.android.aconfig.storage.test_4\",\n                    \"enabled_rw\", \"true\", \"ReadWriteBoolean\", true, false,\n                    false);\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_defaults {\n    name: \"aconfig_storage_read_api.defaults\",\n    edition: \"2021\",\n    lints: \"none\",\n    srcs: [\"src/lib.rs\"],\n    rustlibs: [\n        \"libanyhow\",\n        \"libmemmap2\",\n        \"libcxx\",\n        \"libthiserror\",\n        \"libaconfig_storage_file\",\n    ],\n}\n\nrust_library {\n    name: \"libaconfig_storage_read_api\",\n    crate_name: \"aconfig_storage_read_api\",\n    host_supported: true,\n    defaults: [\"aconfig_storage_read_api.defaults\"],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"29\",\n}\n\nrust_test_host {\n    name: \"aconfig_storage_read_api.test\",\n    test_suites: [\"general-tests\"],\n    defaults: [\"aconfig_storage_read_api.defaults\"],\n    rustlibs: [\n        \"librand\",\n    ],\n    data: [\n        \"tests/data/v1/package_v1.map\",\n        \"tests/data/v1/flag_v1.map\",\n        \"tests/data/v1/flag_v1.val\",\n        \"tests/data/v1/flag_v1.info\",\n    ],\n}\n\n// cxx source codegen from rust api\ngenrule {\n    name: \"libcxx_aconfig_storage_read_api_bridge_code\",\n    tools: [\"cxxbridge\"],\n    cmd: \"$(location cxxbridge) $(in) > $(out)\",\n    srcs: [\"src/lib.rs\"],\n    out: [\"aconfig_storage/lib.rs.cc\"],\n}\n\n// cxx header codegen from rust api\ngenrule {\n    name: \"libcxx_aconfig_storage_read_api_bridge_header\",\n    tools: [\"cxxbridge\"],\n    cmd: \"$(location cxxbridge) $(in) --header > $(out)\",\n    srcs: [\"src/lib.rs\"],\n    out: [\"aconfig_storage/lib.rs.h\"],\n}\n\n// a static cc lib based on generated code\nrust_ffi_static {\n    name: \"libaconfig_storage_read_api_cxx_bridge\",\n    crate_name: \"aconfig_storage_read_api_cxx_bridge\",\n    host_supported: true,\n    vendor_available: true,\n    product_available: true,\n    defaults: [\"aconfig_storage_read_api.defaults\"],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"29\",\n}\n\n// flag read api cc interface\ncc_library {\n    name: \"libaconfig_storage_read_api_cc\",\n    srcs: [\"aconfig_storage_read_api.cpp\"],\n    generated_headers: [\n        \"cxx-bridge-header\",\n        \"libcxx_aconfig_storage_read_api_bridge_header\",\n    ],\n    generated_sources: [\"libcxx_aconfig_storage_read_api_bridge_code\"],\n    whole_static_libs: [\"libaconfig_storage_read_api_cxx_bridge\"],\n    export_include_dirs: [\"include\"],\n    static_libs: [\n        \"libbase\",\n    ],\n    host_supported: true,\n    vendor_available: true,\n    product_available: true,\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"29\",\n    target: {\n        linux: {\n            version_script: \"libaconfig_storage_read_api_cc.map\",\n        },\n    },\n    double_loadable: true,\n    afdo: true,\n}\n\ncc_defaults {\n    name: \"aconfig_lib_cc_shared_link.defaults\",\n    shared_libs: select(release_flag(\"RELEASE_READ_FROM_NEW_STORAGE\"), {\n        true: [\"libaconfig_storage_read_api_cc\"],\n        default: [],\n    }),\n}\n\ncc_defaults {\n    name: \"aconfig_lib_cc_static_link.defaults\",\n    shared_libs: [\n        \"libaconfig_storage_read_api_cc\",\n        \"liblog\",\n    ],\n}\n\nrust_ffi_shared {\n    name: \"libaconfig_storage_read_api_rust_jni\",\n    crate_name: \"aconfig_storage_read_api_rust_jni\",\n    srcs: [\"srcs/lib.rs\"],\n    rustlibs: [\n        \"libaconfig_storage_file\",\n        \"libaconfig_storage_read_api\",\n        \"libanyhow\",\n        \"libjni\",\n    ],\n    prefer_rlib: true,\n}\n\njava_library {\n    name: \"libaconfig_storage_read_api_java\",\n    srcs: [\n        \"srcs/android/aconfig/storage/AconfigStorageReadAPI.java\",\n        \"srcs/android/aconfig/storage/FlagReadContext.java\",\n        \"srcs/android/aconfig/storage/PackageReadContext.java\",\n    ],\n    required: [\"libaconfig_storage_read_api_rust_jni\"],\n    min_sdk_version: \"UpsideDownCake\",\n    apex_available: [\n        \"//apex_available:anyapex\",\n        \"//apex_available:platform\",\n    ],\n}\n\njava_library {\n    name: \"aconfig_storage_reader_java\",\n    srcs: [\n        \"srcs/android/os/flagging/*.java\",\n    ],\n    libs: [\n        \"unsupportedappusage\",\n    ],\n    static_libs: [\n        \"aconfig_storage_file_java\",\n    ],\n    sdk_version: \"current\",\n    visibility: [\n        \"//frameworks/base\",\n        \"//build/make/tools/aconfig/aconfig_storage_read_api/tests\",\n    ],\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/Cargo.toml",
    "content": "[package]\nname = \"aconfig_storage_read_api\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[features]\ndefault = [\"cargo\"]\ncargo = []\n\n[dependencies]\nrand = \"0.8.5\"\nanyhow = \"1.0.69\"\nmemmap2 = \"0.8.0\"\ncxx = \"1.0\"\nthiserror = \"1.0.56\"\naconfig_storage_file = { path = \"../aconfig_storage_file\" }\n\n[build-dependencies]\nprotobuf-codegen = \"3.2.0\"\ncxx-build = \"1.0\"\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp",
    "content": "#include <android-base/unique_fd.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <string.h>\n\n#include \"rust/cxx.h\"\n#include \"aconfig_storage/lib.rs.h\"\n#include \"aconfig_storage/aconfig_storage_read_api.hpp\"\n\nnamespace aconfig_storage {\n\n/// Storage location pb file\nstatic constexpr char kStorageDir[] = \"/metadata/aconfig\";\n\n/// destructor\nMappedStorageFile::~MappedStorageFile() {\n  munmap(file_ptr, file_size);\n}\n\n/// Get storage file path\nstatic Result<std::string> find_storage_file(\n    std::string const& storage_dir,\n    std::string const& container,\n    StorageFileType file_type) {\n  switch(file_type) {\n    case StorageFileType::package_map:\n      return storage_dir + \"/maps/\" + container + \".package.map\";\n    case StorageFileType::flag_map:\n      return storage_dir + \"/maps/\" + container + \".flag.map\";\n    case StorageFileType::flag_val:\n      return storage_dir + \"/boot/\" + container + \".val\";\n    case StorageFileType::flag_info:\n      return storage_dir + \"/boot/\" + container + \".info\";\n    default:\n      auto result = Result<std::string>();\n      result.errmsg = \"Invalid storage file type\";\n      return result;\n  }\n}\n\nnamespace private_internal_api {\n\n/// Get mapped file implementation.\nResult<MappedStorageFile*> get_mapped_file_impl(\n    std::string const& storage_dir,\n    std::string const& container,\n    StorageFileType file_type) {\n  auto file_result = find_storage_file(storage_dir, container, file_type);\n  if (!file_result.ok()) {\n    auto result = Result<MappedStorageFile*>();\n    result.errmsg = file_result.error();\n    return result;\n  }\n  return map_storage_file(*file_result);\n}\n\n} // namespace private internal api\n\n/// Map a storage file\nResult<MappedStorageFile*> map_storage_file(std::string const& file) {\n  android::base::unique_fd ufd(open(file.c_str(), O_CLOEXEC | O_NOFOLLOW | O_RDONLY));\n  if (ufd.get() == -1) {\n    auto result = Result<MappedStorageFile*>();\n    result.errmsg = std::string(\"failed to open \") + file + \": \" + strerror(errno);\n    return result;\n  };\n\n  struct stat fd_stat;\n  if (fstat(ufd.get(), &fd_stat) < 0) {\n    auto result = Result<MappedStorageFile*>();\n    result.errmsg = std::string(\"fstat failed: \") + strerror(errno);\n    return result;\n  }\n  size_t file_size = fd_stat.st_size;\n\n  void* const map_result = mmap(nullptr, file_size, PROT_READ, MAP_SHARED, ufd.get(), 0);\n  if (map_result == MAP_FAILED) {\n    auto result = Result<MappedStorageFile*>();\n    result.errmsg = std::string(\"mmap failed: \") + strerror(errno);\n    return result;\n  }\n\n  auto mapped_file = new MappedStorageFile();\n  mapped_file->file_ptr = map_result;\n  mapped_file->file_size = file_size;\n\n  return mapped_file;\n}\n\n/// Map from StoredFlagType to FlagValueType\nResult<FlagValueType> map_to_flag_value_type(\n    StoredFlagType stored_type) {\n  switch (stored_type) {\n    case StoredFlagType::ReadWriteBoolean:\n    case StoredFlagType::ReadOnlyBoolean:\n    case StoredFlagType::FixedReadOnlyBoolean:\n      return FlagValueType::Boolean;\n    default:\n      auto result = Result<FlagValueType>();\n      result.errmsg = \"Unsupported stored flag type\";\n      return result;\n  }\n}\n\n/// Get mapped storage file\nResult<MappedStorageFile*> get_mapped_file(\n    std::string const& container,\n    StorageFileType file_type) {\n  return private_internal_api::get_mapped_file_impl(\n      kStorageDir, container, file_type);\n}\n\n/// Get storage file version number\nResult<uint32_t> get_storage_file_version(\n    std::string const& file_path) {\n  auto version_cxx = get_storage_file_version_cxx(\n      rust::Str(file_path.c_str()));\n  if (version_cxx.query_success) {\n    return version_cxx.version_number;\n  } else {\n    auto result = Result<uint32_t>();\n    result.errmsg = version_cxx.error_message.c_str();\n    return result;\n  }\n}\n\n/// Get package context\nResult<PackageReadContext> get_package_read_context(\n    MappedStorageFile const& file,\n    std::string const& package) {\n  auto content = rust::Slice<const uint8_t>(\n      static_cast<uint8_t*>(file.file_ptr), file.file_size);\n  auto context_cxx = get_package_read_context_cxx(content, rust::Str(package.c_str()));\n  if (context_cxx.query_success) {\n    auto context = PackageReadContext();\n    context.package_exists = context_cxx.package_exists;\n    context.package_id = context_cxx.package_id;\n    context.boolean_start_index = context_cxx.boolean_start_index;\n    return context;\n  } else {\n    auto result = Result<PackageReadContext>();\n    result.errmsg = context_cxx.error_message.c_str();\n    return result;\n  }\n}\n\n/// Get flag read context\nResult<FlagReadContext> get_flag_read_context(\n    MappedStorageFile const& file,\n    uint32_t package_id,\n    std::string const& flag_name){\n  auto content = rust::Slice<const uint8_t>(\n      static_cast<uint8_t*>(file.file_ptr), file.file_size);\n  auto context_cxx = get_flag_read_context_cxx(content, package_id, rust::Str(flag_name.c_str()));\n  if (context_cxx.query_success) {\n    auto context = FlagReadContext();\n    context.flag_exists = context_cxx.flag_exists;\n    context.flag_type = static_cast<StoredFlagType>(context_cxx.flag_type);\n    context.flag_index = context_cxx.flag_index;\n    return context;\n  } else {\n    auto result = Result<FlagReadContext>();\n    result.errmsg = context_cxx.error_message.c_str();\n    return result;\n  }\n}\n\n/// Get boolean flag value\nResult<bool> get_boolean_flag_value(\n    MappedStorageFile const& file,\n    uint32_t index) {\n  auto content = rust::Slice<const uint8_t>(\n      static_cast<uint8_t*>(file.file_ptr), file.file_size);\n  auto value_cxx = get_boolean_flag_value_cxx(content, index);\n  if (value_cxx.query_success) {\n    return value_cxx.flag_value;\n  } else {\n    auto result = Result<bool>();\n    result.errmsg = value_cxx.error_message.c_str();\n    return result;\n  }\n}\n\n/// Get boolean flag attribute\nResult<uint8_t> get_flag_attribute(\n    MappedStorageFile const& file,\n    FlagValueType value_type,\n    uint32_t index) {\n  auto content = rust::Slice<const uint8_t>(\n      static_cast<uint8_t*>(file.file_ptr), file.file_size);\n  auto info_cxx = get_flag_attribute_cxx(\n      content, static_cast<uint16_t>(value_type), index);\n  if (info_cxx.query_success) {\n    return info_cxx.flag_attribute;\n  } else {\n    auto result = Result<uint8_t>();\n    result.errmsg = info_cxx.error_message.c_str();\n    return result;\n  }\n}\n} // namespace aconfig_storage\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/build.rs",
    "content": "fn main() {\n    let _ = cxx_build::bridge(\"src/lib.rs\");\n    println!(\"cargo:rerun-if-changed=src/lib.rs\");\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <string>\n#include <cassert>\n\nnamespace aconfig_storage {\n\n/// Storage file type enum, to be consistent with the one defined in\n/// aconfig_storage_file/src/lib.rs\nenum StorageFileType {\n  package_map,\n  flag_map,\n  flag_val,\n  flag_info\n};\n\n/// Flag type enum, to be consistent with the one defined in\n/// aconfig_storage_file/src/lib.rs\nenum StoredFlagType {\n  ReadWriteBoolean = 0,\n  ReadOnlyBoolean = 1,\n  FixedReadOnlyBoolean = 2,\n};\n\n/// Flag value type enum, to be consistent with the one defined in\n/// aconfig_storage_file/src/lib.rs\nenum FlagValueType {\n  Boolean = 0,\n};\n\n/// Flag info enum, to be consistent with the one defined in\n/// aconfig_storage_file/src/flag_info.rs\nenum FlagInfoBit {\n  HasServerOverride = 1<<0,\n  IsReadWrite = 1<<1,\n  HasLocalOverride = 1<<2,\n};\n\n/// Mapped storage file\nstruct MappedStorageFile {\n  void* file_ptr;\n  size_t file_size;\n  virtual ~MappedStorageFile();\n};\n\n/// Package read context query result\nstruct PackageReadContext {\n  bool package_exists;\n  uint32_t package_id;\n  uint32_t boolean_start_index;\n};\n\n/// Flag read context query result\nstruct FlagReadContext {\n  bool flag_exists;\n  StoredFlagType flag_type;\n  uint16_t flag_index;\n};\n\n\ntemplate <class T>\nclass Result {\n  public:\n\n  Result()\n      : data()\n      , errmsg()\n      , has_data(false)\n  {}\n\n  Result(T const& value)\n      : data(value)\n      , errmsg()\n      , has_data(true)\n  {}\n\n  bool ok() {\n    return has_data;\n  }\n\n  T& operator*() {\n    assert(has_data);\n    return data;\n  }\n\n  T* operator->() {\n    assert(has_data);\n    return &data;\n  }\n\n  std::string const& error() {\n    assert(!has_data);\n    return errmsg;\n  }\n\n  T data;\n  std::string errmsg;\n  bool has_data;\n};\n\n/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY\nnamespace private_internal_api {\n\nResult<MappedStorageFile*> get_mapped_file_impl(\n    std::string const& pb_file,\n    std::string const& container,\n    StorageFileType file_type);\n} // namespace private_internal_api\n\n/// Map a storage file\nResult<MappedStorageFile*> map_storage_file(\n    std::string const& file);\n\n\n/// Map from StoredFlagType to FlagValueType\n/// \\input stored_type: stored flag type in the storage file\n/// \\returns the flag value type enum\nResult<FlagValueType> map_to_flag_value_type(\n    StoredFlagType stored_type);\n\n/// Get mapped storage file\n/// \\input container: stoarge container name\n/// \\input file_type: storage file type enum\n/// \\returns a MappedStorageFileQuery\nResult<MappedStorageFile*> get_mapped_file(\n    std::string const& container,\n    StorageFileType file_type);\n\n/// Get storage file version number\n/// \\input file_path: the path to the storage file\n/// \\returns the storage file version\nResult<uint32_t> get_storage_file_version(\n    std::string const& file_path);\n\n/// Get package read context\n/// \\input file: mapped storage file\n/// \\input package: the flag package name\n/// \\returns a package read context\nResult<PackageReadContext> get_package_read_context(\n    MappedStorageFile const& file,\n    std::string const& package);\n\n/// Get flag read context\n/// \\input file: mapped storage file\n/// \\input package_id: the flag package id obtained from package offset query\n/// \\input flag_name: flag name\n/// \\returns the flag read context\nResult<FlagReadContext> get_flag_read_context(\n    MappedStorageFile const& file,\n    uint32_t package_id,\n    std::string const& flag_name);\n\n/// Get boolean flag value\n/// \\input file: mapped storage file\n/// \\input index: the boolean flag index in the file\n/// \\returns the boolean flag value\nResult<bool> get_boolean_flag_value(\n    MappedStorageFile const& file,\n    uint32_t index);\n\n/// Get boolean flag attribute\n/// \\input file: mapped storage file\n/// \\input value_type: flag value type\n/// \\input index: the boolean flag index in the file\n/// \\returns the boolean flag attribute\nResult<uint8_t> get_flag_attribute(\n    MappedStorageFile const& file,\n    FlagValueType value_type,\n    uint32_t index);\n} // namespace aconfig_storage\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! flag value query module defines the flag value file read from mapped bytes\n\nuse crate::AconfigStorageError;\nuse aconfig_storage_file::{\n    flag_info::FlagInfoHeader, read_u8_from_bytes, FlagValueType, MAX_SUPPORTED_FILE_VERSION,\n};\nuse anyhow::anyhow;\n\n/// Get flag attribute bitfield\npub fn find_flag_attribute(\n    buf: &[u8],\n    flag_type: FlagValueType,\n    flag_index: u32,\n) -> Result<u8, AconfigStorageError> {\n    let interpreted_header = FlagInfoHeader::from_bytes(buf)?;\n    if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION {\n        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(\n            \"Cannot read storage file with a higher version of {} with lib version {}\",\n            interpreted_header.version,\n            MAX_SUPPORTED_FILE_VERSION\n        )));\n    }\n\n    // get byte offset to the flag info\n    let mut head = match flag_type {\n        FlagValueType::Boolean => (interpreted_header.boolean_flag_offset + flag_index) as usize,\n    };\n\n    if head >= interpreted_header.file_size as usize {\n        return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(\n            \"Flag info offset goes beyond the end of the file.\"\n        )));\n    }\n\n    let val = read_u8_from_bytes(buf, &mut head)?;\n    Ok(val)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use aconfig_storage_file::{\n        test_utils::create_test_flag_info_list, FlagInfoBit, DEFAULT_FILE_VERSION,\n    };\n\n    #[test]\n    // this test point locks down query if flag has server override\n    fn test_is_flag_sticky() {\n        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes();\n        for offset in 0..8 {\n            let attribute =\n                find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap();\n            assert_eq!((attribute & FlagInfoBit::HasServerOverride as u8) != 0u8, false);\n        }\n    }\n\n    #[test]\n    // this test point locks down query if flag is readwrite\n    fn test_is_flag_readwrite() {\n        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes();\n        let baseline: Vec<bool> = vec![true, false, true, true, false, false, false, true];\n        for offset in 0..8 {\n            let attribute =\n                find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap();\n            assert_eq!(\n                (attribute & FlagInfoBit::IsReadWrite as u8) != 0u8,\n                baseline[offset as usize]\n            );\n        }\n    }\n\n    #[test]\n    // this test point locks down query if flag has local override\n    fn test_flag_has_override() {\n        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes();\n        for offset in 0..8 {\n            let attribute =\n                find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap();\n            assert_eq!((attribute & FlagInfoBit::HasLocalOverride as u8) != 0u8, false);\n        }\n    }\n\n    #[test]\n    // this test point locks down query beyond the end of boolean section\n    fn test_boolean_out_of_range() {\n        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes();\n        let error =\n            find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, 8).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            \"InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)\"\n        );\n    }\n\n    #[test]\n    // this test point locks down query error when file has a higher version\n    fn test_higher_version_storage_file() {\n        let mut info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);\n        info_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1;\n        let flag_info = info_list.into_bytes();\n        let error = find_flag_attribute(&flag_info[..], FlagValueType::Boolean, 4).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            format!(\n                \"HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})\",\n                MAX_SUPPORTED_FILE_VERSION + 1,\n                MAX_SUPPORTED_FILE_VERSION\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! flag table query module defines the flag table file read from mapped bytes\n\nuse crate::AconfigStorageError;\nuse aconfig_storage_file::{\n    flag_table::FlagTableHeader, flag_table::FlagTableNode, read_u32_from_bytes, StoredFlagType,\n    MAX_SUPPORTED_FILE_VERSION,\n};\nuse anyhow::anyhow;\n\n/// Flag table query return\n#[derive(PartialEq, Debug)]\npub struct FlagReadContext {\n    pub flag_type: StoredFlagType,\n    pub flag_index: u16,\n}\n\n/// Query flag read context: flag type and within package flag index\npub fn find_flag_read_context(\n    buf: &[u8],\n    package_id: u32,\n    flag: &str,\n) -> Result<Option<FlagReadContext>, AconfigStorageError> {\n    let interpreted_header = FlagTableHeader::from_bytes(buf)?;\n    if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION {\n        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(\n            \"Cannot read storage file with a higher version of {} with lib version {}\",\n            interpreted_header.version,\n            MAX_SUPPORTED_FILE_VERSION\n        )));\n    }\n\n    let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;\n    let bucket_index = FlagTableNode::find_bucket_index(package_id, flag, num_buckets);\n\n    let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize;\n    let mut flag_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize;\n    if flag_node_offset < interpreted_header.node_offset as usize\n        || flag_node_offset >= interpreted_header.file_size as usize\n    {\n        return Ok(None);\n    }\n\n    loop {\n        let interpreted_node = FlagTableNode::from_bytes(&buf[flag_node_offset..])?;\n        if interpreted_node.package_id == package_id && interpreted_node.flag_name == flag {\n            return Ok(Some(FlagReadContext {\n                flag_type: interpreted_node.flag_type,\n                flag_index: interpreted_node.flag_index,\n            }));\n        }\n        match interpreted_node.next_offset {\n            Some(offset) => flag_node_offset = offset as usize,\n            None => return Ok(None),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use aconfig_storage_file::{test_utils::create_test_flag_table, DEFAULT_FILE_VERSION};\n\n    #[test]\n    // this test point locks down table query\n    fn test_flag_query() {\n        let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes();\n        let baseline = vec![\n            (0, \"enabled_ro\", StoredFlagType::ReadOnlyBoolean, 1u16),\n            (0, \"enabled_rw\", StoredFlagType::ReadWriteBoolean, 2u16),\n            (2, \"enabled_rw\", StoredFlagType::ReadWriteBoolean, 1u16),\n            (1, \"disabled_rw\", StoredFlagType::ReadWriteBoolean, 0u16),\n            (1, \"enabled_fixed_ro\", StoredFlagType::FixedReadOnlyBoolean, 1u16),\n            (1, \"enabled_ro\", StoredFlagType::ReadOnlyBoolean, 2u16),\n            (2, \"enabled_fixed_ro\", StoredFlagType::FixedReadOnlyBoolean, 0u16),\n            (0, \"disabled_rw\", StoredFlagType::ReadWriteBoolean, 0u16),\n        ];\n        for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() {\n            let flag_context =\n                find_flag_read_context(&flag_table[..], package_id, flag_name).unwrap().unwrap();\n            assert_eq!(flag_context.flag_type, flag_type);\n            assert_eq!(flag_context.flag_index, flag_index);\n        }\n    }\n\n    #[test]\n    // this test point locks down table query of a non exist flag\n    fn test_not_existed_flag_query() {\n        let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes();\n        let flag_context = find_flag_read_context(&flag_table[..], 1, \"disabled_fixed_ro\").unwrap();\n        assert_eq!(flag_context, None);\n        let flag_context = find_flag_read_context(&flag_table[..], 2, \"disabled_rw\").unwrap();\n        assert_eq!(flag_context, None);\n    }\n\n    #[test]\n    // this test point locks down query error when file has a higher version\n    fn test_higher_version_storage_file() {\n        let mut table = create_test_flag_table(DEFAULT_FILE_VERSION);\n        table.header.version = MAX_SUPPORTED_FILE_VERSION + 1;\n        let flag_table = table.into_bytes();\n        let error = find_flag_read_context(&flag_table[..], 0, \"enabled_ro\").unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            format!(\n                \"HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})\",\n                MAX_SUPPORTED_FILE_VERSION + 1,\n                MAX_SUPPORTED_FILE_VERSION\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! flag value query module defines the flag value file read from mapped bytes\n\nuse crate::AconfigStorageError;\nuse aconfig_storage_file::{\n    flag_value::FlagValueHeader, read_u8_from_bytes, MAX_SUPPORTED_FILE_VERSION,\n};\nuse anyhow::anyhow;\n\n/// Query flag value\npub fn find_boolean_flag_value(buf: &[u8], flag_index: u32) -> Result<bool, AconfigStorageError> {\n    let interpreted_header = FlagValueHeader::from_bytes(buf)?;\n    if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION {\n        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(\n            \"Cannot read storage file with a higher version of {} with lib version {}\",\n            interpreted_header.version,\n            MAX_SUPPORTED_FILE_VERSION\n        )));\n    }\n\n    // Find byte offset to the flag value, each boolean flag cost one byte to store\n    let mut head = (interpreted_header.boolean_value_offset + flag_index) as usize;\n    if head >= interpreted_header.file_size as usize {\n        return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(\n            \"Flag value offset goes beyond the end of the file.\"\n        )));\n    }\n\n    let val = read_u8_from_bytes(buf, &mut head)?;\n    Ok(val == 1)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use aconfig_storage_file::{test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION};\n\n    #[test]\n    // this test point locks down flag value query\n    fn test_flag_value_query() {\n        let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes();\n        let baseline: Vec<bool> = vec![false, true, true, false, true, true, true, true];\n        for (offset, expected_value) in baseline.into_iter().enumerate() {\n            let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap();\n            assert_eq!(flag_value, expected_value);\n        }\n    }\n\n    #[test]\n    // this test point locks down query beyond the end of boolean section\n    fn test_boolean_out_of_range() {\n        let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes();\n        let error = find_boolean_flag_value(&flag_value_list[..], 8).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            \"InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)\"\n        );\n    }\n\n    #[test]\n    // this test point locks down query error when file has a higher version\n    fn test_higher_version_storage_file() {\n        let mut value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);\n        value_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1;\n        let flag_value = value_list.into_bytes();\n        let error = find_boolean_flag_value(&flag_value[..], 4).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            format!(\n                \"HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})\",\n                MAX_SUPPORTED_FILE_VERSION + 1,\n                MAX_SUPPORTED_FILE_VERSION\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/src/lib.rs",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! `aconfig_storage_read_api` is a crate that defines read apis to read flags from storage\n//! files. It provides four apis to interface with storage files:\n//!\n//! 1, function to get package read context\n//! pub fn get_packager_read_context(container: &str, package: &str)\n//! -> `Result<Option<PackageReadContext>>>`\n//!\n//! 2, function to get flag read context\n//! pub fn get_flag_read_context(container: &str, package_id: u32, flag: &str)\n//! -> `Result<Option<FlagReadContext>>>`\n//!\n//! 3, function to get the actual flag value given the global index (combined package and\n//! flag index).\n//! pub fn get_boolean_flag_value(container: &str, offset: u32) -> `Result<bool>`\n//!\n//! 4, function to get storage file version without mmapping the file.\n//! pub fn get_storage_file_version(file_path: &str) -> Result<u32, AconfigStorageError>\n//!\n//! Note these are low level apis that are expected to be only used in auto generated flag\n//! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis\n//! please refer to the g3doc go/android-flags\n\npub mod flag_info_query;\npub mod flag_table_query;\npub mod flag_value_query;\npub mod mapped_file;\npub mod package_table_query;\n\npub use aconfig_storage_file::{AconfigStorageError, FlagValueType, StorageFileType};\npub use flag_table_query::FlagReadContext;\npub use mapped_file::map_file;\npub use package_table_query::PackageReadContext;\n\nuse aconfig_storage_file::read_u32_from_bytes;\nuse flag_info_query::find_flag_attribute;\nuse flag_table_query::find_flag_read_context;\nuse flag_value_query::find_boolean_flag_value;\nuse package_table_query::find_package_read_context;\n\nuse anyhow::anyhow;\npub use memmap2::Mmap;\nuse std::fs::File;\nuse std::io::Read;\n\n/// Storage file location\npub const STORAGE_LOCATION: &str = \"/metadata/aconfig\";\n\n/// Get read only mapped storage files.\n///\n/// \\input container: the flag package container\n/// \\input file_type: stoarge file type enum\n/// \\return a result of read only mapped file\n///\n/// # Safety\n///\n/// The memory mapped file may have undefined behavior if there are writes to this\n/// file after being mapped. Ensure no writes can happen to this file while this\n/// mapping stays alive.\npub unsafe fn get_mapped_storage_file(\n    container: &str,\n    file_type: StorageFileType,\n) -> Result<Mmap, AconfigStorageError> {\n    unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION, container, file_type) }\n}\n\n/// Get package read context for a specific package.\n///\n/// \\input file: mapped package file\n/// \\input package: package name\n///\n/// \\return\n/// If a package is found, it returns Ok(Some(PackageReadContext))\n/// If a package is not found, it returns Ok(None)\n/// If errors out, it returns an Err(errmsg)\npub fn get_package_read_context(\n    file: &Mmap,\n    package: &str,\n) -> Result<Option<PackageReadContext>, AconfigStorageError> {\n    find_package_read_context(file, package)\n}\n\n/// Get flag read context for a specific flag.\n///\n/// \\input file: mapped flag file\n/// \\input package_id: package id obtained from package mapping file\n/// \\input flag: flag name\n///\n/// \\return\n/// If a flag is found, it returns Ok(Some(FlagReadContext))\n/// If a flag is not found, it returns Ok(None)\n/// If errors out, it returns an Err(errmsg)\npub fn get_flag_read_context(\n    file: &Mmap,\n    package_id: u32,\n    flag: &str,\n) -> Result<Option<FlagReadContext>, AconfigStorageError> {\n    find_flag_read_context(file, package_id, flag)\n}\n\n/// Get the boolean flag value.\n///\n/// \\input file: a byte slice, can be either &Mmap or &MapMut\n/// \\input index: boolean flag offset\n///\n/// \\return\n/// If the provide offset is valid, it returns the boolean flag value, otherwise it\n/// returns the error message.\npub fn get_boolean_flag_value(file: &[u8], index: u32) -> Result<bool, AconfigStorageError> {\n    find_boolean_flag_value(file, index)\n}\n\n/// Get storage file version number\n///\n/// This function would read the first four bytes of the file and interpret it as the\n/// version number of the file. There are unit tests in aconfig_storage_file crate to\n/// lock down that for all storage files, the first four bytes will be the version\n/// number of the storage file\npub fn get_storage_file_version(file_path: &str) -> Result<u32, AconfigStorageError> {\n    let mut file = File::open(file_path).map_err(|errmsg| {\n        AconfigStorageError::FileReadFail(anyhow!(\"Failed to open file {}: {}\", file_path, errmsg))\n    })?;\n    let mut buffer = [0; 4];\n    file.read(&mut buffer).map_err(|errmsg| {\n        AconfigStorageError::FileReadFail(anyhow!(\n            \"Failed to read 4 bytes from file {}: {}\",\n            file_path,\n            errmsg\n        ))\n    })?;\n    let mut head = 0;\n    read_u32_from_bytes(&buffer, &mut head)\n}\n\n/// Get the flag attribute.\n///\n/// \\input file: a byte slice, can be either &Mmap or &MapMut\n/// \\input flag_type: flag value type\n/// \\input flag_index: flag index\n///\n/// \\return\n/// If the provide offset is valid, it returns the flag attribute bitfiled, otherwise it\n/// returns the error message.\npub fn get_flag_attribute(\n    file: &[u8],\n    flag_type: FlagValueType,\n    flag_index: u32,\n) -> Result<u8, AconfigStorageError> {\n    find_flag_attribute(file, flag_type, flag_index)\n}\n\n// *************************************** //\n// CC INTERLOP\n// *************************************** //\n\n// Exported rust data structure and methods, c++ code will be generated\n#[cxx::bridge]\nmod ffi {\n    // Storage file version query return for cc interlop\n    pub struct VersionNumberQueryCXX {\n        pub query_success: bool,\n        pub error_message: String,\n        pub version_number: u32,\n    }\n\n    // Package table query return for cc interlop\n    pub struct PackageReadContextQueryCXX {\n        pub query_success: bool,\n        pub error_message: String,\n        pub package_exists: bool,\n        pub package_id: u32,\n        pub boolean_start_index: u32,\n    }\n\n    // Flag table query return for cc interlop\n    pub struct FlagReadContextQueryCXX {\n        pub query_success: bool,\n        pub error_message: String,\n        pub flag_exists: bool,\n        pub flag_type: u16,\n        pub flag_index: u16,\n    }\n\n    // Flag value query return for cc interlop\n    pub struct BooleanFlagValueQueryCXX {\n        pub query_success: bool,\n        pub error_message: String,\n        pub flag_value: bool,\n    }\n\n    // Flag info query return for cc interlop\n    pub struct FlagAttributeQueryCXX {\n        pub query_success: bool,\n        pub error_message: String,\n        pub flag_attribute: u8,\n    }\n\n    // Rust export to c++\n    extern \"Rust\" {\n        pub fn get_storage_file_version_cxx(file_path: &str) -> VersionNumberQueryCXX;\n\n        pub fn get_package_read_context_cxx(\n            file: &[u8],\n            package: &str,\n        ) -> PackageReadContextQueryCXX;\n\n        pub fn get_flag_read_context_cxx(\n            file: &[u8],\n            package_id: u32,\n            flag: &str,\n        ) -> FlagReadContextQueryCXX;\n\n        pub fn get_boolean_flag_value_cxx(file: &[u8], offset: u32) -> BooleanFlagValueQueryCXX;\n\n        pub fn get_flag_attribute_cxx(\n            file: &[u8],\n            flag_type: u16,\n            flag_index: u32,\n        ) -> FlagAttributeQueryCXX;\n    }\n}\n\n/// Implement the package offset interlop return type, create from actual package offset api return type\nimpl ffi::PackageReadContextQueryCXX {\n    pub(crate) fn new(\n        offset_result: Result<Option<PackageReadContext>, AconfigStorageError>,\n    ) -> Self {\n        match offset_result {\n            Ok(offset_opt) => match offset_opt {\n                Some(offset) => Self {\n                    query_success: true,\n                    error_message: String::from(\"\"),\n                    package_exists: true,\n                    package_id: offset.package_id,\n                    boolean_start_index: offset.boolean_start_index,\n                },\n                None => Self {\n                    query_success: true,\n                    error_message: String::from(\"\"),\n                    package_exists: false,\n                    package_id: 0,\n                    boolean_start_index: 0,\n                },\n            },\n            Err(errmsg) => Self {\n                query_success: false,\n                error_message: format!(\"{:?}\", errmsg),\n                package_exists: false,\n                package_id: 0,\n                boolean_start_index: 0,\n            },\n        }\n    }\n}\n\n/// Implement the flag offset interlop return type, create from actual flag offset api return type\nimpl ffi::FlagReadContextQueryCXX {\n    pub(crate) fn new(offset_result: Result<Option<FlagReadContext>, AconfigStorageError>) -> Self {\n        match offset_result {\n            Ok(offset_opt) => match offset_opt {\n                Some(offset) => Self {\n                    query_success: true,\n                    error_message: String::from(\"\"),\n                    flag_exists: true,\n                    flag_type: offset.flag_type as u16,\n                    flag_index: offset.flag_index,\n                },\n                None => Self {\n                    query_success: true,\n                    error_message: String::from(\"\"),\n                    flag_exists: false,\n                    flag_type: 0u16,\n                    flag_index: 0u16,\n                },\n            },\n            Err(errmsg) => Self {\n                query_success: false,\n                error_message: format!(\"{:?}\", errmsg),\n                flag_exists: false,\n                flag_type: 0u16,\n                flag_index: 0u16,\n            },\n        }\n    }\n}\n\n/// Implement the flag value interlop return type, create from actual flag value api return type\nimpl ffi::BooleanFlagValueQueryCXX {\n    pub(crate) fn new(value_result: Result<bool, AconfigStorageError>) -> Self {\n        match value_result {\n            Ok(value) => {\n                Self { query_success: true, error_message: String::from(\"\"), flag_value: value }\n            }\n            Err(errmsg) => Self {\n                query_success: false,\n                error_message: format!(\"{:?}\", errmsg),\n                flag_value: false,\n            },\n        }\n    }\n}\n\n/// Implement the flag info interlop return type, create from actual flag info api return type\nimpl ffi::FlagAttributeQueryCXX {\n    pub(crate) fn new(info_result: Result<u8, AconfigStorageError>) -> Self {\n        match info_result {\n            Ok(info) => {\n                Self { query_success: true, error_message: String::from(\"\"), flag_attribute: info }\n            }\n            Err(errmsg) => Self {\n                query_success: false,\n                error_message: format!(\"{:?}\", errmsg),\n                flag_attribute: 0u8,\n            },\n        }\n    }\n}\n\n/// Implement the storage version number interlop return type, create from actual version number\n/// api return type\nimpl ffi::VersionNumberQueryCXX {\n    pub(crate) fn new(version_result: Result<u32, AconfigStorageError>) -> Self {\n        match version_result {\n            Ok(version) => Self {\n                query_success: true,\n                error_message: String::from(\"\"),\n                version_number: version,\n            },\n            Err(errmsg) => Self {\n                query_success: false,\n                error_message: format!(\"{:?}\", errmsg),\n                version_number: 0,\n            },\n        }\n    }\n}\n\n/// Get package read context cc interlop\npub fn get_package_read_context_cxx(file: &[u8], package: &str) -> ffi::PackageReadContextQueryCXX {\n    ffi::PackageReadContextQueryCXX::new(find_package_read_context(file, package))\n}\n\n/// Get flag read context cc interlop\npub fn get_flag_read_context_cxx(\n    file: &[u8],\n    package_id: u32,\n    flag: &str,\n) -> ffi::FlagReadContextQueryCXX {\n    ffi::FlagReadContextQueryCXX::new(find_flag_read_context(file, package_id, flag))\n}\n\n/// Get boolean flag value cc interlop\npub fn get_boolean_flag_value_cxx(file: &[u8], offset: u32) -> ffi::BooleanFlagValueQueryCXX {\n    ffi::BooleanFlagValueQueryCXX::new(find_boolean_flag_value(file, offset))\n}\n\n/// Get flag attribute cc interlop\npub fn get_flag_attribute_cxx(\n    file: &[u8],\n    flag_type: u16,\n    flag_index: u32,\n) -> ffi::FlagAttributeQueryCXX {\n    match FlagValueType::try_from(flag_type) {\n        Ok(value_type) => {\n            ffi::FlagAttributeQueryCXX::new(find_flag_attribute(file, value_type, flag_index))\n        }\n        Err(errmsg) => ffi::FlagAttributeQueryCXX::new(Err(errmsg)),\n    }\n}\n\n/// Get storage version number cc interlop\npub fn get_storage_file_version_cxx(file_path: &str) -> ffi::VersionNumberQueryCXX {\n    ffi::VersionNumberQueryCXX::new(get_storage_file_version(file_path))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::mapped_file::get_mapped_file;\n    use aconfig_storage_file::{FlagInfoBit, StoredFlagType};\n    use rand::Rng;\n    use std::fs;\n\n    fn create_test_storage_files() -> String {\n        let mut rng = rand::thread_rng();\n        let number: u32 = rng.gen();\n        let storage_dir = String::from(\"/tmp/\") + &number.to_string();\n        if std::fs::metadata(&storage_dir).is_ok() {\n            fs::remove_dir_all(&storage_dir).unwrap();\n        }\n        let maps_dir = storage_dir.clone() + \"/maps\";\n        let boot_dir = storage_dir.clone() + \"/boot\";\n        fs::create_dir(&storage_dir).unwrap();\n        fs::create_dir(&maps_dir).unwrap();\n        fs::create_dir(&boot_dir).unwrap();\n\n        let package_map = storage_dir.clone() + \"/maps/mockup.package.map\";\n        let flag_map = storage_dir.clone() + \"/maps/mockup.flag.map\";\n        let flag_val = storage_dir.clone() + \"/boot/mockup.val\";\n        let flag_info = storage_dir.clone() + \"/boot/mockup.info\";\n        fs::copy(\"./tests/data/v1/package_v1.map\", &package_map).unwrap();\n        fs::copy(\"./tests/data/v1/flag_v1.map\", &flag_map).unwrap();\n        fs::copy(\"./tests/data/v1/flag_v1.val\", &flag_val).unwrap();\n        fs::copy(\"./tests/data/v1/flag_v1.info\", &flag_info).unwrap();\n\n        return storage_dir;\n    }\n\n    #[test]\n    // this test point locks down flag package read context query\n    fn test_package_context_query() {\n        let storage_dir = create_test_storage_files();\n        let package_mapped_file = unsafe {\n            get_mapped_file(&storage_dir, \"mockup\", StorageFileType::PackageMap).unwrap()\n        };\n\n        let package_context =\n            get_package_read_context(&package_mapped_file, \"com.android.aconfig.storage.test_1\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context =\n            PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 0 };\n        assert_eq!(package_context, expected_package_context);\n\n        let package_context =\n            get_package_read_context(&package_mapped_file, \"com.android.aconfig.storage.test_2\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context =\n            PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 0 };\n        assert_eq!(package_context, expected_package_context);\n\n        let package_context =\n            get_package_read_context(&package_mapped_file, \"com.android.aconfig.storage.test_4\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context =\n            PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 0 };\n        assert_eq!(package_context, expected_package_context);\n    }\n\n    #[test]\n    // this test point locks down flag read context query\n    fn test_flag_context_query() {\n        let storage_dir = create_test_storage_files();\n        let flag_mapped_file =\n            unsafe { get_mapped_file(&storage_dir, \"mockup\", StorageFileType::FlagMap).unwrap() };\n\n        let baseline = vec![\n            (0, \"enabled_ro\", StoredFlagType::ReadOnlyBoolean, 1u16),\n            (0, \"enabled_rw\", StoredFlagType::ReadWriteBoolean, 2u16),\n            (2, \"enabled_rw\", StoredFlagType::ReadWriteBoolean, 1u16),\n            (1, \"disabled_rw\", StoredFlagType::ReadWriteBoolean, 0u16),\n            (1, \"enabled_fixed_ro\", StoredFlagType::FixedReadOnlyBoolean, 1u16),\n            (1, \"enabled_ro\", StoredFlagType::ReadOnlyBoolean, 2u16),\n            (2, \"enabled_fixed_ro\", StoredFlagType::FixedReadOnlyBoolean, 0u16),\n            (0, \"disabled_rw\", StoredFlagType::ReadWriteBoolean, 0u16),\n        ];\n        for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() {\n            let flag_context =\n                get_flag_read_context(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();\n            assert_eq!(flag_context.flag_type, flag_type);\n            assert_eq!(flag_context.flag_index, flag_index);\n        }\n    }\n\n    #[test]\n    // this test point locks down flag value query\n    fn test_flag_value_query() {\n        let storage_dir = create_test_storage_files();\n        let flag_value_file =\n            unsafe { get_mapped_file(&storage_dir, \"mockup\", StorageFileType::FlagVal).unwrap() };\n        let baseline: Vec<bool> = vec![false, true, true, false, true, true, true, true];\n        for (offset, expected_value) in baseline.into_iter().enumerate() {\n            let flag_value = get_boolean_flag_value(&flag_value_file, offset as u32).unwrap();\n            assert_eq!(flag_value, expected_value);\n        }\n    }\n\n    #[test]\n    // this test point locks donw flag info query\n    fn test_flag_info_query() {\n        let storage_dir = create_test_storage_files();\n        let flag_info_file =\n            unsafe { get_mapped_file(&storage_dir, \"mockup\", StorageFileType::FlagInfo).unwrap() };\n        let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true];\n        for (offset, expected_value) in is_rw.into_iter().enumerate() {\n            let attribute =\n                get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();\n            assert_eq!((attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, expected_value);\n            assert!((attribute & FlagInfoBit::HasServerOverride as u8) == 0u8);\n            assert!((attribute & FlagInfoBit::HasLocalOverride as u8) == 0u8);\n        }\n    }\n\n    #[test]\n    // this test point locks down flag storage file version number query api\n    fn test_storage_version_query() {\n        assert_eq!(get_storage_file_version(\"./tests/data/v1/package_v1.map\").unwrap(), 1);\n        assert_eq!(get_storage_file_version(\"./tests/data/v1/flag_v1.map\").unwrap(), 1);\n        assert_eq!(get_storage_file_version(\"./tests/data/v1/flag_v1.val\").unwrap(), 1);\n        assert_eq!(get_storage_file_version(\"./tests/data/v1/flag_v1.info\").unwrap(), 1);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse anyhow::anyhow;\nuse memmap2::Mmap;\nuse std::fs::File;\n\nuse crate::AconfigStorageError::{self, FileReadFail, MapFileFail, StorageFileNotFound};\nuse crate::StorageFileType;\n\n/// Get the read only memory mapping of a storage file\n///\n/// # Safety\n///\n/// The memory mapped file may have undefined behavior if there are writes to this\n/// file after being mapped. Ensure no writes can happen to this file while this\n/// mapping stays alive.\npub unsafe fn map_file(file_path: &str) -> Result<Mmap, AconfigStorageError> {\n    let file = File::open(file_path)\n        .map_err(|errmsg| FileReadFail(anyhow!(\"Failed to open file {}: {}\", file_path, errmsg)))?;\n    unsafe {\n        let mapped_file = Mmap::map(&file).map_err(|errmsg| {\n            MapFileFail(anyhow!(\"fail to map storage file {}: {}\", file_path, errmsg))\n        })?;\n        Ok(mapped_file)\n    }\n}\n\n/// Get a mapped storage file given the container and file type\n///\n/// # Safety\n///\n/// The memory mapped file may have undefined behavior if there are writes to this\n/// file after being mapped. Ensure no writes can happen to this file while this\n/// mapping stays alive.\npub unsafe fn get_mapped_file(\n    storage_dir: &str,\n    container: &str,\n    file_type: StorageFileType,\n) -> Result<Mmap, AconfigStorageError> {\n    let storage_file = match file_type {\n        StorageFileType::PackageMap => {\n            String::from(storage_dir) + \"/maps/\" + container + \".package.map\"\n        }\n        StorageFileType::FlagMap => String::from(storage_dir) + \"/maps/\" + container + \".flag.map\",\n        StorageFileType::FlagVal => String::from(storage_dir) + \"/boot/\" + container + \".val\",\n        StorageFileType::FlagInfo => String::from(storage_dir) + \"/boot/\" + container + \".info\",\n    };\n    if std::fs::metadata(&storage_file).is_err() {\n        return Err(StorageFileNotFound(anyhow!(\"storage file {} does not exist\", storage_file)));\n    }\n    unsafe { map_file(&storage_file) }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use rand::Rng;\n    use std::fs;\n    use std::io::Read;\n\n    fn map_and_verify(storage_dir: &str, file_type: StorageFileType, actual_file: &str) {\n        let mut opened_file = File::open(actual_file).unwrap();\n        let mut content = Vec::new();\n        opened_file.read_to_end(&mut content).unwrap();\n        let mmaped_file = unsafe { get_mapped_file(storage_dir, \"mockup\", file_type).unwrap() };\n        assert_eq!(mmaped_file[..], content[..]);\n    }\n\n    fn create_test_storage_files() -> String {\n        let mut rng = rand::thread_rng();\n        let number: u32 = rng.gen();\n        let storage_dir = String::from(\"/tmp/\") + &number.to_string();\n        if std::fs::metadata(&storage_dir).is_ok() {\n            fs::remove_dir_all(&storage_dir).unwrap();\n        }\n        let maps_dir = storage_dir.clone() + \"/maps\";\n        let boot_dir = storage_dir.clone() + \"/boot\";\n        fs::create_dir(&storage_dir).unwrap();\n        fs::create_dir(&maps_dir).unwrap();\n        fs::create_dir(&boot_dir).unwrap();\n\n        let package_map = storage_dir.clone() + \"/maps/mockup.package.map\";\n        let flag_map = storage_dir.clone() + \"/maps/mockup.flag.map\";\n        let flag_val = storage_dir.clone() + \"/boot/mockup.val\";\n        let flag_info = storage_dir.clone() + \"/boot/mockup.info\";\n        fs::copy(\"./tests/data/v1/package_v1.map\", &package_map).unwrap();\n        fs::copy(\"./tests/data/v1/flag_v1.map\", &flag_map).unwrap();\n        fs::copy(\"./tests/data/v1/flag_v1.val\", &flag_val).unwrap();\n        fs::copy(\"./tests/data/v1/flag_v1.info\", &flag_info).unwrap();\n\n        return storage_dir;\n    }\n\n    #[test]\n    fn test_mapped_file_contents() {\n        let storage_dir = create_test_storage_files();\n        map_and_verify(&storage_dir, StorageFileType::PackageMap, \"./tests/data/v1/package_v1.map\");\n        map_and_verify(&storage_dir, StorageFileType::FlagMap, \"./tests/data/v1/flag_v1.map\");\n        map_and_verify(&storage_dir, StorageFileType::FlagVal, \"./tests/data/v1/flag_v1.val\");\n        map_and_verify(&storage_dir, StorageFileType::FlagInfo, \"./tests/data/v1/flag_v1.info\");\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! package table query module defines the package table file read from mapped bytes\n\nuse crate::AconfigStorageError;\nuse aconfig_storage_file::{\n    package_table::PackageTableHeader, package_table::PackageTableNode, read_u32_from_bytes,\n    MAX_SUPPORTED_FILE_VERSION,\n};\nuse anyhow::anyhow;\n\n/// Package table query return\n#[derive(PartialEq, Debug)]\npub struct PackageReadContext {\n    pub package_id: u32,\n    pub boolean_start_index: u32,\n    pub fingerprint: u64,\n}\n\n/// Query package read context: package id and start index\npub fn find_package_read_context(\n    buf: &[u8],\n    package: &str,\n) -> Result<Option<PackageReadContext>, AconfigStorageError> {\n    let interpreted_header = PackageTableHeader::from_bytes(buf)?;\n    if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION {\n        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(\n            \"Cannot read storage file with a higher version of {} with lib version {}\",\n            interpreted_header.version,\n            MAX_SUPPORTED_FILE_VERSION\n        )));\n    }\n\n    let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;\n    let bucket_index = PackageTableNode::find_bucket_index(package, num_buckets);\n\n    let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize;\n    let mut package_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize;\n    if package_node_offset < interpreted_header.node_offset as usize\n        || package_node_offset >= interpreted_header.file_size as usize\n    {\n        return Ok(None);\n    }\n\n    loop {\n        let interpreted_node =\n            PackageTableNode::from_bytes(&buf[package_node_offset..], interpreted_header.version)?;\n        if interpreted_node.package_name == package {\n            return Ok(Some(PackageReadContext {\n                package_id: interpreted_node.package_id,\n                boolean_start_index: interpreted_node.boolean_start_index,\n                fingerprint: interpreted_node.fingerprint,\n            }));\n        }\n        match interpreted_node.next_offset {\n            Some(offset) => package_node_offset = offset as usize,\n            None => return Ok(None),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use aconfig_storage_file::{test_utils::create_test_package_table, DEFAULT_FILE_VERSION};\n\n    #[test]\n    // this test point locks down table query\n    fn test_package_query() {\n        let package_table = create_test_package_table(DEFAULT_FILE_VERSION).into_bytes();\n        let package_context =\n            find_package_read_context(&package_table[..], \"com.android.aconfig.storage.test_1\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context =\n            PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 0 };\n        assert_eq!(package_context, expected_package_context);\n        let package_context =\n            find_package_read_context(&package_table[..], \"com.android.aconfig.storage.test_2\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context =\n            PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 0 };\n        assert_eq!(package_context, expected_package_context);\n        let package_context =\n            find_package_read_context(&package_table[..], \"com.android.aconfig.storage.test_4\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context =\n            PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 0 };\n        assert_eq!(package_context, expected_package_context);\n    }\n\n    #[test]\n    // this test point locks down table query\n    fn test_package_query_v2() {\n        let package_table = create_test_package_table(2).into_bytes();\n        let package_context =\n            find_package_read_context(&package_table[..], \"com.android.aconfig.storage.test_1\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context = PackageReadContext {\n            package_id: 0,\n            boolean_start_index: 0,\n            fingerprint: 15248948510590158086u64,\n        };\n        assert_eq!(package_context, expected_package_context);\n        let package_context =\n            find_package_read_context(&package_table[..], \"com.android.aconfig.storage.test_2\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context = PackageReadContext {\n            package_id: 1,\n            boolean_start_index: 3,\n            fingerprint: 4431940502274857964u64,\n        };\n        assert_eq!(package_context, expected_package_context);\n        let package_context =\n            find_package_read_context(&package_table[..], \"com.android.aconfig.storage.test_4\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context = PackageReadContext {\n            package_id: 2,\n            boolean_start_index: 6,\n            fingerprint: 16233229917711622375u64,\n        };\n        assert_eq!(package_context, expected_package_context);\n    }\n\n    #[test]\n    // this test point locks down table query of a non exist package\n    fn test_not_existed_package_query() {\n        // this will land at an empty bucket\n        let package_table = create_test_package_table(DEFAULT_FILE_VERSION).into_bytes();\n        let package_context =\n            find_package_read_context(&package_table[..], \"com.android.aconfig.storage.test_3\")\n                .unwrap();\n        assert_eq!(package_context, None);\n        // this will land at the end of a linked list\n        let package_context =\n            find_package_read_context(&package_table[..], \"com.android.aconfig.storage.test_5\")\n                .unwrap();\n        assert_eq!(package_context, None);\n    }\n\n    #[test]\n    // this test point locks down query error when file has a higher version\n    fn test_higher_version_storage_file() {\n        let mut table = create_test_package_table(DEFAULT_FILE_VERSION);\n        table.header.version = MAX_SUPPORTED_FILE_VERSION + 1;\n        let package_table = table.into_bytes();\n        let error =\n            find_package_read_context(&package_table[..], \"com.android.aconfig.storage.test_1\")\n                .unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            format!(\n                \"HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})\",\n                MAX_SUPPORTED_FILE_VERSION + 1,\n                MAX_SUPPORTED_FILE_VERSION\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigStorageReadAPI.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage;\n\nimport dalvik.annotation.optimization.FastNative;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\n\npublic class AconfigStorageReadAPI {\n\n    // Storage file dir on device\n    private static final String STORAGEDIR = \"/metadata/aconfig\";\n\n    // Stoarge file type\n    public enum StorageFileType {\n        PACKAGE_MAP,\n        FLAG_MAP,\n        FLAG_VAL,\n        FLAG_INFO\n    }\n\n    // Map a storage file given file path\n    public static MappedByteBuffer mapStorageFile(String file) throws IOException {\n        FileInputStream stream = new FileInputStream(file);\n        FileChannel channel = stream.getChannel();\n        return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());\n    }\n\n    // Map a storage file given container and file type\n    public static MappedByteBuffer getMappedFile(String container, StorageFileType type)\n            throws IOException {\n        switch (type) {\n            case PACKAGE_MAP:\n                return mapStorageFile(STORAGEDIR + \"/maps/\" + container + \".package.map\");\n            case FLAG_MAP:\n                return mapStorageFile(STORAGEDIR + \"/maps/\" + container + \".flag.map\");\n            case FLAG_VAL:\n                return mapStorageFile(STORAGEDIR + \"/boot/\" + container + \".val\");\n            case FLAG_INFO:\n                return mapStorageFile(STORAGEDIR + \"/boot/\" + container + \".info\");\n            default:\n                throw new IOException(\"Invalid storage file type\");\n        }\n    }\n\n    // JNI interface to get package read context\n    // @param mappedFile: memory mapped package map file\n    // @param packageName: package name\n    // @throws IOException if the passed in file is not a valid package map file\n    @FastNative\n    private static native ByteBuffer getPackageReadContextImpl(\n            ByteBuffer mappedFile, String packageName) throws IOException;\n\n    // API to get package read context\n    // @param mappedFile: memory mapped package map file\n    // @param packageName: package name\n    // @throws IOException if the passed in file is not a valid package map file\n    public static PackageReadContext getPackageReadContext(\n            ByteBuffer mappedFile, String packageName) throws IOException {\n        ByteBuffer buffer = getPackageReadContextImpl(mappedFile, packageName);\n        buffer.order(ByteOrder.LITTLE_ENDIAN);\n        return new PackageReadContext(buffer.getInt(), buffer.getInt(4));\n    }\n\n    // JNI interface to get flag read context\n    // @param mappedFile: memory mapped flag map file\n    // @param packageId: package id to represent a specific package, obtained from\n    // package map file\n    // @param flagName: flag name\n    // @throws IOException if the passed in file is not a valid flag map file\n    @FastNative\n    private static native ByteBuffer getFlagReadContextImpl(\n            ByteBuffer mappedFile, int packageId, String flagName) throws IOException;\n\n    // API to get flag read context\n    // @param mappedFile: memory mapped flag map file\n    // @param packageId: package id to represent a specific package, obtained from\n    // package map file\n    // @param flagName: flag name\n    // @throws IOException if the passed in file is not a valid flag map file\n    public static FlagReadContext getFlagReadContext(\n            ByteBuffer mappedFile, int packageId, String flagName) throws IOException {\n        ByteBuffer buffer = getFlagReadContextImpl(mappedFile, packageId, flagName);\n        buffer.order(ByteOrder.LITTLE_ENDIAN);\n        return new FlagReadContext(buffer.getInt(), buffer.getInt(4));\n    }\n\n    // JNI interface to get boolean flag value\n    // @param mappedFile: memory mapped flag value file\n    // @param flagIndex: flag global index in the flag value array\n    // @throws IOException if the passed in file is not a valid flag value file or the\n    // flag index went over the file boundary.\n    @FastNative\n    public static native boolean getBooleanFlagValue(ByteBuffer mappedFile, int flagIndex)\n            throws IOException;\n\n    @FastNative\n    public static native long hash(String packageName) throws IOException;\n\n    static {\n        System.loadLibrary(\"aconfig_storage_read_api_rust_jni\");\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/FlagReadContext.java",
    "content": "package android.aconfig.storage;\n/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npublic class FlagReadContext {\n    public StoredFlagType mFlagType;\n    public int mFlagIndex;\n\n    public FlagReadContext(int flagType,\n            int flagIndex) {\n        mFlagType = StoredFlagType.fromInteger(flagType);\n        mFlagIndex = flagIndex;\n    }\n\n    // Flag type enum, consistent with the definition in aconfig_storage_file/src/lib.rs\n    public enum StoredFlagType {\n        ReadWriteBoolean,\n        ReadOnlyBoolean,\n        FixedReadOnlyBoolean;\n\n        public static StoredFlagType fromInteger(int x) {\n            switch(x) {\n                case 0:\n                    return ReadWriteBoolean;\n                case 1:\n                    return ReadOnlyBoolean;\n                case 2:\n                    return FixedReadOnlyBoolean;\n                default:\n                    return null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/PackageReadContext.java",
    "content": "package android.aconfig.storage;\n/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npublic class PackageReadContext {\n    public int mPackageId;\n    public int mBooleanStartIndex;\n\n    public PackageReadContext(int packageId,\n                              int booleanStartIndex) {\n        mPackageId = packageId;\n        mBooleanStartIndex = booleanStartIndex;\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.os.flagging;\n\nimport static android.aconfig.storage.TableUtils.StorageFilesBundle;\n\nimport android.aconfig.storage.AconfigStorageException;\nimport android.aconfig.storage.FlagTable;\nimport android.aconfig.storage.FlagValueList;\nimport android.aconfig.storage.PackageTable;\nimport android.compat.annotation.UnsupportedAppUsage;\nimport android.util.Log;\n\nimport java.io.Closeable;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardOpenOption;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * An {@code aconfig} package containing the enabled state of its flags.\n *\n * <p><strong>Note: this is intended only to be used by generated code. To determine if a given flag\n * is enabled in app code, the generated android flags should be used.</strong>\n *\n * <p>This class is used to read the flag from platform Aconfig Package.Each instance of this class\n * will cache information related to one package. To read flags from a different package, a new\n * instance of this class should be {@link #load loaded}.\n *\n * @hide\n */\npublic class PlatformAconfigPackage {\n    private static final String TAG = \"PlatformAconfigPackage\";\n    private static final String MAP_PATH = \"/metadata/aconfig/maps/\";\n    private static final String BOOT_PATH = \"/metadata/aconfig/boot/\";\n\n    private FlagTable mFlagTable;\n    private FlagValueList mFlagValueList;\n\n    private int mPackageBooleanStartOffset = -1;\n    private int mPackageId = -1;\n\n    private PlatformAconfigPackage() {}\n\n    /** @hide */\n    static final Map<String, StorageFilesBundle> sStorageFilesCache = new HashMap<>();\n\n    /** @hide */\n    @UnsupportedAppUsage\n    public static final Set<String> PLATFORM_PACKAGE_MAP_FILES =\n            Set.of(\n                    \"system.package.map\",\n                    \"system_ext.package.map\",\n                    \"vendor.package.map\",\n                    \"product.package.map\");\n\n    static {\n        for (String pf : PLATFORM_PACKAGE_MAP_FILES) {\n            try {\n                PackageTable pTable = PackageTable.fromBytes(mapStorageFile(MAP_PATH + pf));\n                String container = pTable.getHeader().getContainer();\n                FlagTable fTable =\n                        FlagTable.fromBytes(mapStorageFile(MAP_PATH + container + \".flag.map\"));\n                FlagValueList fValueList =\n                        FlagValueList.fromBytes(mapStorageFile(BOOT_PATH + container + \".val\"));\n                StorageFilesBundle files = new StorageFilesBundle(pTable, fTable, fValueList);\n                for (String packageName : pTable.getPackageList()) {\n                    sStorageFilesCache.put(packageName, files);\n                }\n            } catch (Exception e) {\n                // pass\n                Log.w(TAG, e.toString());\n            }\n        }\n    }\n\n    /**\n     * Loads a platform Aconfig Package from Aconfig Storage.\n     *\n     * <p>This method attempts to load the specified platform Aconfig package.\n     *\n     * @param packageName The name of the Aconfig package to load.\n     * @return An instance of {@link PlatformAconfigPackage}, which may be empty if the package is\n     *     not found in the container. Null if the package is not found in platform partitions.\n     * @throws AconfigStorageReadException if there is an error reading from Aconfig Storage, such\n     *     as if the storage system is not found, or there is an error reading the storage file. The\n     *     specific error code can be got using {@link AconfigStorageReadException#getErrorCode()}.\n     * @hide\n     */\n    @UnsupportedAppUsage\n    public static PlatformAconfigPackage load(String packageName) {\n        try {\n            PlatformAconfigPackage aconfigPackage = new PlatformAconfigPackage();\n            StorageFilesBundle files = sStorageFilesCache.get(packageName);\n            if (files == null) {\n                return null;\n            }\n            PackageTable.Node pNode = files.packageTable.get(packageName);\n            aconfigPackage.mFlagTable = files.flagTable;\n            aconfigPackage.mFlagValueList = files.flagValueList;\n            aconfigPackage.mPackageBooleanStartOffset = pNode.getBooleanStartIndex();\n            aconfigPackage.mPackageId = pNode.getPackageId();\n            return aconfigPackage;\n        } catch (AconfigStorageException e) {\n            throw new AconfigStorageReadException(\n                    e.getErrorCode(), \"Fail to create PlatformAconfigPackage: \" + packageName, e);\n        } catch (Exception e) {\n            throw new AconfigStorageReadException(\n                    AconfigStorageReadException.ERROR_GENERIC,\n                    \"Fail to create PlatformAconfigPackage: \" + packageName,\n                    e);\n        }\n    }\n\n    /**\n     * Retrieves the value of a boolean flag.\n     *\n     * <p>This method retrieves the value of the specified flag. If the flag exists within the\n     * loaded Aconfig Package, its value is returned. Otherwise, the provided `defaultValue` is\n     * returned.\n     *\n     * @param flagName The name of the flag (excluding any package name prefix).\n     * @param defaultValue The value to return if the flag is not found.\n     * @return The boolean value of the flag, or `defaultValue` if the flag is not found.\n     * @hide\n     */\n    @UnsupportedAppUsage\n    public boolean getBooleanFlagValue(String flagName, boolean defaultValue) {\n        FlagTable.Node fNode = mFlagTable.get(mPackageId, flagName);\n        if (fNode == null) {\n            return defaultValue;\n        }\n        return mFlagValueList.getBoolean(fNode.getFlagIndex() + mPackageBooleanStartOffset);\n    }\n\n    // Map a storage file given file path\n    private static MappedByteBuffer mapStorageFile(String file) {\n        FileChannel channel = null;\n        try {\n            channel = FileChannel.open(Paths.get(file), StandardOpenOption.READ);\n            return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());\n        } catch (Exception e) {\n            throw new AconfigStorageReadException(\n                    AconfigStorageReadException.ERROR_CANNOT_READ_STORAGE_FILE,\n                    \"Fail to mmap storage\",\n                    e);\n        } finally {\n            quietlyDispose(channel);\n        }\n    }\n\n    private static void quietlyDispose(Closeable closable) {\n        try {\n            if (closable != null) {\n                closable.close();\n            }\n        } catch (Exception e) {\n            // no need to care, at least as of now\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackageInternal.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.os.flagging;\n\nimport static android.aconfig.storage.TableUtils.StorageFilesBundle;\n\nimport android.aconfig.storage.AconfigStorageException;\nimport android.aconfig.storage.FlagValueList;\nimport android.aconfig.storage.PackageTable;\nimport android.compat.annotation.UnsupportedAppUsage;\n\n/**\n * An {@code aconfig} package containing the enabled state of its flags.\n *\n * <p><strong>Note: this is intended only to be used by generated code. To determine if a given flag\n * is enabled in app code, the generated android flags should be used.</strong>\n *\n * <p>This class is not part of the public API and should be used by Acnofig Flag internally </b> It\n * is intended for internal use only and will be changed or removed without notice.\n *\n * <p>This class is used to read the flag from Aconfig Package.Each instance of this class will\n * cache information related to one package. To read flags from a different package, a new instance\n * of this class should be {@link #load loaded}.\n *\n * @hide\n */\npublic class PlatformAconfigPackageInternal {\n\n    private final FlagValueList mFlagValueList;\n    private final int mPackageBooleanStartOffset;\n\n    private PlatformAconfigPackageInternal(\n            FlagValueList flagValueList, int packageBooleanStartOffset) {\n        this.mFlagValueList = flagValueList;\n        this.mPackageBooleanStartOffset = packageBooleanStartOffset;\n    }\n\n    /**\n     * Loads an Aconfig package from the specified container and verifies its fingerprint.\n     *\n     * <p>This method is intended for internal use only and may be changed or removed without\n     * notice.\n     *\n     * @param packageName The name of the Aconfig package.\n     * @param packageFingerprint The expected fingerprint of the package.\n     * @return An instance of {@link PlatformAconfigPackageInternal} representing the loaded\n     *     package.\n     * @hide\n     */\n    @UnsupportedAppUsage\n    public static PlatformAconfigPackageInternal load(String packageName, long packageFingerprint) {\n        StorageFilesBundle files = PlatformAconfigPackage.sStorageFilesCache.get(packageName);\n        if (files == null) {\n            throw new AconfigStorageException(\n                    AconfigStorageException.ERROR_PACKAGE_NOT_FOUND,\n                    \"package \" + packageName + \" cannot be found on the device\");\n        }\n        PackageTable.Node pNode = files.packageTable.get(packageName);\n        FlagValueList vList = files.flagValueList;\n\n        if (pNode.hasPackageFingerprint() && packageFingerprint != pNode.getPackageFingerprint()) {\n            throw new AconfigStorageException(\n                    AconfigStorageException.ERROR_FILE_FINGERPRINT_MISMATCH,\n                    \"package \" + packageName + \"fingerprint doesn't match the one on device\");\n        }\n\n        return new PlatformAconfigPackageInternal(vList, pNode.getBooleanStartIndex());\n    }\n\n    /**\n     * Retrieves the value of a boolean flag using its index.\n     *\n     * <p>This method is intended for internal use only and may be changed or removed without\n     * notice.\n     *\n     * <p>This method retrieves the value of a flag within the loaded Aconfig package using its\n     * index. The index is generated at build time and may vary between builds.\n     *\n     * @param index The index of the flag within the package.\n     * @return The boolean value of the flag.\n     * @hide\n     */\n    @UnsupportedAppUsage\n    public boolean getBooleanFlagValue(int index) {\n        return mFlagValueList.getBoolean(index + mPackageBooleanStartOffset);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/srcs/lib.rs",
    "content": "//! aconfig storage read api java rust interlop\n\nuse aconfig_storage_file::SipHasher13;\nuse aconfig_storage_read_api::flag_table_query::find_flag_read_context;\nuse aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;\nuse aconfig_storage_read_api::package_table_query::find_package_read_context;\nuse aconfig_storage_read_api::{FlagReadContext, PackageReadContext};\n\nuse anyhow::Result;\nuse jni::objects::{JByteBuffer, JClass, JString};\nuse jni::sys::{jboolean, jint, jlong};\nuse jni::JNIEnv;\nuse std::hash::Hasher;\n\n/// Call rust find package read context\nfn get_package_read_context_java(\n    env: &mut JNIEnv,\n    file: JByteBuffer,\n    package: JString,\n) -> Result<Option<PackageReadContext>> {\n    // SAFETY:\n    // The safety here is ensured as the package name is guaranteed to be a java string\n    let package_name: String = unsafe { env.get_string_unchecked(&package)?.into() };\n    let buffer_ptr = env.get_direct_buffer_address(&file)?;\n    let buffer_size = env.get_direct_buffer_capacity(&file)?;\n    // SAFETY:\n    // The safety here is ensured as only non null MemoryMappedBuffer will be passed in,\n    // so the conversion to slice is guaranteed to be valid\n    let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_size) };\n    Ok(find_package_read_context(buffer, &package_name)?)\n}\n\n/// Get package read context JNI\n#[no_mangle]\n#[allow(unused)]\npub extern \"system\" fn Java_android_aconfig_storage_AconfigStorageReadAPI_getPackageReadContextImpl<\n    'local,\n>(\n    mut env: JNIEnv<'local>,\n    class: JClass<'local>,\n    file: JByteBuffer<'local>,\n    package: JString<'local>,\n) -> JByteBuffer<'local> {\n    let mut package_id = -1;\n    let mut boolean_start_index = -1;\n\n    match get_package_read_context_java(&mut env, file, package) {\n        Ok(context_opt) => {\n            if let Some(context) = context_opt {\n                package_id = context.package_id as i32;\n                boolean_start_index = context.boolean_start_index as i32;\n            }\n        }\n        Err(errmsg) => {\n            env.throw((\"java/io/IOException\", errmsg.to_string())).expect(\"failed to throw\");\n        }\n    }\n\n    let mut bytes = Vec::new();\n    bytes.extend_from_slice(&package_id.to_le_bytes());\n    bytes.extend_from_slice(&boolean_start_index.to_le_bytes());\n    let (addr, len) = {\n        let buf = bytes.leak();\n        (buf.as_mut_ptr(), buf.len())\n    };\n    // SAFETY:\n    // The safety here is ensured as the content is ensured to be valid\n    unsafe { env.new_direct_byte_buffer(addr, len).expect(\"failed to create byte buffer\") }\n}\n\n/// Call rust find flag read context\nfn get_flag_read_context_java(\n    env: &mut JNIEnv,\n    file: JByteBuffer,\n    package_id: jint,\n    flag: JString,\n) -> Result<Option<FlagReadContext>> {\n    // SAFETY:\n    // The safety here is ensured as the flag name is guaranteed to be a java string\n    let flag_name: String = unsafe { env.get_string_unchecked(&flag)?.into() };\n    let buffer_ptr = env.get_direct_buffer_address(&file)?;\n    let buffer_size = env.get_direct_buffer_capacity(&file)?;\n    // SAFETY:\n    // The safety here is ensured as only non null MemoryMappedBuffer will be passed in,\n    // so the conversion to slice is guaranteed to be valid\n    let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_size) };\n    Ok(find_flag_read_context(buffer, package_id as u32, &flag_name)?)\n}\n\n/// Get flag read context JNI\n#[no_mangle]\n#[allow(unused)]\npub extern \"system\" fn Java_android_aconfig_storage_AconfigStorageReadAPI_getFlagReadContextImpl<\n    'local,\n>(\n    mut env: JNIEnv<'local>,\n    class: JClass<'local>,\n    file: JByteBuffer<'local>,\n    package_id: jint,\n    flag: JString<'local>,\n) -> JByteBuffer<'local> {\n    let mut flag_type = -1;\n    let mut flag_index = -1;\n\n    match get_flag_read_context_java(&mut env, file, package_id, flag) {\n        Ok(context_opt) => {\n            if let Some(context) = context_opt {\n                flag_type = context.flag_type as i32;\n                flag_index = context.flag_index as i32;\n            }\n        }\n        Err(errmsg) => {\n            env.throw((\"java/io/IOException\", errmsg.to_string())).expect(\"failed to throw\");\n        }\n    }\n\n    let mut bytes = Vec::new();\n    bytes.extend_from_slice(&flag_type.to_le_bytes());\n    bytes.extend_from_slice(&flag_index.to_le_bytes());\n    let (addr, len) = {\n        let buf = bytes.leak();\n        (buf.as_mut_ptr(), buf.len())\n    };\n    // SAFETY:\n    // The safety here is ensured as the content is ensured to be valid\n    unsafe { env.new_direct_byte_buffer(addr, len).expect(\"failed to create byte buffer\") }\n}\n\n/// Call rust find boolean flag value\nfn get_boolean_flag_value_java(\n    env: &mut JNIEnv,\n    file: JByteBuffer,\n    flag_index: jint,\n) -> Result<bool> {\n    let buffer_ptr = env.get_direct_buffer_address(&file)?;\n    let buffer_size = env.get_direct_buffer_capacity(&file)?;\n    // SAFETY:\n    // The safety here is ensured as only non null MemoryMappedBuffer will be passed in,\n    // so the conversion to slice is guaranteed to be valid\n    let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_size) };\n    Ok(find_boolean_flag_value(buffer, flag_index as u32)?)\n}\n\n/// Get flag value JNI\n#[no_mangle]\n#[allow(unused)]\npub extern \"system\" fn Java_android_aconfig_storage_AconfigStorageReadAPI_getBooleanFlagValue<\n    'local,\n>(\n    mut env: JNIEnv<'local>,\n    class: JClass<'local>,\n    file: JByteBuffer<'local>,\n    flag_index: jint,\n) -> jboolean {\n    match get_boolean_flag_value_java(&mut env, file, flag_index) {\n        Ok(value) => value as u8,\n        Err(errmsg) => {\n            env.throw((\"java/io/IOException\", errmsg.to_string())).expect(\"failed to throw\");\n            0u8\n        }\n    }\n}\n\n/// Get flag value JNI\n#[no_mangle]\n#[allow(unused)]\npub extern \"system\" fn Java_android_aconfig_storage_AconfigStorageReadAPI_hash<'local>(\n    mut env: JNIEnv<'local>,\n    class: JClass<'local>,\n    package_name: JString<'local>,\n) -> jlong {\n    match siphasher13_hash(&mut env, package_name) {\n        Ok(value) => value as jlong,\n        Err(errmsg) => {\n            env.throw((\"java/io/IOException\", errmsg.to_string())).expect(\"failed to throw\");\n            0i64\n        }\n    }\n}\n\nfn siphasher13_hash(env: &mut JNIEnv, package_name: JString) -> Result<u64> {\n    // SAFETY:\n    // The safety here is ensured as the flag name is guaranteed to be a java string\n    let flag_name: String = unsafe { env.get_string_unchecked(&package_name)?.into() };\n    let mut s = SipHasher13::new();\n    s.write(flag_name.as_bytes());\n    s.write_u8(0xff);\n    Ok(s.finish())\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/tests/AconfigStorageReadFunctionalTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2024 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<configuration description=\"Config for aconfig storage read java api tests\">\n    <!-- Need root to start virtualizationservice -->\n    <target_preparer class=\"com.android.tradefed.targetprep.RootTargetPreparer\"/>\n\n    <!-- Prepare test directories. -->\n    <target_preparer class=\"com.android.tradefed.targetprep.RunCommandTargetPreparer\">\n        <option name=\"throw-if-cmd-fail\" value=\"true\" />\n        <option name=\"run-command\" value=\"mkdir -p /data/local/tmp/aconfig_java_api\" />\n        <option name=\"teardown-command\" value=\"rm -rf /data/local/tmp/aconfig_java_api\" />\n    </target_preparer>\n\n    <target_preparer class=\"com.android.tradefed.targetprep.TestAppInstallSetup\">\n        <option name=\"test-file-name\" value=\"aconfig_storage_read_functional.apk\" />\n    </target_preparer>\n\n    <target_preparer class=\"com.android.tradefed.targetprep.DisableSELinuxTargetPreparer\" />\n\n    <!-- Test data files -->\n    <target_preparer class=\"com.android.tradefed.targetprep.PushFilePreparer\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"abort-on-push-failure\" value=\"true\" />\n        <option name=\"push-file\" key=\"package_v1.map\"\n                value=\"/data/local/tmp/aconfig_java_api_test/maps/mockup.package.map\" />\n        <option name=\"push-file\" key=\"flag_v1.map\"\n                value=\"/data/local/tmp/aconfig_java_api_test/maps/mockup.flag.map\" />\n        <option name=\"push-file\" key=\"flag_v1.val\"\n                value=\"/data/local/tmp/aconfig_java_api_test/boot/mockup.val\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.AndroidJUnitTest\" >\n        <option name=\"runner\" value=\"androidx.test.runner.AndroidJUnitRunner\" />\n        <option name=\"package\" value=\"android.aconfig.storage.test\" />\n        <option name=\"runtime-hint\" value=\"1m\" />\n    </test>\n</configuration>"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/tests/Android.bp",
    "content": "filegroup {\n    name: \"read_api_test_storage_files\",\n    srcs: [\n        \"data/v1/package_v1.map\",\n        \"data/v1/flag_v1.map\",\n        \"data/v1/flag_v1.val\",\n        \"data/v1/flag_v1.info\",\n        \"data/v2/package_v2.map\",\n        \"data/v2/flag_v2.map\",\n        \"data/v2/flag_v2.val\",\n        \"data/v2/flag_v2.info\",\n    ],\n}\n\nrust_test {\n    name: \"aconfig_storage_read_api.test.rust\",\n    srcs: [\n        \"storage_read_api_test.rs\",\n    ],\n    rustlibs: [\n        \"libanyhow\",\n        \"libaconfig_storage_file\",\n        \"libaconfig_storage_read_api\",\n        \"librand\",\n    ],\n    data: [\n        \":read_api_test_storage_files\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\ncc_test {\n    name: \"aconfig_storage_read_api.test.cpp\",\n    srcs: [\n        \"storage_read_api_test.cpp\",\n    ],\n    static_libs: [\n        \"libgmock\",\n        \"libaconfig_storage_read_api_cc\",\n        \"libbase\",\n        \"liblog\",\n    ],\n    data: [\n        \":read_api_test_storage_files\",\n    ],\n    test_suites: [\n        \"device-tests\",\n        \"general-tests\",\n    ],\n}\n\nandroid_test {\n    name: \"aconfig_storage_read_functional\",\n    srcs: [\n        \"functional/srcs/**/*.java\",\n    ],\n    static_libs: [\n        \"aconfig_device_paths_java_util\",\n        \"aconfig_storage_file_java\",\n        \"androidx.test.rules\",\n        \"libaconfig_storage_read_api_java\",\n        \"junit\",\n    ],\n    jni_libs: [\n        \"libaconfig_storage_read_api_rust_jni\",\n    ],\n    data: [\n        \":read_api_test_storage_files\",\n    ],\n    platform_apis: true,\n    certificate: \"platform\",\n    test_suites: [\n        \"general-tests\",\n    ],\n    test_config: \"AconfigStorageReadFunctionalTest.xml\",\n    team: \"trendy_team_android_core_experiments\",\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/tests/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright (C) 2024 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"android.aconfig.storage.test\">\n    <application>\n        <uses-library android:name=\"android.test.runner\" />\n    </application>\n\n    <instrumentation android:name=\"androidx.test.runner.AndroidJUnitRunner\"\n                     android:targetPackage=\"android.aconfig.storage.test\" />\n\n</manifest>\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/AconfigStorageReadAPITest.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport android.aconfig.DeviceProtosTestUtil;\nimport android.aconfig.nano.Aconfig.parsed_flag;\nimport android.aconfig.storage.AconfigStorageReadAPI;\nimport android.aconfig.storage.FlagReadContext;\nimport android.aconfig.storage.FlagReadContext.StoredFlagType;\nimport android.aconfig.storage.PackageReadContext;\nimport android.aconfig.storage.SipHasher13;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.io.IOException;\nimport java.nio.MappedByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@RunWith(JUnit4.class)\npublic class AconfigStorageReadAPITest {\n\n    private String mStorageDir = \"/data/local/tmp/aconfig_java_api_test\";\n\n    @Test\n    public void testPackageContextQuery() {\n        MappedByteBuffer packageMap = null;\n        try {\n            packageMap =\n                    AconfigStorageReadAPI.mapStorageFile(mStorageDir + \"/maps/mockup.package.map\");\n        } catch (IOException ex) {\n            assertTrue(ex.toString(), false);\n        }\n        assertTrue(packageMap != null);\n\n        try {\n            PackageReadContext context =\n                    AconfigStorageReadAPI.getPackageReadContext(\n                            packageMap, \"com.android.aconfig.storage.test_1\");\n            assertEquals(context.mPackageId, 0);\n            assertEquals(context.mBooleanStartIndex, 0);\n\n            context =\n                    AconfigStorageReadAPI.getPackageReadContext(\n                            packageMap, \"com.android.aconfig.storage.test_2\");\n            assertEquals(context.mPackageId, 1);\n            assertEquals(context.mBooleanStartIndex, 3);\n\n            context =\n                    AconfigStorageReadAPI.getPackageReadContext(\n                            packageMap, \"com.android.aconfig.storage.test_4\");\n            assertEquals(context.mPackageId, 2);\n            assertEquals(context.mBooleanStartIndex, 6);\n        } catch (IOException ex) {\n            assertTrue(ex.toString(), false);\n        }\n    }\n\n    @Test\n    public void testNonExistPackageContextQuery() {\n        MappedByteBuffer packageMap = null;\n        try {\n            packageMap =\n                    AconfigStorageReadAPI.mapStorageFile(mStorageDir + \"/maps/mockup.package.map\");\n        } catch (IOException ex) {\n            assertTrue(ex.toString(), false);\n        }\n        assertTrue(packageMap != null);\n\n        try {\n            PackageReadContext context =\n                    AconfigStorageReadAPI.getPackageReadContext(packageMap, \"unknown\");\n            assertEquals(context.mPackageId, -1);\n            assertEquals(context.mBooleanStartIndex, -1);\n        } catch (IOException ex) {\n            assertTrue(ex.toString(), false);\n        }\n    }\n\n    @Test\n    public void testFlagContextQuery() {\n        MappedByteBuffer flagMap = null;\n        try {\n            flagMap = AconfigStorageReadAPI.mapStorageFile(mStorageDir + \"/maps/mockup.flag.map\");\n        } catch (IOException ex) {\n            assertTrue(ex.toString(), false);\n        }\n        assertTrue(flagMap != null);\n\n        class Baseline {\n            public int mPackageId;\n            public String mFlagName;\n            public StoredFlagType mFlagType;\n            public int mFlagIndex;\n\n            public Baseline(\n                    int packageId, String flagName, StoredFlagType flagType, int flagIndex) {\n                mPackageId = packageId;\n                mFlagName = flagName;\n                mFlagType = flagType;\n                mFlagIndex = flagIndex;\n            }\n        }\n\n        List<Baseline> baselines = new ArrayList();\n        baselines.add(new Baseline(0, \"enabled_ro\", StoredFlagType.ReadOnlyBoolean, 1));\n        baselines.add(new Baseline(0, \"enabled_rw\", StoredFlagType.ReadWriteBoolean, 2));\n        baselines.add(new Baseline(2, \"enabled_rw\", StoredFlagType.ReadWriteBoolean, 1));\n        baselines.add(new Baseline(1, \"disabled_rw\", StoredFlagType.ReadWriteBoolean, 0));\n        baselines.add(new Baseline(1, \"enabled_fixed_ro\", StoredFlagType.FixedReadOnlyBoolean, 1));\n        baselines.add(new Baseline(1, \"enabled_ro\", StoredFlagType.ReadOnlyBoolean, 2));\n        baselines.add(new Baseline(2, \"enabled_fixed_ro\", StoredFlagType.FixedReadOnlyBoolean, 0));\n        baselines.add(new Baseline(0, \"disabled_rw\", StoredFlagType.ReadWriteBoolean, 0));\n\n        try {\n            for (Baseline baseline : baselines) {\n                FlagReadContext context =\n                        AconfigStorageReadAPI.getFlagReadContext(\n                                flagMap, baseline.mPackageId, baseline.mFlagName);\n                assertEquals(context.mFlagType, baseline.mFlagType);\n                assertEquals(context.mFlagIndex, baseline.mFlagIndex);\n            }\n        } catch (IOException ex) {\n            assertTrue(ex.toString(), false);\n        }\n    }\n\n    @Test\n    public void testNonExistFlagContextQuery() {\n        MappedByteBuffer flagMap = null;\n        try {\n            flagMap = AconfigStorageReadAPI.mapStorageFile(mStorageDir + \"/maps/mockup.flag.map\");\n        } catch (IOException ex) {\n            assertTrue(ex.toString(), false);\n        }\n        assertTrue(flagMap != null);\n\n        try {\n            FlagReadContext context =\n                    AconfigStorageReadAPI.getFlagReadContext(flagMap, 0, \"unknown\");\n            assertEquals(context.mFlagType, null);\n            assertEquals(context.mFlagIndex, -1);\n\n            context = AconfigStorageReadAPI.getFlagReadContext(flagMap, 3, \"enabled_ro\");\n            assertEquals(context.mFlagType, null);\n            assertEquals(context.mFlagIndex, -1);\n        } catch (IOException ex) {\n            assertTrue(ex.toString(), false);\n        }\n    }\n\n    @Test\n    public void testBooleanFlagValueQuery() {\n        MappedByteBuffer flagVal = null;\n        try {\n            flagVal = AconfigStorageReadAPI.mapStorageFile(mStorageDir + \"/boot/mockup.val\");\n        } catch (IOException ex) {\n            assertTrue(ex.toString(), false);\n        }\n        assertTrue(flagVal != null);\n\n        boolean[] baselines = {false, true, true, false, true, true, true, true};\n        for (int i = 0; i < 8; ++i) {\n            try {\n                Boolean value = AconfigStorageReadAPI.getBooleanFlagValue(flagVal, i);\n                assertEquals(value, baselines[i]);\n            } catch (IOException ex) {\n                assertTrue(ex.toString(), false);\n            }\n        }\n    }\n\n    @Test\n    public void testInvalidBooleanFlagValueQuery() {\n        MappedByteBuffer flagVal = null;\n        try {\n            flagVal = AconfigStorageReadAPI.mapStorageFile(mStorageDir + \"/boot/mockup.val\");\n        } catch (IOException ex) {\n            assertTrue(ex.toString(), false);\n        }\n        assertTrue(flagVal != null);\n\n        try {\n            Boolean value = AconfigStorageReadAPI.getBooleanFlagValue(flagVal, 9);\n            assertTrue(\"should throw\", false);\n        } catch (IOException ex) {\n            String expectedErrmsg = \"invalid storage file byte offset\";\n            assertTrue(ex.toString(), ex.toString().contains(expectedErrmsg));\n        }\n    }\n\n    @Test\n    public void testRustJavaEqualHash() throws IOException {\n        List<parsed_flag> flags = DeviceProtosTestUtil.loadAndParseFlagProtos();\n        for (parsed_flag flag : flags) {\n            String packageName = flag.package_;\n            String flagName = flag.name;\n            long rHash = AconfigStorageReadAPI.hash(packageName);\n            long jHash = SipHasher13.hash(packageName.getBytes());\n            assertEquals(rHash, jHash);\n\n            String fullFlagName = packageName + \"/\" + flagName;\n            rHash = AconfigStorageReadAPI.hash(fullFlagName);\n            jHash = SipHasher13.hash(fullFlagName.getBytes());\n            assertEquals(rHash, jHash);\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertThrows;\n\nimport android.aconfig.DeviceProtosTestUtil;\nimport android.aconfig.nano.Aconfig;\nimport android.aconfig.nano.Aconfig.parsed_flag;\nimport android.aconfig.storage.FlagTable;\nimport android.aconfig.storage.FlagValueList;\nimport android.aconfig.storage.PackageTable;\nimport android.aconfig.storage.StorageFileProvider;\nimport android.internal.aconfig.storage.AconfigStorageException;\nimport android.os.flagging.PlatformAconfigPackageInternal;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n@RunWith(JUnit4.class)\npublic class PlatformAconfigPackageInternalTest {\n\n    private static final Set<String> PLATFORM_CONTAINERS =\n            Set.of(\"system\", \"system_ext\", \"vendor\", \"product\");\n\n    @Test\n    public void testPlatformAconfigPackageInternal_load() throws IOException {\n        List<parsed_flag> flags = DeviceProtosTestUtil.loadAndParseFlagProtos();\n        Map<String, PlatformAconfigPackageInternal> readerMap = new HashMap<>();\n        StorageFileProvider fp = StorageFileProvider.getDefaultProvider();\n\n        for (parsed_flag flag : flags) {\n            if (flag.permission == Aconfig.READ_ONLY && flag.state == Aconfig.DISABLED) {\n                continue;\n            }\n            String container = flag.container;\n            String packageName = flag.package_;\n            String flagName = flag.name;\n            if (!PLATFORM_CONTAINERS.contains(container)) continue;\n\n            PackageTable pTable = fp.getPackageTable(container);\n            PackageTable.Node pNode = pTable.get(packageName);\n            FlagTable fTable = fp.getFlagTable(container);\n            FlagTable.Node fNode = fTable.get(pNode.getPackageId(), flagName);\n            FlagValueList fList = fp.getFlagValueList(container);\n\n            int index = pNode.getBooleanStartIndex() + fNode.getFlagIndex();\n            boolean rVal = fList.getBoolean(index);\n\n            long fingerprint = pNode.getPackageFingerprint();\n\n            PlatformAconfigPackageInternal reader = readerMap.get(packageName);\n            if (reader == null) {\n                reader = PlatformAconfigPackageInternal.load(packageName, fingerprint);\n                readerMap.put(packageName, reader);\n            }\n            boolean jVal = reader.getBooleanFlagValue(fNode.getFlagIndex());\n\n            assertEquals(rVal, jVal);\n        }\n    }\n\n    @Test\n    public void testPlatformAconfigPackage_load_withError() throws IOException {\n        // package not found\n        AconfigStorageException e =\n                assertThrows(\n                        AconfigStorageException.class,\n                        () -> PlatformAconfigPackageInternal.load(\"fake_package\", 0));\n        assertEquals(AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, e.getErrorCode());\n\n        // fingerprint doesn't match\n        List<parsed_flag> flags = DeviceProtosTestUtil.loadAndParseFlagProtos();\n        StorageFileProvider fp = StorageFileProvider.getDefaultProvider();\n\n        parsed_flag flag = flags.get(0);\n\n        String container = flag.container;\n        String packageName = flag.package_;\n        boolean value = flag.state == Aconfig.ENABLED;\n\n        PackageTable pTable = fp.getPackageTable(container);\n        PackageTable.Node pNode = pTable.get(packageName);\n\n        if (pNode.hasPackageFingerprint()) {\n            long fingerprint = pNode.getPackageFingerprint();\n            e =\n                    assertThrows(\n                            AconfigStorageException.class,\n                            () ->\n                                    PlatformAconfigPackageInternal.load(\n                                            packageName, fingerprint + 1));\n            assertEquals(AconfigStorageException.ERROR_FILE_FINGERPRINT_MISMATCH, e.getErrorCode());\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.aconfig.storage.test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\n\nimport android.aconfig.DeviceProtosTestUtil;\nimport android.aconfig.nano.Aconfig;\nimport android.aconfig.nano.Aconfig.parsed_flag;\nimport android.aconfig.storage.FlagTable;\nimport android.aconfig.storage.FlagValueList;\nimport android.aconfig.storage.PackageTable;\nimport android.aconfig.storage.StorageFileProvider;\nimport android.os.flagging.PlatformAconfigPackage;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n@RunWith(JUnit4.class)\npublic class PlatformAconfigPackageTest {\n\n    private static final Set<String> PLATFORM_CONTAINERS =\n            Set.of(\"system\", \"system_ext\", \"vendor\", \"product\");\n\n    @Test\n    public void testPlatformAconfigPackage_StorageFilesCache() throws IOException {\n        List<parsed_flag> flags = DeviceProtosTestUtil.loadAndParseFlagProtos();\n        for (parsed_flag flag : flags) {\n            if (flag.permission == Aconfig.READ_ONLY && flag.state == Aconfig.DISABLED) {\n                continue;\n            }\n            String container = flag.container;\n            String packageName = flag.package_;\n            if (!PLATFORM_CONTAINERS.contains(container)) continue;\n            assertNotNull(PlatformAconfigPackage.load(packageName));\n        }\n    }\n\n    @Test\n    public void testPlatformAconfigPackage_load() throws IOException {\n        List<parsed_flag> flags = DeviceProtosTestUtil.loadAndParseFlagProtos();\n        Map<String, PlatformAconfigPackage> readerMap = new HashMap<>();\n        StorageFileProvider fp = StorageFileProvider.getDefaultProvider();\n\n        for (parsed_flag flag : flags) {\n            if (flag.permission == Aconfig.READ_ONLY && flag.state == Aconfig.DISABLED) {\n                continue;\n            }\n            String container = flag.container;\n            String packageName = flag.package_;\n            String flagName = flag.name;\n            if (!PLATFORM_CONTAINERS.contains(container)) continue;\n\n            PackageTable pTable = fp.getPackageTable(container);\n            PackageTable.Node pNode = pTable.get(packageName);\n            FlagTable fTable = fp.getFlagTable(container);\n            FlagTable.Node fNode = fTable.get(pNode.getPackageId(), flagName);\n            FlagValueList fList = fp.getFlagValueList(container);\n\n            int index = pNode.getBooleanStartIndex() + fNode.getFlagIndex();\n            boolean rVal = fList.getBoolean(index);\n\n            long fingerprint = pNode.getPackageFingerprint();\n\n            PlatformAconfigPackage reader = readerMap.get(packageName);\n            if (reader == null) {\n                reader = PlatformAconfigPackage.load(packageName);\n                readerMap.put(packageName, reader);\n            }\n            boolean jVal = reader.getBooleanFlagValue(flagName, !rVal);\n\n            assertEquals(rVal, jVal);\n        }\n    }\n\n    @Test\n    public void testPlatformAconfigPackage_load_withError() throws IOException {\n        // package not found\n        assertNull(PlatformAconfigPackage.load(\"fake_container\"));\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <string>\n#include <vector>\n#include <memory>\n#include <cstdio>\n\n#include <sys/stat.h>\n#include \"aconfig_storage/aconfig_storage_read_api.hpp\"\n#include <gtest/gtest.h>\n#include <android-base/file.h>\n#include <android-base/result.h>\n\nusing namespace android::base;\n\nnamespace api = aconfig_storage;\nnamespace private_api = aconfig_storage::private_internal_api;\n\nclass AconfigStorageTest : public ::testing::Test {\n protected:\n  Result<void> copy_file(std::string const& src_file,\n                         std::string const& dst_file) {\n    auto content = std::string();\n    if (!ReadFileToString(src_file, &content)) {\n      return Error() << \"failed to read file: \" << src_file;\n    }\n    if (!WriteStringToFile(content, dst_file)) {\n      return Error() << \"failed to copy file: \" << dst_file;\n    }\n    return {};\n  }\n\n  void SetUp() override {\n    auto const test_base_dir = android::base::GetExecutableDirectory();\n    auto const test_dir = test_base_dir + \"/data/v1\";\n    storage_dir = std::string(root_dir.path);\n    auto maps_dir = storage_dir + \"/maps\";\n    auto boot_dir = storage_dir + \"/boot\";\n    mkdir(maps_dir.c_str(), 0775);\n    mkdir(boot_dir.c_str(), 0775);\n    package_map = std::string(maps_dir) + \"/mockup.package.map\";\n    flag_map = std::string(maps_dir) + \"/mockup.flag.map\";\n    flag_val = std::string(boot_dir) + \"/mockup.val\";\n    flag_info = std::string(boot_dir) + \"/mockup.info\";\n    copy_file(test_dir + \"/package_v1.map\", package_map);\n    copy_file(test_dir + \"/flag_v1.map\", flag_map);\n    copy_file(test_dir + \"/flag_v1.val\", flag_val);\n    copy_file(test_dir + \"/flag_v1.info\", flag_info);\n  }\n\n  void TearDown() override {\n    std::remove(package_map.c_str());\n    std::remove(flag_map.c_str());\n    std::remove(flag_val.c_str());\n    std::remove(flag_info.c_str());\n  }\n\n  TemporaryDir root_dir;\n  std::string storage_dir;\n  std::string package_map;\n  std::string flag_map;\n  std::string flag_val;\n  std::string flag_info;\n};\n\n/// Test to lock down storage file version query api\nTEST_F(AconfigStorageTest, test_storage_version_query) {\n  auto version = api::get_storage_file_version(package_map);\n  ASSERT_TRUE(version.ok());\n  ASSERT_EQ(*version, 1);\n  version = api::get_storage_file_version(flag_map);\n  ASSERT_TRUE(version.ok());\n  ASSERT_EQ(*version, 1);\n  version = api::get_storage_file_version(flag_val);\n  ASSERT_TRUE(version.ok());\n  ASSERT_EQ(*version, 1);\n  version = api::get_storage_file_version(flag_info);\n  ASSERT_TRUE(version.ok());\n  ASSERT_EQ(*version, 1);\n}\n\n/// Negative test to lock down the error when mapping none exist storage files\nTEST_F(AconfigStorageTest, test_none_exist_storage_file_mapping) {\n  auto mapped_file_result = private_api::get_mapped_file_impl(\n      storage_dir, \"vendor\", api::StorageFileType::package_map);\n  ASSERT_FALSE(mapped_file_result.ok());\n  ASSERT_EQ(mapped_file_result.error(),\n            std::string(\"failed to open \") + storage_dir\n            + \"/maps/vendor.package.map: No such file or directory\");\n}\n\n/// Test to lock down storage package context query api\nTEST_F(AconfigStorageTest, test_package_context_query) {\n  auto mapped_file_result = private_api::get_mapped_file_impl(\n      storage_dir, \"mockup\", api::StorageFileType::package_map);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);\n\n  auto context = api::get_package_read_context(\n      *mapped_file, \"com.android.aconfig.storage.test_1\");\n  ASSERT_TRUE(context.ok());\n  ASSERT_TRUE(context->package_exists);\n  ASSERT_EQ(context->package_id, 0);\n  ASSERT_EQ(context->boolean_start_index, 0);\n\n  context = api::get_package_read_context(\n      *mapped_file, \"com.android.aconfig.storage.test_2\");\n  ASSERT_TRUE(context.ok());\n  ASSERT_TRUE(context->package_exists);\n  ASSERT_EQ(context->package_id, 1);\n  ASSERT_EQ(context->boolean_start_index, 3);\n\n  context = api::get_package_read_context(\n      *mapped_file, \"com.android.aconfig.storage.test_4\");\n  ASSERT_TRUE(context.ok());\n  ASSERT_TRUE(context->package_exists);\n  ASSERT_EQ(context->package_id, 2);\n  ASSERT_EQ(context->boolean_start_index, 6);\n}\n\n/// Test to lock down when querying none exist package\nTEST_F(AconfigStorageTest, test_none_existent_package_context_query) {\n  auto mapped_file_result = private_api::get_mapped_file_impl(\n      storage_dir, \"mockup\", api::StorageFileType::package_map);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);\n\n  auto context = api::get_package_read_context(\n      *mapped_file, \"com.android.aconfig.storage.test_3\");\n  ASSERT_TRUE(context.ok());\n  ASSERT_FALSE(context->package_exists);\n}\n\n/// Test to lock down storage flag context query api\nTEST_F(AconfigStorageTest, test_flag_context_query) {\n  auto mapped_file_result = private_api::get_mapped_file_impl(\n      storage_dir, \"mockup\", api::StorageFileType::flag_map);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);\n\n  auto baseline = std::vector<std::tuple<int, std::string, api::StoredFlagType, int>>{\n    {0, \"enabled_ro\", api::StoredFlagType::ReadOnlyBoolean, 1},\n    {0, \"enabled_rw\", api::StoredFlagType::ReadWriteBoolean, 2},\n    {2, \"enabled_rw\", api::StoredFlagType::ReadWriteBoolean, 1},\n    {1, \"disabled_rw\", api::StoredFlagType::ReadWriteBoolean, 0},\n    {1, \"enabled_fixed_ro\", api::StoredFlagType::FixedReadOnlyBoolean, 1},\n    {1, \"enabled_ro\", api::StoredFlagType::ReadOnlyBoolean, 2},\n    {2, \"enabled_fixed_ro\", api::StoredFlagType::FixedReadOnlyBoolean, 0},\n    {0, \"disabled_rw\", api::StoredFlagType::ReadWriteBoolean, 0},\n  };\n  for (auto const&[package_id, flag_name, flag_type, flag_index] : baseline) {\n    auto context = api::get_flag_read_context(*mapped_file, package_id, flag_name);\n    ASSERT_TRUE(context.ok());\n    ASSERT_TRUE(context->flag_exists);\n    ASSERT_EQ(context->flag_type, flag_type);\n    ASSERT_EQ(context->flag_index, flag_index);\n  }\n}\n\n/// Test to lock down when querying none exist flag\nTEST_F(AconfigStorageTest, test_none_existent_flag_context_query) {\n  auto mapped_file_result = private_api::get_mapped_file_impl(\n      storage_dir, \"mockup\", api::StorageFileType::flag_map);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);\n\n  auto context = api::get_flag_read_context(*mapped_file, 0, \"none_exist\");\n  ASSERT_TRUE(context.ok());\n  ASSERT_FALSE(context->flag_exists);\n\n  context = api::get_flag_read_context(*mapped_file, 3, \"enabled_ro\");\n  ASSERT_TRUE(context.ok());\n  ASSERT_FALSE(context->flag_exists);\n}\n\n/// Test to lock down storage flag value query api\nTEST_F(AconfigStorageTest, test_boolean_flag_value_query) {\n  auto mapped_file_result = private_api::get_mapped_file_impl(\n      storage_dir, \"mockup\", api::StorageFileType::flag_val);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);\n\n  auto expected_value = std::vector<bool>{\n    false, true, true, false, true, true, true, true};\n  for (int index = 0; index < 8; ++index) {\n    auto value = api::get_boolean_flag_value(*mapped_file, index);\n    ASSERT_TRUE(value.ok());\n    ASSERT_EQ(*value, expected_value[index]);\n  }\n}\n\n/// Negative test to lock down the error when querying flag value out of range\nTEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_query) {\n  auto mapped_file_result = private_api::get_mapped_file_impl(\n      storage_dir, \"mockup\", api::StorageFileType::flag_val);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);\n\n  auto value = api::get_boolean_flag_value(*mapped_file, 8);\n  ASSERT_FALSE(value.ok());\n  ASSERT_EQ(value.error(),\n            std::string(\"InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)\"));\n}\n\n/// Test to lock down storage flag info query api\nTEST_F(AconfigStorageTest, test_boolean_flag_info_query) {\n  auto mapped_file_result = private_api::get_mapped_file_impl(\n      storage_dir, \"mockup\", api::StorageFileType::flag_info);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);\n\n  auto expected_value = std::vector<bool>{\n    true, false, true, true, false, false, false, true};\n  for (int index = 0; index < 8; ++index) {\n    auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, index);\n    ASSERT_TRUE(attribute.ok());\n    ASSERT_EQ(*attribute & static_cast<uint8_t>(api::FlagInfoBit::HasServerOverride), 0);\n    ASSERT_EQ((*attribute & static_cast<uint8_t>(api::FlagInfoBit::IsReadWrite)) != 0,\n              expected_value[index]);\n    ASSERT_EQ(*attribute & static_cast<uint8_t>(api::FlagInfoBit::HasLocalOverride), 0);\n  }\n}\n\n/// Negative test to lock down the error when querying flag info out of range\nTEST_F(AconfigStorageTest, test_invalid_boolean_flag_info_query) {\n  auto mapped_file_result = private_api::get_mapped_file_impl(\n      storage_dir, \"mockup\", api::StorageFileType::flag_info);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MappedStorageFile>(*mapped_file_result);\n\n  auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, 8);\n  ASSERT_FALSE(attribute.ok());\n  ASSERT_EQ(attribute.error(),\n            std::string(\"InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)\"));\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs",
    "content": "#[cfg(not(feature = \"cargo\"))]\nmod aconfig_storage_rust_test {\n    use aconfig_storage_file::{FlagInfoBit, FlagValueType, StorageFileType, StoredFlagType};\n    use aconfig_storage_read_api::{\n        get_boolean_flag_value, get_flag_attribute, get_flag_read_context,\n        get_package_read_context, get_storage_file_version, mapped_file::get_mapped_file,\n        PackageReadContext,\n    };\n    use rand::Rng;\n    use std::fs;\n\n    fn create_test_storage_files(version: u32) -> String {\n        let mut rng = rand::thread_rng();\n        let number: u32 = rng.gen();\n        let storage_dir = String::from(\"/tmp/\") + &number.to_string();\n        if std::fs::metadata(&storage_dir).is_ok() {\n            fs::remove_dir_all(&storage_dir).unwrap();\n        }\n        let maps_dir = storage_dir.clone() + \"/maps\";\n        let boot_dir = storage_dir.clone() + \"/boot\";\n        fs::create_dir(&storage_dir).unwrap();\n        fs::create_dir(maps_dir).unwrap();\n        fs::create_dir(boot_dir).unwrap();\n\n        let package_map = storage_dir.clone() + \"/maps/mockup.package.map\";\n        let flag_map = storage_dir.clone() + \"/maps/mockup.flag.map\";\n        let flag_val = storage_dir.clone() + \"/boot/mockup.val\";\n        let flag_info = storage_dir.clone() + \"/boot/mockup.info\";\n        fs::copy(format!(\"./data/v{0}/package_v{0}.map\", version), package_map).unwrap();\n        fs::copy(format!(\"./data/v{0}/flag_v{0}.map\", version), flag_map).unwrap();\n        fs::copy(format!(\"./data/v{}/flag_v{0}.val\", version), flag_val).unwrap();\n        fs::copy(format!(\"./data/v{}/flag_v{0}.info\", version), flag_info).unwrap();\n\n        storage_dir\n    }\n\n    #[test]\n    fn test_unavailable_storage() {\n        let storage_dir = create_test_storage_files(1);\n        // SAFETY:\n        // The safety here is ensured as the test process will not write to temp storage file\n        let err = unsafe {\n            get_mapped_file(&storage_dir, \"vendor\", StorageFileType::PackageMap).unwrap_err()\n        };\n        assert_eq!(\n            format!(\"{:?}\", err),\n            format!(\n                \"StorageFileNotFound(storage file {}/maps/vendor.package.map does not exist)\",\n                storage_dir\n            )\n        );\n    }\n\n    #[test]\n    fn test_package_context_query() {\n        let storage_dir = create_test_storage_files(1);\n        // SAFETY:\n        // The safety here is ensured as the test process will not write to temp storage file\n        let package_mapped_file = unsafe {\n            get_mapped_file(&storage_dir, \"mockup\", StorageFileType::PackageMap).unwrap()\n        };\n\n        let package_context =\n            get_package_read_context(&package_mapped_file, \"com.android.aconfig.storage.test_1\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context =\n            PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 0 };\n        assert_eq!(package_context, expected_package_context);\n\n        let package_context =\n            get_package_read_context(&package_mapped_file, \"com.android.aconfig.storage.test_2\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context =\n            PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 0 };\n        assert_eq!(package_context, expected_package_context);\n\n        let package_context =\n            get_package_read_context(&package_mapped_file, \"com.android.aconfig.storage.test_4\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context =\n            PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 0 };\n        assert_eq!(package_context, expected_package_context);\n    }\n\n    #[test]\n    fn test_package_context_query_with_fingerprint() {\n        let storage_dir = create_test_storage_files(2);\n        // SAFETY:\n        // The safety here is ensured as the test process will not write to temp storage file\n        let package_mapped_file = unsafe {\n            get_mapped_file(&storage_dir, \"mockup\", StorageFileType::PackageMap).unwrap()\n        };\n\n        let package_context =\n            get_package_read_context(&package_mapped_file, \"com.android.aconfig.storage.test_1\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context = PackageReadContext {\n            package_id: 0,\n            boolean_start_index: 0,\n            fingerprint: 15248948510590158086u64,\n        };\n        assert_eq!(package_context, expected_package_context);\n\n        let package_context =\n            get_package_read_context(&package_mapped_file, \"com.android.aconfig.storage.test_2\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context = PackageReadContext {\n            package_id: 1,\n            boolean_start_index: 3,\n            fingerprint: 4431940502274857964u64,\n        };\n        assert_eq!(package_context, expected_package_context);\n\n        let package_context =\n            get_package_read_context(&package_mapped_file, \"com.android.aconfig.storage.test_4\")\n                .unwrap()\n                .unwrap();\n        let expected_package_context = PackageReadContext {\n            package_id: 2,\n            boolean_start_index: 6,\n            fingerprint: 16233229917711622375u64,\n        };\n        assert_eq!(package_context, expected_package_context);\n    }\n\n    #[test]\n    fn test_none_exist_package_context_query() {\n        let storage_dir = create_test_storage_files(1);\n        // SAFETY:\n        // The safety here is ensured as the test process will not write to temp storage file\n        let package_mapped_file = unsafe {\n            get_mapped_file(&storage_dir, \"mockup\", StorageFileType::PackageMap).unwrap()\n        };\n\n        let package_context_option =\n            get_package_read_context(&package_mapped_file, \"com.android.aconfig.storage.test_3\")\n                .unwrap();\n        assert_eq!(package_context_option, None);\n    }\n\n    #[test]\n    fn test_flag_context_query() {\n        let storage_dir = create_test_storage_files(1);\n        // SAFETY:\n        // The safety here is ensured as the test process will not write to temp storage file\n        let flag_mapped_file =\n            unsafe { get_mapped_file(&storage_dir, \"mockup\", StorageFileType::FlagMap).unwrap() };\n\n        let baseline = vec![\n            (0, \"enabled_ro\", StoredFlagType::ReadOnlyBoolean, 1u16),\n            (0, \"enabled_rw\", StoredFlagType::ReadWriteBoolean, 2u16),\n            (2, \"enabled_rw\", StoredFlagType::ReadWriteBoolean, 1u16),\n            (1, \"disabled_rw\", StoredFlagType::ReadWriteBoolean, 0u16),\n            (1, \"enabled_fixed_ro\", StoredFlagType::FixedReadOnlyBoolean, 1u16),\n            (1, \"enabled_ro\", StoredFlagType::ReadOnlyBoolean, 2u16),\n            (2, \"enabled_fixed_ro\", StoredFlagType::FixedReadOnlyBoolean, 0u16),\n            (0, \"disabled_rw\", StoredFlagType::ReadWriteBoolean, 0u16),\n        ];\n        for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() {\n            let flag_context =\n                get_flag_read_context(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();\n            assert_eq!(flag_context.flag_type, flag_type);\n            assert_eq!(flag_context.flag_index, flag_index);\n        }\n    }\n\n    #[test]\n    fn test_none_exist_flag_context_query() {\n        let storage_dir = create_test_storage_files(1);\n        // SAFETY:\n        // The safety here is ensured as the test process will not write to temp storage file\n        let flag_mapped_file =\n            unsafe { get_mapped_file(&storage_dir, \"mockup\", StorageFileType::FlagMap).unwrap() };\n        let flag_context_option =\n            get_flag_read_context(&flag_mapped_file, 0, \"none_exist\").unwrap();\n        assert_eq!(flag_context_option, None);\n\n        let flag_context_option =\n            get_flag_read_context(&flag_mapped_file, 3, \"enabled_ro\").unwrap();\n        assert_eq!(flag_context_option, None);\n    }\n\n    #[test]\n    fn test_boolean_flag_value_query() {\n        let storage_dir = create_test_storage_files(1);\n        // SAFETY:\n        // The safety here is ensured as the test process will not write to temp storage file\n        let flag_value_file =\n            unsafe { get_mapped_file(&storage_dir, \"mockup\", StorageFileType::FlagVal).unwrap() };\n        let baseline: Vec<bool> = vec![false, true, true, false, true, true, true, true];\n        for (offset, expected_value) in baseline.into_iter().enumerate() {\n            let flag_value = get_boolean_flag_value(&flag_value_file, offset as u32).unwrap();\n            assert_eq!(flag_value, expected_value);\n        }\n    }\n\n    #[test]\n    fn test_invalid_boolean_flag_value_query() {\n        let storage_dir = create_test_storage_files(1);\n        // SAFETY:\n        // The safety here is ensured as the test process will not write to temp storage file\n        let flag_value_file =\n            unsafe { get_mapped_file(&storage_dir, \"mockup\", StorageFileType::FlagVal).unwrap() };\n        let err = get_boolean_flag_value(&flag_value_file, 8u32).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", err),\n            \"InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)\"\n        );\n    }\n\n    #[test]\n    fn test_flag_info_query() {\n        let storage_dir = create_test_storage_files(1);\n        // SAFETY:\n        // The safety here is ensured as the test process will not write to temp storage file\n        let flag_info_file =\n            unsafe { get_mapped_file(&storage_dir, \"mockup\", StorageFileType::FlagInfo).unwrap() };\n        let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true];\n        for (offset, expected_value) in is_rw.into_iter().enumerate() {\n            let attribute =\n                get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();\n            assert!((attribute & FlagInfoBit::HasServerOverride as u8) == 0u8);\n            assert_eq!((attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, expected_value);\n            assert!((attribute & FlagInfoBit::HasLocalOverride as u8) == 0u8);\n        }\n    }\n\n    #[test]\n    fn test_invalid_boolean_flag_info_query() {\n        let storage_dir = create_test_storage_files(1);\n        // SAFETY:\n        // The safety here is ensured as the test process will not write to temp storage file\n        let flag_info_file =\n            unsafe { get_mapped_file(&storage_dir, \"mockup\", StorageFileType::FlagInfo).unwrap() };\n        let err = get_flag_attribute(&flag_info_file, FlagValueType::Boolean, 8u32).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", err),\n            \"InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)\"\n        );\n    }\n\n    #[test]\n    fn test_storage_version_query_v1() {\n        assert_eq!(get_storage_file_version(\"./data/v1/package_v1.map\").unwrap(), 1);\n        assert_eq!(get_storage_file_version(\"./data/v1/flag_v1.map\").unwrap(), 1);\n        assert_eq!(get_storage_file_version(\"./data/v1/flag_v1.val\").unwrap(), 1);\n        assert_eq!(get_storage_file_version(\"./data/v1/flag_v1.info\").unwrap(), 1);\n    }\n\n    #[test]\n    fn test_storage_version_query_v2() {\n        assert_eq!(get_storage_file_version(\"./data/v2/package_v2.map\").unwrap(), 2);\n        assert_eq!(get_storage_file_version(\"./data/v2/flag_v2.map\").unwrap(), 2);\n        assert_eq!(get_storage_file_version(\"./data/v2/flag_v2.val\").unwrap(), 2);\n        assert_eq!(get_storage_file_version(\"./data/v2/flag_v2.info\").unwrap(), 2);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_defaults {\n    name: \"aconfig_storage_write_api.defaults\",\n    edition: \"2021\",\n    lints: \"none\",\n    srcs: [\"src/lib.rs\"],\n    rustlibs: [\n        \"libanyhow\",\n        \"libtempfile\",\n        \"libmemmap2\",\n        \"libcxx\",\n        \"libthiserror\",\n        \"libaconfig_storage_file\",\n        \"libaconfig_storage_read_api\",\n    ],\n    min_sdk_version: \"34\",\n    apex_available: [\n        \"//apex_available:anyapex\",\n        \"//apex_available:platform\",\n    ],\n}\n\nrust_library {\n    name: \"libaconfig_storage_write_api\",\n    crate_name: \"aconfig_storage_write_api\",\n    host_supported: true,\n    defaults: [\"aconfig_storage_write_api.defaults\"],\n}\n\nrust_test_host {\n    name: \"aconfig_storage_write_api.test\",\n    test_suites: [\"general-tests\"],\n    defaults: [\"aconfig_storage_write_api.defaults\"],\n    data: [\n        \"tests/flag.val\",\n        \"tests/flag.info\",\n    ],\n    rustlibs: [\n        \"libaconfig_storage_read_api\",\n    ],\n}\n\n// cxx source codegen from rust api\ngenrule {\n    name: \"libcxx_aconfig_storage_write_api_bridge_code\",\n    tools: [\"cxxbridge\"],\n    cmd: \"$(location cxxbridge) $(in) > $(out)\",\n    srcs: [\"src/lib.rs\"],\n    out: [\"aconfig_storage/lib.rs.cc\"],\n}\n\n// cxx header codegen from rust api\ngenrule {\n    name: \"libcxx_aconfig_storage_write_api_bridge_header\",\n    tools: [\"cxxbridge\"],\n    cmd: \"$(location cxxbridge) $(in) --header > $(out)\",\n    srcs: [\"src/lib.rs\"],\n    out: [\"aconfig_storage/lib.rs.h\"],\n}\n\n// a static cc lib based on generated code\nrust_ffi_static {\n    name: \"libaconfig_storage_write_api_cxx_bridge\",\n    crate_name: \"aconfig_storage_write_api_cxx_bridge\",\n    host_supported: true,\n    defaults: [\"aconfig_storage_write_api.defaults\"],\n}\n\n// flag write api cc interface\ncc_library_static {\n    name: \"libaconfig_storage_write_api_cc\",\n    srcs: [\"aconfig_storage_write_api.cpp\"],\n    generated_headers: [\n        \"cxx-bridge-header\",\n        \"libcxx_aconfig_storage_write_api_bridge_header\",\n    ],\n    generated_sources: [\"libcxx_aconfig_storage_write_api_bridge_code\"],\n    whole_static_libs: [\"libaconfig_storage_write_api_cxx_bridge\"],\n    export_include_dirs: [\"include\"],\n    static_libs: [\n        \"libaconfig_storage_read_api_cc\",\n        \"libprotobuf-cpp-lite\",\n        \"libbase\",\n    ],\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/Cargo.toml",
    "content": "[package]\nname = \"aconfig_storage_write_api\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[features]\ndefault = [\"cargo\"]\ncargo = []\n\n[dependencies]\nanyhow = \"1.0.69\"\ncxx = \"1.0\"\nmemmap2 = \"0.8.0\"\ntempfile = \"3.9.0\"\nthiserror = \"1.0.56\"\naconfig_storage_file = { path = \"../aconfig_storage_file\" }\naconfig_storage_read_api = { path = \"../aconfig_storage_read_api\" }\n\n[build-dependencies]\ncxx-build = \"1.0\"\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp",
    "content": "\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n\n#include \"rust/cxx.h\"\n#include \"aconfig_storage/lib.rs.h\"\n#include \"aconfig_storage/aconfig_storage_write_api.hpp\"\n\nnamespace aconfig_storage {\n\n/// Map a storage file\nandroid::base::Result<MutableMappedStorageFile *> map_mutable_storage_file(\n    std::string const &file) {\n  struct stat file_stat;\n  if (stat(file.c_str(), &file_stat) < 0) {\n    return android::base::ErrnoError() << \"stat failed\";\n  }\n\n  if ((file_stat.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {\n    return android::base::Error() << \"cannot map nonwriteable file\";\n  }\n\n  size_t file_size = file_stat.st_size;\n\n  android::base::unique_fd ufd(open(file.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC));\n  if (ufd.get() == -1) {\n    return android::base::ErrnoError() << \"failed to open \" << file;\n  };\n\n  void *const map_result =\n      mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, ufd.get(), 0);\n  if (map_result == MAP_FAILED) {\n    return android::base::ErrnoError() << \"mmap failed\";\n  }\n\n  auto mapped_file = new MutableMappedStorageFile();\n  mapped_file->file_ptr = map_result;\n  mapped_file->file_size = file_size;\n\n  return mapped_file;\n}\n\n/// Set boolean flag value\nandroid::base::Result<void> set_boolean_flag_value(\n    const MutableMappedStorageFile &file,\n    uint32_t offset,\n    bool value) {\n  auto content = rust::Slice<uint8_t>(\n      static_cast<uint8_t *>(file.file_ptr), file.file_size);\n  auto update_cxx = update_boolean_flag_value_cxx(content, offset, value);\n  if (!update_cxx.update_success) {\n    return android::base::Error() << update_cxx.error_message.c_str();\n  }\n  if (!msync(static_cast<uint8_t *>(file.file_ptr) + update_cxx.offset, 1, MS_SYNC)) {\n    return android::base::ErrnoError() << \"msync failed\";\n  }\n  return {};\n}\n\n/// Set if flag has server override\nandroid::base::Result<void> set_flag_has_server_override(\n    const MutableMappedStorageFile &file,\n    FlagValueType value_type,\n    uint32_t offset,\n    bool value) {\n  auto content = rust::Slice<uint8_t>(\n      static_cast<uint8_t *>(file.file_ptr), file.file_size);\n  auto update_cxx = update_flag_has_server_override_cxx(\n      content, static_cast<uint16_t>(value_type), offset, value);\n  if (!update_cxx.update_success) {\n    return android::base::Error() << update_cxx.error_message.c_str();\n  }\n  if (!msync(static_cast<uint8_t *>(file.file_ptr) + update_cxx.offset, 1, MS_SYNC)) {\n    return android::base::ErrnoError() << \"msync failed\";\n  }\n  return {};\n}\n\n/// Set if flag has local override\nandroid::base::Result<void> set_flag_has_local_override(\n    const MutableMappedStorageFile &file,\n    FlagValueType value_type,\n    uint32_t offset,\n    bool value) {\n  auto content = rust::Slice<uint8_t>(\n      static_cast<uint8_t *>(file.file_ptr), file.file_size);\n  auto update_cxx = update_flag_has_local_override_cxx(\n      content, static_cast<uint16_t>(value_type), offset, value);\n  if (!update_cxx.update_success) {\n    return android::base::Error() << update_cxx.error_message.c_str();\n  }\n  if (!msync(static_cast<uint8_t *>(file.file_ptr) + update_cxx.offset, 1, MS_SYNC)) {\n    return android::base::ErrnoError() << \"msync failed\";\n  }\n  return {};\n}\n\n} // namespace aconfig_storage\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/build.rs",
    "content": "fn main() {\n    let _ = cxx_build::bridge(\"src/lib.rs\");\n    println!(\"cargo:rerun-if-changed=src/lib.rs\");\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <string>\n\n#include <android-base/result.h>\n#include <aconfig_storage/aconfig_storage_read_api.hpp>\n\n\nnamespace aconfig_storage {\n\n/// Mapped flag value file\nstruct MutableMappedStorageFile : MappedStorageFile {};\n\n/// Map a storage file\nandroid::base::Result<MutableMappedStorageFile*> map_mutable_storage_file(\n    std::string const& file);\n\n/// Set boolean flag value\nandroid::base::Result<void> set_boolean_flag_value(\n    const MutableMappedStorageFile& file,\n    uint32_t offset,\n    bool value);\n\n/// Set if flag has server override\nandroid::base::Result<void> set_flag_has_server_override(\n    const MutableMappedStorageFile& file,\n    FlagValueType value_type,\n    uint32_t offset,\n    bool value);\n\n/// Set if flag has local override\nandroid::base::Result<void> set_flag_has_local_override(\n    const MutableMappedStorageFile& file,\n    FlagValueType value_type,\n    uint32_t offset,\n    bool value);\n\n} // namespace aconfig_storage\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! flag info update module defines the flag info file write to mapped bytes\n\nuse aconfig_storage_file::{\n    read_u8_from_bytes, AconfigStorageError, FlagInfoBit, FlagInfoHeader, FlagValueType,\n    MAX_SUPPORTED_FILE_VERSION,\n};\nuse anyhow::anyhow;\n\nfn get_flag_info_offset(\n    buf: &mut [u8],\n    flag_type: FlagValueType,\n    flag_index: u32,\n) -> Result<usize, AconfigStorageError> {\n    let interpreted_header = FlagInfoHeader::from_bytes(buf)?;\n    if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION {\n        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(\n            \"Cannot write to storage file with a higher version of {} with lib version {}\",\n            interpreted_header.version,\n            MAX_SUPPORTED_FILE_VERSION\n        )));\n    }\n\n    // get byte offset to the flag info\n    let head = match flag_type {\n        FlagValueType::Boolean => (interpreted_header.boolean_flag_offset + flag_index) as usize,\n    };\n\n    if head >= interpreted_header.file_size as usize {\n        return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(\n            \"Flag value offset goes beyond the end of the file.\"\n        )));\n    }\n\n    Ok(head)\n}\n\nfn get_flag_attribute_and_offset(\n    buf: &mut [u8],\n    flag_type: FlagValueType,\n    flag_index: u32,\n) -> Result<(u8, usize), AconfigStorageError> {\n    let head = get_flag_info_offset(buf, flag_type, flag_index)?;\n    let mut pos = head;\n    let attribute = read_u8_from_bytes(buf, &mut pos)?;\n    Ok((attribute, head))\n}\n\n/// Set if flag has server override\npub fn update_flag_has_server_override(\n    buf: &mut [u8],\n    flag_type: FlagValueType,\n    flag_index: u32,\n    value: bool,\n) -> Result<usize, AconfigStorageError> {\n    let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?;\n    let has_override = (attribute & (FlagInfoBit::HasServerOverride as u8)) != 0;\n    if has_override != value {\n        buf[head] = (attribute ^ FlagInfoBit::HasServerOverride as u8).to_le_bytes()[0];\n    }\n    Ok(head)\n}\n\n/// Set if flag has local override\npub fn update_flag_has_local_override(\n    buf: &mut [u8],\n    flag_type: FlagValueType,\n    flag_index: u32,\n    value: bool,\n) -> Result<usize, AconfigStorageError> {\n    let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?;\n    let has_override = (attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0;\n    if has_override != value {\n        buf[head] = (attribute ^ FlagInfoBit::HasLocalOverride as u8).to_le_bytes()[0];\n    }\n    Ok(head)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use aconfig_storage_file::{test_utils::create_test_flag_info_list, DEFAULT_FILE_VERSION};\n    use aconfig_storage_read_api::flag_info_query::find_flag_attribute;\n\n    #[test]\n    // this test point locks down has server override update\n    fn test_update_flag_has_server_override() {\n        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);\n        let mut buf = flag_info_list.into_bytes();\n        for i in 0..flag_info_list.header.num_flags {\n            update_flag_has_server_override(&mut buf, FlagValueType::Boolean, i, true).unwrap();\n            let attribute = find_flag_attribute(&buf, FlagValueType::Boolean, i).unwrap();\n            assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) != 0);\n            update_flag_has_server_override(&mut buf, FlagValueType::Boolean, i, false).unwrap();\n            let attribute = find_flag_attribute(&buf, FlagValueType::Boolean, i).unwrap();\n            assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) == 0);\n        }\n    }\n\n    #[test]\n    // this test point locks down has local override update\n    fn test_update_flag_has_local_override() {\n        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);\n        let mut buf = flag_info_list.into_bytes();\n        for i in 0..flag_info_list.header.num_flags {\n            update_flag_has_local_override(&mut buf, FlagValueType::Boolean, i, true).unwrap();\n            let attribute = find_flag_attribute(&buf, FlagValueType::Boolean, i).unwrap();\n            assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0);\n            update_flag_has_local_override(&mut buf, FlagValueType::Boolean, i, false).unwrap();\n            let attribute = find_flag_attribute(&buf, FlagValueType::Boolean, i).unwrap();\n            assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) == 0);\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! flag value update module defines the flag value file write to mapped bytes\n\nuse aconfig_storage_file::{AconfigStorageError, FlagValueHeader, MAX_SUPPORTED_FILE_VERSION};\nuse anyhow::anyhow;\n\n/// Set flag value\npub fn update_boolean_flag_value(\n    buf: &mut [u8],\n    flag_index: u32,\n    flag_value: bool,\n) -> Result<usize, AconfigStorageError> {\n    let interpreted_header = FlagValueHeader::from_bytes(buf)?;\n    if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION {\n        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(\n            \"Cannot write to storage file with a higher version of {} with lib version {}\",\n            interpreted_header.version,\n            MAX_SUPPORTED_FILE_VERSION\n        )));\n    }\n\n    // get byte offset to the flag\n    let head = (interpreted_header.boolean_value_offset + flag_index) as usize;\n    if head >= interpreted_header.file_size as usize {\n        return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(\n            \"Flag value offset goes beyond the end of the file.\"\n        )));\n    }\n\n    buf[head] = u8::from(flag_value).to_le_bytes()[0];\n    Ok(head)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use aconfig_storage_file::{test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION};\n\n    #[test]\n    // this test point locks down flag value update\n    fn test_boolean_flag_value_update() {\n        let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);\n        let value_offset = flag_value_list.header.boolean_value_offset;\n        let mut content = flag_value_list.into_bytes();\n        let true_byte = u8::from(true).to_le_bytes()[0];\n        let false_byte = u8::from(false).to_le_bytes()[0];\n\n        for i in 0..flag_value_list.header.num_flags {\n            let offset = (value_offset + i) as usize;\n            update_boolean_flag_value(&mut content, i, true).unwrap();\n            assert_eq!(content[offset], true_byte);\n            update_boolean_flag_value(&mut content, i, false).unwrap();\n            assert_eq!(content[offset], false_byte);\n        }\n    }\n\n    #[test]\n    // this test point locks down update beyond the end of boolean section\n    fn test_boolean_out_of_range() {\n        let mut flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes();\n        let error = update_boolean_flag_value(&mut flag_value_list[..], 8, true).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            \"InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)\"\n        );\n    }\n\n    #[test]\n    // this test point locks down query error when file has a higher version\n    fn test_higher_version_storage_file() {\n        let mut value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);\n        value_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1;\n        let mut flag_value = value_list.into_bytes();\n        let error = update_boolean_flag_value(&mut flag_value[..], 4, true).unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", error),\n            format!(\n                \"HigherStorageFileVersion(Cannot write to storage file with a higher version of {} with lib version {})\",\n                MAX_SUPPORTED_FILE_VERSION + 1,\n                MAX_SUPPORTED_FILE_VERSION\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/src/lib.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! `aconfig_storage_write_api` is a crate that defines write apis to update flag value\n//! in storage file. It provides one api to interface with storage files.\n\npub mod flag_info_update;\npub mod flag_value_update;\npub mod mapped_file;\n\n#[cfg(test)]\nmod test_utils;\n\nuse aconfig_storage_file::{AconfigStorageError, FlagValueType};\n\nuse anyhow::anyhow;\nuse memmap2::MmapMut;\n\n/// Get read write mapped storage files.\n///\n/// \\input file_path: path to the storage file\n///\n/// # Safety\n///\n/// The memory mapped file may have undefined behavior if there are writes to this\n/// file not thru this memory mapped file or there are concurrent writes to this\n/// memory mapped file. Ensure all writes to the underlying file are thru this memory\n/// mapped file and there are no concurrent writes.\npub unsafe fn map_mutable_storage_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {\n    crate::mapped_file::map_file(file_path)\n}\n\n/// Set boolean flag value thru mapped file and flush the change to file\n///\n/// \\input mapped_file: the mapped flag value file\n/// \\input index: flag index\n/// \\input value: updated flag value\n/// \\return a result of ()\n///\npub fn set_boolean_flag_value(\n    file: &mut MmapMut,\n    index: u32,\n    value: bool,\n) -> Result<(), AconfigStorageError> {\n    crate::flag_value_update::update_boolean_flag_value(file, index, value)?;\n    file.flush().map_err(|errmsg| {\n        AconfigStorageError::MapFlushFail(anyhow!(\"fail to flush storage file: {}\", errmsg))\n    })\n}\n\n/// Set if flag is has server override thru mapped file and flush the change to file\n///\n/// \\input mapped_file: the mapped flag info file\n/// \\input index: flag index\n/// \\input value: updated flag has server override value\n/// \\return a result of ()\n///\npub fn set_flag_has_server_override(\n    file: &mut MmapMut,\n    flag_type: FlagValueType,\n    index: u32,\n    value: bool,\n) -> Result<(), AconfigStorageError> {\n    crate::flag_info_update::update_flag_has_server_override(file, flag_type, index, value)?;\n    file.flush().map_err(|errmsg| {\n        AconfigStorageError::MapFlushFail(anyhow!(\"fail to flush storage file: {}\", errmsg))\n    })\n}\n\n/// Set if flag has local override thru mapped file and flush the change to file\n///\n/// \\input mapped_file: the mapped flag info file\n/// \\input index: flag index\n/// \\input value: updated flag has local override value\n/// \\return a result of ()\n///\npub fn set_flag_has_local_override(\n    file: &mut MmapMut,\n    flag_type: FlagValueType,\n    index: u32,\n    value: bool,\n) -> Result<(), AconfigStorageError> {\n    crate::flag_info_update::update_flag_has_local_override(file, flag_type, index, value)?;\n    file.flush().map_err(|errmsg| {\n        AconfigStorageError::MapFlushFail(anyhow!(\"fail to flush storage file: {}\", errmsg))\n    })\n}\n\n// *************************************** //\n// CC INTERLOP\n// *************************************** //\n\n// Exported rust data structure and methods, c++ code will be generated\n#[cxx::bridge]\nmod ffi {\n    // Flag value update return for cc interlop\n    pub struct BooleanFlagValueUpdateCXX {\n        pub update_success: bool,\n        pub offset: usize,\n        pub error_message: String,\n    }\n\n    // Flag has server override update return for cc interlop\n    pub struct FlagHasServerOverrideUpdateCXX {\n        pub update_success: bool,\n        pub offset: usize,\n        pub error_message: String,\n    }\n\n    // Flag has local override update return for cc interlop\n    pub struct FlagHasLocalOverrideUpdateCXX {\n        pub update_success: bool,\n        pub offset: usize,\n        pub error_message: String,\n    }\n\n    // Rust export to c++\n    extern \"Rust\" {\n        pub fn update_boolean_flag_value_cxx(\n            file: &mut [u8],\n            offset: u32,\n            value: bool,\n        ) -> BooleanFlagValueUpdateCXX;\n\n        pub fn update_flag_has_server_override_cxx(\n            file: &mut [u8],\n            flag_type: u16,\n            offset: u32,\n            value: bool,\n        ) -> FlagHasServerOverrideUpdateCXX;\n\n        pub fn update_flag_has_local_override_cxx(\n            file: &mut [u8],\n            flag_type: u16,\n            offset: u32,\n            value: bool,\n        ) -> FlagHasLocalOverrideUpdateCXX;\n    }\n}\n\npub(crate) fn update_boolean_flag_value_cxx(\n    file: &mut [u8],\n    offset: u32,\n    value: bool,\n) -> ffi::BooleanFlagValueUpdateCXX {\n    match crate::flag_value_update::update_boolean_flag_value(file, offset, value) {\n        Ok(head) => ffi::BooleanFlagValueUpdateCXX {\n            update_success: true,\n            offset: head,\n            error_message: String::from(\"\"),\n        },\n        Err(errmsg) => ffi::BooleanFlagValueUpdateCXX {\n            update_success: false,\n            offset: usize::MAX,\n            error_message: format!(\"{:?}\", errmsg),\n        },\n    }\n}\n\npub(crate) fn update_flag_has_server_override_cxx(\n    file: &mut [u8],\n    flag_type: u16,\n    offset: u32,\n    value: bool,\n) -> ffi::FlagHasServerOverrideUpdateCXX {\n    match FlagValueType::try_from(flag_type) {\n        Ok(value_type) => {\n            match crate::flag_info_update::update_flag_has_server_override(\n                file, value_type, offset, value,\n            ) {\n                Ok(head) => ffi::FlagHasServerOverrideUpdateCXX {\n                    update_success: true,\n                    offset: head,\n                    error_message: String::from(\"\"),\n                },\n                Err(errmsg) => ffi::FlagHasServerOverrideUpdateCXX {\n                    update_success: false,\n                    offset: usize::MAX,\n                    error_message: format!(\"{:?}\", errmsg),\n                },\n            }\n        }\n        Err(errmsg) => ffi::FlagHasServerOverrideUpdateCXX {\n            update_success: false,\n            offset: usize::MAX,\n            error_message: format!(\"{:?}\", errmsg),\n        },\n    }\n}\n\npub(crate) fn update_flag_has_local_override_cxx(\n    file: &mut [u8],\n    flag_type: u16,\n    offset: u32,\n    value: bool,\n) -> ffi::FlagHasLocalOverrideUpdateCXX {\n    match FlagValueType::try_from(flag_type) {\n        Ok(value_type) => {\n            match crate::flag_info_update::update_flag_has_local_override(\n                file, value_type, offset, value,\n            ) {\n                Ok(head) => ffi::FlagHasLocalOverrideUpdateCXX {\n                    update_success: true,\n                    offset: head,\n                    error_message: String::from(\"\"),\n                },\n                Err(errmsg) => ffi::FlagHasLocalOverrideUpdateCXX {\n                    update_success: false,\n                    offset: usize::MAX,\n                    error_message: format!(\"{:?}\", errmsg),\n                },\n            }\n        }\n        Err(errmsg) => ffi::FlagHasLocalOverrideUpdateCXX {\n            update_success: false,\n            offset: usize::MAX,\n            error_message: format!(\"{:?}\", errmsg),\n        },\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::test_utils::copy_to_temp_file;\n    use aconfig_storage_file::FlagInfoBit;\n    use aconfig_storage_read_api::flag_info_query::find_flag_attribute;\n    use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;\n    use std::fs::File;\n    use std::io::Read;\n\n    fn get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool {\n        let mut f = File::open(&file).unwrap();\n        let mut bytes = Vec::new();\n        f.read_to_end(&mut bytes).unwrap();\n        find_boolean_flag_value(&bytes, offset).unwrap()\n    }\n\n    #[test]\n    fn test_set_boolean_flag_value() {\n        let flag_value_file = copy_to_temp_file(\"./tests/flag.val\", false).unwrap();\n        let flag_value_path = flag_value_file.path().display().to_string();\n\n        // SAFETY:\n        // The safety here is guaranteed as only this single threaded test process will\n        // write to this file\n        unsafe {\n            let mut file = map_mutable_storage_file(&flag_value_path).unwrap();\n            for i in 0..8 {\n                set_boolean_flag_value(&mut file, i, true).unwrap();\n                let value = get_boolean_flag_value_at_offset(&flag_value_path, i);\n                assert_eq!(value, true);\n\n                set_boolean_flag_value(&mut file, i, false).unwrap();\n                let value = get_boolean_flag_value_at_offset(&flag_value_path, i);\n                assert_eq!(value, false);\n            }\n        }\n    }\n\n    fn get_flag_attribute_at_offset(file: &str, value_type: FlagValueType, offset: u32) -> u8 {\n        let mut f = File::open(&file).unwrap();\n        let mut bytes = Vec::new();\n        f.read_to_end(&mut bytes).unwrap();\n        find_flag_attribute(&bytes, value_type, offset).unwrap()\n    }\n\n    #[test]\n    fn test_set_flag_has_server_override() {\n        let flag_info_file = copy_to_temp_file(\"./tests/flag.info\", false).unwrap();\n        let flag_info_path = flag_info_file.path().display().to_string();\n\n        // SAFETY:\n        // The safety here is guaranteed as only this single threaded test process will\n        // write to this file\n        unsafe {\n            let mut file = map_mutable_storage_file(&flag_info_path).unwrap();\n            for i in 0..8 {\n                set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, true).unwrap();\n                let attribute =\n                    get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);\n                assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) != 0);\n                set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, false).unwrap();\n                let attribute =\n                    get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);\n                assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) == 0);\n            }\n        }\n    }\n\n    #[test]\n    fn test_set_flag_has_local_override() {\n        let flag_info_file = copy_to_temp_file(\"./tests/flag.info\", false).unwrap();\n        let flag_info_path = flag_info_file.path().display().to_string();\n\n        // SAFETY:\n        // The safety here is guaranteed as only this single threaded test process will\n        // write to this file\n        unsafe {\n            let mut file = map_mutable_storage_file(&flag_info_path).unwrap();\n            for i in 0..8 {\n                set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, true).unwrap();\n                let attribute =\n                    get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);\n                assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0);\n                set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, false).unwrap();\n                let attribute =\n                    get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);\n                assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) == 0);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse anyhow::anyhow;\nuse memmap2::MmapMut;\nuse std::fs::{self, OpenOptions};\n\nuse aconfig_storage_file::AconfigStorageError::{self, FileReadFail, MapFileFail};\n\n/// Get the mutable memory mapping of a storage file\n///\n/// # Safety\n///\n/// The memory mapped file may have undefined behavior if there are writes to this\n/// file not thru this memory mapped file or there are concurrent writes to this\n/// memory mapped file. Ensure all writes to the underlying file are thru this memory\n/// mapped file and there are no concurrent writes.\npub(crate) unsafe fn map_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {\n    // make sure file has read write permission\n    let perms = fs::metadata(file_path).unwrap().permissions();\n    if perms.readonly() {\n        return Err(MapFileFail(anyhow!(\"fail to map non read write storage file {}\", file_path)));\n    }\n\n    let file =\n        OpenOptions::new().read(true).write(true).open(file_path).map_err(|errmsg| {\n            FileReadFail(anyhow!(\"Failed to open file {}: {}\", file_path, errmsg))\n        })?;\n\n    unsafe {\n        let mapped_file = MmapMut::map_mut(&file).map_err(|errmsg| {\n            MapFileFail(anyhow!(\"fail to map storage file {}: {}\", file_path, errmsg))\n        })?;\n        Ok(mapped_file)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::test_utils::copy_to_temp_file;\n    use std::io::Read;\n\n    #[test]\n    fn test_mapped_file_contents() {\n        let mut rw_val_file = copy_to_temp_file(\"./tests/flag.val\", false).unwrap();\n        let mut rw_info_file = copy_to_temp_file(\"./tests/flag.info\", false).unwrap();\n        let flag_val = rw_val_file.path().display().to_string();\n        let flag_info = rw_info_file.path().display().to_string();\n\n        let mut content = Vec::new();\n        rw_val_file.read_to_end(&mut content).unwrap();\n\n        // SAFETY:\n        // The safety here is guaranteed here as no writes happens to this temp file\n        unsafe {\n            let mmaped_file = map_file(&flag_val).unwrap();\n            assert_eq!(mmaped_file[..], content[..]);\n        }\n\n        let mut content = Vec::new();\n        rw_info_file.read_to_end(&mut content).unwrap();\n\n        // SAFETY:\n        // The safety here is guaranteed here as no writes happens to this temp file\n        unsafe {\n            let mmaped_file = map_file(&flag_info).unwrap();\n            assert_eq!(mmaped_file[..], content[..]);\n        }\n    }\n\n    #[test]\n    fn test_mapped_read_only_file() {\n        let ro_val_file = copy_to_temp_file(\"./tests/flag.val\", true).unwrap();\n        let flag_val = ro_val_file.path().display().to_string();\n\n        // SAFETY:\n        // The safety here is guaranteed here as no writes happens to this temp file\n        unsafe {\n            let error = map_file(&flag_val).unwrap_err();\n            assert_eq!(\n                format!(\"{:?}\", error),\n                format!(\"MapFileFail(fail to map non read write storage file {})\", flag_val)\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/src/test_utils.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse anyhow::Result;\nuse std::fs;\nuse tempfile::NamedTempFile;\n\n/// Create temp file copy\npub(crate) fn copy_to_temp_file(source_file: &str, read_only: bool) -> Result<NamedTempFile> {\n    let file = NamedTempFile::new()?;\n    fs::copy(source_file, file.path())?;\n    if read_only {\n        let file_name = file.path().display().to_string();\n        let mut perms = fs::metadata(file_name).unwrap().permissions();\n        perms.set_readonly(true);\n        fs::set_permissions(file.path(), perms.clone()).unwrap();\n    }\n    Ok(file)\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/tests/Android.bp",
    "content": "rust_test {\n    name: \"aconfig_storage_write_api.test.rust\",\n    srcs: [\n        \"storage_write_api_test.rs\",\n    ],\n    rustlibs: [\n        \"libanyhow\",\n        \"libaconfig_storage_file\",\n        \"libaconfig_storage_read_api\",\n        \"libaconfig_storage_write_api\",\n        \"libprotobuf\",\n        \"libtempfile\",\n    ],\n    data: [\n        \"flag.val\",\n        \"flag.info\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\ncc_test {\n    name: \"aconfig_storage_write_api.test.cpp\",\n    srcs: [\n        \"storage_write_api_test.cpp\",\n    ],\n    static_libs: [\n        \"libgmock\",\n        \"libaconfig_storage_read_api_cc\",\n        \"libaconfig_storage_write_api_cc\",\n        \"libbase\",\n        \"liblog\",\n    ],\n    data: [\n        \"flag.val\",\n        \"flag.info\",\n    ],\n    test_suites: [\n        \"device-tests\",\n        \"general-tests\",\n    ],\n    generated_headers: [\n        \"cxx-bridge-header\",\n        \"libcxx_aconfig_storage_read_api_bridge_header\",\n    ],\n    generated_sources: [\"libcxx_aconfig_storage_read_api_bridge_code\"],\n    whole_static_libs: [\"libaconfig_storage_read_api_cxx_bridge\"],\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <string>\n#include <vector>\n#include <cstdio>\n\n#include <sys/stat.h>\n#include \"aconfig_storage/aconfig_storage_read_api.hpp\"\n#include \"aconfig_storage/aconfig_storage_write_api.hpp\"\n#include <gtest/gtest.h>\n#include <android-base/file.h>\n#include <android-base/result.h>\n\n#include \"rust/cxx.h\"\n#include \"aconfig_storage/lib.rs.h\"\n\nusing namespace android::base;\n\nnamespace api = aconfig_storage;\nnamespace private_api = aconfig_storage::private_internal_api;\n\nclass AconfigStorageTest : public ::testing::Test {\n protected:\n  Result<std::string> copy_to_rw_temp_file(std::string const& source_file) {\n    auto temp_file = std::string(std::tmpnam(nullptr));\n    auto content = std::string();\n    if (!ReadFileToString(source_file, &content)) {\n      return Error() << \"failed to read file: \" << source_file;\n    }\n    if (!WriteStringToFile(content, temp_file)) {\n      return Error() << \"failed to copy file: \" << source_file;\n    }\n    if (chmod(temp_file.c_str(),\n              S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH) == -1) {\n      return Error() << \"failed to chmod\";\n    }\n    return temp_file;\n  }\n\n  void SetUp() override {\n    auto const test_dir = android::base::GetExecutableDirectory();\n    flag_val = *copy_to_rw_temp_file(test_dir + \"/flag.val\");\n    flag_info = *copy_to_rw_temp_file(test_dir + \"/flag.info\");\n  }\n\n  void TearDown() override {\n    std::remove(flag_val.c_str());\n    std::remove(flag_info.c_str());\n  }\n\n  std::string flag_val;\n  std::string flag_info;\n};\n\n/// Negative test to lock down the error when mapping a non writeable storage file\nTEST_F(AconfigStorageTest, test_non_writable_storage_file_mapping) {\n  ASSERT_TRUE(chmod(flag_val.c_str(), S_IRUSR | S_IRGRP | S_IROTH) != -1);\n  auto mapped_file_result = api::map_mutable_storage_file(flag_val);\n  ASSERT_FALSE(mapped_file_result.ok());\n  auto it = mapped_file_result.error().message().find(\"cannot map nonwriteable file\");\n  ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();\n}\n\n/// Test to lock down storage flag value update api\nTEST_F(AconfigStorageTest, test_boolean_flag_value_update) {\n  auto mapped_file_result = api::map_mutable_storage_file(flag_val);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MutableMappedStorageFile>(*mapped_file_result);\n\n  for (int offset = 0; offset < 8; ++offset) {\n    auto update_result = api::set_boolean_flag_value(*mapped_file, offset, true);\n    ASSERT_TRUE(update_result.ok());\n    auto value = api::get_boolean_flag_value(*mapped_file, offset);\n    ASSERT_TRUE(value.ok());\n    ASSERT_TRUE(*value);\n  }\n\n  // load the file on disk and check has been updated\n  std::ifstream file(flag_val, std::ios::binary | std::ios::ate);\n  std::streamsize size = file.tellg();\n  file.seekg(0, std::ios::beg);\n\n  std::vector<uint8_t> buffer(size);\n  file.read(reinterpret_cast<char *>(buffer.data()), size);\n\n  auto content = rust::Slice<const uint8_t>(\n      buffer.data(), mapped_file->file_size);\n\n  for (int offset = 0; offset < 8; ++offset) {\n    auto value_cxx = get_boolean_flag_value_cxx(content, offset);\n    ASSERT_TRUE(value_cxx.query_success);\n    ASSERT_TRUE(value_cxx.flag_value);\n  }\n}\n\n/// Negative test to lock down the error when querying flag value out of range\nTEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_update) {\n  auto mapped_file_result = api::map_mutable_storage_file(flag_val);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MutableMappedStorageFile>(*mapped_file_result);\n  auto update_result = api::set_boolean_flag_value(*mapped_file, 8, true);\n  ASSERT_FALSE(update_result.ok());\n  ASSERT_EQ(update_result.error().message(),\n            std::string(\"InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)\"));\n}\n\n/// Test to lock down storage flag has server override update api\nTEST_F(AconfigStorageTest, test_flag_has_server_override_update) {\n  auto mapped_file_result = api::map_mutable_storage_file(flag_info);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MutableMappedStorageFile>(*mapped_file_result);\n\n  for (int offset = 0; offset < 8; ++offset) {\n    auto update_result = api::set_flag_has_server_override(\n        *mapped_file, api::FlagValueType::Boolean, offset, true);\n    ASSERT_TRUE(update_result.ok()) << update_result.error();\n    auto attribute = api::get_flag_attribute(\n        *mapped_file, api::FlagValueType::Boolean, offset);\n    ASSERT_TRUE(attribute.ok());\n    ASSERT_TRUE(*attribute & api::FlagInfoBit::HasServerOverride);\n  }\n\n  // load the file on disk and check has been updated\n  std::ifstream file(flag_info, std::ios::binary | std::ios::ate);\n  std::streamsize size = file.tellg();\n  file.seekg(0, std::ios::beg);\n\n  std::vector<uint8_t> buffer(size);\n  file.read(reinterpret_cast<char *>(buffer.data()), size);\n\n  auto content = rust::Slice<const uint8_t>(\n      buffer.data(), mapped_file->file_size);\n\n  for (int offset = 0; offset < 8; ++offset) {\n    auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset);\n    ASSERT_TRUE(attribute.query_success);\n    ASSERT_TRUE(attribute.flag_attribute & api::FlagInfoBit::HasServerOverride);\n  }\n\n  for (int offset = 0; offset < 8; ++offset) {\n    auto update_result = api::set_flag_has_server_override(\n        *mapped_file, api::FlagValueType::Boolean, offset, false);\n    ASSERT_TRUE(update_result.ok());\n    auto attribute = api::get_flag_attribute(\n        *mapped_file, api::FlagValueType::Boolean, offset);\n    ASSERT_TRUE(attribute.ok());\n    ASSERT_FALSE(*attribute & api::FlagInfoBit::HasServerOverride);\n  }\n\n  std::ifstream file2(flag_info, std::ios::binary);\n  buffer.clear();\n  file2.read(reinterpret_cast<char *>(buffer.data()), size);\n  for (int offset = 0; offset < 8; ++offset) {\n    auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset);\n    ASSERT_TRUE(attribute.query_success);\n    ASSERT_FALSE(attribute.flag_attribute & api::FlagInfoBit::HasServerOverride);\n  }\n}\n\n/// Test to lock down storage flag has local override update api\nTEST_F(AconfigStorageTest, test_flag_has_local_override_update) {\n  auto mapped_file_result = api::map_mutable_storage_file(flag_info);\n  ASSERT_TRUE(mapped_file_result.ok());\n  auto mapped_file = std::unique_ptr<api::MutableMappedStorageFile>(*mapped_file_result);\n\n  for (int offset = 0; offset < 8; ++offset) {\n    auto update_result = api::set_flag_has_local_override(\n        *mapped_file, api::FlagValueType::Boolean, offset, true);\n    ASSERT_TRUE(update_result.ok());\n    auto attribute = api::get_flag_attribute(\n        *mapped_file, api::FlagValueType::Boolean, offset);\n    ASSERT_TRUE(attribute.ok());\n    ASSERT_TRUE(*attribute & api::FlagInfoBit::HasLocalOverride);\n  }\n\n  // load the file on disk and check has been updated\n  std::ifstream file(flag_info, std::ios::binary | std::ios::ate);\n  std::streamsize size = file.tellg();\n  file.seekg(0, std::ios::beg);\n\n  std::vector<uint8_t> buffer(size);\n  file.read(reinterpret_cast<char *>(buffer.data()), size);\n\n  auto content = rust::Slice<const uint8_t>(\n      buffer.data(), mapped_file->file_size);\n\n  for (int offset = 0; offset < 8; ++offset) {\n    auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset);\n    ASSERT_TRUE(attribute.query_success);\n    ASSERT_TRUE(attribute.flag_attribute & api::FlagInfoBit::HasLocalOverride);\n  }\n\n  for (int offset = 0; offset < 8; ++offset) {\n    auto update_result = api::set_flag_has_local_override(\n        *mapped_file, api::FlagValueType::Boolean, offset, false);\n    ASSERT_TRUE(update_result.ok());\n    auto attribute = api::get_flag_attribute(\n        *mapped_file, api::FlagValueType::Boolean, offset);\n    ASSERT_TRUE(attribute.ok());\n    ASSERT_FALSE(*attribute & api::FlagInfoBit::HasLocalOverride);\n  }\n\n  std::ifstream file2(flag_info, std::ios::binary);\n  buffer.clear();\n  file2.read(reinterpret_cast<char *>(buffer.data()), size);\n  for (int offset = 0; offset < 8; ++offset) {\n    auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset);\n    ASSERT_TRUE(attribute.query_success);\n    ASSERT_FALSE(attribute.flag_attribute & api::FlagInfoBit::HasLocalOverride);\n  }\n}\n"
  },
  {
    "path": "tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs",
    "content": "#[cfg(not(feature = \"cargo\"))]\nmod aconfig_storage_write_api_test {\n    use aconfig_storage_file::{FlagInfoBit, FlagValueType};\n    use aconfig_storage_read_api::flag_info_query::find_flag_attribute;\n    use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;\n    use aconfig_storage_write_api::{\n        map_mutable_storage_file, set_boolean_flag_value, set_flag_has_local_override,\n        set_flag_has_server_override,\n    };\n\n    use std::fs::{self, File};\n    use std::io::Read;\n    use tempfile::NamedTempFile;\n\n    /// Create temp file copy\n    fn copy_to_temp_rw_file(source_file: &str) -> NamedTempFile {\n        let file = NamedTempFile::new().unwrap();\n        fs::copy(source_file, file.path()).unwrap();\n        file\n    }\n\n    /// Get boolean flag value from offset\n    fn get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool {\n        let mut f = File::open(file).unwrap();\n        let mut bytes = Vec::new();\n        f.read_to_end(&mut bytes).unwrap();\n        find_boolean_flag_value(&bytes, offset).unwrap()\n    }\n\n    /// Get flag attribute at offset\n    fn get_flag_attribute_at_offset(file: &str, value_type: FlagValueType, offset: u32) -> u8 {\n        let mut f = File::open(file).unwrap();\n        let mut bytes = Vec::new();\n        f.read_to_end(&mut bytes).unwrap();\n        find_flag_attribute(&bytes, value_type, offset).unwrap()\n    }\n\n    #[test]\n    /// Test to lock down flag value update api\n    fn test_boolean_flag_value_update() {\n        let flag_value_file = copy_to_temp_rw_file(\"./flag.val\");\n        let flag_value_path = flag_value_file.path().display().to_string();\n\n        // SAFETY:\n        // The safety here is ensured as only this single threaded test process will\n        // write to this file\n        let mut file = unsafe { map_mutable_storage_file(&flag_value_path).unwrap() };\n        for i in 0..8 {\n            set_boolean_flag_value(&mut file, i, true).unwrap();\n            let value = get_boolean_flag_value_at_offset(&flag_value_path, i);\n            assert!(value);\n\n            set_boolean_flag_value(&mut file, i, false).unwrap();\n            let value = get_boolean_flag_value_at_offset(&flag_value_path, i);\n            assert!(!value);\n        }\n    }\n\n    #[test]\n    /// Test to lock down flag has server override update api\n    fn test_set_flag_has_server_override() {\n        let flag_info_file = copy_to_temp_rw_file(\"./flag.info\");\n        let flag_info_path = flag_info_file.path().display().to_string();\n\n        // SAFETY:\n        // The safety here is ensured as only this single threaded test process will\n        // write to this file\n        let mut file = unsafe { map_mutable_storage_file(&flag_info_path).unwrap() };\n        for i in 0..8 {\n            set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, true).unwrap();\n            let attribute =\n                get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);\n            assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) != 0);\n            set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, false).unwrap();\n            let attribute =\n                get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);\n            assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) == 0);\n        }\n    }\n\n    #[test]\n    /// Test to lock down flag has local override update api\n    fn test_set_flag_has_local_override() {\n        let flag_info_file = copy_to_temp_rw_file(\"./flag.info\");\n        let flag_info_path = flag_info_file.path().display().to_string();\n\n        // SAFETY:\n        // The safety here is ensured as only this single threaded test process will\n        // write to this file\n        let mut file = unsafe { map_mutable_storage_file(&flag_info_path).unwrap() };\n        for i in 0..8 {\n            set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, true).unwrap();\n            let attribute =\n                get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);\n            assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0);\n            set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, false).unwrap();\n            let attribute =\n                get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);\n            assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) == 0);\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aflags/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_defaults {\n    name: \"aflags.defaults\",\n    edition: \"2021\",\n    clippy_lints: \"android\",\n    lints: \"android\",\n    srcs: [\"src/main.rs\"],\n    rustlibs: [\n        \"libaconfig_device_paths\",\n        \"libaconfig_flags\",\n        \"libaconfig_protos\",\n        \"libaconfigd_protos_rust\",\n        \"libaconfig_storage_read_api\",\n        \"libaconfig_storage_file\",\n        \"libanyhow\",\n        \"libclap\",\n        \"libnix\",\n        \"libprotobuf\",\n        \"libregex\",\n        // TODO: b/371021174 remove this fake dependency once we find a proper strategy to\n        // deal with test aconfig libs are not present in storage because they are never used\n        // by the actual build\n        \"libaconfig_test_rust_library\",\n    ],\n}\n\nrust_binary {\n    name: \"aflags\",\n    host_supported: true,\n    defaults: [\"aflags.defaults\"],\n    apex_available: [\n        \"//apex_available:platform\",\n    ],\n}\n\nrust_test_host {\n    name: \"aflags.test\",\n    defaults: [\"aflags.defaults\"],\n    test_suites: [\"general-tests\"],\n}\n"
  },
  {
    "path": "tools/aconfig/aflags/Cargo.toml",
    "content": "[package]\nname = \"aflags\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nanyhow = \"1.0.69\"\npaste = \"1.0.11\"\nprotobuf = \"3.2.0\"\nregex = \"1.10.3\"\naconfig_protos = { path = \"../aconfig_protos\" }\naconfigd_protos = { version = \"0.1.0\", path = \"../../../../../packages/modules/ConfigInfrastructure/aconfigd/proto\"}\nnix = { version = \"0.28.0\", features = [\"user\"] }\naconfig_storage_file = { version = \"0.1.0\", path = \"../aconfig_storage_file\" }\naconfig_storage_read_api = { version = \"0.1.0\", path = \"../aconfig_storage_read_api\" }\nclap = {version = \"4.5.2\" }\naconfig_device_paths = { version = \"0.1.0\", path = \"../aconfig_device_paths\" }\naconfig_flags = { version = \"0.1.0\", path = \"../aconfig_flags\" }\n"
  },
  {
    "path": "tools/aconfig/aflags/src/aconfig_storage_source.rs",
    "content": "use crate::load_protos;\nuse crate::{Flag, FlagSource};\nuse crate::{FlagPermission, FlagValue, ValuePickedFrom};\nuse aconfigd_protos::{\n    ProtoFlagQueryReturnMessage, ProtoListStorageMessage, ProtoListStorageMessageMsg,\n    ProtoStorageRequestMessage, ProtoStorageRequestMessageMsg, ProtoStorageRequestMessages,\n    ProtoStorageReturnMessage, ProtoStorageReturnMessageMsg, ProtoStorageReturnMessages,\n};\nuse anyhow::anyhow;\nuse anyhow::Result;\nuse protobuf::Message;\nuse protobuf::SpecialFields;\nuse std::collections::HashMap;\nuse std::io::{Read, Write};\nuse std::net::Shutdown;\nuse std::os::unix::net::UnixStream;\n\npub struct AconfigStorageSource {}\n\nstatic ACONFIGD_SYSTEM_SOCKET_NAME: &str = \"/dev/socket/aconfigd_system\";\nstatic ACONFIGD_MAINLINE_SOCKET_NAME: &str = \"/dev/socket/aconfigd_mainline\";\n\nenum AconfigdSocket {\n    System,\n    Mainline,\n}\n\nimpl AconfigdSocket {\n    pub fn name(&self) -> &str {\n        match self {\n            AconfigdSocket::System => ACONFIGD_SYSTEM_SOCKET_NAME,\n            AconfigdSocket::Mainline => ACONFIGD_MAINLINE_SOCKET_NAME,\n        }\n    }\n}\n\nfn load_flag_to_container() -> Result<HashMap<String, String>> {\n    Ok(load_protos::load()?.into_iter().map(|p| (p.qualified_name(), p.container)).collect())\n}\n\nfn convert(msg: ProtoFlagQueryReturnMessage, containers: &HashMap<String, String>) -> Result<Flag> {\n    let (value, value_picked_from) = match (\n        &msg.boot_flag_value,\n        msg.default_flag_value,\n        msg.local_flag_value,\n        msg.has_local_override,\n    ) {\n        (_, _, Some(local), Some(has_local)) if has_local => {\n            (FlagValue::try_from(local.as_str())?, ValuePickedFrom::Local)\n        }\n        (Some(boot), Some(default), _, _) => {\n            let value = FlagValue::try_from(boot.as_str())?;\n            if *boot == default {\n                (value, ValuePickedFrom::Default)\n            } else {\n                (value, ValuePickedFrom::Server)\n            }\n        }\n        _ => return Err(anyhow!(\"missing override\")),\n    };\n\n    let staged_value = match (msg.boot_flag_value, msg.server_flag_value, msg.has_server_override) {\n        (Some(boot), Some(server), _) if boot == server => None,\n        (Some(boot), Some(server), Some(has_server)) if boot != server && has_server => {\n            Some(FlagValue::try_from(server.as_str())?)\n        }\n        _ => None,\n    };\n\n    let permission = match msg.is_readwrite {\n        Some(is_readwrite) => {\n            if is_readwrite {\n                FlagPermission::ReadWrite\n            } else {\n                FlagPermission::ReadOnly\n            }\n        }\n        None => return Err(anyhow!(\"missing permission\")),\n    };\n\n    let name = msg.flag_name.ok_or(anyhow!(\"missing flag name\"))?;\n    let package = msg.package_name.ok_or(anyhow!(\"missing package name\"))?;\n    let qualified_name = format!(\"{package}.{name}\");\n    Ok(Flag {\n        name,\n        package,\n        value,\n        permission,\n        value_picked_from,\n        staged_value,\n        container: containers\n            .get(&qualified_name)\n            .cloned()\n            .unwrap_or_else(|| \"<no container>\".to_string())\n            .to_string(),\n        // TODO: remove once DeviceConfig is not in the CLI.\n        namespace: \"-\".to_string(),\n    })\n}\n\nfn read_from_socket(socket: AconfigdSocket) -> Result<Vec<ProtoFlagQueryReturnMessage>> {\n    let messages = ProtoStorageRequestMessages {\n        msgs: vec![ProtoStorageRequestMessage {\n            msg: Some(ProtoStorageRequestMessageMsg::ListStorageMessage(ProtoListStorageMessage {\n                msg: Some(ProtoListStorageMessageMsg::All(true)),\n                special_fields: SpecialFields::new(),\n            })),\n            special_fields: SpecialFields::new(),\n        }],\n        special_fields: SpecialFields::new(),\n    };\n\n    let mut socket = UnixStream::connect(socket.name())?;\n\n    let message_buffer = messages.write_to_bytes()?;\n    let mut message_length_buffer: [u8; 4] = [0; 4];\n    let message_size = &message_buffer.len();\n    message_length_buffer[0] = (message_size >> 24) as u8;\n    message_length_buffer[1] = (message_size >> 16) as u8;\n    message_length_buffer[2] = (message_size >> 8) as u8;\n    message_length_buffer[3] = *message_size as u8;\n    socket.write_all(&message_length_buffer)?;\n    socket.write_all(&message_buffer)?;\n    socket.shutdown(Shutdown::Write)?;\n\n    let mut response_length_buffer: [u8; 4] = [0; 4];\n    socket.read_exact(&mut response_length_buffer)?;\n    let response_length = u32::from_be_bytes(response_length_buffer) as usize;\n    let mut response_buffer = vec![0; response_length];\n    socket.read_exact(&mut response_buffer)?;\n\n    let response: ProtoStorageReturnMessages =\n        protobuf::Message::parse_from_bytes(&response_buffer)?;\n\n    match response.msgs.as_slice() {\n        [ProtoStorageReturnMessage {\n            msg: Some(ProtoStorageReturnMessageMsg::ListStorageMessage(list_storage_message)),\n            ..\n        }] => Ok(list_storage_message.flags.clone()),\n        _ => Err(anyhow!(\"unexpected response from aconfigd\")),\n    }\n}\n\nimpl FlagSource for AconfigStorageSource {\n    fn list_flags() -> Result<Vec<Flag>> {\n        let containers = load_flag_to_container()?;\n        let system_messages = read_from_socket(AconfigdSocket::System);\n        let mainline_messages = read_from_socket(AconfigdSocket::Mainline);\n\n        let mut all_messages = vec![];\n        if let Ok(system_messages) = system_messages {\n            all_messages.extend_from_slice(&system_messages);\n        }\n        if let Ok(mainline_messages) = mainline_messages {\n            all_messages.extend_from_slice(&mainline_messages);\n        }\n\n        all_messages\n            .into_iter()\n            .map(|query_message| convert(query_message.clone(), &containers))\n            .collect()\n    }\n\n    fn override_flag(_namespace: &str, _qualified_name: &str, _value: &str) -> Result<()> {\n        todo!()\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aflags/src/device_config_source.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse crate::load_protos;\nuse crate::{Flag, FlagSource, FlagValue, ValuePickedFrom};\n\nuse anyhow::{anyhow, bail, Result};\nuse regex::Regex;\nuse std::collections::HashMap;\nuse std::process::Command;\nuse std::str;\n\npub struct DeviceConfigSource {}\n\nfn parse_device_config(raw: &str) -> Result<HashMap<String, FlagValue>> {\n    let mut flags = HashMap::new();\n    let regex = Regex::new(r\"(?m)^([[[:alnum:]]_]+/[[[:alnum:]]_\\.]+)=(true|false)$\")?;\n    for capture in regex.captures_iter(raw) {\n        let key =\n            capture.get(1).ok_or(anyhow!(\"invalid device_config output\"))?.as_str().to_string();\n        let value = FlagValue::try_from(\n            capture.get(2).ok_or(anyhow!(\"invalid device_config output\"))?.as_str(),\n        )?;\n        flags.insert(key, value);\n    }\n    Ok(flags)\n}\n\nfn read_device_config_output(command: &[&str]) -> Result<String> {\n    let output = Command::new(\"/system/bin/device_config\").args(command).output()?;\n    if !output.status.success() {\n        let reason = match output.status.code() {\n            Some(code) => {\n                let output = str::from_utf8(&output.stdout)?;\n                if !output.is_empty() {\n                    format!(\"exit code {code}, output was {output}\")\n                } else {\n                    format!(\"exit code {code}\")\n                }\n            }\n            None => \"terminated by signal\".to_string(),\n        };\n        bail!(\"failed to access flag storage: {}\", reason);\n    }\n    Ok(str::from_utf8(&output.stdout)?.to_string())\n}\n\nfn read_device_config_flags() -> Result<HashMap<String, FlagValue>> {\n    let list_output = read_device_config_output(&[\"list\"])?;\n    parse_device_config(&list_output)\n}\n\n/// Parse the list of newline-separated staged flags.\n///\n/// The output is a newline-sepaarated list of entries which follow this format:\n///   `namespace*flagname=value`\n///\n/// The resulting map maps from `namespace/flagname` to `value`, if a staged flag exists for\n/// `namespace/flagname`.\nfn parse_staged_flags(raw: &str) -> Result<HashMap<String, FlagValue>> {\n    let mut flags = HashMap::new();\n    for line in raw.split('\\n') {\n        match (line.find('*'), line.find('=')) {\n            (Some(star_index), Some(equal_index)) => {\n                let namespace = &line[..star_index];\n                let flag = &line[star_index + 1..equal_index];\n                if let Ok(value) = FlagValue::try_from(&line[equal_index + 1..]) {\n                    flags.insert(namespace.to_owned() + \"/\" + flag, value);\n                }\n            }\n            _ => continue,\n        };\n    }\n    Ok(flags)\n}\n\nfn read_staged_flags() -> Result<HashMap<String, FlagValue>> {\n    let staged_flags_output = read_device_config_output(&[\"list\", \"staged\"])?;\n    parse_staged_flags(&staged_flags_output)\n}\n\nfn reconcile(\n    pb_flags: &[Flag],\n    dc_flags: HashMap<String, FlagValue>,\n    staged_flags: HashMap<String, FlagValue>,\n) -> Vec<Flag> {\n    pb_flags\n        .iter()\n        .map(|f| {\n            let server_override = dc_flags.get(&format!(\"{}/{}\", f.namespace, f.qualified_name()));\n            let (value_picked_from, selected_value) = match server_override {\n                Some(value) if *value != f.value => (ValuePickedFrom::Server, *value),\n                _ => (ValuePickedFrom::Default, f.value),\n            };\n            Flag { value_picked_from, value: selected_value, ..f.clone() }\n        })\n        .map(|f| {\n            let staged_value = staged_flags\n                .get(&format!(\"{}/{}\", f.namespace, f.qualified_name()))\n                .map(|value| if *value != f.value { Some(*value) } else { None })\n                .unwrap_or(None);\n            Flag { staged_value, ..f }\n        })\n        .collect()\n}\n\nimpl FlagSource for DeviceConfigSource {\n    fn list_flags() -> Result<Vec<Flag>> {\n        let pb_flags = load_protos::load()?;\n        let dc_flags = read_device_config_flags()?;\n        let staged_flags = read_staged_flags()?;\n\n        let flags = reconcile(&pb_flags, dc_flags, staged_flags);\n        Ok(flags)\n    }\n\n    fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()> {\n        read_device_config_output(&[\"put\", namespace, qualified_name, value]).map(|_| ())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_parse_device_config() {\n        let input = r#\"\nnamespace_one/com.foo.bar.flag_one=true\nnamespace_one/com.foo.bar.flag_two=false\nrandom_noise;\nnamespace_two/android.flag_one=true\nnamespace_two/android.flag_two=nonsense\n\"#;\n        let expected = HashMap::from([\n            (\"namespace_one/com.foo.bar.flag_one\".to_string(), FlagValue::Enabled),\n            (\"namespace_one/com.foo.bar.flag_two\".to_string(), FlagValue::Disabled),\n            (\"namespace_two/android.flag_one\".to_string(), FlagValue::Enabled),\n        ]);\n        let actual = parse_device_config(input).unwrap();\n        assert_eq!(expected, actual);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/aflags/src/load_protos.rs",
    "content": "use crate::{Flag, FlagPermission, FlagValue, ValuePickedFrom};\nuse aconfig_protos::ProtoFlagPermission as ProtoPermission;\nuse aconfig_protos::ProtoFlagState as ProtoState;\nuse aconfig_protos::ProtoParsedFlag;\nuse aconfig_protos::ProtoParsedFlags;\nuse anyhow::Result;\nuse std::fs;\nuse std::path::Path;\n\n// TODO(b/329875578): use container field directly instead of inferring.\nfn infer_container(path: &Path) -> String {\n    let path_str = path.to_string_lossy();\n    path_str\n        .strip_prefix(\"/apex/\")\n        .or_else(|| path_str.strip_prefix('/'))\n        .unwrap_or(&path_str)\n        .strip_suffix(\"/etc/aconfig_flags.pb\")\n        .unwrap_or(&path_str)\n        .to_string()\n}\n\nfn convert_parsed_flag(path: &Path, flag: &ProtoParsedFlag) -> Flag {\n    let namespace = flag.namespace().to_string();\n    let package = flag.package().to_string();\n    let name = flag.name().to_string();\n\n    let value = match flag.state() {\n        ProtoState::ENABLED => FlagValue::Enabled,\n        ProtoState::DISABLED => FlagValue::Disabled,\n    };\n\n    let permission = match flag.permission() {\n        ProtoPermission::READ_ONLY => FlagPermission::ReadOnly,\n        ProtoPermission::READ_WRITE => FlagPermission::ReadWrite,\n    };\n\n    Flag {\n        namespace,\n        package,\n        name,\n        container: infer_container(path),\n        value,\n        staged_value: None,\n        permission,\n        value_picked_from: ValuePickedFrom::Default,\n    }\n}\n\npub(crate) fn load() -> Result<Vec<Flag>> {\n    let mut result = Vec::new();\n\n    let paths = aconfig_device_paths::parsed_flags_proto_paths()?;\n    for path in paths {\n        let Ok(bytes) = fs::read(&path) else {\n            eprintln!(\"warning: failed to read {:?}\", path);\n            continue;\n        };\n        let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)?;\n        for flag in parsed_flags.parsed_flag {\n            // TODO(b/334954748): enforce one-container-per-flag invariant.\n            result.push(convert_parsed_flag(&path, &flag));\n        }\n    }\n    Ok(result)\n}\n\npub(crate) fn list_containers() -> Result<Vec<String>> {\n    Ok(aconfig_device_paths::parsed_flags_proto_paths()?\n        .into_iter()\n        .map(|p| infer_container(&p))\n        .collect())\n}\n"
  },
  {
    "path": "tools/aconfig/aflags/src/main.rs",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! `aflags` is a device binary to read and write aconfig flags.\n\nuse std::env;\nuse std::process::{Command as OsCommand, Stdio};\n\nuse anyhow::{anyhow, ensure, Result};\nuse clap::Parser;\n\nmod device_config_source;\nuse device_config_source::DeviceConfigSource;\n\nmod aconfig_storage_source;\nuse aconfig_storage_source::AconfigStorageSource;\n\nmod load_protos;\n\n#[derive(Clone, PartialEq, Debug)]\nenum FlagPermission {\n    ReadOnly,\n    ReadWrite,\n}\n\nimpl std::fmt::Display for FlagPermission {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(\n            f,\n            \"{}\",\n            match &self {\n                Self::ReadOnly => \"read-only\",\n                Self::ReadWrite => \"read-write\",\n            }\n        )\n    }\n}\n\n#[derive(Clone, Debug)]\nenum ValuePickedFrom {\n    Default,\n    Server,\n    Local,\n}\n\nimpl std::fmt::Display for ValuePickedFrom {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(\n            f,\n            \"{}\",\n            match &self {\n                Self::Default => \"default\",\n                Self::Server => \"server\",\n                Self::Local => \"local\",\n            }\n        )\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\nenum FlagValue {\n    Enabled,\n    Disabled,\n}\n\nimpl TryFrom<&str> for FlagValue {\n    type Error = anyhow::Error;\n\n    fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {\n        match value {\n            \"true\" | \"enabled\" => Ok(Self::Enabled),\n            \"false\" | \"disabled\" => Ok(Self::Disabled),\n            _ => Err(anyhow!(\"cannot convert string '{}' to FlagValue\", value)),\n        }\n    }\n}\n\nimpl std::fmt::Display for FlagValue {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(\n            f,\n            \"{}\",\n            match &self {\n                Self::Enabled => \"enabled\",\n                Self::Disabled => \"disabled\",\n            }\n        )\n    }\n}\n\n#[derive(Clone, Debug)]\nstruct Flag {\n    namespace: String,\n    name: String,\n    package: String,\n    container: String,\n    value: FlagValue,\n    staged_value: Option<FlagValue>,\n    permission: FlagPermission,\n    value_picked_from: ValuePickedFrom,\n}\n\nimpl Flag {\n    fn qualified_name(&self) -> String {\n        format!(\"{}.{}\", self.package, self.name)\n    }\n\n    fn display_staged_value(&self) -> String {\n        match (&self.permission, self.staged_value) {\n            (FlagPermission::ReadOnly, _) => \"-\".to_string(),\n            (FlagPermission::ReadWrite, None) => \"-\".to_string(),\n            (FlagPermission::ReadWrite, Some(v)) => format!(\"(->{})\", v),\n        }\n    }\n}\n\ntrait FlagSource {\n    fn list_flags() -> Result<Vec<Flag>>;\n    fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>;\n}\n\nenum FlagSourceType {\n    DeviceConfig,\n    AconfigStorage,\n}\n\nconst ABOUT_TEXT: &str = \"Tool for reading and writing flags.\n\nRows in the table from the `list` command follow this format:\n\n  package flag_name value provenance permission container\n\n  * `package`: package set for this flag in its .aconfig definition.\n  * `flag_name`: flag name, also set in definition.\n  * `value`: the value read from the flag.\n  * `staged_value`: the value on next boot:\n    + `-`: same as current value\n    + `(->enabled) flipped to enabled on boot.\n    + `(->disabled) flipped to disabled on boot.\n  * `provenance`: one of:\n    + `default`: the flag value comes from its build-time default.\n    + `server`: the flag value comes from a server override.\n  * `permission`: read-write or read-only.\n  * `container`: the container for the flag, configured in its definition.\n\";\n\n#[derive(Parser, Debug)]\n#[clap(long_about=ABOUT_TEXT)]\nstruct Cli {\n    #[clap(subcommand)]\n    command: Command,\n}\n\n#[derive(Parser, Debug)]\nenum Command {\n    /// List all aconfig flags on this device.\n    List {\n        /// Optionally filter by container name.\n        #[clap(short = 'c', long = \"container\")]\n        container: Option<String>,\n    },\n\n    /// Enable an aconfig flag on this device, on the next boot.\n    Enable {\n        /// <package>.<flag_name>\n        qualified_name: String,\n    },\n\n    /// Disable an aconfig flag on this device, on the next boot.\n    Disable {\n        /// <package>.<flag_name>\n        qualified_name: String,\n    },\n\n    /// Display which flag storage backs aconfig flags.\n    WhichBacking,\n}\n\nstruct PaddingInfo {\n    longest_flag_col: usize,\n    longest_val_col: usize,\n    longest_staged_val_col: usize,\n    longest_value_picked_from_col: usize,\n    longest_permission_col: usize,\n}\n\nstruct Filter {\n    container: Option<String>,\n}\n\nimpl Filter {\n    fn apply(&self, flags: &[Flag]) -> Vec<Flag> {\n        flags\n            .iter()\n            .filter(|flag| match &self.container {\n                Some(c) => flag.container == *c,\n                None => true,\n            })\n            .cloned()\n            .collect()\n    }\n}\n\nfn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String {\n    let full_name = flag.qualified_name();\n    let p0 = info.longest_flag_col + 1;\n\n    let val = flag.value.to_string();\n    let p1 = info.longest_val_col + 1;\n\n    let staged_val = flag.display_staged_value();\n    let p2 = info.longest_staged_val_col + 1;\n\n    let value_picked_from = flag.value_picked_from.to_string();\n    let p3 = info.longest_value_picked_from_col + 1;\n\n    let perm = flag.permission.to_string();\n    let p4 = info.longest_permission_col + 1;\n\n    let container = &flag.container;\n\n    format!(\n        \"{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\\n\"\n    )\n}\n\nfn set_flag(qualified_name: &str, value: &str) -> Result<()> {\n    let flags_binding = DeviceConfigSource::list_flags()?;\n    let flag = flags_binding.iter().find(|f| f.qualified_name() == qualified_name).ok_or(\n        anyhow!(\"no aconfig flag '{qualified_name}'. Does the flag have an .aconfig definition?\"),\n    )?;\n\n    ensure!(flag.permission == FlagPermission::ReadWrite,\n            format!(\"could not write flag '{qualified_name}', it is read-only for the current release configuration.\"));\n\n    DeviceConfigSource::override_flag(&flag.namespace, qualified_name, value)?;\n\n    Ok(())\n}\n\nfn list(source_type: FlagSourceType, container: Option<String>) -> Result<String> {\n    let flags_unfiltered = match source_type {\n        FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?,\n        FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?,\n    };\n\n    if let Some(ref c) = container {\n        ensure!(\n            load_protos::list_containers()?.contains(c),\n            format!(\"container '{}' not found\", &c)\n        );\n    }\n\n    let flags = (Filter { container }).apply(&flags_unfiltered);\n    let padding_info = PaddingInfo {\n        longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0),\n        longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0),\n        longest_staged_val_col: flags\n            .iter()\n            .map(|f| f.display_staged_value().len())\n            .max()\n            .unwrap_or(0),\n        longest_value_picked_from_col: flags\n            .iter()\n            .map(|f| f.value_picked_from.to_string().len())\n            .max()\n            .unwrap_or(0),\n        longest_permission_col: flags\n            .iter()\n            .map(|f| f.permission.to_string().len())\n            .max()\n            .unwrap_or(0),\n    };\n\n    let mut result = String::from(\"\");\n    for flag in flags {\n        let row = format_flag_row(&flag, &padding_info);\n        result.push_str(&row);\n    }\n    Ok(result)\n}\n\nfn display_which_backing() -> String {\n    if aconfig_flags::auto_generated::enable_only_new_storage() {\n        \"aconfig_storage\".to_string()\n    } else {\n        \"device_config\".to_string()\n    }\n}\n\nfn invoke_updatable_aflags() {\n    let updatable_command = \"/apex/com.android.configinfrastructure/bin/aflags_updatable\";\n\n    let args: Vec<String> = env::args().collect();\n    let command_args = if args.len() >= 2 { &args[1..] } else { &[\"--help\".to_string()] };\n\n    let mut child = OsCommand::new(updatable_command);\n    for arg in command_args {\n        child.arg(arg);\n    }\n\n    let output = child\n        .stdin(Stdio::piped())\n        .stdout(Stdio::piped())\n        .spawn()\n        .expect(\"failed to execute child\")\n        .wait_with_output()\n        .expect(\"failed to execute command\");\n\n    let output_str = String::from_utf8_lossy(&output.stdout).trim().to_string();\n    if !output_str.is_empty() {\n        println!(\"{}\", output_str);\n    }\n}\n\nfn main() -> Result<()> {\n    if aconfig_flags::auto_generated::invoke_updatable_aflags() {\n        invoke_updatable_aflags();\n        return Ok(());\n    }\n\n    ensure!(nix::unistd::Uid::current().is_root(), \"must be root\");\n\n    let cli = Cli::parse();\n    let output = match cli.command {\n        Command::List { container } => {\n            if aconfig_flags::auto_generated::enable_only_new_storage() {\n                list(FlagSourceType::AconfigStorage, container)\n                    .map_err(|err| anyhow!(\"could not list flags: {err}\"))\n                    .map(Some)\n            } else {\n                list(FlagSourceType::DeviceConfig, container).map(Some)\n            }\n        }\n        Command::Enable { qualified_name } => set_flag(&qualified_name, \"true\").map(|_| None),\n        Command::Disable { qualified_name } => set_flag(&qualified_name, \"false\").map(|_| None),\n        Command::WhichBacking => Ok(Some(display_which_backing())),\n    };\n    match output {\n        Ok(Some(text)) => println!(\"{text}\"),\n        Ok(None) => (),\n        Err(message) => println!(\"Error: {message}\"),\n    }\n\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_filter_container() {\n        let flags = vec![\n            Flag {\n                namespace: \"namespace\".to_string(),\n                name: \"test1\".to_string(),\n                package: \"package\".to_string(),\n                value: FlagValue::Disabled,\n                staged_value: None,\n                permission: FlagPermission::ReadWrite,\n                value_picked_from: ValuePickedFrom::Default,\n                container: \"system\".to_string(),\n            },\n            Flag {\n                namespace: \"namespace\".to_string(),\n                name: \"test2\".to_string(),\n                package: \"package\".to_string(),\n                value: FlagValue::Disabled,\n                staged_value: None,\n                permission: FlagPermission::ReadWrite,\n                value_picked_from: ValuePickedFrom::Default,\n                container: \"not_system\".to_string(),\n            },\n            Flag {\n                namespace: \"namespace\".to_string(),\n                name: \"test3\".to_string(),\n                package: \"package\".to_string(),\n                value: FlagValue::Disabled,\n                staged_value: None,\n                permission: FlagPermission::ReadWrite,\n                value_picked_from: ValuePickedFrom::Default,\n                container: \"system\".to_string(),\n            },\n        ];\n\n        assert_eq!((Filter { container: Some(\"system\".to_string()) }).apply(&flags).len(), 2);\n    }\n\n    #[test]\n    fn test_filter_no_container() {\n        let flags = vec![\n            Flag {\n                namespace: \"namespace\".to_string(),\n                name: \"test1\".to_string(),\n                package: \"package\".to_string(),\n                value: FlagValue::Disabled,\n                staged_value: None,\n                permission: FlagPermission::ReadWrite,\n                value_picked_from: ValuePickedFrom::Default,\n                container: \"system\".to_string(),\n            },\n            Flag {\n                namespace: \"namespace\".to_string(),\n                name: \"test2\".to_string(),\n                package: \"package\".to_string(),\n                value: FlagValue::Disabled,\n                staged_value: None,\n                permission: FlagPermission::ReadWrite,\n                value_picked_from: ValuePickedFrom::Default,\n                container: \"not_system\".to_string(),\n            },\n            Flag {\n                namespace: \"namespace\".to_string(),\n                name: \"test3\".to_string(),\n                package: \"package\".to_string(),\n                value: FlagValue::Disabled,\n                staged_value: None,\n                permission: FlagPermission::ReadWrite,\n                value_picked_from: ValuePickedFrom::Default,\n                container: \"system\".to_string(),\n            },\n        ];\n\n        assert_eq!((Filter { container: None }).apply(&flags).len(), 3);\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/convert_finalized_flags/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_defaults {\n    name: \"convert_finalized_flags.defaults\",\n    edition: \"2021\",\n    clippy_lints: \"android\",\n    lints: \"android\",\n    rustlibs: [\n        \"libanyhow\",\n        \"libclap\",\n        \"libitertools\",\n        \"libprotobuf\",\n        \"libserde\",\n        \"libserde_json\",\n        \"libtempfile\",\n        \"libtinytemplate\",\n    ],\n}\n\nrust_library_host {\n    name: \"libconvert_finalized_flags\",\n    crate_name: \"convert_finalized_flags\",\n    defaults: [\"convert_finalized_flags.defaults\"],\n    srcs: [\n        \"src/lib.rs\",\n    ],\n}\n\nrust_binary_host {\n    name: \"convert_finalized_flags\",\n    defaults: [\"convert_finalized_flags.defaults\"],\n    srcs: [\"src/main.rs\"],\n    rustlibs: [\n        \"libconvert_finalized_flags\",\n        \"libserde_json\",\n    ],\n}\n\nrust_test_host {\n    name: \"convert_finalized_flags.test\",\n    defaults: [\"convert_finalized_flags.defaults\"],\n    test_suites: [\"general-tests\"],\n    srcs: [\"src/lib.rs\"],\n}\n\ngenrule {\n    name: \"finalized_flags_record.json\",\n    srcs: [\n        \"//prebuilts/sdk:finalized-api-flags\",\n    ],\n    tool_files: [\"extended_flags_list_35.txt\"],\n    out: [\"finalized_flags_record.json\"],\n    tools: [\"convert_finalized_flags\"],\n    cmd: \"args=\\\"\\\" && \" +\n        \"for f in $(locations //prebuilts/sdk:finalized-api-flags); \" +\n        \" do args=\\\"$$args --flag_file_path $$f\\\"; done && \" +\n        \"$(location convert_finalized_flags) $$args  --extended-flag-file-path $(location extended_flags_list_35.txt) > $(out)\",\n}\n"
  },
  {
    "path": "tools/aconfig/convert_finalized_flags/Cargo.toml",
    "content": "[package]\nname = \"convert_finalized_flags\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[features]\ndefault = [\"cargo\"]\ncargo = []\n\n[dependencies]\nanyhow = \"1.0.69\"\nclap = { version = \"4.1.8\", features = [\"derive\"] }\nserde = { version = \"1.0.152\", features = [\"derive\"] }\nserde_json = \"1.0.93\"\ntempfile = \"3.13.0\"\n"
  },
  {
    "path": "tools/aconfig/convert_finalized_flags/extended_flags_list_35.txt",
    "content": ""
  },
  {
    "path": "tools/aconfig/convert_finalized_flags/src/lib.rs",
    "content": "/*\n* Copyright (C) 2025 The Android Open Source Project\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*      http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n//! Functions to extract finalized flag information from\n//! /prebuilts/sdk/#/finalized-flags.txt.\n//! These functions are very specific to that file setup as well as the format\n//! of the files (just a list of the fully-qualified flag names).\n//! There are also some helper functions for local building using cargo. These\n//! functions are only invoked via cargo for quick local testing and will not\n//! be used during actual soong building. They are marked as such.\nuse anyhow::{anyhow, Result};\nuse serde::{Deserialize, Serialize};\nuse std::collections::{HashMap, HashSet};\nuse std::fs;\nuse std::io::{self, BufRead};\n\nconst SDK_INT_MULTIPLIER: u32 = 100_000;\n\n/// Just the fully qualified flag name (package_name.flag_name).\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]\npub struct FinalizedFlag {\n    /// Name of the flag.\n    pub flag_name: String,\n    /// Name of the package.\n    pub package_name: String,\n}\n\n/// API level in which the flag was finalized.\n#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]\npub struct ApiLevel(pub i32);\n\n/// API level of the extended flags file of version 35\npub const EXTENDED_FLAGS_35_APILEVEL: ApiLevel = ApiLevel(35);\n\n/// Contains all flags finalized for a given API level.\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]\npub struct FinalizedFlagMap(HashMap<ApiLevel, HashSet<FinalizedFlag>>);\n\nimpl FinalizedFlagMap {\n    /// Creates a new, empty instance.\n    pub fn new() -> Self {\n        Self(HashMap::new())\n    }\n\n    /// Convenience method for is_empty on the underlying map.\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    /// Returns the API level in which the flag was finalized .\n    pub fn get_finalized_level(&self, flag: &FinalizedFlag) -> Option<ApiLevel> {\n        for (api_level, flags_for_level) in &self.0 {\n            if flags_for_level.contains(flag) {\n                return Some(*api_level);\n            }\n        }\n        None\n    }\n\n    /// Insert the flag into the map for the given level if the flag is not\n    /// present in the map already - for *any* level (not just the one given).\n    pub fn insert_if_new(&mut self, level: ApiLevel, flag: FinalizedFlag) {\n        if self.contains(&flag) {\n            return;\n        }\n        self.0.entry(level).or_default().insert(flag);\n    }\n\n    fn contains(&self, flag: &FinalizedFlag) -> bool {\n        self.0.values().any(|flags_set| flags_set.contains(flag))\n    }\n}\n\n#[allow(dead_code)] // TODO: b/378936061: Use with SDK_INT_FULL check.\nfn parse_full_version(version: String) -> Result<u32> {\n    let (major, minor) = if let Some(decimal_index) = version.find('.') {\n        (version[..decimal_index].parse::<u32>()?, version[decimal_index + 1..].parse::<u32>()?)\n    } else {\n        (version.parse::<u32>()?, 0)\n    };\n\n    if major >= 21474 {\n        return Err(anyhow!(\"Major version too large, must be less than 21474.\"));\n    }\n    if minor >= SDK_INT_MULTIPLIER {\n        return Err(anyhow!(\"Minor version too large, must be less than {}.\", SDK_INT_MULTIPLIER));\n    }\n\n    Ok(major * SDK_INT_MULTIPLIER + minor)\n}\n\nconst EXTENDED_FLAGS_LIST_35: &str = \"extended_flags_list_35.txt\";\n\n/// Converts a string to an int. Will parse to int even if the string is \"X.0\".\n/// Returns error for \"X.1\".\nfn str_to_api_level(numeric_string: &str) -> Result<ApiLevel> {\n    let float_value = numeric_string.parse::<f64>()?;\n\n    if float_value.fract() == 0.0 {\n        Ok(ApiLevel(float_value as i32))\n    } else {\n        Err(anyhow!(\"Numeric string is float, can't parse to int.\"))\n    }\n}\n\n/// For each file, extracts the qualified flag names into a FinalizedFlag, then\n/// enters them in a map at the API level corresponding to their directory.\n/// Ex: /prebuilts/sdk/35/finalized-flags.txt -> {36, [flag1, flag2]}.\npub fn read_files_to_map_using_path(flag_files: Vec<String>) -> Result<FinalizedFlagMap> {\n    let mut data_map = FinalizedFlagMap::new();\n\n    for flag_file in flag_files {\n        // Split /path/sdk/<int.int>/finalized-flags.txt -> ['/path/sdk', 'int.int', 'finalized-flags.txt'].\n        let flag_file_split: Vec<String> =\n            flag_file.clone().rsplitn(3, '/').map(|s| s.to_string()).collect();\n\n        if &flag_file_split[0] != \"finalized-flags.txt\" {\n            return Err(anyhow!(\"Provided incorrect file, must be finalized-flags.txt\"));\n        }\n\n        let api_level_string = &flag_file_split[1];\n\n        // For now, skip any directory with full API level, e.g. \"36.1\". The\n        // finalized flag files each contain all flags finalized *up to* that\n        // level (including prior levels), so skipping intermediate levels means\n        // the flags will be included at the next full number.\n        // TODO: b/378936061 - Support full SDK version.\n        // In the future, we should error if provided a non-numeric directory.\n        let Ok(api_level) = str_to_api_level(api_level_string) else {\n            continue;\n        };\n\n        let file = fs::File::open(&flag_file)?;\n\n        io::BufReader::new(file).lines().for_each(|flag| {\n            let flag =\n                flag.unwrap_or_else(|_| panic!(\"Failed to read line from file {}\", flag_file));\n            let finalized_flag = build_finalized_flag(&flag)\n                .unwrap_or_else(|_| panic!(\"cannot build finalized flag {}\", flag));\n            data_map.insert_if_new(api_level, finalized_flag);\n        });\n    }\n\n    Ok(data_map)\n}\n\n/// Read the qualified flag names into a FinalizedFlag set\npub fn read_extend_file_to_map_using_path(extened_file: String) -> Result<HashSet<FinalizedFlag>> {\n    let (_, file_name) =\n        extened_file.rsplit_once('/').ok_or(anyhow!(\"Invalid file: '{}'\", extened_file))?;\n    if file_name != EXTENDED_FLAGS_LIST_35 {\n        return Err(anyhow!(\"Provided incorrect file, must be {}\", EXTENDED_FLAGS_LIST_35));\n    }\n    let file = fs::File::open(extened_file)?;\n    let extended_flags = io::BufReader::new(file)\n        .lines()\n        .map(|flag| {\n            let flag = flag.expect(\"Failed to read line from extended file\");\n            build_finalized_flag(&flag)\n                .unwrap_or_else(|_| panic!(\"cannot build finalized flag {}\", flag))\n        })\n        .collect::<HashSet<FinalizedFlag>>();\n    Ok(extended_flags)\n}\n\nfn build_finalized_flag(qualified_flag_name: &String) -> Result<FinalizedFlag> {\n    // Split the qualified flag name into package and flag name:\n    // com.my.package.name.my_flag_name -> ('com.my.package.name', 'my_flag_name')\n    let (package_name, flag_name) = qualified_flag_name\n        .rsplit_once('.')\n        .ok_or(anyhow!(\"Invalid qualified flag name format: '{}'\", qualified_flag_name))?;\n\n    Ok(FinalizedFlag { flag_name: flag_name.to_string(), package_name: package_name.to_string() })\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::fs::File;\n    use std::io::Write;\n    use tempfile::tempdir;\n\n    const FLAG_FILE_NAME: &str = \"finalized-flags.txt\";\n\n    // Creates some flags for testing.\n    fn create_test_flags() -> Vec<FinalizedFlag> {\n        vec![\n            FinalizedFlag { flag_name: \"name1\".to_string(), package_name: \"package1\".to_string() },\n            FinalizedFlag { flag_name: \"name2\".to_string(), package_name: \"package2\".to_string() },\n            FinalizedFlag { flag_name: \"name3\".to_string(), package_name: \"package3\".to_string() },\n        ]\n    }\n\n    // Writes the fully qualified flag names in the given file.\n    fn add_flags_to_file(flag_file: &mut File, flags: &[FinalizedFlag]) {\n        for flag in flags {\n            let _unused = writeln!(flag_file, \"{}.{}\", flag.package_name, flag.flag_name);\n        }\n    }\n\n    #[test]\n    fn test_read_flags_one_file() {\n        let flags = create_test_flags();\n\n        // Create the file <temp_dir>/35/finalized-flags.txt.\n        let temp_dir = tempdir().unwrap();\n        let mut file_path = temp_dir.path().to_path_buf();\n        file_path.push(\"35\");\n        fs::create_dir_all(&file_path).unwrap();\n        file_path.push(FLAG_FILE_NAME);\n        let mut file = File::create(&file_path).unwrap();\n\n        // Write all flags to the file.\n        add_flags_to_file(&mut file, &[flags[0].clone(), flags[1].clone()]);\n        let flag_file_path = file_path.to_string_lossy().to_string();\n\n        // Convert to map.\n        let map = read_files_to_map_using_path(vec![flag_file_path]).unwrap();\n\n        assert_eq!(map.0.len(), 1);\n        assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[0]));\n        assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[1]));\n    }\n\n    #[test]\n    fn test_read_flags_two_files() {\n        let flags = create_test_flags();\n\n        // Create the file <temp_dir>/35/finalized-flags.txt and for 36.\n        let temp_dir = tempdir().unwrap();\n        let mut file_path1 = temp_dir.path().to_path_buf();\n        file_path1.push(\"35\");\n        fs::create_dir_all(&file_path1).unwrap();\n        file_path1.push(FLAG_FILE_NAME);\n        let mut file1 = File::create(&file_path1).unwrap();\n\n        let mut file_path2 = temp_dir.path().to_path_buf();\n        file_path2.push(\"36\");\n        fs::create_dir_all(&file_path2).unwrap();\n        file_path2.push(FLAG_FILE_NAME);\n        let mut file2 = File::create(&file_path2).unwrap();\n\n        // Write all flags to the files.\n        add_flags_to_file(&mut file1, &[flags[0].clone()]);\n        add_flags_to_file(&mut file2, &[flags[0].clone(), flags[1].clone(), flags[2].clone()]);\n        let flag_file_path1 = file_path1.to_string_lossy().to_string();\n        let flag_file_path2 = file_path2.to_string_lossy().to_string();\n\n        // Convert to map.\n        let map = read_files_to_map_using_path(vec![flag_file_path1, flag_file_path2]).unwrap();\n\n        // Assert there are two API levels, 35 and 36.\n        assert_eq!(map.0.len(), 2);\n        assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[0]));\n\n        // 36 should not have the first flag in the set, as it was finalized in\n        // an earlier API level.\n        assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[1]));\n        assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[2]));\n    }\n\n    #[test]\n    fn test_read_flags_full_numbers() {\n        let flags = create_test_flags();\n\n        // Create the file <temp_dir>/35/finalized-flags.txt and for 36.\n        let temp_dir = tempdir().unwrap();\n        let mut file_path1 = temp_dir.path().to_path_buf();\n        file_path1.push(\"35.0\");\n        fs::create_dir_all(&file_path1).unwrap();\n        file_path1.push(FLAG_FILE_NAME);\n        let mut file1 = File::create(&file_path1).unwrap();\n\n        let mut file_path2 = temp_dir.path().to_path_buf();\n        file_path2.push(\"36.0\");\n        fs::create_dir_all(&file_path2).unwrap();\n        file_path2.push(FLAG_FILE_NAME);\n        let mut file2 = File::create(&file_path2).unwrap();\n\n        // Write all flags to the files.\n        add_flags_to_file(&mut file1, &[flags[0].clone()]);\n        add_flags_to_file(&mut file2, &[flags[0].clone(), flags[1].clone(), flags[2].clone()]);\n        let flag_file_path1 = file_path1.to_string_lossy().to_string();\n        let flag_file_path2 = file_path2.to_string_lossy().to_string();\n\n        // Convert to map.\n        let map = read_files_to_map_using_path(vec![flag_file_path1, flag_file_path2]).unwrap();\n\n        assert_eq!(map.0.len(), 2);\n        assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[0]));\n        assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[1]));\n        assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[2]));\n    }\n\n    #[test]\n    fn test_read_flags_fractions_round_up() {\n        let flags = create_test_flags();\n\n        // Create the file <temp_dir>/35/finalized-flags.txt and for 36.\n        let temp_dir = tempdir().unwrap();\n        let mut file_path1 = temp_dir.path().to_path_buf();\n        file_path1.push(\"35.1\");\n        fs::create_dir_all(&file_path1).unwrap();\n        file_path1.push(FLAG_FILE_NAME);\n        let mut file1 = File::create(&file_path1).unwrap();\n\n        let mut file_path2 = temp_dir.path().to_path_buf();\n        file_path2.push(\"36.0\");\n        fs::create_dir_all(&file_path2).unwrap();\n        file_path2.push(FLAG_FILE_NAME);\n        let mut file2 = File::create(&file_path2).unwrap();\n\n        // Write all flags to the files.\n        add_flags_to_file(&mut file1, &[flags[0].clone()]);\n        add_flags_to_file(&mut file2, &[flags[0].clone(), flags[1].clone(), flags[2].clone()]);\n        let flag_file_path1 = file_path1.to_string_lossy().to_string();\n        let flag_file_path2 = file_path2.to_string_lossy().to_string();\n\n        // Convert to map.\n        let map = read_files_to_map_using_path(vec![flag_file_path1, flag_file_path2]).unwrap();\n\n        // No flags were added in 35. All 35.1 flags were rolled up to 36.\n        assert_eq!(map.0.len(), 1);\n        assert!(!map.0.contains_key(&ApiLevel(35)));\n        assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[0]));\n        assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[1]));\n        assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[2]));\n    }\n\n    #[test]\n    fn test_read_flags_non_numeric() {\n        let flags = create_test_flags();\n\n        // Create the file <temp_dir>/35/finalized-flags.txt.\n        let temp_dir = tempdir().unwrap();\n        let mut file_path = temp_dir.path().to_path_buf();\n        file_path.push(\"35\");\n        fs::create_dir_all(&file_path).unwrap();\n        file_path.push(FLAG_FILE_NAME);\n        let mut flag_file = File::create(&file_path).unwrap();\n\n        let mut invalid_path = temp_dir.path().to_path_buf();\n        invalid_path.push(\"sdk-annotations\");\n        fs::create_dir_all(&invalid_path).unwrap();\n        invalid_path.push(FLAG_FILE_NAME);\n        File::create(&invalid_path).unwrap();\n\n        // Write all flags to the file.\n        add_flags_to_file(&mut flag_file, &[flags[0].clone(), flags[1].clone()]);\n        let flag_file_path = file_path.to_string_lossy().to_string();\n\n        // Convert to map.\n        let map = read_files_to_map_using_path(vec![\n            flag_file_path,\n            invalid_path.to_string_lossy().to_string(),\n        ])\n        .unwrap();\n\n        // No set should be created for sdk-annotations.\n        assert_eq!(map.0.len(), 1);\n        assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[0]));\n        assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[1]));\n    }\n\n    #[test]\n    fn test_read_flags_wrong_file_err() {\n        let flags = create_test_flags();\n\n        // Create the file <temp_dir>/35/finalized-flags.txt.\n        let temp_dir = tempdir().unwrap();\n        let mut file_path = temp_dir.path().to_path_buf();\n        file_path.push(\"35\");\n        fs::create_dir_all(&file_path).unwrap();\n        file_path.push(FLAG_FILE_NAME);\n        let mut flag_file = File::create(&file_path).unwrap();\n\n        let mut pre_flag_path = temp_dir.path().to_path_buf();\n        pre_flag_path.push(\"18\");\n        fs::create_dir_all(&pre_flag_path).unwrap();\n        pre_flag_path.push(\"some_random_file.txt\");\n        File::create(&pre_flag_path).unwrap();\n\n        // Write all flags to the file.\n        add_flags_to_file(&mut flag_file, &[flags[0].clone(), flags[1].clone()]);\n        let flag_file_path = file_path.to_string_lossy().to_string();\n\n        // Convert to map.\n        let map = read_files_to_map_using_path(vec![\n            flag_file_path,\n            pre_flag_path.to_string_lossy().to_string(),\n        ]);\n\n        assert!(map.is_err());\n    }\n\n    #[test]\n    fn test_flags_map_insert_if_new() {\n        let flags = create_test_flags();\n        let mut map = FinalizedFlagMap::new();\n        let l35 = ApiLevel(35);\n        let l36 = ApiLevel(36);\n\n        map.insert_if_new(l35, flags[0].clone());\n        map.insert_if_new(l35, flags[1].clone());\n        map.insert_if_new(l35, flags[2].clone());\n        map.insert_if_new(l36, flags[0].clone());\n\n        assert!(map.0.get(&l35).unwrap().contains(&flags[0]));\n        assert!(map.0.get(&l35).unwrap().contains(&flags[1]));\n        assert!(map.0.get(&l35).unwrap().contains(&flags[2]));\n        assert!(!map.0.contains_key(&l36));\n    }\n\n    #[test]\n    fn test_flags_map_get_level() {\n        let flags = create_test_flags();\n        let mut map = FinalizedFlagMap::new();\n        let l35 = ApiLevel(35);\n        let l36 = ApiLevel(36);\n\n        map.insert_if_new(l35, flags[0].clone());\n        map.insert_if_new(l36, flags[1].clone());\n\n        assert_eq!(map.get_finalized_level(&flags[0]).unwrap(), l35);\n        assert_eq!(map.get_finalized_level(&flags[1]).unwrap(), l36);\n    }\n\n    #[test]\n    fn test_read_flag_from_extended_file() {\n        let flags = create_test_flags();\n\n        // Create the file <temp_dir>/35/extended_flags_list_35.txt\n        let temp_dir = tempdir().unwrap();\n        let mut file_path = temp_dir.path().to_path_buf();\n        file_path.push(\"35\");\n        fs::create_dir_all(&file_path).unwrap();\n        file_path.push(EXTENDED_FLAGS_LIST_35);\n        let mut file = File::create(&file_path).unwrap();\n\n        // Write all flags to the file.\n        add_flags_to_file(&mut file, &[flags[0].clone(), flags[1].clone()]);\n\n        let flags_set =\n            read_extend_file_to_map_using_path(file_path.to_string_lossy().to_string()).unwrap();\n        assert_eq!(flags_set.len(), 2);\n        assert!(flags_set.contains(&flags[0]));\n        assert!(flags_set.contains(&flags[1]));\n    }\n\n    #[test]\n    fn test_read_flag_from_wrong_extended_file_err() {\n        let flags = create_test_flags();\n\n        // Create the file <temp_dir>/35/extended_flags_list.txt\n        let temp_dir = tempdir().unwrap();\n        let mut file_path = temp_dir.path().to_path_buf();\n        file_path.push(\"35\");\n        fs::create_dir_all(&file_path).unwrap();\n        file_path.push(\"extended_flags_list.txt\");\n        let mut file = File::create(&file_path).unwrap();\n\n        // Write all flags to the file.\n        add_flags_to_file(&mut file, &[flags[0].clone(), flags[1].clone()]);\n\n        let err = read_extend_file_to_map_using_path(file_path.to_string_lossy().to_string())\n            .unwrap_err();\n        assert_eq!(\n            format!(\"{:?}\", err),\n            \"Provided incorrect file, must be extended_flags_list_35.txt\"\n        );\n    }\n\n    #[test]\n    fn test_parse_full_version_correct_input_major_dot_minor() {\n        let version = parse_full_version(\"12.34\".to_string());\n\n        assert!(version.is_ok());\n        assert_eq!(version.unwrap(), 1_200_034);\n    }\n\n    #[test]\n    fn test_parse_full_version_correct_input_omit_dot_minor() {\n        let version = parse_full_version(\"1234\".to_string());\n\n        assert!(version.is_ok());\n        assert_eq!(version.unwrap(), 123_400_000);\n    }\n\n    #[test]\n    fn test_parse_full_version_incorrect_input_empty_string() {\n        let version = parse_full_version(\"\".to_string());\n\n        assert!(version.is_err());\n    }\n\n    #[test]\n    fn test_parse_full_version_incorrect_input_no_numbers_in_string() {\n        let version = parse_full_version(\"hello\".to_string());\n\n        assert!(version.is_err());\n    }\n\n    #[test]\n    fn test_parse_full_version_incorrect_input_unexpected_patch_version() {\n        let version = parse_full_version(\"1.2.3\".to_string());\n\n        assert!(version.is_err());\n    }\n\n    #[test]\n    fn test_parse_full_version_incorrect_input_leading_dot_missing_major_version() {\n        let version = parse_full_version(\".1234\".to_string());\n\n        assert!(version.is_err());\n    }\n\n    #[test]\n    fn test_parse_full_version_incorrect_input_trailing_dot_missing_minor_version() {\n        let version = parse_full_version(\"1234.\".to_string());\n\n        assert!(version.is_err());\n    }\n\n    #[test]\n    fn test_parse_full_version_incorrect_input_negative_major_version() {\n        let version = parse_full_version(\"-12.34\".to_string());\n\n        assert!(version.is_err());\n    }\n\n    #[test]\n    fn test_parse_full_version_incorrect_input_negative_minor_version() {\n        let version = parse_full_version(\"12.-34\".to_string());\n\n        assert!(version.is_err());\n    }\n\n    #[test]\n    fn test_parse_full_version_incorrect_input_major_version_too_large() {\n        let version = parse_full_version(\"40000.1\".to_string());\n\n        assert!(version.is_err());\n    }\n\n    #[test]\n    fn test_parse_full_version_incorrect_input_minor_version_too_large() {\n        let version = parse_full_version(\"3.99999999\".to_string());\n\n        assert!(version.is_err());\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/convert_finalized_flags/src/main.rs",
    "content": "/*\n* Copyright (C) 2025 The Android Open Source Project\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*      http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n//! convert_finalized_flags is a build time tool used to convert the finalized\n//! flags text files under prebuilts/sdk into structured data (FinalizedFlag\n//! struct).\n//! This binary is intended to run as part of a genrule to create a json file\n//! which is provided to the aconfig binary that creates the codegen.\n//! Usage:\n//! cargo run -- --flag-files-path path/to/prebuilts/sdk/finalized-flags.txt file2.txt etc\nuse anyhow::Result;\nuse clap::Parser;\n\nuse convert_finalized_flags::{\n    read_extend_file_to_map_using_path, read_files_to_map_using_path, EXTENDED_FLAGS_35_APILEVEL,\n};\n\nconst ABOUT_TEXT: &str = \"Tool for processing finalized-flags.txt files.\n\nThese files contain the list of qualified flag names that have been finalized,\neach on a newline. The directory of the flag file is the finalized API level.\n\nThe output is a json map of API level to set of FinalizedFlag objects. The only\nsupported use case for this tool is via a genrule at build time for aconfig\ncodegen.\n\nArgs:\n* `flag-files-path`: Space-separated list of absolute paths for the finalized\nflags files.\n\";\n\n#[derive(Parser, Debug)]\n#[clap(long_about=ABOUT_TEXT, bin_name=\"convert-finalized-flags\")]\nstruct Cli {\n    /// Flags files.\n    #[arg(long = \"flag_file_path\")]\n    flag_file_path: Vec<String>,\n\n    #[arg(long)]\n    extended_flag_file_path: String,\n}\n\nfn main() -> Result<()> {\n    let cli = Cli::parse();\n    let mut finalized_flags_map = read_files_to_map_using_path(cli.flag_file_path)?;\n    let extended_flag_set = read_extend_file_to_map_using_path(cli.extended_flag_file_path)?;\n    for flag in extended_flag_set {\n        finalized_flags_map.insert_if_new(EXTENDED_FLAGS_35_APILEVEL, flag);\n    }\n\n    let json_str = serde_json::to_string(&finalized_flags_map)?;\n    println!(\"{}\", json_str);\n    Ok(())\n}\n"
  },
  {
    "path": "tools/aconfig/exported_flag_check/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_defaults {\n    name: \"exported-flag-check-defaults\",\n    edition: \"2021\",\n    clippy_lints: \"android\",\n    lints: \"android\",\n    srcs: [\"src/main.rs\"],\n    rustlibs: [\n        \"libaconfig_protos\",\n        \"libanyhow\",\n        \"libclap\",\n        \"libregex\",\n    ],\n}\n\nrust_binary_host {\n    name: \"exported-flag-check\",\n    defaults: [\"record-finalized-flags-defaults\"],\n}\n\nrust_test_host {\n    name: \"exported-flag-check-test\",\n    defaults: [\"record-finalized-flags-defaults\"],\n    test_suites: [\"general-tests\"],\n}\n"
  },
  {
    "path": "tools/aconfig/exported_flag_check/Cargo.toml",
    "content": "[package]\nname = \"exported-flag-check\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[features]\ndefault = [\"cargo\"]\ncargo = []\n\n[dependencies]\naconfig_protos = { path = \"../aconfig_protos\" }\nanyhow = \"1.0.69\"\nclap = { version = \"4.1.8\", features = [\"derive\"] }\nregex = \"1.11.1\"\n"
  },
  {
    "path": "tools/aconfig/exported_flag_check/allow_flag_list.txt",
    "content": "android.adpf.adpf_viewrootimpl_action_down_boost\nandroid.app.admin.flags.coexistence_migration_for_supervision_enabled\nandroid.app.admin.flags.enable_supervision_service_sync\nandroid.app.admin.flags.lock_now_coexistence\nandroid.app.admin.flags.permission_migration_for_zero_trust_api_enabled\nandroid.app.admin.flags.reset_password_with_token_coexistence\nandroid.app.admin.flags.set_application_restrictions_coexistence\nandroid.app.admin.flags.set_backup_service_enabled_coexistence\nandroid.app.admin.flags.set_keyguard_disabled_features_coexistence\nandroid.app.admin.flags.set_permission_grant_state_coexistence\nandroid.app.app_restrictions_api\nandroid.app.enforce_pic_testmode_protocol\nandroid.app.job.backup_jobs_exemption\nandroid.app.pic_uses_shared_memory\nandroid.app.pinner_service_client_api\nandroid.app.supervision.flags.deprecate_dpm_supervision_apis\nandroid.app.supervision.flags.enable_sync_with_dpm\nandroid.app.supervision.flags.supervision_api\nandroid.app.supervision.flags.supervision_api_on_wear\nandroid.app.ui_rich_ongoing\nandroid.appwidget.flags.use_smaller_app_widget_system_radius\nandroid.car.feature.always_send_initial_value_event\nandroid.car.feature.android_b_vehicle_properties\nandroid.car.feature.android_vic_vehicle_properties\nandroid.car.feature.area_id_config_access\nandroid.car.feature.async_audio_service_init\nandroid.car.feature.audio_control_hal_configuration\nandroid.car.feature.audio_legacy_mode_navigation_volume\nandroid.car.feature.audio_vendor_freeze_improvements\nandroid.car.feature.batched_subscriptions\nandroid.car.feature.car_app_card\nandroid.car.feature.car_audio_dynamic_devices\nandroid.car.feature.car_audio_fade_manager_configuration\nandroid.car.feature.car_audio_min_max_activation_volume\nandroid.car.feature.car_audio_mute_ambiguity\nandroid.car.feature.car_evs_query_service_status\nandroid.car.feature.car_evs_stream_management\nandroid.car.feature.car_night_global_setting\nandroid.car.feature.car_power_cancel_shell_command\nandroid.car.feature.car_property_detailed_error_codes\nandroid.car.feature.car_property_supported_value\nandroid.car.feature.car_property_value_property_status\nandroid.car.feature.cluster_health_monitoring\nandroid.car.feature.display_compatibility\nandroid.car.feature.handle_property_events_in_binder_thread\nandroid.car.feature.persist_ap_settings\nandroid.car.feature.projection_query_bt_profile_inhibit\nandroid.car.feature.serverless_remote_access\nandroid.car.feature.subscription_with_resolution\nandroid.car.feature.supports_secure_passenger_users\nandroid.car.feature.switch_user_ignoring_uxr\nandroid.car.feature.variable_update_rate\nandroid.car.feature.visible_background_user_restrictions\nandroid.companion.new_association_builder\nandroid.companion.ongoing_perm_sync\nandroid.companion.virtualdevice.flags.camera_multiple_input_streams\nandroid.companion.virtualdevice.flags.notifications_for_device_streaming\nandroid.content.pm.get_package_storage_stats\nandroid.content.res.layout_readwrite_flags\nandroid.content.res.resources_minor_version_support\nandroid.content.res.rro_control_for_android_no_overlayable\nandroid.content.res.self_targeting_android_resource_frro\nandroid.content.res.system_context_handle_app_info_changed\nandroid.credentials.flags.settings_activity_enabled\nandroid.hardware.biometrics.screen_off_unlock_udfps\nandroid.hardware.devicestate.feature.flags.device_state_property_migration\nandroid.hardware.devicestate.feature.flags.device_state_rdm_v2\nandroid.hardware.devicestate.feature.flags.device_state_requester_cancel_state\nandroid.hardware.usb.flags.enable_interface_name_device_filter\nandroid.hardware.usb.flags.enable_is_mode_change_supported_api\nandroid.media.audio.focus_exclusive_with_recording\nandroid.media.audio.focus_freeze_test_api\nandroid.media.audio.foreground_audio_control\nandroid.media.audio.hardening_permission_api\nandroid.media.audio.hardening_permission_spa\nandroid.media.audio.ro_foreground_audio_control\nandroid.media.audiopolicy.audio_mix_test_api\nandroid.media.codec.aidl_hal_input_surface\nandroid.media.swcodec.flags.apv_software_codec\nandroid.media.swcodec.flags.mpeg2_keep_threads_active\nandroid.media.tv.flags.enable_le_audio_broadcast_ui\nandroid.media.tv.flags.enable_le_audio_unicast_ui\nandroid.media.tv.flags.hdmi_control_collect_physical_address\nandroid.media.tv.flags.hdmi_control_enhanced_behavior\nandroid.media.tv.flags.tif_unbind_inactive_tis\nandroid.multiuser.enable_biometrics_to_unlock_private_space\nandroid.net.platform.flags.mdns_improvement_for_25q2\nandroid.nfc.nfc_persist_log\nandroid.nfc.nfc_watchdog\nandroid.os.adpf_graphics_pipeline\nandroid.os.android_os_build_vanilla_ice_cream\nandroid.os.battery_saver_supported_check_api\nandroid.os.network_time_uses_shared_memory\nandroid.os.profiling.persist_queue\nandroid.os.profiling.redaction_enabled\nandroid.permission.flags.allow_host_permission_dialogs_on_virtual_devices\nandroid.permission.flags.device_aware_permissions_enabled\nandroid.permission.flags.device_policy_management_role_split_create_managed_profile_enabled\nandroid.permission.flags.enable_aiai_proxied_text_classifiers\nandroid.permission.flags.enable_otp_in_text_classifiers\nandroid.permission.flags.enable_sqlite_appops_accesses\nandroid.permission.flags.location_bypass_privacy_dashboard_enabled\nandroid.permission.flags.note_op_batching_enabled\nandroid.permission.flags.permission_request_short_circuit_enabled\nandroid.permission.flags.rate_limit_batched_note_op_async_callbacks_enabled\nandroid.permission.flags.sensitive_notification_app_protection\nandroid.permission.flags.supervision_role_permission_update_enabled\nandroid.permission.flags.unknown_call_package_install_blocking_enabled\nandroid.permission.flags.updatable_text_classifier_for_otp_detection_enabled\nandroid.permission.flags.use_profile_labels_for_default_app_section_titles\nandroid.permission.flags.wallet_role_cross_user_enabled\nandroid.provider.allow_config_maximum_call_log_entries_per_sim\nandroid.provider.backup_tasks_settings_screen\nandroid.provider.flags.new_storage_writer_system_api\nandroid.service.autofill.fill_dialog_improvements_impl\nandroid.service.chooser.fix_resolver_memory_leak\nandroid.service.notification.redact_sensitive_notifications_big_text_style\nandroid.service.notification.redact_sensitive_notifications_from_untrusted_listeners\nandroid.view.accessibility.motion_event_observing\nandroid.view.flags.expected_presentation_time_api\nandroid.view.flags.toolkit_frame_rate_touch_boost_25q1\nandroid.view.inputmethod.concurrent_input_methods\nandroid.view.inputmethod.ime_switcher_revamp\nandroid.view.inputmethod.imm_userhandle_hostsidetests\nandroid.webkit.mainline_apis\nandroid.widget.flags.use_wear_material3_ui\ncom.android.aconfig.test.disabled_rw_exported\ncom.android.aconfig.test.enabled_fixed_ro_exported\ncom.android.aconfig.test.enabled_ro_exported\ncom.android.aconfig.test.exported.exported_flag\ncom.android.aconfig.test.forcereadonly.fro_exported\ncom.android.adservices.ondevicepersonalization.flags.on_device_personalization_apis_enabled\ncom.android.appsearch.flags.app_open_event_indexer_enabled\ncom.android.appsearch.flags.apps_indexer_enabled\ncom.android.appsearch.flags.enable_app_functions_schema_parser\ncom.android.appsearch.flags.enable_apps_indexer_incremental_put\ncom.android.appsearch.flags.enable_contacts_index_first_middle_and_last_names\ncom.android.appsearch.flags.enable_document_limiter_replace_tracking\ncom.android.appsearch.flags.enable_enterprise_empty_batch_result_fix\ncom.android.bluetooth.flags.allow_switching_hid_and_hogp\ncom.android.bluetooth.flags.bt_offload_socket_api\ncom.android.bluetooth.flags.channel_sounding\ncom.android.bluetooth.flags.fix_started_module_race\ncom.android.bluetooth.flags.le_subrate_api\ncom.android.bluetooth.flags.leaudio_broadcast_monitor_source_sync_status\ncom.android.bluetooth.flags.leaudio_broadcast_volume_control_for_connected_devices\ncom.android.bluetooth.flags.leaudio_multiple_vocs_instances_api\ncom.android.bluetooth.flags.metadata_api_inactive_audio_device_upon_connection\ncom.android.bluetooth.flags.settings_can_control_hap_preset\ncom.android.bluetooth.flags.unix_file_socket_creation_failure\ncom.android.graphics.flags.icon_load_drawable_return_null_when_uri_decode_fails\ncom.android.graphics.hwui.flags.animated_image_drawable_filter_bitmap\ncom.android.hardware.input.manage_key_gestures\ncom.android.healthfitness.flags.activity_intensity_db\ncom.android.healthfitness.flags.add_missing_access_logs\ncom.android.healthfitness.flags.architecture_improvement\ncom.android.healthfitness.flags.cloud_backup_and_restore\ncom.android.healthfitness.flags.cycle_phases\ncom.android.healthfitness.flags.d2d_file_deletion_bug_fix\ncom.android.healthfitness.flags.dependency_injection\ncom.android.healthfitness.flags.development_database\ncom.android.healthfitness.flags.ecosystem_metrics\ncom.android.healthfitness.flags.ecosystem_metrics_db_changes\ncom.android.healthfitness.flags.export_import\ncom.android.healthfitness.flags.export_import_fast_follow\ncom.android.healthfitness.flags.export_import_nice_to_have\ncom.android.healthfitness.flags.expressive_theming_enabled\ncom.android.healthfitness.flags.health_connect_mappings\ncom.android.healthfitness.flags.immediate_export\ncom.android.healthfitness.flags.logcat_censor_iae\ncom.android.healthfitness.flags.new_information_architecture\ncom.android.healthfitness.flags.onboarding\ncom.android.healthfitness.flags.permission_metrics\ncom.android.healthfitness.flags.permission_tracker_fix_mapping_init\ncom.android.healthfitness.flags.personal_health_record_database\ncom.android.healthfitness.flags.personal_health_record_disable_d2d\ncom.android.healthfitness.flags.personal_health_record_disable_export_import\ncom.android.healthfitness.flags.personal_health_record_enable_d2d_and_export_import\ncom.android.healthfitness.flags.personal_health_record_entries_screen\ncom.android.healthfitness.flags.personal_health_record_lock_screen_banner\ncom.android.healthfitness.flags.personal_health_record_telemetry\ncom.android.healthfitness.flags.personal_health_record_telemetry_private_ww\ncom.android.healthfitness.flags.personal_health_record_ui_telemetry\ncom.android.healthfitness.flags.phr_fhir_basic_complex_type_validation\ncom.android.healthfitness.flags.phr_fhir_complex_type_validation\ncom.android.healthfitness.flags.phr_fhir_oneof_validation\ncom.android.healthfitness.flags.phr_fhir_primitive_type_validation\ncom.android.healthfitness.flags.phr_fhir_structural_validation\ncom.android.healthfitness.flags.phr_read_medical_resources_fix_query_limit\ncom.android.healthfitness.flags.phr_upsert_fix_parcel_size_calculation\ncom.android.healthfitness.flags.phr_upsert_fix_use_shared_memory\ncom.android.icu.icu_v_api\ncom.android.internal.telephony.flags.async_init_carrier_privileges_tracker\ncom.android.internal.telephony.flags.cleanup_carrier_app_update_enabled_state_logic\ncom.android.internal.telephony.flags.oem_enabled_satellite_phase_2\ncom.android.internal.telephony.flags.remap_disconnect_cause_sip_request_cancelled\ncom.android.libcore.hpke_v_apis\ncom.android.libcore.read_only_dynamic_code_load\ncom.android.libcore.v_apis\ncom.android.media.audio.hardening_impl\ncom.android.media.audio.hardening_strict\ncom.android.media.extractor.flags.extractor_mp4_enable_apv\ncom.android.media.extractor.flags.extractor_sniff_midi_optimizations\ncom.android.media.flags.enable_cross_user_routing_in_media_router2\ncom.android.media.flags.enable_notifying_activity_manager_with_media_session_status_change\ncom.android.media.metrics.flags.mediametrics_to_module\ncom.android.media.projection.flags.media_projection_connected_display\ncom.android.media.projection.flags.media_projection_connected_display_no_virtual_device\ncom.android.net.ct.flags.certificate_transparency_job\ncom.android.net.ct.flags.certificate_transparency_service\ncom.android.net.flags.restrict_local_network\ncom.android.net.flags.tethering_active_sessions_metrics\ncom.android.net.thread.flags.thread_mobile_enabled\ncom.android.nfc.module.flags.nfc_hce_latency_events\ncom.android.org.conscrypt.flags.certificate_transparency_checkservertrusted_api\ncom.android.permission.flags.add_banners_to_privacy_sensitive_apps_for_aaos\ncom.android.permission.flags.app_permission_fragment_uses_preferences\ncom.android.permission.flags.archiving_read_only\ncom.android.permission.flags.decluttered_permission_manager_enabled\ncom.android.permission.flags.enable_coarse_fine_location_prompt_for_aaos\ncom.android.permission.flags.enhanced_confirmation_backport_enabled\ncom.android.permission.flags.expressive_design_enabled\ncom.android.permission.flags.livedata_refactor_permission_timeline_enabled\ncom.android.permission.flags.odad_notifications_supported\ncom.android.permission.flags.permission_timeline_attribution_label_fix\ncom.android.permission.flags.private_profile_supported\ncom.android.permission.flags.safety_center_enabled_no_device_config\ncom.android.permission.flags.safety_center_issue_only_affects_group_status\ncom.android.permission.flags.wear_compose_material3\ncom.android.permission.flags.wear_privacy_dashboard_enabled_read_only\ncom.android.providers.contactkeys.flags.contactkeys_strip_fix\ncom.android.providers.media.flags.enable_backup_and_restore\ncom.android.providers.media.flags.enable_malicious_app_detector\ncom.android.providers.media.flags.enable_mark_media_as_favorite_api\ncom.android.providers.media.flags.enable_modern_photopicker\ncom.android.providers.media.flags.enable_photopicker_search\ncom.android.providers.media.flags.enable_photopicker_transcoding\ncom.android.providers.media.flags.enable_stable_uris_for_external_primary_volume\ncom.android.providers.media.flags.enable_stable_uris_for_public_volume\ncom.android.providers.media.flags.enable_unicode_check\ncom.android.providers.media.flags.index_media_latitude_longitude\ncom.android.providers.media.flags.version_lockdown\ncom.android.ranging.flags.ranging_stack_updates_25q4\ncom.android.server.backup.enable_read_all_external_storage_files\ncom.android.server.telecom.flags.allow_system_apps_resolve_voip_calls\ncom.android.server.telecom.flags.telecom_app_label_proxy_hsum_aware\ncom.android.server.telecom.flags.telecom_main_user_in_block_check\ncom.android.server.telecom.flags.telecom_main_user_in_get_respond_message_app\ncom.android.server.updates.certificate_transparency_installer\ncom.android.system.virtualmachine.flags.terminal_gui_support\ncom.android.tradeinmode.flags.enable_trade_in_mode\ncom.android.update_engine.minor_changes_2025q4\ncom.android.uwb.flags.uwb_fira_3_0_25q4\ncom.android.wifi.flags.network_provider_battery_charging_status\ncom.android.wifi.flags.p2p_dialog2\ncom.android.wifi.flags.shared_connectivity_broadcast_receiver_test_api\ncom.android.wifi.flags.wep_disabled_in_apm\ncom.android.window.flags.untrusted_embedding_state_sharing\nvendor.vibrator.hal.flags.enable_pwle_v2\nvendor.vibrator.hal.flags.remove_capo"
  },
  {
    "path": "tools/aconfig/exported_flag_check/allow_package_list.txt",
    "content": ""
  },
  {
    "path": "tools/aconfig/exported_flag_check/src/main.rs",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! `exported-flag-check` is a tool to ensures that exported flags are used as intended\nuse anyhow::{ensure, Result};\nuse clap::Parser;\nuse std::{collections::HashSet, fs::File, path::PathBuf};\n\nmod utils;\n\nuse utils::{\n    check_all_exported_flags, extract_flagged_api_flags, get_exported_flags_from_binary_proto,\n    read_finalized_flags,\n};\n\nconst ABOUT: &str = \"CCheck Exported Flags\n\nThis tool ensures that exported flags are used as intended. Exported flags, marked with\n`is_exported: true` in their declaration, are designed to control access to specific API\nfeatures. This tool identifies and reports any exported flags that are not currently\nassociated with an API feature, preventing unnecessary flag proliferation and maintaining\na clear API design.\n\nThis tool works as follows:\n\n  - Read API signature files from source tree (*current.txt files) [--api-signature-file]\n  - Read the current aconfig flag values from source tree [--parsed-flags-file]\n  - Read the previous finalized-flags.txt files from prebuilts/sdk [--finalized-flags-file]\n  - Extract the flags slated for API by scanning through the API signature files\n  - Merge the found flags with the recorded flags from previous API finalizations\n  - Error if exported flags are not in the set\n\";\n\n#[derive(Parser, Debug)]\n#[clap(about=ABOUT)]\nstruct Cli {\n    #[arg(long)]\n    parsed_flags_file: PathBuf,\n\n    #[arg(long)]\n    api_signature_file: Vec<PathBuf>,\n\n    #[arg(long)]\n    finalized_flags_file: PathBuf,\n}\n\nfn main() -> Result<()> {\n    let args = Cli::parse();\n\n    let mut flags_used_with_flaggedapi_annotation = HashSet::new();\n    for path in &args.api_signature_file {\n        let file = File::open(path)?;\n        let flags = extract_flagged_api_flags(file)?;\n        flags_used_with_flaggedapi_annotation.extend(flags);\n    }\n\n    let file = File::open(args.parsed_flags_file)?;\n    let all_flags = get_exported_flags_from_binary_proto(file)?;\n\n    let file = File::open(args.finalized_flags_file)?;\n    let already_finalized_flags = read_finalized_flags(file)?;\n\n    let exported_flags = check_all_exported_flags(\n        &flags_used_with_flaggedapi_annotation,\n        &all_flags,\n        &already_finalized_flags,\n    )?;\n\n    println!(\"{}\", exported_flags.join(\"\\n\"));\n\n    ensure!(\n        exported_flags.is_empty(),\n        \"Flags {} are exported but not used to guard any API. \\\n    Exported flag should be used to guard API\",\n        exported_flags.join(\",\")\n    );\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test() {\n        let input = include_bytes!(\"../tests/api-signature-file.txt\");\n        let flags_used_with_flaggedapi_annotation = extract_flagged_api_flags(&input[..]).unwrap();\n\n        let input = include_bytes!(\"../tests/flags.protobuf\");\n        let all_flags_to_be_finalized = get_exported_flags_from_binary_proto(&input[..]).unwrap();\n\n        let input = include_bytes!(\"../tests/finalized-flags.txt\");\n        let already_finalized_flags = read_finalized_flags(&input[..]).unwrap();\n\n        let exported_flags = check_all_exported_flags(\n            &flags_used_with_flaggedapi_annotation,\n            &all_flags_to_be_finalized,\n            &already_finalized_flags,\n        )\n        .unwrap();\n\n        assert_eq!(1, exported_flags.len());\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/exported_flag_check/src/utils.rs",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse aconfig_protos::ParsedFlagExt;\nuse anyhow::{anyhow, Context, Result};\nuse regex::Regex;\nuse std::{\n    collections::HashSet,\n    io::{BufRead, BufReader, Read},\n};\n\npub(crate) type FlagId = String;\n\n/// Grep for all flags used with @FlaggedApi annotations in an API signature file (*current.txt\n/// file).\npub(crate) fn extract_flagged_api_flags<R: Read>(mut reader: R) -> Result<HashSet<FlagId>> {\n    let mut haystack = String::new();\n    reader.read_to_string(&mut haystack)?;\n    let regex = Regex::new(r#\"(?ms)@FlaggedApi\\(\"(.*?)\"\\)\"#).unwrap();\n    let iter = regex.captures_iter(&haystack).map(|cap| cap[1].to_owned());\n    Ok(HashSet::from_iter(iter))\n}\n\n/// Read a list of flag names. The input is expected to be plain text, with each line containing\n/// the name of a single flag.\npub(crate) fn read_finalized_flags<R: Read>(reader: R) -> Result<HashSet<FlagId>> {\n    BufReader::new(reader)\n        .lines()\n        .map(|line_result| line_result.context(\"Failed to read line from finalized flags file\"))\n        .collect()\n}\n\n/// Parse a ProtoParsedFlags binary protobuf blob and return the fully qualified names of flags\n/// have is_exported as true.\npub(crate) fn get_exported_flags_from_binary_proto<R: Read>(\n    mut reader: R,\n) -> Result<HashSet<FlagId>> {\n    let mut buffer = Vec::new();\n    reader.read_to_end(&mut buffer)?;\n    let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&buffer)\n        .map_err(|_| anyhow!(\"failed to parse binary proto\"))?;\n    let iter = parsed_flags\n        .parsed_flag\n        .into_iter()\n        .filter(|flag| flag.is_exported())\n        .map(|flag| flag.fully_qualified_name());\n    Ok(HashSet::from_iter(iter))\n}\n\nfn get_allow_flag_list() -> Result<HashSet<FlagId>> {\n    let allow_list: HashSet<FlagId> =\n        include_str!(\"../allow_flag_list.txt\").lines().map(|x| x.into()).collect();\n    Ok(allow_list)\n}\n\nfn get_allow_package_list() -> Result<HashSet<FlagId>> {\n    let allow_list: HashSet<FlagId> =\n        include_str!(\"../allow_package_list.txt\").lines().map(|x| x.into()).collect();\n    Ok(allow_list)\n}\n\n/// Filter out the flags have is_exported as true but not used with @FlaggedApi annotations\n/// in the source tree, or in the previously finalized flags set.\npub(crate) fn check_all_exported_flags(\n    flags_used_with_flaggedapi_annotation: &HashSet<FlagId>,\n    all_flags: &HashSet<FlagId>,\n    already_finalized_flags: &HashSet<FlagId>,\n) -> Result<Vec<FlagId>> {\n    let allow_flag_list = get_allow_flag_list()?;\n    let allow_package_list = get_allow_package_list()?;\n\n    let new_flags: Vec<FlagId> = all_flags\n        .difference(flags_used_with_flaggedapi_annotation)\n        .cloned()\n        .collect::<HashSet<_>>()\n        .difference(already_finalized_flags)\n        .cloned()\n        .collect::<HashSet<_>>()\n        .difference(&allow_flag_list)\n        .filter(|flag| {\n            if let Some(last_dot_index) = flag.rfind('.') {\n                let package_name = &flag[..last_dot_index];\n                !allow_package_list.contains(package_name)\n            } else {\n                true\n            }\n        })\n        .cloned()\n        .collect();\n\n    Ok(new_flags)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_extract_flagged_api_flags() {\n        let api_signature_file = include_bytes!(\"../tests/api-signature-file.txt\");\n        let flags = extract_flagged_api_flags(&api_signature_file[..]).unwrap();\n        assert_eq!(\n            flags,\n            HashSet::from_iter(vec![\n                \"record_finalized_flags.test.foo\".to_string(),\n                \"this.flag.is.not.used\".to_string(),\n            ])\n        );\n    }\n\n    #[test]\n    fn test_read_finalized_flags() {\n        let input = include_bytes!(\"../tests/finalized-flags.txt\");\n        let flags = read_finalized_flags(&input[..]).unwrap();\n        assert_eq!(\n            flags,\n            HashSet::from_iter(vec![\n                \"record_finalized_flags.test.bar\".to_string(),\n                \"record_finalized_flags.test.baz\".to_string(),\n            ])\n        );\n    }\n\n    #[test]\n    fn test_disabled_or_read_write_flags_are_ignored() {\n        let bytes = include_bytes!(\"../tests/flags.protobuf\");\n        let flags = get_exported_flags_from_binary_proto(&bytes[..]).unwrap();\n        assert_eq!(\n            flags,\n            HashSet::from_iter(vec![\n                \"record_finalized_flags.test.foo\".to_string(),\n                \"record_finalized_flags.test.not_enabled\".to_string()\n            ])\n        );\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/exported_flag_check/tests/api-signature-file.txt",
    "content": "// Signature format: 2.0\npackage android {\n\n  public final class C {\n    ctor public C();\n  }\n\n  public static final class C.inner {\n    ctor public C.inner();\n    field @FlaggedApi(\"record_finalized_flags.test.foo\") public static final String FOO = \"foo\";\n    field @FlaggedApi(\"this.flag.is.not.used\") public static final String BAR = \"bar\";\n  }\n\n}\n\n"
  },
  {
    "path": "tools/aconfig/exported_flag_check/tests/finalized-flags.txt",
    "content": "record_finalized_flags.test.bar\nrecord_finalized_flags.test.baz\n"
  },
  {
    "path": "tools/aconfig/exported_flag_check/tests/flags.declarations",
    "content": "package: \"record_finalized_flags.test\"\ncontainer: \"system\"\n\nflag {\n    name: \"foo\"\n    namespace: \"test\"\n    description: \"FIXME\"\n    bug: \"\"\n    is_exported:true\n}\n\nflag {\n    name: \"not_enabled\"\n    namespace: \"test\"\n    description: \"FIXME\"\n    bug: \"\"\n    is_exported:true\n}\n"
  },
  {
    "path": "tools/aconfig/exported_flag_check/tests/flags.values",
    "content": "flag_value {\n    package: \"record_finalized_flags.test\"\n    name: \"foo\"\n    state: ENABLED\n    permission: READ_ONLY\n}\n\nflag_value {\n    package: \"record_finalized_flags.test\"\n    name: \"not_enabled\"\n    state: DISABLED\n    permission: READ_ONLY\n}\n"
  },
  {
    "path": "tools/aconfig/exported_flag_check/tests/generate-flags-protobuf.sh",
    "content": "#!/bin/bash\naconfig create-cache \\\n    --package record_finalized_flags.test \\\n    --container system \\\n    --declarations flags.declarations \\\n    --values flags.values \\\n    --cache flags.protobuf\n"
  },
  {
    "path": "tools/aconfig/fake_device_config/Android.bp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\njava_library {\n    name: \"fake_device_config\",\n    srcs: [\n        \"src/**/*.java\",\n    ],\n    sdk_version: \"none\",\n    system_modules: \"core-all-system-modules\",\n    host_supported: true,\n    is_stubs_module: true,\n}\n\njava_library {\n    name: \"aconfig_storage_stub\",\n    srcs: [\n        \"src/android/os/flagging/**/*.java\",\n    ],\n    sdk_version: \"core_current\",\n    host_supported: true,\n    is_stubs_module: true,\n}\n\njava_library {\n    name: \"aconfig_storage_stub_none\",\n    srcs: [\n        \"src/android/os/flagging/**/*.java\",\n    ],\n    sdk_version: \"none\",\n    system_modules: \"core-all-system-modules\",\n    host_supported: true,\n    is_stubs_module: true,\n}\n"
  },
  {
    "path": "tools/aconfig/fake_device_config/src/android/os/Build.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.os;\n\npublic class Build {\n    public static class VERSION {\n        public static final int SDK_INT = placeholder();\n        private static int placeholder() {\n            throw new UnsupportedOperationException(\"Stub!\");\n        }\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/fake_device_config/src/android/os/flagging/AconfigPackage.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.os.flagging;\n\n/*\n * This class allows generated aconfig code to compile independently of the framework.\n */\npublic class AconfigPackage {\n    public static AconfigPackage load(String packageName) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n\n    public boolean getBooleanFlagValue(String flagName, boolean defaultValue) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/fake_device_config/src/android/os/flagging/AconfigPackageInternal.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.os.flagging;\n\n/*\n * This class allows generated aconfig code to compile independently of the framework.\n */\npublic class AconfigPackageInternal {\n\n    public static AconfigPackageInternal load(String packageName, long packageFingerprint) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n\n    public boolean getBooleanFlagValue(int index) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.os.flagging;\n\nimport java.util.Set;\n\n/*\n * This class allows generated aconfig code to compile independently of the framework.\n */\npublic class PlatformAconfigPackage {\n\n    public static final Set<String> PLATFORM_PACKAGE_MAP_FILES =\n            Set.of(\n                    \"system.package.map\",\n                    \"system_ext.package.map\",\n                    \"vendor.package.map\",\n                    \"product.package.map\");\n\n    public static PlatformAconfigPackage load(String packageName) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n\n    public boolean getBooleanFlagValue(String flagName, boolean defaultValue) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackageInternal.java",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.os.flagging;\n\n/*\n * This class allows generated aconfig code to compile independently of the framework.\n */\npublic class PlatformAconfigPackageInternal {\n\n    public static PlatformAconfigPackageInternal load(String packageName, long packageFingerprint) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n\n    public boolean getBooleanFlagValue(int index) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/fake_device_config/src/android/util/Log.java",
    "content": "package android.util;\n\npublic final class Log {\n    public static int i(String tag, String msg) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n\n    public static int w(String tag, String msg) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n\n    public static int e(String tag, String msg) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n\n    public static int e(String tag, String msg, Throwable tr) {\n        throw new UnsupportedOperationException(\"Stub!\");\n    }\n}\n"
  },
  {
    "path": "tools/aconfig/overrideflags/overrideflags.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Create Aconfig value building rules.\n\nThis script will help to create Aconfig flag value building rules. It will\nparse necessary information in the value file to create the building rules, but\nit will not validate the value file. The validation will defer to the building\nsystem.\n\"\"\"\n\nimport argparse\nimport pathlib\nimport re\nimport sys\n\n\n_VALUE_LIST_TEMPLATE: str = \"\"\"\nACONFIG_VALUES_LIST_LOCAL = [{}]\n\"\"\"\n\n_ACONFIG_VALUES_TEMPLATE: str = \"\"\"\naconfig_values {{\n    name: \"{}\",\n    package: \"{}\",\n    srcs: [\n        \"{}\",\n    ]\n}}\n\"\"\"\n\n_ACONFIG_VALUES_NAME_SUFFIX: str = \"aconfig-local-override-{}\"\n\n_PACKAGE_REGEX = re.compile(r\"^package\\:\\s*\\\"([\\w\\d\\.]+)\\\"\")\n_ANDROID_BP_FILE_NAME = r\"Android.bp\"\n\n\ndef _parse_packages(file: pathlib.Path) -> set[str]:\n  packages = set()\n  with open(file) as f:\n    for line in f:\n      line = line.strip()\n      package_match = _PACKAGE_REGEX.match(line)\n      if package_match is None:\n        continue\n      package_name = package_match.group(1)\n      packages.add(package_name)\n\n  return packages\n\n\ndef _create_android_bp(packages: set[str], file_name: str) -> str:\n  android_bp = \"\"\n  value_list = \",\\n    \".join(\n      map(f'\"{_ACONFIG_VALUES_NAME_SUFFIX}\"'.format, packages)\n  )\n  if value_list:\n    value_list = \"\\n    \" + value_list + \"\\n\"\n  android_bp += _VALUE_LIST_TEMPLATE.format(value_list) + \"\\n\"\n\n  for package in packages:\n    android_bp += _ACONFIG_VALUES_TEMPLATE.format(\n        _ACONFIG_VALUES_NAME_SUFFIX.format(package), package, file_name\n    )\n    android_bp += \"\\n\"\n\n  return android_bp\n\n\ndef _write_android_bp(new_android_bp: str, out: pathlib.Path) -> None:\n  if not out.is_dir():\n    out.mkdir(parents=True, exist_ok=True)\n\n  output = out.joinpath(_ANDROID_BP_FILE_NAME)\n  with open(output, \"r+\", encoding=\"utf8\") as file:\n    lines = []\n    for line in file:\n      line = line.rstrip(\"\\n\")\n      if line.startswith(\"ACONFIG_VALUES_LIST_LOCAL\"):\n        break\n      lines.append(line)\n    # Overwrite the file with the updated contents.\n    file.seek(0)\n    file.truncate()\n    file.write(\"\\n\".join(lines))\n    file.write(new_android_bp)\n\n\ndef main(args):\n  \"\"\"Program entry point.\"\"\"\n  args_parser = argparse.ArgumentParser()\n  args_parser.add_argument(\n      \"--overrides\",\n      required=True,\n      help=\"The path to override file.\",\n  )\n  args_parser.add_argument(\n      \"--out\",\n      required=True,\n      help=\"The path to output directory.\",\n  )\n\n  args = args_parser.parse_args(args)\n  file = pathlib.Path(args.overrides)\n  out = pathlib.Path(args.out)\n  if not file.is_file():\n    raise FileNotFoundError(f\"File '{file}' is not found\")\n\n  packages = _parse_packages(file)\n  new_android_bp = _create_android_bp(packages, file.name)\n  _write_android_bp(new_android_bp, out)\n\n\nif __name__ == \"__main__\":\n  main(sys.argv[1:])\n"
  },
  {
    "path": "tools/acp/Android.bp",
    "content": "// Copyright 2005 The Android Open Source Project\n//\n// Custom version of cp.\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary_host {\n\n    srcs: [\"acp.c\"],\n    cflags: [\"-Wall\", \"-Werror\"],\n\n    static_libs: [\"libhost\"],\n    name: \"acp\",\n    stl: \"none\",\n\n}\n"
  },
  {
    "path": "tools/acp/README",
    "content": "README for Android \"acp\" Command\n\nThe \"cp\" command was judged and found wanting.  The issues are:\n\nMac OS X:\n - Uses the BSD cp, not the fancy GNU cp.  It lacks the \"-u\" flag, which\n   only copies files if they are newer than the destination.  This can\n   slow the build when copying lots of content.\n - Doesn't take the \"-d\" flag, which causes symlinks to be copied as\n   links.  This is the default behavior, so it's not all bad, but it\n   complains if you supply \"-d\".\n\nMinGW/Cygwin:\n - Gets really weird when copying a file called \"foo.exe\", failing with\n   \"cp: skipping file 'foo.exe', as it was replaced while being copied\".\n   This only seems to happen when the source file is on an NFS/Samba\n   volume.  \"cp\" works okay copying from local disk.\n\nLinux:\n - On some systems it's possible to have microsecond-accurate timestamps\n   on an NFS volume, and non-microsecond timestamps on a local volume.\n   If you copy from NFS to local disk, your NFS files will always be\n   newer, because the local disk time stamp is truncated rather than\n   rounded up.  This foils the \"-u\" flag if you also supply the \"-p\" flag\n   to preserve timestamps.\n - The Darwin linker insists that ranlib be current.  If you copy the\n   library, the time stamp no longer matches.  Preserving the time\n   stamp is essential, so simply turning the \"-p\" flag off doesn't work.\n\nFutzing around these in make with GNU make functions is awkward at best.\nIt's easier and more reliable to write a cp command that works properly.\n\n\nThe \"acp\" command takes most of the standard flags, following the GNU\nconventions.  It adds a \"-e\" flag, used when copying executables around.\nOn most systems it is ignored, but on MinGW/Cygwin it allows \"cp foo bar\"\nto work when what is actually meant is \"cp foo.exe bar.exe\".  Unlike the\ndefault Cygwin cp, \"acp foo bar\" will not find foo.exe unless you add\nthe \"-e\" flag, avoiding potential ambiguity.\n\n"
  },
  {
    "path": "tools/acp/acp.c",
    "content": "/*\n * Copyright 2005 The Android Open Source Project\n *\n * Android \"cp\" replacement.\n *\n * The GNU/Linux \"cp\" uses O_LARGEFILE in its open() calls, utimes() instead\n * of utime(), and getxattr()/setxattr() instead of chmod().  These are\n * probably \"better\", but are non-portable, and not necessary for our\n * purposes.\n */\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <getopt.h>\n#include <dirent.h>\n#include <fcntl.h>\n#include <utime.h>\n#include <limits.h>\n#include <errno.h>\n#include <assert.h>\n#include <host/CopyFile.h>\n\n/*#define DEBUG_MSGS*/\n#ifdef DEBUG_MSGS\n# define DBUG(x) printf x\n#else\n# define DBUG(x) ((void)0)\n#endif\n\n#define FSSEP '/'       /* filename separator char */\n\n\n/*\n * Process the command-line file arguments.\n *\n * Returns 0 on success.\n */\nint process(int argc, char* const argv[], unsigned int options)\n{\n    int retVal = 0;\n    int i;\n    char* stripDest = NULL;\n    int stripDestLen;\n    bool destMustBeDir = false;\n    struct stat sb;\n\n    assert(argc >= 2);\n\n    /*\n     * Check for and trim a trailing slash on the last arg.\n     *\n     * It's useful to be able to say \"cp foo bar/\" when you want to copy\n     * a single file into a directory.  If you say \"cp foo bar\", and \"bar\"\n     * does not exist, it will create \"bar\", when what you really wanted\n     * was for the cp command to fail with \"directory does not exist\".\n     */\n    stripDestLen = strlen(argv[argc-1]);\n    stripDest = malloc(stripDestLen+1);\n    memcpy(stripDest, argv[argc-1], stripDestLen+1);\n    if (stripDest[stripDestLen-1] == FSSEP) {\n        stripDest[--stripDestLen] = '\\0';\n        destMustBeDir = true;\n    }\n\n    if (argc > 2)\n        destMustBeDir = true;\n\n    /*\n     * Start with a quick check to ensure that, if we're expecting to copy\n     * to a directory, the target already exists and is actually a directory.\n     * It's okay if it's a symlink to a directory.\n     *\n     * If it turns out to be a directory, go ahead and raise the\n     * destMustBeDir flag so we do some path concatenation below.\n     */\n    if (stat(stripDest, &sb) < 0) {\n        if (destMustBeDir) {\n            if (errno == ENOENT)\n                fprintf(stderr,\n                    \"acp: destination directory '%s' does not exist\\n\",\n                    stripDest);\n            else\n                fprintf(stderr, \"acp: unable to stat dest dir\\n\");\n            retVal = 1;\n            goto bail;\n        }\n    } else {\n        if (S_ISDIR(sb.st_mode)) {\n            DBUG((\"--- dest exists and is a dir, setting flag\\n\"));\n            destMustBeDir = true;\n        } else if (destMustBeDir) {\n            fprintf(stderr,\n                \"acp: destination '%s' is not a directory\\n\",\n                stripDest);\n            retVal = 1;\n            goto bail;\n        }\n    }\n\n    /*\n     * Copying files.\n     *\n     * Strip trailing slashes off.  They shouldn't be there, but\n     * sometimes file completion will put them in for directories.\n     *\n     * The observed behavior of GNU and BSD cp is that they print warnings\n     * if something fails, but continue on.  If any part fails, the command\n     * exits with an error status.\n     */\n    for (i = 0; i < argc-1; i++) {\n        const char* srcName;\n        char* src;\n        char* dst;\n        int copyResult;\n        int srcLen;\n\n        /* make a copy of the source name, and strip trailing '/' */\n        srcLen = strlen(argv[i]);\n        src = malloc(srcLen+1);\n        memcpy(src, argv[i], srcLen+1);\n\n        if (src[srcLen-1] == FSSEP)\n            src[--srcLen] = '\\0';\n\n        /* find just the name part */\n        srcName = strrchr(src, FSSEP);\n        if (srcName == NULL) {\n            srcName = src;\n        } else {\n            srcName++;\n            assert(*srcName != '\\0');\n        }\n        \n        if (destMustBeDir) {\n            /* concatenate dest dir and src name */\n            int srcNameLen = strlen(srcName);\n\n            dst = malloc(stripDestLen +1 + srcNameLen +1);\n            memcpy(dst, stripDest, stripDestLen);\n            dst[stripDestLen] = FSSEP;\n            memcpy(dst + stripDestLen+1, srcName, srcNameLen+1);\n        } else {\n            /* simple */\n            dst = stripDest;\n        }\n\n        /*\n         * Copy the source to the destination.\n         */\n        copyResult = copyFile(src, dst, options);\n\n        if (copyResult != 0)\n            retVal = 1;\n\n        free(src);\n        if (dst != stripDest)\n            free(dst);\n    }\n\nbail:\n    free(stripDest);\n    return retVal;\n}\n\n/*\n * Set up the options.\n */\nint main(int argc, char* const argv[])\n{\n    bool wantUsage;\n    int ic, retVal;\n    int verboseLevel;\n    unsigned int options;\n\n    verboseLevel = 0;\n    options = 0;\n    wantUsage = false;\n\n    while (1) {\n        ic = getopt(argc, argv, \"defprtuv\");\n        if (ic < 0)\n            break;\n\n        switch (ic) {\n            case 'd':\n                options |= COPY_NO_DEREFERENCE;\n                break;\n            case 'e':\n                options |= COPY_TRY_EXE;\n                break;\n            case 'f':\n                options |= COPY_FORCE;\n                break;\n            case 'p':\n                options |= COPY_PERMISSIONS;\n                break;\n            case 't':\n                options |= COPY_TIMESTAMPS;\n                break;\n            case 'r':\n                options |= COPY_RECURSIVE;\n                break;\n            case 'u':\n                options |= COPY_UPDATE_ONLY;\n                break;\n            case 'v':\n                verboseLevel++;\n                break;\n            default:\n                fprintf(stderr, \"Unexpected arg -%c\\n\", ic);\n                wantUsage = true;\n                break;\n        }\n\n        if (wantUsage)\n            break;\n    }\n\n    options |= verboseLevel & COPY_VERBOSE_MASK;\n\n    if (optind == argc-1) {\n        fprintf(stderr, \"acp: missing destination file\\n\");\n        return 2;\n    } else if (optind+2 > argc)\n        wantUsage = true;\n\n    if (wantUsage) {\n        fprintf(stderr, \"Usage: acp [OPTION]... SOURCE DEST\\n\");\n        fprintf(stderr, \"  or:  acp [OPTION]... SOURCE... DIRECTORY\\n\");\n        fprintf(stderr, \"\\nOptions:\\n\");\n        fprintf(stderr, \"  -d  never follow (dereference) symbolic links\\n\");\n        fprintf(stderr, \"  -e  if source file doesn't exist, try adding \"\n                        \"'.exe' [Win32 only]\\n\");\n        fprintf(stderr, \"  -f  use force, removing existing file if it's \"\n                        \"not writeable\\n\");\n        fprintf(stderr, \"  -p  preserve mode, ownership\\n\");\n        fprintf(stderr, \"  -r  recursive copy\\n\");\n        fprintf(stderr, \"  -t  preserve timestamps\\n\");\n        fprintf(stderr, \"  -u  update only: don't copy if dest is newer\\n\");\n        fprintf(stderr, \"  -v  verbose output (-vv is more verbose)\\n\");\n        return 2;\n    }\n\n    retVal = process(argc-optind, argv+optind, options);\n    DBUG((\"EXIT: %d\\n\", retVal));\n    return retVal;\n}\n\n"
  },
  {
    "path": "tools/apicheck/Android.bp",
    "content": "// Copyright (C) 2008 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\njava_binary_host {\n    name: \"apicheck\",\n    wrapper: \"etc/apicheck\",\n    static_libs: [\n        \"doclava\",\n        \"jsilver\",\n    ],\n}\n"
  },
  {
    "path": "tools/apicheck/etc/apicheck",
    "content": "#!/bin/bash\n#\n# Copyright (C) 2005, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Set up prog to be the path of this script, including following symlinks,\n# and set up progdir to be the fully-qualified pathname of its directory.\n#\n# The classpath and other java options used in apicheck are specified in\n# build/make/core/tasks/apicheck.mk.\n\nprog=\"$0\"\nwhile [ -h \"${prog}\" ]; do\n    newProg=`/bin/ls -ld \"${prog}\"`\n    newProg=`expr \"${newProg}\" : \".* -> \\(.*\\)$\"`\n    if expr \"x${newProg}\" : 'x/' >/dev/null; then\n        prog=\"${newProg}\"\n    else\n        progdir=`dirname \"${prog}\"`\n        prog=\"${progdir}/${newProg}\"\n    fi\ndone\noldwd=`pwd`\nprogdir=`dirname \"${prog}\"`\ncd \"${progdir}\"\nprogdir=`pwd`\nprog=\"${progdir}\"/`basename \"${prog}\"`\ncd \"${oldwd}\"\n\njavaOpts=\"\"\nwhile expr \"x$1\" : 'x-J' >/dev/null; do\n    opt=`expr \"x$1\" : 'x-J\\(.*\\)'`\n    javaOpts=\"${javaOpts} -${opt}\"\n    shift\ndone\n\nexec java $javaOpts com.google.doclava.apicheck.ApiCheck \"$@\"\n"
  },
  {
    "path": "tools/atree/Android.bp",
    "content": "// Copyright 2007 The Android Open Source Project\n//\n// Copies files into the directory structure described by a manifest\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary_host {\n    name: \"atree\",\n    srcs: [\n        \"atree.cpp\",\n        \"files.cpp\",\n        \"fs.cpp\",\n    ],\n    cflags: [\"-Wall\", \"-Werror\"],\n    static_libs: [\"libhost\"],\n}\n"
  },
  {
    "path": "tools/atree/atree.cpp",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdarg.h>\n#include \"options.h\"\n#include \"files.h\"\n#include \"fs.h\"\n#include <set>\n#include <iostream>\n#include <sstream>\n\nusing namespace std;\n\nbool g_debug = getenv(\"ATREE_DEBUG\") != NULL;\nvector<string> g_listFiles;\nvector<string> g_inputBases;\nmap<string, string> g_variables;\nstring g_outputBase;\nstring g_dependency;\nbool g_useHardLinks = false;\n\nconst char* USAGE =\n\"\\n\"\n\"Usage: atree OPTIONS\\n\"\n\"\\n\"\n\"Options:\\n\"\n\"  -f FILELIST    Specify one or more files containing the\\n\"\n\"                 list of files to copy.\\n\"\n\"  -I INPUTDIR    Specify one or more base directories in\\n\"\n\"                 which to look for the files\\n\"\n\"  -o OUTPUTDIR   Specify the directory to copy all of the\\n\"\n\"                 output files to.\\n\"\n\"  -l             Use hard links instead of copying the files.\\n\"\n\"  -m DEPENDENCY  Output a make-formatted file containing the list.\\n\"\n\"                 of files included.  It sets the variable ATREE_FILES.\\n\"\n\"  -v VAR=VAL     Replaces ${VAR} by VAL when reading input files.\\n\"\n\"  -d             Verbose debug mode.\\n\"\n\"\\n\"\n\"FILELIST file format:\\n\"\n\"  The FILELIST files contain the list of files that will end up\\n\"\n\"  in the final OUTPUTDIR.  Atree will look for files in the INPUTDIR\\n\"\n\"  directories in the order they are specified.\\n\"\n\"\\n\"\n\"  In a FILELIST file, comment lines start with a #.  Other lines\\n\"\n\"  are of the format:\\n\"\n\"\\n\"\n\"    [rm|strip] DEST\\n\"\n\"    SRC [strip] DEST\\n\"\n\"    -SRCPATTERN\\n\"\n\"\\n\"\n\"  DEST should be path relative to the output directory.\\n\"\n\"  'rm DEST' removes the destination file and fails if it's missing.\\n\"\n\"  'strip DEST' strips the binary destination file.\\n\"\n\"  If SRC is supplied, the file names can be different.\\n\"\n\"  SRCPATTERN is a pattern for the filenames.\\n\"\n\"\\n\";\n\nint usage()\n{\n    fwrite(USAGE, strlen(USAGE), 1, stderr);\n    return 1;\n}\n\nstatic bool\nadd_variable(const char* arg) {\n    const char* p = arg;\n    while (*p && *p != '=') p++;\n\n    if (*p == 0 || p == arg || p[1] == 0) {\n        return false;\n    }\n\n    ostringstream var;\n    var << \"${\" << string(arg, p-arg) << \"}\";\n    g_variables[var.str()] = string(p+1);\n    return true;\n}\n\nstatic void\ndebug_printf(const char* format, ...)\n{\n    if (g_debug) {\n        fflush(stderr);\n        va_list ap;\n        va_start(ap, format);\n        vprintf(format, ap);\n        va_end(ap);\n        fflush(stdout);\n    }\n}\n\n// Escape the filename so that it can be added to the makefile properly.\nstatic string\nescape_filename(const string& name)\n{\n    ostringstream new_name;\n    for (string::const_iterator iter = name.begin(); iter != name.end(); ++iter)\n    {\n        switch (*iter)\n        {\n            case '$':\n                new_name << \"$$\";\n                break;\n            default:\n                new_name << *iter;\n                break;\n        }\n    }\n    return new_name.str();\n}\n\nint\nmain(int argc, char* const* argv)\n{\n    int err;\n    bool done = false;\n    while (!done) {\n        int opt = getopt(argc, argv, \"f:I:o:hlm:v:d\");\n        switch (opt)\n        {\n            case -1:\n                done = true;\n                break;\n            case 'f':\n                g_listFiles.push_back(string(optarg));\n                break;\n            case 'I':\n                g_inputBases.push_back(string(optarg));\n                break;\n            case 'o':\n                if (g_outputBase.length() != 0) {\n                    fprintf(stderr, \"%s: -o may only be supplied once -- \"\n                                \"-o %s\\n\", argv[0], optarg);\n                    return usage();\n                }\n                g_outputBase = optarg;\n                break;\n            case 'l':\n                g_useHardLinks = true;\n                break;\n            case 'm':\n                if (g_dependency.length() != 0) {\n                    fprintf(stderr, \"%s: -m may only be supplied once -- \"\n                                \"-m %s\\n\", argv[0], optarg);\n                    return usage();\n                }\n                g_dependency = optarg;\n                break;\n            case 'v':\n                if (!add_variable(optarg)) {\n                    fprintf(stderr, \"%s Invalid expression in '-v %s': \"\n                            \"expected format is '-v VAR=VALUE'.\\n\",\n                            argv[0], optarg);\n                    return usage();\n                }\n                break;\n            case 'd':\n                g_debug = true;\n                break;\n            default:\n            case '?':\n            case 'h':\n                return usage();\n        }\n    }\n    if (optind != argc) {\n        fprintf(stderr, \"%s: invalid argument -- %s\\n\", argv[0], argv[optind]);\n        return usage();\n    }\n\n    if (g_listFiles.size() == 0) {\n        fprintf(stderr, \"%s: At least one -f option must be supplied.\\n\",\n                 argv[0]);\n        return usage();\n    }\n\n    if (g_inputBases.size() == 0) {\n        fprintf(stderr, \"%s: At least one -I option must be supplied.\\n\",\n                 argv[0]);\n        return usage();\n    }\n\n    if (g_outputBase.length() == 0) {\n        fprintf(stderr, \"%s: -o option must be supplied.\\n\", argv[0]);\n        return usage();\n    }\n\n\n#if 0\n    for (vector<string>::iterator it=g_listFiles.begin();\n                                it!=g_listFiles.end(); it++) {\n        printf(\"-f \\\"%s\\\"\\n\", it->c_str());\n    }\n    for (vector<string>::iterator it=g_inputBases.begin();\n                                it!=g_inputBases.end(); it++) {\n        printf(\"-I \\\"%s\\\"\\n\", it->c_str());\n    }\n    printf(\"-o \\\"%s\\\"\\n\", g_outputBase.c_str());\n    if (g_useHardLinks) {\n        printf(\"-l\\n\");\n    }\n#endif\n\n    vector<FileRecord> files;\n    vector<FileRecord> more;\n    vector<string> excludes;\n    set<string> directories;\n    set<string> deleted;\n\n    // read file lists\n    for (vector<string>::iterator it=g_listFiles.begin();\n                                 it!=g_listFiles.end(); it++) {\n        err = read_list_file(*it, g_variables, &files, &excludes);\n        if (err != 0) {\n            return err;\n        }\n    }\n\n    // look for input files\n    err = 0;\n    for (vector<FileRecord>::iterator it=files.begin();\n                                it!=files.end(); it++) {\n        err |= locate(&(*it), g_inputBases);\n    }\n\n    // expand the directories that we should copy into a list of files\n    for (vector<FileRecord>::iterator it=files.begin();\n                                it!=files.end(); it++) {\n        if (it->sourceIsDir) {\n            err |= list_dir(*it, excludes, &more);\n        }\n    }\n    for (vector<FileRecord>::iterator it=more.begin();\n                                it!=more.end(); it++) {\n        files.push_back(*it);\n    }\n\n    // get the name and modtime of the output files\n    for (vector<FileRecord>::iterator it=files.begin();\n                                it!=files.end(); it++) {\n        stat_out(g_outputBase, &(*it));\n    }\n\n    if (err != 0) {\n        return 1;\n    }\n\n    // gather directories\n    for (vector<FileRecord>::iterator it=files.begin();\n                                it!=files.end(); it++) {\n        if (it->sourceIsDir) {\n            directories.insert(it->outPath);\n        } else {\n            string s = dir_part(it->outPath);\n            if (s != \".\") {\n                directories.insert(s);\n            }\n        }\n    }\n\n    // gather files that should become directores\n    // and directories that should become files\n    for (vector<FileRecord>::iterator it=files.begin();\n                                it!=files.end(); it++) {\n        if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) {\n            deleted.insert(it->outPath);\n        }\n    }\n\n    // delete files\n    for (set<string>::iterator it=deleted.begin();\n                                it!=deleted.end(); it++) {\n        debug_printf(\"deleting %s\\n\", it->c_str());\n        err = remove_recursively(*it);\n        if (err != 0) {\n            return err;\n        }\n    }\n\n    // remove all files or directories as requested from the input atree file.\n    // must be done before create new directories.\n    for (vector<FileRecord>::iterator it=files.begin();\n                                it!=files.end(); it++) {\n        if (!it->sourceIsDir) {\n            if (it->fileOp == FILE_OP_REMOVE &&\n                    deleted.count(it->outPath) == 0) {\n                debug_printf(\"remove %s\\n\", it->outPath.c_str());\n                err = remove_recursively(it->outPath);\n                if (err != 0) {\n                    return err;\n                }\n            }\n        }\n    }\n\n    // make directories\n    for (set<string>::iterator it=directories.begin();\n                                it!=directories.end(); it++) {\n        debug_printf(\"mkdir %s\\n\", it->c_str());\n        err = mkdir_recursively(*it);\n        if (err != 0) {\n            return err;\n        }\n    }\n\n    // copy (or link) files that are newer or of different size\n    for (vector<FileRecord>::iterator it=files.begin();\n                                it!=files.end(); it++) {\n        if (!it->sourceIsDir) {\n            if (it->fileOp == FILE_OP_REMOVE) {\n                continue;\n            }\n\n            debug_printf(\"copy %s(%ld) ==> %s(%ld)\",\n                it->sourcePath.c_str(), it->sourceMod,\n                it->outPath.c_str(), it->outMod);\n\n            if (it->outSize != it->sourceSize || it->outMod < it->sourceMod) {\n                err = copy_file(it->sourcePath, it->outPath);\n                debug_printf(\" done.\\n\");\n                if (err != 0) {\n                    return err;\n                }\n            } else {\n                debug_printf(\" skipping.\\n\");\n            }\n\n            if (it->fileOp == FILE_OP_STRIP) {\n                debug_printf(\"strip %s\\n\", it->outPath.c_str());\n                err = strip_file(it->outPath);\n                if (err != 0) {\n                    return err;\n                }\n            }\n        }\n    }\n\n    // output the dependency file\n    if (g_dependency.length() != 0) {\n        FILE *f = fopen(g_dependency.c_str(), \"w\");\n        if (f != NULL) {\n            fprintf(f, \"ATREE_FILES := $(ATREE_FILES) \\\\\\n\");\n            for (vector<FileRecord>::iterator it=files.begin();\n                                it!=files.end(); it++) {\n                if (!it->sourceIsDir) {\n                    fprintf(f, \"%s \\\\\\n\",\n                            escape_filename(it->sourcePath).c_str());\n                }\n            }\n            fprintf(f, \"\\n\");\n            fclose(f);\n        } else {\n            fprintf(stderr, \"error opening manifest file for write: %s\\n\",\n                    g_dependency.c_str());\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "tools/atree/files.cpp",
    "content": "#include \"files.h\"\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <dirent.h>\n#include <fnmatch.h>\n#include <string.h>\n#include <stdlib.h>\n\nstatic bool\nis_comment_line(const char* p)\n{\n    while (*p && isspace(*p)) {\n        p++;\n    }\n    return *p == '#';\n}\n\nstatic string\npath_append(const string& base, const string& leaf)\n{\n    string full = base;\n    if (base.length() > 0 && leaf.length() > 0) {\n        full += '/';\n    }\n    full += leaf;\n    return full;\n}\n\nstatic bool\nis_whitespace_line(const char* p)\n{\n    while (*p) {\n        if (!isspace(*p)) {\n            return false;\n        }\n        p++;\n    }\n    return true;\n}\n\nstatic bool\nis_exclude_line(const char* p) {\n    while (*p) {\n        if (*p == '-') {\n            return true;\n        }\n        else if (isspace(*p)) {\n            p++;\n        }\n        else {\n            return false;\n        }\n    }\n    return false;\n}\n\nvoid\nsplit_line(const char* p, vector<string>* out)\n{\n    const char* q = p;\n    enum { WHITE, TEXT, IN_QUOTE } state = WHITE;\n    while (*p) {\n        if (*p == '#') {\n            break;\n        }\n\n        switch (state)\n        {\n            case WHITE:\n                if (!isspace(*p)) {\n                    q = p;\n                    state = (*p == '\"') ? IN_QUOTE : TEXT;\n                }\n                break;\n            case IN_QUOTE:\n                if (*p == '\"') {\n                    state = TEXT;\n                    break;\n                }\n                [[fallthrough]];\n            case TEXT:\n                if (state != IN_QUOTE && isspace(*p)) {\n                    if (q != p) {\n                        const char* start = q;\n                        size_t len = p-q;\n                        if (len > 2 && *start == '\"' && start[len - 1] == '\"') {\n                            start++;\n                            len -= 2;\n                        }\n                        out->push_back(string(start, len));\n                    }\n                    state = WHITE;\n                }\n                break;\n        }\n        p++;\n    }\n    if (state == TEXT) {\n        const char* start = q;\n        size_t len = p-q;\n        if (len > 2 && *start == '\"' && start[len - 1] == '\"') {\n            start++;\n            len -= 2;\n        }\n        out->push_back(string(start, len));\n    }\n}\n\nstatic void\nadd_file(vector<FileRecord>* files, const FileOpType fileOp,\n            const string& listFile, int listLine,\n            const string& sourceName, const string& outName)\n{\n    FileRecord rec;\n    rec.listFile = listFile;\n    rec.listLine = listLine;\n    rec.fileOp = fileOp;\n    rec.sourceName = sourceName;\n    rec.outName = outName;\n    files->push_back(rec);\n}\n\nstatic string\nreplace_variables(const string& input,\n                  const map<string, string>& variables,\n                  bool* error) {\n    if (variables.empty()) {\n        return input;\n    }\n\n    // Abort if the variable prefix is not found\n    if (input.find(\"${\") == string::npos) {\n        return input;\n    }\n\n    string result = input;\n\n    // Note: rather than be fancy to detect recursive replacements,\n    // we simply iterate till a given threshold is met.\n\n    int retries = 1000;\n    bool did_replace;\n\n    do {\n        did_replace = false;\n        for (map<string, string>::const_iterator it = variables.begin();\n             it != variables.end(); ++it) {\n            string::size_type pos = 0;\n            while((pos = result.find(it->first, pos)) != string::npos) {\n                result = result.replace(pos, it->first.length(), it->second);\n                pos += it->second.length();\n                did_replace = true;\n            }\n        }\n        if (did_replace && --retries == 0) {\n            *error = true;\n            fprintf(stderr, \"Recursive replacement detected during variables \"\n                    \"substitution. Full list of variables is: \");\n\n            for (map<string, string>::const_iterator it = variables.begin();\n                 it != variables.end(); ++it) {\n                fprintf(stderr, \"  %s=%s\\n\",\n                        it->first.c_str(), it->second.c_str());\n            }\n\n            return result;\n        }\n    } while (did_replace);\n\n    return result;\n}\n\nint\nread_list_file(const string& filename,\n               const map<string, string>& variables,\n               vector<FileRecord>* files,\n               vector<string>* excludes)\n{\n    int err = 0;\n    FILE* f = NULL;\n    long size;\n    char* buf = NULL;\n    char *p, *q;\n    int i, lineCount;\n\n    f = fopen(filename.c_str(), \"r\");\n    if (f == NULL) {\n        fprintf(stderr, \"Could not open list file (%s): %s\\n\",\n                    filename.c_str(), strerror(errno));\n        err = errno;\n        goto cleanup;\n    }\n\n    err = fseek(f, 0, SEEK_END);\n    if (err != 0) {\n        fprintf(stderr, \"Could not seek to the end of file %s. (%s)\\n\",\n                    filename.c_str(), strerror(errno));\n        err = errno;\n        goto cleanup;\n    }\n\n    size = ftell(f);\n\n    err = fseek(f, 0, SEEK_SET);\n    if (err != 0) {\n        fprintf(stderr, \"Could not seek to the beginning of file %s. (%s)\\n\",\n                    filename.c_str(), strerror(errno));\n        err = errno;\n        goto cleanup;\n    }\n\n    buf = (char*)malloc(size+1);\n    if (buf == NULL) {\n        // (potentially large)\n        fprintf(stderr, \"out of memory (%ld)\\n\", size);\n        err = ENOMEM;\n        goto cleanup;\n    }\n\n    if (1 != fread(buf, size, 1, f)) {\n        fprintf(stderr, \"error reading file %s. (%s)\\n\",\n                    filename.c_str(), strerror(errno));\n        err = errno;\n        goto cleanup;\n    }\n\n    // split on lines\n    p = buf;\n    q = buf+size;\n    lineCount = 0;\n    while (p<q) {\n        if (*p == '\\r' || *p == '\\n') {\n            *p = '\\0';\n            lineCount++;\n        }\n        p++;\n    }\n\n    // read lines\n    p = buf;\n    for (i=0; i<lineCount; i++) {\n        int len = strlen(p);\n        q = p + len + 1;\n        if (is_whitespace_line(p) || is_comment_line(p)) {\n            ;\n        }\n        else if (is_exclude_line(p)) {\n            while (*p != '-') p++;\n            p++;\n            excludes->push_back(string(p));\n        }\n        else {\n            vector<string> words;\n\n            split_line(p, &words);\n\n#if 0\n            printf(\"[ \");\n            for (size_t k=0; k<words.size(); k++) {\n                printf(\"'%s' \", words[k].c_str());\n            }\n            printf(\"]\\n\");\n#endif\n            FileOpType op = FILE_OP_COPY;\n            string paths[2];\n            int pcount = 0;\n            string errstr;\n            for (vector<string>::iterator it = words.begin(); it != words.end(); ++it) {\n                const string& word = *it;\n                if (word == \"rm\") {\n                    if (op != FILE_OP_COPY) {\n                        errstr = \"Error: you can only specifiy 'rm' or 'strip' once per line.\";\n                        break;\n                    }\n                    op = FILE_OP_REMOVE;\n                } else if (word == \"strip\") {\n                    if (op != FILE_OP_COPY) {\n                        errstr = \"Error: you can only specifiy 'rm' or 'strip' once per line.\";\n                        break;\n                    }\n                    op = FILE_OP_STRIP;\n                } else if (pcount < 2) {\n                    bool error = false;\n                    paths[pcount++] = replace_variables(word, variables, &error);\n                    if (error) {\n                        err = 1;\n                        goto cleanup;\n                    }\n                } else {\n                    errstr = \"Error: More than 2 paths per line.\";\n                    break;\n                }\n            }\n\n            if (pcount == 0 && !errstr.empty()) {\n                errstr = \"Error: No path found on line.\";\n            }\n\n            if (!errstr.empty()) {\n                fprintf(stderr, \"%s:%d: bad format: %s\\n%s\\nExpected: [SRC] [rm|strip] DEST\\n\",\n                        filename.c_str(), i+1, p, errstr.c_str());\n                err = 1;\n            } else {\n                if (pcount == 1) {\n                    // pattern: [rm|strip] DEST\n                    paths[1] = paths[0];\n                }\n\n                add_file(files, op, filename, i+1, paths[0], paths[1]);\n            }\n        }\n        p = q;\n    }\n\ncleanup:\n    if (buf != NULL) {\n        free(buf);\n    }\n    if (f != NULL) {\n        fclose(f);\n    }\n    return err;\n}\n\n\nint\nlocate(FileRecord* rec, const vector<string>& search)\n{\n    if (rec->fileOp == FILE_OP_REMOVE) {\n        // Don't touch source files when removing a destination.\n        rec->sourceMod = 0;\n        rec->sourceSize = 0;\n        rec->sourceIsDir = false;\n        return 0;\n    }\n\n    int err;\n\n    for (vector<string>::const_iterator it=search.begin();\n                it!=search.end(); it++) {\n        string full = path_append(*it, rec->sourceName);\n        struct stat st;\n        err = stat(full.c_str(), &st);\n        if (err == 0) {\n            rec->sourceBase = *it;\n            rec->sourcePath = full;\n            rec->sourceMod = st.st_mtime;\n            rec->sourceSize = st.st_size;\n            rec->sourceIsDir = S_ISDIR(st.st_mode);\n            return 0;\n        }\n    }\n\n    fprintf(stderr, \"%s:%d: couldn't locate source file: %s\\n\",\n                rec->listFile.c_str(), rec->listLine, rec->sourceName.c_str());\n    return 1;\n}\n\nvoid\nstat_out(const string& base, FileRecord* rec)\n{\n    rec->outPath = path_append(base, rec->outName);\n\n    int err;\n    struct stat st;\n    err = stat(rec->outPath.c_str(), &st);\n    if (err == 0) {\n        rec->outMod = st.st_mtime;\n        rec->outSize = st.st_size;\n        rec->outIsDir = S_ISDIR(st.st_mode);\n    } else {\n        rec->outMod = 0;\n        rec->outSize = 0;\n        rec->outIsDir = false;\n    }\n}\n\nstring\ndir_part(const string& filename)\n{\n    int pos = filename.rfind('/');\n    if (pos <= 0) {\n        return \".\";\n    }\n    return filename.substr(0, pos);\n}\n\nstatic void\nadd_more(const string& entry, bool isDir,\n         const FileRecord& rec, vector<FileRecord>*more)\n{\n    FileRecord r;\n    r.listFile = rec.listFile;\n    r.listLine = rec.listLine;\n    r.sourceName = path_append(rec.sourceName, entry);\n    r.sourcePath = path_append(rec.sourceBase, r.sourceName);\n    struct stat st;\n    int err = stat(r.sourcePath.c_str(), &st);\n    if (err == 0) {\n        r.sourceMod = st.st_mtime;\n    }\n    r.sourceIsDir = isDir;\n    r.outName = path_append(rec.outName, entry);\n    more->push_back(r);\n}\n\nstatic bool\nmatches_excludes(const char* file, const vector<string>& excludes)\n{\n    for (vector<string>::const_iterator it=excludes.begin();\n            it!=excludes.end(); it++) {\n        if (0 == fnmatch(it->c_str(), file, FNM_PERIOD)) {\n            return true;\n        }\n    }\n    return false;\n}\n\nstatic int\nlist_dir(const string& path, const FileRecord& rec,\n                const vector<string>& excludes,\n                vector<FileRecord>* more)\n{\n    string full = path_append(rec.sourceBase, rec.sourceName);\n    full = path_append(full, path);\n\n    DIR *d = opendir(full.c_str());\n    if (d == NULL) {\n        return errno;\n    }\n\n    vector<string> dirs;\n\n    struct dirent *ent;\n    while (NULL != (ent = readdir(d))) {\n        if (0 == strcmp(\".\", ent->d_name)\n                || 0 == strcmp(\"..\", ent->d_name)) {\n            continue;\n        }\n        if (matches_excludes(ent->d_name, excludes)) {\n            continue;\n        }\n        string entry = path_append(path, ent->d_name);\n        bool is_directory = (ent->d_type == DT_DIR);\n        add_more(entry, is_directory, rec, more);\n        if (is_directory) {\n            dirs.push_back(entry);\n        }\n    }\n    closedir(d);\n\n    for (vector<string>::iterator it=dirs.begin(); it!=dirs.end(); it++) {\n        list_dir(*it, rec, excludes, more);\n    }\n\n    return 0;\n}\n\nint\nlist_dir(const FileRecord& rec, const vector<string>& excludes,\n            vector<FileRecord>* files)\n{\n    return list_dir(\"\", rec, excludes, files);\n}\n\nFileRecord::FileRecord() {\n    fileOp = FILE_OP_COPY;\n}\n\n"
  },
  {
    "path": "tools/atree/files.h",
    "content": "#ifndef FILES_H\n#define FILES_H\n\n#include <map>\n#include <string>\n#include <vector>\n#include <sys/types.h>\n\nusing namespace std;\n\nenum FileOpType {\n    FILE_OP_COPY = 0,\n    FILE_OP_REMOVE,\n    FILE_OP_STRIP\n};\n\nstruct FileRecord\n{\n    FileRecord();\n\n    string listFile;\n    int listLine;\n\n    string sourceBase;\n    string sourceName;\n    string sourcePath;\n    bool sourceIsDir;\n    time_t sourceMod;\n    off_t  sourceSize;\n    FileOpType fileOp;\n\n    string outName;\n    string outPath;\n    off_t  outSize;\n    time_t outMod;\n    bool outIsDir;\n    unsigned int mode;\n};\n\nint read_list_file(const string& filename,\n                   const map<string, string>& variables,\n                   vector<FileRecord>* files,\n                   vector<string>* excludes);\nint locate(FileRecord* rec, const vector<string>& search);\nvoid stat_out(const string& base, FileRecord* rec);\nstring dir_part(const string& filename);\nint list_dir(const FileRecord& rec, const vector<string>& excludes,\n                    vector<FileRecord>* files);\n\n#endif // FILES_H\n"
  },
  {
    "path": "tools/atree/fs.cpp",
    "content": "#include \"fs.h\"\n#include \"files.h\"\n#include <unistd.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <dirent.h>\n#include <string>\n#include <vector>\n#include <stdio.h>\n#include <string.h>\n#include <errno.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <string.h>\n#include <host/CopyFile.h>\n\nusing namespace std;\n\nstatic bool\nis_dir(const string& path)\n{\n    int err;\n    struct stat st;\n    err = stat(path.c_str(), &st);\n    return err != 0 || S_ISDIR(st.st_mode);\n}\n\nstatic int\nremove_file(const string& path)\n{\n    int err = unlink(path.c_str());\n    if (err != 0) {\n        fprintf(stderr, \"error deleting file %s (%s)\\n\", path.c_str(),\n                strerror(errno));\n        return errno;\n    }\n    return 0;\n}\n\nint\nremove_recursively(const string& path)\n{\n    int err;\n\n    if (is_dir(path)) {\n        DIR *d = opendir(path.c_str());\n        if (d == NULL) {\n            fprintf(stderr, \"error getting directory contents %s (%s)\\n\",\n                    path.c_str(), strerror(errno));\n            return errno;\n        }\n\n        vector<string> files;\n        vector<string> dirs;\n\n        struct dirent *ent;\n        while (NULL != (ent = readdir(d))) {\n            if (0 == strcmp(\".\", ent->d_name)\n                    || 0 == strcmp(\"..\", ent->d_name)) {\n                continue;\n            }\n            string full = path;\n            full += '/';\n            full += ent->d_name;\n            bool is_directory = (ent->d_type == DT_DIR);\n            if (is_directory) {\n                dirs.push_back(full);\n            } else {\n                files.push_back(full);\n            }\n        }\n        closedir(d);\n\n        for (vector<string>::iterator it=files.begin(); it!=files.end(); it++) {\n            err = remove_file(*it);\n            if (err != 0) {\n                return err;\n            }\n        }\n\n        for (vector<string>::iterator it=dirs.begin(); it!=dirs.end(); it++) {\n            err = remove_recursively(*it);\n            if (err != 0) {\n                return err;\n            }\n        }\n\n        err = rmdir(path.c_str());\n        if (err != 0) {\n            fprintf(stderr, \"error deleting directory %s (%s)\\n\", path.c_str(),\n                    strerror(errno));\n            return errno;\n        }\n        return 0;\n    } else {\n        return remove_file(path);\n    }\n}\n\nint\nmkdir_recursively(const string& path)\n{\n    int err;\n    size_t pos = 0;\n    // For absolute pathnames, that starts with leading '/'\n    // use appropriate initial value.\n    if (path.length() != 0 and path[0] == '/') pos++;\n\n    while (true) {\n        pos = path.find('/', pos);\n        string p = path.substr(0, pos);\n        struct stat st;\n        err = stat(p.c_str(), &st);\n        if (err != 0) {\n            err = mkdir(p.c_str(), 0770);\n            if (err != 0) {\n                fprintf(stderr, \"can't create directory %s (%s)\\n\",\n                        path.c_str(), strerror(errno));\n                return errno;\n            }\n        }\n        else if (!S_ISDIR(st.st_mode)) {\n            fprintf(stderr, \"can't create directory %s because %s is a file.\\n\",\n                        path.c_str(), p.c_str());\n            return 1;\n        }\n        pos++;\n        if (p == path) {\n            return 0;\n        }\n    }\n}\n\nint\ncopy_file(const string& src, const string& dst)\n{\n    int err;\n\n    err = copyFile(src.c_str(), dst.c_str(),\n                    COPY_NO_DEREFERENCE | COPY_FORCE | COPY_PERMISSIONS);\n    return err;\n}\n\nint\nstrip_file(const string& path)\n{\n    // Default strip command to run is \"strip\" unless overridden by the ATREE_STRIP env var.\n    const char* strip_cmd = getenv(\"ATREE_STRIP\");\n    if (!strip_cmd || !strip_cmd[0]) {\n        strip_cmd = \"strip\";\n    }\n    pid_t pid = fork();\n    if (pid == -1) {\n        // Fork failed. errno should be set.\n        return -1;\n    } else if (pid == 0) {\n        // Exec in the child. Only returns if execve failed.\n\n        int num_args = 0;\n        const char *s = strip_cmd;\n        while (*s) {\n            while (*s == ' ') ++s;\n            if (*s && *s != ' ') {\n                ++num_args;\n                while (*s && *s != ' ') ++s;\n            }\n        }\n\n        if (num_args <= 0) {\n            fprintf(stderr, \"Invalid ATREE_STRIP command '%s'\\n\", strip_cmd);\n            return 1;\n\n        } else if (num_args == 1) {\n            return execlp(strip_cmd, strip_cmd, path.c_str(), (char *)NULL);\n\n        } else {\n            // Split the arguments if more than 1\n            char* cmd = strdup(strip_cmd);\n            const char** args = (const char**) calloc((num_args + 2), sizeof(const char*));\n\n            const char** curr = args;\n            char* s = cmd;\n            while (*s) {\n                while (*s == ' ') ++s;\n                if (*s && *s != ' ') {\n                    *curr = s;\n                    ++curr;\n                    while (*s && *s != ' ') ++s;\n                    if (*s) {\n                        *s = '\\0';\n                        ++s;\n                    }\n                }\n            }\n\n            args[num_args] = path.c_str();\n            args[num_args + 1] = NULL;\n\n            int ret = execvp(args[0], (char* const*)args);\n            free(args);\n            free(cmd);\n            return ret;\n        }\n    } else {\n        // Wait for child pid and return its exit code.\n        int status;\n        waitpid(pid, &status, 0);\n        return status;\n    }\n}\n\n"
  },
  {
    "path": "tools/atree/fs.h",
    "content": "#ifndef FS_H\n#define FS_H\n\n#include <string>\n\nusing namespace std;\n\nint remove_recursively(const string& path);\nint mkdir_recursively(const string& path);\nint copy_file(const string& src, const string& dst);\nint strip_file(const string& path);\n\n#endif // FS_H\n"
  },
  {
    "path": "tools/atree/options.h",
    "content": "#ifndef OPTIONS_H\n#define OPTIONS_H\n\n#include <string>\n#include <vector>\n\nusing namespace std;\n\nextern vector<string> g_listFiles;\nextern vector<string> g_inputBases;\nextern string g_outputBase;\nextern bool g_useHardLinks;\n\n#endif // OPTIONS_H\n"
  },
  {
    "path": "tools/auto_gen_test_config.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"A tool to generate TradeFed test config file.\n\"\"\"\n\nimport argparse\nimport re\nimport os\nimport shutil\nimport sys\nfrom xml.dom.minidom import parse\n\nATTRIBUTE_LABEL = 'android:label'\nATTRIBUTE_RUNNER = 'android:name'\nATTRIBUTE_PACKAGE = 'package'\n\nPLACEHOLDER_LABEL = '{LABEL}'\nPLACEHOLDER_EXTRA_CONFIGS = '{EXTRA_CONFIGS}'\nPLACEHOLDER_MODULE = '{MODULE}'\nPLACEHOLDER_PACKAGE = '{PACKAGE}'\nPLACEHOLDER_RUNNER = '{RUNNER}'\nPLACEHOLDER_TEST_TYPE = '{TEST_TYPE}'\nPLACEHOLDER_EXTRA_TEST_RUNNER_CONFIGS = '{EXTRA_TEST_RUNNER_CONFIGS}'\n\n\ndef main(argv):\n  \"\"\"Entry point of auto_gen_test_config.\n\n  Args:\n    argv: A list of arguments.\n  Returns:\n    0 if no error, otherwise 1.\n  \"\"\"\n\n  parser = argparse.ArgumentParser()\n  parser.add_argument(\n      \"target_config\",\n      help=\"Path to the generated output config.\")\n  parser.add_argument(\n      \"android_manifest\",\n      help=\"Path to AndroidManifest.xml or output of 'aapt2 dump xmltree' with .xmltree extension.\")\n  parser.add_argument(\n      \"empty_config\",\n      help=\"Path to the empty config template.\")\n  parser.add_argument(\n      \"instrumentation_test_config_template\",\n      help=\"Path to the instrumentation test config template.\")\n  parser.add_argument(\"--extra-configs\", default=\"\")\n  parser.add_argument(\"--extra-test-runner-configs\", default=\"\")\n  args = parser.parse_args(argv)\n\n  target_config = args.target_config\n  android_manifest = args.android_manifest\n  empty_config = args.empty_config\n  instrumentation_test_config_template = args.instrumentation_test_config_template\n  extra_configs = '\\n'.join(args.extra_configs.split('\\\\n'))\n  extra_test_runner_configs = '\\n'.join(args.extra_test_runner_configs.split('\\\\n'))\n\n  module = os.path.splitext(os.path.basename(target_config))[0]\n\n  # If the AndroidManifest.xml is not available, but the APK is, this tool also\n  # accepts the output of `aapt2 dump xmltree <apk> AndroidManifest.xml` written\n  # into a file. This is a custom structured aapt2 output - not raw XML!\n  if android_manifest.endswith(\".xmltree\"):\n    label = module\n    with open(android_manifest, encoding=\"utf-8\") as manifest:\n      # e.g. A: package=\"android.test.example.helloworld\" (Raw: \"android.test.example.helloworld\")\n      #                                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n      pattern = re.compile(r\"\\(Raw:\\s\\\"(.*)\\\"\\)$\")\n      curr_element = None\n      for line in manifest:\n        curr_line = line.strip()\n        if curr_line.startswith(\"E:\"):\n          # e.g. \"E: instrumentation (line=9)\"\n          #          ^^^^^^^^^^^^^^^\n          curr_element = curr_line.split(\" \")[1]\n        if curr_element == \"instrumentation\":\n          if ATTRIBUTE_RUNNER in curr_line:\n            runner =  re.findall(pattern, curr_line)[0]\n          if ATTRIBUTE_LABEL in curr_line:\n            label = re.findall(pattern, curr_line)[0]\n        if curr_element == \"manifest\":\n          if ATTRIBUTE_PACKAGE in curr_line:\n            package = re.findall(pattern, curr_line)[0]\n\n    if not (runner and label and package):\n      # Failed to locate instrumentation or manifest element in AndroidManifest.\n      # file. Empty test config file will be created.\n      shutil.copyfile(empty_config, target_config)\n      return 0\n\n  else:\n    # If the AndroidManifest.xml file is directly available, read it as an XML file.\n    manifest = parse(android_manifest)\n    instrumentation_elements = manifest.getElementsByTagName('instrumentation')\n    manifest_elements = manifest.getElementsByTagName('manifest')\n    if len(instrumentation_elements) != 1 or len(manifest_elements) != 1:\n      # Failed to locate instrumentation or manifest element in AndroidManifest.\n      # file. Empty test config file will be created.\n      shutil.copyfile(empty_config, target_config)\n      return 0\n\n    instrumentation = instrumentation_elements[0]\n    manifest = manifest_elements[0]\n    if ATTRIBUTE_LABEL in instrumentation.attributes:\n      label = instrumentation.attributes[ATTRIBUTE_LABEL].value\n    else:\n      label = module\n    runner = instrumentation.attributes[ATTRIBUTE_RUNNER].value\n    package = manifest.attributes[ATTRIBUTE_PACKAGE].value\n\n  test_type = ('InstrumentationTest'\n              if runner.endswith('.InstrumentationTestRunner')\n              else 'AndroidJUnitTest')\n\n  with open(instrumentation_test_config_template) as template:\n    config = template.read()\n    config = config.replace(PLACEHOLDER_LABEL, label)\n    config = config.replace(PLACEHOLDER_MODULE, module)\n    config = config.replace(PLACEHOLDER_PACKAGE, package)\n    config = config.replace(PLACEHOLDER_TEST_TYPE, test_type)\n    config = config.replace(PLACEHOLDER_EXTRA_CONFIGS, extra_configs)\n    config = config.replace(PLACEHOLDER_EXTRA_TEST_RUNNER_CONFIGS, extra_test_runner_configs)\n    config = config.replace(PLACEHOLDER_RUNNER, runner)\n    with open(target_config, 'w') as config_file:\n      config_file.write(config)\n  return 0\n\nif __name__ == '__main__':\n  sys.exit(main(sys.argv[1:]))\n"
  },
  {
    "path": "tools/auto_gen_test_config_test.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright 2017, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Unittests for auto_gen_test_config.\"\"\"\n\nimport os\nimport shutil\nimport tempfile\nimport unittest\n\nimport auto_gen_test_config\n\nTEST_MODULE = 'TestModule'\n\nMANIFEST_INVALID = \"\"\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n</manifest>\n\"\"\"\n\nXMLTREE_JUNIT_TEST = \"\"\"N: android=http://schemas.android.com/apk/res/android (line=2)\n  E: manifest (line=2)\n    A: package=\"com.android.my.tests.x\" (Raw: \"com.android.my.tests.x\")\n      E: instrumentation (line=9)\n        A: http://schemas.android.com/apk/res/android:label(0x01010001)=\"TestModule\" (Raw: \"TestModule\")\n        A: http://schemas.android.com/apk/res/android:name(0x01010003)=\"androidx.test.runner.AndroidJUnitRunner\" (Raw: \"androidx.test.runner.AndroidJUnitRunner\")\n        A: http://schemas.android.com/apk/res/android:targetPackage(0x01010021)=\"com.android.my.tests\" (Raw: \"com.android.my.tests\")\n\"\"\"\n\nXMLTREE_INSTRUMENTATION_TEST = \"\"\"N: android=http://schemas.android.com/apk/res/android (line=2)\n  E: manifest (line=2)\n    A: package=\"com.android.my.tests.x\" (Raw: \"com.android.my.tests.x\")\n      E: instrumentation (line=9)\n        A: http://schemas.android.com/apk/res/android:label(0x01010001)=\"TestModule\" (Raw: \"TestModule\")\n        A: http://schemas.android.com/apk/res/android:name(0x01010003)=\"android.test.InstrumentationTestRunner\" (Raw: \"android.test.InstrumentationTestRunner\")\n        A: http://schemas.android.com/apk/res/android:targetPackage(0x01010021)=\"com.android.my.tests\" (Raw: \"com.android.my.tests\")\n\"\"\"\n\nMANIFEST_JUNIT_TEST = \"\"\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  package=\"com.android.my.tests.x\">\n    <instrumentation\n        android:name=\"androidx.test.runner.AndroidJUnitRunner\"\n        android:targetPackage=\"com.android.my.tests\" />\n</manifest>\n\"\"\"\n\nMANIFEST_INSTRUMENTATION_TEST = \"\"\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  package=\"com.android.my.tests.x\">\n    <instrumentation\n        android:name=\"android.test.InstrumentationTestRunner\"\n        android:targetPackage=\"com.android.my.tests\"\n        android:label=\"TestModule\" />\n</manifest>\n\"\"\"\n\nEXPECTED_JUNIT_TEST_CONFIG = \"\"\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2023 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs TestModule.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-instrumentation\" />\n\n    <target_preparer class=\"com.android.tradefed.targetprep.suite.SuiteApkInstaller\">\n        <option name=\"cleanup-apks\" value=\"true\" />\n        <option name=\"test-file-name\" value=\"TestModule.apk\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.AndroidJUnitTest\" >\n        <option name=\"package\" value=\"com.android.my.tests.x\" />\n        <option name=\"runner\" value=\"androidx.test.runner.AndroidJUnitRunner\" />\n    </test>\n</configuration>\n\"\"\"\n\nEXPECTED_INSTRUMENTATION_TEST_CONFIG = \"\"\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2023 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs TestModule.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-instrumentation\" />\n\n    <target_preparer class=\"com.android.tradefed.targetprep.suite.SuiteApkInstaller\">\n        <option name=\"cleanup-apks\" value=\"true\" />\n        <option name=\"test-file-name\" value=\"TestModule.apk\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.InstrumentationTest\" >\n        <option name=\"package\" value=\"com.android.my.tests.x\" />\n        <option name=\"runner\" value=\"android.test.InstrumentationTestRunner\" />\n    </test>\n</configuration>\n\"\"\"\n\nEMPTY_TEST_CONFIG_CONTENT = \"\"\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2017 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- No AndroidTest.xml was provided and the manifest does not include\n     instrumentation, hence this apk is not instrumentable.\n-->\n<configuration description=\"Empty Configuration\" />\n\"\"\"\n\nINSTRUMENTATION_TEST_CONFIG_TEMPLATE_CONTENT = \"\"\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2023 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<!-- This test config file is auto-generated. -->\n<configuration description=\"Runs {LABEL}.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-instrumentation\" />\n{EXTRA_CONFIGS}\n    <target_preparer class=\"com.android.tradefed.targetprep.suite.SuiteApkInstaller\">\n        <option name=\"cleanup-apks\" value=\"true\" />\n        <option name=\"test-file-name\" value=\"{MODULE}.apk\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.{TEST_TYPE}\" >\n        <option name=\"package\" value=\"{PACKAGE}\" />\n        <option name=\"runner\" value=\"{RUNNER}\" />\n    </test>\n</configuration>\n\"\"\"\n\n\nclass AutoGenTestConfigUnittests(unittest.TestCase):\n  \"\"\"Unittests for auto_gen_test_config.\"\"\"\n\n  def setUp(self):\n    \"\"\"Setup directory for test.\"\"\"\n    self.test_dir = tempfile.mkdtemp()\n    self.config_file = os.path.join(self.test_dir, TEST_MODULE + '.config')\n    self.manifest_file = os.path.join(self.test_dir, 'AndroidManifest.xml')\n    self.xmltree_file = os.path.join(self.test_dir, TEST_MODULE + '.xmltree')\n    self.empty_test_config_file = os.path.join(self.test_dir, 'empty.config')\n    self.instrumentation_test_config_template_file = os.path.join(\n        self.test_dir, 'instrumentation.config')\n\n    with open(self.empty_test_config_file, 'w') as f:\n      f.write(EMPTY_TEST_CONFIG_CONTENT)\n\n    with open(self.instrumentation_test_config_template_file, 'w') as f:\n      f.write(INSTRUMENTATION_TEST_CONFIG_TEMPLATE_CONTENT)\n\n  def tearDown(self):\n    \"\"\"Cleanup the test directory.\"\"\"\n    shutil.rmtree(self.test_dir, ignore_errors=True)\n\n  def testInvalidManifest(self):\n    \"\"\"An empty test config should be generated if AndroidManifest is invalid.\n    \"\"\"\n    with open(self.manifest_file, 'w') as f:\n      f.write(MANIFEST_INVALID)\n\n    argv = [self.config_file,\n            self.manifest_file,\n            self.empty_test_config_file,\n            self.instrumentation_test_config_template_file]\n    auto_gen_test_config.main(argv)\n    with open(self.config_file) as config_file:\n      with open(self.empty_test_config_file) as empty_config:\n        self.assertEqual(config_file.read(), empty_config.read())\n\n  def testCreateJUnitTestConfig(self):\n    \"\"\"Test creating test config for AndroidJUnitTest.\n    \"\"\"\n    with open(self.manifest_file, 'w') as f:\n      f.write(MANIFEST_JUNIT_TEST)\n\n    argv = [self.config_file,\n            self.manifest_file,\n            self.empty_test_config_file,\n            self.instrumentation_test_config_template_file]\n    auto_gen_test_config.main(argv)\n    with open(self.config_file) as config_file:\n      self.assertEqual(config_file.read(), EXPECTED_JUNIT_TEST_CONFIG)\n\n  def testCreateInstrumentationTestConfig(self):\n    \"\"\"Test creating test config for InstrumentationTest.\n    \"\"\"\n    with open(self.manifest_file, 'w') as f:\n      f.write(MANIFEST_INSTRUMENTATION_TEST)\n\n    argv = [self.config_file,\n            self.manifest_file,\n            self.empty_test_config_file,\n            self.instrumentation_test_config_template_file]\n    auto_gen_test_config.main(argv)\n    with open(self.config_file) as config_file:\n      self.assertEqual(\n          config_file.read(), EXPECTED_INSTRUMENTATION_TEST_CONFIG)\n\n  def testCreateJUnitTestConfigWithXMLTree(self):\n    \"\"\"Test creating test config for AndroidJUnitTest.\n    \"\"\"\n    with open(self.xmltree_file, 'w') as f:\n      f.write(XMLTREE_JUNIT_TEST)\n\n    argv = [self.config_file,\n            self.xmltree_file,\n            self.empty_test_config_file,\n            self.instrumentation_test_config_template_file]\n    auto_gen_test_config.main(argv)\n    with open(self.config_file) as config_file:\n      self.assertEqual(config_file.read(), EXPECTED_JUNIT_TEST_CONFIG)\n\n  def testCreateInstrumentationTestConfigWithXMLTree(self):\n    \"\"\"Test creating test config for InstrumentationTest.\n    \"\"\"\n    with open(self.xmltree_file, 'w') as f:\n      f.write(XMLTREE_INSTRUMENTATION_TEST)\n\n    argv = [self.config_file,\n            self.xmltree_file,\n            self.empty_test_config_file,\n            self.instrumentation_test_config_template_file]\n    auto_gen_test_config.main(argv)\n    with open(self.config_file) as config_file:\n      self.assertEqual(\n          config_file.read(), EXPECTED_INSTRUMENTATION_TEST_CONFIG)\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "tools/brillo-clang-format",
    "content": "#\n# Copyright (C) 2016 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n### DO NOT COPY THIS FILE TO YOUR PROJECT. ###\n\n#\n# This is the .clang-format file used by all Brillo projects, conforming to the\n# style guide defined by Brillo. To use this file create a *relative* symlink in\n# your project pointing to this file, as this repository is expected to be\n# present in all manifests.\n#\n# See go/brillo-c++-style for details about the style guide.\n#\n\nBasedOnStyle: Google\nAllowShortFunctionsOnASingleLine: Inline\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nBinPackArguments: false\nBinPackParameters: false\nCommentPragmas: NOLINT:.*\nDerivePointerAlignment: false\nPointerAlignment: Left\nTabWidth: 2\n"
  },
  {
    "path": "tools/build-runfiles.cc",
    "content": "// Copyright 2014 The Bazel Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n// This program creates a \"runfiles tree\" from a \"runfiles manifest\".\n//\n// The command line arguments are an input manifest INPUT and an output\n// directory RUNFILES. First, the files in the RUNFILES directory are scanned\n// and any extraneous ones are removed. Second, any missing files are created.\n// Finally, a copy of the input manifest is written to RUNFILES/MANIFEST.\n//\n// The input manifest consists of lines, each containing a relative path within\n// the runfiles, a space, and an optional absolute path.  If this second path\n// is present, a symlink is created pointing to it; otherwise an empty file is\n// created.\n//\n// Given the line\n//   <workspace root>/output/path /real/path\n// we will create directories\n//   RUNFILES/<workspace root>\n//   RUNFILES/<workspace root>/output\n// a symlink\n//   RUNFILES/<workspace root>/output/path -> /real/path\n// and the output manifest will contain a line\n//   <workspace root>/output/path /real/path\n//\n// If --use_metadata is supplied, every other line is treated as opaque\n// metadata, and is ignored here.\n//\n// All output paths must be relative and generally (but not always) begin with\n// <workspace root>. No output path may be equal to another.  No output path may\n// be a path prefix of another.\n\n#define _FILE_OFFSET_BITS 64\n\n#include <dirent.h>\n#include <err.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <map>\n#include <string>\n\n// program_invocation_short_name is not portable.\nstatic const char *argv0;\n\nconst char *input_filename;\nconst char *output_base_dir;\n\nenum FileType {\n  FILE_TYPE_REGULAR,\n  FILE_TYPE_DIRECTORY,\n  FILE_TYPE_SYMLINK\n};\n\nstruct FileInfo {\n  FileType type;\n  std::string symlink_target;\n\n  bool operator==(const FileInfo &other) const {\n    return type == other.type && symlink_target == other.symlink_target;\n  }\n\n  bool operator!=(const FileInfo &other) const {\n    return !(*this == other);\n  }\n};\n\ntypedef std::map<std::string, FileInfo> FileInfoMap;\n\nclass RunfilesCreator {\n public:\n  explicit RunfilesCreator(const std::string &output_base)\n      : output_base_(output_base),\n        output_filename_(\"MANIFEST\"),\n        temp_filename_(output_filename_ + \".tmp\") {\n    SetupOutputBase();\n    if (chdir(output_base_.c_str()) != 0) {\n      err(2, \"chdir '%s'\", output_base_.c_str());\n    }\n  }\n\n  void ReadManifest(const std::string &manifest_file, bool allow_relative,\n                    bool use_metadata) {\n    FILE *outfile = fopen(temp_filename_.c_str(), \"w\");\n    if (!outfile) {\n      err(2, \"opening '%s/%s' for writing\", output_base_.c_str(),\n           temp_filename_.c_str());\n    }\n    FILE *infile = fopen(manifest_file.c_str(), \"r\");\n    if (!infile) {\n      err(2, \"opening '%s' for reading\", manifest_file.c_str());\n    }\n\n    // read input manifest\n    int lineno = 0;\n    char buf[3 * PATH_MAX];\n    while (fgets(buf, sizeof buf, infile)) {\n      // copy line to output manifest\n      if (fputs(buf, outfile) == EOF) {\n        err(2, \"writing to '%s/%s'\", output_base_.c_str(),\n             temp_filename_.c_str());\n      }\n\n      // parse line\n      ++lineno;\n      // Skip metadata lines. They are used solely for\n      // dependency checking.\n      if (use_metadata && lineno % 2 == 0) continue;\n\n      char *tok = strtok(buf, \" \\n\");\n      if (tok == nullptr) {\n        continue;\n      } else if (*tok == '/') {\n        errx(2, \"%s:%d: paths must not be absolute\", input_filename, lineno);\n      }\n      std::string link(tok);\n\n      const char *target = strtok(nullptr, \" \\n\");\n      if (target == nullptr) {\n        target = \"\";\n      } else if (strtok(nullptr, \" \\n\") != nullptr) {\n        errx(2, \"%s:%d: link or target filename contains space\", input_filename, lineno);\n      } else if (!allow_relative && target[0] != '/') {\n        errx(2, \"%s:%d: expected absolute path\", input_filename, lineno);\n      }\n\n      FileInfo *info = &manifest_[link];\n      if (target[0] == '\\0') {\n        // No target means an empty file.\n        info->type = FILE_TYPE_REGULAR;\n      } else {\n        info->type = FILE_TYPE_SYMLINK;\n        info->symlink_target = target;\n      }\n\n      FileInfo parent_info;\n      parent_info.type = FILE_TYPE_DIRECTORY;\n\n      while (true) {\n        int k = link.rfind('/');\n        if (k < 0) break;\n        link.erase(k, std::string::npos);\n        if (!manifest_.insert(std::make_pair(link, parent_info)).second) break;\n      }\n    }\n    if (fclose(outfile) != 0) {\n      err(2, \"writing to '%s/%s'\", output_base_.c_str(),\n           temp_filename_.c_str());\n    }\n    fclose(infile);\n\n    // Don't delete the temp manifest file.\n    manifest_[temp_filename_].type = FILE_TYPE_REGULAR;\n  }\n\n  void CreateRunfiles() {\n    if (unlink(output_filename_.c_str()) != 0 && errno != ENOENT) {\n      err(2, \"removing previous file at '%s/%s'\", output_base_.c_str(),\n           output_filename_.c_str());\n    }\n\n    ScanTreeAndPrune(\".\");\n    CreateFiles();\n\n    // rename output file into place\n    if (rename(temp_filename_.c_str(), output_filename_.c_str()) != 0) {\n      err(2, \"renaming '%s/%s' to '%s/%s'\",\n           output_base_.c_str(), temp_filename_.c_str(),\n           output_base_.c_str(), output_filename_.c_str());\n    }\n  }\n\n private:\n  void SetupOutputBase() {\n    struct stat st;\n    if (stat(output_base_.c_str(), &st) != 0) {\n      // Technically, this will cause problems if the user's umask contains\n      // 0200, but we don't care. Anyone who does that deserves what's coming.\n      if (mkdir(output_base_.c_str(), 0777) != 0) {\n        err(2, \"creating directory '%s'\", output_base_.c_str());\n      }\n    } else {\n      EnsureDirReadAndWritePerms(output_base_);\n    }\n  }\n\n  void ScanTreeAndPrune(const std::string &path) {\n    // A note on non-empty files:\n    // We don't distinguish between empty and non-empty files. That is, if\n    // there's a file that has contents, we don't truncate it here, even though\n    // the manifest supports creation of empty files, only. Given that\n    // .runfiles are *supposed* to be immutable, this shouldn't be a problem.\n    EnsureDirReadAndWritePerms(path);\n\n    struct dirent *entry;\n    DIR *dh = opendir(path.c_str());\n    if (!dh) {\n      err(2, \"opendir '%s'\", path.c_str());\n    }\n\n    errno = 0;\n    const std::string prefix = (path == \".\" ? \"\" : path + \"/\");\n    while ((entry = readdir(dh)) != nullptr) {\n      if (!strcmp(entry->d_name, \".\") || !strcmp(entry->d_name, \"..\")) continue;\n\n      std::string entry_path = prefix + entry->d_name;\n      FileInfo actual_info;\n      actual_info.type = DentryToFileType(entry_path, entry);\n\n      if (actual_info.type == FILE_TYPE_SYMLINK) {\n        ReadLinkOrDie(entry_path, &actual_info.symlink_target);\n      }\n\n      FileInfoMap::iterator expected_it = manifest_.find(entry_path);\n      if (expected_it == manifest_.end() ||\n          expected_it->second != actual_info) {\n        DelTree(entry_path, actual_info.type);\n      } else {\n        manifest_.erase(expected_it);\n        if (actual_info.type == FILE_TYPE_DIRECTORY) {\n          ScanTreeAndPrune(entry_path);\n        }\n      }\n\n      errno = 0;\n    }\n    if (errno != 0) {\n      err(2, \"reading directory '%s'\", path.c_str());\n    }\n    closedir(dh);\n  }\n\n  void CreateFiles() {\n    for (FileInfoMap::const_iterator it = manifest_.begin();\n         it != manifest_.end(); ++it) {\n      const std::string &path = it->first;\n      switch (it->second.type) {\n        case FILE_TYPE_DIRECTORY:\n          if (mkdir(path.c_str(), 0777) != 0) {\n            err(2, \"mkdir '%s'\", path.c_str());\n          }\n          break;\n        case FILE_TYPE_REGULAR:\n          {\n            int fd = open(path.c_str(), O_CREAT|O_EXCL|O_WRONLY, 0555);\n            if (fd < 0) {\n              err(2, \"creating empty file '%s'\", path.c_str());\n            }\n            close(fd);\n          }\n          break;\n        case FILE_TYPE_SYMLINK:\n          {\n            const std::string& target = it->second.symlink_target;\n            if (symlink(target.c_str(), path.c_str()) != 0) {\n              err(2, \"symlinking '%s' -> '%s'\", path.c_str(), target.c_str());\n            }\n          }\n          break;\n      }\n    }\n  }\n\n  FileType DentryToFileType(const std::string &path, struct dirent *ent) {\n#ifdef _DIRENT_HAVE_D_TYPE\n    if (ent->d_type != DT_UNKNOWN) {\n      if (ent->d_type == DT_DIR) {\n        return FILE_TYPE_DIRECTORY;\n      } else if (ent->d_type == DT_LNK) {\n        return FILE_TYPE_SYMLINK;\n      } else {\n        return FILE_TYPE_REGULAR;\n      }\n    } else  // NOLINT (the brace is in the next line)\n#endif\n    {\n      struct stat st;\n      LStatOrDie(path, &st);\n      if (S_ISDIR(st.st_mode)) {\n        return FILE_TYPE_DIRECTORY;\n      } else if (S_ISLNK(st.st_mode)) {\n        return FILE_TYPE_SYMLINK;\n      } else {\n        return FILE_TYPE_REGULAR;\n      }\n    }\n  }\n\n  void LStatOrDie(const std::string &path, struct stat *st) {\n    if (lstat(path.c_str(), st) != 0) {\n      err(2, \"lstating file '%s'\", path.c_str());\n    }\n  }\n\n  void StatOrDie(const std::string &path, struct stat *st) {\n    if (stat(path.c_str(), st) != 0) {\n      err(2, \"stating file '%s'\", path.c_str());\n    }\n  }\n\n  void ReadLinkOrDie(const std::string &path, std::string *output) {\n    char readlink_buffer[PATH_MAX];\n    int sz = readlink(path.c_str(), readlink_buffer, sizeof(readlink_buffer));\n    if (sz < 0) {\n      err(2, \"reading symlink '%s'\", path.c_str());\n    }\n    // readlink returns a non-null terminated string.\n    std::string(readlink_buffer, sz).swap(*output);\n  }\n\n  void EnsureDirReadAndWritePerms(const std::string &path) {\n    const int kMode = 0700;\n    struct stat st;\n    LStatOrDie(path, &st);\n    if ((st.st_mode & kMode) != kMode) {\n      int new_mode = st.st_mode | kMode;\n      if (chmod(path.c_str(), new_mode) != 0) {\n        err(2, \"chmod '%s'\", path.c_str());\n      }\n    }\n  }\n\n  bool DelTree(const std::string &path, FileType file_type) {\n    if (file_type != FILE_TYPE_DIRECTORY) {\n      if (unlink(path.c_str()) != 0) {\n        err(2, \"unlinking '%s'\", path.c_str());\n        return false;\n      }\n      return true;\n    }\n\n    EnsureDirReadAndWritePerms(path);\n\n    struct dirent *entry;\n    DIR *dh = opendir(path.c_str());\n    if (!dh) {\n      err(2, \"opendir '%s'\", path.c_str());\n    }\n    errno = 0;\n    while ((entry = readdir(dh)) != nullptr) {\n      if (!strcmp(entry->d_name, \".\") || !strcmp(entry->d_name, \"..\")) continue;\n      const std::string entry_path = path + '/' + entry->d_name;\n      FileType entry_file_type = DentryToFileType(entry_path, entry);\n      DelTree(entry_path, entry_file_type);\n      errno = 0;\n    }\n    if (errno != 0) {\n      err(2, \"readdir '%s'\", path.c_str());\n    }\n    closedir(dh);\n    if (rmdir(path.c_str()) != 0) {\n      err(2, \"rmdir '%s'\", path.c_str());\n    }\n    return true;\n  }\n\n private:\n  std::string output_base_;\n  std::string output_filename_;\n  std::string temp_filename_;\n\n  FileInfoMap manifest_;\n};\n\nint main(int argc, char **argv) {\n  argv0 = argv[0];\n\n  argc--; argv++;\n  bool allow_relative = false;\n  bool use_metadata = false;\n\n  while (argc >= 1) {\n    if (strcmp(argv[0], \"--allow_relative\") == 0) {\n      allow_relative = true;\n      argc--; argv++;\n    } else if (strcmp(argv[0], \"--use_metadata\") == 0) {\n      use_metadata = true;\n      argc--; argv++;\n    } else {\n      break;\n    }\n  }\n\n  if (argc != 2) {\n    fprintf(stderr, \"usage: %s \"\n            \"[--allow_relative] [--use_metadata] \"\n            \"INPUT RUNFILES\\n\",\n            argv0);\n    return 1;\n  }\n\n  input_filename = argv[0];\n  output_base_dir = argv[1];\n\n  std::string manifest_file = input_filename;\n  if (input_filename[0] != '/') {\n    char cwd_buf[PATH_MAX];\n    if (getcwd(cwd_buf, sizeof(cwd_buf)) == nullptr) {\n      err(2, \"getcwd failed\");\n    }\n    manifest_file = std::string(cwd_buf) + '/' + manifest_file;\n  }\n\n  RunfilesCreator runfiles_creator(output_base_dir);\n  runfiles_creator.ReadManifest(manifest_file, allow_relative, use_metadata);\n  runfiles_creator.CreateRunfiles();\n\n  return 0;\n}\n"
  },
  {
    "path": "tools/canoninja/README.md",
    "content": "# Ninja File Canonicalizer\n\nSuppose we have a tool that generates a Ninja file from some other description (think Kati and makefiles), and during\nthe testing we discovered a regression. Furthermore, suppose that the generated Ninja file is large (think millions of\nlines). And, the new Ninja file has build statements and rules in a slightly different order. As the tool generates the\nrule names, the real differences in the output of the `diff` command are drowned in noise. Enter Canoninja.\n\nCanoninja renames each Ninja rule to the hash of its contents. After that, we can just sort the build statements, and a\nsimple `comm` command immediately reveal the essential difference between the files.\n\n## Example\n\nConsider the following makefile\n\n```makefile\nsecond :=\nfirst: foo\nfoo:\n\t@echo foo\nsecond: bar\nbar:\n\t@echo bar\n```\n\nDepending on Kati version converting it to Ninja file will yield either:\n\n```\n$ cat /tmp/1.ninja\n# Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb\n\npool local_pool\n depth = 72\n\nbuild _kati_always_build_: phony\n\nbuild first: phony foo\nrule rule0\n description = build $out\n command = /bin/sh -c \"echo foo\"\nbuild foo: rule0\nbuild second: phony bar\nrule rule1\n description = build $out\n command = /bin/sh -c \"echo bar\"\nbuild bar: rule1\n\ndefault first\n```\n\nor\n\n```\n$ cat 2.ninja\n# Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310\n\npool local_pool\n depth = 72\n\nbuild _kati_always_build_: phony\n\nbuild second: phony bar\nrule rule0\n description = build $out\n command = /bin/sh -c \"echo bar\"\nbuild bar: rule0\nbuild first: phony foo\nrule rule1\n description = build $out\n command = /bin/sh -c \"echo foo\"\nbuild foo: rule1\n\ndefault first\n```\n\nThis is a quirk in Kati, see https://github.com/google/kati/issues/238\n\nTrying to find out the difference between the targets even after sorting them isn't too helpful:\n\n```\ndiff <(grep '^build' /tmp/1.ninja|sort) <(grep '^build' /tmp/2.ninja | sort)\n1c1\n< build bar: rule1\n---\n> build bar: rule0\n3c3\n< build foo: rule0\n---\n> build foo: rule1\n```\n\nHowever, running these files through `canoninja` yields\n\n```\n$ canoninja /tmp/1.ninja\n# Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb\n\npool local_pool\n depth = 72\n\nbuild _kati_always_build_: phony\n\nbuild first: phony foo\nrule R2f9981d3c152fc255370dc67028244f7bed72a03\n description = build $out\n command = /bin/sh -c \"echo foo\"\nbuild foo: R2f9981d3c152fc255370dc67028244f7bed72a03\nbuild second: phony bar\nrule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c\n description = build $out\n command = /bin/sh -c \"echo bar\"\nbuild bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c\n\ndefault first\n```\n\nand\n\n```\n~/go/bin/canoninja /tmp/2.ninja\n# Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310\n\npool local_pool\n depth = 72\n\nbuild _kati_always_build_: phony\n\nbuild second: phony bar\nrule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c\n description = build $out\n command = /bin/sh -c \"echo bar\"\nbuild bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c\nbuild first: phony foo\nrule R2f9981d3c152fc255370dc67028244f7bed72a03\n description = build $out\n command = /bin/sh -c \"echo foo\"\nbuild foo: R2f9981d3c152fc255370dc67028244f7bed72a03\n\ndefault first\n```\n\nand when we extract only build statements and sort them, we see that both Ninja files define the same graph:\n\n```shell\n$ diff <(~/go/bin/canoninja /tmp/1.ninja | grep '^build' | sort) \\\n       <(~/go/bin/canoninja /tmp/2.ninja | grep '^build' | sort)\n```\n\n# Todo\n\n* Optionally output only the build statements, optionally sorted\n* Handle continuation lines correctly\n"
  },
  {
    "path": "tools/canoninja/canoninja.go",
    "content": "package canoninja\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha1\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n)\n\nvar (\n\trulePrefix  = []byte(\"rule \")\n\tbuildPrefix = []byte(\"build \")\n\tphonyRule   = []byte(\"phony\")\n)\n\nfunc Generate(path string, buffer []byte, sink io.Writer) error {\n\t// Break file into lines\n\tfrom := 0\n\tvar lines [][]byte\n\tfor from < len(buffer) {\n\t\tline := getLine(buffer[from:])\n\t\tlines = append(lines, line)\n\t\tfrom += len(line)\n\t}\n\n\t// FOr each rule, calculate and remember its digest\n\truleDigest := make(map[string]string)\n\tfor i := 0; i < len(lines); {\n\t\tif bytes.HasPrefix(lines[i], rulePrefix) {\n\t\t\t// Find ruleName\n\t\t\trn := ruleName(lines[i])\n\t\t\tif len(rn) == 0 {\n\t\t\t\treturn fmt.Errorf(\"%s:%d: rule name is missing or on the next line\", path, i+1)\n\t\t\t}\n\t\t\tsRuleName := string(rn)\n\t\t\tif _, ok := ruleDigest[sRuleName]; ok {\n\t\t\t\treturn fmt.Errorf(\"%s:%d: the rule %s has been already defined\", path, i+1, sRuleName)\n\t\t\t}\n\t\t\t// Calculate rule text digest as a digests of line digests.\n\t\t\tvar digests []byte\n\t\t\tdoDigest := func(b []byte) {\n\t\t\t\th := sha1.New()\n\t\t\t\th.Write(b)\n\t\t\t\tdigests = h.Sum(digests)\n\n\t\t\t}\n\t\t\t// For the first line, digest everything after rule's name\n\t\t\tdoDigest(lines[i][cap(lines[i])+len(rn)-cap(rn):])\n\t\t\tfor i++; i < len(lines) && lines[i][0] == ' '; i++ {\n\t\t\t\tdoDigest(lines[i])\n\t\t\t}\n\t\t\th := sha1.New()\n\t\t\th.Write(digests)\n\t\t\truleDigest[sRuleName] = \"R\" + hex.EncodeToString(h.Sum(nil))\n\n\t\t} else {\n\t\t\ti++\n\t\t}\n\t}\n\n\t// Rewrite rule names.\n\tfor i, line := range lines {\n\t\tif bytes.HasPrefix(line, buildPrefix) {\n\t\t\tbrn := getBuildRuleName(line)\n\t\t\tif bytes.Equal(brn, phonyRule) {\n\t\t\t\tsink.Write(line)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif len(brn) == 0 {\n\t\t\t\treturn fmt.Errorf(\"%s:%d: build statement lacks rule name\", path, i+1)\n\t\t\t}\n\t\t\tsink.Write(line[0 : cap(line)-cap(brn)])\n\t\t\tif digest, ok := ruleDigest[string(brn)]; ok {\n\t\t\t\tsink.Write([]byte(digest))\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"%s:%d: no rule for this build target\", path, i+1)\n\t\t\t}\n\t\t\tsink.Write(line[cap(line)+len(brn)-cap(brn):])\n\t\t} else if bytes.HasPrefix(line, rulePrefix) {\n\t\t\trn := ruleName(line)\n\t\t\t// Write everything before it\n\t\t\tsink.Write(line[0 : cap(line)-cap(rn)])\n\t\t\tsink.Write([]byte(ruleDigest[string(rn)]))\n\t\t\tsink.Write(line[cap(line)+len(rn)-cap(rn):])\n\t\t} else {\n\t\t\t//goland:noinspection GoUnhandledErrorResult\n\t\t\tsink.Write(line)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc getLine(b []byte) []byte {\n\tif n := bytes.IndexByte(b, '\\n'); n >= 0 {\n\t\treturn b[:n+1]\n\t}\n\treturn b\n}\n\n// Returns build statement's rule name\nfunc getBuildRuleName(line []byte) []byte {\n\tn := bytes.IndexByte(line, ':')\n\tif n <= 0 {\n\t\treturn nil\n\t}\n\truleName := line[n+1:]\n\tif ruleName[0] == ' ' {\n\t\truleName = bytes.TrimLeft(ruleName, \" \")\n\t}\n\tif n := bytes.IndexAny(ruleName, \" \\t\\r\\n\"); n >= 0 {\n\t\truleName = ruleName[0:n]\n\t}\n\treturn ruleName\n}\n\n// Returns rule statement's rule name\nfunc ruleName(lineAfterRule []byte) []byte {\n\truleName := lineAfterRule[len(rulePrefix):]\n\tif len(ruleName) == 0 {\n\t\treturn ruleName\n\t}\n\tif ruleName[0] == ' ' {\n\t\truleName = bytes.TrimLeft(ruleName, \" \")\n\t}\n\tif n := bytes.IndexAny(ruleName, \" \\t\\r\\n\"); n >= 0 {\n\t\truleName = ruleName[0:n]\n\t}\n\treturn ruleName\n}\n"
  },
  {
    "path": "tools/canoninja/canoninja_test.go",
    "content": "package canoninja\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestGenerate(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tin       []byte\n\t\twantSink string\n\t\twantErr  bool\n\t}{\n\t\t{\n\t\t\tname: \"1\",\n\t\t\tin: []byte(`\nrule rule1\n  abcd\nrule rule2\n  abcd\nbuild x: rule1\n`),\n\t\t\twantSink: `\nrule R9c97aba7f61994be6862f5ea9a62d26130c7f48b\n  abcd\nrule R9c97aba7f61994be6862f5ea9a62d26130c7f48b\n  abcd\nbuild x: R9c97aba7f61994be6862f5ea9a62d26130c7f48b\n`,\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tsink := &bytes.Buffer{}\n\t\t\terr := Generate(\"<file>\", tt.in, sink)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"Generate() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif gotSink := sink.String(); gotSink != tt.wantSink {\n\t\t\t\tt.Errorf(\"Generate() gotSink = %v, want %v\", gotSink, tt.wantSink)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/canoninja/cmd/canoninja.go",
    "content": "package main\n\n/*\n   Canoninja reads a Ninja file and changes the rule names to be the digest of the rule contents.\n   Feed  it to a filter that extracts only build statements, sort them, and you will have a crude\n   but effective tool to find small differences between two Ninja files.\n*/\n\nimport (\n\t\"canoninja\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\tflag.Parse()\n\tfiles := flag.Args()\n\tif len(files) == 0 {\n\t\tfiles = []string{\"/dev/stdin\"}\n\t}\n\trc := 0\n\tfor _, f := range files {\n\t\tif buffer, err := os.ReadFile(f); err == nil {\n\t\t\terr = canoninja.Generate(f, buffer, os.Stdout)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err)\n\t\t\t\trc = 1\n\t\t\t}\n\t\t} else {\n\t\t\tfmt.Fprintf(os.Stderr, \"%s: %s\\n\", f, err)\n\t\t\trc = 1\n\t\t}\n\t}\n\tos.Exit(rc)\n}\n"
  },
  {
    "path": "tools/canoninja/go.mod",
    "content": "module canoninja\n\ngo 1.19\n"
  },
  {
    "path": "tools/characteristics_rro_generator.py",
    "content": "#!/usr/bin/env python3\nimport sys\n\nif __name__ == '__main__':\n    if len(sys.argv) != 3:\n        sys.exit(f\"usage: {sys_argv[0]} target_package_name output\\n\")\n    with open(sys.argv[2], \"w\") as f:\n        f.write(f'''<?xml version=\"1.0\" encoding=\"utf-8\"?>\n                <manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"{sys.argv[1]}.auto_generated_characteristics_rro\">\n    <application android:hasCode=\"false\" />\n    <overlay android:targetPackage=\"{sys.argv[1]}\"\n             android:isStatic=\"true\"\n             android:priority=\"0\" />\n</manifest>\n''')\n"
  },
  {
    "path": "tools/check-flagged-apis/Android.bp",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_team: \"trendy_team_updatable_sdk_apis\",\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\njava_defaults {\n    name: \"check-flagged-apis-defaults\",\n    srcs: [\n        \"src/com/android/checkflaggedapis/Main.kt\",\n    ],\n    static_libs: [\n        \"libaconfig_java_proto_lite\",\n        \"metalava-signature-reader\",\n        \"metalava-tools-common-m2-deps\",\n    ],\n}\n\njava_binary_host {\n    name: \"check-flagged-apis\",\n    defaults: [\n        \"check-flagged-apis-defaults\",\n    ],\n    main_class: \"com.android.checkflaggedapis.Main\",\n}\n\njava_test_host {\n    name: \"check-flagged-apis-test\",\n    defaults: [\n        \"check-flagged-apis-defaults\",\n    ],\n    srcs: [\n        \"src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt\",\n    ],\n    static_libs: [\n        \"junit\",\n    ],\n}\n"
  },
  {
    "path": "tools/check-flagged-apis/OWNERS",
    "content": "amhk@google.com\ngurpreetgs@google.com\nmichaelwr@google.com\npaulduffin@google.com\n"
  },
  {
    "path": "tools/check-flagged-apis/check-flagged-apis.sh",
    "content": "#!/bin/bash\n\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Run check-flagged-apis for public APIs and the three @SystemApi flavours.\n#\n# This script expects an argument to tell it which subcommand of\n# check-flagged-apis to execute. Run the script without any arguments to see\n# the valid options.\n#\n# Remember to lunch to select the relevant release config before running this script.\n\nsource $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../shell_utils.sh\nrequire_top\n\nPUBLIC_XML_VERSIONS=out/target/common/obj/PACKAGING/api_versions_public_generated-api-versions.xml\nSYSTEM_XML_VERSIONS=out/target/common/obj/PACKAGING/api_versions_system_generated-api-versions.xml\nSYSTEM_SERVER_XML_VERSONS=out/target/common/obj/PACKAGING/api_versions_system_server_complete_generated-api-versions.xml\nMODULE_LIB_XML_VERSIONS=out/target/common/obj/PACKAGING/api_versions_module_lib_complete_generated-api-versions.xml\n\nfunction m() {\n    $(gettop)/build/soong/soong_ui.bash --build-mode --all-modules --dir=\"$(pwd)\" \"$@\"\n}\n\nfunction build() {\n    m \\\n        check-flagged-apis \\\n        all_aconfig_declarations \\\n        frameworks-base-api-current.txt \\\n        frameworks-base-api-system-current.txt \\\n        frameworks-base-api-system-server-current.txt \\\n        frameworks-base-api-module-lib-current.txt \\\n        $PUBLIC_XML_VERSIONS \\\n        $SYSTEM_XML_VERSIONS \\\n        $SYSTEM_SERVER_XML_VERSONS \\\n        $MODULE_LIB_XML_VERSIONS\n}\n\nfunction noop() {\n    true\n}\n\nfunction aninja() {\n    local T=\"$(gettop)\"\n    (\\cd \"${T}\" && prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-${TARGET_PRODUCT}.ninja \"$@\")\n}\n\nfunction path_to_api_signature_file {\n    aninja -t query device_\"$1\"_all_targets | grep -A1 -e input: | tail -n1\n}\n\nfunction run_check() {\n    local errors=0\n\n    echo \"# current\"\n    check-flagged-apis check \\\n        --api-signature $(path_to_api_signature_file \"frameworks-base-api-current.txt\") \\\n        --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb \\\n        --api-versions $PUBLIC_XML_VERSIONS\n    (( errors += $? ))\n\n    echo\n    echo \"# system-current\"\n    check-flagged-apis check \\\n        --api-signature $(path_to_api_signature_file \"frameworks-base-api-system-current.txt\") \\\n        --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb \\\n        --api-versions $SYSTEM_XML_VERSIONS\n    (( errors += $? ))\n\n    echo\n    echo \"# system-server-current\"\n    check-flagged-apis check \\\n        --api-signature $(path_to_api_signature_file \"frameworks-base-api-system-server-current.txt\") \\\n        --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb \\\n        --api-versions $SYSTEM_SERVER_XML_VERSONS\n    (( errors += $? ))\n\n    echo\n    echo \"# module-lib\"\n    check-flagged-apis check \\\n        --api-signature $(path_to_api_signature_file \"frameworks-base-api-module-lib-current.txt\") \\\n        --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb \\\n        --api-versions $MODULE_LIB_XML_VERSIONS\n    (( errors += $? ))\n\n    return $errors\n}\n\nfunction run_list() {\n    echo \"# current\"\n    check-flagged-apis list \\\n        --api-signature $(path_to_api_signature_file \"frameworks-base-api-current.txt\") \\\n        --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb\n\n    echo\n    echo \"# system-current\"\n    check-flagged-apis list \\\n        --api-signature $(path_to_api_signature_file \"frameworks-base-api-system-current.txt\") \\\n        --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb\n\n    echo\n    echo \"# system-server-current\"\n    check-flagged-apis list \\\n        --api-signature $(path_to_api_signature_file \"frameworks-base-api-system-server-current.txt\") \\\n        --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb\n\n    echo\n    echo \"# module-lib\"\n    check-flagged-apis list \\\n        --api-signature $(path_to_api_signature_file \"frameworks-base-api-module-lib-current.txt\") \\\n        --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb\n}\n\nbuild_cmd=build\nif [[ \"$1\" == \"--skip-build\" ]]; then\n    build_cmd=noop\n    shift 1\nfi\n\ncase \"$1\" in\n    check) $build_cmd && run_check ;;\n    list) $build_cmd && run_list ;;\n    *) echo \"usage: $(basename $0): [--skip-build] check|list\"; exit 1\nesac\n"
  },
  {
    "path": "tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.checkflaggedapis\n\nimport android.aconfig.Aconfig\nimport android.aconfig.Aconfig.flag_state.DISABLED\nimport android.aconfig.Aconfig.flag_state.ENABLED\nimport java.io.ByteArrayInputStream\nimport java.io.ByteArrayOutputStream\nimport java.io.InputStream\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.junit.runners.JUnit4\n\nprivate val API_SIGNATURE =\n    \"\"\"\n      // Signature format: 2.0\n      package android {\n        @FlaggedApi(\"android.flag.foo\") public final class Clazz {\n          ctor @FlaggedApi(\"android.flag.foo\") public Clazz();\n          field @FlaggedApi(\"android.flag.foo\") public static final int FOO = 1; // 0x1\n          method @FlaggedApi(\"android.flag.foo\") public int getErrorCode();\n          method @FlaggedApi(\"android.flag.foo\") public <T,U> boolean setData(int, int[][], @NonNull android.util.Utility<T, U>);\n          method @FlaggedApi(\"android.flag.foo\") public boolean setVariableData(int, android.util.Atom...);\n          method @FlaggedApi(\"android.flag.foo\") public boolean innerClassArg(android.Clazz.Builder);\n        }\n        @FlaggedApi(\"android.flag.bar\") public static class Clazz.Builder {\n        }\n      }\n\"\"\"\n        .trim()\n\nprivate val API_VERSIONS =\n    \"\"\"\n      <?xml version=\"1.0\" encoding=\"utf-8\"?>\n      <api version=\"3\">\n        <class name=\"android/Clazz\" since=\"1\">\n          <extends name=\"java/lang/Object\"/>\n          <method name=\"&lt;init>()V\"/>\n          <field name=\"FOO\"/>\n          <method name=\"getErrorCode()I\"/>\n          <method name=\"setData(I[[ILandroid/util/Utility;)Z\"/>\n          <method name=\"setVariableData(I[Landroid/util/Atom;)Z\"/>\n          <method name=\"innerClassArg(Landroid/Clazz${\"$\"}Builder;)\"/>\n        </class>\n        <class name=\"android/Clazz${\"$\"}Builder\" since=\"2\">\n          <extends name=\"java/lang/Object\"/>\n        </class>\n      </api>\n\"\"\"\n        .trim()\n\nprivate fun generateFlagsProto(\n    fooState: Aconfig.flag_state,\n    barState: Aconfig.flag_state\n): InputStream {\n  val fooFlag =\n      Aconfig.parsed_flag\n          .newBuilder()\n          .setPackage(\"android.flag\")\n          .setName(\"foo\")\n          .setState(fooState)\n          .setPermission(Aconfig.flag_permission.READ_ONLY)\n          .build()\n  val barFlag =\n      Aconfig.parsed_flag\n          .newBuilder()\n          .setPackage(\"android.flag\")\n          .setName(\"bar\")\n          .setState(barState)\n          .setPermission(Aconfig.flag_permission.READ_ONLY)\n          .build()\n  val flags =\n      Aconfig.parsed_flags.newBuilder().addParsedFlag(fooFlag).addParsedFlag(barFlag).build()\n  val binaryProto = ByteArrayOutputStream()\n  flags.writeTo(binaryProto)\n  return ByteArrayInputStream(binaryProto.toByteArray())\n}\n\n@RunWith(JUnit4::class)\nclass CheckFlaggedApisTest {\n  @Test\n  fun testParseApiSignature() {\n    val expected =\n        setOf(\n            Pair(\n                Symbol.createClass(\"android/Clazz\", \"java/lang/Object\", setOf()),\n                Flag(\"android.flag.foo\")),\n            Pair(Symbol.createMethod(\"android/Clazz\", \"Clazz()\"), Flag(\"android.flag.foo\")),\n            Pair(Symbol.createField(\"android/Clazz\", \"FOO\"), Flag(\"android.flag.foo\")),\n            Pair(Symbol.createMethod(\"android/Clazz\", \"getErrorCode()\"), Flag(\"android.flag.foo\")),\n            Pair(\n                Symbol.createMethod(\"android/Clazz\", \"setData(I[[ILandroid/util/Utility;)\"),\n                Flag(\"android.flag.foo\")),\n            Pair(\n                Symbol.createMethod(\"android/Clazz\", \"setVariableData(I[Landroid/util/Atom;)\"),\n                Flag(\"android.flag.foo\")),\n            Pair(\n                Symbol.createMethod(\"android/Clazz\", \"innerClassArg(Landroid/Clazz/Builder;)\"),\n                Flag(\"android.flag.foo\")),\n            Pair(\n                Symbol.createClass(\"android/Clazz/Builder\", \"java/lang/Object\", setOf()),\n                Flag(\"android.flag.bar\")),\n        )\n    val actual = parseApiSignature(\"in-memory\", API_SIGNATURE.byteInputStream())\n    assertEquals(expected, actual)\n  }\n\n  @Test\n  fun testParseApiSignatureInterfacesInheritFromJavaLangObject() {\n    val apiSignature =\n        \"\"\"\n          // Signature format: 2.0\n          package android {\n            @FlaggedApi(\"android.flag.foo\") public interface Interface {\n            }\n          }\n        \"\"\"\n            .trim()\n    val expected =\n        setOf(\n            Pair(\n                Symbol.createClass(\"android/Interface\", \"java/lang/Object\", setOf()),\n                Flag(\"android.flag.foo\")))\n    val actual = parseApiSignature(\"in-memory\", apiSignature.byteInputStream())\n    assertEquals(expected, actual)\n  }\n\n  @Test\n  fun testParseFlagValues() {\n    val expected: Map<Flag, Boolean> =\n        mapOf(Flag(\"android.flag.foo\") to true, Flag(\"android.flag.bar\") to true)\n    val actual = parseFlagValues(generateFlagsProto(ENABLED, ENABLED))\n    assertEquals(expected, actual)\n  }\n\n  @Test\n  fun testParseApiVersions() {\n    val expected: Set<Symbol> =\n        setOf(\n            Symbol.createClass(\"android/Clazz\", \"java/lang/Object\", setOf()),\n            Symbol.createMethod(\"android/Clazz\", \"Clazz()\"),\n            Symbol.createField(\"android/Clazz\", \"FOO\"),\n            Symbol.createMethod(\"android/Clazz\", \"getErrorCode()\"),\n            Symbol.createMethod(\"android/Clazz\", \"setData(I[[ILandroid/util/Utility;)\"),\n            Symbol.createMethod(\"android/Clazz\", \"setVariableData(I[Landroid/util/Atom;)\"),\n            Symbol.createMethod(\"android/Clazz\", \"innerClassArg(Landroid/Clazz/Builder;)\"),\n            Symbol.createClass(\"android/Clazz/Builder\", \"java/lang/Object\", setOf()),\n        )\n    val actual = parseApiVersions(API_VERSIONS.byteInputStream())\n    assertEquals(expected, actual)\n  }\n\n  @Test\n  fun testParseApiVersionsNestedClasses() {\n    val apiVersions =\n        \"\"\"\n          <?xml version=\"1.0\" encoding=\"utf-8\"?>\n          <api version=\"3\">\n            <class name=\"android/Clazz${'$'}Foo${'$'}Bar\" since=\"1\">\n              <extends name=\"java/lang/Object\"/>\n              <method name=\"&lt;init>()V\"/>\n            </class>\n          </api>\n        \"\"\"\n            .trim()\n    val expected: Set<Symbol> =\n        setOf(\n            Symbol.createClass(\"android/Clazz/Foo/Bar\", \"java/lang/Object\", setOf()),\n            Symbol.createMethod(\"android/Clazz/Foo/Bar\", \"Bar()\"),\n        )\n    val actual = parseApiVersions(apiVersions.byteInputStream())\n    assertEquals(expected, actual)\n  }\n\n  @Test\n  fun testFindErrorsNoErrors() {\n    val expected = setOf<ApiError>()\n    val actual =\n        findErrors(\n            parseApiSignature(\"in-memory\", API_SIGNATURE.byteInputStream()),\n            parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),\n            parseApiVersions(API_VERSIONS.byteInputStream()))\n    assertEquals(expected, actual)\n  }\n\n  @Test\n  fun testFindErrorsVerifyImplements() {\n    val apiSignature =\n        \"\"\"\n          // Signature format: 2.0\n          package android {\n            @FlaggedApi(\"android.flag.foo\") public final class Clazz implements android.Interface {\n              method @FlaggedApi(\"android.flag.foo\") public boolean foo();\n              method @FlaggedApi(\"android.flag.foo\") public boolean bar();\n            }\n            public interface Interface {\n              method public boolean bar();\n            }\n          }\n        \"\"\"\n            .trim()\n\n    val apiVersions =\n        \"\"\"\n          <?xml version=\"1.0\" encoding=\"utf-8\"?>\n          <api version=\"3\">\n            <class name=\"android/Clazz\" since=\"1\">\n              <extends name=\"java/lang/Object\"/>\n              <implements name=\"android/Interface\"/>\n              <method name=\"foo()Z\"/>\n            </class>\n            <class name=\"android/Interface\" since=\"1\">\n              <method name=\"bar()Z\"/>\n            </class>\n          </api>\n        \"\"\"\n            .trim()\n\n    val expected = setOf<ApiError>()\n    val actual =\n        findErrors(\n            parseApiSignature(\"in-memory\", apiSignature.byteInputStream()),\n            parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),\n            parseApiVersions(apiVersions.byteInputStream()))\n    assertEquals(expected, actual)\n  }\n\n  @Test\n  fun testFindErrorsVerifySuperclass() {\n    val apiSignature =\n        \"\"\"\n          // Signature format: 2.0\n          package android {\n            @FlaggedApi(\"android.flag.foo\") public final class C extends android.B {\n              method @FlaggedApi(\"android.flag.foo\") public boolean c();\n              method @FlaggedApi(\"android.flag.foo\") public boolean b();\n              method @FlaggedApi(\"android.flag.foo\") public boolean a();\n            }\n            public final class B extends android.A {\n              method public boolean b();\n            }\n            public final class A {\n              method public boolean a();\n            }\n          }\n        \"\"\"\n            .trim()\n\n    val apiVersions =\n        \"\"\"\n          <?xml version=\"1.0\" encoding=\"utf-8\"?>\n          <api version=\"3\">\n            <class name=\"android/C\" since=\"1\">\n              <extends name=\"android/B\"/>\n              <method name=\"c()Z\"/>\n            </class>\n            <class name=\"android/B\" since=\"1\">\n              <extends name=\"android/A\"/>\n              <method name=\"b()Z\"/>\n            </class>\n            <class name=\"android/A\" since=\"1\">\n              <method name=\"a()Z\"/>\n            </class>\n          </api>\n        \"\"\"\n            .trim()\n\n    val expected = setOf<ApiError>()\n    val actual =\n        findErrors(\n            parseApiSignature(\"in-memory\", apiSignature.byteInputStream()),\n            parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),\n            parseApiVersions(apiVersions.byteInputStream()))\n    assertEquals(expected, actual)\n  }\n\n  @Test\n  fun testNestedFlagsOuterFlagWins() {\n    val apiSignature =\n        \"\"\"\n          // Signature format: 2.0\n          package android {\n            @FlaggedApi(\"android.flag.foo\") public final class A {\n              method @FlaggedApi(\"android.flag.bar\") public boolean method();\n            }\n            @FlaggedApi(\"android.flag.bar\") public final class B {\n              method @FlaggedApi(\"android.flag.foo\") public boolean method();\n            }\n          }\n        \"\"\"\n            .trim()\n\n    val apiVersions =\n        \"\"\"\n          <?xml version=\"1.0\" encoding=\"utf-8\"?>\n          <api version=\"3\">\n            <class name=\"android/B\" since=\"1\">\n            <extends name=\"java/lang/Object\"/>\n            </class>\n          </api>\n        \"\"\"\n            .trim()\n\n    val expected = setOf<ApiError>()\n    val actual =\n        findErrors(\n            parseApiSignature(\"in-memory\", apiSignature.byteInputStream()),\n            parseFlagValues(generateFlagsProto(DISABLED, ENABLED)),\n            parseApiVersions(apiVersions.byteInputStream()))\n    assertEquals(expected, actual)\n  }\n\n  @Test\n  fun testFindErrorsDisabledFlaggedApiIsPresent() {\n    val expected =\n        setOf<ApiError>(\n            DisabledFlaggedApiIsPresentError(\n                Symbol.createClass(\"android/Clazz\", \"java/lang/Object\", setOf()),\n                Flag(\"android.flag.foo\")),\n            DisabledFlaggedApiIsPresentError(\n                Symbol.createMethod(\"android/Clazz\", \"Clazz()\"), Flag(\"android.flag.foo\")),\n            DisabledFlaggedApiIsPresentError(\n                Symbol.createField(\"android/Clazz\", \"FOO\"), Flag(\"android.flag.foo\")),\n            DisabledFlaggedApiIsPresentError(\n                Symbol.createMethod(\"android/Clazz\", \"getErrorCode()\"), Flag(\"android.flag.foo\")),\n            DisabledFlaggedApiIsPresentError(\n                Symbol.createMethod(\"android/Clazz\", \"setData(I[[ILandroid/util/Utility;)\"),\n                Flag(\"android.flag.foo\")),\n            DisabledFlaggedApiIsPresentError(\n                Symbol.createMethod(\"android/Clazz\", \"setVariableData(I[Landroid/util/Atom;)\"),\n                Flag(\"android.flag.foo\")),\n            DisabledFlaggedApiIsPresentError(\n                Symbol.createMethod(\"android/Clazz\", \"innerClassArg(Landroid/Clazz/Builder;)\"),\n                Flag(\"android.flag.foo\")),\n            DisabledFlaggedApiIsPresentError(\n                Symbol.createClass(\"android/Clazz/Builder\", \"java/lang/Object\", setOf()),\n                Flag(\"android.flag.bar\")),\n        )\n    val actual =\n        findErrors(\n            parseApiSignature(\"in-memory\", API_SIGNATURE.byteInputStream()),\n            parseFlagValues(generateFlagsProto(DISABLED, DISABLED)),\n            parseApiVersions(API_VERSIONS.byteInputStream()))\n    assertEquals(expected, actual)\n  }\n\n  @Test\n  fun testListFlaggedApis() {\n    val expected =\n        listOf(\n            \"android.flag.bar DISABLED android/Clazz/Builder\",\n            \"android.flag.foo ENABLED android/Clazz\",\n            \"android.flag.foo ENABLED android/Clazz/Clazz()\",\n            \"android.flag.foo ENABLED android/Clazz/FOO\",\n            \"android.flag.foo ENABLED android/Clazz/getErrorCode()\",\n            \"android.flag.foo ENABLED android/Clazz/innerClassArg(Landroid/Clazz/Builder;)\",\n            \"android.flag.foo ENABLED android/Clazz/setData(I[[ILandroid/util/Utility;)\",\n            \"android.flag.foo ENABLED android/Clazz/setVariableData(I[Landroid/util/Atom;)\")\n    val actual =\n        listFlaggedApis(\n            parseApiSignature(\"in-memory\", API_SIGNATURE.byteInputStream()),\n            parseFlagValues(generateFlagsProto(ENABLED, DISABLED)))\n    assertEquals(expected, actual)\n  }\n}\n"
  },
  {
    "path": "tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:JvmName(\"Main\")\n\npackage com.android.checkflaggedapis\n\nimport android.aconfig.Aconfig\nimport com.android.tools.metalava.model.BaseItemVisitor\nimport com.android.tools.metalava.model.CallableItem\nimport com.android.tools.metalava.model.ClassItem\nimport com.android.tools.metalava.model.FieldItem\nimport com.android.tools.metalava.model.Item\nimport com.android.tools.metalava.model.text.ApiFile\nimport com.github.ajalt.clikt.core.CliktCommand\nimport com.github.ajalt.clikt.core.ProgramResult\nimport com.github.ajalt.clikt.core.subcommands\nimport com.github.ajalt.clikt.parameters.options.help\nimport com.github.ajalt.clikt.parameters.options.option\nimport com.github.ajalt.clikt.parameters.options.required\nimport com.github.ajalt.clikt.parameters.types.path\nimport java.io.InputStream\nimport javax.xml.parsers.DocumentBuilderFactory\nimport org.w3c.dom.Node\n\n/**\n * Class representing the fully qualified name of a class, method or field.\n *\n * This tool reads a multitude of input formats all of which represents the fully qualified path to\n * a Java symbol slightly differently. To keep things consistent, all parsed APIs are converted to\n * Symbols.\n *\n * Symbols are encoded using the format similar to the one described in section 4.3.2 of the JVM\n * spec [1], that is, \"package.class.inner-class.method(int, int[], android.util.Clazz)\" is\n * represented as\n * <pre>\n *   package.class.inner-class.method(II[Landroid/util/Clazz;)\n * <pre>\n *\n * Where possible, the format has been simplified (to make translation of the\n * various input formats easier): for instance, only / is used as delimiter (#\n * and $ are never used).\n *\n * 1. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2\n */\ninternal sealed class Symbol {\n  companion object {\n    private val FORBIDDEN_CHARS = listOf('#', '$', '.')\n\n    fun createClass(clazz: String, superclass: String?, interfaces: Set<String>): Symbol {\n      return ClassSymbol(\n          toInternalFormat(clazz),\n          superclass?.let { toInternalFormat(it) },\n          interfaces.map { toInternalFormat(it) }.toSet())\n    }\n\n    fun createField(clazz: String, field: String): Symbol {\n      require(!field.contains(\"(\") && !field.contains(\")\"))\n      return MemberSymbol(toInternalFormat(clazz), toInternalFormat(field))\n    }\n\n    fun createMethod(clazz: String, method: String): Symbol {\n      return MemberSymbol(toInternalFormat(clazz), toInternalFormat(method))\n    }\n\n    protected fun toInternalFormat(name: String): String {\n      var internalName = name\n      for (ch in FORBIDDEN_CHARS) {\n        internalName = internalName.replace(ch, '/')\n      }\n      return internalName\n    }\n  }\n\n  abstract fun toPrettyString(): String\n}\n\ninternal data class ClassSymbol(\n    val clazz: String,\n    val superclass: String?,\n    val interfaces: Set<String>\n) : Symbol() {\n  override fun toPrettyString(): String = \"$clazz\"\n}\n\ninternal data class MemberSymbol(val clazz: String, val member: String) : Symbol() {\n  override fun toPrettyString(): String = \"$clazz/$member\"\n}\n\n/**\n * Class representing the fully qualified name of an aconfig flag.\n *\n * This includes both the flag's package and name, separated by a dot, e.g.:\n * <pre>\n *   com.android.aconfig.test.disabled_ro\n * <pre>\n */\n@JvmInline\ninternal value class Flag(val name: String) {\n  override fun toString(): String = name.toString()\n}\n\ninternal sealed class ApiError {\n  abstract val symbol: Symbol\n  abstract val flag: Flag\n}\n\ninternal data class EnabledFlaggedApiNotPresentError(\n    override val symbol: Symbol,\n    override val flag: Flag\n) : ApiError() {\n  override fun toString(): String {\n    return \"error: enabled @FlaggedApi not present in built artifact: symbol=${symbol.toPrettyString()} flag=$flag\"\n  }\n}\n\ninternal data class DisabledFlaggedApiIsPresentError(\n    override val symbol: Symbol,\n    override val flag: Flag\n) : ApiError() {\n  override fun toString(): String {\n    return \"error: disabled @FlaggedApi is present in built artifact: symbol=${symbol.toPrettyString()} flag=$flag\"\n  }\n}\n\ninternal data class UnknownFlagError(override val symbol: Symbol, override val flag: Flag) :\n    ApiError() {\n  override fun toString(): String {\n    return \"error: unknown flag: symbol=${symbol.toPrettyString()} flag=$flag\"\n  }\n}\n\nval ARG_API_SIGNATURE = \"--api-signature\"\nval ARG_API_SIGNATURE_HELP =\n    \"\"\"\nPath to API signature file.\nUsually named *current.txt.\nTip: `m frameworks-base-api-current.txt` will generate a file that includes all platform and mainline APIs.\n\"\"\"\n\nval ARG_FLAG_VALUES = \"--flag-values\"\nval ARG_FLAG_VALUES_HELP =\n    \"\"\"\nPath to aconfig parsed_flags binary proto file.\nTip: `m all_aconfig_declarations` will generate a file that includes all information about all flags.\n\"\"\"\n\nval ARG_API_VERSIONS = \"--api-versions\"\nval ARG_API_VERSIONS_HELP =\n    \"\"\"\nPath to API versions XML file.\nUsually named xml-versions.xml.\nTip: `m sdk dist` will generate a file that includes all platform and mainline APIs.\n\"\"\"\n\nclass MainCommand : CliktCommand() {\n  override fun run() {}\n}\n\nclass CheckCommand :\n    CliktCommand(\n        help =\n            \"\"\"\nCheck that all flagged APIs are used in the correct way.\n\nThis tool reads the API signature file and checks that all flagged APIs are used in the correct way.\n\nThe tool will exit with a non-zero exit code if any flagged APIs are found to be used in the incorrect way.\n\"\"\") {\n  private val apiSignaturePath by\n      option(ARG_API_SIGNATURE)\n          .help(ARG_API_SIGNATURE_HELP)\n          .path(mustExist = true, canBeDir = false, mustBeReadable = true)\n          .required()\n  private val flagValuesPath by\n      option(ARG_FLAG_VALUES)\n          .help(ARG_FLAG_VALUES_HELP)\n          .path(mustExist = true, canBeDir = false, mustBeReadable = true)\n          .required()\n  private val apiVersionsPath by\n      option(ARG_API_VERSIONS)\n          .help(ARG_API_VERSIONS_HELP)\n          .path(mustExist = true, canBeDir = false, mustBeReadable = true)\n          .required()\n\n  override fun run() {\n    val flaggedSymbols =\n        apiSignaturePath.toFile().inputStream().use {\n          parseApiSignature(apiSignaturePath.toString(), it)\n        }\n    val flags = flagValuesPath.toFile().inputStream().use { parseFlagValues(it) }\n    val exportedSymbols = apiVersionsPath.toFile().inputStream().use { parseApiVersions(it) }\n    val errors = findErrors(flaggedSymbols, flags, exportedSymbols)\n    for (e in errors) {\n      println(e)\n    }\n    throw ProgramResult(errors.size)\n  }\n}\n\nclass ListCommand :\n    CliktCommand(\n        help =\n            \"\"\"\nList all flagged APIs and corresponding flags.\n\nThe output format is \"<fully-qualified-name-of-flag> <state-of-flag> <API>\", one line per API.\n\nThe output can be post-processed by e.g. piping it to grep to filter out only enabled APIs, or all APIs guarded by a given flag.\n\"\"\") {\n  private val apiSignaturePath by\n      option(ARG_API_SIGNATURE)\n          .help(ARG_API_SIGNATURE_HELP)\n          .path(mustExist = true, canBeDir = false, mustBeReadable = true)\n          .required()\n  private val flagValuesPath by\n      option(ARG_FLAG_VALUES)\n          .help(ARG_FLAG_VALUES_HELP)\n          .path(mustExist = true, canBeDir = false, mustBeReadable = true)\n          .required()\n\n  override fun run() {\n    val flaggedSymbols =\n        apiSignaturePath.toFile().inputStream().use {\n          parseApiSignature(apiSignaturePath.toString(), it)\n        }\n    val flags = flagValuesPath.toFile().inputStream().use { parseFlagValues(it) }\n    val output = listFlaggedApis(flaggedSymbols, flags)\n    if (output.isNotEmpty()) {\n      println(output.joinToString(\"\\n\"))\n    }\n  }\n}\n\ninternal fun parseApiSignature(path: String, input: InputStream): Set<Pair<Symbol, Flag>> {\n  val output = mutableSetOf<Pair<Symbol, Flag>>()\n  val visitor =\n      object : BaseItemVisitor() {\n        override fun visitClass(cls: ClassItem) {\n          getFlagOrNull(cls)?.let { flag ->\n            val symbol =\n                Symbol.createClass(\n                    cls.baselineElementId(),\n                    if (cls.isInterface()) {\n                      \"java/lang/Object\"\n                    } else {\n                      cls.superClass()?.baselineElementId()\n                    },\n                    cls.allInterfaces()\n                        .map { it.baselineElementId() }\n                        .filter { it != cls.baselineElementId() }\n                        .toSet())\n            output.add(Pair(symbol, flag))\n          }\n        }\n\n        override fun visitField(field: FieldItem) {\n          getFlagOrNull(field)?.let { flag ->\n            val symbol =\n                Symbol.createField(field.containingClass().baselineElementId(), field.name())\n            output.add(Pair(symbol, flag))\n          }\n        }\n\n        override fun visitCallable(callable: CallableItem) {\n          getFlagOrNull(callable)?.let { flag ->\n            val callableSignature = buildString {\n              append(callable.name())\n              append(\"(\")\n              callable.parameters().joinTo(this, separator = \"\") { it.type().internalName() }\n              append(\")\")\n            }\n            val symbol =\n                Symbol.createMethod(callable.containingClass().qualifiedName(), callableSignature)\n            output.add(Pair(symbol, flag))\n          }\n        }\n\n        private fun getFlagOrNull(item: Item): Flag? {\n          return item.modifiers\n              .findAnnotation(\"android.annotation.FlaggedApi\")\n              ?.findAttribute(\"value\")\n              ?.legacyValue\n              ?.let { Flag(it.value() as String) }\n        }\n      }\n  val codebase = ApiFile.parseApi(path, input)\n  codebase.accept(visitor)\n  return output\n}\n\ninternal fun parseFlagValues(input: InputStream): Map<Flag, Boolean> {\n  val parsedFlags = Aconfig.parsed_flags.parseFrom(input).getParsedFlagList()\n  return parsedFlags.associateBy(\n      { Flag(\"${it.getPackage()}.${it.getName()}\") },\n      { it.getState() == Aconfig.flag_state.ENABLED })\n}\n\ninternal fun parseApiVersions(input: InputStream): Set<Symbol> {\n  fun Node.getAttribute(name: String): String? = getAttributes()?.getNamedItem(name)?.getNodeValue()\n\n  val output = mutableSetOf<Symbol>()\n  val factory = DocumentBuilderFactory.newInstance()\n  val parser = factory.newDocumentBuilder()\n  val document = parser.parse(input)\n\n  val classes = document.getElementsByTagName(\"class\")\n  // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead\n  for (i in 0.rangeUntil(classes.getLength())) {\n    val cls = classes.item(i)\n    val className =\n        requireNotNull(cls.getAttribute(\"name\")) {\n          \"Bad XML: <class> element without name attribute\"\n        }\n    var superclass: String? = null\n    val interfaces = mutableSetOf<String>()\n    val children = cls.getChildNodes()\n    for (j in 0.rangeUntil(children.getLength())) {\n      val child = children.item(j)\n      when (child.getNodeName()) {\n        \"extends\" -> {\n          superclass =\n              requireNotNull(child.getAttribute(\"name\")) {\n                \"Bad XML: <extends> element without name attribute\"\n              }\n        }\n        \"implements\" -> {\n          val interfaceName =\n              requireNotNull(child.getAttribute(\"name\")) {\n                \"Bad XML: <implements> element without name attribute\"\n              }\n          interfaces.add(interfaceName)\n        }\n      }\n    }\n    output.add(Symbol.createClass(className, superclass, interfaces))\n  }\n\n  val fields = document.getElementsByTagName(\"field\")\n  // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead\n  for (i in 0.rangeUntil(fields.getLength())) {\n    val field = fields.item(i)\n    val fieldName =\n        requireNotNull(field.getAttribute(\"name\")) {\n          \"Bad XML: <field> element without name attribute\"\n        }\n    val className =\n        requireNotNull(field.getParentNode()?.getAttribute(\"name\")) {\n          \"Bad XML: top level <field> element\"\n        }\n    output.add(Symbol.createField(className, fieldName))\n  }\n\n  val methods = document.getElementsByTagName(\"method\")\n  // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead\n  for (i in 0.rangeUntil(methods.getLength())) {\n    val method = methods.item(i)\n    val methodSignature =\n        requireNotNull(method.getAttribute(\"name\")) {\n          \"Bad XML: <method> element without name attribute\"\n        }\n    val methodSignatureParts = methodSignature.split(Regex(\"\\\\(|\\\\)\"))\n    if (methodSignatureParts.size != 3) {\n      throw Exception(\"Bad XML: method signature '$methodSignature'\")\n    }\n    var (methodName, methodArgs, _) = methodSignatureParts\n    val packageAndClassName =\n        requireNotNull(method.getParentNode()?.getAttribute(\"name\")) {\n              \"Bad XML: top level <method> element, or <class> element missing name attribute\"\n            }\n            .replace(\"$\", \"/\")\n    if (methodName == \"<init>\") {\n      methodName = packageAndClassName.split(\"/\").last()\n    }\n    output.add(Symbol.createMethod(packageAndClassName, \"$methodName($methodArgs)\"))\n  }\n\n  return output\n}\n\n/**\n * Find errors in the given data.\n *\n * @param flaggedSymbolsInSource the set of symbols that are flagged in the source code\n * @param flags the set of flags and their values\n * @param symbolsInOutput the set of symbols that are present in the output\n * @return the set of errors found\n */\ninternal fun findErrors(\n    flaggedSymbolsInSource: Set<Pair<Symbol, Flag>>,\n    flags: Map<Flag, Boolean>,\n    symbolsInOutput: Set<Symbol>\n): Set<ApiError> {\n  fun Set<Symbol>.containsSymbol(symbol: Symbol): Boolean {\n    // trivial case: the symbol is explicitly listed in api-versions.xml\n    if (contains(symbol)) {\n      return true\n    }\n\n    // non-trivial case: the symbol could be part of the surrounding class'\n    // super class or interfaces\n    val (className, memberName) =\n        when (symbol) {\n          is ClassSymbol -> return false\n          is MemberSymbol -> {\n            Pair(symbol.clazz, symbol.member)\n          }\n        }\n    val clazz = find { it is ClassSymbol && it.clazz == className } as? ClassSymbol?\n    if (clazz == null) {\n      return false\n    }\n\n    for (interfaceName in clazz.interfaces) {\n      // createMethod is the same as createField, except it allows parenthesis\n      val interfaceSymbol = Symbol.createMethod(interfaceName, memberName)\n      if (contains(interfaceSymbol)) {\n        return true\n      }\n    }\n\n    if (clazz.superclass != null) {\n      val superclassSymbol = Symbol.createMethod(clazz.superclass, memberName)\n      return containsSymbol(superclassSymbol)\n    }\n\n    return false\n  }\n\n  /**\n   * Returns whether the given flag is enabled for the given symbol.\n   *\n   * A flagged member inside a flagged class is ignored (and the flag value considered disabled) if\n   * the class' flag is disabled.\n   *\n   * @param symbol the symbol to check\n   * @param flag the flag to check\n   * @return whether the flag is enabled for the given symbol\n   */\n  fun isFlagEnabledForSymbol(symbol: Symbol, flag: Flag): Boolean {\n    when (symbol) {\n      is ClassSymbol -> return flags.getValue(flag)\n      is MemberSymbol -> {\n        val memberFlagValue = flags.getValue(flag)\n        if (!memberFlagValue) {\n          return false\n        }\n        // Special case: if the MemberSymbol's flag is enabled, but the outer\n        // ClassSymbol's flag (if the class is flagged) is disabled, consider\n        // the MemberSymbol's flag as disabled:\n        //\n        //   @FlaggedApi(this-flag-is-disabled) Clazz {\n        //       @FlaggedApi(this-flag-is-enabled) method(); // The Clazz' flag \"wins\"\n        //   }\n        //\n        // Note: the current implementation does not handle nested classes.\n        val classFlagValue =\n            flaggedSymbolsInSource\n                .find { it.first.toPrettyString() == symbol.clazz }\n                ?.let { flags.getValue(it.second) } ?: true\n        return classFlagValue\n      }\n    }\n  }\n\n  val errors = mutableSetOf<ApiError>()\n  for ((symbol, flag) in flaggedSymbolsInSource) {\n    try {\n      if (isFlagEnabledForSymbol(symbol, flag)) {\n        if (!symbolsInOutput.containsSymbol(symbol)) {\n          errors.add(EnabledFlaggedApiNotPresentError(symbol, flag))\n        }\n      } else {\n        if (symbolsInOutput.containsSymbol(symbol)) {\n          errors.add(DisabledFlaggedApiIsPresentError(symbol, flag))\n        }\n      }\n    } catch (e: NoSuchElementException) {\n      errors.add(UnknownFlagError(symbol, flag))\n    }\n  }\n  return errors\n}\n\n/**\n * Collect all known info about all @FlaggedApi annotated APIs.\n *\n * Each API will be represented as a String, on the format\n * <pre>\n *   &lt;fully-qualified-name-of-flag&lt; &lt;state-of-flag&lt; &lt;API&lt;\n * </pre>\n *\n * @param flaggedSymbolsInSource the set of symbols that are flagged in the source code\n * @param flags the set of flags and their values\n * @return a list of Strings encoding API data using the format described above, sorted\n *   alphabetically\n */\ninternal fun listFlaggedApis(\n    flaggedSymbolsInSource: Set<Pair<Symbol, Flag>>,\n    flags: Map<Flag, Boolean>\n): List<String> {\n  val output = mutableListOf<String>()\n  for ((symbol, flag) in flaggedSymbolsInSource) {\n    val flagState =\n        when (flags.get(flag)) {\n          true -> \"ENABLED\"\n          false -> \"DISABLED\"\n          null -> \"UNKNOWN\"\n        }\n    output.add(\"$flag $flagState ${symbol.toPrettyString()}\")\n  }\n  output.sort()\n  return output\n}\n\nfun main(args: Array<String>) = MainCommand().subcommands(CheckCommand(), ListCommand()).main(args)\n"
  },
  {
    "path": "tools/check_elf_file.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"ELF file checker.\n\nThis command ensures all undefined symbols in an ELF file can be resolved to\nglobal (or weak) symbols defined in shared objects specified in DT_NEEDED\nentries.\n\"\"\"\n\nfrom __future__ import print_function\n\nimport argparse\nimport collections\nimport os\nimport os.path\nimport re\nimport struct\nimport subprocess\nimport sys\n\n\n_ELF_MAGIC = b'\\x7fELF'\n\n\n# Known machines\n_EM_386 = 3\n_EM_ARM = 40\n_EM_X86_64 = 62\n_EM_AARCH64 = 183\n\n_32_BIT_MACHINES = {_EM_386, _EM_ARM}\n_64_BIT_MACHINES = {_EM_X86_64, _EM_AARCH64}\n_KNOWN_MACHINES = _32_BIT_MACHINES | _64_BIT_MACHINES\n\n# ELF header struct\n_ELF_HEADER_STRUCT = (\n  ('ei_magic', '4s'),\n  ('ei_class', 'B'),\n  ('ei_data', 'B'),\n  ('ei_version', 'B'),\n  ('ei_osabi', 'B'),\n  ('ei_pad', '8s'),\n  ('e_type', 'H'),\n  ('e_machine', 'H'),\n  ('e_version', 'I'),\n)\n\n_ELF_HEADER_STRUCT_FMT = ''.join(_fmt for _, _fmt in _ELF_HEADER_STRUCT)\n\n\nELFHeader = collections.namedtuple(\n  'ELFHeader', [_name for _name, _ in _ELF_HEADER_STRUCT])\n\n\nELF = collections.namedtuple(\n  'ELF',\n  ('alignments', 'dt_soname', 'dt_needed', 'imported', 'exported', 'header'))\n\n\ndef _get_os_name():\n  \"\"\"Get the host OS name.\"\"\"\n  if sys.platform.startswith('linux'):\n    return 'linux'\n  if sys.platform.startswith('darwin'):\n    return 'darwin'\n  raise ValueError(sys.platform + ' is not supported')\n\n\ndef _get_build_top():\n  \"\"\"Find the build top of the source tree ($ANDROID_BUILD_TOP).\"\"\"\n  prev_path = None\n  curr_path = os.path.abspath(os.getcwd())\n  while prev_path != curr_path:\n    if os.path.exists(os.path.join(curr_path, '.repo')):\n      return curr_path\n    prev_path = curr_path\n    curr_path = os.path.dirname(curr_path)\n  return None\n\n\ndef _select_latest_llvm_version(versions):\n  \"\"\"Select the latest LLVM prebuilts version from a set of versions.\"\"\"\n  pattern = re.compile('clang-r([0-9]+)([a-z]?)')\n  found_rev = 0\n  found_ver = None\n  for curr_ver in versions:\n    match = pattern.match(curr_ver)\n    if not match:\n      continue\n    curr_rev = int(match.group(1))\n    if not found_ver or curr_rev > found_rev or (\n        curr_rev == found_rev and curr_ver > found_ver):\n      found_rev = curr_rev\n      found_ver = curr_ver\n  return found_ver\n\n\ndef _get_latest_llvm_version(llvm_dir):\n  \"\"\"Find the latest LLVM prebuilts version from `llvm_dir`.\"\"\"\n  return _select_latest_llvm_version(os.listdir(llvm_dir))\n\n\ndef _get_llvm_dir():\n  \"\"\"Find the path to LLVM prebuilts.\"\"\"\n  build_top = _get_build_top()\n\n  llvm_prebuilts_base = os.environ.get('LLVM_PREBUILTS_BASE')\n  if not llvm_prebuilts_base:\n    llvm_prebuilts_base = os.path.join('prebuilts', 'clang', 'host')\n\n  llvm_dir = os.path.join(\n    build_top, llvm_prebuilts_base, _get_os_name() + '-x86')\n\n  if not os.path.exists(llvm_dir):\n    return None\n\n  llvm_prebuilts_version = os.environ.get('LLVM_PREBUILTS_VERSION')\n  if not llvm_prebuilts_version:\n    llvm_prebuilts_version = _get_latest_llvm_version(llvm_dir)\n\n  llvm_dir = os.path.join(llvm_dir, llvm_prebuilts_version)\n\n  if not os.path.exists(llvm_dir):\n    return None\n\n  return llvm_dir\n\n\ndef _get_llvm_readobj():\n  \"\"\"Find the path to llvm-readobj executable.\"\"\"\n  llvm_dir = _get_llvm_dir()\n  llvm_readobj = os.path.join(llvm_dir, 'bin', 'llvm-readobj')\n  return llvm_readobj if os.path.exists(llvm_readobj) else 'llvm-readobj'\n\n\nclass ELFError(ValueError):\n  \"\"\"Generic ELF parse error\"\"\"\n  pass\n\n\nclass ELFInvalidMagicError(ELFError):\n  \"\"\"Invalid ELF magic word error\"\"\"\n  def __init__(self):\n    super(ELFInvalidMagicError, self).__init__('bad ELF magic')\n\n\nclass ELFParser(object):\n  \"\"\"ELF file parser\"\"\"\n\n  @classmethod\n  def _read_elf_header(cls, elf_file_path):\n    \"\"\"Read the ELF magic word from the beginning of the file.\"\"\"\n    with open(elf_file_path, 'rb') as elf_file:\n      buf = elf_file.read(struct.calcsize(_ELF_HEADER_STRUCT_FMT))\n      try:\n        return ELFHeader(*struct.unpack(_ELF_HEADER_STRUCT_FMT, buf))\n      except struct.error:\n        return None\n\n\n  @classmethod\n  def open(cls, elf_file_path, llvm_readobj):\n    \"\"\"Open and parse the ELF file.\"\"\"\n    # Parse the ELF header to check the magic word.\n    header = cls._read_elf_header(elf_file_path)\n    if not header or header.ei_magic != _ELF_MAGIC:\n      raise ELFInvalidMagicError()\n\n    # Run llvm-readobj and parse the output.\n    return cls._read_llvm_readobj(elf_file_path, header, llvm_readobj)\n\n\n  @classmethod\n  def _find_prefix(cls, pattern, lines_it):\n    \"\"\"Iterate `lines_it` until finding a string that starts with `pattern`.\"\"\"\n    for line in lines_it:\n      if line.startswith(pattern):\n        return True\n    return False\n\n\n  @classmethod\n  def _read_llvm_readobj(cls, elf_file_path, header, llvm_readobj):\n    \"\"\"Run llvm-readobj and parse the output.\"\"\"\n    cmd = [llvm_readobj, '--program-headers', '--dynamic-table',\n           '--dyn-symbols', elf_file_path]\n    out = subprocess.check_output(cmd, text=True)\n    lines = out.splitlines()\n    return cls._parse_llvm_readobj(elf_file_path, header, lines)\n\n\n  @classmethod\n  def _parse_llvm_readobj(cls, elf_file_path, header, lines):\n    \"\"\"Parse the output of llvm-readobj.\"\"\"\n    lines_it = iter(lines)\n    alignments = cls._parse_program_headers(lines_it)\n    dt_soname, dt_needed = cls._parse_dynamic_table(elf_file_path, lines_it)\n    imported, exported = cls._parse_dynamic_symbols(lines_it)\n    return ELF(alignments, dt_soname, dt_needed, imported, exported, header)\n\n\n  _PROGRAM_HEADERS_START_PATTERN = 'ProgramHeaders ['\n  _PROGRAM_HEADERS_END_PATTERN = ']'\n  _PROGRAM_HEADER_START_PATTERN = 'ProgramHeader {'\n  _PROGRAM_HEADER_TYPE_PATTERN = re.compile('^\\\\s+Type:\\\\s+(.*)$')\n  _PROGRAM_HEADER_ALIGN_PATTERN = re.compile('^\\\\s+Alignment:\\\\s+(.*)$')\n  _PROGRAM_HEADER_END_PATTERN = '}'\n\n\n  @classmethod\n  def _parse_program_headers(cls, lines_it):\n    \"\"\"Parse the dynamic table section.\"\"\"\n    alignments = []\n\n    if not cls._find_prefix(cls._PROGRAM_HEADERS_START_PATTERN, lines_it):\n      raise ELFError()\n\n    for line in lines_it:\n      # Parse each program header\n      if line.strip() == cls._PROGRAM_HEADER_START_PATTERN:\n        p_align = None\n        p_type = None\n        for line in lines_it:\n          if line.strip() == cls._PROGRAM_HEADER_END_PATTERN:\n            if not p_align:\n              raise ELFError(\"Could not parse alignment from program header!\")\n            if not p_type:\n              raise ELFError(\"Could not parse type from program header!\")\n\n            if p_type.startswith(\"PT_LOAD \"):\n              alignments.append(int(p_align))\n            break\n\n          match = cls._PROGRAM_HEADER_TYPE_PATTERN.match(line)\n          if match:\n            p_type = match.group(1)\n\n          match = cls._PROGRAM_HEADER_ALIGN_PATTERN.match(line)\n          if match:\n            p_align = match.group(1)\n\n      if line == cls._PROGRAM_HEADERS_END_PATTERN:\n        break\n\n    return alignments\n\n\n  _DYNAMIC_SECTION_START_PATTERN = 'DynamicSection ['\n\n  _DYNAMIC_SECTION_NEEDED_PATTERN = re.compile(\n    '^  0x[0-9a-fA-F]+\\\\s+NEEDED\\\\s+Shared library: \\\\[(.*)\\\\]$')\n\n  _DYNAMIC_SECTION_SONAME_PATTERN = re.compile(\n    '^  0x[0-9a-fA-F]+\\\\s+SONAME\\\\s+Library soname: \\\\[(.*)\\\\]$')\n\n  _DYNAMIC_SECTION_END_PATTERN = ']'\n\n\n  @classmethod\n  def _parse_dynamic_table(cls, elf_file_path, lines_it):\n    \"\"\"Parse the dynamic table section.\"\"\"\n    dt_soname = os.path.basename(elf_file_path)\n    dt_needed = []\n\n    dynamic = cls._find_prefix(cls._DYNAMIC_SECTION_START_PATTERN, lines_it)\n    if not dynamic:\n      return (dt_soname, dt_needed)\n\n    for line in lines_it:\n      if line == cls._DYNAMIC_SECTION_END_PATTERN:\n        break\n\n      match = cls._DYNAMIC_SECTION_NEEDED_PATTERN.match(line)\n      if match:\n        dt_needed.append(match.group(1))\n        continue\n\n      match = cls._DYNAMIC_SECTION_SONAME_PATTERN.match(line)\n      if match:\n        dt_soname = match.group(1)\n        continue\n\n    return (dt_soname, dt_needed)\n\n\n  _DYNAMIC_SYMBOLS_START_PATTERN = 'DynamicSymbols ['\n  _DYNAMIC_SYMBOLS_END_PATTERN = ']'\n\n  _SYMBOL_ENTRY_START_PATTERN = '  Symbol {'\n  _SYMBOL_ENTRY_PATTERN = re.compile('^    ([A-Za-z0-9_]+): (.*)$')\n  _SYMBOL_ENTRY_PAREN_PATTERN = re.compile(\n    '\\\\s+\\\\((?:(?:\\\\d+)|(?:0x[0-9a-fA-F]+))\\\\)$')\n  _SYMBOL_ENTRY_END_PATTERN = '  }'\n\n\n  @staticmethod\n  def _parse_symbol_name(name_with_version):\n    \"\"\"Split `name_with_version` into name and version. This function may split\n    at last occurrence of `@@` or `@`.\"\"\"\n    pos = name_with_version.rfind('@')\n    if pos == -1:\n      name = name_with_version\n      version = ''\n    else:\n      if pos > 0 and name_with_version[pos - 1] == '@':\n        name = name_with_version[0:pos - 1]\n      else:\n        name = name_with_version[0:pos]\n      version = name_with_version[pos + 1:]\n    return (name, version)\n\n\n  @classmethod\n  def _parse_dynamic_symbols(cls, lines_it):\n    \"\"\"Parse dynamic symbol table and collect imported and exported symbols.\"\"\"\n    imported = collections.defaultdict(set)\n    exported = collections.defaultdict(set)\n\n    for symbol in cls._parse_dynamic_symbols_internal(lines_it):\n      name, version = cls._parse_symbol_name(symbol['Name'])\n      if name:\n        if symbol['Section'] == 'Undefined':\n          if symbol['Binding'] != 'Weak':\n            imported[name].add(version)\n        else:\n          if symbol['Binding'] != 'Local':\n            exported[name].add(version)\n\n    # Freeze the returned imported/exported dict.\n    return (dict(imported), dict(exported))\n\n\n  @classmethod\n  def _parse_dynamic_symbols_internal(cls, lines_it):\n    \"\"\"Parse symbols entries and yield each symbols.\"\"\"\n\n    if not cls._find_prefix(cls._DYNAMIC_SYMBOLS_START_PATTERN, lines_it):\n      return\n\n    for line in lines_it:\n      if line == cls._DYNAMIC_SYMBOLS_END_PATTERN:\n        return\n\n      if line == cls._SYMBOL_ENTRY_START_PATTERN:\n        symbol = {}\n        continue\n\n      if line == cls._SYMBOL_ENTRY_END_PATTERN:\n        yield symbol\n        symbol = None\n        continue\n\n      match = cls._SYMBOL_ENTRY_PATTERN.match(line)\n      if match:\n        key = match.group(1)\n        value = cls._SYMBOL_ENTRY_PAREN_PATTERN.sub('', match.group(2))\n        symbol[key] = value\n        continue\n\n\nclass Checker(object):\n  \"\"\"ELF file checker that checks DT_SONAME, DT_NEEDED, and symbols.\"\"\"\n\n  def __init__(self, llvm_readobj):\n    self._file_path = ''\n    self._file_under_test = None\n    self._shared_libs = []\n\n    self._llvm_readobj = llvm_readobj\n\n\n  if sys.stderr.isatty():\n    _ERROR_TAG = '\\033[0;1;31merror:\\033[m'  # Red error\n    _NOTE_TAG = '\\033[0;1;30mnote:\\033[m'  # Black note\n  else:\n    _ERROR_TAG = 'error:'  # Red error\n    _NOTE_TAG = 'note:'  # Black note\n\n\n  def _error(self, *args):\n    \"\"\"Emit an error to stderr.\"\"\"\n    print(self._file_path + ': ' + self._ERROR_TAG, *args, file=sys.stderr)\n\n\n  def _note(self, *args):\n    \"\"\"Emit a note to stderr.\"\"\"\n    print(self._file_path + ': ' + self._NOTE_TAG, *args, file=sys.stderr)\n\n\n  def _load_elf_file(self, path, skip_bad_elf_magic):\n    \"\"\"Load an ELF file from the `path`.\"\"\"\n    try:\n      return ELFParser.open(path, self._llvm_readobj)\n    except (IOError, OSError):\n      self._error('Failed to open \"{}\".'.format(path))\n      sys.exit(2)\n    except ELFInvalidMagicError:\n      if skip_bad_elf_magic:\n        sys.exit(0)\n      else:\n        self._error('File \"{}\" must have a valid ELF magic word.'.format(path))\n        sys.exit(2)\n    except:\n      self._error('An unknown error occurred while opening \"{}\".'.format(path))\n      raise\n\n\n  def load_file_under_test(self, path, skip_bad_elf_magic,\n                           skip_unknown_elf_machine):\n    \"\"\"Load file-under-test (either an executable or a shared lib).\"\"\"\n    self._file_path = path\n    self._file_under_test = self._load_elf_file(path, skip_bad_elf_magic)\n\n    if skip_unknown_elf_machine and \\\n        self._file_under_test.header.e_machine not in _KNOWN_MACHINES:\n      sys.exit(0)\n\n\n  def load_shared_libs(self, shared_lib_paths):\n    \"\"\"Load shared libraries.\"\"\"\n    for path in shared_lib_paths:\n      self._shared_libs.append(self._load_elf_file(path, False))\n\n\n  def check_dt_soname(self, soname):\n    \"\"\"Check whether DT_SONAME matches installation file name.\"\"\"\n    if self._file_under_test.dt_soname != soname:\n      self._error('DT_SONAME \"{}\" must be equal to the file name \"{}\".'\n                  .format(self._file_under_test.dt_soname, soname))\n      sys.exit(2)\n\n\n  def check_dt_needed(self, system_shared_lib_names):\n    \"\"\"Check whether all DT_NEEDED entries are specified in the build\n    system.\"\"\"\n\n    missing_shared_libs = False\n\n    # Collect the DT_SONAMEs from shared libs specified in the build system.\n    specified_sonames = {lib.dt_soname for lib in self._shared_libs}\n\n    # Chech whether all DT_NEEDED entries are specified.\n    for lib in self._file_under_test.dt_needed:\n      if lib not in specified_sonames:\n        self._error(f'DT_NEEDED \"{lib}\" is not specified in shared_libs.')\n        missing_shared_libs = True\n\n    if missing_shared_libs:\n      dt_needed = sorted(set(self._file_under_test.dt_needed))\n      modules = [re.sub('\\\\.so$', '', lib) for lib in dt_needed]\n\n      # Remove system shared libraries from the suggestion since they are added\n      # by default.\n      modules = [name for name in modules\n                 if name not in system_shared_lib_names]\n\n      self._note()\n      self._note('Fix suggestions:')\n      self._note(\n        '  Android.bp: shared_libs: [' +\n        ', '.join('\"' + module + '\"' for module in modules) + '],')\n      self._note(\n        '  Android.mk: LOCAL_SHARED_LIBRARIES := ' + ' '.join(modules))\n\n      self._note()\n      self._note('If the fix above doesn\\'t work, bypass this check with:')\n      self._note('  Android.bp: check_elf_files: false,')\n      self._note('  Android.mk: LOCAL_CHECK_ELF_FILES := false')\n\n      sys.exit(2)\n\n  def check_max_page_size(self, max_page_size):\n    if self._file_under_test.header.e_machine in _32_BIT_MACHINES:\n      # Skip test on 32-bit machines. 16 KB pages is an arm64 feature\n      # and no 32-bit systems in Android use it.\n      return\n\n    for alignment in self._file_under_test.alignments:\n      if alignment % max_page_size != 0:\n        self._error(f'Load segment has alignment {alignment} but '\n                    f'{max_page_size} required.')\n        self._note()\n        self._note('Fix suggestions:')\n        self._note(f'  use linker flag \"-Wl,-z,max-page-size={max_page_size}\" '\n                   f'when compiling this lib')\n        self._note()\n        self._note('If the fix above doesn\\'t work, bypass this check with:')\n        self._note('  Android.bp: ignore_max_page_size: true,')\n        self._note('  Android.mk: LOCAL_IGNORE_MAX_PAGE_SIZE := true')\n        self._note('  Device mk: PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE := false')\n\n        # TODO: instead of exiting immediately, we may want to collect the\n        # errors from all checks and emit them at once\n        sys.exit(2)\n\n  @staticmethod\n  def _find_symbol(lib, name, version):\n    \"\"\"Check whether the symbol name and version matches a definition in\n    lib.\"\"\"\n    try:\n      lib_sym_vers = lib.exported[name]\n    except KeyError:\n      return False\n    if version == '':  # Symbol version is not requested\n      return True\n    return version in lib_sym_vers\n\n\n  @classmethod\n  def _find_symbol_from_libs(cls, libs, name, version):\n    \"\"\"Check whether the symbol name and version is defined in one of the\n    shared libraries in libs.\"\"\"\n    for lib in libs:\n      if cls._find_symbol(lib, name, version):\n        return lib\n    return None\n\n\n  def check_symbols(self):\n    \"\"\"Check whether all undefined symbols are resolved to a definition.\"\"\"\n    all_elf_files = [self._file_under_test] + self._shared_libs\n    missing_symbols = []\n    for sym, imported_vers in self._file_under_test.imported.items():\n      for imported_ver in imported_vers:\n        lib = self._find_symbol_from_libs(all_elf_files, sym, imported_ver)\n        if not lib:\n          missing_symbols.append((sym, imported_ver))\n\n    if missing_symbols:\n      for sym, ver in sorted(missing_symbols):\n        if ver:\n          sym += '@' + ver\n        self._error(f'Unresolved symbol: {sym}')\n\n      self._note()\n      self._note('Some dependencies might be changed, thus the symbol(s) '\n                 'above cannot be resolved.')\n      self._note(f'Please re-build the prebuilt file: \"{self._file_path}\".')\n\n      self._note()\n      self._note('If this is a new prebuilt file and it is designed to have '\n                 'unresolved symbols, add one of the following properties:')\n      self._note('  Android.bp: allow_undefined_symbols: true,')\n      self._note('  Android.mk: LOCAL_ALLOW_UNDEFINED_SYMBOLS := true')\n\n      sys.exit(2)\n\n\ndef _parse_args():\n  \"\"\"Parse command line options.\"\"\"\n  parser = argparse.ArgumentParser()\n\n  # Input file\n  parser.add_argument('file',\n                      help='Path to the input file to be checked')\n  parser.add_argument('--soname',\n                      help='Shared object name of the input file')\n\n  # Shared library dependencies\n  parser.add_argument('--shared-lib', action='append', default=[],\n                      help='Path to shared library dependencies')\n\n  # System Shared library names\n  parser.add_argument('--system-shared-lib', action='append', default=[],\n                      help='System shared libraries to be hidden from fix '\n                      'suggestions')\n\n  # Check options\n  parser.add_argument('--skip-bad-elf-magic', action='store_true',\n                      help='Ignore the input file without the ELF magic word')\n  parser.add_argument('--skip-unknown-elf-machine', action='store_true',\n                      help='Ignore the input file with unknown machine ID')\n  parser.add_argument('--allow-undefined-symbols', action='store_true',\n                      help='Ignore unresolved undefined symbols')\n  parser.add_argument('--max-page-size', action='store', type=int,\n                      help='Required page size alignment support')\n\n  # Other options\n  parser.add_argument('--llvm-readobj',\n                      help='Path to the llvm-readobj executable')\n\n  return parser.parse_args()\n\n\ndef main():\n  \"\"\"Main function\"\"\"\n  args = _parse_args()\n\n  llvm_readobj = args.llvm_readobj\n  if not llvm_readobj:\n    llvm_readobj = _get_llvm_readobj()\n\n  # Load ELF files\n  checker = Checker(llvm_readobj)\n  checker.load_file_under_test(\n    args.file, args.skip_bad_elf_magic, args.skip_unknown_elf_machine)\n  checker.load_shared_libs(args.shared_lib)\n\n  # Run checks\n  if args.soname:\n    checker.check_dt_soname(args.soname)\n\n  checker.check_dt_needed(args.system_shared_lib)\n\n  if args.max_page_size:\n    checker.check_max_page_size(args.max_page_size)\n\n  if not args.allow_undefined_symbols:\n    checker.check_symbols()\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tools/check_identical_lib.sh",
    "content": "#!/bin/bash\nset -e\n\nSTRIP_PATH=\"${1}\"\nCORE=\"${2}\"\nVENDOR=\"${3}\"\n\nTMPDIR=\"$(mktemp -d ${CORE}.vndk_lib_check.XXXXXXXX)\"\nstripped_core=\"${TMPDIR}/core\"\nstripped_vendor=\"${TMPDIR}/vendor\"\n\nfunction cleanup() {\n  rm -f \"${stripped_core}\" \"${stripped_vendor}\"\n  rmdir \"${TMPDIR}\"\n}\ntrap cleanup EXIT\n\nfunction strip_lib() {\n  ${STRIP_PATH} \\\n    -i ${1} \\\n    -o ${2} \\\n    -d /dev/null \\\n    --remove-build-id\n}\n\nstrip_lib ${CORE} ${stripped_core}\nstrip_lib ${VENDOR} ${stripped_vendor}\nif ! cmp -s ${stripped_core} ${stripped_vendor}; then\n  echo \"ERROR: VNDK library $(basename ${CORE%.so}) has different core and\" \\\n    \"vendor variants! This means that the copy used in the system.img/etc\" \\\n    \"and vendor.img/etc images are different. In order to preserve space on\" \\\n    \"some devices, it is helpful if they are the same. Frequently, \" \\\n    \"libraries are different because they or their dependencies compile\" \\\n    \"things based on the macro '__ANDROID_VNDK__' or they specify custom\" \\\n    \"options under 'target: { vendor: { ... } }'. Here are some possible\" \\\n    \"resolutions:\"\n  echo \"ERROR: 1). Remove differences, possibly using the libvndksupport\" \\\n    \"function android_is_in_vendor_process in order to turn this into a\" \\\n    \"runtime difference.\"\n  echo \"ERROR: 2). Add the library to the VndkMustUseVendorVariantList\" \\\n    \"variable in build/soong/cc/config/vndk.go, which is used to\" \\\n    \"acknowledge this difference.\"\n  exit 1\nfi\n"
  },
  {
    "path": "tools/check_radio_versions.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2012 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport sys\nimport os\n\ntry:\n  from hashlib import sha1\nexcept ImportError:\n  from sha import sha as sha1\n\nimport argparse\n\nparser = argparse.ArgumentParser()\nparser.add_argument(\"--board_info_txt\", nargs=\"?\", required=True)\nparser.add_argument(\"--board_info_check\", nargs=\"*\", required=True)\nargs = parser.parse_args()\n\nif not args.board_info_txt:\n  sys.exit(0)\n\nbuild_info = {}\nf = open(args.board_info_txt)\nfor line in f:\n  line = line.strip()\n  if line.startswith(\"require\"):\n    key, value = line.split()[1].split(\"=\", 1)\n    build_info[key] = value\nf.close()\n\nbad = False\n\nfor item in args.board_info_check:\n  key, fn = item.split(\":\", 1)\n\n  values = build_info.get(key, None)\n  if not values:\n    continue\n  values = values.split(\"|\")\n\n  f = open(fn, \"rb\")\n  digest = sha1(f.read()).hexdigest()\n  f.close()\n\n  versions = {}\n  try:\n    f = open(fn + \".sha1\")\n  except IOError:\n    if not bad: print()\n    print(\"*** Error opening \\\"%s.sha1\\\"; can't verify %s\" % (fn, key))\n    bad = True\n    continue\n  for line in f:\n    line = line.strip()\n    if not line or line.startswith(\"#\"): continue\n    h, v = line.split()\n    versions[h] = v\n\n  if digest not in versions:\n    if not bad: print()\n    print(\"*** SHA-1 hash of \\\"%s\\\" doesn't appear in \\\"%s.sha1\\\"\" % (fn, fn))\n    bad = True\n    continue\n\n  if versions[digest] not in values:\n    if not bad: print()\n    print(\"*** \\\"%s\\\" is version %s; not any %s allowed by \\\"%s\\\".\" % (\n        fn, versions[digest], key, args.board_info_txt))\n    bad = True\n\nif bad:\n  print()\n  sys.exit(1)\n"
  },
  {
    "path": "tools/compare_builds.py",
    "content": "#!/usr/bin/env -S python3 -u\n\n\"\"\"\nThis script helps find various build behaviors that make builds less hermetic\nand repeatable. Depending on the flags, it runs a sequence of builds and looks\nfor files that have changed or have been improperly regenerated, updating\ntheir timestamps incorrectly. It also looks for changes that the build has\ndone to the source tree, and for files whose contents are dependent on the\nlocation of the out directory.\n\nThis utility has two major modes, full and incremental. By default, this tool\nruns in full mode. To run in incremental mode, pass the --incremental flag.\n\n\nFULL MODE\n\nIn full mode, this tool helps verify BUILD CORRECTNESS by examining its\nREPEATABILITY. In full mode, this tool runs two complete builds in different\ndirectories and compares the CONTENTS of the two directories. Lists of any\nfiles that are added, removed or changed are printed, sorted by the timestamp\nof that file, to aid finding which dependencies trigger the rebuilding of\nother files.\n\n\nINCREMENTAL MODE\n\nIn incremental mode, this tool helps verfiy the SPEED of the build. It runs two\nbuilds and looks at the TIMESTAMPS of the generated files, and reports files\nthat were changed by the second build. In theory, an incremental build with no\nsource files touched should not have any generated targets changed. As in full\nbuilds, the file list is returned sorted by timestamp.\n\n\nOTHER CHECKS\n\nIn both full and incremental mode, this tool looks at the timestamps of all\nsource files in the tree, and reports on files that have been touched. In the\noutput, these are labeled with the header \"Source files touched after start of\nbuild.\"\n\nIn addition, by default, this tool sets the OUT_DIR environment variable to\nsomething other than \"out\" in order to find build rules that are not respecting\nthe OUT_DIR. If you see these, you should fix them, but if your build can not\ncomplete for some reason because of this, you can pass the --no-check-out-dir\nflag to suppress this check.\n\n\nOTHER FLAGS\n\nIn full mode, the --detect-embedded-paths flag does the two builds in different\ndirectories, to help in finding rules that embed the out directory path into\nthe targets.\n\nThe --hide-build-output flag hides the output of successful bulds, to make\nscript output cleaner. The output of builds that fail is still shown.\n\nThe --no-build flag is useful if you have already done a build and would\njust like to re-run the analysis.\n\nThe --target flag lets you specify a build target other than the default\nfull build (droid). You can pass \"nothing\" as in the example below, or a\nspecific target, to reduce the scope of the checks performed.\n\nThe --touch flag lets you specify a list of source files to touch between\nthe builds, to examine the consequences of editing a particular file.\n\n\nEXAMPLE COMMANDLINES\n\nPlease run build/make/tools/compare_builds.py --help for a full listing\nof the commandline flags. Here are a sampling of useful combinations.\n\n  1. Find files changed during an incremental build that doesn't build\n     any targets.\n\n       build/make/tools/compare_builds.py --incremental --target nothing\n\n     Long incremental build times, or consecutive builds that re-run build actions\n     are usually caused by files being touched as part of loading the makefiles.\n\n     The nothing build (m nothing) loads the make and blueprint files, generates\n     the dependency graph, but then doesn't actually build any targets. Checking\n     against this build is the fastest and easiest way to find files that are\n     modified while makefiles are read, for example with $(shell) invocations.\n\n  2. Find packaging targets that are different, ignoring intermediate files.\n\n       build/make/tools/compare_builds.py --subdirs --detect-embedded-paths\n\n     These flags will compare the final staging directories for partitions,\n     as well as the APKs, apexes, testcases, and the like (the full directory\n     list is in the DEFAULT_DIRS variable below). Since these are the files\n     that are ultimately released, it is more important that these files be\n     replicable, even if the intermediates that went into them are not (for\n     example, when debugging symbols are stripped).\n\n  3. Check that all targets are repeatable.\n\n       build/make/tools/compare_builds.py --detect-embedded-paths\n\n     This check will list all of the differences in built targets that it can\n     find. Be aware that the AOSP tree still has quite a few targets that\n     are flagged by this check, so OEM changes might be lost in that list.\n     That said, each file shown here is a potential blocker for a repeatable\n     build.\n\n  4. See what targets are rebuilt when a file is touched between builds.\n\n       build/make/tools/compare_builds.py --incremental \\\n            --touch frameworks/base/core/java/android/app/Activity.java\n\n     This check simulates the common engineer workflow of touching a single\n     file and rebuilding the whole system. To see a restricted view, consider\n     also passing a --target option for a common use case. For example:\n\n       build/make/tools/compare_builds.py --incremental --target framework \\\n            --touch frameworks/base/core/java/android/app/Activity.java\n\"\"\"\n\nimport argparse\nimport itertools\nimport os\nimport shutil\nimport stat\nimport subprocess\nimport sys\n\n\n# Soong\nSOONG_UI = \"build/soong/soong_ui.bash\"\n\n\n# Which directories to use if no --subdirs is supplied without explicit directories.\nDEFAULT_DIRS = (\n    \"apex\",\n    \"data\",\n    \"product\",\n    \"ramdisk\",\n    \"recovery\",\n    \"root\",\n    \"system\",\n    \"system_ext\",\n    \"system_other\",\n    \"testcases\",\n    \"vendor\",\n)\n\n\n# Files to skip for incremental timestamp checking\nBUILD_INTERNALS_PREFIX_SKIP = (\n    \"soong/.glob/\",\n    \".path/\",\n)\n\n\nBUILD_INTERNALS_SUFFIX_SKIP = (\n    \"/soong/soong_build_metrics.pb\",\n    \"/.installable_test_files\",\n    \"/files.db\",\n    \"/.blueprint.bootstrap\",\n    \"/build_number.txt\",\n    \"/build.ninja\",\n    \"/.out-dir\",\n    \"/build_fingerprint.txt\",\n    \"/build_thumbprint.txt\",\n    \"/.copied_headers_list\",\n    \"/.installable_files\",\n)\n\n\nclass DiffType(object):\n  def __init__(self, code, message):\n    self.code = code\n    self.message = message\n\nDIFF_NONE = DiffType(\"DIFF_NONE\", \"Files are the same\")\nDIFF_MODE = DiffType(\"DIFF_MODE\", \"Stat mode bits differ\")\nDIFF_SIZE = DiffType(\"DIFF_SIZE\", \"File size differs\")\nDIFF_SYMLINK = DiffType(\"DIFF_SYMLINK\", \"Symlinks point to different locations\")\nDIFF_CONTENTS = DiffType(\"DIFF_CONTENTS\", \"File contents differ\")\n\n\ndef main():\n  argparser = argparse.ArgumentParser(description=\"Diff build outputs from two builds.\",\n                                      epilog=\"Run this command from the root of the tree.\"\n                                        + \" Before running this command, the build environment\"\n                                        + \" must be set up, including sourcing build/envsetup.sh\"\n                                        + \" and running lunch.\")\n  argparser.add_argument(\"--detect-embedded-paths\", action=\"store_true\",\n      help=\"Use unique out dirs to detect paths embedded in binaries.\")\n  argparser.add_argument(\"--incremental\", action=\"store_true\",\n      help=\"Compare which files are touched in two consecutive builds without a clean in between.\")\n  argparser.add_argument(\"--hide-build-output\", action=\"store_true\",\n      help=\"Don't print the build output for successful builds\")\n  argparser.add_argument(\"--no-build\", dest=\"run_build\", action=\"store_false\",\n      help=\"Don't build or clean, but do everything else.\")\n  argparser.add_argument(\"--no-check-out-dir\", dest=\"check_out_dir\", action=\"store_false\",\n      help=\"Don't check for rules not honoring movable out directories.\")\n  argparser.add_argument(\"--subdirs\", nargs=\"*\",\n      help=\"Only scan these subdirs of $PRODUCT_OUT instead of the whole out directory.\"\n           + \" The --subdirs argument with no listed directories will give a default list.\")\n  argparser.add_argument(\"--target\", default=\"droid\",\n      help=\"Make target to run. The default is droid\")\n  argparser.add_argument(\"--touch\", nargs=\"+\", default=[],\n      help=\"Files to touch between builds. Must pair with --incremental.\")\n  args = argparser.parse_args(sys.argv[1:])\n\n  if args.detect_embedded_paths and args.incremental:\n    sys.stderr.write(\"Can't pass --detect-embedded-paths and --incremental together.\\n\")\n    sys.exit(1)\n  if args.detect_embedded_paths and not args.check_out_dir:\n    sys.stderr.write(\"Can't pass --detect-embedded-paths and --no-check-out-dir together.\\n\")\n    sys.exit(1)\n  if args.touch and not args.incremental:\n    sys.stderr.write(\"The --incremental flag is required if the --touch flag is passed.\")\n    sys.exit(1)\n\n  AssertAtTop()\n  RequireEnvVar(\"TARGET_PRODUCT\")\n  RequireEnvVar(\"TARGET_BUILD_VARIANT\")\n\n  # Out dir file names:\n  #   - dir_prefix - The directory we'll put everything in (except for maybe the top level\n  #     out/ dir).\n  #   - *work_dir - The directory that we will build directly into. This is in dir_prefix\n  #     unless --no-check-out-dir is set.\n  #   - *out_dir - After building, if work_dir is different from out_dir, we move the out\n  #     directory to here so we can do the comparisions.\n  #   - timestamp_* - Files we touch so we know the various phases between the builds, so we\n  #     can compare timestamps of files.\n  if args.incremental:\n    dir_prefix = \"out_incremental\"\n    if args.check_out_dir:\n      first_work_dir = first_out_dir = dir_prefix + \"/out\"\n      second_work_dir = second_out_dir = dir_prefix + \"/out\"\n    else:\n      first_work_dir = first_out_dir = \"out\"\n      second_work_dir = second_out_dir = \"out\"\n  else:\n    dir_prefix = \"out_full\"\n    first_out_dir = dir_prefix + \"/out_1\"\n    second_out_dir = dir_prefix + \"/out_2\"\n    if not args.check_out_dir:\n      first_work_dir = second_work_dir = \"out\"\n    elif args.detect_embedded_paths:\n      first_work_dir = first_out_dir\n      second_work_dir = second_out_dir\n    else:\n      first_work_dir = dir_prefix + \"/work\"\n      second_work_dir = dir_prefix + \"/work\"\n  timestamp_start = dir_prefix + \"/timestamp_start\"\n  timestamp_between = dir_prefix + \"/timestamp_between\"\n  timestamp_end = dir_prefix + \"/timestamp_end\"\n\n  if args.run_build:\n    # Initial clean, if necessary\n    print(\"Cleaning \" + dir_prefix + \"/\")\n    Clean(dir_prefix)\n    print(\"Cleaning out/\")\n    Clean(\"out\")\n    CreateEmptyFile(timestamp_start)\n    print(\"Running the first build in \" + first_work_dir)\n    RunBuild(first_work_dir, first_out_dir, args.target, args.hide_build_output)\n    for f in args.touch:\n      print(\"Touching \" + f)\n      TouchFile(f)\n    CreateEmptyFile(timestamp_between)\n    print(\"Running the second build in \" + second_work_dir)\n    RunBuild(second_work_dir, second_out_dir, args.target, args.hide_build_output)\n    CreateEmptyFile(timestamp_end)\n    print(\"Done building\")\n    print()\n\n  # Which out directories to scan\n  if args.subdirs is not None:\n    if args.subdirs:\n      subdirs = args.subdirs\n    else:\n      subdirs = DEFAULT_DIRS\n    first_files = ProductFiles(RequireBuildVar(first_out_dir, \"PRODUCT_OUT\"), subdirs)\n    second_files = ProductFiles(RequireBuildVar(second_out_dir, \"PRODUCT_OUT\"), subdirs)\n  else:\n    first_files = OutFiles(first_out_dir)\n    second_files = OutFiles(second_out_dir)\n\n  printer = Printer()\n\n  if args.incremental:\n    # Find files that were rebuilt unnecessarily\n    touched_incrementally = FindOutFilesTouchedAfter(first_files,\n                                                     GetFileTimestamp(timestamp_between))\n    printer.PrintList(\"Touched in incremental build\", touched_incrementally)\n  else:\n    # Compare the two out dirs\n    added, removed, changed = DiffFileList(first_files, second_files)\n    printer.PrintList(\"Added\", added)\n    printer.PrintList(\"Removed\", removed)\n    printer.PrintList(\"Changed\", changed, \"%s %s\")\n\n  # Find files in the source tree that were touched\n  touched_during = FindSourceFilesTouchedAfter(GetFileTimestamp(timestamp_start))\n  printer.PrintList(\"Source files touched after start of build\", touched_during)\n\n  # Find files and dirs that were output to \"out\" and didn't respect $OUT_DIR\n  if args.check_out_dir:\n    bad_out_dir_contents = FindFilesAndDirectories(\"out\")\n    printer.PrintList(\"Files and directories created by rules that didn't respect $OUT_DIR\",\n                      bad_out_dir_contents)\n\n  # If we didn't find anything, print success message\n  if not printer.printed_anything:\n    print(\"No bad behaviors found.\")\n\n\ndef AssertAtTop():\n  \"\"\"If the current directory is not the top of an android source tree, print an error\n     message and exit.\"\"\"\n  if not os.access(SOONG_UI, os.X_OK):\n    sys.stderr.write(\"FAILED: Please run from the root of the tree.\\n\")\n    sys.exit(1)\n\n\ndef RequireEnvVar(name):\n  \"\"\"Gets an environment variable. If that fails, then print an error message and exit.\"\"\"\n  result = os.environ.get(name)\n  if not result:\n    sys.stderr.write(\"error: Can't determine %s. Please run lunch first.\\n\" % name)\n    sys.exit(1)\n  return result\n\n\ndef RunSoong(out_dir, args, capture_output):\n  env = dict(os.environ)\n  env[\"OUT_DIR\"] = out_dir\n  args = [SOONG_UI,] + args\n  if capture_output:\n    proc = subprocess.Popen(args, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\n    combined_output, none = proc.communicate()\n    return proc.returncode, combined_output\n  else:\n    result = subprocess.run(args, env=env)\n    return result.returncode, None\n\n\ndef GetBuildVar(out_dir, name):\n  \"\"\"Gets a variable from the build system.\"\"\"\n  returncode, output = RunSoong(out_dir, [\"--dumpvar-mode\", name], True)\n  if returncode != 0:\n    return None\n  else:\n    return output.decode(\"utf-8\").strip()\n\n\ndef RequireBuildVar(out_dir, name):\n  \"\"\"Gets a variable from the builds system. If that fails, then print an error\n     message and exit.\"\"\"\n  value = GetBuildVar(out_dir, name)\n  if not value:\n    sys.stderr.write(\"error: Can't determine %s. Please run lunch first.\\n\" % name)\n    sys.exit(1)\n  return value\n\n\ndef Clean(directory):\n  \"\"\"\"Deletes the supplied directory.\"\"\"\n  try:\n    shutil.rmtree(directory)\n  except FileNotFoundError:\n    pass\n\n\ndef RunBuild(work_dir, out_dir, target, hide_build_output):\n  \"\"\"Runs a build. If the build fails, prints a message and exits.\"\"\"\n  returncode, output = RunSoong(work_dir,\n                    [\"--build-mode\", \"--all-modules\", \"--dir=\" + os.getcwd(), target],\n                    hide_build_output)\n  if work_dir != out_dir:\n    os.replace(work_dir, out_dir)\n  if returncode != 0:\n    if hide_build_output:\n      # The build output was hidden, so print it now for debugging\n      sys.stderr.buffer.write(output)\n    sys.stderr.write(\"FAILED: Build failed. Stopping.\\n\")\n    sys.exit(1)\n\n\ndef DiffFileList(first_files, second_files):\n  \"\"\"Examines the files.\n\n  Returns:\n    Filenames of files in first_filelist but not second_filelist (added files)\n    Filenames of files in second_filelist but not first_filelist (removed files)\n    2-Tuple of filenames for the files that are in both but are different (changed files)\n  \"\"\"\n  # List of files, relative to their respective PRODUCT_OUT directories\n  first_filelist = sorted([x for x in first_files], key=lambda x: x[1])\n  second_filelist = sorted([x for x in second_files], key=lambda x: x[1])\n\n  added = []\n  removed = []\n  changed = []\n\n  first_index = 0\n  second_index = 0\n\n  while first_index < len(first_filelist) and second_index < len(second_filelist):\n    # Path relative to source root and path relative to PRODUCT_OUT\n    first_full_filename, first_relative_filename = first_filelist[first_index]\n    second_full_filename, second_relative_filename = second_filelist[second_index]\n\n    if first_relative_filename < second_relative_filename:\n      # Removed\n      removed.append(first_full_filename)\n      first_index += 1\n    elif first_relative_filename > second_relative_filename:\n      # Added\n      added.append(second_full_filename)\n      second_index += 1\n    else:\n      # Both present\n      diff_type = DiffFiles(first_full_filename, second_full_filename)\n      if diff_type != DIFF_NONE:\n        changed.append((first_full_filename, second_full_filename))\n      first_index += 1\n      second_index += 1\n\n  while first_index < len(first_filelist):\n    first_full_filename, first_relative_filename = first_filelist[first_index]\n    removed.append(first_full_filename)\n    first_index += 1\n\n  while second_index < len(second_filelist):\n    second_full_filename, second_relative_filename = second_filelist[second_index]\n    added.append(second_full_filename)\n    second_index += 1\n\n  return (SortByTimestamp(added),\n          SortByTimestamp(removed),\n          SortByTimestamp(changed, key=lambda item: item[1]))\n\n\ndef FindOutFilesTouchedAfter(files, timestamp):\n  \"\"\"Find files in the given file iterator that were touched after timestamp.\"\"\"\n  result = []\n  for full, relative in files:\n    ts = GetFileTimestamp(full)\n    if ts > timestamp:\n      result.append(TouchedFile(full, ts))\n  return [f.filename for f in sorted(result, key=lambda f: f.timestamp)]\n\n\ndef GetFileTimestamp(filename):\n  \"\"\"Get timestamp for a file (just wraps stat).\"\"\"\n  st = os.stat(filename, follow_symlinks=False)\n  return st.st_mtime\n\n\ndef SortByTimestamp(items, key=lambda item: item):\n  \"\"\"Sort the list by timestamp of files.\n  Args:\n    items - the list of items to sort\n    key - a function to extract a filename from each element in items\n  \"\"\"\n  return [x[0] for x in sorted([(item, GetFileTimestamp(key(item))) for item in items],\n                               key=lambda y: y[1])]\n\n\ndef FindSourceFilesTouchedAfter(timestamp):\n  \"\"\"Find files in the source tree that have changed after timestamp. Ignores\n  the out directory.\"\"\"\n  result = []\n  for root, dirs, files in os.walk(\".\", followlinks=False):\n    if root == \".\":\n      RemoveItemsFromList(dirs, (\".repo\", \"out\", \"out_full\", \"out_incremental\"))\n    for f in files:\n      full = os.path.sep.join((root, f))[2:]\n      ts = GetFileTimestamp(full)\n      if ts > timestamp:\n        result.append(TouchedFile(full, ts))\n  return [f.filename for f in sorted(result, key=lambda f: f.timestamp)]\n\n\ndef FindFilesAndDirectories(directory):\n  \"\"\"Finds all files and directories inside a directory.\"\"\"\n  result = []\n  for root, dirs, files in os.walk(directory, followlinks=False):\n    result += [os.path.sep.join((root, x, \"\")) for x in dirs]\n    result += [os.path.sep.join((root, x)) for x in files]\n  return result\n\n\ndef CreateEmptyFile(filename):\n  \"\"\"Create an empty file with now as the timestamp at filename.\"\"\"\n  try:\n    os.makedirs(os.path.dirname(filename))\n  except FileExistsError:\n    pass\n  open(filename, \"w\").close()\n  os.utime(filename)\n\n\ndef TouchFile(filename):\n  os.utime(filename)\n\n\ndef DiffFiles(first_filename, second_filename):\n  def AreFileContentsSame(remaining, first_filename, second_filename):\n    \"\"\"Compare the file contents. They must be known to be the same size.\"\"\"\n    CHUNK_SIZE = 32*1024\n    with open(first_filename, \"rb\") as first_file:\n      with open(second_filename, \"rb\") as second_file:\n        while remaining > 0:\n          size = min(CHUNK_SIZE, remaining)\n          if first_file.read(CHUNK_SIZE) != second_file.read(CHUNK_SIZE):\n            return False\n          remaining -= size\n        return True\n\n  first_stat = os.stat(first_filename, follow_symlinks=False)\n  second_stat = os.stat(first_filename, follow_symlinks=False)\n\n  # Mode bits\n  if first_stat.st_mode != second_stat.st_mode:\n    return DIFF_MODE\n\n  # File size\n  if first_stat.st_size != second_stat.st_size:\n    return DIFF_SIZE\n\n  # Contents\n  if stat.S_ISLNK(first_stat.st_mode):\n    if os.readlink(first_filename) != os.readlink(second_filename):\n      return DIFF_SYMLINK\n  elif stat.S_ISREG(first_stat.st_mode):\n    if not AreFileContentsSame(first_stat.st_size, first_filename, second_filename):\n      return DIFF_CONTENTS\n\n  return DIFF_NONE\n\n\nclass FileIterator(object):\n  \"\"\"Object that produces an iterator containing all files in a given directory.\n\n  Each iteration yields a tuple containing:\n\n  [0] (full) Path to file relative to source tree.\n  [1] (relative) Path to the file relative to the base directory given in the\n      constructor.\n  \"\"\"\n\n  def __init__(self, base_dir):\n    self._base_dir = base_dir\n\n  def __iter__(self):\n    return self._Iterator(self, self._base_dir)\n\n  def ShouldIncludeFile(self, root, path):\n    return False\n\n  class _Iterator(object):\n    def __init__(self, parent, base_dir):\n      self._parent = parent\n      self._base_dir = base_dir\n      self._walker = os.walk(base_dir, followlinks=False)\n      self._current_index = 0\n      self._current_dir = []\n\n    def __iter__(self):\n      return self\n\n    def __next__(self):\n      # os.walk's iterator will eventually terminate by raising StopIteration\n      while True:\n        if self._current_index >= len(self._current_dir):\n          root, dirs, files = self._walker.__next__()\n          full_paths = [os.path.sep.join((root, f)) for f in files]\n          pairs = [(f, f[len(self._base_dir)+1:]) for f in full_paths]\n          self._current_dir = [(full, relative) for full, relative in pairs\n                               if self._parent.ShouldIncludeFile(root, relative)]\n          self._current_index = 0\n          if not self._current_dir:\n            continue\n        index = self._current_index\n        self._current_index += 1\n        return self._current_dir[index]\n\n\nclass OutFiles(FileIterator):\n  \"\"\"Object that produces an iterator containing all files in a given out directory,\n  except for files which are known to be touched as part of build setup.\n  \"\"\"\n  def __init__(self, out_dir):\n    super().__init__(out_dir)\n    self._out_dir = out_dir\n\n  def ShouldIncludeFile(self, root, relative):\n    # Skip files in root, although note that this could actually skip\n    # files that are sadly generated directly into that directory.\n    if root == self._out_dir:\n      return False\n    # Skiplist\n    for skip in BUILD_INTERNALS_PREFIX_SKIP:\n      if relative.startswith(skip):\n        return False\n    for skip in BUILD_INTERNALS_SUFFIX_SKIP:\n      if relative.endswith(skip):\n        return False\n    return True\n\n\nclass ProductFiles(FileIterator):\n  \"\"\"Object that produces an iterator containing files in listed subdirectories of $PRODUCT_OUT.\n  \"\"\"\n  def __init__(self, product_out, subdirs):\n    super().__init__(product_out)\n    self._subdirs = subdirs\n\n  def ShouldIncludeFile(self, root, relative):\n    for subdir in self._subdirs:\n      if relative.startswith(subdir):\n        return True\n    return False\n\n\nclass TouchedFile(object):\n  \"\"\"A file in the out directory with a timestamp.\"\"\"\n  def __init__(self, filename, timestamp):\n    self.filename = filename\n    self.timestamp = timestamp\n\n\ndef RemoveItemsFromList(haystack, needles):\n  for needle in needles:\n    try:\n      haystack.remove(needle)\n    except ValueError:\n      pass\n\n\nclass Printer(object):\n  def __init__(self):\n    self.printed_anything = False\n\n  def PrintList(self, title, items, fmt=\"%s\"):\n    if items:\n      if self.printed_anything:\n        sys.stdout.write(\"\\n\")\n      sys.stdout.write(\"%s:\\n\" % title)\n      for item in items:\n        sys.stdout.write(\"  %s\\n\" % fmt % item)\n      self.printed_anything = True\n\n\nif __name__ == \"__main__\":\n  try:\n    main()\n  except KeyboardInterrupt:\n    pass\n\n\n# vim: ts=2 sw=2 sts=2 nocindent\n"
  },
  {
    "path": "tools/compliance/Android.bp",
    "content": "//\n// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nblueprint_go_binary {\n    name: \"compliance_checkmetadata\",\n    srcs: [\"cmd/checkmetadata/checkmetadata.go\"],\n    deps: [\n        \"compliance-module\",\n        \"projectmetadata-module\",\n        \"soong-response\",\n    ],\n    testSrcs: [\"cmd/checkmetadata/checkmetadata_test.go\"],\n}\n\nblueprint_go_binary {\n    name: \"compliance_checkshare\",\n    srcs: [\"cmd/checkshare/checkshare.go\"],\n    deps: [\n        \"compliance-module\",\n        \"soong-response\",\n    ],\n    testSrcs: [\"cmd/checkshare/checkshare_test.go\"],\n}\n\nblueprint_go_binary {\n    name: \"compliancenotice_shippedlibs\",\n    srcs: [\"cmd/shippedlibs/shippedlibs.go\"],\n    deps: [\n        \"compliance-module\",\n        \"soong-response\",\n    ],\n    testSrcs: [\"cmd/shippedlibs/shippedlibs_test.go\"],\n}\n\nblueprint_go_binary {\n    name: \"compliance_listshare\",\n    srcs: [\"cmd/listshare/listshare.go\"],\n    deps: [\n        \"compliance-module\",\n        \"soong-response\",\n    ],\n    testSrcs: [\"cmd/listshare/listshare_test.go\"],\n}\n\nblueprint_go_binary {\n    name: \"compliance_dumpgraph\",\n    srcs: [\"cmd/dumpgraph/dumpgraph.go\"],\n    deps: [\n        \"compliance-module\",\n        \"soong-response\",\n    ],\n    testSrcs: [\"cmd/dumpgraph/dumpgraph_test.go\"],\n}\n\nblueprint_go_binary {\n    name: \"compliance_dumpresolutions\",\n    srcs: [\"cmd/dumpresolutions/dumpresolutions.go\"],\n    deps: [\n        \"compliance-module\",\n        \"soong-response\",\n    ],\n    testSrcs: [\"cmd/dumpresolutions/dumpresolutions_test.go\"],\n}\n\nblueprint_go_binary {\n    name: \"htmlnotice\",\n    srcs: [\"cmd/htmlnotice/htmlnotice.go\"],\n    deps: [\n        \"compliance-module\",\n        \"blueprint-deptools\",\n        \"soong-response\",\n    ],\n    testSrcs: [\"cmd/htmlnotice/htmlnotice_test.go\"],\n}\n\nblueprint_go_binary {\n    name: \"compliance_rtrace\",\n    srcs: [\"cmd/rtrace/rtrace.go\"],\n    deps: [\n        \"compliance-module\",\n        \"soong-response\",\n    ],\n    testSrcs: [\"cmd/rtrace/rtrace_test.go\"],\n}\n\nblueprint_go_binary {\n    name: \"textnotice\",\n    srcs: [\"cmd/textnotice/textnotice.go\"],\n    deps: [\n        \"compliance-module\",\n        \"blueprint-deptools\",\n        \"soong-response\",\n    ],\n    testSrcs: [\"cmd/textnotice/textnotice_test.go\"],\n}\n\nblueprint_go_binary {\n    name: \"xmlnotice\",\n    srcs: [\"cmd/xmlnotice/xmlnotice.go\"],\n    deps: [\n        \"compliance-module\",\n        \"blueprint-deptools\",\n        \"soong-response\",\n    ],\n    testSrcs: [\"cmd/xmlnotice/xmlnotice_test.go\"],\n}\n\nbootstrap_go_package {\n    name: \"compliance-module\",\n    srcs: [\n        \"condition.go\",\n        \"conditionset.go\",\n        \"doc.go\",\n        \"graph.go\",\n        \"noticeindex.go\",\n        \"policy_policy.go\",\n        \"policy_resolve.go\",\n        \"policy_resolvenotices.go\",\n        \"policy_resolveshare.go\",\n        \"policy_resolveprivacy.go\",\n        \"policy_shareprivacyconflicts.go\",\n        \"policy_shipped.go\",\n        \"policy_walk.go\",\n        \"readgraph.go\",\n        \"resolution.go\",\n        \"resolutionset.go\",\n    ],\n    testSrcs: [\n        \"condition_test.go\",\n        \"conditionset_test.go\",\n        \"readgraph_test.go\",\n        \"policy_policy_test.go\",\n        \"policy_resolve_test.go\",\n        \"policy_resolvenotices_test.go\",\n        \"policy_resolveshare_test.go\",\n        \"policy_resolveprivacy_test.go\",\n        \"policy_shareprivacyconflicts_test.go\",\n        \"policy_shipped_test.go\",\n        \"policy_walk_test.go\",\n        \"resolutionset_test.go\",\n        \"test_util.go\",\n    ],\n    deps: [\n        \"compliance-test-fs-module\",\n        \"projectmetadata-module\",\n        \"golang-protobuf-proto\",\n        \"golang-protobuf-encoding-prototext\",\n        \"license_metadata_proto\",\n    ],\n    pkgPath: \"android/soong/tools/compliance\",\n}\n"
  },
  {
    "path": "tools/compliance/README.md",
    "content": "# Compliance\n\n<!-- Much of this content appears too in doc.go\nWhen changing this file consider whether the change also applies to doc.go -->\n\nPackage compliance provides an approved means for reading, consuming, and\nanalyzing license metadata graphs.\n\nAssuming the license metadata and dependencies are fully and accurately\nrecorded in the build system, any discrepancy between the official policy for\nopen source license compliance and this code is **a bug in this code.**\n\n## Naming\n\nAll of the code that directly reflects a policy decision belongs in a file with\na name begninning `policy_`. Changes to these files need to be authored or\nreviewed by someone in OSPO or whichever successor group governs policy.\n\nThe files with names not beginning `policy_` describe data types, and general,\nreusable algorithms.\n\nThe source code for binary tools and utilities appears under the `cmd/`\nsubdirectory. Other subdirectories contain reusable components that are not\n`compliance` per se.\n\n## Data Types\n\nA few principal types to understand are LicenseGraph, LicenseCondition, and\nResolutionSet.\n\n### LicenseGraph\n\nA LicenseGraph is an immutable graph of the targets and dependencies reachable\nfrom a specific set of root targets. In general, the root targets will be the\nartifacts in a release or distribution. While conceptually immutable, parts of\nthe graph may be loaded or evaluated lazily.\n\nConceptually, the graph itself will always be a directed acyclic graph. One\nrepresentation is a set of directed edges. Another is a set of nodes with\ndirected edges to their dependencies.\n\nThe edges have annotations, which can distinguish between build tools, runtime\ndependencies, and dependencies like 'contains' that make a derivative work.\n\n### LicenseCondition\n\nA LicenseCondition is an immutable tuple pairing a condition name with an\noriginating target. e.g. Per current policy, a static library licensed under an\nMIT license would pair a \"notice\" condition with the static library target, and\na dynamic license licensed under GPL would pair a \"restricted\" condition with\nthe dynamic library target.\n\n### ResolutionSet\n\nA ResolutionSet is an immutable set of `AttachesTo`, `ActsOn`, `Resolves`\ntuples describing how license conditions apply to targets.\n\n`AttachesTo` is the trigger for acting. Distribution of the target invokes\nthe policy.\n\n`ActsOn` is the target to share, give notice for, hide etc.\n\n`Resolves` is the set of conditions that the action resolves.\n\nFor most condition types, `ActsOn` will be the target where the condition\noriginated. For example, a notice condition policy means attribution or notice\nmust be given for the target where the condition originates. Likewise, a\nproprietary condition policy means the privacy of the target where the\ncondition originates must be respected. i.e. The thing acted on is the origin.\n\nRestricted conditions are different. The infectious nature of restricted often\nmeans sharing code that is not the target where the restricted condition\noriginates. Linking an MIT library to a GPL library implies a policy to share\nthe MIT library despite the MIT license having no source sharing requirement.\n\nIn this case, one or more resolution tuples will have the MIT license module in\n`ActsOn` and the restricted condition originating at the GPL library module in\n`Resolves`. These tuples will `AttachTo` every target that depends on the GPL\nlibrary because shipping any of those targets trigger the policy to share the\ncode.\n\n## Processes\n\n### ReadLicenseGraph\n\nThe principal means to ingest license metadata. Given the distribution targets,\nReadLicenseGraph populates the LicenseGraph for those root targets.\n\n### NoticeIndex.IndexLicenseTexts\n\nIndexLicenseTexts reads, deduplicates and caches license texts for notice\nfiles. Also reads and caches project metadata for deriving library names.\n\nThe algorithm for deriving library names has not been dictated by OSPO policy,\nbut reflects a pragmatic attempt to comply with Android policy regarding\nunreleased product names, proprietary partner names etc.\n\n### projectmetadata.Index.MetadataForProjects\n\nMetadataForProjects reads, deduplicates and caches project METADATA files used\nfor notice library names, and various properties appearing in SBOMs.\n"
  },
  {
    "path": "tools/compliance/cmd/checkmetadata/checkmetadata.go",
    "content": "// Copyright 2022 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"android/soong/response\"\n\t\"android/soong/tools/compliance\"\n\t\"android/soong/tools/compliance/projectmetadata\"\n)\n\nvar (\n\tfailNoneRequested = fmt.Errorf(\"\\nNo projects requested\")\n)\n\nfunc main() {\n\tvar expandedArgs []string\n\tfor _, arg := range os.Args[1:] {\n\t\tif strings.HasPrefix(arg, \"@\") {\n\t\t\tf, err := os.Open(strings.TrimPrefix(arg, \"@\"))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\trespArgs, err := response.ReadRspFile(f)\n\t\t\tf.Close()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\texpandedArgs = append(expandedArgs, respArgs...)\n\t\t} else {\n\t\t\texpandedArgs = append(expandedArgs, arg)\n\t\t}\n\t}\n\n\tflags := flag.NewFlagSet(\"flags\", flag.ExitOnError)\n\n\tflags.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, `Usage: %s {-o outfile} projectdir {projectdir...}\n\nTries to open the METADATA.android or METADATA file in each projectdir\nreporting any errors on stderr.\n\nReports \"FAIL\" to stdout if any errors found and exits with status 1.\n\nOtherwise, reports \"PASS\" and the number of project metadata files\nfound exiting with status 0.\n`, filepath.Base(os.Args[0]))\n\t\tflags.PrintDefaults()\n\t}\n\n\toutputFile := flags.String(\"o\", \"-\", \"Where to write the output. (default stdout)\")\n\n\tflags.Parse(expandedArgs)\n\n\t// Must specify at least one root target.\n\tif flags.NArg() == 0 {\n\t\tflags.Usage()\n\t\tos.Exit(2)\n\t}\n\n\tif len(*outputFile) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"must specify file for -o; use - for stdout\\n\")\n\t\tos.Exit(2)\n\t} else {\n\t\tdir, err := filepath.Abs(filepath.Dir(*outputFile))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot determine path to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfi, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot read directory %q of %q: %s\\n\", dir, *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !fi.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"parent %q of %q is not a directory\\n\", dir, *outputFile)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar ofile io.Writer\n\tofile = os.Stdout\n\tvar obuf *bytes.Buffer\n\tif *outputFile != \"-\" {\n\t\tobuf = &bytes.Buffer{}\n\t\tofile = obuf\n\t}\n\n\terr := checkProjectMetadata(ofile, os.Stderr, compliance.FS, flags.Args()...)\n\tif err != nil {\n\t\tif err == failNoneRequested {\n\t\t\tflags.Usage()\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"%s\\n\", err.Error())\n\t\tfmt.Fprintln(ofile, \"FAIL\")\n\t\tos.Exit(1)\n\t}\n\tif *outputFile != \"-\" {\n\t\terr := os.WriteFile(*outputFile, obuf.Bytes(), 0666)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write output to %q from %q: %s\\n\", *outputFile, os.Getenv(\"PWD\"), err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tos.Exit(0)\n}\n\n// checkProjectMetadata implements the checkmetadata utility.\nfunc checkProjectMetadata(stdout, stderr io.Writer, rootFS fs.FS, projects ...string) error {\n\n\tif len(projects) < 1 {\n\t\treturn failNoneRequested\n\t}\n\n\t// Read the project metadata files from `projects`\n\tix := projectmetadata.NewIndex(rootFS)\n\tpms, err := ix.MetadataForProjects(projects...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read project metadata file(s) %q from %q: %w\\n\", projects, os.Getenv(\"PWD\"), err)\n\t}\n\n\tfmt.Fprintf(stdout, \"PASS -- parsed %d project metadata files for %d projects\\n\", len(pms), len(projects))\n\treturn nil\n}\n"
  },
  {
    "path": "tools/compliance/cmd/checkmetadata/checkmetadata_test.go",
    "content": "// Copyright 2022 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the parent directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"..\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc Test(t *testing.T) {\n\ttests := []struct {\n\t\tname           string\n\t\tprojects       []string\n\t\texpectedStdout string\n\t}{\n\t\t{\n\t\t\tname:           \"1p\",\n\t\t\tprojects:       []string{\"firstparty\"},\n\t\t\texpectedStdout: \"PASS -- parsed 1 project metadata files for 1 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"notice\",\n\t\t\tprojects:       []string{\"notice\"},\n\t\t\texpectedStdout: \"PASS -- parsed 1 project metadata files for 1 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"1p+notice\",\n\t\t\tprojects:       []string{\"firstparty\", \"notice\"},\n\t\t\texpectedStdout: \"PASS -- parsed 2 project metadata files for 2 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"reciprocal\",\n\t\t\tprojects:       []string{\"reciprocal\"},\n\t\t\texpectedStdout: \"PASS -- parsed 1 project metadata files for 1 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"1p+notice+reciprocal\",\n\t\t\tprojects:       []string{\"firstparty\", \"notice\", \"reciprocal\"},\n\t\t\texpectedStdout: \"PASS -- parsed 3 project metadata files for 3 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"restricted\",\n\t\t\tprojects:       []string{\"restricted\"},\n\t\t\texpectedStdout: \"PASS -- parsed 1 project metadata files for 1 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"1p+notice+reciprocal+restricted\",\n\t\t\tprojects:       []string{\n\t\t\t\t\"firstparty\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t},\n\t\t\texpectedStdout: \"PASS -- parsed 4 project metadata files for 4 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"proprietary\",\n\t\t\tprojects:       []string{\"proprietary\"},\n\t\t\texpectedStdout: \"PASS -- parsed 1 project metadata files for 1 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"1p+notice+reciprocal+restricted+proprietary\",\n\t\t\tprojects:       []string{\n\t\t\t\t\"firstparty\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"proprietary\",\n\t\t\t},\n\t\t\texpectedStdout: \"PASS -- parsed 5 project metadata files for 5 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"missing1\",\n\t\t\tprojects:       []string{\"regressgpl1\"},\n\t\t\texpectedStdout: \"PASS -- parsed 0 project metadata files for 1 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"1p+notice+reciprocal+restricted+proprietary+missing1\",\n\t\t\tprojects:       []string{\n\t\t\t\t\"firstparty\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"regressgpl1\",\n\t\t\t},\n\t\t\texpectedStdout: \"PASS -- parsed 5 project metadata files for 6 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"missing2\",\n\t\t\tprojects:       []string{\"regressgpl2\"},\n\t\t\texpectedStdout: \"PASS -- parsed 0 project metadata files for 1 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"1p+notice+reciprocal+restricted+proprietary+missing1+missing2\",\n\t\t\tprojects:       []string{\n\t\t\t\t\"firstparty\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"regressgpl1\",\n\t\t\t\t\"regressgpl2\",\n\t\t\t},\n\t\t\texpectedStdout: \"PASS -- parsed 5 project metadata files for 7 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"missing2+1p+notice+reciprocal+restricted+proprietary+missing1\",\n\t\t\tprojects:       []string{\n\t\t\t\t\"regressgpl2\",\n\t\t\t\t\"firstparty\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"regressgpl1\",\n\t\t\t},\n\t\t\texpectedStdout: \"PASS -- parsed 5 project metadata files for 7 projects\",\n\t\t},\n\t\t{\n\t\t\tname:           \"missing2+1p+notice+missing1+reciprocal+restricted+proprietary\",\n\t\t\tprojects:       []string{\n\t\t\t\t\"regressgpl2\",\n\t\t\t\t\"firstparty\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"regressgpl1\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"proprietary\",\n\t\t\t},\n\t\t\texpectedStdout: \"PASS -- parsed 5 project metadata files for 7 projects\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\tprojects := make([]string, 0, len(tt.projects))\n\t\t\tfor _, project := range tt.projects {\n\t\t\t\tprojects = append(projects, \"testdata/\"+project)\n\t\t\t}\n\t\t\terr := checkProjectMetadata(stdout, stderr, compliance.GetFS(\"\"), projects...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"checkmetadata: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvar actualStdout string\n\t\t\tfor _, s := range strings.Split(stdout.String(), \"\\n\") {\n\t\t\t\tts := strings.TrimLeft(s, \" \\t\")\n\t\t\t\tif len(ts) < 1 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(actualStdout) > 0 {\n\t\t\t\t\tt.Errorf(\"checkmetadata: unexpected multiple output lines %q, want %q\", actualStdout+\"\\n\"+ts, tt.expectedStdout)\n\t\t\t\t}\n\t\t\t\tactualStdout = ts\n\t\t\t}\n\t\t\tif actualStdout != tt.expectedStdout {\n\t\t\t\tt.Errorf(\"checkmetadata: unexpected stdout %q, want %q\", actualStdout, tt.expectedStdout)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/cmd/checkshare/checkshare.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"android/soong/response\"\n\t\"android/soong/tools/compliance\"\n)\n\nvar (\n\tfailConflicts     = fmt.Errorf(\"conflicts\")\n\tfailNoneRequested = fmt.Errorf(\"\\nNo metadata files requested\")\n\tfailNoLicenses    = fmt.Errorf(\"No licenses\")\n)\n\n// byError orders conflicts by error string\ntype byError []compliance.SourceSharePrivacyConflict\n\nfunc (l byError) Len() int           { return len(l) }\nfunc (l byError) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }\nfunc (l byError) Less(i, j int) bool { return l[i].Error() < l[j].Error() }\n\nfunc main() {\n\tvar expandedArgs []string\n\tfor _, arg := range os.Args[1:] {\n\t\tif strings.HasPrefix(arg, \"@\") {\n\t\t\tf, err := os.Open(strings.TrimPrefix(arg, \"@\"))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\trespArgs, err := response.ReadRspFile(f)\n\t\t\tf.Close()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\texpandedArgs = append(expandedArgs, respArgs...)\n\t\t} else {\n\t\t\texpandedArgs = append(expandedArgs, arg)\n\t\t}\n\t}\n\n\tflags := flag.NewFlagSet(\"flags\", flag.ExitOnError)\n\n\tflags.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, `Usage: %s {-o outfile} file.meta_lic {file.meta_lic...}\n\nReports on stderr any targets where policy says that the source both\nmust and must not be shared. The error report indicates the target, the\nlicense condition that has a source privacy policy, and the license\ncondition that has a source sharing policy.\n\nAny given target may appear multiple times with different combinations\nof conflicting license conditions.\n\nIf all the source code that policy says must be shared may be shared,\noutputs \"PASS\" to stdout and exits with status 0.\n\nIf policy says any source must both be shared and not be shared,\noutputs \"FAIL\" to stdout and exits with status 1.\n`, filepath.Base(os.Args[0]))\n\t\tflags.PrintDefaults()\n\t}\n\n\toutputFile := flags.String(\"o\", \"-\", \"Where to write the output. (default stdout)\")\n\n\tflags.Parse(expandedArgs)\n\n\t// Must specify at least one root target.\n\tif flags.NArg() == 0 {\n\t\tflags.Usage()\n\t\tos.Exit(2)\n\t}\n\n\tif len(*outputFile) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"must specify file for -o; use - for stdout\\n\")\n\t\tos.Exit(2)\n\t} else {\n\t\tdir, err := filepath.Abs(filepath.Dir(*outputFile))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot determine path to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfi, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot read directory %q of %q: %s\\n\", dir, *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !fi.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"parent %q of %q is not a directory\\n\", dir, *outputFile)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar ofile io.Writer\n\tofile = os.Stdout\n\tvar obuf *bytes.Buffer\n\tif *outputFile != \"-\" {\n\t\tobuf = &bytes.Buffer{}\n\t\tofile = obuf\n\t}\n\n\terr := checkShare(ofile, os.Stderr, compliance.FS, flags.Args()...)\n\tif err != nil {\n\t\tif err != failConflicts {\n\t\t\tif err == failNoneRequested {\n\t\t\t\tflags.Usage()\n\t\t\t}\n\t\t\tfmt.Fprintf(os.Stderr, \"%s\\n\", err.Error())\n\t\t}\n\t\tos.Exit(1)\n\t}\n\tif *outputFile != \"-\" {\n\t\terr := os.WriteFile(*outputFile, obuf.Bytes(), 0666)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write output to %q from %q: %s\\n\", *outputFile, os.Getenv(\"PWD\"), err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tos.Exit(0)\n}\n\n// checkShare implements the checkshare utility.\nfunc checkShare(stdout, stderr io.Writer, rootFS fs.FS, files ...string) error {\n\n\tif len(files) < 1 {\n\t\treturn failNoneRequested\n\t}\n\n\t// Read the license graph from the license metadata files (*.meta_lic).\n\tlicenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license metadata file(s) %q from %q: %w\\n\", files, os.Getenv(\"PWD\"), err)\n\t}\n\tif licenseGraph == nil {\n\t\treturn failNoLicenses\n\t}\n\n\t// Apply policy to find conflicts and report them to stderr lexicographically ordered.\n\tconflicts := compliance.ConflictingSharedPrivateSource(licenseGraph)\n\tsort.Sort(byError(conflicts))\n\tfor _, conflict := range conflicts {\n\t\tfmt.Fprintln(stderr, conflict.Error())\n\t}\n\n\t// Indicate pass or fail on stdout.\n\tif len(conflicts) > 0 {\n\t\tfmt.Fprintln(stdout, \"FAIL\")\n\t\treturn failConflicts\n\t}\n\tfmt.Fprintln(stdout, \"PASS\")\n\treturn nil\n}\n"
  },
  {
    "path": "tools/compliance/cmd/checkshare/checkshare_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the parent directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"..\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\ntype outcome struct {\n\ttarget           string\n\tprivacyCondition string\n\tshareCondition   string\n}\n\nfunc (o *outcome) String() string {\n\treturn fmt.Sprintf(\"%s %s and must share from %s\", o.target, o.privacyCondition, o.shareCondition)\n}\n\ntype outcomeList []*outcome\n\nfunc (ol outcomeList) String() string {\n\tresult := \"\"\n\tfor _, o := range ol {\n\t\tresult = result + o.String() + \"\\n\"\n\t}\n\treturn result\n}\n\nfunc Test(t *testing.T) {\n\ttests := []struct {\n\t\tcondition        string\n\t\tname             string\n\t\toutDir           string\n\t\troots            []string\n\t\texpectedStdout   string\n\t\texpectedOutcomes outcomeList\n\t}{\n\t\t{\n\t\t\tcondition:      \"firstparty\",\n\t\t\tname:           \"apex\",\n\t\t\troots:          []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"firstparty\",\n\t\t\tname:           \"container\",\n\t\t\troots:          []string{\"container.zip.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"firstparty\",\n\t\t\tname:           \"application\",\n\t\t\troots:          []string{\"application.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"firstparty\",\n\t\t\tname:           \"binary\",\n\t\t\troots:          []string{\"bin/bin2.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"firstparty\",\n\t\t\tname:           \"library\",\n\t\t\troots:          []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"notice\",\n\t\t\tname:           \"apex\",\n\t\t\troots:          []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"notice\",\n\t\t\tname:           \"container\",\n\t\t\troots:          []string{\"container.zip.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"notice\",\n\t\t\tname:           \"application\",\n\t\t\troots:          []string{\"application.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"notice\",\n\t\t\tname:           \"binary\",\n\t\t\troots:          []string{\"bin/bin2.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"notice\",\n\t\t\tname:           \"library\",\n\t\t\troots:          []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"reciprocal\",\n\t\t\tname:           \"apex\",\n\t\t\troots:          []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"reciprocal\",\n\t\t\tname:           \"container\",\n\t\t\troots:          []string{\"container.zip.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"reciprocal\",\n\t\t\tname:           \"application\",\n\t\t\troots:          []string{\"application.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"reciprocal\",\n\t\t\tname:           \"binary\",\n\t\t\troots:          []string{\"bin/bin2.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"reciprocal\",\n\t\t\tname:           \"library\",\n\t\t\troots:          []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"restricted\",\n\t\t\tname:           \"apex\",\n\t\t\troots:          []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"restricted\",\n\t\t\tname:           \"container\",\n\t\t\troots:          []string{\"container.zip.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"restricted\",\n\t\t\tname:           \"application\",\n\t\t\troots:          []string{\"application.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"restricted\",\n\t\t\tname:           \"binary\",\n\t\t\troots:          []string{\"bin/bin2.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"restricted\",\n\t\t\tname:           \"library\",\n\t\t\troots:          []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"proprietary\",\n\t\t\tname:           \"apex\",\n\t\t\troots:          []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedStdout: \"FAIL\",\n\t\t\texpectedOutcomes: outcomeList{\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:      \"proprietary\",\n\t\t\tname:           \"container\",\n\t\t\troots:          []string{\"container.zip.meta_lic\"},\n\t\t\texpectedStdout: \"FAIL\",\n\t\t\texpectedOutcomes: outcomeList{\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:      \"proprietary\",\n\t\t\tname:           \"application\",\n\t\t\troots:          []string{\"application.meta_lic\"},\n\t\t\texpectedStdout: \"FAIL\",\n\t\t\texpectedOutcomes: outcomeList{\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:      \"proprietary\",\n\t\t\tname:           \"binary\",\n\t\t\troots:          []string{\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\"},\n\t\t\texpectedStdout: \"FAIL\",\n\t\t\texpectedOutcomes: outcomeList{\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:      \"proprietary\",\n\t\t\tname:           \"library\",\n\t\t\troots:          []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedStdout: \"PASS\",\n\t\t},\n\t\t{\n\t\t\tcondition:      \"regressconcur\",\n\t\t\tname:           \"container\",\n\t\t\troots:          []string{\"container.zip.meta_lic\"},\n\t\t\texpectedStdout: \"FAIL\",\n\t\t\texpectedOutcomes: outcomeList{\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/regressconcur/bin/bin1.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/regressconcur/bin/bin2.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/regressconcur/bin/bin3.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/regressconcur/bin/bin4.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/regressconcur/bin/bin5.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/regressconcur/bin/bin6.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/regressconcur/bin/bin7.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/regressconcur/bin/bin8.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t\t&outcome{\n\t\t\t\t\ttarget:           \"testdata/regressconcur/bin/bin9.meta_lic\",\n\t\t\t\t\tprivacyCondition: \"proprietary\",\n\t\t\t\t\tshareCondition:   \"restricted\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\t\t\terr := checkShare(stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)\n\t\t\tif err != nil && err != failConflicts {\n\t\t\t\tt.Fatalf(\"checkshare: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvar actualStdout string\n\t\t\tfor _, s := range strings.Split(stdout.String(), \"\\n\") {\n\t\t\t\tts := strings.TrimLeft(s, \" \\t\")\n\t\t\t\tif len(ts) < 1 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(actualStdout) > 0 {\n\t\t\t\t\tt.Errorf(\"checkshare: unexpected multiple output lines %q, want %q\", actualStdout+\"\\n\"+ts, tt.expectedStdout)\n\t\t\t\t}\n\t\t\t\tactualStdout = ts\n\t\t\t}\n\t\t\tif actualStdout != tt.expectedStdout {\n\t\t\t\tt.Errorf(\"checkshare: unexpected stdout %q, want %q\", actualStdout, tt.expectedStdout)\n\t\t\t}\n\t\t\terrList := strings.Split(stderr.String(), \"\\n\")\n\t\t\tactualOutcomes := make(outcomeList, 0, len(errList))\n\t\t\tfor _, cstring := range errList {\n\t\t\t\tts := strings.TrimLeft(cstring, \" \\t\")\n\t\t\t\tif len(ts) < 1 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcFields := strings.Split(ts, \" \")\n\t\t\t\tactualOutcomes = append(actualOutcomes, &outcome{\n\t\t\t\t\ttarget:           cFields[0],\n\t\t\t\t\tprivacyCondition: cFields[1],\n\t\t\t\t\tshareCondition:   cFields[6],\n\t\t\t\t})\n\t\t\t}\n\t\t\tif len(actualOutcomes) != len(tt.expectedOutcomes) {\n\t\t\t\tt.Errorf(\"checkshare: unexpected got %d outcomes %s, want %d outcomes %s\",\n\t\t\t\t\tlen(actualOutcomes), actualOutcomes, len(tt.expectedOutcomes), tt.expectedOutcomes)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor i := range actualOutcomes {\n\t\t\t\tif actualOutcomes[i].String() != tt.expectedOutcomes[i].String() {\n\t\t\t\t\tt.Errorf(\"checkshare: unexpected outcome #%d, got %q, want %q\",\n\t\t\t\t\t\ti+1, actualOutcomes[i], tt.expectedOutcomes[i])\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/cmd/dumpgraph/dumpgraph.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"android/soong/response\"\n\t\"android/soong/tools/compliance\"\n)\n\nvar (\n\tfailNoneRequested = fmt.Errorf(\"\\nNo license metadata files requested\")\n\tfailNoLicenses    = fmt.Errorf(\"No licenses found\")\n)\n\ntype context struct {\n\tgraphViz        bool\n\tlabelConditions bool\n\tstripPrefix     []string\n}\n\nfunc (ctx context) strip(installPath string) string {\n\tfor _, prefix := range ctx.stripPrefix {\n\t\tif strings.HasPrefix(installPath, prefix) {\n\t\t\tp := strings.TrimPrefix(installPath, prefix)\n\t\t\tif 0 == len(p) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn p\n\t\t}\n\t}\n\treturn installPath\n}\n\n// newMultiString creates a flag that allows multiple values in an array.\nfunc newMultiString(flags *flag.FlagSet, name, usage string) *multiString {\n\tvar f multiString\n\tflags.Var(&f, name, usage)\n\treturn &f\n}\n\n// multiString implements the flag `Value` interface for multiple strings.\ntype multiString []string\n\nfunc (ms *multiString) String() string     { return strings.Join(*ms, \", \") }\nfunc (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }\n\nfunc main() {\n\tvar expandedArgs []string\n\tfor _, arg := range os.Args[1:] {\n\t\tif strings.HasPrefix(arg, \"@\") {\n\t\t\tf, err := os.Open(strings.TrimPrefix(arg, \"@\"))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\trespArgs, err := response.ReadRspFile(f)\n\t\t\tf.Close()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\texpandedArgs = append(expandedArgs, respArgs...)\n\t\t} else {\n\t\t\texpandedArgs = append(expandedArgs, arg)\n\t\t}\n\t}\n\n\tflags := flag.NewFlagSet(\"flags\", flag.ExitOnError)\n\n\tflags.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}\n\nOutputs space-separated Target Dependency Annotations tuples for each\nedge in the license graph. When -dot flag given, outputs the nodes and\nedges in graphViz directed graph format.\n\nIn plain text mode, multiple values within a field are colon-separated.\ne.g. multiple annotations appear as annotation1:annotation2:annotation3\nor when -label_conditions is requested, Target and Dependency become\ntarget:condition1:condition2 etc.\n\nOptions:\n`, filepath.Base(os.Args[0]))\n\t\tflags.PrintDefaults()\n\t}\n\n\tgraphViz := flags.Bool(\"dot\", false, \"Whether to output graphviz (i.e. dot) format.\")\n\tlabelConditions := flags.Bool(\"label_conditions\", false, \"Whether to label target nodes with conditions.\")\n\toutputFile := flags.String(\"o\", \"-\", \"Where to write the output. (default stdout)\")\n\tstripPrefix := newMultiString(flags, \"strip_prefix\", \"Prefix to remove from paths. i.e. path to root (multiple allowed)\")\n\n\tflags.Parse(expandedArgs)\n\n\t// Must specify at least one root target.\n\tif flags.NArg() == 0 {\n\t\tflags.Usage()\n\t\tos.Exit(2)\n\t}\n\n\tif len(*outputFile) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"must specify file for -o; use - for stdout\\n\")\n\t\tos.Exit(2)\n\t} else {\n\t\tdir, err := filepath.Abs(filepath.Dir(*outputFile))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot determine path to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfi, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot read directory %q of %q: %s\\n\", dir, *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !fi.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"parent %q of %q is not a directory\\n\", dir, *outputFile)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar ofile io.Writer\n\tofile = os.Stdout\n\tvar obuf *bytes.Buffer\n\tif *outputFile != \"-\" {\n\t\tobuf = &bytes.Buffer{}\n\t\tofile = obuf\n\t}\n\n\tctx := &context{*graphViz, *labelConditions, *stripPrefix}\n\n\terr := dumpGraph(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...)\n\tif err != nil {\n\t\tif err == failNoneRequested {\n\t\t\tflags.Usage()\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"%s\\n\", err.Error())\n\t\tos.Exit(1)\n\t}\n\tif *outputFile != \"-\" {\n\t\terr := os.WriteFile(*outputFile, obuf.Bytes(), 0666)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write output to %q from %q: %s\\n\", *outputFile, os.Getenv(\"PWD\"), err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tos.Exit(0)\n}\n\n// dumpGraph implements the dumpgraph utility.\nfunc dumpGraph(ctx *context, stdout, stderr io.Writer, rootFS fs.FS, files ...string) error {\n\tif len(files) < 1 {\n\t\treturn failNoneRequested\n\t}\n\n\t// Read the license graph from the license metadata files (*.meta_lic).\n\tlicenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license metadata file(s) %q: %w\\n\", files, err)\n\t}\n\tif licenseGraph == nil {\n\t\treturn failNoLicenses\n\t}\n\n\t// Sort the edges of the graph.\n\tedges := licenseGraph.Edges()\n\tsort.Sort(edges)\n\n\t// nodes maps license metadata file names to graphViz node names when ctx.graphViz is true.\n\tvar nodes map[string]string\n\tn := 0\n\n\t// targetOut calculates the string to output for `target` separating conditions as needed using `sep`.\n\ttargetOut := func(target *compliance.TargetNode, sep string) string {\n\t\ttOut := ctx.strip(target.Name())\n\t\tif ctx.labelConditions {\n\t\t\tconditions := target.LicenseConditions().Names()\n\t\t\tsort.Strings(conditions)\n\t\t\tif len(conditions) > 0 {\n\t\t\t\ttOut += sep + strings.Join(conditions, sep)\n\t\t\t}\n\t\t}\n\t\treturn tOut\n\t}\n\n\t// makeNode maps `target` to a graphViz node name.\n\tmakeNode := func(target *compliance.TargetNode) {\n\t\ttName := target.Name()\n\t\tif _, ok := nodes[tName]; !ok {\n\t\t\tnodeName := fmt.Sprintf(\"n%d\", n)\n\t\t\tnodes[tName] = nodeName\n\t\t\tfmt.Fprintf(stdout, \"\\t%s [label=\\\"%s\\\"];\\n\", nodeName, targetOut(target, \"\\\\n\"))\n\t\t\tn++\n\t\t}\n\t}\n\n\t// If graphviz output, map targets to node names, and start the directed graph.\n\tif ctx.graphViz {\n\t\tnodes = make(map[string]string)\n\t\ttargets := licenseGraph.Targets()\n\t\tsort.Sort(targets)\n\n\t\tfmt.Fprintf(stdout, \"strict digraph {\\n\\trankdir=RL;\\n\")\n\t\tfor _, target := range targets {\n\t\t\tmakeNode(target)\n\t\t}\n\t}\n\n\t// Print the sorted edges to stdout ...\n\tfor _, e := range edges {\n\t\t// sort the annotations for repeatability/stability\n\t\tannotations := e.Annotations().AsList()\n\t\tsort.Strings(annotations)\n\n\t\ttName := e.Target().Name()\n\t\tdName := e.Dependency().Name()\n\n\t\tif ctx.graphViz {\n\t\t\t// ... one edge per line labelled with \\\\n-separated annotations.\n\t\t\ttNode := nodes[tName]\n\t\t\tdNode := nodes[dName]\n\t\t\tfmt.Fprintf(stdout, \"\\t%s -> %s [label=\\\"%s\\\"];\\n\", dNode, tNode, strings.Join(annotations, \"\\\\n\"))\n\t\t} else {\n\t\t\t// ... one edge per line with annotations in a colon-separated tuple.\n\t\t\tfmt.Fprintf(stdout, \"%s %s %s\\n\", targetOut(e.Target(), \":\"), targetOut(e.Dependency(), \":\"), strings.Join(annotations, \":\"))\n\t\t}\n\t}\n\n\t// If graphViz output, rank the root nodes together, and complete the directed graph.\n\tif ctx.graphViz {\n\t\tfmt.Fprintf(stdout, \"\\t{rank=same;\")\n\t\tfor _, f := range files {\n\t\t\tfName := f\n\t\t\tif !strings.HasSuffix(fName, \".meta_lic\") {\n\t\t\t\tfName += \".meta_lic\"\n\t\t\t}\n\t\t\tif fNode, ok := nodes[fName]; ok {\n\t\t\t\tfmt.Fprintf(stdout, \" %s\", fNode)\n\t\t\t}\n\t\t}\n\t\tfmt.Fprintf(stdout, \"}\\n}\\n\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tools/compliance/cmd/dumpgraph/dumpgraph_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the parent directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"..\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc Test_plaintext(t *testing.T) {\n\ttests := []struct {\n\t\tcondition   string\n\t\tname        string\n\t\toutDir      string\n\t\troots       []string\n\t\tctx         context\n\t\texpectedOut []string\n\t}{\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin1.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin2.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/firstparty/\"}},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic static\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic static\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/firstparty/\"}, labelConditions: true},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice static\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice static\",\n\t\t\t\t\"bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic\",\n\t\t\t\t\"bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin1.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin2.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/firstparty/application.meta_lic testdata/firstparty/bin/bin3.meta_lic toolchain\",\n\t\t\t\t\"testdata/firstparty/application.meta_lic testdata/firstparty/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/application.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin1.meta_lic static\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin2.meta_lic static\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic testdata/notice/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic testdata/notice/lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/notice/\"}},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic static\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic static\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/notice/\"}, labelConditions: true},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice static\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice static\",\n\t\t\t\t\"bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic\",\n\t\t\t\t\"bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic testdata/notice/bin/bin1.meta_lic static\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic testdata/notice/bin/bin2.meta_lic static\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic testdata/notice/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic testdata/notice/lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/notice/application.meta_lic testdata/notice/bin/bin3.meta_lic toolchain\",\n\t\t\t\t\"testdata/notice/application.meta_lic testdata/notice/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/notice/application.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin1.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin2.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/reciprocal/\"}},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic static\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic static\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/reciprocal/\"}, labelConditions: true},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:reciprocal static\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static\",\n\t\t\t\t\"bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic\",\n\t\t\t\t\"bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/liba.so.meta_lic:reciprocal static\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin1.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin2.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/reciprocal/application.meta_lic testdata/reciprocal/bin/bin3.meta_lic toolchain\",\n\t\t\t\t\"testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic static\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic static\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/restricted/\"}},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic static\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic static\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/restricted/\"}, labelConditions: true},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted_if_statically_linked static\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static\",\n\t\t\t\t\"bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted dynamic\",\n\t\t\t\t\"bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted_if_statically_linked static\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic static\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic static\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/application.meta_lic testdata/restricted/bin/bin3.meta_lic toolchain\",\n\t\t\t\t\"testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/restricted/application.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin1.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/proprietary/\"}},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic static\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic static\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic static\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/proprietary/\"}, labelConditions: true},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary static\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:by_exception_only:proprietary static\",\n\t\t\t\t\"bin/bin2.meta_lic:by_exception_only:proprietary lib/libb.so.meta_lic:restricted dynamic\",\n\t\t\t\t\"bin/bin2.meta_lic:by_exception_only:proprietary lib/libd.so.meta_lic:notice dynamic\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin2.meta_lic:by_exception_only:proprietary static\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary static\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic dynamic\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin1.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/proprietary/application.meta_lic testdata/proprietary/bin/bin3.meta_lic toolchain\",\n\t\t\t\t\"testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/application.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\texpectedOut := &bytes.Buffer{}\n\t\t\tfor _, eo := range tt.expectedOut {\n\t\t\t\texpectedOut.WriteString(eo)\n\t\t\t\texpectedOut.WriteString(\"\\n\")\n\t\t\t}\n\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\t\t\terr := dumpGraph(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"dumpgraph: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif stderr.Len() > 0 {\n\t\t\t\tt.Errorf(\"dumpgraph: gotStderr = %v, want none\", stderr)\n\t\t\t}\n\t\t\tout := stdout.String()\n\t\t\texpected := expectedOut.String()\n\t\t\tif out != expected {\n\t\t\t\toutList := strings.Split(out, \"\\n\")\n\t\t\t\texpectedList := strings.Split(expected, \"\\n\")\n\t\t\t\tstartLine := 0\n\t\t\t\tfor len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] {\n\t\t\t\t\tstartLine++\n\t\t\t\t}\n\t\t\t\tt.Errorf(\"listshare: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v\",\n\t\t\t\t\tout, expected, startLine+1, outList[startLine], expectedList[startLine])\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype testContext struct {\n\tnextNode int\n\tnodes    map[string]string\n}\n\ntype matcher interface {\n\tmatchString(*testContext) string\n\ttypeString() string\n}\n\ntype targetMatcher struct {\n\ttarget     string\n\tconditions []string\n}\n\nfunc (tm *targetMatcher) matchString(ctx *testContext) string {\n\tm := tm.target\n\tif len(tm.conditions) > 0 {\n\t\tm += \"\\\\n\" + strings.Join(tm.conditions, \"\\\\n\")\n\t}\n\tm = ctx.nodes[tm.target] + \" [label=\\\"\" + m + \"\\\"];\"\n\treturn m\n}\n\nfunc (tm *targetMatcher) typeString() string {\n\treturn \"target\"\n}\n\ntype edgeMatcher struct {\n\ttarget      string\n\tdep         string\n\tannotations []string\n}\n\nfunc (em *edgeMatcher) matchString(ctx *testContext) string {\n\treturn ctx.nodes[em.dep] + \" -> \" + ctx.nodes[em.target] + \" [label=\\\"\" + strings.Join(em.annotations, \"\\\\n\") + \"\\\"];\"\n}\n\nfunc (tm *edgeMatcher) typeString() string {\n\treturn \"edge\"\n}\n\ntype getMatcher func(*testContext) matcher\n\nfunc matchTarget(target string, conditions ...string) getMatcher {\n\treturn func(ctx *testContext) matcher {\n\t\tctx.nodes[target] = fmt.Sprintf(\"n%d\", ctx.nextNode)\n\t\tctx.nextNode++\n\t\treturn &targetMatcher{target, append([]string{}, conditions...)}\n\t}\n}\n\nfunc matchEdge(target, dep string, annotations ...string) getMatcher {\n\treturn func(ctx *testContext) matcher {\n\t\tif _, ok := ctx.nodes[target]; !ok {\n\t\t\tpanic(fmt.Errorf(\"no node for target %v in %v -> %v [label=\\\"%s\\\"];\", target, dep, target, strings.Join(annotations, \"\\\\n\")))\n\t\t}\n\t\tif _, ok := ctx.nodes[dep]; !ok {\n\t\t\tpanic(fmt.Errorf(\"no node for dep %v in %v -> %v [label=\\\"%s\\\"];\", target, dep, target, strings.Join(annotations, \"\\\\n\")))\n\t\t}\n\t\treturn &edgeMatcher{target, dep, append([]string{}, annotations...)}\n\t}\n}\n\nfunc Test_graphviz(t *testing.T) {\n\ttests := []struct {\n\t\tcondition   string\n\t\tname        string\n\t\toutDir      string\n\t\troots       []string\n\t\tctx         context\n\t\texpectedOut []getMatcher\n\t}{\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/bin/bin1.meta_lic\", \"testdata/firstparty/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/bin/bin1.meta_lic\", \"testdata/firstparty/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/bin/bin2.meta_lic\", \"testdata/firstparty/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/bin/bin2.meta_lic\", \"testdata/firstparty/lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/highest.apex.meta_lic\", \"testdata/firstparty/bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/highest.apex.meta_lic\", \"testdata/firstparty/bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/highest.apex.meta_lic\", \"testdata/firstparty/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/highest.apex.meta_lic\", \"testdata/firstparty/lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/firstparty/\"}},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/firstparty/\"}, labelConditions: true},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libd.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/container.zip.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/bin/bin1.meta_lic\", \"testdata/firstparty/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/bin/bin1.meta_lic\", \"testdata/firstparty/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/bin/bin2.meta_lic\", \"testdata/firstparty/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/bin/bin2.meta_lic\", \"testdata/firstparty/lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/container.zip.meta_lic\", \"testdata/firstparty/bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/container.zip.meta_lic\", \"testdata/firstparty/bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/container.zip.meta_lic\", \"testdata/firstparty/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/container.zip.meta_lic\", \"testdata/firstparty/lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/firstparty/application.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin3.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/application.meta_lic\", \"testdata/firstparty/bin/bin3.meta_lic\", \"toolchain\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/application.meta_lic\", \"testdata/firstparty/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/application.meta_lic\", \"testdata/firstparty/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/bin/bin1.meta_lic\", \"testdata/firstparty/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/firstparty/bin/bin1.meta_lic\", \"testdata/firstparty/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{matchTarget(\"testdata/firstparty/lib/libd.so.meta_lic\")},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/notice/bin/bin1.meta_lic\", \"testdata/notice/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/bin/bin1.meta_lic\", \"testdata/notice/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/bin/bin2.meta_lic\", \"testdata/notice/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/notice/bin/bin2.meta_lic\", \"testdata/notice/lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/notice/highest.apex.meta_lic\", \"testdata/notice/bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/highest.apex.meta_lic\", \"testdata/notice/bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/highest.apex.meta_lic\", \"testdata/notice/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/highest.apex.meta_lic\", \"testdata/notice/lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/notice/\"}},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/notice/\"}, labelConditions: true},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libd.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/container.zip.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/notice/bin/bin1.meta_lic\", \"testdata/notice/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/bin/bin1.meta_lic\", \"testdata/notice/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/bin/bin2.meta_lic\", \"testdata/notice/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/notice/bin/bin2.meta_lic\", \"testdata/notice/lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/notice/container.zip.meta_lic\", \"testdata/notice/bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/container.zip.meta_lic\", \"testdata/notice/bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/container.zip.meta_lic\", \"testdata/notice/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/container.zip.meta_lic\", \"testdata/notice/lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/notice/application.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin3.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/notice/application.meta_lic\", \"testdata/notice/bin/bin3.meta_lic\", \"toolchain\"),\n\t\t\t\tmatchEdge(\"testdata/notice/application.meta_lic\", \"testdata/notice/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/application.meta_lic\", \"testdata/notice/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/notice/bin/bin1.meta_lic\", \"testdata/notice/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/notice/bin/bin1.meta_lic\", \"testdata/notice/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{matchTarget(\"testdata/notice/lib/libd.so.meta_lic\")},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/bin/bin1.meta_lic\", \"testdata/reciprocal/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/bin/bin1.meta_lic\", \"testdata/reciprocal/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/bin/bin2.meta_lic\", \"testdata/reciprocal/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/bin/bin2.meta_lic\", \"testdata/reciprocal/lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/highest.apex.meta_lic\", \"testdata/reciprocal/bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/highest.apex.meta_lic\", \"testdata/reciprocal/bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/highest.apex.meta_lic\", \"testdata/reciprocal/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/highest.apex.meta_lic\", \"testdata/reciprocal/lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/reciprocal/\"}},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/reciprocal/\"}, labelConditions: true},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\", \"reciprocal\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\", \"reciprocal\"),\n\t\t\t\tmatchTarget(\"lib/libd.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/container.zip.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/bin/bin1.meta_lic\", \"testdata/reciprocal/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/bin/bin1.meta_lic\", \"testdata/reciprocal/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/bin/bin2.meta_lic\", \"testdata/reciprocal/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/bin/bin2.meta_lic\", \"testdata/reciprocal/lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/container.zip.meta_lic\", \"testdata/reciprocal/bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/container.zip.meta_lic\", \"testdata/reciprocal/bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/container.zip.meta_lic\", \"testdata/reciprocal/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/container.zip.meta_lic\", \"testdata/reciprocal/lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/reciprocal/application.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin3.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/application.meta_lic\", \"testdata/reciprocal/bin/bin3.meta_lic\", \"toolchain\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/application.meta_lic\", \"testdata/reciprocal/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/application.meta_lic\", \"testdata/reciprocal/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/bin/bin1.meta_lic\", \"testdata/reciprocal/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/reciprocal/bin/bin1.meta_lic\", \"testdata/reciprocal/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{matchTarget(\"testdata/reciprocal/lib/libd.so.meta_lic\")},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/bin/bin1.meta_lic\", \"testdata/restricted/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/bin/bin1.meta_lic\", \"testdata/restricted/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/bin/bin2.meta_lic\", \"testdata/restricted/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/bin/bin2.meta_lic\", \"testdata/restricted/lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/highest.apex.meta_lic\", \"testdata/restricted/bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/highest.apex.meta_lic\", \"testdata/restricted/bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/highest.apex.meta_lic\", \"testdata/restricted/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/highest.apex.meta_lic\", \"testdata/restricted/lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/restricted/\"}},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/restricted/\"}, labelConditions: true},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\", \"restricted_if_statically_linked\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\", \"restricted\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\", \"reciprocal\"),\n\t\t\t\tmatchTarget(\"lib/libd.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/container.zip.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/bin/bin1.meta_lic\", \"testdata/restricted/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/bin/bin1.meta_lic\", \"testdata/restricted/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/bin/bin2.meta_lic\", \"testdata/restricted/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/bin/bin2.meta_lic\", \"testdata/restricted/lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/container.zip.meta_lic\", \"testdata/restricted/bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/container.zip.meta_lic\", \"testdata/restricted/bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/container.zip.meta_lic\", \"testdata/restricted/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/container.zip.meta_lic\", \"testdata/restricted/lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/restricted/application.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin3.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/application.meta_lic\", \"testdata/restricted/bin/bin3.meta_lic\", \"toolchain\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/application.meta_lic\", \"testdata/restricted/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/application.meta_lic\", \"testdata/restricted/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/bin/bin1.meta_lic\", \"testdata/restricted/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/restricted/bin/bin1.meta_lic\", \"testdata/restricted/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{matchTarget(\"testdata/restricted/lib/libd.so.meta_lic\")},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/bin/bin1.meta_lic\", \"testdata/proprietary/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/bin/bin1.meta_lic\", \"testdata/proprietary/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/bin/bin2.meta_lic\", \"testdata/proprietary/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/bin/bin2.meta_lic\", \"testdata/proprietary/lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/highest.apex.meta_lic\", \"testdata/proprietary/bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/highest.apex.meta_lic\", \"testdata/proprietary/bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/highest.apex.meta_lic\", \"testdata/proprietary/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/highest.apex.meta_lic\", \"testdata/proprietary/lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/proprietary/\"}},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/proprietary/\"}, labelConditions: true},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\", \"by_exception_only\", \"proprietary\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\", \"by_exception_only\", \"proprietary\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\", \"restricted\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\", \"by_exception_only\", \"proprietary\"),\n\t\t\t\tmatchTarget(\"lib/libd.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin1.meta_lic\", \"lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"highest.apex.meta_lic\", \"lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/container.zip.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/bin/bin1.meta_lic\", \"testdata/proprietary/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/bin/bin1.meta_lic\", \"testdata/proprietary/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/bin/bin2.meta_lic\", \"testdata/proprietary/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/bin/bin2.meta_lic\", \"testdata/proprietary/lib/libd.so.meta_lic\", \"dynamic\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/container.zip.meta_lic\", \"testdata/proprietary/bin/bin1.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/container.zip.meta_lic\", \"testdata/proprietary/bin/bin2.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/container.zip.meta_lic\", \"testdata/proprietary/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/container.zip.meta_lic\", \"testdata/proprietary/lib/libb.so.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/proprietary/application.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin3.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/application.meta_lic\", \"testdata/proprietary/bin/bin3.meta_lic\", \"toolchain\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/application.meta_lic\", \"testdata/proprietary/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/application.meta_lic\", \"testdata/proprietary/lib/libb.so.meta_lic\", \"dynamic\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/bin/bin1.meta_lic\", \"testdata/proprietary/lib/liba.so.meta_lic\", \"static\"),\n\t\t\t\tmatchEdge(\"testdata/proprietary/bin/bin1.meta_lic\", \"testdata/proprietary/lib/libc.a.meta_lic\", \"static\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{matchTarget(\"testdata/proprietary/lib/libd.so.meta_lic\")},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\tctx := &testContext{0, make(map[string]string)}\n\n\t\t\texpectedOut := &bytes.Buffer{}\n\t\t\tfor _, eo := range tt.expectedOut {\n\t\t\t\tm := eo(ctx)\n\t\t\t\texpectedOut.WriteString(m.matchString(ctx))\n\t\t\t\texpectedOut.WriteString(\"\\n\")\n\t\t\t}\n\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\t\t\ttt.ctx.graphViz = true\n\t\t\terr := dumpGraph(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"dumpgraph: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif stderr.Len() > 0 {\n\t\t\t\tt.Errorf(\"dumpgraph: gotStderr = %v, want none\", stderr)\n\t\t\t}\n\t\t\toutList := strings.Split(stdout.String(), \"\\n\")\n\t\t\toutLine := 0\n\t\t\tif outList[outLine] != \"strict digraph {\" {\n\t\t\t\tt.Errorf(\"dumpgraph: got 1st line %v, want strict digraph {\", outList[outLine])\n\t\t\t}\n\t\t\toutLine++\n\t\t\tif strings.HasPrefix(strings.TrimLeft(outList[outLine], \" \\t\"), \"rankdir\") {\n\t\t\t\toutLine++\n\t\t\t}\n\t\t\tendOut := len(outList)\n\t\t\tfor endOut > 0 && strings.TrimLeft(outList[endOut-1], \" \\t\") == \"\" {\n\t\t\t\tendOut--\n\t\t\t}\n\t\t\tif outList[endOut-1] != \"}\" {\n\t\t\t\tt.Errorf(\"dumpgraph: got last line %v, want }\", outList[endOut-1])\n\t\t\t}\n\t\t\tendOut--\n\t\t\tif strings.HasPrefix(strings.TrimLeft(outList[endOut-1], \" \\t\"), \"{rank=same\") {\n\t\t\t\tendOut--\n\t\t\t}\n\t\t\texpectedList := strings.Split(expectedOut.String(), \"\\n\")\n\t\t\tfor len(expectedList) > 0 && expectedList[len(expectedList)-1] == \"\" {\n\t\t\t\texpectedList = expectedList[0 : len(expectedList)-1]\n\t\t\t}\n\t\t\tmatchLine := 0\n\n\t\t\tfor outLine < endOut && matchLine < len(expectedList) && strings.TrimLeft(outList[outLine], \" \\t\") == expectedList[matchLine] {\n\t\t\t\toutLine++\n\t\t\t\tmatchLine++\n\t\t\t}\n\t\t\tif outLine < endOut || matchLine < len(expectedList) {\n\t\t\t\tif outLine >= endOut {\n\t\t\t\t\tt.Errorf(\"dumpgraph: missing lines at end of graph, want %d lines %v\", len(expectedList)-matchLine, strings.Join(expectedList[matchLine:], \"\\n\"))\n\t\t\t\t} else if matchLine >= len(expectedList) {\n\t\t\t\t\tt.Errorf(\"dumpgraph: unexpected lines at end of graph starting line %d, got %v, want nothing\", outLine+1, strings.Join(outList[outLine:], \"\\n\"))\n\t\t\t\t} else {\n\t\t\t\t\tt.Errorf(\"dumpgraph: at line %d, got %v, want %v\", outLine+1, strings.Join(outList[outLine:], \"\\n\"), strings.Join(expectedList[matchLine:], \"\\n\"))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/cmd/dumpresolutions/dumpresolutions.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"android/soong/response\"\n\t\"android/soong/tools/compliance\"\n)\n\nvar (\n\tfailNoneRequested = fmt.Errorf(\"\\nNo license metadata files requested\")\n\tfailNoLicenses    = fmt.Errorf(\"No licenses found\")\n)\n\ntype context struct {\n\tconditions      []compliance.LicenseCondition\n\tgraphViz        bool\n\tlabelConditions bool\n\tstripPrefix     []string\n}\n\nfunc (ctx context) strip(installPath string) string {\n\tfor _, prefix := range ctx.stripPrefix {\n\t\tif strings.HasPrefix(installPath, prefix) {\n\t\t\tp := strings.TrimPrefix(installPath, prefix)\n\t\t\tif 0 == len(p) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn p\n\t\t}\n\t}\n\treturn installPath\n}\n\n// newMultiString creates a flag that allows multiple values in an array.\nfunc newMultiString(flags *flag.FlagSet, name, usage string) *multiString {\n\tvar f multiString\n\tflags.Var(&f, name, usage)\n\treturn &f\n}\n\n// multiString implements the flag `Value` interface for multiple strings.\ntype multiString []string\n\nfunc (ms *multiString) String() string     { return strings.Join(*ms, \", \") }\nfunc (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }\n\nfunc main() {\n\tvar expandedArgs []string\n\tfor _, arg := range os.Args[1:] {\n\t\tif strings.HasPrefix(arg, \"@\") {\n\t\t\tf, err := os.Open(strings.TrimPrefix(arg, \"@\"))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\trespArgs, err := response.ReadRspFile(f)\n\t\t\tf.Close()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\texpandedArgs = append(expandedArgs, respArgs...)\n\t\t} else {\n\t\t\texpandedArgs = append(expandedArgs, arg)\n\t\t}\n\t}\n\n\tflags := flag.NewFlagSet(\"flags\", flag.ExitOnError)\n\n\tflags.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}\n\nOutputs a space-separated Target ActsOn Origin Condition tuple for each\nresolution in the graph. When -dot flag given, outputs nodes and edges\nin graphviz directed graph format.\n\nIf one or more '-c condition' conditions are given, outputs the\nresolution for the union of the conditions. Otherwise, outputs the\nresolution for all conditions.\n\nIn plain text mode, when '-label_conditions' is requested, the Target\nand Origin have colon-separated license conditions appended:\ni.e. target:condition1:condition2 etc.\n\nOptions:\n`, filepath.Base(os.Args[0]))\n\t\tflags.PrintDefaults()\n\t}\n\n\tconditions := newMultiString(flags, \"c\", \"License condition to resolve. (may be given multiple times)\")\n\tgraphViz := flags.Bool(\"dot\", false, \"Whether to output graphviz (i.e. dot) format.\")\n\tlabelConditions := flags.Bool(\"label_conditions\", false, \"Whether to label target nodes with conditions.\")\n\toutputFile := flags.String(\"o\", \"-\", \"Where to write the output. (default stdout)\")\n\tstripPrefix := newMultiString(flags, \"strip_prefix\", \"Prefix to remove from paths. i.e. path to root (multiple allowed)\")\n\n\tflags.Parse(expandedArgs)\n\n\t// Must specify at least one root target.\n\tif flags.NArg() == 0 {\n\t\tflags.Usage()\n\t\tos.Exit(2)\n\t}\n\n\tif len(*outputFile) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"must specify file for -o; use - for stdout\\n\")\n\t\tos.Exit(2)\n\t} else {\n\t\tdir, err := filepath.Abs(filepath.Dir(*outputFile))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot determine path to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfi, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot read directory %q of %q: %s\\n\", dir, *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !fi.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"parent %q of %q is not a directory\\n\", dir, *outputFile)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar ofile io.Writer\n\tofile = os.Stdout\n\tvar obuf *bytes.Buffer\n\tif *outputFile != \"-\" {\n\t\tobuf = &bytes.Buffer{}\n\t\tofile = obuf\n\t}\n\n\tlcs := make([]compliance.LicenseCondition, 0, len(*conditions))\n\tfor _, name := range *conditions {\n\t\tlcs = append(lcs, compliance.RecognizedConditionNames[name])\n\t}\n\tctx := &context{\n\t\tconditions:      lcs,\n\t\tgraphViz:        *graphViz,\n\t\tlabelConditions: *labelConditions,\n\t\tstripPrefix:     *stripPrefix,\n\t}\n\t_, err := dumpResolutions(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...)\n\tif err != nil {\n\t\tif err == failNoneRequested {\n\t\t\tflags.Usage()\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"%s\\n\", err.Error())\n\t\tos.Exit(1)\n\t}\n\tif *outputFile != \"-\" {\n\t\terr := os.WriteFile(*outputFile, obuf.Bytes(), 0666)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write output to %q from %q: %s\\n\", *outputFile, os.Getenv(\"PWD\"), err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tos.Exit(0)\n}\n\n// dumpResolutions implements the dumpresolutions utility.\nfunc dumpResolutions(ctx *context, stdout, stderr io.Writer, rootFS fs.FS, files ...string) (*compliance.LicenseGraph, error) {\n\tif len(files) < 1 {\n\t\treturn nil, failNoneRequested\n\t}\n\n\t// Read the license graph from the license metadata files (*.meta_lic).\n\tlicenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Unable to read license metadata file(s) %q: %v\\n\", files, err)\n\t}\n\tif licenseGraph == nil {\n\t\treturn nil, failNoLicenses\n\t}\n\n\tcompliance.ResolveTopDownConditions(licenseGraph)\n\tcs := compliance.AllLicenseConditions\n\tif len(ctx.conditions) > 0 {\n\t\tcs = compliance.NewLicenseConditionSet()\n\t\tfor _, c := range ctx.conditions {\n\t\t\tcs = cs.Plus(c)\n\t\t}\n\t}\n\n\tresolutions := compliance.WalkResolutionsForCondition(licenseGraph, cs)\n\n\t// nodes maps license metadata file names to graphViz node names when graphViz requested.\n\tnodes := make(map[string]string)\n\tn := 0\n\n\t// targetOut calculates the string to output for `target` adding `sep`-separated conditions as needed.\n\ttargetOut := func(target *compliance.TargetNode, sep string) string {\n\t\ttOut := ctx.strip(target.Name())\n\t\tif ctx.labelConditions {\n\t\t\tconditions := target.LicenseConditions().Names()\n\t\t\tif len(conditions) > 0 {\n\t\t\t\ttOut += sep + strings.Join(conditions, sep)\n\t\t\t}\n\t\t}\n\t\treturn tOut\n\t}\n\n\t// makeNode maps `target` to a graphViz node name.\n\tmakeNode := func(target *compliance.TargetNode) {\n\t\ttName := target.Name()\n\t\tif _, ok := nodes[tName]; !ok {\n\t\t\tnodeName := fmt.Sprintf(\"n%d\", n)\n\t\t\tnodes[tName] = nodeName\n\t\t\tfmt.Fprintf(stdout, \"\\t%s [label=\\\"%s\\\"];\\n\", nodeName, targetOut(target, \"\\\\n\"))\n\t\t\tn++\n\t\t}\n\t}\n\n\t// outputResolution prints a resolution in the requested format to `stdout`, where one can read\n\t// a resolution as `tname` resolves `oname`'s conditions named in `cnames`.\n\t// `tname` is the name of the target the resolution applies to.\n\t// `cnames` is the list of conditions to resolve.\n\toutputResolution := func(tname, aname string, cnames []string) {\n\t\tif ctx.graphViz {\n\t\t\t// ... one edge per line labelled with \\\\n-separated annotations.\n\t\t\ttNode := nodes[tname]\n\t\t\taNode := nodes[aname]\n\t\t\tfmt.Fprintf(stdout, \"\\t%s -> %s [label=\\\"%s\\\"];\\n\", tNode, aNode, strings.Join(cnames, \"\\\\n\"))\n\t\t} else {\n\t\t\t// ... one edge per line with names in a colon-separated tuple.\n\t\t\tfmt.Fprintf(stdout, \"%s %s %s\\n\", tname, aname, strings.Join(cnames, \":\"))\n\t\t}\n\t}\n\n\t// Sort the resolutions by targetname for repeatability/stability.\n\ttargets := resolutions.AttachesTo()\n\tsort.Sort(targets)\n\n\t// If graphviz output, start the directed graph.\n\tif ctx.graphViz {\n\t\tfmt.Fprintf(stdout, \"strict digraph {\\n\\trankdir=LR;\\n\")\n\t\tfor _, target := range targets {\n\t\t\tmakeNode(target)\n\t\t\trl := resolutions.Resolutions(target)\n\t\t\tsort.Sort(rl)\n\t\t\tfor _, r := range rl {\n\t\t\t\tmakeNode(r.ActsOn())\n\t\t\t}\n\t\t}\n\t}\n\n\t// Output the sorted targets.\n\tfor _, target := range targets {\n\t\tvar tname string\n\t\tif ctx.graphViz {\n\t\t\ttname = target.Name()\n\t\t} else {\n\t\t\ttname = targetOut(target, \":\")\n\t\t}\n\n\t\trl := resolutions.Resolutions(target)\n\t\tsort.Sort(rl)\n\t\tfor _, r := range rl {\n\t\t\tvar aname string\n\t\t\tif ctx.graphViz {\n\t\t\t\taname = r.ActsOn().Name()\n\t\t\t} else {\n\t\t\t\taname = targetOut(r.ActsOn(), \":\")\n\t\t\t}\n\n\t\t\t// cnames accumulates the list of condition names originating at a single origin that apply to `target`.\n\t\t\tcnames := r.Resolves().Names()\n\n\t\t\t// Output 1 line for each attachesTo+actsOn combination.\n\t\t\toutputResolution(tname, aname, cnames)\n\t\t}\n\t}\n\t// If graphViz output, rank the root nodes together, and complete the directed graph.\n\tif ctx.graphViz {\n\t\tfmt.Fprintf(stdout, \"\\t{rank=same;\")\n\t\tfor _, f := range files {\n\t\t\tfName := f\n\t\t\tif !strings.HasSuffix(fName, \".meta_lic\") {\n\t\t\t\tfName += \".meta_lic\"\n\t\t\t}\n\t\t\tif fNode, ok := nodes[fName]; ok {\n\t\t\t\tfmt.Fprintf(stdout, \" %s\", fNode)\n\t\t\t}\n\t\t}\n\t\tfmt.Fprintf(stdout, \"}\\n}\\n\")\n\t}\n\treturn licenseGraph, nil\n}\n"
  },
  {
    "path": "tools/compliance/cmd/dumpresolutions/dumpresolutions_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the parent directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"..\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc Test_plaintext(t *testing.T) {\n\ttests := []struct {\n\t\tcondition   string\n\t\tname        string\n\t\toutDir      string\n\t\troots       []string\n\t\tctx         context\n\t\texpectedOut []string\n\t}{\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/highest.apex.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/firstparty/\"}},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic notice\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic notice\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_notice\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  []compliance.LicenseCondition{compliance.NoticeCondition},\n\t\t\t\tstripPrefix: []string{\"testdata/firstparty/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic notice\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic notice\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_share\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/firstparty/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesPrivate.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/firstparty/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_share_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  append(compliance.ImpliesPrivate.AsList(), compliance.ImpliesShared.AsList()...),\n\t\t\t\tstripPrefix: []string{\"testdata/firstparty/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/firstparty/\"}, labelConditions: true},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice notice\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice notice\",\n\t\t\t\t\"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice notice\",\n\t\t\t\t\"lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice\",\n\t\t\t\t\"lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic testdata/firstparty/container.zip.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/firstparty/application.meta_lic testdata/firstparty/application.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/application.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic testdata/notice/highest.apex.meta_lic notice\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic testdata/notice/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic testdata/notice/lib/libb.so.meta_lic notice\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic testdata/notice/lib/libc.a.meta_lic notice\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/notice/\"}},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic notice\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic notice\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_notice\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  []compliance.LicenseCondition{compliance.NoticeCondition},\n\t\t\t\tstripPrefix: []string{\"testdata/notice/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic notice\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic notice\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic notice\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_share\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/notice/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesPrivate.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/notice/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_share_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...),\n\t\t\t\tstripPrefix: []string{\"testdata/notice/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/notice/\"}, labelConditions: true},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice notice\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice notice\",\n\t\t\t\t\"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice notice\",\n\t\t\t\t\"lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice\",\n\t\t\t\t\"lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic testdata/notice/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic testdata/notice/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic testdata/notice/container.zip.meta_lic notice\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic testdata/notice/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic testdata/notice/lib/libb.so.meta_lic notice\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic testdata/notice/lib/libc.a.meta_lic notice\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/notice/application.meta_lic testdata/notice/application.meta_lic notice\",\n\t\t\t\t\"testdata/notice/application.meta_lic testdata/notice/lib/liba.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/highest.apex.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/reciprocal/\"}},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_notice\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  []compliance.LicenseCondition{compliance.NoticeCondition},\n\t\t\t\tstripPrefix: []string{\"testdata/reciprocal/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_share\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/reciprocal/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesPrivate.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/reciprocal/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_share_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...),\n\t\t\t\tstripPrefix: []string{\"testdata/reciprocal/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/reciprocal/\"}, labelConditions: true},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:reciprocal reciprocal\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal\",\n\t\t\t\t\"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/liba.so.meta_lic:reciprocal reciprocal\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal\",\n\t\t\t\t\"lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal reciprocal\",\n\t\t\t\t\"lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/container.zip.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/reciprocal/application.meta_lic testdata/reciprocal/application.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic notice:restricted:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/restricted/\"}},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic notice:restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic notice:restricted\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic notice:restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic notice:restricted\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic notice:restricted:restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_notice\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  []compliance.LicenseCondition{compliance.NoticeCondition},\n\t\t\t\tstripPrefix: []string{\"testdata/restricted/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_share\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/restricted/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic restricted\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic restricted:restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesPrivate.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/restricted/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_share_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...),\n\t\t\t\tstripPrefix: []string{\"testdata/restricted/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic restricted\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic restricted:restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/restricted/\"}, labelConditions: true},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice:restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted_if_statically_linked restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice:restricted\",\n\t\t\t\t\"bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted restricted\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice:restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice:restricted\",\n\t\t\t\t\"highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice:restricted:restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted_if_statically_linked restricted_if_statically_linked\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"lib/liba.so.meta_lic:restricted_if_statically_linked lib/liba.so.meta_lic:restricted_if_statically_linked restricted_if_statically_linked\",\n\t\t\t\t\"lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic notice:restricted:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic notice:restricted:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted:restricted_if_statically_linked\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/highest.apex.meta_lic notice:restricted\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/proprietary/\"}},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic restricted:proprietary:by_exception_only\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic restricted:proprietary:by_exception_only\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic notice:restricted\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_notice\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  []compliance.LicenseCondition{compliance.NoticeCondition},\n\t\t\t\tstripPrefix: []string{\"testdata/proprietary/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin1.meta_lic notice\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_share\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/proprietary/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic restricted\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesPrivate.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/proprietary/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic proprietary\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic proprietary\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic proprietary\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic proprietary\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic proprietary\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic proprietary\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_share_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...),\n\t\t\t\tstripPrefix: []string{\"testdata/proprietary/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic lib/liba.so.meta_lic proprietary\",\n\t\t\t\t\"bin/bin1.meta_lic lib/libc.a.meta_lic proprietary\",\n\t\t\t\t\"bin/bin2.meta_lic bin/bin2.meta_lic restricted:proprietary\",\n\t\t\t\t\"bin/bin2.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic bin/bin2.meta_lic restricted:proprietary\",\n\t\t\t\t\"highest.apex.meta_lic highest.apex.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic lib/liba.so.meta_lic proprietary\",\n\t\t\t\t\"highest.apex.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"highest.apex.meta_lic lib/libc.a.meta_lic proprietary\",\n\t\t\t\t\"lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary\",\n\t\t\t\t\"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/proprietary/\"}, labelConditions: true},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only\",\n\t\t\t\t\"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:proprietary:by_exception_only proprietary:by_exception_only\",\n\t\t\t\t\"bin/bin2.meta_lic:proprietary:by_exception_only bin/bin2.meta_lic:proprietary:by_exception_only restricted:proprietary:by_exception_only\",\n\t\t\t\t\"bin/bin2.meta_lic:proprietary:by_exception_only lib/libb.so.meta_lic:restricted restricted\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice\",\n\t\t\t\t\"highest.apex.meta_lic:notice bin/bin2.meta_lic:proprietary:by_exception_only restricted:proprietary:by_exception_only\",\n\t\t\t\t\"highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice:restricted\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted\",\n\t\t\t\t\"highest.apex.meta_lic:notice lib/libc.a.meta_lic:proprietary:by_exception_only proprietary:by_exception_only\",\n\t\t\t\t\"lib/liba.so.meta_lic:proprietary:by_exception_only lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only\",\n\t\t\t\t\"lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic testdata/proprietary/container.zip.meta_lic notice:restricted\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/proprietary/application.meta_lic testdata/proprietary/application.meta_lic notice:restricted\",\n\t\t\t\t\"testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic restricted:proprietary:by_exception_only\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic notice\",\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\texpectedOut := &bytes.Buffer{}\n\t\t\tfor _, eo := range tt.expectedOut {\n\t\t\t\texpectedOut.WriteString(eo)\n\t\t\t\texpectedOut.WriteString(\"\\n\")\n\t\t\t}\n\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\t\t\t_, err := dumpResolutions(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"dumpresolutions: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif stderr.Len() > 0 {\n\t\t\t\tt.Errorf(\"dumpresolutions: gotStderr = %v, want none\", stderr)\n\t\t\t}\n\t\t\tout := stdout.String()\n\t\t\texpected := expectedOut.String()\n\t\t\tif out != expected {\n\t\t\t\toutList := strings.Split(out, \"\\n\")\n\t\t\t\texpectedList := strings.Split(expected, \"\\n\")\n\t\t\t\tstartLine := 0\n\t\t\t\tfor len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] {\n\t\t\t\t\tstartLine++\n\t\t\t\t}\n\t\t\t\tt.Errorf(\"dumpresoliutions: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v\",\n\t\t\t\t\tout, expected, startLine+1, outList[startLine], expectedList[startLine])\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype testContext struct {\n\tnextNode int\n\tnodes    map[string]string\n}\n\ntype matcher interface {\n\tmatchString(*testContext, *compliance.LicenseGraph) string\n\ttypeString() string\n}\n\ntype targetMatcher struct {\n\ttarget     string\n\tconditions []string\n}\n\n// newTestCondition constructs a test license condition in the license graph.\nfunc newTestCondition(lg *compliance.LicenseGraph, conditionName ...string) compliance.LicenseConditionSet {\n\tcs := compliance.NewLicenseConditionSet()\n\tfor _, name := range conditionName {\n\t\tcs = cs.Plus(compliance.RecognizedConditionNames[name])\n\t}\n\tif cs.IsEmpty() && len(conditionName) != 0 {\n\t\tpanic(fmt.Errorf(\"attempt to create unrecognized condition: %q\", conditionName))\n\t}\n\treturn cs\n}\n\nfunc (tm *targetMatcher) matchString(ctx *testContext, lg *compliance.LicenseGraph) string {\n\tcs := newTestCondition(lg, tm.conditions...)\n\tm := tm.target\n\tif !cs.IsEmpty() {\n\t\tm += \"\\\\n\" + strings.Join(cs.Names(), \"\\\\n\")\n\t}\n\tm = ctx.nodes[tm.target] + \" [label=\\\"\" + m + \"\\\"];\"\n\treturn m\n}\n\nfunc (tm *targetMatcher) typeString() string {\n\treturn \"target\"\n}\n\ntype resolutionMatcher struct {\n\tappliesTo  string\n\tactsOn     string\n\tconditions []string\n}\n\nfunc (rm *resolutionMatcher) matchString(ctx *testContext, lg *compliance.LicenseGraph) string {\n\tcs := newTestCondition(lg, rm.conditions...)\n\treturn ctx.nodes[rm.appliesTo] + \" -> \" + ctx.nodes[rm.actsOn] +\n\t\t\" [label=\\\"\" + strings.Join(cs.Names(), \"\\\\n\") + \"\\\"];\"\n}\n\nfunc (rm *resolutionMatcher) typeString() string {\n\treturn \"resolution\"\n}\n\ntype getMatcher func(*testContext) matcher\n\nfunc matchTarget(target string, conditions ...string) getMatcher {\n\treturn func(ctx *testContext) matcher {\n\t\tctx.nodes[target] = fmt.Sprintf(\"n%d\", ctx.nextNode)\n\t\tctx.nextNode++\n\t\treturn &targetMatcher{target, append([]string{}, conditions...)}\n\t}\n}\n\nfunc matchResolution(appliesTo, actsOn string, conditions ...string) getMatcher {\n\treturn func(ctx *testContext) matcher {\n\t\tif _, ok := ctx.nodes[appliesTo]; !ok {\n\t\t\tctx.nodes[appliesTo] = fmt.Sprintf(\"unknown%d\", ctx.nextNode)\n\t\t\tctx.nextNode++\n\t\t}\n\t\tif _, ok := ctx.nodes[actsOn]; !ok {\n\t\t\tctx.nodes[actsOn] = fmt.Sprintf(\"unknown%d\", ctx.nextNode)\n\t\t\tctx.nextNode++\n\t\t}\n\t\treturn &resolutionMatcher{appliesTo, actsOn, append([]string{}, conditions...)}\n\t}\n}\n\nfunc Test_graphviz(t *testing.T) {\n\ttests := []struct {\n\t\tcondition   string\n\t\tname        string\n\t\toutDir      string\n\t\troots       []string\n\t\tctx         context\n\t\texpectedOut []getMatcher\n\t}{\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/firstparty/\"}},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_notice\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  []compliance.LicenseCondition{compliance.NoticeCondition},\n\t\t\t\tstripPrefix: []string{\"testdata/firstparty/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_share\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/firstparty/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesPrivate.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/firstparty/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_share_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/firstparty/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/firstparty/\"}, labelConditions: true},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/container.zip.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/container.zip.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/firstparty/application.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/application.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/application.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/application.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/firstparty/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/firstparty/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/notice/\"}},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_notice\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  []compliance.LicenseCondition{compliance.NoticeCondition},\n\t\t\t\tstripPrefix: []string{\"testdata/notice/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_share\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/notice/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesPrivate.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/notice/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_share_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/notice/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/notice/\"}, labelConditions: true},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/container.zip.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/notice/application.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/application.meta_lic\",\n\t\t\t\t\t\"testdata/notice/application.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/application.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/notice/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/notice/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/reciprocal/\"}},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_notice\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  []compliance.LicenseCondition{compliance.NoticeCondition},\n\t\t\t\tstripPrefix: []string{\"testdata/reciprocal/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_share\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/reciprocal/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesPrivate.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/reciprocal/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_share_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/reciprocal/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/reciprocal/\"}, labelConditions: true},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\", \"reciprocal\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\", \"reciprocal\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\", \"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/container.zip.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/reciprocal/application.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/application.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/application.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/application.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/reciprocal/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/reciprocal/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/highest.apex.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/restricted/\"}},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_notice\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  []compliance.LicenseCondition{compliance.NoticeCondition},\n\t\t\t\tstripPrefix: []string{\"testdata/restricted/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_share\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/restricted/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesPrivate.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/restricted/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_share_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/restricted/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/restricted/\"}, labelConditions: true},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\", \"restricted_if_statically_linked\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\", \"reciprocal\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\", \"restricted\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\", \"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/container.zip.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/container.zip.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/restricted/application.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/application.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/application.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/application.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/restricted/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t\"reciprocal\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/restricted/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/proprietary/\"}},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_notice\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  []compliance.LicenseCondition{compliance.NoticeCondition},\n\t\t\t\tstripPrefix: []string{\"testdata/proprietary/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_share\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/proprietary/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesPrivate.AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/proprietary/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_share_private\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tconditions:  compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(),\n\t\t\t\tstripPrefix: []string{\"testdata/proprietary/\"},\n\t\t\t},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_labelled\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx:       context{stripPrefix: []string{\"testdata/proprietary/\"}, labelConditions: true},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"bin/bin1.meta_lic\", \"notice\"),\n\t\t\t\tmatchTarget(\"lib/liba.so.meta_lic\", \"by_exception_only\", \"proprietary\"),\n\t\t\t\tmatchTarget(\"lib/libc.a.meta_lic\", \"by_exception_only\", \"proprietary\"),\n\t\t\t\tmatchTarget(\"bin/bin2.meta_lic\", \"by_exception_only\", \"proprietary\"),\n\t\t\t\tmatchTarget(\"lib/libb.so.meta_lic\", \"restricted\"),\n\t\t\t\tmatchTarget(\"highest.apex.meta_lic\", \"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"highest.apex.meta_lic\",\n\t\t\t\t\t\"lib/libc.a.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin2.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libb.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/container.zip.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/container.zip.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/container.zip.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/proprietary/application.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/application.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/application.meta_lic\",\n\t\t\t\t\t\"notice\",\n\t\t\t\t\t\"restricted\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/application.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/proprietary/bin/bin1.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/liba.so.meta_lic\"),\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libc.a.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\t\"by_exception_only\",\n\t\t\t\t\t\"proprietary\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []getMatcher{\n\t\t\t\tmatchTarget(\"testdata/proprietary/lib/libd.so.meta_lic\"),\n\t\t\t\tmatchResolution(\n\t\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t\t\t\"notice\"),\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\tctx := &testContext{0, make(map[string]string)}\n\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\t\t\ttt.ctx.graphViz = true\n\t\t\tlg, err := dumpResolutions(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"dumpresolutions: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif stderr.Len() > 0 {\n\t\t\t\tt.Errorf(\"dumpresolutions: gotStderr = %v, want none\", stderr)\n\t\t\t}\n\n\t\t\texpectedOut := &bytes.Buffer{}\n\t\t\tfor _, eo := range tt.expectedOut {\n\t\t\t\tm := eo(ctx)\n\t\t\t\texpectedOut.WriteString(m.matchString(ctx, lg))\n\t\t\t\texpectedOut.WriteString(\"\\n\")\n\t\t\t}\n\n\t\t\toutList := strings.Split(stdout.String(), \"\\n\")\n\t\t\toutLine := 0\n\t\t\tif outList[outLine] != \"strict digraph {\" {\n\t\t\t\tt.Errorf(\"dumpresolutions: got 1st line %v, want strict digraph {\", outList[outLine])\n\t\t\t}\n\t\t\toutLine++\n\t\t\tif strings.HasPrefix(strings.TrimLeft(outList[outLine], \" \\t\"), \"rankdir\") {\n\t\t\t\toutLine++\n\t\t\t}\n\t\t\tendOut := len(outList)\n\t\t\tfor endOut > 0 && strings.TrimLeft(outList[endOut-1], \" \\t\") == \"\" {\n\t\t\t\tendOut--\n\t\t\t}\n\t\t\tif outList[endOut-1] != \"}\" {\n\t\t\t\tt.Errorf(\"dumpresolutions: got last line %v, want }\", outList[endOut-1])\n\t\t\t}\n\t\t\tendOut--\n\t\t\tif strings.HasPrefix(strings.TrimLeft(outList[endOut-1], \" \\t\"), \"{rank=same\") {\n\t\t\t\tendOut--\n\t\t\t}\n\t\t\texpectedList := strings.Split(expectedOut.String(), \"\\n\")\n\t\t\tfor len(expectedList) > 0 && expectedList[len(expectedList)-1] == \"\" {\n\t\t\t\texpectedList = expectedList[0 : len(expectedList)-1]\n\t\t\t}\n\t\t\tmatchLine := 0\n\n\t\t\tfor outLine < endOut && matchLine < len(expectedList) && strings.TrimLeft(outList[outLine], \" \\t\") == expectedList[matchLine] {\n\t\t\t\toutLine++\n\t\t\t\tmatchLine++\n\t\t\t}\n\t\t\tif outLine < endOut || matchLine < len(expectedList) {\n\t\t\t\tif outLine >= endOut {\n\t\t\t\t\tt.Errorf(\"dumpresolutions: missing lines at end of graph, want %d lines %v\", len(expectedList)-matchLine, strings.Join(expectedList[matchLine:], \"\\n\"))\n\t\t\t\t} else if matchLine >= len(expectedList) {\n\t\t\t\t\tt.Errorf(\"dumpresolutions: unexpected lines at end of graph starting line %d, got %v, want nothing\", outLine+1, strings.Join(outList[outLine:], \"\\n\"))\n\t\t\t\t} else {\n\t\t\t\t\tt.Errorf(\"dumpresolutions: at line %d, got %v, want %v\", outLine+1, strings.Join(outList[outLine:], \"\\n\"), strings.Join(expectedList[matchLine:], \"\\n\"))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/cmd/htmlnotice/htmlnotice.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"flag\"\n\t\"fmt\"\n\t\"html\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"android/soong/response\"\n\t\"android/soong/tools/compliance\"\n\n\t\"github.com/google/blueprint/deptools\"\n)\n\nvar (\n\tfailNoneRequested = fmt.Errorf(\"\\nNo license metadata files requested\")\n\tfailNoLicenses    = fmt.Errorf(\"No licenses found\")\n)\n\ntype context struct {\n\tstdout      io.Writer\n\tstderr      io.Writer\n\trootFS      fs.FS\n\tincludeTOC  bool\n\tproduct     string\n\tstripPrefix []string\n\ttitle       string\n\tdeps        *[]string\n}\n\nfunc (ctx context) strip(installPath string) string {\n\tfor _, prefix := range ctx.stripPrefix {\n\t\tif strings.HasPrefix(installPath, prefix) {\n\t\t\tp := strings.TrimPrefix(installPath, prefix)\n\t\t\tif 0 == len(p) {\n\t\t\t\tp = ctx.product\n\t\t\t}\n\t\t\tif 0 == len(p) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn p\n\t\t}\n\t}\n\treturn installPath\n}\n\n// newMultiString creates a flag that allows multiple values in an array.\nfunc newMultiString(flags *flag.FlagSet, name, usage string) *multiString {\n\tvar f multiString\n\tflags.Var(&f, name, usage)\n\treturn &f\n}\n\n// multiString implements the flag `Value` interface for multiple strings.\ntype multiString []string\n\nfunc (ms *multiString) String() string     { return strings.Join(*ms, \", \") }\nfunc (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }\n\nfunc main() {\n\tvar expandedArgs []string\n\tfor _, arg := range os.Args[1:] {\n\t\tif strings.HasPrefix(arg, \"@\") {\n\t\t\tf, err := os.Open(strings.TrimPrefix(arg, \"@\"))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\trespArgs, err := response.ReadRspFile(f)\n\t\t\tf.Close()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\texpandedArgs = append(expandedArgs, respArgs...)\n\t\t} else {\n\t\t\texpandedArgs = append(expandedArgs, arg)\n\t\t}\n\t}\n\n\tflags := flag.NewFlagSet(\"flags\", flag.ExitOnError)\n\n\tflags.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}\n\nOutputs an html NOTICE.html or gzipped NOTICE.html.gz file if the -o filename\nends with \".gz\".\n\nOptions:\n`, filepath.Base(os.Args[0]))\n\t\tflags.PrintDefaults()\n\t}\n\n\toutputFile := flags.String(\"o\", \"-\", \"Where to write the NOTICE text file. (default stdout)\")\n\tdepsFile := flags.String(\"d\", \"\", \"Where to write the deps file\")\n\tincludeTOC := flags.Bool(\"toc\", true, \"Whether to include a table of contents.\")\n\tproduct := flags.String(\"product\", \"\", \"The name of the product for which the notice is generated.\")\n\tstripPrefix := newMultiString(flags, \"strip_prefix\", \"Prefix to remove from paths. i.e. path to root (multiple allowed)\")\n\ttitle := flags.String(\"title\", \"\", \"The title of the notice file.\")\n\n\tflags.Parse(expandedArgs)\n\n\t// Must specify at least one root target.\n\tif flags.NArg() == 0 {\n\t\tflags.Usage()\n\t\tos.Exit(2)\n\t}\n\n\tif len(*outputFile) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"must specify file for -o; use - for stdout\\n\")\n\t\tos.Exit(2)\n\t} else {\n\t\tdir, err := filepath.Abs(filepath.Dir(*outputFile))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot determine path to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfi, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot read directory %q of %q: %s\\n\", dir, *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !fi.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"parent %q of %q is not a directory\\n\", dir, *outputFile)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar ofile io.Writer\n\tvar closer io.Closer\n\tofile = os.Stdout\n\tvar obuf *bytes.Buffer\n\tif *outputFile != \"-\" {\n\t\tobuf = &bytes.Buffer{}\n\t\tofile = obuf\n\t}\n\tif strings.HasSuffix(*outputFile, \".gz\") {\n\t\tofile, _ = gzip.NewWriterLevel(obuf, gzip.BestCompression)\n\t\tcloser = ofile.(io.Closer)\n\t}\n\n\tvar deps []string\n\n\tctx := &context{ofile, os.Stderr, compliance.FS, *includeTOC, *product, *stripPrefix, *title, &deps}\n\n\terr := htmlNotice(ctx, flags.Args()...)\n\tif err != nil {\n\t\tif err == failNoneRequested {\n\t\t\tflags.Usage()\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"%s\\n\", err.Error())\n\t\tos.Exit(1)\n\t}\n\tif closer != nil {\n\t\tcloser.Close()\n\t}\n\n\tif *outputFile != \"-\" {\n\t\terr := os.WriteFile(*outputFile, obuf.Bytes(), 0666)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write output to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tif *depsFile != \"\" {\n\t\terr := deptools.WriteDepFile(*depsFile, *outputFile, deps)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write deps to %q: %s\\n\", *depsFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tos.Exit(0)\n}\n\n// htmlNotice implements the htmlnotice utility.\nfunc htmlNotice(ctx *context, files ...string) error {\n\t// Must be at least one root file.\n\tif len(files) < 1 {\n\t\treturn failNoneRequested\n\t}\n\n\t// Read the license graph from the license metadata files (*.meta_lic).\n\tlicenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license metadata file(s) %q: %v\\n\", files, err)\n\t}\n\tif licenseGraph == nil {\n\t\treturn failNoLicenses\n\t}\n\n\t// rs contains all notice resolutions.\n\trs := compliance.ResolveNotices(licenseGraph)\n\n\tni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license text file(s) for %q: %v\\n\", files, err)\n\t}\n\n\tfmt.Fprintln(ctx.stdout, \"<!DOCTYPE html>\")\n\tfmt.Fprintln(ctx.stdout, \"<html><head>\")\n\tfmt.Fprintln(ctx.stdout, \"<style type=\\\"text/css\\\">\")\n\tfmt.Fprintln(ctx.stdout, \"body { padding: 2px; margin: 0; }\")\n\tfmt.Fprintln(ctx.stdout, \"ul { list-style-type: none; margin: 0; padding: 0; }\")\n\tfmt.Fprintln(ctx.stdout, \"li { padding-left: 1em; }\")\n\tfmt.Fprintln(ctx.stdout, \".file-list { margin-left: 1em; }\")\n\tfmt.Fprintln(ctx.stdout, \"</style>\")\n\tif len(ctx.title) > 0 {\n\t\tfmt.Fprintf(ctx.stdout, \"<title>%s</title>\\n\", html.EscapeString(ctx.title))\n\t} else if len(ctx.product) > 0 {\n\t\tfmt.Fprintf(ctx.stdout, \"<title>%s</title>\\n\", html.EscapeString(ctx.product))\n\t}\n\tfmt.Fprintln(ctx.stdout, \"</head>\")\n\tfmt.Fprintln(ctx.stdout, \"<body>\")\n\n\tif len(ctx.title) > 0 {\n\t\tfmt.Fprintf(ctx.stdout, \"  <h1>%s</h1>\\n\", html.EscapeString(ctx.title))\n\t} else if len(ctx.product) > 0 {\n\t\tfmt.Fprintf(ctx.stdout, \"  <h1>%s</h1>\\n\", html.EscapeString(ctx.product))\n\t}\n\tids := make(map[string]string)\n\tif ctx.includeTOC {\n\t\tfmt.Fprintln(ctx.stdout, \"  <ul class=\\\"toc\\\">\")\n\t\ti := 0\n\t\tfor installPath := range ni.InstallPaths() {\n\t\t\tid := fmt.Sprintf(\"id%d\", i)\n\t\t\ti++\n\t\t\tids[installPath] = id\n\t\t\tfmt.Fprintf(ctx.stdout, \"    <li id=\\\"%s\\\"><strong>%s</strong>\\n      <ul>\\n\", id, html.EscapeString(ctx.strip(installPath)))\n\t\t\tfor _, h := range ni.InstallHashes(installPath) {\n\t\t\t\tlibs := ni.InstallHashLibs(installPath, h)\n\t\t\t\tfmt.Fprintf(ctx.stdout, \"        <li><a href=\\\"#%s\\\">%s</a>\\n\", h.String(), html.EscapeString(strings.Join(libs, \", \")))\n\t\t\t}\n\t\t\tfmt.Fprintln(ctx.stdout, \"      </ul>\")\n\t\t}\n\t\tfmt.Fprintln(ctx.stdout, \"  </ul><!-- toc -->\")\n\t}\n\tfor h := range ni.Hashes() {\n\t\tfmt.Fprintln(ctx.stdout, \"  <hr>\")\n\t\tfor _, libName := range ni.HashLibs(h) {\n\t\t\tfmt.Fprintf(ctx.stdout, \"  <strong>%s</strong> used by:\\n    <ul class=\\\"file-list\\\">\\n\", html.EscapeString(libName))\n\t\t\tfor _, installPath := range ni.HashLibInstalls(h, libName) {\n\t\t\t\tif id, ok := ids[installPath]; ok {\n\t\t\t\t\tfmt.Fprintf(ctx.stdout, \"      <li><a href=\\\"#%s\\\">%s</a>\\n\", id, html.EscapeString(ctx.strip(installPath)))\n\t\t\t\t} else {\n\t\t\t\t\tfmt.Fprintf(ctx.stdout, \"      <li>%s\\n\", html.EscapeString(ctx.strip(installPath)))\n\t\t\t\t}\n\t\t\t}\n\t\t\tfmt.Fprintf(ctx.stdout, \"    </ul>\\n\")\n\t\t}\n\t\tfmt.Fprintf(ctx.stdout, \"  </ul>\\n  <a id=\\\"%s\\\"/><pre class=\\\"license-text\\\">\", h.String())\n\t\tfmt.Fprintln(ctx.stdout, html.EscapeString(string(ni.HashText(h))))\n\t\tfmt.Fprintln(ctx.stdout, \"  </pre><!-- license-text -->\")\n\t}\n\tfmt.Fprintln(ctx.stdout, \"</body></html>\")\n\n\t*ctx.deps = ni.InputFiles()\n\tsort.Strings(*ctx.deps)\n\n\treturn nil\n}\n"
  },
  {
    "path": "tools/compliance/cmd/htmlnotice/htmlnotice_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"html\"\n\t\"os\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance\"\n)\n\nvar (\n\thorizontalRule = regexp.MustCompile(`^\\s*<hr>\\s*$`)\n\tbodyTag        = regexp.MustCompile(`^\\s*<body>\\s*$`)\n\tboilerPlate    = regexp.MustCompile(`^\\s*(?:<ul class=\"file-list\">|<ul>|</.*)\\s*$`)\n\ttocTag         = regexp.MustCompile(`^\\s*<ul class=\"toc\">\\s*$`)\n\tlibraryName    = regexp.MustCompile(`^\\s*<strong>(.*)</strong>\\s\\s*used\\s\\s*by\\s*:\\s*$`)\n\tlicenseText    = regexp.MustCompile(`^\\s*<a id=\"[^\"]{32}\"/><pre class=\"license-text\">(.*)$`)\n\ttitleTag       = regexp.MustCompile(`^\\s*<title>(.*)</title>\\s*$`)\n\th1Tag          = regexp.MustCompile(`^\\s*<h1>(.*)</h1>\\s*$`)\n\tusedByTarget   = regexp.MustCompile(`^\\s*<li>(?:<a href=\"#id[0-9]+\">)?((?:out/(?:[^/<]*/)+)[^/<]*)(?:</a>)?\\s*$`)\n\tinstallTarget  = regexp.MustCompile(`^\\s*<li id=\"id[0-9]+\"><strong>(.*)</strong>\\s*$`)\n\tlibReference   = regexp.MustCompile(`^\\s*<li><a href=\"#[^\"]{32}\">(.*)</a>\\s*$`)\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the parent directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"..\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc Test(t *testing.T) {\n\ttests := []struct {\n\t\tcondition    string\n\t\tname         string\n\t\toutDir       string\n\t\troots        []string\n\t\tincludeTOC   bool\n\t\tstripPrefix  string\n\t\ttitle        string\n\t\texpectedOut  []matcher\n\t\texpectedDeps []string\n\t}{\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:  \"firstparty\",\n\t\t\tname:       \"apex+toc\",\n\t\t\troots:      []string{\"highest.apex.meta_lic\"},\n\t\t\tincludeTOC: true,\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttoc{},\n\t\t\t\ttarget{\"highest.apex\"},\n\t\t\t\tuses{\"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\"},\n\t\t\t\tuses{\"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin2\"},\n\t\t\t\tuses{\"Android\"},\n\t\t\t\ttarget{\"highest.apex/lib/liba.so\"},\n\t\t\t\tuses{\"Android\"},\n\t\t\t\ttarget{\"highest.apex/lib/libb.so\"},\n\t\t\t\tuses{\"Android\"},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex-with-title\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\ttitle:     \"Emperor\",\n\t\t\texpectedOut: []matcher{\n\t\t\t\tpageTitle{\"Emperor\"},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:  \"firstparty\",\n\t\t\tname:       \"apex-with-title+toc\",\n\t\t\troots:      []string{\"highest.apex.meta_lic\"},\n\t\t\tincludeTOC: true,\n\t\t\ttitle:      \"Emperor\",\n\t\t\texpectedOut: []matcher{\n\t\t\t\tpageTitle{\"Emperor\"},\n\t\t\t\ttoc{},\n\t\t\t\ttarget{\"highest.apex\"},\n\t\t\t\tuses{\"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\"},\n\t\t\t\tuses{\"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin2\"},\n\t\t\t\tuses{\"Android\"},\n\t\t\t\ttarget{\"highest.apex/lib/liba.so\"},\n\t\t\t\tuses{\"Android\"},\n\t\t\t\ttarget{\"highest.apex/lib/libb.so\"},\n\t\t\t\tuses{\"Android\"},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tusedBy{\"container.zip/liba.so\"},\n\t\t\t\tusedBy{\"container.zip/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/application.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"lib/libd.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tusedBy{\"container.zip/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/application.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"lib/libd.so\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tusedBy{\"container.zip/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/application.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"lib/libd.so\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\trestricted{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tusedBy{\"container.zip/libb.so\"},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/liba.so\"},\n\t\t\t\trestricted{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\trestricted{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/application.meta_lic\",\n\t\t\t\t\"testdata/restricted/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\trestricted{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"lib/libd.so\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\trestricted{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tusedBy{\"container.zip/libb.so\"},\n\t\t\t\trestricted{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/application.meta_lic\",\n\t\t\t\t\"testdata/proprietary/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"lib/libd.so\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\n\t\t\tvar deps []string\n\n\t\t\tctx := context{stdout, stderr, compliance.GetFS(tt.outDir), tt.includeTOC, \"\", []string{tt.stripPrefix}, tt.title, &deps}\n\n\t\t\terr := htmlNotice(&ctx, rootFiles...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"htmlnotice: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif stderr.Len() > 0 {\n\t\t\t\tt.Errorf(\"htmlnotice: gotStderr = %v, want none\", stderr)\n\t\t\t}\n\n\t\t\tt.Logf(\"got stdout: %s\", stdout.String())\n\n\t\t\tt.Logf(\"want stdout: %s\", matcherList(tt.expectedOut).String())\n\n\t\t\tout := bufio.NewScanner(stdout)\n\t\t\tlineno := 0\n\t\t\tinBody := false\n\t\t\thasTitle := false\n\t\t\tttle, expectTitle := tt.expectedOut[0].(pageTitle)\n\t\t\tfor out.Scan() {\n\t\t\t\tline := out.Text()\n\t\t\t\tif strings.TrimLeft(line, \" \") == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !inBody {\n\t\t\t\t\tif expectTitle {\n\t\t\t\t\t\tif tl := checkTitle(line); len(tl) > 0 {\n\t\t\t\t\t\t\tif tl != ttle.t {\n\t\t\t\t\t\t\t\tt.Errorf(\"htmlnotice: unexpected title: got %q, want %q\", tl, ttle.t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\thasTitle = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif bodyTag.MatchString(line) {\n\t\t\t\t\t\tinBody = true\n\t\t\t\t\t\tif expectTitle && !hasTitle {\n\t\t\t\t\t\t\tt.Errorf(\"htmlnotice: missing title: got no <title> tag, want <title>%s</title>\", ttle.t)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif boilerPlate.MatchString(line) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(tt.expectedOut) <= lineno {\n\t\t\t\t\tt.Errorf(\"htmlnotice: unexpected output at line %d: got %q, want nothing (wanted %d lines)\", lineno+1, line, len(tt.expectedOut))\n\t\t\t\t} else if !tt.expectedOut[lineno].isMatch(line) {\n\t\t\t\t\tt.Errorf(\"htmlnotice: unexpected output at line %d: got %q, want %q\", lineno+1, line, tt.expectedOut[lineno].String())\n\t\t\t\t}\n\t\t\t\tlineno++\n\t\t\t}\n\t\t\tif !inBody {\n\t\t\t\tt.Errorf(\"htmlnotice: missing body: got no <body> tag, want <body> tag followed by %s\", matcherList(tt.expectedOut).String())\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor ; lineno < len(tt.expectedOut); lineno++ {\n\t\t\t\tt.Errorf(\"htmlnotice: missing output line %d: ended early, want %q\", lineno+1, tt.expectedOut[lineno].String())\n\t\t\t}\n\n\t\t\tt.Logf(\"got deps: %q\", deps)\n\n\t\t\tt.Logf(\"want deps: %q\", tt.expectedDeps)\n\n\t\t\tif g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {\n\t\t\t\tt.Errorf(\"unexpected deps, wanted:\\n%s\\ngot:\\n%s\\n\",\n\t\t\t\t\tstrings.Join(w, \"\\n\"), strings.Join(g, \"\\n\"))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc checkTitle(line string) string {\n\tgroups := titleTag.FindStringSubmatch(line)\n\tif len(groups) != 2 {\n\t\treturn \"\"\n\t}\n\treturn groups[1]\n}\n\ntype matcher interface {\n\tisMatch(line string) bool\n\tString() string\n}\n\ntype pageTitle struct {\n\tt string\n}\n\nfunc (m pageTitle) isMatch(line string) bool {\n\tgroups := h1Tag.FindStringSubmatch(line)\n\tif len(groups) != 2 {\n\t\treturn false\n\t}\n\treturn groups[1] == html.EscapeString(m.t)\n}\n\nfunc (m pageTitle) String() string {\n\treturn \"  <h1>\" + html.EscapeString(m.t) + \"</h1>\"\n}\n\ntype toc struct{}\n\nfunc (m toc) isMatch(line string) bool {\n\treturn tocTag.MatchString(line)\n}\n\nfunc (m toc) String() string {\n\treturn `  <ul class=\"toc\">`\n}\n\ntype target struct {\n\tname string\n}\n\nfunc (m target) isMatch(line string) bool {\n\tgroups := installTarget.FindStringSubmatch(line)\n\tif len(groups) != 2 {\n\t\treturn false\n\t}\n\treturn strings.HasPrefix(groups[1], \"out/\") && strings.HasSuffix(groups[1], \"/\"+html.EscapeString(m.name))\n}\n\nfunc (m target) String() string {\n\treturn `  <li id=\"id#\"><strong>` + html.EscapeString(m.name) + `</strong>`\n}\n\ntype uses struct {\n\tname string\n}\n\nfunc (m uses) isMatch(line string) bool {\n\tgroups := libReference.FindStringSubmatch(line)\n\tif len(groups) != 2 {\n\t\treturn false\n\t}\n\treturn groups[1] == html.EscapeString(m.name)\n}\n\nfunc (m uses) String() string {\n\treturn `  <li><a href=\"#hash\">` + html.EscapeString(m.name) + `</a>`\n}\n\ntype hr struct{}\n\nfunc (m hr) isMatch(line string) bool {\n\treturn horizontalRule.MatchString(line)\n}\n\nfunc (m hr) String() string {\n\treturn \"  <hr>\"\n}\n\ntype library struct {\n\tname string\n}\n\nfunc (m library) isMatch(line string) bool {\n\tgroups := libraryName.FindStringSubmatch(line)\n\tif len(groups) != 2 {\n\t\treturn false\n\t}\n\treturn groups[1] == html.EscapeString(m.name)\n}\n\nfunc (m library) String() string {\n\treturn \"  <strong>\" + html.EscapeString(m.name) + \"</strong> used by:\"\n}\n\ntype usedBy struct {\n\tname string\n}\n\nfunc (m usedBy) isMatch(line string) bool {\n\tgroups := usedByTarget.FindStringSubmatch(line)\n\tif len(groups) != 2 {\n\t\treturn false\n\t}\n\treturn strings.HasPrefix(groups[1], \"out/\") && strings.HasSuffix(groups[1], \"/\"+html.EscapeString(m.name))\n}\n\nfunc (m usedBy) String() string {\n\treturn \"  <li>out/.../\" + html.EscapeString(m.name)\n}\n\nfunc matchesText(line, text string) bool {\n\tgroups := licenseText.FindStringSubmatch(line)\n\tif len(groups) != 2 {\n\t\treturn false\n\t}\n\treturn groups[1] == html.EscapeString(text)\n}\n\nfunc expectedText(text string) string {\n\treturn `  <a href=\"#hash\"/><pre class=\"license-text\">` + html.EscapeString(text)\n}\n\ntype firstParty struct{}\n\nfunc (m firstParty) isMatch(line string) bool {\n\treturn matchesText(line, \"&&&First Party License&&&\")\n}\n\nfunc (m firstParty) String() string {\n\treturn expectedText(\"&&&First Party License&&&\")\n}\n\ntype notice struct{}\n\nfunc (m notice) isMatch(line string) bool {\n\treturn matchesText(line, \"%%%Notice License%%%\")\n}\n\nfunc (m notice) String() string {\n\treturn expectedText(\"%%%Notice License%%%\")\n}\n\ntype reciprocal struct{}\n\nfunc (m reciprocal) isMatch(line string) bool {\n\treturn matchesText(line, \"$$$Reciprocal License$$$\")\n}\n\nfunc (m reciprocal) String() string {\n\treturn expectedText(\"$$$Reciprocal License$$$\")\n}\n\ntype restricted struct{}\n\nfunc (m restricted) isMatch(line string) bool {\n\treturn matchesText(line, \"###Restricted License###\")\n}\n\nfunc (m restricted) String() string {\n\treturn expectedText(\"###Restricted License###\")\n}\n\ntype proprietary struct{}\n\nfunc (m proprietary) isMatch(line string) bool {\n\treturn matchesText(line, \"@@@Proprietary License@@@\")\n}\n\nfunc (m proprietary) String() string {\n\treturn expectedText(\"@@@Proprietary License@@@\")\n}\n\ntype matcherList []matcher\n\nfunc (l matcherList) String() string {\n\tvar sb strings.Builder\n\tfor _, m := range l {\n\t\ts := m.String()\n\t\tif s[:3] == s[len(s)-3:] {\n\t\t\tfmt.Fprintln(&sb)\n\t\t}\n\t\tfmt.Fprintf(&sb, \"%s\\n\", s)\n\t\tif s[:3] == s[len(s)-3:] {\n\t\t\tfmt.Fprintln(&sb)\n\t\t}\n\t}\n\treturn sb.String()\n}\n"
  },
  {
    "path": "tools/compliance/cmd/listshare/listshare.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"android/soong/response\"\n\t\"android/soong/tools/compliance\"\n)\n\nvar (\n\tfailNoneRequested = fmt.Errorf(\"\\nNo license metadata files requested\")\n\tfailNoLicenses    = fmt.Errorf(\"No licenses found\")\n)\n\nfunc main() {\n\tvar expandedArgs []string\n\tfor _, arg := range os.Args[1:] {\n\t\tif strings.HasPrefix(arg, \"@\") {\n\t\t\tf, err := os.Open(strings.TrimPrefix(arg, \"@\"))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\trespArgs, err := response.ReadRspFile(f)\n\t\t\tf.Close()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\texpandedArgs = append(expandedArgs, respArgs...)\n\t\t} else {\n\t\t\texpandedArgs = append(expandedArgs, arg)\n\t\t}\n\t}\n\n\tflags := flag.NewFlagSet(\"flags\", flag.ExitOnError)\n\n\tflags.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, `Usage: %s {-o outfile} file.meta_lic {file.meta_lic...}\n\nOutputs a csv file with 1 project per line in the first field followed\nby target:condition pairs describing why the project must be shared.\n\nEach target is the path to a generated license metadata file for a\nSoong module or Make target, and the license condition is either\nrestricted (e.g. GPL) or reciprocal (e.g. MPL).\n`, filepath.Base(os.Args[0]))\n\t}\n\n\toutputFile := flags.String(\"o\", \"-\", \"Where to write the list of projects to share. (default stdout)\")\n\n\tflags.Parse(expandedArgs)\n\n\t// Must specify at least one root target.\n\tif flags.NArg() == 0 {\n\t\tflags.Usage()\n\t\tos.Exit(2)\n\t}\n\n\tif len(*outputFile) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"must specify file for -o; use - for stdout\\n\")\n\t\tos.Exit(2)\n\t} else {\n\t\tdir, err := filepath.Abs(filepath.Dir(*outputFile))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot determine path to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfi, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot read directory %q of %q: %s\\n\", dir, *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !fi.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"parent %q of %q is not a directory\\n\", dir, *outputFile)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar ofile io.Writer\n\tofile = os.Stdout\n\tvar obuf *bytes.Buffer\n\tif *outputFile != \"-\" {\n\t\tobuf = &bytes.Buffer{}\n\t\tofile = obuf\n\t}\n\n\terr := listShare(ofile, os.Stderr, compliance.FS, flags.Args()...)\n\tif err != nil {\n\t\tif err == failNoneRequested {\n\t\t\tflags.Usage()\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"%s\\n\", err.Error())\n\t\tos.Exit(1)\n\t}\n\tif *outputFile != \"-\" {\n\t\terr := os.WriteFile(*outputFile, obuf.Bytes(), 0666)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write output to %q from %q: %s\\n\", *outputFile, os.Getenv(\"PWD\"), err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tos.Exit(0)\n}\n\n// listShare implements the listshare utility.\nfunc listShare(stdout, stderr io.Writer, rootFS fs.FS, files ...string) error {\n\t// Must be at least one root file.\n\tif len(files) < 1 {\n\t\treturn failNoneRequested\n\t}\n\n\t// Read the license graph from the license metadata files (*.meta_lic).\n\tlicenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license metadata file(s) %q from %q: %v\\n\", files, os.Getenv(\"PWD\"), err)\n\t}\n\tif licenseGraph == nil {\n\t\treturn failNoLicenses\n\t}\n\n\t// shareSource contains all source-sharing resolutions.\n\tshareSource := compliance.ResolveSourceSharing(licenseGraph)\n\n\t// Group the resolutions by project.\n\tpresolution := make(map[string]compliance.LicenseConditionSet)\n\tfor _, target := range shareSource.AttachesTo() {\n\t\tif shareSource.IsPureAggregate(target) && !target.LicenseConditions().MatchesAnySet(compliance.ImpliesShared) {\n\t\t\tcontinue\n\t\t}\n\t\trl := shareSource.Resolutions(target)\n\t\tsort.Sort(rl)\n\t\tfor _, r := range rl {\n\t\t\tfor _, p := range r.ActsOn().Projects() {\n\t\t\t\tif _, ok := presolution[p]; !ok {\n\t\t\t\t\tpresolution[p] = r.Resolves()\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tpresolution[p] = presolution[p].Union(r.Resolves())\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort the projects for repeatability/stability.\n\tprojects := make([]string, 0, len(presolution))\n\tfor p := range presolution {\n\t\tprojects = append(projects, p)\n\t}\n\tsort.Strings(projects)\n\n\t// Output the sorted projects and the source-sharing license conditions that each project resolves.\n\tfor _, p := range projects {\n\t\tif presolution[p].IsEmpty() {\n\t\t\tfmt.Fprintf(stdout, \"%s\\n\", p)\n\t\t} else {\n\t\t\tfmt.Fprintf(stdout, \"%s,%s\\n\", p, strings.Join(presolution[p].Names(), \",\"))\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "tools/compliance/cmd/listshare/listshare_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the parent directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"..\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc Test(t *testing.T) {\n\ttype projectShare struct {\n\t\tproject    string\n\t\tconditions []string\n\t}\n\ttests := []struct {\n\t\tcondition   string\n\t\tname        string\n\t\toutDir      string\n\t\troots       []string\n\t\texpectedOut []projectShare\n\t}{\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"device/library\",\n\t\t\t\t\tconditions: []string{\"reciprocal\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject: \"static/library\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"device/library\",\n\t\t\t\t\tconditions: []string{\"reciprocal\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject: \"static/library\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"device/library\",\n\t\t\t\t\tconditions: []string{\"reciprocal\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject: \"device/library\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject: \"static/library\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"base/library\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"device/library\",\n\t\t\t\t\tconditions: []string{\"restricted_if_statically_linked\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"dynamic/binary\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject: \"static/binary\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject: \"static/library\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"base/library\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"device/library\",\n\t\t\t\t\tconditions: []string{\"restricted_if_statically_linked\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"dynamic/binary\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject: \"static/binary\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject: \"static/library\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject: \"device/library\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject: \"distributable/application\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"restricted\",\n\t\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject: \"device/library\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject: \"static/binary\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject: \"static/library\",\n\t\t\t\t\tconditions: []string{\n\t\t\t\t\t\t\"reciprocal\",\n\t\t\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"base/library\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"dynamic/binary\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"base/library\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"dynamic/binary\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"device/library\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"distributable/application\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []projectShare{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"regressgpl1\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"bin/threelibraries\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"regressgpl1\",\n\t\t\tname:      \"containerplus\",\n\t\t\troots:     []string{\"container.zip.meta_lic\", \"lib/libapache.so.meta_lic\", \"lib/libc++.so.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"bin/threelibraries\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"lib/apache\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"lib/c++\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"regressgpl2\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"bin/threelibraries\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"lib/apache\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"lib/c++\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"lib/gpl\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"regressgpl2\",\n\t\t\tname:      \"containerplus\",\n\t\t\troots:     []string{\"container.zip.meta_lic\", \"lib/libapache.so.meta_lic\", \"lib/libc++.so.meta_lic\"},\n\t\t\texpectedOut: []projectShare{\n\t\t\t\t{\n\t\t\t\t\tproject:    \"bin/threelibraries\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"lib/apache\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"lib/c++\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:    \"lib/gpl\",\n\t\t\t\t\tconditions: []string{\"restricted\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\texpectedOut := &bytes.Buffer{}\n\t\t\tfor _, p := range tt.expectedOut {\n\t\t\t\texpectedOut.WriteString(p.project)\n\t\t\t\tfor _, lc := range p.conditions {\n\t\t\t\t\texpectedOut.WriteString(\",\")\n\t\t\t\t\texpectedOut.WriteString(lc)\n\t\t\t\t}\n\t\t\t\texpectedOut.WriteString(\"\\n\")\n\t\t\t}\n\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\t\t\terr := listShare(stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"listshare: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif stderr.Len() > 0 {\n\t\t\t\tt.Errorf(\"listshare: gotStderr = %v, want none\", stderr)\n\t\t\t}\n\t\t\tout := stdout.String()\n\t\t\texpected := expectedOut.String()\n\t\t\tif out != expected {\n\t\t\t\toutList := strings.Split(out, \"\\n\")\n\t\t\t\texpectedList := strings.Split(expected, \"\\n\")\n\t\t\t\tstartLine := 0\n\t\t\t\tfor len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] {\n\t\t\t\t\tstartLine++\n\t\t\t\t}\n\t\t\t\tt.Errorf(\"listshare: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v\",\n\t\t\t\t\tout, expected, startLine+1, outList[startLine], expectedList[startLine])\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/cmd/rtrace/rtrace.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"android/soong/response\"\n\t\"android/soong/tools/compliance\"\n)\n\nvar (\n\tfailNoneRequested = fmt.Errorf(\"\\nNo license metadata files requested\")\n\tfailNoSources     = fmt.Errorf(\"\\nNo projects or metadata files to trace back from\")\n\tfailNoLicenses    = fmt.Errorf(\"No licenses found\")\n)\n\ntype context struct {\n\tsources     []string\n\tstripPrefix []string\n}\n\nfunc (ctx context) strip(installPath string) string {\n\tfor _, prefix := range ctx.stripPrefix {\n\t\tif strings.HasPrefix(installPath, prefix) {\n\t\t\tp := strings.TrimPrefix(installPath, prefix)\n\t\t\tif 0 == len(p) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn p\n\t\t}\n\t}\n\treturn installPath\n}\n\n// newMultiString creates a flag that allows multiple values in an array.\nfunc newMultiString(flags *flag.FlagSet, name, usage string) *multiString {\n\tvar f multiString\n\tflags.Var(&f, name, usage)\n\treturn &f\n}\n\n// multiString implements the flag `Value` interface for multiple strings.\ntype multiString []string\n\nfunc (ms *multiString) String() string     { return strings.Join(*ms, \", \") }\nfunc (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }\n\nfunc main() {\n\tvar expandedArgs []string\n\tfor _, arg := range os.Args[1:] {\n\t\tif strings.HasPrefix(arg, \"@\") {\n\t\t\tf, err := os.Open(strings.TrimPrefix(arg, \"@\"))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\trespArgs, err := response.ReadRspFile(f)\n\t\t\tf.Close()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\texpandedArgs = append(expandedArgs, respArgs...)\n\t\t} else {\n\t\t\texpandedArgs = append(expandedArgs, arg)\n\t\t}\n\t}\n\n\tflags := flag.NewFlagSet(\"flags\", flag.ExitOnError)\n\n\tflags.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}\n\nCalculates the source-sharing requirements in reverse starting at the\n-rtrace projects or metadata files that inherited source-sharing and\nworking back to the targets where the source-sharing requirmements\noriginate.\n\nOutputs a space-separated pair where the first field is an originating\ntarget with one or more restricted conditions and where the second\nfield is a colon-separated list of the restricted conditions.\n\nOutputs a count of the originating targets, and if the count is zero,\noutputs a warning to check the -rtrace projects and/or filenames.\n\nOptions:\n`, filepath.Base(os.Args[0]))\n\t\tflags.PrintDefaults()\n\t}\n\n\toutputFile := flags.String(\"o\", \"-\", \"Where to write the output. (default stdout)\")\n\tsources := newMultiString(flags, \"rtrace\", \"Projects or metadata files to trace back from. (required; multiple allowed)\")\n\tstripPrefix := newMultiString(flags, \"strip_prefix\", \"Prefix to remove from paths. i.e. path to root (multiple allowed)\")\n\n\tflags.Parse(expandedArgs)\n\n\t// Must specify at least one root target.\n\tif flags.NArg() == 0 {\n\t\tflags.Usage()\n\t\tos.Exit(2)\n\t}\n\n\tif len(*sources) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"\\nMust specify at least 1 --rtrace source.\\n\")\n\t\tos.Exit(2)\n\t}\n\n\tif len(*outputFile) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"must specify file for -o; use - for stdout\\n\")\n\t\tos.Exit(2)\n\t} else {\n\t\tdir, err := filepath.Abs(filepath.Dir(*outputFile))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot determine path to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfi, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot read directory %q of %q: %s\\n\", dir, *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !fi.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"parent %q of %q is not a directory\\n\", dir, *outputFile)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar ofile io.Writer\n\tofile = os.Stdout\n\tvar obuf *bytes.Buffer\n\tif *outputFile != \"-\" {\n\t\tobuf = &bytes.Buffer{}\n\t\tofile = obuf\n\t}\n\n\tctx := &context{\n\t\tsources:     *sources,\n\t\tstripPrefix: *stripPrefix,\n\t}\n\t_, err := traceRestricted(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...)\n\tif err != nil {\n\t\tif err == failNoneRequested {\n\t\t\tflags.Usage()\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"%s\\n\", err.Error())\n\t\tos.Exit(1)\n\t}\n\tif *outputFile != \"-\" {\n\t\terr := os.WriteFile(*outputFile, obuf.Bytes(), 0666)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write output to %q from %q: %s\\n\", *outputFile, os.Getenv(\"PWD\"), err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tos.Exit(0)\n}\n\n// traceRestricted implements the rtrace utility.\nfunc traceRestricted(ctx *context, stdout, stderr io.Writer, rootFS fs.FS, files ...string) (*compliance.LicenseGraph, error) {\n\tif len(files) < 1 {\n\t\treturn nil, failNoneRequested\n\t}\n\n\tif len(ctx.sources) < 1 {\n\t\treturn nil, failNoSources\n\t}\n\n\t// Read the license graph from the license metadata files (*.meta_lic).\n\tlicenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Unable to read license metadata file(s) %q: %v\\n\", files, err)\n\t}\n\tif licenseGraph == nil {\n\t\treturn nil, failNoLicenses\n\t}\n\n\tsourceMap := make(map[string]struct{})\n\tfor _, source := range ctx.sources {\n\t\tsourceMap[source] = struct{}{}\n\t}\n\n\tcompliance.TraceTopDownConditions(licenseGraph, func(tn *compliance.TargetNode) compliance.LicenseConditionSet {\n\t\tif _, isPresent := sourceMap[tn.Name()]; isPresent {\n\t\t\treturn compliance.ImpliesRestricted\n\t\t}\n\t\tfor _, project := range tn.Projects() {\n\t\t\tif _, isPresent := sourceMap[project]; isPresent {\n\t\t\t\treturn compliance.ImpliesRestricted\n\t\t\t}\n\t\t}\n\t\treturn compliance.NewLicenseConditionSet()\n\t})\n\n\t// targetOut calculates the string to output for `target` adding `sep`-separated conditions as needed.\n\ttargetOut := func(target *compliance.TargetNode, sep string) string {\n\t\ttOut := ctx.strip(target.Name())\n\t\treturn tOut\n\t}\n\n\t// outputResolution prints a resolution in the requested format to `stdout`, where one can read\n\t// a resolution as `tname` resolves conditions named in `cnames`.\n\t// `tname` is the name of the target the resolution traces back to.\n\t// `cnames` is the list of conditions to resolve.\n\toutputResolution := func(tname string, cnames []string) {\n\t\t// ... one edge per line with names in a colon-separated tuple.\n\t\tfmt.Fprintf(stdout, \"%s %s\\n\", tname, strings.Join(cnames, \":\"))\n\t}\n\n\t// Sort the resolutions by targetname for repeatability/stability.\n\tactions := compliance.WalkResolutionsForCondition(licenseGraph, compliance.ImpliesShared).AllActions()\n\ttargets := make(compliance.TargetNodeList, 0, len(actions))\n\tfor tn := range actions {\n\t\tif tn.LicenseConditions().MatchesAnySet(compliance.ImpliesRestricted) {\n\t\t\ttargets = append(targets, tn)\n\t\t}\n\t}\n\tsort.Sort(targets)\n\n\t// Output the sorted targets.\n\tfor _, target := range targets {\n\t\tvar tname string\n\t\ttname = targetOut(target, \":\")\n\n\t\t// cnames accumulates the list of condition names originating at a single origin that apply to `target`.\n\t\tcnames := target.LicenseConditions().Names()\n\n\t\t// Output 1 line for each attachesTo+actsOn combination.\n\t\toutputResolution(tname, cnames)\n\t}\n\tfmt.Fprintf(stdout, \"restricted conditions trace to %d targets\\n\", len(targets))\n\tif 0 == len(targets) {\n\t\tfmt.Fprintln(stdout, \"  (check for typos in project names or metadata files)\")\n\t}\n\treturn licenseGraph, nil\n}\n"
  },
  {
    "path": "tools/compliance/cmd/rtrace/rtrace_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the parent directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"..\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc Test_plaintext(t *testing.T) {\n\ttests := []struct {\n\t\tcondition   string\n\t\tname        string\n\t\toutDir      string\n\t\troots       []string\n\t\tctx         context\n\t\texpectedOut []string\n\t}{\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tsources:     []string{\"testdata/firstparty/bin/bin1.meta_lic\"},\n\t\t\t\tstripPrefix: []string{\"testdata/firstparty/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tsources:     []string{\"testdata/notice/bin/bin1.meta_lic\"},\n\t\t\t\tstripPrefix: []string{\"testdata/notice/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex_trimmed\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tsources:     []string{\"testdata/reciprocal/bin/bin1.meta_lic\"},\n\t\t\t\tstripPrefix: []string{\"testdata/reciprocal/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_bin1\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tsources:     []string{\"testdata/restricted/bin/bin1.meta_lic\"},\n\t\t\t\tstripPrefix: []string{\"testdata/restricted/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\"lib/liba.so.meta_lic restricted_if_statically_linked\"},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex_trimmed_bin2\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tsources:     []string{\"testdata/restricted/bin/bin2.meta_lic\"},\n\t\t\t\tstripPrefix: []string{\"testdata/restricted/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\"lib/libb.so.meta_lic restricted\"},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic restricted\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\"testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\"testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\"testdata/proprietary/lib/libb.so.meta_lic restricted\"},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_bin1\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tsources:     []string{\"testdata/proprietary/bin/bin1.meta_lic\"},\n\t\t\t\tstripPrefix: []string{\"testdata/proprietary/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex_trimmed_bin2\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\tctx: context{\n\t\t\t\tsources:     []string{\"testdata/proprietary/bin/bin2.meta_lic\"},\n\t\t\t\tstripPrefix: []string{\"testdata/proprietary/\"},\n\t\t\t},\n\t\t\texpectedOut: []string{\"lib/libb.so.meta_lic restricted\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\"testdata/proprietary/lib/libb.so.meta_lic restricted\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\texpectedOut := &bytes.Buffer{}\n\t\t\tfor _, eo := range tt.expectedOut {\n\t\t\t\texpectedOut.WriteString(eo)\n\t\t\t\texpectedOut.WriteString(\"\\n\")\n\t\t\t}\n\t\t\tfmt.Fprintf(expectedOut, \"restricted conditions trace to %d targets\\n\", len(tt.expectedOut))\n\t\t\tif 0 == len(tt.expectedOut) {\n\t\t\t\tfmt.Fprintln(expectedOut, \"  (check for typos in project names or metadata files)\")\n\t\t\t}\n\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\t\t\tif len(tt.ctx.sources) < 1 {\n\t\t\t\ttt.ctx.sources = rootFiles\n\t\t\t}\n\t\t\t_, err := traceRestricted(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)\n\t\t\tt.Logf(\"rtrace: stderr = %v\", stderr)\n\t\t\tt.Logf(\"rtrace: stdout = %v\", stdout)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"rtrace: error = %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif stderr.Len() > 0 {\n\t\t\t\tt.Errorf(\"rtrace: gotStderr = %v, want none\", stderr)\n\t\t\t}\n\t\t\tout := stdout.String()\n\t\t\texpected := expectedOut.String()\n\t\t\tif out != expected {\n\t\t\t\toutList := strings.Split(out, \"\\n\")\n\t\t\t\texpectedList := strings.Split(expected, \"\\n\")\n\t\t\t\tstartLine := 0\n\t\t\t\tfor startLine < len(outList) && startLine < len(expectedList) && outList[startLine] == expectedList[startLine] {\n\t\t\t\t\tstartLine++\n\t\t\t\t}\n\t\t\t\tt.Errorf(\"rtrace: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v\",\n\t\t\t\t\tout, expected, startLine+1, outList[startLine], expectedList[startLine])\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/cmd/shippedlibs/shippedlibs.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"android/soong/response\"\n\t\"android/soong/tools/compliance\"\n)\n\nvar (\n\tfailNoneRequested = fmt.Errorf(\"\\nNo license metadata files requested\")\n\tfailNoLicenses    = fmt.Errorf(\"No licenses found\")\n)\n\ntype context struct {\n\tstdout io.Writer\n\tstderr io.Writer\n\trootFS fs.FS\n}\n\nfunc main() {\n\tvar expandedArgs []string\n\tfor _, arg := range os.Args[1:] {\n\t\tif strings.HasPrefix(arg, \"@\") {\n\t\t\tf, err := os.Open(strings.TrimPrefix(arg, \"@\"))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\trespArgs, err := response.ReadRspFile(f)\n\t\t\tf.Close()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\texpandedArgs = append(expandedArgs, respArgs...)\n\t\t} else {\n\t\t\texpandedArgs = append(expandedArgs, arg)\n\t\t}\n\t}\n\n\tflags := flag.NewFlagSet(\"flags\", flag.ExitOnError)\n\n\toutputFile := flags.String(\"o\", \"-\", \"Where to write the library list. (default stdout)\")\n\n\tflags.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}\n\nOutputs a list of libraries used in the shipped images.\n\nOptions:\n`, filepath.Base(os.Args[0]))\n\t\tflags.PrintDefaults()\n\t}\n\n\terr := flags.Parse(expandedArgs)\n\tif err != nil {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"%v\\n\", err)\n\t}\n\n\t// Must specify at least one root target.\n\tif flags.NArg() == 0 {\n\t\tflags.Usage()\n\t\tos.Exit(2)\n\t}\n\n\tif len(*outputFile) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"must specify file for -o; use - for stdout\\n\")\n\t\tos.Exit(2)\n\t} else {\n\t\tdir, err := filepath.Abs(filepath.Dir(*outputFile))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot determine path to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfi, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot read directory %q of %q: %s\\n\", dir, *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !fi.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"parent %q of %q is not a directory\\n\", dir, *outputFile)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar ofile io.Writer\n\tofile = os.Stdout\n\tif *outputFile != \"-\" {\n\t\tofile = &bytes.Buffer{}\n\t}\n\n\tctx := &context{ofile, os.Stderr, compliance.FS}\n\n\terr = shippedLibs(ctx, flags.Args()...)\n\tif err != nil {\n\t\tif err == failNoneRequested {\n\t\t\tflags.Usage()\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"%s\\n\", err.Error())\n\t\tos.Exit(1)\n\t}\n\tif *outputFile != \"-\" {\n\t\terr := os.WriteFile(*outputFile, ofile.(*bytes.Buffer).Bytes(), 0666)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write output to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tos.Exit(0)\n}\n\n// shippedLibs implements the shippedlibs utility.\nfunc shippedLibs(ctx *context, files ...string) error {\n\t// Must be at least one root file.\n\tif len(files) < 1 {\n\t\treturn failNoneRequested\n\t}\n\n\t// Read the license graph from the license metadata files (*.meta_lic).\n\tlicenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license metadata file(s) %q: %v\\n\", files, err)\n\t}\n\tif licenseGraph == nil {\n\t\treturn failNoLicenses\n\t}\n\n\t// rs contains all notice resolutions.\n\trs := compliance.ResolveNotices(licenseGraph)\n\n\tni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license text file(s) for %q: %v\\n\", files, err)\n\t}\n\n\tfor lib := range ni.Libraries() {\n\t\tfmt.Fprintln(ctx.stdout, lib)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tools/compliance/cmd/shippedlibs/shippedlibs_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the parent directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"..\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc Test(t *testing.T) {\n\ttests := []struct {\n\t\tcondition   string\n\t\tname        string\n\t\toutDir      string\n\t\troots       []string\n\t\texpectedOut []string\n\t}{\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"firstparty\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"notice\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{\"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"reciprocal\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{\"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"restricted\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{\"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"apex\",\n\t\t\troots:       []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"container\",\n\t\t\troots:       []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"application\",\n\t\t\troots:       []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"binary\",\n\t\t\troots:       []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []string{\"Android\", \"Device\", \"External\"},\n\t\t},\n\t\t{\n\t\t\tcondition:   \"proprietary\",\n\t\t\tname:        \"library\",\n\t\t\troots:       []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []string{\"External\"},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\n\t\t\tctx := context{stdout, stderr, compliance.GetFS(tt.outDir)}\n\n\t\t\terr := shippedLibs(&ctx, rootFiles...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"shippedLibs: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif stderr.Len() > 0 {\n\t\t\t\tt.Errorf(\"shippedLibs: gotStderr = %v, want none\", stderr)\n\t\t\t}\n\n\t\t\tt.Logf(\"got stdout: %s\", stdout.String())\n\n\t\t\tt.Logf(\"want stdout: %s\", strings.Join(tt.expectedOut, \"\\n\"))\n\n\t\t\tout := bufio.NewScanner(stdout)\n\t\t\tlineno := 0\n\t\t\tfor out.Scan() {\n\t\t\t\tline := out.Text()\n\t\t\t\tif strings.TrimLeft(line, \" \") == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(tt.expectedOut) <= lineno {\n\t\t\t\t\tt.Errorf(\"shippedLibs: unexpected output at line %d: got %q, want nothing (wanted %d lines)\", lineno+1, line, len(tt.expectedOut))\n\t\t\t\t} else if tt.expectedOut[lineno] != line {\n\t\t\t\t\tt.Errorf(\"shippedLibs: unexpected output at line %d: got %q, want %q\", lineno+1, line, tt.expectedOut[lineno])\n\t\t\t\t}\n\t\t\t\tlineno++\n\t\t\t}\n\t\t\tfor ; lineno < len(tt.expectedOut); lineno++ {\n\t\t\t\tt.Errorf(\"shippedLibs: missing output line %d: ended early, want %q\", lineno+1, tt.expectedOut[lineno])\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/README.md",
    "content": "## Test data\n\nEach non-regression directory under testdata/ defines a similar build graph.\nAll have the same structure, but different versions of the graph have different\nlicense metadata.\n\nThe regression* directories can have whatever structure is required for the\nspecific test case.\n\n### Testdata build graph structure:\n\nThe structure is meant to simulate some common scenarios:\n\n*   a `lib/` directory with some libraries\n*   a `bin/` directory with some executables\n*   one of the binaries, `bin3`, is a toolchain executable like a compiler\n*   an `application` built with the `bin3` compiler and linking a couple libraries\n*   a pure aggregation `continer.zip` that merely bundles files together, and\n*   an apex file (more like an apk file) with some binaries and libraries.\n\nThe testdata starts with a `firstparty/` version containng only first-party\nlicenses, and each subsequent directory introduces more restrictive conditions:\n\n*   `notice/` starts with `firstparty/` adds third-party notice conditions\n*   `reciprocal/` starts with `notice/` and adds some reciprocal conditions\n*   `restricted/` starts with `reciprocal/` and adds some restricted conditions\n*   `proprietary/` starts with `restricted/` and add some privacy conditions\n\n#### a `lib/` directory with some libraries\n\n```dot\nstrict digraph {\n\tliba [label=\"lib/liba.so.meta_lic\"];\n\tlibb [label=\"lib/libb.so.meta_lic\"];\n\tlibc [label=\"lib/libc.a.meta_lic\"];\n\tlibd [label=\"lib/libd.so.meta_lic\"];\n}\n```\n\n#### a `bin/` directory with some executables\n\nstrict digraph {\n\trankdir=LR;\n\tbin1 [label=\"bin/bin1.meta_lic\"];\n\tbin2 [label=\"bin/bin2.meta_lic\"];\n\tbin3 [label=\"bin/bin3.meta_lic\\ntoolchain\"];\n\tliba [label=\"lib/liba.so.meta_lic\"];\n\tlibb [label=\"lib/libb.so.meta_lic\"];\n\tlibc [label=\"lib/libc.a.meta_lic\"];\n\tlibd [label=\"lib/libd.so.meta_lic\"];\n\tbin1 -> liba [label=\"static\"];\n\tbin1 -> libc [label=\"static\"];\n\tbin2 -> libb [label=\"dynamic\"];\n\tbin2 -> libd [label=\"dynamic\"];\n\t{rank=same; bin1 bin2 bin3}\n}\n\n#### an `application` built with the `bin3` compiler and linking a couple libraries\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tapp [label=\"application.meta_lic\"];\n\tbin3 [label=\"bin/bin3.meta_lic\"];\n\tliba [label=\"lib/liba.so.meta_lic\"];\n\tlibb [label=\"lib/libb.so.meta_lic\"];\n\tapp -> bin3 [label=\"toolchain\"];\n\tapp -> liba [label=\"static\"];\n\tapp -> libb [label=\"dynamic\"];\n\t{rank=same; app}\n}\n```\n\n#### a pure aggregation `container.zip` that merely bundles files together\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tbin1 [label=\"bin/bin1.meta_lic\"];\n\tbin2 [label=\"bin/bin2.meta_lic\"];\n\tcontainer [label=\"container.zip.meta_lic\"];\n\tliba [label=\"lib/liba.so.meta_lic\"];\n\tlibb [label=\"lib/libb.so.meta_lic\"];\n\tlibc [label=\"lib/libc.a.meta_lic\"];\n\tlibd [label=\"lib/libd.so.meta_lic\"];\n\tbin1 -> liba [label=\"static\"];\n\tbin1 -> libc [label=\"static\"];\n\tbin2 -> libb [label=\"dynamic\"];\n\tbin2 -> libd [label=\"dynamic\"];\n\tcontainer -> bin1 [label=\"static\"];\n\tcontainer -> bin2 [label=\"static\"];\n\tcontainer -> liba [label=\"static\"];\n\tcontainer -> libb [label=\"static\"];\n\t{rank=same; container}\n}\n```\n\n#### an apex file (more like an apk file) with some binaries and libraries\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tapex [label=\"highest.apex.meta_lic\"];\n\tbin1 [label=\"bin/bin1.meta_lic\"];\n\tbin2 [label=\"bin/bin2.meta_lic\"];\n\tbin3 [label=\"bin/bin3.meta_lic\"];\n\tliba [label=\"lib/liba.so.meta_lic\"];\n\tlibb [label=\"lib/libb.so.meta_lic\"];\n\tlibc [label=\"lib/libc.a.meta_lic\"];\n\tlibd [label=\"lib/libd.so.meta_lic\"];\n\tbin1 -> liba [label=\"static\"];\n\tbin1 -> libc [label=\"static\"];\n\tbin2 -> libb [label=\"dynamic\"];\n\tbin2 -> libd [label=\"dynamic\"];\n\tapex -> bin1 [label=\"static\"];\n\tapex -> bin2 [label=\"static\"];\n\tapex -> liba [label=\"static\"];\n\tapex -> libb [label=\"static\"];\n\t{rank=same; apex}\n}\n```\n\n#### the whole build graph\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tapex [label=\"highest.apex.meta_lic\"];\n\tapp [label=\"application.meta_lic\"];\n\tbin1 [label=\"bin/bin1.meta_lic\"];\n\tbin2 [label=\"bin/bin2.meta_lic\"];\n\tbin3 [label=\"bin/bin3.meta_lic\"];\n\tcontainer [label=\"container.zip.meta_lic\"];\n\tliba [label=\"lib/liba.so.meta_lic\"];\n\tlibb [label=\"lib/libb.so.meta_lic\"];\n\tlibc [label=\"lib/libc.a.meta_lic\"];\n\tlibd [label=\"lib/libd.so.meta_lic\"];\n\tapp -> bin3 [label=\"toolchain\"];\n\tapp -> liba [label=\"static\"];\n\tapp -> libb [label=\"dynamic\"];\n\tbin1 -> liba [label=\"static\"];\n\tbin1 -> libc [label=\"static\"];\n\tbin2 -> libb [label=\"dynamic\"];\n\tbin2 -> libd [label=\"dynamic\"];\n\tcontainer -> bin1 [label=\"static\"];\n\tcontainer -> bin2 [label=\"static\"];\n\tcontainer -> liba [label=\"static\"];\n\tcontainer -> libb [label=\"static\"];\n\tapex -> bin1 [label=\"static\"];\n\tapex -> bin2 [label=\"static\"];\n\tapex -> liba [label=\"static\"];\n\tapex -> libb [label=\"static\"];\n\t{rank=same; app container apex}\n}\n```\n\n\n### firstparty/ testdata starts with all first-party licensing\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tapp [label=\"firstparty/application.meta_lic\"];\n\tbin1 [label=\"firstparty/bin/bin1.meta_lic\"];\n\tbin2 [label=\"firstparty/bin/bin2.meta_lic\"];\n\tbin3 [label=\"firstparty/bin/bin3.meta_lic\"];\n\tcontainer [label=\"firstparty/container.zip.meta_lic\"];\n\tapex [label=\"firstparty/highest.apex.meta_lic\"];\n\tliba [label=\"firstparty/lib/liba.so.meta_lic\"];\n\tlibb [label=\"firstparty/lib/libb.so.meta_lic\"];\n\tlibc [label=\"firstparty/lib/libc.a.meta_lic\"];\n\tlib [label=\"firstparty/lib/libd.so.meta_lic\"];\n\tapp -> bin3 [label=\"toolchain\"];\n\tapp -> liba [label=\"static\"];\n\tapp -> libb [label=\"dynamic\"];\n\tbin1 -> liba [label=\"static\"];\n\tbin1 -> libc [label=\"static\"];\n\tbin2 -> libb [label=\"dynamic\"];\n\tbin2 -> libd [label=\"dynamic\"];\n\tcontainer -> bin1 [label=\"static\"];\n\tcontainer -> bin2 [label=\"static\"];\n\tcontainer -> liba [label=\"static\"];\n\tcontainer -> libb [label=\"static\"];\n\tapex -> bin1 [label=\"static\"];\n\tapex -> bin2 [label=\"static\"];\n\tapex -> liba [label=\"static\"];\n\tapex -> libb [label=\"static\"];\n\t{rank=same; app container apex}\n}\n```\n\n### notice/ testdata introduces third-party notice conditions\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tapp [label=\"notice/application.meta_lic\"];\n\tbin1 [label=\"notice/bin/bin1.meta_lic\"];\n\tbin2 [label=\"notice/bin/bin2.meta_lic\"];\n\tbin3 [label=\"notice/bin/bin3.meta_lic\\nnotice\"];\n\tcontainer [label=\"notice/container.zip.meta_lic\"];\n\tapex [label=\"notice/highest.apex.meta_lic\"];\n\tliba [label=\"notice/lib/liba.so.meta_lic\\nnotice\"];\n\tlibb [label=\"notice/lib/libb.so.meta_lic\"];\n\tlibc [label=\"notice/lib/libc.a.meta_lic\\nnotice\"];\n\tlibd [label=\"notice/lib/libd.so.meta_lic\\nnotice\"];\n\tapp -> bin3 [label=\"toolchain\"];\n\tapp -> liba [label=\"static\"];\n\tapp -> libb [label=\"dynamic\"];\n\tbin1 -> liba [label=\"static\"];\n\tbin1 -> libc [label=\"static\"];\n\tbin2 -> libb [label=\"dynamic\"];\n\tbin2 -> libd [label=\"dynamic\"];\n\tcontainer -> bin1 [label=\"static\"];\n\tcontainer -> bin2 [label=\"static\"];\n\tcontainer -> liba [label=\"static\"];\n\tcontainer -> libb [label=\"static\"];\n\tapex -> bin1 [label=\"static\"];\n\tapex -> bin2 [label=\"static\"];\n\tapex -> liba [label=\"static\"];\n\tapex -> libb [label=\"static\"];\n\t{rank=same; app container apex}\n}\n```\n\n### reciprocal/ testdata introduces third-party reciprocal sharing conditions\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tapp [label=\"reciprocal/application.meta_lic\"];\n\tbin1 [label=\"reciprocal/bin/bin1.meta_lic\"];\n\tbin2 [label=\"reciprocal/bin/bin2.meta_lic\"];\n\tbin3 [label=\"reciprocal/bin/bin3.meta_lic\\nnotice\"];\n\tcontainer [label=\"reciprocal/container.zip.meta_lic\"];\n\tapex [label=\"reciprocal/highest.apex.meta_lic\"];\n\tliba [label=\"reciprocal/lib/liba.so.meta_lic\\nreciprocal\"];\n\tlibb [label=\"reciprocal/lib/libb.so.meta_lic\"];\n\tlibc [label=\"reciprocal/lib/libc.a.meta_lic\\nreciprocal\"];\n\tlibd [label=\"reciprocal/lib/libd.so.meta_lic\\nnotice\"];\n\tapp -> bin3 [label=\"toolchain\"];\n\tapp -> liba [label=\"static\"];\n\tapp -> libb [label=\"dynamic\"];\n\tbin1 -> liba [label=\"static\"];\n\tbin1 -> libc [label=\"static\"];\n\tbin2 -> libb [label=\"dynamic\"];\n\tbin2 -> libd [label=\"dynamic\"];\n\tcontainer -> bin1 [label=\"static\"];\n\tcontainer -> bin2 [label=\"static\"];\n\tcontainer -> liba [label=\"static\"];\n\tcontainer -> libb [label=\"static\"];\n\tapex -> bin1 [label=\"static\"];\n\tapex -> bin2 [label=\"static\"];\n\tapex -> liba [label=\"static\"];\n\tapex -> libb [label=\"static\"];\n\t{rank=same; app container apex}\n}\n```\n\n### restricted/ testdata introduces restricted source sharing conditions\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tapp [label=\"restricted/application.meta_lic\"];\n\tbin1 [label=\"restricted/bin/bin1.meta_lic\"];\n\tbin2 [label=\"restricted/bin/bin2.meta_lic\"];\n\tbin3 [label=\"restricted/bin/bin3.meta_lic\\nrestricted\"];\n\tcontainer [label=\"restricted/container.zip.meta_lic\"];\n\tapex [label=\"restricted/highest.apex.meta_lic\"];\n\tliba [label=\"restricted/lib/liba.so.meta_lic\\nrestricted\"];\n\tlibb [label=\"restricted/lib/libb.so.meta_lic\\nrestricted\"];\n\tlibc [label=\"restricted/lib/libc.a.meta_lic\\nreciprocal\"];\n\tlibd [label=\"restricted/lib/libd.so.meta_lic\\nnotice\"];\n\tapp -> bin3 [label=\"toolchain\"];\n\tapp -> liba [label=\"static\"];\n\tapp -> libb [label=\"dynamic\"];\n\tbin1 -> liba [label=\"static\"];\n\tbin1 -> libc [label=\"static\"];\n\tbin2 -> libb [label=\"dynamic\"];\n\tbin2 -> libd [label=\"dynamic\"];\n\tcontainer -> bin1 [label=\"static\"];\n\tcontainer -> bin2 [label=\"static\"];\n\tcontainer -> liba [label=\"static\"];\n\tcontainer -> libb [label=\"static\"];\n\tapex -> bin1 [label=\"static\"];\n\tapex -> bin2 [label=\"static\"];\n\tapex -> liba [label=\"static\"];\n\tapex -> libb [label=\"static\"];\n\t{rank=same; app container apex}\n}\n```\n\n### proprietary/ testdata introduces privacy conditions\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tapp [label=\"proprietary/application.meta_lic\"];\n\tbin1 [label=\"proprietary/bin/bin1.meta_lic\"];\n\tbin2 [label=\"proprietary/bin/bin2.meta_lic\\nby_exception_only\\nproprietary\"];\n\tbin3 [label=\"proprietary/bin/bin3.meta_lic\\nrestricted\"];\n\tcontainer [label=\"proprietary/container.zip.meta_lic\"];\n\tapex [label=\"proprietary/highest.apex.meta_lic\"];\n\tliba [label=\"proprietary/lib/liba.so.meta_lic\\nby_exception_only\\nproprietary\"];\n\tlibb [label=\"proprietary/lib/libb.so.meta_lic\\nrestricted\"];\n\tlibc [label=\"proprietary/lib/libc.a.meta_lic\\nby_exception_only\\nproprietary\"];\n\tlibd [label=\"proprietary/lib/libd.so.meta_lic\\nnotice\"];\n\tapp -> bin3 [label=\"toolchain\"];\n\tapp -> liba [label=\"static\"];\n\tapp -> libb [label=\"dynamic\"];\n\tbin1 -> liba [label=\"static\"];\n\tbin1 -> libc [label=\"static\"];\n\tbin2 -> libb [label=\"dynamic\"];\n\tbin2 -> libd [label=\"dynamic\"];\n\tcontainer -> bin1 [label=\"static\"];\n\tcontainer -> bin2 [label=\"static\"];\n\tcontainer -> liba [label=\"static\"];\n\tcontainer -> libb [label=\"static\"];\n\tapex -> bin1 [label=\"static\"];\n\tapex -> bin2 [label=\"static\"];\n\tapex -> liba [label=\"static\"];\n\tapex -> libb [label=\"static\"];\n\t{rank=same; app container apex}\n}\n```\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/FIRST_PARTY_LICENSE",
    "content": "&&&First Party License&&&\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/METADATA",
    "content": "# Comments are allowed\nname: \"1ptd\"\ndescription: \"First Party Test Data\"\nthird_party {\n    version: \"1.0\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/application.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"distributable/application\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application\"\ninstalled:  \"out/target/product/fictional/bin/application\"\nsources:  \"out/target/product/fictional/system/lib/liba.a\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin3\"\ndeps:  {\n  file:  \"testdata/firstparty/bin/bin3.meta_lic\"\n  annotations:  \"toolchain\"\n}\ndeps:  {\n  file:  \"testdata/firstparty/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/firstparty/lib/libb.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/bin/bin1.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"static/binary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1\"\ninstalled:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/lib/liba.a\"\nsources:  \"out/target/product/fictional/system/lib/libc.a\"\ndeps:  {\n  file:  \"testdata/firstparty/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/firstparty/lib/libc.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/bin/bin2.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"dynamic/binary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2\"\ninstalled:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/lib/libd.so\"\ndeps:  {\n  file:  \"testdata/firstparty/lib/libb.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/firstparty/lib/libd.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/bin/bin3.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"standalone/binary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3\"\ninstalled:  \"out/target/product/fictional/system/bin/bin3\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/container.zip.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"container/zip\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/container_intermediates/container.zip\"\ninstalled:  \"out/target/product/fictional/data/container.zip\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/\"\n  container_path:  \"/\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/\"\n  container_path:  \"/\"\n}\nsources:  \"out/target/product/fictional/system/lib/liba.so\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\ndeps:  {\n  file:  \"testdata/firstparty/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/firstparty/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/firstparty/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/firstparty/lib/libb.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/highest.apex.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"highest/apex\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex\"\ninstalled:  \"out/target/product/fictional/system/apex/highest.apex\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/liba.so\"\n  container_path:  \"/lib/liba.so\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/libb.so\"\n  container_path:  \"/lib/libb.so\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/bin1\"\n  container_path:  \"/bin/bin1\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/bin2\"\n  container_path:  \"/bin/bin2\"\n}\nsources:  \"out/target/product/fictional/system/lib/liba.so\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\ndeps:  {\n  file:  \"testdata/firstparty/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/firstparty/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/firstparty/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/firstparty/lib/libb.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/lib/liba.so.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"device/library\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a\"\ninstalled:  \"out/target/product/fictional/system/lib/liba.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/lib/libb.so.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"base/library\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a\"\ninstalled:  \"out/target/product/fictional/system/lib/libb.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/lib/libc.a.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"static/library\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/firstparty/lib/libd.so.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"dynamic/library\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so\"\ninstalled:  \"out/target/product/fictional/system/lib/libd.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/METADATA",
    "content": "# Comments are allowed\nname: \"noticetd\"\ndescription: \"Notice Test Data\"\nthird_party {\n    version: \"1.0\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/NOTICE_LICENSE",
    "content": "%%%Notice License%%%\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/application.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"distributable/application\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application\"\ninstalled:  \"out/target/product/fictional/bin/application\"\nsources:  \"out/target/product/fictional/system/lib/liba.a\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin3\"\ndeps:  {\n  file:  \"testdata/notice/bin/bin3.meta_lic\"\n  annotations:  \"toolchain\"\n}\ndeps:  {\n  file:  \"testdata/notice/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/notice/lib/libb.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/bin/bin1.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"static/binary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1\"\ninstalled:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/lib/liba.a\"\nsources:  \"out/target/product/fictional/system/lib/libc.a\"\ndeps:  {\n  file:  \"testdata/notice/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/notice/lib/libc.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/bin/bin2.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"dynamic/binary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2\"\ninstalled:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/lib/libd.so\"\ndeps:  {\n  file:  \"testdata/notice/lib/libb.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/notice/lib/libd.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/bin/bin3.meta_lic",
    "content": "package_name:  \"Compiler\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"standalone/binary\"\nlicense_kinds:  \"SPDX-license-identifier-NCSA\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/notice/NOTICE_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3\"\ninstalled:  \"out/target/product/fictional/system/bin/bin3\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/container.zip.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"container/zip\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/container_intermediates/container.zip\"\ninstalled:  \"out/target/product/fictional/data/container.zip\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/\"\n  container_path:  \"/\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/\"\n  container_path:  \"/\"\n}\nsources:  \"out/target/product/fictional/system/lib/liba.so\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\ndeps:  {\n  file:  \"testdata/notice/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/notice/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/notice/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/notice/lib/libb.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/highest.apex.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"highest/apex\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex\"\ninstalled:  \"out/target/product/fictional/system/apex/highest.apex\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/liba.so\"\n  container_path:  \"/lib/liba.so\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/libb.so\"\n  container_path:  \"/lib/libb.so\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/bin1\"\n  container_path:  \"/bin/bin1\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/bin2\"\n  container_path:  \"/bin/bin2\"\n}\nsources:  \"out/target/product/fictional/system/lib/liba.so\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\ndeps:  {\n  file:  \"testdata/notice/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/notice/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/notice/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/notice/lib/libb.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/lib/liba.so.meta_lic",
    "content": "package_name:  \"Device\"\nprojects:  \"device/library\"\nlicense_kinds:  \"SPDX-license-identifier-BSD\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/notice/NOTICE_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a\"\ninstalled:  \"out/target/product/fictional/system/lib/liba.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/lib/libb.so.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"base/library\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a\"\ninstalled:  \"out/target/product/fictional/system/lib/libb.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/lib/libc.a.meta_lic",
    "content": "package_name:  \"External\"\nprojects:  \"static/library\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/notice/NOTICE_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/notice/lib/libd.so.meta_lic",
    "content": "package_name:  \"External\"\nprojects:  \"dynamic/library\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/notice/NOTICE_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so\"\ninstalled:  \"out/target/product/fictional/system/lib/libd.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/METADATA",
    "content": "# comments are allowed\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/PROPRIETARY_LICENSE",
    "content": "@@@Proprietary License@@@\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/application.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"distributable/application\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application\"\ninstalled:  \"out/target/product/fictional/bin/application\"\nsources:  \"out/target/product/fictional/system/lib/liba.a\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin3\"\ndeps:  {\n  file:  \"testdata/proprietary/bin/bin3.meta_lic\"\n  annotations:  \"toolchain\"\n}\ndeps:  {\n  file:  \"testdata/proprietary/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/proprietary/lib/libb.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/bin/bin1.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"static/binary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1\"\ninstalled:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/lib/liba.a\"\nsources:  \"out/target/product/fictional/system/lib/libc.a\"\ndeps:  {\n  file:  \"testdata/proprietary/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/proprietary/lib/libc.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/bin/bin2.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"dynamic/binary\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nlicense_conditions:  \"by_exception_only\"\nlicense_texts:  \"testdata/proprietary/PROPRIETARY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2\"\ninstalled:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/lib/libd.so\"\ndeps:  {\n  file:  \"testdata/proprietary/lib/libb.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/proprietary/lib/libd.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic",
    "content": "package_name:  \"Compiler\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"standalone/binary\"\nlicense_kinds:  \"SPDX-license-identifier-LGPL-2.0\"\nlicense_conditions:  \"restricted_if_statically_linked\"\nlicense_texts:  \"testdata/restricted/RESTRICTED_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3\"\ninstalled:  \"out/target/product/fictional/system/bin/bin3\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/container.zip.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"container/zip\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/container_intermediates/container.zip\"\ninstalled:  \"out/target/product/fictional/data/container.zip\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/\"\n  container_path:  \"/\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/\"\n  container_path:  \"/\"\n}\nsources:  \"out/target/product/fictional/system/lib/liba.so\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\ndeps:  {\n  file:  \"testdata/proprietary/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/proprietary/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/proprietary/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/proprietary/lib/libb.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/highest.apex.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"highest/apex\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex\"\ninstalled:  \"out/target/product/fictional/system/apex/highest.apex\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/liba.so\"\n  container_path:  \"/lib/liba.so\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/libb.so\"\n  container_path:  \"/lib/libb.so\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/bin1\"\n  container_path:  \"/bin/bin1\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/bin2\"\n  container_path:  \"/bin/bin2\"\n}\nsources:  \"out/target/product/fictional/system/lib/liba.so\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\ndeps:  {\n  file:  \"testdata/proprietary/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/proprietary/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/proprietary/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/proprietary/lib/libb.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/lib/liba.so.meta_lic",
    "content": "package_name:  \"Device\"\nprojects:  \"device/library\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nlicense_conditions:  \"by_exception_only\"\nlicense_texts:  \"testdata/proprietary/PROPRIETARY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a\"\ninstalled:  \"out/target/product/fictional/system/lib/liba.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/lib/libb.so.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"base/library\"\nlicense_kinds:  \"SPDX-license-identifier-GPL-2.0\"\nlicense_conditions:  \"restricted\"\nlicense_texts:  \"testdata/restricted/RESTRICTED_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a\"\ninstalled:  \"out/target/product/fictional/system/lib/libb.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/lib/libc.a.meta_lic",
    "content": "package_name:  \"External\"\nprojects:  \"static/library\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nlicense_conditions:  \"by_exception_only\"\nlicense_texts:  \"testdata/proprietary/PROPRIETARY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/proprietary/lib/libd.so.meta_lic",
    "content": "package_name:  \"External\"\nprojects:  \"dynamic/library\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/notice/NOTICE_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so\"\ninstalled:  \"out/target/product/fictional/system/lib/libd.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/METADATA",
    "content": "# Comments are allowed\ndescription: \"Reciprocal Test Data\"\nthird_party {\n    version: \"1.0\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/RECIPROCAL_LICENSE",
    "content": "$$$Reciprocal License$$$\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/application.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"distributable/application\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application\"\ninstalled:  \"out/target/product/fictional/bin/application\"\nsources:  \"out/target/product/fictional/system/lib/liba.a\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin3\"\ndeps:  {\n  file:  \"testdata/reciprocal/bin/bin3.meta_lic\"\n  annotations:  \"toolchain\"\n}\ndeps:  {\n  file:  \"testdata/reciprocal/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/reciprocal/lib/libb.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/bin/bin1.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"static/binary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1\"\ninstalled:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/lib/liba.a\"\nsources:  \"out/target/product/fictional/system/lib/libc.a\"\ndeps:  {\n  file:  \"testdata/reciprocal/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/reciprocal/lib/libc.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/bin/bin2.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"dynamic/binary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2\"\ninstalled:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/lib/libd.so\"\ndeps:  {\n  file:  \"testdata/reciprocal/lib/libb.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/reciprocal/lib/libd.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/bin/bin3.meta_lic",
    "content": "package_name:  \"Compiler\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"standalone/binary\"\nlicense_kinds:  \"SPDX-license-identifier-NCSA\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/notice/NOTICE_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3\"\ninstalled:  \"out/target/product/fictional/system/bin/bin3\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/container.zip.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"container/zip\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/container_intermediates/container.zip\"\ninstalled:  \"out/target/product/fictional/data/container.zip\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/\"\n  container_path:  \"/\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/\"\n  container_path:  \"/\"\n}\nsources:  \"out/target/product/fictional/system/lib/liba.so\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\ndeps:  {\n  file:  \"testdata/reciprocal/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/reciprocal/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/reciprocal/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/reciprocal/lib/libb.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/highest.apex.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"highest/apex\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex\"\ninstalled:  \"out/target/product/fictional/system/apex/highest.apex\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/liba.so\"\n  container_path:  \"/lib/liba.so\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/libb.so\"\n  container_path:  \"/lib/libb.so\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/bin1\"\n  container_path:  \"/bin/bin1\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/bin2\"\n  container_path:  \"/bin/bin2\"\n}\nsources:  \"out/target/product/fictional/system/lib/liba.so\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\ndeps:  {\n  file:  \"testdata/reciprocal/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/reciprocal/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/reciprocal/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/reciprocal/lib/libb.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/lib/liba.so.meta_lic",
    "content": "package_name:  \"Device\"\nprojects:  \"device/library\"\nlicense_kinds:  \"SPDX-license-identifier-MPL\"\nlicense_conditions:  \"reciprocal\"\nlicense_texts:  \"testdata/reciprocal/RECIPROCAL_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a\"\ninstalled:  \"out/target/product/fictional/system/lib/liba.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/lib/libb.so.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"base/library\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a\"\ninstalled:  \"out/target/product/fictional/system/lib/libb.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/lib/libc.a.meta_lic",
    "content": "package_name:  \"External\"\nprojects:  \"static/library\"\nlicense_kinds:  \"SPDX-license-identifier-MPL\"\nlicense_conditions:  \"reciprocal\"\nlicense_texts:  \"testdata/reciprocal/RECIPROCAL_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/reciprocal/lib/libd.so.meta_lic",
    "content": "package_name:  \"External\"\nprojects:  \"dynamic/library\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/notice/NOTICE_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so\"\ninstalled:  \"out/target/product/fictional/system/lib/libd.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/README.md",
    "content": "## Start long walks followed by short walks\n\nDetect concurrency error where \"already started\" treated as\n\"already finished\".\n\n### Testdata build graph structure:\n\nA restricted licensed library sandwiched between a notice library and a notice\nbinary. The source-code for the libraries only needs to be shared if shipped\nalongside the container with the binaries.\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tbin1 [label=\"bin/bin1.meta_lic\\nproprietary\"];\n\tbin2 [label=\"bin/bin2.meta_lic\\nproprietary\"];\n\tbin3 [label=\"bin/bin3.meta_lic\\nproprietary\"];\n\tbin4 [label=\"bin/bin4.meta_lic\\nproprietary\"];\n\tbin5 [label=\"bin/bin5.meta_lic\\nproprietary\"];\n\tbin6 [label=\"bin/bin6.meta_lic\\nproprietary\"];\n\tbin7 [label=\"bin/bin7.meta_lic\\nproprietary\"];\n\tbin8 [label=\"bin/bin8.meta_lic\\nproprietary\"];\n\tbin9 [label=\"bin/bin9.meta_lic\\nproprietary\"];\n\tcontainer [label=\"container.zip.meta_lic\\nnotice\"];\n\tlib1 [label=\"lib/lib1.so.meta_lic\\nnotice\"];\n\tlib2 [label=\"lib/lib2.so.meta_lic\\nnotice\"];\n\tlib3 [label=\"lib/lib3.so.meta_lic\\nnotice\"];\n\tlib4 [label=\"lib/lib4.so.meta_lic\\nnotice\"];\n\tlib5 [label=\"lib/lib5.so.meta_lic\\nnotice\"];\n\tlib6 [label=\"lib/lib6.so.meta_lic\\nnotice\"];\n\tlib7 [label=\"lib/lib7.so.meta_lic\\nnotice\"];\n\tlib8 [label=\"lib/lib8.so.meta_lic\\nnotice\"];\n\tlib9 [label=\"lib/lib9.so.meta_lic\\nrestricted\"];\n\tcontainer -> bin1 [label=\"static\"];\n\tcontainer -> bin2 [label=\"static\"];\n\tcontainer -> bin3 [label=\"static\"];\n\tcontainer -> bin4 [label=\"static\"];\n\tcontainer -> bin5 [label=\"static\"];\n\tcontainer -> bin6 [label=\"static\"];\n\tcontainer -> bin7 [label=\"static\"];\n\tcontainer -> bin8 [label=\"static\"];\n\tcontainer -> bin9 [label=\"static\"];\n\tbin1 -> lib1 [label=\"static\"];\n\tbin2 -> lib2 [label=\"static\"];\n\tbin3 -> lib3 [label=\"static\"];\n\tbin4 -> lib4 [label=\"static\"];\n\tbin5 -> lib5 [label=\"static\"];\n\tbin6 -> lib6 [label=\"static\"];\n\tbin7 -> lib7 [label=\"static\"];\n\tbin8 -> lib8 [label=\"static\"];\n\tbin9 -> lib9 [label=\"static\"];\n\tlib1 -> lib2 [label=\"static\"];\n\tlib2 -> lib3 [label=\"static\"];\n\tlib3 -> lib4 [label=\"static\"];\n\tlib4 -> lib5 [label=\"static\"];\n\tlib5 -> lib6 [label=\"static\"];\n\tlib6 -> lib7 [label=\"static\"];\n\tlib7 -> lib8 [label=\"static\"];\n\tlib8 -> lib9 [label=\"static\"];\n\t{rank=same; container}\n}\n```\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/bin/bin1.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1\"\ninstalled:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/lib/lib1.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib1.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/bin/bin2.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2\"\ninstalled:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/lib/lib2.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib2.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/bin/bin3.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3\"\ninstalled:  \"out/target/product/fictional/system/bin/bin3\"\nsources:  \"out/target/product/fictional/system/lib/lib3.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib3.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/bin/bin4.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin4\"\ninstalled:  \"out/target/product/fictional/system/bin/bin4\"\nsources:  \"out/target/product/fictional/system/lib/lib4.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib4.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/bin/bin5.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin5\"\ninstalled:  \"out/target/product/fictional/system/bin/bin5\"\nsources:  \"out/target/product/fictional/system/lib/lib5.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib5.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/bin/bin6.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin6\"\ninstalled:  \"out/target/product/fictional/system/bin/bin6\"\nsources:  \"out/target/product/fictional/system/lib/lib6.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib6.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/bin/bin7.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin7\"\ninstalled:  \"out/target/product/fictional/system/bin/bin7\"\nsources:  \"out/target/product/fictional/system/lib/lib7.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib7.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/bin/bin8.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin8\"\ninstalled:  \"out/target/product/fictional/system/bin/bin8\"\nsources:  \"out/target/product/fictional/system/lib/lib8.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib8.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/bin/bin9.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"legacy_proprietary\"\nlicense_conditions:  \"proprietary\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin9\"\ninstalled:  \"out/target/product/fictional/system/bin/bin9\"\nsources:  \"out/target/product/fictional/system/lib/lib9.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib9.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/container.zip.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"container/zip\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/container_intermediates/container.zip\"\ninstalled:  \"out/target/product/fictional/data/container.zip\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/\"\n  container_path:  \"/\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/\"\n  container_path:  \"/\"\n}\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/bin/bin3\"\nsources:  \"out/target/product/fictional/system/bin/bin4\"\nsources:  \"out/target/product/fictional/system/bin/bin5\"\nsources:  \"out/target/product/fictional/system/bin/bin6\"\nsources:  \"out/target/product/fictional/system/bin/bin7\"\nsources:  \"out/target/product/fictional/system/bin/bin8\"\nsources:  \"out/target/product/fictional/system/bin/bin9\"\ndeps:  {\n  file:  \"testdata/regressconcur/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressconcur/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressconcur/bin/bin3.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressconcur/bin/bin4.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressconcur/bin/bin5.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressconcur/bin/bin6.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressconcur/bin/bin7.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressconcur/bin/bin8.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressconcur/bin/bin9.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/lib/lib1.a.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/restricted\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib1.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib1.a\"\ninstalled:  \"out/target/product/fictional/system/lib/lib1.so\"\nsources:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib2.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib2.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/lib/lib2.a.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/restricted\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib2.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib2.a\"\ninstalled:  \"out/target/product/fictional/system/lib/lib2.so\"\nsources:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib3.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib3.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/lib/lib3.a.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/restricted\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib3.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib3.a\"\ninstalled:  \"out/target/product/fictional/system/lib/lib3.so\"\nsources:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib4.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib4.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/lib/lib4.a.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/restricted\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib4.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib4.a\"\ninstalled:  \"out/target/product/fictional/system/lib/lib4.so\"\nsources:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib5.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib5.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/lib/lib5.a.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/restricted\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib5.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib5.a\"\ninstalled:  \"out/target/product/fictional/system/lib/lib5.so\"\nsources:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib6.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib6.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/lib/lib6.a.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/restricted\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib6.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib6.a\"\ninstalled:  \"out/target/product/fictional/system/lib/lib6.so\"\nsources:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib7.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib7.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/lib/lib7.a.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/restricted\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib7.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib7.a\"\ninstalled:  \"out/target/product/fictional/system/lib/lib7.so\"\nsources:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib8.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib8.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/lib/lib8.a.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/restricted\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib8.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib8.a\"\ninstalled:  \"out/target/product/fictional/system/lib/lib8.so\"\nsources:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib9.a\"\ndeps:  {\n  file:  \"testdata/regressconcur/lib/lib9.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressconcur/lib/lib9.a.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/restricted\"\nlicense_kinds:  \"SPDX-license-identifier-GPL-2.0\"\nlicense_conditions:  \"restricted\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib9.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib9.a\"\ninstalled:  \"out/target/product/fictional/system/lib/lib9.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl1/README.md",
    "content": "## Shipped versus non-shipped libraries with restricted license\n\n### Testdata build graph structure:\n\nA restricted licensed library sandwiched between a notice library and a notice\nbinary. The source-code for the libraries only needs to be shared if shipped\nalongside the container with the binaries.\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tbin1 [label=\"bin/bin1.meta_lic\\nnotice\"];\n\tbin2 [label=\"bin/bin2.meta_lic\\nnotice\"];\n\tbin3 [label=\"bin/bin3.meta_lic\\nnotice\"];\n\tcontainer [label=\"container.zip.meta_lic\\nnotice\"];\n\tlibapache [label=\"lib/libapache.so.meta_lic\\nnotice\"];\n\tlibcxx [label=\"lib/libc++.so.meta_lic\\nnotice\"];\n\tlibgpl [label=\"lib/libgpl.so.meta_lic\\nrestricted\"];\n\tcontainer -> bin1[label=\"static\"];\n\tcontainer -> bin2 [label=\"static\"];\n\tcontainer -> bin3 [label=\"static\"];\n\tbin1 -> libcxx [label=\"dynamic\"];\n\tbin2 -> libapache [label=\"dynamic\"];\n\tbin2 -> libcxx [label=\"dynamic\"];\n\tbin3 -> libapache [label=\"dynamic\"];\n\tbin3 -> libcxx [label=\"dynamic\"];\n\tbin3 -> libgpl [label=\"dynamic\"];\n\tlibapache -> libcxx [label=\"dynamic\"];\n\tlibgpl -> libcxx [label=\"dynamic\"];\n\t{rank=same; container}\n}\n```\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl1/bin/bin1.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"build/soong/licenses/LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1\"\ninstalled:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/lib/libc++.so\"\ndeps:  {\n  file:  \"testdata/regressgpl1/lib/libc++.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl1/bin/bin2.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/twolibraries\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"build/soong/licenses/LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2\"\ninstalled:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/lib/libc++.so\"\nsources:  \"out/target/product/fictional/system/lib/libapache.so\"\ndeps:  {\n  file:  \"testdata/regressgpl1/lib/libc++.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl1/lib/libapache.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl1/bin/bin3.meta_lic",
    "content": "package_name:  \"Compiler\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/threelibraries\"\nlicense_kinds:  \"SPDX-license-identifier-NCSA\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3\"\ninstalled:  \"out/target/product/fictional/system/bin/bin3\"\nsources:  \"out/target/product/fictional/system/lib/libc++.so\"\nsources:  \"out/target/product/fictional/system/lib/libapache.so\"\nsources:  \"out/target/product/fictional/system/lib/libgpl.so\"\ndeps:  {\n  file:  \"testdata/regressgpl1/lib/libc++.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl1/lib/libapache.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl1/lib/libgpl.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl1/container.zip.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"container/zip\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"build/soong/licenses/LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/container_intermediates/container.zip\"\ninstalled:  \"out/target/product/fictional/data/container.zip\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/\"\n  container_path:  \"/\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/\"\n  container_path:  \"/\"\n}\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/bin/bin3\"\nsources:  \"out/target/product/fictional/system/lib/libapache.so\"\ndeps:  {\n  file:  \"testdata/regressgpl1/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl1/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl1/bin/bin3.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl1/lib/libapache.so.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/apache\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"build/soong/licenses/LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.a\"\ninstalled:  \"out/target/product/fictional/system/lib/libapache.so\"\nsources:  \"out/target/product/fictional/system/lib/libc++.so\"\ndeps:  {\n  file:  \"testdata/regressgpl1/lib/libc++.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl1/lib/libc++.so.meta_lic",
    "content": "package_name:  \"Device\"\nprojects:  \"lib/c++\"\nlicense_kinds:  \"SPDX-license-identifier-BSD\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.a\"\ninstalled:  \"out/target/product/fictional/system/lib/libc++.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl1/lib/libgpl.so.meta_lic",
    "content": "package_name:  \"External\"\nprojects:  \"lib/gpl\"\nlicense_kinds:  \"SPDX-license-identifier-GPL-2.0\"\nlicense_conditions:  \"restricted\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libgpl.so\"\ninstalled:  \"out/target/product/fictional/system/lib/libgpl.so\"\nsources:  \"out/target/product/fictional/system/lib/libc++.so\"\ndeps:  {\n  file:  \"testdata/regressgpl1/lib/libc++.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl2/README.md",
    "content": "## Libraries shipped inside container with restricted license\n\n### Testdata build graph structure:\n\nA restricted licensed library sandwiched between a notice library and a notice\nbinary. The source-code for the libraries needs to be shared when shipped as\npart of the container with the binaries.\n\n```dot\nstrict digraph {\n\trankdir=LR;\n\tbin1 [label=\"bin/bin1.meta_lic\\nnotice\"];\n\tbin2 [label=\"bin/bin2.meta_lic\\nnotice\"];\n\tbin3 [label=\"bin/bin3.meta_lic\\nnotice\"];\n\tcontainer [label=\"container.zip.meta_lic\\nnotice\"];\n\tlibapache [label=\"lib/libapache.so.meta_lic\\nnotice\"];\n\tlibcxx [label=\"lib/libc++.so.meta_lic\\nnotice\"];\n\tlibgpl [label=\"lib/libgpl.so.meta_lic\\nrestricted\"];\n\tcontainer -> bin1[label=\"static\"];\n\tcontainer -> bin2 [label=\"static\"];\n\tcontainer -> bin3 [label=\"static\"];\n\tcontainer -> libapache [label=\"static\"];\n\tcontainer -> libcxx [label=\"static\"];\n\tcontainer -> libgpl [label=\"static\"];\n\tbin1 -> libcxx [label=\"dynamic\"];\n\tbin2 -> libapache [label=\"dynamic\"];\n\tbin2 -> libcxx [label=\"dynamic\"];\n\tbin3 -> libapache [label=\"dynamic\"];\n\tbin3 -> libcxx [label=\"dynamic\"];\n\tbin3 -> libgpl [label=\"dynamic\"];\n\tlibapache -> libcxx [label=\"dynamic\"];\n\tlibgpl -> libcxx [label=\"dynamic\"];\n\t{rank=same; container}\n}\n```\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl2/bin/bin1.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/onelibrary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"build/soong/licenses/LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1\"\ninstalled:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/lib/libc++.so\"\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libc++.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl2/bin/bin2.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/twolibraries\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"build/soong/licenses/LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2\"\ninstalled:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/lib/libc++.so\"\nsources:  \"out/target/product/fictional/system/lib/libapache.so\"\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libc++.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libapache.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl2/bin/bin3.meta_lic",
    "content": "package_name:  \"Compiler\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"bin/threelibraries\"\nlicense_kinds:  \"SPDX-license-identifier-NCSA\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3\"\ninstalled:  \"out/target/product/fictional/system/bin/bin3\"\nsources:  \"out/target/product/fictional/system/lib/libc++.so\"\nsources:  \"out/target/product/fictional/system/lib/libapache.so\"\nsources:  \"out/target/product/fictional/system/lib/libgpl.so\"\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libc++.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libapache.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libgpl.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl2/container.zip.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"container/zip\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"build/soong/licenses/LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/container_intermediates/container.zip\"\ninstalled:  \"out/target/product/fictional/data/container.zip\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/\"\n  container_path:  \"/\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/\"\n  container_path:  \"/\"\n}\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/bin/bin3\"\nsources:  \"out/target/product/fictional/system/lib/libapache.so\"\ndeps:  {\n  file:  \"testdata/regressgpl2/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl2/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl2/bin/bin3.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libapache.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libc++.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libgpl.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl2/lib/libapache.so.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"lib/apache\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"build/soong/licenses/LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.a\"\ninstalled:  \"out/target/product/fictional/system/lib/libapache.so\"\nsources:  \"out/target/product/fictional/system/lib/libc++.so\"\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libc++.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl2/lib/libc++.so.meta_lic",
    "content": "package_name:  \"Device\"\nprojects:  \"lib/c++\"\nlicense_kinds:  \"SPDX-license-identifier-BSD\"\nlicense_conditions:  \"notice\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.a\"\ninstalled:  \"out/target/product/fictional/system/lib/libc++.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/regressgpl2/lib/libgpl.so.meta_lic",
    "content": "package_name:  \"External\"\nprojects:  \"lib/gpl\"\nlicense_kinds:  \"SPDX-license-identifier-GPL-2.0\"\nlicense_conditions:  \"restricted\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libgpl.so\"\ninstalled:  \"out/target/product/fictional/system/lib/libgpl.so\"\nsources:  \"out/target/product/fictional/system/lib/libc++.so\"\ndeps:  {\n  file:  \"testdata/regressgpl2/lib/libc++.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/METADATA",
    "content": "name {\n    id: 1\n}\nthird_party {\n    version: 2\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/METADATA.android",
    "content": "# Comments are allowed\nname: \"testdata\"\ndescription: \"Restricted Test Data\"\nthird_party {\n    version: \"1.0\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/RESTRICTED_LICENSE",
    "content": "###Restricted License###\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/application.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"distributable/application\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application\"\ninstalled:  \"out/target/product/fictional/bin/application\"\nsources:  \"out/target/product/fictional/system/lib/liba.a\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin3\"\ndeps:  {\n  file:  \"testdata/restricted/bin/bin3.meta_lic\"\n  annotations:  \"toolchain\"\n}\ndeps:  {\n  file:  \"testdata/restricted/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/restricted/lib/libb.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/bin/bin1.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"static/binary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1\"\ninstalled:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/lib/liba.a\"\nsources:  \"out/target/product/fictional/system/lib/libc.a\"\ndeps:  {\n  file:  \"testdata/restricted/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/restricted/lib/libc.a.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/bin/bin2.meta_lic",
    "content": "package_name:  \"Android\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"dynamic/binary\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2\"\ninstalled:  \"out/target/product/fictional/system/bin/bin2\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/lib/libd.so\"\ndeps:  {\n  file:  \"testdata/restricted/lib/libb.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\ndeps:  {\n  file:  \"testdata/restricted/lib/libd.so.meta_lic\"\n  annotations:  \"dynamic\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic",
    "content": "package_name:  \"Compiler\"\nmodule_classes: \"EXECUTABLES\"\nprojects:  \"standalone/binary\"\nlicense_kinds:  \"SPDX-license-identifier-LGPL-2.0\"\nlicense_conditions:  \"restricted_if_statically_linked\"\nlicense_texts:  \"testdata/restricted/RESTRICTED_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3\"\ninstalled:  \"out/target/product/fictional/system/bin/bin3\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/container.zip.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"container/zip\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/container_intermediates/container.zip\"\ninstalled:  \"out/target/product/fictional/data/container.zip\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/\"\n  container_path:  \"/\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/\"\n  container_path:  \"/\"\n}\nsources:  \"out/target/product/fictional/system/lib/liba.so\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\ndeps:  {\n  file:  \"testdata/restricted/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/restricted/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/restricted/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/restricted/lib/libb.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/highest.apex.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"highest/apex\"\nlicense_kinds:  \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/firstparty/FIRST_PARTY_LICENSE\"\nis_container:  true\nbuilt:  \"out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex\"\ninstalled:  \"out/target/product/fictional/system/apex/highest.apex\"\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/liba.so\"\n  container_path:  \"/lib/liba.so\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/lib/libb.so\"\n  container_path:  \"/lib/libb.so\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/bin1\"\n  container_path:  \"/bin/bin1\"\n}\ninstall_map {\n  from_path:  \"out/target/product/fictional/system/bin/bin2\"\n  container_path:  \"/bin/bin2\"\n}\nsources:  \"out/target/product/fictional/system/lib/liba.so\"\nsources:  \"out/target/product/fictional/system/lib/libb.so\"\nsources:  \"out/target/product/fictional/system/bin/bin1\"\nsources:  \"out/target/product/fictional/system/bin/bin2\"\ndeps:  {\n  file:  \"testdata/restricted/bin/bin1.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/restricted/bin/bin2.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/restricted/lib/liba.so.meta_lic\"\n  annotations:  \"static\"\n}\ndeps:  {\n  file:  \"testdata/restricted/lib/libb.so.meta_lic\"\n  annotations:  \"static\"\n}\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic",
    "content": "package_name:  \"Device\"\nprojects:  \"device/library\"\nlicense_kinds:  \"SPDX-license-identifier-LGPL-2.0\"\nlicense_conditions:  \"restricted_if_statically_linked\"\nlicense_texts:  \"testdata/restricted/RESTRICTED_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a\"\ninstalled:  \"out/target/product/fictional/system/lib/liba.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/lib/libb.so.meta_lic",
    "content": "package_name:  \"Android\"\nprojects:  \"base/library\"\nlicense_kinds:  \"SPDX-license-identifier-GPL-2.0\"\nlicense_conditions:  \"restricted\"\nlicense_texts:  \"testdata/restricted/RESTRICTED_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so\"\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a\"\ninstalled:  \"out/target/product/fictional/system/lib/libb.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/lib/libc.a.meta_lic",
    "content": "package_name:  \"External\"\nprojects:  \"static/library\"\nlicense_kinds:  \"SPDX-license-identifier-MPL\"\nlicense_conditions:  \"reciprocal\"\nlicense_texts:  \"testdata/reciprocal/RECIPROCAL_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a\"\n"
  },
  {
    "path": "tools/compliance/cmd/testdata/restricted/lib/libd.so.meta_lic",
    "content": "package_name:  \"External\"\nprojects:  \"dynamic/library\"\nlicense_kinds:  \"SPDX-license-identifier-MIT\"\nlicense_conditions:  \"notice\"\nlicense_texts:  \"testdata/notice/NOTICE_LICENSE\"\nis_container:  false\nbuilt:  \"out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so\"\ninstalled:  \"out/target/product/fictional/system/lib/libd.so\"\n"
  },
  {
    "path": "tools/compliance/cmd/textnotice/textnotice.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"android/soong/response\"\n\t\"android/soong/tools/compliance\"\n\n\t\"github.com/google/blueprint/deptools\"\n)\n\nvar (\n\tfailNoneRequested = fmt.Errorf(\"\\nNo license metadata files requested\")\n\tfailNoLicenses    = fmt.Errorf(\"No licenses found\")\n)\n\ntype context struct {\n\tstdout      io.Writer\n\tstderr      io.Writer\n\trootFS      fs.FS\n\tproduct     string\n\tstripPrefix []string\n\ttitle       string\n\tdeps        *[]string\n}\n\nfunc (ctx context) strip(installPath string) string {\n\tfor _, prefix := range ctx.stripPrefix {\n\t\tif strings.HasPrefix(installPath, prefix) {\n\t\t\tp := strings.TrimPrefix(installPath, prefix)\n\t\t\tif 0 == len(p) {\n\t\t\t\tp = ctx.product\n\t\t\t}\n\t\t\tif 0 == len(p) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn p\n\t\t}\n\t}\n\treturn installPath\n}\n\n// newMultiString creates a flag that allows multiple values in an array.\nfunc newMultiString(flags *flag.FlagSet, name, usage string) *multiString {\n\tvar f multiString\n\tflags.Var(&f, name, usage)\n\treturn &f\n}\n\n// multiString implements the flag `Value` interface for multiple strings.\ntype multiString []string\n\nfunc (ms *multiString) String() string     { return strings.Join(*ms, \", \") }\nfunc (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }\n\nfunc main() {\n\tvar expandedArgs []string\n\tfor _, arg := range os.Args[1:] {\n\t\tif strings.HasPrefix(arg, \"@\") {\n\t\t\tf, err := os.Open(strings.TrimPrefix(arg, \"@\"))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\trespArgs, err := response.ReadRspFile(f)\n\t\t\tf.Close()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\texpandedArgs = append(expandedArgs, respArgs...)\n\t\t} else {\n\t\t\texpandedArgs = append(expandedArgs, arg)\n\t\t}\n\t}\n\n\tflags := flag.NewFlagSet(\"flags\", flag.ExitOnError)\n\n\tflags.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}\n\nOutputs a text NOTICE file.\n\nOptions:\n`, filepath.Base(os.Args[0]))\n\t\tflags.PrintDefaults()\n\t}\n\n\toutputFile := flags.String(\"o\", \"-\", \"Where to write the NOTICE text file. (default stdout)\")\n\tdepsFile := flags.String(\"d\", \"\", \"Where to write the deps file\")\n\tproduct := flags.String(\"product\", \"\", \"The name of the product for which the notice is generated.\")\n\tstripPrefix := newMultiString(flags, \"strip_prefix\", \"Prefix to remove from paths. i.e. path to root (multiple allowed)\")\n\ttitle := flags.String(\"title\", \"\", \"The title of the notice file.\")\n\n\tflags.Parse(expandedArgs)\n\n\t// Must specify at least one root target.\n\tif flags.NArg() == 0 {\n\t\tflags.Usage()\n\t\tos.Exit(2)\n\t}\n\n\tif len(*outputFile) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"must specify file for -o; use - for stdout\\n\")\n\t\tos.Exit(2)\n\t} else {\n\t\tdir, err := filepath.Abs(filepath.Dir(*outputFile))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot determine path to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfi, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot read directory %q of %q: %s\\n\", dir, *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !fi.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"parent %q of %q is not a directory\\n\", dir, *outputFile)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar ofile io.Writer\n\tvar closer io.Closer\n\tofile = os.Stdout\n\tvar obuf *bytes.Buffer\n\tif *outputFile != \"-\" {\n\t\tobuf = &bytes.Buffer{}\n\t\tofile = obuf\n\t}\n\tif strings.HasSuffix(*outputFile, \".gz\") {\n\t\tofile, _ = gzip.NewWriterLevel(obuf, gzip.BestCompression)\n\t\tcloser = ofile.(io.Closer)\n\t}\n\n\tvar deps []string\n\n\tctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, *title, &deps}\n\n\terr := textNotice(ctx, flags.Args()...)\n\tif err != nil {\n\t\tif err == failNoneRequested {\n\t\t\tflags.Usage()\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"%s\\n\", err.Error())\n\t\tos.Exit(1)\n\t}\n\tif closer != nil {\n\t\tcloser.Close()\n\t}\n\n\tif *outputFile != \"-\" {\n\t\terr := os.WriteFile(*outputFile, obuf.Bytes(), 0666)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write output to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tif *depsFile != \"\" {\n\t\terr := deptools.WriteDepFile(*depsFile, *outputFile, deps)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write deps to %q: %s\\n\", *depsFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tos.Exit(0)\n}\n\n// textNotice implements the textNotice utility.\nfunc textNotice(ctx *context, files ...string) error {\n\t// Must be at least one root file.\n\tif len(files) < 1 {\n\t\treturn failNoneRequested\n\t}\n\n\t// Read the license graph from the license metadata files (*.meta_lic).\n\tlicenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license metadata file(s) %q: %v\\n\", files, err)\n\t}\n\tif licenseGraph == nil {\n\t\treturn failNoLicenses\n\t}\n\n\t// rs contains all notice resolutions.\n\trs := compliance.ResolveNotices(licenseGraph)\n\n\tni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license text file(s) for %q: %v\\n\", files, err)\n\t}\n\n\tif len(ctx.title) > 0 {\n\t\tfmt.Fprintf(ctx.stdout, \"%s\\n\\n\", ctx.title)\n\t}\n\tfor h := range ni.Hashes() {\n\t\tfmt.Fprintln(ctx.stdout, \"==============================================================================\")\n\t\tfor _, libName := range ni.HashLibs(h) {\n\t\t\tfmt.Fprintf(ctx.stdout, \"%s used by:\\n\", libName)\n\t\t\tfor _, installPath := range ni.HashLibInstalls(h, libName) {\n\t\t\t\tfmt.Fprintf(ctx.stdout, \"  %s\\n\", ctx.strip(installPath))\n\t\t\t}\n\t\t\tfmt.Fprintln(ctx.stdout)\n\t\t}\n\t\tctx.stdout.Write(ni.HashText(h))\n\t\tfmt.Fprintln(ctx.stdout)\n\t}\n\n\t*ctx.deps = ni.InputFiles()\n\tsort.Strings(*ctx.deps)\n\n\treturn nil\n}\n"
  },
  {
    "path": "tools/compliance/cmd/textnotice/textnotice_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance\"\n)\n\nvar (\n\thorizontalRule = regexp.MustCompile(\"^===[=]*===$\")\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the parent directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"..\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc Test(t *testing.T) {\n\ttests := []struct {\n\t\tcondition    string\n\t\tname         string\n\t\toutDir       string\n\t\troots        []string\n\t\tstripPrefix  string\n\t\texpectedOut  []matcher\n\t\texpectedDeps []string\n\t}{\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tusedBy{\"container.zip/liba.so\"},\n\t\t\t\tusedBy{\"container.zip/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/application.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"lib/libd.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tusedBy{\"container.zip/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/application.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"lib/libd.so\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tusedBy{\"container.zip/libb.so\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/application.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"lib/libd.so\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\trestricted{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tusedBy{\"container.zip/libb.so\"},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/liba.so\"},\n\t\t\t\trestricted{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\trestricted{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/application.meta_lic\",\n\t\t\t\t\"testdata/restricted/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\trestricted{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"lib/libd.so\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tusedBy{\"highest.apex/lib/libb.so\"},\n\t\t\t\trestricted{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin2\"},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tusedBy{\"highest.apex/lib/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"highest.apex/bin/bin1\"},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tusedBy{\"container.zip/libb.so\"},\n\t\t\t\trestricted{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"container.zip/bin2\"},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tusedBy{\"container.zip/liba.so\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"container.zip/bin1\"},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"application\"},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/application.meta_lic\",\n\t\t\t\t\"testdata/proprietary/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Android\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tfirstParty{},\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"Device\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"bin/bin1\"},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\thr{},\n\t\t\t\tlibrary{\"External\"},\n\t\t\t\tusedBy{\"lib/libd.so\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\n\t\t\tvar deps []string\n\n\t\t\tctx := context{stdout, stderr, compliance.GetFS(tt.outDir), \"\", []string{tt.stripPrefix}, \"\", &deps}\n\n\t\t\terr := textNotice(&ctx, rootFiles...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"textnotice: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif stderr.Len() > 0 {\n\t\t\t\tt.Errorf(\"textnotice: gotStderr = %v, want none\", stderr)\n\t\t\t}\n\n\t\t\tt.Logf(\"got stdout: %s\", stdout.String())\n\n\t\t\tt.Logf(\"want stdout: %s\", matcherList(tt.expectedOut).String())\n\n\t\t\tout := bufio.NewScanner(stdout)\n\t\t\tlineno := 0\n\t\t\tfor out.Scan() {\n\t\t\t\tline := out.Text()\n\t\t\t\tif strings.TrimLeft(line, \" \") == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(tt.expectedOut) <= lineno {\n\t\t\t\t\tt.Errorf(\"unexpected output at line %d: got %q, want nothing (wanted %d lines)\", lineno+1, line, len(tt.expectedOut))\n\t\t\t\t} else if !tt.expectedOut[lineno].isMatch(line) {\n\t\t\t\t\tt.Errorf(\"unexpected output at line %d: got %q, want %q\", lineno+1, line, tt.expectedOut[lineno].String())\n\t\t\t\t}\n\t\t\t\tlineno++\n\t\t\t}\n\t\t\tfor ; lineno < len(tt.expectedOut); lineno++ {\n\t\t\t\tt.Errorf(\"textnotice: missing output line %d: ended early, want %q\", lineno+1, tt.expectedOut[lineno].String())\n\t\t\t}\n\n\t\t\tt.Logf(\"got deps: %q\", deps)\n\n\t\t\tt.Logf(\"want deps: %q\", tt.expectedDeps)\n\n\t\t\tif g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {\n\t\t\t\tt.Errorf(\"unexpected deps, wanted:\\n%s\\ngot:\\n%s\\n\",\n\t\t\t\t\tstrings.Join(w, \"\\n\"), strings.Join(g, \"\\n\"))\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype matcher interface {\n\tisMatch(line string) bool\n\tString() string\n}\n\ntype hr struct{}\n\nfunc (m hr) isMatch(line string) bool {\n\treturn horizontalRule.MatchString(line)\n}\n\nfunc (m hr) String() string {\n\treturn \" ================================================== \"\n}\n\ntype library struct {\n\tname string\n}\n\nfunc (m library) isMatch(line string) bool {\n\treturn strings.HasPrefix(line, m.name+\" \")\n}\n\nfunc (m library) String() string {\n\treturn m.name + \" used by:\"\n}\n\ntype usedBy struct {\n\tname string\n}\n\nfunc (m usedBy) isMatch(line string) bool {\n\treturn len(line) > 0 && line[0] == ' ' && strings.HasPrefix(strings.TrimLeft(line, \" \"), \"out/\") && strings.HasSuffix(line, \"/\"+m.name)\n}\n\nfunc (m usedBy) String() string {\n\treturn \"  out/.../\" + m.name\n}\n\ntype firstParty struct{}\n\nfunc (m firstParty) isMatch(line string) bool {\n\treturn strings.HasPrefix(strings.TrimLeft(line, \" \"), \"&&&First Party License&&&\")\n}\n\nfunc (m firstParty) String() string {\n\treturn \"&&&First Party License&&&\"\n}\n\ntype notice struct{}\n\nfunc (m notice) isMatch(line string) bool {\n\treturn strings.HasPrefix(strings.TrimLeft(line, \" \"), \"%%%Notice License%%%\")\n}\n\nfunc (m notice) String() string {\n\treturn \"%%%Notice License%%%\"\n}\n\ntype reciprocal struct{}\n\nfunc (m reciprocal) isMatch(line string) bool {\n\treturn strings.HasPrefix(strings.TrimLeft(line, \" \"), \"$$$Reciprocal License$$$\")\n}\n\nfunc (m reciprocal) String() string {\n\treturn \"$$$Reciprocal License$$$\"\n}\n\ntype restricted struct{}\n\nfunc (m restricted) isMatch(line string) bool {\n\treturn strings.HasPrefix(strings.TrimLeft(line, \" \"), \"###Restricted License###\")\n}\n\nfunc (m restricted) String() string {\n\treturn \"###Restricted License###\"\n}\n\ntype proprietary struct{}\n\nfunc (m proprietary) isMatch(line string) bool {\n\treturn strings.HasPrefix(strings.TrimLeft(line, \" \"), \"@@@Proprietary License@@@\")\n}\n\nfunc (m proprietary) String() string {\n\treturn \"@@@Proprietary License@@@\"\n}\n\ntype matcherList []matcher\n\nfunc (l matcherList) String() string {\n\tvar sb strings.Builder\n\tfor _, m := range l {\n\t\ts := m.String()\n\t\tif s[:3] == s[len(s)-3:] {\n\t\t\tfmt.Fprintln(&sb)\n\t\t}\n\t\tfmt.Fprintf(&sb, \"%s\\n\", s)\n\t\tif s[:3] == s[len(s)-3:] {\n\t\t\tfmt.Fprintln(&sb)\n\t\t}\n\t}\n\treturn sb.String()\n}\n"
  },
  {
    "path": "tools/compliance/cmd/xmlnotice/xmlnotice.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"encoding/xml\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"android/soong/response\"\n\t\"android/soong/tools/compliance\"\n\n\t\"github.com/google/blueprint/deptools\"\n)\n\nvar (\n\tfailNoneRequested = fmt.Errorf(\"\\nNo license metadata files requested\")\n\tfailNoLicenses    = fmt.Errorf(\"No licenses found\")\n)\n\ntype context struct {\n\tstdout      io.Writer\n\tstderr      io.Writer\n\trootFS      fs.FS\n\tproduct     string\n\tstripPrefix []string\n\ttitle       string\n\tdeps        *[]string\n}\n\nfunc (ctx context) strip(installPath string) string {\n\tfor _, prefix := range ctx.stripPrefix {\n\t\tif strings.HasPrefix(installPath, prefix) {\n\t\t\tp := strings.TrimPrefix(installPath, prefix)\n\t\t\tif 0 == len(p) {\n\t\t\t\tp = ctx.product\n\t\t\t}\n\t\t\tif 0 == len(p) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn p\n\t\t}\n\t}\n\treturn installPath\n}\n\n// newMultiString creates a flag that allows multiple values in an array.\nfunc newMultiString(flags *flag.FlagSet, name, usage string) *multiString {\n\tvar f multiString\n\tflags.Var(&f, name, usage)\n\treturn &f\n}\n\n// multiString implements the flag `Value` interface for multiple strings.\ntype multiString []string\n\nfunc (ms *multiString) String() string     { return strings.Join(*ms, \", \") }\nfunc (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }\n\nfunc main() {\n\tvar expandedArgs []string\n\tfor _, arg := range os.Args[1:] {\n\t\tif strings.HasPrefix(arg, \"@\") {\n\t\t\tf, err := os.Open(strings.TrimPrefix(arg, \"@\"))\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\trespArgs, err := response.ReadRspFile(f)\n\t\t\tf.Close()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\texpandedArgs = append(expandedArgs, respArgs...)\n\t\t} else {\n\t\t\texpandedArgs = append(expandedArgs, arg)\n\t\t}\n\t}\n\n\tflags := flag.NewFlagSet(\"flags\", flag.ExitOnError)\n\n\tflags.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}\n\nOutputs an xml NOTICE.xml or gzipped NOTICE.xml.gz file if the -o filename ends\nwith \".gz\".\n\nOptions:\n`, filepath.Base(os.Args[0]))\n\t\tflags.PrintDefaults()\n\t}\n\n\toutputFile := flags.String(\"o\", \"-\", \"Where to write the NOTICE xml or xml.gz file. (default stdout)\")\n\tdepsFile := flags.String(\"d\", \"\", \"Where to write the deps file\")\n\tproduct := flags.String(\"product\", \"\", \"The name of the product for which the notice is generated.\")\n\tstripPrefix := newMultiString(flags, \"strip_prefix\", \"Prefix to remove from paths. i.e. path to root (multiple allowed)\")\n\ttitle := flags.String(\"title\", \"\", \"The title of the notice file.\")\n\n\tflags.Parse(expandedArgs)\n\n\t// Must specify at least one root target.\n\tif flags.NArg() == 0 {\n\t\tflags.Usage()\n\t\tos.Exit(2)\n\t}\n\n\tif len(*outputFile) == 0 {\n\t\tflags.Usage()\n\t\tfmt.Fprintf(os.Stderr, \"must specify file for -o; use - for stdout\\n\")\n\t\tos.Exit(2)\n\t} else {\n\t\tdir, err := filepath.Abs(filepath.Dir(*outputFile))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot determine path to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfi, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cannot read directory %q of %q: %s\\n\", dir, *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !fi.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"parent %q of %q is not a directory\\n\", dir, *outputFile)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar ofile io.Writer\n\tvar closer io.Closer\n\tofile = os.Stdout\n\tvar obuf *bytes.Buffer\n\tif *outputFile != \"-\" {\n\t\tobuf = &bytes.Buffer{}\n\t\tofile = obuf\n\t}\n\tif strings.HasSuffix(*outputFile, \".gz\") {\n\t\tofile, _ = gzip.NewWriterLevel(obuf, gzip.BestCompression)\n\t\tcloser = ofile.(io.Closer)\n\t}\n\n\tvar deps []string\n\n\tctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, *title, &deps}\n\n\terr := xmlNotice(ctx, flags.Args()...)\n\tif err != nil {\n\t\tif err == failNoneRequested {\n\t\t\tflags.Usage()\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"%s\\n\", err.Error())\n\t\tos.Exit(1)\n\t}\n\tif closer != nil {\n\t\tcloser.Close()\n\t}\n\n\tif *outputFile != \"-\" {\n\t\terr := os.WriteFile(*outputFile, obuf.Bytes(), 0666)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write output to %q: %s\\n\", *outputFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tif *depsFile != \"\" {\n\t\terr := deptools.WriteDepFile(*depsFile, *outputFile, deps)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"could not write deps to %q: %s\\n\", *depsFile, err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tos.Exit(0)\n}\n\n// xmlNotice implements the xmlnotice utility.\nfunc xmlNotice(ctx *context, files ...string) error {\n\t// Must be at least one root file.\n\tif len(files) < 1 {\n\t\treturn failNoneRequested\n\t}\n\n\t// Read the license graph from the license metadata files (*.meta_lic).\n\tlicenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license metadata file(s) %q: %v\\n\", files, err)\n\t}\n\tif licenseGraph == nil {\n\t\treturn failNoLicenses\n\t}\n\n\t// rs contains all notice resolutions.\n\trs := compliance.ResolveNotices(licenseGraph)\n\n\tni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Unable to read license text file(s) for %q: %v\\n\", files, err)\n\t}\n\n\tfmt.Fprintln(ctx.stdout, \"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\")\n\tfmt.Fprintln(ctx.stdout, \"<licenses>\")\n\n\tfor installPath := range ni.InstallPaths() {\n\t\tp := ctx.strip(installPath)\n\t\tfor _, h := range ni.InstallHashes(installPath) {\n\t\t\tfor _, lib := range ni.InstallHashLibs(installPath, h) {\n\t\t\t\tfmt.Fprintf(ctx.stdout, \"<file-name contentId=\\\"%s\\\" lib=\\\"\", h.String())\n\t\t\t\txml.EscapeText(ctx.stdout, []byte(lib))\n\t\t\t\tfmt.Fprintf(ctx.stdout, \"\\\">\")\n\t\t\t\txml.EscapeText(ctx.stdout, []byte(p))\n\t\t\t\tfmt.Fprintln(ctx.stdout, \"</file-name>\")\n\t\t\t}\n\t\t}\n\t}\n\tfor h := range ni.Hashes() {\n\t\tfmt.Fprintf(ctx.stdout, \"<file-content contentId=\\\"%s\\\"><![CDATA[\", h)\n\t\txml.EscapeText(ctx.stdout, ni.HashText(h))\n\t\tfmt.Fprintf(ctx.stdout, \"]]></file-content>\\n\\n\")\n\t}\n\tfmt.Fprintln(ctx.stdout, \"</licenses>\")\n\n\t*ctx.deps = ni.InputFiles()\n\tsort.Strings(*ctx.deps)\n\n\treturn nil\n}\n"
  },
  {
    "path": "tools/compliance/cmd/xmlnotice/xmlnotice_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance\"\n)\n\nvar (\n\tinstallTarget = regexp.MustCompile(`^<file-name contentId=\"[^\"]{32}\" lib=\"([^\"]*)\">([^<]+)</file-name>`)\n\tlicenseText = regexp.MustCompile(`^<file-content contentId=\"[^\"]{32}\"><![[]CDATA[[]([^]]*)[]][]]></file-content>`)\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the parent directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"..\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc Test(t *testing.T) {\n\ttests := []struct {\n\t\tcondition    string\n\t\tname         string\n\t\toutDir       string\n\t\troots        []string\n\t\tstripPrefix  string\n\t\texpectedOut  []matcher\n\t\texpectedDeps []string\n\t}{\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"highest.apex\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin2\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/lib/liba.so\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/lib/libb.so\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/firstparty/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"container.zip\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin2\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/liba.so\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/libb.so\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/firstparty/container.zip.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"application\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/application.meta_lic\",\n\t\t\t\t\"testdata/firstparty/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"bin/bin1\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/firstparty/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"firstparty\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"lib/libd.so\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/firstparty/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"highest.apex\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"Device\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"External\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin2\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/lib/liba.so\", \"Device\"},\n\t\t\t\ttarget{\"highest.apex/lib/libb.so\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"container.zip\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"Device\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"External\"},\n\t\t\t\ttarget{\"container.zip/bin2\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/liba.so\", \"Device\"},\n\t\t\t\ttarget{\"container.zip/libb.so\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"application\", \"Android\"},\n\t\t\t\ttarget{\"application\", \"Device\"},\n\t\t\t\tfirstParty{},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/application.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"bin/bin1\", \"Android\"},\n\t\t\t\ttarget{\"bin/bin1\", \"Device\"},\n\t\t\t\ttarget{\"bin/bin1\", \"External\"},\n\t\t\t\tfirstParty{},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"notice\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"lib/libd.so\", \"External\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"highest.apex\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"Device\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"External\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin2\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/lib/liba.so\", \"Device\"},\n\t\t\t\ttarget{\"highest.apex/lib/libb.so\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"container.zip\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"Device\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"External\"},\n\t\t\t\ttarget{\"container.zip/bin2\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/liba.so\", \"Device\"},\n\t\t\t\ttarget{\"container.zip/libb.so\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/container.zip.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"application\", \"Android\"},\n\t\t\t\ttarget{\"application\", \"Device\"},\n\t\t\t\tfirstParty{},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/application.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"bin/bin1\", \"Android\"},\n\t\t\t\ttarget{\"bin/bin1\", \"Device\"},\n\t\t\t\ttarget{\"bin/bin1\", \"External\"},\n\t\t\t\tfirstParty{},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/reciprocal/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"reciprocal\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"lib/libd.so\", \"External\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"highest.apex\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"Device\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"External\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin2\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin2\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/lib/liba.so\", \"Device\"},\n\t\t\t\ttarget{\"highest.apex/lib/libb.so\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t\trestricted{},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/restricted/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"container.zip\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"Device\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"External\"},\n\t\t\t\ttarget{\"container.zip/bin2\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin2\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/liba.so\", \"Device\"},\n\t\t\t\ttarget{\"container.zip/libb.so\", \"Android\"},\n\t\t\t\tfirstParty{},\n\t\t\t\trestricted{},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/restricted/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/restricted/container.zip.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"application\", \"Android\"},\n\t\t\t\ttarget{\"application\", \"Device\"},\n\t\t\t\tfirstParty{},\n\t\t\t\trestricted{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/application.meta_lic\",\n\t\t\t\t\"testdata/restricted/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"bin/bin1\", \"Android\"},\n\t\t\t\ttarget{\"bin/bin1\", \"Device\"},\n\t\t\t\ttarget{\"bin/bin1\", \"External\"},\n\t\t\t\tfirstParty{},\n\t\t\t\trestricted{},\n\t\t\t\treciprocal{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/reciprocal/RECIPROCAL_LICENSE\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t\t\"testdata/restricted/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"restricted\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"lib/libd.so\", \"External\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/restricted/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"apex\",\n\t\t\troots:     []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"highest.apex\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"Device\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin1\", \"External\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin2\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/bin/bin2\", \"Android\"},\n\t\t\t\ttarget{\"highest.apex/lib/liba.so\", \"Device\"},\n\t\t\t\ttarget{\"highest.apex/lib/libb.so\", \"Android\"},\n\t\t\t\trestricted{},\n\t\t\t\tfirstParty{},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/proprietary/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"container\",\n\t\t\troots:     []string{\"container.zip.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"container.zip\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"Device\"},\n\t\t\t\ttarget{\"container.zip/bin1\", \"External\"},\n\t\t\t\ttarget{\"container.zip/bin2\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/bin2\", \"Android\"},\n\t\t\t\ttarget{\"container.zip/liba.so\", \"Device\"},\n\t\t\t\ttarget{\"container.zip/libb.so\", \"Android\"},\n\t\t\t\trestricted{},\n\t\t\t\tfirstParty{},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/proprietary/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/proprietary/container.zip.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t\t\"testdata/restricted/RESTRICTED_LICENSE\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"application\",\n\t\t\troots:     []string{\"application.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"application\", \"Android\"},\n\t\t\t\ttarget{\"application\", \"Device\"},\n\t\t\t\tfirstParty{},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/application.meta_lic\",\n\t\t\t\t\"testdata/proprietary/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"binary\",\n\t\t\troots:     []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"bin/bin1\", \"Android\"},\n\t\t\t\ttarget{\"bin/bin1\", \"Device\"},\n\t\t\t\ttarget{\"bin/bin1\", \"External\"},\n\t\t\t\tfirstParty{},\n\t\t\t\tproprietary{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/firstparty/FIRST_PARTY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/PROPRIETARY_LICENSE\",\n\t\t\t\t\"testdata/proprietary/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/proprietary/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tcondition: \"proprietary\",\n\t\t\tname:      \"library\",\n\t\t\troots:     []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedOut: []matcher{\n\t\t\t\ttarget{\"lib/libd.so\", \"External\"},\n\t\t\t\tnotice{},\n\t\t\t},\n\t\t\texpectedDeps: []string{\n\t\t\t\t\"testdata/notice/NOTICE_LICENSE\",\n\t\t\t\t\"testdata/proprietary/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.condition+\" \"+tt.name, func(t *testing.T) {\n\t\t\tstdout := &bytes.Buffer{}\n\t\t\tstderr := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/\"+tt.condition+\"/\"+r)\n\t\t\t}\n\n\t\t\tvar deps []string\n\n\t\t\tctx := context{stdout, stderr, compliance.GetFS(tt.outDir), \"\", []string{tt.stripPrefix}, \"\", &deps}\n\n\t\t\terr := xmlNotice(&ctx, rootFiles...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"xmlnotice: error = %v, stderr = %v\", err, stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif stderr.Len() > 0 {\n\t\t\t\tt.Errorf(\"xmlnotice: gotStderr = %v, want none\", stderr)\n\t\t\t}\n\n\t\t\tt.Logf(\"got stdout: %s\", stdout.String())\n\n\t\t\tt.Logf(\"want stdout: %s\", matcherList(tt.expectedOut).String())\n\n\t\t\tout := bufio.NewScanner(stdout)\n\t\t\tlineno := 0\n\t\t\tinBody := false\n\t\t\toutOfBody := true\n\t\t\tfor out.Scan() {\n\t\t\t\tline := out.Text()\n\t\t\t\tif strings.TrimLeft(line, \" \") == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif lineno == 0 && !inBody && `<?xml version=\"1.0\" encoding=\"utf-8\"?>` == line {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !inBody {\n\t\t\t\t\tif \"<licenses>\" == line {\n\t\t\t\t\t\tinBody = true\n\t\t\t\t\t\toutOfBody = false\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t} else if \"</licenses>\" == line {\n\t\t\t\t\toutOfBody = true\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif len(tt.expectedOut) <= lineno {\n\t\t\t\t\tt.Errorf(\"xmlnotice: unexpected output at line %d: got %q, want nothing (wanted %d lines)\", lineno+1, line, len(tt.expectedOut))\n\t\t\t\t} else if !tt.expectedOut[lineno].isMatch(line) {\n\t\t\t\t\tt.Errorf(\"xmlnotice: unexpected output at line %d: got %q, want %q\", lineno+1, line, tt.expectedOut[lineno].String())\n\t\t\t\t}\n\t\t\t\tlineno++\n\t\t\t}\n\t\t\tif !inBody {\n\t\t\t\tt.Errorf(\"xmlnotice: missing <licenses> tag: got no <licenses> tag, want <licenses> tag on 2nd line\")\n\t\t\t}\n\t\t\tif !outOfBody {\n\t\t\t\tt.Errorf(\"xmlnotice: missing </licenses> tag: got no </licenses> tag, want </licenses> tag on last line\")\n\t\t\t}\n\t\t\tfor ; lineno < len(tt.expectedOut); lineno++ {\n\t\t\t\tt.Errorf(\"xmlnotice: missing output line %d: ended early, want %q\", lineno+1, tt.expectedOut[lineno].String())\n\t\t\t}\n\n\t\t\tt.Logf(\"got deps: %q\", deps)\n\n\t\t\tt.Logf(\"want deps: %q\", tt.expectedDeps)\n\n\t\t\tif g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {\n\t\t\t\tt.Errorf(\"unexpected deps, wanted:\\n%s\\ngot:\\n%s\\n\",\n\t\t\t\t\tstrings.Join(w, \"\\n\"), strings.Join(g, \"\\n\"))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc escape(s string) string {\n\tb := &bytes.Buffer{}\n\txml.EscapeText(b, []byte(s))\n\treturn b.String()\n}\n\ntype matcher interface {\n\tisMatch(line string) bool\n\tString() string\n}\n\ntype target struct {\n\tname string\n\tlib string\n}\n\nfunc (m target) isMatch(line string) bool {\n\tgroups := installTarget.FindStringSubmatch(line)\n\tif len(groups) != 3 {\n\t\treturn false\n\t}\n\treturn groups[1] == escape(m.lib) && strings.HasPrefix(groups[2], \"out/\") && strings.HasSuffix(groups[2], \"/\"+escape(m.name))\n}\n\nfunc (m target) String() string {\n\treturn `<file-name contentId=\"hash\" lib=\"` + escape(m.lib) + `\">` + escape(m.name) + `</file-name>`\n}\n\nfunc matchesText(line, text string) bool {\n\tgroups := licenseText.FindStringSubmatch(line)\n\tif len(groups) != 2 {\n\t\treturn false\n\t}\n\treturn groups[1] == escape(text + \"\\n\")\n}\n\nfunc expectedText(text string) string {\n\treturn `<file-content contentId=\"hash\"><![CDATA[` + escape(text + \"\\n\") + `]]></file-content>`\n}\n\ntype firstParty struct{}\n\nfunc (m firstParty) isMatch(line string) bool {\n\treturn matchesText(line, \"&&&First Party License&&&\")\n}\n\nfunc (m firstParty) String() string {\n\treturn expectedText(\"&&&First Party License&&&\")\n}\n\ntype notice struct{}\n\nfunc (m notice) isMatch(line string) bool {\n\treturn matchesText(line, \"%%%Notice License%%%\")\n}\n\nfunc (m notice) String() string {\n\treturn expectedText(\"%%%Notice License%%%\")\n}\n\ntype reciprocal struct{}\n\nfunc (m reciprocal) isMatch(line string) bool {\n\treturn matchesText(line, \"$$$Reciprocal License$$$\")\n}\n\nfunc (m reciprocal) String() string {\n\treturn expectedText(\"$$$Reciprocal License$$$\")\n}\n\ntype restricted struct{}\n\nfunc (m restricted) isMatch(line string) bool {\n\treturn matchesText(line, \"###Restricted License###\")\n}\n\nfunc (m restricted) String() string {\n\treturn expectedText(\"###Restricted License###\")\n}\n\ntype proprietary struct{}\n\nfunc (m proprietary) isMatch(line string) bool {\n\treturn matchesText(line, \"@@@Proprietary License@@@\")\n}\n\nfunc (m proprietary) String() string {\n\treturn expectedText(\"@@@Proprietary License@@@\")\n}\n\ntype matcherList []matcher\n\nfunc (l matcherList) String() string {\n\tvar sb strings.Builder\n\tfmt.Fprintln(&sb, `<?xml version=\"1.0\" encoding=\"utf-8\"?>`)\n\tfmt.Fprintln(&sb, `<licenses>`)\n\tfor _, m := range l {\n\t\ts := m.String()\n\t\tfmt.Fprintln(&sb, s)\n\t\tif _, ok := m.(target); !ok {\n\t\t\tfmt.Fprintln(&sb)\n\t\t}\n\t}\n\tfmt.Fprintln(&sb, `/<licenses>`)\n\treturn sb.String()\n}\n"
  },
  {
    "path": "tools/compliance/condition.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"fmt\"\n)\n\n// LicenseCondition identifies a recognized license condition by setting the\n// corresponding bit.\ntype LicenseCondition uint16\n\n// LicenseConditionMask is a bitmask for the recognized license conditions.\nconst LicenseConditionMask = LicenseCondition(0x1ff)\n\nconst (\n\t// UnencumberedCondition identifies public domain or public domain-\n\t// like license that disclaims copyright.\n\tUnencumberedCondition = LicenseCondition(0x0001)\n\t// PermissiveCondition identifies a license without notice or other\n\t// significant requirements.\n\tPermissiveCondition = LicenseCondition(0x0002)\n\t// NoticeCondition identifies a typical open-source license with only\n\t// notice or attribution requirements.\n\tNoticeCondition = LicenseCondition(0x0004)\n\t// ReciprocalCondition identifies a license with requirement to share\n\t// the module's source only.\n\tReciprocalCondition = LicenseCondition(0x0008)\n\t// RestrictedCondition identifies a license with requirement to share\n\t// all source code linked to the module's source.\n\tRestrictedCondition = LicenseCondition(0x0010)\n\t// WeaklyRestrictedCondition identifies a RestrictedCondition waived\n\t// for dynamic linking.\n\tWeaklyRestrictedCondition = LicenseCondition(0x0020)\n\t// ProprietaryCondition identifies a license with source privacy\n\t// requirements.\n\tProprietaryCondition = LicenseCondition(0x0040)\n\t// ByExceptionOnly identifies a license where policy requires product\n\t// counsel review prior to use.\n\tByExceptionOnlyCondition = LicenseCondition(0x0080)\n\t// NotAllowedCondition identifies a license with onerous conditions\n\t// where policy prohibits use.\n\tNotAllowedCondition = LicenseCondition(0x0100)\n)\n\nvar (\n\t// RecognizedConditionNames maps condition strings to LicenseCondition.\n\tRecognizedConditionNames = map[string]LicenseCondition{\n\t\t\"unencumbered\":                    UnencumberedCondition,\n\t\t\"permissive\":                      PermissiveCondition,\n\t\t\"notice\":                          NoticeCondition,\n\t\t\"reciprocal\":                      ReciprocalCondition,\n\t\t\"restricted\":                      RestrictedCondition,\n\t\t\"restricted_if_statically_linked\": WeaklyRestrictedCondition,\n\t\t\"proprietary\":                     ProprietaryCondition,\n\t\t\"by_exception_only\":               ByExceptionOnlyCondition,\n\t\t\"not_allowed\":                     NotAllowedCondition,\n\t}\n)\n\n// Name returns the condition string corresponding to the LicenseCondition.\nfunc (lc LicenseCondition) Name() string {\n\tswitch lc {\n\tcase UnencumberedCondition:\n\t\treturn \"unencumbered\"\n\tcase PermissiveCondition:\n\t\treturn \"permissive\"\n\tcase NoticeCondition:\n\t\treturn \"notice\"\n\tcase ReciprocalCondition:\n\t\treturn \"reciprocal\"\n\tcase RestrictedCondition:\n\t\treturn \"restricted\"\n\tcase WeaklyRestrictedCondition:\n\t\treturn \"restricted_if_statically_linked\"\n\tcase ProprietaryCondition:\n\t\treturn \"proprietary\"\n\tcase ByExceptionOnlyCondition:\n\t\treturn \"by_exception_only\"\n\tcase NotAllowedCondition:\n\t\treturn \"not_allowed\"\n\t}\n\tpanic(fmt.Errorf(\"unrecognized license condition: %#v\", lc))\n}\n"
  },
  {
    "path": "tools/compliance/condition_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"testing\"\n)\n\nfunc TestConditionSetHas(t *testing.T) {\n\timpliesShare := ImpliesShared\n\n\tt.Logf(\"testing with imliesShare=%#v\", impliesShare)\n\n\tif impliesShare.HasAny(NoticeCondition) {\n\t\tt.Errorf(\"impliesShare.HasAny(\\\"notice\\\"=%#v) got true, want false\", NoticeCondition)\n\t}\n\n\tif !impliesShare.HasAny(RestrictedCondition) {\n\t\tt.Errorf(\"impliesShare.HasAny(\\\"restricted\\\"=%#v) got false, want true\", RestrictedCondition)\n\t}\n\n\tif !impliesShare.HasAny(ReciprocalCondition) {\n\t\tt.Errorf(\"impliesShare.HasAny(\\\"reciprocal\\\"=%#v) got false, want true\", ReciprocalCondition)\n\t}\n\n\tif impliesShare.HasAny(LicenseCondition(0x0000)) {\n\t\tt.Errorf(\"impliesShare.HasAny(nil=%#v) got true, want false\", LicenseCondition(0x0000))\n\t}\n}\n\nfunc TestConditionName(t *testing.T) {\n\tfor expected, condition := range RecognizedConditionNames {\n\t\tactual := condition.Name()\n\t\tif expected != actual {\n\t\t\tt.Errorf(\"unexpected name for condition %#v: got %s, want %s\", condition, actual, expected)\n\t\t}\n\t}\n}\n\nfunc TestConditionName_InvalidCondition(t *testing.T) {\n\tpanicked := false\n\tvar lc LicenseCondition\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\tpanicked = true\n\t\t\t}\n\t\t}()\n\t\tname := lc.Name()\n\t\tt.Errorf(\"invalid condition unexpected name: got %s, wanted panic\", name)\n\t}()\n\tif !panicked {\n\t\tt.Errorf(\"no expected panic for %#v.Name(): got no panic, wanted panic\", lc)\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/conditionset.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// LicenseConditionSet identifies sets of license conditions.\ntype LicenseConditionSet LicenseCondition\n\n// AllLicenseConditions is the set of all recognized license conditions.\nconst AllLicenseConditions = LicenseConditionSet(LicenseConditionMask)\n\n// NewLicenseConditionSet returns a set containing exactly the elements of\n// `conditions`.\nfunc NewLicenseConditionSet(conditions ...LicenseCondition) LicenseConditionSet {\n\tcs := LicenseConditionSet(0x00)\n\tfor _, lc := range conditions {\n\t\tcs |= LicenseConditionSet(lc)\n\t}\n\treturn cs\n}\n\n// Plus returns a new set containing all of the elements of `cs` and all of the\n// `conditions`.\nfunc (cs LicenseConditionSet) Plus(conditions ...LicenseCondition) LicenseConditionSet {\n\tresult := cs\n\tfor _, lc := range conditions {\n\t\tresult |= LicenseConditionSet(lc)\n\t}\n\treturn result\n}\n\n// Union returns a new set containing all of the elements of `cs` and all of the\n// elements of the `other` sets.\nfunc (cs LicenseConditionSet) Union(other ...LicenseConditionSet) LicenseConditionSet {\n\tresult := cs\n\tfor _, ls := range other {\n\t\tresult |= ls\n\t}\n\treturn result\n}\n\n// MatchingAny returns the subset of `cs` equal to any of the `conditions`.\nfunc (cs LicenseConditionSet) MatchingAny(conditions ...LicenseCondition) LicenseConditionSet {\n\tresult := LicenseConditionSet(0x00)\n\tfor _, lc := range conditions {\n\t\tresult |= cs & LicenseConditionSet(lc)\n\t}\n\treturn result\n}\n\n// MatchingAnySet returns the subset of `cs` that are members of any of the\n// `other` sets.\nfunc (cs LicenseConditionSet) MatchingAnySet(other ...LicenseConditionSet) LicenseConditionSet {\n\tresult := LicenseConditionSet(0x00)\n\tfor _, ls := range other {\n\t\tresult |= cs & ls\n\t}\n\treturn result\n}\n\n// HasAny returns true when `cs` contains at least one of the `conditions`.\nfunc (cs LicenseConditionSet) HasAny(conditions ...LicenseCondition) bool {\n\tfor _, lc := range conditions {\n\t\tif 0x0000 != (cs & LicenseConditionSet(lc)) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// MatchesAnySet returns true when `cs` has a non-empty intersection with at\n// least one of the `other` condition sets.\nfunc (cs LicenseConditionSet) MatchesAnySet(other ...LicenseConditionSet) bool {\n\tfor _, ls := range other {\n\t\tif 0x0000 != (cs & ls) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// HasAll returns true when `cs` contains every one of the `conditions`.\nfunc (cs LicenseConditionSet) HasAll(conditions ...LicenseCondition) bool {\n\tfor _, lc := range conditions {\n\t\tif 0x0000 == (cs & LicenseConditionSet(lc)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// MatchesEverySet returns true when `cs` has a non-empty intersection with\n// each of the `other` condition sets.\nfunc (cs LicenseConditionSet) MatchesEverySet(other ...LicenseConditionSet) bool {\n\tfor _, ls := range other {\n\t\tif 0x0000 == (cs & ls) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Intersection returns the subset of `cs` that are members of every `other`\n// set.\nfunc (cs LicenseConditionSet) Intersection(other ...LicenseConditionSet) LicenseConditionSet {\n\tresult := cs\n\tfor _, ls := range other {\n\t\tresult &= ls\n\t}\n\treturn result\n}\n\n// Minus returns the subset of `cs` that are not equaal to any `conditions`.\nfunc (cs LicenseConditionSet) Minus(conditions ...LicenseCondition) LicenseConditionSet {\n\tresult := cs\n\tfor _, lc := range conditions {\n\t\tresult &^= LicenseConditionSet(lc)\n\t}\n\treturn result\n}\n\n// Difference returns the subset of `cs` that are not members of any `other`\n// set.\nfunc (cs LicenseConditionSet) Difference(other ...LicenseConditionSet) LicenseConditionSet {\n\tresult := cs\n\tfor _, ls := range other {\n\t\tresult &^= ls\n\t}\n\treturn result\n}\n\n// Len returns the number of license conditions in the set.\nfunc (cs LicenseConditionSet) Len() int {\n\tsize := 0\n\tfor lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {\n\t\tif 0x00 != (cs & lc) {\n\t\t\tsize++\n\t\t}\n\t}\n\treturn size\n}\n\n// AsList returns an array of the license conditions in the set.\nfunc (cs LicenseConditionSet) AsList() []LicenseCondition {\n\tresult := make([]LicenseCondition, 0, cs.Len())\n\tfor lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {\n\t\tif 0x00 != (cs & lc) {\n\t\t\tresult = append(result, LicenseCondition(lc))\n\t\t}\n\t}\n\treturn result\n}\n\n// Names returns an array of the names of the license conditions in the set.\nfunc (cs LicenseConditionSet) Names() []string {\n\tresult := make([]string, 0, cs.Len())\n\tfor lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {\n\t\tif 0x00 != (cs & lc) {\n\t\t\tresult = append(result, LicenseCondition(lc).Name())\n\t\t}\n\t}\n\treturn result\n}\n\n// IsEmpty returns true when the set contains no license conditions.\nfunc (cs LicenseConditionSet) IsEmpty() bool {\n\treturn 0x00 == (cs & AllLicenseConditions)\n}\n\n// String returns a human-readable string representation of the set.\nfunc (cs LicenseConditionSet) String() string {\n\treturn fmt.Sprintf(\"{%s}\", strings.Join(cs.Names(), \"|\"))\n}\n"
  },
  {
    "path": "tools/compliance/conditionset_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestConditionSet(t *testing.T) {\n\ttests := []struct {\n\t\tname        string\n\t\tconditions  []string\n\t\tplus        *[]string\n\t\tminus       *[]string\n\t\tmatchingAny map[string][]string\n\t\texpected    []string\n\t}{\n\t\t{\n\t\t\tname:       \"empty\",\n\t\t\tconditions: []string{},\n\t\t\tplus:       &[]string{},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"notice\":                []string{},\n\t\t\t\t\"restricted\":            []string{},\n\t\t\t\t\"restricted|reciprocal\": []string{},\n\t\t\t},\n\t\t\texpected: []string{},\n\t\t},\n\t\t{\n\t\t\tname:       \"emptyminusnothing\",\n\t\t\tconditions: []string{},\n\t\t\tminus:      &[]string{},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"notice\":                []string{},\n\t\t\t\t\"restricted\":            []string{},\n\t\t\t\t\"restricted|reciprocal\": []string{},\n\t\t\t},\n\t\t\texpected: []string{},\n\t\t},\n\t\t{\n\t\t\tname:       \"emptyminusnotice\",\n\t\t\tconditions: []string{},\n\t\t\tminus:      &[]string{\"notice\"},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"notice\":                []string{},\n\t\t\t\t\"restricted\":            []string{},\n\t\t\t\t\"restricted|reciprocal\": []string{},\n\t\t\t},\n\t\t\texpected: []string{},\n\t\t},\n\t\t{\n\t\t\tname:       \"noticeonly\",\n\t\t\tconditions: []string{\"notice\"},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"notice\":             []string{\"notice\"},\n\t\t\t\t\"notice|proprietary\": []string{\"notice\"},\n\t\t\t\t\"restricted\":         []string{},\n\t\t\t},\n\t\t\texpected: []string{\"notice\"},\n\t\t},\n\t\t{\n\t\t\tname:       \"allnoticeonly\",\n\t\t\tconditions: []string{\"notice\"},\n\t\t\tplus:       &[]string{\"notice\"},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"notice\":             []string{\"notice\"},\n\t\t\t\t\"notice|proprietary\": []string{\"notice\"},\n\t\t\t\t\"restricted\":         []string{},\n\t\t\t},\n\t\t\texpected: []string{\"notice\"},\n\t\t},\n\t\t{\n\t\t\tname:       \"emptyplusnotice\",\n\t\t\tconditions: []string{},\n\t\t\tplus:       &[]string{\"notice\"},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"notice\":             []string{\"notice\"},\n\t\t\t\t\"notice|proprietary\": []string{\"notice\"},\n\t\t\t\t\"restricted\":         []string{},\n\t\t\t},\n\t\t\texpected: []string{\"notice\"},\n\t\t},\n\t\t{\n\t\t\tname:       \"everything\",\n\t\t\tconditions: []string{\"unencumbered\", \"permissive\", \"notice\", \"reciprocal\", \"restricted\", \"proprietary\"},\n\t\t\tplus:       &[]string{\"restricted_if_statically_linked\", \"by_exception_only\", \"not_allowed\"},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"unencumbered\":                    []string{\"unencumbered\"},\n\t\t\t\t\"permissive\":                      []string{\"permissive\"},\n\t\t\t\t\"notice\":                          []string{\"notice\"},\n\t\t\t\t\"reciprocal\":                      []string{\"reciprocal\"},\n\t\t\t\t\"restricted\":                      []string{\"restricted\"},\n\t\t\t\t\"restricted_if_statically_linked\": []string{\"restricted_if_statically_linked\"},\n\t\t\t\t\"proprietary\":                     []string{\"proprietary\"},\n\t\t\t\t\"by_exception_only\":               []string{\"by_exception_only\"},\n\t\t\t\t\"not_allowed\":                     []string{\"not_allowed\"},\n\t\t\t\t\"notice|proprietary\":              []string{\"notice\", \"proprietary\"},\n\t\t\t},\n\t\t\texpected: []string{\n\t\t\t\t\"unencumbered\",\n\t\t\t\t\"permissive\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"by_exception_only\",\n\t\t\t\t\"not_allowed\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"everythingplusminusnothing\",\n\t\t\tconditions: []string{\n\t\t\t\t\"unencumbered\",\n\t\t\t\t\"permissive\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"by_exception_only\",\n\t\t\t\t\"not_allowed\",\n\t\t\t},\n\t\t\tplus:  &[]string{},\n\t\t\tminus: &[]string{},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"unencumbered|permissive|notice\": []string{\"unencumbered\", \"permissive\", \"notice\"},\n\t\t\t\t\"restricted|reciprocal\":          []string{\"reciprocal\", \"restricted\"},\n\t\t\t\t\"proprietary|by_exception_only\":  []string{\"proprietary\", \"by_exception_only\"},\n\t\t\t\t\"not_allowed\":                    []string{\"not_allowed\"},\n\t\t\t},\n\t\t\texpected: []string{\n\t\t\t\t\"unencumbered\",\n\t\t\t\t\"permissive\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"by_exception_only\",\n\t\t\t\t\"not_allowed\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"allbutone\",\n\t\t\tconditions: []string{\"unencumbered\", \"permissive\", \"notice\", \"reciprocal\", \"restricted\", \"proprietary\"},\n\t\t\tplus:       &[]string{\"restricted_if_statically_linked\", \"by_exception_only\", \"not_allowed\"},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"unencumbered\":                    []string{\"unencumbered\"},\n\t\t\t\t\"permissive\":                      []string{\"permissive\"},\n\t\t\t\t\"notice\":                          []string{\"notice\"},\n\t\t\t\t\"reciprocal\":                      []string{\"reciprocal\"},\n\t\t\t\t\"restricted\":                      []string{\"restricted\"},\n\t\t\t\t\"restricted_if_statically_linked\": []string{\"restricted_if_statically_linked\"},\n\t\t\t\t\"proprietary\":                     []string{\"proprietary\"},\n\t\t\t\t\"by_exception_only\":               []string{\"by_exception_only\"},\n\t\t\t\t\"not_allowed\":                     []string{\"not_allowed\"},\n\t\t\t\t\"notice|proprietary\":              []string{\"notice\", \"proprietary\"},\n\t\t\t},\n\t\t\texpected: []string{\n\t\t\t\t\"unencumbered\",\n\t\t\t\t\"permissive\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"by_exception_only\",\n\t\t\t\t\"not_allowed\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"everythingminusone\",\n\t\t\tconditions: []string{\n\t\t\t\t\"unencumbered\",\n\t\t\t\t\"permissive\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"by_exception_only\",\n\t\t\t\t\"not_allowed\",\n\t\t\t},\n\t\t\tminus: &[]string{\"restricted_if_statically_linked\"},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"unencumbered\":                    []string{\"unencumbered\"},\n\t\t\t\t\"permissive\":                      []string{\"permissive\"},\n\t\t\t\t\"notice\":                          []string{\"notice\"},\n\t\t\t\t\"reciprocal\":                      []string{\"reciprocal\"},\n\t\t\t\t\"restricted\":                      []string{\"restricted\"},\n\t\t\t\t\"restricted_if_statically_linked\": []string{},\n\t\t\t\t\"proprietary\":                     []string{\"proprietary\"},\n\t\t\t\t\"by_exception_only\":               []string{\"by_exception_only\"},\n\t\t\t\t\"not_allowed\":                     []string{\"not_allowed\"},\n\t\t\t\t\"restricted|proprietary\":          []string{\"restricted\", \"proprietary\"},\n\t\t\t},\n\t\t\texpected: []string{\n\t\t\t\t\"unencumbered\",\n\t\t\t\t\"permissive\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"by_exception_only\",\n\t\t\t\t\"not_allowed\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"everythingminuseverything\",\n\t\t\tconditions: []string{\n\t\t\t\t\"unencumbered\",\n\t\t\t\t\"permissive\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"by_exception_only\",\n\t\t\t\t\"not_allowed\",\n\t\t\t},\n\t\t\tminus: &[]string{\n\t\t\t\t\"unencumbered\",\n\t\t\t\t\"permissive\",\n\t\t\t\t\"notice\",\n\t\t\t\t\"reciprocal\",\n\t\t\t\t\"restricted\",\n\t\t\t\t\"restricted_if_statically_linked\",\n\t\t\t\t\"proprietary\",\n\t\t\t\t\"by_exception_only\",\n\t\t\t\t\"not_allowed\",\n\t\t\t},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"unencumbered\":                    []string{},\n\t\t\t\t\"permissive\":                      []string{},\n\t\t\t\t\"notice\":                          []string{},\n\t\t\t\t\"reciprocal\":                      []string{},\n\t\t\t\t\"restricted\":                      []string{},\n\t\t\t\t\"restricted_if_statically_linked\": []string{},\n\t\t\t\t\"proprietary\":                     []string{},\n\t\t\t\t\"by_exception_only\":               []string{},\n\t\t\t\t\"not_allowed\":                     []string{},\n\t\t\t\t\"restricted|proprietary\":          []string{},\n\t\t\t},\n\t\t\texpected: []string{},\n\t\t},\n\t\t{\n\t\t\tname:       \"restrictedplus\",\n\t\t\tconditions: []string{\"restricted\", \"restricted_if_statically_linked\"},\n\t\t\tplus:       &[]string{\"permissive\", \"notice\", \"restricted\", \"proprietary\"},\n\t\t\tmatchingAny: map[string][]string{\n\t\t\t\t\"unencumbered\":                    []string{},\n\t\t\t\t\"permissive\":                      []string{\"permissive\"},\n\t\t\t\t\"notice\":                          []string{\"notice\"},\n\t\t\t\t\"restricted\":                      []string{\"restricted\"},\n\t\t\t\t\"restricted_if_statically_linked\": []string{\"restricted_if_statically_linked\"},\n\t\t\t\t\"proprietary\":                     []string{\"proprietary\"},\n\t\t\t\t\"restricted|proprietary\":          []string{\"restricted\", \"proprietary\"},\n\t\t\t\t\"by_exception_only\":               []string{},\n\t\t\t\t\"proprietary|by_exception_only\":   []string{\"proprietary\"},\n\t\t\t},\n\t\t\texpected: []string{\"permissive\", \"notice\", \"restricted\", \"restricted_if_statically_linked\", \"proprietary\"},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\ttoConditions := func(names []string) []LicenseCondition {\n\t\t\tresult := make([]LicenseCondition, 0, len(names))\n\t\t\tfor _, name := range names {\n\t\t\t\tresult = append(result, RecognizedConditionNames[name])\n\t\t\t}\n\t\t\treturn result\n\t\t}\n\t\tpopulate := func() LicenseConditionSet {\n\t\t\ttestSet := NewLicenseConditionSet(toConditions(tt.conditions)...)\n\t\t\tif tt.plus != nil {\n\t\t\t\ttestSet = testSet.Plus(toConditions(*tt.plus)...)\n\t\t\t}\n\t\t\tif tt.minus != nil {\n\t\t\t\ttestSet = testSet.Minus(toConditions(*tt.minus)...)\n\t\t\t}\n\t\t\treturn testSet\n\t\t}\n\t\tpopulateSet := func() LicenseConditionSet {\n\t\t\ttestSet := NewLicenseConditionSet(toConditions(tt.conditions)...)\n\t\t\tif tt.plus != nil {\n\t\t\t\ttestSet = testSet.Union(NewLicenseConditionSet(toConditions(*tt.plus)...))\n\t\t\t}\n\t\t\tif tt.minus != nil {\n\t\t\t\ttestSet = testSet.Difference(NewLicenseConditionSet(toConditions(*tt.minus)...))\n\t\t\t}\n\t\t\treturn testSet\n\t\t}\n\t\tpopulatePlusSet := func() LicenseConditionSet {\n\t\t\ttestSet := NewLicenseConditionSet(toConditions(tt.conditions)...)\n\t\t\tif tt.plus != nil {\n\t\t\t\ttestSet = testSet.Union(NewLicenseConditionSet(toConditions(*tt.plus)...))\n\t\t\t}\n\t\t\tif tt.minus != nil {\n\t\t\t\ttestSet = testSet.Minus(toConditions(*tt.minus)...)\n\t\t\t}\n\t\t\treturn testSet\n\t\t}\n\t\tpopulateMinusSet := func() LicenseConditionSet {\n\t\t\ttestSet := NewLicenseConditionSet(toConditions(tt.conditions)...)\n\t\t\tif tt.plus != nil {\n\t\t\t\ttestSet = testSet.Plus(toConditions(*tt.plus)...)\n\t\t\t}\n\t\t\tif tt.minus != nil {\n\t\t\t\ttestSet = testSet.Difference(NewLicenseConditionSet(toConditions(*tt.minus)...))\n\t\t\t}\n\t\t\treturn testSet\n\t\t}\n\t\tcheckMatching := func(cs LicenseConditionSet, t *testing.T) {\n\t\t\tfor data, expectedNames := range tt.matchingAny {\n\t\t\t\texpectedConditions := toConditions(expectedNames)\n\t\t\t\texpected := NewLicenseConditionSet(expectedConditions...)\n\t\t\t\tactual := cs.MatchingAny(toConditions(strings.Split(data, \"|\"))...)\n\t\t\t\tactualNames := actual.Names()\n\n\t\t\t\tt.Logf(\"MatchingAny(%s): actual set %#v %s\", data, actual, actual.String())\n\t\t\t\tt.Logf(\"MatchingAny(%s): expected set %#v %s\", data, expected, expected.String())\n\n\t\t\t\tif actual != expected {\n\t\t\t\t\tt.Errorf(\"MatchingAny(%s): got %#v, want %#v\", data, actual, expected)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(actualNames) != len(expectedNames) {\n\t\t\t\t\tt.Errorf(\"len(MatchinAny(%s).Names()): got %d, want %d\",\n\t\t\t\t\t\tdata, len(actualNames), len(expectedNames))\n\t\t\t\t} else {\n\t\t\t\t\tfor i := 0; i < len(actualNames); i++ {\n\t\t\t\t\t\tif actualNames[i] != expectedNames[i] {\n\t\t\t\t\t\t\tt.Errorf(\"MatchingAny(%s).Names()[%d]: got %s, want %s\",\n\t\t\t\t\t\t\t\tdata, i, actualNames[i], expectedNames[i])\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tactualConditions := actual.AsList()\n\t\t\t\tif len(actualConditions) != len(expectedConditions) {\n\t\t\t\t\tt.Errorf(\"len(MatchingAny(%s).AsList()):  got %d, want %d\",\n\t\t\t\t\t\tdata, len(actualNames), len(expectedNames))\n\t\t\t\t} else {\n\t\t\t\t\tfor i := 0; i < len(actualNames); i++ {\n\t\t\t\t\t\tif actualNames[i] != expectedNames[i] {\n\t\t\t\t\t\t\tt.Errorf(\"MatchingAny(%s).AsList()[%d]: got %s, want %s\",\n\t\t\t\t\t\t\t\tdata, i, actualNames[i], expectedNames[i])\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcheckMatchingSet := func(cs LicenseConditionSet, t *testing.T) {\n\t\t\tfor data, expectedNames := range tt.matchingAny {\n\t\t\t\texpected := NewLicenseConditionSet(toConditions(expectedNames)...)\n\t\t\t\tactual := cs.MatchingAnySet(NewLicenseConditionSet(toConditions(strings.Split(data, \"|\"))...))\n\t\t\t\tactualNames := actual.Names()\n\n\t\t\t\tt.Logf(\"MatchingAnySet(%s): actual set %#v %s\", data, actual, actual.String())\n\t\t\t\tt.Logf(\"MatchingAnySet(%s): expected set %#v %s\", data, expected, expected.String())\n\n\t\t\t\tif actual != expected {\n\t\t\t\t\tt.Errorf(\"MatchingAnySet(%s): got %#v, want %#v\", data, actual, expected)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(actualNames) != len(expectedNames) {\n\t\t\t\t\tt.Errorf(\"len(MatchingAnySet(%s).Names()): got %d, want %d\",\n\t\t\t\t\t\tdata, len(actualNames), len(expectedNames))\n\t\t\t\t} else {\n\t\t\t\t\tfor i := 0; i < len(actualNames); i++ {\n\t\t\t\t\t\tif actualNames[i] != expectedNames[i] {\n\t\t\t\t\t\t\tt.Errorf(\"MatchingAnySet(%s).Names()[%d]: got %s, want %s\",\n\t\t\t\t\t\t\t\tdata, i, actualNames[i], expectedNames[i])\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\texpectedConditions := toConditions(expectedNames)\n\t\t\t\tactualConditions := actual.AsList()\n\t\t\t\tif len(actualConditions) != len(expectedConditions) {\n\t\t\t\t\tt.Errorf(\"len(MatchingAnySet(%s).AsList()): got %d, want %d\",\n\t\t\t\t\t\tdata, len(actualNames), len(expectedNames))\n\t\t\t\t} else {\n\t\t\t\t\tfor i := 0; i < len(actualNames); i++ {\n\t\t\t\t\t\tif actualNames[i] != expectedNames[i] {\n\t\t\t\t\t\t\tt.Errorf(\"MatchingAnySet(%s).AsList()[%d]: got %s, want %s\",\n\t\t\t\t\t\t\t\tdata, i, actualNames[i], expectedNames[i])\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcheckExpected := func(actual LicenseConditionSet, t *testing.T) bool {\n\t\t\tt.Logf(\"checkExpected{%s}\", strings.Join(tt.expected, \", \"))\n\n\t\t\texpectedConditions := toConditions(tt.expected)\n\t\t\texpected := NewLicenseConditionSet(expectedConditions...)\n\n\t\t\tactualNames := actual.Names()\n\n\t\t\tt.Logf(\"actual license condition set: %#v %s\", actual, actual.String())\n\t\t\tt.Logf(\"expected license condition set: %#v %s\", expected, expected.String())\n\n\t\t\tif actual != expected {\n\t\t\t\tt.Errorf(\"checkExpected: got %#v, want %#v\", actual, expected)\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif len(actualNames) != len(tt.expected) {\n\t\t\t\tt.Errorf(\"len(actual.Names()): got %d, want %d\", len(actualNames), len(tt.expected))\n\t\t\t} else {\n\t\t\t\tfor i := 0; i < len(actualNames); i++ {\n\t\t\t\t\tif actualNames[i] != tt.expected[i] {\n\t\t\t\t\t\tt.Errorf(\"actual.Names()[%d]: got %s, want %s\", i, actualNames[i], tt.expected[i])\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tactualConditions := actual.AsList()\n\t\t\tif len(actualConditions) != len(expectedConditions) {\n\t\t\t\tt.Errorf(\"len(actual.AsList()): got %d, want %d\", len(actualConditions), len(expectedConditions))\n\t\t\t} else {\n\t\t\t\tfor i := 0; i < len(actualConditions); i++ {\n\t\t\t\t\tif actualConditions[i] != expectedConditions[i] {\n\t\t\t\t\t\tt.Errorf(\"actual.AsList()[%d]: got %s, want %s\",\n\t\t\t\t\t\t\ti, actualConditions[i].Name(), expectedConditions[i].Name())\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(tt.expected) == 0 {\n\t\t\t\tif !actual.IsEmpty() {\n\t\t\t\t\tt.Errorf(\"actual.IsEmpty(): got false, want true\")\n\t\t\t\t}\n\t\t\t\tif actual.HasAny(expectedConditions...) {\n\t\t\t\t\tt.Errorf(\"actual.HasAny(): got true, want false\")\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif actual.IsEmpty() {\n\t\t\t\t\tt.Errorf(\"actual.IsEmpty(): got true, want false\")\n\t\t\t\t}\n\t\t\t\tif !actual.HasAny(expectedConditions...) {\n\t\t\t\t\tt.Errorf(\"actual.HasAny(all expected): got false, want true\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !actual.HasAll(expectedConditions...) {\n\t\t\t\tt.Errorf(\"actual.Hasll(all expected): want true, got false\")\n\t\t\t}\n\t\t\tfor _, expectedCondition := range expectedConditions {\n\t\t\t\tif !actual.HasAny(expectedCondition) {\n\t\t\t\t\tt.Errorf(\"actual.HasAny(%q): got false, want true\", expectedCondition.Name())\n\t\t\t\t}\n\t\t\t\tif !actual.HasAll(expectedCondition) {\n\t\t\t\t\tt.Errorf(\"actual.HasAll(%q): got false, want true\", expectedCondition.Name())\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tnotExpected := (AllLicenseConditions &^ expected)\n\t\t\tnotExpectedList := notExpected.AsList()\n\t\t\tt.Logf(\"not expected license condition set: %#v %s\", notExpected, notExpected.String())\n\n\t\t\tif len(tt.expected) == 0 {\n\t\t\t\tif actual.HasAny(append(expectedConditions, notExpectedList...)...) {\n\t\t\t\t\tt.Errorf(\"actual.HasAny(all conditions): want false, got true\")\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif !actual.HasAny(append(expectedConditions, notExpectedList...)...) {\n\t\t\t\t\tt.Errorf(\"actual.HasAny(all conditions): want true, got false\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(notExpectedList) == 0 {\n\t\t\t\tif !actual.HasAll(append(expectedConditions, notExpectedList...)...) {\n\t\t\t\t\tt.Errorf(\"actual.HasAll(all conditions): want true, got false\")\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif actual.HasAll(append(expectedConditions, notExpectedList...)...) {\n\t\t\t\t\tt.Errorf(\"actual.HasAll(all conditions): want false, got true\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, unexpectedCondition := range notExpectedList {\n\t\t\t\tif actual.HasAny(unexpectedCondition) {\n\t\t\t\t\tt.Errorf(\"actual.HasAny(%q): got true, want false\", unexpectedCondition.Name())\n\t\t\t\t}\n\t\t\t\tif actual.HasAll(unexpectedCondition) {\n\t\t\t\t\tt.Errorf(\"actual.HasAll(%q): got true, want false\", unexpectedCondition.Name())\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\n\t\tcheckExpectedSet := func(actual LicenseConditionSet, t *testing.T) bool {\n\t\t\tt.Logf(\"checkExpectedSet{%s}\", strings.Join(tt.expected, \", \"))\n\n\t\t\texpectedConditions := toConditions(tt.expected)\n\t\t\texpected := NewLicenseConditionSet(expectedConditions...)\n\n\t\t\tactualNames := actual.Names()\n\n\t\t\tt.Logf(\"actual license condition set: %#v %s\", actual, actual.String())\n\t\t\tt.Logf(\"expected license condition set: %#v %s\", expected, expected.String())\n\n\t\t\tif actual != expected {\n\t\t\t\tt.Errorf(\"checkExpectedSet: got %#v, want %#v\", actual, expected)\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif len(actualNames) != len(tt.expected) {\n\t\t\t\tt.Errorf(\"len(actual.Names()): got %d, want %d\", len(actualNames), len(tt.expected))\n\t\t\t} else {\n\t\t\t\tfor i := 0; i < len(actualNames); i++ {\n\t\t\t\t\tif actualNames[i] != tt.expected[i] {\n\t\t\t\t\t\tt.Errorf(\"actual.Names()[%d]: got %s, want %s\", i, actualNames[i], tt.expected[i])\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tactualConditions := actual.AsList()\n\t\t\tif len(actualConditions) != len(expectedConditions) {\n\t\t\t\tt.Errorf(\"len(actual.AsList()): got %d, want %d\", len(actualConditions), len(expectedConditions))\n\t\t\t} else {\n\t\t\t\tfor i := 0; i < len(actualConditions); i++ {\n\t\t\t\t\tif actualConditions[i] != expectedConditions[i] {\n\t\t\t\t\t\tt.Errorf(\"actual.AsList()[%d}: got %s, want %s\",\n\t\t\t\t\t\t\ti, actualConditions[i].Name(), expectedConditions[i].Name())\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(tt.expected) == 0 {\n\t\t\t\tif !actual.IsEmpty() {\n\t\t\t\t\tt.Errorf(\"actual.IsEmpty(): got false, want true\")\n\t\t\t\t}\n\t\t\t\tif actual.MatchesAnySet(expected) {\n\t\t\t\t\tt.Errorf(\"actual.MatchesAnySet({}): got true, want false\")\n\t\t\t\t}\n\t\t\t\tif actual.MatchesEverySet(expected, expected) {\n\t\t\t\t\tt.Errorf(\"actual.MatchesEverySet({}, {}): want false, got true\")\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif actual.IsEmpty() {\n\t\t\t\t\tt.Errorf(\"actual.IsEmpty(): got true, want false\")\n\t\t\t\t}\n\t\t\t\tif !actual.MatchesAnySet(expected) {\n\t\t\t\t\tt.Errorf(\"actual.MatchesAnySet({all expected}): want true, got false\")\n\t\t\t\t}\n\t\t\t\tif !actual.MatchesEverySet(expected, expected) {\n\t\t\t\t\tt.Errorf(\"actual.MatchesEverySet({all expected}, {all expected}): want true, got false\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tnotExpected := (AllLicenseConditions &^ expected)\n\t\t\tt.Logf(\"not expected license condition set: %#v %s\", notExpected, notExpected.String())\n\n\t\t\tif len(tt.expected) == 0 {\n\t\t\t\tif actual.MatchesAnySet(expected, notExpected) {\n\t\t\t\t\tt.Errorf(\"empty actual.MatchesAnySet({expected}, {not expected}): want false, got true\")\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif !actual.MatchesAnySet(expected, notExpected) {\n\t\t\t\t\tt.Errorf(\"actual.MatchesAnySet({expected}, {not expected}): want true, got false\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif actual.MatchesAnySet(notExpected) {\n\t\t\t\tt.Errorf(\"actual.MatchesAnySet({not expected}): want false, got true\")\n\t\t\t}\n\t\t\tif actual.MatchesEverySet(notExpected) {\n\t\t\t\tt.Errorf(\"actual.MatchesEverySet({not expected}): want false, got true\")\n\t\t\t}\n\t\t\tif actual.MatchesEverySet(expected, notExpected) {\n\t\t\t\tt.Errorf(\"actual.MatchesEverySet({expected}, {not expected}): want false, got true\")\n\t\t\t}\n\n\t\t\tif !actual.Difference(expected).IsEmpty() {\n\t\t\t\tt.Errorf(\"actual.Difference({expected}).IsEmpty(): want true, got false\")\n\t\t\t}\n\t\t\tif expected != actual.Intersection(expected) {\n\t\t\t\tt.Errorf(\"expected == actual.Intersection({expected}): want true, got false (%#v != %#v)\", expected, actual.Intersection(expected))\n\t\t\t}\n\t\t\tif actual != actual.Intersection(expected) {\n\t\t\t\tt.Errorf(\"actual == actual.Intersection({expected}): want true, got false (%#v != %#v)\", actual, actual.Intersection(expected))\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcs := populate()\n\t\t\tif checkExpected(cs, t) {\n\t\t\t\tcheckMatching(cs, t)\n\t\t\t}\n\t\t\tif checkExpectedSet(cs, t) {\n\t\t\t\tcheckMatchingSet(cs, t)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(tt.name+\"_sets\", func(t *testing.T) {\n\t\t\tcs := populateSet()\n\t\t\tif checkExpected(cs, t) {\n\t\t\t\tcheckMatching(cs, t)\n\t\t\t}\n\t\t\tif checkExpectedSet(cs, t) {\n\t\t\t\tcheckMatchingSet(cs, t)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(tt.name+\"_plusset\", func(t *testing.T) {\n\t\t\tcs := populatePlusSet()\n\t\t\tif checkExpected(cs, t) {\n\t\t\t\tcheckMatching(cs, t)\n\t\t\t}\n\t\t\tif checkExpectedSet(cs, t) {\n\t\t\t\tcheckMatchingSet(cs, t)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(tt.name+\"_minusset\", func(t *testing.T) {\n\t\t\tcs := populateMinusSet()\n\t\t\tif checkExpected(cs, t) {\n\t\t\t\tcheckMatching(cs, t)\n\t\t\t}\n\t\t\tif checkExpectedSet(cs, t) {\n\t\t\t\tcheckMatchingSet(cs, t)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/doc.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Much of this content appears too in README.md\n// When changing this file consider whether the change also applies to README.md\n\n/*\n\nPackage compliance provides an approved means for reading, consuming, and\nanalyzing license metadata graphs.\n\nAssuming the license metadata and dependencies are fully and accurately\nrecorded in the build system, any discrepancy between the official policy for\nopen source license compliance and this code is a bug in this code.\n\nA few principal types to understand are LicenseGraph, LicenseCondition, and\nResolutionSet.\n\nLicenseGraph\n------------\n\nA LicenseGraph is an immutable graph of the targets and dependencies reachable\nfrom a specific set of root targets. In general, the root targets will be the\nartifacts in a release or distribution. While conceptually immutable, parts of\nthe graph may be loaded or evaluated lazily.\n\nConceptually, the graph itself will always be a directed acyclic graph. One\nrepresentation is a set of directed edges. Another is a set of nodes with\ndirected edges to their dependencies.\n\nThe edges have annotations, which can distinguish between build tools, runtime\ndependencies, and dependencies like 'contains' that make a derivative work.\n\nLicenseCondition\n----------------\n\nA LicenseCondition is an immutable tuple pairing a condition name with an\noriginating target. e.g. Per current policy, a static library licensed under an\nMIT license would pair a \"notice\" condition with the static library target, and\na dynamic license licensed under GPL would pair a \"restricted\" condition with\nthe dynamic library target.\n\nResolutionSet\n-------------\n\nA ResolutionSet is an immutable set of `AttachesTo`, `ActsOn`, `Resolves`\ntuples describing how license conditions apply to targets.\n\n`AttachesTo` is the trigger for acting. Distribution of the target invokes\nthe policy.\n\n`ActsOn` is the target to share, give notice for, hide etc.\n\n`Resolves` is the set of condition types that the action resolves.\n\nFor most condition types, `ActsOn` will be the target where the condition\noriginated. For example, a notice condition policy means attribution or notice\nmust be given for the target where the condition originates. Likewise, a\nproprietary condition policy means the privacy of the target where the\ncondition originates must be respected. i.e. The thing acted on is the origin.\n\nRestricted conditions are different. The infectious nature of restricted often\nmeans sharing code that is not the target where the restricted condition\noriginates. Linking an MIT library to a GPL library implies a policy to share\nthe MIT library despite the MIT license having no source sharing requirement.\n\nIn this case, one or more resolution tuples will have the MIT license module in\n`ActsOn` and the restricted condition originating at the GPL library module in\n`Resolves`. These tuples will `AttachTo` every target that depends on the GPL\nlibrary because shipping any of those targets trigger the policy to share the\ncode.\n*/\npackage compliance\n"
  },
  {
    "path": "tools/compliance/go.mod",
    "content": "go 1.22\n\nmodule android/soong/tools/compliance\n\nrequire (\n\tgithub.com/google/blueprint v0.0.0\n\tandroid/soong v0.0.0\n\tgoogle.golang.org/protobuf v0.0.0\n\tgithub.com/spdx/tools-golang v0.0.0\n\tgithub.com/google/go-cmp v0.0.0\n)\n"
  },
  {
    "path": "tools/compliance/go.work",
    "content": "go 1.23\n\nuse (\n\t.\n\t../../../../build/blueprint\n\t../../../../build/soong\n\t../../../../external/go-cmp\n\t../../../../external/golang-protobuf\n\t../../../../external/spdx-tools\n)\n\nreplace (\n\tgithub.com/google/blueprint v0.0.0 => ../../../../build/blueprint\n\tandroid/soong v0.0.0 => ../../../../build/soong\n\tgithub.com/google/go-cmp v0.0.0 => ../../../../external/go-cmp\n\tgoogle.golang.org/protobuf v0.0.0 => ../../../../external/golang-protobuf\n\tgithub.com/spdx/tools-golang v0.0.0 => ../../../../external/spdx-tools\n)\n"
  },
  {
    "path": "tools/compliance/graph.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n)\n\n// LicenseGraph describes the immutable license metadata for a set of root\n// targets and the transitive closure of their dependencies.\n//\n// Alternatively, a graph is a set of edges. In this case directed, annotated\n// edges from targets to dependencies.\n//\n// A LicenseGraph provides the frame of reference for all of the other types\n// defined here. It is possible to have multiple graphs, and to have targets,\n// edges, and resolutions from multiple graphs. But it is an error to try to\n// mix items from different graphs in the same operation.\n// May panic if attempted.\n//\n// The compliance package assumes specific private implementations of each of\n// these interfaces. May panic if attempts are made to combine different\n// implementations of some interfaces with expected implementations of other\n// interfaces here.\ntype LicenseGraph struct {\n\t// rootFiles identifies the original set of files to read. (immutable)\n\t//\n\t// Defines the starting \"top\" for top-down walks.\n\t//\n\t// Alternatively, an instance of licenseGraphImp conceptually defines a scope within\n\t// the universe of build graphs as a sub-graph rooted at rootFiles where all edges\n\t// and targets for the instance are defined relative to and within that scope. For\n\t// most analyses, the correct scope is to root the graph at all of the distributed\n\t// artifacts.\n\trootFiles []string\n\n\t// edges lists the directed edges in the graph from target to dependency. (guarded by mu)\n\t//\n\t// Alternatively, the graph is the set of `edges`.\n\tedges TargetEdgeList\n\n\t// targets identifies, indexes, and describes the entire set of target node files.\n\t/// (guarded by mu)\n\ttargets map[string]*TargetNode\n\n\t// onceBottomUp makes sure the bottom-up resolve walk only happens one time.\n\tonceBottomUp sync.Once\n\n\t// onceTopDown makes sure the top-down resolve walk only happens one time.\n\tonceTopDown sync.Once\n\n\t// shippedNodes caches the results of a full walk of nodes identifying targets\n\t// distributed either directly or as derivative works. (creation guarded by mu)\n\tshippedNodes *TargetNodeSet\n\n\t// mu guards against concurrent update.\n\tmu sync.Mutex\n}\n\n// Edges returns the list of edges in the graph. (unordered)\nfunc (lg *LicenseGraph) Edges() TargetEdgeList {\n\tedges := make(TargetEdgeList, 0, len(lg.edges))\n\tedges = append(edges, lg.edges...)\n\treturn edges\n}\n\n// Targets returns the list of target nodes in the graph. (unordered)\nfunc (lg *LicenseGraph) Targets() TargetNodeList {\n\ttargets := make(TargetNodeList, 0, len(lg.targets))\n\tfor _, target := range lg.targets {\n\t\ttargets = append(targets, target)\n\t}\n\treturn targets\n}\n\n// TargetNames returns the list of target node names in the graph. (unordered)\nfunc (lg *LicenseGraph) TargetNames() []string {\n\ttargets := make([]string, 0, len(lg.targets))\n\tfor target := range lg.targets {\n\t\ttargets = append(targets, target)\n\t}\n\treturn targets\n}\n\n// compliance-only LicenseGraph methods\n\n// newLicenseGraph constructs a new, empty instance of LicenseGraph.\nfunc newLicenseGraph() *LicenseGraph {\n\treturn &LicenseGraph{\n\t\trootFiles: []string{},\n\t\ttargets:   make(map[string]*TargetNode),\n\t}\n}\n\n// TargetEdge describes a directed, annotated edge from a target to a\n// dependency. (immutable)\n//\n// A LicenseGraph, above, is a set of TargetEdges.\n//\n// i.e. `Target` depends on `Dependency` in the manner described by\n// `Annotations`.\ntype TargetEdge struct {\n\t// target and dependency identify the nodes connected by the edge.\n\ttarget, dependency *TargetNode\n\n\t// annotations identifies the set of compliance-relevant annotations describing the edge.\n\tannotations TargetEdgeAnnotations\n}\n\n// Target identifies the target that depends on the dependency.\n//\n// Target needs Dependency to build.\nfunc (e *TargetEdge) Target() *TargetNode {\n\treturn e.target\n}\n\n// Dependency identifies the target depended on by the target.\n//\n// Dependency builds without Target, but Target needs Dependency to build.\nfunc (e *TargetEdge) Dependency() *TargetNode {\n\treturn e.dependency\n}\n\n// Annotations describes the type of edge by the set of annotations attached to\n// it.\n//\n// Only annotations prescribed by policy have any meaning for licensing, and\n// the meaning for licensing is likewise prescribed by policy. Other annotations\n// are preserved and ignored by policy.\nfunc (e *TargetEdge) Annotations() TargetEdgeAnnotations {\n\treturn e.annotations\n}\n\n// IsRuntimeDependency returns true for edges representing shared libraries\n// linked dynamically at runtime.\nfunc (e *TargetEdge) IsRuntimeDependency() bool {\n\treturn edgeIsDynamicLink(e)\n}\n\n// IsDerivation returns true for edges where the target is a derivative\n// work of dependency.\nfunc (e *TargetEdge) IsDerivation() bool {\n\treturn edgeIsDerivation(e)\n}\n\n// IsBuildTool returns true for edges where the target is built\n// by dependency.\nfunc (e *TargetEdge) IsBuildTool() bool {\n\treturn !edgeIsDerivation(e) && !edgeIsDynamicLink(e)\n}\n\n// String returns a human-readable string representation of the edge.\nfunc (e *TargetEdge) String() string {\n\treturn fmt.Sprintf(\"%s -[%s]> %s\", e.target.name, strings.Join(e.annotations.AsList(), \", \"), e.dependency.name)\n}\n\n// TargetEdgeList orders lists of edges by target then dependency then annotations.\ntype TargetEdgeList []*TargetEdge\n\n// Len returns the count of the elmements in the list.\nfunc (l TargetEdgeList) Len() int { return len(l) }\n\n// Swap rearranges 2 elements so that each occupies the other's former position.\nfunc (l TargetEdgeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }\n\n// Less returns true when the `i`th element is lexicographically less than the `j`th.\nfunc (l TargetEdgeList) Less(i, j int) bool {\n\tnamei := l[i].target.name\n\tnamej := l[j].target.name\n\tif namei == namej {\n\t\tnamei = l[i].dependency.name\n\t\tnamej = l[j].dependency.name\n\t}\n\tif namei == namej {\n\t\treturn l[i].annotations.Compare(l[j].annotations) < 0\n\t}\n\treturn namei < namej\n}\n\n// TargetEdgePathSegment describes a single arc in a TargetPath associating the\n// edge with a context `ctx` defined by whatever process is creating the path.\ntype TargetEdgePathSegment struct {\n\tedge *TargetEdge\n\tctx  interface{}\n}\n\n// Target identifies the target that depends on the dependency.\n//\n// Target needs Dependency to build.\nfunc (s TargetEdgePathSegment) Target() *TargetNode {\n\treturn s.edge.target\n}\n\n// Dependency identifies the target depended on by the target.\n//\n// Dependency builds without Target, but Target needs Dependency to build.\nfunc (s TargetEdgePathSegment) Dependency() *TargetNode {\n\treturn s.edge.dependency\n}\n\n// Edge describes the target edge.\nfunc (s TargetEdgePathSegment) Edge() *TargetEdge {\n\treturn s.edge\n}\n\n// Annotations describes the type of edge by the set of annotations attached to\n// it.\n//\n// Only annotations prescribed by policy have any meaning for licensing, and\n// the meaning for licensing is likewise prescribed by policy. Other annotations\n// are preserved and ignored by policy.\nfunc (s TargetEdgePathSegment) Annotations() TargetEdgeAnnotations {\n\treturn s.edge.annotations\n}\n\n// Context returns the context associated with the path segment. The type and\n// value of the context defined by the process creating the path.\nfunc (s TargetEdgePathSegment) Context() interface{} {\n\treturn s.ctx\n}\n\n// String returns a human-readable string representation of the edge.\nfunc (s TargetEdgePathSegment) String() string {\n\treturn fmt.Sprintf(\"%s -[%s]> %s\", s.edge.target.name, strings.Join(s.edge.annotations.AsList(), \", \"), s.edge.dependency.name)\n}\n\n// TargetEdgePath describes a sequence of edges starting at a root and ending\n// at some final dependency.\ntype TargetEdgePath []TargetEdgePathSegment\n\n// NewTargetEdgePath creates a new, empty path with capacity `cap`.\nfunc NewTargetEdgePath(cap int) *TargetEdgePath {\n\tp := make(TargetEdgePath, 0, cap)\n\treturn &p\n}\n\n// Push appends a new edge to the list verifying that the target of the new\n// edge is the dependency of the prior.\nfunc (p *TargetEdgePath) Push(edge *TargetEdge, ctx interface{}) {\n\tif len(*p) == 0 {\n\t\t*p = append(*p, TargetEdgePathSegment{edge, ctx})\n\t\treturn\n\t}\n\tif (*p)[len(*p)-1].edge.dependency != edge.target {\n\t\tpanic(fmt.Errorf(\"disjoint path %s does not end at %s\", p.String(), edge.target.name))\n\t}\n\t*p = append(*p, TargetEdgePathSegment{edge, ctx})\n}\n\n// Pop shortens the path by 1 edge.\nfunc (p *TargetEdgePath) Pop() {\n\tif len(*p) == 0 {\n\t\tpanic(fmt.Errorf(\"attempt to remove edge from empty path\"))\n\t}\n\t*p = (*p)[:len(*p)-1]\n}\n\n// Clear makes the path length 0.\nfunc (p *TargetEdgePath) Clear() {\n\t*p = (*p)[:0]\n}\n\n// Copy makes a new path with the same value.\nfunc (p *TargetEdgePath) Copy() *TargetEdgePath {\n\tresult := make(TargetEdgePath, 0, len(*p))\n\tfor _, e := range *p {\n\t\tresult = append(result, e)\n\t}\n\treturn &result\n}\n\n// String returns a string representation of the path: [n1 -> n2 -> ... -> nn].\nfunc (p *TargetEdgePath) String() string {\n\tif p == nil {\n\t\treturn \"nil\"\n\t}\n\tif len(*p) == 0 {\n\t\treturn \"[]\"\n\t}\n\tvar sb strings.Builder\n\tfmt.Fprintf(&sb, \"[\")\n\tfor _, s := range *p {\n\t\tfmt.Fprintf(&sb, \"%s -> \", s.edge.target.name)\n\t}\n\tlastSegment := (*p)[len(*p)-1]\n\tfmt.Fprintf(&sb, \"%s]\", lastSegment.edge.dependency.name)\n\treturn sb.String()\n}\n\n// TargetNode describes a module or target identified by the name of a specific\n// metadata file. (immutable)\n//\n// Each metadata file corresponds to a Soong module or to a Make target.\n//\n// A target node can appear as the target or as the dependency in edges.\n// Most target nodes appear as both target in one edge and as dependency in\n// other edges.\ntype TargetNode targetNode\n\n// Name returns the string that identifies the target node.\n// i.e. path to license metadata file\nfunc (tn *TargetNode) Name() string {\n\treturn tn.name\n}\n\n// Dependencies returns the list of edges to dependencies of `tn`.\nfunc (tn *TargetNode) Dependencies() TargetEdgeList {\n\tedges := make(TargetEdgeList, 0, len(tn.edges))\n\tedges = append(edges, tn.edges...)\n\treturn edges\n}\n\n// PackageName returns the string that identifes the package for the target.\nfunc (tn *TargetNode) PackageName() string {\n\treturn tn.proto.GetPackageName()\n}\n\n// ModuleName returns the module name of the target.\nfunc (tn *TargetNode) ModuleName() string {\n\treturn tn.proto.GetModuleName()\n}\n\n// Projects returns the projects defining the target node. (unordered)\n//\n// In an ideal world, only 1 project defines a target, but the interaction\n// between Soong and Make for a variety of architectures and for host versus\n// product means a module is sometimes defined more than once.\nfunc (tn *TargetNode) Projects() []string {\n\treturn append([]string{}, tn.proto.Projects...)\n}\n\n// LicenseConditions returns a copy of the set of license conditions\n// originating at the target. The values that appear and how each is resolved\n// is a matter of policy. (unordered)\n//\n// e.g. notice or proprietary\nfunc (tn *TargetNode) LicenseConditions() LicenseConditionSet {\n\treturn tn.licenseConditions\n}\n\n// LicenseTexts returns the paths to the files containing the license texts for\n// the target. (unordered)\nfunc (tn *TargetNode) LicenseTexts() []string {\n\treturn append([]string{}, tn.proto.LicenseTexts...)\n}\n\n// IsContainer returns true if the target represents a container that merely\n// aggregates other targets.\nfunc (tn *TargetNode) IsContainer() bool {\n\treturn tn.proto.GetIsContainer()\n}\n\n// Built returns the list of files built by the module or target. (unordered)\nfunc (tn *TargetNode) Built() []string {\n\treturn append([]string{}, tn.proto.Built...)\n}\n\n// Installed returns the list of files installed by the module or target.\n// (unordered)\nfunc (tn *TargetNode) Installed() []string {\n\treturn append([]string{}, tn.proto.Installed...)\n}\n\n// TargetFiles returns the list of files built or installed by the module or\n// target. (unordered)\nfunc (tn *TargetNode) TargetFiles() []string {\n\treturn append(tn.proto.Built, tn.proto.Installed...)\n}\n\n// InstallMap returns the list of path name transformations to make to move\n// files from their original location in the file system to their destination\n// inside a container. (unordered)\nfunc (tn *TargetNode) InstallMap() []InstallMap {\n\tresult := make([]InstallMap, 0, len(tn.proto.InstallMap))\n\tfor _, im := range tn.proto.InstallMap {\n\t\tresult = append(result, InstallMap{im.GetFromPath(), im.GetContainerPath()})\n\t}\n\treturn result\n}\n\n// Sources returns the list of file names depended on by the target, which may\n// be a proper subset of those made available by dependency modules.\n// (unordered)\nfunc (tn *TargetNode) Sources() []string {\n\treturn append([]string{}, tn.proto.Sources...)\n}\n\n// InstallMap describes the mapping from an input filesystem file to file in a\n// container.\ntype InstallMap struct {\n\t// FromPath is the input path on the filesystem.\n\tFromPath string\n\n\t// ContainerPath is the path to the same file inside the container or\n\t// installed location.\n\tContainerPath string\n}\n\n// TargetEdgeAnnotations describes an immutable set of annotations attached to\n// an edge from a target to a dependency.\n//\n// Annotations typically distinguish between static linkage versus dynamic\n// versus tools that are used at build time but are not linked in any way.\ntype TargetEdgeAnnotations struct {\n\tannotations map[string]struct{}\n}\n\n// newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.\nfunc newEdgeAnnotations() TargetEdgeAnnotations {\n\treturn TargetEdgeAnnotations{make(map[string]struct{})}\n}\n\n// HasAnnotation returns true if an annotation `ann` is in the set.\nfunc (ea TargetEdgeAnnotations) HasAnnotation(ann string) bool {\n\t_, ok := ea.annotations[ann]\n\treturn ok\n}\n\n// Compare orders TargetAnnotations returning:\n// -1 when ea < other,\n// +1 when ea > other, and\n// 0 when ea == other.\nfunc (ea TargetEdgeAnnotations) Compare(other TargetEdgeAnnotations) int {\n\ta1 := ea.AsList()\n\ta2 := other.AsList()\n\tsort.Strings(a1)\n\tsort.Strings(a2)\n\tfor k := 0; k < len(a1) && k < len(a2); k++ {\n\t\tif a1[k] < a2[k] {\n\t\t\treturn -1\n\t\t}\n\t\tif a1[k] > a2[k] {\n\t\t\treturn 1\n\t\t}\n\t}\n\tif len(a1) < len(a2) {\n\t\treturn -1\n\t}\n\tif len(a1) > len(a2) {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\n// AsList returns the list of annotation names attached to the edge.\n// (unordered)\nfunc (ea TargetEdgeAnnotations) AsList() []string {\n\tl := make([]string, 0, len(ea.annotations))\n\tfor ann := range ea.annotations {\n\t\tl = append(l, ann)\n\t}\n\treturn l\n}\n\n// TargetNodeSet describes a set of distinct nodes in a license graph.\ntype TargetNodeSet map[*TargetNode]struct{}\n\n// Contains returns true when `target` is an element of the set.\nfunc (ts TargetNodeSet) Contains(target *TargetNode) bool {\n\t_, isPresent := ts[target]\n\treturn isPresent\n}\n\n// Names returns the array of target node namess in the set. (unordered)\nfunc (ts TargetNodeSet) Names() []string {\n\tresult := make([]string, 0, len(ts))\n\tfor tn := range ts {\n\t\tresult = append(result, tn.name)\n\t}\n\treturn result\n}\n\n// String returns a human-readable string representation of the set.\nfunc (ts TargetNodeSet) String() string {\n\treturn fmt.Sprintf(\"{%s}\", strings.Join(ts.Names(), \", \"))\n}\n\n// TargetNodeList orders a list of targets by name.\ntype TargetNodeList []*TargetNode\n\n// Len returns the count of elements in the list.\nfunc (l TargetNodeList) Len() int { return len(l) }\n\n// Swap rearranges 2 elements so that each occupies the other's former position.\nfunc (l TargetNodeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }\n\n// Less returns true when the `i`th element is lexicographicallt less than the `j`th.\nfunc (l TargetNodeList) Less(i, j int) bool {\n\treturn l[i].name < l[j].name\n}\n\n// String returns a string representation of the list.\nfunc (l TargetNodeList) String() string {\n\tvar sb strings.Builder\n\tfmt.Fprintf(&sb, \"[\")\n\tsep := \"\"\n\tfor _, tn := range l {\n\t\tfmt.Fprintf(&sb, \"%s%s\", sep, tn.name)\n\t\tsep = \" \"\n\t}\n\tfmt.Fprintf(&sb, \"]\")\n\treturn sb.String()\n}\n\n// Names returns an array the names of the nodes in the same order as the nodes in the list.\nfunc (l TargetNodeList) Names() []string {\n\tresult := make([]string, 0, len(l))\n\tfor _, tn := range l {\n\t\tresult = append(result, tn.name)\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "tools/compliance/noticeindex.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"crypto/md5\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net/url\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"android/soong/tools/compliance/projectmetadata\"\n)\n\nvar (\n\tlicensesPathRegexp = regexp.MustCompile(`licen[cs]es?/`)\n)\n\n// NoticeIndex transforms license metadata into license text hashes, library\n// names, and install paths indexing them for fast lookup/iteration.\ntype NoticeIndex struct {\n\t// lg identifies the license graph to which the index applies.\n\tlg *LicenseGraph\n\t// pmix indexes project metadata\n\tpmix *projectmetadata.Index\n\t// rs identifies the set of resolutions upon which the index is based.\n\trs ResolutionSet\n\t// shipped identifies the set of target nodes shipped directly or as derivative works.\n\tshipped TargetNodeSet\n\t// rootFS locates the root of the file system from which to read the files.\n\trootFS fs.FS\n\t// hash maps license text filenames to content hashes\n\thash map[string]hash\n\t// text maps content hashes to content\n\ttext map[hash][]byte\n\t// hashLibInstall maps hashes to libraries to install paths.\n\thashLibInstall map[hash]map[string]map[string]struct{}\n\t// installHashLib maps install paths to libraries to hashes.\n\tinstallHashLib map[string]map[hash]map[string]struct{}\n\t// libHash maps libraries to hashes.\n\tlibHash map[string]map[hash]struct{}\n\t// targetHash maps target nodes to hashes.\n\ttargetHashes map[*TargetNode]map[hash]struct{}\n\t// projectName maps project directory names to project name text.\n\tprojectName map[string]string\n\t// files lists all the files accessed during indexing\n\tfiles []string\n}\n\n// IndexLicenseTexts creates a hashed index of license texts for `lg` and `rs`\n// using the files rooted at `rootFS`.\nfunc IndexLicenseTexts(rootFS fs.FS, lg *LicenseGraph, rs ResolutionSet) (*NoticeIndex, error) {\n\tif rs == nil {\n\t\trs = ResolveNotices(lg)\n\t}\n\tni := &NoticeIndex{\n\t\tlg:             lg,\n\t\tpmix:           projectmetadata.NewIndex(rootFS),\n\t\trs:             rs,\n\t\tshipped:        ShippedNodes(lg),\n\t\trootFS:         rootFS,\n\t\thash:           make(map[string]hash),\n\t\ttext:           make(map[hash][]byte),\n\t\thashLibInstall: make(map[hash]map[string]map[string]struct{}),\n\t\tinstallHashLib: make(map[string]map[hash]map[string]struct{}),\n\t\tlibHash:        make(map[string]map[hash]struct{}),\n\t\ttargetHashes:   make(map[*TargetNode]map[hash]struct{}),\n\t\tprojectName:    make(map[string]string),\n\t}\n\n\t// index adds all license texts for `tn` to the index.\n\tindex := func(tn *TargetNode) (map[hash]struct{}, error) {\n\t\tif hashes, ok := ni.targetHashes[tn]; ok {\n\t\t\treturn hashes, nil\n\t\t}\n\t\thashes := make(map[hash]struct{})\n\t\tfor _, text := range tn.LicenseTexts() {\n\t\t\tfname := strings.SplitN(text, \":\", 2)[0]\n\t\t\tif _, ok := ni.hash[fname]; !ok {\n\t\t\t\terr := ni.addText(fname)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\thash := ni.hash[fname]\n\t\t\tif _, ok := hashes[hash]; !ok {\n\t\t\t\thashes[hash] = struct{}{}\n\t\t\t}\n\t\t}\n\t\tni.targetHashes[tn] = hashes\n\t\treturn hashes, nil\n\t}\n\n\tlink := func(tn *TargetNode, hashes map[hash]struct{}, installPaths []string) error {\n\t\tfor h := range hashes {\n\t\t\tlibName, err := ni.getLibName(tn, h)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, ok := ni.libHash[libName]; !ok {\n\t\t\t\tni.libHash[libName] = make(map[hash]struct{})\n\t\t\t}\n\t\t\tif _, ok := ni.hashLibInstall[h]; !ok {\n\t\t\t\tni.hashLibInstall[h] = make(map[string]map[string]struct{})\n\t\t\t}\n\t\t\tif _, ok := ni.libHash[libName][h]; !ok {\n\t\t\t\tni.libHash[libName][h] = struct{}{}\n\t\t\t}\n\t\t\tfor _, installPath := range installPaths {\n\t\t\t\tif _, ok := ni.installHashLib[installPath]; !ok {\n\t\t\t\t\tni.installHashLib[installPath] = make(map[hash]map[string]struct{})\n\t\t\t\t\tni.installHashLib[installPath][h] = make(map[string]struct{})\n\t\t\t\t\tni.installHashLib[installPath][h][libName] = struct{}{}\n\t\t\t\t} else if _, ok = ni.installHashLib[installPath][h]; !ok {\n\t\t\t\t\tni.installHashLib[installPath][h] = make(map[string]struct{})\n\t\t\t\t\tni.installHashLib[installPath][h][libName] = struct{}{}\n\t\t\t\t} else if _, ok = ni.installHashLib[installPath][h][libName]; !ok {\n\t\t\t\t\tni.installHashLib[installPath][h][libName] = struct{}{}\n\t\t\t\t}\n\t\t\t\tif _, ok := ni.hashLibInstall[h]; !ok {\n\t\t\t\t\tni.hashLibInstall[h] = make(map[string]map[string]struct{})\n\t\t\t\t\tni.hashLibInstall[h][libName] = make(map[string]struct{})\n\t\t\t\t\tni.hashLibInstall[h][libName][installPath] = struct{}{}\n\t\t\t\t} else if _, ok = ni.hashLibInstall[h][libName]; !ok {\n\t\t\t\t\tni.hashLibInstall[h][libName] = make(map[string]struct{})\n\t\t\t\t\tni.hashLibInstall[h][libName][installPath] = struct{}{}\n\t\t\t\t} else if _, ok = ni.hashLibInstall[h][libName][installPath]; !ok {\n\t\t\t\t\tni.hashLibInstall[h][libName][installPath] = struct{}{}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tcacheMetadata := func(tn *TargetNode) {\n\t\tni.pmix.MetadataForProjects(tn.Projects()...)\n\t}\n\n\t// returns error from walk below.\n\tvar err error\n\n\tWalkTopDown(NoEdgeContext{}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tif !ni.shipped.Contains(tn) {\n\t\t\treturn false\n\t\t}\n\t\tgo cacheMetadata(tn)\n\t\tinstallPaths := getInstallPaths(tn, path)\n\t\tvar hashes map[hash]struct{}\n\t\thashes, err = index(tn)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\terr = link(tn, hashes, installPaths)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tif tn.IsContainer() {\n\t\t\treturn true\n\t\t}\n\n\t\tfor _, r := range rs.Resolutions(tn) {\n\t\t\thashes, err = index(r.actsOn)\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\terr = link(r.actsOn, hashes, installPaths)\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn false\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ni, nil\n}\n\n// Hashes returns an ordered channel of the hashed license texts.\nfunc (ni *NoticeIndex) Hashes() chan hash {\n\tc := make(chan hash)\n\tgo func() {\n\t\tlibs := make([]string, 0, len(ni.libHash))\n\t\tfor libName := range ni.libHash {\n\t\t\tlibs = append(libs, libName)\n\t\t}\n\t\tsort.Strings(libs)\n\t\thashes := make(map[hash]struct{})\n\t\tfor _, libName := range libs {\n\t\t\thl := make([]hash, 0, len(ni.libHash[libName]))\n\t\t\tfor h := range ni.libHash[libName] {\n\t\t\t\tif _, ok := hashes[h]; ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\thashes[h] = struct{}{}\n\t\t\t\thl = append(hl, h)\n\t\t\t}\n\t\t\tif len(hl) > 0 {\n\t\t\t\tsort.Sort(hashList{ni, libName, \"\", &hl})\n\t\t\t\tfor _, h := range hl {\n\t\t\t\t\tc <- h\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tclose(c)\n\t}()\n\treturn c\n\n}\n\n// InputFiles returns the complete list of files read during indexing.\nfunc (ni *NoticeIndex) InputFiles() []string {\n\tprojectMeta := ni.pmix.AllMetadataFiles()\n\tfiles := make([]string, 0, len(ni.files) + len(ni.lg.targets) + len(projectMeta))\n\tfiles = append(files, ni.files...)\n\tfor f := range ni.lg.targets {\n\t\tfiles = append(files, f)\n\t}\n\tfiles = append(files, projectMeta...)\n\treturn files\n}\n\n// HashLibs returns the ordered array of library names using the license text\n// hashed as `h`.\nfunc (ni *NoticeIndex) HashLibs(h hash) []string {\n\tlibs := make([]string, 0, len(ni.hashLibInstall[h]))\n\tfor libName := range ni.hashLibInstall[h] {\n\t\tlibs = append(libs, libName)\n\t}\n\tsort.Strings(libs)\n\treturn libs\n}\n\n// HashLibInstalls returns the ordered array of install paths referencing\n// library `libName` using the license text hashed as `h`.\nfunc (ni *NoticeIndex) HashLibInstalls(h hash, libName string) []string {\n\tinstalls := make([]string, 0, len(ni.hashLibInstall[h][libName]))\n\tfor installPath := range ni.hashLibInstall[h][libName] {\n\t\tinstalls = append(installs, installPath)\n\t}\n\tsort.Strings(installs)\n\treturn installs\n}\n\n// InstallPaths returns the ordered channel of indexed install paths.\nfunc (ni *NoticeIndex) InstallPaths() chan string {\n\tc := make(chan string)\n\tgo func() {\n\t\tpaths := make([]string, 0, len(ni.installHashLib))\n\t\tfor path := range ni.installHashLib {\n\t\t\tpaths = append(paths, path)\n\t\t}\n\t\tsort.Strings(paths)\n\t\tfor _, installPath := range paths {\n\t\t\tc <- installPath\n\t\t}\n\t\tclose(c)\n\t}()\n\treturn c\n}\n\n// InstallHashes returns the ordered array of hashes attached to `installPath`.\nfunc (ni *NoticeIndex) InstallHashes(installPath string) []hash {\n\tresult := make([]hash, 0, len(ni.installHashLib[installPath]))\n\tfor h := range ni.installHashLib[installPath] {\n\t\tresult = append(result, h)\n\t}\n\tif len(result) > 0 {\n\t\tsort.Sort(hashList{ni, \"\", installPath, &result})\n\t}\n\treturn result\n}\n\n// InstallHashLibs returns the ordered array of library names attached to\n// `installPath` as hash `h`.\nfunc (ni *NoticeIndex) InstallHashLibs(installPath string, h hash) []string {\n\tresult := make([]string, 0, len(ni.installHashLib[installPath][h]))\n\tfor libName := range ni.installHashLib[installPath][h] {\n\t\tresult = append(result, libName)\n\t}\n\tsort.Strings(result)\n\treturn result\n}\n\n// Libraries returns the ordered channel of indexed library names.\nfunc (ni *NoticeIndex) Libraries() chan string {\n\tc := make(chan string)\n\tgo func() {\n\t\tlibs := make([]string, 0, len(ni.libHash))\n\t\tfor lib := range ni.libHash {\n\t\t\tlibs = append(libs, lib)\n\t\t}\n\t\tsort.Strings(libs)\n\t\tfor _, lib := range libs {\n\t\t\tc <- lib\n\t\t}\n\t\tclose(c)\n\t}()\n\treturn c\n}\n\n// HashText returns the file content of the license text hashed as `h`.\nfunc (ni *NoticeIndex) HashText(h hash) []byte {\n\treturn ni.text[h]\n}\n\n// getLibName returns the name of the library associated with `noticeFor`.\nfunc (ni *NoticeIndex) getLibName(noticeFor *TargetNode, h hash) (string, error) {\n\tfor _, text := range noticeFor.LicenseTexts() {\n\t\tif !strings.Contains(text, \":\") {\n\t\t\tif ni.hash[text].key != h.key {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tln, err := ni.checkMetadataForLicenseText(noticeFor, text)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\tif len(ln) > 0 {\n\t\t\t\treturn ln, nil\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tfields := strings.SplitN(text, \":\", 2)\n\t\tfname, pname := fields[0], fields[1]\n\t\tif ni.hash[fname].key != h.key {\n\t\t\tcontinue\n\t\t}\n\n\t\tln, err := url.QueryUnescape(pname)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\treturn ln, nil\n\t}\n\t// use name from METADATA if available\n\tln, err := ni.checkMetadata(noticeFor)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(ln) > 0 {\n\t\treturn ln, nil\n\t}\n\t// use package_name: from license{} module if available\n\tpn := noticeFor.PackageName()\n\tif len(pn) > 0 {\n\t\treturn pn, nil\n\t}\n\tfor _, p := range noticeFor.Projects() {\n\t\tif strings.HasPrefix(p, \"prebuilts/\") {\n\t\t\tfor _, licenseText := range noticeFor.LicenseTexts() {\n\t\t\t\tif !strings.HasPrefix(licenseText, \"prebuilts/\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !strings.Contains(licenseText, \":\") {\n\t\t\t\t\tif ni.hash[licenseText].key != h.key {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfields := strings.SplitN(licenseText, \":\", 2)\n\t\t\t\t\tfname := fields[0]\n\t\t\t\t\tif ni.hash[fname].key != h.key {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor _, safePrebuiltPrefix := range safePrebuiltPrefixes {\n\t\t\t\t\tmatch := safePrebuiltPrefix.re.FindString(licenseText)\n\t\t\t\t\tif len(match) == 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif safePrebuiltPrefix.strip {\n\t\t\t\t\t\t// strip entire prefix\n\t\t\t\t\t\tmatch = licenseText[len(match):]\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// strip from prebuilts/ until safe prefix\n\t\t\t\t\t\tmatch = licenseText[len(match)-len(safePrebuiltPrefix.prefix):]\n\t\t\t\t\t}\n\t\t\t\t\t// remove LICENSE or NOTICE or other filename\n\t\t\t\t\tli := strings.LastIndex(match, \"/\")\n\t\t\t\t\tif li > 0 {\n\t\t\t\t\t\tmatch = match[:li]\n\t\t\t\t\t}\n\t\t\t\t\t// remove *licenses/ path segment and subdirectory if in path\n\t\t\t\t\tif offsets := licensesPathRegexp.FindAllStringIndex(match, -1); offsets != nil && offsets[len(offsets)-1][0] > 0 {\n\t\t\t\t\t\tmatch = match[:offsets[len(offsets)-1][0]]\n\t\t\t\t\t\tli = strings.LastIndex(match, \"/\")\n\t\t\t\t\t\tif li > 0 {\n\t\t\t\t\t\t\tmatch = match[:li]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn match, nil\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tfor _, safePathPrefix := range safePathPrefixes {\n\t\t\tif strings.HasPrefix(p, safePathPrefix.prefix) {\n\t\t\t\tif safePathPrefix.strip {\n\t\t\t\t\treturn p[len(safePathPrefix.prefix):], nil\n\t\t\t\t} else {\n\t\t\t\t\treturn p, nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// strip off [./]meta_lic from license metadata path and extract base name\n\tn := noticeFor.name[:len(noticeFor.name)-9]\n\tli := strings.LastIndex(n, \"/\")\n\tif li > 0 {\n\t\tn = n[li+1:]\n\t}\n\tfi := strings.Index(n, \"@\")\n\tif fi > 0 {\n\t\tn = n[:fi]\n\t}\n\treturn n, nil\n}\n\n// checkMetadata tries to look up a library name from a METADATA file associated with `noticeFor`.\nfunc (ni *NoticeIndex) checkMetadata(noticeFor *TargetNode) (string, error) {\n\tpms, err := ni.pmix.MetadataForProjects(noticeFor.Projects()...)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tfor _, pm := range pms {\n\t\tname := pm.VersionedName()\n\t\tif name != \"\" {\n\t\t\treturn name, nil\n\t\t}\n\t}\n\treturn \"\", nil\n}\n\n// checkMetadataForLicenseText\nfunc (ni *NoticeIndex) checkMetadataForLicenseText(noticeFor *TargetNode, licenseText string) (string, error) {\n\tp := \"\"\n\tfor _, proj := range noticeFor.Projects() {\n\t\tif strings.HasPrefix(licenseText, proj) {\n\t\t\tp = proj\n\t\t}\n\t}\n\tif len(p) == 0 {\n\t\tp = filepath.Dir(licenseText)\n\t\tfor {\n\t\t\tfi, err := fs.Stat(ni.rootFS, filepath.Join(p, \".git\"))\n\t\t\tif err == nil && fi.IsDir() {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif strings.Contains(p, \"/\") && p != \"/\" {\n\t\t\t\tp = filepath.Dir(p)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn \"\", nil\n\t\t}\n\t}\n\tpms, err := ni.pmix.MetadataForProjects(p)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif pms == nil {\n\t\treturn \"\", nil\n\t}\n\treturn pms[0].VersionedName(), nil\n}\n\n// addText reads and indexes the content of a license text file.\nfunc (ni *NoticeIndex) addText(file string) error {\n\tf, err := ni.rootFS.Open(filepath.Clean(file))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error opening license text file %q: %w\", file, err)\n\t}\n\n\t// read the file\n\ttext, err := io.ReadAll(f)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error reading license text file %q: %w\", file, err)\n\t}\n\n\thash := hash{fmt.Sprintf(\"%x\", md5.Sum(text))}\n\tni.hash[file] = hash\n\tif _, alreadyPresent := ni.text[hash]; !alreadyPresent {\n\t\tni.text[hash] = text\n\t}\n\n\tni.files = append(ni.files, file)\n\n\treturn nil\n}\n\n// getInstallPaths returns the names of the used dependencies mapped to their\n// installed locations.\nfunc getInstallPaths(attachesTo *TargetNode, path TargetEdgePath) []string {\n\tif len(path) == 0 {\n\t\tinstalls := attachesTo.Installed()\n\t\tif 0 == len(installs) {\n\t\t\tinstalls = attachesTo.Built()\n\t\t}\n\t\treturn installs\n\t}\n\n\tvar getInstalls func(path TargetEdgePath) []string\n\n\tgetInstalls = func(path TargetEdgePath) []string {\n\t\t// deps contains the output targets from the dependencies in the path\n\t\tvar deps []string\n\t\tif len(path) > 1 {\n\t\t\t// recursively get the targets from the sub-path skipping 1 path segment\n\t\t\tdeps = getInstalls(path[1:])\n\t\t} else {\n\t\t\t// stop recursion at 1 path segment\n\t\t\tdeps = path[0].Dependency().TargetFiles()\n\t\t}\n\t\tsize := 0\n\t\tprefixes := path[0].Target().TargetFiles()\n\t\tinstallMap := path[0].Target().InstallMap()\n\t\tsources := path[0].Target().Sources()\n\t\tfor _, dep := range deps {\n\t\t\tfound := false\n\t\t\tfor _, source := range sources {\n\t\t\t\tif strings.HasPrefix(dep, source) {\n\t\t\t\t\tfound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !found {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, im := range installMap {\n\t\t\t\tif strings.HasPrefix(dep, im.FromPath) {\n\t\t\t\t\tsize += len(prefixes)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinstalls := make([]string, 0, size)\n\t\tfor _, dep := range deps {\n\t\t\tfound := false\n\t\t\tfor _, source := range sources {\n\t\t\t\tif strings.HasPrefix(dep, source) {\n\t\t\t\t\tfound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !found {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, im := range installMap {\n\t\t\t\tif strings.HasPrefix(dep, im.FromPath) {\n\t\t\t\t\tfor _, prefix := range prefixes {\n\t\t\t\t\t\tinstalls = append(installs, prefix+im.ContainerPath+dep[len(im.FromPath):])\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn installs\n\t}\n\tallInstalls := getInstalls(path)\n\tinstalls := path[0].Target().Installed()\n\tif len(installs) == 0 {\n\t\treturn allInstalls\n\t}\n\tresult := make([]string, 0, len(allInstalls))\n\tfor _, install := range allInstalls {\n\t\tfor _, prefix := range installs {\n\t\t\tif strings.HasPrefix(install, prefix) {\n\t\t\t\tresult = append(result, install)\n\t\t\t}\n\t\t}\n\t}\n\treturn result\n}\n\n// hash is an opaque string derived from md5sum.\ntype hash struct {\n\tkey string\n}\n\n// String returns the hexadecimal representation of the hash.\nfunc (h hash) String() string {\n\treturn h.key\n}\n\n// hashList orders an array of hashes\ntype hashList struct {\n\tni          *NoticeIndex\n\tlibName     string\n\tinstallPath string\n\thashes      *[]hash\n}\n\n// Len returns the count of elements in the slice.\nfunc (l hashList) Len() int { return len(*l.hashes) }\n\n// Swap rearranges 2 elements of the slice so that each occupies the other's\n// former position.\nfunc (l hashList) Swap(i, j int) { (*l.hashes)[i], (*l.hashes)[j] = (*l.hashes)[j], (*l.hashes)[i] }\n\n// Less returns true when the `i`th element is lexicographically less than\n// the `j`th element.\nfunc (l hashList) Less(i, j int) bool {\n\tvar insti, instj int\n\tif len(l.libName) > 0 {\n\t\tinsti = len(l.ni.hashLibInstall[(*l.hashes)[i]][l.libName])\n\t\tinstj = len(l.ni.hashLibInstall[(*l.hashes)[j]][l.libName])\n\t} else {\n\t\tlibsi := l.ni.InstallHashLibs(l.installPath, (*l.hashes)[i])\n\t\tlibsj := l.ni.InstallHashLibs(l.installPath, (*l.hashes)[j])\n\t\tlibsis := strings.Join(libsi, \" \")\n\t\tlibsjs := strings.Join(libsj, \" \")\n\t\tif libsis != libsjs {\n\t\t\treturn libsis < libsjs\n\t\t}\n\t}\n\tif insti == instj {\n\t\tleni := len(l.ni.text[(*l.hashes)[i]])\n\t\tlenj := len(l.ni.text[(*l.hashes)[j]])\n\t\tif leni == lenj {\n\t\t\t// all else equal, just order by hash value\n\t\t\treturn (*l.hashes)[i].key < (*l.hashes)[j].key\n\t\t}\n\t\t// put shortest texts first within same # of installs\n\t\treturn leni < lenj\n\t}\n\t// reverse order of # installs so that most popular appears first\n\treturn instj < insti\n}\n"
  },
  {
    "path": "tools/compliance/policy_policy.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"regexp\"\n\t\"strings\"\n)\n\nvar (\n\t// RecognizedAnnotations identifies the set of annotations that have\n\t// meaning for compliance policy.\n\tRecognizedAnnotations = map[string]string{\n\t\t// used in readgraph.go to avoid creating 1000's of copies of the below 3 strings.\n\t\t\"static\":    \"static\",\n\t\t\"dynamic\":   \"dynamic\",\n\t\t\"toolchain\": \"toolchain\",\n\t}\n\n\t// safePathPrefixes maps the path prefixes presumed not to contain any\n\t// proprietary or confidential pathnames to whether to strip the prefix\n\t// from the path when used as the library name for notices.\n\tsafePathPrefixes = []safePathPrefixesType{\n\t\t{\"external/\", true},\n\t\t{\"art/\", false},\n\t\t{\"build/\", false},\n\t\t{\"cts/\", false},\n\t\t{\"dalvik/\", false},\n\t\t{\"developers/\", false},\n\t\t{\"development/\", false},\n\t\t{\"frameworks/\", false},\n\t\t{\"packages/\", true},\n\t\t{\"prebuilts/module_sdk/\", true},\n\t\t{\"prebuilts/\", false},\n\t\t{\"sdk/\", false},\n\t\t{\"system/\", false},\n\t\t{\"test/\", false},\n\t\t{\"toolchain/\", false},\n\t\t{\"tools/\", false},\n\t}\n\n\t// safePrebuiltPrefixes maps the regular expression to match a prebuilt\n\t// containing the path of a safe prefix to the safe prefix.\n\tsafePrebuiltPrefixes []safePrebuiltPrefixesType\n\n\t// ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.\n\tImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition)\n\n\t// ImpliesPermissive lists the condition names representing copyrighted but \"licensed without policy requirements\".\n\tImpliesPermissive = LicenseConditionSet(PermissiveCondition)\n\n\t// ImpliesNotice lists the condition names implying a notice or attribution policy.\n\tImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition |\n\t\tRestrictedCondition | WeaklyRestrictedCondition | ProprietaryCondition | ByExceptionOnlyCondition)\n\n\t// ImpliesReciprocal lists the condition names implying a local source-sharing policy.\n\tImpliesReciprocal = LicenseConditionSet(ReciprocalCondition)\n\n\t// Restricted lists the condition names implying an infectious source-sharing policy.\n\tImpliesRestricted = LicenseConditionSet(RestrictedCondition | WeaklyRestrictedCondition)\n\n\t// ImpliesProprietary lists the condition names implying a confidentiality policy.\n\tImpliesProprietary = LicenseConditionSet(ProprietaryCondition)\n\n\t// ImpliesByExceptionOnly lists the condition names implying a policy for \"license review and approval before use\".\n\tImpliesByExceptionOnly = LicenseConditionSet(ProprietaryCondition | ByExceptionOnlyCondition)\n\n\t// ImpliesPrivate lists the condition names implying a source-code privacy policy.\n\tImpliesPrivate = LicenseConditionSet(ProprietaryCondition)\n\n\t// ImpliesShared lists the condition names implying a source-code sharing policy.\n\tImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | WeaklyRestrictedCondition)\n)\n\ntype safePathPrefixesType struct {\n\tprefix string\n\tstrip  bool\n}\n\ntype safePrebuiltPrefixesType struct {\n\tsafePathPrefixesType\n\tre *regexp.Regexp\n}\n\nvar (\n\tanyLgpl      = regexp.MustCompile(`^SPDX-license-identifier-LGPL.*`)\n\tversionedGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL-\\p{N}.*`)\n\tgenericGpl   = regexp.MustCompile(`^SPDX-license-identifier-GPL$`)\n\tccBySa       = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)\n)\n\nfunc init() {\n\tfor _, safePathPrefix := range safePathPrefixes {\n\t\tif strings.HasPrefix(safePathPrefix.prefix, \"prebuilts/\") {\n\t\t\tcontinue\n\t\t}\n\t\tr := regexp.MustCompile(\"^prebuilts/(?:runtime/mainline/)?\" + safePathPrefix.prefix)\n\t\tsafePrebuiltPrefixes = append(safePrebuiltPrefixes,\n\t\t\tsafePrebuiltPrefixesType{safePathPrefix, r})\n\t}\n}\n\n// LicenseConditionSetFromNames returns a set containing the recognized `names` and\n// silently ignoring or discarding the unrecognized `names`.\nfunc LicenseConditionSetFromNames(names ...string) LicenseConditionSet {\n\tcs := NewLicenseConditionSet()\n\tfor _, name := range names {\n\t\tif lc, ok := RecognizedConditionNames[name]; ok {\n\t\t\tcs |= LicenseConditionSet(lc)\n\t\t}\n\t}\n\treturn cs\n}\n\n// Resolution happens in three phases:\n//\n// 1. A bottom-up traversal propagates (restricted) license conditions up to\n// targets from dendencies as needed.\n//\n// 2. For each condition of interest, a top-down traversal propagates\n// (restricted) conditions down from targets into linked dependencies.\n//\n// 3. Finally, a walk of the shipped target nodes attaches resolutions to the\n// ancestor nodes from the root down to and including the first non-container.\n//\n// e.g. If a disk image contains a binary bin1 that links a library liba, the\n// notice requirement for liba gets attached to the disk image and to bin1.\n// Because liba doesn't actually get shipped as a separate artifact, but only\n// as bits in bin1, it has no actions 'attached' to it. The actions attached\n// to the image and to bin1 'act on' liba by providing notice.\n//\n// The behavior of the 3 phases gets controlled by the 3 functions below.\n//\n// The first function controls what happens during the bottom-up propagation.\n// Restricted conditions propagate up all non-toolchain dependencies; except,\n// some do not propagate up dynamic links, which may depend on whether the\n// modules are independent.\n//\n// The second function controls what happens during the top-down propagation.\n// Restricted conditions propagate down as above with the added caveat that\n// inherited restricted conditions do not propagate from pure aggregates to\n// their dependencies.\n//\n// The final function controls which conditions apply/get attached to ancestors\n// depending on the types of dependencies involved. All conditions apply across\n// normal derivation dependencies. No conditions apply across toolchain\n// dependencies. Some restricted conditions apply across dynamic link\n// dependencies.\n//\n// Not all restricted licenses are create equal. Some have special rules or\n// exceptions. e.g. LGPL or \"with classpath excption\".\n\n// depConditionsPropagatingToTarget returns the conditions which propagate up an\n// edge from dependency to target.\n//\n// This function sets the policy for the bottom-up propagation and how conditions\n// flow up the graph from dependencies to targets.\n//\n// If a pure aggregation is built into a derivative work that is not a pure\n// aggregation, per policy it ceases to be a pure aggregation in the context of\n// that derivative work. The `treatAsAggregate` parameter will be false for\n// non-aggregates and for aggregates in non-aggregate contexts.\nfunc depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {\n\tresult := LicenseConditionSet(0x0000)\n\tif edgeIsDerivation(e) {\n\t\tresult |= depConditions & ImpliesRestricted\n\t\treturn result\n\t}\n\tif !edgeIsDynamicLink(e) {\n\t\treturn result\n\t}\n\n\tresult |= depConditions & LicenseConditionSet(RestrictedCondition)\n\treturn result\n}\n\n// targetConditionsPropagatingToDep returns the conditions which propagate down\n// an edge from target to dependency.\n//\n// This function sets the policy for the top-down traversal and how conditions\n// flow down the graph from targets to dependencies.\n//\n// If a pure aggregation is built into a derivative work that is not a pure\n// aggregation, per policy it ceases to be a pure aggregation in the context of\n// that derivative work. The `treatAsAggregate` parameter will be false for\n// non-aggregates and for aggregates in non-aggregate contexts.\nfunc targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool, conditionsFn TraceConditions) LicenseConditionSet {\n\tresult := targetConditions\n\n\t// reverse direction -- none of these apply to things depended-on, only to targets depending-on.\n\tresult = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition)\n\n\tif !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {\n\t\t// target is not a derivative work of dependency and is not linked to dependency\n\t\tresult = result.Difference(ImpliesRestricted)\n\t\treturn result\n\t}\n\tif treatAsAggregate {\n\t\t// If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.\n\t\t// Otherwise, restricted does not propagate back down to dependencies.\n\t\tif !conditionsFn(e.target).MatchesAnySet(ImpliesRestricted) {\n\t\t\tresult = result.Difference(ImpliesRestricted)\n\t\t}\n\t\treturn result\n\t}\n\tif edgeIsDerivation(e) {\n\t\treturn result\n\t}\n\tresult = result.Minus(WeaklyRestrictedCondition)\n\treturn result\n}\n\n// conditionsAttachingAcrossEdge returns the subset of conditions in `universe`\n// that apply across edge `e`.\n//\n// This function sets the policy for attaching actions to ancestor nodes in the\n// final resolution walk.\nfunc conditionsAttachingAcrossEdge(lg *LicenseGraph, e *TargetEdge, universe LicenseConditionSet) LicenseConditionSet {\n\tresult := universe\n\tif edgeIsDerivation(e) {\n\t\treturn result\n\t}\n\tif !edgeIsDynamicLink(e) {\n\t\treturn NewLicenseConditionSet()\n\t}\n\n\tresult &= LicenseConditionSet(RestrictedCondition)\n\treturn result\n}\n\n// edgeIsDynamicLink returns true for edges representing shared libraries\n// linked dynamically at runtime.\nfunc edgeIsDynamicLink(e *TargetEdge) bool {\n\treturn e.annotations.HasAnnotation(\"dynamic\")\n}\n\n// edgeIsDerivation returns true for edges where the target is a derivative\n// work of dependency.\nfunc edgeIsDerivation(e *TargetEdge) bool {\n\tisDynamic := e.annotations.HasAnnotation(\"dynamic\")\n\tisToolchain := e.annotations.HasAnnotation(\"toolchain\")\n\treturn !isDynamic && !isToolchain\n}\n"
  },
  {
    "path": "tools/compliance/policy_policy_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance/testfs\"\n)\n\nfunc TestPolicy_edgeConditions(t *testing.T) {\n\ttests := []struct {\n\t\tname                     string\n\t\tedge                     annotated\n\t\ttreatAsAggregate         bool\n\t\totherCondition           string\n\t\texpectedDepActions       []string\n\t\texpectedTargetConditions []string\n\t}{\n\t\t{\n\t\t\tname:                     \"firstparty\",\n\t\t\tedge:                     annotated{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"notice\",\n\t\t\tedge:                     annotated{\"mitBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname: \"fponlgpl\",\n\t\t\tedge: annotated{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions: []string{\n\t\t\t\t\"apacheBin.meta_lic:lgplLib.meta_lic:restricted_if_statically_linked\",\n\t\t\t\t\"lgplLib.meta_lic:lgplLib.meta_lic:restricted_if_statically_linked\",\n\t\t\t},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"fponlgpldynamic\",\n\t\t\tedge:                     annotated{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname: \"fpongpl\",\n\t\t\tedge: annotated{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions: []string{\n\t\t\t\t\"apacheBin.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t\t\"gplLib.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname: \"fpongpldynamic\",\n\t\t\tedge: annotated{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\texpectedDepActions: []string{\n\t\t\t\t\"apacheBin.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t\t\"gplLib.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"independentmodule\",\n\t\t\tedge:                     annotated{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"independentmodulestatic\",\n\t\t\tedge:                     annotated{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"dependentmodule\",\n\t\t\tedge:                     annotated{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\n\t\t{\n\t\t\tname:                     \"lgplonfp\",\n\t\t\tedge:                     annotated{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{\"lgplBin.meta_lic:restricted_if_statically_linked\"},\n\t\t},\n\t\t{\n\t\t\tname:                     \"lgplonfpdynamic\",\n\t\t\tedge:                     annotated{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"gplonfp\",\n\t\t\tedge:                     annotated{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{\"gplBin.meta_lic:restricted\"},\n\t\t},\n\t\t{\n\t\t\tname:                     \"gplcontainer\",\n\t\t\tedge:                     annotated{\"gplContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\ttreatAsAggregate:         true,\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{\"gplContainer.meta_lic:restricted\"},\n\t\t},\n\t\t{\n\t\t\tname:             \"gploncontainer\",\n\t\t\tedge:             annotated{\"apacheContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\ttreatAsAggregate: true,\n\t\t\totherCondition:   \"gplLib.meta_lic:restricted\",\n\t\t\texpectedDepActions: []string{\n\t\t\t\t\"apacheContainer.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t\t\"apacheLib.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t\t\"gplLib.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:             \"gplonbin\",\n\t\t\tedge:             annotated{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\ttreatAsAggregate: false,\n\t\t\totherCondition:   \"gplLib.meta_lic:restricted\",\n\t\t\texpectedDepActions: []string{\n\t\t\t\t\"apacheBin.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t\t\"apacheLib.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t\t\"gplLib.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t},\n\t\t\texpectedTargetConditions: []string{\"gplLib.meta_lic:restricted\"},\n\t\t},\n\t\t{\n\t\t\tname:                     \"gplonfpdynamic\",\n\t\t\tedge:                     annotated{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{\"gplBin.meta_lic:restricted\"},\n\t\t},\n\t\t{\n\t\t\tname:                     \"independentmodulereverse\",\n\t\t\tedge:                     annotated{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"dynamic\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"independentmodulereversestatic\",\n\t\t\tedge:                     annotated{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"dependentmodulereverse\",\n\t\t\tedge:                     annotated{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\", []string{\"dynamic\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname: \"ponr\",\n\t\t\tedge: annotated{\"proprietary.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions: []string{\n\t\t\t\t\"proprietary.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t\t\"gplLib.meta_lic:gplLib.meta_lic:restricted\",\n\t\t\t},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"ronp\",\n\t\t\tedge:                     annotated{\"gplBin.meta_lic\", \"proprietary.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{\"gplBin.meta_lic:restricted\"},\n\t\t},\n\t\t{\n\t\t\tname:                     \"noticeonb_e_o\",\n\t\t\tedge:                     annotated{\"mitBin.meta_lic\", \"by_exception.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"b_e_oonnotice\",\n\t\t\tedge:                     annotated{\"by_exception.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"noticeonrecip\",\n\t\t\tedge:                     annotated{\"mitBin.meta_lic\", \"mplLib.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                     \"reciponnotice\",\n\t\t\tedge:                     annotated{\"mplBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\texpectedDepActions:       []string{},\n\t\t\texpectedTargetConditions: []string{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tfs := make(testfs.TestFS)\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\ttarget := meta[tt.edge.target] + fmt.Sprintf(\"deps: {\\n  file: \\\"%s\\\"\\n\", tt.edge.dep)\n\t\t\tfor _, ann := range tt.edge.annotations {\n\t\t\t\ttarget += fmt.Sprintf(\"  annotations: \\\"%s\\\"\\n\", ann)\n\t\t\t}\n\t\t\tfs[tt.edge.target] = []byte(target + \"}\\n\")\n\t\t\tfs[tt.edge.dep] = []byte(meta[tt.edge.dep])\n\t\t\tlg, err := ReadLicenseGraph(&fs, stderr, []string{tt.edge.target})\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected error reading graph: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tedge := lg.Edges()[0]\n\t\t\t// simulate a condition inherited from another edge/dependency.\n\t\t\totherTarget := \"\"\n\t\t\totherCondition := \"\"\n\t\t\tvar otn *TargetNode\n\t\t\tif len(tt.otherCondition) > 0 {\n\t\t\t\tfields := strings.Split(tt.otherCondition, \":\")\n\t\t\t\totherTarget = fields[0]\n\t\t\t\totherCondition = fields[1]\n\t\t\t\totn = &TargetNode{name: otherTarget}\n\t\t\t\t// other target must exist in graph\n\t\t\t\tlg.targets[otherTarget] = otn\n\t\t\t\totn.licenseConditions = LicenseConditionSet(RecognizedConditionNames[otherCondition])\n\t\t\t}\n\t\t\ttargets := make(map[string]*TargetNode)\n\t\t\ttargets[edge.target.name] = edge.target\n\t\t\ttargets[edge.dependency.name] = edge.dependency\n\t\t\tif otn != nil {\n\t\t\t\ttargets[otn.name] = otn\n\t\t\t}\n\t\t\tif tt.expectedDepActions != nil {\n\t\t\t\tt.Run(\"depConditionsPropagatingToTarget\", func(t *testing.T) {\n\t\t\t\t\tdepConditions := edge.dependency.LicenseConditions()\n\t\t\t\t\tif otherTarget != \"\" {\n\t\t\t\t\t\t// simulate a sub-dependency's condition having already propagated up to dep and about to go to target\n\t\t\t\t\t\totherCs := otn.LicenseConditions()\n\t\t\t\t\t\tdepConditions |= otherCs\n\t\t\t\t\t}\n\t\t\t\t\tt.Logf(\"calculate target actions for edge=%s, dep conditions=%#v %s, treatAsAggregate=%v\", edge.String(), depConditions, depConditions, tt.treatAsAggregate)\n\t\t\t\t\tcsActual := depConditionsPropagatingToTarget(lg, edge, depConditions, tt.treatAsAggregate)\n\t\t\t\t\tt.Logf(\"calculated target conditions as %#v %s\", csActual, csActual)\n\t\t\t\t\tcsExpected := NewLicenseConditionSet()\n\t\t\t\t\tfor _, triple := range tt.expectedDepActions {\n\t\t\t\t\t\tfields := strings.Split(triple, \":\")\n\t\t\t\t\t\texpectedConditions := NewLicenseConditionSet()\n\t\t\t\t\t\tfor _, cname := range fields[2:] {\n\t\t\t\t\t\t\texpectedConditions = expectedConditions.Plus(RecognizedConditionNames[cname])\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcsExpected |= expectedConditions\n\t\t\t\t\t}\n\t\t\t\t\tt.Logf(\"expected target conditions as %#v %s\", csExpected, csExpected)\n\t\t\t\t\tif csActual != csExpected {\n\t\t\t\t\t\tt.Errorf(\"unexpected license conditions: got %#v, want %#v\", csActual, csExpected)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t\tif tt.expectedTargetConditions != nil {\n\t\t\t\tt.Run(\"targetConditionsPropagatingToDep\", func(t *testing.T) {\n\t\t\t\t\ttargetConditions := edge.target.LicenseConditions()\n\t\t\t\t\tif otherTarget != \"\" {\n\t\t\t\t\t\ttargetConditions = targetConditions.Union(otn.licenseConditions)\n\t\t\t\t\t}\n\t\t\t\t\tt.Logf(\"calculate dep conditions for edge=%s, target conditions=%v, treatAsAggregate=%v\", edge.String(), targetConditions.Names(), tt.treatAsAggregate)\n\t\t\t\t\tcs := targetConditionsPropagatingToDep(lg, edge, targetConditions, tt.treatAsAggregate, AllResolutions)\n\t\t\t\t\tt.Logf(\"calculated dep conditions as %v\", cs.Names())\n\t\t\t\t\tactual := cs.Names()\n\t\t\t\t\tsort.Strings(actual)\n\t\t\t\t\texpected := make([]string, 0)\n\t\t\t\t\tfor _, expectedDepCondition := range tt.expectedTargetConditions {\n\t\t\t\t\t\texpected = append(expected, strings.Split(expectedDepCondition, \":\")[1])\n\t\t\t\t\t}\n\t\t\t\t\tsort.Strings(expected)\n\t\t\t\t\tif len(actual) != len(expected) {\n\t\t\t\t\t\tt.Errorf(\"unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions\",\n\t\t\t\t\t\t\tactual, len(actual), expected, len(expected))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor i := 0; i < len(actual); i++ {\n\t\t\t\t\t\t\tif actual[i] != expected[i] {\n\t\t\t\t\t\t\t\tt.Errorf(\"unexpected target condition at element %d: got %q, want %q\",\n\t\t\t\t\t\t\t\t\ti, actual[i], expected[i])\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/policy_resolve.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nvar (\n\t// AllResolutions is a TraceConditions function that resolves all\n\t// unfiltered license conditions.\n\tAllResolutions = TraceConditions(func(tn *TargetNode) LicenseConditionSet { return tn.licenseConditions })\n)\n\n// TraceConditions is a function that returns the conditions to trace for each\n// target node `tn`.\ntype TraceConditions func(tn *TargetNode) LicenseConditionSet\n\n// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph\n// propagating conditions up the graph as necessary according to the properties\n// of each edge and according to each license condition in question.\n//\n// e.g. if a \"restricted\" condition applies to a binary, it also applies to all\n// of the statically-linked libraries and the transitive closure of their static\n// dependencies; even if neither they nor the transitive closure of their\n// dependencies originate any \"restricted\" conditions. The bottom-up walk will\n// not resolve the library and its transitive closure, but the later top-down\n// walk will.\nfunc ResolveBottomUpConditions(lg *LicenseGraph) {\n\tTraceBottomUpConditions(lg, AllResolutions)\n}\n\n// TraceBottomUpConditions performs a bottom-up walk of the LicenseGraph\n// propagating trace conditions from `conditionsFn` up the graph as necessary\n// according to the properties of each edge and according to each license\n// condition in question.\nfunc TraceBottomUpConditions(lg *LicenseGraph, conditionsFn TraceConditions) {\n\n\t// short-cut if already walked and cached\n\tlg.onceBottomUp.Do(func() {\n\t\t// amap identifes targets previously walked. (guarded by mu)\n\t\tamap := make(map[*TargetNode]struct{})\n\n\t\tvar walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet\n\n\t\twalk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet {\n\t\t\tpriorWalkResults := func() (LicenseConditionSet, bool) {\n\t\t\t\tif _, alreadyWalked := amap[target]; alreadyWalked {\n\t\t\t\t\tif treatAsAggregate {\n\t\t\t\t\t\treturn target.resolution, true\n\t\t\t\t\t}\n\t\t\t\t\tif !target.pure {\n\t\t\t\t\t\treturn target.resolution, true\n\t\t\t\t\t}\n\t\t\t\t\t// previously walked in a pure aggregate context,\n\t\t\t\t\t// needs to walk again in non-aggregate context\n\t\t\t\t} else {\n\t\t\t\t\ttarget.resolution |= conditionsFn(target)\n\t\t\t\t\tamap[target] = struct{}{}\n\t\t\t\t}\n\t\t\t\ttarget.pure = treatAsAggregate\n\t\t\t\treturn target.resolution, false\n\t\t\t}\n\t\t\tcs, alreadyWalked := priorWalkResults()\n\t\t\tif alreadyWalked {\n\t\t\t\treturn cs\n\t\t\t}\n\n\t\t\t// add all the conditions from all the dependencies\n\t\t\tfor _, edge := range target.edges {\n\t\t\t\t// walk dependency to get its conditions\n\t\t\t\tdcs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer())\n\n\t\t\t\t// turn those into the conditions that apply to the target\n\t\t\t\tdcs = depConditionsPropagatingToTarget(lg, edge, dcs, treatAsAggregate)\n\t\t\t\tcs |= dcs\n\t\t\t}\n\t\t\ttarget.resolution |= cs\n\t\t\tcs = target.resolution\n\n\t\t\t// return conditions up the tree\n\t\t\treturn cs\n\t\t}\n\n\t\t// walk each of the roots\n\t\tfor _, rname := range lg.rootFiles {\n\t\t\trnode := lg.targets[rname]\n\t\t\t_ = walk(rnode, rnode.IsContainer())\n\t\t}\n\t})\n}\n\n// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph\n// propagating conditions from target to dependency.\n//\n// e.g. For current policy, none of the conditions propagate from target to\n// dependency except restricted. For restricted, the policy is to share the\n// source of any libraries linked to restricted code and to provide notice.\nfunc ResolveTopDownConditions(lg *LicenseGraph) {\n\tTraceTopDownConditions(lg, AllResolutions)\n}\n\n// TraceTopDownCondtions performs a top-down walk of the LicenseGraph\n// propagating trace conditions returned by `conditionsFn` from target to\n// dependency.\nfunc TraceTopDownConditions(lg *LicenseGraph, conditionsFn TraceConditions) {\n\n\t// short-cut if already walked and cached\n\tlg.onceTopDown.Do(func() {\n\t\t// start with the conditions propagated up the graph\n\t\tTraceBottomUpConditions(lg, conditionsFn)\n\n\t\t// amap contains the set of targets already walked. (guarded by mu)\n\t\tamap := make(map[*TargetNode]struct{})\n\n\t\tvar walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool)\n\n\t\twalk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {\n\t\t\tcontinueWalk := func() bool {\n\t\t\t\tif _, alreadyWalked := amap[fnode]; alreadyWalked {\n\t\t\t\t\tif cs.IsEmpty() {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif cs.Difference(fnode.resolution).IsEmpty() {\n\t\t\t\t\t\t// no new conditions\n\n\t\t\t\t\t\t// pure aggregates never need walking a 2nd time with same conditions\n\t\t\t\t\t\tif treatAsAggregate {\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// non-aggregates don't need walking as non-aggregate a 2nd time\n\t\t\t\t\t\tif !fnode.pure {\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// previously walked as pure aggregate; need to re-walk as non-aggregate\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfnode.resolution |= conditionsFn(fnode)\n\t\t\t\t}\n\t\t\t\tfnode.resolution |= cs\n\t\t\t\tfnode.pure = treatAsAggregate\n\t\t\t\tamap[fnode] = struct{}{}\n\t\t\t\tcs = fnode.resolution\n\t\t\t\treturn true\n\t\t\t}()\n\t\t\tif !continueWalk {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// for each dependency\n\t\t\tfor _, edge := range fnode.edges {\n\t\t\t\t// dcs holds the dpendency conditions inherited from the target\n\t\t\t\tdcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate, conditionsFn)\n\t\t\t\tdnode := edge.dependency\n\t\t\t\t// add the conditions to the dependency\n\t\t\t\twalk(dnode, dcs, treatAsAggregate && dnode.IsContainer())\n\t\t\t}\n\t\t}\n\n\t\t// walk each of the roots\n\t\tfor _, rname := range lg.rootFiles {\n\t\t\trnode := lg.targets[rname]\n\t\t\t// add the conditions to the root and its transitive closure\n\t\t\twalk(rnode, NewLicenseConditionSet(), rnode.IsContainer())\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "tools/compliance/policy_resolve_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"bytes\"\n\t\"sort\"\n\t\"testing\"\n)\n\nfunc TestResolveBottomUpConditions(t *testing.T) {\n\ttests := []struct {\n\t\tname            string\n\t\troots           []string\n\t\tedges           []annotated\n\t\texpectedActions []tcond\n\t}{\n\t\t{\n\t\t\tname:  \"firstparty\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"firstpartytool\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"toolchain\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"firstpartydeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"firstpartywide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"firstpartydynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"firstpartydynamicdeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"firstpartydynamicwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricted\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restrictedtool\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"toolchain\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restrictedwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamicdeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamicwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricted\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestrictedtool\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"toolchain\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestrictedwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamicdeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamicwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpath\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdependent\",\n\t\t\troots: []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"dependentModule.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdependentdynamic\",\n\t\t\troots: []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"dependentModule.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tlg, err := toGraph(stderr, tt.roots, tt.edges)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlogGraph(lg, t)\n\n\t\t\tResolveBottomUpConditions(lg)\n\t\t\tactual := asActionList(lg)\n\t\t\tsort.Sort(actual)\n\t\t\tt.Logf(\"actual: %s\", actual.String())\n\n\t\t\texpected := toActionList(lg, tt.expectedActions)\n\t\t\tsort.Sort(expected)\n\t\t\tt.Logf(\"expected: %s\", expected.String())\n\n\t\t\tif len(actual) != len(expected) {\n\t\t\t\tt.Errorf(\"unexpected number of actions: got %d, want %d\", len(actual), len(expected))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor i := 0; i < len(actual); i++ {\n\t\t\t\tif actual[i] != expected[i] {\n\t\t\t\t\tt.Errorf(\"unexpected action at index %d: got %s, want %s\", i, actual[i].String(), expected[i].String())\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestResolveTopDownConditions(t *testing.T) {\n\ttests := []struct {\n\t\tname            string\n\t\troots           []string\n\t\tedges           []annotated\n\t\texpectedActions []tcond\n\t}{\n\t\t{\n\t\t\tname:  \"firstparty\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"firstpartydynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricted\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restrictedtool\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplBin.meta_lic\", []string{\"toolchain\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplBin.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"mitBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mplLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"mplLib.meta_lic\", \"reciprocal|restricted\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restrictedwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice|restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamicdeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"mitBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"mplLib.meta_lic\", \"reciprocal|restricted\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamicwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricted\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestrictedtool\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplBin.meta_lic\", []string{\"toolchain\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestrictedwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamicdeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamicwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpath\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdependent\",\n\t\t\troots: []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"dependentModule.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"dependentModule.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdependentdynamic\",\n\t\t\troots: []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"dependentModule.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []tcond{\n\t\t\t\t{\"dependentModule.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tlg, err := toGraph(stderr, tt.roots, tt.edges)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlogGraph(lg, t)\n\n\t\t\tResolveTopDownConditions(lg)\n\t\t\tactual := asActionList(lg)\n\t\t\tsort.Sort(actual)\n\t\t\tt.Logf(\"actual: %s\", actual.String())\n\n\t\t\texpected := toActionList(lg, tt.expectedActions)\n\t\t\tsort.Sort(expected)\n\t\t\tt.Logf(\"expected: %s\", expected.String())\n\n\t\t\tif len(actual) != len(expected) {\n\t\t\t\tt.Errorf(\"unexpected number of actions: got %d, want %d\", len(actual), len(expected))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor i := 0; i < len(actual); i++ {\n\t\t\t\tif actual[i] != expected[i] {\n\t\t\t\t\tt.Errorf(\"unexpected action at index %d: got %s, want %s\", i, actual[i].String(), expected[i].String())\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/policy_resolvenotices.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\n// ResolveNotices implements the policy for notices.\nfunc ResolveNotices(lg *LicenseGraph) ResolutionSet {\n\tResolveTopDownConditions(lg)\n\treturn WalkResolutionsForCondition(lg, ImpliesNotice)\n}\n"
  },
  {
    "path": "tools/compliance/policy_resolvenotices_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestResolveNotices(t *testing.T) {\n\ttests := []struct {\n\t\tname                string\n\t\troots               []string\n\t\tedges               []annotated\n\t\texpectedResolutions []res\n\t}{\n\t\t{\n\t\t\tname:  \"firstparty\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"firstpartydynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"firstpartydynamicshipped\",\n\t\t\troots: []string{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricted\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice|restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restrictedtool\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplBin.meta_lic\", []string{\"toolchain\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"mitBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mplLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"mplLib.meta_lic\", \"reciprocal|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mplLib.meta_lic\", \"reciprocal|restricted\"},\n\t\t\t\t{\"mitBin.meta_lic\", \"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restrictedwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamicshipped\",\n\t\t\troots: []string{\"apacheBin.meta_lic\", \"mitLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"mitLib.meta_lic\", \"notice|restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamicdeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"mitBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"mitBin.meta_lic\", \"mitBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamicwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restricteddynamicwideshipped\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricted\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestrictedtool\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplBin.meta_lic\", []string{\"toolchain\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestrictedtoolshipped\",\n\t\t\troots: []string{\"apacheBin.meta_lic\", \"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplBin.meta_lic\", []string{\"toolchain\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplBin.meta_lic\", \"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"mitLib.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestrictedwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamicshipped\",\n\t\t\troots: []string{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamicdeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamicdeepshipped\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamicwide\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"weakrestricteddynamicwideshipped\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpath\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdependent\",\n\t\t\troots: []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"dependentModule.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"dependentModule.meta_lic\", \"dependentModule.meta_lic\", \"notice\"},\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t\t{\"dependentModule.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdynamicshipped\",\n\t\t\troots: []string{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdependentdynamic\",\n\t\t\troots: []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"dependentModule.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"dependentModule.meta_lic\", \"dependentModule.meta_lic\", \"notice\"},\n\t\t\t\t{\"dependentModule.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"classpathdependentdynamicshipped\",\n\t\t\troots: []string{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"dependentModule.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"dependentModule.meta_lic\", \"dependentModule.meta_lic\", \"notice\"},\n\t\t\t\t{\"dependentModule.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tlg, err := toGraph(stderr, tt.roots, tt.edges)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\texpectedRs := toResolutionSet(lg, tt.expectedResolutions)\n\t\t\tactualRs := ResolveNotices(lg)\n\t\t\tcheckSame(actualRs, expectedRs, t)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/policy_resolveprivacy.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\n// ResolveSourcePrivacy implements the policy for source privacy.\nfunc ResolveSourcePrivacy(lg *LicenseGraph) ResolutionSet {\n\tResolveTopDownConditions(lg)\n\treturn WalkResolutionsForCondition(lg, ImpliesPrivate)\n}\n"
  },
  {
    "path": "tools/compliance/policy_resolveprivacy_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestResolveSourcePrivacy(t *testing.T) {\n\ttests := []struct {\n\t\tname                string\n\t\troots               []string\n\t\tedges               []annotated\n\t\texpectedResolutions []res\n\t}{\n\t\t{\n\t\t\tname:  \"firstparty\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"notice\",\n\t\t\troots: []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"lgpl\",\n\t\t\troots: []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"proprietaryonresricted\",\n\t\t\troots: []string{\"proprietary.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"proprietary.meta_lic\", \"proprietary.meta_lic\", \"proprietary\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restrictedonproprietary\",\n\t\t\troots: []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", \"proprietary\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tlg, err := toGraph(stderr, tt.roots, tt.edges)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\texpectedRs := toResolutionSet(lg, tt.expectedResolutions)\n\t\t\tactualRs := ResolveSourcePrivacy(lg)\n\t\t\tcheckResolves(actualRs, expectedRs, t)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/policy_resolveshare.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\n// ResolveSourceSharing implements the policy for source-sharing.\nfunc ResolveSourceSharing(lg *LicenseGraph) ResolutionSet {\n\tResolveTopDownConditions(lg)\n\treturn WalkResolutionsForCondition(lg, ImpliesShared)\n}\n"
  },
  {
    "path": "tools/compliance/policy_resolveshare_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestResolveSourceSharing(t *testing.T) {\n\ttests := []struct {\n\t\tname                string\n\t\troots               []string\n\t\tedges               []annotated\n\t\texpectedResolutions []res\n\t}{\n\t\t{\n\t\t\tname:  \"independentmodulerestricted\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"independentmodulerestrictedshipped\",\n\t\t\troots: []string{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"independentmodulestaticrestricted\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"dependentmodulerestricted\",\n\t\t\troots: []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"dependentmodulerestrictedshipclasspath\",\n\t\t\troots: []string{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"lgplonfprestricted\",\n\t\t\troots: []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"lgplBin.meta_lic\", \"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"lgplonfpdynamicrestricted\",\n\t\t\troots: []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"lgplBin.meta_lic\", \"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"lgplonfpdynamicrestrictedshiplib\",\n\t\t\troots: []string{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"lgplBin.meta_lic\", \"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"gplonfprestricted\",\n\t\t\troots: []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"gplcontainerrestricted\",\n\t\t\troots: []string{\"gplContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplContainer.meta_lic\", \"gplContainer.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplContainer.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"gploncontainerrestricted\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"gplonbinrestricted\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"gplonfpdynamicrestricted\",\n\t\t\troots: []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"gplonfpdynamicrestrictedshiplib\",\n\t\t\troots: []string{\"gplBin.meta_lic\", \"apacheLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"independentmodulereverserestricted\",\n\t\t\troots: []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"independentmodulereversestaticrestricted\",\n\t\t\troots: []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"dependentmodulereverserestricted\",\n\t\t\troots: []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"dependentmodulereverserestrictedshipdependent\",\n\t\t\troots: []string{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"ponrrestricted\",\n\t\t\troots: []string{\"proprietary.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"proprietary.meta_lic\", \"proprietary.meta_lic\", \"restricted\"},\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"ronprestricted\",\n\t\t\troots: []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"noticeonb_e_orestricted\",\n\t\t\troots: []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"by_exception.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"b_e_oonnoticerestricted\",\n\t\t\troots: []string{\"by_exception.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"by_exception.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:  \"noticeonreciprecip\",\n\t\t\troots: []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"mplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"mitBin.meta_lic\", \"mplLib.meta_lic\", \"reciprocal\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"reciponnoticerecip\",\n\t\t\troots: []string{\"mplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mplBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"mplBin.meta_lic\", \"mplBin.meta_lic\", \"reciprocal\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tlg, err := toGraph(stderr, tt.roots, tt.edges)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\texpectedRs := toResolutionSet(lg, tt.expectedResolutions)\n\t\t\tactualRs := ResolveSourceSharing(lg)\n\t\t\tcheckResolves(actualRs, expectedRs, t)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/policy_shareprivacyconflicts.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"fmt\"\n)\n\n// SourceSharePrivacyConflict describes an individual conflict between a source-sharing\n// condition and a source privacy condition\ntype SourceSharePrivacyConflict struct {\n\tSourceNode       *TargetNode\n\tShareCondition   LicenseCondition\n\tPrivacyCondition LicenseCondition\n}\n\n// Error returns a string describing the conflict.\nfunc (conflict SourceSharePrivacyConflict) Error() string {\n\treturn fmt.Sprintf(\"%s %s and must share from %s condition\\n\", conflict.SourceNode.name,\n\t\tconflict.PrivacyCondition.Name(), conflict.ShareCondition.Name())\n}\n\n// IsEqualTo returns true when `conflict` and `other` describe the same conflict.\nfunc (conflict SourceSharePrivacyConflict) IsEqualTo(other SourceSharePrivacyConflict) bool {\n\treturn conflict.SourceNode.name == other.SourceNode.name &&\n\t\tconflict.ShareCondition == other.ShareCondition &&\n\t\tconflict.PrivacyCondition == other.PrivacyCondition\n}\n\n// ConflictingSharedPrivateSource lists all of the targets where conflicting conditions to\n// share the source and to keep the source private apply to the target.\nfunc ConflictingSharedPrivateSource(lg *LicenseGraph) []SourceSharePrivacyConflict {\n\n\tResolveTopDownConditions(lg)\n\t// combined is the combination of source-sharing and source privacy.\n\tcombined := WalkActionsForCondition(lg, ImpliesShared.Union(ImpliesPrivate))\n\n\t// size is the size of the result\n\tsize := 0\n\tfor actsOn, cs := range combined {\n\t\tif actsOn.pure && !actsOn.LicenseConditions().MatchesAnySet(ImpliesShared) {\n\t\t\t// no need to share code to build \"a distribution medium\"\n\t\t\tcontinue\n\t\t}\n\t\tsize += cs.Intersection(ImpliesShared).Len() * cs.Intersection(ImpliesPrivate).Len()\n\t}\n\tif size == 0 {\n\t\treturn nil\n\t}\n\tresult := make([]SourceSharePrivacyConflict, 0, size)\n\tfor actsOn, cs := range combined {\n\t\tif actsOn.pure { // no need to share code for \"a distribution medium\"\n\t\t\tcontinue\n\t\t}\n\t\tpconditions := cs.Intersection(ImpliesPrivate).AsList()\n\t\tssconditions := cs.Intersection(ImpliesShared).AsList()\n\n\t\t// report all conflicting condition combinations\n\t\tfor _, p := range pconditions {\n\t\t\tfor _, ss := range ssconditions {\n\t\t\t\tresult = append(result, SourceSharePrivacyConflict{actsOn, ss, p})\n\t\t\t}\n\t\t}\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "tools/compliance/policy_shareprivacyconflicts_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"bytes\"\n\t\"sort\"\n\t\"testing\"\n)\n\n// byConflict orders conflicts by target then share then privacy\ntype byConflict []SourceSharePrivacyConflict\n\n// Len returns the count of elements in the slice.\nfunc (l byConflict) Len() int { return len(l) }\n\n// Swap rearranged 2 elements so that each occupies the other's former\n// position.\nfunc (l byConflict) Swap(i, j int) { l[i], l[j] = l[j], l[i] }\n\n// Less returns true when the `i`th element is lexicographically less than\n// the `j`th element.\nfunc (l byConflict) Less(i, j int) bool {\n\tif l[i].SourceNode.Name() == l[j].SourceNode.Name() {\n\t\tif l[i].ShareCondition.Name() == l[j].ShareCondition.Name() {\n\t\t\treturn l[i].PrivacyCondition.Name() < l[j].PrivacyCondition.Name()\n\t\t}\n\t\treturn l[i].ShareCondition.Name() < l[j].ShareCondition.Name()\n\t}\n\treturn l[i].SourceNode.Name() < l[j].SourceNode.Name()\n}\n\nfunc TestConflictingSharedPrivateSource(t *testing.T) {\n\ttests := []struct {\n\t\tname              string\n\t\troots             []string\n\t\tedges             []annotated\n\t\texpectedConflicts []confl\n\t}{\n\t\t{\n\t\t\tname:  \"firstparty\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedConflicts: []confl{},\n\t\t},\n\t\t{\n\t\t\tname:  \"notice\",\n\t\t\troots: []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedConflicts: []confl{},\n\t\t},\n\t\t{\n\t\t\tname:  \"lgpl\",\n\t\t\troots: []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedConflicts: []confl{},\n\t\t},\n\t\t{\n\t\t\tname:  \"proprietaryonrestricted\",\n\t\t\troots: []string{\"proprietary.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedConflicts: []confl{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic:restricted\", \"proprietary.meta_lic:proprietary\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"restrictedonproprietary\",\n\t\t\troots: []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedConflicts: []confl{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplBin.meta_lic:restricted\", \"proprietary.meta_lic:proprietary\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tlg, err := toGraph(stderr, tt.roots, tt.edges)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\texpectedConflicts := toConflictList(lg, tt.expectedConflicts)\n\t\t\tactualConflicts := ConflictingSharedPrivateSource(lg)\n\t\t\tsort.Sort(byConflict(expectedConflicts))\n\t\t\tsort.Sort(byConflict(actualConflicts))\n\t\t\tif len(expectedConflicts) != len(actualConflicts) {\n\t\t\t\tt.Errorf(\"unexpected number of share/privacy conflicts: got %v with %d conflicts, want %v with %d conflicts\",\n\t\t\t\t\tactualConflicts, len(actualConflicts), expectedConflicts, len(expectedConflicts))\n\t\t\t} else {\n\t\t\t\tfor i := 0; i < len(actualConflicts); i++ {\n\t\t\t\t\tif !actualConflicts[i].IsEqualTo(expectedConflicts[i]) {\n\t\t\t\t\t\tt.Errorf(\"unexpected share/privacy conflict at element %d: got %q, want %q\",\n\t\t\t\t\t\t\ti, actualConflicts[i].Error(), expectedConflicts[i].Error())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/policy_shipped.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\n// ShippedNodes returns the set of nodes in a license graph where the target or\n// a derivative work gets distributed. (caches result)\nfunc ShippedNodes(lg *LicenseGraph) TargetNodeSet {\n\tlg.mu.Lock()\n\tshipped := lg.shippedNodes\n\tlg.mu.Unlock()\n\tif shipped != nil {\n\t\treturn *shipped\n\t}\n\n\ttset := make(TargetNodeSet)\n\n\tWalkTopDown(NoEdgeContext{}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {\n\t\tif _, alreadyWalked := tset[tn]; alreadyWalked {\n\t\t\treturn false\n\t\t}\n\t\tif len(path) > 0 {\n\t\t\tif !edgeIsDerivation(path[len(path)-1].edge) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\ttset[tn] = struct{}{}\n\t\treturn true\n\t})\n\n\tshipped = &tset\n\n\tlg.mu.Lock()\n\tif lg.shippedNodes == nil {\n\t\tlg.shippedNodes = shipped\n\t} else {\n\t\t// if we end up with 2, release the later for garbage collection.\n\t\tshipped = lg.shippedNodes\n\t}\n\tlg.mu.Unlock()\n\n\treturn *shipped\n}\n"
  },
  {
    "path": "tools/compliance/policy_shipped_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"bytes\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestShippedNodes(t *testing.T) {\n\ttests := []struct {\n\t\tname          string\n\t\troots         []string\n\t\tedges         []annotated\n\t\texpectedNodes []string\n\t}{\n\t\t{\n\t\t\tname:          \"singleton\",\n\t\t\troots:         []string{\"apacheLib.meta_lic\"},\n\t\t\tedges:         []annotated{},\n\t\t\texpectedNodes: []string{\"apacheLib.meta_lic\"},\n\t\t},\n\t\t{\n\t\t\tname:  \"simplebinary\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedNodes: []string{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\"},\n\t\t},\n\t\t{\n\t\t\tname:  \"simpledynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedNodes: []string{\"apacheBin.meta_lic\"},\n\t\t},\n\t\t{\n\t\t\tname:  \"container\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedNodes: []string{\n\t\t\t\t\"apacheContainer.meta_lic\",\n\t\t\t\t\"apacheLib.meta_lic\",\n\t\t\t\t\"gplLib.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"binary\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedNodes: []string{\n\t\t\t\t\"apacheBin.meta_lic\",\n\t\t\t\t\"apacheLib.meta_lic\",\n\t\t\t\t\"gplLib.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"binarydynamic\",\n\t\t\troots: []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedNodes: []string{\n\t\t\t\t\"apacheBin.meta_lic\",\n\t\t\t\t\"apacheLib.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"containerdeep\",\n\t\t\troots: []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheLib.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedNodes: []string{\n\t\t\t\t\"apacheContainer.meta_lic\",\n\t\t\t\t\"apacheBin.meta_lic\",\n\t\t\t\t\"apacheLib.meta_lic\",\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tlg, err := toGraph(stderr, tt.roots, tt.edges)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.Logf(\"graph:\")\n\t\t\tfor _, edge := range lg.Edges() {\n\t\t\t\tt.Logf(\"  %s\", edge.String())\n\t\t\t}\n\t\t\texpectedNodes := append([]string{}, tt.expectedNodes...)\n\t\t\tnodeset := ShippedNodes(lg)\n\t\t\tt.Logf(\"shipped node set: %s\", nodeset.String())\n\n\t\t\tactualNodes := nodeset.Names()\n\t\t\tt.Logf(\"shipped nodes: [%s]\", strings.Join(actualNodes, \", \"))\n\n\t\t\tsort.Strings(expectedNodes)\n\t\t\tsort.Strings(actualNodes)\n\n\t\t\tt.Logf(\"sorted nodes: [%s]\", strings.Join(actualNodes, \", \"))\n\t\t\tt.Logf(\"expected nodes: [%s]\", strings.Join(expectedNodes, \", \"))\n\t\t\tif len(expectedNodes) != len(actualNodes) {\n\t\t\t\tt.Errorf(\"unexpected number of shipped nodes: %d nodes, want %d nodes\",\n\t\t\t\t\tlen(actualNodes), len(expectedNodes))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor i := 0; i < len(actualNodes); i++ {\n\t\t\t\tif expectedNodes[i] != actualNodes[i] {\n\t\t\t\t\tt.Errorf(\"unexpected node at index %d: got %q, want %q\",\n\t\t\t\t\t\ti, actualNodes[i], expectedNodes[i])\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/policy_walk.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\n// EdgeContextProvider is an interface for injecting edge-specific context\n// into walk paths.\ntype EdgeContextProvider interface {\n\t// Context returns the context for `edge` when added to `path`.\n\tContext(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{}\n}\n\n// NoEdgeContext implements EdgeContextProvider for walks that use no context.\ntype NoEdgeContext struct{}\n\n// Context returns nil.\nfunc (ctx NoEdgeContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} {\n\treturn nil\n}\n\n// ApplicableConditionsContext provides the subset of conditions in `universe`\n// that apply to each edge in a path.\ntype ApplicableConditionsContext struct {\n\tuniverse LicenseConditionSet\n}\n\n// Context returns the LicenseConditionSet applicable to the edge.\nfunc (ctx ApplicableConditionsContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} {\n\tuniverse := ctx.universe\n\tif len(path) > 0 {\n\t\tuniverse = path[len(path)-1].ctx.(LicenseConditionSet)\n\t}\n\treturn conditionsAttachingAcrossEdge(lg, edge, universe)\n}\n\n// VisitNode is called for each root and for each walked dependency node by\n// WalkTopDown and WalkTopDownBreadthFirst. When VisitNode returns true, WalkTopDown will proceed to walk\n// down the dependences of the node\ntype VisitNode func(lg *LicenseGraph, target *TargetNode, path TargetEdgePath) bool\n\n// WalkTopDown does a top-down walk of `lg` calling `visit` and descending\n// into depenencies when `visit` returns true.\nfunc WalkTopDown(ctx EdgeContextProvider, lg *LicenseGraph, visit VisitNode) {\n\tpath := NewTargetEdgePath(32)\n\n\tvar walk func(fnode *TargetNode)\n\twalk = func(fnode *TargetNode) {\n\t\tvisitChildren := visit(lg, fnode, *path)\n\t\tif !visitChildren {\n\t\t\treturn\n\t\t}\n\t\tfor _, edge := range fnode.edges {\n\t\t\tvar edgeContext interface{}\n\t\t\tif ctx == nil {\n\t\t\t\tedgeContext = nil\n\t\t\t} else {\n\t\t\t\tedgeContext = ctx.Context(lg, *path, edge)\n\t\t\t}\n\t\t\tpath.Push(edge, edgeContext)\n\t\t\twalk(edge.dependency)\n\t\t\tpath.Pop()\n\t\t}\n\t}\n\n\tfor _, r := range lg.rootFiles {\n\t\tpath.Clear()\n\t\twalk(lg.targets[r])\n\t}\n}\n\n// WalkTopDownBreadthFirst performs a Breadth-first top down walk of `lg` calling `visit` and descending\n// into depenencies when `visit` returns true.\nfunc WalkTopDownBreadthFirst(ctx EdgeContextProvider, lg *LicenseGraph, visit VisitNode) {\n\tpath := NewTargetEdgePath(32)\n\n\tvar walk func(fnode *TargetNode)\n\twalk = func(fnode *TargetNode) {\n\t\tedgesToWalk := make(TargetEdgeList, 0, len(fnode.edges))\n\t\tfor _, edge := range fnode.edges {\n\t\t\tvar edgeContext interface{}\n\t\t\tif ctx == nil {\n\t\t\t\tedgeContext = nil\n\t\t\t} else {\n\t\t\t\tedgeContext = ctx.Context(lg, *path, edge)\n\t\t\t}\n\t\t\tpath.Push(edge, edgeContext)\n\t\t\tif visit(lg, edge.dependency, *path){\n\t\t\t\tedgesToWalk = append(edgesToWalk, edge)\n\t\t\t}\n\t\t\tpath.Pop()\n\t\t}\n\n\t\tfor _, edge := range(edgesToWalk) {\n\t\t\tvar edgeContext interface{}\n\t\t\tif ctx == nil {\n\t\t\t\tedgeContext = nil\n\t\t\t} else {\n\t\t\t\tedgeContext = ctx.Context(lg, *path, edge)\n\t\t\t}\n\t\t\tpath.Push(edge, edgeContext)\n\t\t\twalk(edge.dependency)\n\t\t\tpath.Pop()\n\t\t}\n\t}\n\n\tpath.Clear()\n\trootsToWalk := make([]*TargetNode, 0, len(lg.rootFiles))\n\tfor _, r := range lg.rootFiles {\n\t\tif visit(lg, lg.targets[r], *path){\n\t\t\trootsToWalk = append(rootsToWalk, lg.targets[r])\n\t\t}\n\t}\n\n\tfor _, rnode := range(rootsToWalk) {\n\t\twalk(rnode)\n\t}\n}\n\n// resolutionKey identifies results from walking a specific target for a\n// specific set of conditions.\ntype resolutionKey struct {\n\ttarget *TargetNode\n\tcs     LicenseConditionSet\n}\n\n// WalkResolutionsForCondition performs a top-down walk of the LicenseGraph\n// resolving all distributed works for `conditions`.\nfunc WalkResolutionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ResolutionSet {\n\tshipped := ShippedNodes(lg)\n\n\t// rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions\n\trmap := make(map[resolutionKey]ActionSet)\n\n\t// cmap identifies previously walked target/condition pairs.\n\tcmap := make(map[resolutionKey]struct{})\n\n\t// result accumulates the resolutions to return.\n\tresult := make(ResolutionSet)\n\tWalkTopDown(ApplicableConditionsContext{conditions}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {\n\t\tuniverse := conditions\n\t\tif len(path) > 0 {\n\t\t\tuniverse = path[len(path)-1].ctx.(LicenseConditionSet)\n\t\t}\n\n\t\tif universe.IsEmpty() {\n\t\t\treturn false\n\t\t}\n\t\tkey := resolutionKey{tn, universe}\n\n\t\tif _, alreadyWalked := cmap[key]; alreadyWalked {\n\t\t\tpure := true\n\t\t\tfor _, p := range path {\n\t\t\t\ttarget := p.Target()\n\t\t\t\ttkey := resolutionKey{target, universe}\n\t\t\t\tif _, ok := rmap[tkey]; !ok {\n\t\t\t\t\trmap[tkey] = make(ActionSet)\n\t\t\t\t}\n\t\t\t\t// attach prior walk outcome to ancestor\n\t\t\t\tfor actsOn, cs := range rmap[key] {\n\t\t\t\t\trmap[tkey][actsOn] = cs\n\t\t\t\t}\n\t\t\t\t// if prior walk produced results, copy results\n\t\t\t\t// to ancestor.\n\t\t\t\tif _, ok := result[tn]; ok && pure {\n\t\t\t\t\tif _, ok := result[target]; !ok {\n\t\t\t\t\t\tresult[target] = make(ActionSet)\n\t\t\t\t\t}\n\t\t\t\t\tfor actsOn, cs := range result[tn] {\n\t\t\t\t\t\tresult[target][actsOn] = cs\n\t\t\t\t\t}\n\t\t\t\t\tpure = target.IsContainer()\n\t\t\t\t}\n\t\t\t}\n\t\t\t// if all ancestors are pure aggregates, attach\n\t\t\t// matching prior walk conditions to self. Prior walk\n\t\t\t// will not have done so if any ancestor was not an\n\t\t\t// aggregate.\n\t\t\tif pure {\n\t\t\t\tmatch := rmap[key][tn].Intersection(universe)\n\t\t\t\tif !match.IsEmpty() {\n\t\t\t\t\tif _, ok := result[tn]; !ok {\n\t\t\t\t\t\tresult[tn] = make(ActionSet)\n\t\t\t\t\t}\n\t\t\t\t\tresult[tn][tn] = match\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t\t// no need to walk node or dependencies if not shipped\n\t\tif !shipped.Contains(tn) {\n\t\t\treturn false\n\t\t}\n\t\tif _, ok := rmap[key]; !ok {\n\t\t\trmap[key] = make(ActionSet)\n\t\t}\n\t\t// add self to walk outcome\n\t\trmap[key][tn] = tn.resolution\n\t\tcmap[key] = struct{}{}\n\t\tcs := tn.resolution\n\t\tif !cs.IsEmpty() {\n\t\t\tcs = cs.Intersection(universe)\n\t\t\tpure := true\n\t\t\tfor _, p := range path {\n\t\t\t\ttarget := p.Target()\n\t\t\t\ttkey := resolutionKey{target, universe}\n\t\t\t\tif _, ok := rmap[tkey]; !ok {\n\t\t\t\t\trmap[tkey] = make(ActionSet)\n\t\t\t\t}\n\t\t\t\t// copy current node's action into ancestor\n\t\t\t\trmap[tkey][tn] = tn.resolution\n\t\t\t\t// conditionally put matching conditions into\n\t\t\t\t// result\n\t\t\t\tif pure && !cs.IsEmpty() {\n\t\t\t\t\tif _, ok := result[target]; !ok {\n\t\t\t\t\t\tresult[target] = make(ActionSet)\n\t\t\t\t\t}\n\t\t\t\t\tresult[target][tn] = cs\n\t\t\t\t\tpure = target.IsContainer()\n\t\t\t\t}\n\t\t\t}\n\t\t\t// if all ancestors are pure aggregates, attach\n\t\t\t// matching conditions to self.\n\t\t\tif pure && !cs.IsEmpty() {\n\t\t\t\tif _, ok := result[tn]; !ok {\n\t\t\t\t\tresult[tn] = make(ActionSet)\n\t\t\t\t}\n\t\t\t\tresult[tn][tn] = cs\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\n\treturn result\n}\n\n// WalkActionsForCondition performs a top-down walk of the LicenseGraph\n// resolving all distributed works for `conditions`.\nfunc WalkActionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ActionSet {\n\t// amap maps 'actsOn' targets to the applicable conditions\n\t//\n\t// amap is the resulting ActionSet\n\tamap := make(ActionSet)\n\n\tfor tn := range ShippedNodes(lg) {\n\t\tif cs := conditions.Intersection(tn.resolution); !cs.IsEmpty() {\n\t\t\tamap[tn] = cs\n\t\t}\n\t}\n\n\treturn amap\n}\n"
  },
  {
    "path": "tools/compliance/policy_walk_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// Change into the cmd directory before running the tests\n\t// so they can find the testdata directory.\n\tif err := os.Chdir(\"cmd\"); err != nil {\n\t\tfmt.Printf(\"failed to change to testdata directory: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc TestWalkResolutionsForCondition(t *testing.T) {\n\ttests := []struct {\n\t\tname                string\n\t\tcondition           LicenseConditionSet\n\t\troots               []string\n\t\tedges               []annotated\n\t\texpectedResolutions []res\n\t}{\n\t\t{\n\t\t\tname:      \"firstparty\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"notice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"mitBin.meta_lic\", \"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"fponlgplnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"fponlgpldynamicnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulerestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulestaticnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulestaticrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:      \"dependentmodulenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"dependentModule.meta_lic\", \"dependentModule.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"dependentmodulerestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:      \"lgplonfpnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"lgplBin.meta_lic\", \"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"lgplonfprestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"lgplBin.meta_lic\", \"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"lgplonfpdynamicnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"lgplBin.meta_lic\", \"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"lgplonfpdynamicrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"lgplBin.meta_lic\", \"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonfpnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", \"notice|restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonfprestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplcontainernotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplContainer.meta_lic\", \"gplContainer.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplContainer.meta_lic\", \"apacheLib.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"apacheLib.meta_lic\", \"notice|restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplcontainerrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplContainer.meta_lic\", \"gplContainer.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplContainer.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gploncontainernotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheLib.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"apacheLib.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gploncontainerrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheContainer.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonbinnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonbinrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonfpdynamicnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonfpdynamicrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonfpdynamicrestrictedshipped\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplBin.meta_lic\", \"apacheLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulereversenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulereverserestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulereverserestrictedshipped\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulereversestaticnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulereversestaticrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:      \"dependentmodulereversenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"dependentmodulereverserestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:      \"dependentmodulereverserestrictedshipped\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:      \"ponrnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"proprietary.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"proprietary.meta_lic\", \"proprietary.meta_lic\", \"restricted|proprietary\"},\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"ponrrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"proprietary.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"proprietary.meta_lic\", \"proprietary.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"ponrproprietary\",\n\t\t\tcondition: ImpliesProprietary,\n\t\t\troots:     []string{\"proprietary.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"proprietary.meta_lic\", \"proprietary.meta_lic\", \"proprietary\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"ronpnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", \"restricted|proprietary\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"ronprestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"ronpproprietary\",\n\t\t\tcondition: ImpliesProprietary,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", \"proprietary\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"noticeonb_e_onotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"by_exception.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"mitBin.meta_lic\", \"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"mitBin.meta_lic\", \"by_exception.meta_lic\", \"by_exception_only\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"noticeonb_e_orestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"by_exception.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:      \"noticeonb_e_ob_e_o\",\n\t\t\tcondition: ImpliesByExceptionOnly,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"by_exception.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"mitBin.meta_lic\", \"by_exception.meta_lic\", \"by_exception_only\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"b_e_oonnoticenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"by_exception.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"by_exception.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"by_exception.meta_lic\", \"by_exception.meta_lic\", \"by_exception_only\"},\n\t\t\t\t{\"by_exception.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"b_e_oonnoticerestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"by_exception.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"by_exception.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{},\n\t\t},\n\t\t{\n\t\t\tname:      \"b_e_oonnoticeb_e_o\",\n\t\t\tcondition: ImpliesByExceptionOnly,\n\t\t\troots:     []string{\"by_exception.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"by_exception.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"by_exception.meta_lic\", \"by_exception.meta_lic\", \"by_exception_only\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"noticeonrecipnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"mplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"mitBin.meta_lic\", \"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"mitBin.meta_lic\", \"mplLib.meta_lic\", \"reciprocal\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"noticeonreciprecip\",\n\t\t\tcondition: ImpliesReciprocal,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"mplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"mitBin.meta_lic\", \"mplLib.meta_lic\", \"reciprocal\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"reciponnoticenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"mplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mplBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"mplBin.meta_lic\", \"mplBin.meta_lic\", \"reciprocal\"},\n\t\t\t\t{\"mplBin.meta_lic\", \"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"reciponnoticerecip\",\n\t\t\tcondition: ImpliesReciprocal,\n\t\t\troots:     []string{\"mplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mplBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedResolutions: []res{\n\t\t\t\t{\"mplBin.meta_lic\", \"mplBin.meta_lic\", \"reciprocal\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tlg, err := toGraph(stderr, tt.roots, tt.edges)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\texpectedRs := toResolutionSet(lg, tt.expectedResolutions)\n\t\t\tResolveTopDownConditions(lg)\n\t\t\tactualRs := WalkResolutionsForCondition(lg, tt.condition)\n\t\t\tcheckResolves(actualRs, expectedRs, t)\n\t\t})\n\t}\n}\n\nfunc TestWalkActionsForCondition(t *testing.T) {\n\ttests := []struct {\n\t\tname            string\n\t\tcondition       LicenseConditionSet\n\t\troots           []string\n\t\tedges           []annotated\n\t\texpectedActions []act\n\t}{\n\t\t{\n\t\t\tname:      \"firstparty\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"notice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"fponlgplnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"fponlgpldynamicnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulerestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulestaticnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulestaticrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{},\n\t\t},\n\t\t{\n\t\t\tname:      \"dependentmodulenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"dependentModule.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"dependentmodulerestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"dependentModule.meta_lic\", \"gplWithClasspathException.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{},\n\t\t},\n\t\t{\n\t\t\tname:      \"lgplonfpnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice|restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"lgplonfprestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"lgplonfpdynamicnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"lgplonfpdynamicrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"lgplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"lgplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"lgplBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonfpnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice|restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonfprestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplcontainernotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplContainer.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice|restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplcontainerrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplContainer.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gploncontainernotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gploncontainerrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"apacheContainer.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheContainer.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"apacheContainer.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonbinnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"notice|restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonbinrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"apacheBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonfpdynamicnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplBin.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonfpdynamicrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplBin.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"gplonfpdynamicrestrictedshipped\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplBin.meta_lic\", \"apacheLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"apacheLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"apacheLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulereversenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulereverserestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulereverserestrictedshipped\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulereversestaticnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t\t{\"apacheBin.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"independentmodulereversestaticrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"apacheBin.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{},\n\t\t},\n\t\t{\n\t\t\tname:      \"dependentmodulereversenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"permissive\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"dependentmodulereverserestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{},\n\t\t},\n\t\t{\n\t\t\tname:      \"dependentmodulereverserestrictedshipped\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplWithClasspathException.meta_lic\", \"dependentModule.meta_lic\", []string{\"dynamic\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{},\n\t\t},\n\t\t{\n\t\t\tname:      \"ponrnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"proprietary.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"proprietary.meta_lic\", \"restricted|proprietary\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"ponrrestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"proprietary.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"proprietary.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"ponrproprietary\",\n\t\t\tcondition: ImpliesProprietary,\n\t\t\troots:     []string{\"proprietary.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"proprietary.meta_lic\", \"gplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"proprietary.meta_lic\", \"proprietary\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"ronpnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"proprietary.meta_lic\", \"restricted|proprietary\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"ronprestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"gplBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"proprietary.meta_lic\", \"restricted\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"ronpproprietary\",\n\t\t\tcondition: ImpliesProprietary,\n\t\t\troots:     []string{\"gplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"gplBin.meta_lic\", \"proprietary.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"proprietary.meta_lic\", \"proprietary\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"noticeonb_e_onotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"by_exception.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"by_exception.meta_lic\", \"by_exception_only\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"noticeonb_e_orestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"by_exception.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{},\n\t\t},\n\t\t{\n\t\t\tname:      \"noticeonb_e_ob_e_o\",\n\t\t\tcondition: ImpliesByExceptionOnly,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"by_exception.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"by_exception.meta_lic\", \"by_exception_only\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"b_e_oonnoticenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"by_exception.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"by_exception.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"by_exception.meta_lic\", \"by_exception_only\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"b_e_oonnoticerestricted\",\n\t\t\tcondition: ImpliesRestricted,\n\t\t\troots:     []string{\"by_exception.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"by_exception.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{},\n\t\t},\n\t\t{\n\t\t\tname:      \"b_e_oonnoticeb_e_o\",\n\t\t\tcondition: ImpliesByExceptionOnly,\n\t\t\troots:     []string{\"by_exception.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"by_exception.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"by_exception.meta_lic\", \"by_exception_only\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"noticeonrecipnotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"mplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"mitBin.meta_lic\", \"notice\"},\n\t\t\t\t{\"mplLib.meta_lic\", \"reciprocal\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"noticeonreciprecip\",\n\t\t\tcondition: ImpliesReciprocal,\n\t\t\troots:     []string{\"mitBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mitBin.meta_lic\", \"mplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"mplLib.meta_lic\", \"reciprocal\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"reciponnoticenotice\",\n\t\t\tcondition: ImpliesNotice,\n\t\t\troots:     []string{\"mplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mplBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"mplBin.meta_lic\", \"reciprocal\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"notice\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"reciponnoticerecip\",\n\t\t\tcondition: ImpliesReciprocal,\n\t\t\troots:     []string{\"mplBin.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"mplBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"mplBin.meta_lic\", \"reciprocal\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:      \"regress-walk-twice\",\n\t\t\tcondition: ImpliesShared,\n\t\t\troots:     []string{\"mitBin.meta_lic\", \"apacheBin.meta_lic\", \"gplLib.meta_lic\"},\n\t\t\tedges: []annotated{\n\t\t\t\t{\"apacheBin.meta_lic\", \"mitLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"apacheBin.meta_lic\", \"gplLib.meta_lic\", []string{\"dynamic\"}},\n\t\t\t\t{\"mitBin.meta_lic\", \"mitLib.meta_lic\", []string{\"static\"}},\n\t\t\t\t{\"mitBin.meta_lic\", \"lgplLib.meta_lic\", []string{\"static\"}},\n\t\t\t},\n\t\t\texpectedActions: []act{\n\t\t\t\t{\"apacheBin.meta_lic\", \"restricted\"},\n\t\t\t\t{\"mitLib.meta_lic\", \"restricted|restricted_if_statically_linked\"},\n\t\t\t\t{\"gplLib.meta_lic\", \"restricted\"},\n\t\t\t\t{\"mitBin.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t\t{\"lgplLib.meta_lic\", \"restricted_if_statically_linked\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tlg, err := toGraph(stderr, tt.roots, tt.edges)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\texpectedAs := toActionSet(lg, tt.expectedActions)\n\t\t\tResolveTopDownConditions(lg)\n\t\t\tactualAs := WalkActionsForCondition(lg, tt.condition)\n\t\t\tcheckResolvesActions(lg, actualAs, expectedAs, t)\n\t\t})\n\t}\n}\n\nfunc TestWalkTopDownBreadthFirst(t *testing.T) {\n\ttests := []struct {\n\t\tname           string\n\t\troots          []string\n\t\tedges          []annotated\n\t\texpectedResult []string\n\t}{\n\t\t{\n\t\t\tname:  \"bin/bin1\",\n\t\t\troots: []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"bin/bin2\",\n\t\t\troots: []string{\"bin/bin2.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"bin/bin3\",\n\t\t\troots: []string{\"bin/bin3.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/bin/bin3.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"lib/liba.so\",\n\t\t\troots: []string{\"lib/liba.so.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"lib/libb.so\",\n\t\t\troots: []string{\"lib/libb.so.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"lib/libc.so\",\n\t\t\troots: []string{\"lib/libc.a.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"lib/libd.so\",\n\t\t\troots: []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"highest.apex\",\n\t\t\troots: []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"container.zip\",\n\t\t\troots: []string{\"container.zip.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"application\",\n\t\t\troots: []string{\"application.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/application.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"bin/bin1&lib/liba\",\n\t\t\troots: []string{\"bin/bin1.meta_lic\",\"lib/liba.so.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"bin/bin2&lib/libd\",\n\t\t\troots: []string{\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"application&bin/bin3\",\n\t\t\troots: []string{\"application.meta_lic\", \"bin/bin3.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/application.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"highest.apex&container.zip\",\n\t\t\troots: []string{\"highest.apex.meta_lic\", \"container.zip.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tactualOut := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/notice/\"+r)\n\t\t\t}\n\n\t\t\tlg, err := ReadLicenseGraph(GetFS(\"\"), stderr, rootFiles)\n\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\texpectedRst := tt.expectedResult\n\n\t\t\tWalkTopDownBreadthFirst(nil, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {\n\t\t\t\tfmt.Fprintln(actualOut, tn.Name())\n\t\t\t\treturn true\n\t\t\t})\n\n\t\t\tactualRst := strings.Split(actualOut.String(), \"\\n\")\n\n\t\t\tif len(actualRst) > 0 {\n\t\t\t\tactualRst = actualRst[:len(actualRst)-1]\n\t\t\t}\n\n\t\t\tt.Logf(\"actual nodes visited: %s\", actualOut.String())\n\t\t\tt.Logf(\"expected nodes visited: %s\", strings.Join(expectedRst, \"\\n\"))\n\n\t\t\tif len(actualRst) != len(expectedRst) {\n\t\t\t\tt.Errorf(\"WalkTopDownBreadthFirst: number of visited nodes is different: got %d, want %d\", len(actualRst), len(expectedRst))\n\t\t\t}\n\n\t\t\tfor i := 0; i < len(actualRst) && i < len(expectedRst); i++ {\n\t\t\t\tif actualRst[i] != expectedRst[i] {\n\t\t\t\t\tt.Errorf(\"WalkTopDownBreadthFirst: lines differ at index %d: got %q, want %q\", i, actualRst[i], expectedRst[i])\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(actualRst) < len(expectedRst) {\n\t\t\t\tt.Errorf(\"WalkTopDownBreadthFirst: extra lines at %d: got %q, want nothing\", len(actualRst), expectedRst[len(actualRst)])\n\t\t\t}\n\n\t\t\tif len(expectedRst) < len(actualRst) {\n\t\t\t\tt.Errorf(\"WalkTopDownBreadthFirst: missing lines at %d: got nothing, want %q\", len(expectedRst), actualRst[len(expectedRst)])\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestWalkTopDownBreadthFirstWithoutDuplicates(t *testing.T) {\n\ttests := []struct {\n\t\tname           string\n\t\troots          []string\n\t\tedges          []annotated\n\t\texpectedResult []string\n\t}{\n\t\t{\n\t\t\tname:  \"bin/bin1\",\n\t\t\troots: []string{\"bin/bin1.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"bin/bin2\",\n\t\t\troots: []string{\"bin/bin2.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"bin/bin3\",\n\t\t\troots: []string{\"bin/bin3.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/bin/bin3.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"lib/liba.so\",\n\t\t\troots: []string{\"lib/liba.so.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"lib/libb.so\",\n\t\t\troots: []string{\"lib/libb.so.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"lib/libc.so\",\n\t\t\troots: []string{\"lib/libc.a.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"lib/libd.so\",\n\t\t\troots: []string{\"lib/libd.so.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"highest.apex\",\n\t\t\troots: []string{\"highest.apex.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"container.zip\",\n\t\t\troots: []string{\"container.zip.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"application\",\n\t\t\troots: []string{\"application.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/application.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"bin/bin1&lib/liba\",\n\t\t\troots: []string{\"bin/bin1.meta_lic\", \"lib/liba.so.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"bin/bin2&lib/libd\",\n\t\t\troots: []string{\"bin/bin2.meta_lic\", \"lib/libd.so.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"application&bin/bin3\",\n\t\t\troots: []string{\"application.meta_lic\", \"bin/bin3.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/application.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin3.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"highest.apex&container.zip\",\n\t\t\troots: []string{\"highest.apex.meta_lic\", \"container.zip.meta_lic\"},\n\t\t\texpectedResult: []string{\n\t\t\t\t\"testdata/notice/highest.apex.meta_lic\",\n\t\t\t\t\"testdata/notice/container.zip.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin1.meta_lic\",\n\t\t\t\t\"testdata/notice/bin/bin2.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/liba.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libb.so.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libc.a.meta_lic\",\n\t\t\t\t\"testdata/notice/lib/libd.so.meta_lic\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tactualOut := &bytes.Buffer{}\n\n\t\t\trootFiles := make([]string, 0, len(tt.roots))\n\t\t\tfor _, r := range tt.roots {\n\t\t\t\trootFiles = append(rootFiles, \"testdata/notice/\"+r)\n\t\t\t}\n\n\t\t\tlg, err := ReadLicenseGraph(GetFS(\"\"), stderr, rootFiles)\n\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected test data error: got %s, want no error\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\texpectedRst := tt.expectedResult\n\n\t\t\t//Keeping track of the visited nodes\n\t\t\t//Only add to actualOut if not visited\n\t\t\tvisitedNodes := make(map[string]struct{})\n\t\t\tWalkTopDownBreadthFirst(nil, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {\n\t\t\t\tif _, alreadyVisited := visitedNodes[tn.Name()]; alreadyVisited {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tfmt.Fprintln(actualOut, tn.Name())\n\t\t\t\tvisitedNodes[tn.Name()] = struct{}{}\n\t\t\t\treturn true\n\t\t\t})\n\n\t\t\tactualRst := strings.Split(actualOut.String(), \"\\n\")\n\n\t\t\tif len(actualRst) > 0 {\n\t\t\t\tactualRst = actualRst[:len(actualRst)-1]\n\t\t\t}\n\n\t\t\tt.Logf(\"actual nodes visited: %s\", actualOut.String())\n\t\t\tt.Logf(\"expected nodes visited: %s\", strings.Join(expectedRst, \"\\n\"))\n\n\t\t\tif len(actualRst) != len(expectedRst) {\n\t\t\t\tt.Errorf(\"WalkTopDownBreadthFirst: number of visited nodes is different: got %d, want %d\", len(actualRst), len(expectedRst))\n\t\t\t}\n\n\t\t\tfor i := 0; i < len(actualRst) && i < len(expectedRst); i++ {\n\t\t\t\tif actualRst[i] != expectedRst[i] {\n\t\t\t\t\tt.Errorf(\"WalkTopDownBreadthFirst: lines differ at index %d: got %q, want %q\", i, actualRst[i], expectedRst[i])\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(actualRst) < len(expectedRst) {\n\t\t\t\tt.Errorf(\"WalkTopDownBreadthFirst: extra lines at %d: got %q, want nothing\", len(actualRst), expectedRst[len(actualRst)])\n\t\t\t}\n\n\t\t\tif len(expectedRst) < len(actualRst) {\n\t\t\t\tt.Errorf(\"WalkTopDownBreadthFirst: missing lines at %d: got nothing, want %q\", len(expectedRst), actualRst[len(expectedRst)])\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/projectmetadata/Android.bp",
    "content": "// Copyright (C) 2022 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nbootstrap_go_package {\n    name: \"projectmetadata-module\",\n    srcs: [\n        \"projectmetadata.go\",\n    ],\n    deps: [\n        \"compliance-test-fs-module\",\n        \"golang-protobuf-proto\",\n        \"golang-protobuf-encoding-prototext\",\n        \"project_metadata_proto\",\n    ],\n    testSrcs: [\n        \"projectmetadata_test.go\",\n    ],\n    pkgPath: \"android/soong/tools/compliance/projectmetadata\",\n}\n"
  },
  {
    "path": "tools/compliance/projectmetadata/projectmetadata.go",
    "content": "// Copyright 2022 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage projectmetadata\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"android/soong/compliance/project_metadata_proto\"\n\n\t\"google.golang.org/protobuf/encoding/prototext\"\n)\n\nvar (\n\t// ConcurrentReaders is the size of the task pool for limiting resource usage e.g. open files.\n\tConcurrentReaders = 5\n)\n\n// ProjectMetadata contains the METADATA for a git project.\ntype ProjectMetadata struct {\n\tproto project_metadata_proto.Metadata\n\n\t// project is the path to the directory containing the METADATA file.\n\tproject string\n}\n\n// ProjectUrlMap maps url type name to url value\ntype ProjectUrlMap map[string]string\n\n// DownloadUrl returns the address of a download location\nfunc (m ProjectUrlMap) DownloadUrl() string {\n\tfor _, urlType := range []string{\"GIT\", \"SVN\", \"HG\", \"DARCS\"} {\n\t\tif url, ok := m[urlType]; ok {\n\t\t\treturn url\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// String returns a string representation of the metadata for error messages.\nfunc (pm *ProjectMetadata) String() string {\n\treturn fmt.Sprintf(\"project: %q\\n%s\", pm.project, pm.proto.String())\n}\n\n// Project returns the path to the directory containing the METADATA file\nfunc (pm *ProjectMetadata) Project() string {\n\treturn pm.project\n}\n\n// Name returns the name of the project.\nfunc (pm *ProjectMetadata) Name() string {\n\treturn pm.proto.GetName()\n}\n\n// Version returns the version of the project if available.\nfunc (pm *ProjectMetadata) Version() string {\n\ttp := pm.proto.GetThirdParty()\n\tif tp != nil {\n\t\tversion := tp.GetVersion()\n\t\treturn version\n\t}\n\treturn \"\"\n}\n\n// VersionedName returns the name of the project including the version if any.\nfunc (pm *ProjectMetadata) VersionedName() string {\n\tname := pm.proto.GetName()\n\tif name != \"\" {\n\t\ttp := pm.proto.GetThirdParty()\n\t\tif tp != nil {\n\t\t\tversion := tp.GetVersion()\n\t\t\tif version != \"\" {\n\t\t\t\tif version[0] == 'v' || version[0] == 'V' {\n\t\t\t\t\treturn name + \"_\" + version\n\t\t\t\t} else {\n\t\t\t\t\treturn name + \"_v_\" + version\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn name\n\t}\n\treturn pm.proto.GetDescription()\n}\n\n// UrlsByTypeName returns a map of URLs by Type Name\nfunc (pm *ProjectMetadata) UrlsByTypeName() ProjectUrlMap {\n\ttp := pm.proto.GetThirdParty()\n\tif tp == nil {\n\t\treturn nil\n\t}\n\tif len(tp.Url) == 0 {\n\t\treturn nil\n\t}\n\turls := make(ProjectUrlMap)\n\n\tfor _, url := range tp.Url {\n\t\turi := url.GetValue()\n\t\tif uri == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\turls[project_metadata_proto.URL_Type_name[int32(url.GetType())]] = uri\n\t}\n\treturn urls\n}\n\n// projectIndex describes a project to be read; after `wg.Wait()`, will contain either\n// a `ProjectMetadata`, pm (can be nil even without error), or a non-nil `err`.\ntype projectIndex struct {\n\tproject string\n\tpath    string\n\tpm      *ProjectMetadata\n\terr     error\n\tdone    chan struct{}\n}\n\n// finish marks the task to read the `projectIndex` completed.\nfunc (pi *projectIndex) finish() {\n\tclose(pi.done)\n}\n\n// wait suspends execution until the `projectIndex` task completes.\nfunc (pi *projectIndex) wait() {\n\t<-pi.done\n}\n\n// Index reads and caches ProjectMetadata (thread safe)\ntype Index struct {\n\t// projecs maps project name to a wait group if read has already started, and\n\t// to a `ProjectMetadata` or to an `error` after the read completes.\n\tprojects sync.Map\n\n\t// task provides a fixed-size task pool to limit concurrent open files etc.\n\ttask chan bool\n\n\t// rootFS locates the root of the file system from which to read the files.\n\trootFS fs.FS\n}\n\n// NewIndex constructs a project metadata `Index` for the given file system.\nfunc NewIndex(rootFS fs.FS) *Index {\n\tix := &Index{task: make(chan bool, ConcurrentReaders), rootFS: rootFS}\n\tfor i := 0; i < ConcurrentReaders; i++ {\n\t\tix.task <- true\n\t}\n\treturn ix\n}\n\n// MetadataForProjects returns 0..n ProjectMetadata for n `projects`, or an error.\n// Each project that has a METADATA.android or a METADATA file in the root of the project will have\n// a corresponding ProjectMetadata in the result. Projects with neither file get skipped. A nil\n// result with no error indicates none of the given `projects` has a METADATA file.\n// (thread safe -- can be called concurrently from multiple goroutines)\nfunc (ix *Index) MetadataForProjects(projects ...string) ([]*ProjectMetadata, error) {\n\tif ConcurrentReaders < 1 {\n\t\treturn nil, fmt.Errorf(\"need at least one task in project metadata pool\")\n\t}\n\tif len(projects) == 0 {\n\t\treturn nil, nil\n\t}\n\t// Identify the projects that have never been read\n\tprojectsToRead := make([]*projectIndex, 0, len(projects))\n\tprojectIndexes := make([]*projectIndex, 0, len(projects))\n\tfor _, p := range projects {\n\t\tpi, loaded := ix.projects.LoadOrStore(p, &projectIndex{project: p, done: make(chan struct{})})\n\t\tif !loaded {\n\t\t\tprojectsToRead = append(projectsToRead, pi.(*projectIndex))\n\t\t}\n\t\tprojectIndexes = append(projectIndexes, pi.(*projectIndex))\n\t}\n\t// findMeta locates and reads the appropriate METADATA file, if any.\n\tfindMeta := func(pi *projectIndex) {\n\t\t<-ix.task\n\t\tdefer func() {\n\t\t\tix.task <- true\n\t\t\tpi.finish()\n\t\t}()\n\n\t\t// Support METADATA.android for projects that already have a different sort of METADATA file.\n\t\tpath := filepath.Join(pi.project, \"METADATA.android\")\n\t\tfi, err := fs.Stat(ix.rootFS, path)\n\t\tif err == nil {\n\t\t\tif fi.Mode().IsRegular() {\n\t\t\t\tix.readMetadataFile(pi, path)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// No METADATA.android try METADATA file.\n\t\tpath = filepath.Join(pi.project, \"METADATA\")\n\t\tfi, err = fs.Stat(ix.rootFS, path)\n\t\tif err == nil {\n\t\t\tif fi.Mode().IsRegular() {\n\t\t\t\tix.readMetadataFile(pi, path)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// no METADATA file exists -- leave nil and finish\n\t}\n\t// Look for the METADATA files to read, and record any missing.\n\tfor _, p := range projectsToRead {\n\t\tgo findMeta(p)\n\t}\n\t// Wait until all of the projects have been read.\n\tvar msg strings.Builder\n\tresult := make([]*ProjectMetadata, 0, len(projects))\n\tfor _, pi := range projectIndexes {\n\t\tpi.wait()\n\t\t// Combine any errors into a single error.\n\t\tif pi.err != nil {\n\t\t\tfmt.Fprintf(&msg, \"  %v\\n\", pi.err)\n\t\t} else if pi.pm != nil {\n\t\t\tresult = append(result, pi.pm)\n\t\t}\n\t}\n\tif msg.Len() > 0 {\n\t\treturn nil, fmt.Errorf(\"error reading project(s):\\n%s\", msg.String())\n\t}\n\tif len(result) == 0 {\n\t\treturn nil, nil\n\t}\n\treturn result, nil\n}\n\n// AllMetadataFiles returns the sorted list of all METADATA files read thus far.\nfunc (ix *Index) AllMetadataFiles() []string {\n\tvar files []string\n\tix.projects.Range(func(key, value any) bool {\n\t\tpi := value.(*projectIndex)\n\t\tif pi.path != \"\" {\n\t\t\tfiles = append(files, pi.path)\n\t\t}\n\t\treturn true\n\t})\n\treturn files\n}\n\n// readMetadataFile tries to read and parse a METADATA file at `path` for `project`.\nfunc (ix *Index) readMetadataFile(pi *projectIndex, path string) {\n\tf, err := ix.rootFS.Open(path)\n\tif err != nil {\n\t\tpi.err = fmt.Errorf(\"error opening project %q metadata %q: %w\", pi.project, path, err)\n\t\treturn\n\t}\n\n\t// read the file\n\tdata, err := io.ReadAll(f)\n\tif err != nil {\n\t\tpi.err = fmt.Errorf(\"error reading project %q metadata %q: %w\", pi.project, path, err)\n\t\treturn\n\t}\n\tf.Close()\n\n\tuo := prototext.UnmarshalOptions{DiscardUnknown: true}\n\tpm := &ProjectMetadata{project: pi.project}\n\terr = uo.Unmarshal(data, &pm.proto)\n\tif err != nil {\n\t\tpi.err = fmt.Errorf(`error in project %q METADATA %q: %v\n\nMETADATA and METADATA.android files must parse as text protobufs\ndefined by\n   build/soong/compliance/project_metadata_proto/project_metadata.proto\n\n* unknown fields don't matter\n* check invalid ENUM names\n* check quoting\n* check unescaped nested quotes\n* check the comment marker for protobuf is '#' not '//'\n\nif importing a library that uses a different sort of METADATA file, add\na METADATA.android file beside it to parse instead\n`, pi.project, path, err)\n\t\treturn\n\t}\n\n\tpi.path = path\n\tpi.pm = pm\n}\n"
  },
  {
    "path": "tools/compliance/projectmetadata/projectmetadata_test.go",
    "content": "// Copyright 2022 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage projectmetadata\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/compliance/project_metadata_proto\"\n\t\"android/soong/tools/compliance/testfs\"\n)\n\nconst (\n\t// EMPTY represents a METADATA file with no recognized fields\n\tEMPTY = ``\n\n\t// INVALID_NAME represents a METADATA file with the wrong type of name\n\tINVALID_NAME = `name: a library\\n`\n\n\t// INVALID_DESCRIPTION represents a METADATA file with the wrong type of description\n\tINVALID_DESCRIPTION = `description: unquoted text\\n`\n\n\t// INVALID_VERSION represents a METADATA file with the wrong type of version\n\tINVALID_VERSION = `third_party { version: 1 }`\n\n\t// MY_LIB_1_0 represents a METADATA file for version 1.0 of mylib\n\tMY_LIB_1_0 = `name: \"mylib\" description: \"my library\" third_party { version: \"1.0\" }`\n\n\t// NO_NAME_0_1 represents a METADATA file with a description but no name\n\tNO_NAME_0_1 = `description: \"my library\" third_party { version: \"0.1\" }`\n\n\t// URL values per type\n\tGIT_URL          = \"http://example.github.com/my_lib\"\n\tSVN_URL          = \"http://example.svn.com/my_lib\"\n\tHG_URL           = \"http://example.hg.com/my_lib\"\n\tDARCS_URL        = \"http://example.darcs.com/my_lib\"\n\tPIPER_URL        = \"http://google3/third_party/my/package\"\n\tHOMEPAGE_URL     = \"http://example.com/homepage\"\n\tOTHER_URL        = \"http://google.com/\"\n\tARCHIVE_URL      = \"http://ftp.example.com/\"\n\tLOCAL_SOURCE_URL = \"https://android.googlesource.com/platform/external/apache-http/\"\n)\n\n// libWithUrl returns a METADATA file with the right download url\nfunc libWithUrl(urlTypes ...string) string {\n\tvar sb strings.Builder\n\n\tfmt.Fprintln(&sb, `name: \"mylib\" description: \"my library\"\n\t third_party {\n\t \tversion: \"1.0\"`)\n\n\tfor _, urltype := range urlTypes {\n\t\tvar urlValue string\n\t\tswitch urltype {\n\t\tcase \"GIT\":\n\t\t\turlValue = GIT_URL\n\t\tcase \"SVN\":\n\t\t\turlValue = SVN_URL\n\t\tcase \"HG\":\n\t\t\turlValue = HG_URL\n\t\tcase \"DARCS\":\n\t\t\turlValue = DARCS_URL\n\t\tcase \"PIPER\":\n\t\t\turlValue = PIPER_URL\n\t\tcase \"HOMEPAGE\":\n\t\t\turlValue = HOMEPAGE_URL\n\t\tcase \"OTHER\":\n\t\t\turlValue = OTHER_URL\n\t\tcase \"ARCHIVE\":\n\t\t\turlValue = ARCHIVE_URL\n\t\tcase \"LOCAL_SOURCE\":\n\t\t\turlValue = LOCAL_SOURCE_URL\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(\"unknown url type: %q. Please update libWithUrl() in build/make/tools/compliance/projectmetadata/projectmetadata_test.go\", urltype))\n\t\t}\n\t\tfmt.Fprintf(&sb, \"  url { type: %s value: %q }\\n\", urltype, urlValue)\n\t}\n\tfmt.Fprintln(&sb, `}`)\n\n\treturn sb.String()\n}\n\nfunc TestVerifyAllUrlTypes(t *testing.T) {\n\tt.Run(\"verifyAllUrlTypes\", func(t *testing.T) {\n\t\ttypes := make([]string, 0, len(project_metadata_proto.URL_Type_value))\n\t\tfor t := range project_metadata_proto.URL_Type_value {\n\t\t\ttypes = append(types, t)\n\t\t}\n\t\tlibWithUrl(types...)\n\t})\n}\n\nfunc TestUnknownPanics(t *testing.T) {\n\tt.Run(\"Unknown panics\", func(t *testing.T) {\n\t\tdefer func() {\n\t\t\tif r := recover(); r == nil {\n\t\t\t\tt.Errorf(\"unexpected success: got no error, want panic\")\n\t\t\t}\n\t\t}()\n\t\tlibWithUrl(\"SOME WILD VALUE THAT DOES NOT EXIST\")\n\t})\n}\n\nfunc TestReadMetadataForProjects(t *testing.T) {\n\ttests := []struct {\n\t\tname          string\n\t\tfs            *testfs.TestFS\n\t\tprojects      []string\n\t\texpectedError string\n\t\texpected      []pmeta\n\t}{\n\t\t{\n\t\t\tname: \"trivial\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(\"name: \\\"Android\\\"\\n\"),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"Android\",\n\t\t\t\tname:          \"Android\",\n\t\t\t\tversion:       \"\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"versioned\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(MY_LIB_1_0),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_homepage\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"HOMEPAGE\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_git\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"GIT\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   GIT_URL,\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_svn\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"SVN\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   SVN_URL,\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_hg\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"HG\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   HG_URL,\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_darcs\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"DARCS\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   DARCS_URL,\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_piper\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"PIPER\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_other\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"OTHER\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_local_source\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"LOCAL_SOURCE\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_archive\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"ARCHIVE\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_all_downloads\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"DARCS\", \"HG\", \"SVN\", \"GIT\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   GIT_URL,\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_all_downloads_in_different_order\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"DARCS\", \"GIT\", \"SVN\", \"HG\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   GIT_URL,\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_all_but_git\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"DARCS\", \"HG\", \"SVN\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   SVN_URL,\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_all_but_git_and_svn\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"DARCS\", \"HG\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   HG_URL,\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_all_nondownloads_and_git\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"HOMEPAGE\", \"LOCAL_SOURCE\", \"PIPER\", \"ARCHIVE\", \"GIT\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   GIT_URL,\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_all_nondownloads\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl(\"HOMEPAGE\", \"LOCAL_SOURCE\", \"PIPER\", \"ARCHIVE\")),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"lib_with_all_nondownloads\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(libWithUrl()),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"versioneddesc\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(NO_NAME_0_1),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"my library\",\n\t\t\t\tname:          \"\",\n\t\t\t\tversion:       \"0.1\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"unterminated\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(\"name: \\\"Android\\n\"),\n\t\t\t},\n\t\t\tprojects:      []string{\"/a\"},\n\t\t\texpectedError: `invalid character '\\n' in string`,\n\t\t},\n\t\t{\n\t\t\tname: \"abc\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(EMPTY),\n\t\t\t\t\"/b/METADATA\": []byte(MY_LIB_1_0),\n\t\t\t\t\"/c/METADATA\": []byte(NO_NAME_0_1),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\", \"/b\", \"/c\"},\n\t\t\texpected: []pmeta{\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/a\",\n\t\t\t\t\tversionedName: \"\",\n\t\t\t\t\tname:          \"\",\n\t\t\t\t\tversion:       \"\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/b\",\n\t\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\t\tname:          \"mylib\",\n\t\t\t\t\tversion:       \"1.0\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/c\",\n\t\t\t\t\tversionedName: \"my library\",\n\t\t\t\t\tname:          \"\",\n\t\t\t\t\tversion:       \"0.1\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ab\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(EMPTY),\n\t\t\t\t\"/b/METADATA\": []byte(MY_LIB_1_0),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\", \"/b\", \"/c\"},\n\t\t\texpected: []pmeta{\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/a\",\n\t\t\t\t\tversionedName: \"\",\n\t\t\t\t\tname:          \"\",\n\t\t\t\t\tversion:       \"\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/b\",\n\t\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\t\tname:          \"mylib\",\n\t\t\t\t\tversion:       \"1.0\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ac\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(EMPTY),\n\t\t\t\t\"/c/METADATA\": []byte(NO_NAME_0_1),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\", \"/b\", \"/c\"},\n\t\t\texpected: []pmeta{\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/a\",\n\t\t\t\t\tversionedName: \"\",\n\t\t\t\t\tname:          \"\",\n\t\t\t\t\tversion:       \"\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/c\",\n\t\t\t\t\tversionedName: \"my library\",\n\t\t\t\t\tname:          \"\",\n\t\t\t\t\tversion:       \"0.1\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"bc\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/b/METADATA\": []byte(MY_LIB_1_0),\n\t\t\t\t\"/c/METADATA\": []byte(NO_NAME_0_1),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\", \"/b\", \"/c\"},\n\t\t\texpected: []pmeta{\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/b\",\n\t\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\t\tname:          \"mylib\",\n\t\t\t\t\tversion:       \"1.0\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/c\",\n\t\t\t\t\tversionedName: \"my library\",\n\t\t\t\t\tname:          \"\",\n\t\t\t\t\tversion:       \"0.1\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"wrongnametype\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(INVALID_NAME),\n\t\t\t},\n\t\t\tprojects:      []string{\"/a\"},\n\t\t\texpectedError: `invalid value for string type`,\n\t\t},\n\t\t{\n\t\t\tname: \"wrongdescriptiontype\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(INVALID_DESCRIPTION),\n\t\t\t},\n\t\t\tprojects:      []string{\"/a\"},\n\t\t\texpectedError: `invalid value for string type`,\n\t\t},\n\t\t{\n\t\t\tname: \"wrongversiontype\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(INVALID_VERSION),\n\t\t\t},\n\t\t\tprojects:      []string{\"/a\"},\n\t\t\texpectedError: `invalid value for string type`,\n\t\t},\n\t\t{\n\t\t\tname: \"wrongtype\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),\n\t\t\t},\n\t\t\tprojects:      []string{\"/a\"},\n\t\t\texpectedError: `invalid value for string type`,\n\t\t},\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\": []byte(EMPTY),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"\",\n\t\t\t\tname:          \"\",\n\t\t\t\tversion:       \"\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"emptyother\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA.bp\": []byte(EMPTY),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t},\n\t\t{\n\t\t\tname:     \"emptyfs\",\n\t\t\tfs:       &testfs.TestFS{},\n\t\t\tprojects: []string{\"/a\"},\n\t\t},\n\t\t{\n\t\t\tname: \"override\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\":         []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),\n\t\t\t\t\"/a/METADATA.android\": []byte(MY_LIB_1_0),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\"},\n\t\t\texpected: []pmeta{{\n\t\t\t\tproject:       \"/a\",\n\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\tname:          \"mylib\",\n\t\t\t\tversion:       \"1.0\",\n\t\t\t\tdownloadUrl:   \"\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"enchilada\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"/a/METADATA\":         []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),\n\t\t\t\t\"/a/METADATA.android\": []byte(EMPTY),\n\t\t\t\t\"/b/METADATA\":         []byte(MY_LIB_1_0),\n\t\t\t\t\"/c/METADATA\":         []byte(NO_NAME_0_1),\n\t\t\t},\n\t\t\tprojects: []string{\"/a\", \"/b\", \"/c\"},\n\t\t\texpected: []pmeta{\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/a\",\n\t\t\t\t\tversionedName: \"\",\n\t\t\t\t\tname:          \"\",\n\t\t\t\t\tversion:       \"\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/b\",\n\t\t\t\t\tversionedName: \"mylib_v_1.0\",\n\t\t\t\t\tname:          \"mylib\",\n\t\t\t\t\tversion:       \"1.0\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproject:       \"/c\",\n\t\t\t\t\tversionedName: \"my library\",\n\t\t\t\t\tname:          \"\",\n\t\t\t\t\tversion:       \"0.1\",\n\t\t\t\t\tdownloadUrl:   \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tix := NewIndex(tt.fs)\n\t\t\tpms, err := ix.MetadataForProjects(tt.projects...)\n\t\t\tif err != nil {\n\t\t\t\tif len(tt.expectedError) == 0 {\n\t\t\t\t\tt.Errorf(\"unexpected error: got %s, want no error\", err)\n\t\t\t\t} else if !strings.Contains(err.Error(), tt.expectedError) {\n\t\t\t\t\tt.Errorf(\"unexpected error: got %s, want %q\", err, tt.expectedError)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.Logf(\"actual %d project metadata\", len(pms))\n\t\t\tfor _, pm := range pms {\n\t\t\t\tt.Logf(\"  %v\", pm.String())\n\t\t\t}\n\t\t\tt.Logf(\"expected %d project metadata\", len(tt.expected))\n\t\t\tfor _, pm := range tt.expected {\n\t\t\t\tt.Logf(\"  %s\", pm.String())\n\t\t\t}\n\t\t\tif len(tt.expectedError) > 0 {\n\t\t\t\tt.Errorf(\"unexpected success: got no error, want %q err\", tt.expectedError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(pms) != len(tt.expected) {\n\t\t\t\tt.Errorf(\"missing project metadata: got %d project metadata, want %d\", len(pms), len(tt.expected))\n\t\t\t}\n\t\t\tfor i := 0; i < len(pms) && i < len(tt.expected); i++ {\n\t\t\t\tif msg := tt.expected[i].difference(pms[i]); msg != \"\" {\n\t\t\t\t\tt.Errorf(\"unexpected metadata starting at index %d: %s\", i, msg)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(pms) < len(tt.expected) {\n\t\t\t\tt.Errorf(\"missing metadata starting at index %d: got nothing, want %s\", len(pms), tt.expected[len(pms)].String())\n\t\t\t}\n\t\t\tif len(tt.expected) < len(pms) {\n\t\t\t\tt.Errorf(\"unexpected metadata starting at index %d: got %s, want nothing\", len(tt.expected), pms[len(tt.expected)].String())\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype pmeta struct {\n\tproject       string\n\tversionedName string\n\tname          string\n\tversion       string\n\tdownloadUrl   string\n}\n\nfunc (pm pmeta) String() string {\n\treturn fmt.Sprintf(\"project: %q versionedName: %q name: %q version: %q downloadUrl: %q\\n\", pm.project, pm.versionedName, pm.name, pm.version, pm.downloadUrl)\n}\n\nfunc (pm pmeta) equals(other *ProjectMetadata) bool {\n\tif pm.project != other.project {\n\t\treturn false\n\t}\n\tif pm.versionedName != other.VersionedName() {\n\t\treturn false\n\t}\n\tif pm.name != other.Name() {\n\t\treturn false\n\t}\n\tif pm.version != other.Version() {\n\t\treturn false\n\t}\n\tif pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (pm pmeta) difference(other *ProjectMetadata) string {\n\tif pm.equals(other) {\n\t\treturn \"\"\n\t}\n\tvar sb strings.Builder\n\tfmt.Fprintf(&sb, \"got\")\n\tif pm.project != other.project {\n\t\tfmt.Fprintf(&sb, \" project: %q\", other.project)\n\t}\n\tif pm.versionedName != other.VersionedName() {\n\t\tfmt.Fprintf(&sb, \" versionedName: %q\", other.VersionedName())\n\t}\n\tif pm.name != other.Name() {\n\t\tfmt.Fprintf(&sb, \" name: %q\", other.Name())\n\t}\n\tif pm.version != other.Version() {\n\t\tfmt.Fprintf(&sb, \" version: %q\", other.Version())\n\t}\n\tif pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() {\n\t\tfmt.Fprintf(&sb, \" downloadUrl: %q\", other.UrlsByTypeName().DownloadUrl())\n\t}\n\tfmt.Fprintf(&sb, \", want\")\n\tif pm.project != other.project {\n\t\tfmt.Fprintf(&sb, \" project: %q\", pm.project)\n\t}\n\tif pm.versionedName != other.VersionedName() {\n\t\tfmt.Fprintf(&sb, \" versionedName: %q\", pm.versionedName)\n\t}\n\tif pm.name != other.Name() {\n\t\tfmt.Fprintf(&sb, \" name: %q\", pm.name)\n\t}\n\tif pm.version != other.Version() {\n\t\tfmt.Fprintf(&sb, \" version: %q\", pm.version)\n\t}\n\tif pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() {\n\t\tfmt.Fprintf(&sb, \" downloadUrl: %q\", pm.downloadUrl)\n\t}\n\treturn sb.String()\n}\n"
  },
  {
    "path": "tools/compliance/readgraph.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"android/soong/compliance/license_metadata_proto\"\n\n\t\"google.golang.org/protobuf/encoding/prototext\"\n)\n\nvar (\n\t// ConcurrentReaders is the size of the task pool for limiting resource usage e.g. open files.\n\tConcurrentReaders = 5\n)\n\ntype globalFS struct{}\n\nvar _ fs.FS = globalFS{}\nvar _ fs.StatFS = globalFS{}\n\nfunc (s globalFS) Open(name string) (fs.File, error) {\n\treturn os.Open(name)\n}\n\nfunc (s globalFS) Stat(name string) (fs.FileInfo, error) {\n\treturn os.Stat(name)\n}\n\nvar FS globalFS\n\n// GetFS returns a filesystem for accessing files under the OUT_DIR environment variable.\nfunc GetFS(outDir string) fs.FS {\n\tif len(outDir) > 0 {\n\t\treturn os.DirFS(outDir)\n\t}\n\treturn os.DirFS(\".\")\n}\n\n// result describes the outcome of reading and parsing a single license metadata file.\ntype result struct {\n\t// file identifies the path to the license metadata file\n\tfile string\n\n\t// target contains the parsed metadata or nil if an error\n\ttarget *TargetNode\n\n\t// err is nil unless an error occurs\n\terr error\n}\n\n// receiver coordinates the tasks for reading and parsing license metadata files.\ntype receiver struct {\n\t// lg accumulates the read metadata and becomes the final resulting LicenseGraph.\n\tlg *LicenseGraph\n\n\t// rootFS locates the root of the file system from which to read the files.\n\trootFS fs.FS\n\n\t// stderr identifies the error output writer.\n\tstderr io.Writer\n\n\t// task provides a fixed-size task pool to limit concurrent open files etc.\n\ttask chan bool\n\n\t// results returns one license metadata file result at a time.\n\tresults chan *result\n\n\t// wg detects when done\n\twg sync.WaitGroup\n}\n\n// ReadLicenseGraph reads and parses `files` and their dependencies into a LicenseGraph.\n//\n// `files` become the root files of the graph for top-down walks of the graph.\nfunc ReadLicenseGraph(rootFS fs.FS, stderr io.Writer, files []string) (*LicenseGraph, error) {\n\tif len(files) == 0 {\n\t\treturn nil, fmt.Errorf(\"no license metadata to analyze\")\n\t}\n\tif ConcurrentReaders < 1 {\n\t\treturn nil, fmt.Errorf(\"need at least one task in pool\")\n\t}\n\n\tlg := newLicenseGraph()\n\tfor _, f := range files {\n\t\tif strings.HasSuffix(f, \"meta_lic\") {\n\t\t\tlg.rootFiles = append(lg.rootFiles, f)\n\t\t} else {\n\t\t\tlg.rootFiles = append(lg.rootFiles, f+\".meta_lic\")\n\t\t}\n\t}\n\n\trecv := &receiver{\n\t\tlg:      lg,\n\t\trootFS:  rootFS,\n\t\tstderr:  stderr,\n\t\ttask:    make(chan bool, ConcurrentReaders),\n\t\tresults: make(chan *result, ConcurrentReaders),\n\t\twg:      sync.WaitGroup{},\n\t}\n\tfor i := 0; i < ConcurrentReaders; i++ {\n\t\trecv.task <- true\n\t}\n\n\treadFiles := func() {\n\t\tlg.mu.Lock()\n\t\t// identify the metadata files to schedule reading tasks for\n\t\tfor _, f := range lg.rootFiles {\n\t\t\tlg.targets[f] = nil\n\t\t}\n\t\tlg.mu.Unlock()\n\n\t\t// schedule tasks to read the files\n\t\tfor _, f := range lg.rootFiles {\n\t\t\treadFile(recv, f)\n\t\t}\n\n\t\t// schedule a task to wait until finished and close the channel.\n\t\tgo func() {\n\t\t\trecv.wg.Wait()\n\t\t\tclose(recv.task)\n\t\t\tclose(recv.results)\n\t\t}()\n\t}\n\tgo readFiles()\n\n\t// tasks to read license metadata files are scheduled; read and process results from channel\n\tvar err error\n\tfor recv.results != nil {\n\t\tselect {\n\t\tcase r, ok := <-recv.results:\n\t\t\tif ok {\n\t\t\t\t// handle errors by nil'ing ls, setting err, and clobbering results channel\n\t\t\t\tif r.err != nil {\n\t\t\t\t\terr = r.err\n\t\t\t\t\tfmt.Fprintf(recv.stderr, \"%s\\n\", err.Error())\n\t\t\t\t\tlg = nil\n\t\t\t\t\trecv.results = nil\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// record the parsed metadata (guarded by mutex)\n\t\t\t\trecv.lg.mu.Lock()\n\t\t\t\tlg.targets[r.target.name] = r.target\n\t\t\t\trecv.lg.mu.Unlock()\n\t\t\t} else {\n\t\t\t\t// finished -- nil the results channel\n\t\t\t\trecv.results = nil\n\t\t\t}\n\t\t}\n\t}\n\n\tif lg != nil {\n\t\tesize := 0\n\t\tfor _, tn := range lg.targets {\n\t\t\tesize += len(tn.proto.Deps)\n\t\t}\n\t\tlg.edges = make(TargetEdgeList, 0, esize)\n\t\tfor _, tn := range lg.targets {\n\t\t\ttn.licenseConditions = LicenseConditionSetFromNames(tn.proto.LicenseConditions...)\n\t\t\terr = addDependencies(lg, tn)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error indexing dependencies for %q: %w\", tn.name, err)\n\t\t\t}\n\t\t\ttn.proto.Deps = []*license_metadata_proto.AnnotatedDependency{}\n\t\t}\n\t}\n\treturn lg, err\n\n}\n\n// targetNode contains the license metadata for a node in the license graph.\ntype targetNode struct {\n\tproto license_metadata_proto.LicenseMetadata\n\n\t// name is the path to the metadata file.\n\tname string\n\n\t// lg is the license graph the node belongs to.\n\tlg *LicenseGraph\n\n\t// edges identifies the dependencies of the target.\n\tedges TargetEdgeList\n\n\t// licenseConditions identifies the set of license conditions originating at the target node.\n\tlicenseConditions LicenseConditionSet\n\n\t// resolution identifies the set of conditions resolved by acting on the target node.\n\tresolution LicenseConditionSet\n\n\t// pure indicates whether to treat the node as a pure aggregate (no internal linkage)\n\tpure bool\n}\n\n// addDependencies converts the proto AnnotatedDependencies into `edges`\nfunc addDependencies(lg *LicenseGraph, tn *TargetNode) error {\n\ttn.edges = make(TargetEdgeList, 0, len(tn.proto.Deps))\n\tfor _, ad := range tn.proto.Deps {\n\t\tdependency := ad.GetFile()\n\t\tif len(dependency) == 0 {\n\t\t\treturn fmt.Errorf(\"missing dependency name\")\n\t\t}\n\t\tdtn, ok := lg.targets[dependency]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"unknown dependency name %q\", dependency)\n\t\t}\n\t\tif dtn == nil {\n\t\t\treturn fmt.Errorf(\"nil dependency for name %q\", dependency)\n\t\t}\n\t\tannotations := newEdgeAnnotations()\n\t\tfor _, a := range ad.Annotations {\n\t\t\t// look up a common constant annotation string from a small map\n\t\t\t// instead of creating 1000's of copies of the same 3 strings.\n\t\t\tif ann, ok := RecognizedAnnotations[a]; ok {\n\t\t\t\tannotations.annotations[ann] = struct{}{}\n\t\t\t}\n\t\t}\n\t\tedge := &TargetEdge{tn, dtn, annotations}\n\t\tlg.edges = append(lg.edges, edge)\n\t\ttn.edges = append(tn.edges, edge)\n\t}\n\treturn nil\n}\n\n// readFile is a task to read and parse a single license metadata file, and to schedule\n// additional tasks for reading and parsing dependencies as necessary.\nfunc readFile(recv *receiver, file string) {\n\trecv.wg.Add(1)\n\t<-recv.task\n\tgo func() {\n\t\tf, err := recv.rootFS.Open(file)\n\t\tif err != nil {\n\t\t\trecv.results <- &result{file, nil, fmt.Errorf(\"error opening license metadata %q: %w\", file, err)}\n\t\t\treturn\n\t\t}\n\n\t\t// read the file\n\t\tdata, err := io.ReadAll(f)\n\t\tif err != nil {\n\t\t\trecv.results <- &result{file, nil, fmt.Errorf(\"error reading license metadata %q: %w\", file, err)}\n\t\t\treturn\n\t\t}\n\t\tf.Close()\n\n\t\ttn := &TargetNode{lg: recv.lg, name: file}\n\n\t\terr = prototext.Unmarshal(data, &tn.proto)\n\t\tif err != nil {\n\t\t\trecv.results <- &result{file, nil, fmt.Errorf(\"error license metadata %q: %w\", file, err)}\n\t\t\treturn\n\t\t}\n\n\t\t// send result for this file and release task before scheduling dependencies,\n\t\t// but do not signal done to WaitGroup until dependencies are scheduled.\n\t\trecv.results <- &result{file, tn, nil}\n\t\trecv.task <- true\n\n\t\t// schedule tasks as necessary to read dependencies\n\t\tfor _, ad := range tn.proto.Deps {\n\t\t\tdependency := ad.GetFile()\n\t\t\t// decide, signal and record whether to schedule task in critical section\n\t\t\trecv.lg.mu.Lock()\n\t\t\t_, alreadyScheduled := recv.lg.targets[dependency]\n\t\t\tif !alreadyScheduled {\n\t\t\t\trecv.lg.targets[dependency] = nil\n\t\t\t}\n\t\t\trecv.lg.mu.Unlock()\n\t\t\t// schedule task to read dependency file outside critical section\n\t\t\tif !alreadyScheduled {\n\t\t\t\treadFile(recv, dependency)\n\t\t\t}\n\t\t}\n\n\t\t// signal task done after scheduling dependencies\n\t\trecv.wg.Done()\n\t}()\n}\n"
  },
  {
    "path": "tools/compliance/readgraph_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"bytes\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance/testfs\"\n)\n\nfunc TestReadLicenseGraph(t *testing.T) {\n\ttests := []struct {\n\t\tname            string\n\t\tfs              *testfs.TestFS\n\t\troots           []string\n\t\texpectedError   string\n\t\texpectedEdges   []edge\n\t\texpectedTargets []string\n\t}{\n\t\t{\n\t\t\tname: \"trivial\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"app.meta_lic\": []byte(\"package_name: \\\"Android\\\"\\n\"),\n\t\t\t},\n\t\t\troots:           []string{\"app.meta_lic\"},\n\t\t\texpectedEdges:   []edge{},\n\t\t\texpectedTargets: []string{\"app.meta_lic\"},\n\t\t},\n\t\t{\n\t\t\tname: \"unterminated\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"app.meta_lic\": []byte(\"package_name: \\\"Android\\n\"),\n\t\t\t},\n\t\t\troots:         []string{\"app.meta_lic\"},\n\t\t\texpectedError: `invalid character '\\n' in string`,\n\t\t},\n\t\t{\n\t\t\tname: \"danglingref\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"app.meta_lic\": []byte(AOSP + \"deps: {\\n  file: \\\"lib.meta_lic\\\"\\n}\\n\"),\n\t\t\t},\n\t\t\troots:         []string{\"app.meta_lic\"},\n\t\t\texpectedError: `unknown file \"lib.meta_lic\"`,\n\t\t},\n\t\t{\n\t\t\tname: \"singleedge\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"app.meta_lic\": []byte(AOSP + \"deps: {\\n  file: \\\"lib.meta_lic\\\"\\n}\\n\"),\n\t\t\t\t\"lib.meta_lic\": []byte(AOSP),\n\t\t\t},\n\t\t\troots:           []string{\"app.meta_lic\"},\n\t\t\texpectedEdges:   []edge{{\"app.meta_lic\", \"lib.meta_lic\"}},\n\t\t\texpectedTargets: []string{\"app.meta_lic\", \"lib.meta_lic\"},\n\t\t},\n\t\t{\n\t\t\tname: \"fullgraph\",\n\t\t\tfs: &testfs.TestFS{\n\t\t\t\t\"apex.meta_lic\": []byte(AOSP + \"deps: {\\n  file: \\\"app.meta_lic\\\"\\n}\\ndeps: {\\n  file: \\\"bin.meta_lic\\\"\\n}\\n\"),\n\t\t\t\t\"app.meta_lic\":  []byte(AOSP),\n\t\t\t\t\"bin.meta_lic\":  []byte(AOSP + \"deps: {\\n  file: \\\"lib.meta_lic\\\"\\n}\\n\"),\n\t\t\t\t\"lib.meta_lic\":  []byte(AOSP),\n\t\t\t},\n\t\t\troots: []string{\"apex.meta_lic\"},\n\t\t\texpectedEdges: []edge{\n\t\t\t\t{\"apex.meta_lic\", \"app.meta_lic\"},\n\t\t\t\t{\"apex.meta_lic\", \"bin.meta_lic\"},\n\t\t\t\t{\"bin.meta_lic\", \"lib.meta_lic\"},\n\t\t\t},\n\t\t\texpectedTargets: []string{\"apex.meta_lic\", \"app.meta_lic\", \"bin.meta_lic\", \"lib.meta_lic\"},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstderr := &bytes.Buffer{}\n\t\t\tlg, err := ReadLicenseGraph(tt.fs, stderr, tt.roots)\n\t\t\tif err != nil {\n\t\t\t\tif len(tt.expectedError) == 0 {\n\t\t\t\t\tt.Errorf(\"unexpected error: got %s, want no error\", err)\n\t\t\t\t} else if !strings.Contains(err.Error(), tt.expectedError) {\n\t\t\t\t\tt.Errorf(\"unexpected error: got %s, want %q\", err, tt.expectedError)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(tt.expectedError) > 0 {\n\t\t\t\tt.Errorf(\"unexpected success: got no error, want %q err\", tt.expectedError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif lg == nil {\n\t\t\t\tt.Errorf(\"missing license graph: got nil, want license graph\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tactualEdges := make([]edge, 0)\n\t\t\tfor _, e := range lg.Edges() {\n\t\t\t\tactualEdges = append(actualEdges, edge{e.Target().Name(), e.Dependency().Name()})\n\t\t\t}\n\t\t\tsort.Sort(byEdge(tt.expectedEdges))\n\t\t\tsort.Sort(byEdge(actualEdges))\n\t\t\tt.Logf(\"actualEdges:\")\n\t\t\tfor _, edge := range actualEdges {\n\t\t\t\tt.Logf(\"  %s\", edge.String())\n\t\t\t}\n\t\t\tt.Logf(\"expectedEdges:\")\n\t\t\tfor _, edge := range actualEdges {\n\t\t\t\tt.Logf(\"  %s\", edge.String())\n\t\t\t}\n\t\t\tif len(tt.expectedEdges) != len(actualEdges) {\n\t\t\t\tt.Errorf(\"len(actualEdges): got %d, want %d\", len(actualEdges), len(tt.expectedEdges))\n\t\t\t} else {\n\t\t\t\tfor i := 0; i < len(actualEdges); i++ {\n\t\t\t\t\tif tt.expectedEdges[i] != actualEdges[i] {\n\t\t\t\t\t\tt.Errorf(\"actualEdges[%d]: got %s, want %s\", i, actualEdges[i], tt.expectedEdges[i])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tactualTargets := make([]string, 0)\n\t\t\tfor _, t := range lg.Targets() {\n\t\t\t\tactualTargets = append(actualTargets, t.Name())\n\t\t\t}\n\t\t\tsort.Strings(tt.expectedTargets)\n\t\t\tsort.Strings(actualTargets)\n\n\t\t\tt.Logf(\"actualTargets: %v\", actualTargets)\n\t\t\tt.Logf(\"expectedTargets: %v\", tt.expectedTargets)\n\n\t\t\tif len(tt.expectedTargets) != len(actualTargets) {\n\t\t\t\tt.Errorf(\"len(actualTargets): got %d, want %d\", len(actualTargets), len(tt.expectedTargets))\n\t\t\t} else {\n\t\t\t\tfor i := 0; i < len(actualTargets); i++ {\n\t\t\t\t\tif tt.expectedTargets[i] != actualTargets[i] {\n\t\t\t\t\t\tt.Errorf(\"actualTargets[%d]: got %s, want %s\", i, actualTargets[i], tt.expectedTargets[i])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/resolution.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n)\n\n// Resolution describes an action to resolve one or more license conditions.\n//\n// `AttachesTo` identifies the target node that when distributed triggers the action.\n// `ActsOn` identifies the target node that is the object of the action.\n// `Resolves` identifies one or more license conditions that the action resolves.\n//\n// e.g. Suppose an MIT library is linked to a binary that also links to GPL code.\n//\n// A resolution would attach to the binary to share (act on) the MIT library to\n// resolve the restricted condition originating from the GPL code.\ntype Resolution struct {\n\tattachesTo, actsOn *TargetNode\n\tcs                 LicenseConditionSet\n}\n\n// AttachesTo returns the target node the resolution attaches to.\nfunc (r Resolution) AttachesTo() *TargetNode {\n\treturn r.attachesTo\n}\n\n// ActsOn returns the target node that must be acted on to resolve the condition.\n//\n// i.e. The node for which notice must be given or whose source must be shared etc.\nfunc (r Resolution) ActsOn() *TargetNode {\n\treturn r.actsOn\n}\n\n// Resolves returns the set of license condition the resolution satisfies.\nfunc (r Resolution) Resolves() LicenseConditionSet {\n\treturn r.cs\n}\n\n// asString returns a string representation of the resolution.\nfunc (r Resolution) asString() string {\n\tvar sb strings.Builder\n\tnames := r.cs.Names()\n\tsort.Strings(names)\n\tfmt.Fprintf(&sb, \"%s -> %s{%s}\", r.attachesTo.name, r.actsOn.name, strings.Join(names, \", \"))\n\treturn sb.String()\n}\n\n// ResolutionList represents a partial order of Resolutions ordered by\n// AttachesTo() and ActsOn() leaving `Resolves()` unordered.\ntype ResolutionList []Resolution\n\n// Len returns the count of elements in the list.\nfunc (l ResolutionList) Len() int { return len(l) }\n\n// Swap rearranges 2 elements so that each occupies the other's former position.\nfunc (l ResolutionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }\n\n// Less returns true when the `i`th element is lexicographically less than tht `j`th.\nfunc (l ResolutionList) Less(i, j int) bool {\n\tif l[i].attachesTo.name == l[j].attachesTo.name {\n\t\treturn l[i].actsOn.name < l[j].actsOn.name\n\t}\n\treturn l[i].attachesTo.name < l[j].attachesTo.name\n}\n\n// String returns a string representation of the list.\nfunc (rl ResolutionList) String() string {\n\tvar sb strings.Builder\n\tfmt.Fprintf(&sb, \"[\")\n\tsep := \"\"\n\tfor _, r := range rl {\n\t\tfmt.Fprintf(&sb, \"%s%s\", sep, r.asString())\n\t\tsep = \", \"\n\t}\n\tfmt.Fprintf(&sb, \"]\")\n\treturn sb.String()\n}\n\n// AllConditions returns the union of all license conditions resolved by any\n// element of the list.\nfunc (rl ResolutionList) AllConditions() LicenseConditionSet {\n\tresult := NewLicenseConditionSet()\n\tfor _, r := range rl {\n\t\tresult = result.Union(r.cs)\n\t}\n\treturn result\n}\n\n// ByName returns the sub-list of resolutions resolving conditions matching\n// `names`.\nfunc (rl ResolutionList) Matching(conditions LicenseConditionSet) ResolutionList {\n\tresult := make(ResolutionList, 0, rl.CountMatching(conditions))\n\tfor _, r := range rl {\n\t\tif r.Resolves().MatchesAnySet(conditions) {\n\t\t\tresult = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.MatchingAnySet(conditions)})\n\t\t}\n\t}\n\treturn result\n}\n\n// CountMatching returns the number of resolutions resolving conditions matching\n// `conditions`.\nfunc (rl ResolutionList) CountMatching(conditions LicenseConditionSet) int {\n\tc := 0\n\tfor _, r := range rl {\n\t\tif r.Resolves().MatchesAnySet(conditions) {\n\t\t\tc++\n\t\t}\n\t}\n\treturn c\n}\n\n// ByActsOn returns the sub-list of resolutions matching `actsOn`.\nfunc (rl ResolutionList) ByActsOn(actsOn *TargetNode) ResolutionList {\n\tresult := make(ResolutionList, 0, rl.CountByActsOn(actsOn))\n\tfor _, r := range rl {\n\t\tif r.actsOn == actsOn {\n\t\t\tresult = append(result, r)\n\t\t}\n\t}\n\treturn result\n}\n\n// CountByActsOn returns the number of resolutions matching `actsOn`.\nfunc (rl ResolutionList) CountByActsOn(actsOn *TargetNode) int {\n\tc := 0\n\tfor _, r := range rl {\n\t\tif r.actsOn == actsOn {\n\t\t\tc++\n\t\t}\n\t}\n\treturn c\n}\n"
  },
  {
    "path": "tools/compliance/resolutionset.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// ResolutionSet describes an immutable set of targets and the license\n// conditions each target must satisfy or \"resolve\" in a specific context.\n//\n// Ultimately, the purpose of recording the license metadata and building a\n// license graph is to identify, describe, and verify the necessary actions or\n// operations for compliance policy.\n//\n// i.e. What is the source-sharing policy? Has it been met? Meet it.\n//\n// i.e. Are there incompatible policy requirements? Such as a source-sharing\n// policy applied to code that policy also says may not be shared? If so, stop\n// and remove the dependencies that create the situation.\n//\n// The ResolutionSet is the base unit for mapping license conditions to the\n// targets triggering some necessary action per policy. Different ResolutionSet\n// values may be calculated for different contexts.\n//\n// e.g. Suppose an unencumbered binary links in a notice .a library.\n//\n// An \"unencumbered\" condition would originate from the binary, and a \"notice\"\n// condition would originate from the .a library. A ResolutionSet for the\n// context of the Notice policy might attach both conditions to the binary to\n// act on the origin of each condition. By attaching the notice condition to\n// the binary, the ResolutionSet stipulates the policy that the release of the\n// unencumbered binary must provide suitable notice for the .a library.\n//\n// The resulting ResolutionSet could be used for building a notice file, for\n// validating that a suitable notice has been built into the distribution, or\n// for reporting what notices need to be given.\n//\n// The action is defined by the context. In the above example, the action is\n// providing notice for the module acted on. In another context, the action\n// might be sharing the source-code or preserving the privacy of the module\n// acted on.\ntype ResolutionSet map[*TargetNode]ActionSet\n\n// AttachesTo identifies the list of targets triggering action to resolve\n// conditions. (unordered)\nfunc (rs ResolutionSet) AttachesTo() TargetNodeList {\n\tresult := make(TargetNodeList, 0, len(rs))\n\tfor attachesTo := range rs {\n\t\tresult = append(result, attachesTo)\n\t}\n\treturn result\n}\n\n// AttachesToTarget returns true if the set contains conditions that\n// are `attachedTo`.\nfunc (rs ResolutionSet) AttachesToTarget(target *TargetNode) bool {\n\t_, isPresent := rs[target]\n\treturn isPresent\n}\n\n// IsPureAggregate returns true if `target`, which must be in\n// `AttachesTo()` resolves to a pure aggregate in the resolution.\nfunc (rs ResolutionSet) IsPureAggregate(target *TargetNode) bool {\n\t_, isPresent := rs[target]\n\tif !isPresent {\n\t\tpanic(fmt.Errorf(\"ResolutionSet.IsPureAggregate(%s): not attached to %s\", target.Name(), target.Name()))\n\t}\n\treturn target.pure\n}\n\n// Resolutions returns the list of resolutions that `attachedTo`\n// target must resolve. Returns empty list if no conditions apply.\nfunc (rs ResolutionSet) Resolutions(attachesTo *TargetNode) ResolutionList {\n\tas, ok := rs[attachesTo]\n\tif !ok {\n\t\treturn nil\n\t}\n\tresult := make(ResolutionList, 0, len(as))\n\tfor actsOn, cs := range as {\n\t\tresult = append(result, Resolution{attachesTo, actsOn, cs})\n\t}\n\treturn result\n}\n\n// AllActions returns the set of actions required to resolve the set omitting\n// the attachment.\nfunc (rs ResolutionSet) AllActions() ActionSet {\n\tresult := make(ActionSet)\n\tfor _, as := range rs {\n\t\tfor actsOn, cs := range as {\n\t\t\tif _, ok := result[actsOn]; ok {\n\t\t\t\tresult[actsOn] = cs.Union(result[actsOn])\n\t\t\t} else {\n\t\t\t\tresult[actsOn] = cs\n\t\t\t}\n\t\t}\n\t}\n\treturn result\n}\n\n// String returns a human-readable string representation of the set.\nfunc (rs ResolutionSet) String() string {\n\tvar sb strings.Builder\n\tfmt.Fprintf(&sb, \"{\")\n\tsep := \"\"\n\tfor attachesTo, as := range rs {\n\t\tfmt.Fprintf(&sb, \"%s%s -> %s\", sep, attachesTo.Name(), as.String())\n\t\tsep = \", \"\n\t}\n\tfmt.Fprintf(&sb, \"}\")\n\treturn sb.String()\n}\n\n// ActionSet identifies a set of targets to act on and the license conditions\n// the action will resolve.\ntype ActionSet map[*TargetNode]LicenseConditionSet\n\n// String returns a human-readable string representation of the set.\nfunc (as ActionSet) String() string {\n\tvar sb strings.Builder\n\tfmt.Fprintf(&sb, \"{\")\n\tsep := \"\"\n\tfor actsOn, cs := range as {\n\t\tfmt.Fprintf(&sb, \"%s%s%s\", sep, actsOn.Name(), cs.String())\n\t\tsep = \", \"\n\t}\n\tfmt.Fprintf(&sb, \"}\")\n\treturn sb.String()\n}\n"
  },
  {
    "path": "tools/compliance/resolutionset_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"sort\"\n\t\"testing\"\n)\n\nvar (\n\t// bottomUp describes the bottom-up resolve of a hypothetical graph\n\t// the graph has a container image, a couple binaries, and a couple\n\t// libraries. bin1 statically links lib1 and dynamically links lib2;\n\t// bin2 dynamically links lib1 and statically links lib2.\n\t// binc represents a compiler or other toolchain binary used for\n\t// building the other binaries.\n\tbottomUp = []res{\n\t\t{\"image\", \"image\", \"notice|restricted\"},\n\t\t{\"image\", \"bin1\", \"reciprocal\"},\n\t\t{\"image\", \"bin2\", \"restricted\"},\n\t\t{\"image\", \"lib1\", \"notice\"},\n\t\t{\"image\", \"lib2\", \"notice\"},\n\t\t{\"binc\", \"binc\", \"proprietary\"},\n\t\t{\"bin1\", \"bin1\", \"reciprocal\"},\n\t\t{\"bin1\", \"lib1\", \"notice\"},\n\t\t{\"bin2\", \"bin2\", \"restricted\"},\n\t\t{\"bin2\", \"lib2\", \"notice\"},\n\t\t{\"lib1\", \"lib1\", \"notice\"},\n\t\t{\"lib2\", \"lib2\", \"notice\"},\n\t}\n\n\t// notice describes bottomUp after a top-down notice resolve.\n\tnotice = []res{\n\t\t{\"image\", \"image\", \"notice|restricted\"},\n\t\t{\"image\", \"bin1\", \"reciprocal\"},\n\t\t{\"image\", \"bin2\", \"restricted\"},\n\t\t{\"image\", \"lib1\", \"notice\"},\n\t\t{\"image\", \"lib2\", \"notice|restricted\"},\n\t\t{\"bin1\", \"bin1\", \"reciprocal\"},\n\t\t{\"bin1\", \"lib1\", \"notice\"},\n\t\t{\"bin2\", \"bin2\", \"restricted\"},\n\t\t{\"bin2\", \"lib2\", \"notice|restricted\"},\n\t\t{\"lib1\", \"lib1\", \"notice\"},\n\t\t{\"lib2\", \"lib2\", \"notice\"},\n\t}\n\n\t// share describes bottomUp after a top-down share resolve.\n\tshare = []res{\n\t\t{\"image\", \"image\", \"restricted\"},\n\t\t{\"image\", \"bin1\", \"reciprocal\"},\n\t\t{\"image\", \"bin2\", \"restricted\"},\n\t\t{\"image\", \"lib2\", \"restricted\"},\n\t\t{\"bin1\", \"bin1\", \"reciprocal\"},\n\t\t{\"bin2\", \"bin2\", \"restricted\"},\n\t\t{\"bin2\", \"lib2\", \"restricted\"},\n\t}\n\n\t// proprietary describes bottomUp after a top-down proprietary resolve.\n\t// Note that the proprietary binc is not reachable through the toolchain\n\t// dependency.\n\tproprietary = []res{}\n)\n\nfunc TestResolutionSet_AttachesTo(t *testing.T) {\n\tlg := newLicenseGraph()\n\n\trsShare := toResolutionSet(lg, share)\n\n\tt.Logf(\"checking resolution set %s\", rsShare.String())\n\n\tactual := rsShare.AttachesTo().Names()\n\tsort.Strings(actual)\n\n\texpected := []string{\"bin1\", \"bin2\", \"image\"}\n\n\tt.Logf(\"actual rsShare: %v\", actual)\n\tt.Logf(\"expected rsShare: %v\", expected)\n\n\tif len(actual) != len(expected) {\n\t\tt.Errorf(\"rsShare: wrong number of targets: got %d, want %d\", len(actual), len(expected))\n\t\treturn\n\t}\n\tfor i := 0; i < len(actual); i++ {\n\t\tif actual[i] != expected[i] {\n\t\t\tt.Errorf(\"rsShare: unexpected target at index %d: got %s, want %s\", i, actual[i], expected[i])\n\t\t}\n\t}\n\n\trsPrivate := toResolutionSet(lg, proprietary)\n\tactual = rsPrivate.AttachesTo().Names()\n\texpected = []string{}\n\n\tt.Logf(\"actual rsPrivate: %v\", actual)\n\tt.Logf(\"expected rsPrivate: %v\", expected)\n\n\tif len(actual) != len(expected) {\n\t\tt.Errorf(\"rsPrivate: wrong number of targets: got %d, want %d\", len(actual), len(expected))\n\t\treturn\n\t}\n\tfor i := 0; i < len(actual); i++ {\n\t\tif actual[i] != expected[i] {\n\t\t\tt.Errorf(\"rsPrivate: unexpected target at index %d: got %s, want %s\", i, actual[i], expected[i])\n\t\t}\n\t}\n}\n\nfunc TestResolutionSet_AttachesToTarget(t *testing.T) {\n\tlg := newLicenseGraph()\n\n\trsShare := toResolutionSet(lg, share)\n\n\tt.Logf(\"checking resolution set %s\", rsShare.String())\n\n\tif rsShare.AttachesToTarget(newTestNode(lg, \"binc\")) {\n\t\tt.Errorf(\"actual.AttachesToTarget(\\\"binc\\\"): got true, want false\")\n\t}\n\tif !rsShare.AttachesToTarget(newTestNode(lg, \"image\")) {\n\t\tt.Errorf(\"actual.AttachesToTarget(\\\"image\\\"): got false want true\")\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/test_util.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage compliance\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"android/soong/tools/compliance/testfs\"\n)\n\nconst (\n\t// AOSP starts a test metadata file for Android Apache-2.0 licensing.\n\tAOSP = `` +\n\t\t`package_name: \"Android\"\nlicense_kinds: \"SPDX-license-identifier-Apache-2.0\"\nlicense_conditions: \"notice\"\n`\n\n\t// GPL starts a test metadata file for GPL 2.0 licensing.\n\tGPL = `` +\n\t\t`package_name: \"Free Software\"\nlicense_kinds: \"SPDX-license-identifier-GPL-2.0\"\nlicense_conditions: \"restricted\"\n`\n\n\t// Classpath starts a test metadata file for GPL 2.0 with classpath exception licensing.\n\tClasspath = `` +\n\t\t`package_name: \"Free Software\"\nlicense_kinds: \"SPDX-license-identifier-GPL-2.0-with-classpath-exception\"\nlicense_conditions: \"permissive\"\n`\n\n\t// DependentModule starts a test metadata file for a module in the same package as `Classpath`.\n\tDependentModule = `` +\n\t\t`package_name: \"Free Software\"\nlicense_kinds: \"SPDX-license-identifier-MIT\"\nlicense_conditions: \"notice\"\n`\n\n\t// LGPL starts a test metadata file for a module with LGPL 2.0 licensing.\n\tLGPL = `` +\n\t\t`package_name: \"Free Library\"\nlicense_kinds: \"SPDX-license-identifier-LGPL-2.0\"\nlicense_conditions: \"restricted_if_statically_linked\"\n`\n\n\t// MPL starts a test metadata file for a module with MPL 2.0 reciprical licensing.\n\tMPL = `` +\n\t\t`package_name: \"Reciprocal\"\nlicense_kinds: \"SPDX-license-identifier-MPL-2.0\"\nlicense_conditions: \"reciprocal\"\n`\n\n\t// MIT starts a test metadata file for a module with generic notice (MIT) licensing.\n\tMIT = `` +\n\t\t`package_name: \"Android\"\nlicense_kinds: \"SPDX-license-identifier-MIT\"\nlicense_conditions: \"notice\"\n`\n\n\t// Proprietary starts a test metadata file for a module with proprietary licensing.\n\tProprietary = `` +\n\t\t`package_name: \"Android\"\nlicense_kinds: \"legacy_proprietary\"\nlicense_conditions: \"proprietary\"\n`\n\n\t// ByException starts a test metadata file for a module with by_exception_only licensing.\n\tByException = `` +\n\t\t`package_name: \"Special\"\nlicense_kinds: \"legacy_by_exception_only\"\nlicense_conditions: \"by_exception_only\"\n`\n)\n\nvar (\n\t// meta maps test file names to metadata file content without dependencies.\n\tmeta = map[string]string{\n\t\t\"apacheBin.meta_lic\":                 AOSP,\n\t\t\"apacheLib.meta_lic\":                 AOSP,\n\t\t\"apacheContainer.meta_lic\":           AOSP + \"is_container: true\\n\",\n\t\t\"dependentModule.meta_lic\":           DependentModule,\n\t\t\"gplWithClasspathException.meta_lic\": Classpath,\n\t\t\"gplBin.meta_lic\":                    GPL,\n\t\t\"gplLib.meta_lic\":                    GPL,\n\t\t\"gplContainer.meta_lic\":              GPL + \"is_container: true\\n\",\n\t\t\"lgplBin.meta_lic\":                   LGPL,\n\t\t\"lgplLib.meta_lic\":                   LGPL,\n\t\t\"mitBin.meta_lic\":                    MIT,\n\t\t\"mitLib.meta_lic\":                    MIT,\n\t\t\"mplBin.meta_lic\":                    MPL,\n\t\t\"mplLib.meta_lic\":                    MPL,\n\t\t\"proprietary.meta_lic\":               Proprietary,\n\t\t\"by_exception.meta_lic\":              ByException,\n\t}\n)\n\n// newTestNode constructs a test node in the license graph.\nfunc newTestNode(lg *LicenseGraph, targetName string) *TargetNode {\n\tif tn, alreadyExists := lg.targets[targetName]; alreadyExists {\n\t\treturn tn\n\t}\n\ttn := &TargetNode{name: targetName}\n\tlg.targets[targetName] = tn\n\treturn tn\n}\n\n// newTestCondition constructs a test license condition.\nfunc newTestCondition(conditionName string) LicenseCondition {\n\tcl := LicenseConditionSetFromNames(conditionName).AsList()\n\tif len(cl) == 0 {\n\t\tpanic(fmt.Errorf(\"attempt to create unrecognized condition: %q\", conditionName))\n\t} else if len(cl) != 1 {\n\t\tpanic(fmt.Errorf(\"unexpected multiple conditions from condition name: %q: got %d, want 1\", conditionName, len(cl)))\n\t}\n\tlc := cl[0]\n\treturn lc\n}\n\n// newTestConditionSet constructs a test license condition set.\nfunc newTestConditionSet(conditionName []string) LicenseConditionSet {\n\tcs := LicenseConditionSetFromNames(conditionName...)\n\tif cs.IsEmpty() {\n\t\tpanic(fmt.Errorf(\"attempt to create unrecognized condition: %q\", conditionName))\n\t}\n\treturn cs\n}\n\n// edge describes test data edges to define test graphs.\ntype edge struct {\n\ttarget, dep string\n}\n\n// String returns a string representation of the edge.\nfunc (e edge) String() string {\n\treturn e.target + \" -> \" + e.dep\n}\n\n// byEdge orders edges by target then dep name then annotations.\ntype byEdge []edge\n\n// Len returns the count of elements in the slice.\nfunc (l byEdge) Len() int { return len(l) }\n\n// Swap rearranges 2 elements of the slice so that each occupies the other's\n// former position.\nfunc (l byEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] }\n\n// Less returns true when the `i`th element is lexicographically less than\n// the `j`th element.\nfunc (l byEdge) Less(i, j int) bool {\n\tif l[i].target == l[j].target {\n\t\treturn l[i].dep < l[j].dep\n\t}\n\treturn l[i].target < l[j].target\n}\n\n// annotated describes annotated test data edges to define test graphs.\ntype annotated struct {\n\ttarget, dep string\n\tannotations []string\n}\n\nfunc (e annotated) String() string {\n\tif e.annotations != nil {\n\t\treturn e.target + \" -> \" + e.dep + \" [\" + strings.Join(e.annotations, \", \") + \"]\"\n\t}\n\treturn e.target + \" -> \" + e.dep\n}\n\nfunc (e annotated) IsEqualTo(other annotated) bool {\n\tif e.target != other.target {\n\t\treturn false\n\t}\n\tif e.dep != other.dep {\n\t\treturn false\n\t}\n\tif len(e.annotations) != len(other.annotations) {\n\t\treturn false\n\t}\n\ta1 := append([]string{}, e.annotations...)\n\ta2 := append([]string{}, other.annotations...)\n\tfor i := 0; i < len(a1); i++ {\n\t\tif a1[i] != a2[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// toGraph converts a list of roots and a list of annotated edges into a test license graph.\nfunc toGraph(stderr io.Writer, roots []string, edges []annotated) (*LicenseGraph, error) {\n\tdeps := make(map[string][]annotated)\n\tfor _, root := range roots {\n\t\tdeps[root] = []annotated{}\n\t}\n\tfor _, edge := range edges {\n\t\tif prev, ok := deps[edge.target]; ok {\n\t\t\tdeps[edge.target] = append(prev, edge)\n\t\t} else {\n\t\t\tdeps[edge.target] = []annotated{edge}\n\t\t}\n\t\tif _, ok := deps[edge.dep]; !ok {\n\t\t\tdeps[edge.dep] = []annotated{}\n\t\t}\n\t}\n\tfs := make(testfs.TestFS)\n\tfor file, edges := range deps {\n\t\tbody := meta[file]\n\t\tfor _, edge := range edges {\n\t\t\tbody += fmt.Sprintf(\"deps: {\\n  file: %q\\n\", edge.dep)\n\t\t\tfor _, ann := range edge.annotations {\n\t\t\t\tbody += fmt.Sprintf(\"  annotations: %q\\n\", ann)\n\t\t\t}\n\t\t\tbody += \"}\\n\"\n\t\t}\n\t\tfs[file] = []byte(body)\n\t}\n\n\treturn ReadLicenseGraph(&fs, stderr, roots)\n}\n\n// logGraph outputs a representation of the graph to a test log.\nfunc logGraph(lg *LicenseGraph, t *testing.T) {\n\tt.Logf(\"license graph:\")\n\tt.Logf(\"  targets:\")\n\tfor _, target := range lg.Targets() {\n\t\tt.Logf(\"    %s%s in package %q\", target.Name(), target.LicenseConditions().String(), target.PackageName())\n\t}\n\tt.Logf(\"  /targets\")\n\tt.Logf(\"  edges:\")\n\tfor _, edge := range lg.Edges() {\n\t\tt.Logf(\"    %s\", edge.String())\n\t}\n\tt.Logf(\"  /edges\")\n\tt.Logf(\"/license graph\")\n}\n\n// byAnnotatedEdge orders edges by target then dep name then annotations.\ntype byAnnotatedEdge []annotated\n\nfunc (l byAnnotatedEdge) Len() int      { return len(l) }\nfunc (l byAnnotatedEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] }\nfunc (l byAnnotatedEdge) Less(i, j int) bool {\n\tif l[i].target == l[j].target {\n\t\tif l[i].dep == l[j].dep {\n\t\t\tai := append([]string{}, l[i].annotations...)\n\t\t\taj := append([]string{}, l[j].annotations...)\n\t\t\tsort.Strings(ai)\n\t\t\tsort.Strings(aj)\n\t\t\tfor k := 0; k < len(ai) && k < len(aj); k++ {\n\t\t\t\tif ai[k] == aj[k] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn ai[k] < aj[k]\n\t\t\t}\n\t\t\treturn len(ai) < len(aj)\n\t\t}\n\t\treturn l[i].dep < l[j].dep\n\t}\n\treturn l[i].target < l[j].target\n}\n\n// act describes test data resolution actions to define test action sets.\ntype act struct {\n\tactsOn, condition string\n}\n\n// String returns a human-readable string representing the test action.\nfunc (a act) String() string {\n\treturn fmt.Sprintf(\"%s{%s}\", a.actsOn, a.condition)\n}\n\n// toActionSet converts a list of act test data into a test action set.\nfunc toActionSet(lg *LicenseGraph, data []act) ActionSet {\n\tas := make(ActionSet)\n\tfor _, a := range data {\n\t\tactsOn := newTestNode(lg, a.actsOn)\n\t\tcs := newTestConditionSet(strings.Split(a.condition, \"|\"))\n\t\tas[actsOn] = cs\n\t}\n\treturn as\n}\n\n// res describes test data resolutions to define test resolution sets.\ntype res struct {\n\tattachesTo, actsOn, condition string\n}\n\n// toResolutionSet converts a list of res test data into a test resolution set.\nfunc toResolutionSet(lg *LicenseGraph, data []res) ResolutionSet {\n\trmap := make(ResolutionSet)\n\tfor _, r := range data {\n\t\tattachesTo := newTestNode(lg, r.attachesTo)\n\t\tactsOn := newTestNode(lg, r.actsOn)\n\t\tif _, ok := rmap[attachesTo]; !ok {\n\t\t\trmap[attachesTo] = make(ActionSet)\n\t\t}\n\t\tcs := newTestConditionSet(strings.Split(r.condition, \"|\"))\n\t\trmap[attachesTo][actsOn] |= cs\n\t}\n\treturn rmap\n}\n\n// tcond associates a target name with '|' separated string conditions.\ntype tcond struct {\n\ttarget, conditions string\n}\n\n// action represents a single element of an ActionSet for testing.\ntype action struct {\n\ttarget *TargetNode\n\tcs     LicenseConditionSet\n}\n\n// String returns a human-readable string representation of the action.\nfunc (a action) String() string {\n\treturn fmt.Sprintf(\"%s%s\", a.target.Name(), a.cs.String())\n}\n\n// actionList represents an array of actions and a total order defined by\n// target name followed by license condition set.\ntype actionList []action\n\n// String returns a human-readable string representation of the list.\nfunc (l actionList) String() string {\n\tvar sb strings.Builder\n\tfmt.Fprintf(&sb, \"[\")\n\tsep := \"\"\n\tfor _, a := range l {\n\t\tfmt.Fprintf(&sb, \"%s%s\", sep, a.String())\n\t\tsep = \", \"\n\t}\n\tfmt.Fprintf(&sb, \"]\")\n\treturn sb.String()\n}\n\n// Len returns the count of elements in the slice.\nfunc (l actionList) Len() int { return len(l) }\n\n// Swap rearranges 2 elements of the slice so that each occupies the other's\n// former position.\nfunc (l actionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }\n\n// Less returns true when the `i`th element is lexicographically less than\n// the `j`th element.\nfunc (l actionList) Less(i, j int) bool {\n\tif l[i].target == l[j].target {\n\t\treturn l[i].cs < l[j].cs\n\t}\n\treturn l[i].target.Name() < l[j].target.Name()\n}\n\n// asActionList represents the resolved license conditions in a license graph\n// as an actionList for comparison in a test.\nfunc asActionList(lg *LicenseGraph) actionList {\n\tresult := make(actionList, 0, len(lg.targets))\n\tfor _, target := range lg.targets {\n\t\tcs := target.resolution\n\t\tif cs.IsEmpty() {\n\t\t\tcontinue\n\t\t}\n\t\tresult = append(result, action{target, cs})\n\t}\n\treturn result\n}\n\n// toActionList converts an array of tcond into an actionList for comparison\n// in a test.\nfunc toActionList(lg *LicenseGraph, actions []tcond) actionList {\n\tresult := make(actionList, 0, len(actions))\n\tfor _, actn := range actions {\n\t\ttarget := newTestNode(lg, actn.target)\n\t\tcs := NewLicenseConditionSet()\n\t\tfor _, name := range strings.Split(actn.conditions, \"|\") {\n\t\t\tlc, ok := RecognizedConditionNames[name]\n\t\t\tif !ok {\n\t\t\t\tpanic(fmt.Errorf(\"Unrecognized test condition name: %q\", name))\n\t\t\t}\n\t\t\tcs = cs.Plus(lc)\n\t\t}\n\t\tresult = append(result, action{target, cs})\n\t}\n\treturn result\n}\n\n// confl defines test data for a SourceSharePrivacyConflict as a target name,\n// source condition name, privacy condition name triple.\ntype confl struct {\n\tsourceNode, share, privacy string\n}\n\n// toConflictList converts confl test data into an array of\n// SourceSharePrivacyConflict for comparison in a test.\nfunc toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict {\n\tresult := make([]SourceSharePrivacyConflict, 0, len(data))\n\tfor _, c := range data {\n\t\tfields := strings.Split(c.share, \":\")\n\t\tcshare := fields[1]\n\t\tfields = strings.Split(c.privacy, \":\")\n\t\tcprivacy := fields[1]\n\t\tresult = append(result, SourceSharePrivacyConflict{\n\t\t\tnewTestNode(lg, c.sourceNode),\n\t\t\tnewTestCondition(cshare),\n\t\t\tnewTestCondition(cprivacy),\n\t\t})\n\t}\n\treturn result\n}\n\n// checkSameActions compares an actual action set to an expected action set for a test.\nfunc checkSameActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) {\n\trsActual := make(ResolutionSet)\n\trsExpected := make(ResolutionSet)\n\ttestNode := newTestNode(lg, \"test\")\n\trsActual[testNode] = asActual\n\trsExpected[testNode] = asExpected\n\tcheckSame(rsActual, rsExpected, t)\n}\n\n// checkSame compares an actual resolution set to an expected resolution set for a test.\nfunc checkSame(rsActual, rsExpected ResolutionSet, t *testing.T) {\n\tt.Logf(\"actual resolution set: %s\", rsActual.String())\n\tt.Logf(\"expected resolution set: %s\", rsExpected.String())\n\n\tactualTargets := rsActual.AttachesTo()\n\tsort.Sort(actualTargets)\n\n\texpectedTargets := rsExpected.AttachesTo()\n\tsort.Sort(expectedTargets)\n\n\tt.Logf(\"actual targets: %s\", actualTargets.String())\n\tt.Logf(\"expected targets: %s\", expectedTargets.String())\n\n\tfor _, target := range expectedTargets {\n\t\tif !rsActual.AttachesToTarget(target) {\n\t\t\tt.Errorf(\"unexpected missing target: got AttachesToTarget(%q) is false, want true\", target.name)\n\t\t\tcontinue\n\t\t}\n\t\texpectedRl := rsExpected.Resolutions(target)\n\t\tsort.Sort(expectedRl)\n\t\tactualRl := rsActual.Resolutions(target)\n\t\tsort.Sort(actualRl)\n\t\tif len(expectedRl) != len(actualRl) {\n\t\t\tt.Errorf(\"unexpected number of resolutions attach to %q: %d elements, %d elements\",\n\t\t\t\ttarget.name, len(actualRl), len(expectedRl))\n\t\t\tcontinue\n\t\t}\n\t\tfor i := 0; i < len(expectedRl); i++ {\n\t\t\tif expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name {\n\t\t\t\tt.Errorf(\"unexpected resolution attaches to %q at index %d: got %s, want %s\",\n\t\t\t\t\ttarget.name, i, actualRl[i].asString(), expectedRl[i].asString())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\texpectedConditions := expectedRl[i].Resolves()\n\t\t\tactualConditions := actualRl[i].Resolves()\n\t\t\tif expectedConditions != actualConditions {\n\t\t\t\tt.Errorf(\"unexpected conditions apply to %q acting on %q: got %#v with names %s, want %#v with names %s\",\n\t\t\t\t\ttarget.name, expectedRl[i].actsOn.name,\n\t\t\t\t\tactualConditions, actualConditions.Names(),\n\t\t\t\t\texpectedConditions, expectedConditions.Names())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t}\n\tfor _, target := range actualTargets {\n\t\tif !rsExpected.AttachesToTarget(target) {\n\t\t\tt.Errorf(\"unexpected extra target: got expected.AttachesTo(%q) is false, want true\", target.name)\n\t\t}\n\t}\n}\n\n// checkResolvesActions compares an actual action set to an expected action set for a test verifying the actual set\n// resolves all of the expected conditions.\nfunc checkResolvesActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) {\n\trsActual := make(ResolutionSet)\n\trsExpected := make(ResolutionSet)\n\ttestNode := newTestNode(lg, \"test\")\n\trsActual[testNode] = asActual\n\trsExpected[testNode] = asExpected\n\tcheckResolves(rsActual, rsExpected, t)\n}\n\n// checkResolves compares an actual resolution set to an expected resolution set for a test verifying the actual set\n// resolves all of the expected conditions.\nfunc checkResolves(rsActual, rsExpected ResolutionSet, t *testing.T) {\n\tt.Logf(\"actual resolution set: %s\", rsActual.String())\n\tt.Logf(\"expected resolution set: %s\", rsExpected.String())\n\n\tactualTargets := rsActual.AttachesTo()\n\tsort.Sort(actualTargets)\n\n\texpectedTargets := rsExpected.AttachesTo()\n\tsort.Sort(expectedTargets)\n\n\tt.Logf(\"actual targets: %s\", actualTargets.String())\n\tt.Logf(\"expected targets: %s\", expectedTargets.String())\n\n\tfor _, target := range expectedTargets {\n\t\tif !rsActual.AttachesToTarget(target) {\n\t\t\tt.Errorf(\"unexpected missing target: got AttachesToTarget(%q) is false, want true\", target.name)\n\t\t\tcontinue\n\t\t}\n\t\texpectedRl := rsExpected.Resolutions(target)\n\t\tsort.Sort(expectedRl)\n\t\tactualRl := rsActual.Resolutions(target)\n\t\tsort.Sort(actualRl)\n\t\tif len(expectedRl) != len(actualRl) {\n\t\t\tt.Errorf(\"unexpected number of resolutions attach to %q: %d elements, %d elements\",\n\t\t\t\ttarget.name, len(actualRl), len(expectedRl))\n\t\t\tcontinue\n\t\t}\n\t\tfor i := 0; i < len(expectedRl); i++ {\n\t\t\tif expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name {\n\t\t\t\tt.Errorf(\"unexpected resolution attaches to %q at index %d: got %s, want %s\",\n\t\t\t\t\ttarget.name, i, actualRl[i].asString(), expectedRl[i].asString())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\texpectedConditions := expectedRl[i].Resolves()\n\t\t\tactualConditions := actualRl[i].Resolves()\n\t\t\tif expectedConditions != (expectedConditions & actualConditions) {\n\t\t\t\tt.Errorf(\"expected conditions missing from %q acting on %q: got %#v with names %s, want %#v with names %s\",\n\t\t\t\t\ttarget.name, expectedRl[i].actsOn.name,\n\t\t\t\t\tactualConditions, actualConditions.Names(),\n\t\t\t\t\texpectedConditions, expectedConditions.Names())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t}\n\tfor _, target := range actualTargets {\n\t\tif !rsExpected.AttachesToTarget(target) {\n\t\t\tt.Errorf(\"unexpected extra target: got expected.AttachesTo(%q) is false, want true\", target.name)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tools/compliance/testfs/Android.bp",
    "content": "// Copyright (C) 2022 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nbootstrap_go_package {\n    name: \"compliance-test-fs-module\",\n    srcs: [\n        \"testfs.go\",\n    ],\n    pkgPath: \"android/soong/tools/compliance/testfs\",\n}\n"
  },
  {
    "path": "tools/compliance/testfs/testfs.go",
    "content": "// Copyright 2022 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage testfs\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"strings\"\n\t\"time\"\n)\n\n// TestFS implements a test file system (fs.FS) simulated by a map from filename to []byte content.\ntype TestFS map[string][]byte\n\nvar _ fs.FS = (*TestFS)(nil)\nvar _ fs.StatFS = (*TestFS)(nil)\n\n// Open implements fs.FS.Open() to open a file based on the filename.\nfunc (tfs *TestFS) Open(name string) (fs.File, error) {\n\tif _, ok := (*tfs)[name]; !ok {\n\t\treturn nil, fmt.Errorf(\"unknown file %q\", name)\n\t}\n\treturn &TestFile{tfs, name, 0}, nil\n}\n\n// Stat implements fs.StatFS.Stat() to examine a file based on the filename.\nfunc (tfs *TestFS) Stat(name string) (fs.FileInfo, error) {\n\tif content, ok := (*tfs)[name]; ok {\n\t\treturn &TestFileInfo{name, len(content), 0666}, nil\n\t}\n\tdirname := name\n\tif !strings.HasSuffix(dirname, \"/\") {\n\t\tdirname = dirname + \"/\"\n\t}\n\tfor name := range (*tfs) {\n\t\tif strings.HasPrefix(name, dirname) {\n\t\t\treturn &TestFileInfo{name, 8, fs.ModeDir | fs.ModePerm}, nil\n\t\t}\n\t}\n\treturn nil, fmt.Errorf(\"file not found: %q\", name)\n}\n\n// TestFileInfo implements a file info (fs.FileInfo) based on TestFS above.\ntype TestFileInfo struct {\n\tname string\n\tsize int\n\tmode fs.FileMode\n}\n\nvar _ fs.FileInfo = (*TestFileInfo)(nil)\n\n// Name returns the name of the file\nfunc (fi *TestFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size returns the size of the file in bytes.\nfunc (fi *TestFileInfo) Size() int64 {\n\treturn int64(fi.size)\n}\n\n// Mode returns the fs.FileMode bits.\nfunc (fi *TestFileInfo) Mode() fs.FileMode {\n\treturn fi.mode\n}\n\n// ModTime fakes a modification time.\nfunc (fi *TestFileInfo) ModTime() time.Time {\n\treturn time.UnixMicro(0xb0bb)\n}\n\n// IsDir is a synonym for Mode().IsDir()\nfunc (fi *TestFileInfo) IsDir() bool {\n\treturn fi.mode.IsDir()\n}\n\n// Sys is unused and returns nil.\nfunc (fi *TestFileInfo) Sys() any {\n\treturn nil\n}\n\n// TestFile implements a test file (fs.File) based on TestFS above.\ntype TestFile struct {\n\tfs   *TestFS\n\tname string\n\tposn int\n}\n\nvar _ fs.File = (*TestFile)(nil)\n\n// Stat not implemented to obviate implementing fs.FileInfo.\nfunc (f *TestFile) Stat() (fs.FileInfo, error) {\n\treturn f.fs.Stat(f.name)\n}\n\n// Read copies bytes from the TestFS map.\nfunc (f *TestFile) Read(b []byte) (int, error) {\n\tif f.posn < 0 {\n\t\treturn 0, fmt.Errorf(\"file not open: %q\", f.name)\n\t}\n\tif f.posn >= len((*f.fs)[f.name]) {\n\t\treturn 0, io.EOF\n\t}\n\tn := copy(b, (*f.fs)[f.name][f.posn:])\n\tf.posn += n\n\treturn n, nil\n}\n\n// Close marks the TestFile as no longer in use.\nfunc (f *TestFile) Close() error {\n\tif f.posn < 0 {\n\t\treturn fmt.Errorf(\"file already closed: %q\", f.name)\n\t}\n\tf.posn = -1\n\treturn nil\n}\n"
  },
  {
    "path": "tools/dependency_mapper/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n    default_team: \"trendy_team_android_crumpet\",\n}\n\njava_binary_host {\n    name: \"dependency-mapper\",\n    main_class: \"com.android.dependencymapper.Main\",\n    static_libs: [\n        \"dependency-mapper-host-lib\",\n    ],\n    visibility: [\"//visibility:public\"],\n}\n\njava_library_host {\n    name: \"dependency-mapper-host-lib\",\n    srcs: [\n        \"src/**/*.java\",\n        \"proto/**/*.proto\",\n    ],\n    static_libs: [\n        \"gson\",\n        \"ow2-asm\",\n    ],\n}\n\njava_test_host {\n    name: \"dependency-mapper-tests\",\n    srcs: [\"tests/src/**/*.java\"],\n    static_libs: [\n        \"junit\",\n        \"dependency-mapper-host-lib\",\n    ],\n    data: [\n        \"tests/res/**/*\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n}\n\njava_library {\n    name: \"dependency-mapper-test-data\",\n    srcs: [\"tests/res/**/*.java\"],\n}\n"
  },
  {
    "path": "tools/dependency_mapper/OWNERS",
    "content": "himanshuz@google.com"
  },
  {
    "path": "tools/dependency_mapper/README.md",
    "content": "# Dependency Mapper\n\n[dependency-mapper] command line tool. This tool finds the usage based dependencies between java\nfiles by utilizing byte-code and java file analysis.\n\n# Getting Started\n\n## Inputs\n* rsp file, containing list of java files separated by whitespace.\n* jar file, containing class files generated after compiling the contents of rsp file.\n\n## Output\n* proto file, representing the list of dependencies for each java file present in input rsp file,\nrepresented by [proto/usage.proto]\n\n## Usage\n```\ndependency-mapper --src-path [src-list.rsp] --jar-path [classes.jar] --usage-map-path [usage-map.proto]\"\n```\n\n# Notes\n## Dependencies enlisted are only within the java files present in input.\n## Ensure that [SourceFile] is present in the classes present in the jar.\n## To ensure dependencies are listed correctly\n* Classes jar should only contain class files generated from the source rsp files.\n* Classes jar should not exclude any class file that was generated from source rsp files."
  },
  {
    "path": "tools/dependency_mapper/proto/dependency.proto",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nsyntax = \"proto2\";\n\npackage com.android.dependencymapper;\noption java_package = \"com.android.dependencymapper\";\noption java_outer_classname = \"DependencyProto\";\n\n/**\n * A com.android.dependencymapper.DependencyProto.FileDependency object.\n */\n\nmessage FileDependency {\n\n  // java file path on disk\n  optional string file_path = 1;\n  // if a change in this file warrants recompiling all files\n  optional bool is_dependency_to_all = 2;\n  // class files generated when this java file is compiled\n  repeated string generated_classes = 3;\n  // dependencies of this file.\n  repeated string file_dependencies = 4;\n}\n\n/**\n * A com.android.dependencymapper.DependencyProto.FileDependencyList object.\n */\nmessage FileDependencyList {\n\n  // List of java file usages\n  repeated FileDependency fileDependency = 1;\n}"
  },
  {
    "path": "tools/dependency_mapper/src/com/android/dependencymapper/ClassDependenciesVisitor.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport org.objectweb.asm.signature.SignatureReader;\nimport org.objectweb.asm.signature.SignatureVisitor;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.TypePath;\n\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * An ASM based class visitor to analyze and club all dependencies of a java file.\n * Most of the logic of this class is inspired from\n * <a href=\"https://github.com/gradle/gradle/blob/master/platforms/jvm/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/asm/ClassDependenciesVisitor.java\">gradle incremental compilation</a>\n */\npublic class ClassDependenciesVisitor extends ClassVisitor {\n\n    private final static int API = Opcodes.ASM9;\n\n    private final Set<String> mClassTypes;\n    private final Set<Object> mConstantsDefined;\n    private final Set<Object> mInlinedUsages;\n    private String mSource;\n    private boolean isAnnotationType;\n    private boolean mIsDependencyToAll;\n    private final RetentionPolicyVisitor retentionPolicyVisitor;\n\n    private final ClassRelevancyFilter mClassFilter;\n\n    private ClassDependenciesVisitor(ClassReader reader, ClassRelevancyFilter filter) {\n        super(API);\n        this.mClassTypes = new HashSet<>();\n        this.mConstantsDefined = new HashSet<>();\n        this.mInlinedUsages =  new HashSet<>();\n        this.retentionPolicyVisitor = new RetentionPolicyVisitor();\n        this.mClassFilter = filter;\n        collectRemainingClassDependencies(reader);\n    }\n\n    public static ClassDependencyData analyze(\n            String className, ClassReader reader, ClassRelevancyFilter filter) {\n        ClassDependenciesVisitor visitor = new ClassDependenciesVisitor(reader, filter);\n        reader.accept(visitor, ClassReader.SKIP_FRAMES);\n        // Sometimes a class may contain references to the same class, we remove such cases to\n        // prevent circular dependency.\n        visitor.getClassTypes().remove(className);\n        return new ClassDependencyData(Utils.buildPackagePrependedClassSource(\n                className, visitor.getSource()), className, visitor.getClassTypes(),\n                visitor.isDependencyToAll(), visitor.getConstantsDefined(),\n                visitor.getInlinedUsages());\n    }\n\n    @Override\n    public void visitSource(String source, String debug) {\n        mSource = source;\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName,\n            String[] interfaces) {\n        isAnnotationType = isAnnotationType(interfaces);\n        maybeAddClassTypesFromSignature(signature, mClassTypes);\n        if (superName != null) {\n            // superName can be null if what we are analyzing is `java.lang.Object`\n            // which can happen when a custom Java SDK is on classpath (typically, android.jar)\n            Type type = Type.getObjectType(superName);\n            maybeAddClassType(mClassTypes, type);\n        }\n        for (String s : interfaces) {\n            Type interfaceType = Type.getObjectType(s);\n            maybeAddClassType(mClassTypes, interfaceType);\n        }\n    }\n\n    // performs a fast analysis of classes referenced in bytecode (method bodies)\n    // avoiding us to implement a costly visitor and potentially missing edge cases\n    private void collectRemainingClassDependencies(ClassReader reader) {\n        char[] charBuffer = new char[reader.getMaxStringLength()];\n        for (int i = 1; i < reader.getItemCount(); i++) {\n            int itemOffset = reader.getItem(i);\n            // see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4\n            if (itemOffset > 0 && reader.readByte(itemOffset - 1) == 7) {\n                // A CONSTANT_Class entry, read the class descriptor\n                String classDescriptor = reader.readUTF8(itemOffset, charBuffer);\n                Type type = Type.getObjectType(classDescriptor);\n                maybeAddClassType(mClassTypes, type);\n            }\n        }\n    }\n\n    private void maybeAddClassTypesFromSignature(String signature, Set<String> types) {\n        if (signature != null) {\n            SignatureReader signatureReader = new SignatureReader(signature);\n            signatureReader.accept(new SignatureVisitor(API) {\n                @Override\n                public void visitClassType(String className) {\n                    Type type = Type.getObjectType(className);\n                    maybeAddClassType(types, type);\n                }\n            });\n        }\n    }\n\n    protected void maybeAddClassType(Set<String> types, Type type) {\n        while (type.getSort() == Type.ARRAY) {\n            type = type.getElementType();\n        }\n        if (type.getSort() != Type.OBJECT) {\n            return;\n        }\n        //String name = Utils.classPackageToFilePath(type.getClassName());\n        String name = type.getClassName();\n        if (mClassFilter.test(name)) {\n            types.add(name);\n        }\n    }\n\n    public String getSource() {\n        return mSource;\n    }\n\n    public Set<String> getClassTypes() {\n        return mClassTypes;\n    }\n\n    public Set<Object> getConstantsDefined() {\n        return mConstantsDefined;\n    }\n\n    public Set<Object> getInlinedUsages() {\n        return mInlinedUsages;\n    }\n\n    private boolean isAnnotationType(String[] interfaces) {\n        return interfaces.length == 1 && interfaces[0].equals(\"java/lang/annotation/Annotation\");\n    }\n\n    @Override\n    public FieldVisitor visitField(\n            int access, String name, String desc, String signature, Object value) {\n        maybeAddClassTypesFromSignature(signature, mClassTypes);\n        maybeAddClassType(mClassTypes, Type.getType(desc));\n        if (isAccessibleConstant(access, value)) {\n            mConstantsDefined.add(value);\n        }\n        return new FieldVisitor(mClassTypes);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(\n            int access, String name, String desc, String signature, String[] exceptions) {\n        maybeAddClassTypesFromSignature(signature, mClassTypes);\n        Type methodType = Type.getMethodType(desc);\n        maybeAddClassType(mClassTypes, methodType.getReturnType());\n        for (Type argType : methodType.getArgumentTypes()) {\n            maybeAddClassType(mClassTypes, argType);\n        }\n        return new MethodVisitor(mClassTypes);\n    }\n\n    @Override\n    public org.objectweb.asm.AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n        if (isAnnotationType && \"Ljava/lang/annotation/Retention;\".equals(desc)) {\n            return retentionPolicyVisitor;\n        } else {\n            maybeAddClassType(mClassTypes, Type.getType(desc));\n            return new AnnotationVisitor(mClassTypes);\n        }\n    }\n\n    private static boolean isAccessible(int access) {\n        return (access & Opcodes.ACC_PRIVATE) == 0;\n    }\n\n    private static boolean isAccessibleConstant(int access, Object value) {\n        return isConstant(access) && isAccessible(access) && value != null;\n    }\n\n    private static boolean isConstant(int access) {\n        return (access & Opcodes.ACC_FINAL) != 0 && (access & Opcodes.ACC_STATIC) != 0;\n    }\n\n    public boolean isDependencyToAll() {\n        return mIsDependencyToAll;\n    }\n\n    private class FieldVisitor extends org.objectweb.asm.FieldVisitor {\n        private final Set<String> types;\n\n        public FieldVisitor(Set<String> types) {\n            super(API);\n            this.types = types;\n        }\n\n        @Override\n        public org.objectweb.asm.AnnotationVisitor visitAnnotation(\n                String descriptor, boolean visible) {\n            maybeAddClassType(types, Type.getType(descriptor));\n            return new AnnotationVisitor(types);\n        }\n\n        @Override\n        public org.objectweb.asm.AnnotationVisitor visitTypeAnnotation(int typeRef,\n                TypePath typePath, String descriptor, boolean visible) {\n            maybeAddClassType(types, Type.getType(descriptor));\n            return new AnnotationVisitor(types);\n        }\n    }\n\n    private class MethodVisitor extends org.objectweb.asm.MethodVisitor {\n        private final Set<String> types;\n\n        protected MethodVisitor(Set<String> types) {\n            super(API);\n            this.types = types;\n        }\n\n        @Override\n        public void visitLdcInsn(Object value) {\n            mInlinedUsages.add(value);\n            super.visitLdcInsn(value);\n        }\n\n        @Override\n        public void visitLocalVariable(\n                String name, String desc, String signature, Label start, Label end, int index) {\n            maybeAddClassTypesFromSignature(signature, mClassTypes);\n            maybeAddClassType(mClassTypes, Type.getType(desc));\n            super.visitLocalVariable(name, desc, signature, start, end, index);\n        }\n\n        @Override\n        public org.objectweb.asm.AnnotationVisitor visitAnnotation(\n                String descriptor, boolean visible) {\n            maybeAddClassType(types, Type.getType(descriptor));\n            return new AnnotationVisitor(types);\n        }\n\n        @Override\n        public org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(\n                int parameter, String descriptor, boolean visible) {\n            maybeAddClassType(types, Type.getType(descriptor));\n            return new AnnotationVisitor(types);\n        }\n\n        @Override\n        public org.objectweb.asm.AnnotationVisitor visitTypeAnnotation(\n                int typeRef, TypePath typePath, String descriptor, boolean visible) {\n            maybeAddClassType(types, Type.getType(descriptor));\n            return new AnnotationVisitor(types);\n        }\n    }\n\n    private class RetentionPolicyVisitor extends org.objectweb.asm.AnnotationVisitor {\n        public RetentionPolicyVisitor() {\n            super(ClassDependenciesVisitor.API);\n        }\n\n        @Override\n        public void visitEnum(String name, String desc, String value) {\n            if (\"Ljava/lang/annotation/RetentionPolicy;\".equals(desc)) {\n                RetentionPolicy policy = RetentionPolicy.valueOf(value);\n                if (policy == RetentionPolicy.SOURCE) {\n                    mIsDependencyToAll = true;\n                }\n            }\n        }\n    }\n\n    private class AnnotationVisitor extends org.objectweb.asm.AnnotationVisitor {\n        private final Set<String> types;\n\n        public AnnotationVisitor(Set<String> types) {\n            super(ClassDependenciesVisitor.API);\n            this.types = types;\n        }\n\n        @Override\n        public void visit(String name, Object value) {\n            if (value instanceof Type) {\n                maybeAddClassType(types, (Type) value);\n            }\n        }\n\n        @Override\n        public org.objectweb.asm.AnnotationVisitor visitArray(String name) {\n            return this;\n        }\n\n        @Override\n        public org.objectweb.asm.AnnotationVisitor visitAnnotation(String name, String descriptor) {\n            maybeAddClassType(types, Type.getType(descriptor));\n            return this;\n        }\n    }\n}"
  },
  {
    "path": "tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyAnalyzer.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport org.objectweb.asm.ClassReader;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\n/**\n * An utility class that reads each class file present in the classes jar, then analyzes the same,\n * collecting the dependencies in {@link List<ClassDependencyData>}\n */\npublic class ClassDependencyAnalyzer {\n\n    public static List<ClassDependencyData> analyze(Path classJar, ClassRelevancyFilter classFilter) {\n        List<ClassDependencyData> classAnalysisList = new ArrayList<>();\n        try (JarFile jarFile = new JarFile(classJar.toFile())) {\n            Enumeration<JarEntry> entries = jarFile.entries();\n            while (entries.hasMoreElements()) {\n                JarEntry entry = entries.nextElement();\n                if (entry.getName().endsWith(\".class\")) {\n                    try (InputStream inputStream = jarFile.getInputStream(entry)) {\n                        String name = Utils.trimAndConvertToPackageBasedPath(entry.getName());\n                        ClassDependencyData classAnalysis = ClassDependenciesVisitor.analyze(name,\n                                new ClassReader(inputStream), classFilter);\n                        classAnalysisList.add(classAnalysis);\n                    }\n                }\n            }\n        } catch (IOException e) {\n            System.err.println(\"Error reading the jar file at: \" + classJar);\n            throw new RuntimeException(e);\n        }\n        return classAnalysisList;\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyData.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport java.util.Set;\n\n/**\n * Represents the Class Dependency Data collected via ASM analysis.\n */\npublic class ClassDependencyData {\n    private final String mPackagePrependedClassSource;\n    private final String mQualifiedName;\n    private final Set<String> mClassDependencies;\n    private final boolean mIsDependencyToAll;\n    private final Set<Object> mConstantsDefined;\n    private final Set<Object> mInlinedUsages;\n\n    public ClassDependencyData(String packagePrependedClassSource, String className,\n            Set<String> classDependencies, boolean isDependencyToAll, Set<Object> constantsDefined,\n            Set<Object> inlinedUsages) {\n        this.mPackagePrependedClassSource = packagePrependedClassSource;\n        this.mQualifiedName = className;\n        this.mClassDependencies = classDependencies;\n        this.mIsDependencyToAll = isDependencyToAll;\n        this.mConstantsDefined = constantsDefined;\n        this.mInlinedUsages = inlinedUsages;\n    }\n\n    public String getPackagePrependedClassSource() {\n        return mPackagePrependedClassSource;\n    }\n\n    public String getQualifiedName() {\n        return mQualifiedName;\n    }\n\n    public Set<String> getClassDependencies() {\n        return mClassDependencies;\n    }\n\n    public Set<Object> getConstantsDefined() {\n        return mConstantsDefined;\n    }\n\n    public Set<Object> inlinedUsages() {\n        return mInlinedUsages;\n    }\n\n    public boolean isDependencyToAll() {\n        return mIsDependencyToAll;\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/src/com/android/dependencymapper/ClassRelevancyFilter.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport java.util.Set;\nimport java.util.function.Predicate;\n\n/**\n * A filter representing the list of class files which are relevant for dependency analysis.\n */\npublic class ClassRelevancyFilter implements Predicate<String> {\n\n    private final Set<String> mAllowlistedClassNames;\n\n    public ClassRelevancyFilter(Set<String> allowlistedClassNames) {\n        this.mAllowlistedClassNames = allowlistedClassNames;\n    }\n\n    @Override\n    public boolean test(String className) {\n        return mAllowlistedClassNames.contains(className);\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/src/com/android/dependencymapper/DependencyMapper.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport com.android.dependencymapper.DependencyProto;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * This class binds {@link List<ClassDependencyData>} and {@link List<JavaSourceData>} together as a\n * flat map, which represents dependency related attributes of a java file.\n */\npublic class DependencyMapper {\n    private final List<ClassDependencyData> mClassAnalysisList;\n    private final List<JavaSourceData> mJavaSourceDataList;\n    private final Map<String, String> mClassToSourceMap = new HashMap<>();\n    private final Map<String, Set<String>> mFileDependencies = new HashMap<>();\n    private final Set<String> mDependencyToAll = new HashSet<>();\n    private final Map<String, Set<String>> mSourceToClasses = new HashMap<>();\n\n    public DependencyMapper(List<ClassDependencyData> classAnalysisList, List<JavaSourceData> javaSourceDataList) {\n        this.mClassAnalysisList = classAnalysisList;\n        this.mJavaSourceDataList = javaSourceDataList;\n    }\n\n    public DependencyProto.FileDependencyList buildDependencyMaps() {\n        buildClassDependencyMaps();\n        buildSourceToClassMap();\n        return createFileDependencies();\n    }\n\n    private void buildClassDependencyMaps() {\n        // Create a map between package appended file names and file paths.\n        Map<String, String> sourcePaths = generateSourcePaths();\n        // A map between qualified className and its dependencies\n        Map<String, Set<String>> classDependencies = new HashMap<>();\n        // A map between constant values and the their declarations.\n        Map<Object, Set<String>> constantRegistry = new HashMap<>();\n        // A map between constant values and the their inlined usages.\n        Map<Object, Set<String>> inlinedUsages = new HashMap<>();\n\n        for (ClassDependencyData analysis : mClassAnalysisList) {\n            String className = analysis.getQualifiedName();\n\n            // Compute qualified class name to source path map.\n            String sourceKey = analysis.getPackagePrependedClassSource();\n            String sourcePath = sourcePaths.get(sourceKey);\n            mClassToSourceMap.put(className, sourcePath);\n\n            // compute classDependencies\n            classDependencies.computeIfAbsent(className, k ->\n                    new HashSet<>()).addAll(analysis.getClassDependencies());\n\n            // Compute constantRegistry\n            analysis.getConstantsDefined().forEach(c ->\n                    constantRegistry.computeIfAbsent(c, k -> new HashSet<>()).add(className));\n            // Compute inlinedUsages map.\n            analysis.inlinedUsages().forEach(u ->\n                    inlinedUsages.computeIfAbsent(u, k -> new HashSet<>()).add(className));\n\n            if (analysis.isDependencyToAll()) {\n                mDependencyToAll.add(sourcePath);\n            }\n        }\n        // Finally build file dependencies\n        buildFileDependencies(\n                combineDependencies(classDependencies, inlinedUsages, constantRegistry));\n    }\n\n    private Map<String, String> generateSourcePaths() {\n        Map<String, String> sourcePaths = new HashMap<>();\n        mJavaSourceDataList.forEach(data ->\n                sourcePaths.put(data.getPackagePrependedFileName(), data.getFilePath()));\n        return sourcePaths;\n    }\n\n    private Map<String, Set<String>> combineDependencies(Map<String, Set<String>> classDependencies,\n            Map<Object, Set<String>> inlinedUsages,\n            Map<Object, Set<String>> constantRegistry) {\n        Map<String, Set<String>> combined = new HashMap<>(\n                buildConstantDependencies(inlinedUsages, constantRegistry));\n        classDependencies.forEach((k, v) ->\n                combined.computeIfAbsent(k, key -> new HashSet<>()).addAll(v));\n        return combined;\n    }\n\n    private Map<String, Set<String>> buildConstantDependencies(\n            Map<Object, Set<String>> inlinedUsages, Map<Object, Set<String>> constantRegistry) {\n        Map<String, Set<String>> constantDependencies = new HashMap<>();\n        for (Map.Entry<Object, Set<String>> usageEntry : inlinedUsages.entrySet()) {\n            Object usage = usageEntry.getKey();\n            Set<String> usageClasses = usageEntry.getValue();\n            if (constantRegistry.containsKey(usage)) {\n                Set<String> declarationClasses = constantRegistry.get(usage);\n                for (String usageClass : usageClasses) {\n                    // Sometimes Usage and Declarations are in the same file, we remove such cases\n                    // to prevent circular dependency.\n                    declarationClasses.remove(usageClass);\n                    constantDependencies.computeIfAbsent(usageClass, k ->\n                            new HashSet<>()).addAll(declarationClasses);\n                }\n            }\n        }\n\n        return constantDependencies;\n    }\n\n    private void buildFileDependencies(Map<String, Set<String>> combinedClassDependencies) {\n        combinedClassDependencies.forEach((className, dependencies) -> {\n            String sourceFile = mClassToSourceMap.get(className);\n            if (sourceFile == null) {\n                throw new IllegalArgumentException(\"Class '\" + className\n                        + \"' does not have a corresponding source file.\");\n            }\n            mFileDependencies.computeIfAbsent(sourceFile, k -> new HashSet<>());\n            dependencies.forEach(dependency -> {\n                String dependencySource = mClassToSourceMap.get(dependency);\n                if (dependencySource == null) {\n                    throw new IllegalArgumentException(\"Dependency '\" + dependency\n                            + \"' does not have a corresponding source file.\");\n                }\n                mFileDependencies.get(sourceFile).add(dependencySource);\n            });\n        });\n    }\n\n    private void buildSourceToClassMap() {\n        mClassToSourceMap.forEach((className, sourceFile) ->\n                mSourceToClasses.computeIfAbsent(sourceFile, k ->\n                        new HashSet<>()).add(className));\n    }\n\n    private DependencyProto.FileDependencyList createFileDependencies() {\n        List<DependencyProto.FileDependency> fileDependencies = new ArrayList<>();\n        mFileDependencies.forEach((file, dependencies) -> {\n            DependencyProto.FileDependency dependency = DependencyProto.FileDependency.newBuilder()\n                    .setFilePath(file)\n                    .setIsDependencyToAll(mDependencyToAll.contains(file))\n                    .addAllGeneratedClasses(mSourceToClasses.get(file))\n                    .addAllFileDependencies(dependencies)\n                    .build();\n            fileDependencies.add(dependency);\n        });\n        return DependencyProto.FileDependencyList.newBuilder()\n                .addAllFileDependency(fileDependencies).build();\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceAnalyzer.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport java.io.BufferedReader;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * An utility class that reads each java file present in the rsp content then analyzes the same,\n * collecting the analysis in {@link List<JavaSourceData>}\n */\npublic class JavaSourceAnalyzer {\n\n    // Regex that matches against \"package abc.xyz.lmn;\" declarations in a java file.\n    private static final String PACKAGE_REGEX = \"^package\\\\s+([a-zA-Z_][a-zA-Z0-9_.]*);\";\n\n    public static List<JavaSourceData> analyze(Path srcRspFile) {\n        List<JavaSourceData> javaSourceDataList = new ArrayList<>();\n        try (BufferedReader reader = new BufferedReader(new FileReader(srcRspFile.toFile()))) {\n            String line;\n            while ((line = reader.readLine()) != null) {\n                // Split the line by spaces, tabs, multiple java files can be on a single line.\n                String[] files = line.trim().split(\"\\\\s+\");\n                for (String file : files) {\n                    Path p = Paths.get(\"\", file);\n                    System.out.println(p.toAbsolutePath().toString());\n                    javaSourceDataList\n                            .add(new JavaSourceData(file, constructPackagePrependedFileName(file)));\n                }\n            }\n        } catch (IOException e) {\n            System.err.println(\"Error reading rsp file at: \" + srcRspFile);\n            throw new RuntimeException(e);\n        }\n        return javaSourceDataList;\n    }\n\n    private static String constructPackagePrependedFileName(String filePath) {\n        String packageAppendedFileName = null;\n        // if the file path is abc/def/ghi/JavaFile.java we extract JavaFile.java\n        String javaFileName = filePath.substring(filePath.lastIndexOf(\"/\") + 1);\n        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {\n            String line;\n            // Process each line and match against the package regex pattern.\n            while ((line = reader.readLine()) != null) {\n                Pattern pattern = Pattern.compile(PACKAGE_REGEX);\n                Matcher matcher = pattern.matcher(line);\n                if (matcher.find()) {\n                    packageAppendedFileName = matcher.group(1) + \".\" + javaFileName;\n                    break;\n                }\n            }\n        } catch (IOException e) {\n            System.err.println(\"Error reading java file at: \" + filePath);\n            throw new RuntimeException(e);\n        }\n        // Should not be null\n        assert packageAppendedFileName != null;\n        return packageAppendedFileName;\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceData.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\n/**\n * POJO representing the data collected from Java Source file analysis.\n */\npublic class JavaSourceData {\n\n    private final String mFilePath;\n    private final String mPackagePrependedFileName;\n\n    public JavaSourceData(String filePath, String packagePrependedFileName) {\n        mFilePath = filePath;\n        mPackagePrependedFileName = packagePrependedFileName;\n    }\n\n    public String getFilePath() {\n        return mFilePath;\n    }\n\n    public String getPackagePrependedFileName() {\n        return mPackagePrependedFileName;\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/src/com/android/dependencymapper/Main.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport static com.android.dependencymapper.Utils.listClassesInJar;\n\nimport com.android.dependencymapper.DependencyProto;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Set;\n\npublic class Main {\n\n    public static void main(String[] args) throws IOException, InterruptedException {\n        try {\n            InputData input = parseAndValidateInput(args);\n            generateDependencyMap(input);\n        } catch (IllegalArgumentException e) {\n            System.err.println(\"Error: \" + e.getMessage());\n            showUsage();\n        }\n    }\n\n    private static class InputData {\n        public Path srcList;\n        public Path classesJar;\n        public Path dependencyMapProto;\n\n        public InputData(Path srcList, Path classesJar, Path dependencyMapProto) {\n            this.srcList = srcList;\n            this.classesJar = classesJar;\n            this.dependencyMapProto = dependencyMapProto;\n        }\n    }\n\n    private static InputData parseAndValidateInput(String[] args) {\n        for (String arg : args) {\n            if (\"--help\".equals(arg)) {\n                showUsage();\n                System.exit(0); // Indicate successful exit after showing help\n            }\n        }\n\n        if (args.length != 6) { // Explicitly check for the correct number of arguments\n            throw new IllegalArgumentException(\"Incorrect number of arguments\");\n        }\n\n        Path srcList = null;\n        Path classesJar = null;\n        Path dependencyMapProto = null;\n\n        for (int i = 0; i < args.length; i += 2) {\n            String arg = args[i].trim();\n            String argValue = args[i + 1].trim();\n\n            switch (arg) {\n                case \"--src-path\" -> srcList = Path.of(argValue);\n                case \"--jar-path\" -> classesJar = Path.of(argValue);\n                case \"--dependency-map-path\" -> dependencyMapProto = Path.of(argValue);\n                default -> throw new IllegalArgumentException(\"Unknown argument: \" + arg);\n            }\n        }\n\n        // Validate file existence and readability\n        validateFile(srcList, \"--src-path\");\n        validateFile(classesJar, \"--jar-path\");\n\n        return new InputData(srcList, classesJar, dependencyMapProto);\n    }\n\n    private static void validateFile(Path path, String argName) {\n        if (path == null) {\n            throw new IllegalArgumentException(argName + \" is required\");\n        }\n        if (!Files.exists(path)) {\n            throw new IllegalArgumentException(argName + \" does not exist: \" + path);\n        }\n        if (!Files.isReadable(path)) {\n            throw new IllegalArgumentException(argName + \" is not readable: \" + path);\n        }\n    }\n\n    private static void generateDependencyMap(InputData input) {\n        // First collect all classes in the jar.\n        Set<String> classesInJar = listClassesInJar(input.classesJar);\n        // Perform dependency analysis.\n        List<ClassDependencyData> classDependencyDataList = ClassDependencyAnalyzer\n                .analyze(input.classesJar, new ClassRelevancyFilter(classesInJar));\n        // Perform java source analysis.\n        List<JavaSourceData> javaSourceDataList = JavaSourceAnalyzer.analyze(input.srcList);\n        // Collect all dependencies and map them as DependencyProto.FileDependencyList\n        DependencyMapper dp = new DependencyMapper(classDependencyDataList, javaSourceDataList);\n        DependencyProto.FileDependencyList dependencyList =  dp.buildDependencyMaps();\n\n        // Write the proto to output file\n        Utils.writeContentsToProto(dependencyList, input.dependencyMapProto);\n    }\n\n    private static void showUsage() {\n        System.err.println(\n                \"Usage: dependency-mapper \"\n                        + \"--src-path [src-list.rsp] \"\n                        + \"--jar-path [classes.jar] \"\n                        + \"--dependency-map-path [dependency-map.proto]\");\n    }\n\n}"
  },
  {
    "path": "tools/dependency_mapper/src/com/android/dependencymapper/Utils.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport com.android.dependencymapper.DependencyProto;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\n\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\npublic class Utils {\n\n    public static String trimAndConvertToPackageBasedPath(String fileBasedPath) {\n        // Remove \".class\" from the fileBasedPath, then replace \"/\" with \".\"\n        return fileBasedPath.replaceAll(\"\\\\..*\", \"\").replaceAll(\"/\", \".\");\n    }\n\n    public static String buildPackagePrependedClassSource(String qualifiedClassPath,\n            String classSource) {\n        // Find the location of the start of classname in the qualifiedClassPath\n        int classNameSt = qualifiedClassPath.lastIndexOf(\".\") + 1;\n        // Replace the classname in qualifiedClassPath with classSource\n        return qualifiedClassPath.substring(0, classNameSt) + classSource;\n    }\n\n    public static void writeContentsToJson(DependencyProto.FileDependencyList contents, Path jsonOut) {\n        Gson gson = new GsonBuilder().setPrettyPrinting().create();\n        Map<String, Set<String>> jsonMap = new HashMap<>();\n        for (DependencyProto.FileDependency fileDependency : contents.getFileDependencyList()) {\n            jsonMap.putIfAbsent(fileDependency.getFilePath(),\n                    Set.copyOf(fileDependency.getFileDependenciesList()));\n        }\n        String json = gson.toJson(jsonMap);\n        try (FileWriter file = new FileWriter(jsonOut.toFile())) {\n            file.write(json);\n        } catch (IOException e) {\n            System.err.println(\"Error writing json output to: \" + jsonOut);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static void writeContentsToProto(DependencyProto.FileDependencyList usages, Path protoOut) {\n        try {\n            OutputStream outputStream = Files.newOutputStream(protoOut);\n            usages.writeDelimitedTo(outputStream);\n        } catch (IOException e) {\n            System.err.println(\"Error writing proto output to: \" + protoOut);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static Set<String> listClassesInJar(Path classesJarPath) {\n        Set<String> classes = new HashSet<>();\n        try (JarFile jarFile = new JarFile(classesJarPath.toFile())) {\n            Enumeration<JarEntry> entries = jarFile.entries();\n            while (entries.hasMoreElements()) {\n                JarEntry entry = entries.nextElement();\n                if (entry.getName().endsWith(\".class\")) {\n                    String name = Utils.trimAndConvertToPackageBasedPath(entry.getName());\n                    classes.add(name);\n                }\n            }\n        } catch (IOException e) {\n            System.err.println(\"Error reading the jar file at: \" + classesJarPath);\n            throw new RuntimeException(e);\n        }\n        return classes;\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/annotation/AnnotationUsage.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.annotation;\n\n@res.testdata.annotation.RuntimeAnnotation\npublic class AnnotationUsage {\n\n    private final int mSourceAnnField;\n\n    public AnnotationUsage(@res.testdata.annotation.SourceAnnotation int sourceAnnField) {\n        mSourceAnnField = sourceAnnField;\n    }\n\n    public @res.testdata.annotation.SourceAnnotation int getSourceAnnField() {\n        return mSourceAnnField;\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/annotation/RuntimeAnnotation.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface RuntimeAnnotation {\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/annotation/SourceAnnotation.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n@Retention(RetentionPolicy.SOURCE)\npublic @interface SourceAnnotation {\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/constants/ConstantDefinition.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.constants;\n\npublic class ConstantDefinition {\n    public static final String TEST_CONSTANT = \"test_constant\";\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/constants/ConstantUsage.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.constants;\n\npublic class ConstantUsage {\n\n    public ConstantUsage(){}\n\n    public String useConstantInMethodBody() {\n        return res.testdata.constants.ConstantDefinition.TEST_CONSTANT;\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/inheritance/BaseClass.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.inheritance;\n\npublic class BaseClass {\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/inheritance/BaseImpl.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.inheritance;\n\npublic interface BaseImpl {\n\n    void baseImpl();\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/inheritance/InheritanceUsage.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.inheritance;\n\npublic class InheritanceUsage extends res.testdata.inheritance.BaseClass implements\n        res.testdata.inheritance.BaseImpl {\n    @Override\n    public void baseImpl() {\n\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/methods/FieldUsage.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.methods;\n\npublic class FieldUsage {\n\n    private res.testdata.methods.ReferenceClass1 mReferenceClass1;\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/methods/MethodUsage.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.methods;\n\npublic class MethodUsage {\n\n    public void methodReferences(res.testdata.methods.ReferenceClass1 mReferenceClass1) {\n        res.testdata.methods.ReferenceClass2 referenceClass2 =\n                new res.testdata.methods.ReferenceClass2();\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass1.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.methods;\n\npublic class ReferenceClass1 {\n\n    public ReferenceClass1(){}\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass2.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage res.testdata.methods;\n\npublic class ReferenceClass2 {\n    public ReferenceClass2(){}\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/res/testfiles/sources.rsp",
    "content": "tests/res/testdata/annotation/AnnotationUsage.java\ntests/res/testdata/annotation/SourceAnnotation.java\ntests/res/testdata/annotation/RuntimeAnnotation.java\ntests/res/testdata/constants/ConstantDefinition.java\ntests/res/testdata/constants/ConstantUsage.java\ntests/res/testdata/inheritance/InheritanceUsage.java\ntests/res/testdata/inheritance/BaseClass.java\ntests/res/testdata/inheritance/BaseImpl.java\ntests/res/testdata/methods/FieldUsage.java\ntests/res/testdata/methods/MethodUsage.java\ntests/res/testdata/methods/ReferenceClass1.java\ntests/res/testdata/methods/ReferenceClass2.java"
  },
  {
    "path": "tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassDependencyAnalyzerTest.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport static com.android.dependencymapper.Utils.listClassesInJar;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\nimport java.net.URISyntaxException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class ClassDependencyAnalyzerTest {\n\n    private static List<ClassDependencyData> mClassDependencyDataList;\n\n    private static final String CLASSES_JAR_PATH =\n            \"tests/res/testfiles/dependency-mapper-test-data.jar\";\n\n    @BeforeClass\n    public static void beforeClass() throws URISyntaxException {\n        Path path = Paths.get(CLASSES_JAR_PATH);\n        Set<String> classesInJar = listClassesInJar(path);\n        // Perform dependency analysis.\n        mClassDependencyDataList = ClassDependencyAnalyzer.analyze(path,\n                new ClassRelevancyFilter(classesInJar));\n    }\n\n    @Test\n    public void testAnnotationDeps(){\n        String annoClass = \"res.testdata.annotation.AnnotationUsage\";\n        String sourceAnno = \"res.testdata.annotation.SourceAnnotation\";\n        String runTimeAnno = \"res.testdata.annotation.RuntimeAnnotation\";\n\n        dependencyVerifier(annoClass,\n                new HashSet<>(List.of(runTimeAnno)), new HashSet<>(List.of(sourceAnno)));\n\n        for (ClassDependencyData dep : mClassDependencyDataList) {\n            if (dep.getQualifiedName().equals(sourceAnno)) {\n                assertTrue(sourceAnno + \" is not dependencyToAll \", dep.isDependencyToAll());\n            }\n            if (dep.getQualifiedName().equals(runTimeAnno)) {\n                assertFalse(runTimeAnno + \" is dependencyToAll \", dep.isDependencyToAll());\n            }\n        }\n    }\n\n    @Test\n    public void testConstantsDeps(){\n        String constDefined = \"test_constant\";\n        String constDefClass = \"res.testdata.constants.ConstantDefinition\";\n        String constUsageClass = \"res.testdata.constants.ConstantUsage\";\n\n        boolean constUsageClassFound = false;\n        boolean constDefClassFound = false;\n        for (ClassDependencyData dep : mClassDependencyDataList) {\n            if (dep.getQualifiedName().equals(constUsageClass)) {\n                constUsageClassFound = true;\n                assertTrue(\"InlinedUsage of : \" + constDefined + \" not found\",\n                        dep.inlinedUsages().contains(constDefined));\n            }\n            if (dep.getQualifiedName().equals(constDefClass)) {\n                constDefClassFound = true;\n                assertTrue(\"Constant \" + constDefined + \" not defined\",\n                        dep.getConstantsDefined().contains(constDefined));\n            }\n        }\n        assertTrue(\"Class \" + constUsageClass + \" not found\", constUsageClassFound);\n        assertTrue(\"Class \" + constDefClass + \" not found\", constDefClassFound);\n    }\n\n    @Test\n    public void testInheritanceDeps(){\n        String sourceClass = \"res.testdata.inheritance.InheritanceUsage\";\n        String baseClass = \"res.testdata.inheritance.BaseClass\";\n        String baseImpl = \"res.testdata.inheritance.BaseImpl\";\n\n        dependencyVerifier(sourceClass,\n                new HashSet<>(List.of(baseClass, baseImpl)), new HashSet<>());\n    }\n\n\n    @Test\n    public void testMethodDeps(){\n        String fieldUsage = \"res.testdata.methods.FieldUsage\";\n        String methodUsage = \"res.testdata.methods.MethodUsage\";\n        String ref1 = \"res.testdata.methods.ReferenceClass1\";\n        String ref2 = \"res.testdata.methods.ReferenceClass2\";\n\n        dependencyVerifier(fieldUsage,\n                new HashSet<>(List.of(ref1)), new HashSet<>(List.of(ref2)));\n        dependencyVerifier(methodUsage,\n                new HashSet<>(List.of(ref1, ref2)), new HashSet<>());\n    }\n\n    private void dependencyVerifier(String qualifiedName, Set<String> deps, Set<String> nonDeps) {\n        boolean depFound = false;\n        for (ClassDependencyData classDependencyData : mClassDependencyDataList) {\n            if (classDependencyData.getQualifiedName().equals(qualifiedName)) {\n                depFound = true;\n                for (String dep : deps) {\n                    assertTrue(qualifiedName + \" does not depends on \" + dep,\n                            classDependencyData.getClassDependencies().contains(dep));\n                }\n                for (String nonDep : nonDeps) {\n                    assertFalse(qualifiedName + \" depends on \" + nonDep,\n                            classDependencyData.getClassDependencies().contains(nonDep));\n                }\n            }\n        }\n        assertTrue(\"Class \" + qualifiedName + \" not found\", depFound);\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassRelevancyFilterTest.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport static com.android.dependencymapper.Utils.listClassesInJar;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotEquals;\n\nimport com.android.dependencymapper.ClassDependencyAnalyzer;\nimport com.android.dependencymapper.ClassDependencyData;\nimport com.android.dependencymapper.ClassRelevancyFilter;\n\nimport org.junit.Test;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.Set;\n\npublic class ClassRelevancyFilterTest {\n\n    private static final String CLASSES_JAR_PATH =\n            \"tests/res/testfiles/dependency-mapper-test-data.jar\";\n\n    @Test\n    public void testClassRelevancyFilter() {\n        Path path = Paths.get(CLASSES_JAR_PATH);\n        Set<String> classesInJar = listClassesInJar(path);\n\n        // Add a relevancy filter that skips a class.\n        String skippedClass = \"res.testdata.BaseClass\";\n        classesInJar.remove(skippedClass);\n\n        // Perform dependency analysis.\n        List<ClassDependencyData> classDependencyDataList =\n                ClassDependencyAnalyzer.analyze(path, new ClassRelevancyFilter(classesInJar));\n\n        // check that the skipped class is not present in classDepsList\n        for (ClassDependencyData dep : classDependencyDataList) {\n            assertNotEquals(\"SkippedClass \" + skippedClass + \" is present\",\n                    skippedClass, dep.getQualifiedName());\n            assertFalse(\"SkippedClass \" + skippedClass + \" is present as dependency of \" + dep,\n                    dep.getClassDependencies().contains(skippedClass));\n        }\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/src/com/android/dependencymapper/DependencyMapperTest.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\npublic class DependencyMapperTest {\n\n    private static final List<JavaSourceData> mJavaSourceData = new ArrayList<>();\n    private static final List<ClassDependencyData> mClassDependencyData = new ArrayList<>();\n\n    private static Map<String, DependencyProto.FileDependency>  mFileDependencyMap;\n\n    public static String AUDIO_CONS = \"AUDIO_CONS\";\n    public static String AUDIO_CONS_PATH = \"frameworks/base/audio/AudioPermission.java\";\n    public static String AUDIO_CONS_PACKAGE = \"com.android.audio.AudioPermission\";\n\n    public static String AUDIO_TONE_CONS_1 = \"AUDIO_TONE_CONS_1\";\n    public static String AUDIO_TONE_CONS_2 = \"AUDIO_TONE_CONS_2\";\n    public static String AUDIO_TONE_CONS_PATH = \"frameworks/base/audio/Audio$Tones.java\";\n    public static String AUDIO_TONE_CONS_PACKAGE = \"com.android.audio.Audio$Tones\";\n\n    public static String ST_MANAGER_PATH = \"frameworks/base/core/storage/StorageManager.java\";\n    public static String ST_MANAGER_PACKAGE = \"com.android.storage.StorageManager\";\n\n    public static String CONST_OUTSIDE_SCOPE = \"CONST_OUTSIDE_SCOPE\";\n    public static String PERM_MANAGER_PATH =  \"frameworks/base/core/permission/PermissionManager.java\";\n    public static String PERM_MANAGER_PACKAGE =  \"com.android.permission.PermissionManager\";\n\n    public static String SOURCE_ANNO_PATH = \"frameworks/base/anno/SourceAnno.java\";\n    public static String SOURCE_ANNO_PACKAGE = \"com.android.anno.SourceAnno\";\n\n    public static String PERM_SOURCE_PATH = \"frameworks/base/core/permission/PermissionSources.java\";\n    public static String PERM_SOURCE_PACKAGE = \"com.android.permission.PermissionSources\";\n\n    public static String PERM_DATA_PATH = \"frameworks/base/core/permission/PermissionSources$Data.java\";\n    public static String PERM_DATA_PACKAGE = \"com.android.permission.PermissionSources$Data\";\n\n    static {\n        JavaSourceData audioConstants = new JavaSourceData(AUDIO_CONS_PATH, AUDIO_CONS_PACKAGE + \".java\");\n        JavaSourceData audioToneConstants =\n                new JavaSourceData(AUDIO_TONE_CONS_PATH, AUDIO_TONE_CONS_PACKAGE + \".java\"); //f2\n        JavaSourceData stManager = new JavaSourceData( ST_MANAGER_PATH, ST_MANAGER_PACKAGE + \".java\");\n        JavaSourceData permManager = new JavaSourceData(PERM_MANAGER_PATH, PERM_MANAGER_PACKAGE + \".java\");\n        JavaSourceData permSource = new JavaSourceData(PERM_SOURCE_PATH, PERM_SOURCE_PACKAGE + \".java\");\n        JavaSourceData permSourceData = new JavaSourceData(PERM_DATA_PATH, PERM_DATA_PACKAGE + \".java\");\n\n        JavaSourceData sourceNotPresentInClass =\n                new JavaSourceData(SOURCE_ANNO_PATH, SOURCE_ANNO_PACKAGE);\n\n        mJavaSourceData.addAll(List.of(audioConstants, audioToneConstants, stManager,\n                permManager, permSource, permSourceData, sourceNotPresentInClass));\n\n        ClassDependencyData audioConstantsDeps =\n                new ClassDependencyData(AUDIO_CONS_PACKAGE + \".java\",\n                        AUDIO_CONS_PACKAGE, new HashSet<>(), false,\n                        new HashSet<>(List.of(AUDIO_CONS)), new HashSet<>());\n\n        ClassDependencyData audioToneConstantsDeps =\n                new ClassDependencyData(AUDIO_TONE_CONS_PACKAGE + \".java\",\n                        AUDIO_TONE_CONS_PACKAGE, new HashSet<>(), false,\n                        new HashSet<>(List.of(AUDIO_TONE_CONS_1, AUDIO_TONE_CONS_2)),\n                        new HashSet<>());\n\n        ClassDependencyData stManagerDeps =\n                new ClassDependencyData(ST_MANAGER_PACKAGE + \".java\",\n                        ST_MANAGER_PACKAGE, new HashSet<>(List.of(PERM_SOURCE_PACKAGE)), false,\n                        new HashSet<>(), new HashSet<>(List.of(AUDIO_CONS, AUDIO_TONE_CONS_1)));\n\n        ClassDependencyData permManagerDeps =\n                new ClassDependencyData(PERM_MANAGER_PACKAGE + \".java\", PERM_MANAGER_PACKAGE,\n                        new HashSet<>(List.of(PERM_SOURCE_PACKAGE, PERM_DATA_PACKAGE)), false,\n                        new HashSet<>(), new HashSet<>(List.of(CONST_OUTSIDE_SCOPE)));\n\n        ClassDependencyData permSourceDeps =\n                new ClassDependencyData(PERM_SOURCE_PACKAGE + \".java\",\n                        PERM_SOURCE_PACKAGE, new HashSet<>(), false,\n                        new HashSet<>(), new HashSet<>());\n\n        ClassDependencyData permSourceDataDeps =\n                new ClassDependencyData(PERM_DATA_PACKAGE + \".java\",\n                        PERM_DATA_PACKAGE, new HashSet<>(), false,\n                        new HashSet<>(), new HashSet<>());\n\n        mClassDependencyData.addAll(List.of(audioConstantsDeps, audioToneConstantsDeps,\n                stManagerDeps, permManagerDeps, permSourceDeps, permSourceDataDeps));\n    }\n\n    @BeforeClass\n    public static void beforeAll(){\n        mFileDependencyMap = buildActualDepsMap(\n                new DependencyMapper(mClassDependencyData, mJavaSourceData).buildDependencyMaps());\n    }\n\n    @Test\n    public void testFileDependencies() {\n        // Test for AUDIO_CONS_PATH\n        DependencyProto.FileDependency audioDepsActual = mFileDependencyMap.get(AUDIO_CONS_PATH);\n        assertNotNull(AUDIO_CONS_PATH + \" not found in dependencyList\", audioDepsActual);\n        // This file should have 0 dependencies.\n        validateDependencies(audioDepsActual, AUDIO_CONS_PATH, 0, new ArrayList<>());\n\n        // Test for AUDIO_TONE_CONS_PATH\n        DependencyProto.FileDependency audioToneDepsActual =\n                mFileDependencyMap.get(AUDIO_TONE_CONS_PATH);\n        assertNotNull(AUDIO_TONE_CONS_PATH + \" not found in dependencyList\", audioDepsActual);\n        // This file should have 0 dependencies.\n        validateDependencies(audioToneDepsActual, AUDIO_TONE_CONS_PATH, 0, new ArrayList<>());\n\n        // Test for ST_MANAGER_PATH\n        DependencyProto.FileDependency stManagerDepsActual =\n                mFileDependencyMap.get(ST_MANAGER_PATH);\n        assertNotNull(ST_MANAGER_PATH + \" not found in dependencyList\", audioDepsActual);\n        // This file should have 3 dependencies.\n        validateDependencies(stManagerDepsActual, ST_MANAGER_PATH, 3,\n                new ArrayList<>(List.of(AUDIO_CONS_PATH, AUDIO_TONE_CONS_PATH, PERM_SOURCE_PATH)));\n\n        // Test for PERM_MANAGER_PATH\n        DependencyProto.FileDependency permManagerDepsActual =\n                mFileDependencyMap.get(PERM_MANAGER_PATH);\n        assertNotNull(PERM_MANAGER_PATH + \" not found in dependencyList\", audioDepsActual);\n        // This file should have 2 dependencies.\n        validateDependencies(permManagerDepsActual, PERM_MANAGER_PATH, 2,\n                new ArrayList<>(List.of(PERM_SOURCE_PATH, PERM_DATA_PATH)));\n\n        // Test for PERM_SOURCE_PATH\n        DependencyProto.FileDependency permSourceDepsActual =\n                mFileDependencyMap.get(PERM_SOURCE_PATH);\n        assertNotNull(PERM_SOURCE_PATH + \" not found in dependencyList\", audioDepsActual);\n        // This file should have 0 dependencies.\n        validateDependencies(permSourceDepsActual, PERM_SOURCE_PATH, 0, new ArrayList<>());\n\n        // Test for PERM_DATA_PATH\n        DependencyProto.FileDependency permDataDepsActual =\n                mFileDependencyMap.get(PERM_DATA_PATH);\n        assertNotNull(PERM_DATA_PATH + \" not found in dependencyList\", audioDepsActual);\n        // This file should have 0 dependencies.\n        validateDependencies(permDataDepsActual, PERM_DATA_PATH, 0, new ArrayList<>());\n    }\n\n    private void validateDependencies(DependencyProto.FileDependency dependency, String fileName, int fileDepsCount, List<String> fileDeps) {\n        assertEquals(fileName + \" does not have expected dependencies\", fileDepsCount, dependency.getFileDependenciesCount());\n        assertTrue(fileName + \" does not have expected dependencies\", dependency.getFileDependenciesList().containsAll(fileDeps));\n    }\n\n    private static Map<String, DependencyProto.FileDependency> buildActualDepsMap(\n            DependencyProto.FileDependencyList fileDependencyList) {\n        Map<String, DependencyProto.FileDependency> dependencyMap = new HashMap<>();\n        for (DependencyProto.FileDependency fileDependency : fileDependencyList.getFileDependencyList()) {\n            if (fileDependency.getFilePath().equals(AUDIO_CONS_PATH)) {\n                dependencyMap.put(AUDIO_CONS_PATH, fileDependency);\n            }\n            if (fileDependency.getFilePath().equals(AUDIO_TONE_CONS_PATH)) {\n                dependencyMap.put(AUDIO_TONE_CONS_PATH, fileDependency);\n            }\n            if (fileDependency.getFilePath().equals(ST_MANAGER_PATH)) {\n                dependencyMap.put(ST_MANAGER_PATH, fileDependency);\n            }\n            if (fileDependency.getFilePath().equals(PERM_MANAGER_PATH)) {\n                dependencyMap.put(PERM_MANAGER_PATH, fileDependency);\n            }\n            if (fileDependency.getFilePath().equals(PERM_SOURCE_PATH)) {\n                dependencyMap.put(PERM_SOURCE_PATH, fileDependency);\n            }\n            if (fileDependency.getFilePath().equals(PERM_DATA_PATH)) {\n                dependencyMap.put(PERM_DATA_PATH, fileDependency);\n            }\n            if (fileDependency.getFilePath().equals(SOURCE_ANNO_PATH)) {\n                dependencyMap.put(SOURCE_ANNO_PATH, fileDependency);\n            }\n        }\n        assertFalse(SOURCE_ANNO_PATH + \" found in dependencyList\",\n                dependencyMap.containsKey(SOURCE_ANNO_PATH));\n        return dependencyMap;\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/src/com/android/dependencymapper/JavaSourceAnalyzerTest.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\nimport java.net.URISyntaxException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class JavaSourceAnalyzerTest {\n    private static List<JavaSourceData> mJavaSourceDataList;\n\n    private static final String SOURCES_RSP_PATH =\n            \"tests/res/testfiles/sources.rsp\";\n\n    @BeforeClass\n    public static void beforeClass() throws URISyntaxException {\n        Path path = Paths.get(SOURCES_RSP_PATH);\n        // Perform source analysis.\n        mJavaSourceDataList = JavaSourceAnalyzer.analyze(path);\n    }\n\n    @Test\n    public void validateSourceData() {\n        Map<String, String> expectedSourceData = expectedSourceData();\n        int expectedFileCount = expectedSourceData.size();\n        int actualFileCount = 0;\n        for (JavaSourceData javaSourceData : mJavaSourceDataList) {\n            String file =  javaSourceData.getFilePath();\n            if (expectedSourceData.containsKey(file)) {\n                actualFileCount++;\n                assertEquals(\"Source Data not generated correctly for \" + file,\n                        expectedSourceData.get(file), javaSourceData.getPackagePrependedFileName());\n            }\n        }\n        assertEquals(\"Not all source files processed\", expectedFileCount, actualFileCount);\n    }\n\n    private Map<String, String> expectedSourceData() {\n        Map<String, String> expectedSourceData = new HashMap<>();\n        expectedSourceData.put(\"tests/res/testdata/annotation/AnnotationUsage.java\",\n                \"res.testdata.annotation.AnnotationUsage.java\");\n        expectedSourceData.put(\"tests/res/testdata/constants/ConstantUsage.java\",\n                \"res.testdata.constants.ConstantUsage.java\");\n        expectedSourceData.put(\"tests/res/testdata/inheritance/BaseClass.java\",\n                \"res.testdata.inheritance.BaseClass.java\");\n        expectedSourceData.put(\"tests/res/testdata/methods/FieldUsage.java\",\n                \"res.testdata.methods.FieldUsage.java\");\n        return expectedSourceData;\n    }\n}\n"
  },
  {
    "path": "tools/dependency_mapper/tests/src/com/android/dependencymapper/UtilsTest.java",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.dependencymapper;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\nimport com.android.dependencymapper.Utils;\n\npublic class UtilsTest {\n\n    @Test\n    public void testTrimAndConvertToPackageBasedPath() {\n        String testPath1 = \"com/android/storage/StorageManager.class\";\n        String testPath2 = \"com/android/package/PackageManager$Package.class\";\n\n        String expectedPackageBasedPath1 = \"com.android.storage.StorageManager\";\n        String expectedPackageBasedPath2 = \"com.android.package.PackageManager$Package\";\n\n        assertEquals(\"Package Based Path not constructed correctly\",\n                expectedPackageBasedPath1, Utils.trimAndConvertToPackageBasedPath(testPath1));\n        assertEquals(\"Package Based Path not constructed correctly\",\n                expectedPackageBasedPath2, Utils.trimAndConvertToPackageBasedPath(testPath2));\n    }\n\n    @Test\n    public void testBuildPackagePrependedClassSource() {\n        String qualifiedClassPath1 = \"com.android.storage.StorageManager\";\n        String sourcePath1 = \"StorageManager.java\";\n        String qualifiedClassPath2 = \"com.android.package.PackageManager$Package\";\n        String sourcePath2 = \"PackageManager.java\";\n        String qualifiedClassPath3 = \"com.android.storage.StorageManager$Storage\";\n        String sourcePath3 = \"StorageManager$Storage.java\";\n\n\n        String expectedPackagePrependedPath1 = \"com.android.storage.StorageManager.java\";\n        String expectedPackagePrependedPath2 = \"com.android.package.PackageManager.java\";\n        String expectedPackagePrependedPath3 = \"com.android.storage.StorageManager$Storage.java\";\n\n        assertEquals(\"Package Prepended Class Source not constructed correctly\",\n                expectedPackagePrependedPath1,\n                Utils.buildPackagePrependedClassSource(qualifiedClassPath1, sourcePath1));\n        assertEquals(\"Package Prepended Class Source not constructed correctly\",\n                expectedPackagePrependedPath2,\n                Utils.buildPackagePrependedClassSource(qualifiedClassPath2, sourcePath2));\n        assertEquals(\"Package Prepended Class Source not constructed correctly\",\n                expectedPackagePrependedPath3,\n                Utils.buildPackagePrependedClassSource(qualifiedClassPath3, sourcePath3));\n    }\n}\n"
  },
  {
    "path": "tools/docker/.gitignore",
    "content": "gitconfig\n"
  },
  {
    "path": "tools/docker/Dockerfile",
    "content": "FROM ubuntu:14.04\nARG userid\nARG groupid\nARG username\n\nRUN apt-get update && apt-get install -y git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache libgl1-mesa-dev libxml2-utils xsltproc unzip python openjdk-7-jdk\n\nRUN curl -o jdk8.tgz https://android.googlesource.com/platform/prebuilts/jdk/jdk8/+archive/master.tar.gz \\\n && tar -zxf jdk8.tgz linux-x86 \\\n && mv linux-x86 /usr/lib/jvm/java-8-openjdk-amd64 \\\n && rm -rf jdk8.tgz\n\nRUN curl -o /usr/local/bin/repo https://storage.googleapis.com/git-repo-downloads/repo \\\n && echo \"d06f33115aea44e583c8669375b35aad397176a411de3461897444d247b6c220  /usr/local/bin/repo\" | sha256sum --strict -c - \\\n && chmod a+x /usr/local/bin/repo\n\nRUN groupadd -g $groupid $username \\\n && useradd -m -u $userid -g $groupid $username \\\n && echo $username >/root/username \\\n && echo \"export USER=\"$username >>/home/$username/.gitconfig\nCOPY gitconfig /home/$username/.gitconfig\nRUN chown $userid:$groupid /home/$username/.gitconfig\nENV HOME=/home/$username\nENV USER=$username\n\nENTRYPOINT chroot --userspec=$(cat /root/username):$(cat /root/username) / /bin/bash -i\n"
  },
  {
    "path": "tools/docker/README.md",
    "content": "The Dockerfile in this directory sets up an Ubuntu Trusty image ready to build\na variety of Android branches (>= Lollipop). It's particulary useful to build\nolder branches that required 14.04 if you've upgraded to something newer.\n\nFirst, build the image:\n```\n# Copy your host gitconfig, or create a stripped down version\n$ cp ~/.gitconfig gitconfig\n$ docker build --build-arg userid=$(id -u) --build-arg groupid=$(id -g) --build-arg username=$(id -un) -t android-build-trusty .\n```\n\nThen you can start up new instances with:\n```\n$ docker run -it --rm -v $ANDROID_BUILD_TOP:/src android-build-trusty\n> cd /src; source build/envsetup.sh\n> lunch aosp_arm-eng\n> m -j50\n```\n"
  },
  {
    "path": "tools/droiddoc/Android.bp",
    "content": "// Copyright (C) 2013 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\n        \"Android-Apache-2.0\",\n        \"build_make_tools_droiddoc_license\",\n    ],\n}\n\nlicense {\n    name: \"build_make_tools_droiddoc_license\",\n    package_name: \"Android Droiddoc Templates\",\n    license_kinds: [\n        \"SPDX-license-identifier-BSD\",\n        \"SPDX-license-identifier-CC-BY-2.5\",\n        \"SPDX-license-identifier-GPL-3.0\",\n        \"SPDX-license-identifier-MIT\",\n    ],\n    license_text: [\"LICENSE\"],\n}\n\ndroiddoc_exported_dir {\n    name: \"droiddoc-templates-pdk\",\n    path: \"templates-pdk\",\n}\n"
  },
  {
    "path": "tools/droiddoc/LICENSE",
    "content": "-----------------------------------------------------\nmicrotemplate.js\n\n// Simple JavaScript Templating\n// John Resig - http://ejohn.org/ - MIT Licensed\n\n-----------------------------------------------------\njquery-history.js\n\n/**\n * jQuery history event v0.1\n * Copyright (c) 2008 Tom Rodenberg <tarodenberg gmail com>\n * Licensed under the GPL (http://www.gnu.org/licenses/gpl.html) license.\n */\n\n                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n\n-----------------------------------------------------\nyui-3.3.0-reset-min.css\n\n/*\nCopyright (c) 2010, Yahoo! Inc. All rights reserved.\nCode licensed under the BSD License:\nhttp://developer.yahoo.com/yui/license.html\nversion: 3.3.0\nbuild: 3167\n*/\n\n\nSoftware License Agreement (BSD License)\nCopyright (c) 2010, Yahoo! Inc.\nAll rights reserved.\n\nRedistribution and use of this software in source and binary forms, with or\nwithout modification, are permitted provided that the following conditions are\nmet:\n\n    Redistributions of source code must retain the above copyright notice, this\n    list of conditions and the following disclaimer.\n    Redistributions in binary form must reproduce the above copyright notice,\n    this list of conditions and the following disclaimer in the documentation\n    and/or other materials provided with the distribution.\n    Neither the name of Yahoo! Inc. nor the names of its contributors may be\n    used to endorse or promote products derived from this software without\n    specific prior written permission of Yahoo! Inc.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\nSources of Intellectual Property Included in the YUI Library\n\nYUI is issued by Yahoo! under the BSD license above. Below is a list of certain\npublicly available software that is the source of intellectual property in YUI,\nalong with the licensing terms that pertain to thosesources of IP. This list is\nfor informational purposes only and is not intended to represent an exhaustive\nlist of third party contributions to the YUI.\n\n    Douglas Crockford's JSON parsing and stringifying methods: In the JSON\n    Utility, Douglas Crockford's JSON parsing and stringifying methods are\n    adapted from work published at JSON.org. The adapted work is in the public\n    domain.\n\n    Robert Penner's animation-easing algorithms: In the Animation Utility, YUI\n    makes use of Robert Penner's algorithms for easing.\n\n    Geoff Stearns's SWFObject: In the Charts Control and the Uploader versions\n    through 2.7.0, YUI makes use of Geoff Stearns's SWFObject v1.5 for Flash\n    Player detection and embedding. More information on SWFObject can be found\n    here (http://blog.deconcept.com/swfobject/). SWFObject is (c) 2007 Geoff\n    Stearns and is released under the MIT License\n    (http://www.opensource.org/licenses/mit-license.php).\n\n    Diego Perini's IEContentLoaded technique: The Event Utility employs a\n    technique developed by Diego Perini and licensed under GPL. YUI's use of\n    this technique is included under our BSD license with the author's\n    permission.\n\n\nFrom MIT license link above:\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n-----------------------------------------------------\ncustomizations.cs\n\n  Except as noted, this content is \n  licensed under <a href=\"http://creativecommons.org/licenses/by/2.5/\">\n  Creative Commons Attribution 2.5</a>.\n\n\nCreative Commons\nCreative Commons Legal Code\n\nAttribution 2.5\nCREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL\nSERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT\nRELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN \"AS-IS\" BASIS.\nCREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND\nDISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.\n\nLicense\n\nTHE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE\nCOMMONS PUBLIC LICENSE (\"CCPL\" OR \"LICENSE\"). THE WORK IS PROTECTED BY\nCOPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS\nAUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.\n\nBY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE\nBOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS\nCONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND\nCONDITIONS.\n\n1. Definitions\n\n    \"Collective Work\" means a work, such as a periodical issue, anthology or\n    encyclopedia, in which the Work in its entirety in unmodified form, along\n    with a number of other contributions, constituting separate and independent\n    works in themselves, are assembled into a collective whole. A work that\n    constitutes a Collective Work will not be considered a Derivative Work (as\n    defined below) for the purposes of this License.\n\n    \"Derivative Work\" means a work based upon the Work or upon the Work and\n    other pre-existing works, such as a translation, musical arrangement,\n    dramatization, fictionalization, motion picture version, sound recording,\n    art reproduction, abridgment, condensation, or any other form in which the\n    Work may be recast, transformed, or adapted, except that a work that\n    constitutes a Collective Work will not be considered a Derivative Work for\n    the purpose of this License. For the avoidance of doubt, where the Work is\n    a musical composition or sound recording, the synchronization of the Work\n    in timed-relation with a moving image (\"synching\") will be considered a\n    Derivative Work for the purpose of this License.\n\n    \"Licensor\" means the individual or entity that offers the Work under the\n    terms of this License.\n\n    \"Original Author\" means the individual or entity who created the Work.\n\n    \"Work\" means the copyrightable work of authorship offered under the terms\n    of this License.\n\n    \"You\" means an individual or entity exercising rights under this License\n    who has not previously violated the terms of this License with respect to\n    the Work, or who has received express permission from the Licensor to\n    exercise rights under this License despite a previous violation.\n\n2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or\nrestrict any rights arising from fair use, first sale or other limitations on\nthe exclusive rights of the copyright owner under copyright law or other\napplicable laws.\n\n3. License Grant. Subject to the terms and conditions of this License, Licensor\nhereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the\nduration of the applicable copyright) license to exercise the rights in the\nWork as stated below:\n\n    to reproduce the Work, to incorporate the Work into one or more Collective\n    Works, and to reproduce the Work as incorporated in the Collective Works;\n\n    to create and reproduce Derivative Works;\n\n    to distribute copies or phonorecords of, display publicly, perform\n    publicly, and perform publicly by means of a digital audio transmission the\n    Work including as incorporated in Collective Works;\n\n    to distribute copies or phonorecords of, display publicly, perform\n    publicly, and perform publicly by means of a digital audio transmission\n    Derivative Works.\n\n    For the avoidance of doubt, where the work is a musical composition:\n        Performance Royalties Under Blanket Licenses. Licensor waives the\n        exclusive right to collect, whether individually or via a performance\n        rights society (e.g. ASCAP, BMI, SESAC), royalties for the public\n        performance or public digital performance (e.g. webcast) of the Work.\n\n        Mechanical Rights and Statutory Royalties. Licensor waives the\n        exclusive right to collect, whether individually or via a music rights\n        agency or designated agent (e.g. Harry Fox Agency), royalties for any\n        phonorecord You create from the Work (\"cover version\") and distribute,\n        subject to the compulsory license created by 17 USC Section 115 of the\n        US Copyright Act (or the equivalent in other jurisdictions).\n\n    Webcasting Rights and Statutory Royalties. For the avoidance of doubt,\n    where the Work is a sound recording, Licensor waives the exclusive right to\n    collect, whether individually or via a performance-rights society (e.g.\n    SoundExchange), royalties for the public digital performance (e.g. webcast)\n    of the Work, subject to the compulsory license created by 17 USC Section\n    114 of the US Copyright Act (or the equivalent in other jurisdictions).\n\nThe above rights may be exercised in all media and formats whether now known or\nhereafter devised. The above rights include the right to make such\nmodifications as are technically necessary to exercise the rights in other\nmedia and formats. All rights not expressly granted by Licensor are hereby\nreserved.\n\n4. Restrictions.The license granted in Section 3 above is expressly made\nsubject to and limited by the following restrictions:\n\n    You may distribute, publicly display, publicly perform, or publicly\n    digitally perform the Work only under the terms of this License, and You\n    must include a copy of, or the Uniform Resource Identifier for, this\n    License with every copy or phonorecord of the Work You distribute, publicly\n    display, publicly perform, or publicly digitally perform. You may not offer\n    or impose any terms on the Work that alter or restrict the terms of this\n    License or the recipients' exercise of the rights granted hereunder. You\n    may not sublicense the Work. You must keep intact all notices that refer to\n    this License and to the disclaimer of warranties. You may not distribute,\n    publicly display, publicly perform, or publicly digitally perform the Work\n    with any technological measures that control access or use of the Work in a\n    manner inconsistent with the terms of this License Agreement. The above\n    applies to the Work as incorporated in a Collective Work, but this does not\n    require the Collective Work apart from the Work itself to be made subject\n    to the terms of this License. If You create a Collective Work, upon notice\n    from any Licensor You must, to the extent practicable, remove from the\n    Collective Work any credit as required by clause 4(b), as requested. If You\n    create a Derivative Work, upon notice from any Licensor You must, to the\n    extent practicable, remove from the Derivative Work any credit as required\n    by clause 4(b), as requested.\n\n    If you distribute, publicly display, publicly perform, or publicly\n    digitally perform the Work or any Derivative Works or Collective Works, You\n    must keep intact all copyright notices for the Work and provide, reasonable\n    to the medium or means You are utilizing: (i) the name of the Original\n    Author (or pseudonym, if applicable) if supplied, and/or (ii) if the\n    Original Author and/or Licensor designate another party or parties (e.g. a\n    sponsor institute, publishing entity, journal) for attribution in\n    Licensor's copyright notice, terms of service or by other reasonable means,\n    the name of such party or parties; the title of the Work if supplied; to\n    the extent reasonably practicable, the Uniform Resource Identifier, if any,\n    that Licensor specifies to be associated with the Work, unless such URI\n    does not refer to the copyright notice or licensing information for the\n    Work; and in the case of a Derivative Work, a credit identifying the use of\n    the Work in the Derivative Work (e.g., \"French translation of the Work by\n    Original Author,\" or \"Screenplay based on original Work by Original\n    Author\"). Such credit may be implemented in any reasonable manner;\n    provided, however, that in the case of a Derivative Work or Collective\n    Work, at a minimum such credit will appear where any other comparable\n    authorship credit appears and in a manner at least as prominent as such\n    other comparable authorship credit.\n\n5. Representations, Warranties and Disclaimer\n\nUNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS\nTHE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND\nCONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING,\nWITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A\nPARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,\nACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.\nSOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH\nEXCLUSION MAY NOT APPLY TO YOU.\n\n6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN\nNO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL,\nINCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS\nLICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n7. Termination\n\n    This License and the rights granted hereunder will terminate automatically\n    upon any breach by You of the terms of this License. Individuals or\n    entities who have received Derivative Works or Collective Works from You\n    under this License, however, will not have their licenses terminated\n    provided such individuals or entities remain in full compliance with those\n    licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of\n    this License.\n\n    Subject to the above terms and conditions, the license granted here is\n    perpetual (for the duration of the applicable copyright in the Work).\n    Notwithstanding the above, Licensor reserves the right to release the Work\n    under different license terms or to stop distributing the Work at any time;\n    provided, however that any such election will not serve to withdraw this\n    License (or any other license that has been, or is required to be, granted\n    under the terms of this License), and this License will continue in full\n    force and effect unless terminated as stated above.\n\n8. Miscellaneous\n\n    Each time You distribute or publicly digitally perform the Work or a\n    Collective Work, the Licensor offers to the recipient a license to the Work\n    on the same terms and conditions as the license granted to You under this\n    License.\n\n    Each time You distribute or publicly digitally perform a Derivative Work,\n    Licensor offers to the recipient a license to the original Work on the same\n    terms and conditions as the license granted to You under this License.\n\n    If any provision of this License is invalid or unenforceable under\n    applicable law, it shall not affect the validity or enforceability of the\n    remainder of the terms of this License, and without further action by the\n    parties to this agreement, such provision shall be reformed to the minimum\n    extent necessary to make such provision valid and enforceable.\n\n    No term or provision of this License shall be deemed waived and no breach\n    consented to unless such waiver or consent shall be in writing and signed\n    by the party to be charged with such waiver or consent.\n\n    This License constitutes the entire agreement between the parties with\n    respect to the Work licensed here. There are no understandings, agreements\n    or representations with respect to the Work not specified here. Licensor\n    shall not be bound by any additional provisions that may appear in any\n    communication from You. This License may not be modified without the mutual\n    written agreement of the Licensor and You.\n\nCreative Commons is not a party to this License, and makes no warranty\nwhatsoever in connection with the Work. Creative Commons will not be liable to\nYou or any party on any legal theory for any damages whatsoever, including\nwithout limitation any general, special, incidental or consequential damages\narising in connection to this license. Notwithstanding the foregoing two (2)\nsentences, if Creative Commons has expressly identified itself as the Licensor\nhereunder, it shall have all rights and obligations of Licensor.\n\nExcept for the limited purpose of indicating to the public that the Work is\nlicensed under the CCPL, neither party will use the trademark \"Creative\nCommons\" or any related trademark or logo of Creative Commons without the prior\nwritten consent of Creative Commons. Any permitted use will be in compliance\nwith Creative Commons' then-current trademark usage guidelines, as may be\npublished on its website or otherwise made available upon request from time to\ntime.\n\nCreative Commons may be contacted at https://creativecommons.org/.\n\n-----------------------------------------------------\njquery-resizable.min.js\n\n/*\n * jQuery JavaScript Library v1.3.2\n * http://jquery.com/\n *\n * Copyright (c) 2009 John Resig\n * Dual licensed under the MIT and GPL licenses.\n * http://docs.jquery.com/License\n *\n * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)\n * Revision: 6246\n */\n\nThe MIT License (MIT)\n\nCopyright (c) 2009 John Resig\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n-----------------------------------------------------\njquery-1.6.2.min.js\n\n/*!\n * jQuery JavaScript Library v1.6.2\n * http://jquery.com/\n *\n * Copyright 2011, John Resig\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * Includes Sizzle.js\n * http://sizzlejs.com/\n * Copyright 2011, The Dojo Foundation\n * Released under the MIT, BSD, and GPL Licenses.\n *\n * Date: Thu Jun 30 14:16:56 2011 -0400\n */\n\nThe MIT License (MIT)\n\nCopyright (c) 2011 John Resig, and The Dojo Foundation\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "tools/droiddoc/README",
    "content": "If you're looking for the templates-sdk/ files, they've moved\nto external/doclava/res/assets/.\n\nThe remaining template files here should also be eventually removed\nso that we can unify the structure and style of all DocLava builds.\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/android-developer-core.css",
    "content": "/* file: android-developer-core.css\n   author: smain\n   date: september 2008\n   info: core developer styles (developer.android.com)\n*/\n\n\n/* RESET STYLES */\n\nhtml,body,div,h1,h2,h3,h4,h5,h6,p,img,\ndl,dt,dd,ol,ul,li,table,caption,tbody,\ntfoot,thead,tr,th,td,form,fieldset,\nembed,object,applet {\n  margin: 0;\n  padding: 0;\n  border: 0;\n}\n\n/* BASICS */\n\nhtml, body {\n  overflow:hidden; /* keeps scrollbar off IE */\n  background-color:#fff;\n}\n\nbody {\n  font-family:arial,sans-serif;\n  color:#000;\n  font-size:13px;\n  color:#333;\n  background-image:url(images/bg_fade.jpg);\n  background-repeat:repeat-x;\n}\n\na, a code {\n  color:#006699;\n}\n\na:active,\na:active code {\n  color:#f00;\n} \n\na:visited,\na:visited code {\n  color:#006699;\n}\n\ninput, select,\ntextarea, option, label {\n  font-family:inherit;\n  font-size:inherit;\n  padding:0;\n  margin:0;\n  vertical-align:middle;\n}\n\noption {\n  padding:0 4px;\n}\n\np, form {\n  padding:0;\n  margin:0 0 1em;\n}\n\ncode, pre {\n  color:#007000;\n  font-family:monospace;\n  line-height:1em;\n}\n\nvar {\n  color:#007000;\n  font-style:italic;\n}\n\npre {\n  border:1px solid #ccc;\n  background-color:#fafafa;\n  padding:10px;\n  margin:0 0 1em 1em;\n  overflow:auto;\n  line-height:inherit; /* fixes vertical scrolling in webkit */\n}\n\nh1,h2,h3,h4,h5 {\n  margin:1em 0;\n  padding:0;\n}\n\np,ul,ol,dl,dd,dt,li {\n  line-height:1.3em;\n}\n\nul,ol {\n  margin:0 0 .8em;\n  padding:0 0 0 2em;\n}\n\nli {\n  padding:0 0 .5em;\n}\n\ndl {\n  margin:0 0 1em 0;\n  padding:0;\n}\n\ndt {\n  margin:0;\n  padding:0;\n}\n\ndd {\n  margin:0 0 1em;\n  padding:0 0 0 2em;\n}\n\nli p {\n  margin:.5em 0 0;\n}\n\ndd p {\n  margin:1em 0 0;\n}\n\nli pre, li table, li img {\n  margin:.5em 0 0 1em;\n}\n\ndd pre,\n#jd-content dd table,\n#jd-content dd img {\n  margin:1em 0 0 1em;\n}\n\nli ul,\nli ol,\ndd ul,\ndd ol {\n  margin:0;\n  padding: 0 0 0 2em;\n}\n\nli li,\ndd li {\n  margin:0;\n  padding:.5em 0 0;\n}\n\ndl dl,\nol dl,\nul dl {\n  margin:0 0 1em;\n  padding:0;\n}\n\ntable {\n  font-size:1em;\n  margin:0 0 1em;\n  padding:0;\n  border-collapse:collapse;\n  border-width:0;\n  empty-cells:show;\n}\n\ntd,th {\n  border:1px solid #ccc;\n  padding:6px 12px;\n  text-align:left;\n  vertical-align:top;\n  background-color:inherit;\n}\n\nth {\n  background-color:#dee8f1;\n}\n\ntd > p:last-child {\n  margin:0;\n}\n\nhr.blue {\n  background-color:#DDF0F2;\n  border:none;\n  height:5px;\n  margin:20px 0 10px;\n}\n\nblockquote {\n  margin: 0 0 1em 1em;\n  padding: 0 4em 0 1em;\n  border-left:2px solid #eee;\n}\n/* LAYOUT */\n\n#body-content {\n  /* \"Preliminary\" watermark for preview releases and interim builds.\n  background:transparent url(images/preliminary.png) repeat scroll 0 0; */\n  margin:0;\n  position:relative;\n  width:100%;\n}\n\n#header {\n  height: 114px;\n  position:relative;\n  z-index:100;\n  min-width:675px; /* min width for the tabs, before they wrap */\n  padding:0 10px;\n  border-bottom:3px solid #94b922;\n}\n\n#headerLeft{\n  padding: 25px 0 0;\n}\n\n#headerLeft img{\n  height:50px;\n}\n\n#headerRight {\n  position:absolute;\n  right:0;\n  top:0;\n  text-align:right;\n}\n\n/* Tabs in the header */\n\n#header ul {\n  list-style: none;\n  margin: 7px 0 0;\n  padding: 0;\n  height: 29px;\n}\n\n#header li {\n  float: left;\n  margin: 0px 2px 0px 0px;\n  padding:0;\n}\n\n#header li a {\n  text-decoration: none;\n  display: block;\n  background-image: url(images/bg_images_sprite.png);\n  background-position: 0 -58px;\n  background-repeat: no-repeat;\n  color: #666;\n  font-size: 13px;\n  font-weight: bold;\n  width: 94px;\n  height: 29px;\n  text-align: center;\n  margin: 0px;\n}\n\n#header li a:hover {\n  background-image: url(images/bg_images_sprite.png);\n  background-position: 0 -29px;\n  background-repeat: no-repeat;\n}\n\n#header li a span {\n  position:relative;\n  top:7px;\n}\n\n#header li a span+span {\n  display:none;\n}\n\n/* tab highlighting */\n\n.home #home-link a,\n.guide #guide-link a,\n.reference #reference-link a,\n.sdk #sdk-link a,\n.resources #resources-link a,\n.videos #videos-link a {\n  background-image: url(images/bg_images_sprite.png);\n  background-position: 0 0;\n  background-repeat: no-repeat;\n  color: #fff;\n  font-weight: bold;\n  cursor:default;\n}\n\n.home #home-link a:hover,\n.guide #guide-link a:hover,\n.reference #reference-link a:hover,\n.sdk #sdk-link a:hover,\n.resources #resources-link a:hover,\n.videos #videos-link  a:hover {\n  background-image: url(images/bg_images_sprite.png);\n  background-position: 0 0;\n}\n\n#headerLinks {\n  margin:10px 10px 0 0;\n  height:13px;\n  font-size: 11px;\n  vertical-align: top;\n}\n\n#headerLinks a {\n  color: #7FA9B5;\n}\n\n#headerLinks img {\n  vertical-align:middle;\n}\n\n#language {\n  margin:0 10px 0 4px;\n}\n\n#search {\n  height:45px;\n  margin:15px 10px 0 0;\n}\n\n/* MAIN BODY */\n\n#mainBodyFluid {\n  margin: 20px 10px;\n  color:#333;\n}\n\n#mainBodyFixed {\n  margin: 20px 10px;\n  color: #333;\n  width:930px;\n  position:relative;\n}\n\n#mainBodyFixed h3,\n#mainBodyFluid h3 {\n  color:#336666;\n  font-size:1.25em;\n  margin: 0em 0em 0em 0em;\n  padding-bottom:.5em;\n}\n\n#mainBodyFixed h2,\n#mainBodyFluid h2 {\n  color:#336666;\n  font-size:1.25em;\n  margin: 0;\n  padding-bottom:.5em;\n}\n\n#mainBodyFixed h1,\n#mainBodyFluid h1 {\n  color:#435A6E;\n  font-size:1.7em;\n  margin: 1em 0;\n}\n\n#mainBodyFixed .green,\n#mainBodyFluid .green,\n#jd-content .green {\n  color:#7BB026;\n  background-color:none;\n}\n\n#mainBodyLeft {\n  float: left;\n  width: 600px;\n  margin-right: 20px;\n  color: #333;\n  position:relative;\n}\n\ndiv.indent {\n  margin-left: 40px;\n  margin-right: 70px;\n}\n\n#mainBodyLeft p {\n  color: #333;\n  font-size: 13px;\n}\n\n#mainBodyLeft p.blue {\n  color: #669999;\n}\n\n#mainBodyLeft #communityDiv {\n  float: left;\n  background-image:url(images/bg_community_leftDiv.jpg);\n  background-repeat: no-repeat;\n  width: 581px;\n  height: 347px;\n  padding: 20px 0px 0px 20px;\n}\n\n#mainBodyRight {\n  float: left;\n  width: 300px;\n  color: #333;\n}\n\n#mainBodyRight p {\n  padding-right: 50px;\n  color: #333;\n}\n\n#mainBodyRight table {\n  width: 100%;\n}\n\n#mainBodyRight td {\n  border:0px solid #666;\n  padding:0px 5px;\n  text-align:left;\n}\n\n#mainBodyRight td p {\n  margin:0 0 1em 0;\n}\n\n#mainBodyRight .blueBorderBox {\n  border:5px solid #ddf0f2;\n  padding:18px 18px 18px 18px;\n  text-align:left;\n}\n\n#mainBodyFixed .seperator {\n  background-image:url(images/hr_gray_side.jpg);\n  background-repeat:no-repeat;\n  width: 100%;\n  float: left;\n  clear: both;\n}\n\n#mainBodyBottom {\n  float: left;\n  width: 100%;\n  clear:both;\n  color: #333;\n}\n\n#mainBodyBottom .seperator {\n  background-image:url(images/hr_gray_main.jpg);\n  background-repeat:no-repeat;\n  width: 100%;\n  float: left;\n  clear: both;\n}\n\n/* FOOTER */\n\n#footer {\n  float: left;\n  width:90%;\n  margin: 20px;\n  color: #aaa;\n  font-size: 11px;\n}\n\n#footer a {\n  color: #aaa;\n  font-size: 11px;\n}\n\n#footer a:hover {\n  text-decoration: underline;\n  color:#aaa;\n}\n\n#footerlinks {\n  margin-top:2px;\n}\n\n#footerlinks a,\n#footerlinks a:visited {\n  color:#006699;\n}\n\n/* SEARCH FILTER */\n\n#search_autocomplete {\n  color:#aaa;\n}\n\n#search-button {\n  display:inline;\n}\n\n#search_filtered_div {\n  position:absolute;\n  margin-top:-1px;\n  z-index:101;\n  border:1px solid #BCCDF0;\n  background-color:#fff;\n}\n\n#search_filtered {\n  min-width:100%;\n}\n#search_filtered td{\n  background-color:#fff;\n  border-bottom: 1px solid #669999;\n  line-height:1.5em;\n}\n\n#search_filtered .jd-selected {\n  background-color: #94b922;\n  cursor:pointer;\n}\n#search_filtered .jd-selected,\n#search_filtered .jd-selected a {\n  color:#fff;\n}\n\n.no-display {\n  display: none;\n}\n\n.jd-autocomplete {\n  font-family: Arial, sans-serif;\n  padding-left: 6px;\n  padding-right: 6px;\n  padding-top: 1px;\n  padding-bottom: 1px;\n  font-size: 0.81em;\n  border: none;\n  margin: 0;\n  line-height: 1.05em;\n}\n\n.show-row {\n  display: table-row;\n}\n.hide-row {\n  display: hidden;\n}\n\n/* SEARCH */\n\n/* restrict global search form width */\n#searchForm {\n  width:350px;\n}\n\n#searchTxt {\n  width:200px;\n}\n\n/* disable twiddle and size selectors for left column */\n#leftSearchControl div {\n  width: 100%;\n}\n\n#leftSearchControl .gsc-twiddle {\n  background-image : none;\n}\n\n#leftSearchControl td, #searchForm td {\n  border: 0px solid #000;\n}\n\n#leftSearchControl .gsc-resultsHeader .gsc-title {\n  padding-left : 0px;\n  font-weight : bold;\n  font-size : 13px;\n  color:#006699;\n  display : none;\n}\n\n#leftSearchControl .gsc-resultsHeader div.gsc-results-selector {\n  display : none;\n}\n\n#leftSearchControl .gsc-resultsRoot {\n  padding-top : 6px;\n}\n\n#leftSearchControl div.gs-visibleUrl-long {\n  display : block;\n  color:#006699;\n}\n\n.gsc-webResult div.gs-visibleUrl-short,\ntable.gsc-branding,\n.gsc-clear-button {\n  display : none;\n}\n\n.gsc-cursor-box .gsc-cursor div.gsc-cursor-page,\n.gsc-cursor-box .gsc-trailing-more-results a.gsc-trailing-more-results,\n#leftSearchControl a,\n#leftSearchControl a b {\n  color:#006699;\n}\n\n.gsc-resultsHeader {\n  display: none;\n}\n\n/* Disable built in search forms */\n.gsc-control form.gsc-search-box {\n  display : none;\n}\ntable.gsc-search-box {\n  margin:6px 0 0 0;\n  border-collapse:collapse;\n}\n\ntd.gsc-input {\n  padding:0 2px;\n  width:100%;\n  vertical-align:middle;\n}\n\ninput.gsc-input {\n  border:1px solid #BCCDF0;\n  width:99%;\n  padding-left:2px;\n  font-size:.95em;\n}\n\ntd.gsc-search-button {\n  text-align: right;\n  padding:0;\n  vertical-align:top;\n}\n\n#search-button {\n  margin:0 0 0 2px;\n  font-size:11px;\n}\n\n/* search result tabs */\n\n#doc-content .gsc-control {\n  position:relative;\n}\n\n#doc-content .gsc-tabsArea {\n  position:relative;\n  white-space:nowrap;\n}\n\n#doc-content .gsc-tabHeader {\n  padding: 3px 6px;\n  position:relative;\n  width:auto;\n}\n\n#doc-content .gsc-tabHeader.gsc-tabhActive {\n  border-top: 2px solid #94B922;\n}\n\n#doc-content h2#searchTitle {\n  padding:0;\n}\n\n#doc-content .gsc-resultsbox-visible {\n  padding:1em 0 0 6px;\n}\n\n/* CAROUSEL */\n\n#homeMiddle {\n  padding: 0px 0px 0px 0px;\n  float: left;\n  width: 584px;\n  height: 627px;\n  position:relative;\n}\n\n#topAnnouncement {\n  background:url(images/home/bg_home_announcement.png) no-repeat 0 0;\n}\n  \n#homeTitle {\n  padding:15px 15px 0;\n  height:30px;\n}\n\n#homeTitle h2 {\n  padding:0;\n}\n\n#announcement-block {\n  padding:0 15px 0;\n  overflow:hidden;\n  background: url(images/hr_gray_side.jpg) no-repeat 15px 0;\n  zoom:1;\n}\n\n#announcement-block>* {\n  padding:15px 0 0;\n}\n\n#announcement-block img {\n  float:left;\n  margin:0 30px 0 0;\n}\n\n#announcement {\n  float:left;\n  margin:0;\n}\n\n#carousel {\n  background:url(images/home/bg_home_carousel.png) no-repeat 0 0;\n  position:relative;\n  height:400px;\n}\n\n#carouselMain {\n  background: url(images/home/bg_home_carousel_board.png) 0 0 no-repeat;\n  height:auto;\n  padding: 25px 21px 0;\n  overflow:hidden;\n  position:relative;\n  zoom:1; /*IE6*/\n}\n\n#carouselMain img {\n  margin:0;\n}\n\n#carouselMain .bulletinDesc h3 {\n  margin:0;\n  padding:0;\n}\n\n#carouselMain .bulletinDesc p {\n  margin:0;\n  padding:0.7em 0 0;\n}\n\n#carouselWheel {\n  background: url(images/home/bg_home_carousel_wheel.png) 0 0 no-repeat;\n  padding-top:40px;\n  height:150px;\n}\n\n.clearer { clear:both; }\n\na#arrow-left, a#arrow-right {\n  float:left;\n  width:42px;\n  height:42px;\n  background-image:url(images/home/carousel_buttons_sprite.png);\n  background-repeat:no-repeat;\n}\na#arrow-left {\n  margin:35px 3px 0 10px;\n}\na#arrow-right {\n  margin:35px 10px 0 0;\n}\na.arrow-left-off,\na#arrow-left.arrow-left-off:hover {\n  background-position:0 0;\n}\na.arrow-right-off,\na#arrow-right.arrow-right-off:hover {\n  background-position:-42px 0;\n}\na#arrow-left:hover {\n  background-position:0 -42px;\n}\na#arrow-right:hover {\n  background-position:-42px -42px;\n}\na.arrow-left-on {\n  background-position:0 0;\n}\na.arrow-right-on {\n  background-position:-42px 0;\n}\na.arrow-right-off,\na.arrow-left-off {\n  cursor:default;\n}\n\n.app-list-container {\n  margin:0 20px;\n  position:relative;\n  width:100%;\n}\n\ndiv#list-clip {\n  height:110px;\n  width:438px;\n  overflow:hidden;\n  position:relative;\n  float:left;\n}\n\ndiv#app-list {\n  left:0;\n  z-index:1;\n  position:absolute;\n  margin:11px 0 0;\n  _margin-top:13px;\n  width:1000%;\n}\n\n#app-list a {\n  display:block;\n  float:left;\n  height:90px;\n  width:90px;\n  margin:0 24px 0;\n  padding:3px;\n  background:#99cccc;\n  -webkit-border-radius:7px;\n  -moz-border-radius:7px;\n  border-radius:7px;\n  text-decoration:none;\n  text-align:center;\n  font-size:11px;\n  line-height:11px;\n}\n\n#app-list a span {\n  position:relative;\n  top:-4px;\n}\n\n#app-list img {\n  width:90px;\n  height:70px;\n  margin:0;\n}\n\n#app-list a.selected,\n#app-list a:active.selected,\n#app-list a:hover.selected {\n  background:#A4C639;\n  color:#fff;\n  cursor:default;\n  text-decoration:none;\n}\n\n#app-list a:hover,\n#app-list a:active {\n  background:#ff9900;\n}\n\n#app-list a:hover span,\n#app-list a:active span {\n  text-decoration:underline;\n}\n\n#droid-name {\n  padding-top:.5em;\n  color:#666;\n  padding-bottom:.25em;\n}\n\n/*IE6*/\n* html #app-list a { zoom: 1; margin:0 24px 0 15px;}\n\n* html #list-clip {\n  width:430px !important;\n}\n\n/*carousel bulletin layouts*/\n/*460px width*/\n/*185px height*/\n.img-left {\n  float:left;\n  width:230px;\n  overflow:hidden;\n  padding:8px 0 8px 8px;\n}\n.desc-right {\n  float:left;\n  width:270px;\n  padding:10px;\n}\n.img-right {\n  float:right;\n  width:220px;\n  overflow:hidden;\n  padding:8px 8px 8px 0;\n}\n.desc-left {\n  float:right;\n  width:280px;\n  padding:10px;\n  text-align:right;\n}\n.img-top {\n  padding:20px 20px 0;\n}\n.desc-bottom {\n  padding:10px;\n}\n\n\n/* VIDEO PAGE */\n\n#mainBodyLeft.videoPlayer {\n  width:570px;\n}\n\n#mainBodyRight.videoPlayer {\n  width:330px;\n}\n\n/* player */\n\n#videoPlayerBox {\n  background-color: #DAF3FC;\n  border-radius:7px;\n  -moz-border-radius:7px;\n  -webkit-border-radius:7px;\n  width:530px;\n  padding:20px;\n  border:1px solid #d3ecf5;\n  box-shadow:2px 3px 1px #eee;\n  -moz-box-shadow:2px 3px 1px #eee;\n  -webkit-box-shadow:2px 3px 1px #eee;\n}\n\n#videoBorder {\n  background-color: #FFF;\n  min-height:399px;\n  height:auto !important;\n  border:1px solid #ccdada;\n  border-radius:7px 7px 0 0;\n  -moz-border-radius:7px 7px 0 0;\n  -webkit-border-top-left-radius:7px;\n  -webkit-border-top-right-radius:7px;\n}\n\n#videoPlayerTitle {\n  width:500px;\n  padding:15px 15px 0;\n}\n\n#videoPlayerTitle h2 {\n  font-weight:bold;\n  font-size:1.2em;\n  color:#336666;\n  margin:0;\n  padding:0;\n}\n\n#objectWrapper {\n  padding:15px 15px;\n  height:334px;\n  width:500px;\n}\n\n/* playlist tabs */\n\nul#videoTabs {\n  list-style-type:none;\n  padding:0;\n  clear:both;\n  margin:0;\n  padding: 20px 0 0 15px;\n  zoom:1; /* IE7/8, otherwise top-padding is double */\n}\n\nul#videoTabs li {\n  display:inline;\n  padding:0;\n  margin:0 3px 0 0;\n  line-height:2em;\n}\n\nul#videoTabs li a {\n  border-radius:7px 7px 0 0;\n  -moz-border-radius:7px 7px 0 0;\n  -webkit-border-top-left-radius:7px;\n  -webkit-border-top-right-radius:7px;\n  background:#95c0d0;\n  color:#fff;\n  text-decoration:none;\n  padding:.45em 1.5em;\n  font-weight:bold;\n}\n\nul#videoTabs li.selected a {\n  font-weight:bold;\n  text-decoration:none;\n  color:#555;\n  background:#daf3fc;\n  border-bottom:1px solid #daf3fc;\n}\n\nul#videoTabs li:hover a {\n  background:#85acba;\n}\n\nul#videoTabs li.selected:hover a {\n  background:#daf3fc;\n}\n\n/* playlists */\n\n#videos {\n  background:#daf3fc;\n  margin-bottom:1.5em;\n  padding:15px;\n  border-radius:5px;\n  -moz-border-radius:5px;\n  -webkit-border-radius:5px;\n  box-shadow:2px 3px 1px #eee;\n  -moz-box-shadow:2px 3px 1px #eee;\n  -webkit-box-shadow:2px 3px 1px #eee;\n}\n\n#videos div {\n  display:none;\n}\n\n#videos div.selected {\n  display:block;\n}\n\nul.videoPreviews {\n  list-style:none;\n  padding:0;\n  margin:0;\n  zoom:1; /* IE, otherwise, layout doesn't update when showing 'more' */\n}\n\nul.videoPreviews li {\n  margin:0 0 5px;\n  padding:0;\n  overflow:hidden;\n  position:relative;\n}\n\n#mainBodyFixed ul.videoPreviews h3 {\n  font-size: 12px;\n  margin:0 0 1em 130px;\n  padding:0;\n  font-weight:bold;\n  color:inherit;\n}\n\nul.videoPreviews a {\n  margin:1px;\n  padding:10px;\n  text-decoration:none;\n  height:90px;\n  display:block;\n  border-radius:5px;\n  -moz-border-radius:5px;\n  -webkit-border-radius:5px;\n  background-color:transparent;\n}\n\nul.videoPreviews a:hover {\n  background-color:#FFF;\n  border:none; /* IE8, otherwise, bg doesn't work */\n}\n\nul.videoPreviews a.selected {\n  background-color: #FF9900;\n}\n\nul.videoPreviews img {\n  float:left;\n  clear:left;\n  margin:0;\n}\n\nul.videoPreviews h3 {\n  font-size:12px;\n  font-weight:bold;\n  text-decoration:none;\n  margin:0 0 1em 130px;\n  padding:0;\n}\n\nul.videoPreviews p {\n  font-size: 12px;\n  text-decoration:none;\n  margin:0 0 1.2em 130px;\n}\n\nul.videoPreviews p.full {\n  display:none;\n}\n\nul.videoPreviews span.more {\n  padding:0 0 0 12px;\n  background:url(images/arrow_bluelink_down.png) 0 2px no-repeat;\n}\n\nul.videoPreviews span.less {\n  padding:0 0 0 12px;\n  background:url(images/arrow_bluelink_up.png) 0 2px no-repeat;\n  display:none;\n}\n\nul.videoPreviews p.toggle {\n  position:absolute;\n  margin:0;\n  margin-top:-23px; /* instead of bottom:23px, because IE won't do it correctly */\n  left:140px;\n}\n\nul.videoPreviews p.toggle a {\n  height:auto;\n  margin:0;\n  padding:0;\n  zoom:1; /* IE6, otherwise the margin considers the img on redraws */\n}\n\nul.videoPreviews p.toggle a:hover {\n  text-decoration:underline;\n  background:transparent; /* IE6, otherwise it inherits white */\n}\n\n/* featured videos */\n\n#mainBodyRight h2 {\n  padding:0 0 5px;\n}\n\n#mainBodyRight ul.videoPreviews {\n  margin:10px 0 0;\n}\n\n#mainBodyRight ul.videoPreviews li {\n  font-size:11px;\n  line-height:13px;\n  margin:0 0 5px;\n  padding:0;\n}\n\n#mainBodyRight ul.videoPreviews h3 {\n  padding:0;\n  margin:0;\n  font-size:100%;\n}\n\n#mainBodyRight ul.videoPreviews a {\n  text-decoration:none;\n  height:108px;\n  border:1px solid #FFF;\n}\n\n#mainBodyRight ul.videoPreviews a:hover {\n  border:1px solid #CCDADA;\n}\n\n#mainBodyRight ul.videoPreviews a.selected {\n  border:1px solid #FFF;\n}\n\n#mainBodyRight ul.videoPreviews p {\n  line-height:1.2em;\n  padding:0;\n  margin:4px 0 0 130px;\n}\n\n#mainBodyRight ul.videoPreviews img {\n  margin-top:5px;\n}\n\n/* Pretty printing styles. Used with prettify.js. */\n\n.str { color: #080; }\n.kwd { color: #008; }\n.com { color: #800; }\n.typ { color: #606; }\n.lit { color: #066; }\n.pun { color: #660; }\n.pln { color: #000; }\ndl.tag-list dt code,\n.tag { color: #008; }\ndl.atn-list dt code,\n.atn { color: #828; }\n.atv { color: #080; }\n.dec { color: #606; }\n\n@media print {\n  .str { color: #060; }\n  .kwd { color: #006; font-weight: bold; }\n  .com { color: #600; font-style: italic; }\n  .typ { color: #404; font-weight: bold; }\n  .lit { color: #044; }\n  .pun { color: #440; }\n  .pln { color: #000; }\n  .tag { color: #006; font-weight: bold; }\n  .atn { color: #404; }\n  .atv { color: #060; }\n}\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/android-developer-docs-devguide.css",
    "content": "\n@import url(\"android-developer-docs.css\");\n\n/* Page title */\n\n#jd-header h1 {\n  padding: 8px 0 0 0;\n}\n\n/* Page content container */\n\n#jd-header table {\nmargin: 0 0 1em 1em;\n}\n\n#jd-content table table,\n#jd-content table img {\n  margin:1em 0;\n}"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/android-developer-docs.css",
    "content": "/* file: android-developer-docs.css\n   author: smain\n   date: september 2008\n   info: developer doc styles (developer.android.com)\n*/\n\n@import url(\"android-developer-core.css\");\n\n#title {\n  border-bottom: 4px solid #ccc;\n  display:none;\n}\n\n#title h1 {\n  color:#336666;\n  margin:0;\n  padding: 5px 10px;\n  font-size: 1em;\n  line-height: 15px;\n}\n\n#title h1 .small{\n  color:#000;\n  margin:0;\n  font-size: 13px;\n  padding:0 0 0 15px;\n}\n\n/* SIDE NAVIGATION */\n\n#side-nav {\n  padding:0 6px 0 0;\n  background-color: #fff;\n  font-size:12px;\n}\n\n#resize-packages-nav {\n/* keeps the resize handle below the h-scroll handle */\n  height:270px;\n  overflow:hidden;\n  max-height:100%;\n}\n\n#packages-nav {\n  height:270px;\n  max-height:inherit;\n  position:relative;\n  overflow:auto;\n}\n\n#classes-nav,\n#devdoc-nav {\n  overflow:auto;\n  position:relative;\n}\n\n#side-nav ul {\n  list-style: none;\n  margin: 0;\n  padding:5px 0;\n}\n\n#side-nav ul ul {\n  margin: .5em 0 0 0;\n  padding: 0;\n}\n\n#side-nav li {\n  padding:0;\n  padding:1px 0 1px 0;\n  zoom:1;\n}\n\n#side-nav li span.heading,\n#side-nav li h2 {\n  display:block;\n  font-size:12px;\n  font-weight: bold;\n  margin:.5em 0 0 0;\n  padding: 3px 0 1px 9px;\n}\n\n#side-nav li a {\n  display: inline-block; /* needed to apply padding to line-wraps */\n  text-decoration:none;\n  padding: 0 0 0 18px;\n  zoom:1;\n}\n\n#side-nav li a span+span {\n  display:none;\n}\n\n#side-nav li a:hover {\n  text-decoration:underline;\n}\n\n#side-nav li a+a {\n  padding: 0;\n}\n/*second level (nested) list*/\n#side-nav li li li a {\n  padding: 0 0 0 28px;\n}\n/*third level (nested) list*/\n#side-nav li li li li a {\n  padding: 0 0 0 38px;\n}\n\n#side-nav .selected {\n  background-color: #435a6e;\n  color: #fff;\n  font-weight:bold;\n}\n\n#side-nav .selected a {\n  color: #fff;\n  text-decoration:none;\n}\n\n#side-nav strong {\n  display:block;\n}\n\n#side-nav .toggle-list .toggle-img {\n  margin:0;\n  padding:0;\n  position:absolute;\n  top:0;\n  left:0;\n  height:16px;\n  width:15px;\n  outline-style:none;\n}\n/* second-level toggle */\n#side-nav .toggle-list .toggle-list .toggle-img {\n  left:10px;\n}\n\n#side-nav .closed .toggle-img,\n#side-nav .open .closed .toggle-img {\n  background:url('images/triangle-closed-small.png') 7px 4px no-repeat;\n}\n#side-nav .open .toggle-img {\n  background:url('images/triangle-opened-small.png') 7px 4px no-repeat;\n}\n\n#side-nav .toggle-list {\n  position:relative;\n}\n\n#side-nav .toggle-list ul {\n  margin:0;\n  display:none;\n}\n\n#side-nav .toggle-list div {\n  display:block;\n}\n\n#index-links .selected {\n  background-color: #fff;\n  color: #000;\n  font-weight:normal;\n  text-decoration:none;\n}\n\n#index-links {\n  padding:7px 0 4px 10px;\n}\n\n/* nav tree */\n\n#nav-tree ul {\n  padding:5px 0 1.5em;\n}\n\n#side-nav #nav-tree ul li a,\n#side-nav #nav-tree ul li span.no-children {\n  padding: 0 0 0 0;\n  margin: 0;\n}\n\n#nav-tree .plus {\n  margin: 0 3px 0 0;\n}\n\n#nav-tree ul ul {\n  list-style: none;\n  margin: 0;\n  padding: 0 0 0 0;\n}\n\n#nav-tree ul li {\n  margin: 0;\n  padding: 0 0 0 0;\n  white-space: nowrap;\n}\n\n#nav-tree .children_ul {\n  margin:0;\n}\n\n#nav-tree a.nolink {\n  color: black;\n  text-decoration: none;\n}\n\n#nav-tree span.label {\n  width: 100%;\n}\n\n#nav-tree {\n  overflow-x: auto;\n  overflow-y: scroll;\n}\n\n#nav-swap {\n  font-size:10px;\n  line-height:10px;\n  margin-left:1em;\n  text-decoration:none;\n  display:block;\n}\n\n#tree-link {\n\n}\n\n/* DOCUMENT BODY */\n\n#doc-content {\n  overflow:auto;\n}\n\n#jd-header {\n  background-color: #E2E2E2;\n  padding: 7px 15px;\n}\n\n#jd-header h1 {\n  margin: 0 0 10px;\n  font-size:1.7em;\n}\n\n#jd-header .crumb {\n  font-size:.9em;\n  line-height:1em;\n  color:#777;\n}\n\n#jd-header .crumb a,\n#jd-header .crumb a:visited {\n  text-decoration:none;\n  color:#777;\n}\n\n#jd-header .crumb a:hover {\n  text-decoration:underline;\n}\n\n#jd-header table {\n  margin:0;\n  padding:0;\n}\n\n#jd-header td {\n  border:none;\n  padding:0;\n  vertical-align:top;\n}\n\n#jd-header.guide-header {\n  background-color:#fff;\n  color:#435a6e;\n  height:50px;\n}\n\n#jd-descr {\n  position:relative;\n}\n\n/* summary tables for reference pages */\n.jd-sumtable {\n  margin: .5em 1em 1em 1em;\n  width:95%; /* consistent table widths; within IE's quirks */\n  font-size:.9em;\n}\n\n.jd-sumtable a {\n  text-decoration:none;\n}\n\n.jd-sumtable a:hover {\n  text-decoration:underline;\n}\n\n/* the link inside a sumtable for \"Show All/Hide All\" */\n.toggle-all {\n  display:block;\n  float:right;\n  font-weight:normal;\n  font-size:0.9em;\n}\n\n/* adjustments for in/direct subclasses tables */\n.jd-sumtable-subclasses {\n  margin: 1em 0 0 0;\n  max-width:968px;\n}\n\n/* extra space between end of method name and open-paren */\n.sympad {\n  margin-right: 2px;\n}\n\n/* right alignment for the return type in sumtable */\n.jd-sumtable .jd-typecol {\n  text-align:right;\n}\n\n/* adjustments for the expando table-in-table */\n.jd-sumtable-expando {\n  margin:.5em 0;\n  padding:0;\n}\n\n/* a div that holds a short description */\n.jd-descrdiv {\n  padding:3px 1em 0 1em;\n  margin:0;\n  border:0;\n}\n\n/* page-top-right container for reference pages (holds\nlinks to summary tables) */\n#api-info-block {\n  font-size:.8em;\n  padding:6px 10px;\n  font-weight:normal;\n  float:right;\n  text-align:right;\n  color:#999;\n  max-width:70%;\n}\n\n#api-level-toggle {\n  padding:0 10px;\n  font-size:11px;\n  float:right;\n}\n\n#api-level-toggle label.disabled {\n  color:#999;\n}\n\ndiv.api-level {\n  font-size:.8em;\n  font-weight:normal;\n  color:#999;\n  float:right;\n  padding:0 7px 0;\n  margin-top:-25px;\n}\n\n#api-info-block div.api-level {\n  font-size:1.3em;\n  font-weight:bold;\n  float:none;\n  color:#444;\n  padding:0;\n  margin:0;\n}\n\n/* Force link colors for IE6 */\ndiv.api-level a {\n  color:#999;\n}\n#api-info-block div.api-level a:link {\n  color:#444;\n}\n#api-level-toggle a {\n  color:#999;\n}\n\ndiv#deprecatedSticker {\n  display:none;\n  z-index:99;\n  position:fixed;\n  right:15px;\n  top:114px;\n  margin:0;\n  padding:1em;\n  background:#FFF;\n  border:1px solid #dddd00;\n  box-shadow:-5px 5px 10px #ccc;\n  -moz-box-shadow:-5px 5px 10px #ccc;\n  -webkit-box-shadow:-5px 5px 10px #ccc;\n}\n\ndiv#naMessage {\n  display:none;\n  width:555px;\n  height:0;\n  margin:0 auto;\n}\n\ndiv#naMessage div {\n  z-index:99;\n  width:450px;\n  position:fixed;\n  margin:50px 0;\n  padding:4em 4em 3em;\n  background:#FFF;\n  border:1px solid #dddd00;\n  box-shadow:-10px 10px 40px #888;\n  -moz-box-shadow:-10px 10px 40px #888;\n  -webkit-box-shadow:-10px 10px 40px #888;\n}\n/* IE6 can't position fixed */\n* html div#naMessage div { position:absolute; }\n\ndiv#naMessage strong {\n  font-size:1.1em;\n}\n\n.absent,\n.absent a:link,\n.absent a:visited,\n.absent a:hover,\n.absent * {\n  color:#bbb !important;\n  cursor:default !important;\n  text-decoration:none !important;\n}\n\n#api-level-toggle a,\n.api-level a {\n  color:inherit;\n  text-decoration:none;\n}\n\n#api-level-toggle a:hover,\n.api-level a:hover {\n  color:inherit;\n  text-decoration:underline !important;\n  cursor:pointer !important;\n}\n\n#side-nav li.absent.selected,\n#side-nav li.absent.selected *,\n#side-nav div.label.absent.selected,\n#side-nav div.label.absent.selected * {\n  background-color:#eaeaea !important;\n}\n/* IE6 quirk (won't chain classes, so just keep background blue) */\n* html #side-nav li.selected,\n* html #side-nav li.selected *,\n* html #side-nav div.label.selected,\n* html #side-nav div.label.selected * {\n  background-color: #435a6e !important;\n}\n\n\n.absent h4.jd-details-title,\n.absent h4.jd-details-title * {\n  background-color:#f6f6f6 !important;\n}\n\n.absent img {\n  opacity: .3;\n  filter: alpha(opacity=30);\n  -ms-filter:\"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)\";\n}\n\n\n/* applies to a div containing links to summary tables */\n.sum-details-links {\n  padding:0;\n  font-weight:normal;\n}\n\n.sum-details-links a {\n  text-decoration:none;\n}\n\n.sum-details-links a:hover {\n  text-decoration:underline;\n}\n\n\n/* inheritance table */\n.jd-inheritance-table {\n  border-spacing:0;\n  margin:0;\n  padding:0;\n  font-size:.9em;\n}\n.jd-inheritance-table td {\n  border: none;\n  margin: 0;\n  padding: 0;\n}\n.jd-inheritance-table .jd-inheritance-space {\n  font-weight:bold;\n  width:1em;\n}\n.jd-inheritance-table .jd-inheritance-interface-cell {\n  padding-left: 17px;\n}\n\n#jd-content {\n  padding: 18px 15px;\n}\n\nhr {\n  background-color:#ccc;\n  border-color:#fff;\n  margin:2em 0 1em;\n}\n\n/* DOC CLASSES */\n\n#jd-content h1 {\n/*sdk page*/\n  font-size:1.6em;\n  color:#336666;\n  margin:0 0 .5em;\n}\n\n#jd-content h2 {\n  font-size:1.45em;\n  color:#111;\n  border-top:2px solid #ccc;\n  padding: .5em 0 0;\n  margin: 2em 0 1em 0;\n}\n\n#jd-content h3 {\n  font-size:1.3em;\n  color:#3a3a3a;\n  padding: 0;\n  margin: 1.5em 0 .65em 0;\n}\n\n#jd-content h4 {\n  font-size:1.1em;\n  color:#3a3a3a;\n  padding: 0;\n  margin: 1.25em 0 .65em 0;\n}\n\n#jd-content h5 {\n  font-size:1.0em;\n  color:#3a3a3a;\n  padding: 0;\n  margin: 1em 0 .65em 0;\n}\n\n#jd-content .small-header {\n  font-size:1em;\n  color:#000;\n  font-weight:bold;\n  border:none;\n  padding:0;\n  margin:1em 0 .5em;\n  position:inherit;\n}\n\n#jd-content table {\n  margin: 0 0 1em 1em;\n}\n\n#jd-content img {\n  margin: 0 0 1em 1em;\n}\n\n#jd-content li img,\n#jd-content dd img {\n  margin:.5em 0 .5em 1em;\n}\n\n.nolist {\n  list-style:none;\n  padding:0;\n  margin:0 0 1em 1em;\n}\n\n.nolist li {\n  padding:0 0 2px;\n  margin:0;\n}\n\nh4 .normal {\n  font-size:.9em;\n  font-weight:normal;\n}\n\n.caps {\n  font-variant:small-caps;\n  font-size:1.2em;\n}\n\ndl.tag-list dl.atn-list {\n  padding:0 0 0 2em;\n}\n\n.jd-details {\n/*  border:1px solid #669999;\n  padding:4px; */\n  margin:0 0 1em;\n}\n\n/* API reference: a container for the\n.tagdata blocks that make up the detailed\ndescription */\n.jd-details-descr {\n  padding:0;\n  margin:.5em .25em;\n}\n\n/* API reference: a block containing\na detailed description, a params table,\nseealso list, etc */\n.jd-tagdata {\n  margin:.5em 1em;\n}\n\n.jd-tagdata p {\n  margin:0 0 1em 1em;\n}\n\n/* API reference: adjustments to\nthe detailed description block */\n.jd-tagdescr {\n  margin:.25em 0 .75em 0;\n  line-height:1em;\n}\n\n.jd-tagdescr p {\n  margin:.5em 0;\n  padding:0;\n\n}\n\n.jd-tagdescr ol,\n.jd-tagdescr ul {\n  margin:0 2.5em;\n  padding:0;\n}\n\n.jd-tagdescr table,\n.jd-tagdescr img {\n  margin:.25em 1em;\n}\n\n.jd-tagdescr li {\nmargin:0 0 .25em 0;\npadding:0;\n}\n\n/* API reference: heading marking\nthe details section for constants,\nattrs, methods, etc. */\nh4.jd-details-title {\n  font-size:1.15em;\n  background-color: #E2E2E2;\n  margin:1.5em 0 .6em;\n  padding:3px 95px 3px 3px; /* room for api-level */\n}\n\nh4.jd-tagtitle {\n  margin:0;\n}\n\n/* API reference: heading for \"Parameters\", \"See Also\", etc.,\nin details sections */\nh5.jd-tagtitle {\n  margin:0 0 .25em 0;\n  font-size:1em;\n}\n\n.jd-tagtable {\n  margin:0;\n}\n\n.jd-tagtable td,\n.jd-tagtable th {\n  border:none;\n  background-color:#fff;\n  vertical-align:top;\n  font-weight:normal;\n  padding:2px 10px;\n}\n\n.jd-tagtable th {\n  font-style:italic;\n}\n\n#jd-content table h2 {\n  background-color: #d6d6d6;\n  font-size: 1.1em;\n  margin:0 0 10px;\n  padding:5px;\n  left:0;\n  width:auto;\n}\n\ndiv.design-announce {\n  border-top:1px solid #33B5E5;\n  border-bottom:1px solid #33B5E5;\n  padding:5px 10px 10px 55px;\n  margin:2em 0;\n  background:url('images/icon_design.png') 5px 13px no-repeat;\n}\n\ndiv.design-announce p {\n  margin: .5em 0 0 0;\n}\n\ndiv.special {\n  padding: .5em 1em 1em 1em;\n  margin: 0 0 1em;\n  background-color: #DAF3FC;\n  border:1px solid #d3ecf5;\n  border-radius:5px;\n  -moz-border-radius:5px;\n  -webkit-border-radius:5px;\n}\n\ndiv.special p {\n  margin: .5em 0 0 0;\n}\n\ndiv.special ol {\n  margin: 0;\n}\n\ndiv.special ol li {\n  margin: 0;\n  padding: 0;\n}\n\n#jd-content div.special h2,\n#jd-content div.special h3 {\n  color:#669999;\n  font-size:1.2em;\n  border:none;\n  margin:0 0 .5em;\n  padding:0;\n}\n\n#jd-content div.special.reference h2,\n#jd-content div.special.reference h3,\n#jd-content div.special.reference h4 {\n  color:#000;\n  font-size:1em;\n  border:none;\n  font-weight:bold;\n  margin:.5em 0;\n  padding:0;\n}\n\np.note, div.note,\np.caution, div.caution,\np.warning, div.warning {\n  margin: 1em;\n  padding: 0 0 0 .5em;\n  border-left: 4px solid;\n}\n\np.special-note,\ndiv.special-note {\n  background-color:#EBF3DB;\n  padding:10px 20px;\n  margin:0 0 1em;\n}\n\np.note,\ndiv.note {\n border-color: #99aacc;\n}\n\np.warning,\ndiv.warning {\n  border-color: #aa0033;\n}\n\np.caution,\ndiv.caution {\n  border-color: #ffcf00;\n}\n\nli .note,\nli .caution,\nli .warning {\n  margin: .5em 0 0 0;\n  padding: .2em .5em .2em .9em;\n}\n\n/* Makes sure the first paragraph does not add top-whitespace within the box*/\nli .note>p:first-child,\nli .caution>p:first-child,\nli .warning>p:first-child {\n  margin-top:0;\n  padding-top:0;\n}\n\ndl.xml dt {\n  font-variant:small-caps;\n  font-size:1.2em;\n}\n\ndl.xml dl {\n  padding:0;\n}\n\ndl.xml dl dt {\n  font-variant:normal;\n  font-size:1em;\n}\n\n.listhead li {\n  font-weight: bold;\n}\n\n.listhead li *, /*ie*/.listhead li li {\n  font-weight: normal;\n}\n\nol.no-style,\nul.no-style {\n  list-style:none;\n  padding-left:1em;\n}\n\n.new,\n.new-child {\n  font-size: .78em;\n  font-weight: bold;\n  color: #ff3d3d;\n  text-decoration: none;\n  vertical-align:top;\n  line-height:.9em;\n  white-space:nowrap;\n}\n\n.toggle-list.open .new-child {\n  display:none;\n}\n\npre.classic {\n  background-color:transparent;\n  border:none;\n  padding:0;\n}\n\np.img-caption {\n  margin: -0.5em 0 1em 1em; /* matches default img left-margin */\n}\n\ndiv.figure {\n  float:right;\n  clear:right;\n  margin:1em 0 0 0;\n  padding:0 0 0 3em;\n  background-color:#fff;\n  /* width must be defined w/ an inline style matching the image width */\n}\n\n#jd-content\ndiv.figure img {\n  margin: 0 0 1em;\n}\n\ndiv.figure p.img-caption {\n  margin: -0.5em 0 1em 0;\n}\n\np.table-caption {\n  margin: 0 0 0.5em 1em; /* matches default table left-margin */\n}\n\n\n/* toggle for misc content (such as long sample code) \n   see toggleContent() script in android-developer-docs.js */\n.toggle-content.closed .toggle-content-toggleme {\n  display:none;\n}\n\n.toggle-content a[href=\"#\"] {\n  text-decoration:none;\n  color:inherit;\n}\n\n.toggle-content-toggleme {\n  padding-bottom:1px; /* fixes animation bounce due to margins */\n}\n\n#jd-content .toggle-content img.toggle-content-img {\n  margin:0;\n}\n\n\n/* BEGIN quickview sidebar element styles */\n\n#qv-wrapper {\n  float: right;\n  width:310px; /* +35px padding */\n  background-color:#fff;\n  margin:-48px 0 2px 0;\n  padding:0 0 20px 35px;\n}\n\n#qv {\n  background-color:#fff;\n  border:4px solid #dee8f1;\n  margin:0;\n  padding:0 5px 5px;\n  width:292px; /* +10px padding; +8px border */\n  font-size:.9em;\n}\n\n#qv ol {\n  list-style:none;\n  padding: 0;\n}\n\n#qv ol ol{\n  list-style:none;\n  padding: 0 0 0 12px;\n  margin:0;\n}\n\n#qv ul {\n  padding: 0 10px 0 2em;\n}\n\n#qv li {\n  padding: 0 10px 3px;\n  line-height: 1.2em;\n}\n\n#qv li li {\n  padding: 3px 10px 0;\n}\n\n#qv ul li {\n  padding: 0 10px 0 0;\n}\n\n#qv li.selected a {\n  color:#555;\n  text-decoration:none;\n}\n\n#qv a,\n#qv a code {\n  color:#cc6600;\n}\n\n#qv p {\n  margin:8px 0 0;\n  padding:0 10px;\n}\n\n#jd-content #qv h2 {\n  font-size:1.05em;\n  font-weight:bold;\n  margin:12px 0 .25em 0;\n  padding:0 10px;\n  background-color:transparent;\n  color:#7BB026;\n  border:none;\n  left:0;\n  z-index:1;\n}\n\n#qv-extra #rule {\n  padding: 0 10px;\n  margin: 0;\n}\n\n#qv-sub-rule {\n  padding: 5px 15px 10px;\n  margin: 0;\n}\n\n#jd-content\n#qv-sub-rule h2 {\n  margin: 0 0 .5em 0;\n}\n\n/* END quickview sidebar element styles */\n\n/* Begin sidebox sidebar element styles */\n\n.sidebox-wrapper {\n  float:right;\n  clear:right;\n  width:310px; /* +35px padding */\n  background-color:#fff;\n  margin:0;\n  padding:0 0 20px 35px;\n}\n\n.sidebox {\n  border-left:1px solid #dee8f1;\n  background-color:#ffffee;\n  margin:0;\n  padding:8px 12px;\n  font-size:0.9em;\n  width:285px; /* +24px padding; +1px border */\n}\n\n.sidebox p {\n  margin-bottom: .75em;\n}\n\n.sidebox ul {\n  padding: 0 0 0 1.5em;\n}\n\n.sidebox li ul {\n  margin-top:0;\n  margin-bottom:.1em;\n}\n\n.sidebox li {\npadding:0 0 0 0em;\n}\n\n#jd-content .sidebox h2,\n#jd-content .sidebox h3,\n#jd-content .sidebox h4,\n#jd-content .sidebox h5 {\n  border:none;\n  font-size:1em;\n  margin:0;\n  padding:0 0 8px;\n  left:0;\n  z-index:0;\n}\n\n.sidebox hr {\n  background-color:#ccc;\n  border:none;\n}\n\n/* End sidebox sidebar element styles */\n\n/* BEGIN developer training bar styles */\n\ndiv#tb-wrapper {\n  float: right;\n  clear:right;\n  width:380px; /* +25px padding = 405 */\n  background-color:#fff;\n  margin:0 0 2px 0;\n  padding:0 0 20px 25px;\n}\n\ndiv#tb {\n  margin:0;\n  padding:0 15px;\n  width:350px; /* +15px padding = 380 */\n  font-size:.9em;\n  background:#e9e9e9;\n  border:1px solid #aaa;\n  border-radius:5px;\n  -moz-border-radius:5px;\n  -webkit-border-radius:5px;\n  overflow:auto;\n}\n\ndiv#tb h2 {\n  font-size:1.3em;\n  font-weight:bold;\n  margin:1em 0;\n  padding:0;\n  background-color:transparent;\n  border:none;\n  clear:both;\n}\n\ndiv.download-box a.button {\n  color: #069;\n  font-size:1.1em;\n  font-weight:bold;\n  text-decoration:none;\n  height:27px;\n  line-height:27px;\n  text-align:center;\n  padding:5px 8px;\n  background-color: #fff;\n  border: 1px solid #aaa;\n  -webkit-border-radius: 2px;\n  -moz-border-radius: 2px;\n  border-radius: 2px;\n}\n\ndiv.download-box a.button:hover {\n  border-color: #09C;\n  background-color: #4CADCB;\n  background-image: -webkit-gradient(linear,left top,left bottom,from(#5dbcd9),to(#4cadcb));\n  background-image: -webkit-linear-gradient(top,#5dbcd9,#4cadcb);\n  background-image: -moz-linear-gradient(top,#5dbcd9,#4cadcb);\n  background-image: -ms-linear-gradient(top,#5dbcd9,#4cadcb);\n  background-image: -o-linear-gradient(top,#5dbcd9,#4cadcb);\n  background-image: linear-gradient(top,#5dbcd9,#4cadcb);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#5dbcd9',EndColorStr='#4cadcb');\n  color: #fff;\n}\n\ndiv.download-box a.button:active {\n  background-color: #1E799A;\n  background-image: none;\n  border-color: #30B7E6;\n}\n\ndiv.download-box p.filename {\n  font-size:0.85em;\n  color:#888;\n  margin:4px 0 1em 10px;\n}\n\n/* End developer training bar */\n\n/* Training nav bar (previous/next) */\n\ndiv.training-nav-top {\n  float: right;\n  width:380px; /* +25px padding = 405 */\n  margin:-58px 0 0 0;\n  padding:0 0 20px 25px;\n}\n\ndiv.training-nav-bottom {\n  padding:1px; /* for weird FF bug (scrollbar appears) */\n  margin:3em 0;\n  overflow:auto;\n}\n\ndiv.training-nav-button-next a,\ndiv.training-nav-button-previous a {\n  display:block;\n  width:160px;\n  height:55px;\n  padding:4px 7px;\n  border:1px solid #aaa;\n  border-radius:5px;\n  -moz-border-radius:5px;\n  -webkit-border-radius:5px;\n  text-decoration:none;\n  font-weight:bold;\n}\n\ndiv.training-nav-button-next a:hover,\ndiv.training-nav-button-previous a:hover {\n  border:1px solid #069; /* match link color */\n}\n\ndiv.training-nav-button-next a:active,\ndiv.training-nav-button-previous a:active {\n  border:1px solid #f00; /* match link color */\n}\n  \ndiv.training-nav-button-previous {\n  float:left;\n  text-align:left;\n}\n\ndiv.training-nav-button-next {\n  float:right;\n  text-align:right;\n}\n\nspan.training-nav-button-title {\n  display:block;\n  font-size:.85em;\n  font-weight:normal;\n  line-height:1.3em;\n  margin:.5em 0 0;\n}\n\n/* End training nav bar */\n\n/* BEGIN image and caption styles (originally for UI Guidelines docs) */\n\ntable.image-caption {\n  padding:0;\n  margin:.5em 0;\n  border:0;\n}\n\ntd.image-caption-i {\n  font-size:92%;\n  padding:0 5px;\n  margin:0;\n  border:0;\n}\n\ntd.image-caption-i img {\n  padding:0 1em;\n  margin:0;\n}\n\n.image-list {\n  width:24px;\n  text-align:center;\n}\n\ntd.image-caption-c {\n  font-size:92%;\n  padding:1em 2px 2px 2px;\n  margin:0;\n  border:0;\n  width:350px;\n}\n\n.grad-rule-top {\nbackground-image:url(images/grad-rule-qv.png);\nbackground-repeat:no-repeat;\npadding-top:1em;\nmargin-top:0;\n}\n\n.image-caption-nested {\n  margin-top:0;\n  padding:0 0 0 1em;\n}\n\n.image-caption-nested td {\n  padding:0 4px 2px 0;\n  margin:0;\n  border:0;\n}\n\n/* END image and caption styles */\n\n/* table of contents */\n\nol.toc {\n  margin: 0 0 1em 0;\n  padding: 0;\n  list-style: none;\n  font-size:95%;\n}\n\nol.toc li {\n  font-weight: bold;\n  margin: 0 0 .5em 1em;\n  padding: 0;\n}\n\nol.toc li p {\n  font-weight: normal;\n}\n\nol.toc li ol {\n  margin: 0;\n  padding: 0;\n}\n\nol.toc li li {\n  padding: 0;\n  margin: 0 0 0 1em;\n  font-weight: normal;\n  list-style: none;\n}\n\ntable ol.toc {\n  margin-left: 0;\n}\n\n.columns td {\n  padding:0 5px;\n  border:none;\n}\n\n/* link table */\n.jd-linktable {\n  margin: 0 0 1em;\n  border-bottom: 1px solid #888;\n}\n.jd-linktable th,\n.jd-linktable td {\n  padding: 3px 5px;\n  vertical-align: top;\n  text-align: left;\n  border:none;\n}\n.jd-linktable tr {\n  background-color: #fff;\n}\n.jd-linktable td {\n  border-top: 1px solid #888;\n  background-color: inherit;\n}\n.jd-linktable td  p {\n  padding: 0 0 5px;\n}\n.jd-linktable .jd-linkcol {\n}\n.jd-linktable .jd-descrcol {\n}\n.jd-linktable .jd-typecol {\n  text-align:right;\n}\n.jd-linktable .jd-valcol {\n}\n.jd-linktable .jd-commentrow {\n  border-top:none;\n  padding-left:25px;\n}\n.jd-deprecated-warning {\n  margin-top: 0;\n  margin-bottom: 10px;\n}\n\ntr.alt-color {\n  background-color: #f6f6f6;\n}\n\n/* expando trigger */\n#jd-content .jd-expando-trigger-img {\n  margin:0;\n}\n\n/* jd-expando */\n.jd-inheritedlinks {\n  padding:0 0 0 13px\n}\n\n/* SDK PAGE */\ntable.download tr {\n  background-color:#d9d9d9;\n}\n\ntable.download tr.alt-color {\n  background-color:#ededed;\n}\n\ntable.download td,\ntable.download th {\n  border:2px solid #fff;\n  padding:10px 5px;\n}\n\ntable.download th {\n  background-color:#6d8293;\n  color:#fff;\n}\n\n/* INLAY 180 COPY and 240PX EXTENSION */\n/* modified to 43px so that all browsers eliminate the package panel h-scroll */\n.g-tpl-240 .g-unit,\n.g-unit .g-tpl-240 .g-unit,\n.g-unit .g-unit .g-tpl-240 .g-unit {\n  display: block;\n  margin: 0 0 0 243px;\n  width: auto;\n  float: none;\n}\n.g-unit .g-unit .g-tpl-240 .g-first,\n.g-unit .g-tpl-240 .g-first,\n.g-tpl-240 .g-first {\n  display: block;\n  margin: 0;\n  width: 243px;\n  float: left;\n}\n/* 240px alt */\n.g-tpl-240-alt .g-unit,\n.g-unit .g-tpl-240-alt .g-unit,\n.g-unit .g-unit .g-tpl-240-alt .g-unit {\n  display: block;\n  margin: 0 243px 0 0;\n  width: auto;\n  float: none;\n}\n.g-unit .g-unit .g-tpl-240-alt .g-first,\n.g-unit .g-tpl-240-alt .g-first,\n.g-tpl-240-alt .g-first {\n  display: block;\n  margin: 0;\n  width: 243px;\n  float: right;\n}\n\n/* 200px */\n.g-tpl-200 .g-unit,\n.g-unit .g-tpl-200 .g-unit,\n.g-unit .g-unit .g-tpl-200 .g-unit {\n  display: block;\n  margin: 0 0 0 200px;\n  width: auto;\n  float: none;\n}\n.g-unit .g-unit .g-tpl-200 .g-first,\n.g-unit .g-tpl-200 .g-first,\n.g-tpl-200 .g-first {\n  display: block;\n  margin: 0;\n  width: 200px;\n  float: left;\n}\n/* 200px alt */\n.g-tpl-200-alt .g-unit,\n.g-unit .g-tpl-200-alt .g-unit,\n.g-unit .g-unit .g-tpl-200-alt .g-unit {\n  display: block;\n  margin: 0 200px 0 0;\n  width: auto;\n  float: none;\n}\n.g-unit .g-unit .g-tpl-200-alt .g-first,\n.g-unit .g-tpl-200-alt .g-first,\n.g-tpl-200-alt .g-first {\n  display: block;\n  margin: 0;\n  width: 200px;\n  float: right;\n}\n\n/* 190px */\n.g-tpl-190 .g-unit,\n.g-unit .g-tpl-190 .g-unit,\n.g-unit .g-unit .g-tpl-190 .g-unit {\n  display: block;\n  margin: 0 0 0 190px;\n  width: auto;\n  float: none;\n}\n.g-unit .g-unit .g-tpl-190 .g-first,\n.g-unit .g-tpl-190 .g-first,\n.g-tpl-190 .g-first {\n  display: block;\n  margin: 0;\n  width: 190px;\n  float: left;\n}\n/* 190px alt */\n.g-tpl-190-alt .g-unit,\n.g-unit .g-tpl-190-alt .g-unit,\n.g-unit .g-unit .g-tpl-190-alt .g-unit {\n  display: block;\n  margin: 0 190px 0 0;\n  width: auto;\n  float: none;\n}\n.g-unit .g-unit .g-tpl-190-alt .g-first,\n.g-unit .g-tpl-190-alt .g-first,\n.g-tpl-190-alt .g-first {\n  display: block;\n  margin: 0;\n  width: 190px;\n  float: right;\n}\n\n/* 180px */\n.g-tpl-180 .g-unit,\n.g-unit .g-tpl-180 .g-unit,\n.g-unit .g-unit .g-tpl-180 .g-unit {\n  display: block;\n  margin: 0 0 0 180px;\n  width: auto;\n  float: none;\n}\n.g-unit .g-unit .g-tpl-180 .g-first,\n.g-unit .g-tpl-180 .g-first,\n.g-tpl-180 .g-first {\n  display: block;\n  margin: 0;\n  width: 180px;\n  float: left;\n}\n/* 180px alt */\n.g-tpl-180-alt .g-unit,\n.g-unit .g-tpl-180-alt .g-unit,\n.g-unit .g-unit .g-tpl-180-alt .g-unit {\n  display: block;\n  margin: 0 180px 0 0;\n  width: auto;\n  float: none;\n}\n.g-unit .g-unit .g-tpl-180-alt .g-first,\n.g-unit .g-tpl-180-alt .g-first,\n.g-tpl-180-alt .g-first {\n  display: block;\n  margin: 0;\n  width: 180px;\n  float: right;\n}\n\n\n/* JQUERY RESIZABLE STYLES */\n.ui-resizable { position: relative; }\n.ui-resizable-handle { position: absolute; display: none; font-size: 0.1px; z-index:1; }\n.ui-resizable .ui-resizable-handle { display: block; }\nbody .ui-resizable-disabled .ui-resizable-handle { display: none; }\nbody .ui-resizable-autohide .ui-resizable-handle { display: none; }\n.ui-resizable-s { cursor: s-resize; height: 6px; width: 100%; bottom: 0px; left: 0px;\n  background: transparent url(\"images/resizable-s2.gif\") repeat scroll center top; }\n.ui-resizable-e { cursor: e-resize; width: 6px; right: 0px; top: 0px; height: 100%;\n  background: transparent url(\"images/resizable-e2.gif\") repeat scroll right center; }\n\n@media print {\n\n  body {\n    overflow:visible;\n  }\n\n  #header {\n    height:60px;\n  }\n\n  #headerLeft {\n    padding:0;\n  }\n\n  #header-tabs,\n  #headerRight,\n  #side-nav,\n  #api-info-block {\n    display:none;\n  }\n\n  #body-content {\n    position:inherit;\n  }\n\n  #doc-content {\n    margin-left:0 !important;\n    height:auto !important;\n    width:auto !important;\n    overflow:inherit;\n    display:inline;\n  }\n\n  #jd-header {\n    padding:10px 0;\n  }\n\n  #jd-content {\n    padding:15px 0 0;\n  }\n\n  #footer {\n    float:none;\n    margin:2em 0 0;\n  }\n\n  h4.jd-details-title {\n    border-bottom:1px solid #666;\n  }\n\n  pre {\n    /* these allow lines to break (if there's a white space) */\n    overflow: visible;\n    text-wrap: unrestricted;\n    white-space: -moz-pre-wrap; /* Moz */\n    white-space: -pre-wrap; /* Opera 4-6 */\n    white-space: -o-pre-wrap; /* Opera 7 */\n    white-space: pre-wrap; /* CSS3  */\n    word-wrap: break-word; /* IE 5.5+ */\n  }\n\n  h1, h2, h3, h4, h5, h6 {\n    page-break-after: avoid;\n  }\n\n  table, img {\n    page-break-inside: avoid;\n  }\n}\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/android-developer-docs.js",
    "content": "var resizePackagesNav;\nvar classesNav;\nvar devdocNav;\nvar sidenav;\nvar content;\nvar HEADER_HEIGHT = 117;\nvar cookie_namespace = 'android_developer';\nvar NAV_PREF_TREE = \"tree\";\nvar NAV_PREF_PANELS = \"panels\";\nvar nav_pref;\nvar toRoot;\nvar isMobile = false; // true if mobile, so we can adjust some layout\nvar isIE6 = false; // true if IE6\n\n// TODO: use $(document).ready instead\nfunction addLoadEvent(newfun) {\n  var current = window.onload;\n  if (typeof window.onload != 'function') {\n    window.onload = newfun;\n  } else {\n    window.onload = function() {\n      current();\n      newfun();\n    }\n  }\n}\n\nvar agent = navigator['userAgent'].toLowerCase();\n// If a mobile phone, set flag and do mobile setup\nif ((agent.indexOf(\"mobile\") != -1) ||      // android, iphone, ipod\n    (agent.indexOf(\"blackberry\") != -1) ||\n    (agent.indexOf(\"webos\") != -1) ||\n    (agent.indexOf(\"mini\") != -1)) {        // opera mini browsers\n  isMobile = true;\n  addLoadEvent(mobileSetup);\n// If not a mobile browser, set the onresize event for IE6, and others\n} else if (agent.indexOf(\"msie 6\") != -1) {\n  isIE6 = true;\n  addLoadEvent(function() {\n    window.onresize = resizeAll;\n  });\n} else {\n  addLoadEvent(function() {\n    window.onresize = resizeHeight;\n  });\n}\n\nfunction mobileSetup() {\n  $(\"body\").css({'overflow':'auto'});\n  $(\"html\").css({'overflow':'auto'});\n  $(\"#body-content\").css({'position':'relative', 'top':'0'});\n  $(\"#doc-content\").css({'overflow':'visible', 'border-left':'3px solid #DDD'});\n  $(\"#side-nav\").css({'padding':'0'});\n  $(\"#nav-tree\").css({'overflow-y': 'auto'});\n}\n\n/* loads the lists.js file to the page.\nLoading this in the head was slowing page load time */\naddLoadEvent( function() {\n  var lists = document.createElement(\"script\");\n  lists.setAttribute(\"type\",\"text/javascript\");\n  lists.setAttribute(\"src\", toRoot+\"reference/lists.js\");\n  document.getElementsByTagName(\"head\")[0].appendChild(lists);\n} );\n\naddLoadEvent( function() {\n  $(\"pre:not(.no-pretty-print)\").addClass(\"prettyprint\");\n  prettyPrint();\n} );\n\nfunction setToRoot(root) {\n  toRoot = root;\n  // note: toRoot also used by carousel.js\n}\n\nfunction restoreWidth(navWidth) {\n  var windowWidth = $(window).width() + \"px\";\n  content.css({marginLeft:parseInt(navWidth) + 6 + \"px\"}); //account for 6px-wide handle-bar\n\n  if (isIE6) {\n    content.css({width:parseInt(windowWidth) - parseInt(navWidth) - 6 + \"px\"}); // necessary in order for scrollbars to be visible\n  }\n\n  sidenav.css({width:navWidth});\n  resizePackagesNav.css({width:navWidth});\n  classesNav.css({width:navWidth});\n  $(\"#packages-nav\").css({width:navWidth});\n}\n\nfunction restoreHeight(packageHeight) {\n  var windowHeight = ($(window).height() - HEADER_HEIGHT);\n  var swapperHeight = windowHeight - 13;\n  $(\"#swapper\").css({height:swapperHeight + \"px\"});\n  sidenav.css({height:windowHeight + \"px\"});\n  content.css({height:windowHeight + \"px\"});\n  resizePackagesNav.css({maxHeight:swapperHeight + \"px\", height:packageHeight});\n  classesNav.css({height:swapperHeight - parseInt(packageHeight) + \"px\"});\n  $(\"#packages-nav\").css({height:parseInt(packageHeight) - 6 + \"px\"}); //move 6px to give space for the resize handle\n  devdocNav.css({height:sidenav.css(\"height\")});\n  $(\"#nav-tree\").css({height:swapperHeight + \"px\"});\n}\n\nfunction readCookie(cookie) {\n  var myCookie = cookie_namespace+\"_\"+cookie+\"=\";\n  if (document.cookie) {\n    var index = document.cookie.indexOf(myCookie);\n    if (index != -1) {\n      var valStart = index + myCookie.length;\n      var valEnd = document.cookie.indexOf(\";\", valStart);\n      if (valEnd == -1) {\n        valEnd = document.cookie.length;\n      }\n      var val = document.cookie.substring(valStart, valEnd);\n      return val;\n    }\n  }\n  return 0;\n}\n\nfunction writeCookie(cookie, val, section, expiration) {\n  if (val==undefined) return;\n  section = section == null ? \"_\" : \"_\"+section+\"_\";\n  if (expiration == null) {\n    var date = new Date();\n    date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week\n    expiration = date.toGMTString();\n  }\n  document.cookie = cookie_namespace + section + cookie + \"=\" + val + \"; expires=\" + expiration+\"; path=/\";\n}\n\nfunction init() {\n  $(\"#side-nav\").css({position:\"absolute\",left:0});\n  content = $(\"#doc-content\");\n  resizePackagesNav = $(\"#resize-packages-nav\");\n  classesNav = $(\"#classes-nav\");\n  sidenav = $(\"#side-nav\");\n  devdocNav = $(\"#devdoc-nav\");\n\n  var cookiePath = \"\";\n  if (location.href.indexOf(\"/reference/\") != -1) {\n    cookiePath = \"reference_\";\n  } else if (location.href.indexOf(\"/guide/\") != -1) {\n    cookiePath = \"guide_\";\n  } else if (location.href.indexOf(\"/sdk/\") != -1) {\n    cookiePath = \"sdk_\";\n  } else if ((location.href.indexOf(\"/resources/\") != -1) || \n             (location.href.indexOf(\"/training/\") != -1)) {\n    cookiePath = \"resources_\";\n  }\n\n  if (!isMobile) {\n    $(\"#resize-packages-nav\").resizable({handles: \"s\", resize: function(e, ui) { resizePackagesHeight(); } });\n    $(\"#side-nav\").resizable({handles: \"e\", resize: function(e, ui) { resizeWidth(); } });\n    var cookieWidth = readCookie(cookiePath+'width');\n    var cookieHeight = readCookie(cookiePath+'height');\n    if (cookieWidth) {\n      restoreWidth(cookieWidth);\n    } else if ($(\"#side-nav\").length) {\n      resizeWidth();\n    }\n    if (cookieHeight) {\n      restoreHeight(cookieHeight);\n    } else {\n      resizeHeight();\n    }\n  }\n\n  if (devdocNav.length) { // only dev guide, resources, and sdk\n    tryPopulateResourcesNav();\n    highlightNav(location.href);\n  }\n}\n\nfunction tryPopulateResourcesNav() {\n  var sampleList = $('#devdoc-nav-sample-list');\n  var articleList = $('#devdoc-nav-article-list');\n  var tutorialList = $('#devdoc-nav-tutorial-list');\n  var topicList = $('#devdoc-nav-topic-list');\n\n  if (!topicList.length || !ANDROID_TAGS || !ANDROID_RESOURCES)\n    return;\n\n  var topics = [];\n  for (var topic in ANDROID_TAGS['topic']) {\n    topics.push({name:topic,title:ANDROID_TAGS['topic'][topic]});\n  }\n  topics.sort(function(x,y){ return (x.title < y.title) ? -1 : 1; });\n  for (var i = 0; i < topics.length; i++) {\n    topicList.append(\n        $('<li>').append(\n          $('<a>')\n            .attr('href', toRoot + \"resources/browser.html?tag=\" + topics[i].name)\n            .append($('<span>')\n              .addClass('en')\n              .html(topics[i].title)\n            )\n          )\n        );\n  }\n\n  var _renderResourceList = function(tag, listNode) {\n    var resources = [];\n    var tags;\n    var resource;\n    var i, j;\n    for (i = 0; i < ANDROID_RESOURCES.length; i++) {\n      resource = ANDROID_RESOURCES[i];\n      tags = resource.tags || [];\n      var hasTag = false;\n      for (j = 0; j < tags.length; j++)\n        if (tags[j] == tag) {\n          hasTag = true;\n          break;\n        }\n      if (!hasTag)\n        continue;\n      resources.push(resource);\n    }\n    //resources.sort(function(x,y){ return (x.title.en < y.title.en) ? -1 : 1; });\n    for (i = 0; i < resources.length; i++) {\n      resource = resources[i];\n      var listItemNode = $('<li>').append(\n          $('<a>')\n            .attr('href', toRoot + \"resources/\" + resource.path)\n            .append($('<span>')\n              .addClass('en')\n              .html(resource.title.en)\n            )\n          );\n      tags = resource.tags || [];\n      for (j = 0; j < tags.length; j++) {\n        if (tags[j] == 'new') {\n          listItemNode.get(0).innerHTML += '&nbsp;<span class=\"new\">new!</span>';\n          break;\n        } else if (tags[j] == 'updated') {\n          listItemNode.get(0).innerHTML += '&nbsp;<span class=\"new\">updated!</span>';\n          break;\n        }\n      }\n      listNode.append(listItemNode);\n    }\n  };\n\n  _renderResourceList('sample', sampleList);\n  _renderResourceList('article', articleList);\n  _renderResourceList('tutorial', tutorialList);\n}\n\nfunction highlightNav(fullPageName) {\n  var lastSlashPos = fullPageName.lastIndexOf(\"/\");\n  var firstSlashPos;\n  if (fullPageName.indexOf(\"/guide/\") != -1) {\n    firstSlashPos = fullPageName.indexOf(\"/guide/\");\n  } else if (fullPageName.indexOf(\"/sdk/\") != -1) {\n    firstSlashPos = fullPageName.indexOf(\"/sdk/\");\n  } else if (fullPageName.indexOf(\"/resources/\") != -1) {\n    firstSlashPos = fullPageName.indexOf(\"/resources/\");\n  } else if (fullPageName.indexOf(\"/training/\") != -1) {\n    firstSlashPos = fullPageName.indexOf(\"/training/\");\n  }\n  if (lastSlashPos == (fullPageName.length - 1)) { // if the url ends in slash (add 'index.html')\n    fullPageName = fullPageName + \"index.html\";\n  }\n\n  // get the path and page name from the URL (such as 'guide/topics/graphics/index.html')\n  var htmlPos = fullPageName.indexOf(\".html\");\n  var pathPageName = fullPageName.slice(firstSlashPos, htmlPos + 5); // +5 advances past \".html\"\n  // find instances of the page name in the side nav\n  var link = $(\"#devdoc-nav a[href$='\"+ pathPageName+\"']\");\n  // if there's no match, then let's backstep through the directory until we find an index.html\n  // page that matches our ancestor directories (only for dev guide and resources)\n  if ((link.length == 0) && ((fullPageName.indexOf(\"/guide/\") != -1) ||\n                  (fullPageName.indexOf(\"/resources/\") != -1))) {\n    lastBackstep = pathPageName.lastIndexOf(\"/\");\n    while (link.length == 0) {\n      backstepDirectory = pathPageName.lastIndexOf(\"/\", lastBackstep);\n      link = $(\"#devdoc-nav a[href$='\"+ pathPageName.slice(0, backstepDirectory +\n                      1)+\"index.html']\");\n      lastBackstep = pathPageName.lastIndexOf(\"/\", lastBackstep - 1);\n      if (lastBackstep == 0) break;\n    }\n  }\n\n  // add 'selected' to the <li> or <div> that wraps this <a>\n  link.parent().addClass('selected');\n\n  // if we're in a toggleable root link (<li class=toggle-list><div><a>)\n  if (link.parent().parent().hasClass('toggle-list')) {\n    toggle(link.parent().parent(), false); // open our own list\n    // then also check if we're in a third-level nested list that's toggleable\n    if (link.parent().parent().parent().is(':hidden')) {\n      toggle(link.parent().parent().parent().parent(), false); // open the super parent list\n    }\n  }\n  // if we're in a normal nav link (<li><a>) and the parent <ul> is hidden\n  else if (link.parent().parent().is(':hidden')) {\n    toggle(link.parent().parent().parent(), false); // open the parent list\n    // then also check if the parent list is also nested in a hidden list\n    if (link.parent().parent().parent().parent().is(':hidden')) {\n      toggle(link.parent().parent().parent().parent().parent(), false); // open the super parent list\n    }\n  }\n}\n\n/* Resize the height of the nav panels in the reference,\n * and save the new size to a cookie */\nfunction resizePackagesHeight() {\n  var windowHeight = ($(window).height() - HEADER_HEIGHT);\n  var swapperHeight = windowHeight - 13; // move 13px for swapper link at the bottom\n  resizePackagesNav.css({maxHeight:swapperHeight + \"px\"});\n  classesNav.css({height:swapperHeight - parseInt(resizePackagesNav.css(\"height\")) + \"px\"});\n\n  $(\"#swapper\").css({height:swapperHeight + \"px\"});\n  $(\"#packages-nav\").css({height:parseInt(resizePackagesNav.css(\"height\")) - 6 + \"px\"}); //move 6px for handle\n\n  var basePath = getBaseUri(location.pathname);\n  var section = basePath.substring(1,basePath.indexOf(\"/\",1));\n  writeCookie(\"height\", resizePackagesNav.css(\"height\"), section, null);\n}\n\n/* Resize the height of the side-nav and doc-content divs,\n * which creates the frame effect */\nfunction resizeHeight() {\n  var docContent = $(\"#doc-content\");\n\n  // Get the window height and always resize the doc-content and side-nav divs\n  var windowHeight = ($(window).height() - HEADER_HEIGHT);\n  docContent.css({height:windowHeight + \"px\"});\n  $(\"#side-nav\").css({height:windowHeight + \"px\"});\n\n  var href = location.href;\n  // If in the reference docs, also resize the \"swapper\", \"classes-nav\", and \"nav-tree\"  divs\n  if (href.indexOf(\"/reference/\") != -1) {\n    var swapperHeight = windowHeight - 13;\n    $(\"#swapper\").css({height:swapperHeight + \"px\"});\n    $(\"#classes-nav\").css({height:swapperHeight - parseInt(resizePackagesNav.css(\"height\")) + \"px\"});\n    $(\"#nav-tree\").css({height:swapperHeight + \"px\"});\n\n  // Also resize the \"devdoc-nav\" div\n  } else if ($(\"#devdoc-nav\").length) {\n    $(\"#devdoc-nav\").css({height:sidenav.css(\"height\")});\n  }\n\n  // Hide the \"Go to top\" link if there's no vertical scroll\n  if ( parseInt($(\"#jd-content\").css(\"height\")) <= parseInt(docContent.css(\"height\")) ) {\n    $(\"a[href='#top']\").css({'display':'none'});\n  } else {\n    $(\"a[href='#top']\").css({'display':'inline'});\n  }\n}\n\n/* Resize the width of the \"side-nav\" and the left margin of the \"doc-content\" div,\n * which creates the resizable side bar */\nfunction resizeWidth() {\n  var windowWidth = $(window).width() + \"px\";\n  var sidenav = $(\"#side-nav\");\n  if (sidenav.length) {\n    var sidenavWidth = sidenav.css(\"width\");\n  } else {\n    var sidenavWidth = 0;\n  }\n  content.css({marginLeft:parseInt(sidenavWidth) + 6 + \"px\"}); //account for 6px-wide handle-bar\n\n  if (isIE6) {\n    content.css({width:parseInt(windowWidth) - parseInt(sidenavWidth) - 6 + \"px\"}); // necessary in order to for scrollbars to be visible\n  }\n\n  resizePackagesNav.css({width:sidenavWidth});\n  classesNav.css({width:sidenavWidth});\n  $(\"#packages-nav\").css({width:sidenavWidth});\n\n  if (sidenav.length) { // Must check if the nav exists because IE6 calls resizeWidth() from resizeAll() for all pages\n    var basePath = getBaseUri(location.pathname);\n    var section = basePath.substring(1,basePath.indexOf(\"/\",1));\n    section = section.indexOf(\"training\") != -1 ? \"resources\" : section;\n    writeCookie(\"width\", sidenavWidth, section, null);\n  }\n}\n\n/* For IE6 only,\n * because it can't properly perform auto width for \"doc-content\" div,\n * avoiding this for all browsers provides better performance */\nfunction resizeAll() {\n  resizeHeight();\n  resizeWidth();\n}\n\nfunction getBaseUri(uri) {\n  var intlUrl = (uri.substring(0,6) == \"/intl/\");\n  if (intlUrl) {\n    base = uri.substring(uri.indexOf('intl/')+5,uri.length);\n    base = base.substring(base.indexOf('/')+1, base.length);\n      //alert(\"intl, returning base url: /\" + base);\n    return (\"/\" + base);\n  } else {\n      //alert(\"not intl, returning uri as found.\");\n    return uri;\n  }\n}\n\nfunction requestAppendHL(uri) {\n//append \"?hl=<lang> to an outgoing request (such as to blog)\n  var lang = getLangPref();\n  if (lang) {\n    var q = 'hl=' + lang;\n    uri += '?' + q;\n    window.location = uri;\n    return false;\n  } else {\n    return true;\n  }\n}\n\nfunction loadLast(cookiePath) {\n  var location = window.location.href;\n  if (location.indexOf(\"/\"+cookiePath+\"/\") != -1) {\n    return true;\n  }\n  var lastPage = readCookie(cookiePath + \"_lastpage\");\n  if (lastPage) {\n    window.location = lastPage;\n    return false;\n  }\n  return true;\n}\n\n$(window).unload(function(){\n  var path = getBaseUri(location.pathname);\n  if (path.indexOf(\"/reference/\") != -1) {\n    writeCookie(\"lastpage\", path, \"reference\", null);\n  } else if (path.indexOf(\"/guide/\") != -1) {\n    writeCookie(\"lastpage\", path, \"guide\", null);\n  } else if ((path.indexOf(\"/resources/\") != -1) || (path.indexOf(\"/training/\") != -1)) {\n    writeCookie(\"lastpage\", path, \"resources\", null);\n  }\n});\n\nfunction toggle(obj, slide) {\n  var ul = $(\"ul:first\", obj);\n  var li = ul.parent();\n  if (li.hasClass(\"closed\")) {\n    if (slide) {\n      ul.slideDown(\"fast\");\n    } else {\n      ul.show();\n    }\n    li.removeClass(\"closed\");\n    li.addClass(\"open\");\n    $(\".toggle-img\", li).attr(\"title\", \"hide pages\");\n  } else {\n    ul.slideUp(\"fast\");\n    li.removeClass(\"open\");\n    li.addClass(\"closed\");\n    $(\".toggle-img\", li).attr(\"title\", \"show pages\");\n  }\n}\n\nfunction buildToggleLists() {\n  $(\".toggle-list\").each(\n    function(i) {\n      $(\"div:first\", this).append(\"<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>\");\n      $(this).addClass(\"closed\");\n    });\n}\n\nfunction getNavPref() {\n  var v = readCookie('reference_nav');\n  if (v != NAV_PREF_TREE) {\n    v = NAV_PREF_PANELS;\n  }\n  return v;\n}\n\nfunction chooseDefaultNav() {\n  nav_pref = getNavPref();\n  if (nav_pref == NAV_PREF_TREE) {\n    $(\"#nav-panels\").toggle();\n    $(\"#panel-link\").toggle();\n    $(\"#nav-tree\").toggle();\n    $(\"#tree-link\").toggle();\n  }\n}\n\nfunction swapNav() {\n  if (nav_pref == NAV_PREF_TREE) {\n    nav_pref = NAV_PREF_PANELS;\n  } else {\n    nav_pref = NAV_PREF_TREE;\n    init_default_navtree(toRoot);\n  }\n  var date = new Date();\n  date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years\n  writeCookie(\"nav\", nav_pref, \"reference\", date.toGMTString());\n\n  $(\"#nav-panels\").toggle();\n  $(\"#panel-link\").toggle();\n  $(\"#nav-tree\").toggle();\n  $(\"#tree-link\").toggle();\n\n  if ($(\"#nav-tree\").is(':visible')) scrollIntoView(\"nav-tree\");\n  else {\n    scrollIntoView(\"packages-nav\");\n    scrollIntoView(\"classes-nav\");\n  }\n}\n\nfunction scrollIntoView(nav) {\n  var navObj = $(\"#\"+nav);\n  if (navObj.is(':visible')) {\n    var selected = $(\".selected\", navObj);\n    if (selected.length == 0) return;\n    if (selected.is(\"div\")) selected = selected.parent(); // when the selected item is a parent\n\n    var scrolling = document.getElementById(nav);\n    var navHeight = navObj.height();\n    var offsetTop = selected.position().top;\n\n    // handle nested items\n    if (selected.parent().parent().is(\".toggle-list\")) {\n      selected = selected.parent().parent();\n      // handle second level nested items\n      if (selected.parent().parent().is(\".toggle-list\")) {\n        selected = selected.parent().parent();\n      }\n      offsetTop += selected.position().top;\n    }\n\n    // 180px from the bottom of the list is the threshold\n    if(offsetTop > navHeight - 180) {\n      scrolling.scrollTop = offsetTop - navHeight + 180;\n    }\n  }\n}\n\nfunction changeTabLang(lang) {\n  var nodes = $(\"#header-tabs\").find(\".\"+lang);\n  for (i=0; i < nodes.length; i++) { // for each node in this language\n    var node = $(nodes[i]);\n    node.siblings().css(\"display\",\"none\"); // hide all siblings\n    if (node.not(\":empty\").length != 0) { //if this languages node has a translation, show it\n      node.css(\"display\",\"inline\");\n    } else { //otherwise, show English instead\n      node.css(\"display\",\"none\");\n      node.siblings().filter(\".en\").css(\"display\",\"inline\");\n    }\n  }\n}\n\nfunction changeNavLang(lang) {\n  var nodes = $(\"#side-nav\").find(\".\"+lang);\n  for (i=0; i < nodes.length; i++) { // for each node in this language\n    var node = $(nodes[i]);\n    node.siblings().css(\"display\",\"none\"); // hide all siblings\n    if (node.not(\":empty\").length != 0) { // if this languages node has a translation, show it\n      node.css(\"display\",\"inline\");\n    } else { // otherwise, show English instead\n      node.css(\"display\",\"none\");\n      node.siblings().filter(\".en\").css(\"display\",\"inline\");\n    }\n  }\n}\n\nfunction changeDocLang(lang) {\n  changeTabLang(lang);\n  changeNavLang(lang);\n}\n\nfunction changeLangPref(lang, refresh) {\n  var date = new Date();\n  expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000))); // keep this for 50 years\n  //alert(\"expires: \" + expires)\n  writeCookie(\"pref_lang\", lang, null, expires);\n  //changeDocLang(lang);\n  if (refresh) {\n    l = getBaseUri(location.pathname);\n    window.location = l;\n  }\n}\n\nfunction loadLangPref() {\n  var lang = readCookie(\"pref_lang\");\n  if (lang != 0) {\n    $(\"#language\").find(\"option[value='\"+lang+\"']\").attr(\"selected\",true);\n  }\n}\n\nfunction getLangPref() {\n  var lang = $(\"#language\").find(\":selected\").attr(\"value\");\n  if (!lang) {\n    lang = readCookie(\"pref_lang\");\n  }\n  return (lang != 0) ? lang : 'en';\n}\n\n\n/* Used to hide and reveal supplemental content, such as long code samples.\n   See the companion CSS in android-developer-docs.css */\nfunction toggleContent(obj) {\n  var div = $(obj.parentNode.parentNode);\n  var toggleMe = $(\".toggle-content-toggleme\",div);\n  if (div.hasClass(\"closed\")) { // if it's closed, open it\n    toggleMe.slideDown();\n    $(\".toggle-content-text\", obj).toggle();\n    div.removeClass(\"closed\").addClass(\"open\");\n    $(\".toggle-content-img\", div).attr(\"title\", \"hide\").attr(\"src\", toRoot + \"assets/images/triangle-opened.png\");\n  } else { // if it's open, close it\n    toggleMe.slideUp('fast', function() {  // Wait until the animation is done before closing arrow\n      $(\".toggle-content-text\", obj).toggle();\n      div.removeClass(\"open\").addClass(\"closed\");\n      $(\".toggle-content-img\", div).attr(\"title\", \"show\").attr(\"src\", toRoot + \"assets/images/triangle-closed.png\");\n    });\n  }\n  return false;\n}\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/android-developer-reference.js",
    "content": "\n/* API LEVEL TOGGLE */\naddLoadEvent(changeApiLevel);\n\nvar API_LEVEL_ENABLED_COOKIE = \"api_level_enabled\";\nvar API_LEVEL_COOKIE = \"api_level\";\nvar minLevel = 1;\nvar maxLevel = 1;\n\nfunction toggleApiLevelSelector(checkbox) {\n  var date = new Date();\n  date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years\n  var expiration = date.toGMTString();\n  if (checkbox.checked) {\n    $(\"#apiLevelSelector\").removeAttr(\"disabled\");\n    $(\"#api-level-toggle label\").removeClass(\"disabled\");\n    writeCookie(API_LEVEL_ENABLED_COOKIE, 1, null, expiration);\n  } else {\n    $(\"#apiLevelSelector\").attr(\"disabled\",\"disabled\");\n    $(\"#api-level-toggle label\").addClass(\"disabled\");\n    writeCookie(API_LEVEL_ENABLED_COOKIE, 0, null, expiration);\n  }\n  changeApiLevel();\n}\n\nfunction buildApiLevelSelector() {\n  maxLevel = SINCE_DATA.length;\n  var userApiLevelEnabled = readCookie(API_LEVEL_ENABLED_COOKIE);\n  var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));\n  userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default\n\n  if (userApiLevelEnabled == 0) {\n    $(\"#apiLevelSelector\").attr(\"disabled\",\"disabled\");\n  } else {\n    $(\"#apiLevelCheckbox\").attr(\"checked\",\"checked\");\n    $(\"#api-level-toggle label\").removeClass(\"disabled\");\n  }\n\n  minLevel = parseInt($(\"body\").attr(\"class\"));\n  // Handle provisional api levels; the provisional level will always be the highest possible level\n  // Provisional api levels will also have a length; other stuff that's just missing a level won't,\n  // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)\n  if (isNaN(minLevel) && minLevel.length) {\n    minLevel = maxLevel;\n  }\n  var select = $(\"#apiLevelSelector\").html(\"\").change(changeApiLevel);\n  for (var i = maxLevel-1; i >= 0; i--) {\n    var option = $(\"<option />\").attr(\"value\",\"\"+SINCE_DATA[i]).append(\"\"+SINCE_DATA[i]);\n  //  if (SINCE_DATA[i] < minLevel) option.addClass(\"absent\"); // always false for strings (codenames)\n    select.append(option);\n  }\n\n  // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)\n  var selectedLevelItem = $(\"#apiLevelSelector option[value='\"+userApiLevel+\"']\").get(0);\n  selectedLevelItem.setAttribute('selected',true);\n}\n\nfunction changeApiLevel() {\n  maxLevel = SINCE_DATA.length;\n  var userApiLevelEnabled = readCookie(API_LEVEL_ENABLED_COOKIE);\n  var selectedLevel = maxLevel;\n\n  if (userApiLevelEnabled == 0) {\n    toggleVisisbleApis(selectedLevel, \"body\");\n  } else {\n    selectedLevel = parseInt($(\"#apiLevelSelector option:selected\").val());\n    toggleVisisbleApis(selectedLevel, \"body\");\n\n    var date = new Date();\n    date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years\n    var expiration = date.toGMTString();\n    writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);\n  }\n\n  if (selectedLevel < minLevel) {\n    var thing = ($(\"#jd-header\").html().indexOf(\"package\") != -1) ? \"package\" : \"class\";\n    $(\"#naMessage\").show().html(\"<div><p><strong>This \" + thing + \" is not available with API Level \" + selectedLevel + \".</strong></p>\"\n                              + \"<p>To use this \" + thing + \", your application must specify API Level \\\"\" + $(\"body\").attr(\"class\") + \"\\\" or higher in its manifest \"\n                              + \"and be compiled against a version of the Android library that supports an equal or higher API Level. To reveal this \"\n                              + \"document, change the value of the API Level filter above.</p>\"\n                              + \"<p><a href='\" +toRoot+ \"guide/appendix/api-levels.html'>What is the API Level?</a></p></div>\");\n  } else {\n    $(\"#naMessage\").hide();\n  }\n}\n\nfunction toggleVisisbleApis(selectedLevel, context) {\n  var apis = $(\".api\",context);\n  apis.each(function(i) {\n    var obj = $(this);\n    var className = obj.attr(\"class\");\n    var apiLevelIndex = className.lastIndexOf(\"-\")+1;\n    var apiLevelEndIndex = className.indexOf(\" \", apiLevelIndex);\n    apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;\n    var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);\n    if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail\n      return;\n    }\n    apiLevel = parseInt(apiLevel);\n\n    // Handle provisional api levels; if this item's level is the provisional one, set it to the max\n    var selectedLevelNum = parseInt(selectedLevel)\n    var apiLevelNum = parseInt(apiLevel);\n    if (isNaN(apiLevelNum)) {\n        apiLevelNum = maxLevel;\n    }\n\n    // Grey things out that aren't available and give a tooltip title\n    if (apiLevelNum > selectedLevelNum) obj.addClass(\"absent\").attr(\"title\",\"Requires API Level \\\"\"\n            + apiLevel + \"\\\" or higher\");\n    else obj.removeClass(\"absent\").removeAttr(\"title\");\n  });\n}\n\n/* NAVTREE */\n\nfunction new_node(me, mom, text, link, children_data, api_level)\n{\n  var node = new Object();\n  node.children = Array();\n  node.children_data = children_data;\n  node.depth = mom.depth + 1;\n\n  node.li = document.createElement(\"li\");\n  mom.get_children_ul().appendChild(node.li);\n\n  node.label_div = document.createElement(\"div\");\n  node.label_div.className = \"label\";\n  if (api_level != null) {\n    $(node.label_div).addClass(\"api\");\n    $(node.label_div).addClass(\"api-level-\"+api_level);\n  }\n  node.li.appendChild(node.label_div);\n  node.label_div.style.paddingLeft = 10*node.depth + \"px\";\n\n  if (children_data == null) {\n    // 12 is the width of the triangle and padding extra space\n    node.label_div.style.paddingLeft = ((10*node.depth)+12) + \"px\";\n  } else {\n    node.label_div.style.paddingLeft = 10*node.depth + \"px\";\n    node.expand_toggle = document.createElement(\"a\");\n    node.expand_toggle.href = \"javascript:void(0)\";\n    node.expand_toggle.onclick = function() {\n          if (node.expanded) {\n            $(node.get_children_ul()).slideUp(\"fast\");\n            node.plus_img.src = me.toroot + \"assets/images/triangle-closed-small.png\";\n            node.expanded = false;\n          } else {\n            expand_node(me, node);\n          }\n       };\n    node.label_div.appendChild(node.expand_toggle);\n\n    node.plus_img = document.createElement(\"img\");\n    node.plus_img.src = me.toroot + \"assets/images/triangle-closed-small.png\";\n    node.plus_img.className = \"plus\";\n    node.plus_img.border = \"0\";\n    node.expand_toggle.appendChild(node.plus_img);\n\n    node.expanded = false;\n  }\n\n  var a = document.createElement(\"a\");\n  node.label_div.appendChild(a);\n  node.label = document.createTextNode(text);\n  a.appendChild(node.label);\n  if (link) {\n    a.href = me.toroot + link;\n  } else {\n    if (children_data != null) {\n      a.className = \"nolink\";\n      a.href = \"javascript:void(0)\";\n      a.onclick = node.expand_toggle.onclick;\n      // This next line shouldn't be necessary.  I'll buy a beer for the first\n      // person who figures out how to remove this line and have the link\n      // toggle shut on the first try. --joeo@android.com\n      node.expanded = false;\n    }\n  }\n  \n\n  node.children_ul = null;\n  node.get_children_ul = function() {\n      if (!node.children_ul) {\n        node.children_ul = document.createElement(\"ul\");\n        node.children_ul.className = \"children_ul\";\n        node.children_ul.style.display = \"none\";\n        node.li.appendChild(node.children_ul);\n      }\n      return node.children_ul;\n    };\n\n  return node;\n}\n\nfunction expand_node(me, node)\n{\n  if (node.children_data && !node.expanded) {\n    if (node.children_visited) {\n      $(node.get_children_ul()).slideDown(\"fast\");\n    } else {\n      get_node(me, node);\n      if ($(node.label_div).hasClass(\"absent\")) $(node.get_children_ul()).addClass(\"absent\");\n      $(node.get_children_ul()).slideDown(\"fast\");\n    }\n    node.plus_img.src = me.toroot + \"assets/images/triangle-opened-small.png\";\n    node.expanded = true;\n\n    // perform api level toggling because new nodes are new to the DOM\n    var selectedLevel = $(\"#apiLevelSelector option:selected\").val();\n    toggleVisisbleApis(selectedLevel, \"#side-nav\");\n  }\n}\n\nfunction get_node(me, mom)\n{\n  mom.children_visited = true;\n  for (var i in mom.children_data) {\n    var node_data = mom.children_data[i];\n    mom.children[i] = new_node(me, mom, node_data[0], node_data[1],\n        node_data[2], node_data[3]);\n  }\n}\n\nfunction this_page_relative(toroot)\n{\n  var full = document.location.pathname;\n  var file = \"\";\n  if (toroot.substr(0, 1) == \"/\") {\n    if (full.substr(0, toroot.length) == toroot) {\n      return full.substr(toroot.length);\n    } else {\n      // the file isn't under toroot.  Fail.\n      return null;\n    }\n  } else {\n    if (toroot != \"./\") {\n      toroot = \"./\" + toroot;\n    }\n    do {\n      if (toroot.substr(toroot.length-3, 3) == \"../\" || toroot == \"./\") {\n        var pos = full.lastIndexOf(\"/\");\n        file = full.substr(pos) + file;\n        full = full.substr(0, pos);\n        toroot = toroot.substr(0, toroot.length-3);\n      }\n    } while (toroot != \"\" && toroot != \"/\");\n    return file.substr(1);\n  }\n}\n\nfunction find_page(url, data)\n{\n  var nodes = data;\n  var result = null;\n  for (var i in nodes) {\n    var d = nodes[i];\n    if (d[1] == url) {\n      return new Array(i);\n    }\n    else if (d[2] != null) {\n      result = find_page(url, d[2]);\n      if (result != null) {\n        return (new Array(i).concat(result));\n      }\n    }\n  }\n  return null;\n}\n\nfunction load_navtree_data(toroot) {\n  var navtreeData = document.createElement(\"script\");\n  navtreeData.setAttribute(\"type\",\"text/javascript\");\n  navtreeData.setAttribute(\"src\", toroot+\"navtree_data.js\");\n  $(\"head\").append($(navtreeData));\n}\n\nfunction init_default_navtree(toroot) {\n  init_navtree(\"nav-tree\", toroot, NAVTREE_DATA);\n  \n  // perform api level toggling because because the whole tree is new to the DOM\n  var selectedLevel = $(\"#apiLevelSelector option:selected\").val();\n  toggleVisisbleApis(selectedLevel, \"#side-nav\");\n}\n\nfunction init_navtree(navtree_id, toroot, root_nodes)\n{\n  var me = new Object();\n  me.toroot = toroot;\n  me.node = new Object();\n\n  me.node.li = document.getElementById(navtree_id);\n  me.node.children_data = root_nodes;\n  me.node.children = new Array();\n  me.node.children_ul = document.createElement(\"ul\");\n  me.node.get_children_ul = function() { return me.node.children_ul; };\n  //me.node.children_ul.className = \"children_ul\";\n  me.node.li.appendChild(me.node.children_ul);\n  me.node.depth = 0;\n\n  get_node(me, me.node);\n\n  me.this_page = this_page_relative(toroot);\n  me.breadcrumbs = find_page(me.this_page, root_nodes);\n  if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {\n    var mom = me.node;\n    for (var i in me.breadcrumbs) {\n      var j = me.breadcrumbs[i];\n      mom = mom.children[j];\n      expand_node(me, mom);\n    }\n    mom.label_div.className = mom.label_div.className + \" selected\";\n    addLoadEvent(function() {\n      scrollIntoView(\"nav-tree\");\n      });\n  }\n}\n\n/* TOGGLE INHERITED MEMBERS */\n\n/* Toggle an inherited class (arrow toggle)\n * @param linkObj  The link that was clicked.\n * @param expand  'true' to ensure it's expanded. 'false' to ensure it's closed.\n *                'null' to simply toggle.\n */\nfunction toggleInherited(linkObj, expand) {\n    var base = linkObj.getAttribute(\"id\");\n    var list = document.getElementById(base + \"-list\");\n    var summary = document.getElementById(base + \"-summary\");\n    var trigger = document.getElementById(base + \"-trigger\");\n    var a = $(linkObj);\n    if ( (expand == null && a.hasClass(\"closed\")) || expand ) {\n        list.style.display = \"none\";\n        summary.style.display = \"block\";\n        trigger.src = toRoot + \"assets/images/triangle-opened.png\";\n        a.removeClass(\"closed\");\n        a.addClass(\"opened\");\n    } else if ( (expand == null && a.hasClass(\"opened\")) || (expand == false) ) {\n        list.style.display = \"block\";\n        summary.style.display = \"none\";\n        trigger.src = toRoot + \"assets/images/triangle-closed.png\";\n        a.removeClass(\"opened\");\n        a.addClass(\"closed\");\n    }\n    return false;\n}\n\n/* Toggle all inherited classes in a single table (e.g. all inherited methods)\n * @param linkObj  The link that was clicked.\n * @param expand  'true' to ensure it's expanded. 'false' to ensure it's closed.\n *                'null' to simply toggle.\n */\nfunction toggleAllInherited(linkObj, expand) {\n  var a = $(linkObj);\n  var table = $(a.parent().parent().parent()); // ugly way to get table/tbody\n  var expandos = $(\".jd-expando-trigger\", table);\n  if ( (expand == null && a.text() == \"[Expand]\") || expand ) {\n    expandos.each(function(i) {\n      toggleInherited(this, true);\n    });\n    a.text(\"[Collapse]\");\n  } else if ( (expand == null && a.text() == \"[Collapse]\") || (expand == false) ) {\n    expandos.each(function(i) {\n      toggleInherited(this, false);\n    });\n    a.text(\"[Expand]\");\n  }\n  return false;\n}\n\n/* Toggle all inherited members in the class (link in the class title)\n */\nfunction toggleAllClassInherited() {\n  var a = $(\"#toggleAllClassInherited\"); // get toggle link from class title\n  var toggles = $(\".toggle-all\", $(\"#doc-content\"));\n  if (a.text() == \"[Expand All]\") {\n    toggles.each(function(i) {\n      toggleAllInherited(this, true);\n    });\n    a.text(\"[Collapse All]\");\n  } else {\n    toggles.each(function(i) {\n      toggleAllInherited(this, false);\n    });\n    a.text(\"[Expand All]\");\n  }\n  return false;\n}\n\n/* Expand all inherited members in the class. Used when initiating page search */\nfunction ensureAllInheritedExpanded() {\n  var toggles = $(\".toggle-all\", $(\"#doc-content\"));\n  toggles.each(function(i) {\n    toggleAllInherited(this, true);\n  });\n  $(\"#toggleAllClassInherited\").text(\"[Collapse All]\");\n}\n\n\n/* HANDLE KEY EVENTS\n * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)\n */\nvar agent = navigator['userAgent'].toLowerCase();\nvar mac = agent.indexOf(\"macintosh\") != -1;\n\n$(document).keydown( function(e) {\nvar control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key\n  if (control && e.which == 70) {  // 70 is \"F\"\n    ensureAllInheritedExpanded();\n  }\n});"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/android-developer-resource-browser.css",
    "content": "/* Resource Browser */\n\n#resource-browser-results .no-results {\n  font-style: italic;\n  display: none;\n}\n\n#resource-browser-results .result {\n  position: relative;\n  padding-left: 84px;\n  background: transparent none no-repeat scroll 4px 12px;\n  border-bottom: 1px solid #ddd;\n}\n\n#resource-browser-results .tagged-article {\n  background-image: url(images/resource-article.png);\n}\n\n#resource-browser-results .tagged-sample {\n  background-image: url(images/resource-sample.png);\n}\n\n#resource-browser-results .tagged-tutorial {\n  background-image: url(images/resource-tutorial.png);\n}\n\n#resource-browser-results .resource-meta {\n  margin-top: -1em;\n  font-size: 0.85em;\n  font-weight: normal;\n}\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/android-developer-resource-browser.js",
    "content": "(function() { // anonymize\n\nvar allTags = {};\nvar loadedResults = [];\n\n/**\n * Initialization code run upon the DOM being ready.\n */\n$(document).ready(function() {\n  // Parse page query parameters.\n  var params = parseParams(document.location.search);\n  params.tag = params.tag ? makeArray(params.tag) : null;\n\n  // Load tag and resource dataset.\n  loadTags();\n  loadResources();\n\n  showResults(params);\n\n  // Watch for keypresses in the keyword filter textbox, and update\n  // search results to reflect the keyword filter.\n  $('#resource-browser-keyword-filter').keyup(function() {\n    // Filter results on screen by keyword.\n    var keywords = $(this).val().split(/\\s+/g);\n    for (var i = 0; i < loadedResults.length; i++) {\n      var hide = false;\n      for (var j = 0; j < keywords.length; j++) {\n        if (!resultMatchesKeyword(loadedResults[i].result, keywords[j])) {\n          hide = true;\n          break;\n        }\n      }\n\n      loadedResults[i].node[hide ? 'hide' : 'show']();\n    }\n  });\n});\n\n/**\n * Returns whether or not the given search result contains the given keyword.\n */\nfunction resultMatchesKeyword(result, keyword) {\n  keyword = keyword.toLowerCase();\n  if (result.title &&\n      result.title.en.toLowerCase().indexOf(keyword) >= 0)\n    return true;\n  else if (result.description &&\n           result.description.en.toLowerCase().indexOf(keyword) >= 0)\n    return true;\n  else if (result.topicsHtml &&\n           result.topicsHtml.replace(/\\<.*?\\>/g,'').toLowerCase().indexOf(keyword) >= 0)\n    return true;\n  return false;\n}\n\n/**\n * Populates the allTags array with tag data from the ANDROID_TAGS\n * variable in the resource data JS file.\n */\nfunction loadTags() {\n  for (var tagClass in ANDROID_TAGS) {\n    for (var tag in ANDROID_TAGS[tagClass]) {\n      allTags[tag] = {\n        displayTag: ANDROID_TAGS[tagClass][tag],\n        tagClass: tagClass\n      };\n    }\n  }\n}\n\n/**\n * Massage the ANDROID_RESOURCES resource list in the resource data JS file.\n */\nfunction loadResources() {\n  for (var i = 0; i < ANDROID_RESOURCES.length; i++) {\n    var resource = ANDROID_RESOURCES[i];\n\n    // Convert the tags array to a tags hash for easier querying.\n    resource.tagsHash = {};\n    for (var j = 0; j < resource.tags.length; j++)\n      resource.tagsHash[resource.tags[j]] = true;\n\n    // Determine the type and topics of the resource by inspecting its tags.\n    resource.topics = [];\n    for (tag in resource.tagsHash)\n      if (tag in allTags) {\n        if (allTags[tag].tagClass == 'type') {\n          resource.type = tag;\n        } else if (allTags[tag].tagClass == 'topic') {\n          resource.topics.push(tag);\n        }\n      }\n\n    // Add a humanized topics list string.\n    resource.topicsHtml = humanizeList(resource.topics, function(item) {\n      return '<strong>' + allTags[item].displayTag + '</strong>';\n    });\n  }\n}\n\n/**\n * Loads resources for the given query parameters.\n */\nfunction showResults(params) {\n  loadedResults = [];\n  $('#resource-browser-search-params').empty();\n  $('#resource-browser-results').empty();\n\n  var i, j;\n  var searchTags = [];\n  if (params.tag) {\n    for (i = 0; i < params.tag.length; i++) {\n      var tag = params.tag[i];\n      if (tag.toLowerCase() in allTags) {\n        searchTags.push(tag.toLowerCase());\n      }\n    }\n  }\n\n  if (searchTags.length) {\n    // Show query params.\n    var taggedWithHtml = ['Showing technical resources tagged with '];\n    taggedWithHtml.push(humanizeList(searchTags, function(item) {\n      return '<strong>' + allTags[item].displayTag + '</strong>';\n    }));\n    $('#resource-browser-search-params').html(taggedWithHtml.join('') + ':');\n  } else {\n    $('#resource-browser-search-params').html('Showing all technical resources:');\n  }\n\n  var results = [];\n\n  // Create the list of resources to show.\n  for (i = 0; i < ANDROID_RESOURCES.length; i++) {\n    var resource = ANDROID_RESOURCES[i];\n    var skip = false;\n\n    if (searchTags.length) {\n      for (j = 0; j < searchTags.length; j++)\n        if (!(searchTags[j] in resource.tagsHash)) {\n          skip = true;\n          break;\n        }\n\n      if (skip)\n        continue;\n\n      results.push(resource);\n      continue;\n    }\n\n    results.push(resource);\n  }\n\n  // Format and show the list of resource results.\n  if (results.length) {\n    $('#resource-browser-results .no-results').hide();\n    for (i = 0; i < results.length; i++) {\n      var result = results[i];\n      var resultJqNode = $(tmpl('tmpl_resource_browser_result', result));\n      for (tag in result.tagsHash)\n        resultJqNode.addClass('tagged-' + tag);\n      $('#resource-browser-results').append(resultJqNode);\n\n      loadedResults.push({ node: resultJqNode, result: result });\n    }\n  } else {\n    $('#resource-browser-results .no-results').show();\n  }\n}\n\n/**\n * Formats the given array into a human readable, English string, ala\n * 'a, b and c', with an optional item formatter/wrapper function.\n */\nfunction humanizeList(arr, itemFormatter) {\n  itemFormatter = itemFormatter || function(o){ return o; };\n  arr = arr || [];\n\n  var out = [];\n  for (var i = 0; i < arr.length; i++) {\n    out.push(itemFormatter(arr[i]) +\n        ((i < arr.length - 2) ? ', ' : '') +\n        ((i == arr.length - 2) ? ' and ' : ''));\n  }\n\n  return out.join('');\n}\n\n/**\n * Parses a parameter string, i.e. foo=1&bar=2 into\n * a dictionary object.\n */\nfunction parseParams(paramStr) {\n  var params = {};\n  paramStr = paramStr.replace(/^[?#]/, '');\n\n  var pairs = paramStr.split('&');\n  for (var i = 0; i < pairs.length; i++) {\n    var p = pairs[i].split('=');\n    var key = p[0] ? decodeURIComponent(p[0]) : p[0];\n    var val = p[1] ? decodeURIComponent(p[1]) : p[1];\n    if (val === '0')\n      val = 0;\n    if (val === '1')\n      val = 1;\n\n    if (key in params) {\n      // Handle array values.\n      params[key] = makeArray(params[key]);\n      params[key].push(val);\n    } else {\n      params[key] = val;\n    }\n  }\n\n  return params;\n}\n\n/**\n * Returns the argument as a single-element array, or the argument itself\n * if it's already an array.\n */\nfunction makeArray(o) {\n  if (!o)\n    return [];\n\n  if (typeof o === 'object' && 'splice' in o) {\n    return o;\n  } else {\n    return [o];\n  }\n}\n\n})();\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/carousel.js",
    "content": "/* file: carousel.js\n   date: oct 2008\n   author: jeremydw,smain\n   info: operates the carousel widget for announcements on \n         the android developers home page. modified from the\n         original market.js from jeremydw. */\n\n/* -- video switcher -- */\n\nvar oldVid = \"multi\"; // set the default video\nvar nowPlayingString = \"Now playing:\";\nvar assetsRoot = \"assets/\";\n\n\n/* -- app thumbnail switcher -- */\n\nvar currentDroid;\nvar oldDroid;\n\n// shows a random application\nfunction randomDroid(){\n\n\t// count the total number of apps\n\tvar droidListLength = 0;\n\tfor (var k in droidList)\n\t\tdroidListLength++;\n\t\t\n\t// pick a random app and show it\n  var j = 0;\n  var i = Math.floor(droidListLength*Math.random());\n  for (var x in droidList) {\n    if(j++ == i){\n    \tcurrentDroid = x;\n    \tshowPreview(x);\n    \tcenterSlide(x);\n    }\n  }\n\n}\n\n// shows a bulletin, swaps the carousel highlighting\nfunction droid(appName){\n\n  oldDroid = $(\"#droidlink-\"+currentDroid);\n  currentDroid = appName;\n\n  var droid = droidList[appName];\n  \n  $(\"#\"+appName).show().siblings().hide();\n\n  if(oldDroid)\n    oldDroid.removeClass(\"selected\");\n\n  $(\"#droidlink-\"+appName).addClass(\"selected\");\n}\n\n\n// -- * build the carousel based on the droidList * -- //\nfunction buildCarousel() {\n  var appList = document.getElementById(\"app-list\");\n  for (var x in droidList) {\n    var droid = droidList[x];\n    var icon = droid.icon;\n    var name = droid.name;\n    var a = document.createElement(\"a\");\n    var img = document.createElement(\"img\");\n    var br = document.createElement(\"br\");\n    var span = document.createElement(\"span\");\n    var text = document.createTextNode(droid.name);\n\n    a.setAttribute(\"id\", \"droidlink-\" + x);\n    a.className = x;\n    a.setAttribute(\"href\", \"#\");\n    a.onclick = function() { showPreview(this.className); return false; }\n    img.setAttribute(\"src\", toRoot + assetsRoot + \"images/home/\" + droid.icon);\n    img.setAttribute(\"alt\", \"\");\n\n    span.appendChild(text);\n    a.appendChild(img);\n    a.appendChild(br);\n    a.appendChild(span);\n    appList.appendChild(a);\n    \n    \n    /* add the bulletins */\n    var layout = droid.layout;\n    var div = document.createElement(\"div\");\n    var imgDiv = document.createElement(\"div\");\n    var descDiv = document.createElement(\"div\");\n    \n    div.setAttribute(\"id\", x);\n    div.setAttribute(\"style\", \"display:none\");\n    imgDiv.setAttribute(\"class\", \"bulletinImg\");\n    descDiv.setAttribute(\"class\", \"bulletinDesc\");\n\t\n\t  if (layout == \"imgLeft\") {\n\t    $(imgDiv).addClass(\"img-left\");\n\t    $(descDiv).addClass(\"desc-right\");\n\t  } else if (layout == \"imgTop\") {\n\t    $(imgDiv).addClass(\"img-top\");\n\t    $(descDiv).addClass(\"desc-bottom\");\n\t  } else if (layout == \"imgRight\") {\n\t    $(imgDiv).addClass(\"img-right\");\n\t    $(descDiv).addClass(\"desc-left\");\n\t  }\n\t\n\t  imgDiv.innerHTML = \"<img src='\" + toRoot + assetsRoot + \"images/home/\" + droid.img + \"'>\";\n\t  descDiv.innerHTML = (droid.title != \"\") ? \"<h3>\" + droid.title + \"</h3>\" + droid.desc : droid.desc;\n\t\t$(div).append(imgDiv);\n\t\t$(div).append(descDiv);\n    \n    $(\"#carouselMain\").append(div);\n    \n  }\n}\n\n// -- * slider * -- //\n\n// -- dependencies:\n//    (1) div containing slides, (2) a \"clip\" div to hide the scroller\n//    (3) control arrows\n\n// -- * config below * -- //\n\nvar slideCode = droidList; // the dictionary of slides\nvar slideList = 'app-list'; // the div containing the slides\nvar arrowRight = 'arrow-right'; // the right control arrow\nvar arrowLeft = 'arrow-left'; // the left control arrow\n\n\nfunction showPreview(slideName) {\n  centerSlide(slideName);\n  if (slideName.indexOf('selected') != -1) {\n    return false;\n  }\n  droid(slideName); // do this function when slide is clicked\n}\n\nvar thumblist = document.getElementById(slideList);// the div containing the slides\n\nvar slideWidth = 144; // width of a slide including all margins, etc.\nvar slidesAtOnce = 3; // no. of slides to appear at once (requires odd number to have a centered slide)\n\n// -- * no editing should be needed below * -- //\n\nvar originPosition = {};\nvar is_animating = 0;\nvar currentStripPosition = 0;\nvar centeringPoint = 0;\nvar rightScrollLimit = 0;\n\n// makeSlideStrip()\n// - figures out how many slides there are\n// - determines the centering point of the slide strip\nfunction makeSlideStrip() {\n  var slideTotal = 0;\n  centeringPoint = Math.ceil(slidesAtOnce/2);\n  for (var x in slideCode) {\n    slideTotal++;\n  }\n  var i = 0;\n  for (var code in slideCode) {\n    if (i <= centeringPoint-1) {\n      originPosition[code] = 0;\n    } else {\n      if (i >= slideTotal-centeringPoint+1)  {\n        originPosition[code] = (slideTotal-slidesAtOnce)*slideWidth;\n      } else {\n        originPosition[code] = (i-centeringPoint+1)*slideWidth;\n      }\n    }\n    i++;\n  }\n  rightScrollLimit = -1*(slideTotal-slidesAtOnce)*slideWidth;\n}\n\n// slides with acceleration\nfunction slide(goal, id, go_left, cp) {\n  var div = document.getElementById(id);\n  var animation = {};\n  animation.time = 0.5;  // in seconds\n  animation.fps = 60;\n  animation.goal = goal;\n  origin = 0.0;\n  animation.origin = Math.abs(origin);  \n  animation.frames = (animation.time * animation.fps) - 1.0;\n  var current_frame = 0;\n  var motions = Math.abs(animation.goal - animation.origin);\n  function animate() {\n    var ease_right = function (t) { return (1 - Math.cos(t * Math.PI))/2.0; };\n    var ease = ease_right;\n    if (go_left == 1) {\n      ease = function(t) { return 1.0 - ease_right(t); };\n    }\n    var left = (ease(current_frame/animation.frames) * Math.abs(animation.goal - animation.origin)) - cp; \n    if(left < 0) {\n      left = 0;\n    }\n    if(!isNaN(left)) {\n      div.style.left = '-' + Math.round(left) + 'px';\n    }\n    current_frame += 1;\n    if (current_frame == animation.frames) {\n      is_animating = 0;\n      window.clearInterval(timeoutId)\n    }\n  }\n  var timeoutId = window.setInterval(animate, animation.time/animation.fps * 1000);\n}\n\n//Get style property\nfunction getStyle(element, cssProperty){\n  var elem = document.getElementById(element);\n  if(elem.currentStyle){\n    return elem.currentStyle[cssProperty]; //IE\n  } else{\n    var style =  document.defaultView.getComputedStyle(elem, null); //firefox, Opera\n    return style.getPropertyValue(cssProperty);\n  }\n}\n\n// Left and right arrows\nfunction page_left() {\n  var amount = slideWidth;\n  animateSlide(amount, 'left');\n}\n\nfunction page_right() { \n  var amount = slideWidth;\n  animateSlide(amount, 'right');\n}\n\n\n// animates the strip\n// - sets arrows to on or off\nfunction animateSlide(amount,dir) {\n  var currentStripPosition = parseInt(getStyle(slideList,'left'));\n  var motionDistance;\n  if (amount == slideWidth ) {\n    motionDistance = slideWidth;\n  } else {\n    motionDistance = amount;\n  }\n  \n  var rightarrow = document.getElementById(arrowRight);\n  var leftarrow = document.getElementById(arrowLeft);\n  \n  function aToggle(state,aDir) {\n    if (state == 'on') {\n      if (aDir =='right') {\n        rightarrow.className = 'arrow-right-on';\n        rightarrow.href = \"javascript:page_right()\";\n      } else {\n        leftarrow.className = 'arrow-left-on';\n        leftarrow.href = \"javascript:page_left()\";\n      }\n    } else {\n      if (aDir =='right') {\n        rightarrow.href = \"javascript:{}\";\n        rightarrow.className = 'arrow-right-off'; \n      } else {\n        leftarrow.href = \"javascript:{}\";\n        leftarrow.className = 'arrow-left-off';\n      }\n    }\n  }\n  \n  function arrowChange(rP) {\n    if (rP >= rightScrollLimit) {\n      aToggle('on','right');\n    }\n    if (rP <= rightScrollLimit) {\n      aToggle('off','right');\n    }\n    if (rP <= slideWidth) {\n      aToggle('on','left');\n    }\n    if (rP >= 0) {\n      aToggle('off','left');\n    }\n  }\n\n  if (dir == 'right' && is_animating == 0) {\n    arrowChange(currentStripPosition-motionDistance);\n    is_animating = 1;\n    slide(motionDistance, slideList, 0, currentStripPosition);\n  } else if (dir == 'left' && is_animating == 0) {\n    arrowChange(currentStripPosition+motionDistance);\n    is_animating = 1;\n    rightStripPosition = currentStripPosition + motionDistance;\n    slide(motionDistance, slideList, 1, rightStripPosition);\n  }\n}\n\nfunction centerSlide(slideName) {\n  var currentStripPosition = parseInt(getStyle(slideList,'left'));\n  var dir = 'left';\n  var originpoint = Math.abs(currentStripPosition);\n  if (originpoint <= originPosition[slideName]) {\n    dir = 'right';\n  }\n  var motionValue = Math.abs(originPosition[slideName]-originpoint);\n  animateSlide(motionValue,dir);\n}\n\n\nfunction initCarousel(def) {\n  buildCarousel();\n  showPreview(def);\n  makeSlideStrip();\n}\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/customizations.js",
    "content": "function showApiWarning(thing, selectedLevel, minLevel) {\n  if (selectedLevel < minLevel) {\n\t  $(\"#naMessage\").show().html(\"<div><p><strong>This \" + thing + \" is not available with API Level \" + selectedLevel + \".</strong></p>\"\n\t      + \"<p>To use this \" + thing + \", your application must specify API Level \" + minLevel + \" or higher in its manifest \"\n\t      + \"and be compiled against a version of the Android library that supports an equal or higher API Level. To reveal this \"\n\t      + \"document, change the value of the API Level filter above.</p>\"\n\t      + \"<p><a href='\" +toRoot+ \"guide/appendix/api-levels.html'>What is the API Level?</a></p></div>\");\n  } else {\n    $(\"#naMessage\").hide();\n  }\n}\n\n// Direct searches to search.html\nHAS_SEARCH_PAGE = true;\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/design/default.css",
    "content": "/* color definitions */\n/* 16 column layout */\n/* clearfix idiom */\n/* common mixins */\n/* page layout + top-level styles */\n::-webkit-selection,\n::-moz-selection,\n::selection {\n  background-color: #0099cc;\n  color: #fff; }\n\nhtml, body {\n  height: 100%;\n  margin: 0;\n  padding: 0;\n  background: #eee none no-repeat fixed top left;\n  background-image: -webkit-gradient(linear, 100% 0%, 100% 100%, from(#dddddd), color-stop(25%, #f2f2f2), color-stop(75%, #f2f2f2), to(#dddddd));\n  background-image: -moz-linear-gradient(top, #dddddd, #f2f2f2, #f2f2f2, #dddddd);\n  -webkit-font-smoothing: antialiased;\n  /* prevent subpixel antialiasing, which thickens the text */\n  /* text-rendering: optimizeLegibility; */\n  /* turned off ligatures due to bug 5945455 */ }\n\nbody {\n  color: #555555;\n  font: 14px/20px Roboto, sans-serif;\n  font-weight: 400; }\n\n#page-container {\n  width: 940px;\n  margin: 0 40px; }\n\n#page-header {\n  height: 80px;\n  margin-bottom: 20px;\n  font-size: 48px;\n  line-height: 48px;\n  font-weight: 100;\n  padding-left: 10px; }\n  #page-header a {\n    display: block;\n    position: relative;\n    top: 20px;\n    text-decoration: none;\n    color: #555555 !important; }\n\n#main-row {\n  display: inline-block; }\n  #main-row:after {\n    content: \".\";\n    display: block;\n    height: 0;\n    clear: both;\n    visibility: hidden; }\n  * html #main-row {\n    height: 1px; }\n\n#page-footer {\n  margin-left: 190px;\n  margin-top: 80px;\n  color: #999999;\n  padding-bottom: 40px;\n  font-size: 12px;\n  line-height: 15px; }\n  #page-footer a {\n    color: #777777; }\n  #page-footer #copyright {\n    margin-bottom: 10px; }\n\n#nav-container {\n  width: 160px;\n  min-height: 10px;\n  margin-right: 20px;\n  float: left; }\n\n#nav {\n  width: 160px; }\n\n#nav.fixed {\n  position: fixed;\n  top: 40px; }\n\n#content {\n  width: 760px;\n  float: left; }\n\na,\na:visited {\n  color: #333333; }\n\na:hover,\nacronym:hover {\n  color: #7aa1b0 !important; }\n\na:focus,\na:active {\n  color: #33b5e5 !important; }\n\nimg {\n  border: none; }\n\nul {\n  margin: 0;\n  padding: 0; }\n\nstrong {\n  font-weight: 500; }\n\nem {\n  font-style: italic; }\n\ncode {\n  font-family: Courier New, monospace; }\n\nacronym {\n  border-bottom: 1px dotted #555555;\n  cursor: help; }\n\nacronym:hover {\n  border-bottom-color: #7aa1b0; }\n\nimg.with-shadow,\nvideo.with-shadow {\n  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25); }\n\n/* disclosures mixin */\n/* content layout */\n.layout-content-row {\n  display: inline-block;\n  margin-bottom: 10px; }\n  .layout-content-row:after {\n    content: \".\";\n    display: block;\n    height: 0;\n    clear: both;\n    visibility: hidden; }\n  * html .layout-content-row {\n    height: 1px; }\n\n.layout-content-col {\n  float: left;\n  margin-left: 20px; }\n  .layout-content-col:first-child {\n    margin-left: 0; }\n\n.layout-content-col.span-1 {\n  width: 40px; }\n\n.layout-content-col.span-2 {\n  width: 100px; }\n\n.layout-content-col.span-3 {\n  width: 160px; }\n\n.layout-content-col.span-4 {\n  width: 220px; }\n\n.layout-content-col.span-5 {\n  width: 280px; }\n\n.layout-content-col.span-6 {\n  width: 340px; }\n\n.layout-content-col.span-7 {\n  width: 400px; }\n\n.layout-content-col.span-8 {\n  width: 460px; }\n\n.layout-content-col.span-9 {\n  width: 520px; }\n\n.layout-content-col.span-10 {\n  width: 580px; }\n\n.layout-content-col.span-11 {\n  width: 640px; }\n\n.layout-content-col.span-12 {\n  width: 700px; }\n\n.layout-content-col.span-13 {\n  width: 760px; }\n\n.vspace.size-1 {\n  height: 10px; }\n\n.vspace.size-2 {\n  height: 20px; }\n\n.vspace.size-3 {\n  height: 30px; }\n\n.vspace.size-4 {\n  height: 40px; }\n\n.vspace.size-5 {\n  height: 50px; }\n\n.vspace.size-6 {\n  height: 60px; }\n\n.vspace.size-7 {\n  height: 70px; }\n\n.vspace.size-8 {\n  height: 80px; }\n\n.vspace.size-9 {\n  height: 90px; }\n\n.vspace.size-10 {\n  height: 100px; }\n\n.vspace.size-11 {\n  height: 110px; }\n\n.vspace.size-12 {\n  height: 120px; }\n\n.vspace.size-13 {\n  height: 130px; }\n\n.vspace.size-14 {\n  height: 140px; }\n\n.vspace.size-15 {\n  height: 150px; }\n\n.vspace.size-16 {\n  height: 160px; }\n\n/* nav */\n#nav {\n  /* section header divs */\n  /* expanded section header divs */\n  /* sublinks */ }\n  #nav li {\n    list-style-type: none;\n    font-size: 14px;\n    line-height: 10px; }\n  #nav a {\n    color: #555555;\n    text-decoration: none; }\n  #nav li.selected > a,\n  #nav li.selected .nav-section-header a {\n    font-weight: 500;\n    color: #0099cc !important; }\n  #nav .nav-section-header {\n    position: relative;\n    padding: 10px;\n    margin-bottom: 1px;\n    /* section header links */ }\n    #nav .nav-section-header a {\n      color: #333333;\n      font-weight: 500;\n      text-transform: uppercase; }\n    #nav .nav-section-header:after {\n      content: '';\n      background: transparent url(disclosure_down.png) no-repeat scroll top left;\n      width: 10px;\n      height: 10px;\n      display: block;\n      position: absolute;\n      top: 10px;\n      right: 10px; }\n    #nav .nav-section-header.empty:after {\n      display: none; }\n  #nav li.expanded .nav-section-header {\n    background: rgba(0, 0, 0, 0.05); }\n    #nav li.expanded .nav-section-header:after {\n      content: '';\n      background: transparent url(disclosure_up.png) no-repeat scroll top left;\n      width: 10px;\n      height: 10px; }\n  #nav > li > ul {\n    height: 0;\n    overflow: hidden;\n    margin-bottom: 0; }\n    #nav > li > ul.animate-height {\n      -webkit-transition: height 0.25s ease-in;\n      -moz-transition: height 0.25s ease-in;\n      transition: height 0.25s ease-in; }\n    #nav > li > ul li {\n      padding: 10px 10px 11px 10px; }\n  #nav > li.expanded > ul {\n    height: auto; }\n    #nav > li.expanded > ul li {\n      background: rgba(0, 0, 0, 0.03); }\n  #nav #back-dac-section {\n    padding: 10px;\n    border-top: 1px solid #ddd; }\n    #nav #back-dac-section a {\n      color: #333333;\n      font-weight: 500;\n      text-transform: uppercase; }\n\n/* content header */\n.content-header {\n  border-bottom: 1px solid #33b5e5;\n  height: 30px; }\n  .content-header h2 {\n    border-bottom: 0; }\n  .content-header.just-links {\n    border-bottom: 0; }\n\n.content-footer {\n  border-top: 1px solid #33b5e5;\n  margin-top: 10px;\n  height: 30px; }\n\n.paging-links {\n  position: relative; }\n  .paging-links a {\n    position: absolute;\n    font-size: 14px;\n    line-height: 30px;\n    color: #555555;\n    text-decoration: none;\n    text-transform: uppercase; }\n  .paging-links .prev-page-link {\n    display: none;\n    left: -5px; }\n    .paging-links .prev-page-link:before {\n      content: '';\n      background: transparent url(disclosure_left.png) no-repeat scroll top left;\n      width: 10px;\n      height: 10px;\n      display: inline-block;\n      margin-right: 5px; }\n  .paging-links .next-page-link {\n    display: none;\n    right: 10px; }\n    .paging-links .next-page-link:after {\n      content: '';\n      background: transparent url(disclosure_right.png) no-repeat scroll top left;\n      width: 10px;\n      height: 10px;\n      display: inline-block;\n      margin-left: 5px; }\n\n/* content body */\n@-webkit-keyframes glowheader {\n  from {\n    background-color: #33b5e5;\n    color: #000;\n    border-bottom-color: #000; }\n\n  to {\n    background-color: transparent;\n    color: #33b5e5;\n    border-bottom-color: #33b5e5; } }\n\n@-moz-keyframes glowheader {\n  from {\n    background-color: #33b5e5;\n    color: #000;\n    border-bottom-color: #000; }\n\n  to {\n    background-color: transparent;\n    color: #33b5e5;\n    border-bottom-color: #33b5e5; } }\n\n@keyframes glowheader {\n  from {\n    background-color: #33b5e5;\n    color: #000;\n    border-bottom-color: #000; }\n\n  to {\n    background-color: transparent;\n    color: #33b5e5;\n    border-bottom-color: #33b5e5; } }\n\n#content p,\n#content ul,\n#content ol,\n#content h3 {\n  margin: 0 10px 10px 10px; }\n#content h2 {\n  padding-left: 10px;\n  padding-right: 10px;\n  margin-bottom: 10px;\n  font-size: 16px;\n  line-height: 30px;\n  font-weight: 500;\n  color: #33b5e5;\n  border-bottom: 1px solid #33b5e5;\n  height: 30px; }\n  #content h2:target {\n    -webkit-animation-name: glowheader;\n    -moz-animation-name: glowheader;\n    animation-name: glowheader;\n    -webkit-animation-duration: 0.7s;\n    -moz-animation-duration: 0.7s;\n    animation-duration: 0.7s;\n    -webkit-animation-timing-function: ease-out;\n    -moz-animation-timing-function: ease-out;\n    animation-timing-function: ease-out; }\n#content hr {\n  border: 0;\n  border-bottom: 1px solid #33b5e5;\n  margin-bottom: 20px; }\n#content h3 {\n  color: #33b5e5;\n  text-transform: uppercase;\n  font-size: 14px;\n  line-height: 20px;\n  font-weight: 500; }\n#content h4 {\n  margin: 0 10px;\n  color: #333333;\n  font-weight: 500;\n  font-size: 14px;\n  line-height: 20px; }\n#content strong {\n  color: #333333; }\n#content ul li,\n#content ol li {\n  margin-left: 20px; }\n  #content ul li h4,\n  #content ol li h4 {\n    margin: 0; }\n  #content ul li p,\n  #content ol li p {\n    margin-left: 0; }\n#content ul li {\n  list-style-type: square;\n  list-style-type: none;\n  position: relative; }\n  #content ul li:before {\n    content: '\\2022';\n    font-family: verdana;\n    font-size: 14px;\n    line-height: 20px;\n    position: absolute;\n    left: -20px;\n    top: -1px; }\n#content ol {\n  counter-reset: item; }\n  #content ol li {\n    font-size: 14px;\n    line-height: 20px;\n    list-style-type: none;\n    position: relative; }\n    #content ol li:before {\n      content: counter(item) \". \";\n      counter-increment: item;\n      position: absolute;\n      left: -20px;\n      top: 0; }\n    #content ol li.value-1:before {\n      content: \"1. \"; }\n    #content ol li.value-2:before {\n      content: \"2. \"; }\n    #content ol li.value-3:before {\n      content: \"3. \"; }\n    #content ol li.value-4:before {\n      content: \"4. \"; }\n    #content ol li.value-5:before {\n      content: \"5. \"; }\n    #content ol li.value-6:before {\n      content: \"6. \"; }\n    #content ol li.value-7:before {\n      content: \"7. \"; }\n    #content ol li.value-8:before {\n      content: \"8. \"; }\n    #content ol li.value-9:before {\n      content: \"9. \"; }\n    #content ol li.value-10:before {\n      content: \"10. \"; }\n#content .with-callouts ol li {\n  list-style-position: inside;\n  margin-left: 0; }\n  #content .with-callouts ol li:before {\n    position: static;\n    display: inline;\n    left: 0;\n    float: left;\n    width: 17px;\n    color: #33b5e5;\n    font-weight: 500; }\n\n/* special list items */\nli.no-bullet {\n  list-style-type: none !important; }\n\n#content li.with-icon {\n  position: relative;\n  margin-left: 40px;\n  min-height: 30px; }\n  #content li.with-icon p {\n    margin-left: 0 !important; }\n  #content li.with-icon:before {\n    position: absolute;\n    left: -40px;\n    top: 0;\n    content: '';\n    width: 30px;\n    height: 30px; }\n  #content li.with-icon.tablet:before {\n    background-image: url(ico_phone_tablet.png); }\n  #content li.with-icon.web:before {\n    background-image: url(ico_web.png); }\n  #content li.with-icon.checklist:before {\n    background-image: url(ico_checklist.png); }\n  #content li.with-icon.action:before {\n    background-image: url(ico_action.png); }\n  #content li.with-icon.use:before {\n    background-image: url(ico_use.png); }\n\n/* figures and callouts */\n.figure {\n  position: relative; }\n  .figure.pad-below {\n    margin-bottom: 20px; }\n  .figure .figure-callout {\n    position: absolute;\n    color: #fff;\n    font-weight: 500;\n    font-size: 16px;\n    line-height: 23px;\n    text-align: center;\n    background: transparent url(callout.png) no-repeat scroll 50% 50%;\n    padding-right: 2px;\n    width: 30px;\n    height: 29px;\n    z-index: 1000; }\n    .figure .figure-callout.top {\n      top: -9px; }\n    .figure .figure-callout.right {\n      right: -5px; }\n\n.figure-caption {\n  margin: 0 10px 20px 10px;\n  font-size: 14px;\n  line-height: 20px;\n  font-style: italic; }\n\n/* rows of figures */\n.figure-row {\n  font-size: 0;\n  line-height: 0;\n  /* to prevent space between figures */ }\n  .figure-row .figure {\n    display: inline-block;\n    vertical-align: top; }\n  .figure-row .figure + .figure {\n    margin-left: 10px;\n    /* reintroduce space between figures */ }\n\n/* video  containers */\n.framed-galaxynexus-land-span-13 {\n  background: transparent url(device_galaxynexus_blank_land_span13.png) no-repeat scroll top left;\n  padding: 42px 122px 62px 126px;\n  overflow: hidden; }\n  .framed-galaxynexus-land-span-13, .framed-galaxynexus-land-span-13 video, .framed-galaxynexus-land-span-13 img {\n    width: 512px;\n    height: 286px; }\n\n.framed-galaxynexus-port-span-9 {\n  background: transparent url(device_galaxynexus_blank_port_span9.png) no-repeat scroll top left;\n  padding: 95px 122px 107px 124px;\n  overflow: hidden; }\n  .framed-galaxynexus-port-span-9, .framed-galaxynexus-port-span-9 video, .framed-galaxynexus-port-span-9 img {\n    width: 274px;\n    height: 488px; }\n\n.framed-galaxynexus-port-span-5 {\n  background: transparent url(device_galaxynexus_blank_port_span5.png) no-repeat scroll top left;\n  padding: 75px 31px 76px 33px;\n  overflow: hidden; }\n  .framed-galaxynexus-port-span-5, .framed-galaxynexus-port-span-5 video, .framed-galaxynexus-port-span-5 img {\n    width: 216px;\n    height: 384px; }\n\n/* landing page disclosures */\n.landing-page-link {\n  text-decoration: none;\n  font-weight: 500;\n  color: #333333; }\n  .landing-page-link:after {\n    content: '';\n    background: transparent url(disclosure_right.png) no-repeat scroll top left;\n    width: 10px;\n    height: 10px;\n    display: inline-block;\n    margin-left: 5px; }\n\n/* tooltips */\n.tooltip-box {\n  position: absolute;\n  background-color: rgba(0, 0, 0, 0.9);\n  border-radius: 2px;\n  font-size: 14px;\n  line-height: 20px;\n  color: #fff;\n  padding: 6px 10px;\n  max-width: 250px;\n  z-index: 10000; }\n  .tooltip-box.below:after {\n    position: absolute;\n    content: '';\n    line-height: 0;\n    display: block;\n    top: -10px;\n    left: 5px;\n    border: 5px solid transparent;\n    border-bottom-color: rgba(0, 0, 0, 0.9); }\n\n/* video note */\n.video-instructions {\n  margin-top: 10px;\n  margin-bottom: 10px; }\n  .video-instructions:before {\n    content: '';\n    background: transparent url(ico_movie_inline.png) no-repeat scroll top left;\n    display: inline-block;\n    width: 12px;\n    height: 12px;\n    margin-right: 8px; }\n  .video-instructions:after {\n    content: 'Click to replay movie.'; }\n\n/* download buttons */\n.download-button {\n  display: block;\n  margin-bottom: 5px;\n  text-decoration: none;\n  background-color: #33b5e5;\n  color: #fff !important;\n  font-weight: 500;\n  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.12);\n  padding: 6px 12px;\n  border-radius: 2px; }\n  .download-button:hover, .download-button:focus {\n    background-color: #0099cc;\n    color: #fff !important; }\n  .download-button:active {\n    background-color: #006699; }\n\n/* UI tables and other things found in Writing style and Settings pattern */\n.ui-table {\n  width: 100%;\n  background: #282828;\n  color: #fff;\n  border-radius: 2px;\n  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);\n  border-collapse: separate; }\n  .ui-table th,\n  .ui-table td {\n    padding: 5px 10px; }\n  .ui-table thead th {\n    font-weight: 600; }\n  .ui-table tfoot td {\n    border-top: 1px solid #494949;\n    border-right: 1px solid #494949;\n    text-align: center; }\n    .ui-table tfoot td:last-child {\n      border-right: 0; }\n\n.layout-with-list-item-margins {\n  margin-left: 30px !important; }\n\n.emulate-content-left-padding {\n  margin-left: 10px; }\n\n.do-dont-label {\n  margin-bottom: 10px;\n  padding-left: 20px;\n  background: transparent none no-repeat scroll 0px 3px; }\n  .do-dont-label.bad {\n    background-image: url(ico_wrong.png); }\n  .do-dont-label.good {\n    background-image: url(ico_good.png); }\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/design/default.js",
    "content": "$(document).ready(function() {\n  // prep nav expandos\n  var pagePath = document.location.pathname;\n  if (pagePath.indexOf(SITE_ROOT) == 0) {\n    pagePath = pagePath.substr(SITE_ROOT.length);\n    if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {\n      pagePath += 'index.html';\n    }\n  }\n\n  if (SITE_ROOT.match(/\\.\\.\\//) || SITE_ROOT == '') {\n    // If running locally, SITE_ROOT will be a relative path, so account for that by\n    // finding the relative URL to this page. This will allow us to find links on the page\n    // leading back to this page.\n    var pathParts = pagePath.split('/');\n    var relativePagePathParts = [];\n    var upDirs = (SITE_ROOT.match(/(\\.\\.\\/)+/) || [''])[0].length / 3;\n    for (var i = 0; i < upDirs; i++) {\n      relativePagePathParts.push('..');\n    }\n    for (var i = 0; i < upDirs; i++) {\n      relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);\n    }\n    relativePagePathParts.push(pathParts[pathParts.length - 1]);\n    pagePath = relativePagePathParts.join('/');\n  } else {\n    // Otherwise the page path should be an absolute URL.\n    pagePath = SITE_ROOT + pagePath;\n  }\n\n  // select current page in sidenav and set up prev/next links if they exist\n  var $selNavLink = $('#nav').find('a[href=\"' + pagePath + '\"]');\n  if ($selNavLink.length) {\n    $selListItem = $selNavLink.closest('li');\n\n    $selListItem.addClass('selected');\n    $selListItem.closest('li.nav-section').addClass('expanded');\n\n    // set up prev links\n    var $prevLink = [];\n    var $prevListItem = $selListItem.prev('li');\n    if ($prevListItem.length) {\n      if ($prevListItem.hasClass('nav-section')) {\n        // jump to last topic of previous section\n        $prevLink = $prevListItem.find('a:last');\n      } else {\n        // jump to previous topic in this section\n        $prevLink = $prevListItem.find('a:eq(0)');\n      }\n    } else {\n      // jump to this section's index page (if it exists)\n      $prevLink = $selListItem.parents('li').find('a');\n    }\n\n    if ($prevLink.length) {\n      var prevHref = $prevLink.attr('href');\n      if (prevHref == SITE_ROOT + 'index.html') {\n        // Don't show Previous when it leads to the homepage\n        $('.prev-page-link').hide();\n      } else {\n        $('.prev-page-link').attr('href', prevHref).show();\n      }\n    } else {\n      $('.prev-page-link').hide();\n    }\n\n    // set up next links\n    var $nextLink = [];\n    if ($selListItem.hasClass('nav-section')) {\n      // we're on an index page, jump to the first topic\n      $nextLink = $selListItem.find('ul').find('a:eq(0)')\n    } else {\n      // jump to the next topic in this section (if it exists)\n      $nextLink = $selListItem.next('li').find('a:eq(0)');\n      if (!$nextLink.length) {\n        // no more topics in this section, jump to the first topic in the next section\n        $nextLink = $selListItem.parents('li').next('li.nav-section').find('a:eq(0)');\n      }\n    }\n    if ($nextLink.length) {\n      $('.next-page-link').attr('href', $nextLink.attr('href')).show();\n    } else {\n      $('.next-page-link').hide();\n    }\n  }\n\n  // Set up expand/collapse behavior\n  $('#nav li.nav-section').click(function() {\n    if ($(this).hasClass('expanded')) {\n      return;\n    }\n\n    // hide other\n    var $old = $('#nav li.nav-section.expanded');\n    if ($old.length) {\n      var $oldUl = $old.children('ul');\n      $oldUl.css('height', $oldUl.height() + 'px');\n      window.setTimeout(function() {\n        $oldUl\n            .addClass('animate-height')\n            .css('height', '');\n      }, 0);\n      $old.removeClass('expanded');\n    }\n\n    // show me\n    $(this).addClass('expanded');\n    var $ul = $(this).children('ul');\n    var expandedHeight = $ul.height();\n    $ul\n        .removeClass('animate-height')\n        .css('height', 0);\n    window.setTimeout(function() {\n      $ul\n          .addClass('animate-height')\n          .css('height', expandedHeight + 'px');\n    }, 0);\n  });\n\n  // Stop expand/collapse behavior when clicking on nav section links (since we're navigating away\n  // from the page)\n  $('.nav-section-header').find('a:eq(0)').click(function(evt) {\n    window.location.href = $(this).attr('href');\n    return false;\n  });\n\n  // Set up play-on-hover <video> tags.\n  $('video.play-on-hover').bind('click', function(){\n    $(this).get(0).load(); // in case the video isn't seekable\n    $(this).get(0).play();\n  });\n\n  // Set up tooltips\n  var TOOLTIP_MARGIN = 10;\n  $('acronym').each(function() {\n    var $target = $(this);\n    var $tooltip = $('<div>')\n        .addClass('tooltip-box')\n        .text($target.attr('title'))\n        .hide()\n        .appendTo('body');\n    $target.removeAttr('title');\n\n    $target.hover(function() {\n      // in\n      var targetRect = $target.offset();\n      targetRect.width = $target.width();\n      targetRect.height = $target.height();\n\n      $tooltip.css({\n        left: targetRect.left,\n        top: targetRect.top + targetRect.height + TOOLTIP_MARGIN\n      });\n      $tooltip.addClass('below');\n      $tooltip.show();\n    }, function() {\n      // out\n      $tooltip.hide();\n    });\n  });\n\n  // Set up <h2> deeplinks\n  $('h2').click(function() {\n    var id = $(this).attr('id');\n    if (id) {\n      document.location.hash = id;\n    }\n  });\n\n  // Set up fixed navbar\n  var navBarIsFixed = false;\n  $(window).scroll(function() {\n    var scrollTop = $(window).scrollTop();\n    var navBarShouldBeFixed = (scrollTop > (100 - 40));\n    if (navBarIsFixed != navBarShouldBeFixed) {\n      if (navBarShouldBeFixed) {\n        $('#nav')\n            .addClass('fixed')\n            .prependTo('#page-container');\n      } else {\n        $('#nav')\n            .removeClass('fixed')\n            .prependTo('#nav-container');\n      }\n      navBarIsFixed = navBarShouldBeFixed;\n    }\n  });\n});"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/jquery-history.js",
    "content": "/**\n * jQuery history event v0.1\n * Copyright (c) 2008 Tom Rodenberg <tarodenberg gmail com>\n * Licensed under the GPL (http://www.gnu.org/licenses/gpl.html) license.\n */\n(function($) {\n    var currentHash, previousNav, timer, hashTrim = /^.*#/;\n\n    var msie = {\n        iframe: null,\n        getDoc: function() {\n            return msie.iframe.contentWindow.document;\n        },\n        getHash: function() {\n            return msie.getDoc().location.hash;\n        },\n        setHash: function(hash) {\n            var d = msie.getDoc();\n            d.open();\n            d.close();\n            d.location.hash = hash;\n        }\n    };\n\n    var historycheck = function() {\n        var hash = msie.iframe ? msie.getHash() : location.hash;\n        if (hash != currentHash) {\n            currentHash = hash;\n            if (msie.iframe) {\n                location.hash = currentHash;\n            }\n            var current = $.history.getCurrent();\n            $.event.trigger('history', [current, previousNav]);\n            previousNav = current;\n        }\n    };\n\n    $.history = {\n        add: function(hash) {\n            hash = '#' + hash.replace(hashTrim, '');\n            if (currentHash != hash) {\n                var previous = $.history.getCurrent();\n                location.hash = currentHash = hash;\n                if (msie.iframe) {\n                    msie.setHash(currentHash);\n                }\n                $.event.trigger('historyadd', [$.history.getCurrent(), previous]);\n            }\n            if (!timer) {\n                timer = setInterval(historycheck, 100);\n            }\n        },\n        getCurrent: function() {\n            if (currentHash) {\n              return currentHash.replace(hashTrim, '');\n            } else { \n              return \"\"; \n            }\n        }\n    };\n\n    $.fn.history = function(fn) {\n        $(this).bind('history', fn);\n    };\n\n    $.fn.historyadd = function(fn) {\n        $(this).bind('historyadd', fn);\n    };\n\n    $(function() {\n        currentHash = location.hash;\n        if ($.browser.msie) {\n            msie.iframe = $('<iframe style=\"display:none\" src=\"javascript:false;\"></iframe>').prependTo('body')[0];\n            msie.setHash(currentHash);\n            currentHash = msie.getHash();\n        }\n    });\n})(jQuery);\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/microtemplate.js",
    "content": "// Simple JavaScript Templating\n// John Resig - http://ejohn.org/ - MIT Licensed\n(function(){\n  var cache = {};\n\n  this.tmpl = function tmpl(str, data){\n    // Figure out if we're getting a template, or if we need to\n    // load the template - and be sure to cache the result.\n    var fn = !/\\W/.test(str) ?\n      cache[str] = cache[str] ||\n        tmpl(document.getElementById(str).innerHTML) :\n\n      // Generate a reusable function that will serve as a template\n      // generator (and which will be cached).\n      new Function(\"obj\",\n        \"var p=[],print=function(){p.push.apply(p,arguments);};\" +\n\n        // Introduce the data as local variables using with(){}\n        \"with(obj){p.push('\" +\n\n        // Convert the template into pure JavaScript\n        str\n          .replace(/[\\r\\t\\n]/g, \" \")\n          .split(\"<%\").join(\"\\t\")\n          .replace(/((^|%>)[^\\t]*)'/g, \"$1\\r\")\n          .replace(/\\t=(.*?)%>/g, \"',$1,'\")\n          .split(\"\\t\").join(\"');\")\n          .split(\"%>\").join(\"p.push('\")\n          .split(\"\\r\").join(\"\\\\'\")\n      + \"');}return p.join('');\");\n\n    // Provide some basic currying to the user\n    return data ? fn( data ) : fn;\n  };\n})();"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/prettify.js",
    "content": "(function(){\nvar o=true,r=null,z=false;window.PR_SHOULD_USE_CONTINUATION=o;window.PR_TAB_WIDTH=8;window.PR_normalizedHtml=window.PR=window.prettyPrintOne=window.prettyPrint=void 0;window._pr_isIE6=function(){var N=navigator&&navigator.userAgent&&/\\bMSIE 6\\./.test(navigator.userAgent);window._pr_isIE6=function(){return N};return N};\nvar aa=\"!\",ba=\"!=\",ca=\"!==\",F=\"#\",da=\"%\",ea=\"%=\",G=\"&\",fa=\"&&\",ja=\"&&=\",ka=\"&=\",H=\"(\",la=\"*\",ma=\"*=\",na=\"+=\",oa=\",\",pa=\"-=\",qa=\"->\",ra=\"/\",sa=\"/=\",ta=\":\",ua=\"::\",va=\";\",I=\"<\",wa=\"<<\",xa=\"<<=\",ya=\"<=\",za=\"=\",Aa=\"==\",Ba=\"===\",J=\">\",Ca=\">=\",Da=\">>\",Ea=\">>=\",Fa=\">>>\",Ga=\">>>=\",Ha=\"?\",Ia=\"@\",L=\"[\",M=\"^\",Ta=\"^=\",Ua=\"^^\",Va=\"^^=\",Wa=\"{\",O=\"|\",Xa=\"|=\",Ya=\"||\",Za=\"||=\",$a=\"~\",ab=\"break\",bb=\"case\",cb=\"continue\",db=\"delete\",eb=\"do\",fb=\"else\",gb=\"finally\",hb=\"instanceof\",ib=\"return\",jb=\"throw\",kb=\"try\",lb=\"typeof\",\nmb=\"(?:^^|[+-]\",nb=\"\\\\$1\",ob=\")\\\\s*\",pb=\"&amp;\",qb=\"&lt;\",rb=\"&gt;\",sb=\"&quot;\",tb=\"&#\",ub=\"x\",vb=\"'\",wb='\"',xb=\" \",yb=\"XMP\",zb=\"</\",Ab='=\"',P=\"\",Q=\"\\\\\",Bb=\"b\",Cb=\"t\",Db=\"n\",Eb=\"v\",Fb=\"f\",Gb=\"r\",Hb=\"u\",Ib=\"0\",Jb=\"1\",Kb=\"2\",Lb=\"3\",Mb=\"4\",Nb=\"5\",Ob=\"6\",Pb=\"7\",Qb=\"\\\\x0\",Rb=\"\\\\x\",Sb=\"-\",Tb=\"]\",Ub=\"\\\\\\\\u[0-9A-Fa-f]{4}|\\\\\\\\x[0-9A-Fa-f]{2}|\\\\\\\\[0-3][0-7]{0,2}|\\\\\\\\[0-7]{1,2}|\\\\\\\\[\\\\s\\\\S]|-|[^-\\\\\\\\]\",R=\"g\",Vb=\"\\\\B\",Wb=\"\\\\b\",Xb=\"\\\\D\",Yb=\"\\\\d\",Zb=\"\\\\S\",$b=\"\\\\s\",ac=\"\\\\W\",bc=\"\\\\w\",cc=\"(?:\\\\[(?:[^\\\\x5C\\\\x5D]|\\\\\\\\[\\\\s\\\\S])*\\\\]|\\\\\\\\u[A-Fa-f0-9]{4}|\\\\\\\\x[A-Fa-f0-9]{2}|\\\\\\\\[0-9]+|\\\\\\\\[^ux0-9]|\\\\(\\\\?[:!=]|[\\\\(\\\\)\\\\^]|[^\\\\x5B\\\\x5C\\\\(\\\\)\\\\^]+)\",\ndc=\"(?:\",ec=\")\",fc=\"gi\",gc=\"PRE\",hc='<!DOCTYPE foo PUBLIC \"foo bar\">\\n<foo />',ic=\"\\t\",jc=\"\\n\",kc=\"[^<]+|<!--[\\\\s\\\\S]*?--\\>|<!\\\\[CDATA\\\\[[\\\\s\\\\S]*?\\\\]\\\\]>|</?[a-zA-Z][^>]*>|<\",lc=\"nocode\",mc=' $1=\"$2$3$4\"',S=\"pln\",nc=\"string\",T=\"lang-\",oc=\"src\",U=\"str\",pc=\"'\\\"\",qc=\"'\\\"`\",rc=\"\\\"'\",V=\"com\",sc=\"lang-regex\",tc=\"(/(?=[^/*])(?:[^/\\\\x5B\\\\x5C]|\\\\x5C[\\\\s\\\\S]|\\\\x5B(?:[^\\\\x5C\\\\x5D]|\\\\x5C[\\\\s\\\\S])*(?:\\\\x5D|$))+/)\",uc=\"kwd\",vc=\"^(?:\",wc=\")\\\\b\",xc=\" \\r\\n\\t\\u00a0\",yc=\"lit\",zc=\"typ\",Ac=\"0123456789\",Y=\"pun\",Bc=\"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename typeof using virtual wchar_t where break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params partial readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try debugger eval export function get null set undefined var with Infinity NaN caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END break continue do else for if return while case done elif esac eval fi function in local set then until \",\nCc=\"</span>\",Dc='<span class=\"',Ec='\">',Fc=\"$1&nbsp;\",Gc=\"&nbsp;<br />\",Hc=\"<br />\",Ic=\"console\",Jc=\"cannot override language handler %s\",Kc=\"default-markup\",Lc=\"default-code\",Mc=\"dec\",Z=\"lang-js\",$=\"lang-css\",Nc=\"lang-in.tag\",Oc=\"htm\",Pc=\"html\",Qc=\"mxml\",Rc=\"xhtml\",Sc=\"xml\",Tc=\"xsl\",Uc=\" \\t\\r\\n\",Vc=\"atv\",Wc=\"tag\",Xc=\"atn\",Yc=\"lang-uq.val\",Zc=\"in.tag\",$c=\"uq.val\",ad=\"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename typeof using virtual wchar_t where \",\nbd=\"c\",cd=\"cc\",dd=\"cpp\",ed=\"cxx\",fd=\"cyc\",gd=\"m\",hd=\"null true false\",id=\"json\",jd=\"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params partial readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var \",\nkd=\"cs\",ld=\"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient \",md=\"java\",nd=\"break continue do else for if return while case done elif esac eval fi function in local set then until \",\nod=\"bsh\",pd=\"csh\",qd=\"sh\",rd=\"break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None \",sd=\"cv\",td=\"py\",ud=\"caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END \",vd=\"perl\",wd=\"pl\",xd=\"pm\",yd=\"break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END \",\nzd=\"rb\",Ad=\"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try debugger eval export function get null set undefined var with Infinity NaN \",Bd=\"js\",Cd=\"regex\",Dd=\"pre\",Ed=\"code\",Fd=\"xmp\",Gd=\"prettyprint\",Hd=\"class\",Id=\"br\",Jd=\"\\r\";\n(function(){var N=function(){for(var a=[aa,ba,ca,F,da,ea,G,fa,ja,ka,H,la,ma,na,oa,pa,qa,ra,sa,ta,ua,va,I,wa,xa,ya,za,Aa,Ba,J,Ca,Da,Ea,Fa,Ga,Ha,Ia,L,M,Ta,Ua,Va,Wa,O,Xa,Ya,Za,$a,ab,bb,cb,db,eb,fb,gb,hb,ib,jb,kb,lb],b=mb,c=0;c<a.length;++c)b+=O+a[c].replace(/([^=<>:&a-z])/g,nb);b+=ob;return b}(),Ja=/&/g,Ka=/</g,La=/>/g,Kd=/\\\"/g;function Ld(a){return a.replace(Ja,pb).replace(Ka,qb).replace(La,rb).replace(Kd,sb)}function ga(a){return a.replace(Ja,pb).replace(Ka,qb).replace(La,rb)}var Md=/&lt;/g,Nd=/&gt;/g,\nOd=/&apos;/g,Pd=/&quot;/g,Qd=/&amp;/g,Rd=/&nbsp;/g;function Sd(a){var b=a.indexOf(G);if(b<0)return a;for(--b;(b=a.indexOf(tb,b+1))>=0;){var c=a.indexOf(va,b);if(c>=0){var d=a.substring(b+3,c),g=10;if(d&&d.charAt(0)===ub){d=d.substring(1);g=16}var i=parseInt(d,g);isNaN(i)||(a=a.substring(0,b)+String.fromCharCode(i)+a.substring(c+1))}}return a.replace(Md,I).replace(Nd,J).replace(Od,vb).replace(Pd,wb).replace(Qd,G).replace(Rd,xb)}function Ma(a){return yb===a.tagName}function W(a,b){switch(a.nodeType){case 1:var c=\na.tagName.toLowerCase();b.push(I,c);for(var d=0;d<a.attributes.length;++d){var g=a.attributes[d];if(g.specified){b.push(xb);W(g,b)}}b.push(J);for(var i=a.firstChild;i;i=i.nextSibling)W(i,b);if(a.firstChild||!/^(?:br|link|img)$/.test(c))b.push(zb,c,J);break;case 2:b.push(a.name.toLowerCase(),Ab,Ld(a.value),wb);break;case 3:case 4:b.push(ga(a.nodeValue));break}}function Na(a){for(var b=0,c=z,d=z,g=0,i=a.length;g<i;++g){var m=a[g];if(m.ignoreCase)d=o;else if(/[a-z]/i.test(m.source.replace(/\\\\u[0-9a-f]{4}|\\\\x[0-9a-f]{2}|\\\\[^ux]/gi,\nP))){c=o;d=z;break}}function l(j){if(j.charAt(0)!==Q)return j.charCodeAt(0);switch(j.charAt(1)){case Bb:return 8;case Cb:return 9;case Db:return 10;case Eb:return 11;case Fb:return 12;case Gb:return 13;case Hb:case ub:return parseInt(j.substring(2),16)||j.charCodeAt(1);case Ib:case Jb:case Kb:case Lb:case Mb:case Nb:case Ob:case Pb:return parseInt(j.substring(1),8);default:return j.charCodeAt(1)}}function n(j){if(j<32)return(j<16?Qb:Rb)+j.toString(16);var f=String.fromCharCode(j);if(f===Q||f===Sb||\nf===L||f===Tb)f=Q+f;return f}function q(j){for(var f=j.substring(1,j.length-1).match(new RegExp(Ub,R)),s=[],k=[],h=f[0]===M,e=h?1:0,p=f.length;e<p;++e){var t=f[e];switch(t){case Vb:case Wb:case Xb:case Yb:case Zb:case $b:case ac:case bc:s.push(t);continue}var u=l(t),x;if(e+2<p&&Sb===f[e+1]){x=l(f[e+2]);e+=2}else x=u;k.push([u,x]);if(!(x<65||u>122)){x<65||u>90||k.push([Math.max(65,u)|32,Math.min(x,90)|32]);x<97||u>122||k.push([Math.max(97,u)&-33,Math.min(x,122)&-33])}}k.sort(function(Oa,Pa){return Oa[0]-\nPa[0]||Pa[1]-Oa[1]});var B=[],E=[NaN,NaN];for(e=0;e<k.length;++e){var A=k[e];if(A[0]<=E[1]+1)E[1]=Math.max(E[1],A[1]);else B.push(E=A)}var D=[L];h&&D.push(M);D.push.apply(D,s);for(e=0;e<B.length;++e){A=B[e];D.push(n(A[0]));if(A[1]>A[0]){A[1]+1>A[0]&&D.push(Sb);D.push(n(A[1]))}}D.push(Tb);return D.join(P)}function v(j){var f=j.source.match(new RegExp(cc,R)),s=f.length,k=[],h,e=0;for(h=0;e<s;++e){var p=f[e];if(p===H)++h;else if(Q===p.charAt(0)){var t=+p.substring(1);if(t&&t<=h)k[t]=-1}}for(e=1;e<k.length;++e)if(-1===\nk[e])k[e]=++b;for(h=e=0;e<s;++e){p=f[e];if(p===H){++h;if(k[h]===undefined)f[e]=dc}else if(Q===p.charAt(0))if((t=+p.substring(1))&&t<=h)f[e]=Q+k[h]}for(h=e=0;e<s;++e)if(M===f[e]&&M!==f[e+1])f[e]=P;if(j.ignoreCase&&c)for(e=0;e<s;++e){p=f[e];var u=p.charAt(0);if(p.length>=2&&u===L)f[e]=q(p);else if(u!==Q)f[e]=p.replace(/[a-zA-Z]/g,function(x){var B=x.charCodeAt(0);return L+String.fromCharCode(B&-33,B|32)+Tb})}return f.join(P)}var w=[];g=0;for(i=a.length;g<i;++g){m=a[g];if(m.global||m.multiline)throw new Error(P+\nm);w.push(dc+v(m)+ec)}return new RegExp(w.join(O),d?fc:R)}var ha=r;function Td(a){if(r===ha){var b=document.createElement(gc);b.appendChild(document.createTextNode(hc));ha=!/</.test(b.innerHTML)}if(ha){var c=a.innerHTML;if(Ma(a))c=ga(c);return c}for(var d=[],g=a.firstChild;g;g=g.nextSibling)W(g,d);return d.join(P)}function Ud(a){var b=0;return function(c){for(var d=r,g=0,i=0,m=c.length;i<m;++i){var l=c.charAt(i);switch(l){case ic:d||(d=[]);d.push(c.substring(g,i));var n=a-b%a;for(b+=n;n>=0;n-=\"                \".length)d.push(\"                \".substring(0,\nn));g=i+1;break;case jc:b=0;break;default:++b}}if(!d)return c;d.push(c.substring(g));return d.join(P)}}var Vd=new RegExp(kc,R),Wd=/^<\\!--/,Xd=/^<\\[CDATA\\[/,Yd=/^<br\\b/i,Qa=/^<(\\/?)([a-zA-Z]+)/;function Zd(a){var b=a.match(Vd),c=[],d=0,g=[];if(b)for(var i=0,m=b.length;i<m;++i){var l=b[i];if(l.length>1&&l.charAt(0)===I){if(!Wd.test(l))if(Xd.test(l)){c.push(l.substring(9,l.length-3));d+=l.length-12}else if(Yd.test(l)){c.push(jc);++d}else if(l.indexOf(lc)>=0&&$d(l)){var n=l.match(Qa)[2],q=1,v;v=i+1;a:for(;v<\nm;++v){var w=b[v].match(Qa);if(w&&w[2]===n)if(w[1]===ra){if(--q===0)break a}else++q}if(v<m){g.push(d,b.slice(i,v+1).join(P));i=v}else g.push(d,l)}else g.push(d,l)}else{var j=Sd(l);c.push(j);d+=j.length}}return{source:c.join(P),tags:g}}function $d(a){return!!a.replace(/\\s(\\w+)\\s*=\\s*(?:\\\"([^\\\"]*)\\\"|'([^\\']*)'|(\\S+))/g,mc).match(/[cC][lL][aA][sS][sS]=\\\"[^\\\"]*\\bnocode\\b/)}function ia(a,b,c,d){if(b){var g={source:b,b:a};c(g);d.push.apply(d,g.c)}}function K(a,b){var c={},d;(function(){for(var m=a.concat(b),\nl=[],n={},q=0,v=m.length;q<v;++q){var w=m[q],j=w[3];if(j)for(var f=j.length;--f>=0;)c[j.charAt(f)]=w;var s=w[1],k=P+s;if(!n.hasOwnProperty(k)){l.push(s);n[k]=r}}l.push(/[\\0-\\uffff]/);d=Na(l)})();var g=b.length,i=function(m){for(var l=m.source,n=m.b,q=[n,S],v=0,w=l.match(d)||[],j={},f=0,s=w.length;f<s;++f){var k=w[f],h=j[k],e,p;if(typeof h===nc)p=z;else{var t=c[k.charAt(0)];if(t){e=k.match(t[1]);h=t[0]}else{for(var u=0;u<g;++u){t=b[u];if(e=k.match(t[1])){h=t[0];break}}e||(h=S)}if((p=h.length>=5&&T===\nh.substring(0,5))&&!(e&&e[1])){p=z;h=oc}p||(j[k]=h)}var x=v;v+=k.length;if(p){var B=e[1],E=k.indexOf(B),A=E+B.length,D=h.substring(5);ia(n+x,k.substring(0,E),i,q);ia(n+x+E,B,Ra(D,B),q);ia(n+x+A,k.substring(A),i,q)}else q.push(n+x,h)}m.c=q};return i}function C(a){var b=[],c=[];if(a.tripleQuotedStrings)b.push([U,/^(?:\\'\\'\\'(?:[^\\'\\\\]|\\\\[\\s\\S]|\\'{1,2}(?=[^\\']))*(?:\\'\\'\\'|$)|\\\"\\\"\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S]|\\\"{1,2}(?=[^\\\"]))*(?:\\\"\\\"\\\"|$)|\\'(?:[^\\\\\\']|\\\\[\\s\\S])*(?:\\'|$)|\\\"(?:[^\\\\\\\"]|\\\\[\\s\\S])*(?:\\\"|$))/,r,pc]);\nelse a.multiLineStrings?b.push([U,/^(?:\\'(?:[^\\\\\\']|\\\\[\\s\\S])*(?:\\'|$)|\\\"(?:[^\\\\\\\"]|\\\\[\\s\\S])*(?:\\\"|$)|\\`(?:[^\\\\\\`]|\\\\[\\s\\S])*(?:\\`|$))/,r,qc]):b.push([U,/^(?:\\'(?:[^\\\\\\'\\r\\n]|\\\\.)*(?:\\'|$)|\\\"(?:[^\\\\\\\"\\r\\n]|\\\\.)*(?:\\\"|$))/,r,rc]);if(a.hashComments)a.cStyleComments?b.push([V,/^#(?:[^\\r\\n\\/]|\\/(?!\\*)|\\/\\*[^\\r\\n]*?\\*\\/)*/,r,F]):b.push([V,/^#[^\\r\\n]*/,r,F]);if(a.cStyleComments){c.push([V,/^\\/\\/[^\\r\\n]*/,r]);c.push([V,/^\\/\\*[\\s\\S]*?(?:\\*\\/|$)/,r])}a.regexLiterals&&c.push([sc,new RegExp(M+N+tc)]);var d=\na.keywords.replace(/^\\s+|\\s+$/g,P);d.length&&c.push([uc,new RegExp(vc+d.replace(/\\s+/g,O)+wc),r]);b.push([S,/^\\s+/,r,xc]);c.push([yc,/^@[a-z_$][a-z_$@0-9]*/i,r,Ia],[zc,/^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/,r],[S,/^[a-z_$][a-z_$@0-9]*/i,r],[yc,/^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*/i,r,Ac],[Y,/^.[^\\s\\w\\.$@\\'\\\"\\`\\/\\#]*/,r]);return K(b,c)}var ae=C({keywords:Bc,hashComments:o,cStyleComments:o,multiLineStrings:o,regexLiterals:o});function be(a){var b=a.source,c=a.f,d=a.c,\ng=[],i=0,m=r,l=r,n=0,q=0,v=Ud(window.PR_TAB_WIDTH),w=/([\\r\\n ]) /g,j=/(^| ) /gm,f=/\\r\\n?|\\n/g,s=/[ \\r\\n]$/,k=o;function h(p){if(p>i){if(m&&m!==l){g.push(Cc);m=r}if(!m&&l){m=l;g.push(Dc,m,Ec)}var t=ga(v(b.substring(i,p))).replace(k?j:w,Fc);k=s.test(t);var u=window._pr_isIE6()?Gc:Hc;g.push(t.replace(f,u));i=p}}for(;1;){var e;if(e=n<c.length?q<d.length?c[n]<=d[q]:o:z){h(c[n]);if(m){g.push(Cc);m=r}g.push(c[n+1]);n+=2}else if(q<d.length){h(d[q]);l=d[q+1];q+=2}else break}h(b.length);m&&g.push(Cc);a.a=g.join(P)}\nvar X={};function y(a,b){for(var c=b.length;--c>=0;){var d=b[c];if(X.hasOwnProperty(d))Ic in window&&console.i(Jc,d);else X[d]=a}}function Ra(a,b){a&&X.hasOwnProperty(a)||(a=/^\\s*</.test(b)?Kc:Lc);return X[a]}y(ae,[Lc]);y(K([],[[S,/^[^<?]+/],[Mc,/^<!\\w[^>]*(?:>|$)/],[V,/^<\\!--[\\s\\S]*?(?:-\\->|$)/],[T,/^<\\?([\\s\\S]+?)(?:\\?>|$)/],[T,/^<%([\\s\\S]+?)(?:%>|$)/],[Y,/^(?:<[%?]|[%?]>)/],[T,/^<xmp\\b[^>]*>([\\s\\S]+?)<\\/xmp\\b[^>]*>/i],[Z,/^<script\\b[^>]*>([\\s\\S]+?)<\\/script\\b[^>]*>/i],[$,/^<style\\b[^>]*>([\\s\\S]+?)<\\/style\\b[^>]*>/i],\n[Nc,/^(<\\/?[a-z][^<>]*>)/i]]),[Kc,Oc,Pc,Qc,Rc,Sc,Tc]);y(K([[S,/^[\\s]+/,r,Uc],[Vc,/^(?:\\\"[^\\\"]*\\\"?|\\'[^\\']*\\'?)/,r,rc]],[[Wc,/^^<\\/?[a-z](?:[\\w.:-]*\\w)?|\\/?>$/i],[Xc,/^(?!style\\b|on)[a-z](?:[\\w:-]*\\w)?/],[Yc,/^=\\s*([^>\\'\\\"\\s]*(?:[^>\\'\\\"\\s\\/]|\\/(?=\\s)))/],[Y,/^[=<>\\/]+/],[Z,/^on\\w+\\s*=\\s*\\\"([^\\\"]+)\\\"/i],[Z,/^on\\w+\\s*=\\s*\\'([^\\']+)\\'/i],[Z,/^on\\w+\\s*=\\s*([^\\\"\\'>\\s]+)/i],[$,/^sty\\w+\\s*=\\s*\\\"([^\\\"]+)\\\"/i],[$,/^sty\\w+\\s*=\\s*\\'([^\\']+)\\'/i],[$,/^sty\\w+\\s*=\\s*([^\\\"\\'>\\s]+)/i]]),[Zc]);y(K([],[[Vc,/^[\\s\\S]+/]]),\n[$c]);y(C({keywords:ad,hashComments:o,cStyleComments:o}),[bd,cd,dd,ed,fd,gd]);y(C({keywords:hd}),[id]);y(C({keywords:jd,hashComments:o,cStyleComments:o}),[kd]);y(C({keywords:ld,cStyleComments:o}),[md]);y(C({keywords:nd,hashComments:o,multiLineStrings:o}),[od,pd,qd]);y(C({keywords:rd,hashComments:o,multiLineStrings:o,tripleQuotedStrings:o}),[sd,td]);y(C({keywords:ud,hashComments:o,multiLineStrings:o,regexLiterals:o}),[vd,wd,xd]);y(C({keywords:yd,hashComments:o,multiLineStrings:o,regexLiterals:o}),\n[zd]);y(C({keywords:Ad,cStyleComments:o,regexLiterals:o}),[Bd]);y(K([],[[U,/^[\\s\\S]+/]]),[Cd]);function Sa(a){var b=a.e,c=a.d;a.a=b;try{var d=Zd(b),g=d.source;a.source=g;a.b=0;a.f=d.tags;Ra(c,g)(a);be(a)}catch(i){if(Ic in window){console.log(i);console.h()}}}function ce(a,b){var c={e:a,d:b};Sa(c);return c.a}function de(a){for(var b=window._pr_isIE6(),c=[document.getElementsByTagName(Dd),document.getElementsByTagName(Ed),document.getElementsByTagName(Fd)],d=[],g=0;g<c.length;++g)for(var i=0,m=c[g].length;i<\nm;++i)d.push(c[g][i]);c=r;var l=Date;l.now||(l={now:function(){return(new Date).getTime()}});var n=0,q;function v(){for(var j=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;n<d.length&&l.now()<j;n++){var f=d[n];if(f.className&&f.className.indexOf(Gd)>=0){var s=f.className.match(/\\blang-(\\w+)\\b/);if(s)s=s[1];for(var k=z,h=f.parentNode;h;h=h.parentNode)if((h.tagName===Dd||h.tagName===Ed||h.tagName===Fd)&&h.className&&h.className.indexOf(Gd)>=0){k=o;break}if(!k){var e=Td(f);e=e.replace(/(?:\\r\\n?|\\n)$/,\nP);q={e:e,d:s,g:f};Sa(q);w()}}}if(n<d.length)setTimeout(v,250);else a&&a()}function w(){var j=q.a;if(j){var f=q.g;if(Ma(f)){for(var s=document.createElement(gc),k=0;k<f.attributes.length;++k){var h=f.attributes[k];if(h.specified){var e=h.name.toLowerCase();if(e===Hd)s.className=h.value;else s.setAttribute(h.name,h.value)}}s.innerHTML=j;f.parentNode.replaceChild(s,f);f=s}else f.innerHTML=j;if(b&&f.tagName===gc)for(var p=f.getElementsByTagName(Id),t=p.length;--t>=0;){var u=p[t];u.parentNode.replaceChild(document.createTextNode(Jd),\nu)}}}v()}window.PR_normalizedHtml=W;window.prettyPrintOne=ce;window.prettyPrint=de;window.PR={combinePrefixPatterns:Na,createSimpleLexer:K,registerLangHandler:y,sourceDecorator:C,PR_ATTRIB_NAME:Xc,PR_ATTRIB_VALUE:Vc,PR_COMMENT:V,PR_DECLARATION:Mc,PR_KEYWORD:uc,PR_LITERAL:yc,PR_NOCODE:lc,PR_PLAIN:S,PR_PUNCTUATION:Y,PR_SOURCE:oc,PR_STRING:U,PR_TAG:Wc,PR_TYPE:zc}})();\n})()\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/search_autocomplete.js",
    "content": "var gSelectedIndex = -1;\nvar gSelectedID = -1;\nvar gMatches = new Array();\nvar gLastText = \"\";\nvar ROW_COUNT = 20;\nvar gInitialized = false;\nvar DEFAULT_TEXT = \"search developer docs\";\n\nfunction set_row_selected(row, selected)\n{\n    var c1 = row.cells[0];\n  //  var c2 = row.cells[1];\n    if (selected) {\n        c1.className = \"jd-autocomplete jd-selected\";\n  //      c2.className = \"jd-autocomplete jd-selected jd-linktype\";\n    } else {\n        c1.className = \"jd-autocomplete\";\n  //      c2.className = \"jd-autocomplete jd-linktype\";\n    }\n}\n\nfunction set_row_values(toroot, row, match)\n{\n    var link = row.cells[0].childNodes[0];\n    link.innerHTML = match.__hilabel || match.label;\n    link.href = toroot + match.link\n  //  row.cells[1].innerHTML = match.type;\n}\n\nfunction sync_selection_table(toroot)\n{\n    var filtered = document.getElementById(\"search_filtered\");\n    var r; //TR DOM object\n    var i; //TR iterator\n    gSelectedID = -1;\n\n    filtered.onmouseover = function() { \n        if(gSelectedIndex >= 0) {\n          set_row_selected(this.rows[gSelectedIndex], false);\n          gSelectedIndex = -1;\n        }\n    }\n\n    //initialize the table; draw it for the first time (but not visible).\n    if (!gInitialized) {\n        for (i=0; i<ROW_COUNT; i++) {\n            var r = filtered.insertRow(-1);\n            var c1 = r.insertCell(-1);\n        //    var c2 = r.insertCell(-1);\n            c1.className = \"jd-autocomplete\";\n         //   c2.className = \"jd-autocomplete jd-linktype\";\n            var link = document.createElement(\"a\");\n            c1.onmousedown = function() {\n                window.location = this.firstChild.getAttribute(\"href\");\n            }\n            c1.onmouseover = function() {\n                this.className = this.className + \" jd-selected\";\n            }\n            c1.onmouseout = function() {\n                this.className = \"jd-autocomplete\";\n            }\n            c1.appendChild(link);\n        }\n  /*      var r = filtered.insertRow(-1);\n        var c1 = r.insertCell(-1);\n        c1.className = \"jd-autocomplete jd-linktype\";\n        c1.colSpan = 2; */\n        gInitialized = true;\n    }\n\n    //if we have results, make the table visible and initialize result info\n    if (gMatches.length > 0) {\n        document.getElementById(\"search_filtered_div\").className = \"showing\";\n        var N = gMatches.length < ROW_COUNT ? gMatches.length : ROW_COUNT;\n        for (i=0; i<N; i++) {\n            r = filtered.rows[i];\n            r.className = \"show-row\";\n            set_row_values(toroot, r, gMatches[i]);\n            set_row_selected(r, i == gSelectedIndex);\n            if (i == gSelectedIndex) {\n                gSelectedID = gMatches[i].id;\n            }\n        }\n        //start hiding rows that are no longer matches\n        for (; i<ROW_COUNT; i++) {\n            r = filtered.rows[i];\n            r.className = \"no-display\";\n        }\n        //if there are more results we're not showing, so say so.\n/*      if (gMatches.length > ROW_COUNT) {\n            r = filtered.rows[ROW_COUNT];\n            r.className = \"show-row\";\n            c1 = r.cells[0];\n            c1.innerHTML = \"plus \" + (gMatches.length-ROW_COUNT) + \" more\"; \n        } else {\n            filtered.rows[ROW_COUNT].className = \"hide-row\";\n        }*/\n    //if we have no results, hide the table\n    } else {\n        document.getElementById(\"search_filtered_div\").className = \"no-display\";\n    }\n}\n\nfunction search_changed(e, kd, toroot)\n{\n    var search = document.getElementById(\"search_autocomplete\");\n    var text = search.value.replace(/(^ +)|( +$)/g, '');\n\n    // 13 = enter\n    if (e.keyCode == 13) {\n        document.getElementById(\"search_filtered_div\").className = \"no-display\";\n        if (kd && gSelectedIndex >= 0) {\n            window.location = toroot + gMatches[gSelectedIndex].link;\n            return false;\n        } else if (gSelectedIndex < 0) {\n            return true;\n        }\n    }\n    // 38 -- arrow up\n    else if (kd && (e.keyCode == 38)) {\n        if (gSelectedIndex >= 0) {\n            gSelectedIndex--;\n        }\n        sync_selection_table(toroot);\n        return false;\n    }\n    // 40 -- arrow down\n    else if (kd && (e.keyCode == 40)) {\n        if (gSelectedIndex < gMatches.length-1\n                        && gSelectedIndex < ROW_COUNT-1) {\n            gSelectedIndex++;\n        }\n        sync_selection_table(toroot);\n        return false;\n    }\n    else if (!kd) {\n        gMatches = new Array();\n        matchedCount = 0;\n        gSelectedIndex = -1;\n        for (var i=0; i<DATA.length; i++) {\n            var s = DATA[i];\n            if (text.length != 0 &&\n                  s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {\n                gMatches[matchedCount] = s;\n                matchedCount++;\n            }\n        }\n        rank_autocomplete_results(text);\n        for (var i=0; i<gMatches.length; i++) {\n            var s = gMatches[i];\n            if (gSelectedID == s.id) {\n                gSelectedIndex = i;\n            }\n        }\n        highlight_autocomplete_result_labels(text);\n        sync_selection_table(toroot);\n        return true; // allow the event to bubble up to the search api\n    }\n}\n\nfunction rank_autocomplete_results(query) {\n    query = query || '';\n    if (!gMatches || !gMatches.length)\n      return;\n\n    // helper function that gets the last occurence index of the given regex\n    // in the given string, or -1 if not found\n    var _lastSearch = function(s, re) {\n      if (s == '')\n        return -1;\n      var l = -1;\n      var tmp;\n      while ((tmp = s.search(re)) >= 0) {\n        if (l < 0) l = 0;\n        l += tmp;\n        s = s.substr(tmp + 1);\n      }\n      return l;\n    };\n\n    // helper function that counts the occurrences of a given character in\n    // a given string\n    var _countChar = function(s, c) {\n      var n = 0;\n      for (var i=0; i<s.length; i++)\n        if (s.charAt(i) == c) ++n;\n      return n;\n    };\n\n    var queryLower = query.toLowerCase();\n    var queryAlnum = (queryLower.match(/\\w+/) || [''])[0];\n    var partPrefixAlnumRE = new RegExp('\\\\b' + queryAlnum);\n    var partExactAlnumRE = new RegExp('\\\\b' + queryAlnum + '\\\\b');\n\n    var _resultScoreFn = function(result) {\n        // scores are calculated based on exact and prefix matches,\n        // and then number of path separators (dots) from the last\n        // match (i.e. favoring classes and deep package names)\n        var score = 1.0;\n        var labelLower = result.label.toLowerCase();\n        var t;\n        t = _lastSearch(labelLower, partExactAlnumRE);\n        if (t >= 0) {\n            // exact part match\n            var partsAfter = _countChar(labelLower.substr(t + 1), '.');\n            score *= 200 / (partsAfter + 1);\n        } else {\n            t = _lastSearch(labelLower, partPrefixAlnumRE);\n            if (t >= 0) {\n                // part prefix match\n                var partsAfter = _countChar(labelLower.substr(t + 1), '.');\n                score *= 20 / (partsAfter + 1);\n            }\n        }\n\n        return score;\n    };\n\n    for (var i=0; i<gMatches.length; i++) {\n        gMatches[i].__resultScore = _resultScoreFn(gMatches[i]);\n    }\n\n    gMatches.sort(function(a,b){\n        var n = b.__resultScore - a.__resultScore;\n        if (n == 0) // lexicographical sort if scores are the same\n            n = (a.label < b.label) ? -1 : 1;\n        return n;\n    });\n}\n\nfunction highlight_autocomplete_result_labels(query) {\n    query = query || '';\n    if (!gMatches || !gMatches.length)\n      return;\n\n    var queryLower = query.toLowerCase();\n    var queryAlnumDot = (queryLower.match(/[\\w\\.]+/) || [''])[0];\n    var queryRE = new RegExp(\n        '(' + queryAlnumDot.replace(/\\./g, '\\\\.') + ')', 'ig');\n    for (var i=0; i<gMatches.length; i++) {\n        gMatches[i].__hilabel = gMatches[i].label.replace(\n            queryRE, '<b>$1</b>');\n    }\n}\n\nfunction search_focus_changed(obj, focused)\n{\n    if (focused) {\n        if(obj.value == DEFAULT_TEXT){\n            obj.value = \"\";\n            obj.style.color=\"#000000\";\n        }\n    } else {\n        if(obj.value == \"\"){\n          obj.value = DEFAULT_TEXT;\n          obj.style.color=\"#aaaaaa\";\n        }\n        document.getElementById(\"search_filtered_div\").className = \"no-display\";\n    }\n}\n\nfunction submit_search() {\n  var query = document.getElementById('search_autocomplete').value;\n  document.location = toRoot + 'search.html#q=' + query + '&t=0';\n  return false;\n}\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/style.css",
    "content": ".jd-toptitle {\n    padding-left: 6px;\n    margin-bottom: 30px;\n    font-size: 160%;\n    font-weight: bold;\n}\n\ndiv#jd-content table {\n    border: none;\n}\n\ndiv#jd-content td, div#jd-content th {\n    font-size: small;\n}\n\ndiv#jd-content table.jd-linktable {\n    margin-top: 3px;\n    border-spacing: 0;\n}\n\ndiv#jd-content p.jd-deprecated-warning {\n    margin-top: 0;\n    margin-bottom: 10px;\n}\n\ndiv#jd-content table.jd-linktable th {\n    vertical-align: top;\n    text-align: left;\n    padding-top: 2px;\n    padding-bottom: 2px;\n    padding-left: 7px;\n    padding-right: 7px;\n    border: none;\n    border-top: 1px solid #d2d7d0;\n    background-color: #F7FCF4;\n}\n\ndiv#jd-content table.jd-linktable td {\n    border: none;\n}\n\ndiv#jd-content table.jd-linktable td  p {\n    padding: 0;\n    margin: 0;\n    line-height: 110%;\n}\n\ndiv#jd-content table.jd-linktable .jd-linkcol {\n    vertical-align: top;\n    padding-top: 3px;\n    padding-bottom: 0;\n    padding-left: 7px;\n    padding-right: 7px;\n    border-top: 1px solid #d2d7d0;\n    background-color: #E5F1E0;\n    line-height: 110%;\n}\n\ndiv#jd-content table.jd-linktable .jd-descrcol {\n    vertical-align: top;\n    padding-top: 3px;\n    padding-bottom: 0;\n    padding-left: 7px;\n    padding-right: 7px;\n    border-top: 1px solid #d2d7d0;\n    background-color: #F7FCF4;\n    line-height: 110%;\n}\n\ndiv#jd-content table.jd-linktable .jd-descrcol p {\n    padding: 0;\n    margin: 0;\n    line-height: 110%;\n}\n\ndiv#jd-content table.jd-linktable .jd-valcol {\n    vertical-align: top;\n    padding-top: 3px;\n    padding-bottom: 0;\n    padding-left: 7px;\n    padding-right: 7px;\n    border-top: 1px solid #d2d7d0;\n    background-color: #E5F1E0;\n    line-height: 110%;\n}\n\ndiv#jd-content table.jd-linktable .jd-commentrow {\n    vertical-align: top;\n    padding-top: 3px;\n    padding-bottom: 4px;\n    padding-left: 7px;\n    padding-right: 7px;\n    background-color: #F7FCF4;\n    line-height: 110%;\n}\n\ndiv#jd-content div.jd-inheritedlinks {\n    vertical-align: top;\n    margin-top: 9px;\n    padding-left: 7px;\n    padding-right: 7px;\n    background-color: #F7FCF4;\n    line-height: 110%;\n}\n\ndiv#jd-content .jd-page_title-prefix {\n    padding-top: 2em;\n    margin-bottom: -14pt;\n}\n\ndiv#jd-content {\n    margin-left: 0;\n    margin-right: 10px;\n    margin-bottom: 0;\n}\n\ndiv#jd-content h1 {\n    padding-left: 10px;\n}\n\ndiv#jd-content h2 {\n    padding-left: 10px;\n}\n\ndiv#jd-content h4 {\n    margin-top: 9px;\n    margin-bottom: 1px;\n}\n\ndiv#jd-content .jd-descr h5 {\n    margin-bottom: 8px;\n}\n\ndiv#jd-content .sidebox h3 {\n    margin: 1em 0 0 0;\n}\n\ndiv#jd-content .jd-letterlist {\n    margin-top: 20px;\n    margin-bottom: 0;\n}\n\ndiv#jd-content .jd-lettertable {\n    margin-top: 15px;\n    margin-right: 10px;\n}\ndiv#jd-content .jd-letterentries {\n\tlist-style: none;\n\tmargin-left: 0;\n}\ndiv#jd-content .jd-letterentrycomments {\n    color: gray;\n}\n\ndiv#jd-content table.jd-inheritance-table {\n    margin-top: 0;\n    margin-left: 10px;\n    margin-right: 10px;\n    border-spacing: 0;\n}\n\ndiv#jd-content table.jd-inheritance-table td {\n    border: none;\n    margin: 0;\n    padding: 0;\n    background-color: white;\n}\n\ndiv#jd-content table.jd-inheritance-table .jd-inheritance-space {\n    width: 10px;\n}\n\ndiv#jd-content table.jd-inheritance-table .jd-inheritance-interface-cell {\n    padding-left: 17px;\n}\n\ndiv#jd-content h4.jd-details-title {\n    margin: 0;\n    background-color: #E5F1E0;\n    padding: 2px;\n    padding-left: 10px;\n    padding-right: 10px;\n    margin-top: 15px;\n}\n\ndiv#jd-content .jd-details {\n    margin-top: 0;\n    margin-left: -10px;\n}\n\ndiv#jd-content .jd-details-descr {\n    line-height: 120%;\n    padding-left: 10px;\n    padding-top: 10px;\n    padding-right: 20px;\n}\n\ndiv#jd-content .jd-descr h5,\ndiv#jd-content .jd-details h5 {\n    font-style: normal;\n    text-decoration: none;\n    font-size: 120%;\n}\n\ndiv#jd-content .jd-more {\n}\n\ndiv#jd-content .jd-descr {\n    padding-top: 0;\n}\n\ndiv#jd-content .jd-tagdata {\n    margin-top: 6px;\n    margin-bottom: 6px;\n}\n\ndiv#jd-content .jd-tagtitle {\n    margin-top: 0px;\n}\n\ndiv#jd-content .jd-tagtable {\n    margin-top: 10px;\n    border-spacing: 0;\n}\n\ndiv#jd-content .jd-tagtable th {\n    background: white;\n    padding-left: 10px;\n    padding-right: 10px;\nline-height: 120%;\n}\n\ndiv#jd-content .jd-tagtable th,\ndiv#jd-content .jd-tagtable td {\nline-height: 120%;\n    border: none;\n    margin: 0;\n    text-align: left;\n    padding-top: 0px;\n    padding-bottom: 5px;\n}\n\ndiv#jd-content .Code,code,pre,samp,var {\n    color: #004000;\n}\n\ndiv#jd-content pre.Code {\n    padding-left: 20px;\n}\n\n/* XXX I would really like to apply font-size: 9pt only if var/samp\n   is NOT inside of a .jd-descr div. */\ndiv#jd-content .jd-descr code,var,samp {\n    padding-left: 0px;\n}\n\n#search_autocomplete {\n    font-size: 80%;\n}\n\ndiv#jd-searchbox table.jd-autocomplete-table-hidden {\n    display: none;\n}\n\ndiv#jd-searchbox table.jd-autocomplete-table-showing {\n    z-index: 10;\n    border: 1px solid #3366cc;\n    position: relative;\n    top: -14px;\n    left: 5px;\n    background-color: white;\n}\n\ndiv#jd-searchbox td.jd-autocomplete {\n    font-family: Arial, sans-serif;\n    padding-left: 6px;\n    padding-right: 6px;\n    padding-top: 1px;\n    padding-bottom: 1px;\n    font-size: 80%;\n    border: none;\n    margin: 0;\n    line-height: 105%;\n}\n\ndiv#jd-searchbox td.jd-selected {\n    background-color: #E5F1E0;\n}\n\ndiv#jd-searchbox td.jd-linktype {\n    color: #999999;\n}\n\ndiv#jd-content .jd-expando-trigger {\n    margin-left: -8px;\n    margin-right: 0px;\n    border: none;\n}\n\ndiv#jd-build-id {\n    color: #666;\n    width: 100%;\n    text-align: right;\n    padding-right: 5px;\n    padding-bottom: 3px;\n}\n\n@media print {\n    #jd-searchbox, .jd-nav {\n        display: none;\n    }\n    div#jd-content {\n        margin-top: 0px;\n    }\n}\n\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/assets/yui-3.3.0-reset-min.css",
    "content": "/*\nCopyright (c) 2010, Yahoo! Inc. All rights reserved.\nCode licensed under the BSD License:\nhttp://developer.yahoo.com/yui/license.html\nversion: 3.3.0\nbuild: 3167\n*/\nhtml{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}"
  },
  {
    "path": "tools/droiddoc/templates-pdk/components/masthead.cs",
    "content": "<?cs\ndef:custom_masthead() ?>\n  <div id=\"header\">\n      <div id=\"headerLeft\">\n          <a href=\"<?cs var:toroot ?>guide/index.html\"><img\n              src=\"<?cs var:toroot ?>assets/images/android_logo.png\" alt=\"Android Platform Development Kit\" /></a>\n      </div>\n      <div id=\"headerRight\">\n          <div id=\"headerLinks\">\n            <!-- <img src=\"<?cs var:toroot ?>assets/images/icon_world.jpg\" alt=\"\" /> -->\n            <span class=\"text\">\n              <!-- &nbsp;<a href=\"#\">English</a> | -->\n              <a href=\"http://www.android.com\">Android.com</a>\n            </span>\n          </div>\n      </div><!-- headerRight -->\n  </div><!-- header --><?cs \n/def ?><?cs # custom_masthead ?>\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/customizations.cs",
    "content": "<?cs \ndef:sdk_nav() ?>\n  <div class=\"g-section g-tpl-240\" id=\"body-content\">\n    <div class=\"g-unit g-first\" id=\"side-nav\" itemscope itemtype=\"http://schema.org/SiteNavigationElement\">\n      <div id=\"devdoc-nav\"><?cs \n        include:\"../../../../../frameworks/base/docs/html/sdk/sdk_toc.cs\" ?>\n      </div>\n    </div> <!-- end side-nav -->\n<?cs /def ?>\n<?cs \ndef:resources_tab_nav() ?>\n  <div class=\"g-section g-tpl-240\" id=\"body-content\">\n    <div class=\"g-unit g-first\" id=\"side-nav\" itemscope itemtype=\"http://schema.org/SiteNavigationElement\">\n      <div id=\"devdoc-nav\"><?cs \n        include:\"../../../../../frameworks/base/docs/html/resources/resources_toc.cs\" ?>\n      </div>\n    </div> <!-- end side-nav -->\n    <script>\n      addLoadEvent(function() {\n        scrollIntoView(\"devdoc-nav\");\n        });\n    </script>\n<?cs /def ?>\n<?cs \ndef:guide_nav() ?>\n  <div class=\"g-section g-tpl-240\" id=\"body-content\">\n    <div class=\"g-unit g-first\" id=\"side-nav\" itemscope itemtype=\"http://schema.org/SiteNavigationElement\">\n      <div id=\"devdoc-nav\"><?cs \n        include:\"../../../../../vendor/pdk/data/google/docs/guide/guide_toc.cs\" ?>\n      </div>\n    </div> <!-- end side-nav -->\n    <script>\n      addLoadEvent(function() {\n        scrollIntoView(\"devdoc-nav\");\n        });\n    </script>\n<?cs /def ?>\n<?cs\ndef:design_nav() ?>\n  <?cs include:\"../../../../../frameworks/base/docs/html/design/design_toc.cs\" ?>\n<?cs /def ?>\n\n<?cs # The default side navigation for the reference docs ?><?cs \ndef:default_left_nav() ?>\n  <div class=\"g-section g-tpl-240\" id=\"body-content\">\n    <div class=\"g-unit g-first\" id=\"side-nav\" itemscope itemtype=\"http://schema.org/SiteNavigationElement\">\n      <div id=\"swapper\">\n        <div id=\"nav-panels\">\n          <div id=\"resize-packages-nav\">\n            <div id=\"packages-nav\">\n              <div id=\"index-links\"><nobr>\n                <a href=\"<?cs var:toroot ?>reference/packages.html\" <?cs if:(page.title == \"Package Index\") ?>class=\"selected\"<?cs /if ?> >Package Index</a> | \n                <a href=\"<?cs var:toroot ?>reference/classes.html\" <?cs if:(page.title == \"Class Index\") ?>class=\"selected\"<?cs /if ?>>Class Index</a></nobr>\n              </div>\n              <ul>\n              \t<?cs call:package_link_list(docs.packages) ?>\n              </ul><br/>\n            </div> <!-- end packages -->\n          </div> <!-- end resize-packages -->\n          <div id=\"classes-nav\"><?cs \n            if:subcount(class.package) ?>\n            <ul>\n              <?cs call:list(\"Interfaces\", class.package.interfaces) ?>\n              <?cs call:list(\"Classes\", class.package.classes) ?>\n              <?cs call:list(\"Enums\", class.package.enums) ?>\n              <?cs call:list(\"Exceptions\", class.package.exceptions) ?>\n              <?cs call:list(\"Errors\", class.package.errors) ?>\n            </ul><?cs \n            elif:subcount(package) ?>\n            <ul>\n              <?cs call:class_link_list(\"Interfaces\", package.interfaces) ?>\n              <?cs call:class_link_list(\"Classes\", package.classes) ?>\n              <?cs call:class_link_list(\"Enums\", package.enums) ?>\n              <?cs call:class_link_list(\"Exceptions\", package.exceptions) ?>\n              <?cs call:class_link_list(\"Errors\", package.errors) ?>\n            </ul><?cs \n            else ?>\n              <script>\n                /*addLoadEvent(maxPackageHeight);*/\n              </script>\n              <p style=\"padding:10px\">Select a package to view its members</p><?cs \n            /if ?><br/>\n          </div><!-- end classes -->\n        </div><!-- end nav-panels -->\n        <div id=\"nav-tree\" style=\"display:none\">\n          <div id=\"index-links\"><nobr>\n            <a href=\"<?cs var:toroot ?>reference/packages.html\" <?cs if:(page.title == \"Package Index\") ?>class=\"selected\"<?cs /if ?> >Package Index</a> | \n            <a href=\"<?cs var:toroot ?>reference/classes.html\" <?cs if:(page.title == \"Class Index\") ?>class=\"selected\"<?cs /if ?>>Class Index</a></nobr>\n          </div>\n        </div><!-- end nav-tree -->\n      </div><!-- end swapper -->\n    </div> <!-- end side-nav -->\n    <script>\n      if (!isMobile) {\n        $(\"<a href='#' id='nav-swap' onclick='swapNav();return false;' style='font-size:10px;line-height:9px;margin-left:1em;text-decoration:none;'><span id='tree-link'>Use Tree Navigation</span><span id='panel-link' style='display:none'>Use Panel Navigation</span></a>\").appendTo(\"#side-nav\");\n        chooseDefaultNav();\n        if ($(\"#nav-tree\").is(':visible')) {\n          init_default_navtree(\"<?cs var:toroot ?>\");\n        } else {\n          addLoadEvent(function() {\n            scrollIntoView(\"packages-nav\");\n            scrollIntoView(\"classes-nav\");\n          });\n        }\n        $(\"#swapper\").css({borderBottom:\"2px solid #aaa\"});\n      } else {\n        swapNav(); // tree view should be used on mobile\n      }\n    </script><?cs \n/def ?>\n\n<?cs \ndef:custom_left_nav() ?><?cs \n  if:guide ?><?cs \n    call:guide_nav() ?><?cs \n  elif:resources ?><?cs \n    call:resources_tab_nav() ?><?cs \n  elif:sdk ?><?cs \n    call:sdk_nav() ?><?cs \n  else ?><?cs \n    call:default_left_nav() ?><?cs \n  /if ?><?cs \n/def ?>\n\n<?cs # appears at the bottom of every page ?><?cs \ndef:custom_cc_copyright() ?>\n  Except as noted, this content is \n  licensed under <a href=\"http://creativecommons.org/licenses/by/2.5/\">\n  Creative Commons Attribution 2.5</a>. For details and \n  restrictions, see the <a href=\"<?cs var:toroot ?>license.html\">Content \n  License</a>.<?cs \n/def ?>\n\n<?cs \ndef:custom_copyright() ?>\n  Except as noted, this content is licensed under <a\n  href=\"http://www.apache.org/licenses/LICENSE-2.0\">Apache 2.0</a>. \n  For details and restrictions, see the <a href=\"<?cs var:toroot ?>license.html\">\n  Content License</a>.<?cs \n/def ?>\n\n<?cs \ndef:custom_footerlinks() ?>\n  <p>\n    <a href=\"http://www.google.com/intl/en/policies/\" target=\"_blank\">Privacy &amp; Terms</a> -\n    <a href=\"http://www.android.com/branding.html\" target=\"_blank\">Brand Guidelines</a> -\n    <a\nhref=\"http://code.google.com/p/android/issues/entry?template=Developer%20Documentation\"\ntarget=\"_blank\">Report Document Issues</a>\n  </p><?cs \n/def ?>\n\n<?cs # appears on the right side of the blue bar at the bottom off every page ?><?cs \ndef:custom_buildinfo() ?>\n  Android <?cs var:sdk.version ?>&nbsp;r<?cs var:sdk.rel.id ?> - <?cs var:page.now ?>\n<?cs /def ?>"
  },
  {
    "path": "tools/droiddoc/templates-pdk/data.hdf",
    "content": "template {\n    which = normal\n}\n\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/docpage.cs",
    "content": "<?cs include:\"doctype.cs\" ?>\n<?cs include:\"macros.cs\" ?>\n<html>\n<?cs include:\"head_tag.cs\" ?>\n<body class=\"gc-documentation\" itemscope itemtype=\"http://schema.org/Article\">\n<?cs include:\"header.cs\" ?>\n\n<div class=\"g-unit\" id=\"doc-content\"><a name=\"top\"></a>\n\n<div id=\"jd-header\" class=\"guide-header\">\n  <span class=\"crumb\" itemprop=\"breadcrumb\">\n    <?cs if:parent.link ?>\n      <a href=\"<?cs var:parent.link ?>\"><?cs var:parent.title ?></a>:\n    <?cs else ?>&nbsp;\n    <?cs /if ?>\n  </span>\n<h1 itemprop=\"name\"><?cs var:page.title ?></h1>\n</div>\n\n  <?cs # THIS IS THE MAIN DOC CONTENT ?>\n  <div id=\"jd-content\">\n \n    <?cs if:trainingnavtop ?>\n    <div class=\"training-nav-top\">\n\n      <?cs if:next.link ?>\n        <?cs if:startpage ?>\n        <div class=\"training-nav-button-next\">\n          <a href=\"<?cs var:next.link ?>\">\n            Get started\n            <span style=\"font-size:1.2em\">&rsaquo;</span>\n            <span class=\"training-nav-button-title\"><?cs var:next.title ?></span>\n          </a>\n        </div>\n\n        <?cs else ?><?cs # if not startpage ?>\n\n        <div class=\"training-nav-button-next\">\n          <a href=\"<?cs var:next.link ?>\">\n            Next lesson\n            <span style=\"font-size:1.2em\">&rsaquo;</span>\n            <span class=\"training-nav-button-title\"><?cs var:next.title ?></span>\n          </a>\n        </div>\n        <?cs /if ?><?cs # end if/else startpage ?>\n\n      <?cs /if ?><?cs # end if next.link ?>\n\n      <?cs if:previous.link ?>\n      <div class=\"training-nav-button-previous\">\n        <a href=\"<?cs var:previous.link ?>\">\n          <span style=\"font-size:1.2em\">&lsaquo;</span>\n          Previous lesson\n          <span class=\"training-nav-button-title\"><?cs var:previous.title ?></span>\n        </a>\n      </div>\n\n      <?cs /if ?><?cs # end if previous.link ?>\n\n    </div><!-- end training-nav-top -->\n    <?cs /if ?><?cs # end if trainingnavtop ?>\n\n\n    <div class=\"jd-descr\" itemprop=\"articleBody\">\n    <?cs call:tag_list(root.descr) ?>\n    </div>\n\n    <?cs if:!startpage && (previous.link || next.link) ?>\n    <div class=\"training-nav-bottom\">\n      <?cs if:next.link ?>\n      <div class=\"training-nav-button-next\">\n        <a href=\"<?cs var:next.link ?>\">\n          Next lesson\n          <span style=\"font-size:1.2em\">&rsaquo;</span>\n          <br/><span class=\"training-nav-button-title\"><?cs var:next.title ?></span>\n        </a>\n      </div>\n      <?cs /if ?>\n\n      <?cs if:previous.link ?>\n      <div class=\"training-nav-button-previous\">\n        <a href=\"<?cs var:previous.link ?>\">\n          <span style=\"font-size:1.2em\">&lsaquo;</span>\n          Previous lesson\n          <br/><span class=\"training-nav-button-title\"><?cs var:previous.title ?></span>\n        </a>\n      </div>\n      <?cs /if ?>\n    </div> <!-- end training-nav -->\n    <?cs /if ?>\n    \n    <a href=\"#top\" style=\"float:right\">&uarr; Go to top</a>\n    <?cs if:parent.link ?>\n      <p><a href=\"<?cs var:parent.link ?>\">&larr; Back to <?cs var:parent.title ?></a></p>\n    <?cs /if ?>\n\n  </div> <!-- end jd-content -->\n\n<?cs include:\"footer.cs\" ?>\n</div><!-- end doc-content -->\n\n<?cs include:\"trailer.cs\" ?>\n\n</body>\n</html>\n\n\n\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/footer.cs",
    "content": "<div id=\"footer\">\n\n</div> <!-- end footer -->\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/head_tag.cs",
    "content": "<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<?cs if:page.metaDescription ?>\n<meta name=\"Description\" content=\"<?cs var:page.metaDescription ?>\">\n<?cs /if ?>\n<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"<?cs var:toroot ?>favicon.ico\" />\n<title><?cs \n  if:page.title ?><?cs \n    var:page.title ?> | <?cs\n  /if ?>Android Developers</title><?cs \nif:guide||sdk||resources ?>\n<link href=\"<?cs var:toroot ?>assets/android-developer-docs-devguide.css\" rel=\"stylesheet\" type=\"text/css\" /><?cs \nelse ?>\n<link href=\"<?cs var:toroot ?>assets/android-developer-docs.css\" rel=\"stylesheet\" type=\"text/css\" /><?cs \n/if ?>\n<script src=\"<?cs var:toroot ?>assets/search_autocomplete.js\" type=\"text/javascript\"></script>\n<script src=\"<?cs var:toroot ?>assets/jquery-resizable.min.js\" type=\"text/javascript\"></script>\n<script src=\"<?cs var:toroot ?>assets/android-developer-docs.js\" type=\"text/javascript\"></script>\n<script src=\"<?cs var:toroot ?>assets/prettify.js\" type=\"text/javascript\"></script>\n<script type=\"text/javascript\">\n  setToRoot(\"<?cs var:toroot ?>\");\n</script><?cs \nif:reference ?>\n<script src=\"<?cs var:toroot ?>assets/android-developer-reference.js\" type=\"text/javascript\"></script>\n<script src=\"<?cs var:toroot ?>navtree_data.js\" type=\"text/javascript\"></script><?cs \n/if ?><?cs \nif:resources ?>\n<script src=\"<?cs var:toroot ?>resources/resources-data.js\" type=\"text/javascript\"></script><?cs \n/if ?>\n<script type=\"text/javascript\">\n  var _gaq = _gaq || [];\n  _gaq.push(['_setAccount', 'UA-10664927-1']);\n  _gaq.push(['_trackPageview']);\n\n  (function() {\n    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;\n    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';\n    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);\n  })();\n</script>\n<noscript>\n  <style type=\"text/css\">\n    html,body{overflow:auto;}\n    #body-content{position:relative; top:0;}\n    #doc-content{overflow:visible;border-left:3px solid #666;}\n    #side-nav{padding:0;}\n    #side-nav .toggle-list ul {display:block;}\n    #resize-packages-nav{border-bottom:3px solid #666;}\n  </style>\n</noscript>\n</head>\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/header_tabs.cs",
    "content": "<ul id=\"header-tabs\" class=\"<?cs \n\tif:reference ?>reference<?cs\n\telif:guide ?>guide<?cs\n\telif:sdk ?>sdk<?cs\n\telif:home ?>home<?cs\n\telif:resources ?>resources<?cs\n\telif:videos ?>videos<?cs /if ?>\">\n    \n\t<li id=\"home-link\"><a href=\"<?cs var:toroot ?><?cs \n\t                            if:android.whichdoc != \"online\" ?>offline.html<?cs \n\t                            else ?>index.html<?cs /if ?>\">\n\t<?cs if:!sdk.redirect ?>\n\t\t<span class=\"en\">Home</span>\n\t\t<span style=\"display:none\" class=\"de\">Startseite</span>\n\t\t<span style=\"display:none\" class=\"es\"></span>\n\t\t<span style=\"display:none\" class=\"fr\"></span>\n\t\t<span style=\"display:none\" class=\"it\"></span>\n\t\t<span style=\"display:none\" class=\"ja\">ホーム</span>\n\t\t<span style=\"display:none\" class=\"zh-CN\">主页</span>\n\t\t<span style=\"display:none\" class=\"zh-TW\">首頁</span>\n\t<?cs /if ?>\n\t</a></li>\n\t<li id=\"sdk-link\"><a href=\"<?cs var:toroot ?>sdk/index.html\">\n\t\t<span class=\"en\">SDK</span>\n\t</a></li>\n\t<li id=\"guide-link\"><a href=\"<?cs var:toroot ?>guide/index.html\" onClick=\"return loadLast('guide')\">\n\t<?cs if:!sdk.redirect ?>\n\t\t<span class=\"en\">Dev Guide</span>\n\t\t<span style=\"display:none\" class=\"de\">Handbuch</span>\n\t\t<span style=\"display:none\" class=\"es\">Guía</span>\n\t\t<span style=\"display:none\" class=\"fr\">Guide</span>\n\t\t<span style=\"display:none\" class=\"it\">Guida</span>\n\t\t<span style=\"display:none\" class=\"ja\">開発ガイド</span>\n\t\t<span style=\"display:none\" class=\"zh-CN\">开发人员指南</span>\n\t\t<span style=\"display:none\" class=\"zh-TW\">開發指南</span>\n\t<?cs /if ?>\n\t</a></li>\n\t<li id=\"reference-link\"><a href=\"<?cs var:toroot ?>reference/packages.html\" onClick=\"return loadLast('reference')\">\n\t<?cs if:!sdk.redirect ?>\n\t\t<span class=\"en\">Reference</span>\n\t\t<span style=\"display:none\" class=\"de\">Referenz</span>\n\t\t<span style=\"display:none\" class=\"es\">Referencia</span>\n\t\t<span style=\"display:none\" class=\"fr\">Référence</span>\n\t\t<span style=\"display:none\" class=\"it\">Riferimento</span>\n\t\t<span style=\"display:none\" class=\"ja\">リファレンス</span>\n\t\t<span style=\"display:none\" class=\"zh-CN\">参考</span>\n\t\t<span style=\"display:none\" class=\"zh-TW\">參考資料</span>\n\t<?cs /if ?>\n\t</a></li>\n\t<li id=\"resources-link\"><a href=\"<?cs var:toroot ?>resources/index.html\" onClick=\"return loadLast('resources')\">\n\t<?cs if:!sdk.redirect ?>\n\t\t<span class=\"en\">Resources</span>\n\t\t<span style=\"display:none\" class=\"de\"></span>\n\t\t<span style=\"display:none\" class=\"es\"></span>\n\t\t<span style=\"display:none\" class=\"fr\"></span>\n\t\t<span style=\"display:none\" class=\"it\"></span>\n    \t\t<span style=\"display:none\" class=\"ja\"></span>\n\t\t<span style=\"display:none\" class=\"zh-CN\"></span>\n\t\t<span style=\"display:none\" class=\"zh-TW\"></span>\n\t<?cs /if ?>\n\t</a></li>\n\t<li id=\"videos-link\"><a href=\"<?cs var:toroot ?>videos/index.html\" onClick=\"return loadLast('videos')\">\n\t<?cs if:!sdk.redirect ?>\n\t\t<span class=\"en\">Videos</span>\n\t\t<span style=\"display:none\" class=\"de\"></span>\n\t\t<span style=\"display:none\" class=\"es\"></span>\n\t\t<span style=\"display:none\" class=\"fr\"></span>\n\t\t<span style=\"display:none\" class=\"it\"></span>\n\t\t<span style=\"display:none\" class=\"ja\">ビデオ</span>\n\t\t<span style=\"display:none\" class=\"zh-CN\"></span>\n\t\t<span style=\"display:none\" class=\"zh-TW\"></span>\n\t<?cs /if ?>\n\t</a></li>\n\t<li><a href=\"http://android-developers.blogspot.com\" onClick=\"return requestAppendHL(this.href)\">\n\t<?cs if:!sdk.redirect ?>\n\t\t<span class=\"en\">Blog</span>\n\t\t<span style=\"display:none\" class=\"de\"></span>\n\t\t<span style=\"display:none\" class=\"es\"></span>\n\t\t<span style=\"display:none\" class=\"fr\"></span>\n\t\t<span style=\"display:none\" class=\"it\"></span>\n\t\t<span style=\"display:none\" class=\"ja\">ブログ</span>\n\t\t<span style=\"display:none\" class=\"zh-CN\">博客</span>\n\t\t<span style=\"display:none\" class=\"zh-TW\">網誌</span>\n\t<?cs /if ?>\n\t</a></li>\n\n\n     \n</ul>\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/jd_lists_unified.cs",
    "content": "<?cs var:reference_tree ?>\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/sampleindex.cs",
    "content": "<?cs include:\"doctype.cs\" ?>\n<?cs include:\"macros.cs\" ?>\n<?cs set:resources=\"true\" ?>\n<html>\n<?cs include:\"head_tag.cs\" ?>\n<?cs include:\"header.cs\" ?>\n<body class=\"gc-documentation\">\n\n\n<a name=\"top\"></a>\n<div class=\"g-unit\" id=\"doc-content\">\n <div id=\"jd-header\" class=\"guide-header\">\n    <span class=\"crumb\">\n      <a href=\"<?cs var:toroot ?>resources/browser.html?tag=sample\">Sample Code</a> >\n    </span>\n  <h1><?cs var:page.title ?></h1>\n </div>\n\n<div id=\"jd-content\">\n<p><a href=\"../index.html\">&larr; Back</a></p>\n\n<?cs var:summary ?>\n\n  <?cs if:subcount(subdirs) ?>\n      <h2>Subdirectories</h2>\n      <ul class=\"nolist\">\n      <?cs each:dir=subdirs ?>\n        <li><a href=\"<?cs var:dir.name ?>/index.html\"><?cs\n          var:dir.name ?>/</a></li>\n      <?cs /each ?>\n      </ul>\n  <?cs /if ?>\n\n  <?cs if:subcount(files) ?>\n      <h2>Files</h2>\n      <ul class=\"nolist\">\n      <?cs each:file=files ?>\n        <li><a href=\"<?cs var:file.href ?>\"><?cs\n          var:file.name ?></a></li>\n      <?cs /each ?>\n      </ul>\n  <?cs /if ?>\n\n</div><!-- end jd-content -->\n\n<?cs include:\"footer.cs\" ?>\n\n</div><!-- end doc-content -->\n\n<?cs include:\"trailer.cs\" ?>\n\n</body>\n</html>\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/sdkpage.cs",
    "content": "<?cs include:\"doctype.cs\" ?>\n<?cs include:\"macros.cs\" ?>\n<html>\n<?cs if:sdk.redirect ?>\n  <head>\n    <title>Redirecting...</title>\n    <meta http-equiv=\"refresh\" content=\"0;url=<?cs var:toroot ?>sdk/<?cs\n      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?><?cs\n      else ?>index.html<?cs /if ?>\">\n    <link href=\"<?cs var:toroot ?>assets/android-developer-docs.css\" rel=\"stylesheet\" type=\"text/css\" />\n  </head>\n<?cs else ?>\n  <?cs include:\"head_tag.cs\" ?>\n<?cs /if ?>\n<body class=\"gc-documentation\" itemscope itemtype=\"http://schema.org/CreativeWork\">\n<a name=\"top\"></a>\n<?cs call:custom_masthead() ?>\n\n<?cs call:sdk_nav() ?>\n\n<?cs if:sdk.redirect ?>\n\n<div class=\"g-unit\">\n  <div id=\"jd-content\">\n    <p>Redirecting to\n    <a href=\"<?cs var:toroot ?>sdk/<?cs\n      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?><?cs\n      else ?>index.html<?cs /if ?>\"><?cs\n      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?><?cs\n      else ?>Download the SDK<?cs /if ?>\n    </a> ...</p>\n\n<?cs else ?>\n<?cs # else, if NOT redirect ...\n#\n#\n# The following is for SDK/NDK pages\n#\n#\n?>\n\n<div class=\"g-unit\" id=\"doc-content\" >\n  <div id=\"jd-header\" class=\"guide-header\" >\n    <span class=\"crumb\">&nbsp;</span>\n    <h1 itemprop=\"name\"><?cs if:android.whichdoc == \"online\" ?>Download the <?cs /if ?><?cs\nvar:page.title ?></h1>\n  </div>\n\n  <div id=\"jd-content\" itemprop=\"description\">\n\n<?cs if:sdk.not_latest_version ?>\n  <div class=\"special\">\n    <p><strong>This is NOT the current Android SDK release.</strong></p>\n    <p><a href=\"/sdk/index.html\">Download the current Android SDK</a></p>\n  </div>\n<?cs /if ?>\n\n\n<?cs if:ndk ?>\n<?cs #\n#\n#\n#\n#\n#\n#\n# the following is for the NDK\n#\n# (nested in if/else redirect)\n#\n#\n#\n#\n?>\n\n<p>The Android NDK is a companion tool to the Android SDK that lets you build\nperformance-critical portions of your apps in native code. It provides headers and\nlibraries that allow you to build activities, handle user input, use hardware sensors,\naccess application resources, and more, when programming in C or C++. If you write\nnative code, your applications are still packaged into an .apk file and they still run\ninside of a virtual machine on the device. The fundamental Android application model\ndoes not change.</p>\n\n<p>Using native code does not result in an automatic performance increase, \nbut always increases application complexity. If you have not run into any limitations\nusing the Android framework APIs, you probably do not need the NDK. Read <a \nhref=\"<?cs var:toroot ?>sdk/ndk/overview.html\">What is the NDK?</a> for more information about what\nthe NDK offers and whether it will be useful to you.\n</p>\n<p>\nThe NDK is designed for use <em>only</em> in conjunction with the\nAndroid SDK. If you have not already installed and setup the <a\nhref=\"http://developer.android.com/sdk/index.html\">Android SDK</a>, please\ndo so before downloading the NDK. \n</p>\n\n  <table class=\"download\">\n    <tr>\n      <th>Platform</th>\n      <th>Package</th>\n      <th>Size</th>\n      <th>MD5 Checksum</th>\n  </tr>\n  <tr>\n    <td>Windows</td>\n    <td>\n  <a href=\"http://dl.google.com/android/ndk/<?cs var:ndk.win_download ?>\"><?cs var:ndk.win_download ?></a>\n    </td>\n    <td><?cs var:ndk.win_bytes ?> bytes</td>\n    <td><?cs var:ndk.win_checksum ?></td>\n  </tr>\n  <tr class=\"alt-color\">\n    <td>Mac OS X (intel)</td>\n    <td>\n  <a href=\"http://dl.google.com/android/ndk/<?cs var:ndk.mac_download ?>\"><?cs var:ndk.mac_download ?></a>\n    </td>\n    <td><?cs var:ndk.mac_bytes ?> bytes</td>\n    <td><?cs var:ndk.mac_checksum ?></td>\n  </tr>\n  <tr>\n    <td>Linux 32/64-bit (x86)</td>\n    <td>\n  <a href=\"http://dl.google.com/android/ndk/<?cs var:ndk.linux_download ?>\"><?cs var:ndk.linux_download ?></a>\n    </td>\n    <td><?cs var:ndk.linux_bytes ?> bytes</td>\n    <td><?cs var:ndk.linux_checksum ?></td>\n  </tr>\n  </table>\n\n  <?cs else ?>\n<?cs # end if NDK ... \n#\n#\n#\n#\n#\n#\n# the following is for the SDK\n#\n# (nested in if/else redirect and if/else NDK)\n#\n#\n#\n#\n?>\n  <?cs if:android.whichdoc == \"online\" ?>\n\n  <p>Welcome Developers! If you are new to the Android SDK, please read the steps below, for an\noverview of how to set up the SDK. </p>\n\n  <p>If you're already using the Android SDK, you should\nupdate to the latest tools or platform using the <em>Android SDK and AVD Manager</em>, rather than\ndownloading a new SDK starter package. See <a\nhref=\"<?cs var:toroot ?>sdk/adding-components.html\">Adding SDK Components</a>.</p>\n\n  <table class=\"download\">\n    <tr>\n      <th>Platform</th>\n      <th>Package</th>\n      <th>Size</th>\n      <th>MD5 Checksum</th>\n  </tr>\n  <tr>\n    <td rowspan=\"2\">Windows</td>\n    <td>\n  <a onclick=\"onDownload(this)\" href=\"http://dl.google.com/android/<?cs var:sdk.win_download\n?>\"><?cs var:sdk.win_download ?></a>\n    </td>\n    <td><?cs var:sdk.win_bytes ?> bytes</td>\n    <td><?cs var:sdk.win_checksum ?></td>\n  </tr>\n  <tr>\n    <!-- blank TD from Windows rowspan -->\n    <td>\n  <a onclick=\"onDownload(this)\" href=\"http://dl.google.com/android/<?cs var:sdk.win_installer\n?>\"><?cs var:sdk.win_installer ?></a> (Recommended)\n    </td>\n    <td><?cs var:sdk.win_installer_bytes ?> bytes</td>\n    <td><?cs var:sdk.win_installer_checksum ?></td>\n  </tr>\n  <tr class=\"alt-color\">\n    <td>Mac OS X (intel)</td>\n    <td>\n  <a onclick=\"onDownload(this)\" href=\"http://dl.google.com/android/<?cs var:sdk.mac_download\n?>\"><?cs var:sdk.mac_download ?></a>\n    </td>\n    <td><?cs var:sdk.mac_bytes ?> bytes</td>\n    <td><?cs var:sdk.mac_checksum ?></td>\n  </tr>\n  <tr>\n    <td>Linux (i386)</td>\n    <td>\n  <a onclick=\"onDownload(this)\" href=\"http://dl.google.com/android/<?cs var:sdk.linux_download\n?>\"><?cs var:sdk.linux_download ?></a>\n    </td>\n    <td><?cs var:sdk.linux_bytes ?> bytes</td>\n    <td><?cs var:sdk.linux_checksum ?></td>\n  </tr>\n  </table>\n\n\n<div id=\"next-steps\" style=\"display:none\">\n  <p><b><em><span id=\"filename\"></span></em> is now downloading. Follow the steps below to\nget started.</b></p>\n</div>\n\n<script type=\"text/javascript\">\nfunction onDownload(link) {\n  $(\"#filename\").text($(link).html());\n  $(\"#next-steps\").show();\n}\n</script>\n  <?cs else ?> <?cs # end if online ?>\n\n    <?cs if:sdk.preview ?><?cs # it's preview offline docs ?>\n      <p>Welcome developers! We are pleased to provide you with a preview SDK for the upcoming\n    Android 3.0 release, to give you a head-start on developing applications for it.\n    </p>\n    \n      <p>See the <a\n    href=\"<?cs var:toroot ?>sdk/preview/start.html\">Getting Started</a> document for more information\n    about how to set up the preview SDK and get started.</p>\n    <style type=\"text/css\">\n    .non-preview { display:none; }\n    </style>\n    \n    <?cs else ?><?cs # it's normal offline docs ?>\n      <style type=\"text/css\">\n        p.offline-message { display:block; }\n        p.online-message { display:none; }\n      </style>\n    <?cs /if ?>\n    \n  <?cs /if ?> <?cs # end if/else online ?>\n  \n<?cs /if ?> <?cs # end if/else NDK ?>\n\n<?cs /if ?> <?cs # end if/else redirect ?>\n\n<?cs call:tag_list(root.descr) ?>\n\n</div><!-- end jd-content -->\n\n<?cs if:!sdk.redirect ?>\n<?cs include:\"footer.cs\" ?>\n<?cs /if ?>\n\n</div><!-- end g-unit -->\n\n<?cs include:\"trailer.cs\" ?>\n\n</body>\n</html>\n\n\n\n"
  },
  {
    "path": "tools/droiddoc/templates-pdk/trailer.cs",
    "content": "</div> <!-- end body-content --> <?cs # normally opened by header.cs ?>\n<script type=\"text/javascript\">\ninit(); /* initialize android-developer-docs.js */\n</script>\n<!--\n    Copyright 2013 The Android Open Source Project\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n-->\n"
  },
  {
    "path": "tools/droiddoc/test/generics/src/com/android/generics/AbsListView.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.generics;\n\npublic class AbsListView implements AdapterView<ListAdapter> {\n}\n"
  },
  {
    "path": "tools/droiddoc/test/generics/src/com/android/generics/Adapter.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.generics;\n\npublic class Adapter {\n}\n"
  },
  {
    "path": "tools/droiddoc/test/generics/src/com/android/generics/AdapterView.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.generics;\n\npublic interface AdapterView<T extends Adapter> {\n}\n\n"
  },
  {
    "path": "tools/droiddoc/test/generics/src/com/android/generics/Bar.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.generics;\n\npublic interface Bar<K> {\n    public K bar(K arg);\n}\n\n"
  },
  {
    "path": "tools/droiddoc/test/generics/src/com/android/generics/Foo.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.generics;\n\npublic class Foo<V> {\n    public Foo(V v) {\n    }\n\n    public V foo(V arg) {\n        return null;\n    }\n}\n\n\n"
  },
  {
    "path": "tools/droiddoc/test/generics/src/com/android/generics/FooBar.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.generics;\n\npublic class FooBar<K,V,L> extends Foo<V> implements Bar<K> {\n    public class C\n    {\n    }\n\n    public class CI extends C implements Iface\n    {\n    }\n\n    public FooBar(K k) {\n        super(null);\n        throw new RuntimeException(\"!\");\n    }\n\n    public K bar(K arg) {\n        return null;\n    }\n    \n    public FooBar<K,? extends Foo,L> a(K arg) {\n        return null;\n    }\n\n    public FooBar<V,K,L> b(Bar<? extends K> arg) {\n        return null;\n    }\n\n    public <L extends C & Iface> void f(L arg) {\n    }\n\n    public V v;\n}\n\n\n"
  },
  {
    "path": "tools/droiddoc/test/generics/src/com/android/generics/Iface.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.generics;\n\npublic interface Iface {\n}\n"
  },
  {
    "path": "tools/droiddoc/test/generics/src/com/android/generics/ListAdapter.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.generics;\n\npublic class ListAdapter extends Adapter {\n}\n"
  },
  {
    "path": "tools/droiddoc/test/generics/src/com/android/generics/TestComparable.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.generics;\n\npublic interface TestComparable<T> {\n}\n"
  },
  {
    "path": "tools/droiddoc/test/generics/src/com/android/generics/TestEnum.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.generics;\n\npublic class TestEnum<E extends TestEnum<E>> implements TestComparable<E> {\n}\n\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/expected/com/android/stubs/Annot.java",
    "content": "package com.android.stubs;\n@java.lang.annotation.Documented()\n@java.lang.annotation.Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)\n@java.lang.annotation.Target(value={java.lang.annotation.ElementType.TYPE})\npublic @interface Annot\n{\njava.lang.String value() default \"yo\\u1234\";\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/expected/com/android/stubs/InterfaceEnum.java",
    "content": "package com.android.stubs;\npublic enum InterfaceEnum\n  implements com.android.stubs.Parent.Interface\n{\nVAL();\npublic  void method() { throw new RuntimeException(\"Stub!\"); }\npublic static final java.lang.Object OBJECT;\nstatic { OBJECT = null; }\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/expected/com/android/stubs/Parent.java",
    "content": "package com.android.stubs;\n@com.android.stubs.Annot(value=\"asdf\")\npublic class Parent\n{\npublic static interface Interface\n{\npublic  void method();\n}\npublic  Parent() { throw new RuntimeException(\"Stub!\"); }\npublic  java.lang.String methodString() { throw new RuntimeException(\"Stub!\"); }\npublic  int method(boolean b, char c, int i, long l, float f, double d) { throw new RuntimeException(\"Stub!\"); }\nprotected  void protectedMethod() { throw new RuntimeException(\"Stub!\"); }\npublic static final byte public_static_final_byte = 42;\npublic static final short public_static_final_short = 43;\npublic static final int public_static_final_int = 44;\npublic static final long public_static_final_long = 45L;\npublic static final char public_static_final_char = 4660;\npublic static final float public_static_final_float = 42.1f;\npublic static final double public_static_final_double = 42.2;\npublic static int public_static_int;\npublic static final java.lang.String public_static_final_String = \"ps\\u1234fS\";\npublic static java.lang.String public_static_String;\npublic static com.android.stubs.Parent public_static_Parent;\npublic static final com.android.stubs.Parent public_static_final_Parent;\npublic static final com.android.stubs.Parent public_static_final_Parent_null;\nstatic { public_static_final_Parent = null; public_static_final_Parent_null = null; }\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/expected/com/android/stubs/SomeEnum.java",
    "content": "package com.android.stubs;\npublic enum SomeEnum\n{\nA(),\nB(),\nC();\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/expected/com/android/stubs/Types.java",
    "content": "package com.android.stubs;\npublic class Types\n{\npublic static interface Interface\n{\npublic static final boolean public_static_final_boolean = false;\npublic static final char public_static_final_char = 0;\npublic static final short public_static_final_short = 0;\npublic static final int public_static_final_int = 0;\npublic static final long public_static_final_long = 0L;\npublic static final float public_static_final_float = 0.0f;\npublic static final double public_static_final_double = 0.0;\npublic static final java.lang.Object public_static_final_Object = null;\n}\nprotected  Types() { throw new RuntimeException(\"Stub!\"); }\npublic final boolean public_final_boolean;\npublic final char public_final_char;\npublic final short public_final_short;\npublic final int public_final_int;\npublic final long public_final_long;\npublic final float public_final_float;\npublic final double public_final_double;\npublic final java.lang.Object public_final_Object;\npublic static final boolean public_static_final_boolean;\npublic static final char public_static_final_char;\npublic static final short public_static_final_short;\npublic static final int public_static_final_int;\npublic static final long public_static_final_long;\npublic static final float public_static_final_float;\npublic static final double public_static_final_double;\npublic static final java.lang.Object public_static_final_Object;\nstatic { public_static_final_boolean = false; public_static_final_char = 0; public_static_final_short = 0; public_static_final_int = 0; public_static_final_long = 0; public_static_final_float = 0; public_static_final_double = 0; public_static_final_Object = null; }\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/expected/com/android/stubs/a/A.java",
    "content": "package com.android.stubs.a;\npublic abstract class A\n  extends com.android.stubs.Parent\n  implements com.android.stubs.Parent.Interface, com.android.stubs.a.SomeInterface\n{\npublic class Inner\n{\npublic  Inner() { throw new RuntimeException(\"Stub!\"); }\n}\nprotected  A(int a) { throw new RuntimeException(\"Stub!\"); }\npublic  com.android.stubs.a.A varargs(com.android.stubs.Parent[]... args) { throw new RuntimeException(\"Stub!\"); }\npublic  void method() { throw new RuntimeException(\"Stub!\"); }\npublic abstract  java.lang.String[] stringArrayMethod() throws java.io.IOException;\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/expected/com/android/stubs/a/SomeInterface.java",
    "content": "package com.android.stubs.a;\npublic interface SomeInterface\n{\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/expected/com/android/stubs/b/B.java",
    "content": "package com.android.stubs.b;\npublic class B\n{\npublic  B() { throw new RuntimeException(\"Stub!\"); }\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/func.sh",
    "content": "#!/bin/sh\n#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nexport A_STUBS=out/stubs/a/stubs\nexport B_STUBS=out/stubs/b/stubs\nexport EXPECTED_STUBS=out/stubs/expected/stubs\nexport EXPECTED=$DIR/expected\n\nfunction build_stubs()\n{\n    ID=$1\n    SRC_DIR=$2\n    STUBS_DIR=$3\n\n    OBJ_DIR=out/stubs/$ID\n    PLATFORM=${HOST_OS}-${HOST_ARCH}\n\n    rm -rf $OBJ_DIR &> /dev/null\n    mkdir -p $OBJ_DIR\n\n    find $SRC_DIR -name '*.java' > $OBJ_DIR/javadoc-src-list\n    ( \\\n        LD_LIBRARY_PATH=out/host/$PLATFORM/lib \\\n        javadoc \\\n            \\@$OBJ_DIR/javadoc-src-list \\\n            -J-Xmx512m \\\n            -J-Djava.library.path=out/host/$PLATFORM/lib \\\n             \\\n            -quiet \\\n            -doclet DroidDoc \\\n            -docletpath out/host/$PLATFORM/framework/clearsilver.jar:out/host/$PLATFORM/framework/droiddoc.jar:out/host/$PLATFORM/framework/apicheck.jar \\\n            -templatedir tools/droiddoc/templates \\\n            -classpath out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \\\n            -sourcepath $SRC_DIR:out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \\\n            -d $OBJ_DIR/docs \\\n            -hdf page.build MAIN-eng.joeo.20080710.121320 -hdf page.now \"10 Jul 2008 12:13\" \\\n            -stubs $STUBS_DIR \\\n            -stubpackages com.android.stubs:com.android.stubs.a:com.android.stubs.b:com.android.stubs.hidden \\\n        && rm -rf $OBJ_DIR/docs/assets \\\n        && mkdir -p $OBJ_DIR/docs/assets \\\n        && cp -fr tools/droiddoc/templates/assets/* $OBJ_DIR/docs/assets/ \\\n    )# || (rm -rf $OBJ_DIR; exit 45)\n}\n\nfunction compile_stubs()\n{\n    ID=$1\n    STUBS_DIR=$2\n\n    OBJ_DIR=out/stubs/$ID\n    CLASS_DIR=$OBJ_DIR/class\n    mkdir -p $CLASS_DIR\n\n    find $STUBS_DIR -name \"*.java\" > $OBJ_DIR/java-src-list\n    javac @$OBJ_DIR/java-src-list -d $CLASS_DIR\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/run.sh",
    "content": "#!/bin/sh\n#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nDIR=build/make/tools/droiddoc/test/stubs\n\npushd $TOP\n\n. $TOP/$DIR/func.sh\n\nmkdir -p out/stubs_compiled\nfind $DIR/src -name \"*.java\" | xargs javac -d out/stubs_compiled\n\nbuild_stubs a $DIR/src $A_STUBS\nbuild_stubs b $A_STUBS $B_STUBS\n\ncompile_stubs a $A_STUBS\n\necho EXPECTED\ndiff -r $DIR/expected $A_STUBS\necho TWICE STUBBED\ndiff -r $A_STUBS $B_STUBS\n\npopd &> /dev/null\n\n\n\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/Annot.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs;\n\nimport java.lang.annotation.*;\n\n/**\n * poop\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface Annot {\n    String value() default \"yo\\u1234\";\n}\n\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/InterfaceEnum.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs;\n\npublic enum InterfaceEnum implements Parent.Interface {\n    VAL;\n    public static final Object OBJECT = new Object();\n    public void method() { }\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/Parent.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs;\n\n@Annot(\"asdf\")\npublic class Parent {\n    public static final byte public_static_final_byte = 42;\n    public static final short public_static_final_short = 43;\n    public static final int public_static_final_int = 44;\n    public static final long public_static_final_long = 45;\n    public static final char public_static_final_char = '\\u1234';\n    public static final float public_static_final_float = 42.1f;\n    public static final double public_static_final_double = 42.2;\n    public static int public_static_int = 1;\n    public static final String public_static_final_String = \"ps\\u1234fS\";\n    public static String public_static_String = \"psS\";\n    public static Parent public_static_Parent = new Parent();\n    public static final Parent public_static_final_Parent = new Parent();\n    public static final Parent public_static_final_Parent_null = null;\n\n    public interface Interface {\n        void method();\n    }\n\n    public Parent() {\n    }\n\n    public String methodString() {\n        return \"yo\";\n    }\n\n    public int method(boolean b, char c, int i, long l, float f, double d) {\n        return 1;\n    }\n\n    protected void protectedMethod() {\n    }\n\n    void packagePrivateMethod() {\n    }\n\n    /** @hide */\n    public void hiddenMethod() {\n    }\n}\n\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/SomeEnum.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs;\n\npublic enum SomeEnum {\n    A, B, C\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/Types.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs;\n\npublic class Types {\n    public final boolean public_final_boolean;\n    public final char public_final_char;\n    public final short public_final_short;\n    public final int public_final_int;\n    public final long public_final_long;\n    public final float public_final_float;\n    public final double public_final_double;\n    public final Object public_final_Object;\n\n    public static final boolean public_static_final_boolean;\n    public static final char public_static_final_char;\n    public static final short public_static_final_short;\n    public static final int public_static_final_int;\n    public static final long public_static_final_long;\n    public static final float public_static_final_float;\n    public static final double public_static_final_double;\n    public static final Object public_static_final_Object;\n\n    /** @hide */\n    public Types() {\n        public_final_boolean = false;\n        public_final_char = 0;\n        public_final_short = 0;\n        public_final_int = 0;\n        public_final_long = 0;\n        public_final_float = 0;\n        public_final_double = 0;\n        public_final_Object = null;\n    }\n\n    static {\n        public_static_final_boolean = false;\n        public_static_final_char = 0;\n        public_static_final_short = 0;\n        public_static_final_int = 0;\n        public_static_final_long = 0;\n        public_static_final_float = 0;\n        public_static_final_double = 0;\n        public_static_final_Object = null;\n    }\n\n    public interface Interface {\n        public static final boolean public_static_final_boolean = false;\n        public static final char public_static_final_char = 0;\n        public static final short public_static_final_short = 0;\n        public static final int public_static_final_int = 0;\n        public static final long public_static_final_long = 0;\n        public static final float public_static_final_float = 0;\n        public static final double public_static_final_double = 0;\n        public static final Object public_static_final_Object = null;\n    }\n}\n\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/a/A.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs.a;\n\nimport com.android.stubs.Parent;\n\npublic abstract class A extends Parent implements Parent.Interface, SomeInterface {\n    protected A(int a) {\n        super();\n    }\n\n    public A varargs(Parent... args) {\n        return null;\n    }\n\n    public void method() {\n    }\n    public abstract String[] stringArrayMethod() throws java.io.IOException;\n\n    public class Inner {\n        int method() {\n            return 1;\n        }\n        int field;\n    }\n}\n\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/a/SomeInterface.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs.a;\n\npublic interface SomeInterface {\n}\n\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/b/B.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs.b;\n\nimport com.android.stubs.Parent;\nimport com.android.stubs.a.A;\n\npublic class B {\n    Parent method(Parent p) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/hidden/Hidden.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs.c;\n\n/** @hide */\npublic class Hidden {\n\n}\n\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/hidden/HiddenOuter.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs.c;\n\n/** @hide */\npublic class HiddenOuter {\n\n    public class NotHiddenInner {\n    }\n}\n\n"
  },
  {
    "path": "tools/droiddoc/test/stubs/src/com/android/stubs/hidden/PackagePrivate.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.stubs.c;\n\nclass PackagePrivate {\n\n}\n\n"
  },
  {
    "path": "tools/edit_monitor/Android.bp",
    "content": "// Copyright 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Set of error prone rules to ensure code quality\n// PackageLocation check requires the androidCompatible=false otherwise it does not do anything.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n    default_team: \"trendy_team_adte\",\n}\n\npython_library_host {\n    name: \"edit_event_proto\",\n    srcs: [\n        \"proto/edit_event.proto\",\n    ],\n    proto: {\n        canonical_path_from_root: false,\n    },\n}\n\npython_library_host {\n    name: \"edit_monitor_lib\",\n    pkg_path: \"edit_monitor\",\n    srcs: [\n        \"daemon_manager.py\",\n        \"edit_monitor.py\",\n        \"utils.py\",\n    ],\n    libs: [\n        \"asuite_cc_client\",\n        \"edit_event_proto\",\n        \"watchdog\",\n    ],\n}\n\npython_test_host {\n    name: \"daemon_manager_test\",\n    main: \"daemon_manager_test.py\",\n    pkg_path: \"edit_monitor\",\n    srcs: [\n        \"daemon_manager_test.py\",\n    ],\n    libs: [\n        \"edit_monitor_lib\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n}\n\npython_test_host {\n    name: \"edit_monitor_test\",\n    main: \"edit_monitor_test.py\",\n    pkg_path: \"edit_monitor\",\n    srcs: [\n        \"edit_monitor_test.py\",\n    ],\n    libs: [\n        \"edit_monitor_lib\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n}\n\npython_test_host {\n    name: \"edit_monitor_utils_test\",\n    main: \"utils_test.py\",\n    pkg_path: \"edit_monitor\",\n    srcs: [\n        \"utils_test.py\",\n    ],\n    libs: [\n        \"edit_monitor_lib\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n}\n\npython_test_host {\n    name: \"edit_monitor_integration_test\",\n    main: \"edit_monitor_integration_test.py\",\n    pkg_path: \"testdata\",\n    srcs: [\n        \"edit_monitor_integration_test.py\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n    data: [\n        \":edit_monitor\",\n    ],\n}\n\npython_binary_host {\n    name: \"edit_monitor\",\n    pkg_path: \"edit_monitor\",\n    srcs: [\n        \"main.py\",\n    ],\n    libs: [\n        \"edit_monitor_lib\",\n    ],\n    main: \"main.py\",\n}\n"
  },
  {
    "path": "tools/edit_monitor/OWNERS",
    "content": "include platform/tools/asuite:/OWNERS_ADTE_TEAM"
  },
  {
    "path": "tools/edit_monitor/daemon_manager.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport errno\nimport fcntl\nimport getpass\nimport hashlib\nimport logging\nimport multiprocessing\nimport os\nimport pathlib\nimport platform\nimport signal\nimport subprocess\nimport sys\nimport tempfile\nimport time\n\nfrom atest.metrics import clearcut_client\nfrom atest.proto import clientanalytics_pb2\nfrom edit_monitor import utils\nfrom proto import edit_event_pb2\n\nDEFAULT_PROCESS_TERMINATION_TIMEOUT_SECONDS = 5\nDEFAULT_MONITOR_INTERVAL_SECONDS = 5\nDEFAULT_MEMORY_USAGE_THRESHOLD = 0.02  # 2% of total memory\nDEFAULT_CPU_USAGE_THRESHOLD = 200\nDEFAULT_REBOOT_TIMEOUT_SECONDS = 60 * 60 * 24\nBLOCK_SIGN_FILE = \"edit_monitor_block_sign\"\n# Enum of the Clearcut log source defined under\n# /google3/wireless/android/play/playlog/proto/log_source_enum.proto\nLOG_SOURCE = 2524\n\n\ndef default_daemon_target():\n  \"\"\"Place holder for the default daemon target.\"\"\"\n  print(\"default daemon target\")\n\n\nclass DaemonManager:\n  \"\"\"Class to manage and monitor the daemon run as a subprocess.\"\"\"\n\n  def __init__(\n      self,\n      binary_path: str,\n      daemon_target: callable = default_daemon_target,\n      daemon_args: tuple = (),\n      cclient: clearcut_client.Clearcut | None = None,\n  ):\n    self.binary_path = binary_path\n    self.daemon_target = daemon_target\n    self.daemon_args = daemon_args\n    self.cclient = cclient or clearcut_client.Clearcut(LOG_SOURCE)\n\n    self.user_name = getpass.getuser()\n    self.host_name = platform.node()\n    self.source_root = os.environ.get(\"ANDROID_BUILD_TOP\", \"\")\n    self.pid = os.getpid()\n    self.daemon_process = None\n\n    self.max_memory_usage = 0\n    self.max_cpu_usage = 0\n    self.total_memory_size = os.sysconf(\"SC_PAGE_SIZE\") * os.sysconf(\n        \"SC_PHYS_PAGES\"\n    )\n\n    pid_file_dir = pathlib.Path(tempfile.gettempdir()).joinpath(\"edit_monitor\")\n    pid_file_dir.mkdir(parents=True, exist_ok=True)\n    self.pid_file_path = self._get_pid_file_path(pid_file_dir)\n    self.block_sign = pathlib.Path(tempfile.gettempdir()).joinpath(\n        BLOCK_SIGN_FILE\n    )\n\n  def start(self):\n    \"\"\"Writes the pidfile and starts the daemon proces.\"\"\"\n    if not utils.is_feature_enabled(\n        \"edit_monitor\",\n        self.user_name,\n        \"ENABLE_ANDROID_EDIT_MONITOR\",\n        100,\n    ):\n      logging.warning(\"Edit monitor is disabled, exiting...\")\n      return\n\n    if self.block_sign.exists():\n      logging.warning(\"Block sign found, exiting...\")\n      return\n\n    if self.binary_path.startswith(\"/google/cog/\"):\n      logging.warning(\"Edit monitor for cog is not supported, exiting...\")\n      return\n\n    setup_lock_file = pathlib.Path(tempfile.gettempdir()).joinpath(\n        self.pid_file_path.name + \".setup\"\n    )\n    logging.info(\"setup lock file: %s\", setup_lock_file)\n    with open(setup_lock_file, \"w\") as f:\n      try:\n        # Acquire an exclusive lock\n        fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)\n        self._stop_any_existing_instance()\n        self._write_pid_to_pidfile()\n        self._start_daemon_process()\n      except Exception as e:\n        if (\n            isinstance(e, IOError) and e.errno == errno.EAGAIN\n        ):  # Failed to acquire the file lock.\n          logging.warning(\"Another edit monitor is starting, exitinng...\")\n          return\n        else:\n          logging.exception(\"Failed to start daemon manager with error %s\", e)\n          self._send_error_event_to_clearcut(\n              edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR\n          )\n          raise e\n      finally:\n        # Release the lock\n        fcntl.flock(f, fcntl.LOCK_UN)\n\n  def monitor_daemon(\n      self,\n      interval: int = DEFAULT_MONITOR_INTERVAL_SECONDS,\n      memory_threshold: float = DEFAULT_MEMORY_USAGE_THRESHOLD,\n      cpu_threshold: float = DEFAULT_CPU_USAGE_THRESHOLD,\n      reboot_timeout: int = DEFAULT_REBOOT_TIMEOUT_SECONDS,\n  ):\n    \"\"\"Monits the daemon process status.\n\n    Periodically check the CPU/Memory usage of the daemon process as long as the\n    process is still running and kill the process if the resource usage is above\n    given thresholds.\n    \"\"\"\n    if not self.daemon_process:\n      return\n\n    logging.info(\"start monitoring daemon process %d.\", self.daemon_process.pid)\n    reboot_time = time.time() + reboot_timeout\n    while self.daemon_process.is_alive():\n      if time.time() > reboot_time:\n        self.reboot()\n      try:\n        memory_usage = self._get_process_memory_percent(self.daemon_process.pid)\n        self.max_memory_usage = max(self.max_memory_usage, memory_usage)\n\n        cpu_usage = self._get_process_cpu_percent(self.daemon_process.pid)\n        self.max_cpu_usage = max(self.max_cpu_usage, cpu_usage)\n\n        time.sleep(interval)\n      except Exception as e:\n        # Logging the error and continue.\n        logging.warning(\"Failed to monitor daemon process with error: %s\", e)\n\n      if self.max_memory_usage >= memory_threshold:\n        self._send_error_event_to_clearcut(\n            edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_MEMORY_USAGE\n        )\n        logging.error(\n            \"Daemon process is consuming too much memory, rebooting...\"\n        )\n        self.reboot()\n\n      if self.max_cpu_usage >= cpu_threshold:\n        self._send_error_event_to_clearcut(\n            edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_CPU_USAGE\n        )\n        logging.error(\"Daemon process is consuming too much cpu, killing...\")\n        self._terminate_process(self.daemon_process.pid)\n\n    logging.info(\n        \"Daemon process %d terminated. Max memory usage: %f, Max cpu\"\n        \" usage: %f.\",\n        self.daemon_process.pid,\n        self.max_memory_usage,\n        self.max_cpu_usage,\n    )\n\n  def stop(self):\n    \"\"\"Stops the daemon process and removes the pidfile.\"\"\"\n\n    logging.info(\"in daemon manager cleanup.\")\n    try:\n      if self.daemon_process:\n        # The daemon process might already in termination process,\n        # wait some time before kill it explicitly.\n        self._wait_for_process_terminate(self.daemon_process.pid, 1)\n        if self.daemon_process.is_alive():\n          self._terminate_process(self.daemon_process.pid)\n      self._remove_pidfile(self.pid)\n      logging.info(\"Successfully stopped daemon manager.\")\n    except Exception as e:\n      logging.exception(\"Failed to stop daemon manager with error %s\", e)\n      self._send_error_event_to_clearcut(\n          edit_event_pb2.EditEvent.FAILED_TO_STOP_EDIT_MONITOR\n      )\n      sys.exit(1)\n    finally:\n      self.cclient.flush_events()\n\n  def reboot(self):\n    \"\"\"Reboots the current process.\n\n    Stops the current daemon manager and reboots the entire process based on\n    the binary file. Exits directly If the binary file no longer exists.\n    \"\"\"\n    logging.info(\"Rebooting process based on binary %s.\", self.binary_path)\n\n    # Stop the current daemon manager first.\n    self.stop()\n\n    # If the binary no longer exists, exit directly.\n    if not os.path.exists(self.binary_path):\n      logging.info(\"binary %s no longer exists, exiting.\", self.binary_path)\n      sys.exit(0)\n\n    try:\n      os.execv(self.binary_path, sys.argv)\n    except OSError as e:\n      logging.exception(\"Failed to reboot process with error: %s.\", e)\n      self._send_error_event_to_clearcut(\n          edit_event_pb2.EditEvent.FAILED_TO_REBOOT_EDIT_MONITOR\n      )\n      sys.exit(1)  # Indicate an error occurred\n\n  def cleanup(self):\n    \"\"\"Wipes out all edit monitor instances in the system.\n\n    Stops all the existing edit monitor instances and place a block sign\n    to prevent any edit monitor process to start. This method is only used\n    in emergency case when there's something goes wrong with the edit monitor\n    that requires immediate cleanup to prevent damanger to the system.\n    \"\"\"\n    logging.debug(\"Start cleaning up all existing instances.\")\n    self._send_error_event_to_clearcut(edit_event_pb2.EditEvent.FORCE_CLEANUP)\n\n    try:\n      # First places a block sign to prevent any edit monitor process to start.\n      self.block_sign.touch()\n    except (FileNotFoundError, PermissionError, OSError):\n      logging.exception(\"Failed to place the block sign\")\n\n    # Finds and kills all the existing instances of edit monitor.\n    existing_instances_pids = self._find_all_instances_pids()\n    for pid in existing_instances_pids:\n      logging.info(\n          \"Found existing edit monitor instance with pid %d, killing...\", pid\n      )\n      try:\n        self._terminate_process(pid)\n      except Exception:\n        logging.exception(\"Failed to terminate process %d\", pid)\n\n  def _stop_any_existing_instance(self):\n    if not self.pid_file_path.exists():\n      logging.debug(\"No existing instances.\")\n      return\n\n    ex_pid = self._read_pid_from_pidfile()\n\n    if ex_pid:\n      logging.info(\"Found another instance with pid %d.\", ex_pid)\n      self._terminate_process(ex_pid)\n      self._remove_pidfile(ex_pid)\n\n  def _read_pid_from_pidfile(self) -> int | None:\n    try:\n      with open(self.pid_file_path, \"r\") as f:\n        return int(f.read().strip())\n    except FileNotFoundError as e:\n      logging.warning(\"pidfile %s does not exist.\", self.pid_file_path)\n      return None\n\n  def _write_pid_to_pidfile(self):\n    \"\"\"Creates a pidfile and writes the current pid to the file.\n\n    Raise FileExistsError if the pidfile already exists.\n    \"\"\"\n    try:\n      # Use the 'x' mode to open the file for exclusive creation\n      with open(self.pid_file_path, \"x\") as f:\n        f.write(f\"{self.pid}\")\n    except FileExistsError as e:\n      # This could be caused due to race condition that a user is trying\n      # to start two edit monitors at the same time. Or because there is\n      # already an existing edit monitor running and we can not kill it\n      # for some reason.\n      logging.exception(\"pidfile %s already exists.\", self.pid_file_path)\n      raise e\n\n  def _start_daemon_process(self):\n    \"\"\"Starts a subprocess to run the daemon.\"\"\"\n    p = multiprocessing.Process(\n        target=self.daemon_target, args=self.daemon_args\n    )\n    p.daemon = True\n    p.start()\n\n    logging.info(\"Start subprocess with PID %d\", p.pid)\n    self.daemon_process = p\n\n  def _terminate_process(\n      self, pid: int, timeout: int = DEFAULT_PROCESS_TERMINATION_TIMEOUT_SECONDS\n  ):\n    \"\"\"Terminates a process with given pid.\n\n    It first sends a SIGTERM to the process to allow it for proper\n    termination with a timeout. If the process is not terminated within\n    the timeout, kills it forcefully.\n    \"\"\"\n    try:\n      os.kill(pid, signal.SIGTERM)\n      if not self._wait_for_process_terminate(pid, timeout):\n        logging.warning(\n            \"Process %d not terminated within timeout, try force kill\", pid\n        )\n        os.kill(pid, signal.SIGKILL)\n    except ProcessLookupError:\n      logging.info(\"Process with PID %d not found (already terminated)\", pid)\n\n  def _wait_for_process_terminate(self, pid: int, timeout: int) -> bool:\n    start_time = time.time()\n\n    while time.time() < start_time + timeout:\n      if not self._is_process_alive(pid):\n        return True\n      time.sleep(1)\n\n    logging.error(\"Process %d not terminated within %d seconds.\", pid, timeout)\n    return False\n\n  def _is_process_alive(self, pid: int) -> bool:\n    try:\n      output = subprocess.check_output(\n          [\"ps\", \"-p\", str(pid), \"-o\", \"state=\"], text=True\n      ).strip()\n      state = output.split()[0]\n      return state != \"Z\"  # Check if the state is not 'Z' (zombie)\n    except subprocess.CalledProcessError:\n      # Process not found (already dead).\n      return False\n    except (FileNotFoundError, OSError, ValueError) as e:\n      logging.warning(\n          \"Unable to check the status for process %d with error: %s.\", pid, e\n      )\n      return True\n\n  def _remove_pidfile(self, expected_pid: int):\n    recorded_pid = self._read_pid_from_pidfile()\n\n    if recorded_pid is None:\n      logging.info(\"pid file %s already removed.\", self.pid_file_path)\n      return\n\n    if recorded_pid != expected_pid:\n      logging.warning(\n          \"pid file contains pid from a different process, expected pid: %d,\"\n          \" actual pid: %d.\",\n          expected_pid,\n          recorded_pid,\n      )\n      return\n\n    logging.debug(\"removing pidfile written by process %s\", expected_pid)\n    try:\n      os.remove(self.pid_file_path)\n    except FileNotFoundError:\n      logging.info(\"pid file %s already removed.\", self.pid_file_path)\n\n  def _get_pid_file_path(self, pid_file_dir: pathlib.Path) -> pathlib.Path:\n    \"\"\"Generates the path to store the pidfile.\n\n    The file path should have the format of \"/tmp/edit_monitor/xxxx.lock\"\n    where xxxx is a hashed value based on the binary path that starts the\n    process.\n    \"\"\"\n    hash_object = hashlib.sha256()\n    hash_object.update(self.binary_path.encode(\"utf-8\"))\n    pid_file_path = pid_file_dir.joinpath(hash_object.hexdigest() + \".lock\")\n    logging.info(\"pid_file_path: %s\", pid_file_path)\n\n    return pid_file_path\n\n  def _get_process_memory_percent(self, pid: int) -> float:\n    with open(f\"/proc/{pid}/stat\", \"r\") as f:\n      stat_data = f.readline().split()\n      # RSS is the 24th field in /proc/[pid]/stat\n      rss_pages = int(stat_data[23])\n      process_memory = rss_pages * 4 * 1024  # Convert to bytes\n\n    return (\n        process_memory / self.total_memory_size\n        if self.total_memory_size\n        else 0.0\n    )\n\n  def _get_process_cpu_percent(self, pid: int, interval: int = 1) -> float:\n    total_start_time = self._get_total_cpu_time(pid)\n    with open(\"/proc/uptime\", \"r\") as f:\n      uptime_start = float(f.readline().split()[0])\n\n    time.sleep(interval)\n\n    total_end_time = self._get_total_cpu_time(pid)\n    with open(\"/proc/uptime\", \"r\") as f:\n      uptime_end = float(f.readline().split()[0])\n\n    return (\n        (total_end_time - total_start_time) / (uptime_end - uptime_start) * 100\n    )\n\n  def _get_total_cpu_time(self, pid: int) -> float:\n    with open(f\"/proc/{str(pid)}/stat\", \"r\") as f:\n      stats = f.readline().split()\n      # utime is the 14th field in /proc/[pid]/stat measured in clock ticks.\n      utime = int(stats[13])\n      # stime is the 15th field in /proc/[pid]/stat measured in clock ticks.\n      stime = int(stats[14])\n      return (utime + stime) / os.sysconf(os.sysconf_names[\"SC_CLK_TCK\"])\n\n  def _find_all_instances_pids(self) -> list[int]:\n    pids = []\n\n    try:\n      output = subprocess.check_output([\"ps\", \"-ef\", \"--no-headers\"], text=True)\n      for line in output.splitlines():\n        parts = line.split()\n        process_path = parts[7]\n        if pathlib.Path(process_path).name == \"edit_monitor\":\n          pid = int(parts[1])\n          if pid != self.pid:  # exclude the current process\n            pids.append(pid)\n    except Exception:\n      logging.exception(\n          \"Failed to get pids of existing edit monitors from ps command.\"\n      )\n\n    return pids\n\n  def _send_error_event_to_clearcut(self, error_type):\n    edit_monitor_error_event_proto = edit_event_pb2.EditEvent(\n        user_name=self.user_name,\n        host_name=self.host_name,\n        source_root=self.source_root,\n    )\n    edit_monitor_error_event_proto.edit_monitor_error_event.CopyFrom(\n        edit_event_pb2.EditEvent.EditMonitorErrorEvent(error_type=error_type)\n    )\n    log_event = clientanalytics_pb2.LogEvent(\n        event_time_ms=int(time.time() * 1000),\n        source_extension=edit_monitor_error_event_proto.SerializeToString(),\n    )\n    self.cclient.log(log_event)\n"
  },
  {
    "path": "tools/edit_monitor/daemon_manager_test.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Unittests for DaemonManager.\"\"\"\n\nimport fcntl\nimport logging\nimport multiprocessing\nimport os\nimport pathlib\nimport signal\nimport subprocess\nimport sys\nimport tempfile\nimport time\nimport unittest\nfrom unittest import mock\nfrom edit_monitor import daemon_manager\nfrom proto import edit_event_pb2\n\n\nTEST_BINARY_FILE = '/path/to/test_binary'\nTEST_PID_FILE_PATH = (\n    '587239c2d1050afdf54512e2d799f3b929f86b43575eb3c7b4bab105dd9bd25e.lock'\n)\n\n\ndef simple_daemon(output_file):\n  with open(output_file, 'w') as f:\n    f.write('running daemon target')\n\n\ndef long_running_daemon():\n  while True:\n    time.sleep(1)\n\n\ndef memory_consume_daemon_target(size_mb):\n  try:\n    size_bytes = size_mb * 1024 * 1024\n    dummy_data = bytearray(size_bytes)\n    time.sleep(10)\n  except MemoryError:\n    print(f'Process failed to allocate {size_mb} MB of memory.')\n\n\ndef cpu_consume_daemon_target(target_usage_percent):\n  while True:\n    start_time = time.time()\n    while time.time() - start_time < target_usage_percent / 100:\n      pass  # Busy loop to consume CPU\n\n    # Sleep to reduce CPU usage\n    time.sleep(1 - target_usage_percent / 100)\n\n\nclass DaemonManagerTest(unittest.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    super().setUpClass()\n    # Configure to print logging to stdout.\n    logging.basicConfig(filename=None, level=logging.DEBUG)\n    console = logging.StreamHandler(sys.stdout)\n    logging.getLogger('').addHandler(console)\n\n  def setUp(self):\n    super().setUp()\n    self.original_tempdir = tempfile.tempdir\n    self.working_dir = tempfile.TemporaryDirectory()\n    # Sets the tempdir under the working dir so any temp files created during\n    # tests will be cleaned.\n    tempfile.tempdir = self.working_dir.name\n    self.patch = mock.patch.dict(\n        os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'true'}\n    )\n    self.patch.start()\n\n  def tearDown(self):\n    # Cleans up any child processes left by the tests.\n    self._cleanup_child_processes()\n    self.working_dir.cleanup()\n    # Restores tempdir.\n    tempfile.tempdir = self.original_tempdir\n    self.patch.stop()\n    super().tearDown()\n\n  def test_start_success_with_no_existing_instance(self):\n    self.assert_run_simple_daemon_success()\n\n  def test_start_success_with_existing_instance_running(self):\n    # Create a running daemon subprocess\n    p = self._create_fake_deamon_process()\n\n    self.assert_run_simple_daemon_success()\n    self.assert_no_subprocess_running()\n\n  def test_start_success_with_existing_instance_already_dead(self):\n    # Create a pidfile with pid that does not exist.\n    pid_file_path_dir = pathlib.Path(self.working_dir.name).joinpath(\n        'edit_monitor'\n    )\n    pid_file_path_dir.mkdir(parents=True, exist_ok=True)\n    with open(pid_file_path_dir.joinpath(TEST_PID_FILE_PATH), 'w') as f:\n      f.write('123456')\n\n    self.assert_run_simple_daemon_success()\n\n  def test_start_success_with_existing_instance_from_different_binary(self):\n    # First start an instance based on \"some_binary_path\"\n    existing_dm = daemon_manager.DaemonManager(\n        'some_binary_path',\n        daemon_target=long_running_daemon,\n    )\n    existing_dm.start()\n\n    self.assert_run_simple_daemon_success()\n    existing_dm.stop()\n\n  def test_start_return_directly_if_block_sign_exists(self):\n    # Creates the block sign.\n    pathlib.Path(self.working_dir.name).joinpath(\n        daemon_manager.BLOCK_SIGN_FILE\n    ).touch()\n\n    dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)\n    dm.start()\n\n    # Verify no daemon process is started.\n    self.assertIsNone(dm.daemon_process)\n\n  @mock.patch.dict(\n      os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'false'}, clear=True\n  )\n  def test_start_return_directly_if_disabled(self):\n    dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)\n    dm.start()\n\n    # Verify no daemon process is started.\n    self.assertIsNone(dm.daemon_process)\n\n  def test_start_return_directly_if_in_cog_env(self):\n    dm = daemon_manager.DaemonManager(\n        '/google/cog/cloud/user/workspace/edit_monitor'\n    )\n    dm.start()\n\n    # Verify no daemon process is started.\n    self.assertIsNone(dm.daemon_process)\n\n  def test_start_failed_other_instance_is_starting(self):\n    f = open(\n        pathlib.Path(self.working_dir.name).joinpath(\n            TEST_PID_FILE_PATH + '.setup'\n        ),\n        'w',\n    )\n    # Acquire an exclusive lock\n    fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)\n\n    dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)\n    dm.start()\n\n    # Release the lock\n    fcntl.flock(f, fcntl.LOCK_UN)\n    f.close()\n    # Verify no daemon process is started.\n    self.assertIsNone(dm.daemon_process)\n\n  @mock.patch('os.kill')\n  def test_start_failed_to_kill_existing_instance(self, mock_kill):\n    mock_kill.side_effect = OSError('Unknown OSError')\n    pid_file_path_dir = pathlib.Path(self.working_dir.name).joinpath(\n        'edit_monitor'\n    )\n    pid_file_path_dir.mkdir(parents=True, exist_ok=True)\n    with open(pid_file_path_dir.joinpath(TEST_PID_FILE_PATH), 'w') as f:\n      f.write('123456')\n\n    fake_cclient = FakeClearcutClient()\n    with self.assertRaises(OSError):\n      dm = daemon_manager.DaemonManager(TEST_BINARY_FILE, cclient=fake_cclient)\n      dm.start()\n    self._assert_error_event_logged(\n        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR\n    )\n\n  def test_start_failed_to_write_pidfile(self):\n    pid_file_path_dir = pathlib.Path(self.working_dir.name).joinpath(\n        'edit_monitor'\n    )\n    pid_file_path_dir.mkdir(parents=True, exist_ok=True)\n\n    # Makes the directory read-only so write pidfile will fail.\n    os.chmod(pid_file_path_dir, 0o555)\n\n    fake_cclient = FakeClearcutClient()\n    with self.assertRaises(PermissionError):\n      dm = daemon_manager.DaemonManager(TEST_BINARY_FILE, cclient=fake_cclient)\n      dm.start()\n    self._assert_error_event_logged(\n        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR\n    )\n\n  def test_start_failed_to_start_daemon_process(self):\n    fake_cclient = FakeClearcutClient()\n    with self.assertRaises(TypeError):\n      dm = daemon_manager.DaemonManager(\n          TEST_BINARY_FILE,\n          daemon_target='wrong_target',\n          daemon_args=(1),\n          cclient=fake_cclient,\n      )\n      dm.start()\n    self._assert_error_event_logged(\n        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR\n    )\n\n  @mock.patch('os.execv')\n  def test_monitor_reboot_with_high_memory_usage(self, mock_execv):\n    fake_cclient = FakeClearcutClient()\n    binary_file = tempfile.NamedTemporaryFile(\n        dir=self.working_dir.name, delete=False\n    )\n\n    dm = daemon_manager.DaemonManager(\n        binary_file.name,\n        daemon_target=memory_consume_daemon_target,\n        daemon_args=(2,),\n        cclient=fake_cclient,\n    )\n    # set the fake total_memory_size\n    dm.total_memory_size = 100 * 1024 * 1024\n    dm.start()\n    dm.monitor_daemon(interval=1)\n\n    self.assertTrue(dm.max_memory_usage >= 0.02)\n    self.assert_no_subprocess_running()\n    self._assert_error_event_logged(\n        fake_cclient,\n        edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_MEMORY_USAGE,\n    )\n    mock_execv.assert_called_once()\n\n  def test_monitor_daemon_subprocess_killed_high_cpu_usage(self):\n    fake_cclient = FakeClearcutClient()\n    dm = daemon_manager.DaemonManager(\n        TEST_BINARY_FILE,\n        daemon_target=cpu_consume_daemon_target,\n        daemon_args=(20,),\n        cclient=fake_cclient,\n    )\n    dm.start()\n    dm.monitor_daemon(interval=1, cpu_threshold=20)\n\n    self.assertTrue(dm.max_cpu_usage >= 20)\n    self.assert_no_subprocess_running()\n    self._assert_error_event_logged(\n        fake_cclient,\n        edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_CPU_USAGE,\n    )\n\n  @mock.patch('subprocess.check_output')\n  def test_monitor_daemon_failed_does_not_matter(self, mock_output):\n    mock_output.side_effect = OSError('Unknown OSError')\n    self.assert_run_simple_daemon_success()\n\n  @mock.patch('os.execv')\n  def test_monitor_daemon_reboot_triggered(self, mock_execv):\n    binary_file = tempfile.NamedTemporaryFile(\n        dir=self.working_dir.name, delete=False\n    )\n\n    dm = daemon_manager.DaemonManager(\n        binary_file.name,\n        daemon_target=long_running_daemon,\n    )\n    dm.start()\n    dm.monitor_daemon(reboot_timeout=0.5)\n    mock_execv.assert_called_once()\n\n  def test_stop_success(self):\n    dm = daemon_manager.DaemonManager(\n        TEST_BINARY_FILE, daemon_target=long_running_daemon\n    )\n    dm.start()\n    dm.stop()\n\n    self.assert_no_subprocess_running()\n    self.assertFalse(dm.pid_file_path.exists())\n\n  @mock.patch('os.kill')\n  def test_stop_failed_to_kill_daemon_process(self, mock_kill):\n    mock_kill.side_effect = OSError('Unknown OSError')\n    fake_cclient = FakeClearcutClient()\n    dm = daemon_manager.DaemonManager(\n        TEST_BINARY_FILE,\n        daemon_target=long_running_daemon,\n        cclient=fake_cclient,\n    )\n\n    with self.assertRaises(SystemExit):\n      dm.start()\n      dm.stop()\n      self.assertTrue(dm.daemon_process.is_alive())\n      self.assertTrue(dm.pid_file_path.exists())\n    self._assert_error_event_logged(\n        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_STOP_EDIT_MONITOR\n    )\n\n  @mock.patch('os.remove')\n  def test_stop_failed_to_remove_pidfile(self, mock_remove):\n    mock_remove.side_effect = OSError('Unknown OSError')\n\n    fake_cclient = FakeClearcutClient()\n    dm = daemon_manager.DaemonManager(\n        TEST_BINARY_FILE,\n        daemon_target=long_running_daemon,\n        cclient=fake_cclient,\n    )\n\n    with self.assertRaises(SystemExit):\n      dm.start()\n      dm.stop()\n      self.assert_no_subprocess_running()\n      self.assertTrue(dm.pid_file_path.exists())\n\n    self._assert_error_event_logged(\n        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_STOP_EDIT_MONITOR\n    )\n\n  @mock.patch('os.execv')\n  def test_reboot_success(self, mock_execv):\n    binary_file = tempfile.NamedTemporaryFile(\n        dir=self.working_dir.name, delete=False\n    )\n\n    dm = daemon_manager.DaemonManager(\n        binary_file.name, daemon_target=long_running_daemon\n    )\n    dm.start()\n    dm.reboot()\n\n    # Verifies the old process is stopped\n    self.assert_no_subprocess_running()\n    self.assertFalse(dm.pid_file_path.exists())\n\n    mock_execv.assert_called_once()\n\n  @mock.patch('os.execv')\n  def test_reboot_binary_no_longer_exists(self, mock_execv):\n    dm = daemon_manager.DaemonManager(\n        TEST_BINARY_FILE, daemon_target=long_running_daemon\n    )\n    dm.start()\n\n    with self.assertRaises(SystemExit):\n      dm.reboot()\n      mock_execv.assert_not_called()\n      self.assertEqual(cm.exception.code, 0)\n\n  @mock.patch('os.execv')\n  def test_reboot_failed(self, mock_execv):\n    mock_execv.side_effect = OSError('Unknown OSError')\n    fake_cclient = FakeClearcutClient()\n    binary_file = tempfile.NamedTemporaryFile(\n        dir=self.working_dir.name, delete=False\n    )\n\n    dm = daemon_manager.DaemonManager(\n        binary_file.name,\n        daemon_target=long_running_daemon,\n        cclient=fake_cclient,\n    )\n    dm.start()\n\n    with self.assertRaises(SystemExit):\n      dm.reboot()\n      self.assertEqual(cm.exception.code, 1)\n    self._assert_error_event_logged(\n        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_REBOOT_EDIT_MONITOR\n    )\n\n  @mock.patch('subprocess.check_output')\n  def test_cleanup_success(self, mock_check_output):\n    p = self._create_fake_deamon_process()\n    fake_cclient = FakeClearcutClient()\n    mock_check_output.return_value = f'user {p.pid} 1 1 1 1 1 edit_monitor arg'\n\n    dm = daemon_manager.DaemonManager(\n        TEST_BINARY_FILE,\n        daemon_target=long_running_daemon,\n        cclient=fake_cclient,\n    )\n    dm.cleanup()\n\n    self.assertFalse(p.is_alive())\n    self.assertTrue(\n        pathlib.Path(self.working_dir.name)\n        .joinpath(daemon_manager.BLOCK_SIGN_FILE)\n        .exists()\n    )\n\n  def assert_run_simple_daemon_success(self):\n    damone_output_file = tempfile.NamedTemporaryFile(\n        dir=self.working_dir.name, delete=False\n    )\n    dm = daemon_manager.DaemonManager(\n        TEST_BINARY_FILE,\n        daemon_target=simple_daemon,\n        daemon_args=(damone_output_file.name,),\n    )\n    dm.start()\n    dm.monitor_daemon(interval=1)\n\n    # Verifies the expected pid file is created.\n    expected_pid_file_path = pathlib.Path(self.working_dir.name).joinpath(\n        'edit_monitor', TEST_PID_FILE_PATH\n    )\n    self.assertTrue(expected_pid_file_path.exists())\n\n    # Verify the daemon process is executed successfully.\n    with open(damone_output_file.name, 'r') as f:\n      contents = f.read()\n      self.assertEqual(contents, 'running daemon target')\n\n  def assert_no_subprocess_running(self):\n    child_pids = self._get_child_processes(os.getpid())\n    for child_pid in child_pids:\n      self.assertFalse(\n          self._is_process_alive(child_pid), f'process {child_pid} still alive'\n      )\n\n  def _get_child_processes(self, parent_pid: int) -> list[int]:\n    try:\n      output = subprocess.check_output(\n          ['ps', '-o', 'pid,ppid', '--no-headers'], text=True\n      )\n\n      child_processes = []\n      for line in output.splitlines():\n        pid, ppid = line.split()\n        if int(ppid) == parent_pid:\n          child_processes.append(int(pid))\n      return child_processes\n    except subprocess.CalledProcessError as e:\n      self.fail(f'failed to get child process, error: {e}')\n\n  def _is_process_alive(self, pid: int) -> bool:\n    try:\n      output = subprocess.check_output(\n          ['ps', '-p', str(pid), '-o', 'state='], text=True\n      ).strip()\n      state = output.split()[0]\n      return state != 'Z'  # Check if the state is not 'Z' (zombie)\n    except subprocess.CalledProcessError:\n      return False\n\n  def _cleanup_child_processes(self):\n    child_pids = self._get_child_processes(os.getpid())\n    for child_pid in child_pids:\n      try:\n        os.kill(child_pid, signal.SIGKILL)\n      except ProcessLookupError:\n        # process already terminated\n        pass\n\n  def _create_fake_deamon_process(\n      self, name: str = TEST_PID_FILE_PATH\n  ) -> multiprocessing.Process:\n    # Create a long running subprocess\n    p = multiprocessing.Process(target=long_running_daemon)\n    p.start()\n\n    # Create the pidfile with the subprocess pid\n    pid_file_path_dir = pathlib.Path(self.working_dir.name).joinpath(\n        'edit_monitor'\n    )\n    pid_file_path_dir.mkdir(parents=True, exist_ok=True)\n    with open(pid_file_path_dir.joinpath(name), 'w') as f:\n      f.write(str(p.pid))\n    return p\n\n  def _assert_error_event_logged(self, fake_cclient, error_type):\n    error_events = fake_cclient.get_sent_events()\n    self.assertEqual(len(error_events), 1)\n    self.assertEqual(\n        edit_event_pb2.EditEvent.FromString(\n            error_events[0].source_extension\n        ).edit_monitor_error_event.error_type,\n        error_type,\n    )\n\n\nclass FakeClearcutClient:\n\n  def __init__(self):\n    self.pending_log_events = []\n    self.sent_log_event = []\n\n  def log(self, log_event):\n    self.pending_log_events.append(log_event)\n\n  def flush_events(self):\n    self.sent_log_event.extend(self.pending_log_events)\n    self.pending_log_events.clear()\n\n  def get_sent_events(self):\n    return self.sent_log_event + self.pending_log_events\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "tools/edit_monitor/edit_monitor.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport getpass\nimport logging\nimport multiprocessing.connection\nimport os\nimport pathlib\nimport platform\nimport threading\nimport time\n\nfrom atest.metrics import clearcut_client\nfrom atest.proto import clientanalytics_pb2\nfrom proto import edit_event_pb2\nfrom watchdog.events import FileSystemEvent\nfrom watchdog.events import PatternMatchingEventHandler\nfrom watchdog.observers import Observer\n\n# Enum of the Clearcut log source defined under\n# /google3/wireless/android/play/playlog/proto/log_source_enum.proto\nLOG_SOURCE = 2524\nDEFAULT_FLUSH_INTERVAL_SECONDS = 5\nDEFAULT_SINGLE_EVENTS_SIZE_THRESHOLD = 100\n\n\nclass ClearcutEventHandler(PatternMatchingEventHandler):\n\n  def __init__(\n      self,\n      path: str,\n      flush_interval_sec: int,\n      single_events_size_threshold: int,\n      is_dry_run: bool = False,\n      cclient: clearcut_client.Clearcut | None = None,\n  ):\n\n    super().__init__(patterns=[\"*\"], ignore_directories=True)\n    self.root_monitoring_path = path\n    self.flush_interval_sec = flush_interval_sec\n    self.single_events_size_threshold = single_events_size_threshold\n    self.is_dry_run = is_dry_run\n    self.cclient = cclient or clearcut_client.Clearcut(LOG_SOURCE)\n\n    self.user_name = getpass.getuser()\n    self.host_name = platform.node()\n    self.source_root = os.environ.get(\"ANDROID_BUILD_TOP\", \"\")\n\n    self.pending_events = []\n    self._scheduled_log_thread = None\n    self._pending_events_lock = threading.Lock()\n\n  def on_moved(self, event: FileSystemEvent):\n    self._log_edit_event(event, edit_event_pb2.EditEvent.MOVE)\n\n  def on_created(self, event: FileSystemEvent):\n    self._log_edit_event(event, edit_event_pb2.EditEvent.CREATE)\n\n  def on_deleted(self, event: FileSystemEvent):\n    self._log_edit_event(event, edit_event_pb2.EditEvent.DELETE)\n\n  def on_modified(self, event: FileSystemEvent):\n    self._log_edit_event(event, edit_event_pb2.EditEvent.MODIFY)\n\n  def flushall(self):\n    logging.info(\"flushing all pending events.\")\n    if self._scheduled_log_thread:\n      logging.info(\"canceling log thread\")\n      self._scheduled_log_thread.cancel()\n      self._scheduled_log_thread = None\n\n    self._log_clearcut_events()\n    self.cclient.flush_events()\n\n  def _log_edit_event(\n      self, event: FileSystemEvent, edit_type: edit_event_pb2.EditEvent.EditType\n  ):\n    try:\n      event_time = time.time()\n\n      if self._is_hidden_file(pathlib.Path(event.src_path)):\n        logging.debug(\"ignore hidden file: %s.\", event.src_path)\n        return\n\n      if not self._is_under_git_project(pathlib.Path(event.src_path)):\n        logging.debug(\n            \"ignore file %s which does not belong to a git project\",\n            event.src_path,\n        )\n        return\n\n      logging.info(\"%s: %s\", event.event_type, event.src_path)\n\n      event_proto = edit_event_pb2.EditEvent(\n          user_name=self.user_name,\n          host_name=self.host_name,\n          source_root=self.source_root,\n      )\n      event_proto.single_edit_event.CopyFrom(\n          edit_event_pb2.EditEvent.SingleEditEvent(\n              file_path=event.src_path, edit_type=edit_type\n          )\n      )\n      with self._pending_events_lock:\n        self.pending_events.append((event_proto, event_time))\n        if not self._scheduled_log_thread:\n          logging.debug(\n              \"Scheduling thread to run in %d seconds\", self.flush_interval_sec\n          )\n          self._scheduled_log_thread = threading.Timer(\n              self.flush_interval_sec, self._log_clearcut_events\n          )\n          self._scheduled_log_thread.start()\n\n    except Exception:\n      logging.exception(\"Failed to log edit event.\")\n\n  def _is_hidden_file(self, file_path: pathlib.Path) -> bool:\n    return any(\n        part.startswith(\".\")\n        for part in file_path.relative_to(self.root_monitoring_path).parts\n    )\n\n  def _is_under_git_project(self, file_path: pathlib.Path) -> bool:\n    root_path = pathlib.Path(self.root_monitoring_path).resolve()\n    return any(\n        root_path.joinpath(dir).joinpath('.git').exists()\n        for dir in file_path.relative_to(root_path).parents\n    )\n\n  def _log_clearcut_events(self):\n    with self._pending_events_lock:\n      self._scheduled_log_thread = None\n      edit_events = self.pending_events\n      self.pending_events = []\n\n    pending_events_size = len(edit_events)\n    if pending_events_size > self.single_events_size_threshold:\n      logging.info(\n          \"got %d events in %d seconds, sending aggregated events instead\",\n          pending_events_size,\n          self.flush_interval_sec,\n      )\n      aggregated_event_time = edit_events[0][1]\n      aggregated_event_proto = edit_event_pb2.EditEvent(\n          user_name=self.user_name,\n          host_name=self.host_name,\n          source_root=self.source_root,\n      )\n      aggregated_event_proto.aggregated_edit_event.CopyFrom(\n          edit_event_pb2.EditEvent.AggregatedEditEvent(\n              num_edits=pending_events_size\n          )\n      )\n      edit_events = [(aggregated_event_proto, aggregated_event_time)]\n\n    if self.is_dry_run:\n      logging.info(\"Sent %d edit events in dry run.\", len(edit_events))\n      return\n\n    for event_proto, event_time in edit_events:\n      log_event = clientanalytics_pb2.LogEvent(\n          event_time_ms=int(event_time * 1000),\n          source_extension=event_proto.SerializeToString(),\n      )\n      self.cclient.log(log_event)\n\n    logging.info(\"sent %d edit events\", len(edit_events))\n\n\ndef start(\n    path: str,\n    is_dry_run: bool = False,\n    flush_interval_sec: int = DEFAULT_FLUSH_INTERVAL_SECONDS,\n    single_events_size_threshold: int = DEFAULT_SINGLE_EVENTS_SIZE_THRESHOLD,\n    cclient: clearcut_client.Clearcut | None = None,\n    pipe_sender: multiprocessing.connection.Connection | None = None,\n):\n  \"\"\"Method to start the edit monitor.\n\n  This is the entry point to start the edit monitor as a subprocess of\n  the daemon manager.\n\n  params:\n    path: The root path to monitor\n    cclient: The clearcut client to send the edit logs.\n    conn: the sender of the pipe to communicate with the deamon manager.\n  \"\"\"\n  event_handler = ClearcutEventHandler(\n      path, flush_interval_sec, single_events_size_threshold, is_dry_run, cclient)\n  observer = Observer()\n\n  logging.info(\"Starting observer on path %s.\", path)\n  observer.schedule(event_handler, path, recursive=True)\n  observer.start()\n  logging.info(\"Observer started.\")\n  if pipe_sender:\n    pipe_sender.send(\"Observer started.\")\n\n  try:\n    while True:\n      time.sleep(1)\n  finally:\n    event_handler.flushall()\n    observer.stop()\n    observer.join()\n    if pipe_sender:\n      pipe_sender.close()\n"
  },
  {
    "path": "tools/edit_monitor/edit_monitor_integration_test.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Integration tests for Edit Monitor.\"\"\"\n\nimport glob\nfrom importlib import resources\nimport logging\nimport os\nimport pathlib\nimport shutil\nimport signal\nimport subprocess\nimport sys\nimport tempfile\nimport time\nimport unittest\nfrom unittest import mock\n\n\nclass EditMonitorIntegrationTest(unittest.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    super().setUpClass()\n    # Configure to print logging to stdout.\n    logging.basicConfig(filename=None, level=logging.DEBUG)\n    console = logging.StreamHandler(sys.stdout)\n    logging.getLogger(\"\").addHandler(console)\n\n  def setUp(self):\n    super().setUp()\n    self.working_dir = tempfile.TemporaryDirectory()\n    self.root_monitoring_path = pathlib.Path(self.working_dir.name).joinpath(\n        \"files\"\n    )\n    self.root_monitoring_path.mkdir()\n    self.edit_monitor_binary_path = self._import_executable(\"edit_monitor\")\n    self.patch = mock.patch.dict(\n        os.environ, {\"ENABLE_ANDROID_EDIT_MONITOR\": \"true\"}\n    )\n    self.patch.start()\n\n  def tearDown(self):\n    self.patch.stop()\n    self.working_dir.cleanup()\n    super().tearDown()\n\n  def test_log_single_edit_event_success(self):\n    p = self._start_edit_monitor_process()\n\n    # Create the .git file under the monitoring dir.\n    self.root_monitoring_path.joinpath(\".git\").touch()\n\n    # Create and modify a file.\n    test_file = self.root_monitoring_path.joinpath(\"test.txt\")\n    with open(test_file, \"w\") as f:\n      f.write(\"something\")\n\n    # Move the file.\n    test_file_moved = self.root_monitoring_path.joinpath(\"new_test.txt\")\n    test_file.rename(test_file_moved)\n\n    # Delete the file.\n    test_file_moved.unlink()\n\n    # Give some time for the edit monitor to receive the edit event.\n    time.sleep(1)\n    # Stop the edit monitor and flush all events.\n    os.kill(p.pid, signal.SIGINT)\n    p.communicate()\n\n    self.assertEqual(self._get_logged_events_num(), 4)\n\n  def test_start_multiple_edit_monitor_only_one_started(self):\n    p1 = self._start_edit_monitor_process(wait_for_observer_start=False)\n    p2 = self._start_edit_monitor_process(wait_for_observer_start=False)\n    p3 = self._start_edit_monitor_process(wait_for_observer_start=False)\n\n    live_processes = self._get_live_processes([p1, p2, p3])\n\n    # Cleanup all live processes.\n    for p in live_processes:\n      os.kill(p.pid, signal.SIGINT)\n      p.communicate()\n\n    self.assertEqual(len(live_processes), 1)\n\n  def _start_edit_monitor_process(self, wait_for_observer_start=True):\n    command = f\"\"\"\n    export TMPDIR=\"{self.working_dir.name}\"\n    {self.edit_monitor_binary_path} --path={self.root_monitoring_path} --dry_run\"\"\"\n    p = subprocess.Popen(\n        command,\n        shell=True,\n        text=True,\n        start_new_session=True,\n        executable=\"/bin/bash\",\n    )\n    if wait_for_observer_start:\n      self._wait_for_observer_start(time_out=5)\n\n    return p\n\n  def _wait_for_observer_start(self, time_out):\n    start_time = time.time()\n\n    while time.time() < start_time + time_out:\n      log_files = glob.glob(self.working_dir.name + \"/edit_monitor_*/*.log\")\n      if log_files:\n        with open(log_files[0], \"r\") as f:\n          for line in f:\n            logging.debug(\"initial log: %s\", line)\n            if line.rstrip(\"\\n\").endswith(\"Observer started.\"):\n              return\n      else:\n        time.sleep(1)\n\n    self.fail(f\"Observer not started in {time_out} seconds.\")\n\n  def _get_logged_events_num(self):\n    log_files = glob.glob(self.working_dir.name + \"/edit_monitor_*/*.log\")\n    self.assertEqual(len(log_files), 1)\n\n    with open(log_files[0], \"r\") as f:\n      for line in f:\n        logging.debug(\"complete log: %s\", line)\n        if line.rstrip(\"\\n\").endswith(\"in dry run.\"):\n          return int(line.split(\":\")[-1].split(\" \")[2])\n\n    return 0\n\n  def _get_live_processes(self, processes):\n    live_processes = []\n    for p in processes:\n      try:\n        p.wait(timeout=5)\n      except subprocess.TimeoutExpired as e:\n        live_processes.append(p)\n        logging.info(\"process: %d still alive.\", p.pid)\n      else:\n        logging.info(\"process: %d stopped.\", p.pid)\n    return live_processes\n\n  def _import_executable(self, executable_name: str) -> pathlib.Path:\n    binary_dir = pathlib.Path(self.working_dir.name).joinpath(\"binary\")\n    binary_dir.mkdir()\n    executable_path = binary_dir.joinpath(executable_name)\n    with resources.as_file(\n        resources.files(\"testdata\").joinpath(executable_name)\n    ) as binary:\n      shutil.copy(binary, executable_path)\n    executable_path.chmod(0o755)\n    return executable_path\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "tools/edit_monitor/edit_monitor_test.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Unittests for Edit Monitor.\"\"\"\n\nimport logging\nimport multiprocessing\nimport os\nimport pathlib\nimport signal\nimport sys\nimport tempfile\nimport time\nimport unittest\n\nfrom atest.proto import clientanalytics_pb2\nfrom edit_monitor import edit_monitor\nfrom proto import edit_event_pb2\n\n\nclass EditMonitorTest(unittest.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    super().setUpClass()\n    # Configure to print logging to stdout.\n    logging.basicConfig(filename=None, level=logging.DEBUG)\n    console = logging.StreamHandler(sys.stdout)\n    logging.getLogger('').addHandler(console)\n\n  def setUp(self):\n    super().setUp()\n    self.working_dir = tempfile.TemporaryDirectory()\n    self.root_monitoring_path = pathlib.Path(self.working_dir.name).joinpath(\n        'files'\n    )\n    self.root_monitoring_path.mkdir()\n    self.log_event_dir = pathlib.Path(self.working_dir.name).joinpath('logs')\n    self.log_event_dir.mkdir()\n\n  def tearDown(self):\n    self.working_dir.cleanup()\n    super().tearDown()\n\n  def test_log_single_edit_event_success(self):\n    # Create the .git file under the monitoring dir.\n    self.root_monitoring_path.joinpath('.git').touch()\n    fake_cclient = FakeClearcutClient(\n        log_output_file=self.log_event_dir.joinpath('logs.output')\n    )\n    p = self._start_test_edit_monitor_process(fake_cclient)\n\n    # Create and modify a file.\n    test_file = self.root_monitoring_path.joinpath('test.txt')\n    with open(test_file, 'w') as f:\n      f.write('something')\n    # Move the file.\n    test_file_moved = self.root_monitoring_path.joinpath('new_test.txt')\n    test_file.rename(test_file_moved)\n    # Delete the file.\n    test_file_moved.unlink()\n    # Give some time for the edit monitor to receive the edit event.\n    time.sleep(1)\n    # Stop the edit monitor and flush all events.\n    os.kill(p.pid, signal.SIGINT)\n    p.join()\n\n    logged_events = self._get_logged_events()\n    self.assertEqual(len(logged_events), 4)\n    expected_create_event = edit_event_pb2.EditEvent.SingleEditEvent(\n        file_path=str(\n            self.root_monitoring_path.joinpath('test.txt').resolve()\n        ),\n        edit_type=edit_event_pb2.EditEvent.CREATE,\n    )\n    expected_modify_event = edit_event_pb2.EditEvent.SingleEditEvent(\n        file_path=str(\n            self.root_monitoring_path.joinpath('test.txt').resolve()\n        ),\n        edit_type=edit_event_pb2.EditEvent.MODIFY,\n    )\n    expected_move_event = edit_event_pb2.EditEvent.SingleEditEvent(\n        file_path=str(\n            self.root_monitoring_path.joinpath('test.txt').resolve()\n        ),\n        edit_type=edit_event_pb2.EditEvent.MOVE,\n    )\n    expected_delete_event = edit_event_pb2.EditEvent.SingleEditEvent(\n        file_path=str(\n            self.root_monitoring_path.joinpath('new_test.txt').resolve()\n        ),\n        edit_type=edit_event_pb2.EditEvent.DELETE,\n    )\n    self.assertEqual(\n        expected_create_event,\n        edit_event_pb2.EditEvent.FromString(\n            logged_events[0].source_extension\n        ).single_edit_event,\n    )\n    self.assertEqual(\n        expected_modify_event,\n        edit_event_pb2.EditEvent.FromString(\n            logged_events[1].source_extension\n        ).single_edit_event,\n    )\n    self.assertEqual(\n        expected_move_event,\n        edit_event_pb2.EditEvent.FromString(\n            logged_events[2].source_extension\n        ).single_edit_event,\n    )\n    self.assertEqual(\n        expected_delete_event,\n        edit_event_pb2.EditEvent.FromString(\n            logged_events[3].source_extension\n        ).single_edit_event,\n    )\n\n\n  def test_log_aggregated_edit_event_success(self):\n    # Create the .git file under the monitoring dir.\n    self.root_monitoring_path.joinpath('.git').touch()\n    fake_cclient = FakeClearcutClient(\n        log_output_file=self.log_event_dir.joinpath('logs.output')\n    )\n    p = self._start_test_edit_monitor_process(fake_cclient)\n\n    # Create 6 test files\n    for i in range(6):\n      test_file = self.root_monitoring_path.joinpath('test_' + str(i))\n      test_file.touch()\n\n    # Give some time for the edit monitor to receive the edit event.\n    time.sleep(1)\n    # Stop the edit monitor and flush all events.\n    os.kill(p.pid, signal.SIGINT)\n    p.join()\n\n    logged_events = self._get_logged_events()\n    self.assertEqual(len(logged_events), 1)\n\n    expected_aggregated_edit_event = (\n        edit_event_pb2.EditEvent.AggregatedEditEvent(\n            num_edits=6,\n        )\n    )\n\n    self.assertEqual(\n        expected_aggregated_edit_event,\n        edit_event_pb2.EditEvent.FromString(\n            logged_events[0].source_extension\n        ).aggregated_edit_event,\n    )\n\n  def test_do_not_log_edit_event_for_directory_change(self):\n    # Create the .git file under the monitoring dir.\n    self.root_monitoring_path.joinpath('.git').touch()\n    fake_cclient = FakeClearcutClient(\n        log_output_file=self.log_event_dir.joinpath('logs.output')\n    )\n    p = self._start_test_edit_monitor_process(fake_cclient)\n\n    # Create a sub directory\n    self.root_monitoring_path.joinpath('test_dir').mkdir()\n    # Give some time for the edit monitor to receive the edit event.\n    time.sleep(1)\n    # Stop the edit monitor and flush all events.\n    os.kill(p.pid, signal.SIGINT)\n    p.join()\n\n    logged_events = self._get_logged_events()\n    self.assertEqual(len(logged_events), 0)\n\n  def test_do_not_log_edit_event_for_hidden_file(self):\n    # Create the .git file under the monitoring dir.\n    self.root_monitoring_path.joinpath('.git').touch()\n    fake_cclient = FakeClearcutClient(\n        log_output_file=self.log_event_dir.joinpath('logs.output')\n    )\n    p = self._start_test_edit_monitor_process(fake_cclient)\n\n    # Create a hidden file.\n    self.root_monitoring_path.joinpath('.test.txt').touch()\n    # Create a hidden dir.\n    hidden_dir = self.root_monitoring_path.joinpath('.test')\n    hidden_dir.mkdir()\n    hidden_dir.joinpath('test.txt').touch()\n    # Give some time for the edit monitor to receive the edit event.\n    time.sleep(1)\n    # Stop the edit monitor and flush all events.\n    os.kill(p.pid, signal.SIGINT)\n    p.join()\n\n    logged_events = self._get_logged_events()\n    self.assertEqual(len(logged_events), 0)\n\n  def test_do_not_log_edit_event_for_non_git_project_file(self):\n    fake_cclient = FakeClearcutClient(\n        log_output_file=self.log_event_dir.joinpath('logs.output')\n    )\n    p = self._start_test_edit_monitor_process(fake_cclient)\n\n    # Create a file.\n    self.root_monitoring_path.joinpath('test.txt').touch()\n    # Create a file under a sub dir.\n    sub_dir = self.root_monitoring_path.joinpath('.test')\n    sub_dir.mkdir()\n    sub_dir.joinpath('test.txt').touch()\n    # Give some time for the edit monitor to receive the edit event.\n    time.sleep(1)\n    # Stop the edit monitor and flush all events.\n    os.kill(p.pid, signal.SIGINT)\n    p.join()\n\n    logged_events = self._get_logged_events()\n    self.assertEqual(len(logged_events), 0)\n\n  def test_log_edit_event_fail(self):\n    # Create the .git file under the monitoring dir.\n    self.root_monitoring_path.joinpath('.git').touch()\n    fake_cclient = FakeClearcutClient(\n        log_output_file=self.log_event_dir.joinpath('logs.output'),\n        raise_log_exception=True,\n    )\n    p = self._start_test_edit_monitor_process(fake_cclient)\n\n    # Create a file.\n    self.root_monitoring_path.joinpath('test.txt').touch()\n    # Give some time for the edit monitor to receive the edit event.\n    time.sleep(1)\n    # Stop the edit monitor and flush all events.\n    os.kill(p.pid, signal.SIGINT)\n    p.join()\n\n    logged_events = self._get_logged_events()\n    self.assertEqual(len(logged_events), 0)\n\n  def _start_test_edit_monitor_process(\n      self, cclient\n  ) -> multiprocessing.Process:\n    receiver, sender = multiprocessing.Pipe()\n    # Start edit monitor in a subprocess.\n    p = multiprocessing.Process(\n        target=edit_monitor.start,\n        args=(str(self.root_monitoring_path.resolve()), False, 0.5, 5, cclient, sender),\n    )\n    p.daemon = True\n    p.start()\n\n    # Wait until observer started.\n    received_data = receiver.recv()\n    self.assertEqual(received_data, 'Observer started.')\n\n    receiver.close()\n    return p\n\n  def _get_logged_events(self):\n    with open(self.log_event_dir.joinpath('logs.output'), 'rb') as f:\n      data = f.read()\n\n    return [\n        clientanalytics_pb2.LogEvent.FromString(record)\n        for record in data.split(b'\\x00')\n        if record\n    ]\n\n\nclass FakeClearcutClient:\n\n  def __init__(self, log_output_file, raise_log_exception=False):\n    self.pending_log_events = []\n    self.raise_log_exception = raise_log_exception\n    self.log_output_file = log_output_file\n\n  def log(self, log_event):\n    if self.raise_log_exception:\n      raise Exception('unknown exception')\n    self.pending_log_events.append(log_event)\n\n  def flush_events(self):\n    delimiter = b'\\x00'  # Use a null byte as the delimiter\n    with open(self.log_output_file, 'wb') as f:\n      for log_event in self.pending_log_events:\n        f.write(log_event.SerializeToString() + delimiter)\n\n    self.pending_log_events.clear()\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "tools/edit_monitor/main.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport logging\nimport os\nimport signal\nimport sys\nimport tempfile\n\nfrom edit_monitor import daemon_manager\nfrom edit_monitor import edit_monitor\n\n\ndef create_arg_parser():\n  \"\"\"Creates an instance of the default arg parser.\"\"\"\n\n  parser = argparse.ArgumentParser(\n      description=(\n          'Monitors edits in Android source code and uploads the edit logs.'\n      ),\n      add_help=True,\n      formatter_class=argparse.RawDescriptionHelpFormatter,\n  )\n\n  parser.add_argument(\n      '--path',\n      type=str,\n      required=True,\n      help='Root path to monitor the edit events.',\n  )\n\n  parser.add_argument(\n      '--dry_run',\n      action='store_true',\n      help='Dry run the edit monitor. This starts the edit monitor process without actually send the edit logs to clearcut.',\n  )\n\n  parser.add_argument(\n      '--force_cleanup',\n      action='store_true',\n      help=(\n          'Instead of start a new edit monitor, force stop all existing edit'\n          ' monitors in the system. This option is only used in emergent cases'\n          ' when we want to prevent user damage by the edit monitor.'\n      ),\n  )\n\n  parser.add_argument(\n      '--verbose',\n      action='store_true',\n      help=(\n          'Log verbose info in the log file for debugging purpose.'\n      ),\n  )\n\n  return parser\n\n\ndef configure_logging(verbose=False):\n  root_logging_dir = tempfile.mkdtemp(prefix='edit_monitor_')\n  _, log_path = tempfile.mkstemp(dir=root_logging_dir, suffix='.log')\n\n\n  log_fmt = '%(asctime)s.%(msecs)03d %(filename)s:%(lineno)s:%(levelname)s: %(message)s'\n  date_fmt = '%Y-%m-%d %H:%M:%S'\n  log_level = logging.DEBUG if verbose else logging.INFO\n\n  logging.basicConfig(\n      filename=log_path, level=log_level, format=log_fmt, datefmt=date_fmt\n  )\n  # Filter out logs from inotify_buff to prevent log pollution.\n  logging.getLogger('watchdog.observers.inotify_buffer').addFilter(\n      lambda record: record.filename != 'inotify_buffer.py')\n  print(f'logging to file {log_path}')\n\n\ndef term_signal_handler(_signal_number, _frame):\n  logging.info('Process %d received SIGTERM, Terminating...', os.getpid())\n  sys.exit(0)\n\n\ndef main(argv: list[str]):\n  args = create_arg_parser().parse_args(argv[1:])\n  configure_logging(args.verbose)\n  if args.dry_run:\n    logging.info('This is a dry run.')\n  dm = daemon_manager.DaemonManager(\n      binary_path=argv[0],\n      daemon_target=edit_monitor.start,\n      daemon_args=(args.path, args.dry_run),\n  )\n\n  try:\n    if args.force_cleanup:\n      dm.cleanup()\n    else:\n      dm.start()\n      dm.monitor_daemon()\n  except Exception:\n    logging.exception('Unexpected exception raised when run daemon.')\n  finally:\n    dm.stop()\n\n\nif __name__ == '__main__':\n  signal.signal(signal.SIGTERM, term_signal_handler)\n  main(sys.argv)\n"
  },
  {
    "path": "tools/edit_monitor/proto/edit_event.proto",
    "content": "syntax = \"proto3\";\n\npackage tools.asuite.edit_monitor;\n\nmessage EditEvent {\n  enum EditType {\n    UNSUPPORTED_TYPE = 0;\n    CREATE = 1;\n    MODIFY = 2;\n    DELETE = 3;\n    MOVE = 4;\n  }\n\n  enum ErrorType {\n    UNKNOWN_ERROR = 0;\n    FAILED_TO_START_EDIT_MONITOR = 1;\n    FAILED_TO_STOP_EDIT_MONITOR = 2;\n    FAILED_TO_REBOOT_EDIT_MONITOR = 3;\n    KILLED_DUE_TO_EXCEEDED_MEMORY_USAGE = 4;\n    FORCE_CLEANUP = 5;\n    KILLED_DUE_TO_EXCEEDED_CPU_USAGE = 6;\n  }\n\n  // Event that logs a single edit\n  message SingleEditEvent {\n    // Full path of the file that edited.\n    string file_path = 1;\n    // Type of the edit.\n    EditType edit_type = 2;\n  }\n\n  // Event that logs aggregated info for a set of edits.\n  message AggregatedEditEvent {\n    int32 num_edits = 1;\n  }\n\n  // Event that logs errors happened in the edit monitor.\n  message EditMonitorErrorEvent {\n    ErrorType error_type = 1;\n  }\n\n  // ------------------------\n  // FIELDS FOR EditEvent\n  // ------------------------\n  // Internal user name.\n  string user_name = 1;\n  // The root of Android source.\n  string source_root = 2;\n  // Name of the host workstation.\n  string host_name = 3;\n\n  oneof event {\n    SingleEditEvent single_edit_event = 4;\n    AggregatedEditEvent aggregated_edit_event = 5;\n    EditMonitorErrorEvent edit_monitor_error_event = 6;\n  }\n}\n"
  },
  {
    "path": "tools/edit_monitor/utils.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport hashlib\nimport logging\nimport os\n\n\ndef is_feature_enabled(\n    feature_name: str,\n    user_name: str,\n    enable_flag: str = None,\n    rollout_percent: int = 100,\n) -> bool:\n  \"\"\"Determine whether the given feature is enabled.\n\n  Whether a given feature is enabled or not depends on two flags: 1) the\n  enable_flag that explicitly enable/disable the feature and 2) the rollout_flag\n  that controls the rollout percentage.\n\n  Args:\n    feature_name: name of the feature.\n    user_name: system user name.\n    enable_flag: name of the env var that enables/disables the feature\n      explicitly.\n    rollout_flg: name of the env var that controls the rollout percentage, the\n      value stored in the env var should be an int between 0 and 100 string\n  \"\"\"\n  if enable_flag:\n    if os.environ.get(enable_flag, \"\") == \"false\":\n      logging.info(\"feature: %s is disabled\", feature_name)\n      return False\n\n    if os.environ.get(enable_flag, \"\") == \"true\":\n      logging.info(\"feature: %s is enabled\", feature_name)\n      return True\n\n  hash_object = hashlib.sha256()\n  hash_object.update((user_name + feature_name).encode(\"utf-8\"))\n  hash_number = int(hash_object.hexdigest(), 16) % 100\n\n  return hash_number < rollout_percent\n"
  },
  {
    "path": "tools/edit_monitor/utils_test.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Unittests for edit monitor utils.\"\"\"\nimport os\nimport unittest\nfrom unittest import mock\n\nfrom edit_monitor import utils\n\nTEST_USER = 'test_user'\nTEST_FEATURE = 'test_feature'\nENABLE_TEST_FEATURE_FLAG = 'ENABLE_TEST_FEATURE'\nROLLOUT_TEST_FEATURE_FLAG = 'ROLLOUT_TEST_FEATURE'\n\n\nclass EnableFeatureTest(unittest.TestCase):\n\n  def test_feature_enabled_without_flag(self):\n    self.assertTrue(utils.is_feature_enabled(TEST_FEATURE, TEST_USER))\n\n  @mock.patch.dict(os.environ, {ENABLE_TEST_FEATURE_FLAG: 'false'}, clear=True)\n  def test_feature_disabled_with_flag(self):\n    self.assertFalse(\n        utils.is_feature_enabled(\n            TEST_FEATURE, TEST_USER, ENABLE_TEST_FEATURE_FLAG\n        )\n    )\n\n  @mock.patch.dict(os.environ, {ENABLE_TEST_FEATURE_FLAG: 'true'}, clear=True)\n  def test_feature_enabled_with_flag(self):\n    self.assertTrue(\n        utils.is_feature_enabled(\n            TEST_FEATURE, TEST_USER, ENABLE_TEST_FEATURE_FLAG\n        )\n    )\n\n  def test_feature_enabled_with_rollout_percentage(self):\n    self.assertTrue(\n        utils.is_feature_enabled(\n            TEST_FEATURE,\n            TEST_USER,\n            ENABLE_TEST_FEATURE_FLAG,\n            90,\n        )\n    )\n\n  def test_feature_disabled_with_rollout_percentage(self):\n    self.assertFalse(\n        utils.is_feature_enabled(\n            TEST_FEATURE,\n            TEST_USER,\n            ENABLE_TEST_FEATURE_FLAG,\n            10,\n        )\n    )\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "tools/envsetup/run_envsetup_tests",
    "content": "#!/usr/bin/env python3\n\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport pathlib\nimport subprocess\nimport sys\n\nSOURCE_ENVSETUP=\"source build/make/envsetup.sh && \"\n\ndef update_display():\n    sys.stderr.write(\"passed\\n\")\n\ndef go_to_root():\n    while True:\n        if os.path.exists(\"build/make/envsetup.sh\"):\n            return\n        if os.getcwd() == \"/\":\n            sys.stderr.write(\"Can't find root of the source tree\\n\");\n            print(\"\\nFAILED\")\n            sys.exit(1)\n        os.chdir(\"..\")\n\ndef is_test(name, thing):\n    if not callable(thing):\n        return False\n    if name == \"test\":\n        return False\n    return name.startswith(\"test\")\n\n\ndef test(shell, command, expected_return, expected_stdout, expected_stderr, expected_env):\n    command += \"; _rc=$?\"\n    for env in expected_env.keys():\n        command += f\"; echo ENV: {env}=\\\\\\\"${env}\\\\\\\"\"\n    command += \"; exit $_rc\"\n\n    cmd = [shell, \"-c\", command]\n    result = subprocess.run(cmd, capture_output=True, text=True)\n\n    status = True\n\n    if result.returncode != expected_return:\n        print()\n        print(f\"Expected return code: {expected_return}\")\n        print(f\"Actual return code:   {result.returncode}\")\n        status = False\n\n    printed_stdout = False\n    if expected_stdout and expected_stdout not in result.stdout:\n        print()\n        print(f\"Expected stdout to contain:\\n{expected_stdout}\")\n        print(f\"\\nActual stdout:\\n{result.stdout}\")\n        printed_stdout = True\n        status = False\n\n    if expected_stderr and expected_stderr not in result.stderr:\n        print()\n        print(f\"Expected stderr to contain:\\n{expected_stderr}\")\n        print(f\"\\nActual stderr:\\n{result.stderr}\")\n        status = False\n\n    env_failure = False\n    for k, v in expected_env.items():\n        if f\"{k}=\\\"{v}\\\"\" not in result.stdout:\n            print()\n            print(f\"Expected environment variable {k} to be: {v} --- {k}=\\\"{v}\\\"\")\n            env_failure = True\n            status = False\n\n    if env_failure and not printed_stdout:\n        print()\n        print(\"See stdout:\")\n        print(result.stdout)\n\n    if not status:\n        print()\n        print(\"Command to reproduce:\")\n        print(command)\n        print()\n\n    return status\n\nNO_LUNCH = {\n    \"TARGET_PRODUCT\": \"\",\n    \"TARGET_RELEASE\": \"\",\n    \"TARGET_BUILD_VARIANT\": \"\",\n}\n\ndef test_invalid_lunch_target(shell):\n    return test(shell, SOURCE_ENVSETUP + \"lunch invalid-trunk_staging-eng\",\n         expected_return=1, expected_stdout=None,\n         expected_stderr=\"Cannot locate config makefile for product\",\n         expected_env=NO_LUNCH)\n\n\ndef test_aosp_arm(shell):\n    return test(shell, SOURCE_ENVSETUP + \"lunch aosp_arm-trunk_staging-eng\",\n         expected_return=0, expected_stdout=None, expected_stderr=None,\n         expected_env={\n            \"TARGET_PRODUCT\": \"aosp_arm\",\n            \"TARGET_RELEASE\": \"trunk_staging\",\n            \"TARGET_BUILD_VARIANT\": \"eng\",\n        })\n\n\ndef test_lunch2_empty(shell):\n    return test(shell, SOURCE_ENVSETUP + \"lunch2\",\n         expected_return=1, expected_stdout=None,\n         expected_stderr=\"No target specified. See lunch --help\",\n         expected_env=NO_LUNCH)\n\ndef test_lunch2_four_params(shell):\n    return test(shell, SOURCE_ENVSETUP + \"lunch2 a b c d\",\n         expected_return=1, expected_stdout=None,\n         expected_stderr=\"Too many parameters given. See lunch --help\",\n         expected_env=NO_LUNCH)\n\ndef test_lunch2_aosp_arm(shell):\n    return test(shell, SOURCE_ENVSETUP + \"lunch2 aosp_arm\",\n         expected_return=0, expected_stdout=\"=========\", expected_stderr=None,\n         expected_env={\n            \"TARGET_PRODUCT\": \"aosp_arm\",\n            \"TARGET_RELEASE\": \"trunk_staging\",\n            \"TARGET_BUILD_VARIANT\": \"eng\",\n        })\n\ndef test_lunch2_aosp_arm_trunk_staging(shell):\n    # Somewhat unfortunate because trunk_staging is the only config in\n    # aosp so we can't really test that this isn't just getting the default\n    return test(shell, SOURCE_ENVSETUP + \"lunch2 aosp_arm trunk_staging\",\n         expected_return=0, expected_stdout=\"=========\", expected_stderr=None,\n         expected_env={\n            \"TARGET_PRODUCT\": \"aosp_arm\",\n            \"TARGET_RELEASE\": \"trunk_staging\",\n            \"TARGET_BUILD_VARIANT\": \"eng\",\n        })\n\ndef test_lunch2_aosp_arm_trunk_staging_userdebug(shell):\n    return test(shell, SOURCE_ENVSETUP + \"lunch2 aosp_arm trunk_staging userdebug\",\n         expected_return=0, expected_stdout=\"=========\", expected_stderr=None,\n         expected_env={\n            \"TARGET_PRODUCT\": \"aosp_arm\",\n            \"TARGET_RELEASE\": \"trunk_staging\",\n            \"TARGET_BUILD_VARIANT\": \"userdebug\",\n        })\n\ndef test_list_products(shell):\n    return test(shell, \"build/soong/bin/list_products\",\n         expected_return=0, expected_stdout=\"aosp_arm\", expected_stderr=None,\n         expected_env=NO_LUNCH)\n\ndef test_list_releases_param(shell):\n    return test(shell, \"build/soong/bin/list_releases aosp_arm\",\n         expected_return=0, expected_stdout=\"trunk_staging\", expected_stderr=None,\n         expected_env=NO_LUNCH)\n\ndef test_list_releases_env(shell):\n    return test(shell, \"TARGET_PRODUCT=aosp_arm build/soong/bin/list_releases\",\n         expected_return=0, expected_stdout=\"trunk_staging\", expected_stderr=None,\n         expected_env=NO_LUNCH)\n\ndef test_list_releases_no_product(shell):\n    return test(shell, \"build/soong/bin/list_releases\",\n         expected_return=1, expected_stdout=None, expected_stderr=None,\n         expected_env=NO_LUNCH)\n\ndef test_list_variants(shell):\n    return test(shell, \"build/soong/bin/list_variants\",\n         expected_return=0, expected_stdout=\"userdebug\", expected_stderr=None,\n         expected_env=NO_LUNCH)\n\n\ndef test_get_build_var_in_path(shell):\n    return test(shell, SOURCE_ENVSETUP + \"which get_build_var \",\n         expected_return=0, expected_stdout=\"soong/bin\", expected_stderr=None,\n         expected_env=NO_LUNCH)\n\n\n\nTESTS=sorted([(name, thing) for name, thing in locals().items() if is_test(name, thing)])\n\ndef main():\n    if any([x.endswith(\"/soong/bin\") for x in os.getenv(\"PATH\").split(\":\")]):\n        sys.stderr.write(\"run_envsetup_tests must be run in a shell that has not sourced\"\n                + \" envsetup.sh\\n\\nFAILED\\n\")\n        return 1\n\n    go_to_root()\n\n    tests = TESTS\n    if len(sys.argv) > 1:\n        tests = [(name, func) for name, func in tests if name in sys.argv]\n\n    shells = [\"/usr/bin/bash\", \"/usr/bin/zsh\"]\n    total_count = len(tests) * len(shells)\n    index = 1\n    failed_tests = 0\n\n    for name, func in tests:\n        for shell in shells:\n            sys.stdout.write(f\"\\33[2K\\r{index} of {total_count}: {name} in {shell}\")\n            passed = func(shell)\n            if not passed:\n                failed_tests += 1\n            index += 1\n\n    if failed_tests > 0:\n        print(f\"\\n\\nFAILED: {failed_tests} of {total_count}\")\n        return 1\n    else:\n        print(\"\\n\\nSUCCESS\")\n        return 0\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "tools/envsetup/spam_for_lunch",
    "content": "#!/bin/bash\n\n# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This ad is kind of big, so only show it if this appears to be a clean build.\nsource $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../shell_utils.sh\nif [[ ! -e $(getoutdir)/soong/build.${TARGET_PRODUCT}.ninja ]]; then\n  echo\n  echo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n  echo \"  Wondering whether to use user, userdebug or eng?\"\n  echo\n  echo \"  user        The builds that ship to users. Reduced debugability.\"\n  echo \"  userdebug   High fidelity to user builds but with some debugging options\"\n  echo \"              enabled. Best suited for performance testing or day-to-day use\"\n  echo \"              with debugging enabled.\"\n  echo \"  eng         More debugging options enabled and faster build times, but\"\n  echo \"              runtime performance tradeoffs. Best suited for day-to-day\"\n  echo \"              local development when not doing performance testing.\"\n  echo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n  echo\nfi\n\n"
  },
  {
    "path": "tools/event_log_tags.py",
    "content": "# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"A module for reading and parsing event-log-tags files.\"\"\"\n\nimport dataclasses\nimport re\nimport sys\nfrom typing import Optional\n\n@dataclasses.dataclass\nclass Tag:\n  tagnum: int\n  tagname: str\n  description: Optional[str]\n  filename: str\n  linenum: int\n\n\nclass TagFile:\n  \"\"\"Read an input event-log-tags file.\"\"\"\n  def AddError(self, msg, linenum=None):\n    if linenum is None:\n      linenum = self.linenum\n    self.errors.append((self.filename, linenum, msg))\n\n  def AddWarning(self, msg, linenum=None):\n    if linenum is None:\n      linenum = self.linenum\n    self.warnings.append((self.filename, linenum, msg))\n\n  def __init__(self, filename, file_object=None):\n    \"\"\"'filename' is the name of the file (included in any error\n    messages).  If 'file_object' is None, 'filename' will be opened\n    for reading.\"\"\"\n    self.errors = []\n    self.warnings = []\n    self.tags = []\n    self.options = {}\n\n    self.filename = filename\n    self.linenum = 0\n\n    if file_object is None:\n      try:\n        file_object = open(filename, \"rb\")\n      except (IOError, OSError) as e:\n        self.AddError(str(e))\n        return\n\n    try:\n      for self.linenum, line in enumerate(file_object):\n        line = line.decode('utf-8')\n        self.linenum += 1\n        line = re.sub('#.*$', '', line) # strip trailing comments\n        line = line.strip()\n        if not line: continue\n        parts = re.split(r\"\\s+\", line, 2)\n\n        if len(parts) < 2:\n          self.AddError(\"failed to parse \\\"%s\\\"\" % (line,))\n          continue\n\n        if parts[0] == \"option\":\n          self.options[parts[1]] = parts[2:]\n          continue\n\n        try:\n          tag = int(parts[0])\n        except ValueError:\n          self.AddError(\"\\\"%s\\\" isn't an integer tag\" % (parts[0],))\n          continue\n\n        tagname = parts[1]\n        if len(parts) == 3:\n          description = parts[2]\n        else:\n          description = None\n\n        if description:\n          # EventLog.java checks that the description field is\n          # surrounded by parens, so we should too (to avoid a runtime\n          # crash from badly-formatted descriptions).\n          if not re.match(r\"\\(.*\\)\\s*$\", description):\n            self.AddError(\"tag \\\"%s\\\" has unparseable description\" % (tagname,))\n            continue\n\n        self.tags.append(Tag(tag, tagname, description,\n                             self.filename, self.linenum))\n    except (IOError, OSError) as e:\n      self.AddError(str(e))\n\n\ndef BooleanFromString(s):\n  \"\"\"Interpret 's' as a boolean and return its value.  Raise\n  ValueError if it's not something we can interpret as true or\n  false.\"\"\"\n  s = s.lower()\n  if s in (\"true\", \"t\", \"1\", \"on\", \"yes\", \"y\"):\n    return True\n  if s in (\"false\", \"f\", \"0\", \"off\", \"no\", \"n\"):\n    return False\n  raise ValueError(\"'%s' not a valid boolean\" % (s,))\n\n\ndef WriteOutput(output_file, data):\n  \"\"\"Write 'data' to the given output filename (which may be None to\n  indicate stdout).  Emit an error message and die on any failure.\n  'data' may be a string or a StringIO object.\"\"\"\n  if not isinstance(data, str):\n    data = data.getvalue()\n  try:\n    if output_file is None:\n      out = sys.stdout\n      output_file = \"<stdout>\"\n    else:\n      out = open(output_file, \"w\")\n    out.write(data)\n    out.close()\n  except (IOError, OSError) as e:\n    print(\"failed to write %s: %s\" % (output_file, e), file=sys.stderr)\n    sys.exit(1)\n"
  },
  {
    "path": "tools/exercise_compare_builds",
    "content": "#!/bin/bash\n# Tests for compare_builds.py\n# usage (from root of source tree):\n#   build/make/tools/exercise_compare_builds\n\nHIDE_BUILD_OUTPUT=--hide-build-output\n\nfunction run()\n{\n    echo\n    echo\n    echo ============================================================\n    echo $1\n    shift\n    echo ./build/make/tools/compare_builds.py $HIDE_BUILD_OUTPUT --target incidentd $@\n    echo ============================================================\n    time ./build/make/tools/compare_builds.py $HIDE_BUILD_OUTPUT --target incidentd $@\n}\n\nfunction run_tests()\n{\n    # These should error out\n\n    run \"Incremental build,  Separate work dirs  (invalid flag combo, should error out)\" \\\n        --incremental --detect-embedded-paths\n    run \"Use out/ as work dir, Separate work dirs  (invalid flag combo, should error out)\" \\\n        --no-check-out-dir --detect-embedded-paths\n\n    # Each grouping starts with a build, and the following ones use --no-build to save time\n\n    run \"REBUILD: Full builds,  Same work dir,  Whole out dir\"\n    run \"Full builds,  Same work dir,  Default subdirs\" \\\n        --no-build --subdirs\n    run \"Full builds,  Same work dir,  Only $PRODUCT_OUT/system\" \\\n        --no-build --subdirs system\n\n    run \"REBUILD: Full builds,  Use out/ as work dir,  Whole out dir\" \\\n        --no-check-out-dir\n    run \"Full builds,  Use out/ as work dir,  Default subdirs\" \\\n        --no-build --no-check-out-dir --subdirs\n    run \"Full builds,  Use out/ as work dir,  Only $PRODUCT_OUT/system\" \\\n        --no-build --no-check-out-dir --subdirs system\n\n    run \"REBUILD: Full builds,  Separate work dirs,  Whole out dir\" \\\n        --detect-embedded-paths\n    run \"Full builds,  Separate work dirs,  Default subdirs\" \\\n        --no-build --detect-embedded-paths --subdirs\n    run \"Full builds,  Separate work dirs,  Only $PRODUCT_OUT/system\" \\\n        --no-build --detect-embedded-paths --subdirs system\n\n    run \"REBUILD: Incremental build,  Same work dir,  Whole out dir\" \\\n        --incremental\n    run \"Incremental build,  Same work dir,  Default subdirs\" \\\n        --no-build --incremental --subdirs\n    run \"Incremental build,  Same work dir,  Only $PRODUCT_OUT/system\" \\\n        --no-build --incremental --subdirs system\n\n    run \"REBUILD: Incremental build,  Use out/ as work dir,  Whole out dir\" \\\n        --incremental --no-check-out-dir\n    run \"Incremental build,  Use out/ as work dir,  Default subdirs\" \\\n        --no-build --incremental --no-check-out-dir --subdirs\n    run \"Incremental build,  Use out/ as work dir,  Only $PRODUCT_OUT/system\" \\\n        --no-build --incremental --no-check-out-dir --subdirs system\n}\n\ntime run_tests 2>&1 | tee exercise_compare_builds.txt\n"
  },
  {
    "path": "tools/extract_kernel.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nA tool to extract kernel information from a kernel image.\n\"\"\"\n\nimport argparse\nimport subprocess\nimport sys\nimport re\n\nCONFIG_PREFIX = b'IKCFG_ST'\nGZIP_HEADER = b'\\037\\213\\010'\nCOMPRESSION_ALGO = (\n    ([\"gzip\", \"-d\"], GZIP_HEADER),\n    ([\"xz\", \"-d\"], b'\\3757zXZ\\000'),\n    ([\"bzip2\", \"-d\"], b'BZh'),\n    ([\"lz4\", \"-d\", \"-l\"], b'\\002\\041\\114\\030'),\n\n    # These are not supported in the build system yet.\n    # ([\"unlzma\"], b'\\135\\0\\0\\0'),\n    # ([\"lzop\", \"-d\"], b'\\211\\114\\132'),\n)\n\n# \"Linux version \" UTS_RELEASE \" (\" LINUX_COMPILE_BY \"@\"\n# LINUX_COMPILE_HOST \") (\" LINUX_COMPILER \") \" UTS_VERSION \"\\n\";\nLINUX_BANNER_PREFIX = b'Linux version '\nLINUX_BANNER_REGEX = LINUX_BANNER_PREFIX.decode() + \\\n    r'(?P<release>(?P<version>[0-9]+[.][0-9]+[.][0-9]+).*) \\(.*@.*\\) \\((?P<compiler>.*)\\) .*\\n'\n\n\ndef get_from_release(input_bytes, start_idx, key):\n  null_idx = input_bytes.find(b'\\x00', start_idx)\n  if null_idx < 0:\n    return None\n  try:\n    linux_banner = input_bytes[start_idx:null_idx].decode()\n  except UnicodeDecodeError:\n    return None\n  mo = re.match(LINUX_BANNER_REGEX, linux_banner)\n  if mo:\n    return mo.group(key)\n  return None\n\n\ndef dump_from_release(input_bytes, key):\n  \"\"\"\n  Helper of dump_version and dump_release\n  \"\"\"\n  idx = 0\n  while True:\n    idx = input_bytes.find(LINUX_BANNER_PREFIX, idx)\n    if idx < 0:\n      return None\n\n    value = get_from_release(input_bytes, idx, key)\n    if value:\n      return value.encode()\n\n    idx += len(LINUX_BANNER_PREFIX)\n\n\ndef dump_version(input_bytes):\n  \"\"\"\n  Dump kernel version, w.x.y, from input_bytes. Search for the string\n  \"Linux version \" and do pattern matching after it. See LINUX_BANNER_REGEX.\n  \"\"\"\n  return dump_from_release(input_bytes, \"version\")\n\n\ndef dump_compiler(input_bytes):\n  \"\"\"\n  Dump kernel version, w.x.y, from input_bytes. Search for the string\n  \"Linux version \" and do pattern matching after it. See LINUX_BANNER_REGEX.\n  \"\"\"\n  return dump_from_release(input_bytes, \"compiler\")\n\n\ndef dump_release(input_bytes):\n  \"\"\"\n  Dump kernel release, w.x.y-..., from input_bytes. Search for the string\n  \"Linux version \" and do pattern matching after it. See LINUX_BANNER_REGEX.\n  \"\"\"\n  return dump_from_release(input_bytes, \"release\")\n\n\ndef dump_configs(input_bytes):\n  \"\"\"\n  Dump kernel configuration from input_bytes. This can be done when\n  CONFIG_IKCONFIG is enabled, which is a requirement on Treble devices.\n\n  The kernel configuration is archived in GZip format right after the magic\n  string 'IKCFG_ST' in the built kernel.\n  \"\"\"\n\n  # Search for magic string + GZip header\n  idx = input_bytes.find(CONFIG_PREFIX + GZIP_HEADER)\n  if idx < 0:\n    return None\n\n  # Seek to the start of the archive\n  idx += len(CONFIG_PREFIX)\n\n  sp = subprocess.Popen([\"gzip\", \"-d\", \"-c\"], stdin=subprocess.PIPE,\n                        stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n  o, _ = sp.communicate(input=input_bytes[idx:])\n  if sp.returncode == 1: # error\n    return None\n\n  # success or trailing garbage warning\n  assert sp.returncode in (0, 2), sp.returncode\n\n  return o\n\n\ndef try_decompress_bytes(cmd, input_bytes):\n  sp = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,\n                        stderr=subprocess.PIPE)\n  o, _ = sp.communicate(input=input_bytes)\n  # ignore errors\n  return o\n\n\ndef try_decompress(cmd, search_bytes, input_bytes):\n  idx = 0\n  while True:\n    idx = input_bytes.find(search_bytes, idx)\n    if idx < 0:\n      return\n\n    yield try_decompress_bytes(cmd, input_bytes[idx:])\n    idx += 1\n\n\ndef decompress_dump(func, input_bytes):\n  \"\"\"\n  Run func(input_bytes) first; and if that fails (returns value evaluates to\n  False), then try different decompression algorithm before running func.\n  \"\"\"\n  o = func(input_bytes)\n  if o:\n    return o\n  for cmd, search_bytes in COMPRESSION_ALGO:\n    for decompressed in try_decompress(cmd, search_bytes, input_bytes):\n      if decompressed:\n        o = decompress_dump(func, decompressed)\n        if o:\n          return o\n    # Force decompress the whole file even if header doesn't match\n    decompressed = try_decompress_bytes(cmd, input_bytes)\n    if decompressed:\n      o = decompress_dump(func, decompressed)\n      if o:\n        return o\n\n\ndef dump_to_file(f, dump_fn, input_bytes, desc):\n  \"\"\"\n  Call decompress_dump(dump_fn, input_bytes) and write to f. If it fails, return\n  False; otherwise return True.\n  \"\"\"\n  if f is not None:\n    o = decompress_dump(dump_fn, input_bytes)\n    if o:\n      f.write(o)\n    else:\n      sys.stderr.write(\n          \"Cannot extract kernel {}\".format(desc))\n      return False\n  return True\n\ndef to_bytes_io(b):\n  \"\"\"\n  Make b, which is either sys.stdout or sys.stdin, receive bytes as arguments.\n  \"\"\"\n  return b.buffer if sys.version_info.major == 3 else b\n\ndef main():\n  parser = argparse.ArgumentParser(\n      formatter_class=argparse.RawTextHelpFormatter,\n      description=__doc__ +\n      \"\\nThese algorithms are tried when decompressing the image:\\n    \" +\n      \" \".join(tup[0][0] for tup in COMPRESSION_ALGO))\n  parser.add_argument('--input',\n                      help='Input kernel image. If not specified, use stdin',\n                      metavar='FILE',\n                      type=argparse.FileType('rb'),\n                      default=to_bytes_io(sys.stdin))\n  parser.add_argument('--output-configs',\n                      help='If specified, write configs. Use stdout if no file '\n                           'is specified.',\n                      metavar='FILE',\n                      nargs='?',\n                      type=argparse.FileType('wb'),\n                      const=to_bytes_io(sys.stdout))\n  parser.add_argument('--output-version',\n                      help='If specified, write version. Use stdout if no file '\n                           'is specified.',\n                      metavar='FILE',\n                      nargs='?',\n                      type=argparse.FileType('wb'),\n                      const=to_bytes_io(sys.stdout))\n  parser.add_argument('--output-release',\n                      help='If specified, write kernel release. Use stdout if '\n                           'no file is specified.',\n                      metavar='FILE',\n                      nargs='?',\n                      type=argparse.FileType('wb'),\n                      const=to_bytes_io(sys.stdout))\n  parser.add_argument('--output-compiler',\n                      help='If specified, write the compiler information. Use stdout if no file '\n                           'is specified.',\n                      metavar='FILE',\n                      nargs='?',\n                      type=argparse.FileType('wb'),\n                      const=to_bytes_io(sys.stdout))\n  parser.add_argument('--tools',\n                      help='Decompression tools to use. If not specified, PATH '\n                           'is searched.',\n                      metavar='ALGORITHM:EXECUTABLE',\n                      nargs='*')\n  args = parser.parse_args()\n\n  tools = {pair[0]: pair[1]\n           for pair in (token.split(':') for token in args.tools or [])}\n  for cmd, _ in COMPRESSION_ALGO:\n    if cmd[0] in tools:\n      cmd[0] = tools[cmd[0]]\n\n  input_bytes = args.input.read()\n\n  ret = 0\n  if not dump_to_file(args.output_configs, dump_configs, input_bytes,\n                      \"configs in {}\".format(args.input.name)):\n    ret = 1\n  if not dump_to_file(args.output_version, dump_version, input_bytes,\n                      \"version in {}\".format(args.input.name)):\n    ret = 1\n  if not dump_to_file(args.output_release, dump_release, input_bytes,\n                      \"kernel release in {}\".format(args.input.name)):\n    ret = 1\n\n  if not dump_to_file(args.output_compiler, dump_compiler, input_bytes,\n                      \"kernel compiler in {}\".format(args.input.name)):\n    ret = 1\n\n  return ret\n\n\nif __name__ == '__main__':\n  sys.exit(main())\n"
  },
  {
    "path": "tools/fat16copy.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright 2016 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport sys\nimport struct\n\nFAT_TABLE_START = 0x200\nDEL_MARKER = 0xe5\nESCAPE_DEL_MARKER = 0x05\n\nATTRIBUTE_READ_ONLY = 0x1\nATTRIBUTE_HIDDEN = 0x2\nATTRIBUTE_SYSTEM = 0x4\nATTRIBUTE_VOLUME_LABEL = 0x8\nATTRIBUTE_SUBDIRECTORY = 0x10\nATTRIBUTE_ARCHIVE = 0x20\nATTRIBUTE_DEVICE = 0x40\n\nLFN_ATTRIBUTES = \\\n    ATTRIBUTE_VOLUME_LABEL | \\\n    ATTRIBUTE_SYSTEM | \\\n    ATTRIBUTE_HIDDEN | \\\n    ATTRIBUTE_READ_ONLY\nLFN_ATTRIBUTES_BYTE = struct.pack(\"B\", LFN_ATTRIBUTES)\n\nMAX_CLUSTER_ID = 0x7FFF\n\ndef read_le_short(f):\n  \"Read a little-endian 2-byte integer from the given file-like object\"\n  return struct.unpack(\"<H\", f.read(2))[0]\n\ndef read_le_long(f):\n  \"Read a little-endian 4-byte integer from the given file-like object\"\n  return struct.unpack(\"<L\", f.read(4))[0]\n\ndef read_byte(f):\n  \"Read a 1-byte integer from the given file-like object\"\n  return struct.unpack(\"B\", f.read(1))[0]\n\ndef skip_bytes(f, n):\n  \"Fast-forward the given file-like object by n bytes\"\n  f.seek(n, os.SEEK_CUR)\n\ndef skip_short(f):\n  \"Fast-forward the given file-like object 2 bytes\"\n  skip_bytes(f, 2)\n\ndef skip_byte(f):\n  \"Fast-forward the given file-like object 1 byte\"\n  skip_bytes(f, 1)\n\ndef rewind_bytes(f, n):\n  \"Rewind the given file-like object n bytes\"\n  skip_bytes(f, -n)\n\ndef rewind_short(f):\n  \"Rewind the given file-like object 2 bytes\"\n  rewind_bytes(f, 2)\n\nclass fake_file(object):\n  \"\"\"\n  Interface for python file-like objects that we use to manipulate the image.\n  Inheritors must have an idx member which indicates the file pointer, and a\n  size member which indicates the total file size.\n  \"\"\"\n\n  def seek(self, amount, direction=0):\n    \"Implementation of seek from python's file-like object interface.\"\n    if direction == os.SEEK_CUR:\n      self.idx += amount\n    elif direction == os.SEEK_END:\n      self.idx = self.size - amount\n    else:\n      self.idx = amount\n\n    if self.idx < 0:\n      self.idx = 0\n    if self.idx > self.size:\n      self.idx = self.size\n\nclass fat_file(fake_file):\n  \"\"\"\n  A file inside of our fat image. The file may or may not have a dentry, and\n  if it does this object knows nothing about it. All we see is a valid cluster\n  chain.\n  \"\"\"\n\n  def __init__(self, fs, cluster, size=None):\n    \"\"\"\n    fs: The fat() object for the image this file resides in.\n    cluster: The first cluster of data for this file.\n    size: The size of this file. If not given, we use the total length of the\n          cluster chain that starts from the cluster argument.\n    \"\"\"\n    self.fs = fs\n    self.start_cluster = cluster\n    self.size = size\n\n    if self.size is None:\n      self.size = fs.get_chain_size(cluster)\n\n    self.idx = 0\n\n  def read(self, size):\n    \"Read method for pythonic file-like interface.\"\n    if self.idx + size > self.size:\n      size = self.size - self.idx\n    got = self.fs.read_file(self.start_cluster, self.idx, size)\n    self.idx += len(got)\n    return got\n\n  def write(self, data):\n    \"Write method for pythonic file-like interface.\"\n    self.fs.write_file(self.start_cluster, self.idx, data)\n    self.idx += len(data)\n\n    if self.idx > self.size:\n      self.size = self.idx\n\ndef shorten(name, index):\n  \"\"\"\n  Create a file short name from the given long name (with the extension already\n  removed). The index argument gives a disambiguating integer to work into the\n  name to avoid collisions.\n  \"\"\"\n  name = \"\".join(name.split('.')).upper()\n  postfix = \"~\" + str(index)\n  return name[:8 - len(postfix)] + postfix\n\nclass fat_dir(object):\n  \"A directory in our fat filesystem.\"\n\n  def __init__(self, backing):\n    \"\"\"\n    backing: A file-like object from which we can read dentry info. Should have\n    an fs member allowing us to get to the underlying image.\n    \"\"\"\n    self.backing = backing\n    self.dentries = []\n    to_read = self.backing.size / 32\n\n    self.backing.seek(0)\n\n    while to_read > 0:\n      (dent, consumed) = self.backing.fs.read_dentry(self.backing)\n      to_read -= consumed\n\n      if dent:\n        self.dentries.append(dent)\n\n  def __str__(self):\n    return \"\\n\".join([str(x) for x in self.dentries]) + \"\\n\"\n\n  def add_dentry(self, attributes, shortname, ext, longname, first_cluster,\n      size):\n    \"\"\"\n    Add a new dentry to this directory.\n    attributes: Attribute flags for this dentry. See the ATTRIBUTE_ constants\n                above.\n    shortname: Short name of this file. Up to 8 characters, no dots.\n    ext: Extension for this file. Up to 3 characters, no dots.\n    longname: The long name for this file, with extension. Largely unrestricted.\n    first_cluster: The first cluster in the cluster chain holding the contents\n                   of this file.\n    size: The size of this file. Set to 0 for subdirectories.\n    \"\"\"\n    new_dentry = dentry(self.backing.fs, attributes, shortname, ext,\n        longname, first_cluster, size)\n    new_dentry.commit(self.backing)\n    self.dentries.append(new_dentry)\n    return new_dentry\n\n  def make_short_name(self, name):\n    \"\"\"\n    Given a long file name, return an 8.3 short name as a tuple. Name will be\n    engineered not to collide with other such names in this folder.\n    \"\"\"\n    parts = name.rsplit('.', 1)\n\n    if len(parts) == 1:\n      parts.append('')\n\n    name = parts[0]\n    ext = parts[1].upper()\n\n    index = 1\n    shortened = shorten(name, index)\n\n    for dent in self.dentries:\n      assert dent.longname != name, \"File must not exist\"\n      if dent.shortname == shortened:\n        index += 1\n        shortened = shorten(name, index)\n\n    if len(name) <= 8 and len(ext) <= 3 and not '.' in name:\n      return (name.upper().ljust(8), ext.ljust(3))\n\n    return (shortened.ljust(8), ext[:3].ljust(3))\n\n  def new_file(self, name, data=None):\n    \"\"\"\n    Add a new regular file to this directory.\n    name: The name of the new file.\n    data: The contents of the new file. Given as a file-like object.\n    \"\"\"\n    size = 0\n    if data:\n      data.seek(0, os.SEEK_END)\n      size = data.tell()\n\n    # Empty files shouldn't have any clusters assigned.\n    chunk = self.backing.fs.allocate(size) if size > 0 else 0\n    (shortname, ext) = self.make_short_name(name)\n    self.add_dentry(0, shortname, ext, name, chunk, size)\n\n    if data is None:\n      return\n\n    data_file = fat_file(self.backing.fs, chunk, size)\n    data.seek(0)\n    data_file.write(data.read())\n\n  def open_subdirectory(self, name):\n    \"\"\"\n    Open a subdirectory of this directory with the given name. If the\n    subdirectory doesn't exist, a new one is created instead.\n    Returns a fat_dir().\n    \"\"\"\n    for dent in self.dentries:\n      if dent.longname == name:\n        return dent.open_directory()\n\n    chunk = self.backing.fs.allocate(1)\n    (shortname, ext) = self.make_short_name(name)\n    new_dentry = self.add_dentry(ATTRIBUTE_SUBDIRECTORY, shortname,\n            ext, name, chunk, 0)\n    result = new_dentry.open_directory()\n\n    parent_cluster = 0\n\n    if hasattr(self.backing, 'start_cluster'):\n      parent_cluster = self.backing.start_cluster\n\n    result.add_dentry(ATTRIBUTE_SUBDIRECTORY, '.', '', '', chunk, 0)\n    result.add_dentry(ATTRIBUTE_SUBDIRECTORY, '..', '', '', parent_cluster, 0)\n\n    return result\n\ndef lfn_checksum(name_data):\n  \"\"\"\n  Given the characters of an 8.3 file name (concatenated *without* the dot),\n  Compute a one-byte checksum which needs to appear in corresponding long file\n  name entries.\n  \"\"\"\n  assert len(name_data) == 11, \"Name data should be exactly 11 characters\"\n  name_data = struct.unpack(\"B\" * 11, name_data)\n\n  result = 0\n\n  for char in name_data:\n    last_bit = (result & 1) << 7\n    result = (result >> 1) | last_bit\n    result += char\n    result = result & 0xFF\n\n  return struct.pack(\"B\", result)\n\nclass dentry(object):\n  \"A directory entry\"\n  def __init__(self, fs, attributes, shortname, ext, longname,\n      first_cluster, size):\n    \"\"\"\n    fs: The fat() object for the image we're stored in.\n    attributes: The attribute flags for this dentry. See the ATTRIBUTE_ flags\n                above.\n    shortname: The short name stored in this dentry. Up to 8 characters, no\n               dots.\n    ext: The file extension stored in this dentry. Up to 3 characters, no\n         dots.\n    longname: The long file name stored in this dentry.\n    first_cluster: The first cluster in the cluster chain backing the file\n                   this dentry points to.\n    size: Size of the file this dentry points to. 0 for subdirectories.\n    \"\"\"\n    self.fs = fs\n    self.attributes = attributes\n    self.shortname = shortname\n    self.ext = ext\n    self.longname = longname\n    self.first_cluster = first_cluster\n    self.size = size\n\n  def name(self):\n    \"A friendly text file name for this dentry.\"\n    if self.longname:\n      return self.longname\n\n    if not self.ext or len(self.ext) == 0:\n      return self.shortname\n\n    return self.shortname + \".\" + self.ext\n\n  def __str__(self):\n    return self.name() + \" (\" + str(self.size) + \\\n      \" bytes @ \" + str(self.first_cluster) + \")\"\n\n  def is_directory(self):\n    \"Return whether this dentry points to a directory.\"\n    return (self.attributes & ATTRIBUTE_SUBDIRECTORY) != 0\n\n  def open_file(self):\n    \"Open the target of this dentry if it is a regular file.\"\n    assert not self.is_directory(), \"Cannot open directory as file\"\n    return fat_file(self.fs, self.first_cluster, self.size)\n\n  def open_directory(self):\n    \"Open the target of this dentry if it is a directory.\"\n    assert self.is_directory(), \"Cannot open file as directory\"\n    return fat_dir(fat_file(self.fs, self.first_cluster))\n\n  def longname_records(self, checksum):\n    \"\"\"\n    Get the longname records necessary to store this dentry's long name,\n    packed as a series of 32-byte strings.\n    \"\"\"\n    if self.longname is None:\n      return []\n    if len(self.longname) == 0:\n      return []\n\n    encoded_long_name = self.longname.encode('utf-16-le')\n    long_name_padding = \"\\0\" * (26 - (len(encoded_long_name) % 26))\n    padded_long_name = encoded_long_name + long_name_padding\n\n    chunks = [padded_long_name[i:i+26] for i in range(0,\n      len(padded_long_name), 26)]\n    records = []\n    sequence_number = 1\n\n    for c in chunks:\n      sequence_byte = struct.pack(\"B\", sequence_number)\n      sequence_number += 1\n      record = sequence_byte + c[:10] + LFN_ATTRIBUTES_BYTE + \"\\0\" + \\\n          checksum + c[10:22] + \"\\0\\0\" + c[22:]\n      records.append(record)\n\n    last = records.pop()\n    last_seq = struct.unpack(\"B\", last[0])[0]\n    last_seq = last_seq | 0x40\n    last = struct.pack(\"B\", last_seq) + last[1:]\n    records.append(last)\n    records.reverse()\n\n    return records\n\n  def commit(self, f):\n    \"\"\"\n    Write this dentry into the given file-like object,\n    which is assumed to contain a FAT directory.\n    \"\"\"\n    f.seek(0)\n    padded_short_name = self.shortname.ljust(8)\n    padded_ext = self.ext.ljust(3)\n    name_data = padded_short_name + padded_ext\n    longname_record_data = self.longname_records(lfn_checksum(name_data))\n    record = struct.pack(\"<11sBBBHHHHHHHL\",\n        name_data,\n        self.attributes,\n        0,\n        0,\n        0,\n        0,\n        0,\n        0,\n        0,\n        0,\n        self.first_cluster,\n        self.size)\n    entry = \"\".join(longname_record_data + [record])\n\n    record_count = len(longname_record_data) + 1\n\n    found_count = 0\n    while found_count < record_count:\n      record = f.read(32)\n\n      if record is None or len(record) != 32:\n        # We reached the EOF, so we need to extend the file with a new cluster.\n        f.write(\"\\0\" * self.fs.bytes_per_cluster)\n        f.seek(-self.fs.bytes_per_cluster, os.SEEK_CUR)\n        record = f.read(32)\n\n      marker = struct.unpack(\"B\", record[0])[0]\n\n      if marker == DEL_MARKER or marker == 0:\n        found_count += 1\n      else:\n        found_count = 0\n\n    f.seek(-(record_count * 32), os.SEEK_CUR)\n    f.write(entry)\n\nclass root_dentry_file(fake_file):\n  \"\"\"\n  File-like object for the root directory. The root directory isn't stored in a\n  normal file, so we can't use a normal fat_file object to create a view of it.\n  \"\"\"\n  def __init__(self, fs):\n    self.fs = fs\n    self.idx = 0\n    self.size = fs.root_entries * 32\n\n  def read(self, count):\n    f = self.fs.f\n    f.seek(self.fs.data_start() + self.idx)\n\n    if self.idx + count > self.size:\n      count = self.size - self.idx\n\n    ret = f.read(count)\n    self.idx += len(ret)\n    return ret\n\n  def write(self, data):\n    f = self.fs.f\n    f.seek(self.fs.data_start() + self.idx)\n\n    if self.idx + len(data) > self.size:\n      data = data[:self.size - self.idx]\n\n    f.write(data)\n    self.idx += len(data)\n    if self.idx > self.size:\n      self.size = self.idx\n\nclass fat(object):\n  \"A FAT image\"\n\n  def __init__(self, path):\n    \"\"\"\n    path: Path to an image file containing a FAT file system.\n    \"\"\"\n    f = open(path, \"r+b\")\n\n    self.f = f\n\n    f.seek(0xb)\n    bytes_per_sector = read_le_short(f)\n    sectors_per_cluster = read_byte(f)\n\n    self.bytes_per_cluster = bytes_per_sector * sectors_per_cluster\n\n    reserved_sectors = read_le_short(f)\n    assert reserved_sectors == 1, \\\n        \"Can only handle FAT with 1 reserved sector\"\n\n    fat_count = read_byte(f)\n    assert fat_count == 2, \"Can only handle FAT with 2 tables\"\n\n    self.root_entries = read_le_short(f)\n\n    skip_short(f) # Image size. Sort of. Useless field.\n    skip_byte(f) # Media type. We don't care.\n\n    self.fat_size = read_le_short(f) * bytes_per_sector\n    self.root = fat_dir(root_dentry_file(self))\n\n  def data_start(self):\n    \"\"\"\n    Index of the first byte after the FAT tables.\n    \"\"\"\n    return FAT_TABLE_START + self.fat_size * 2\n\n  def get_chain_size(self, head_cluster):\n    \"\"\"\n    Return how many total bytes are in the cluster chain rooted at the given\n    cluster.\n    \"\"\"\n    if head_cluster == 0:\n      return 0\n\n    f = self.f\n    f.seek(FAT_TABLE_START + head_cluster * 2)\n\n    cluster_count = 0\n\n    while head_cluster <= MAX_CLUSTER_ID:\n      cluster_count += 1\n      head_cluster = read_le_short(f)\n      f.seek(FAT_TABLE_START + head_cluster * 2)\n\n    return cluster_count * self.bytes_per_cluster\n\n  def read_dentry(self, f=None):\n    \"\"\"\n    Read and decode a dentry from the given file-like object at its current\n    seek position.\n    \"\"\"\n    f = f or self.f\n    attributes = None\n\n    consumed = 1\n\n    lfn_entries = {}\n\n    while True:\n      skip_bytes(f, 11)\n      attributes = read_byte(f)\n      rewind_bytes(f, 12)\n\n      if attributes & LFN_ATTRIBUTES != LFN_ATTRIBUTES:\n        break\n\n      consumed += 1\n\n      seq = read_byte(f)\n      chars = f.read(10)\n      skip_bytes(f, 3) # Various hackish nonsense\n      chars += f.read(12)\n      skip_short(f) # Lots more nonsense\n      chars += f.read(4)\n\n      chars = unicode(chars, \"utf-16-le\").encode(\"utf-8\")\n\n      lfn_entries[seq] = chars\n\n    ind = read_byte(f)\n\n    if ind == 0 or ind == DEL_MARKER:\n      skip_bytes(f, 31)\n      return (None, consumed)\n\n    if ind == ESCAPE_DEL_MARKER:\n      ind = DEL_MARKER\n\n    ind = str(unichr(ind))\n\n    if ind == '.':\n      skip_bytes(f, 31)\n      return (None, consumed)\n\n    shortname = ind + f.read(7).rstrip()\n    ext = f.read(3).rstrip()\n    skip_bytes(f, 15) # Assorted flags, ctime/atime/mtime, etc.\n    first_cluster = read_le_short(f)\n    size = read_le_long(f)\n\n    lfn = lfn_entries.items()\n    lfn.sort(key=lambda x: x[0])\n    lfn = reduce(lambda x, y: x + y[1], lfn, \"\")\n\n    if len(lfn) == 0:\n      lfn = None\n    else:\n      lfn = lfn.split('\\0', 1)[0]\n\n    return (dentry(self, attributes, shortname, ext, lfn, first_cluster,\n      size), consumed)\n\n  def read_file(self, head_cluster, start_byte, size):\n    \"\"\"\n    Read from a given FAT file.\n    head_cluster: The first cluster in the file.\n    start_byte: How many bytes in to the file to begin the read.\n    size: How many bytes to read.\n    \"\"\"\n    f = self.f\n\n    assert size >= 0, \"Can't read a negative amount\"\n    if size == 0:\n      return \"\"\n\n    got_data = \"\"\n\n    while True:\n      size_now = size\n      if start_byte + size > self.bytes_per_cluster:\n        size_now = self.bytes_per_cluster - start_byte\n\n      if start_byte < self.bytes_per_cluster:\n        size -= size_now\n\n        cluster_bytes_from_root = (head_cluster - 2) * \\\n            self.bytes_per_cluster\n        bytes_from_root = cluster_bytes_from_root + start_byte\n        bytes_from_data_start = bytes_from_root + self.root_entries * 32\n\n        f.seek(self.data_start() + bytes_from_data_start)\n        line = f.read(size_now)\n        got_data += line\n\n        if size == 0:\n          return got_data\n\n      start_byte -= self.bytes_per_cluster\n\n      if start_byte < 0:\n        start_byte = 0\n\n      f.seek(FAT_TABLE_START + head_cluster * 2)\n      assert head_cluster <= MAX_CLUSTER_ID, \"Out-of-bounds read\"\n      head_cluster = read_le_short(f)\n      assert head_cluster > 0, \"Read free cluster\"\n\n    return got_data\n\n  def write_cluster_entry(self, entry):\n    \"\"\"\n    Write a cluster entry to the FAT table. Assumes our backing file is already\n    seeked to the correct entry in the first FAT table.\n    \"\"\"\n    f = self.f\n    f.write(struct.pack(\"<H\", entry))\n    skip_bytes(f, self.fat_size - 2)\n    f.write(struct.pack(\"<H\", entry))\n    rewind_bytes(f, self.fat_size)\n\n  def allocate(self, amount):\n    \"\"\"\n    Allocate a new cluster chain big enough to hold at least the given amount\n    of bytes.\n    \"\"\"\n    assert amount > 0, \"Must allocate a non-zero amount.\"\n\n    f = self.f\n    f.seek(FAT_TABLE_START + 4)\n\n    current = None\n    current_size = 0\n    free_zones = {}\n\n    pos = 2\n    while pos < self.fat_size / 2:\n      data = read_le_short(f)\n\n      if data == 0 and current is not None:\n        current_size += 1\n      elif data == 0:\n        current = pos\n        current_size = 1\n      elif current is not None:\n        free_zones[current] = current_size\n        current = None\n\n      pos += 1\n\n    if current is not None:\n      free_zones[current] = current_size\n\n    free_zones = free_zones.items()\n    free_zones.sort(key=lambda x: x[1])\n\n    grabbed_zones = []\n    grabbed = 0\n\n    while grabbed < amount and len(free_zones) > 0:\n      zone = free_zones.pop()\n      grabbed += zone[1] * self.bytes_per_cluster\n      grabbed_zones.append(zone)\n\n    if grabbed < amount:\n      return None\n\n    excess = (grabbed - amount) / self.bytes_per_cluster\n\n    grabbed_zones[-1] = (grabbed_zones[-1][0],\n        grabbed_zones[-1][1] - excess)\n\n    out = None\n    grabbed_zones.reverse()\n\n    for cluster, size in grabbed_zones:\n      entries = range(cluster + 1, cluster + size)\n      entries.append(out or 0xFFFF)\n      out = cluster\n      f.seek(FAT_TABLE_START + cluster * 2)\n      for entry in entries:\n        self.write_cluster_entry(entry)\n\n    return out\n\n  def extend_cluster(self, cluster, amount):\n    \"\"\"\n    Given a cluster which is the *last* cluster in a chain, extend it to hold\n    at least `amount` more bytes.\n    \"\"\"\n    if amount == 0:\n      return\n    f = self.f\n    entry_offset = FAT_TABLE_START + cluster * 2\n    f.seek(entry_offset)\n    assert read_le_short(f) == 0xFFFF, \"Extending from middle of chain\"\n\n    return_cluster = self.allocate(amount)\n    f.seek(entry_offset)\n    self.write_cluster_entry(return_cluster)\n    return return_cluster\n\n  def write_file(self, head_cluster, start_byte, data):\n    \"\"\"\n    Write to a given FAT file.\n\n    head_cluster: The first cluster in the file.\n    start_byte: How many bytes in to the file to begin the write.\n    data: The data to write.\n    \"\"\"\n    f = self.f\n    last_offset = start_byte + len(data)\n    current_offset = 0\n    current_cluster = head_cluster\n\n    while current_offset < last_offset:\n      # Write everything that falls in the cluster starting at current_offset.\n      data_begin = max(0, current_offset - start_byte)\n      data_end = min(len(data),\n                     current_offset + self.bytes_per_cluster - start_byte)\n      if data_end > data_begin:\n        cluster_file_offset = (self.data_start() + self.root_entries * 32 +\n                               (current_cluster - 2) * self.bytes_per_cluster)\n        f.seek(cluster_file_offset + max(0, start_byte - current_offset))\n        f.write(data[data_begin:data_end])\n\n      # Advance to the next cluster in the chain or get a new cluster if needed.\n      current_offset += self.bytes_per_cluster\n      if last_offset > current_offset:\n        f.seek(FAT_TABLE_START + current_cluster * 2)\n        next_cluster = read_le_short(f)\n        if next_cluster > MAX_CLUSTER_ID:\n          next_cluster = self.extend_cluster(current_cluster, len(data))\n        current_cluster = next_cluster\n        assert current_cluster > 0, \"Cannot write free cluster\"\n\n\ndef add_item(directory, item):\n  \"\"\"\n  Copy a file into the given FAT directory. If the path given is a directory,\n  copy recursively.\n  directory: fat_dir to copy the file in to\n  item: Path of local file to copy\n  \"\"\"\n  if os.path.isdir(item):\n    base = os.path.basename(item)\n    if len(base) == 0:\n      base = os.path.basename(item[:-1])\n    sub = directory.open_subdirectory(base)\n    for next_item in sorted(os.listdir(item)):\n      add_item(sub, os.path.join(item, next_item))\n  else:\n    with open(item, 'rb') as f:\n      directory.new_file(os.path.basename(item), f)\n\nif __name__ == \"__main__\":\n  if len(sys.argv) < 3:\n    print(\"Usage: fat16copy.py <image> <file> [<file> ...]\")\n    print(\"Files are copied into the root of the image.\")\n    print(\"Directories are copied recursively\")\n    sys.exit(1)\n\n  root = fat(sys.argv[1]).root\n\n  for p in sys.argv[2:]:\n    add_item(root, p)\n"
  },
  {
    "path": "tools/filelistdiff/Android.bp",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\npython_binary_host {\n    name: \"file_list_diff\",\n    srcs: [\"file_list_diff.py\"],\n}\n\nprebuilt_etc_host {\n    name: \"system_image_diff_allowlist\",\n    src: \"allowlist\",\n}\n\nprebuilt_etc_host {\n    name: \"system_image_diff_allowlist_next\",\n    src: \"allowlist_next\",\n}\n"
  },
  {
    "path": "tools/filelistdiff/OWNERS",
    "content": "per-file allowlist = justinyun@google.com, jeongik@google.com, kiyoungkim@google.com, inseob@google.com\n"
  },
  {
    "path": "tools/filelistdiff/allowlist",
    "content": "# Known diffs that are installed in either system image with the configuration\n# b/353429422\ninit.environ.rc\n"
  },
  {
    "path": "tools/filelistdiff/allowlist_next",
    "content": "# Allowlist only for the next release configuration.\n# TODO(b/369678122): The list will be cleared when the trunk configurations are\n# available to the next.\n"
  },
  {
    "path": "tools/filelistdiff/file_list_diff.py",
    "content": "# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport sys\n\nCOLOR_WARNING = '\\033[93m'\nCOLOR_ERROR = '\\033[91m'\nCOLOR_NORMAL = '\\033[0m'\n\ndef find_unique_items(kati_installed_files, soong_installed_files, system_module_name, allowlists):\n    with open(kati_installed_files, 'r') as kati_list_file, \\\n            open(soong_installed_files, 'r') as soong_list_file:\n        kati_files = set(kati_list_file.read().split())\n        soong_files = set(soong_list_file.read().split())\n\n    allowed_files = set()\n    for allowlist in allowlists:\n        with open(allowlist, 'r') as allowlist_file:\n            allowed_files.update(set(filter(lambda x: len(x), map(lambda x: x.lstrip().split('#',1)[0].rstrip() , allowlist_file.read().split('\\n')))))\n\n    def is_unknown_diff(filepath):\n        return filepath not in allowed_files\n\n    def is_unnecessary_allowlist(filepath):\n        return filepath not in kati_files.symmetric_difference(soong_files)\n\n    unique_in_kati = set(filter(is_unknown_diff, kati_files - soong_files))\n    unique_in_soong = set(filter(is_unknown_diff, soong_files - kati_files))\n    unnecessary_allowlists = set(filter(is_unnecessary_allowlist, allowed_files))\n\n    if unique_in_kati:\n        print('')\n        print(f'{COLOR_ERROR}Missing required modules in {system_module_name} module.{COLOR_NORMAL}')\n        print(f'To resolve this issue, please add the modules to the Android.bp file for the {system_module_name} to install the following KATI only installed files.')\n        print(f'You can find the correct Android.bp file using the command \"gomod {system_module_name}\".')\n        print(f'{COLOR_WARNING}KATI only installed file(s):{COLOR_NORMAL}')\n        for item in sorted(unique_in_kati):\n            print('  '+item)\n\n    if unique_in_soong:\n        print('')\n        print(f'{COLOR_ERROR}Missing packages in base_system.mk.{COLOR_NORMAL}')\n        print('Please add packages into build/make/target/product/base_system.mk or build/make/tools/filelistdiff/allowlist to install or skip the following Soong only installed files.')\n        print(f'{COLOR_WARNING}Soong only installed file(s):{COLOR_NORMAL}')\n        for item in sorted(unique_in_soong):\n            print('  '+item)\n\n    if unnecessary_allowlists:\n        print('')\n        print(f'{COLOR_ERROR}Unnecessary files in allowlist.{COLOR_NORMAL}')\n        print('Please remove these entries from build/make/tools/filelistdiff/allowlist')\n        for item in sorted(unnecessary_allowlists):\n            print('  '+item)\n\n\n    if unique_in_kati or unique_in_soong or unnecessary_allowlists:\n        print('')\n        sys.exit(1)\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n\n    parser.add_argument('kati_installed_file_list')\n    parser.add_argument('soong_installed_file_list')\n    parser.add_argument('system_module_name')\n    parser.add_argument('--allowlists', nargs='*', default=[])\n    args = parser.parse_args()\n\n    find_unique_items(args.kati_installed_file_list, args.soong_installed_file_list, args.system_module_name, args.allowlists)"
  },
  {
    "path": "tools/fileslist_util.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2016 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport argparse\nimport json\nimport sys\n\ndef PrintFileNames(path):\n  with open(path) as jf:\n    data = json.load(jf)\n  for line in data:\n    print(line[\"Name\"])\n\ndef PrintCanonicalList(path):\n  with open(path) as jf:\n    data = json.load(jf)\n  for line in data:\n    print(f\"{line['Size']:12d}  {line['Name']}\")\n\ndef main():\n  parser = argparse.ArgumentParser()\n  parser.add_argument(\"-n\", action=\"store_true\",\n                      help=\"produces list of files only\")\n  parser.add_argument(\"-c\", action=\"store_true\",\n                      help=\"produces classic installed-files.txt\")\n  parser.add_argument(\"json_files_list\")\n  args = parser.parse_args()\n\n  if args.n and args.c:\n    sys.exit(\"Cannot specify both -n and -c\")\n  elif args.n:\n    PrintFileNames(args.json_files_list)\n  elif args.c:\n    PrintCanonicalList(args.json_files_list)\n  else:\n    sys.exit(\"No conversion option specified\")\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tools/finalization/OWNERS",
    "content": "include platform/build/soong:/OWNERS\namhk@google.com\ngurpreetgs@google.com\nmichaelwr@google.com\npatb@google.com\nsmoreland@google.com\nzyy@google.com\n"
  },
  {
    "path": "tools/finalization/README.md",
    "content": "# Finalization tools\nThis folder contains automation and CI scripts for [finalizing](https://go/android-finalization) Android before release.\n\n## Automation:\n1. [Environment setup](./environment.sh). Set values for varios finalization constants.\n1. [Finalize VINTF](./finalize-vintf-resources.sh). Prepare the branch for VINTF release.\n1. [Finalize SDK](./finalize-sdk-resources.sh). Prepare the branch for SDK release. SDK contains Android Java APIs and other stable APIs. Commonly referred as a 1st step.\n1. [Finalize Android](./finalize-sdk-rel.sh). Mark branch as \"REL\", i.e. prepares for Android release. Any signed build containing these changes will be considered an official Android Release. Referred as a 2nd finalization step.\n1. [Finalize VINTF and submit](./step-0.sh). Do Finalize VINTF step, create CLs, organize them into topic and send to Gerrit.\n1. [Finalize SDK and submit](./step-1.sh). Do Finalize SDK step, create CLs, organize them into topic and send to Gerrit.\n1. [Finalize Android and submit](./step-2.sh). Do [Finalize Android](./finalize-sdk-rel.sh) step, create  CLs, organize them into topic and send to Gerrit.\n\n## CI:\nPerformed in build targets in Finalization branches.\n1. [Finalization Step 0, git_main-fina-0-release](https://android-build.corp.google.com/build_explorer/branch/git_main-fina-0-release). Test Finalize VINTF.\n1. [Finalization Step 1, git_main-fina-1-release](https://android-build.corp.google.com/build_explorer/branch/git_main-fina-1-release). Test Finalize VINTF, Finalize SDK.\n1. [Finalization Step 2, git_main-fina-2-release](https://android-build.corp.google.com/build_explorer/branch/git_main-fina-2-release). Test Finalize VINTF, Finalize SDK, and [2nd step/Finalize Android](./finalize-sdk-rel.sh). Use [local finalization](./localonly-steps.sh) to build and copy presubmits.\n1. [Local finalization steps](./localonly-steps.sh) are done only during local testing or in the CI lab. Normally these steps use artifacts from other builds.\n\n## Utility:\n[Full cleanup](./cleanup.sh). Remove all local changes and switch each project into head-less state. This is the best state to sync/rebase/finalize the branch.\n\n## Dry run:\n[Full cleanup](./dryrun-cleanup.sh). Remove all local changes and switch each project into head-less state. Also removes \"DryRun\" branches.\n[SDK](./dryrun-step-1.sh). Perform SDK finalization and upload the CLs to Gerrit.\n[SDK and REL](./dryrun-step-1-and-2.sh). Perform SDK finalization, plus all necessary changes to switch configuration to REL, and upload the CLs to Gerrit."
  },
  {
    "path": "tools/finalization/build-step-0-and-m.sh",
    "content": "\n#!/bin/bash\n# Copyright 2024 Google Inc. All rights reserved.\nset -ex\nfunction help() {\n    echo \"Finalize VINTF and build a target for test.\"\n    echo \"usage: $(basename \"$0\") target [goals...]\"\n}\nfunction finalize_main_step0_and_m() {\n    if [ $# == 0 ] ; then\n        help\n        exit 1\n    fi;\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/build-step-0.sh\n    local m=\"$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=$1 TARGET_RELEASE=fina_0 TARGET_BUILD_VARIANT=userdebug\"\n    # This command tests the release state for AIDL.\n    AIDL_FROZEN_REL=true $m ${@:2}\n}\nfinalize_main_step0_and_m $@\n"
  },
  {
    "path": "tools/finalization/build-step-0.sh",
    "content": "#!/bin/bash\n# Copyright 2024 Google Inc. All rights reserved.\n\nset -ex\n\nfunction finalize_main_step0() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n\n    local need_vintf_finalize=false\n    if [ \"$FINAL_STATE\" = \"unfinalized\" ] ; then\n        need_vintf_finalize=true\n    else\n        # build-step-0.sh tests the vintf finalization step (step-0) when the\n        # FINAL_BOARD_API_LEVEL is the same as the RELEASE_BOARD_API_LEVEL; and\n        # RELEASE_BOARD_API_LEVEL_FROZEN is not true from the fina_0 configuration.\n        # The FINAL_BOARD_API_LEVEL must be the next vendor API level to be finalized.\n        local board_api_level_vars=$(TARGET_RELEASE=fina_0 $top/build/soong/soong_ui.bash --dumpvars-mode -vars \"RELEASE_BOARD_API_LEVEL_FROZEN RELEASE_BOARD_API_LEVEL\")\n        local target_board_api_level_vars=\"RELEASE_BOARD_API_LEVEL_FROZEN=''\nRELEASE_BOARD_API_LEVEL='$FINAL_BOARD_API_LEVEL'\"\n        if [ \"$board_api_level_vars\" = \"$target_board_api_level_vars\" ] ; then\n            echo The target is a finalization candidate.\n            need_vintf_finalize=true\n        fi;\n    fi;\n\n    if [ \"$need_vintf_finalize\" = true ] ; then        # VINTF finalization\n        source $top/build/make/tools/finalization/finalize-vintf-resources.sh\n    fi;\n}\n\nfinalize_main_step0\n"
  },
  {
    "path": "tools/finalization/build-step-1-and-2.sh",
    "content": "#!/bin/bash\n\nset -ex\n\nfunction finalize_main_step12() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n\n    if [ \"$FINAL_STATE\" = \"unfinalized\" ] ; then\n        # VINTF finalization\n        source $top/build/make/tools/finalization/finalize-vintf-resources.sh\n    fi;\n\n    if [ \"$FINAL_STATE\" = \"unfinalized\" ] || [ \"$FINAL_STATE\" = \"vintf\" ] ; then\n        # SDK codename -> int\n        source $top/build/make/tools/finalization/finalize-sdk-resources.sh\n    fi;\n\n    if [ \"$FINAL_STATE\" = \"unfinalized\" ] || [ \"$FINAL_STATE\" = \"vintf\" ] || [ \"$FINAL_STATE\" = \"sdk\" ] ; then\n        # ADB, Platform/Mainline SDKs build and move to prebuilts\n        source $top/build/make/tools/finalization/localonly-steps.sh\n\n        # REL\n        source $top/build/make/tools/finalization/finalize-sdk-rel.sh\n    fi;\n}\n\nfinalize_main_step12\n\n"
  },
  {
    "path": "tools/finalization/build-step-1-and-m.sh",
    "content": "#!/bin/bash\n\nset -ex\n\nfunction finalize_main_step1_and_m() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/build-step-1.sh\n\n    local m=\"$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n\n    # This command tests:\n    #   ABI difference between user and userdebug builds.\n    #   Resource/SDK finalization.\n    $m\n}\n\nfinalize_main_step1_and_m\n\n"
  },
  {
    "path": "tools/finalization/build-step-1.sh",
    "content": "#!/bin/bash\n\nset -ex\n\nfunction finalize_main_step1() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n\n    if [ \"$FINAL_STATE\" = \"unfinalized\" ] ; then\n        # VINTF finalization\n        source $top/build/make/tools/finalization/finalize-vintf-resources.sh\n    fi;\n\n    if [ \"$FINAL_STATE\" = \"unfinalized\" ] || [ \"$FINAL_STATE\" = \"vintf\" ] ; then\n        # Build finalization artifacts.\n        source $top/build/make/tools/finalization/finalize-sdk-resources.sh\n    fi;\n}\n\nfinalize_main_step1\n\n"
  },
  {
    "path": "tools/finalization/build_soong_java_droidstubs.go.apply_hack.diff",
    "content": "From 12eea1512f2612f41b5cf7004ee2e6a189d548d7 Mon Sep 17 00:00:00 2001\nFrom: Alex Buynytskyy <alexbuy@google.com>\nDate: Thu, 01 Sep 2022 10:44:21 -0700\nSubject: [PATCH] Hacky workaround for half-finalized builds.\n\nMetalava increments the SDK level by one when it's not \"REL\", so we\ntemporarily force the build to be \"REL\" while we're still in the\nprocess of finalizing it.\n\nThis CL must be reverted as part of actually declaring \"REL\".\n\nBug: none\nTest: Build\nChange-Id: I8c24c6dabec0270bc384d8465c582a4ddbe8bd6c\n---\n\ndiff --git a/java/droidstubs.go b/java/droidstubs.go\nindex 5777b18..ec4a0f4 100644\n--- a/java/droidstubs.go\n+++ b/java/droidstubs.go\n@@ -386,7 +386,8 @@\n \t}\n \tif apiVersions != nil {\n \t\tcmd.FlagWithArg(\"--current-version \", ctx.Config().PlatformSdkVersion().String())\n-\t\tcmd.FlagWithArg(\"--current-codename \", ctx.Config().PlatformSdkCodename())\n+\t\t// STOPSHIP: RESTORE THIS LOGIC WHEN DECLARING \"REL\" BUILD\n+\t\t// cmd.FlagWithArg(\"--current-codename \", ctx.Config().PlatformSdkCodename())\n \t\tcmd.FlagWithInput(\"--apply-api-levels \", apiVersions)\n \t}\n }\n"
  },
  {
    "path": "tools/finalization/build_soong_java_droidstubs.go.revert_hack.diff",
    "content": "From c0f6e8fe4c3b6803be97aeea6683631d616412f4 Mon Sep 17 00:00:00 2001\nFrom: Alex Buynytskyy <alexbuy@google.com>\nDate: Thu, 08 Dec 2022 17:52:52 +0000\nSubject: [PATCH] Revert \"Hacky workaround for half-finalized builds.\"\n\nThis reverts commit 12eea1512f2612f41b5cf7004ee2e6a189d548d7.\n\nReason for revert: finalization-2\n\nChange-Id: Ifc801271628808693b1cb20206f8f81c9a6c694d\n---\n\ndiff --git a/java/droidstubs.go b/java/droidstubs.go\nindex ec4a0f4..5777b18 100644\n--- a/java/droidstubs.go\n+++ b/java/droidstubs.go\n@@ -386,8 +386,7 @@\n \t}\n \tif apiVersions != nil {\n \t\tcmd.FlagWithArg(\"--current-version \", ctx.Config().PlatformSdkVersion().String())\n-\t\t// STOPSHIP: RESTORE THIS LOGIC WHEN DECLARING \"REL\" BUILD\n-\t\t// cmd.FlagWithArg(\"--current-codename \", ctx.Config().PlatformSdkCodename())\n+\t\tcmd.FlagWithArg(\"--current-codename \", ctx.Config().PlatformSdkCodename())\n \t\tcmd.FlagWithInput(\"--apply-api-levels \", apiVersions)\n \t}\n }\n"
  },
  {
    "path": "tools/finalization/cleanup.sh",
    "content": "#!/bin/bash\n# Brings local repository to a remote head state.\n\n# set -ex\n\nfunction finalize_revert_local_changes_main() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    local m=\"$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n\n    # remove the out folder\n    $m clobber\n\n    repo selfupdate\n\n    repo forall -c '\\\n        git checkout . ; git revert --abort ; git clean -fdx ;\\\n        git checkout @ --detach ; git branch fina-step1 -D ; git reset --hard; \\\n        repo start fina-step1 ; git checkout @ --detach ; git b fina-step1 -D ;'\n}\n\nfinalize_revert_local_changes_main\n"
  },
  {
    "path": "tools/finalization/command-line-options.sh",
    "content": "ARGV=$(getopt --options '' --long dry-run -- \"$@\")\neval set -- \"$ARGV\"\nwhile true; do\n    case \"$1\" in\n        --dry-run) repo_upload_dry_run_arg=\"--dry-run\"; repo_branch=\"finalization-dry-run\"; shift ;;\n        --) shift; break;;\n        *) break\n    esac\ndone\n"
  },
  {
    "path": "tools/finalization/dryrun-cleanup.sh",
    "content": "#!/bin/bash\n# Brings local repository to a remote head state. Also removes all dryrun branches.\n\n# set -ex\n\nfunction finalize_revert_local_changes_main() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n\n    local m=\"$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n\n    # remove the out folder\n    $m clobber\n\n    repo selfupdate\n\n    repo forall -c '\\\n        git checkout . ; git revert --abort ; git clean -fdx ;\\\n        git checkout @ --detach ; git branch fina-step1 -D ; git reset --hard; \\\n        repo start fina-step1 ; git checkout @ --detach ; git b fina-step1 -D ; \\\n        git b $FINAL_PLATFORM_CODENAME-SDK-Finalization-DryRun -D; \\\n        git b $FINAL_PLATFORM_CODENAME-SDK-Finalization-DryRun-Rel -D; '\n}\n\nfinalize_revert_local_changes_main\n"
  },
  {
    "path": "tools/finalization/dryrun-step-1-and-2.sh",
    "content": "#!/bin/bash\n# Script to perform 1st and 2nd step of Android Finalization, create CLs and upload to Gerrit.\n\nfunction commit_step_2_changes() {\n    repo forall -c '\\\n        if [[ $(git status --short) ]]; then\n            repo start \"$FINAL_PLATFORM_CODENAME-SDK-Finalization-DryRun-Rel\" ;\n            git add -A . ;\n            git commit -m \"$FINAL_PLATFORM_CODENAME/$FINAL_PLATFORM_SDK_VERSION is now REL\" \\\n                       -m \"Ignore-AOSP-First: $FINAL_PLATFORM_CODENAME Finalization\nBug: $FINAL_BUG_ID\nTest: build\";\n\n            repo upload --cbr --no-verify -o nokeycheck -t -y . ;\n        fi'\n}\n\nfunction finalize_step_2_main() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n\n    source $top/build/make/tools/finalization/finalize-sdk-resources.sh\n\n    source $top/build/make/tools/finalization/localonly-steps.sh\n\n    source $top/build/make/tools/finalization/finalize-sdk-rel.sh\n\n    # move all changes to finalization branch/topic and upload to gerrit\n    commit_step_2_changes\n\n    # build to confirm everything is OK\n    local m_next=\"$top/build/soong/soong_ui.bash --make-mode TARGET_RELEASE=next TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n    $m_next\n\n    local m_fina=\"$top/build/soong/soong_ui.bash --make-mode TARGET_RELEASE=fina_2 TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n    $m_fina\n}\n\nfinalize_step_2_main\n"
  },
  {
    "path": "tools/finalization/dryrun-step-1.sh",
    "content": "#!/bin/bash\n# Script to perform a dry run of step 1 of Android Finalization, create CLs and upload to Gerrit.\n\nfunction commit_step_1_changes() {\n    repo forall -c '\\\n        if [[ $(git status --short) ]]; then\n            repo start \"$FINAL_PLATFORM_CODENAME-SDK-Finalization-DryRun\" ;\n            git add -A . ;\n            git commit -m \"$FINAL_PLATFORM_CODENAME is now $FINAL_PLATFORM_SDK_VERSION\" \\\n                       -m \"Ignore-AOSP-First: $FINAL_PLATFORM_CODENAME Finalization\nBug: $FINAL_BUG_ID\nTest: build\";\n\n            repo upload --cbr --no-verify -o nokeycheck -t -y . ;\n        fi'\n}\n\nfunction finalize_step_1_main() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n\n    source $top/build/make/tools/finalization/finalize-sdk-resources.sh\n\n    # move all changes to finalization branch/topic and upload to gerrit\n    commit_step_1_changes\n\n    # build to confirm everything is OK\n    local m_next=\"$top/build/soong/soong_ui.bash --make-mode TARGET_RELEASE=next TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n    $m_next\n\n    local m_fina=\"$top/build/soong/soong_ui.bash --make-mode TARGET_RELEASE=fina_1 TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n    $m_fina\n}\n\nfinalize_step_1_main\n"
  },
  {
    "path": "tools/finalization/environment.sh",
    "content": "#!/bin/bash\n\nset -ex\n\nexport FINAL_BUG_ID='0' # CI only\n\nexport FINAL_PLATFORM_CODENAME='VanillaIceCream'\nexport CURRENT_PLATFORM_CODENAME='VanillaIceCream'\nexport FINAL_PLATFORM_CODENAME_JAVA='VANILLA_ICE_CREAM'\nexport FINAL_PLATFORM_VERSION='15'\n\n# Set arbitrary large values for CI.\n# SDK_VERSION needs to be <61 (lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ApiConstraint.kt)\n# There are multiple places where we rely on next SDK version to be previous + 1, e.g. RESOURCES_SDK_INT.\n# We might or might not fix this in future, but for now let's keep it +1.\nexport FINAL_PLATFORM_SDK_VERSION='35'\n# Feel free to randomize once in a while to detect buggy version detection code.\nexport FINAL_MAINLINE_EXTENSION='13'\n\n# Options:\n# 'unfinalized' - branch is in development state,\n# 'vintf' - VINTF is finalized\n# 'sdk' - VINTF and SDK/API are finalized\n# 'rel' - branch is finalized, switched to REL\nexport FINAL_STATE='rel'\n\nexport BUILD_FROM_SOURCE_STUB=true\n\n# FINAL versions for VINTF\n# TODO(b/323985297): The version must match with that from the release configuration.\n# Instead of hardcoding the version here, read it from a release configuration.\nexport FINAL_BOARD_API_LEVEL='202504'\nexport FINAL_CORRESPONDING_VERSION_LETTER='B'\nexport FINAL_CORRESPONDING_PLATFORM_VERSION='16'\nexport FINAL_NEXT_BOARD_API_LEVEL='202604'\nexport FINAL_NEXT_CORRESPONDING_VERSION_LETTER='C'\nexport FINAL_NEXT_CORRESPONDING_SDK_VERSION='37'\n"
  },
  {
    "path": "tools/finalization/finalize-sdk-rel.sh",
    "content": "#!/bin/bash\n\nset -ex\n\nfunction revert_droidstubs_hack() {\n    if grep -q 'STOPSHIP: RESTORE THIS LOGIC WHEN DECLARING \"REL\" BUILD' \"$top/build/soong/java/droidstubs.go\" ; then\n        patch --strip=1 --no-backup-if-mismatch --directory=\"$top/build/soong\" --input=../../build/make/tools/finalization/build_soong_java_droidstubs.go.revert_hack.diff\n    fi\n}\n\nfunction apply_prerelease_sdk_hack() {\n    if ! grep -q 'STOPSHIP: hack for the pre-release SDK' \"$top/frameworks/base/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java\" ; then\n        patch --strip=1 --no-backup-if-mismatch --directory=\"$top/frameworks/base\" --input=../../build/make/tools/finalization/frameworks_base.apply_hack.diff\n    fi\n}\n\nfunction finalize_sdk_rel() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n\n    # revert droidstubs hack now we are switching to REL\n    revert_droidstubs_hack\n\n    # let the apps built with pre-release SDK parse\n    apply_prerelease_sdk_hack\n\n    # cts\n    if ! grep -q \"${FINAL_PLATFORM_VERSION}\" \"$top/cts/tests/tests/os/assets/platform_versions.txt\" ; then\n        echo ${FINAL_PLATFORM_VERSION} >> \"$top/cts/tests/tests/os/assets/platform_versions.txt\"\n    fi\n    if [ \"$FINAL_PLATFORM_CODENAME\" != \"$CURRENT_PLATFORM_CODENAME\" ]; then\n        echo \"$CURRENT_PLATFORM_CODENAME\" >> \"./cts/tests/tests/os/assets/platform_versions.txt\"\n    fi\n    git -C \"$top/cts\" mv hostsidetests/theme/assets/${FINAL_PLATFORM_CODENAME} hostsidetests/theme/assets/${FINAL_PLATFORM_SDK_VERSION}\n\n    # prebuilts/abi-dumps/platform\n    \"$top/build/soong/soong_ui.bash\" --make-mode TARGET_RELEASE=next TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug create_reference_dumps\n    ANDROID_BUILD_TOP=\"$top\" \"$top/out/host/linux-x86/bin/create_reference_dumps\" -release next --build-variant userdebug --lib-variant APEX\n}\n\nfinalize_sdk_rel\n\n"
  },
  {
    "path": "tools/finalization/finalize-sdk-resources.sh",
    "content": "#!/bin/bash\n\nset -ex\n\nfunction apply_droidstubs_hack() {\n    if ! grep -q 'STOPSHIP: RESTORE THIS LOGIC WHEN DECLARING \"REL\" BUILD' \"$top/build/soong/java/droidstubs.go\" ; then\n        local build_soong_git_root=\"$(readlink -f $top/build/soong)\"\n        patch --strip=1 --no-backup-if-mismatch --directory=\"$build_soong_git_root\" --input=../../build/make/tools/finalization/build_soong_java_droidstubs.go.apply_hack.diff\n    fi\n}\n\nfunction finalize_bionic_ndk() {\n    # Adding __ANDROID_API_<>__.\n    # If this hasn't done then it's not used and not really needed. Still, let's check and add this.\n    local api_level=\"$top/bionic/libc/include/android/api-level.h\"\n    if ! grep -q \"\\__.*$((${FINAL_PLATFORM_SDK_VERSION}))\" $api_level ; then\n        local tmpfile=$(mktemp /tmp/finalization.XXXXXX)\n        echo \"\n/** Names the \\\"${FINAL_PLATFORM_CODENAME:0:1}\\\" API level ($FINAL_PLATFORM_SDK_VERSION), for comparison against \\`__ANDROID_API__\\`. */\n#define __ANDROID_API_${FINAL_PLATFORM_CODENAME:0:1}__ $FINAL_PLATFORM_SDK_VERSION\" > \"$tmpfile\"\n\n        local api_level=\"$top/bionic/libc/include/android/api-level.h\"\n        sed -i -e \"/__.*$((${FINAL_PLATFORM_SDK_VERSION}-1))/r\"\"$tmpfile\" $api_level\n\n        rm \"$tmpfile\"\n    fi\n}\n\nfunction finalize_modules_utils() {\n    local shortCodename=\"${FINAL_PLATFORM_CODENAME:0:1}\"\n    local methodPlaceholder=\"INSERT_NEW_AT_LEAST_${shortCodename}_METHOD_HERE\"\n\n    local tmpfile=$(mktemp /tmp/finalization.XXXXXX)\n    echo \"    /** Checks if the device is running on a release version of Android $FINAL_PLATFORM_CODENAME or newer */\n    @ChecksSdkIntAtLeast(api = $FINAL_PLATFORM_SDK_VERSION /* BUILD_VERSION_CODES.$FINAL_PLATFORM_CODENAME */)\n    public static boolean isAtLeast${FINAL_PLATFORM_CODENAME:0:1}() {\n        return SDK_INT >= $FINAL_PLATFORM_SDK_VERSION ||\n                (SDK_INT == $(($FINAL_PLATFORM_SDK_VERSION - 1)) && isAtLeastPreReleaseCodename(\\\"$FINAL_PLATFORM_CODENAME\\\"));\n    }\" > \"$tmpfile\"\n\n    local javaFuncRegex='\\/\\*\\*[^{]*isAtLeast'\"${shortCodename}\"'() {[^{}]*}'\n    local javaFuncReplace=\"N;N;N;N;N;N;N;N; s/$javaFuncRegex/$methodPlaceholder/; /$javaFuncRegex/!{P;D};\"\n\n    local javaSdkLevel=\"$top/frameworks/libs/modules-utils/java/com/android/modules/utils/build/SdkLevel.java\"\n    sed -i \"$javaFuncReplace\" $javaSdkLevel\n\n    sed -i \"/${methodPlaceholder}\"'/{\n           r '\"$tmpfile\"'\n           d}' $javaSdkLevel\n\n    echo \"// Checks if the device is running on release version of Android ${FINAL_PLATFORM_CODENAME:0:1} or newer.\ninline bool IsAtLeast${FINAL_PLATFORM_CODENAME:0:1}() {\n  return android_get_device_api_level() >= $FINAL_PLATFORM_SDK_VERSION ||\n         (android_get_device_api_level() == $(($FINAL_PLATFORM_SDK_VERSION - 1)) &&\n          detail::IsAtLeastPreReleaseCodename(\\\"$FINAL_PLATFORM_CODENAME\\\"));\n}\" > \"$tmpfile\"\n\n    local cppFuncRegex='\\/\\/[^{]*IsAtLeast'\"${shortCodename}\"'() {[^{}]*}'\n    local cppFuncReplace=\"N;N;N;N;N;N; s/$cppFuncRegex/$methodPlaceholder/; /$cppFuncRegex/!{P;D};\"\n\n    local cppSdkLevel=\"$top/frameworks/libs/modules-utils/build/include/android-modules-utils/sdk_level.h\"\n    sed -i \"$cppFuncReplace\" $cppSdkLevel\n    sed -i \"/${methodPlaceholder}\"'/{\n           r '\"$tmpfile\"'\n           d}' $cppSdkLevel\n\n    rm \"$tmpfile\"\n}\n\nfunction bumpSdkExtensionsVersion() {\n    local SDKEXT=\"packages/modules/SdkExtensions/\"\n\n    # This used to call bump_sdk.sh utility.\n    # However due to TS, we have to build the gen_sdk with a correct set of settings.\n\n    # \"$top/packages/modules/SdkExtensions/gen_sdk/bump_sdk.sh\" ${FINAL_MAINLINE_EXTENSION}\n    # Leave the last commit as a set of modified files.\n    # The code to create a finalization topic will pick it up later.\n    # git -C ${SDKEXT} reset HEAD~1\n\n    local sdk=\"${FINAL_MAINLINE_EXTENSION}\"\n    local modules_arg=\n\n    TARGET_PRODUCT=aosp_arm64 \\\n        TARGET_RELEASE=fina_1 \\\n        TARGET_BUILD_VARIANT=userdebug \\\n        DIST_DIR=out/dist \\\n        $top/build/soong/soong_ui.bash --make-mode --soong-only gen_sdk\n\n    ANDROID_BUILD_TOP=\"$top\" out/soong/host/linux-x86/bin/gen_sdk \\\n        --database ${SDKEXT}/gen_sdk/extensions_db.textpb \\\n        --action new_sdk \\\n        --sdk \"$sdk\" \\\n        $modules_arg\n}\n\nfunction finalize_sdk_resources() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n\n    local SDK_CODENAME=\"public static final int $FINAL_PLATFORM_CODENAME_JAVA = CUR_DEVELOPMENT;\"\n    local SDK_VERSION=\"public static final int $FINAL_PLATFORM_CODENAME_JAVA = $FINAL_PLATFORM_SDK_VERSION;\"\n\n    # The full process can be found at (INTERNAL) go/android-sdk-finalization.\n\n    # apply droidstubs hack to prevent tools from incrementing an API version\n    apply_droidstubs_hack\n\n    # bionic/NDK\n    finalize_bionic_ndk\n\n    # Finalize SDK\n\n    # frameworks/libs/modules-utils\n    finalize_modules_utils\n\n    # development/sdk\n    local platform_source=\"$top/development/sdk/platform_source.prop_template\"\n    sed -i -e 's/Pkg\\.Revision.*/Pkg\\.Revision=1/g' $platform_source\n    local build_tools_source=\"$top/development/sdk/build_tools_source.prop_template\"\n    sed -i -e 's/Pkg\\.Revision.*/Pkg\\.Revision=${PLATFORM_SDK_VERSION}.0.0/g' $build_tools_source\n\n    # build/soong\n    local codename_version=\"\\\"${FINAL_PLATFORM_CODENAME}\\\":     ${FINAL_PLATFORM_SDK_VERSION}\"\n    if ! grep -q \"$codename_version\" \"$top/build/soong/android/api_levels.go\" ; then\n        sed -i -e \"/:.*$((${FINAL_PLATFORM_SDK_VERSION}-1)),/a \\\\\\t\\t$codename_version,\" \"$top/build/soong/android/api_levels.go\"\n    fi\n\n    # cts\n    if ! grep -q \"${FINAL_PLATFORM_VERSION}\" \"$top/cts/tests/tests/os/assets/platform_releases.txt\" ; then\n        echo ${FINAL_PLATFORM_VERSION} >> \"$top/cts/tests/tests/os/assets/platform_releases.txt\"\n    fi\n    if ! grep -q \"$((${FINAL_PLATFORM_SDK_VERSION}-1)), ${FINAL_PLATFORM_VERSION}\" \"$top/cts/tests/tests/os/src/android/os/cts/BuildVersionTest.java\" ; then\n        sed -i -e \"s/.*EXPECTED_SDKS = List.of(.*$((${FINAL_PLATFORM_SDK_VERSION}-1))/&, $FINAL_PLATFORM_SDK_VERSION/\" \"$top/cts/tests/tests/os/src/android/os/cts/BuildVersionTest.java\"\n    fi\n\n    # libcore\n    sed -i \"s%$SDK_CODENAME%$SDK_VERSION%g\" \"$top/libcore/dalvik/src/main/java/dalvik/annotation/compat/VersionCodes.java\"\n\n    # platform_testing\n    local version_codes=\"$top/platform_testing/libraries/compatibility-common-util/src/com/android/compatibility/common/util/VersionCodes.java\"\n    sed -i -e \"/=.*$((${FINAL_PLATFORM_SDK_VERSION}-1));/a \\\\    ${SDK_VERSION}\" $version_codes\n\n    # tools/platform-compat\n    local class2nonsdklist=\"$top/tools/platform-compat/java/com/android/class2nonsdklist/Class2NonSdkList.java\"\n    if ! grep -q \"\\.*map.put($((${FINAL_PLATFORM_SDK_VERSION}))\" $class2nonsdklist ; then\n      local sdk_version=\"map.put(${FINAL_PLATFORM_SDK_VERSION}, FLAG_UNSUPPORTED);\"\n      sed -i -e \"/.*map.put($((${FINAL_PLATFORM_SDK_VERSION}-1))/a \\\\        ${sdk_version}\" $class2nonsdklist\n    fi\n\n    # Finalize resources\n    \"$top/frameworks/base/tools/aapt2/tools/finalize_res.py\" \\\n           \"$top/frameworks/base/core/res/res/values/public-staging.xml\" \\\n           \"$top/frameworks/base/core/res/res/values/public-final.xml\"\n\n    # frameworks/base\n    sed -i \"s%$SDK_CODENAME%$SDK_VERSION%g\" \"$top/frameworks/base/core/java/android/os/Build.java\"\n    sed -i -e \"/=.*$((${FINAL_PLATFORM_SDK_VERSION}-1)),/a \\\\    SDK_${FINAL_PLATFORM_CODENAME_JAVA} = ${FINAL_PLATFORM_SDK_VERSION},\" \"$top/frameworks/base/tools/aapt/SdkConstants.h\"\n    sed -i -e \"/=.*$((${FINAL_PLATFORM_SDK_VERSION}-1)),/a \\\\  SDK_${FINAL_PLATFORM_CODENAME_JAVA} = ${FINAL_PLATFORM_SDK_VERSION},\" \"$top/frameworks/base/tools/aapt2/SdkConstants.h\"\n\n    # Bump Mainline SDK extension version.\n    bumpSdkExtensionsVersion\n\n    # target to build SDK\n    local sdk_m=\"$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_RELEASE=fina_1 TARGET_BUILD_VARIANT=userdebug DIST_DIR=out/dist\"\n\n    # Force update current.txt\n    $sdk_m clobber\n    $sdk_m update-api\n}\n\nfinalize_sdk_resources\n\n"
  },
  {
    "path": "tools/finalization/finalize-vintf-resources.sh",
    "content": "#!/bin/bash\n\nset -ex\n\nfunction finalize_vintf_resources() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n    # environment needed to build dependencies and run scripts\n    # These should remain the same for all steps here to speed up build time\n    export ANDROID_BUILD_TOP=\"$top\"\n    export ANDROID_HOST_OUT=\"$ANDROID_BUILD_TOP/out/host/linux-x86\"\n    export ANDROID_PRODUCT_OUT=\"$ANDROID_BUILD_TOP/out/target/product/generic_arm64\"\n    export PATH=\"$PATH:$ANDROID_HOST_OUT/bin/\"\n    export TARGET_BUILD_VARIANT=userdebug\n    export DIST_DIR=out/dist\n    export TARGET_RELEASE=fina_0\n    export TARGET_PRODUCT=aosp_arm64\n\n    # build/soong\n    local vendor_api_level_map=\"case ${FINAL_NEXT_BOARD_API_LEVEL}:\"\n    if ! grep -q \"$vendor_api_level_map\" \"$top/build/soong/android/vendor_api_levels.go\" ; then\n        sed -i -e \"/case ${FINAL_BOARD_API_LEVEL}:/{N;a \\\\\\t$vendor_api_level_map\\n\\t\\tsdkVersion = ${FINAL_NEXT_CORRESPONDING_SDK_VERSION}\n        }\" \"$top/build/soong/android/vendor_api_levels.go\"\n    fi\n\n    # system/sepolicy\n    \"$top/system/sepolicy/tools/finalize-vintf-resources.sh\" \"$top\" \"$FINAL_BOARD_API_LEVEL\"\n\n    create_new_compat_matrix_and_kernel_configs\n\n    # pre-finalization build target (trunk)\n    local aidl_m=\"$top/build/soong/soong_ui.bash --make-mode\"\n    AIDL_TRANSITIVE_FREEZE=true $aidl_m aidl-freeze-api create_reference_dumps\n\n    # Generate LLNDK ABI dumps\n    # This command depends on ANDROID_BUILD_TOP\n    \"$ANDROID_HOST_OUT/bin/create_reference_dumps\" -release \"$TARGET_RELEASE\" --build-variant \"$TARGET_BUILD_VARIANT\" --lib-variant LLNDK\n}\n\nfunction create_new_compat_matrix_and_kernel_configs() {\n    # The compatibility matrix versions are bumped during vFRC\n    # These will change every time we have a new vFRC\n    local CURRENT_COMPATIBILITY_MATRIX_LEVEL=\"$FINAL_BOARD_API_LEVEL\"\n    local NEXT_COMPATIBILITY_MATRIX_LEVEL=\"$FINAL_NEXT_BOARD_API_LEVEL\"\n    # The kernel configs need the letter of the Android release\n    local CURRENT_RELEASE_LETTER=\"$FINAL_CORRESPONDING_VERSION_LETTER\"\n    local NEXT_RELEASE_LETTER=\"$FINAL_NEXT_CORRESPONDING_VERSION_LETTER\"\n\n\n    # build the targets required before touching the Android.bp/Android.mk files\n    local build_cmd=\"$top/build/soong/soong_ui.bash --make-mode\"\n    $build_cmd bpmodify\n\n    \"$top/prebuilts/build-tools/path/linux-x86/python3\" \"$top/hardware/interfaces/compatibility_matrices/bump.py\" \"$CURRENT_COMPATIBILITY_MATRIX_LEVEL\" \"$NEXT_COMPATIBILITY_MATRIX_LEVEL\" \"$CURRENT_RELEASE_LETTER\" \"$NEXT_RELEASE_LETTER\" \"$FINAL_CORRESPONDING_PLATFORM_VERSION\"\n\n    # Freeze the current framework manifest file. This relies on the\n    # aosp_cf_x86_64-trunk_staging build target to get the right manifest\n    # fragments installed.\n    \"$top/system/libhidl/vintfdata/freeze.sh\" \"$CURRENT_COMPATIBILITY_MATRIX_LEVEL\"\n}\n\nfunction freeze_framework_manifest() {\n   ANDROID_PRODUCT_OUT=~/workspace/internal/main/out/target/product/vsoc_x86 ANDROID_BUILD_TOP=~/workspace/internal/main ANDROID_HOST_OUT=~/workspace/internal/main/out/host/linux-x86 ./freeze.sh 202404\n\n}\n\n\nfinalize_vintf_resources\n\n"
  },
  {
    "path": "tools/finalization/frameworks_base.apply_hack.diff",
    "content": "From 3c9a5321dc94124367f2f4363d85a8f488f5d4d1 Mon Sep 17 00:00:00 2001\nFrom: Yurii Zubrytskyi <zyy@google.com>\nDate: Wed, 04 May 2022 01:05:24 -0700\nSubject: [PATCH] HACK: allow apps with pre-release SDK RESTRICT AUTOMERGE\n\nRevert before releasing\nLet the apps built with pre-release Tiramisu SDK parse\n+ fix a test that didn't expect REL builds to throw\n  when checking for lettered versions\n\nTest: build\nBug: 225745567\nBug: 231407096\nChange-Id: Ia0de2ab1a99e5f186f0d871e6225d88bf3308df6\n---\n\ndiff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java\nindex c15b3e0..3f4df4d 100644\n--- a/core/java/android/content/pm/PackageParser.java\n+++ b/core/java/android/content/pm/PackageParser.java\n@@ -2628,6 +2628,15 @@\n             return Build.VERSION_CODES.CUR_DEVELOPMENT;\n         }\n \n+        // STOPSHIP: hack for the pre-release SDK\n+        if (platformSdkCodenames.length == 0\n+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse(\"\").equals(\n+                targetCode)) {\n+            Slog.w(TAG, \"Package requires development platform \" + targetCode\n+                    + \", returning current version \" + Build.VERSION.SDK_INT);\n+            return Build.VERSION.SDK_INT;\n+        }\n+\n         // Otherwise, we're looking at an incompatible pre-release SDK.\n         if (platformSdkCodenames.length > 0) {\n             outError[0] = \"Requires development platform \" + targetCode\n@@ -2699,6 +2708,15 @@\n             return Build.VERSION_CODES.CUR_DEVELOPMENT;\n         }\n \n+        // STOPSHIP: hack for the pre-release SDK\n+        if (platformSdkCodenames.length == 0\n+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse(\"\").equals(\n+                minCode)) {\n+            Slog.w(TAG, \"Package requires min development platform \" + minCode\n+                    + \", returning current version \" + Build.VERSION.SDK_INT);\n+            return Build.VERSION.SDK_INT;\n+        }\n+\n         // Otherwise, we're looking at an incompatible pre-release SDK.\n         if (platformSdkCodenames.length > 0) {\n             outError[0] = \"Requires development platform \" + minCode\ndiff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java\nindex 3e1c5bb..8cc4cdb 100644\n--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java\n+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java\n@@ -316,6 +316,15 @@\n             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);\n         }\n \n+        // STOPSHIP: hack for the pre-release SDK\n+        if (platformSdkCodenames.length == 0\n+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse(\"\").equals(\n+                        minCode)) {\n+            Slog.w(TAG, \"Parsed package requires min development platform \" + minCode\n+                    + \", returning current version \" + Build.VERSION.SDK_INT);\n+            return input.success(Build.VERSION.SDK_INT);\n+        }\n+\n         // Otherwise, we're looking at an incompatible pre-release SDK.\n         if (platformSdkCodenames.length > 0) {\n             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,\n@@ -368,19 +377,27 @@\n             return input.success(targetVers);\n         }\n \n+        // If it's a pre-release SDK and the codename matches this platform, it\n+        // definitely targets this SDK.\n+        if (matchTargetCode(platformSdkCodenames, targetCode)) {\n+            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);\n+        }\n+\n+        // STOPSHIP: hack for the pre-release SDK\n+        if (platformSdkCodenames.length == 0\n+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse(\"\").equals(\n+                        targetCode)) {\n+            Slog.w(TAG, \"Parsed package requires development platform \" + targetCode\n+                    + \", returning current version \" + Build.VERSION.SDK_INT);\n+            return input.success(Build.VERSION.SDK_INT);\n+        }\n+\n         try {\n             if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {\n                 return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);\n             }\n         } catch (IllegalArgumentException e) {\n-            // isAtMost() throws it when encountering an older SDK codename\n-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());\n-        }\n-\n-        // If it's a pre-release SDK and the codename matches this platform, it\n-        // definitely targets this SDK.\n-        if (matchTargetCode(platformSdkCodenames, targetCode)) {\n-            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);\n+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, \"Bad package SDK\");\n         }\n \n         // Otherwise, we're looking at an incompatible pre-release SDK.\ndiff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java\nindex 92c7871..687e8f7 100644\n--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java\n+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java\n@@ -446,14 +446,14 @@\n                         + \"    <library \\n\"\n                         + \"        name=\\\"foo\\\"\\n\"\n                         + \"        file=\\\"\" + mFooJar + \"\\\"\\n\"\n-                        + \"        on-bootclasspath-before=\\\"Q\\\"\\n\"\n+                        + \"        on-bootclasspath-before=\\\"A\\\"\\n\"\n                         + \"        on-bootclasspath-since=\\\"W\\\"\\n\"\n                         + \"     />\\n\\n\"\n                         + \" </permissions>\";\n         parseSharedLibraries(contents);\n         assertFooIsOnlySharedLibrary();\n         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get(\"foo\");\n-        assertThat(entry.onBootclasspathBefore).isEqualTo(\"Q\");\n+        assertThat(entry.onBootclasspathBefore).isEqualTo(\"A\");\n         assertThat(entry.onBootclasspathSince).isEqualTo(\"W\");\n     }\n \n"
  },
  {
    "path": "tools/finalization/frameworks_base.revert_hack.diff",
    "content": "From b4ae5c71f327d00081bbb0b7b26d48eb88761fbc Mon Sep 17 00:00:00 2001\nFrom: Alex Buynytskyy <alexbuy@google.com>\nDate: Tue, 21 Feb 2023 01:43:14 +0000\nSubject: [PATCH] Revert \"HACK: allow apps with pre-release SDK RESTRICT AUTOMERGE\"\n\nThis reverts commit 3c9a5321dc94124367f2f4363d85a8f488f5d4d1.\n\nReason for revert: not needed anymore\n\nChange-Id: I5c5e3af78a41e7bd8cbc99464dccc57c345105f3\n---\n\ndiff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java\nindex 3f4df4d..c15b3e0 100644\n--- a/core/java/android/content/pm/PackageParser.java\n+++ b/core/java/android/content/pm/PackageParser.java\n@@ -2628,15 +2628,6 @@\n             return Build.VERSION_CODES.CUR_DEVELOPMENT;\n         }\n \n-        // STOPSHIP: hack for the pre-release SDK\n-        if (platformSdkCodenames.length == 0\n-                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse(\"\").equals(\n-                targetCode)) {\n-            Slog.w(TAG, \"Package requires development platform \" + targetCode\n-                    + \", returning current version \" + Build.VERSION.SDK_INT);\n-            return Build.VERSION.SDK_INT;\n-        }\n-\n         // Otherwise, we're looking at an incompatible pre-release SDK.\n         if (platformSdkCodenames.length > 0) {\n             outError[0] = \"Requires development platform \" + targetCode\n@@ -2708,15 +2699,6 @@\n             return Build.VERSION_CODES.CUR_DEVELOPMENT;\n         }\n \n-        // STOPSHIP: hack for the pre-release SDK\n-        if (platformSdkCodenames.length == 0\n-                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse(\"\").equals(\n-                minCode)) {\n-            Slog.w(TAG, \"Package requires min development platform \" + minCode\n-                    + \", returning current version \" + Build.VERSION.SDK_INT);\n-            return Build.VERSION.SDK_INT;\n-        }\n-\n         // Otherwise, we're looking at an incompatible pre-release SDK.\n         if (platformSdkCodenames.length > 0) {\n             outError[0] = \"Requires development platform \" + minCode\ndiff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java\nindex 8cc4cdb..3e1c5bb 100644\n--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java\n+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java\n@@ -316,15 +316,6 @@\n             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);\n         }\n \n-        // STOPSHIP: hack for the pre-release SDK\n-        if (platformSdkCodenames.length == 0\n-                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse(\"\").equals(\n-                        minCode)) {\n-            Slog.w(TAG, \"Parsed package requires min development platform \" + minCode\n-                    + \", returning current version \" + Build.VERSION.SDK_INT);\n-            return input.success(Build.VERSION.SDK_INT);\n-        }\n-\n         // Otherwise, we're looking at an incompatible pre-release SDK.\n         if (platformSdkCodenames.length > 0) {\n             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,\n@@ -377,27 +368,19 @@\n             return input.success(targetVers);\n         }\n \n-        // If it's a pre-release SDK and the codename matches this platform, it\n-        // definitely targets this SDK.\n-        if (matchTargetCode(platformSdkCodenames, targetCode)) {\n-            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);\n-        }\n-\n-        // STOPSHIP: hack for the pre-release SDK\n-        if (platformSdkCodenames.length == 0\n-                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse(\"\").equals(\n-                        targetCode)) {\n-            Slog.w(TAG, \"Parsed package requires development platform \" + targetCode\n-                    + \", returning current version \" + Build.VERSION.SDK_INT);\n-            return input.success(Build.VERSION.SDK_INT);\n-        }\n-\n         try {\n             if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {\n                 return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);\n             }\n         } catch (IllegalArgumentException e) {\n-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, \"Bad package SDK\");\n+            // isAtMost() throws it when encountering an older SDK codename\n+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());\n+        }\n+\n+        // If it's a pre-release SDK and the codename matches this platform, it\n+        // definitely targets this SDK.\n+        if (matchTargetCode(platformSdkCodenames, targetCode)) {\n+            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);\n         }\n \n         // Otherwise, we're looking at an incompatible pre-release SDK.\ndiff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java\nindex 687e8f7..92c7871 100644\n--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java\n+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java\n@@ -446,14 +446,14 @@\n                         + \"    <library \\n\"\n                         + \"        name=\\\"foo\\\"\\n\"\n                         + \"        file=\\\"\" + mFooJar + \"\\\"\\n\"\n-                        + \"        on-bootclasspath-before=\\\"A\\\"\\n\"\n+                        + \"        on-bootclasspath-before=\\\"Q\\\"\\n\"\n                         + \"        on-bootclasspath-since=\\\"W\\\"\\n\"\n                         + \"     />\\n\\n\"\n                         + \" </permissions>\";\n         parseSharedLibraries(contents);\n         assertFooIsOnlySharedLibrary();\n         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get(\"foo\");\n-        assertThat(entry.onBootclasspathBefore).isEqualTo(\"A\");\n+        assertThat(entry.onBootclasspathBefore).isEqualTo(\"Q\");\n         assertThat(entry.onBootclasspathSince).isEqualTo(\"W\");\n     }\n \n"
  },
  {
    "path": "tools/finalization/localonly-steps.sh",
    "content": "#!/bin/bash\n\nset -ex\n\nfunction finalize_locally() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n\n    # default target to modify tree and build SDK\n    local m=\"$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_RELEASE=fina_1 TARGET_BUILD_VARIANT=userdebug DIST_DIR=out/dist\"\n\n    # adb keys\n    # The keys are already generated for Android 15. Keeping the command (commented out) for future reference.\n    # $m adb\n    # LOGNAME=android-eng HOSTNAME=google.com \"$top/out/host/linux-x86/bin/adb\" keygen \"$top/vendor/google/security/adb/${FINAL_PLATFORM_VERSION}.adb_key\"\n\n    # Build Platform SDKs.\n    $top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=sdk TARGET_RELEASE=fina_1 TARGET_BUILD_VARIANT=userdebug sdk dist sdk_repo DIST_DIR=out/dist\n\n    # Build Modules SDKs.\n    TARGET_RELEASE=fina_1 TARGET_BUILD_VARIANT=userdebug UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true DIST_DIR=out/dist \"$top/vendor/google/build/mainline_modules_sdks.sh\" --build-release=next\n\n    # Update prebuilts.\n    \"$top/prebuilts/build-tools/path/linux-x86/python3\" -W ignore::DeprecationWarning \"$top/prebuilts/sdk/update_prebuilts.py\" --local_mode -f ${FINAL_PLATFORM_SDK_VERSION} -e ${FINAL_MAINLINE_EXTENSION} --bug 1 1\n}\n\nfinalize_locally\n"
  },
  {
    "path": "tools/finalization/step-0.sh",
    "content": "#!/bin/bash\n# Copyright 2024 Google Inc. All rights reserved.\n\n# Script to perform a 0th step of Android Finalization: VINTF finalization, create CLs and upload to Gerrit.\n\nset -ex\n\nfunction commit_step_0_changes() {\n    set +e\n    repo forall -c '\\\n        if [[ $(git status --short) ]]; then\n            repo start \"'$repo_branch'\" ;\n            git add -A . ;\n            git commit -m \"Vendor API level $FINAL_BOARD_API_LEVEL is now frozen\" \\\n                       -m \"Ignore-AOSP-First: VINTF $FINAL_BOARD_API_LEVEL Finalization\nBug: $FINAL_BUG_ID\nTest: build\";\n            repo upload '\"$repo_upload_dry_run_arg\"' --cbr --no-verify -o nokeycheck -t -y . ;\n        fi'\n}\n\nfunction finalize_step_0_main() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n    local repo_branch=\"VINTF-$FINAL_BOARD_API_LEVEL-Finalization\"\n    source $top/build/make/tools/finalization/command-line-options.sh\n\n    local m=\"$top/build/soong/soong_ui.bash --make-mode TARGET_RELEASE=next TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n\n    source $top/build/make/tools/finalization/finalize-vintf-resources.sh\n\n    # move all changes to finalization branch/topic and upload to gerrit\n    commit_step_0_changes\n\n    # build to confirm everything is OK\n    AIDL_FROZEN_REL=true $m\n}\n\nfinalize_step_0_main $@\n"
  },
  {
    "path": "tools/finalization/step-1.sh",
    "content": "#!/bin/bash\n# Script to perform a 1st step of Android Finalization: API/SDK finalization, create CLs and upload to Gerrit.\n\nset -ex\n\nfunction commit_step_1_changes() {\n    set +e\n    repo forall -c '\\\n        if [[ $(git status --short) ]]; then\n            repo start \"'$repo_branch'\" ;\n            git add -A . ;\n            git commit -m \"$FINAL_PLATFORM_CODENAME is now $FINAL_PLATFORM_SDK_VERSION and extension version $FINAL_MAINLINE_EXTENSION\" \\\n                       -m \"Ignore-AOSP-First: $FINAL_PLATFORM_CODENAME Finalization\nBug: $FINAL_BUG_ID\nTest: build\";\n            repo upload '\"$repo_upload_dry_run_arg\"' --cbr --no-verify -o nokeycheck -t -y . ;\n        fi'\n}\n\nfunction finalize_step_1_main() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n    local repo_branch=\"$FINAL_PLATFORM_CODENAME-SDK-Finalization\"\n    source $top/build/make/tools/finalization/command-line-options.sh\n\n    source $top/build/make/tools/finalization/finalize-sdk-resources.sh\n\n    # move all changes to finalization branch/topic and upload to gerrit\n    commit_step_1_changes\n\n    # build to confirm everything is OK\n    local m_next=\"$top/build/soong/soong_ui.bash --make-mode TARGET_RELEASE=next TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n    $m_next\n\n    local m_fina=\"$top/build/soong/soong_ui.bash --make-mode TARGET_RELEASE=fina_1 TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n    $m_fina\n}\n\nfinalize_step_1_main $@\n"
  },
  {
    "path": "tools/finalization/step-2.sh",
    "content": "#!/bin/bash\n# Script to perform a 2nd step of Android Finalization: REL finalization, create CLs and upload to Gerrit.\n\nfunction commit_step_2_changes() {\n    repo forall -c '\\\n        if [[ $(git status --short) ]]; then\n            repo start \"'$repo_branch'\" ;\n            git add -A . ;\n            git commit -m \"$FINAL_PLATFORM_CODENAME/$FINAL_PLATFORM_SDK_VERSION is now REL\" \\\n                       -m \"Ignore-AOSP-First: $FINAL_PLATFORM_CODENAME Finalization\nBug: $FINAL_BUG_ID\nTest: build\";\n\n            repo upload '\"$repo_upload_dry_run_arg\"' --cbr --no-verify -o nokeycheck -t -y . ;\n        fi'\n}\n\nfunction finalize_step_2_main() {\n    local top=\"$(dirname \"$0\")\"/../../../..\n    source $top/build/make/tools/finalization/environment.sh\n    local repo_branch=\"$FINAL_PLATFORM_CODENAME-SDK-Finalization-Rel\"\n    source $top/build/make/tools/finalization/command-line-options.sh\n\n    # prebuilts etc\n    source $top/build/make/tools/finalization/finalize-sdk-rel.sh\n\n    # move all changes to finalization branch/topic and upload to gerrit\n    commit_step_2_changes\n\n    # build to confirm everything is OK\n    local m_next=\"$top/build/soong/soong_ui.bash --make-mode TARGET_RELEASE=next TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n    $m_next\n\n    local m_fina=\"$top/build/soong/soong_ui.bash --make-mode TARGET_RELEASE=fina_2 TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug\"\n    $m_fina\n}\n\nfinalize_step_2_main $@\n"
  },
  {
    "path": "tools/find_static_candidates.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"Tool to find static libraries that maybe should be shared libraries and shared libraries that maybe should be static libraries.\n\nThis tool only looks at the module-info.json for the current target.\n\nExample of \"class\" types for each of the modules in module-info.json\n  \"EXECUTABLES\": 2307,\n  \"ETC\": 9094,\n  \"NATIVE_TESTS\": 10461,\n  \"APPS\": 2885,\n  \"JAVA_LIBRARIES\": 5205,\n  \"EXECUTABLES/JAVA_LIBRARIES\": 119,\n  \"FAKE\": 553,\n  \"SHARED_LIBRARIES/STATIC_LIBRARIES\": 7591,\n  \"STATIC_LIBRARIES\": 11535,\n  \"SHARED_LIBRARIES\": 10852,\n  \"HEADER_LIBRARIES\": 1897,\n  \"DYLIB_LIBRARIES\": 1262,\n  \"RLIB_LIBRARIES\": 3413,\n  \"ROBOLECTRIC\": 39,\n  \"PACKAGING\": 5,\n  \"PROC_MACRO_LIBRARIES\": 36,\n  \"RENDERSCRIPT_BITCODE\": 17,\n  \"DYLIB_LIBRARIES/RLIB_LIBRARIES\": 8,\n  \"ETC/FAKE\": 1\n\nNone of the \"SHARED_LIBRARIES/STATIC_LIBRARIES\" are double counted in the\nmodules with one class\nRLIB/\n\nAll of these classes have shared_libs and/or static_libs\n    \"EXECUTABLES\",\n    \"SHARED_LIBRARIES\",\n    \"STATIC_LIBRARIES\",\n    \"SHARED_LIBRARIES/STATIC_LIBRARIES\", # cc_library\n    \"HEADER_LIBRARIES\",\n    \"NATIVE_TESTS\", # test modules\n    \"DYLIB_LIBRARIES\", # rust\n    \"RLIB_LIBRARIES\", # rust\n    \"ETC\", # rust_bindgen\n\"\"\"\n\nfrom collections import defaultdict\n\nimport json, os, argparse\n\nANDROID_PRODUCT_OUT = os.environ.get(\"ANDROID_PRODUCT_OUT\")\n# If a shared library is used less than MAX_SHARED_INCLUSIONS times in a target,\n# then it will likely save memory by changing it to a static library\n# This move will also use less storage\nMAX_SHARED_INCLUSIONS = 2\n# If a static library is used more than MAX_STATIC_INCLUSIONS times in a target,\n# then it will likely save memory by changing it to a shared library\n# This move will also likely use less storage\nMIN_STATIC_INCLUSIONS = 3\n\n\ndef parse_args():\n  parser = argparse.ArgumentParser(\n      description=(\n          \"Parse module-info.jso and display information about static and\"\n          \" shared library dependencies.\"\n      )\n  )\n  parser.add_argument(\n      \"--module\", dest=\"module\", help=\"Print the info for the module.\"\n  )\n  parser.add_argument(\n      \"--shared\",\n      dest=\"print_shared\",\n      action=argparse.BooleanOptionalAction,\n      help=(\n          \"Print the list of libraries that are shared_libs for fewer than {}\"\n          \" modules.\".format(MAX_SHARED_INCLUSIONS)\n      ),\n  )\n  parser.add_argument(\n      \"--static\",\n      dest=\"print_static\",\n      action=argparse.BooleanOptionalAction,\n      help=(\n          \"Print the list of libraries that are static_libs for more than {}\"\n          \" modules.\".format(MIN_STATIC_INCLUSIONS)\n      ),\n  )\n  parser.add_argument(\n      \"--recursive\",\n      dest=\"recursive\",\n      action=argparse.BooleanOptionalAction,\n      default=True,\n      help=(\n          \"Gather all dependencies of EXECUTABLES recursvily before calculating\"\n          \" the stats. This eliminates duplicates from multiple libraries\"\n          \" including the same dependencies in a single binary.\"\n      ),\n  )\n  parser.add_argument(\n      \"--both\",\n      dest=\"both\",\n      action=argparse.BooleanOptionalAction,\n      default=False,\n      help=(\n          \"Print a list of libraries that are including libraries as both\"\n          \" static and shared\"\n      ),\n  )\n  return parser.parse_args()\n\n\nclass TransitiveHelper:\n\n  def __init__(self):\n    # keep a list of already expanded libraries so we don't end up in a cycle\n    self.visited = defaultdict(lambda: defaultdict(set))\n\n  # module is an object from the module-info dictionary\n  # module_info is the dictionary from module-info.json\n  # modify the module's shared_libs and static_libs with all of the transient\n  # dependencies required from all of the explicit dependencies\n  def flattenDeps(self, module, module_info):\n    libs_snapshot = dict(shared_libs = set(module.get(\"shared_libs\",{})), static_libs = set(module.get(\"static_libs\",{})))\n\n    for lib_class in [\"shared_libs\", \"static_libs\"]:\n      for lib in libs_snapshot[lib_class]:\n        if not lib or lib not in module_info or lib_class not in module:\n          continue\n        if lib in self.visited:\n          module[lib_class].update(self.visited[lib][lib_class])\n        else:\n          res = self.flattenDeps(module_info[lib], module_info)\n          module[lib_class].update(res.get(lib_class, {}))\n          self.visited[lib][lib_class].update(res.get(lib_class, {}))\n\n    return module\n\ndef main():\n  module_info = json.load(open(ANDROID_PRODUCT_OUT + \"/module-info.json\"))\n\n  args = parse_args()\n\n  if args.module:\n    if args.module not in module_info:\n      print(\"Module {} does not exist\".format(args.module))\n      exit(1)\n\n  # turn all of the static_libs and shared_libs lists into sets to make them\n  # easier to update\n  for _, module in module_info.items():\n    module[\"shared_libs\"] = set(module.get(\"shared_libs\", {}))\n    module[\"static_libs\"] = set(module.get(\"static_libs\", {}))\n\n  includedStatically = defaultdict(set)\n  includedSharedly = defaultdict(set)\n  includedBothly = defaultdict(set)\n  transitive = TransitiveHelper()\n  for name, module in module_info.items():\n    if args.recursive:\n      # in this recursive mode we only want to see what is included by the executables\n      if \"EXECUTABLES\" not in module[\"class\"]:\n        continue\n      module = transitive.flattenDeps(module, module_info)\n      # filter out fuzzers by their dependency on clang\n      if \"static_libs\" in module:\n        if \"libclang_rt.fuzzer\" in module[\"static_libs\"]:\n          continue\n    else:\n      if \"NATIVE_TESTS\" in module[\"class\"]:\n        # We don't care about how tests are including libraries\n        continue\n\n    # count all of the shared and static libs included in this module\n    if \"shared_libs\" in module:\n      for lib in module[\"shared_libs\"]:\n        includedSharedly[lib].add(name)\n    if \"static_libs\" in module:\n      for lib in module[\"static_libs\"]:\n        includedStatically[lib].add(name)\n\n    if \"shared_libs\" in module and  \"static_libs\" in module:\n      intersection = set(module[\"shared_libs\"]).intersection(\n          module[\"static_libs\"]\n      )\n      if intersection:\n        includedBothly[name] = intersection\n\n  if args.print_shared:\n    print(\n        \"Shared libraries that are included by fewer than {} modules on a\"\n        \" device:\".format(MAX_SHARED_INCLUSIONS)\n    )\n    for name, libs in includedSharedly.items():\n      if len(libs) < MAX_SHARED_INCLUSIONS:\n        print(\"{}: {} included by: {}\".format(name, len(libs), libs))\n\n  if args.print_static:\n    print(\n        \"Libraries that are included statically by more than {} modules on a\"\n        \" device:\".format(MIN_STATIC_INCLUSIONS)\n    )\n    for name, libs in includedStatically.items():\n      if len(libs) > MIN_STATIC_INCLUSIONS:\n        print(\"{}: {} included by: {}\".format(name, len(libs), libs))\n\n  if args.both:\n    allIncludedBothly = set()\n    for name, libs in includedBothly.items():\n      allIncludedBothly.update(libs)\n\n    print(\n        \"List of libraries used both statically and shared in the same\"\n        \" processes:\\n {}\\n\\n\".format(\"\\n\".join(sorted(allIncludedBothly)))\n    )\n    print(\n        \"List of libraries used both statically and shared in any processes:\\n {}\".format(\"\\n\".join(sorted(includedStatically.keys() & includedSharedly.keys()))))\n\n  if args.module:\n    print(json.dumps(module_info[args.module], default=list, indent=2))\n    print(\n        \"{} is included in shared_libs {} times by these modules: {}\".format(\n            args.module, len(includedSharedly[args.module]),\n            includedSharedly[args.module]\n        )\n    )\n    print(\n        \"{} is included in static_libs {} times by these modules: {}\".format(\n            args.module, len(includedStatically[args.module]),\n            includedStatically[args.module]\n        )\n    )\n    print(\"Shared libs included by this module that are used in fewer than {} processes:\\n{}\".format(\n        MAX_SHARED_INCLUSIONS, [x for x in module_info[args.module][\"shared_libs\"] if len(includedSharedly[x]) < MAX_SHARED_INCLUSIONS]))\n\n\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "tools/findleaves.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# Finds files with the specified name under a particular directory, stopping\n# the search in a given subdirectory when the file is found.\n#\n\nimport os\nimport sys\n\ndef perform_find(mindepth, prune, dirlist, filenames):\n  result = []\n  pruneleaves = set(map(lambda x: os.path.split(x)[1], prune))\n  seen = set()\n  for rootdir in dirlist:\n    rootdepth = rootdir.count(\"/\")\n    for root, dirs, files in os.walk(rootdir, followlinks=True):\n      # prune\n      check_prune = False\n      for d in dirs:\n        if d in pruneleaves:\n          check_prune = True\n          break\n      if check_prune:\n        i = 0\n        while i < len(dirs):\n          if dirs[i] in prune:\n            del dirs[i]\n          else:\n            i += 1\n      # mindepth\n      if mindepth > 0:\n        depth = 1 + root.count(\"/\") - rootdepth\n        if depth < mindepth:\n          continue\n      # match\n      for filename in filenames:\n        if filename in files:\n          result.append(os.path.join(root, filename))\n          del dirs[:]\n\n      # filter out inodes that have already been seen due to symlink loops\n      i = 0\n      while i < len(dirs):\n        st = os.stat(os.path.join(root, dirs[i]))\n        key = (st.st_dev, st.st_ino)\n        if key in seen:\n          del dirs[i]\n        else:\n          i += 1\n          seen.add(key)\n\n  return result\n\ndef usage():\n  sys.stderr.write(\"\"\"Usage: %(progName)s [<options>] [--dir=<dir>] <filenames>\nOptions:\n   --mindepth=<mindepth>\n       Both behave in the same way as their find(1) equivalents.\n   --prune=<dirname>\n       Avoids returning results from inside any directory called <dirname>\n       (e.g., \"*/out/*\"). May be used multiple times.\n   --dir=<dir>\n       Add a directory to search.  May be repeated multiple times.  For backwards\n       compatibility, if no --dir argument is provided then all but the last entry\n       in <filenames> are treated as directories.\n\"\"\" % {\n      \"progName\": os.path.split(sys.argv[0])[1],\n    })\n  sys.exit(1)\n\ndef main(argv):\n  mindepth = -1\n  prune = []\n  dirlist = []\n  i=1\n  while i<len(argv) and len(argv[i])>2 and argv[i][0:2] == \"--\":\n    arg = argv[i]\n    if arg.startswith(\"--mindepth=\"):\n      try:\n        mindepth = int(arg[len(\"--mindepth=\"):])\n      except ValueError:\n        usage()\n    elif arg.startswith(\"--prune=\"):\n      p = arg[len(\"--prune=\"):]\n      if len(p) == 0:\n        usage()\n      prune.append(p)\n    elif arg.startswith(\"--dir=\"):\n      d = arg[len(\"--dir=\"):]\n      if len(d) == 0:\n        usage()\n      dirlist.append(d)\n    else:\n      usage()\n    i += 1\n  if len(dirlist) == 0: # backwards compatibility\n    if len(argv)-i < 2: # need both <dirlist> and <filename>\n      usage()\n    dirlist = argv[i:-1]\n    filenames = [argv[-1]]\n  else:\n    if len(argv)-i < 1: # need <filename>\n      usage()\n    filenames = argv[i:]\n  results = list(set(perform_find(mindepth, prune, dirlist, filenames)))\n  results.sort()\n  for r in results:\n    print(r)\n\nif __name__ == \"__main__\":\n  main(sys.argv)\n"
  },
  {
    "path": "tools/fixlinebreaks.sh",
    "content": "#!/bin/sh\n#\n# Convert EOL convention on source files from CRLF to LF.\n#\n\necho \"Scanning...\"\nFILES=`find . \\( -iname '*.c' -o -iname '*.cpp' -o -iname '*.h' -o -iname '*.mk' -o -iname '*.html' -o -iname '*.css' \\) -print`\n\necho \"Converting...\"\nfor file in $FILES ; do\n\techo $file\n\ttr -d \\\\r < $file > _temp_file\n\tmv _temp_file $file\ndone\nexit 0\n\n"
  },
  {
    "path": "tools/fs_config/Android.bp",
    "content": "// Copyright (C) 2008 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nbootstrap_go_package {\n    name: \"soong-fs_config\",\n    pkgPath: \"android/soong/fs_config\",\n    deps: [\n        \"soong-android\",\n        \"soong-genrule\",\n    ],\n    srcs: [\n        \"fs_config.go\",\n    ],\n    pluginFor: [\"soong_build\"],\n}\n\ncc_binary_host {\n    name: \"fs_config\",\n    srcs: [\"fs_config.c\"],\n    shared_libs: [\n        \"libcutils\",\n    ],\n    cflags: [\"-Werror\"],\n}\n\npython_binary_host {\n    name: \"fs_config_generator\",\n    srcs: [\"fs_config_generator.py\"],\n}\n\npython_test_host {\n    name: \"test_fs_config_generator\",\n    main: \"test_fs_config_generator.py\",\n    srcs: [\n        \"test_fs_config_generator.py\",\n        \"fs_config_generator.py\",\n    ],\n}\n\ntarget_fs_config_gen_filegroup {\n    name: \"target_fs_config_gen\",\n}\n\ngenrule {\n    name: \"oemaids_header_gen\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) oemaid --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"generated_oem_aid.h\"],\n}\n\ncc_library_headers {\n    name: \"oemaids_headers\",\n    vendor_available: true,\n    generated_headers: [\"oemaids_header_gen\"],\n    export_generated_headers: [\"oemaids_header_gen\"],\n}\n\n// Generate the */etc/passwd text files for the target\n// These files may be empty if no AIDs are defined in\n// TARGET_FS_CONFIG_GEN files.\ngenrule {\n    name: \"passwd_gen_system\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) passwd --partition=system --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"passwd\"],\n}\n\nprebuilt_etc {\n    name: \"passwd_system\",\n    filename: \"passwd\",\n    src: \":passwd_gen_system\",\n}\n\ngenrule {\n    name: \"passwd_gen_vendor\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) passwd --partition=vendor --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"passwd\"],\n}\n\nprebuilt_etc {\n    name: \"passwd_vendor\",\n    filename: \"passwd\",\n    vendor: true,\n    src: \":passwd_gen_vendor\",\n}\n\ngenrule {\n    name: \"passwd_gen_odm\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) passwd --partition=odm --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"passwd\"],\n}\n\nprebuilt_etc {\n    name: \"passwd_odm\",\n    filename: \"passwd\",\n    device_specific: true,\n    src: \":passwd_gen_odm\",\n}\n\ngenrule {\n    name: \"passwd_gen_product\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) passwd --partition=product --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"passwd\"],\n}\n\nprebuilt_etc {\n    name: \"passwd_product\",\n    filename: \"passwd\",\n    product_specific: true,\n    src: \":passwd_gen_product\",\n}\n\ngenrule {\n    name: \"passwd_gen_system_ext\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) passwd --partition=system_ext --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"passwd\"],\n}\n\nprebuilt_etc {\n    name: \"passwd_system_ext\",\n    filename: \"passwd\",\n    system_ext_specific: true,\n    src: \":passwd_gen_system_ext\",\n}\n\n// Generate the */etc/group text files for the target\n// These files may be empty if no AIDs are defined in\n// TARGET_FS_CONFIG_GEN files.\ngenrule {\n    name: \"group_gen_system\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) group --partition=system --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"group\"],\n}\n\nprebuilt_etc {\n    name: \"group_system\",\n    filename: \"group\",\n    src: \":group_gen_system\",\n}\n\ngenrule {\n    name: \"group_gen_vendor\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) group --partition=vendor --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"group\"],\n}\n\nprebuilt_etc {\n    name: \"group_vendor\",\n    filename: \"group\",\n    vendor: true,\n    src: \":group_gen_vendor\",\n}\n\ngenrule {\n    name: \"group_gen_odm\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) group --partition=odm --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"group\"],\n}\n\nprebuilt_etc {\n    name: \"group_odm\",\n    filename: \"group\",\n    device_specific: true,\n    src: \":group_gen_odm\",\n}\n\ngenrule {\n    name: \"group_gen_product\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) group --partition=product --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"group\"],\n}\n\nprebuilt_etc {\n    name: \"group_product\",\n    filename: \"group\",\n    product_specific: true,\n    src: \":group_gen_product\",\n}\n\ngenrule {\n    name: \"group_gen_system_ext\",\n    tools: [\"fs_config_generator\"],\n    cmd: \"$(location fs_config_generator) group --partition=system_ext --aid-header=$(location :android_filesystem_config_header) $(locations :target_fs_config_gen) >$(out)\",\n    srcs: [\n        \":target_fs_config_gen\",\n        \":android_filesystem_config_header\",\n    ],\n    out: [\"group\"],\n}\n\nprebuilt_etc {\n    name: \"group_system_ext\",\n    filename: \"group\",\n    system_ext_specific: true,\n    src: \":group_gen_system_ext\",\n}\n\nfs_config_cmd = \"$(location fs_config_generator) fsconfig \" +\n    \"--aid-header $(location :android_filesystem_config_header) \" +\n    \"--capability-header $(location :linux_capability_header) \" +\n    \"--out_file $(out) \"\nfs_config_cmd_dirs = fs_config_cmd + \"--dirs \"\nfs_config_cmd_files = fs_config_cmd + \"--files \"\n\ngenrule_defaults {\n    name: \"fs_config_defaults\",\n    tools: [\"fs_config_generator\"],\n    srcs: [\n        \":android_filesystem_config_header\",\n        \":linux_capability_header\",\n        \":target_fs_config_gen\",\n    ],\n    out: [\"out\"],\n}\n\n// system\ngenrule {\n    name: \"fs_config_dirs_system_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_dirs +\n        \"--partition system \" +\n        \"--all-partitions vendor,oem,odm,vendor_dlkm,odm_dlkm,system_dlkm \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_dirs_system\",\n    filename: \"fs_config_dirs\",\n    src: \":fs_config_dirs_system_gen\",\n}\n\ngenrule {\n    name: \"fs_config_files_system_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_files +\n        \"--partition system \" +\n        \"--all-partitions vendor,oem,odm,vendor_dlkm,odm_dlkm,system_dlkm \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_files_system\",\n    filename: \"fs_config_files\",\n    src: \":fs_config_files_system_gen\",\n}\n\n// system_ext\ngenrule {\n    name: \"fs_config_dirs_system_ext_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_dirs +\n        \"--partition system_ext \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_dirs_system_ext\",\n    filename: \"fs_config_dirs\",\n    src: \":fs_config_dirs_system_ext_gen\",\n    system_ext_specific: true,\n}\n\ngenrule {\n    name: \"fs_config_files_system_ext_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_files +\n        \"--partition system_ext \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_files_system_ext\",\n    filename: \"fs_config_files\",\n    src: \":fs_config_files_system_ext_gen\",\n    system_ext_specific: true,\n}\n\n// product\ngenrule {\n    name: \"fs_config_dirs_product_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_dirs +\n        \"--partition product \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_dirs_product\",\n    filename: \"fs_config_dirs\",\n    src: \":fs_config_dirs_product_gen\",\n    product_specific: true,\n}\n\ngenrule {\n    name: \"fs_config_files_product_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_files +\n        \"--partition product \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_files_product\",\n    filename: \"fs_config_files\",\n    src: \":fs_config_files_product_gen\",\n    product_specific: true,\n}\n\n// vendor\ngenrule {\n    name: \"fs_config_dirs_vendor_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_dirs +\n        \"--partition vendor \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_dirs_vendor\",\n    filename: \"fs_config_dirs\",\n    src: \":fs_config_dirs_vendor_gen\",\n    vendor: true,\n}\n\ngenrule {\n    name: \"fs_config_files_vendor_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_files +\n        \"--partition vendor \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_files_vendor\",\n    filename: \"fs_config_files\",\n    src: \":fs_config_files_vendor_gen\",\n    vendor: true,\n}\n\n// odm\ngenrule {\n    name: \"fs_config_dirs_odm_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_dirs +\n        \"--partition odm \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_dirs_odm\",\n    filename: \"fs_config_dirs\",\n    src: \":fs_config_dirs_odm_gen\",\n    device_specific: true,\n}\n\ngenrule {\n    name: \"fs_config_files_odm_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_files +\n        \"--partition odm \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_files_odm\",\n    filename: \"fs_config_files\",\n    src: \":fs_config_files_odm_gen\",\n    device_specific: true,\n}\n\n// system_dlkm\ngenrule {\n    name: \"fs_config_dirs_system_dlkm_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_dirs +\n        \"--partition system_dlkm \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_dirs_system_dlkm\",\n    filename: \"fs_config_dirs\",\n    src: \":fs_config_dirs_system_dlkm_gen\",\n    system_dlkm_specific: true,\n}\n\ngenrule {\n    name: \"fs_config_files_system_dlkm_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_files +\n        \"--partition system_dlkm \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_files_system_dlkm\",\n    filename: \"fs_config_files\",\n    src: \":fs_config_files_system_dlkm_gen\",\n    system_dlkm_specific: true,\n}\n\n// vendor_dlkm\ngenrule {\n    name: \"fs_config_dirs_vendor_dlkm_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_dirs +\n        \"--partition vendor_dlkm \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_dirs_vendor_dlkm\",\n    filename: \"fs_config_dirs\",\n    src: \":fs_config_dirs_vendor_dlkm_gen\",\n    vendor_dlkm_specific: true,\n}\n\ngenrule {\n    name: \"fs_config_files_vendor_dlkm_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_files +\n        \"--partition vendor_dlkm \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_files_vendor_dlkm\",\n    filename: \"fs_config_files\",\n    src: \":fs_config_files_vendor_dlkm_gen\",\n    vendor_dlkm_specific: true,\n}\n\n// odm_dlkm\ngenrule {\n    name: \"fs_config_dirs_odm_dlkm_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_dirs +\n        \"--partition odm_dlkm \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_dirs_odm_dlkm\",\n    filename: \"fs_config_dirs\",\n    src: \":fs_config_dirs_odm_dlkm_gen\",\n    odm_dlkm_specific: true,\n}\n\ngenrule {\n    name: \"fs_config_files_odm_dlkm_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_files +\n        \"--partition odm_dlkm \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_files_odm_dlkm\",\n    filename: \"fs_config_files\",\n    src: \":fs_config_files_odm_dlkm_gen\",\n    odm_dlkm_specific: true,\n}\n\n// oem\ngenrule {\n    name: \"fs_config_dirs_oem_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_dirs +\n        \"--partition oem \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_dirs_oem\",\n    filename: \"fs_config_dirs\",\n    src: \":fs_config_dirs_oem_gen\",\n    oem_specific: true,\n}\n\ngenrule {\n    name: \"fs_config_files_oem_gen\",\n    defaults: [\"fs_config_defaults\"],\n    cmd: fs_config_cmd_files +\n        \"--partition oem \" +\n        \"$(locations :target_fs_config_gen)\",\n}\n\nprebuilt_etc {\n    name: \"fs_config_files_oem\",\n    filename: \"fs_config_files\",\n    src: \":fs_config_files_oem_gen\",\n    oem_specific: true,\n}\n\n// Generate the <p>/etc/fs_config_dirs binary files for each partition.\n// Add fs_config_dirs to PRODUCT_PACKAGES in the device make file to enable.\nphony {\n    name: \"fs_config_dirs\",\n    required: [\n        \"fs_config_dirs_system\",\n        \"fs_config_dirs_system_ext\",\n        \"fs_config_dirs_product\",\n        \"fs_config_dirs_nonsystem\",\n    ],\n}\n\n// Generate the <p>/etc/fs_config_files binary files for each partition.\n// Add fs_config_files to PRODUCT_PACKAGES in the device make file to enable.\nphony {\n    name: \"fs_config_files\",\n    required: [\n        \"fs_config_files_system\",\n        \"fs_config_files_system_ext\",\n        \"fs_config_files_product\",\n        \"fs_config_files_nonsystem\",\n    ],\n}\n\n// Generate the <p>/etc/fs_config_dirs binary files for all enabled partitions\n// excluding /system, /system_ext and /product. Add fs_config_dirs_nonsystem to\n// PRODUCT_PACKAGES in the device make file to enable.\nphony {\n    name: \"fs_config_dirs_nonsystem\",\n    required: [] +\n        select(soong_config_variable(\"fs_config\", \"vendor\"), {\n            true: [\"fs_config_dirs_vendor\"],\n            default: [],\n        }) +\n        select(soong_config_variable(\"fs_config\", \"oem\"), {\n            true: [\"fs_config_dirs_oem\"],\n            default: [],\n        }) +\n        select(soong_config_variable(\"fs_config\", \"odm\"), {\n            true: [\"fs_config_dirs_odm\"],\n            default: [],\n        }) +\n        select(soong_config_variable(\"fs_config\", \"vendor_dlkm\"), {\n            true: [\"fs_config_dirs_vendor_dlkm\"],\n            default: [],\n        }) +\n        select(soong_config_variable(\"fs_config\", \"odm_dlkm\"), {\n            true: [\"fs_config_dirs_odm_dlkm\"],\n            default: [],\n        }) +\n        select(soong_config_variable(\"fs_config\", \"system_dlkm\"), {\n            true: [\"fs_config_dirs_system_dlkm\"],\n            default: [],\n        }),\n}\n\n// Generate the <p>/etc/fs_config_files binary files for all enabled partitions\n// excluding /system, /system_ext and /product. Add fs_config_files_nonsystem to\n// PRODUCT_PACKAGES in the device make file to enable.\nphony {\n    name: \"fs_config_files_nonsystem\",\n    required: [] +\n        select(soong_config_variable(\"fs_config\", \"vendor\"), {\n            true: [\"fs_config_files_vendor\"],\n            default: [],\n        }) +\n        select(soong_config_variable(\"fs_config\", \"oem\"), {\n            true: [\"fs_config_files_oem\"],\n            default: [],\n        }) +\n        select(soong_config_variable(\"fs_config\", \"odm\"), {\n            true: [\"fs_config_files_odm\"],\n            default: [],\n        }) +\n        select(soong_config_variable(\"fs_config\", \"vendor_dlkm\"), {\n            true: [\"fs_config_files_vendor_dlkm\"],\n            default: [],\n        }) +\n        select(soong_config_variable(\"fs_config\", \"odm_dlkm\"), {\n            true: [\"fs_config_files_odm_dlkm\"],\n            default: [],\n        }) +\n        select(soong_config_variable(\"fs_config\", \"system_dlkm\"), {\n            true: [\"fs_config_files_system_dlkm\"],\n            default: [],\n        }),\n}\n"
  },
  {
    "path": "tools/fs_config/OWNERS",
    "content": "include platform/system/core:/janitors/OWNERS\n"
  },
  {
    "path": "tools/fs_config/README.md",
    "content": "# FS Config Generator\n\nThe `fs_config_generator.py` tool uses the platform `android_filesystem_config.h` and the\n`TARGET_FS_CONFIG_GEN` files to generate the following:\n* `fs_config_dirs` and `fs_config_files` files for each partition\n* `passwd` and `group` files for each partition\n* The `generated_oem_aid.h` header\n\n## Outputs\n\n### `fs_config_dirs` and `fs_config_files`\n\nThe `fs_config_dirs` and `fs_config_files` binary files are interpreted by the libcutils\n`fs_config()` function, along with the built-in defaults, to serve as overrides to complete the\nresults. The Target files are used by filesystem and adb tools to ensure that the file and directory\nproperties are preserved during runtime operations. The host files in the `$OUT` directory are used\nin the final stages when building the filesystem images to set the file and directory properties.\n\nSee `./fs_config_generator.py fsconfig --help` for how these files are generated.\n\n### `passwd` and `group` files\n\nThe `passwd` and `group` files are formatted as documented in man pages passwd(5) and group(5) and\nused by bionic for implementing `getpwnam()` and related functions.\n\nSee `./fs_config_generator.py passwd --help` and `./fs_config_generator.py group --help` for how\nthese files are generated.\n\n### The `generated_oem_aid.h` header\n\nThe `generated_oem_aid.h` creates identifiers for non-platform AIDs for developers wishing to use\nthem in their native code.  To do so, include the `oemaids_headers` header library in the\ncorresponding makefile and `#include \"generated_oem_aid.h\"` in the code wishing to use these\nidentifiers.\n\nSee `./fs_config_generator.py oemaid --help` for how this file is generated.\n\n## Parsing\n\nSee the documentation on [source.android.com](https://source.android.com/devices/tech/config/filesystem#configuring-aids) for details and examples.\n\n\n## Ordering\n\nOrdering within the `TARGET_FS_CONFIG_GEN` files is not relevant. The paths for files are sorted\nlike so within their respective array definition:\n * specified path before prefix match\n   * for example: foo before f*\n * lexicographical less than before other\n   * for example: boo before foo\n\nGiven these paths:\n\n    paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*']\n\nThe sort order would be:\n\n    paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*']\n\nThus the `fs_config` tools will match on specified paths before attempting prefix, and match on the\nlongest matching prefix.\n\nThe declared AIDs are sorted in ascending numerical order based on the option \"value\". The string\nrepresentation of value is preserved. Both choices were made for maximum readability of the\ngenerated file and to line up files. Sync lines are placed with the source file as comments in the\ngenerated header file.\n\n## Unit Tests\n\nFrom within the `fs_config` directory, unit tests can be executed like so:\n\n    $ python test_fs_config_generator.py\n    ................\n    ----------------------------------------------------------------------\n    Ran 16 tests in 0.004s\n    OK\n\n\nOne could also use nose if they would like:\n\n    $ nose2\n\nTo add new tests, simply add a `test_<xxx>` method to the test class. It will automatically\nget picked up and added to the test suite.\n"
  },
  {
    "path": "tools/fs_config/end_to_end_test/config.fs",
    "content": "# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This file is used to define the properties of the filesystem\n# images generated by build tools (mkbootfs and mkyaffs2image) and\n# by the device side of adb.\n\n[AID_VENDOR_NEW_SERVICE]\nvalue: 2900\n\n[AID_VENDOR_NEW_SERVICE_TWO]\nvalue:2902\n\n[vendor/bin/service1]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_VENDOR_NEW_SERVICE\ncaps: CHOWN DAC_OVERRIDE\n\n[vendor/bin/service2]\nmode: 0755\nuser: AID_VENDOR_NEW_SERVICE_TWO\ngroup: AID_SYSTEM\ncaps: AUDIT_READ CHOWN SYS_ADMIN\n\n[system/vendor/bin/service3]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_SYSTEM\ncaps: AUDIT_READ CHOWN SYS_ADMIN\n\n[vendor/dir/]\nmode: 0755\nuser: AID_VENDOR_NEW_SERVICE_TWO\ngroup: AID_SYSTEM\ncaps: 0\n\n[system/vendor/dir2/]\nmode: 0755\nuser: AID_VENDOR_NEW_SERVICE_TWO\ngroup: AID_SYSTEM\ncaps: 0\n\n[product/bin/service1]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_SYSTEM\ncaps: 0x34\n\n[product/bin/service2]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_SYSTEM\ncaps: NET_BIND_SERVICE WAKE_ALARM\n\n[system/product/bin/service3]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_SYSTEM\ncaps: NET_BIND_SERVICE WAKE_ALARM\n\n[product/dir/]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_SYSTEM\ncaps: 0\n\n[system/product/dir/]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_SYSTEM\ncaps: 0\n\n[system/bin/service]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_RADIO\ncaps: NET_BIND_SERVICE\n\n[system/dir/]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_RADIO\ncaps: 0\n\n[root_file]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_RADIO\ncaps: 0\n\n[root_dir/]\nmode: 0755\nuser: AID_SYSTEM\ngroup: AID_RADIO\ncaps: 0\n"
  },
  {
    "path": "tools/fs_config/end_to_end_test/run_test.sh",
    "content": "cd $ANDROID_BUILD_TOP/build/make/tools/fs_config/end_to_end_test\n\n$ANDROID_BUILD_TOP/build/make/tools/fs_config/fs_config_generator.py fsconfig \\\n  --aid-header $ANDROID_BUILD_TOP/system/core/libcutils/include/private/android_filesystem_config.h \\\n  --capability-header $ANDROID_BUILD_TOP/bionic/libc/kernel/uapi/linux/capability.h \\\n  --partition system \\\n  --all-partitions vendor,product \\\n  --files \\\n  --out_file result_system_fs_config_files \\\n  ./config.fs\n\ndiff system_fs_config_files result_system_fs_config_files 1>/dev/null && echo 'Success system_fs_config_files' ||\n  echo 'Fail: Mismatch between system_fs_config_files and result_system_fs_config_files'\n\n$ANDROID_BUILD_TOP/build/make/tools/fs_config/fs_config_generator.py fsconfig \\\n  --aid-header $ANDROID_BUILD_TOP/system/core/libcutils/include/private/android_filesystem_config.h \\\n  --capability-header $ANDROID_BUILD_TOP/bionic/libc/kernel/uapi/linux/capability.h \\\n  --partition system \\\n  --all-partitions vendor,product \\\n  --dirs \\\n  --out_file result_system_fs_config_dirs \\\n  ./config.fs\n\ndiff system_fs_config_dirs result_system_fs_config_dirs 1>/dev/null && echo 'Success system_fs_config_dirs' ||\n  echo 'Fail: Mismatch between system_fs_config_dirs and result_system_fs_config_dirs'\n\n$ANDROID_BUILD_TOP/build/make/tools/fs_config/fs_config_generator.py fsconfig \\\n  --aid-header $ANDROID_BUILD_TOP/system/core/libcutils/include/private/android_filesystem_config.h \\\n  --capability-header $ANDROID_BUILD_TOP/bionic/libc/kernel/uapi/linux/capability.h \\\n  --partition vendor \\\n  --files \\\n  --out_file result_vendor_fs_config_files \\\n  ./config.fs\n\ndiff vendor_fs_config_files result_vendor_fs_config_files 1>/dev/null && echo 'Success vendor_fs_config_files' ||\n  echo 'Fail: Mismatch between vendor_fs_config_files and result_vendor_fs_config_files'\n\n$ANDROID_BUILD_TOP/build/make/tools/fs_config/fs_config_generator.py fsconfig \\\n  --aid-header $ANDROID_BUILD_TOP/system/core/libcutils/include/private/android_filesystem_config.h \\\n  --capability-header $ANDROID_BUILD_TOP/bionic/libc/kernel/uapi/linux/capability.h \\\n  --partition vendor \\\n  --dirs \\\n  --out_file result_vendor_fs_config_dirs \\\n  ./config.fs\n\ndiff vendor_fs_config_dirs result_vendor_fs_config_dirs 1>/dev/null && echo 'Success vendor_fs_config_dirs' ||\n  echo 'Fail: Mismatch between vendor_fs_config_dirs and result_vendor_fs_config_dirs'\n\n$ANDROID_BUILD_TOP/build/make/tools/fs_config/fs_config_generator.py fsconfig \\\n  --aid-header $ANDROID_BUILD_TOP/system/core/libcutils/include/private/android_filesystem_config.h \\\n  --capability-header $ANDROID_BUILD_TOP/bionic/libc/kernel/uapi/linux/capability.h \\\n  --partition product \\\n  --files \\\n  --out_file result_product_fs_config_files \\\n  ./config.fs\n\ndiff product_fs_config_files result_product_fs_config_files 1>/dev/null && echo 'Success product_fs_config_files' ||\n  echo 'Fail: Mismatch between product_fs_config_files and result_product_fs_config_files'\n\n$ANDROID_BUILD_TOP/build/make/tools/fs_config/fs_config_generator.py fsconfig \\\n  --aid-header $ANDROID_BUILD_TOP/system/core/libcutils/include/private/android_filesystem_config.h \\\n  --capability-header $ANDROID_BUILD_TOP/bionic/libc/kernel/uapi/linux/capability.h \\\n  --partition product \\\n  --dirs \\\n  --out_file result_product_fs_config_dirs \\\n  ./config.fs\n\ndiff product_fs_config_dirs result_product_fs_config_dirs 1>/dev/null && echo 'Success product_fs_config_dirs' ||\n  echo 'Fail: Mismatch between product_fs_config_dirs and result_product_fs_config_dirs'\n\nrm result_system_fs_config_files\nrm result_system_fs_config_dirs\nrm result_vendor_fs_config_files\nrm result_vendor_fs_config_dirs\nrm result_product_fs_config_files\nrm result_product_fs_config_dirs\n"
  },
  {
    "path": "tools/fs_config/fs_config.c",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <errno.h>\n#include <unistd.h>\n#include <string.h>\n#include <inttypes.h>\n\n#include \"private/android_filesystem_config.h\"\n#include \"private/fs_config.h\"\n\n// This program takes a list of files and directories (indicated by a\n// trailing slash) on the stdin, and prints to stdout each input\n// filename along with its desired uid, gid, and mode (in octal).\n// The leading slash should be stripped from the input.\n//\n// After the first 4 columns, optional key=value pairs are emitted\n// for each file.  Currently, the following keys are supported:\n//\n//   -C: capabilities=[hex capabilities value]\n//\n// Example input:\n//\n//      system/etc/dbus.conf\n//      data/app/\n//\n// Output:\n//\n//      system/etc/dbus.conf 1002 1002 440\n//      data/app 1000 1000 771\n//\n// Note that the output will omit the trailing slash from\n// directories.\n\nstatic void usage() {\n  fprintf(stderr, \"Usage: fs_config [-D product_out_path] [-R root] [-C]\\n\");\n}\n\nint main(int argc, char** argv) {\n  char buffer[1024];\n  const char* product_out_path = NULL;\n  char* root_path = NULL;\n  int print_capabilities = 0;\n  int opt;\n  while((opt = getopt(argc, argv, \"CR:D:\")) != -1) {\n    switch(opt) {\n    case 'C':\n      print_capabilities = 1;\n      break;\n    case 'R':\n      root_path = optarg;\n      break;\n    case 'D':\n      product_out_path = optarg;\n      break;\n    default:\n      usage();\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  if (root_path != NULL) {\n    size_t root_len = strlen(root_path);\n    /* Trim any trailing slashes from the root path. */\n    while (root_len && root_path[--root_len] == '/') {\n      root_path[root_len] = '\\0';\n    }\n  }\n\n  while (fgets(buffer, 1023, stdin) != NULL) {\n    int is_dir = 0;\n    int i;\n    for (i = 0; i < 1024 && buffer[i]; ++i) {\n      switch (buffer[i]) {\n        case '\\n':\n          buffer[i-is_dir] = '\\0';\n          if (i == 0) {\n            is_dir = 1; // empty line is considered as root directory\n          }\n          i = 1025;\n          break;\n        case '/':\n          is_dir = 1;\n          break;\n        default:\n          is_dir = 0;\n          break;\n      }\n    }\n\n    unsigned uid = 0, gid = 0, mode = 0;\n    uint64_t capabilities;\n    fs_config(buffer, is_dir, product_out_path, &uid, &gid, &mode, &capabilities);\n    if (root_path != NULL && strcmp(buffer, root_path) == 0) {\n      /* The root of the filesystem needs to be an empty string. */\n      strcpy(buffer, \"\");\n    }\n    printf(\"%s %d %d %o\", buffer, uid, gid, mode);\n\n    if (print_capabilities) {\n      printf(\" capabilities=0x%\" PRIx64, capabilities);\n    }\n\n    printf(\"\\n\");\n  }\n  return 0;\n}\n"
  },
  {
    "path": "tools/fs_config/fs_config.go",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage fs_config\n\nimport (\n\t\"android/soong/android\"\n)\n\nvar pctx = android.NewPackageContext(\"android/soong/fs_config\")\n\nfunc init() {\n\tandroid.RegisterModuleType(\"target_fs_config_gen_filegroup\", targetFSConfigGenFactory)\n}\n\n// target_fs_config_gen_filegroup is used to expose the files pointed to by TARGET_FS_CONFIG_GEN to\n// genrules in Soong. If TARGET_FS_CONFIG_GEN is empty, it will export an empty file instead.\nfunc targetFSConfigGenFactory() android.Module {\n\tmodule := &targetFSConfigGen{}\n\tandroid.InitAndroidModule(module)\n\treturn module\n}\n\nvar _ android.SourceFileProducer = (*targetFSConfigGen)(nil)\n\ntype targetFSConfigGen struct {\n\tandroid.ModuleBase\n\tpaths android.Paths\n}\n\nfunc (targetFSConfigGen) DepsMutator(ctx android.BottomUpMutatorContext) {}\n\nfunc (t *targetFSConfigGen) GenerateAndroidBuildActions(ctx android.ModuleContext) {\n\tif ret := ctx.DeviceConfig().TargetFSConfigGen(); len(ret) != 0 {\n\t\tt.paths = android.PathsForSource(ctx, ret)\n\t} else {\n\t\tpath := android.PathForModuleGen(ctx, \"empty\")\n\t\tt.paths = android.Paths{path}\n\n\t\trule := android.NewRuleBuilder(pctx, ctx)\n\t\trule.Command().Text(\"rm -rf\").Output(path)\n\t\trule.Command().Text(\"touch\").Output(path)\n\t\trule.Build(\"fs_config_empty\", \"create empty file\")\n\t}\n}\n\nfunc (t *targetFSConfigGen) Srcs() android.Paths {\n\treturn t.paths\n}\n"
  },
  {
    "path": "tools/fs_config/fs_config_generator.py",
    "content": "#!/usr/bin/env python3\n\"\"\"Generates config files for Android file system properties.\n\nThis script is used for generating configuration files for configuring\nAndroid filesystem properties. Internally, its composed of a plug-able\ninterface to support the understanding of new input and output parameters.\n\nRun the help for a list of supported plugins and their capabilities.\n\nFurther documentation can be found in the README.\n\"\"\"\n\nimport argparse\nimport configparser\nimport ctypes\nimport re\nimport sys\nimport textwrap\n\n# Keep the tool in one file to make it easy to run.\n# pylint: disable=too-many-lines\n\n\n# Lowercase generator used to be inline with @staticmethod.\nclass generator(object):  # pylint: disable=invalid-name\n    \"\"\"A decorator class to add commandlet plugins.\n\n    Used as a decorator to classes to add them to\n    the internal plugin interface. Plugins added\n    with @generator() are automatically added to\n    the command line.\n\n    For instance, to add a new generator\n    called foo and have it added just do this:\n\n        @generator(\"foo\")\n        class FooGen(object):\n            ...\n    \"\"\"\n    _generators = {}\n\n    def __init__(self, gen):\n        \"\"\"\n        Args:\n            gen (str): The name of the generator to add.\n\n        Raises:\n            ValueError: If there is a similarly named generator already added.\n\n        \"\"\"\n        self._gen = gen\n\n        if gen in generator._generators:\n            raise ValueError('Duplicate generator name: ' + gen)\n\n        generator._generators[gen] = None\n\n    def __call__(self, cls):\n\n        generator._generators[self._gen] = cls()\n        return cls\n\n    @staticmethod\n    def get():\n        \"\"\"Gets the list of generators.\n\n        Returns:\n           The list of registered generators.\n        \"\"\"\n        return generator._generators\n\n\nclass Utils(object):\n    \"\"\"Various assorted static utilities.\"\"\"\n\n    @staticmethod\n    def in_any_range(value, ranges):\n        \"\"\"Tests if a value is in a list of given closed range tuples.\n\n        A range tuple is a closed range. That means it's inclusive of its\n        start and ending values.\n\n        Args:\n            value (int): The value to test.\n            range [(int, int)]: The closed range list to test value within.\n\n        Returns:\n            True if value is within the closed range, false otherwise.\n        \"\"\"\n\n        return any(lower <= value <= upper for (lower, upper) in ranges)\n\n    @staticmethod\n    def get_login_and_uid_cleansed(aid):\n        \"\"\"Returns a passwd/group file safe logon and uid.\n\n        This checks that the logon and uid of the AID do not\n        contain the delimiter \":\" for a passwd/group file.\n\n        Args:\n            aid (AID): The aid to check\n\n        Returns:\n            logon, uid of the AID after checking its safe.\n\n        Raises:\n            ValueError: If there is a delimiter charcter found.\n        \"\"\"\n        logon = aid.friendly\n        uid = aid.normalized_value\n        if ':' in uid:\n            raise ValueError(\n                'Cannot specify delimiter character \":\" in uid: \"%s\"' % uid)\n        if ':' in logon:\n            raise ValueError(\n                'Cannot specify delimiter character \":\" in logon: \"%s\"' %\n                logon)\n        return logon, uid\n\n\nclass AID(object):\n    \"\"\"This class represents an Android ID or an AID.\n\n    Attributes:\n        identifier (str): The identifier name for a #define.\n        value (str) The User Id (uid) of the associate define.\n        found (str) The file it was found in, can be None.\n        normalized_value (str): Same as value, but base 10.\n        friendly (str): The friendly name of aid.\n    \"\"\"\n\n    PREFIX = 'AID_'\n\n    # Some of the AIDS like AID_MEDIA_EX had names like mediaex\n    # list a map of things to fixup until we can correct these\n    # at a later date.\n    _FIXUPS = {\n        'media_drm': 'mediadrm',\n        'media_ex': 'mediaex',\n        'media_codec': 'mediacodec'\n    }\n\n    def __init__(self, identifier, value, found, login_shell):\n        \"\"\"\n        Args:\n            identifier: The identifier name for a #define <identifier>.\n            value: The value of the AID, aka the uid.\n            found (str): The file found in, not required to be specified.\n            login_shell (str): The shell field per man (5) passwd file.\n        Raises:\n            ValueError: if the friendly name is longer than 31 characters as\n                that is bionic's internal buffer size for name.\n            ValueError: if value is not a valid string number as processed by\n                int(x, 0)\n        \"\"\"\n        self.identifier = identifier\n        self.value = value\n        self.found = found\n        self.login_shell = login_shell\n\n        try:\n            self.normalized_value = str(int(value, 0))\n        except ValueError:\n            raise ValueError(\n                'Invalid \"value\", not aid number, got: \\\"%s\\\"' % value)\n\n        # Where we calculate the friendly name\n        friendly = identifier[len(AID.PREFIX):].lower()\n        self.friendly = AID._fixup_friendly(friendly)\n\n        if len(self.friendly) > 31:\n            raise ValueError(\n                'AID names must be under 32 characters \"%s\"' % self.friendly)\n\n    def __eq__(self, other):\n\n        return self.identifier == other.identifier \\\n            and self.value == other.value and self.found == other.found \\\n            and self.normalized_value == other.normalized_value \\\n            and self.login_shell == other.login_shell\n\n    def __repr__(self):\n        return \"AID { identifier = %s, value = %s, normalized_value = %s, login_shell = %s }\" % (\n            self.identifier, self.value, self.normalized_value, self.login_shell)\n\n    @staticmethod\n    def is_friendly(name):\n        \"\"\"Determines if an AID is a freindly name or C define.\n\n        For example if name is AID_SYSTEM it returns false, if name\n        was system, it would return true.\n\n        Returns:\n            True if name is a friendly name False otherwise.\n        \"\"\"\n\n        return not name.startswith(AID.PREFIX)\n\n    @staticmethod\n    def _fixup_friendly(friendly):\n        \"\"\"Fixup friendly names that historically don't follow the convention.\n\n        Args:\n            friendly (str): The friendly name.\n\n        Returns:\n            The fixedup friendly name as a str.\n        \"\"\"\n\n        if friendly in AID._FIXUPS:\n            return AID._FIXUPS[friendly]\n\n        return friendly\n\n\nclass FSConfig(object):\n    \"\"\"Represents a filesystem config array entry.\n\n    Represents a file system configuration entry for specifying\n    file system capabilities.\n\n    Attributes:\n        mode (str): The mode of the file or directory.\n        user (str): The uid or #define identifier (AID_SYSTEM)\n        group (str): The gid or #define identifier (AID_SYSTEM)\n        caps (str): The capability set.\n        path (str): The path of the file or directory.\n        filename (str): The file it was found in.\n    \"\"\"\n\n    def __init__(self, mode, user, group, caps, path, filename):\n        \"\"\"\n        Args:\n            mode (str): The mode of the file or directory.\n            user (str): The uid or #define identifier (AID_SYSTEM)\n            group (str): The gid or #define identifier (AID_SYSTEM)\n            caps (str): The capability set as a list.\n            path (str): The path of the file or directory.\n            filename (str): The file it was found in.\n        \"\"\"\n        self.mode = mode\n        self.user = user\n        self.group = group\n        self.caps = caps\n        self.path = path\n        self.filename = filename\n\n    def __eq__(self, other):\n\n        return self.mode == other.mode and self.user == other.user \\\n            and self.group == other.group and self.caps == other.caps \\\n            and self.path == other.path and self.filename == other.filename\n\n    def __repr__(self):\n        return 'FSConfig(%r, %r, %r, %r, %r, %r)' % (self.mode, self.user,\n                                                     self.group, self.caps,\n                                                     self.path, self.filename)\n\n\nclass CapabilityHeaderParser(object):\n    \"\"\"Parses capability.h file\n\n    Parses a C header file and extracts lines starting with #define CAP_<name>.\n    \"\"\"\n\n    _CAP_DEFINE = re.compile(r'\\s*#define\\s+(CAP_\\S+)\\s+(\\S+)')\n    _SKIP_CAPS = ['CAP_LAST_CAP', 'CAP_TO_INDEX(x)', 'CAP_TO_MASK(x)']\n\n    def __init__(self, capability_header):\n        \"\"\"\n        Args:\n            capability_header (str): file name for the header file containing AID entries.\n        \"\"\"\n\n        self.caps = {}\n        with open(capability_header) as open_file:\n            self._parse(open_file)\n\n    def _parse(self, capability_file):\n        \"\"\"Parses a capability header file. Internal use only.\n\n        Args:\n            capability_file (file): The open capability header file to parse.\n        \"\"\"\n\n        for line in capability_file:\n            match = CapabilityHeaderParser._CAP_DEFINE.match(line)\n            if match:\n                cap = match.group(1)\n                value = match.group(2)\n\n                if not cap in self._SKIP_CAPS:\n                    try:\n                        self.caps[cap] = int(value, 0)\n                    except ValueError:\n                        sys.exit('Could not parse capability define \"%s\":\"%s\"'\n                                 % (cap, value))\n\n\nclass AIDHeaderParser(object):\n    \"\"\"Parses an android_filesystem_config.h file.\n\n    Parses a C header file and extracts lines starting with #define AID_<name>\n    while capturing the OEM defined ranges and ignoring other ranges. It also\n    skips some hardcoded AIDs it doesn't need to generate a mapping for.\n    It provides some basic checks. The information extracted from this file can\n    later be used to quickly check other things (like oem ranges) as well as\n    generating a mapping of names to uids. It was primarily designed to parse\n    the private/android_filesystem_config.h, but any C header should work.\n    \"\"\"\n\n    _SKIP_AIDS = [\n        re.compile(r'%sUNUSED[0-9].*' % AID.PREFIX),\n        re.compile(r'%sAPP' % AID.PREFIX),\n        re.compile(r'%sUSER' % AID.PREFIX)\n    ]\n    _AID_DEFINE = re.compile(r'\\s*#define\\s+%s.*' % AID.PREFIX)\n    _RESERVED_RANGE = re.compile(\n        r'#define AID_(.+)_RESERVED_(?:(\\d+)_)?(START|END)\\s+(\\d+)')\n\n    # AID lines cannot end with _START or _END, ie AID_FOO is OK\n    # but AID_FOO_START is skiped. Note that AID_FOOSTART is NOT skipped.\n    _AID_SKIP_RANGE = ['_START', '_END']\n    _COLLISION_OK = ['AID_APP', 'AID_APP_START', 'AID_USER', 'AID_USER_OFFSET']\n\n    def __init__(self, aid_header):\n        \"\"\"\n        Args:\n            aid_header (str): file name for the header\n                file containing AID entries.\n        \"\"\"\n        self._aid_header = aid_header\n        self._aid_name_to_value = {}\n        self._aid_value_to_name = {}\n        self._ranges = {}\n\n        with open(aid_header) as open_file:\n            self._parse(open_file)\n\n        try:\n            self._process_and_check()\n        except ValueError as exception:\n            sys.exit('Error processing parsed data: \"%s\"' % (str(exception)))\n\n    def _parse(self, aid_file):\n        \"\"\"Parses an AID header file. Internal use only.\n\n        Args:\n            aid_file (file): The open AID header file to parse.\n        \"\"\"\n\n        ranges_by_name = {}\n        for lineno, line in enumerate(aid_file):\n\n            def error_message(msg):\n                \"\"\"Creates an error message with the current parsing state.\"\"\"\n                # pylint: disable=cell-var-from-loop\n                return 'Error \"{}\" in file: \"{}\" on line: {}'.format(\n                    msg, self._aid_header, str(lineno))\n\n            range_match = self._RESERVED_RANGE.match(line)\n            if range_match:\n                partition, name, start, value = range_match.groups()\n                partition = partition.lower()\n                if name is None:\n                    name = \"unnamed\"\n                start = start == \"START\"\n                value = int(value, 0)\n\n                if partition == 'oem':\n                    partition = 'vendor'\n\n                if partition not in ranges_by_name:\n                    ranges_by_name[partition] = {}\n                if name not in ranges_by_name[partition]:\n                    ranges_by_name[partition][name] = [None, None]\n                if ranges_by_name[partition][name][0 if start else 1] is not None:\n                    sys.exit(error_message(\"{} of range {} of partition {} was already defined\".format(\n                        \"Start\" if start else \"End\", name, partition)))\n                ranges_by_name[partition][name][0 if start else 1] = value\n\n            if AIDHeaderParser._AID_DEFINE.match(line):\n                chunks = line.split()\n                identifier = chunks[1]\n                value = chunks[2]\n\n                if any(\n                        x.match(identifier)\n                        for x in AIDHeaderParser._SKIP_AIDS):\n                    continue\n\n                try:\n                    if not any(\n                            identifier.endswith(x)\n                            for x in AIDHeaderParser._AID_SKIP_RANGE):\n                        self._handle_aid(identifier, value)\n                except ValueError as exception:\n                    sys.exit(\n                        error_message('{} for \"{}\"'.format(\n                            exception, identifier)))\n\n        for partition in ranges_by_name:\n            for name in ranges_by_name[partition]:\n                start = ranges_by_name[partition][name][0]\n                end = ranges_by_name[partition][name][1]\n                if start is None:\n                    sys.exit(\"Range '%s' for partition '%s' had undefined start\" % (name, partition))\n                if end is None:\n                    sys.exit(\"Range '%s' for partition '%s' had undefined end\" % (name, partition))\n                if start > end:\n                    sys.exit(\"Range '%s' for partition '%s' had start after end. Start: %d, end: %d\" % (name, partition, start, end))\n\n                if partition not in self._ranges:\n                    self._ranges[partition] = []\n                self._ranges[partition].append((start, end))\n\n    def _handle_aid(self, identifier, value):\n        \"\"\"Handle an AID C #define.\n\n        Handles an AID, quick checking, generating the friendly name and\n        adding it to the internal maps. Internal use only.\n\n        Args:\n            identifier (str): The name of the #define identifier. ie AID_FOO.\n            value (str): The value associated with the identifier.\n\n        Raises:\n            ValueError: With message set to indicate the error.\n        \"\"\"\n\n        aid = AID(identifier, value, self._aid_header, '/system/bin/sh')\n\n        # duplicate name\n        if aid.friendly in self._aid_name_to_value:\n            raise ValueError('Duplicate aid \"%s\"' % identifier)\n\n        if value in self._aid_value_to_name and aid.identifier not in AIDHeaderParser._COLLISION_OK:\n            raise ValueError(\n                'Duplicate aid value \"%s\" for %s' % (value, identifier))\n\n        self._aid_name_to_value[aid.friendly] = aid\n        self._aid_value_to_name[value] = aid.friendly\n\n    def _process_and_check(self):\n        \"\"\"Process, check and populate internal data structures.\n\n        After parsing and generating the internal data structures, this method\n        is responsible for quickly checking ALL of the acquired data.\n\n        Raises:\n            ValueError: With the message set to indicate the specific error.\n        \"\"\"\n\n        # Check for overlapping ranges\n        for ranges in self._ranges.values():\n            for i, range1 in enumerate(ranges):\n                for range2 in ranges[i + 1:]:\n                    if AIDHeaderParser._is_overlap(range1, range2):\n                        raise ValueError(\n                            \"Overlapping OEM Ranges found %s and %s\" %\n                            (str(range1), str(range2)))\n\n        # No core AIDs should be within any oem range.\n        for aid in self._aid_value_to_name:\n            for ranges in self._ranges.values():\n                if Utils.in_any_range(int(aid, 0), ranges):\n                    name = self._aid_value_to_name[aid]\n                    raise ValueError(\n                        'AID \"%s\" value: %u within reserved OEM Range: \"%s\"' %\n                        (name, aid, str(ranges)))\n\n    @property\n    def ranges(self):\n        \"\"\"Retrieves the OEM closed ranges as a list of tuples.\n\n        Returns:\n            A list of closed range tuples: [ (0, 42), (50, 105) ... ]\n        \"\"\"\n        return self._ranges\n\n    @property\n    def aids(self):\n        \"\"\"Retrieves the list of found AIDs.\n\n        Returns:\n            A list of AID() objects.\n        \"\"\"\n        return self._aid_name_to_value.values()\n\n    @staticmethod\n    def _is_overlap(range_a, range_b):\n        \"\"\"Calculates the overlap of two range tuples.\n\n        A range tuple is a closed range. A closed range includes its endpoints.\n        Note that python tuples use () notation which collides with the\n        mathematical notation for open ranges.\n\n        Args:\n            range_a: The first tuple closed range eg (0, 5).\n            range_b: The second tuple closed range eg (3, 7).\n\n        Returns:\n            True if they overlap, False otherwise.\n        \"\"\"\n\n        return max(range_a[0], range_b[0]) <= min(range_a[1], range_b[1])\n\n\nclass FSConfigFileParser(object):\n    \"\"\"Parses a config.fs ini format file.\n\n    This class is responsible for parsing the config.fs ini format files.\n    It collects and checks all the data in these files and makes it available\n    for consumption post processed.\n    \"\"\"\n\n    # These _AID vars work together to ensure that an AID section name\n    # cannot contain invalid characters for a C define or a passwd/group file.\n    # Since _AID_PREFIX is within the set of _AID_MATCH the error logic only\n    # checks end, if you change this, you may have to update the error\n    # detection code.\n    _AID_MATCH = re.compile('%s[A-Z0-9_]+' % AID.PREFIX)\n    _AID_ERR_MSG = 'Expecting upper case, a number or underscore'\n\n    # list of handler to required options, used to identify the\n    # parsing section\n    _SECTIONS = [('_handle_aid', ('value', )),\n                 ('_handle_path', ('mode', 'user', 'group', 'caps'))]\n\n    def __init__(self, config_files, ranges):\n        \"\"\"\n        Args:\n            config_files ([str]): The list of config.fs files to parse.\n                Note the filename is not important.\n            ranges ({str,[()]): Dictionary of partitions and a list of tuples that correspond to their ranges\n        \"\"\"\n\n        self._files = []\n        self._dirs = []\n        self._aids = []\n\n        self._seen_paths = {}\n        # (name to file, value to aid)\n        self._seen_aids = ({}, {})\n\n        self._ranges = ranges\n\n        self._config_files = config_files\n\n        for config_file in self._config_files:\n            self._parse(config_file)\n\n    def _parse(self, file_name):\n        \"\"\"Parses and verifies config.fs files. Internal use only.\n\n        Args:\n            file_name (str): The config.fs (PythonConfigParser file format)\n                file to parse.\n\n        Raises:\n            Anything raised by ConfigParser.read()\n        \"\"\"\n\n        # Separate config parsers for each file found. If you use\n        # read(filenames...) later files can override earlier files which is\n        # not what we want. Track state across files and enforce with\n        # _handle_dup(). Note, strict ConfigParser is set to true in\n        # Python >= 3.2, so in previous versions same file sections can\n        # override previous\n        # sections.\n\n        config = configparser.ConfigParser()\n        config.read(file_name)\n\n        for section in config.sections():\n\n            found = False\n\n            for test in FSConfigFileParser._SECTIONS:\n                handler = test[0]\n                options = test[1]\n\n                if all([config.has_option(section, item) for item in options]):\n                    handler = getattr(self, handler)\n                    handler(file_name, section, config)\n                    found = True\n                    break\n\n            if not found:\n                sys.exit('Invalid section \"%s\" in file: \"%s\"' % (section,\n                                                                 file_name))\n\n            # sort entries:\n            # * specified path before prefix match\n            # ** ie foo before f*\n            # * lexicographical less than before other\n            # ** ie boo before foo\n            # Given these paths:\n            # paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*']\n            # The sort order would be:\n            # paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*']\n            # Thus the fs_config tools will match on specified paths before\n            # attempting prefix, and match on the longest matching prefix.\n            self._files.sort(key=FSConfigFileParser._file_key)\n\n            # sort on value of (file_name, name, value, strvalue)\n            # This is only cosmetic so AIDS are arranged in ascending order\n            # within the generated file.\n            self._aids.sort(key=lambda item: item.normalized_value)\n\n    def _verify_valid_range(self, aid):\n        \"\"\"Verified an AID entry is in a valid range\"\"\"\n\n        ranges = None\n\n        partitions = list(self._ranges.keys())\n        partitions.sort(key=len, reverse=True)\n        for partition in partitions:\n            if aid.friendly.startswith(partition):\n                ranges = self._ranges[partition]\n                break\n\n        if ranges is None:\n            sys.exit('AID \"%s\" must be prefixed with a partition name' %\n                     aid.friendly)\n\n        if not Utils.in_any_range(int(aid.value, 0), ranges):\n            emsg = '\"value\" for aid \"%s\" not in valid range %s, got: %s'\n            emsg = emsg % (aid.friendly, str(ranges), aid.value)\n            sys.exit(emsg)\n\n    def _handle_aid(self, file_name, section_name, config):\n        \"\"\"Verifies an AID entry and adds it to the aid list.\n\n        Calls sys.exit() with a descriptive message of the failure.\n\n        Args:\n            file_name (str): The filename of the config file being parsed.\n            section_name (str): The section name currently being parsed.\n            config (ConfigParser): The ConfigParser section being parsed that\n                the option values will come from.\n        \"\"\"\n\n        def error_message(msg):\n            \"\"\"Creates an error message with current parsing state.\"\"\"\n            return '{} for: \"{}\" file: \"{}\"'.format(msg, section_name,\n                                                    file_name)\n\n        FSConfigFileParser._handle_dup_and_add('AID', file_name, section_name,\n                                               self._seen_aids[0])\n\n        match = FSConfigFileParser._AID_MATCH.match(section_name)\n        invalid = match.end() if match else len(AID.PREFIX)\n        if invalid != len(section_name):\n            tmp_errmsg = ('Invalid characters in AID section at \"%d\" for: \"%s\"'\n                          % (invalid, FSConfigFileParser._AID_ERR_MSG))\n            sys.exit(error_message(tmp_errmsg))\n\n        value = config.get(section_name, 'value')\n\n        if not value:\n            sys.exit(error_message('Found specified but unset \"value\"'))\n\n        try:\n            aid = AID(section_name, value, file_name, '/bin/sh')\n        except ValueError as exception:\n            sys.exit(error_message(exception))\n\n        self._verify_valid_range(aid)\n\n        # use the normalized int value in the dict and detect\n        # duplicate definitions of the same value\n        FSConfigFileParser._handle_dup_and_add(\n            'AID', file_name, aid.normalized_value, self._seen_aids[1])\n\n        # Append aid tuple of (AID_*, base10(value), _path(value))\n        # We keep the _path version of value so we can print that out in the\n        # generated header so investigating parties can identify parts.\n        # We store the base10 value for sorting, so everything is ascending\n        # later.\n        self._aids.append(aid)\n\n    def _handle_path(self, file_name, section_name, config):\n        \"\"\"Add a file capability entry to the internal list.\n\n        Handles a file capability entry, verifies it, and adds it to\n        to the internal dirs or files list based on path. If it ends\n        with a / its a dir. Internal use only.\n\n        Calls sys.exit() on any validation error with message set.\n\n        Args:\n            file_name (str): The current name of the file being parsed.\n            section_name (str): The name of the section to parse.\n            config (str): The config parser.\n        \"\"\"\n\n        FSConfigFileParser._handle_dup_and_add('path', file_name, section_name,\n                                               self._seen_paths)\n\n        mode = config.get(section_name, 'mode')\n        user = config.get(section_name, 'user')\n        group = config.get(section_name, 'group')\n        caps = config.get(section_name, 'caps')\n\n        errmsg = ('Found specified but unset option: \\\"%s\" in file: \\\"' +\n                  file_name + '\\\"')\n\n        if not mode:\n            sys.exit(errmsg % 'mode')\n\n        if not user:\n            sys.exit(errmsg % 'user')\n\n        if not group:\n            sys.exit(errmsg % 'group')\n\n        if not caps:\n            sys.exit(errmsg % 'caps')\n\n        caps = caps.split()\n\n        tmp = []\n        for cap in caps:\n            try:\n                # test if string is int, if it is, use as is.\n                int(cap, 0)\n                tmp.append(cap)\n            except ValueError:\n                tmp.append('CAP_' + cap.upper())\n\n        caps = tmp\n\n        if len(mode) == 3:\n            mode = '0' + mode\n\n        try:\n            int(mode, 8)\n        except ValueError:\n            sys.exit('Mode must be octal characters, got: \"%s\"' % mode)\n\n        if len(mode) != 4:\n            sys.exit('Mode must be 3 or 4 characters, got: \"%s\"' % mode)\n\n        caps_str = ','.join(caps)\n\n        entry = FSConfig(mode, user, group, caps_str, section_name, file_name)\n        if section_name[-1] == '/':\n            self._dirs.append(entry)\n        else:\n            self._files.append(entry)\n\n    @property\n    def files(self):\n        \"\"\"Get the list of FSConfig file entries.\n\n        Returns:\n             a list of FSConfig() objects for file paths.\n        \"\"\"\n        return self._files\n\n    @property\n    def dirs(self):\n        \"\"\"Get the list of FSConfig dir entries.\n\n        Returns:\n            a list of FSConfig() objects for directory paths.\n        \"\"\"\n        return self._dirs\n\n    @property\n    def aids(self):\n        \"\"\"Get the list of AID entries.\n\n        Returns:\n            a list of AID() objects.\n        \"\"\"\n        return self._aids\n\n    @staticmethod\n    def _file_key(fs_config):\n        \"\"\"Used as the key paramter to sort.\n\n        This is used as a the function to the key parameter of a sort.\n        it wraps the string supplied in a class that implements the\n        appropriate __lt__ operator for the sort on path strings. See\n        StringWrapper class for more details.\n\n        Args:\n            fs_config (FSConfig): A FSConfig entry.\n\n        Returns:\n            A StringWrapper object\n        \"\"\"\n\n        # Wrapper class for custom prefix matching strings\n        class StringWrapper(object):\n            \"\"\"Wrapper class used for sorting prefix strings.\n\n            The algorithm is as follows:\n              - specified path before prefix match\n                - ie foo before f*\n              - lexicographical less than before other\n                - ie boo before foo\n\n            Given these paths:\n            paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*']\n            The sort order would be:\n            paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*']\n            Thus the fs_config tools will match on specified paths before\n            attempting prefix, and match on the longest matching prefix.\n            \"\"\"\n\n            def __init__(self, path):\n                \"\"\"\n                Args:\n                    path (str): the path string to wrap.\n                \"\"\"\n                self.is_prefix = path[-1] == '*'\n                if self.is_prefix:\n                    self.path = path[:-1]\n                else:\n                    self.path = path\n\n            def __lt__(self, other):\n\n                # if were both suffixed the smallest string\n                # is 'bigger'\n                if self.is_prefix and other.is_prefix:\n                    result = len(self.path) > len(other.path)\n                # If I am an the suffix match, im bigger\n                elif self.is_prefix:\n                    result = False\n                # If other is the suffix match, he's bigger\n                elif other.is_prefix:\n                    result = True\n                # Alphabetical\n                else:\n                    result = self.path < other.path\n                return result\n\n        return StringWrapper(fs_config.path)\n\n    @staticmethod\n    def _handle_dup_and_add(name, file_name, section_name, seen):\n        \"\"\"Tracks and detects duplicates. Internal use only.\n\n        Calls sys.exit() on a duplicate.\n\n        Args:\n            name (str): The name to use in the error reporting. The pretty\n                name for the section.\n            file_name (str): The file currently being parsed.\n            section_name (str): The name of the section. This would be path\n                or identifier depending on what's being parsed.\n            seen (dict): The dictionary of seen things to check against.\n        \"\"\"\n        if section_name in seen:\n            dups = '\"' + seen[section_name] + '\" and '\n            dups += file_name\n            sys.exit('Duplicate %s \"%s\" found in files: %s' %\n                     (name, section_name, dups))\n\n        seen[section_name] = file_name\n\n\nclass BaseGenerator(object):\n    \"\"\"Interface for Generators.\n\n    Base class for generators, generators should implement\n    these method stubs.\n    \"\"\"\n\n    def add_opts(self, opt_group):\n        \"\"\"Used to add per-generator options to the command line.\n\n        Args:\n            opt_group (argument group object): The argument group to append to.\n                See the ArgParse docs for more details.\n        \"\"\"\n\n        raise NotImplementedError(\"Not Implemented\")\n\n    def __call__(self, args):\n        \"\"\"This is called to do whatever magic the generator does.\n\n        Args:\n            args (dict): The arguments from ArgParse as a dictionary.\n                ie if you specified an argument of foo in add_opts, access\n                it via args['foo']\n        \"\"\"\n\n        raise NotImplementedError(\"Not Implemented\")\n\n\n@generator('fsconfig')\nclass FSConfigGen(BaseGenerator):\n    \"\"\"Generates the android_filesystem_config.h file.\n\n    Output is  used in generating fs_config_files and fs_config_dirs.\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        BaseGenerator.__init__(args, kwargs)\n\n        self._oem_parser = None\n        self._base_parser = None\n        self._friendly_to_aid = None\n        self._id_to_aid = None\n        self._capability_parser = None\n\n        self._partition = None\n        self._all_partitions = None\n        self._out_file = None\n        self._generate_files = False\n        self._generate_dirs = False\n\n    def add_opts(self, opt_group):\n\n        opt_group.add_argument(\n            'fsconfig', nargs='+', help='The list of fsconfig files to parse')\n\n        opt_group.add_argument(\n            '--aid-header',\n            required=True,\n            help='An android_filesystem_config.h file'\n            ' to parse AIDs and OEM Ranges from')\n\n        opt_group.add_argument(\n            '--capability-header',\n            required=True,\n            help='A capability.h file to parse capability defines from')\n\n        opt_group.add_argument(\n            '--partition',\n            required=True,\n            help='Partition to generate contents for')\n\n        opt_group.add_argument(\n            '--all-partitions',\n            help='Comma separated list of all possible partitions, used to'\n            ' ignore these partitions when generating the output for the system partition'\n        )\n\n        opt_group.add_argument(\n            '--files', action='store_true', help='Output fs_config_files')\n\n        opt_group.add_argument(\n            '--dirs', action='store_true', help='Output fs_config_dirs')\n\n        opt_group.add_argument('--out_file', required=True, help='Output file')\n\n    def __call__(self, args):\n\n        self._capability_parser = CapabilityHeaderParser(\n            args['capability_header'])\n        self._base_parser = AIDHeaderParser(args['aid_header'])\n        self._oem_parser = FSConfigFileParser(args['fsconfig'],\n                                              self._base_parser.ranges)\n\n        self._partition = args['partition']\n        self._all_partitions = args['all_partitions']\n\n        self._out_file = args['out_file']\n\n        self._generate_files = args['files']\n        self._generate_dirs = args['dirs']\n\n        if self._generate_files and self._generate_dirs:\n            sys.exit('Only one of --files or --dirs can be provided')\n\n        if not self._generate_files and not self._generate_dirs:\n            sys.exit('One of --files or --dirs must be provided')\n\n        base_aids = self._base_parser.aids\n        oem_aids = self._oem_parser.aids\n\n        # Detect name collisions on AIDs. Since friendly works as the\n        # identifier for collision testing and we need friendly later on for\n        # name resolution, just calculate and use friendly.\n        # {aid.friendly: aid for aid in base_aids}\n        base_friendly = {aid.friendly: aid for aid in base_aids}\n        oem_friendly = {aid.friendly: aid for aid in oem_aids}\n\n        base_set = set(base_friendly.keys())\n        oem_set = set(oem_friendly.keys())\n\n        common = base_set & oem_set\n\n        if common:\n            emsg = 'Following AID Collisions detected for: \\n'\n            for friendly in common:\n                base = base_friendly[friendly]\n                oem = oem_friendly[friendly]\n                emsg += (\n                    'Identifier: \"%s\" Friendly Name: \"%s\" '\n                    'found in file \"%s\" and \"%s\"' %\n                    (base.identifier, base.friendly, base.found, oem.found))\n                sys.exit(emsg)\n\n        self._friendly_to_aid = oem_friendly\n        self._friendly_to_aid.update(base_friendly)\n\n        self._id_to_aid = {aid.identifier: aid for aid in base_aids}\n        self._id_to_aid.update({aid.identifier: aid for aid in oem_aids})\n\n        self._generate()\n\n    def _to_fs_entry(self, fs_config, out_file):\n        \"\"\"Converts an FSConfig entry to an fs entry.\n\n        Writes the fs_config contents to the output file.\n\n        Calls sys.exit() on error.\n\n        Args:\n            fs_config (FSConfig): The entry to convert to write to file.\n            file (File): The file to write to.\n        \"\"\"\n\n        # Get some short names\n        mode = fs_config.mode\n        user = fs_config.user\n        group = fs_config.group\n        caps = fs_config.caps\n        path = fs_config.path\n\n        emsg = 'Cannot convert \"%s\" to identifier!'\n\n        # convert mode from octal string to integer\n        mode = int(mode, 8)\n\n        # remap names to values\n        if AID.is_friendly(user):\n            if user not in self._friendly_to_aid:\n                sys.exit(emsg % user)\n            user = self._friendly_to_aid[user].value\n        else:\n            if user not in self._id_to_aid:\n                sys.exit(emsg % user)\n            user = self._id_to_aid[user].value\n\n        if AID.is_friendly(group):\n            if group not in self._friendly_to_aid:\n                sys.exit(emsg % group)\n            group = self._friendly_to_aid[group].value\n        else:\n            if group not in self._id_to_aid:\n                sys.exit(emsg % group)\n            group = self._id_to_aid[group].value\n\n        caps_dict = self._capability_parser.caps\n\n        caps_value = 0\n\n        try:\n            # test if caps is an int\n            caps_value = int(caps, 0)\n        except ValueError:\n            caps_split = caps.split(',')\n            for cap in caps_split:\n                if cap not in caps_dict:\n                    sys.exit('Unknown cap \"%s\" found!' % cap)\n                caps_value += 1 << caps_dict[cap]\n\n        path_length_with_null = len(path) + 1\n        path_length_aligned_64 = (path_length_with_null + 7) & ~7\n        # 16 bytes of header plus the path length with alignment\n        length = 16 + path_length_aligned_64\n\n        length_binary = bytearray(ctypes.c_uint16(length))\n        mode_binary = bytearray(ctypes.c_uint16(mode))\n        user_binary = bytearray(ctypes.c_uint16(int(user, 0)))\n        group_binary = bytearray(ctypes.c_uint16(int(group, 0)))\n        caps_binary = bytearray(ctypes.c_uint64(caps_value))\n        path_binary = ctypes.create_string_buffer(path.encode(),\n                                                  path_length_aligned_64).raw\n\n        out_file.write(length_binary)\n        out_file.write(mode_binary)\n        out_file.write(user_binary)\n        out_file.write(group_binary)\n        out_file.write(caps_binary)\n        out_file.write(path_binary)\n\n    def _emit_entry(self, fs_config):\n        \"\"\"Returns a boolean whether or not to emit the input fs_config\"\"\"\n\n        path = fs_config.path\n\n        if self._partition == 'system':\n            if not self._all_partitions:\n                return True\n            for skip_partition in self._all_partitions.split(','):\n                if path.startswith(skip_partition) or path.startswith(\n                        'system/' + skip_partition):\n                    return False\n            return True\n        else:\n            if path.startswith(\n                    self._partition) or path.startswith('system/' +\n                                                        self._partition):\n                return True\n            return False\n\n    def _generate(self):\n        \"\"\"Generates an OEM android_filesystem_config.h header file to stdout.\n\n        Args:\n            files ([FSConfig]): A list of FSConfig objects for file entries.\n            dirs ([FSConfig]): A list of FSConfig objects for directory\n                entries.\n            aids ([AIDS]): A list of AID objects for Android Id entries.\n        \"\"\"\n        dirs = self._oem_parser.dirs\n        files = self._oem_parser.files\n\n        if self._generate_files:\n            with open(self._out_file, 'wb') as open_file:\n                for fs_config in files:\n                    if self._emit_entry(fs_config):\n                        self._to_fs_entry(fs_config, open_file)\n\n        if self._generate_dirs:\n            with open(self._out_file, 'wb') as open_file:\n                for dir_entry in dirs:\n                    if self._emit_entry(dir_entry):\n                        self._to_fs_entry(dir_entry, open_file)\n\n\n@generator('aidarray')\nclass AIDArrayGen(BaseGenerator):\n    \"\"\"Generates the android_id static array.\"\"\"\n\n    _GENERATED = ('/*\\n'\n                  ' * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY!\\n'\n                  ' */')\n\n    _INCLUDE = '#include <private/android_filesystem_config.h>'\n\n    # Note that the android_id name field is of type 'const char[]' instead of\n    # 'const char*'.  While this seems less straightforward as we need to\n    # calculate the max length of all names, this allows the entire android_ids\n    # table to be placed in .rodata section instead of .data.rel.ro section,\n    # resulting in less memory pressure.\n    _STRUCT_FS_CONFIG = textwrap.dedent(\"\"\"\n                         struct android_id_info {\n                             const char name[%d];\n                             unsigned aid;\n                         };\"\"\")\n\n    _OPEN_ID_ARRAY = 'static const struct android_id_info android_ids[] = {'\n\n    _ID_ENTRY = '    { \"%s\", %s },'\n\n    _CLOSE_FILE_STRUCT = '};'\n\n    _COUNT = ('#define android_id_count \\\\\\n'\n              '    (sizeof(android_ids) / sizeof(android_ids[0]))')\n\n    def add_opts(self, opt_group):\n\n        opt_group.add_argument(\n            'hdrfile', help='The android_filesystem_config.h'\n            'file to parse')\n\n    def __call__(self, args):\n\n        hdr = AIDHeaderParser(args['hdrfile'])\n        max_name_length = max(len(aid.friendly) + 1 for aid in hdr.aids)\n\n        print(AIDArrayGen._GENERATED)\n        print()\n        print(AIDArrayGen._INCLUDE)\n        print()\n        print(AIDArrayGen._STRUCT_FS_CONFIG % max_name_length)\n        print()\n        print(AIDArrayGen._OPEN_ID_ARRAY)\n\n        for aid in hdr.aids:\n            print(AIDArrayGen._ID_ENTRY % (aid.friendly, aid.identifier))\n\n        print(AIDArrayGen._CLOSE_FILE_STRUCT)\n        print()\n        print(AIDArrayGen._COUNT)\n        print()\n\n\n@generator('oemaid')\nclass OEMAidGen(BaseGenerator):\n    \"\"\"Generates the OEM AID_<name> value header file.\"\"\"\n\n    _GENERATED = ('/*\\n'\n                  ' * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY!\\n'\n                  ' */')\n\n    _GENERIC_DEFINE = \"#define %s\\t%s\"\n\n    _FILE_COMMENT = '// Defined in file: \\\"%s\\\"'\n\n    # Intentional trailing newline for readability.\n    _FILE_IFNDEF_DEFINE = ('#ifndef GENERATED_OEM_AIDS_H_\\n'\n                           '#define GENERATED_OEM_AIDS_H_\\n')\n\n    _FILE_ENDIF = '#endif'\n\n    def __init__(self):\n\n        self._old_file = None\n\n    def add_opts(self, opt_group):\n\n        opt_group.add_argument(\n            'fsconfig', nargs='+', help='The list of fsconfig files to parse.')\n\n        opt_group.add_argument(\n            '--aid-header',\n            required=True,\n            help='An android_filesystem_config.h file'\n            'to parse AIDs and OEM Ranges from')\n\n    def __call__(self, args):\n\n        hdr_parser = AIDHeaderParser(args['aid_header'])\n\n        parser = FSConfigFileParser(args['fsconfig'], hdr_parser.ranges)\n\n        print(OEMAidGen._GENERATED)\n\n        print(OEMAidGen._FILE_IFNDEF_DEFINE)\n\n        for aid in parser.aids:\n            self._print_aid(aid)\n            print()\n\n        print(OEMAidGen._FILE_ENDIF)\n\n    def _print_aid(self, aid):\n        \"\"\"Prints a valid #define AID identifier to stdout.\n\n        Args:\n            aid to print\n        \"\"\"\n\n        # print the source file location of the AID\n        found_file = aid.found\n        if found_file != self._old_file:\n            print(OEMAidGen._FILE_COMMENT % found_file)\n            self._old_file = found_file\n\n        print(OEMAidGen._GENERIC_DEFINE % (aid.identifier, aid.value))\n\n\n@generator('passwd')\nclass PasswdGen(BaseGenerator):\n    \"\"\"Generates the /etc/passwd file per man (5) passwd.\"\"\"\n\n    def __init__(self):\n\n        self._old_file = None\n\n    def add_opts(self, opt_group):\n\n        opt_group.add_argument(\n            'fsconfig', nargs='+', help='The list of fsconfig files to parse.')\n\n        opt_group.add_argument(\n            '--aid-header',\n            required=True,\n            help='An android_filesystem_config.h file'\n            'to parse AIDs and OEM Ranges from')\n\n        opt_group.add_argument(\n            '--partition',\n            required=True,\n            help=\n            'Filter the input file and only output entries for the given partition.'\n        )\n\n    def __call__(self, args):\n\n        hdr_parser = AIDHeaderParser(args['aid_header'])\n\n        parser = FSConfigFileParser(args['fsconfig'], hdr_parser.ranges)\n\n        filter_partition = args['partition']\n\n        aids = parser.aids\n\n        # nothing to do if no aids defined\n        if not aids:\n            return\n\n        aids_by_partition = {}\n        partitions = list(hdr_parser.ranges.keys())\n        partitions.sort(key=len, reverse=True)\n\n        for aid in aids:\n            for partition in partitions:\n                if aid.friendly.startswith(partition):\n                    if partition in aids_by_partition:\n                        aids_by_partition[partition].append(aid)\n                    else:\n                        aids_by_partition[partition] = [aid]\n                    break\n\n        if filter_partition in aids_by_partition:\n            for aid in aids_by_partition[filter_partition]:\n                self._print_formatted_line(aid)\n\n    def _print_formatted_line(self, aid):\n        \"\"\"Prints the aid to stdout in the passwd format. Internal use only.\n\n        Colon delimited:\n            login name, friendly name\n            encrypted password (optional)\n            uid (int)\n            gid (int)\n            User name or comment field\n            home directory\n            interpreter (optional)\n\n        Args:\n            aid (AID): The aid to print.\n        \"\"\"\n        if self._old_file != aid.found:\n            self._old_file = aid.found\n\n        try:\n            logon, uid = Utils.get_login_and_uid_cleansed(aid)\n        except ValueError as exception:\n            sys.exit(exception)\n\n        print(\"%s::%s:%s::/:%s\" % (logon, uid, uid, aid.login_shell))\n\n\n@generator('group')\nclass GroupGen(PasswdGen):\n    \"\"\"Generates the /etc/group file per man (5) group.\"\"\"\n\n    # Overrides parent\n    def _print_formatted_line(self, aid):\n        \"\"\"Prints the aid to stdout in the group format. Internal use only.\n\n        Formatted (per man 5 group) like:\n            group_name:password:GID:user_list\n\n        Args:\n            aid (AID): The aid to print.\n        \"\"\"\n        if self._old_file != aid.found:\n            self._old_file = aid.found\n\n        try:\n            logon, uid = Utils.get_login_and_uid_cleansed(aid)\n        except ValueError as exception:\n            sys.exit(exception)\n\n        print(\"%s::%s:\" % (logon, uid))\n\n\n@generator('print')\nclass PrintGen(BaseGenerator):\n    \"\"\"Prints just the constants and values, separated by spaces, in an easy to\n    parse format for use by other scripts.\n\n    Each line is just the identifier and the value, separated by a space.\n    \"\"\"\n\n    def add_opts(self, opt_group):\n        opt_group.add_argument(\n            'aid-header', help='An android_filesystem_config.h file.')\n\n    def __call__(self, args):\n\n        hdr_parser = AIDHeaderParser(args['aid-header'])\n        aids = hdr_parser.aids\n\n        aids.sort(key=lambda item: int(item.normalized_value))\n\n        for aid in aids:\n            print('%s %s' % (aid.identifier, aid.normalized_value))\n\n\ndef main():\n    \"\"\"Main entry point for execution.\"\"\"\n\n    opt_parser = argparse.ArgumentParser(\n        description='A tool for parsing fsconfig config files and producing' +\n        'digestable outputs.')\n    subparser = opt_parser.add_subparsers(help='generators')\n\n    gens = generator.get()\n\n    # for each gen, instantiate and add them as an option\n    for name, gen in gens.items():\n\n        generator_option_parser = subparser.add_parser(name, help=gen.__doc__)\n        generator_option_parser.set_defaults(which=name)\n\n        opt_group = generator_option_parser.add_argument_group(name +\n                                                               ' options')\n        gen.add_opts(opt_group)\n\n    args = opt_parser.parse_args()\n\n    args_as_dict = vars(args)\n    which = args_as_dict['which']\n    del args_as_dict['which']\n\n    gens[which](args_as_dict)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/fs_config/pylintrc",
    "content": "[MESSAGES CONTROL]\ndisable=fixme,design,locally-disabled,too-many-lines\n\n[VARIABLES]\ndummy-variables-rgx=_|dummy\n"
  },
  {
    "path": "tools/fs_config/test_fs_config_generator.py",
    "content": "#!/usr/bin/env python3\n\"\"\"Unit test suite for the fs_config_genertor.py tool.\"\"\"\n\nimport tempfile\nimport textwrap\nimport unittest\n\nfrom fs_config_generator import AID\nfrom fs_config_generator import AIDHeaderParser\nfrom fs_config_generator import FSConfigFileParser\nfrom fs_config_generator import FSConfig\nfrom fs_config_generator import Utils\n\n\n# Disable protected access so we can test class internal\n# methods. Also, disable invalid-name as some of the\n# class method names are over length.\n# pylint: disable=protected-access,invalid-name\nclass Tests(unittest.TestCase):\n    \"\"\"Test class for unit tests\"\"\"\n\n    def test_is_overlap(self):\n        \"\"\"Test overlap detection helper\"\"\"\n\n        self.assertTrue(AIDHeaderParser._is_overlap((0, 1), (1, 2)))\n\n        self.assertTrue(AIDHeaderParser._is_overlap((0, 100), (90, 200)))\n\n        self.assertTrue(AIDHeaderParser._is_overlap((20, 50), (1, 101)))\n\n        self.assertFalse(AIDHeaderParser._is_overlap((0, 100), (101, 200)))\n\n        self.assertFalse(AIDHeaderParser._is_overlap((-10, 0), (10, 20)))\n\n    def test_in_any_range(self):\n        \"\"\"Test if value in range\"\"\"\n\n        self.assertFalse(Utils.in_any_range(50, [(100, 200), (1, 2), (1, 1)]))\n        self.assertFalse(Utils.in_any_range(250, [(100, 200), (1, 2), (1, 1)]))\n\n        self.assertTrue(Utils.in_any_range(100, [(100, 200), (1, 2), (1, 1)]))\n        self.assertTrue(Utils.in_any_range(200, [(100, 200), (1, 2), (1, 1)]))\n        self.assertTrue(Utils.in_any_range(150, [(100, 200)]))\n\n    def test_aid(self):\n        \"\"\"Test AID class constructor\"\"\"\n\n        aid = AID('AID_FOO_BAR', '0xFF', 'myfakefile', '/system/bin/sh')\n        self.assertEqual(aid.identifier, 'AID_FOO_BAR')\n        self.assertEqual(aid.value, '0xFF')\n        self.assertEqual(aid.found, 'myfakefile')\n        self.assertEqual(aid.normalized_value, '255')\n        self.assertEqual(aid.friendly, 'foo_bar')\n        self.assertEqual(aid.login_shell, '/system/bin/sh')\n\n        aid = AID('AID_MEDIA_EX', '1234', 'myfakefile', '/vendor/bin/sh')\n        self.assertEqual(aid.identifier, 'AID_MEDIA_EX')\n        self.assertEqual(aid.value, '1234')\n        self.assertEqual(aid.found, 'myfakefile')\n        self.assertEqual(aid.normalized_value, '1234')\n        self.assertEqual(aid.friendly, 'mediaex')\n        self.assertEqual(aid.login_shell, '/vendor/bin/sh')\n\n    def test_aid_header_parser_good(self):\n        \"\"\"Test AID Header Parser good input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                #define AID_FOO 1000\n                #define AID_BAR 1001\n                #define SOMETHING \"something\"\n                #define AID_OEM_RESERVED_START 2900\n                #define AID_OEM_RESERVED_END   2999\n                #define AID_OEM_RESERVED_1_START  7000\n                #define AID_OEM_RESERVED_1_END    8000\n            \"\"\"))\n            temp_file.flush()\n\n            parser = AIDHeaderParser(temp_file.name)\n            ranges = parser.ranges\n            aids = parser.aids\n\n            self.assertTrue((2900, 2999) in ranges[\"vendor\"])\n            self.assertFalse((5000, 6000) in ranges[\"vendor\"])\n\n            for aid in aids:\n                self.assertTrue(aid.normalized_value in ['1000', '1001'])\n                self.assertFalse(aid.normalized_value in ['1', '2', '3'])\n\n    def test_aid_header_parser_good_unordered(self):\n        \"\"\"Test AID Header Parser good unordered input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                #define AID_FOO 1000\n                #define AID_OEM_RESERVED_1_END    8000\n                #define AID_BAR 1001\n                #define SOMETHING \"something\"\n                #define AID_OEM_RESERVED_END   2999\n                #define AID_OEM_RESERVED_1_START  7000\n                #define AID_OEM_RESERVED_START 2900\n            \"\"\"))\n            temp_file.flush()\n\n            parser = AIDHeaderParser(temp_file.name)\n            ranges = parser.ranges\n            aids = parser.aids\n\n            self.assertTrue((2900, 2999) in ranges[\"vendor\"])\n            self.assertFalse((5000, 6000) in ranges[\"vendor\"])\n\n            for aid in aids:\n                self.assertTrue(aid.normalized_value in ['1000', '1001'])\n                self.assertFalse(aid.normalized_value in ['1', '2', '3'])\n\n    def test_aid_header_parser_bad_aid(self):\n        \"\"\"Test AID Header Parser bad aid input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                #define AID_FOO \"bad\"\n            \"\"\"))\n            temp_file.flush()\n\n            with self.assertRaises(SystemExit):\n                AIDHeaderParser(temp_file.name)\n\n    def test_aid_header_parser_bad_oem_range(self):\n        \"\"\"Test AID Header Parser bad oem range input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                #define AID_OEM_RESERVED_START 2900\n                #define AID_OEM_RESERVED_END   1800\n            \"\"\"))\n            temp_file.flush()\n\n            with self.assertRaises(SystemExit):\n                AIDHeaderParser(temp_file.name)\n\n    def test_aid_header_parser_bad_oem_range_no_end(self):\n        \"\"\"Test AID Header Parser bad oem range (no end) input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                #define AID_OEM_RESERVED_START 2900\n            \"\"\"))\n            temp_file.flush()\n\n            with self.assertRaises(SystemExit):\n                AIDHeaderParser(temp_file.name)\n\n    def test_aid_header_parser_bad_oem_range_no_start(self):\n        \"\"\"Test AID Header Parser bad oem range (no start) input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                #define AID_OEM_RESERVED_END 2900\n            \"\"\"))\n            temp_file.flush()\n\n            with self.assertRaises(SystemExit):\n                AIDHeaderParser(temp_file.name)\n\n    def test_aid_header_parser_bad_oem_range_duplicated(self):\n        \"\"\"Test AID Header Parser bad oem range (no start) input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                #define AID_OEM_RESERVED_START 2000\n                #define AID_OEM_RESERVED_END 2900\n                #define AID_OEM_RESERVED_START 3000\n                #define AID_OEM_RESERVED_END 3900\n            \"\"\"))\n            temp_file.flush()\n\n            with self.assertRaises(SystemExit):\n                AIDHeaderParser(temp_file.name)\n\n    def test_aid_header_parser_bad_oem_range_mismatch_start_end(self):\n        \"\"\"Test AID Header Parser bad oem range mismatched input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                #define AID_OEM_RESERVED_START 2900\n                #define AID_OEM_RESERVED_2_END 2900\n            \"\"\"))\n            temp_file.flush()\n\n            with self.assertRaises(SystemExit):\n                AIDHeaderParser(temp_file.name)\n\n    def test_aid_header_parser_bad_duplicate_ranges(self):\n        \"\"\"Test AID Header Parser exits cleanly on duplicate AIDs\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                #define AID_FOO 100\n                #define AID_BAR 100\n            \"\"\"))\n            temp_file.flush()\n\n            with self.assertRaises(SystemExit):\n                AIDHeaderParser(temp_file.name)\n\n    def test_aid_header_parser_no_bad_aids(self):\n        \"\"\"Test AID Header Parser that it doesn't contain:\n        Ranges, ie things the end with \"_START\" or \"_END\"\n        AID_APP\n        AID_USER\n        For more details see:\n          - https://android-review.googlesource.com/#/c/313024\n          - https://android-review.googlesource.com/#/c/313169\n        \"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                #define AID_APP              10000 /* TODO: switch users over to AID_APP_START */\n                #define AID_APP_START        10000 /* first app user */\n                #define AID_APP_END          19999 /* last app user */\n\n                #define AID_CACHE_GID_START  20000 /* start of gids for apps to mark cached data */\n                #define AID_CACHE_GID_END    29999 /* end of gids for apps to mark cached data */\n\n                #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */\n                #define AID_SHARED_GID_END   59999 /* end of gids for apps in each user to share */\n\n                #define AID_ISOLATED_START   99000 /* start of uids for fully isolated sandboxed processes */\n                #define AID_ISOLATED_END     99999 /* end of uids for fully isolated sandboxed processes */\n\n                #define AID_USER            100000 /* TODO: switch users over to AID_USER_OFFSET */\n                #define AID_USER_OFFSET     100000 /* offset for uid ranges for each user */\n            \"\"\"))\n            temp_file.flush()\n\n            parser = AIDHeaderParser(temp_file.name)\n            aids = parser.aids\n\n            bad_aids = ['_START', '_END', 'AID_APP', 'AID_USER']\n\n            for aid in aids:\n                self.assertFalse(\n                    any(bad in aid.identifier for bad in bad_aids),\n                    'Not expecting keywords \"%s\" in aids \"%s\"' %\n                    (str(bad_aids), str([tmp.identifier for tmp in aids])))\n\n    def test_fs_config_file_parser_good(self):\n        \"\"\"Test FSConfig Parser good input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                [/system/bin/file]\n                user: AID_FOO\n                group: AID_SYSTEM\n                mode: 0777\n                caps: BLOCK_SUSPEND\n\n                [/vendor/path/dir/]\n                user: AID_FOO\n                group: AID_SYSTEM\n                mode: 0777\n                caps: 0\n\n                [AID_OEM1]\n                # 5001 in base16\n                value: 0x1389\n            \"\"\"))\n            temp_file.flush()\n\n            parser = FSConfigFileParser([temp_file.name], {\"oem1\": [(5000, 5999)]})\n            files = parser.files\n            dirs = parser.dirs\n            aids = parser.aids\n\n            self.assertEqual(len(files), 1)\n            self.assertEqual(len(dirs), 1)\n            self.assertEqual(len(aids), 1)\n\n            aid = aids[0]\n            fcap = files[0]\n            dcap = dirs[0]\n\n            self.assertEqual(fcap,\n                             FSConfig('0777', 'AID_FOO', 'AID_SYSTEM',\n                                      'CAP_BLOCK_SUSPEND',\n                                      '/system/bin/file', temp_file.name))\n\n            self.assertEqual(dcap,\n                             FSConfig('0777', 'AID_FOO', 'AID_SYSTEM', '0',\n                                      '/vendor/path/dir/', temp_file.name))\n\n            self.assertEqual(aid, AID('AID_OEM1', '0x1389', temp_file.name, '/bin/sh'))\n\n    def test_fs_config_file_parser_bad(self):\n        \"\"\"Test FSConfig Parser bad input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                [/system/bin/file]\n                caps: BLOCK_SUSPEND\n            \"\"\"))\n            temp_file.flush()\n\n            with self.assertRaises(SystemExit):\n                FSConfigFileParser([temp_file.name], {})\n\n    def test_fs_config_file_parser_bad_aid_range(self):\n        \"\"\"Test FSConfig Parser bad aid range value input file\"\"\"\n\n        with tempfile.NamedTemporaryFile(mode='w') as temp_file:\n            temp_file.write(\n                textwrap.dedent(\"\"\"\n                [AID_OEM1]\n                value: 25\n            \"\"\"))\n            temp_file.flush()\n\n            with self.assertRaises(SystemExit):\n                FSConfigFileParser([temp_file.name], {\"oem1\": [(5000, 5999)]})\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tools/generate-enforce-rro-android-manifest.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"\nUtility to generate the Android manifest file of runtime resource overlay\npackage for source module.\n\"\"\"\nfrom xml.dom.minidom import parseString\nimport argparse\nimport os\nimport sys\n\nANDROID_MANIFEST_TEMPLATE=\"\"\"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"%s.auto_generated_rro_%s__\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\">\n    <overlay android:targetPackage=\"%s\" android:priority=\"%s\" android:isStatic=\"true\"/>\n</manifest>\n\"\"\"\n\n\ndef get_args():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        '-u', '--use-package-name', action='store_true',\n        help='Indicate that --package-info is a package name.')\n    parser.add_argument(\n        '-p', '--package-info', required=True,\n        help='Manifest package name or manifest file path of source module.')\n    parser.add_argument(\n        '--partition', required=True,\n        help='The partition this RRO package is installed on.')\n    parser.add_argument(\n        '--priority', required=True,\n        help='The priority for the <overlay>.')\n    parser.add_argument(\n        '-o', '--output', required=True,\n        help='Output manifest file path.')\n    return parser.parse_args()\n\n\ndef main(argv):\n  args = get_args()\n\n  partition = args.partition\n  priority = args.priority\n  if args.use_package_name:\n    package_name = args.package_info\n  else:\n    with open(args.package_info) as f:\n      data = f.read()\n      f.close()\n      dom = parseString(data)\n      package_name = dom.documentElement.getAttribute('package')\n\n  with open(args.output, 'w+') as f:\n    f.write(ANDROID_MANIFEST_TEMPLATE % (package_name, partition, package_name, priority))\n    f.close()\n\n\nif __name__ == \"__main__\":\n  main(sys.argv)\n"
  },
  {
    "path": "tools/generate-notice-files.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2012 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"\nUsage: generate-notice-files --text-output [plain text output file] \\\n               --html-output [html output file] \\\n               --xml-output [xml output file] \\\n               -t [file title] -s [directory of notices]\n\nGenerate the Android notice files, including both text and html files.\n\n-h to display this usage message and exit.\n\"\"\"\nfrom collections import defaultdict\nimport argparse\nimport hashlib\nimport itertools\nimport os\nimport os.path\nimport re\nimport struct\nimport sys\n\nMD5_BLOCKSIZE = 1024 * 1024\nHTML_ESCAPE_TABLE = {\n    b\"&\": b\"&amp;\",\n    b'\"': b\"&quot;\",\n    b\"'\": b\"&apos;\",\n    b\">\": b\"&gt;\",\n    b\"<\": b\"&lt;\",\n    }\n\ndef md5sum(filename):\n    \"\"\"Calculate an MD5 of the file given by FILENAME,\n    and return hex digest as a string.\n    Output should be compatible with md5sum command\"\"\"\n\n    f = open(filename, \"rb\")\n    sum = hashlib.md5()\n    while 1:\n        block = f.read(MD5_BLOCKSIZE)\n        if not block:\n            break\n        sum.update(block)\n    f.close()\n    return sum.hexdigest()\n\n\ndef html_escape(text):\n    \"\"\"Produce entities within text.\"\"\"\n    # Using for i in text doesn't work since i will be an int, not a byte.\n    # There are multiple ways to solve this, but the most performant way\n    # to iterate over a byte array is to use unpack. Using the\n    # for i in range(len(text)) and using that to get a byte using array\n    # slices is twice as slow as this method.\n    return b\"\".join(HTML_ESCAPE_TABLE.get(i,i) for i in struct.unpack(str(len(text)) + 'c', text))\n\nHTML_OUTPUT_CSS=b\"\"\"\n<style type=\"text/css\">\nbody { padding: 0; font-family: sans-serif; }\n.same-license { background-color: #eeeeee; border-top: 20px solid white; padding: 10px; }\n.label { font-weight: bold; }\n.file-list { margin-left: 1em; color: blue; }\n</style>\n\n\"\"\"\n\ndef combine_notice_files_html(file_hash, input_dirs, output_filename):\n    \"\"\"Combine notice files in FILE_HASH and output a HTML version to OUTPUT_FILENAME.\"\"\"\n\n    SRC_DIR_STRIP_RE = re.compile(\"(?:\" + \"|\".join(input_dirs) + \")(/.*).txt\")\n\n    # Set up a filename to row id table (anchors inside tables don't work in\n    # most browsers, but href's to table row ids do)\n    id_table = {}\n    id_count = 0\n    for value in file_hash:\n        for filename in value:\n             id_table[filename] = id_count\n        id_count += 1\n\n    # Open the output file, and output the header pieces\n    output_file = open(output_filename, \"wb\")\n\n    output_file.write(b\"<html><head>\\n\")\n    output_file.write(HTML_OUTPUT_CSS)\n    output_file.write(b'</head><body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\\n')\n\n    # Output our table of contents\n    output_file.write(b'<div class=\"toc\">\\n')\n    output_file.write(b\"<ul>\\n\")\n\n    # Flatten the list of lists into a single list of filenames\n    sorted_filenames = sorted(itertools.chain.from_iterable(file_hash))\n\n    # Print out a nice table of contents\n    for filename in sorted_filenames:\n        stripped_filename = SRC_DIR_STRIP_RE.sub(r\"\\1\", filename)\n        output_file.write(('<li><a href=\"#id%d\">%s</a></li>\\n' % (id_table.get(filename), stripped_filename)).encode())\n\n    output_file.write(b\"</ul>\\n\")\n    output_file.write(b\"</div><!-- table of contents -->\\n\")\n    # Output the individual notice file lists\n    output_file.write(b'<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\\n')\n    for value in file_hash:\n        output_file.write(b'<tr id=\"id%d\"><td class=\"same-license\">\\n' % id_table.get(value[0]))\n        output_file.write(b'<div class=\"label\">Notices for file(s):</div>\\n')\n        output_file.write(b'<div class=\"file-list\">\\n')\n        for filename in value:\n            output_file.write((\"%s <br/>\\n\" % SRC_DIR_STRIP_RE.sub(r\"\\1\", filename)).encode())\n        output_file.write(b\"</div><!-- file-list -->\\n\")\n        output_file.write(b\"\\n\")\n        output_file.write(b'<pre class=\"license-text\">\\n')\n        with open(value[0], \"rb\") as notice_file:\n            output_file.write(html_escape(notice_file.read()))\n        output_file.write(b\"\\n</pre><!-- license-text -->\\n\")\n        output_file.write(b\"</td></tr><!-- same-license -->\\n\\n\\n\\n\")\n\n    # Finish off the file output\n    output_file.write(b\"</table>\\n\")\n    output_file.write(b\"</body></html>\\n\")\n    output_file.close()\n\ndef combine_notice_files_text(file_hash, input_dirs, output_filename, file_title):\n    \"\"\"Combine notice files in FILE_HASH and output a text version to OUTPUT_FILENAME.\"\"\"\n\n    SRC_DIR_STRIP_RE = re.compile(\"(?:\" + \"|\".join(input_dirs) + \")(/.*).txt\")\n    output_file = open(output_filename, \"wb\")\n    output_file.write(file_title.encode())\n    output_file.write(b\"\\n\")\n    for value in file_hash:\n        output_file.write(b\"============================================================\\n\")\n        output_file.write(b\"Notices for file(s):\\n\")\n        for filename in value:\n            output_file.write(SRC_DIR_STRIP_RE.sub(r\"\\1\", filename).encode())\n            output_file.write(b\"\\n\")\n        output_file.write(b\"------------------------------------------------------------\\n\")\n        with open(value[0], \"rb\") as notice_file:\n            output_file.write(notice_file.read())\n            output_file.write(b\"\\n\")\n    output_file.close()\n\ndef combine_notice_files_xml(files_with_same_hash, input_dirs, output_filename):\n    \"\"\"Combine notice files in FILE_HASH and output a XML version to OUTPUT_FILENAME.\"\"\"\n\n    SRC_DIR_STRIP_RE = re.compile(\"(?:\" + \"|\".join(input_dirs) + \")(/.*).txt\")\n\n    # Set up a filename to row id table (anchors inside tables don't work in\n    # most browsers, but href's to table row ids do)\n    id_table = {}\n    for file_key, files in files_with_same_hash.items():\n        for filename in files:\n             id_table[filename] = file_key\n\n    # Open the output file, and output the header pieces\n    output_file = open(output_filename, \"wb\")\n\n    output_file.write(b'<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n')\n    output_file.write(b\"<licenses>\\n\")\n\n    # Flatten the list of lists into a single list of filenames\n    sorted_filenames = sorted(id_table.keys())\n\n    # Print out a nice table of contents\n    for filename in sorted_filenames:\n        stripped_filename = SRC_DIR_STRIP_RE.sub(r\"\\1\", filename)\n        output_file.write(('<file-name contentId=\"%s\">%s</file-name>\\n' % (id_table.get(filename), stripped_filename)).encode())\n    output_file.write(b\"\\n\\n\")\n\n    processed_file_keys = []\n    # Output the individual notice file lists\n    for filename in sorted_filenames:\n        file_key = id_table.get(filename)\n        if file_key in processed_file_keys:\n            continue\n        processed_file_keys.append(file_key)\n\n        output_file.write(('<file-content contentId=\"%s\"><![CDATA[' % file_key).encode())\n        with open(filename, \"rb\") as notice_file:\n            output_file.write(html_escape(notice_file.read()))\n        output_file.write(b\"]]></file-content>\\n\\n\")\n\n    # Finish off the file output\n    output_file.write(b\"</licenses>\\n\")\n    output_file.close()\n\ndef get_args():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        '--text-output', required=True,\n        help='The text output file path.')\n    parser.add_argument(\n        '--html-output',\n        help='The html output file path.')\n    parser.add_argument(\n        '--xml-output',\n        help='The xml output file path.')\n    parser.add_argument(\n        '-t', '--title', required=True,\n        help='The file title.')\n    parser.add_argument(\n        '-s', '--source-dir', required=True, action='append',\n        help='The directory containing notices.')\n    parser.add_argument(\n        '-i', '--included-subdirs', action='append',\n        help='The sub directories which should be included.')\n    parser.add_argument(\n        '-e', '--excluded-subdirs', action='append',\n        help='The sub directories which should be excluded.')\n    return parser.parse_args()\n\ndef main(argv):\n    args = get_args()\n\n    txt_output_file = args.text_output\n    html_output_file = args.html_output\n    xml_output_file = args.xml_output\n    file_title = args.title\n    included_subdirs = []\n    excluded_subdirs = []\n    if args.included_subdirs is not None:\n        included_subdirs = args.included_subdirs\n    if args.excluded_subdirs is not None:\n        excluded_subdirs = args.excluded_subdirs\n\n    input_dirs = [os.path.normpath(source_dir) for source_dir in args.source_dir]\n    # Find all the notice files and md5 them\n    files_with_same_hash = defaultdict(list)\n    for input_dir in input_dirs:\n        for root, dir, files in os.walk(input_dir):\n            for file in files:\n                matched = True\n                if len(included_subdirs) > 0:\n                    matched = False\n                    for subdir in included_subdirs:\n                        if (root == (input_dir + '/' + subdir) or\n                            root.startswith(input_dir + '/' + subdir + '/')):\n                            matched = True\n                            break\n                elif len(excluded_subdirs) > 0:\n                    for subdir in excluded_subdirs:\n                        if (root == (input_dir + '/' + subdir) or\n                            root.startswith(input_dir + '/' + subdir + '/')):\n                            matched = False\n                            break\n                if matched and file.endswith(\".txt\"):\n                    filename = os.path.join(root, file)\n                    file_md5sum = md5sum(filename)\n                    files_with_same_hash[file_md5sum].append(filename)\n\n    filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(list(files_with_same_hash))]\n    combine_notice_files_text(filesets, input_dirs, txt_output_file, file_title)\n\n    if html_output_file is not None:\n        combine_notice_files_html(filesets, input_dirs, html_output_file)\n\n    if xml_output_file is not None:\n        combine_notice_files_xml(files_with_same_hash, input_dirs, xml_output_file)\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n"
  },
  {
    "path": "tools/generate-self-extracting-archive.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nGenerates a self extracting archive with a license click through.\n\nUsage:\n  generate-self-extracting-archive.py $OUTPUT_FILE $INPUT_ARCHIVE $COMMENT $LICENSE_FILE\n\n  The comment will be included at the beginning of the output archive file.\n\nOutput:\n  The output of the script is a single executable file that when run will\n  display the provided license and if the user accepts extract the wrapped\n  archive.\n\n  The layout of the output file is roughly:\n   * Executable shell script that extracts the archive\n   * Actual archive contents\n   * Zip file containing the license\n\"\"\"\n\nimport tempfile\nimport sys\nimport os\nimport zipfile\n\n_HEADER_TEMPLATE = \"\"\"#!/bin/bash\n#\n{comment_line}\n#\n# Usage is subject to the enclosed license agreement\n\necho\necho The license for this software will now be displayed.\necho You must agree to this license before using this software.\necho\necho -n Press Enter to view the license\nread dummy\necho\nmore << EndOfLicense\n{license}\nEndOfLicense\n\nif test $? != 0\nthen\n  echo \"ERROR: Couldn't display license file\" 1>&2\n  exit 1\nfi\necho\necho -n 'Type \"I ACCEPT\" if you agree to the terms of the license: '\nread typed\nif test \"$typed\" != \"I ACCEPT\"\nthen\n  echo\n  echo \"You didn't accept the license. Extraction aborted.\"\n  exit 2\nfi\necho\n{extract_command}\nif test $? != 0\nthen\n  echo\n  echo \"ERROR: Couldn't extract files.\" 1>&2\n  exit 3\nelse\n  echo\n  echo \"Files extracted successfully.\"\nfi\nexit 0\n\"\"\"\n\n_PIPE_CHUNK_SIZE = 1048576\ndef _pipe_bytes(src, dst):\n  while True:\n    b = src.read(_PIPE_CHUNK_SIZE)\n    if not b:\n      break\n    dst.write(b)\n\n_MAX_OFFSET_WIDTH = 20\ndef _generate_extract_command(start, size, extract_name):\n  \"\"\"Generate the extract command.\n\n  The length of this string must be constant no matter what the start and end\n  offsets are so that its length can be computed before the actual command is\n  generated.\n\n  Args:\n    start: offset in bytes of the start of the wrapped file\n    size: size in bytes of the wrapped file\n    extract_name: of the file to create when extracted\n\n  \"\"\"\n  # start gets an extra character for the '+'\n  # for tail +1 is the start of the file, not +0\n  start_str = ('+%d' % (start + 1)).rjust(_MAX_OFFSET_WIDTH + 1)\n  if len(start_str) != _MAX_OFFSET_WIDTH + 1:\n    raise Exception('Start offset too large (%d)' % start)\n\n  size_str = ('%d' % size).rjust(_MAX_OFFSET_WIDTH)\n  if len(size_str) != _MAX_OFFSET_WIDTH:\n    raise Exception('Size too large (%d)' % size)\n\n  return \"tail -c %s $0 | head -c %s > %s\\n\" % (start_str, size_str, extract_name)\n\n\ndef main(argv):\n  if len(argv) != 5:\n    print('generate-self-extracting-archive.py expects exactly 4 arguments')\n    sys.exit(1)\n\n  output_filename = argv[1]\n  input_archive_filename = argv[2]\n  comment = argv[3]\n  license_filename = argv[4]\n\n  input_archive_size = os.stat(input_archive_filename).st_size\n\n  with open(license_filename, 'r') as license_file:\n    license = license_file.read()\n\n  if not license:\n    print('License file was empty')\n    sys.exit(1)\n\n  if 'SOFTWARE LICENSE AGREEMENT' not in license:\n    print('License does not look like a license')\n    sys.exit(1)\n\n  comment_line = '# %s\\n' % comment\n  extract_name = os.path.basename(input_archive_filename)\n\n  # Compute the size of the header before writing the file out. This is required\n  # so that the extract command, which uses the contents offset, can be created\n  # and included inside the header.\n  header_for_size = _HEADER_TEMPLATE.format(\n      comment_line=comment_line,\n      license=license,\n      extract_command=_generate_extract_command(0, 0, extract_name),\n  )\n  header_size = len(header_for_size.encode('utf-8'))\n\n  # write the final output\n  with open(output_filename, 'wb') as output:\n    output.write(_HEADER_TEMPLATE.format(\n        comment_line=comment_line,\n        license=license,\n        extract_command=_generate_extract_command(header_size, input_archive_size, extract_name),\n    ).encode('utf-8'))\n\n    with open(input_archive_filename, 'rb') as input_file:\n      _pipe_bytes(input_file, output)\n\n    with tempfile.TemporaryFile() as trailing_zip:\n      with zipfile.ZipFile(trailing_zip, 'w') as myzip:\n        myzip.writestr('license.txt', license, compress_type=zipfile.ZIP_STORED)\n\n      # append the trailing zip to the end of the file\n      trailing_zip.seek(0)\n      _pipe_bytes(trailing_zip, output)\n\n  umask = os.umask(0)\n  os.umask(umask)\n  os.chmod(output_filename, 0o777 & ~umask)\n\nif __name__ == \"__main__\":\n  main(sys.argv)\n"
  },
  {
    "path": "tools/generate_gts_shared_report.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"\nChecks and generates a report for gts modules that should be open-sourced.\n\nUsage:\n  generate_gts_open_source_report.py\n    --gts-test-metalic [android-gts meta_lic]\n    --checkshare [COMPLIANCE_CHECKSHARE]\n    --gts-test-dir [directory of android-gts]\n    --output [output file]\n\nOutput example:\n  GTS-Modules: PASS/FAIL\n    GtsIncrementalInstallTestCases_BackgroundProcess\n    GtsUnsignedNetworkStackTestCases\n\"\"\"\nimport sys\nimport argparse\nimport subprocess\nimport re\n\ndef _get_args():\n    \"\"\"Parses input arguments.\"\"\"\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        '--gts-test-metalic', required=True,\n        help='license meta_lic file path of android-gts.zip')\n    parser.add_argument(\n        '--checkshare', required=True,\n        help='path of the COMPLIANCE_CHECKSHARE tool')\n    parser.add_argument(\n        '--gts-test-dir', required=True,\n        help='directory of android-gts')\n    parser.add_argument(\n        '-o', '--output', required=True,\n        help='file path of the output report')\n    return parser.parse_args()\n\ndef _check_gts_test(checkshare: str, gts_test_metalic: str,\n                    gts_test_dir: str) -> tuple[str, set[str]]:\n    \"\"\"Checks android-gts license.\n\n    Args:\n      checkshare: path of the COMPLIANCE_CHECKSHARE tool\n      gts_test_metalic: license meta_lic file path of android-gts.zip\n      gts_test_dir: directory of android-gts\n\n    Returns:\n      Check result (PASS when android-gts doesn't need to be shared,\n      FAIL when some gts modules need to be shared) and gts modules\n      that need to be shared.\n    \"\"\"\n    cmd = f'{checkshare} {gts_test_metalic}'\n    proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,\n                            stderr=subprocess.PIPE)\n    _, str_stderr = map(lambda b: b.decode(), proc.communicate())\n    if proc.returncode == 0:\n        return 'PASS', []\n    open_source_modules = set()\n    for error_line in str_stderr.split('\\n'):\n        # Skip the empty liness\n        if not error_line:\n            continue\n        module_meta_lic = error_line.strip().split()[0]\n        groups = re.fullmatch(\n            re.compile(f'.*/{gts_test_dir}/(.*)'), module_meta_lic)\n        if groups:\n            open_source_modules.add(\n                groups[1].removesuffix('.meta_lic'))\n    return 'FAIL', open_source_modules\n\n\ndef main(argv):\n    args = _get_args()\n\n    gts_test_metalic = args.gts_test_metalic\n    output_file = args.output\n    checkshare = args.checkshare\n    gts_test_dir = args.gts_test_dir\n\n    with open(output_file, 'w') as file:\n        result, open_source_modules = _check_gts_test(\n            checkshare, gts_test_metalic, gts_test_dir)\n        file.write(f'GTS-Modules: {result}\\n')\n        for open_source_module in open_source_modules:\n            file.write(f'\\t{open_source_module}\\n')\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n"
  },
  {
    "path": "tools/ide_query/OWNERS",
    "content": "ialiyev@google.com\nivankirichenko@google.com\nkadircet@google.com\nmichaelmerg@google.com\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer/Android.bp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"ide_query_cc_analyzer_defaults\",\n    compile_multilib: \"64\",\n    defaults: [\n        \"llvm-build-host-tools-defaults\",\n    ],\n    cflags: [\n        // LLVM Sources do have unused parameters :(\n        \"-Wno-unused-parameter\",\n    ],\n    target: {\n        host: {\n            cppflags: [\n                \"-fno-rtti\",\n            ],\n        },\n    },\n}\n\ncc_library_host_static {\n    name: \"builtin_headers\",\n    srcs: [\"builtin_headers.cc\"],\n    generated_headers: [\"clang_builtin_headers_resources\"],\n    defaults: [\"ide_query_cc_analyzer_defaults\"],\n}\n\ncc_library_host_static {\n    name: \"include_scanner\",\n    srcs: [\"include_scanner.cc\"],\n    shared_libs: [\"libclang-cpp_host\"],\n    static_libs: [\"builtin_headers\"],\n    defaults: [\"ide_query_cc_analyzer_defaults\"],\n}\n\ncc_library_host_static {\n    name: \"analyzer\",\n    srcs: [\"analyzer.cc\"],\n    shared_libs: [\"libclang-cpp_host\"],\n    static_libs: [\n        \"include_scanner\",\n        \"cc_analyzer_proto\",\n    ],\n    defaults: [\"ide_query_cc_analyzer_defaults\"],\n}\n\ncc_binary_host {\n    name: \"ide_query_cc_analyzer\",\n    defaults: [\"ide_query_cc_analyzer_defaults\"],\n    srcs: [\"main.cc\"],\n    shared_libs: [\n        \"libclang-cpp_host\",\n        \"libprotobuf-cpp-full\",\n    ],\n    static_libs: [\n        \"cc_analyzer_proto\",\n        \"builtin_headers\",\n        \"include_scanner\",\n        \"analyzer\",\n    ],\n}\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer/README.md",
    "content": "See instructions in\n[Android Clang/LLVM-based Tools Readme Doc](https://android.googlesource.com/platform/prebuilts/clang-tools/+/main/README.md)\nfor cutting a new release.\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer/analyzer.cc",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"analyzer.h\"\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"cc_analyzer.pb.h\"\n#include \"clang/Tooling/CompilationDatabase.h\"\n#include \"clang/Tooling/JSONCompilationDatabase.h\"\n#include \"include_scanner.h\"\n#include \"llvm/ADT/SmallString.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/Twine.h\"\n#include \"llvm/Support/Error.h\"\n#include \"llvm/Support/Path.h\"\n#include \"llvm/Support/VirtualFileSystem.h\"\n\nnamespace tools::ide_query::cc_analyzer {\nnamespace {\nllvm::Expected<std::unique_ptr<clang::tooling::CompilationDatabase>> LoadCompDB(\n    llvm::StringRef comp_db_path) {\n  std::string err;\n  std::unique_ptr<clang::tooling::CompilationDatabase> db =\n      clang::tooling::JSONCompilationDatabase::loadFromFile(\n          comp_db_path, err, clang::tooling::JSONCommandLineSyntax::AutoDetect);\n  if (!db) {\n    return llvm::createStringError(llvm::inconvertibleErrorCode(),\n                                   \"Failed to load CDB: \" + err);\n  }\n  // Provide some heuristic support for missing files.\n  return inferMissingCompileCommands(std::move(db));\n}\n}  // namespace\n\n::cc_analyzer::DepsResponse GetDeps(::cc_analyzer::RepoState state) {\n  ::cc_analyzer::DepsResponse results;\n  auto db = LoadCompDB(state.comp_db_path());\n  if (!db) {\n    results.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);\n    results.mutable_status()->set_message(llvm::toString(db.takeError()));\n    return results;\n  }\n  for (llvm::StringRef active_file : state.active_file_path()) {\n    auto& result = *results.add_deps();\n\n    llvm::SmallString<256> abs_file(state.repo_dir());\n    llvm::sys::path::append(abs_file, active_file);\n    auto cmds = db->get()->getCompileCommands(active_file);\n    if (cmds.empty()) {\n      result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);\n      result.mutable_status()->set_message(\n          llvm::Twine(\"Can't find compile flags for file: \", abs_file).str());\n      continue;\n    }\n    result.set_source_file(active_file.str());\n    llvm::StringRef file = cmds[0].Filename;\n    if (llvm::StringRef actual_file(cmds[0].Heuristic);\n        actual_file.consume_front(\"inferred from \")) {\n      file = actual_file;\n    }\n    // TODO: Query ninja graph to figure out a minimal set of targets to build.\n    result.add_build_target(file.str() + \"^\");\n  }\n  return results;\n}\n\n::cc_analyzer::IdeAnalysis GetBuildInputs(::cc_analyzer::RepoState state) {\n  auto db = LoadCompDB(state.comp_db_path());\n  ::cc_analyzer::IdeAnalysis results;\n  if (!db) {\n    results.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);\n    results.mutable_status()->set_message(llvm::toString(db.takeError()));\n    return results;\n  }\n  std::string repo_dir = state.repo_dir();\n  if (!repo_dir.empty() && repo_dir.back() == '/') repo_dir.pop_back();\n\n  llvm::SmallString<256> genfile_root_abs(repo_dir);\n  llvm::sys::path::append(genfile_root_abs, state.out_dir());\n  if (genfile_root_abs.empty() || genfile_root_abs.back() != '/') {\n    genfile_root_abs.push_back('/');\n  }\n\n  for (llvm::StringRef active_file : state.active_file_path()) {\n    auto& result = *results.add_sources();\n    result.set_path(active_file.str());\n\n    llvm::SmallString<256> abs_file(repo_dir);\n    llvm::sys::path::append(abs_file, active_file);\n    auto cmds = db->get()->getCompileCommands(abs_file);\n    if (cmds.empty()) {\n      result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);\n      result.mutable_status()->set_message(\n          llvm::Twine(\"Can't find compile flags for file: \", abs_file).str());\n      continue;\n    }\n    const auto& cmd = cmds.front();\n    llvm::StringRef working_dir = cmd.Directory;\n    if (!working_dir.consume_front(repo_dir)) {\n      result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);\n      result.mutable_status()->set_message(\"Command working dir \" +\n                                           working_dir.str() +\n                                           \" outside repository \" + repo_dir);\n      continue;\n    }\n    working_dir = working_dir.ltrim('/');\n    result.set_working_dir(working_dir.str());\n    for (auto& arg : cmd.CommandLine) result.add_compiler_arguments(arg);\n\n    auto includes =\n        ScanIncludes(cmds.front(), llvm::vfs::createPhysicalFileSystem());\n    if (!includes) {\n      result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);\n      result.mutable_status()->set_message(\n          llvm::toString(includes.takeError()));\n      continue;\n    }\n\n    for (auto& [req_input, contents] : *includes) {\n      llvm::StringRef req_input_ref(req_input);\n      // We're only interested in generated files.\n      if (!req_input_ref.consume_front(genfile_root_abs)) continue;\n      auto& genfile = *result.add_generated();\n      genfile.set_path(req_input_ref.str());\n      genfile.set_contents(std::move(contents));\n    }\n  }\n  return results;\n}\n}  // namespace tools::ide_query::cc_analyzer\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer/analyzer.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _TOOLS_IDE_QUERY_CC_ANALYZER_ANALYZER_H_\n#define _TOOLS_IDE_QUERY_CC_ANALYZER_ANALYZER_H_\n\n#include \"cc_analyzer.pb.h\"\n\nnamespace tools::ide_query::cc_analyzer {\n\n// Scans the build graph and returns target names from the build graph to\n// generate all the dependencies for the active files.\n::cc_analyzer::DepsResponse GetDeps(::cc_analyzer::RepoState state);\n\n// Scans the sources and returns all the source files required for analyzing the\n// active files.\n::cc_analyzer::IdeAnalysis GetBuildInputs(::cc_analyzer::RepoState state);\n\n}  // namespace tools::ide_query::cc_analyzer\n\n#endif\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer/builtin_headers.cc",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"builtin_headers.h\"\n\n#include <cstddef>\n\n#include \"clang_builtin_headers_resources.inc\"\n\nconst struct FileToc *builtin_headers_create() { return kPackedFiles; }\nsize_t builtin_headers_size() {\n  return sizeof(kPackedFiles) / sizeof(FileToc) - 1;\n}\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer/builtin_headers.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef _TOOLS_IDE_QUERY_CC_ANALYZER_BUILTIN_HEADERS_H_\n#define _TOOLS_IDE_QUERY_CC_ANALYZER_BUILTIN_HEADERS_H_\n\n#include <cstddef>\n\nstruct FileToc {\n  const char *name;\n  const char *data;\n};\n\nconst struct FileToc *builtin_headers_create();\nsize_t builtin_headers_size();\n\n#endif\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer/include_scanner.cc",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"include_scanner.h\"\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"builtin_headers.h\"\n#include \"clang/Basic/FileEntry.h\"\n#include \"clang/Basic/FileManager.h\"\n#include \"clang/Basic/Module.h\"\n#include \"clang/Basic/SourceLocation.h\"\n#include \"clang/Basic/SourceManager.h\"\n#include \"clang/Frontend/ASTUnit.h\"\n#include \"clang/Frontend/CompilerInstance.h\"\n#include \"clang/Frontend/FrontendActions.h\"\n#include \"clang/Lex/PPCallbacks.h\"\n#include \"clang/Tooling/ArgumentsAdjusters.h\"\n#include \"clang/Tooling/CompilationDatabase.h\"\n#include \"clang/Tooling/Tooling.h\"\n#include \"llvm/ADT/ArrayRef.h\"\n#include \"llvm/ADT/IntrusiveRefCntPtr.h\"\n#include \"llvm/ADT/STLExtras.h\"\n#include \"llvm/ADT/SmallString.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/Twine.h\"\n#include \"llvm/Support/Error.h\"\n#include \"llvm/Support/MemoryBuffer.h\"\n#include \"llvm/Support/Path.h\"\n#include \"llvm/Support/VirtualFileSystem.h\"\n\nnamespace tools::ide_query::cc_analyzer {\nnamespace {\nstd::string CleanPath(llvm::StringRef path) {\n  // both ./ and ../ has `./` in them.\n  if (!path.contains(\"./\")) return path.str();\n  llvm::SmallString<256> clean_path(path);\n  llvm::sys::path::remove_dots(clean_path, /*remove_dot_dot=*/true);\n  return clean_path.str().str();\n}\n\n// Returns the absolute path to file_name, treating it as relative to cwd if it\n// isn't already absolute.\nstd::string GetAbsolutePath(llvm::StringRef cwd, llvm::StringRef file_name) {\n  if (llvm::sys::path::is_absolute(file_name)) return CleanPath(file_name);\n  llvm::SmallString<256> abs_path(cwd);\n  llvm::sys::path::append(abs_path, file_name);\n  llvm::sys::path::remove_dots(abs_path, /*remove_dot_dot=*/true);\n  return abs_path.str().str();\n}\n\nclass IncludeRecordingPP : public clang::PPCallbacks {\n public:\n  explicit IncludeRecordingPP(\n      std::unordered_map<std::string, std::string> &abs_paths, std::string cwd,\n      const clang::SourceManager &sm)\n      : abs_paths_(abs_paths), cwd_(std::move(cwd)), sm_(sm) {}\n\n  void LexedFileChanged(clang::FileID FID, LexedFileChangeReason Reason,\n                        clang::SrcMgr::CharacteristicKind FileType,\n                        clang::FileID PrevFID,\n                        clang::SourceLocation Loc) override {\n    auto file_entry = sm_.getFileEntryRefForID(FID);\n    if (!file_entry) return;\n    auto abs_path = GetAbsolutePath(cwd_, file_entry->getName());\n    auto [it, inserted] = abs_paths_.try_emplace(abs_path);\n    if (inserted) it->second = sm_.getBufferData(FID);\n  }\n\n  std::unordered_map<std::string, std::string> &abs_paths_;\n  const std::string cwd_;\n  const clang::SourceManager &sm_;\n};\n\nclass IncludeScanningAction final : public clang::PreprocessOnlyAction {\n public:\n  explicit IncludeScanningAction(\n      std::unordered_map<std::string, std::string> &abs_paths)\n      : abs_paths_(abs_paths) {}\n  bool BeginSourceFileAction(clang::CompilerInstance &ci) override {\n    // Be more resilient against all warnings/errors, as we want\n    // include-scanning to work even on incomplete sources.\n    ci.getDiagnostics().setEnableAllWarnings(false);\n    ci.getDiagnostics().setSeverityForAll(clang::diag::Flavor::WarningOrError,\n                                          clang::diag::Severity::Ignored);\n    std::string cwd;\n    auto cwd_or_err = ci.getVirtualFileSystem().getCurrentWorkingDirectory();\n    if (!cwd_or_err || cwd_or_err.get().empty()) return false;\n    cwd = cwd_or_err.get();\n    ci.getPreprocessor().addPPCallbacks(std::make_unique<IncludeRecordingPP>(\n        abs_paths_, std::move(cwd), ci.getSourceManager()));\n    return true;\n  }\n\n private:\n  std::unordered_map<std::string, std::string> &abs_paths_;\n};\n\nllvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayBuiltinHeaders(\n    std::vector<std::string> &argv,\n    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> base) {\n  static constexpr llvm::StringLiteral kResourceDir = \"/resources\";\n  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> overlay(\n      new llvm::vfs::OverlayFileSystem(std::move(base)));\n  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> builtin_headers(\n      new llvm::vfs::InMemoryFileSystem);\n\n  llvm::SmallString<256> file_path;\n  for (const auto &builtin_header :\n       llvm::ArrayRef(builtin_headers_create(), builtin_headers_size())) {\n    file_path.clear();\n    llvm::sys::path::append(file_path, kResourceDir, \"include\",\n                            builtin_header.name);\n    builtin_headers->addFile(\n        file_path,\n        /*ModificationTime=*/0,\n        llvm::MemoryBuffer::getMemBuffer(builtin_header.data));\n  }\n  overlay->pushOverlay(std::move(builtin_headers));\n  argv.insert(llvm::find(argv, \"--\"),\n              llvm::Twine(\"-resource-dir=\", kResourceDir).str());\n  return overlay;\n}\n\n}  // namespace\n\nllvm::Expected<std::vector<std::pair<std::string, std::string>>> ScanIncludes(\n    const clang::tooling::CompileCommand &cmd,\n    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {\n  if (fs->setCurrentWorkingDirectory(cmd.Directory)) {\n    return llvm::createStringError(\n        llvm::inconvertibleErrorCode(),\n        \"Failed to set working directory to: \" + cmd.Directory);\n  }\n\n  auto main_file = fs->getBufferForFile(cmd.Filename);\n  if (!main_file) {\n    return llvm::createStringError(llvm::inconvertibleErrorCode(),\n                                   \"Main file doesn't exist: \" + cmd.Filename);\n  }\n  std::unordered_map<std::string, std::string> abs_paths;\n  abs_paths.try_emplace(GetAbsolutePath(cmd.Directory, cmd.Filename),\n                        main_file.get()->getBuffer().str());\n\n  std::vector<std::string> argv = cmd.CommandLine;\n  // Disable all warnings to be more robust in analysis.\n  argv.insert(llvm::find(argv, \"--\"), {\"-Wno-error\", \"-w\"});\n  fs = OverlayBuiltinHeaders(argv, std::move(fs));\n\n  llvm::IntrusiveRefCntPtr<clang::FileManager> files(\n      new clang::FileManager(/*FileSystemOpts=*/{}, std::move(fs)));\n  clang::tooling::ToolInvocation tool(\n      argv, std::make_unique<IncludeScanningAction>(abs_paths), files.get());\n  if (!tool.run()) {\n    return llvm::createStringError(\n        llvm::inconvertibleErrorCode(),\n        \"Failed to scan includes for: \" + cmd.Filename);\n  }\n\n  std::vector<std::pair<std::string, std::string>> result;\n  result.reserve(abs_paths.size());\n  for (auto &entry : abs_paths) {\n    result.emplace_back(entry.first, std::move(entry.second));\n  }\n  return result;\n}\n}  // namespace tools::ide_query::cc_analyzer\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer/include_scanner.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef _TOOLS_IDE_QUERY_CC_ANALYZER_INCLUDE_SCANNER_H_\n#define _TOOLS_IDE_QUERY_CC_ANALYZER_INCLUDE_SCANNER_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"clang/Tooling/CompilationDatabase.h\"\n#include \"llvm/ADT/IntrusiveRefCntPtr.h\"\n#include \"llvm/Support/Error.h\"\n#include \"llvm/Support/VirtualFileSystem.h\"\n\nnamespace tools::ide_query::cc_analyzer {\n\n// Returns absolute paths and contents for all the includes necessary for\n// compiling source file in command.\nllvm::Expected<std::vector<std::pair<std::string, std::string>>> ScanIncludes(\n    const clang::tooling::CompileCommand &cmd,\n    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs);\n\n}  // namespace tools::ide_query::cc_analyzer\n\n#endif\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer/main.cc",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Driver for c++ extractor. Operates in two modes:\n// - DEPS, scans build graph for active files and reports targets that need to\n// be build for analyzing that file.\n// - INPUTS, scans the source code for active files and returns all the sources\n// required for analyzing that file.\n//\n// Uses stdin/stdout to take in requests and provide responses.\n#include <unistd.h>\n\n#include <memory>\n#include <utility>\n\n#include \"analyzer.h\"\n#include \"google/protobuf/message.h\"\n#include \"cc_analyzer.pb.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/Support/CommandLine.h\"\n#include \"llvm/Support/TargetSelect.h\"\n#include \"llvm/Support/raw_ostream.h\"\n\nnamespace {\nenum class OpMode {\n  DEPS = 0,\n  INPUTS = 1,\n};\nllvm::cl::opt<OpMode> mode{\n    \"mode\",\n    llvm::cl::values(clEnumValN(OpMode::DEPS, \"deps\",\n                                \"Figure out targets that need to be build\"),\n                     clEnumValN(OpMode::INPUTS, \"inputs\",\n                                \"Figure out generated files used\")),\n    llvm::cl::desc(\"Print the list of headers to insert and remove\"),\n};\n\ncc_analyzer::IdeAnalysis ReturnError(llvm::StringRef message) {\n  cc_analyzer::IdeAnalysis result;\n  result.mutable_status()->set_code(cc_analyzer::Status::FAILURE);\n  result.mutable_status()->set_message(message.str());\n  return result;\n}\n\n}  // namespace\n\nint main(int argc, char* argv[]) {\n  llvm::InitializeAllTargetInfos();\n  llvm::cl::ParseCommandLineOptions(argc, argv);\n\n  cc_analyzer::RepoState state;\n  if (!state.ParseFromFileDescriptor(STDIN_FILENO)) {\n    llvm::errs() << \"Failed to parse input!\\n\";\n    return 1;\n  }\n\n  std::unique_ptr<google::protobuf::Message> result;\n  switch (mode) {\n    case OpMode::DEPS: {\n      result = std::make_unique<cc_analyzer::DepsResponse>(\n          tools::ide_query::cc_analyzer::GetDeps(std::move(state)));\n      break;\n    }\n    case OpMode::INPUTS: {\n      result = std::make_unique<cc_analyzer::IdeAnalysis>(\n          tools::ide_query::cc_analyzer::GetBuildInputs(std::move(state)));\n      break;\n    }\n    default:\n      llvm::errs() << \"Unknown operation mode!\\n\";\n      return 1;\n  }\n  if (!result->SerializeToFileDescriptor(STDOUT_FILENO)) {\n    llvm::errs() << \"Failed to serialize result!\\n\";\n    return 1;\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer_proto/Android.bp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_host_static {\n    name: \"cc_analyzer_proto\",\n    srcs: [\n        \"cc_analyzer.proto\",\n    ],\n    proto: {\n        export_proto_headers: true,\n        type: \"full\",\n        canonical_path_from_root: false,\n    },\n    compile_multilib: \"64\",\n    shared_libs: [\"libprotobuf-cpp-full\"],\n}\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer_proto/cc_analyzer.pb.go",
    "content": "//\n// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.30.0\n// \tprotoc        v3.21.12\n// source: cc_analyzer.proto\n\npackage cc_analyzer_proto\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Status_Code int32\n\nconst (\n\tStatus_OK      Status_Code = 0\n\tStatus_FAILURE Status_Code = 1\n)\n\n// Enum value maps for Status_Code.\nvar (\n\tStatus_Code_name = map[int32]string{\n\t\t0: \"OK\",\n\t\t1: \"FAILURE\",\n\t}\n\tStatus_Code_value = map[string]int32{\n\t\t\"OK\":      0,\n\t\t\"FAILURE\": 1,\n\t}\n)\n\nfunc (x Status_Code) Enum() *Status_Code {\n\tp := new(Status_Code)\n\t*p = x\n\treturn p\n}\n\nfunc (x Status_Code) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (Status_Code) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_cc_analyzer_proto_enumTypes[0].Descriptor()\n}\n\nfunc (Status_Code) Type() protoreflect.EnumType {\n\treturn &file_cc_analyzer_proto_enumTypes[0]\n}\n\nfunc (x Status_Code) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use Status_Code.Descriptor instead.\nfunc (Status_Code) EnumDescriptor() ([]byte, []int) {\n\treturn file_cc_analyzer_proto_rawDescGZIP(), []int{0, 0}\n}\n\n// Indicates the success/failure for analysis.\ntype Status struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCode Status_Code `protobuf:\"varint,1,opt,name=code,proto3,enum=cc_analyzer.Status_Code\" json:\"code,omitempty\"`\n\t// Details about the status, might be displayed to user.\n\tMessage *string `protobuf:\"bytes,2,opt,name=message,proto3,oneof\" json:\"message,omitempty\"`\n}\n\nfunc (x *Status) Reset() {\n\t*x = Status{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_cc_analyzer_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Status) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Status) ProtoMessage() {}\n\nfunc (x *Status) ProtoReflect() protoreflect.Message {\n\tmi := &file_cc_analyzer_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Status.ProtoReflect.Descriptor instead.\nfunc (*Status) Descriptor() ([]byte, []int) {\n\treturn file_cc_analyzer_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Status) GetCode() Status_Code {\n\tif x != nil {\n\t\treturn x.Code\n\t}\n\treturn Status_OK\n}\n\nfunc (x *Status) GetMessage() string {\n\tif x != nil && x.Message != nil {\n\t\treturn *x.Message\n\t}\n\treturn \"\"\n}\n\n// Represents an Android checkout on user's workstation.\ntype RepoState struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Absolute path for the checkout in the workstation.\n\t// e.g. /home/user/work/android/\n\tRepoDir string `protobuf:\"bytes,1,opt,name=repo_dir,json=repoDir,proto3\" json:\"repo_dir,omitempty\"`\n\t// Relative to repo_dir.\n\tActiveFilePath []string `protobuf:\"bytes,2,rep,name=active_file_path,json=activeFilePath,proto3\" json:\"active_file_path,omitempty\"`\n\t// Repository relative path to output directory in workstation.\n\tOutDir string `protobuf:\"bytes,3,opt,name=out_dir,json=outDir,proto3\" json:\"out_dir,omitempty\"`\n\t// Repository relative path to compile_commands.json in workstation.\n\tCompDbPath string `protobuf:\"bytes,4,opt,name=comp_db_path,json=compDbPath,proto3\" json:\"comp_db_path,omitempty\"`\n}\n\nfunc (x *RepoState) Reset() {\n\t*x = RepoState{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_cc_analyzer_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RepoState) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RepoState) ProtoMessage() {}\n\nfunc (x *RepoState) ProtoReflect() protoreflect.Message {\n\tmi := &file_cc_analyzer_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use RepoState.ProtoReflect.Descriptor instead.\nfunc (*RepoState) Descriptor() ([]byte, []int) {\n\treturn file_cc_analyzer_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *RepoState) GetRepoDir() string {\n\tif x != nil {\n\t\treturn x.RepoDir\n\t}\n\treturn \"\"\n}\n\nfunc (x *RepoState) GetActiveFilePath() []string {\n\tif x != nil {\n\t\treturn x.ActiveFilePath\n\t}\n\treturn nil\n}\n\nfunc (x *RepoState) GetOutDir() string {\n\tif x != nil {\n\t\treturn x.OutDir\n\t}\n\treturn \"\"\n}\n\nfunc (x *RepoState) GetCompDbPath() string {\n\tif x != nil {\n\t\treturn x.CompDbPath\n\t}\n\treturn \"\"\n}\n\n// Provides all the targets that are pre-requisities for running language\n// services on active_file_paths.\ntype DepsResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tDeps   []*DepsResponse_Deps `protobuf:\"bytes,1,rep,name=deps,proto3\" json:\"deps,omitempty\"`\n\tStatus *Status              `protobuf:\"bytes,2,opt,name=status,proto3,oneof\" json:\"status,omitempty\"`\n}\n\nfunc (x *DepsResponse) Reset() {\n\t*x = DepsResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_cc_analyzer_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DepsResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DepsResponse) ProtoMessage() {}\n\nfunc (x *DepsResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_cc_analyzer_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DepsResponse.ProtoReflect.Descriptor instead.\nfunc (*DepsResponse) Descriptor() ([]byte, []int) {\n\treturn file_cc_analyzer_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *DepsResponse) GetDeps() []*DepsResponse_Deps {\n\tif x != nil {\n\t\treturn x.Deps\n\t}\n\treturn nil\n}\n\nfunc (x *DepsResponse) GetStatus() *Status {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn nil\n}\n\n// Returns all the information necessary for providing language services for the\n// active files.\ntype GeneratedFile struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Path to the file relative to repository root.\n\tPath string `protobuf:\"bytes,1,opt,name=path,proto3\" json:\"path,omitempty\"`\n\t// The text of the generated file, if not provided contents will be read\n\t// from the path above in user's workstation.\n\tContents []byte `protobuf:\"bytes,2,opt,name=contents,proto3,oneof\" json:\"contents,omitempty\"`\n}\n\nfunc (x *GeneratedFile) Reset() {\n\t*x = GeneratedFile{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_cc_analyzer_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GeneratedFile) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GeneratedFile) ProtoMessage() {}\n\nfunc (x *GeneratedFile) ProtoReflect() protoreflect.Message {\n\tmi := &file_cc_analyzer_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GeneratedFile.ProtoReflect.Descriptor instead.\nfunc (*GeneratedFile) Descriptor() ([]byte, []int) {\n\treturn file_cc_analyzer_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *GeneratedFile) GetPath() string {\n\tif x != nil {\n\t\treturn x.Path\n\t}\n\treturn \"\"\n}\n\nfunc (x *GeneratedFile) GetContents() []byte {\n\tif x != nil {\n\t\treturn x.Contents\n\t}\n\treturn nil\n}\n\ntype SourceFile struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Path to the source file relative to repository root.\n\tPath string `protobuf:\"bytes,1,opt,name=path,proto3\" json:\"path,omitempty\"`\n\t// Working directory used by the build system. All the relative\n\t// paths in compiler_arguments should be relative to this path.\n\t// Relative to repository root.\n\tWorkingDir string `protobuf:\"bytes,2,opt,name=working_dir,json=workingDir,proto3\" json:\"working_dir,omitempty\"`\n\t// Compiler arguments to compile the source file. If multiple variants\n\t// of the module being compiled are possible, the query script will choose\n\t// one.\n\tCompilerArguments []string `protobuf:\"bytes,3,rep,name=compiler_arguments,json=compilerArguments,proto3\" json:\"compiler_arguments,omitempty\"`\n\t// Any generated files that are used in compiling the file.\n\tGenerated []*GeneratedFile `protobuf:\"bytes,4,rep,name=generated,proto3\" json:\"generated,omitempty\"`\n\t// Paths to all of the sources, like build files, code generators,\n\t// proto files etc. that were used during analysis. Used to figure\n\t// out when a set of build artifacts are stale and the query tool\n\t// must be re-run.\n\t// Relative to repository root.\n\tDeps []string `protobuf:\"bytes,5,rep,name=deps,proto3\" json:\"deps,omitempty\"`\n\t// Represents analysis status for this particular file. e.g. not part\n\t// of the build graph.\n\tStatus *Status `protobuf:\"bytes,6,opt,name=status,proto3,oneof\" json:\"status,omitempty\"`\n}\n\nfunc (x *SourceFile) Reset() {\n\t*x = SourceFile{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_cc_analyzer_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SourceFile) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SourceFile) ProtoMessage() {}\n\nfunc (x *SourceFile) ProtoReflect() protoreflect.Message {\n\tmi := &file_cc_analyzer_proto_msgTypes[4]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SourceFile.ProtoReflect.Descriptor instead.\nfunc (*SourceFile) Descriptor() ([]byte, []int) {\n\treturn file_cc_analyzer_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *SourceFile) GetPath() string {\n\tif x != nil {\n\t\treturn x.Path\n\t}\n\treturn \"\"\n}\n\nfunc (x *SourceFile) GetWorkingDir() string {\n\tif x != nil {\n\t\treturn x.WorkingDir\n\t}\n\treturn \"\"\n}\n\nfunc (x *SourceFile) GetCompilerArguments() []string {\n\tif x != nil {\n\t\treturn x.CompilerArguments\n\t}\n\treturn nil\n}\n\nfunc (x *SourceFile) GetGenerated() []*GeneratedFile {\n\tif x != nil {\n\t\treturn x.Generated\n\t}\n\treturn nil\n}\n\nfunc (x *SourceFile) GetDeps() []string {\n\tif x != nil {\n\t\treturn x.Deps\n\t}\n\treturn nil\n}\n\nfunc (x *SourceFile) GetStatus() *Status {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn nil\n}\n\ntype IdeAnalysis struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSources []*SourceFile `protobuf:\"bytes,2,rep,name=sources,proto3\" json:\"sources,omitempty\"`\n\t// Status representing overall analysis.\n\t// Should fail only when no analysis can be performed.\n\tStatus *Status `protobuf:\"bytes,3,opt,name=status,proto3,oneof\" json:\"status,omitempty\"`\n}\n\nfunc (x *IdeAnalysis) Reset() {\n\t*x = IdeAnalysis{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_cc_analyzer_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *IdeAnalysis) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*IdeAnalysis) ProtoMessage() {}\n\nfunc (x *IdeAnalysis) ProtoReflect() protoreflect.Message {\n\tmi := &file_cc_analyzer_proto_msgTypes[5]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use IdeAnalysis.ProtoReflect.Descriptor instead.\nfunc (*IdeAnalysis) Descriptor() ([]byte, []int) {\n\treturn file_cc_analyzer_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *IdeAnalysis) GetSources() []*SourceFile {\n\tif x != nil {\n\t\treturn x.Sources\n\t}\n\treturn nil\n}\n\nfunc (x *IdeAnalysis) GetStatus() *Status {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn nil\n}\n\n// Build dependencies of a source file for providing language services.\ntype DepsResponse_Deps struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Relative to repo_dir.\n\tSourceFile string `protobuf:\"bytes,1,opt,name=source_file,json=sourceFile,proto3\" json:\"source_file,omitempty\"`\n\t// Build target to execute for generating dep.\n\tBuildTarget []string `protobuf:\"bytes,2,rep,name=build_target,json=buildTarget,proto3\" json:\"build_target,omitempty\"`\n\tStatus      *Status  `protobuf:\"bytes,3,opt,name=status,proto3,oneof\" json:\"status,omitempty\"`\n}\n\nfunc (x *DepsResponse_Deps) Reset() {\n\t*x = DepsResponse_Deps{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_cc_analyzer_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DepsResponse_Deps) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DepsResponse_Deps) ProtoMessage() {}\n\nfunc (x *DepsResponse_Deps) ProtoReflect() protoreflect.Message {\n\tmi := &file_cc_analyzer_proto_msgTypes[6]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DepsResponse_Deps.ProtoReflect.Descriptor instead.\nfunc (*DepsResponse_Deps) Descriptor() ([]byte, []int) {\n\treturn file_cc_analyzer_proto_rawDescGZIP(), []int{2, 0}\n}\n\nfunc (x *DepsResponse_Deps) GetSourceFile() string {\n\tif x != nil {\n\t\treturn x.SourceFile\n\t}\n\treturn \"\"\n}\n\nfunc (x *DepsResponse_Deps) GetBuildTarget() []string {\n\tif x != nil {\n\t\treturn x.BuildTarget\n\t}\n\treturn nil\n}\n\nfunc (x *DepsResponse_Deps) GetStatus() *Status {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn nil\n}\n\nvar File_cc_analyzer_proto protoreflect.FileDescriptor\n\nvar file_cc_analyzer_proto_rawDesc = []byte{\n\t0x0a, 0x11, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72,\n\t0x22, 0x7e, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2c, 0x0a, 0x04, 0x63, 0x6f,\n\t0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e,\n\t0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x43, 0x6f,\n\t0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,\n\t0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x6d, 0x65, 0x73,\n\t0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x22, 0x1b, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12,\n\t0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x41, 0x49, 0x4c, 0x55,\n\t0x52, 0x45, 0x10, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,\n\t0x22, 0x8b, 0x01, 0x0a, 0x09, 0x52, 0x65, 0x70, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19,\n\t0x0a, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x44, 0x69, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x63, 0x74,\n\t0x69, 0x76, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20,\n\t0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x50,\n\t0x61, 0x74, 0x68, 0x12, 0x17, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x03,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x44, 0x69, 0x72, 0x12, 0x20, 0x0a, 0x0c,\n\t0x63, 0x6f, 0x6d, 0x70, 0x5f, 0x64, 0x62, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x44, 0x62, 0x50, 0x61, 0x74, 0x68, 0x22, 0x89,\n\t0x02, 0x0a, 0x0c, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,\n\t0x32, 0x0a, 0x04, 0x64, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e,\n\t0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x44, 0x65, 0x70, 0x73,\n\t0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x65, 0x70, 0x73, 0x52, 0x04, 0x64,\n\t0x65, 0x70, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20,\n\t0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65,\n\t0x72, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74,\n\t0x75, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x87, 0x01, 0x0a, 0x04, 0x44, 0x65, 0x70, 0x73, 0x12, 0x1f,\n\t0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12,\n\t0x21, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18,\n\t0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x54, 0x61, 0x72, 0x67,\n\t0x65, 0x74, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01,\n\t0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72,\n\t0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,\n\t0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42,\n\t0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x51, 0x0a, 0x0d, 0x47, 0x65,\n\t0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70,\n\t0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12,\n\t0x1f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x88, 0x01, 0x01,\n\t0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xfb, 0x01,\n\t0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04,\n\t0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68,\n\t0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x72, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69,\n\t0x72, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x61, 0x72,\n\t0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x63,\n\t0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73,\n\t0x12, 0x38, 0x0a, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20,\n\t0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65,\n\t0x72, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52,\n\t0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65,\n\t0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x12, 0x30,\n\t0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,\n\t0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61,\n\t0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01,\n\t0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x0b,\n\t0x49, 0x64, 0x65, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x31, 0x0a, 0x07, 0x73,\n\t0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63,\n\t0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63,\n\t0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x30,\n\t0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,\n\t0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61,\n\t0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01,\n\t0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10,\n\t0x02, 0x42, 0x1d, 0x5a, 0x1b, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, 0x63,\n\t0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_cc_analyzer_proto_rawDescOnce sync.Once\n\tfile_cc_analyzer_proto_rawDescData = file_cc_analyzer_proto_rawDesc\n)\n\nfunc file_cc_analyzer_proto_rawDescGZIP() []byte {\n\tfile_cc_analyzer_proto_rawDescOnce.Do(func() {\n\t\tfile_cc_analyzer_proto_rawDescData = protoimpl.X.CompressGZIP(file_cc_analyzer_proto_rawDescData)\n\t})\n\treturn file_cc_analyzer_proto_rawDescData\n}\n\nvar file_cc_analyzer_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_cc_analyzer_proto_msgTypes = make([]protoimpl.MessageInfo, 7)\nvar file_cc_analyzer_proto_goTypes = []interface{}{\n\t(Status_Code)(0),          // 0: cc_analyzer.Status.Code\n\t(*Status)(nil),            // 1: cc_analyzer.Status\n\t(*RepoState)(nil),         // 2: cc_analyzer.RepoState\n\t(*DepsResponse)(nil),      // 3: cc_analyzer.DepsResponse\n\t(*GeneratedFile)(nil),     // 4: cc_analyzer.GeneratedFile\n\t(*SourceFile)(nil),        // 5: cc_analyzer.SourceFile\n\t(*IdeAnalysis)(nil),       // 6: cc_analyzer.IdeAnalysis\n\t(*DepsResponse_Deps)(nil), // 7: cc_analyzer.DepsResponse.Deps\n}\nvar file_cc_analyzer_proto_depIdxs = []int32{\n\t0, // 0: cc_analyzer.Status.code:type_name -> cc_analyzer.Status.Code\n\t7, // 1: cc_analyzer.DepsResponse.deps:type_name -> cc_analyzer.DepsResponse.Deps\n\t1, // 2: cc_analyzer.DepsResponse.status:type_name -> cc_analyzer.Status\n\t4, // 3: cc_analyzer.SourceFile.generated:type_name -> cc_analyzer.GeneratedFile\n\t1, // 4: cc_analyzer.SourceFile.status:type_name -> cc_analyzer.Status\n\t5, // 5: cc_analyzer.IdeAnalysis.sources:type_name -> cc_analyzer.SourceFile\n\t1, // 6: cc_analyzer.IdeAnalysis.status:type_name -> cc_analyzer.Status\n\t1, // 7: cc_analyzer.DepsResponse.Deps.status:type_name -> cc_analyzer.Status\n\t8, // [8:8] is the sub-list for method output_type\n\t8, // [8:8] is the sub-list for method input_type\n\t8, // [8:8] is the sub-list for extension type_name\n\t8, // [8:8] is the sub-list for extension extendee\n\t0, // [0:8] is the sub-list for field type_name\n}\n\nfunc init() { file_cc_analyzer_proto_init() }\nfunc file_cc_analyzer_proto_init() {\n\tif File_cc_analyzer_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_cc_analyzer_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Status); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_cc_analyzer_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*RepoState); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_cc_analyzer_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DepsResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_cc_analyzer_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GeneratedFile); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_cc_analyzer_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SourceFile); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_cc_analyzer_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*IdeAnalysis); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_cc_analyzer_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DepsResponse_Deps); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\tfile_cc_analyzer_proto_msgTypes[0].OneofWrappers = []interface{}{}\n\tfile_cc_analyzer_proto_msgTypes[2].OneofWrappers = []interface{}{}\n\tfile_cc_analyzer_proto_msgTypes[3].OneofWrappers = []interface{}{}\n\tfile_cc_analyzer_proto_msgTypes[4].OneofWrappers = []interface{}{}\n\tfile_cc_analyzer_proto_msgTypes[5].OneofWrappers = []interface{}{}\n\tfile_cc_analyzer_proto_msgTypes[6].OneofWrappers = []interface{}{}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_cc_analyzer_proto_rawDesc,\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   7,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_cc_analyzer_proto_goTypes,\n\t\tDependencyIndexes: file_cc_analyzer_proto_depIdxs,\n\t\tEnumInfos:         file_cc_analyzer_proto_enumTypes,\n\t\tMessageInfos:      file_cc_analyzer_proto_msgTypes,\n\t}.Build()\n\tFile_cc_analyzer_proto = out.File\n\tfile_cc_analyzer_proto_rawDesc = nil\n\tfile_cc_analyzer_proto_goTypes = nil\n\tfile_cc_analyzer_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer_proto/cc_analyzer.proto",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nsyntax = \"proto3\";\n\npackage cc_analyzer;\n\noption go_package = \"ide_query/cc_analyzer_proto\";\n\n// Indicates the success/failure for analysis.\nmessage Status {\n  enum Code {\n    OK = 0;\n    FAILURE = 1;\n  }\n  Code code = 1;\n  // Details about the status, might be displayed to user.\n  optional string message = 2;\n}\n\n// Represents an Android checkout on user's workstation.\nmessage RepoState {\n  // Absolute path for the checkout in the workstation.\n  // e.g. /home/user/work/android/\n  string repo_dir = 1;\n  // Relative to repo_dir.\n  repeated string active_file_path = 2;\n  // Repository relative path to output directory in workstation.\n  string out_dir = 3;\n  // Repository relative path to compile_commands.json in workstation.\n  string comp_db_path = 4;\n}\n\n// Provides all the targets that are pre-requisities for running language\n// services on active_file_paths.\nmessage DepsResponse {\n  // Build dependencies of a source file for providing language services.\n  message Deps {\n    // Relative to repo_dir.\n    string source_file = 1;\n    // Build target to execute for generating dep.\n    repeated string build_target = 2;\n    optional Status status = 3;\n  }\n  repeated Deps deps = 1;\n  optional Status status = 2;\n}\n\n// Returns all the information necessary for providing language services for the\n// active files.\nmessage GeneratedFile {\n  // Path to the file relative to repository root.\n  string path = 1;\n\n  // The text of the generated file, if not provided contents will be read\n  // from the path above in user's workstation.\n  optional bytes contents = 2;\n}\n\nmessage SourceFile {\n  // Path to the source file relative to repository root.\n  string path = 1;\n\n  // Working directory used by the build system. All the relative\n  // paths in compiler_arguments should be relative to this path.\n  // Relative to repository root.\n  string working_dir = 2;\n\n  // Compiler arguments to compile the source file. If multiple variants\n  // of the module being compiled are possible, the query script will choose\n  // one.\n  repeated string compiler_arguments = 3;\n\n  // Any generated files that are used in compiling the file.\n  repeated GeneratedFile generated = 4;\n\n  // Paths to all of the sources, like build files, code generators,\n  // proto files etc. that were used during analysis. Used to figure\n  // out when a set of build artifacts are stale and the query tool\n  // must be re-run.\n  // Relative to repository root.\n  repeated string deps = 5;\n\n  // Represents analysis status for this particular file. e.g. not part\n  // of the build graph.\n  optional Status status = 6;\n}\n\nmessage IdeAnalysis {\n  repeated SourceFile sources = 2;\n\n  // Status representing overall analysis.\n  // Should fail only when no analysis can be performed.\n  optional Status status = 3;\n\n  reserved 1;\n}\n"
  },
  {
    "path": "tools/ide_query/cc_analyzer_proto/regen.sh",
    "content": "#!/bin/bash\n\naprotoc --go_out=paths=source_relative:. cc_analyzer.proto\n"
  },
  {
    "path": "tools/ide_query/go.mod",
    "content": "module ide_query\n\ngo 1.21\n\nrequire (\n  \tgoogle.golang.org/protobuf v0.0.0\n)\n"
  },
  {
    "path": "tools/ide_query/go.work",
    "content": "go 1.21\n\nuse (\n\t.\n)\n\nreplace (\n\tgoogle.golang.org/protobuf v0.0.0 => ../../../../external/golang-protobuf\n)"
  },
  {
    "path": "tools/ide_query/go.work.sum",
    "content": "github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\n"
  },
  {
    "path": "tools/ide_query/ide_query.go",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Binary ide_query generates and analyzes build artifacts.\n// The produced result can be consumed by IDEs to provide language features.\npackage main\n\nimport (\n\t\"bytes\"\n\t\"container/list\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"google.golang.org/protobuf/proto\"\n\tapb \"ide_query/cc_analyzer_proto\"\n\tpb \"ide_query/ide_query_proto\"\n)\n\n// Env contains information about the current environment.\ntype Env struct {\n\tLunchTarget    LunchTarget\n\tRepoDir        string\n\tOutDir         string\n\tClangToolsRoot string\n}\n\n// LunchTarget is a parsed Android lunch target.\n// Input format: <product_name>-<release_type>-<build_variant>\ntype LunchTarget struct {\n\tProduct string\n\tRelease string\n\tVariant string\n}\n\nvar _ flag.Value = (*LunchTarget)(nil)\n\n// // Get implements flag.Value.\n// func (l *LunchTarget) Get() any {\n// \treturn l\n// }\n\n// Set implements flag.Value.\nfunc (l *LunchTarget) Set(s string) error {\n\tparts := strings.Split(s, \"-\")\n\tif len(parts) != 3 {\n\t\treturn fmt.Errorf(\"invalid lunch target: %q, must have form <product_name>-<release_type>-<build_variant>\", s)\n\t}\n\t*l = LunchTarget{\n\t\tProduct: parts[0],\n\t\tRelease: parts[1],\n\t\tVariant: parts[2],\n\t}\n\treturn nil\n}\n\n// String implements flag.Value.\nfunc (l *LunchTarget) String() string {\n\treturn fmt.Sprintf(\"%s-%s-%s\", l.Product, l.Release, l.Variant)\n}\n\nfunc main() {\n\tvar env Env\n\tenv.OutDir = strings.TrimSuffix(os.Getenv(\"OUT_DIR\"), \"/\")\n\tenv.RepoDir = os.Getenv(\"ANDROID_BUILD_TOP\")\n\tenv.ClangToolsRoot = os.Getenv(\"PREBUILTS_CLANG_TOOLS_ROOT\")\n\tflag.Var(&env.LunchTarget, \"lunch_target\", \"The lunch target to query\")\n\tflag.Parse()\n\tfiles := flag.Args()\n\tif len(files) == 0 {\n\t\tfmt.Println(\"No files provided.\")\n\t\tos.Exit(1)\n\t\treturn\n\t}\n\n\tvar ccFiles, javaFiles []string\n\tfor _, f := range files {\n\t\tswitch {\n\t\tcase strings.HasSuffix(f, \".java\") || strings.HasSuffix(f, \".kt\"):\n\t\t\tjavaFiles = append(javaFiles, f)\n\t\tcase strings.HasSuffix(f, \".cc\") || strings.HasSuffix(f, \".cpp\") || strings.HasSuffix(f, \".h\"):\n\t\t\tccFiles = append(ccFiles, f)\n\t\tdefault:\n\t\t\tlog.Printf(\"File %q is supported - will be skipped.\", f)\n\t\t}\n\t}\n\n\tctx := context.Background()\n\t// TODO(michaelmerg): Figure out if module_bp_java_deps.json and compile_commands.json is outdated.\n\trunMake(ctx, env, \"nothing\")\n\n\tjavaModules, err := loadJavaModules(env)\n\tif err != nil {\n\t\tlog.Printf(\"Failed to load java modules: %v\", err)\n\t}\n\n\tvar targets []string\n\tjavaTargetsByFile := findJavaModules(javaFiles, javaModules)\n\tfor _, target := range javaTargetsByFile {\n\t\ttargets = append(targets, javaModules[target].Jars...)\n\t}\n\n\tccTargets, err := getCCTargets(ctx, env, ccFiles)\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to query cc targets: %v\", err)\n\t}\n\ttargets = append(targets, ccTargets...)\n\tif len(targets) == 0 {\n\t\tfmt.Println(\"No targets found.\")\n\t\tos.Exit(1)\n\t\treturn\n\t}\n\n\tfmt.Fprintf(os.Stderr, \"Running make for modules: %v\\n\", strings.Join(targets, \", \"))\n\tif err := runMake(ctx, env, targets...); err != nil {\n\t\tlog.Printf(\"Building modules failed: %v\", err)\n\t}\n\n\tvar analysis pb.IdeAnalysis\n\tresults, units := getJavaInputs(env, javaTargetsByFile, javaModules)\n\tanalysis.Results = results\n\tanalysis.Units = units\n\tif err != nil && analysis.Error == nil {\n\t\tanalysis.Error = &pb.AnalysisError{\n\t\t\tErrorMessage: err.Error(),\n\t\t}\n\t}\n\n\tresults, units, err = getCCInputs(ctx, env, ccFiles)\n\tanalysis.Results = append(analysis.Results, results...)\n\tanalysis.Units = append(analysis.Units, units...)\n\tif err != nil && analysis.Error == nil {\n\t\tanalysis.Error = &pb.AnalysisError{\n\t\t\tErrorMessage: err.Error(),\n\t\t}\n\t}\n\n\tanalysis.BuildOutDir = env.OutDir\n\tdata, err := proto.Marshal(&analysis)\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to marshal result proto: %v\", err)\n\t}\n\n\t_, err = os.Stdout.Write(data)\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to write result proto: %v\", err)\n\t}\n\n\tfor _, r := range analysis.Results {\n\t\tfmt.Fprintf(os.Stderr, \"%s: %+v\\n\", r.GetSourceFilePath(), r.GetStatus())\n\t}\n}\n\nfunc repoState(env Env, filePaths []string) *apb.RepoState {\n\tconst compDbPath = \"soong/development/ide/compdb/compile_commands.json\"\n\treturn &apb.RepoState{\n\t\tRepoDir:        env.RepoDir,\n\t\tActiveFilePath: filePaths,\n\t\tOutDir:         env.OutDir,\n\t\tCompDbPath:     path.Join(env.OutDir, compDbPath),\n\t}\n}\n\nfunc runCCanalyzer(ctx context.Context, env Env, mode string, in []byte) ([]byte, error) {\n\tccAnalyzerPath := path.Join(env.ClangToolsRoot, \"bin/ide_query_cc_analyzer\")\n\toutBuffer := new(bytes.Buffer)\n\n\tinBuffer := new(bytes.Buffer)\n\tinBuffer.Write(in)\n\n\tcmd := exec.CommandContext(ctx, ccAnalyzerPath, \"--mode=\"+mode)\n\tcmd.Dir = env.RepoDir\n\n\tcmd.Stdin = inBuffer\n\tcmd.Stdout = outBuffer\n\tcmd.Stderr = os.Stderr\n\n\terr := cmd.Run()\n\n\treturn outBuffer.Bytes(), err\n}\n\n// Execute cc_analyzer and get all the targets that needs to be build for analyzing files.\nfunc getCCTargets(ctx context.Context, env Env, filePaths []string) ([]string, error) {\n\tstate, err := proto.Marshal(repoState(env, filePaths))\n\tif err != nil {\n\t\tlog.Fatalln(\"Failed to serialize state:\", err)\n\t}\n\n\tresp := new(apb.DepsResponse)\n\tresult, err := runCCanalyzer(ctx, env, \"deps\", state)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := proto.Unmarshal(result, resp); err != nil {\n\t\treturn nil, fmt.Errorf(\"malformed response from cc_analyzer: %v\", err)\n\t}\n\n\tvar targets []string\n\tif resp.Status != nil && resp.Status.Code != apb.Status_OK {\n\t\treturn targets, fmt.Errorf(\"cc_analyzer failed: %v\", resp.Status.Message)\n\t}\n\n\tfor _, deps := range resp.Deps {\n\t\ttargets = append(targets, deps.BuildTarget...)\n\t}\n\treturn targets, nil\n}\n\nfunc getCCInputs(ctx context.Context, env Env, filePaths []string) ([]*pb.AnalysisResult, []*pb.BuildableUnit, error) {\n\tstate, err := proto.Marshal(repoState(env, filePaths))\n\tif err != nil {\n\t\tlog.Fatalln(\"Failed to serialize state:\", err)\n\t}\n\n\tresp := new(apb.IdeAnalysis)\n\tresult, err := runCCanalyzer(ctx, env, \"inputs\", state)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"cc_analyzer failed:\", err)\n\t}\n\tif err := proto.Unmarshal(result, resp); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"malformed response from cc_analyzer: %v\", err)\n\t}\n\tif resp.Status != nil && resp.Status.Code != apb.Status_OK {\n\t\treturn nil, nil, fmt.Errorf(\"cc_analyzer failed: %v\", resp.Status.Message)\n\t}\n\n\tvar results []*pb.AnalysisResult\n\tvar units []*pb.BuildableUnit\n\tfor _, s := range resp.Sources {\n\t\tstatus := &pb.AnalysisResult_Status{\n\t\t\tCode: pb.AnalysisResult_Status_CODE_OK,\n\t\t}\n\t\tif s.GetStatus().GetCode() != apb.Status_OK {\n\t\t\tstatus.Code = pb.AnalysisResult_Status_CODE_BUILD_FAILED\n\t\t\tstatus.StatusMessage = proto.String(s.GetStatus().GetMessage())\n\t\t}\n\n\t\tresult := &pb.AnalysisResult{\n\t\t\tSourceFilePath: s.GetPath(),\n\t\t\tUnitId:         s.GetPath(),\n\t\t\tStatus:         status,\n\t\t}\n\t\tresults = append(results, result)\n\n\t\tvar generated []*pb.GeneratedFile\n\t\tfor _, f := range s.Generated {\n\t\t\tgenerated = append(generated, &pb.GeneratedFile{\n\t\t\t\tPath:     f.GetPath(),\n\t\t\t\tContents: f.GetContents(),\n\t\t\t})\n\t\t}\n\t\tgenUnit := &pb.BuildableUnit{\n\t\t\tId:              \"genfiles_for_\" + s.GetPath(),\n\t\t\tSourceFilePaths: s.GetDeps(),\n\t\t\tGeneratedFiles:  generated,\n\t\t}\n\n\t\tunit := &pb.BuildableUnit{\n\t\t\tId:                s.GetPath(),\n\t\t\tLanguage:          pb.Language_LANGUAGE_CPP,\n\t\t\tSourceFilePaths:   []string{s.GetPath()},\n\t\t\tCompilerArguments: s.GetCompilerArguments(),\n\t\t\tDependencyIds:     []string{genUnit.GetId()},\n\t\t}\n\t\tunits = append(units, unit, genUnit)\n\t}\n\treturn results, units, nil\n}\n\n// findJavaModules tries to find the modules that cover the given file paths.\n// If a file is covered by multiple modules, the first module is returned.\nfunc findJavaModules(paths []string, modules map[string]*javaModule) map[string]string {\n\tret := make(map[string]string)\n\t// A file may be part of multiple modules. To make the result deterministic,\n\t// check the modules in sorted order.\n\tkeys := make([]string, 0, len(modules))\n\tfor name := range modules {\n\t\tkeys = append(keys, name)\n\t}\n\tslices.Sort(keys)\n\tfor _, name := range keys {\n\t\tif strings.HasSuffix(name, \".impl\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tmodule := modules[name]\n\t\tif len(module.Jars) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor i, p := range paths {\n\t\t\tif slices.Contains(module.Srcs, p) {\n\t\t\t\tret[p] = name\n\t\t\t\tpaths = append(paths[:i], paths[i+1:]...)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif len(paths) == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn ret\n}\n\nfunc getJavaInputs(env Env, modulesByPath map[string]string, modules map[string]*javaModule) ([]*pb.AnalysisResult, []*pb.BuildableUnit) {\n\tvar results []*pb.AnalysisResult\n\tunitsById := make(map[string]*pb.BuildableUnit)\n\tfor p, moduleName := range modulesByPath {\n\t\tr := &pb.AnalysisResult{\n\t\t\tSourceFilePath: p,\n\t\t}\n\t\tresults = append(results, r)\n\n\t\tm := modules[moduleName]\n\t\tif m == nil {\n\t\t\tr.Status = &pb.AnalysisResult_Status{\n\t\t\t\tCode:          pb.AnalysisResult_Status_CODE_NOT_FOUND,\n\t\t\t\tStatusMessage: proto.String(\"File not found in any module.\"),\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tr.UnitId = moduleName\n\t\tr.Status = &pb.AnalysisResult_Status{Code: pb.AnalysisResult_Status_CODE_OK}\n\t\tif unitsById[r.UnitId] != nil {\n\t\t\t// File is covered by an already created unit.\n\t\t\tcontinue\n\t\t}\n\n\t\tu := &pb.BuildableUnit{\n\t\t\tId:              moduleName,\n\t\t\tLanguage:        pb.Language_LANGUAGE_JAVA,\n\t\t\tSourceFilePaths: m.Srcs,\n\t\t\tGeneratedFiles:  genFiles(env, m),\n\t\t\tDependencyIds:   m.Deps,\n\t\t}\n\t\tunitsById[u.Id] = u\n\n\t\tq := list.New()\n\t\tfor _, d := range m.Deps {\n\t\t\tq.PushBack(d)\n\t\t}\n\t\tfor q.Len() > 0 {\n\t\t\tname := q.Remove(q.Front()).(string)\n\t\t\tmod := modules[name]\n\t\t\tif mod == nil || unitsById[name] != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tunitsById[name] = &pb.BuildableUnit{\n\t\t\t\tId:              name,\n\t\t\t\tSourceFilePaths: mod.Srcs,\n\t\t\t\tGeneratedFiles:  genFiles(env, mod),\n\t\t\t\tDependencyIds:   mod.Deps,\n\t\t\t}\n\n\t\t\tfor _, d := range mod.Deps {\n\t\t\t\tq.PushBack(d)\n\t\t\t}\n\t\t}\n\t}\n\n\tunits := make([]*pb.BuildableUnit, 0, len(unitsById))\n\tfor _, u := range unitsById {\n\t\tunits = append(units, u)\n\t}\n\treturn results, units\n}\n\n// genFiles returns the generated files (paths that start with outDir/) for the\n// given module. Generated files that do not exist are ignored.\nfunc genFiles(env Env, mod *javaModule) []*pb.GeneratedFile {\n\tvar paths []string\n\tpaths = append(paths, mod.Srcs...)\n\tpaths = append(paths, mod.SrcJars...)\n\tpaths = append(paths, mod.Jars...)\n\n\tprefix := env.OutDir + \"/\"\n\tvar ret []*pb.GeneratedFile\n\tfor _, p := range paths {\n\t\trelPath, ok := strings.CutPrefix(p, prefix)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tcontents, err := os.ReadFile(path.Join(env.RepoDir, p))\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tret = append(ret, &pb.GeneratedFile{\n\t\t\tPath:     relPath,\n\t\t\tContents: contents,\n\t\t})\n\t}\n\treturn ret\n}\n\n// runMake runs Soong build for the given modules.\nfunc runMake(ctx context.Context, env Env, modules ...string) error {\n\targs := []string{\n\t\t\"--make-mode\",\n\t\t\"ANDROID_BUILD_ENVIRONMENT_CONFIG=googler-cog\",\n\t\t\"SOONG_GEN_COMPDB=1\",\n\t\t\"TARGET_PRODUCT=\" + env.LunchTarget.Product,\n\t\t\"TARGET_RELEASE=\" + env.LunchTarget.Release,\n\t\t\"TARGET_BUILD_VARIANT=\" + env.LunchTarget.Variant,\n\t\t\"TARGET_BUILD_TYPE=release\",\n\t\t\"-k\",\n\t}\n\targs = append(args, modules...)\n\tcmd := exec.CommandContext(ctx, \"build/soong/soong_ui.bash\", args...)\n\tcmd.Dir = env.RepoDir\n\tcmd.Stdout = os.Stderr\n\tcmd.Stderr = os.Stderr\n\treturn cmd.Run()\n}\n\ntype javaModule struct {\n\tPath    []string `json:\"path,omitempty\"`\n\tDeps    []string `json:\"dependencies,omitempty\"`\n\tSrcs    []string `json:\"srcs,omitempty\"`\n\tJars    []string `json:\"jars,omitempty\"`\n\tSrcJars []string `json:\"srcjars,omitempty\"`\n}\n\nfunc loadJavaModules(env Env) (map[string]*javaModule, error) {\n\tjavaDepsPath := path.Join(env.RepoDir, env.OutDir, \"soong/module_bp_java_deps.json\")\n\tdata, err := os.ReadFile(javaDepsPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret map[string]*javaModule // module name -> module\n\tif err = json.Unmarshal(data, &ret); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Add top level java_sdk_library for .impl modules.\n\tfor name, module := range ret {\n\t\tif striped := strings.TrimSuffix(name, \".impl\"); striped != name {\n\t\t\tret[striped] = module\n\t\t}\n\t}\n\treturn ret, nil\n}\n"
  },
  {
    "path": "tools/ide_query/ide_query.sh",
    "content": "#!/bin/bash -e\n\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ncd $(dirname $BASH_SOURCE)\nsource $(pwd)/../../shell_utils.sh\nrequire_top\n\n# Ensure cogsetup (out/ will be symlink outside the repo)\nsetup_cog_env_if_needed\n\ncase $(uname -s) in\n    Linux)\n      export PREBUILTS_CLANG_TOOLS_ROOT=\"${TOP}/prebuilts/clang-tools/linux-x86/\"\n      PREBUILTS_GO_ROOT=\"${TOP}/prebuilts/go/linux-x86/\"\n      ;;\n    *)\n      echo \"Only supported for linux hosts\" >&2\n      exit 1\n      ;;\nesac\n\nexport BUILD_ENV_SEQUENCE_NUMBER=13\nexport ANDROID_BUILD_TOP=$TOP\nexport OUT_DIR=${OUT_DIR}\nexec \"${PREBUILTS_GO_ROOT}/bin/go\" \"run\" \"ide_query\" \"$@\"\n"
  },
  {
    "path": "tools/ide_query/ide_query_proto/ide_query.pb.go",
    "content": "//\n// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.30.0\n// \tprotoc        v3.21.12\n// source: ide_query.proto\n\npackage ide_query_proto\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Language int32\n\nconst (\n\tLanguage_LANGUAGE_UNSPECIFIED Language = 0\n\tLanguage_LANGUAGE_JAVA        Language = 1 // also includes Kotlin\n\tLanguage_LANGUAGE_CPP         Language = 2\n)\n\n// Enum value maps for Language.\nvar (\n\tLanguage_name = map[int32]string{\n\t\t0: \"LANGUAGE_UNSPECIFIED\",\n\t\t1: \"LANGUAGE_JAVA\",\n\t\t2: \"LANGUAGE_CPP\",\n\t}\n\tLanguage_value = map[string]int32{\n\t\t\"LANGUAGE_UNSPECIFIED\": 0,\n\t\t\"LANGUAGE_JAVA\":        1,\n\t\t\"LANGUAGE_CPP\":         2,\n\t}\n)\n\nfunc (x Language) Enum() *Language {\n\tp := new(Language)\n\t*p = x\n\treturn p\n}\n\nfunc (x Language) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (Language) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_ide_query_proto_enumTypes[0].Descriptor()\n}\n\nfunc (Language) Type() protoreflect.EnumType {\n\treturn &file_ide_query_proto_enumTypes[0]\n}\n\nfunc (x Language) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use Language.Descriptor instead.\nfunc (Language) EnumDescriptor() ([]byte, []int) {\n\treturn file_ide_query_proto_rawDescGZIP(), []int{0}\n}\n\ntype AnalysisResult_Status_Code int32\n\nconst (\n\tAnalysisResult_Status_CODE_UNSPECIFIED  AnalysisResult_Status_Code = 0\n\tAnalysisResult_Status_CODE_OK           AnalysisResult_Status_Code = 1\n\tAnalysisResult_Status_CODE_NOT_FOUND    AnalysisResult_Status_Code = 2 // no target or module found for the source file.\n\tAnalysisResult_Status_CODE_BUILD_FAILED AnalysisResult_Status_Code = 3\n)\n\n// Enum value maps for AnalysisResult_Status_Code.\nvar (\n\tAnalysisResult_Status_Code_name = map[int32]string{\n\t\t0: \"CODE_UNSPECIFIED\",\n\t\t1: \"CODE_OK\",\n\t\t2: \"CODE_NOT_FOUND\",\n\t\t3: \"CODE_BUILD_FAILED\",\n\t}\n\tAnalysisResult_Status_Code_value = map[string]int32{\n\t\t\"CODE_UNSPECIFIED\":  0,\n\t\t\"CODE_OK\":           1,\n\t\t\"CODE_NOT_FOUND\":    2,\n\t\t\"CODE_BUILD_FAILED\": 3,\n\t}\n)\n\nfunc (x AnalysisResult_Status_Code) Enum() *AnalysisResult_Status_Code {\n\tp := new(AnalysisResult_Status_Code)\n\t*p = x\n\treturn p\n}\n\nfunc (x AnalysisResult_Status_Code) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (AnalysisResult_Status_Code) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_ide_query_proto_enumTypes[1].Descriptor()\n}\n\nfunc (AnalysisResult_Status_Code) Type() protoreflect.EnumType {\n\treturn &file_ide_query_proto_enumTypes[1]\n}\n\nfunc (x AnalysisResult_Status_Code) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use AnalysisResult_Status_Code.Descriptor instead.\nfunc (AnalysisResult_Status_Code) EnumDescriptor() ([]byte, []int) {\n\treturn file_ide_query_proto_rawDescGZIP(), []int{3, 0, 0}\n}\n\ntype GeneratedFile struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Path to the file relative to build_out_dir.\n\tPath string `protobuf:\"bytes,1,opt,name=path,proto3\" json:\"path,omitempty\"`\n\t// The text of the generated file, if not provided contents will be read\n\t// from the path above in user's workstation.\n\tContents []byte `protobuf:\"bytes,2,opt,name=contents,proto3,oneof\" json:\"contents,omitempty\"`\n}\n\nfunc (x *GeneratedFile) Reset() {\n\t*x = GeneratedFile{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ide_query_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GeneratedFile) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GeneratedFile) ProtoMessage() {}\n\nfunc (x *GeneratedFile) ProtoReflect() protoreflect.Message {\n\tmi := &file_ide_query_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GeneratedFile.ProtoReflect.Descriptor instead.\nfunc (*GeneratedFile) Descriptor() ([]byte, []int) {\n\treturn file_ide_query_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *GeneratedFile) GetPath() string {\n\tif x != nil {\n\t\treturn x.Path\n\t}\n\treturn \"\"\n}\n\nfunc (x *GeneratedFile) GetContents() []byte {\n\tif x != nil {\n\t\treturn x.Contents\n\t}\n\treturn nil\n}\n\ntype IdeAnalysis struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Directory that contains build outputs generated by the build system.\n\t// Relative to repository root.\n\tBuildOutDir string `protobuf:\"bytes,1,opt,name=build_out_dir,json=buildOutDir,proto3\" json:\"build_out_dir,omitempty\"`\n\t// Working directory used by the build system.\n\t// Relative to repository root.\n\tWorkingDir string `protobuf:\"bytes,4,opt,name=working_dir,json=workingDir,proto3\" json:\"working_dir,omitempty\"`\n\t// Only set if the whole query failed.\n\tError *AnalysisError `protobuf:\"bytes,5,opt,name=error,proto3,oneof\" json:\"error,omitempty\"`\n\t// List of results, one per queried file.\n\tResults []*AnalysisResult `protobuf:\"bytes,6,rep,name=results,proto3\" json:\"results,omitempty\"`\n\t// List of buildable units directly or indirectly references by the results.\n\tUnits []*BuildableUnit `protobuf:\"bytes,7,rep,name=units,proto3\" json:\"units,omitempty\"`\n}\n\nfunc (x *IdeAnalysis) Reset() {\n\t*x = IdeAnalysis{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ide_query_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *IdeAnalysis) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*IdeAnalysis) ProtoMessage() {}\n\nfunc (x *IdeAnalysis) ProtoReflect() protoreflect.Message {\n\tmi := &file_ide_query_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use IdeAnalysis.ProtoReflect.Descriptor instead.\nfunc (*IdeAnalysis) Descriptor() ([]byte, []int) {\n\treturn file_ide_query_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *IdeAnalysis) GetBuildOutDir() string {\n\tif x != nil {\n\t\treturn x.BuildOutDir\n\t}\n\treturn \"\"\n}\n\nfunc (x *IdeAnalysis) GetWorkingDir() string {\n\tif x != nil {\n\t\treturn x.WorkingDir\n\t}\n\treturn \"\"\n}\n\nfunc (x *IdeAnalysis) GetError() *AnalysisError {\n\tif x != nil {\n\t\treturn x.Error\n\t}\n\treturn nil\n}\n\nfunc (x *IdeAnalysis) GetResults() []*AnalysisResult {\n\tif x != nil {\n\t\treturn x.Results\n\t}\n\treturn nil\n}\n\nfunc (x *IdeAnalysis) GetUnits() []*BuildableUnit {\n\tif x != nil {\n\t\treturn x.Units\n\t}\n\treturn nil\n}\n\ntype AnalysisError struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Human readable error message.\n\tErrorMessage string `protobuf:\"bytes,1,opt,name=error_message,json=errorMessage,proto3\" json:\"error_message,omitempty\"`\n}\n\nfunc (x *AnalysisError) Reset() {\n\t*x = AnalysisError{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ide_query_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AnalysisError) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AnalysisError) ProtoMessage() {}\n\nfunc (x *AnalysisError) ProtoReflect() protoreflect.Message {\n\tmi := &file_ide_query_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AnalysisError.ProtoReflect.Descriptor instead.\nfunc (*AnalysisError) Descriptor() ([]byte, []int) {\n\treturn file_ide_query_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *AnalysisError) GetErrorMessage() string {\n\tif x != nil {\n\t\treturn x.ErrorMessage\n\t}\n\treturn \"\"\n}\n\ntype AnalysisResult struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Path to the source file that was queried, relative to repository root.\n\tSourceFilePath string `protobuf:\"bytes,1,opt,name=source_file_path,json=sourceFilePath,proto3\" json:\"source_file_path,omitempty\"`\n\t// Represents status for this result. e.g. not part of the build graph.\n\tStatus *AnalysisResult_Status `protobuf:\"bytes,2,opt,name=status,proto3\" json:\"status,omitempty\"`\n\t// ID of buildable unit that contains the source file.\n\t// The ide_query script can choose the most relevant unit from multiple\n\t// options.\n\tUnitId string `protobuf:\"bytes,3,opt,name=unit_id,json=unitId,proto3\" json:\"unit_id,omitempty\"`\n\t// Invalidation rule to check if the result is still valid.\n\tInvalidation *Invalidation `protobuf:\"bytes,4,opt,name=invalidation,proto3\" json:\"invalidation,omitempty\"`\n}\n\nfunc (x *AnalysisResult) Reset() {\n\t*x = AnalysisResult{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ide_query_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AnalysisResult) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AnalysisResult) ProtoMessage() {}\n\nfunc (x *AnalysisResult) ProtoReflect() protoreflect.Message {\n\tmi := &file_ide_query_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AnalysisResult.ProtoReflect.Descriptor instead.\nfunc (*AnalysisResult) Descriptor() ([]byte, []int) {\n\treturn file_ide_query_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *AnalysisResult) GetSourceFilePath() string {\n\tif x != nil {\n\t\treturn x.SourceFilePath\n\t}\n\treturn \"\"\n}\n\nfunc (x *AnalysisResult) GetStatus() *AnalysisResult_Status {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn nil\n}\n\nfunc (x *AnalysisResult) GetUnitId() string {\n\tif x != nil {\n\t\treturn x.UnitId\n\t}\n\treturn \"\"\n}\n\nfunc (x *AnalysisResult) GetInvalidation() *Invalidation {\n\tif x != nil {\n\t\treturn x.Invalidation\n\t}\n\treturn nil\n}\n\ntype BuildableUnit struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Unique identifier of the buildable unit.\n\t//\n\t// Examples:\n\t//   - Java: module or target name, e.g. \"framework-bluetooth\" or\n\t//     \"//third_party/hamcrest:hamcrest_java\"\n\t//   - C++: source file, e.g. \"path/to/file.cc\"\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\t// Language of the unit.\n\t// Required for buildable units directly referenced by the AnalysisResult,\n\t// e.g. the unit associated with the compilation stage for the source file.\n\tLanguage Language `protobuf:\"varint,2,opt,name=language,proto3,enum=ide_query.Language\" json:\"language,omitempty\"`\n\t// Source files that are part of this unit.\n\t// Path to the file relative to working_dir.\n\tSourceFilePaths []string `protobuf:\"bytes,3,rep,name=source_file_paths,json=sourceFilePaths,proto3\" json:\"source_file_paths,omitempty\"`\n\t// Compiler arguments to compile the source files.\n\tCompilerArguments []string `protobuf:\"bytes,4,rep,name=compiler_arguments,json=compilerArguments,proto3\" json:\"compiler_arguments,omitempty\"`\n\t// List of generated files produced by this unit.\n\tGeneratedFiles []*GeneratedFile `protobuf:\"bytes,5,rep,name=generated_files,json=generatedFiles,proto3\" json:\"generated_files,omitempty\"`\n\t// List of other BuildableUnits this unit depend on.\n\tDependencyIds []string `protobuf:\"bytes,6,rep,name=dependency_ids,json=dependencyIds,proto3\" json:\"dependency_ids,omitempty\"`\n}\n\nfunc (x *BuildableUnit) Reset() {\n\t*x = BuildableUnit{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ide_query_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *BuildableUnit) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*BuildableUnit) ProtoMessage() {}\n\nfunc (x *BuildableUnit) ProtoReflect() protoreflect.Message {\n\tmi := &file_ide_query_proto_msgTypes[4]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use BuildableUnit.ProtoReflect.Descriptor instead.\nfunc (*BuildableUnit) Descriptor() ([]byte, []int) {\n\treturn file_ide_query_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *BuildableUnit) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\nfunc (x *BuildableUnit) GetLanguage() Language {\n\tif x != nil {\n\t\treturn x.Language\n\t}\n\treturn Language_LANGUAGE_UNSPECIFIED\n}\n\nfunc (x *BuildableUnit) GetSourceFilePaths() []string {\n\tif x != nil {\n\t\treturn x.SourceFilePaths\n\t}\n\treturn nil\n}\n\nfunc (x *BuildableUnit) GetCompilerArguments() []string {\n\tif x != nil {\n\t\treturn x.CompilerArguments\n\t}\n\treturn nil\n}\n\nfunc (x *BuildableUnit) GetGeneratedFiles() []*GeneratedFile {\n\tif x != nil {\n\t\treturn x.GeneratedFiles\n\t}\n\treturn nil\n}\n\nfunc (x *BuildableUnit) GetDependencyIds() []string {\n\tif x != nil {\n\t\treturn x.DependencyIds\n\t}\n\treturn nil\n}\n\n// Invalidation rule to check if the result is still valid.\n// This should contain files/dirs that are not directly part of the build graph\n// but still affect the result. For example BUILD files, directory to the\n// toolchain or config files etc.\ntype Invalidation struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// If any of these files change the result may become invalid.\n\t// Path to the file relative to repository root.\n\tFilePaths []string `protobuf:\"bytes,1,rep,name=file_paths,json=filePaths,proto3\" json:\"file_paths,omitempty\"`\n\t// If any of these rules match a changed file the result may become invalid.\n\tWildcards []*Invalidation_Wildcard `protobuf:\"bytes,4,rep,name=wildcards,proto3\" json:\"wildcards,omitempty\"`\n}\n\nfunc (x *Invalidation) Reset() {\n\t*x = Invalidation{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ide_query_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Invalidation) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Invalidation) ProtoMessage() {}\n\nfunc (x *Invalidation) ProtoReflect() protoreflect.Message {\n\tmi := &file_ide_query_proto_msgTypes[5]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Invalidation.ProtoReflect.Descriptor instead.\nfunc (*Invalidation) Descriptor() ([]byte, []int) {\n\treturn file_ide_query_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *Invalidation) GetFilePaths() []string {\n\tif x != nil {\n\t\treturn x.FilePaths\n\t}\n\treturn nil\n}\n\nfunc (x *Invalidation) GetWildcards() []*Invalidation_Wildcard {\n\tif x != nil {\n\t\treturn x.Wildcards\n\t}\n\treturn nil\n}\n\n// Indicates the success/failure for the query.\ntype AnalysisResult_Status struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCode AnalysisResult_Status_Code `protobuf:\"varint,1,opt,name=code,proto3,enum=ide_query.AnalysisResult_Status_Code\" json:\"code,omitempty\"`\n\t// Details about the status, might be displayed to user.\n\tStatusMessage *string `protobuf:\"bytes,2,opt,name=status_message,json=statusMessage,proto3,oneof\" json:\"status_message,omitempty\"`\n}\n\nfunc (x *AnalysisResult_Status) Reset() {\n\t*x = AnalysisResult_Status{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ide_query_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AnalysisResult_Status) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AnalysisResult_Status) ProtoMessage() {}\n\nfunc (x *AnalysisResult_Status) ProtoReflect() protoreflect.Message {\n\tmi := &file_ide_query_proto_msgTypes[6]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use AnalysisResult_Status.ProtoReflect.Descriptor instead.\nfunc (*AnalysisResult_Status) Descriptor() ([]byte, []int) {\n\treturn file_ide_query_proto_rawDescGZIP(), []int{3, 0}\n}\n\nfunc (x *AnalysisResult_Status) GetCode() AnalysisResult_Status_Code {\n\tif x != nil {\n\t\treturn x.Code\n\t}\n\treturn AnalysisResult_Status_CODE_UNSPECIFIED\n}\n\nfunc (x *AnalysisResult_Status) GetStatusMessage() string {\n\tif x != nil && x.StatusMessage != nil {\n\t\treturn *x.StatusMessage\n\t}\n\treturn \"\"\n}\n\ntype Invalidation_Wildcard struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Prefix of the file path (e.g. \"path/to/\")\n\tPrefix *string `protobuf:\"bytes,1,opt,name=prefix,proto3,oneof\" json:\"prefix,omitempty\"`\n\t// Suffix of the file path (e.g. \"Android.bp\")\n\tSuffix *string `protobuf:\"bytes,2,opt,name=suffix,proto3,oneof\" json:\"suffix,omitempty\"`\n\t// If false, the part of the path between the given `prefix` and `suffix`\n\t// should not contain directory separators ('/').\n\tCanCrossFolder *bool `protobuf:\"varint,3,opt,name=can_cross_folder,json=canCrossFolder,proto3,oneof\" json:\"can_cross_folder,omitempty\"`\n}\n\nfunc (x *Invalidation_Wildcard) Reset() {\n\t*x = Invalidation_Wildcard{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ide_query_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Invalidation_Wildcard) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Invalidation_Wildcard) ProtoMessage() {}\n\nfunc (x *Invalidation_Wildcard) ProtoReflect() protoreflect.Message {\n\tmi := &file_ide_query_proto_msgTypes[7]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Invalidation_Wildcard.ProtoReflect.Descriptor instead.\nfunc (*Invalidation_Wildcard) Descriptor() ([]byte, []int) {\n\treturn file_ide_query_proto_rawDescGZIP(), []int{5, 0}\n}\n\nfunc (x *Invalidation_Wildcard) GetPrefix() string {\n\tif x != nil && x.Prefix != nil {\n\t\treturn *x.Prefix\n\t}\n\treturn \"\"\n}\n\nfunc (x *Invalidation_Wildcard) GetSuffix() string {\n\tif x != nil && x.Suffix != nil {\n\t\treturn *x.Suffix\n\t}\n\treturn \"\"\n}\n\nfunc (x *Invalidation_Wildcard) GetCanCrossFolder() bool {\n\tif x != nil && x.CanCrossFolder != nil {\n\t\treturn *x.CanCrossFolder\n\t}\n\treturn false\n}\n\nvar File_ide_query_proto protoreflect.FileDescriptor\n\nvar file_ide_query_proto_rawDesc = []byte{\n\t0x0a, 0x0f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x12, 0x09, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x51, 0x0a, 0x0d,\n\t0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a,\n\t0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,\n\t0x68, 0x12, 0x1f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20,\n\t0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x88,\n\t0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22,\n\t0x82, 0x02, 0x0a, 0x0b, 0x49, 0x64, 0x65, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12,\n\t0x22, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x69, 0x72,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4f, 0x75, 0x74,\n\t0x44, 0x69, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64,\n\t0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e,\n\t0x67, 0x44, 0x69, 0x72, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20,\n\t0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e,\n\t0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52,\n\t0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x07, 0x72, 0x65, 0x73,\n\t0x75, 0x6c, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x69, 0x64, 0x65,\n\t0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52,\n\t0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2e,\n\t0x0a, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e,\n\t0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x61,\n\t0x62, 0x6c, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x42, 0x08,\n\t0x0a, 0x06, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04,\n\t0x08, 0x03, 0x10, 0x04, 0x22, 0x34, 0x0a, 0x0d, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73,\n\t0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d,\n\t0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72,\n\t0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xa5, 0x03, 0x0a, 0x0e, 0x41,\n\t0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x28, 0x0a,\n\t0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74,\n\t0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46,\n\t0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x38, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,\n\t0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75,\n\t0x65, 0x72, 0x79, 0x2e, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x75,\n\t0x6c, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,\n\t0x73, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x6e, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x74, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x0c, 0x69, 0x6e,\n\t0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,\n\t0x32, 0x17, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x76,\n\t0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x61, 0x6c,\n\t0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xd8, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74,\n\t0x75, 0x73, 0x12, 0x39, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,\n\t0x32, 0x25, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x41, 0x6e, 0x61,\n\t0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74,\n\t0x75, 0x73, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x2a, 0x0a,\n\t0x0e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d,\n\t0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x22, 0x54, 0x0a, 0x04, 0x43, 0x6f, 0x64,\n\t0x65, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,\n\t0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x44, 0x45, 0x5f,\n\t0x4f, 0x4b, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x54,\n\t0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x44, 0x45,\n\t0x5f, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x42,\n\t0x11, 0x0a, 0x0f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,\n\t0x67, 0x65, 0x22, 0x95, 0x02, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x61, 0x62, 0x6c, 0x65,\n\t0x55, 0x6e, 0x69, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x02, 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65,\n\t0x72, 0x79, 0x2e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6c, 0x61, 0x6e,\n\t0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f,\n\t0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,\n\t0x52, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68,\n\t0x73, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x61, 0x72,\n\t0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x63,\n\t0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73,\n\t0x12, 0x41, 0x0a, 0x0f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x69,\n\t0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x64, 0x65, 0x5f,\n\t0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46,\n\t0x69, 0x6c, 0x65, 0x52, 0x0e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69,\n\t0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63,\n\t0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x65, 0x70,\n\t0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x64, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0c, 0x49,\n\t0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x66,\n\t0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,\n\t0x09, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x3e, 0x0a, 0x09, 0x77, 0x69,\n\t0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e,\n\t0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69,\n\t0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x52,\n\t0x09, 0x77, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x73, 0x1a, 0x9e, 0x01, 0x0a, 0x08, 0x57,\n\t0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69,\n\t0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69,\n\t0x78, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x88, 0x01,\n\t0x01, 0x12, 0x2d, 0x0a, 0x10, 0x63, 0x61, 0x6e, 0x5f, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x5f, 0x66,\n\t0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x0e, 0x63,\n\t0x61, 0x6e, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01,\n\t0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x42, 0x09, 0x0a, 0x07, 0x5f,\n\t0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x63, 0x61, 0x6e, 0x5f, 0x63,\n\t0x72, 0x6f, 0x73, 0x73, 0x5f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x2a, 0x49, 0x0a, 0x08, 0x4c,\n\t0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x4c, 0x41, 0x4e, 0x47, 0x55,\n\t0x41, 0x47, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,\n\t0x00, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x41, 0x4e, 0x47, 0x55, 0x41, 0x47, 0x45, 0x5f, 0x4a, 0x41,\n\t0x56, 0x41, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x41, 0x4e, 0x47, 0x55, 0x41, 0x47, 0x45,\n\t0x5f, 0x43, 0x50, 0x50, 0x10, 0x02, 0x42, 0x1b, 0x5a, 0x19, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75,\n\t0x65, 0x72, 0x79, 0x2f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_ide_query_proto_rawDescOnce sync.Once\n\tfile_ide_query_proto_rawDescData = file_ide_query_proto_rawDesc\n)\n\nfunc file_ide_query_proto_rawDescGZIP() []byte {\n\tfile_ide_query_proto_rawDescOnce.Do(func() {\n\t\tfile_ide_query_proto_rawDescData = protoimpl.X.CompressGZIP(file_ide_query_proto_rawDescData)\n\t})\n\treturn file_ide_query_proto_rawDescData\n}\n\nvar file_ide_query_proto_enumTypes = make([]protoimpl.EnumInfo, 2)\nvar file_ide_query_proto_msgTypes = make([]protoimpl.MessageInfo, 8)\nvar file_ide_query_proto_goTypes = []interface{}{\n\t(Language)(0),                   // 0: ide_query.Language\n\t(AnalysisResult_Status_Code)(0), // 1: ide_query.AnalysisResult.Status.Code\n\t(*GeneratedFile)(nil),           // 2: ide_query.GeneratedFile\n\t(*IdeAnalysis)(nil),             // 3: ide_query.IdeAnalysis\n\t(*AnalysisError)(nil),           // 4: ide_query.AnalysisError\n\t(*AnalysisResult)(nil),          // 5: ide_query.AnalysisResult\n\t(*BuildableUnit)(nil),           // 6: ide_query.BuildableUnit\n\t(*Invalidation)(nil),            // 7: ide_query.Invalidation\n\t(*AnalysisResult_Status)(nil),   // 8: ide_query.AnalysisResult.Status\n\t(*Invalidation_Wildcard)(nil),   // 9: ide_query.Invalidation.Wildcard\n}\nvar file_ide_query_proto_depIdxs = []int32{\n\t4, // 0: ide_query.IdeAnalysis.error:type_name -> ide_query.AnalysisError\n\t5, // 1: ide_query.IdeAnalysis.results:type_name -> ide_query.AnalysisResult\n\t6, // 2: ide_query.IdeAnalysis.units:type_name -> ide_query.BuildableUnit\n\t8, // 3: ide_query.AnalysisResult.status:type_name -> ide_query.AnalysisResult.Status\n\t7, // 4: ide_query.AnalysisResult.invalidation:type_name -> ide_query.Invalidation\n\t0, // 5: ide_query.BuildableUnit.language:type_name -> ide_query.Language\n\t2, // 6: ide_query.BuildableUnit.generated_files:type_name -> ide_query.GeneratedFile\n\t9, // 7: ide_query.Invalidation.wildcards:type_name -> ide_query.Invalidation.Wildcard\n\t1, // 8: ide_query.AnalysisResult.Status.code:type_name -> ide_query.AnalysisResult.Status.Code\n\t9, // [9:9] is the sub-list for method output_type\n\t9, // [9:9] is the sub-list for method input_type\n\t9, // [9:9] is the sub-list for extension type_name\n\t9, // [9:9] is the sub-list for extension extendee\n\t0, // [0:9] is the sub-list for field type_name\n}\n\nfunc init() { file_ide_query_proto_init() }\nfunc file_ide_query_proto_init() {\n\tif File_ide_query_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_ide_query_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GeneratedFile); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_ide_query_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*IdeAnalysis); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_ide_query_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AnalysisError); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_ide_query_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AnalysisResult); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_ide_query_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*BuildableUnit); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_ide_query_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Invalidation); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_ide_query_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AnalysisResult_Status); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_ide_query_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Invalidation_Wildcard); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\tfile_ide_query_proto_msgTypes[0].OneofWrappers = []interface{}{}\n\tfile_ide_query_proto_msgTypes[1].OneofWrappers = []interface{}{}\n\tfile_ide_query_proto_msgTypes[6].OneofWrappers = []interface{}{}\n\tfile_ide_query_proto_msgTypes[7].OneofWrappers = []interface{}{}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_ide_query_proto_rawDesc,\n\t\t\tNumEnums:      2,\n\t\t\tNumMessages:   8,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_ide_query_proto_goTypes,\n\t\tDependencyIndexes: file_ide_query_proto_depIdxs,\n\t\tEnumInfos:         file_ide_query_proto_enumTypes,\n\t\tMessageInfos:      file_ide_query_proto_msgTypes,\n\t}.Build()\n\tFile_ide_query_proto = out.File\n\tfile_ide_query_proto_rawDesc = nil\n\tfile_ide_query_proto_goTypes = nil\n\tfile_ide_query_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "tools/ide_query/ide_query_proto/ide_query.proto",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nsyntax = \"proto3\";\n\npackage ide_query;\n\noption go_package = \"ide_query/ide_query_proto\";\n\nmessage GeneratedFile {\n  // Path to the file relative to build_out_dir.\n  string path = 1;\n\n  // The text of the generated file, if not provided contents will be read\n  // from the path above in user's workstation.\n  optional bytes contents = 2;\n}\n\nmessage IdeAnalysis {\n  // Directory that contains build outputs generated by the build system.\n  // Relative to repository root.\n  string build_out_dir = 1;\n  // Working directory used by the build system.\n  // Relative to repository root.\n  string working_dir = 4;\n  // Only set if the whole query failed.\n  optional AnalysisError error = 5;\n  // List of results, one per queried file.\n  repeated AnalysisResult results = 6;\n  // List of buildable units directly or indirectly references by the results.\n  repeated BuildableUnit units = 7;\n\n  reserved 2, 3;\n}\n\nmessage AnalysisError {\n  // Human readable error message.\n  string error_message = 1;\n}\n\nmessage AnalysisResult {\n  // Path to the source file that was queried, relative to repository root.\n  string source_file_path = 1;\n  // Indicates the success/failure for the query.\n  message Status {\n    enum Code {\n      CODE_UNSPECIFIED = 0;\n      CODE_OK = 1;\n      CODE_NOT_FOUND = 2;  // no target or module found for the source file.\n      CODE_BUILD_FAILED = 3;\n    }\n    Code code = 1;\n    // Details about the status, might be displayed to user.\n    optional string status_message = 2;\n  }\n  // Represents status for this result. e.g. not part of the build graph.\n  Status status = 2;\n  // ID of buildable unit that contains the source file.\n  // The ide_query script can choose the most relevant unit from multiple\n  // options.\n  string unit_id = 3;\n  // Invalidation rule to check if the result is still valid.\n  Invalidation invalidation = 4;\n}\n\nenum Language {\n  LANGUAGE_UNSPECIFIED = 0;\n  LANGUAGE_JAVA = 1;  // also includes Kotlin\n  LANGUAGE_CPP = 2;\n}\n\nmessage BuildableUnit {\n  // Unique identifier of the buildable unit.\n  //\n  // Examples:\n  //   - Java: module or target name, e.g. \"framework-bluetooth\" or\n  //   \"//third_party/hamcrest:hamcrest_java\"\n  //   - C++: source file, e.g. \"path/to/file.cc\"\n  string id = 1;\n  // Language of the unit.\n  // Required for buildable units directly referenced by the AnalysisResult,\n  // e.g. the unit associated with the compilation stage for the source file.\n  Language language = 2;\n  // Source files that are part of this unit.\n  // Path to the file relative to working_dir.\n  repeated string source_file_paths = 3;\n  // Compiler arguments to compile the source files.\n  repeated string compiler_arguments = 4;\n  // List of generated files produced by this unit.\n  repeated GeneratedFile generated_files = 5;\n  // List of other BuildableUnits this unit depend on.\n  repeated string dependency_ids = 6;\n}\n\n// Invalidation rule to check if the result is still valid.\n// This should contain files/dirs that are not directly part of the build graph\n// but still affect the result. For example BUILD files, directory to the\n// toolchain or config files etc.\nmessage Invalidation {\n  // If any of these files change the result may become invalid.\n  // Path to the file relative to repository root.\n  repeated string file_paths = 1;\n\n  message Wildcard {\n    // Prefix of the file path (e.g. \"path/to/\")\n    optional string prefix = 1;\n    // Suffix of the file path (e.g. \"Android.bp\")\n    optional string suffix = 2;\n    // If false, the part of the path between the given `prefix` and `suffix`\n    // should not contain directory separators ('/').\n    optional bool can_cross_folder = 3;\n  }\n  // If any of these rules match a changed file the result may become invalid.\n  repeated Wildcard wildcards = 4;\n}"
  },
  {
    "path": "tools/ide_query/ide_query_proto/regen.sh",
    "content": "#!/bin/bash\n\naprotoc --go_out=paths=source_relative:. ide_query.proto\n"
  },
  {
    "path": "tools/ide_query/prober_scripts/cpp/Android.bp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"ide_query_proberscript_cc\",\n    srcs: [\n        \"general.cc\",\n        \"foo.proto\",\n    ],\n    cflags: [\"-Wno-unused-parameter\"],\n    proto: {\n        type: \"lite\",\n    },\n}\n"
  },
  {
    "path": "tools/ide_query/prober_scripts/cpp/foo.proto",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nsyntax = \"proto3\";\n\npackage ide_query.prober_scripts;\n\nmessage ProtoMsg {\n  // Test proto field.\n  int64 some_field = 1;\n  //                   ^ some_field\n}\n"
  },
  {
    "path": "tools/ide_query/prober_scripts/cpp/general.cc",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <vector>\n\n#include \"foo.pb.h\"\n\nusing ide_query::prober_scripts::ProtoMsg;\n\nvoid Foo(int x, double y) {}\nfloat Foo(float x, float y) { return 0.0f; }\n\nvoid TestCompletion() {\n  // Test completion on protos and fuzzy matching of completion suggestions.\n\n  ProtoMsg foo;\n\n  // ^\n\n  // step\n  // workspace.waitForReady()\n  // type(\"f\")\n  // completion.trigger()\n  // assert completion.items.filter(label=\"foo\")\n  // delline()\n  // type(\"foo.sf\")\n  // completion.trigger()\n  // assert completion.items.filter(\n  //  label=\"some_field.*\",\n  //  insertText=\"some_field.*\",\n  // )\n  // delline()\n\n  std::vector<int> v;\n\n  // ^\n\n  // step\n  // workspace.waitForReady()\n  // type(\"v.push\")\n  // completion.trigger()\n  // assert completion.items.filter(label=\"push_back.*\")\n  // delline()\n}\n\nvoid TestNavigation() {\n  std::vector<int> ints;\n  //               ^   ^ ints\n  //      ^\n\n  // step\n  // ; Test navigation to definition on STL types.\n  // workspace.waitForReady()\n  // navigation.trigger()\n  // assert navigation.items.filter(path=\".*/vector\")\n\n  ints.push_back(0);\n  // ^\n\n  // step\n  // ; Test navigation to definition on local symbols.\n  // workspace.waitForReady()\n  // navigation.trigger()\n  // assert navigation.items.filter(path=\".*/general.cc\", range=ints)\n\n  ProtoMsg msg;\n  msg.set_some_field(0);\n  //          ^\n\n  // step\n  // ; Test navigation to definition on proto fields. We do not check for a\n  // ; specific target as it can be in generated code.\n  // workspace.waitForReady()\n  // navigation.trigger()\n  // assert navigation.items\n}\n\nvoid TestParameterInfo() {\n  std::vector<int> v;\n  v.push_back(0);\n  //          ^\n\n  // step\n  // ; Test the signature help for STL functions. We do not check for a specific\n  // ; text as it can be implementation-dependent.\n  // workspace.waitForReady()\n  // paraminfo.trigger()\n  // assert paraminfo.items\n\n  Foo(0, 0.0);\n  //      ^\n\n  // step\n  // ; Test the signature help for the function 'Foo' having two overloads.\n  // workspace.waitForReady()\n  // paraminfo.trigger()\n  // assert paraminfo.items.filter(\n  //  active=true,\n  //  label=\"Foo\\\\(int x, double y\\\\) -> void\",\n  //  selection=\"double y\",\n  // )\n  // assert paraminfo.items.filter(\n  //  active=false,\n  //  label=\"Foo\\\\(float x, float y\\\\) -> float\",\n  // )\n}\n\nint main() { return 0; }\n"
  },
  {
    "path": "tools/ide_query/prober_scripts/cpp_suite.textpb",
    "content": "tests: {\n  name: \"general\"\n  scripts: \"build/make/tools/ide_query/prober_scripts/cpp/general.cc\"\n  scripts: \"build/make/tools/ide_query/prober_scripts/cpp/foo.proto\"\n}\n"
  },
  {
    "path": "tools/ide_query/prober_scripts/ide_query.out",
    "content": "\n\u0003out2x\n8build/make/tools/ide_query/prober_scripts/cpp/general.cc\u0012\u0002\b\u0001\u001a8build/make/tools/ide_query/prober_scripts/cpp/general.cc:\"\n8build/make/tools/ide_query/prober_scripts/cpp/general.cc\u0010\u0002\u001a8build/make/tools/ide_query/prober_scripts/cpp/general.cc\"8prebuilts/clang/host/linux-x86/clang-r530567/bin/clang++\"\f-nostdlibinc\"\u0007-mthumb\"\u0003-Os\"\u0014-fomit-frame-pointer\"\u0006-mllvm\"\u0019-enable-shrink-wrap=false\"\u0003-O2\"\u0005-Wall\"\u0007-Wextra\"\u000b-Winit-self\"\u000f-Wpointer-arith\"\u0018-Wunguarded-availability\"\u0011-Werror=date-time\"\u0016-Werror=int-conversion\"\u0013-Werror=pragma-pack\"&-Werror=pragma-pack-suspicious-include\"\u0018-Werror=sizeof-array-div\"\u0017-Werror=string-plus-int\"'-Werror=unreachable-code-loop-increment\"\"-Wno-error=deprecated-declarations\"\u0013-Wno-c23-extensions\"\u0013-Wno-c99-designator\"\u0019-Wno-gnu-folding-constant\"\"-Wno-inconsistent-missing-override\"\u001c-Wno-error=reorder-init-list\"\u0016-Wno-reorder-init-list\"\u0011-Wno-sign-compare\"\u000b-Wno-unused\"\t-DANDROID\"\b-DNDEBUG\"\u0007-UDEBUG\"(-D__compiler_offsetof=__builtin_offsetof\"*-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__\"\t-faddrsig\"\u0019-fdebug-default-version=5\"\u0013-fcolor-diagnostics\"\u0011-ffp-contract=off\"\u000f-fno-exceptions\"\u0014-fno-strict-aliasing\"\u0012-fmessage-length=0\"\u0017-gsimple-template-names\"\b-gz=zstd\"\u0016-no-canonical-prefixes\"\"-fdebug-prefix-map=/proc/self/cwd=\"\u001c-ftrivial-auto-var-init=zero\"\u0002-g\"\u0013-ffunction-sections\"\u000f-fdata-sections\"\u0010-fno-short-enums\"\u000f-funwind-tables\"\u0018-fstack-protector-strong\"\u0011-Wa,--noexecstack\"\u0013-D_FORTIFY_SOURCE=2\"\u0013-Wstrict-aliasing=2\"\u0013-Werror=return-type\"\u0018-Werror=non-virtual-dtor\"\u000f-Werror=address\"\u0016-Werror=sequence-point\"\u0017-Werror=format-security\"\f-msoft-float\"\u000e-march=armv7-a\"\u0012-mfloat-abi=softfp\"\n-mfpu=neon\"/-Ibuild/make/tools/ide_query/prober_scripts/cpp\"\u0001-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto/build/make/tools/ide_query/prober_scripts/cpp\"\u0001-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto\"\u0014-D__LIBC_API__=10000\"\u0014-D__LIBM_API__=10000\"\u0015-D__LIBDL_API__=10000\"\u0017-Iexternal/protobuf/src\"Y-Iprebuilts/clang/host/linux-x86/clang-r530567/android_libc++/platform/arm/include/c++/v1\"=-Iprebuilts/clang/host/linux-x86/clang-r530567/include/c++/v1\" -Ibionic/libc/async_safe/include\"\u001f-Isystem/logging/liblog/include\"'-Ibionic/libc/system_properties/include\"<-Isystem/core/property_service/libpropertyinfoparser/include\"\b-isystem\"\u0013bionic/libc/include\"\b-isystem\"\u001fbionic/libc/kernel/uapi/asm-arm\"\b-isystem\"\u0017bionic/libc/kernel/uapi\"\b-isystem\"\u001fbionic/libc/kernel/android/scsi\"\b-isystem\"\u001fbionic/libc/kernel/android/uapi\"\u0007-target\"\u001darmv7a-linux-androideabi10000\"\u0010-DANDROID_STRICT\"\u0005-fPIE\"\u0007-Werror\"\u0015-Wno-unused-parameter\"\u0019-DGOOGLE_PROTOBUF_NO_RTTI\"\u0016-Wimplicit-fallthrough\"*-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS\"\u0015-Wno-gnu-include-next\"\u001b-fvisibility-inlines-hidden\"\u0006-mllvm\"\u0019-enable-shrink-wrap=false\"\f-std=gnu++20\"\t-fno-rtti\"\u0015-Isystem/core/include\"\u001f-Isystem/logging/liblog/include\"\u001c-Isystem/media/audio/include\"\u001e-Ihardware/libhardware/include\"%-Ihardware/libhardware_legacy/include\"\u0016-Ihardware/ril/include\"\u001b-Iframeworks/native/include\"\"-Iframeworks/native/opengl/include\"\u0017-Iframeworks/av/include\"\u0016-Werror=bool-operation\" -Werror=format-insufficient-args\"%-Werror=implicit-int-float-conversion\"\u001b-Werror=int-in-bool-context\"\u001b-Werror=int-to-pointer-cast\"\u001b-Werror=pointer-to-int-cast\"\u0017-Werror=xor-used-as-pow\"\u001e-Wno-void-pointer-to-enum-cast\"\u001d-Wno-void-pointer-to-int-cast\"\u0018-Wno-pointer-to-int-cast\"\u0016-Werror=fortify-source\"\u0014-Wno-unused-variable\"\u001f-Wno-missing-field-initializers\"\u0013-Wno-packed-non-pod\"\u001c-Werror=address-of-temporary\"+-Werror=incompatible-function-pointer-types\"\u0018-Werror=null-dereference\"\u0013-Werror=return-type\"\"-Wno-tautological-constant-compare\"$-Wno-tautological-type-limit-compare\"\"-Wno-implicit-int-float-conversion\"!-Wno-tautological-overlap-compare\"\u0014-Wno-deprecated-copy\"\u0019-Wno-range-loop-construct\"\"-Wno-zero-as-null-pointer-constant\")-Wno-deprecated-anon-enum-enum-conversion\"$-Wno-deprecated-enum-enum-conversion\"\u001b-Wno-error=pessimizing-move\"\u001e-Wno-non-c-typedef-for-linkage\"\u0013-Wno-align-mismatch\"\"-Wno-error=unused-but-set-variable\"#-Wno-error=unused-but-set-parameter\"\u001e-Wno-error=deprecated-builtins\"\u0015-Wno-error=deprecated\"&-Wno-deprecated-dynamic-exception-spec\"$-Wno-error=enum-constexpr-conversion\"\u001b-Wno-error=invalid-offsetof\")-Wno-error=thread-safety-reference-return\"\u0016-Wno-vla-cxx-extension\"8build/make/tools/ide_query/prober_scripts/cpp/general.cc2Egenfiles_for_build/make/tools/ide_query/prober_scripts/cpp/general.cc:@\nEgenfiles_for_build/make/tools/ide_query/prober_scripts/cpp/general.cc*?\n\u0001soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto/build/make/tools/ide_query/prober_scripts/cpp/foo.pb.h\u0012>// Generated by the protocol buffer compiler.  DO NOT EDIT!\n// source: build/make/tools/ide_query/prober_scripts/cpp/foo.proto\n\n#ifndef GOOGLE_PROTOBUF_INCLUDED_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto\n#define GOOGLE_PROTOBUF_INCLUDED_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto\n\n#include <cstdint>\n#include <limits>\n#include <string>\n\n#include <google/protobuf/port_def.inc>\n#if PROTOBUF_VERSION < 3021000\n#error This file was generated by a newer version of protoc which is\n#error incompatible with your Protocol Buffer headers. Please update\n#error your headers.\n#endif\n#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION\n#error This file was generated by an older version of protoc which is\n#error incompatible with your Protocol Buffer headers. Please\n#error regenerate this file with a newer version of protoc.\n#endif\n\n#include <google/protobuf/port_undef.inc>\n#include <google/protobuf/io/coded_stream.h>\n#include <google/protobuf/arena.h>\n#include <google/protobuf/arenastring.h>\n#include <google/protobuf/generated_message_util.h>\n#include <google/protobuf/metadata_lite.h>\n#include <google/protobuf/message_lite.h>\n#include <google/protobuf/repeated_field.h>  // IWYU pragma: export\n#include <google/protobuf/extension_set.h>  // IWYU pragma: export\n// @@protoc_insertion_point(includes)\n#include <google/protobuf/port_def.inc>\n#define PROTOBUF_INTERNAL_EXPORT_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto\nPROTOBUF_NAMESPACE_OPEN\nnamespace internal {\nclass AnyMetadata;\n}  // namespace internal\nPROTOBUF_NAMESPACE_CLOSE\n\n// Internal implementation detail -- do not use these members.\nstruct TableStruct_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto {\n  static const ::uint32_t offsets[];\n};\nnamespace ide_query {\nnamespace prober_scripts {\nclass ProtoMsg;\nstruct ProtoMsgDefaultTypeInternal;\nextern ProtoMsgDefaultTypeInternal _ProtoMsg_default_instance_;\n}  // namespace prober_scripts\n}  // namespace ide_query\nPROTOBUF_NAMESPACE_OPEN\ntemplate<> ::ide_query::prober_scripts::ProtoMsg* Arena::CreateMaybeMessage<::ide_query::prober_scripts::ProtoMsg>(Arena*);\nPROTOBUF_NAMESPACE_CLOSE\nnamespace ide_query {\nnamespace prober_scripts {\n\n// ===================================================================\n\nclass ProtoMsg final :\n    public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:ide_query.prober_scripts.ProtoMsg) */ {\n public:\n  inline ProtoMsg() : ProtoMsg(nullptr) {}\n  ~ProtoMsg() override;\n  explicit PROTOBUF_CONSTEXPR ProtoMsg(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);\n\n  ProtoMsg(const ProtoMsg& from);\n  ProtoMsg(ProtoMsg&& from) noexcept\n    : ProtoMsg() {\n    *this = ::std::move(from);\n  }\n\n  inline ProtoMsg& operator=(const ProtoMsg& from) {\n    if (this == &from) return *this;\n    CopyFrom(from);\n    return *this;\n  }\n  inline ProtoMsg& operator=(ProtoMsg&& from) noexcept {\n    if (this == &from) return *this;\n    if (GetOwningArena() == from.GetOwningArena()\n  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE\n        && GetOwningArena() != nullptr\n  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE\n    ) {\n      InternalSwap(&from);\n    } else {\n      CopyFrom(from);\n    }\n    return *this;\n  }\n\n  static const ProtoMsg& default_instance() {\n    return *internal_default_instance();\n  }\n  static inline const ProtoMsg* internal_default_instance() {\n    return reinterpret_cast<const ProtoMsg*>(\n               &_ProtoMsg_default_instance_);\n  }\n  static constexpr int kIndexInFileMessages =\n    0;\n\n  friend void swap(ProtoMsg& a, ProtoMsg& b) {\n    a.Swap(&b);\n  }\n  inline void Swap(ProtoMsg* other) {\n    if (other == this) return;\n  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP\n    if (GetOwningArena() != nullptr &&\n        GetOwningArena() == other->GetOwningArena()) {\n   #else  // PROTOBUF_FORCE_COPY_IN_SWAP\n    if (GetOwningArena() == other->GetOwningArena()) {\n  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP\n      InternalSwap(other);\n    } else {\n      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);\n    }\n  }\n  void UnsafeArenaSwap(ProtoMsg* other) {\n    if (other == this) return;\n    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());\n    InternalSwap(other);\n  }\n\n  // implements Message ----------------------------------------------\n\n  ProtoMsg* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {\n    return CreateMaybeMessage<ProtoMsg>(arena);\n  }\n  ProtoMsg* New() const {\n    return New(nullptr);\n  }\n  void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)  final;\n  void CopyFrom(const ProtoMsg& from);\n  void MergeFrom(const ProtoMsg& from);\n  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;\n  bool IsInitialized() const final;\n\n  size_t ByteSizeLong() const final;\n  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;\n  ::uint8_t* _InternalSerialize(\n      ::uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;\n  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }\n\n  private:\n  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);\n  void SharedDtor();\n  void SetCachedSize(int size) const;\n  void InternalSwap(ProtoMsg* other);\n\n  private:\n  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;\n  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {\n    return \"ide_query.prober_scripts.ProtoMsg\";\n  }\n  protected:\n  explicit ProtoMsg(::PROTOBUF_NAMESPACE_ID::Arena* arena,\n                       bool is_message_owned = false);\n  public:\n\n  std::string GetTypeName() const final;\n\n  // nested types ----------------------------------------------------\n\n  // accessors -------------------------------------------------------\n\n  enum : int {\n    kSomeFieldFieldNumber = 1,\n  };\n  // int64 some_field = 1;\n  void clear_some_field();\n  ::int64_t some_field() const;\n  void set_some_field(::int64_t value);\n  private:\n  ::int64_t _internal_some_field() const;\n  void _internal_set_some_field(::int64_t value);\n  public:\n\n  // @@protoc_insertion_point(class_scope:ide_query.prober_scripts.ProtoMsg)\n private:\n  class _Internal;\n\n  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;\n  typedef void InternalArenaConstructable_;\n  typedef void DestructorSkippable_;\n  struct Impl_ {\n    ::int64_t some_field_;\n    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;\n  };\n  union { Impl_ _impl_; };\n  friend struct ::TableStruct_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto;\n};\n// ===================================================================\n\n\n// ===================================================================\n\n#ifdef __GNUC__\n  #pragma GCC diagnostic push\n  #pragma GCC diagnostic ignored \"-Wstrict-aliasing\"\n#endif  // __GNUC__\n// ProtoMsg\n\n// int64 some_field = 1;\ninline void ProtoMsg::clear_some_field() {\n  _impl_.some_field_ = ::int64_t{0};\n}\ninline ::int64_t ProtoMsg::_internal_some_field() const {\n  return _impl_.some_field_;\n}\ninline ::int64_t ProtoMsg::some_field() const {\n  // @@protoc_insertion_point(field_get:ide_query.prober_scripts.ProtoMsg.some_field)\n  return _internal_some_field();\n}\ninline void ProtoMsg::_internal_set_some_field(::int64_t value) {\n  \n  _impl_.some_field_ = value;\n}\ninline void ProtoMsg::set_some_field(::int64_t value) {\n  _internal_set_some_field(value);\n  // @@protoc_insertion_point(field_set:ide_query.prober_scripts.ProtoMsg.some_field)\n}\n\n#ifdef __GNUC__\n  #pragma GCC diagnostic pop\n#endif  // __GNUC__\n\n// @@protoc_insertion_point(namespace_scope)\n\n}  // namespace prober_scripts\n}  // namespace ide_query\n\n// @@protoc_insertion_point(global_scope)\n\n#include <google/protobuf/port_undef.inc>\n#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto\n"
  },
  {
    "path": "tools/ide_query/prober_scripts/jvm/Android.bp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\njava_library {\n    name: \"ide_query_proberscript_jvm\",\n    srcs: [\n        \"Foo.java\",\n        \"Bar.java\",\n        \"other/Other.java\",\n    ],\n}\n"
  },
  {
    "path": "tools/ide_query/prober_scripts/jvm/Bar.java",
    "content": "/*\n * Copyright 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jvm;\n\n/** Bar class. The class for testing code assist within the same build module. */\nclass Bar<K extends Number, V extends Number> {\n  Bar() {\n    foo(new Foo());\n  }\n\n  void foo(Foo f) {}\n\n  void foo(Object o) {}\n\n  void bar(Foo f) {}\n\n  void baz(Object o) {}\n}"
  },
  {
    "path": "tools/ide_query/prober_scripts/jvm/Foo.java",
    "content": "/*\n * Copyright 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jvm;\n\nimport jvm.other.Other;\n\n/** Foo class. */\npublic final class Foo {\n//               ^  ^ foo_def\n\n  void testParameterInfo() {\n    // Test signature help for type parameters.\n\n    Bar<Integer, Double> b = new Bar<>();\n    //                               ^ ctor\n    //     ^ decl_1\n    //              ^ decl_2\n    System.out.println(b);\n\n    // step at ctor\n    // workspace.waitForReady()\n    // paraminfo.trigger()\n    // assert paraminfo.items.filter(\n    //  label=\"K extends Number, V extends Number\",\n    //  selection=\"K extends Number\",\n    // )\n\n    // step at decl_1\n    // workspace.waitForReady()\n    // paraminfo.trigger()\n    // assert paraminfo.items.filter(\n    //  label=\"K extends Number, V extends Number\",\n    //  selection=\"K extends Number\",\n    // )\n\n    // step at decl_2\n    // workspace.waitForReady()\n    // paraminfo.trigger()\n    // assert paraminfo.items.filter(\n    //  label=\"K extends Number, V extends Number\",\n    //  selection=\"V extends Number\",\n    // )\n\n    // Test signature help for constructor parameters.\n\n    Other other = new Other(123, \"foo\");\n    //                       ^ param_1\n    //                             ^ param_2\n    System.out.println(other);\n\n    // step at param_1\n    // workspace.waitForReady()\n    // paraminfo.trigger()\n    // assert paraminfo.items.filter(\n    //  label=\"\\\\(int first, String second\\\\)\",\n    //  selection=\"int first\",\n    // )\n\n    // step at param_2\n    // workspace.waitForReady()\n    // paraminfo.trigger()\n    // assert paraminfo.items.empty()\n  }\n\n  void testCompletion() {\n    Bar<Integer, Double> b = new Bar<>();\n    System.out.println(b);\n\n    // ^\n\n    // step\n    // ; Test completion on types from the same package.\n    // workspace.waitForReady()\n    // type(\"b.\")\n    // completion.trigger()\n    // assert completion.items.filter(label=\"foo.*\")\n    // delline()\n\n    Other other = new Other(1, \"foo\");\n    System.out.println(other);\n\n    // ^\n\n    // step\n    // ; Test completion on types from a different package.\n    // workspace.waitForReady()\n    // type(\"other.\")\n    // completion.trigger()\n    // apply(completion.items.filter(label=\"other.*\").first())\n    // type(\".\")\n    // completion.trigger()\n    // apply(completion.items.filter(label=\"other.*\").first())\n    // delline()\n  }\n\n  void testDiagnostics() {\n\n    // ^\n\n    // step\n    // ; Test diagnostics about wrong type argument bounds.\n    // workspace.waitForReady()\n    // type(\"Bar<String, Double> b;\")\n    // assert diagnostics.items.filter(\n    //  message=\"type argument .* is not within bounds .*\",\n    //  code=\"compiler.err.not.within.bounds\",\n    // )\n    // delline()\n  }\n}\n"
  },
  {
    "path": "tools/ide_query/prober_scripts/jvm/ide_query.out",
    "content": "\n\u0003out2X\n6build/make/tools/ide_query/prober_scripts/jvm/Foo.java\u0012\u0002\b\u0001\u001a\u001aide_query_proberscript_jvm:\u0001\n\u001aide_query_proberscript_jvm\u0010\u0001\u001a6build/make/tools/ide_query/prober_scripts/jvm/Foo.java\u001a6build/make/tools/ide_query/prober_scripts/jvm/Bar.java\u001a>build/make/tools/ide_query/prober_scripts/jvm/other/Other.java"
  },
  {
    "path": "tools/ide_query/prober_scripts/jvm/other/Other.java",
    "content": "/*\n * Copyright 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage jvm.other;\n\n/** Other class */\npublic class Other {\n  public Other(int first, String second) {}\n\n  public Other other() {\n    return new Other(0, \"\");\n  }\n}\n"
  },
  {
    "path": "tools/ide_query/prober_scripts/jvm/suite.textpb",
    "content": "tests: {\n  name: \"general\"\n  scripts: \"build/make/tools/ide_query/prober_scripts/jvm/Foo.java\"\n}\n"
  },
  {
    "path": "tools/ide_query/prober_scripts/regen.sh",
    "content": "#!/bin/bash -e\n\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This script is used to generate the ide_query.out file.\n#\n# The ide_query.out file is a pre-computed result of running ide_query.sh\n# on a set of files. This allows the prober to run its tests without running\n# ide_query.sh. The prober doesn't check-out the full source code, so it\n# can't run ide_query.sh itself.\n\nfiles_to_build=(\n  build/make/tools/ide_query/prober_scripts/cpp/general.cc\n)\n\nbuild/make/tools/ide_query/ide_query.sh --lunch_target=aosp_arm-trunk_staging-eng ${files_to_build[@]} > build/make/tools/ide_query/prober_scripts/ide_query.out\n"
  },
  {
    "path": "tools/java-event-log-tags.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nGenerate a java class containing constants for each of the event log\ntags in the given input file.\n\"\"\"\n\nfrom io import StringIO\nimport argparse\nimport os\nimport os.path\nimport re\nimport sys\n\nimport event_log_tags\n\nparser = argparse.ArgumentParser(description=__doc__)\nparser.add_argument('-o', dest='output_file')\nparser.add_argument('file')\nargs = parser.parse_args()\n\nfn = args.file\ntagfile = event_log_tags.TagFile(fn)\n\nif \"java_package\" not in tagfile.options:\n  tagfile.AddError(\"java_package option not specified\", linenum=0)\n\nhide = True\nif \"javadoc_hide\" in tagfile.options:\n  hide = event_log_tags.BooleanFromString(tagfile.options[\"javadoc_hide\"][0])\n\nif tagfile.errors:\n  for fn, ln, msg in tagfile.errors:\n    print(\"%s:%d: error: %s\" % (fn, ln, msg), file=sys.stderr)\n  sys.exit(1)\n\nbuffer = StringIO()\nbuffer.write(\"/* This file is auto-generated.  DO NOT MODIFY.\\n\"\n             \" * Source file: %s\\n\"\n             \" */\\n\\n\" % (fn,))\n\n# .rstrip(\";\") to avoid an empty top-level statement errorprone error\nbuffer.write(\"package %s;\\n\\n\" % (tagfile.options[\"java_package\"][0].rstrip(\";\"),))\n\nbasename, _ = os.path.splitext(os.path.basename(fn))\n\nif hide:\n  buffer.write(\"/**\\n\"\n               \" * @hide\\n\"\n               \" */\\n\")\nbuffer.write(\"public class %s {\\n\" % (basename,))\nbuffer.write(\"  private %s() { }  // don't instantiate\\n\" % (basename,))\n\nfor t in tagfile.tags:\n  if t.description:\n    buffer.write(\"\\n  /** %d %s %s */\\n\" % (t.tagnum, t.tagname, t.description))\n  else:\n    buffer.write(\"\\n  /** %d %s */\\n\" % (t.tagnum, t.tagname))\n\n  buffer.write(\"  public static final int %s = %d;\\n\" %\n               (t.tagname.upper(), t.tagnum))\n\nkeywords = frozenset([\"abstract\", \"continue\", \"for\", \"new\", \"switch\", \"assert\",\n                      \"default\", \"goto\", \"package\", \"synchronized\", \"boolean\",\n                      \"do\", \"if\", \"private\", \"this\", \"break\", \"double\",\n                      \"implements\", \"protected\", \"throw\", \"byte\", \"else\",\n                      \"import\", \"public\", \"throws\", \"case\", \"enum\",\n                      \"instanceof\", \"return\", \"transient\", \"catch\", \"extends\",\n                      \"int\", \"short\", \"try\", \"char\", \"final\", \"interface\",\n                      \"static\", \"void\", \"class\", \"finally\", \"long\", \"strictfp\",\n                      \"volatile\", \"const\", \"float\", \"native\", \"super\", \"while\"])\n\ndef javaName(name):\n  out = name[0].lower() + re.sub(r\"[^A-Za-z0-9]\", \"\", name.title())[1:]\n  if out in keywords:\n    out += \"_\"\n  return out\n\njavaTypes = [\"ERROR\", \"int\", \"long\", \"String\", \"Object[]\", \"float\"]\nfor t in tagfile.tags:\n  methodName = javaName(\"write_\" + t.tagname)\n  if t.description:\n    fn_args = [arg.strip(\"() \").split(\"|\") for arg in t.description.split(\",\")]\n  else:\n    fn_args = []\n  argTypesNames = \", \".join([javaTypes[int(arg[1])] + \" \" + javaName(arg[0]) for arg in fn_args])\n  argNames = \"\".join([\", \" + javaName(arg[0]) for arg in fn_args])\n  buffer.write(\"\\n  public static void %s(%s) {\" % (methodName, argTypesNames))\n  buffer.write(\"\\n    android.util.EventLog.writeEvent(%s%s);\" % (t.tagname.upper(), argNames))\n  buffer.write(\"\\n  }\\n\")\n\n\nbuffer.write(\"}\\n\");\n\noutput_dir = os.path.dirname(args.output_file)\nif not os.path.exists(output_dir):\n  os.makedirs(output_dir)\n\nevent_log_tags.WriteOutput(args.output_file, buffer)\n"
  },
  {
    "path": "tools/libhost/Android.bp",
    "content": "package {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_host_static {\n\n    srcs: [\"CopyFile.c\"],\n\n    cflags: [\n        \"-Werror\",\n        \"-Wall\",\n    ],\n\n    name: \"libhost\",\n    target: {\n        windows: {\n            cflags: [\"-Wno-unused-parameter\"],\n            enabled: true,\n        },\n    },\n    local_include_dirs: [\"include\"],\n    export_include_dirs: [\"include\"],\n    stl: \"none\",\n\n}\n"
  },
  {
    "path": "tools/libhost/CopyFile.c",
    "content": "/*\n * Copyright 2005 The Android Open Source Project\n *\n * Android \"cp\" replacement.\n *\n * The GNU/Linux \"cp\" uses O_LARGEFILE in its open() calls, utimes() instead\n * of utime(), and getxattr()/setxattr() instead of chmod().  These are\n * probably \"better\", but are non-portable, and not necessary for our\n * purposes.\n */\n#include <host/CopyFile.h>\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <getopt.h>\n#include <dirent.h>\n#include <fcntl.h>\n#include <utime.h>\n#include <limits.h>\n#include <errno.h>\n#include <assert.h>\n\n#if defined(_WIN32)\n#include <direct.h>  /* For _mkdir() */\n#  define mkdir(path,mode)   _mkdir(path)\n#  define S_ISLNK(s) 0\n#  define lstat stat\n#  ifndef EACCESS   /* seems to be missing from the Mingw headers */\n#    define  EACCESS            13\n#  endif\n#endif\n\n#ifndef O_BINARY\n#  define  O_BINARY  0\n#endif\n\n/*#define DEBUG_MSGS*/\n#ifdef DEBUG_MSGS\n# define DBUG(x) printf x\n#else\n# define DBUG(x) ((void)0)\n#endif\n\n#define FSSEP '/'       /* filename separator char */\n\nstatic int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options);\n\n/*\n * Returns true if the source file is newer than the destination file.\n *\n * The check is based on the modification date, whole seconds only.  This\n * also returns true if the file sizes don't match.\n */\nstatic bool isSourceNewer(const struct stat* pSrcStat, const struct stat* pDstStat)\n{\n    return (pSrcStat->st_mtime > pDstStat->st_mtime) ||\n           (pSrcStat->st_size != pDstStat->st_size);\n}\n\n/*\n * Returns true if the source file has high resolution modification\n * date. Cygwin/Mingw doesn't support st_mtim and always returns false.\n */\nstatic bool isHiresMtime(const struct stat* pSrcStat)\n{\n#if defined(_WIN32)\n    return 0;\n#elif defined(__APPLE__)\n    return pSrcStat->st_mtimespec.tv_nsec > 0;\n#else\n    return pSrcStat->st_mtim.tv_nsec > 0;\n#endif\n}\n\n/*\n * Returns true if the source and destination files are actually the\n * same thing.  We detect this by checking the inode numbers, which seems\n * to work on Cygwin.\n */\nstatic bool isSameFile(const struct stat* pSrcStat, const struct stat* pDstStat)\n{\n#if defined(_WIN32)\n  (void)pSrcStat;\n  (void)pDstStat;\n    /* with MSVCRT.DLL, stat always sets st_ino to 0, and there is no simple way to */\n\t/* get the equivalent information with Win32 (Cygwin does some weird stuff in   */\n\t/* its winsup/cygwin/fhandler_disk_file.cc to emulate this, too complex for us) */\n\treturn 0;\n#else\n    return (pSrcStat->st_ino == pDstStat->st_ino);\n#endif\n}\n\nstatic void printCopyMsg(const char* src, const char* dst, unsigned int options)\n{\n    if ((options & COPY_VERBOSE_MASK) > 0)\n        printf(\"    '%s' --> '%s'\\n\", src, dst);\n}\n\nstatic void printNotNewerMsg(const char* src, const char* dst, unsigned int options)\n{\n    (void)src;\n    if ((options & COPY_VERBOSE_MASK) > 1)\n        printf(\"    '%s' is up-to-date\\n\", dst);\n}\n\n/*\n * Copy the contents of one file to another.\n *\n * The files are assumed to be seeked to the start.\n */\nstatic int copyFileContents(const char* dst, int dstFd, const char* src, int srcFd)\n{\n    unsigned char buf[8192];\n    ssize_t readCount, writeCount;\n\n    /*\n     * Read a chunk, write it, and repeat.\n     */\n    while (1) {\n        readCount = read(srcFd, buf, sizeof(buf));\n        if (readCount < 0) {\n            fprintf(stderr,\n                \"acp: failed reading '%s': %s\\n\", src, strerror(errno));\n            return -1;\n        }\n\n        if (readCount > 0) {\n            writeCount = write(dstFd, buf, readCount);\n            if (writeCount < 0) {\n                fprintf(stderr,\n                    \"acp: failed writing '%s': %s\\n\", dst, strerror(errno));\n                return -1;\n            }\n            if (writeCount != readCount) {\n                fprintf(stderr, \"acp: partial write to '%s' (%zd of %zd)\\n\",\n                    dst, writeCount, readCount);\n                return -1;\n            }\n        }\n\n        if (readCount < (ssize_t) sizeof(buf))\n            break;\n    }\n\n    return 0;\n}\n\n/*\n * Set the permissions, owner, and timestamps on the destination file\n * equal to those of the source file.\n *\n * Failures here are \"soft\"; they don't produce warning messages and don't\n * cause the cp command to report a failure.\n */\nstatic int setPermissions(const char* dst, const struct stat* pSrcStat, unsigned int options)\n{\n    struct utimbuf ut;\n\n    if (options & COPY_TIMESTAMPS) {\n        /*\n         * Start with timestamps.  The access and mod dates are not affected\n         * by the next operations.\n         */\n        ut.actime = pSrcStat->st_atime;\n        ut.modtime = pSrcStat->st_mtime;\n        if (isHiresMtime(pSrcStat))\n            ut.modtime += 1;\n        if (utime(dst, &ut) != 0) {\n            DBUG((\"---   unable to set timestamps on '%s': %s\\n\",\n                dst, strerror(errno)));\n        }\n    }\n\n    if (options & COPY_PERMISSIONS) {\n        /*\n         * Set the permissions.\n         */\n        if (chmod(dst, pSrcStat->st_mode & ~(S_IFMT)) != 0) {\n            DBUG((\"---   unable to set perms on '%s' to 0%o: %s\\n\",\n                dst, pSrcStat->st_mode & ~(S_IFMT), strerror(errno)));\n        }\n#ifndef _WIN32\n        /*\n         * Set the owner.\n         */\n        if (chown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0) {\n            DBUG((\"---   unable to set owner of '%s' to %d/%d: %s\\n\",\n                dst, pSrcStat->st_uid, pSrcStat->st_gid, strerror(errno)));\n        }\n#endif\n    }\n\n    return 0;\n}\n\n/*\n * Copy a regular file.  If the destination file exists and is not a\n * regular file, we fail.  However, we use stat() rather than lstat(),\n * because it's okay to write through a symlink (the noDereference stuff\n * only applies to the source file).\n *\n * If the file doesn't exist, create it.  If it does exist, truncate it.\n */\nstatic int copyRegular(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)\n{\n    struct stat dstStat;\n    int srcFd, dstFd, statResult, copyResult;\n\n    DBUG((\"--- copying regular '%s' to '%s'\\n\", src, dst));\n\n    statResult = stat(dst, &dstStat);\n    if (statResult == 0 && !S_ISREG(dstStat.st_mode)) {\n        fprintf(stderr,\n            \"acp: destination '%s' exists and is not regular file\\n\",\n            dst);\n        return -1;\n    } else if (statResult != 0 && errno != ENOENT) {\n        fprintf(stderr, \"acp: unable to stat destination '%s'\\n\", dst);\n        return -1;\n    }\n\n    if (statResult == 0) {\n        if (isSameFile(pSrcStat, &dstStat)) {\n            fprintf(stderr, \"acp: '%s' and '%s' are the same file\\n\",\n                src, dst);\n            return -1;\n        }\n        if (options & COPY_UPDATE_ONLY) {\n            if (!isSourceNewer(pSrcStat, &dstStat)) {\n                DBUG((\"---  source is not newer: '%s'\\n\", src));\n                printNotNewerMsg(src, dst, options);\n                return 0;\n            }\n        }\n    }\n\n    /* open src */\n    srcFd = open(src, O_RDONLY | O_BINARY, 0);\n    if (srcFd < 0) {\n        fprintf(stderr, \"acp: unable to open '%s': %s\\n\", src, strerror(errno));\n        return -1;\n    }\n\n    /* open dest with O_CREAT | O_TRUNC */\n    DBUG((\"---  opening '%s'\\n\", dst));\n    dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);\n\n    if (dstFd < 0) {\n        if (errno == ENOENT) {\n            /* this happens if the target directory doesn't exist */\n            fprintf(stderr,\n                \"acp: cannot create '%s': %s\\n\", dst, strerror(errno));\n            (void) close(srcFd);\n            return -1;\n        }\n\n        /* if \"force\" is set, try removing the destination file and retry */\n        if (options & COPY_FORCE) {\n            if (unlink(dst) != 0) {\n#ifdef _WIN32\n\t\t\t\t/* MSVCRT.DLL unlink will fail with EACCESS if the file is set read-only */\n\t\t\t\t/* so try to change its mode, and unlink again                           */\n\t\t\t\tif (errno == EACCESS) {\n\t\t\t\t\tif (chmod(dst, S_IWRITE|S_IREAD) == 0 && unlink(dst) == 0)\n\t\t\t\t\t\tgoto Open_File;\n\t\t\t\t}\n#endif\t\t\n                fprintf(stderr, \"acp: unable to remove '%s': %s\\n\",\n                    dst, strerror(errno));\n                (void) close(srcFd);\n                return -1;\n            }\n#ifdef _WIN32\n        Open_File:\n#endif\t\t\t\n            dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);\n        }\n    }\n    if (dstFd < 0) {\n        fprintf(stderr, \"acp: unable to open '%s': %s\\n\",\n            dst, strerror(errno));\n        (void) close(srcFd);\n        return -1;\n    }\n\n    copyResult = copyFileContents(dst, dstFd, src, srcFd);\n\n    (void) close(srcFd);\n    (void) close(dstFd);\n    if (copyResult != 0)\n        return -1;\n\n#if defined(__APPLE__)\n    // Copy Mac OS X resource forks too.\n    {\n        char* srcRsrcName = NULL;\n        char* dstRsrcName = NULL;\n        struct stat rsrcStat;\n\n        srcRsrcName = malloc(strlen(src) + 5 + 1);\n        strcpy(srcRsrcName, src);\n        strcat(srcRsrcName, \"/rsrc\");\n\n        dstRsrcName = malloc(strlen(dst) + 5 + 1);\n        strcpy(dstRsrcName, dst);\n        strcat(dstRsrcName, \"/rsrc\");\n\n        if (stat(srcRsrcName, &rsrcStat) == 0 && rsrcStat.st_size > 0) {\n            DBUG((\"---  RSRC: %s --> %s\\n\", srcRsrcName, dstRsrcName));\n\n            srcFd = open(srcRsrcName, O_RDONLY);\n            dstFd = open(dstRsrcName, O_TRUNC | O_WRONLY, 0);\n            copyResult = -1;\n            if (srcFd >= 0 && dstFd >= 0) {\n                copyResult = copyFileContents(dstRsrcName, dstFd,\n                    srcRsrcName, srcFd);\n                (void) close(srcFd);\n                (void) close(dstFd);\n            }\n\n            if (copyResult != 0) {\n                free(srcRsrcName);\n                free(dstRsrcName);\n                return -1;\n            }\n        }\n\n        free(srcRsrcName);\n        free(dstRsrcName);\n    }\n#endif\n\n    setPermissions(dst, pSrcStat, options);\n\n    printCopyMsg(src, dst, options);\n\n    return 0;\n}\n\n\n/*\n * Copy a symlink.  This only happens if we're in \"no derefence\" mode,\n * in which we copy the links rather than the files that are pointed at.\n *\n * We always discard the destination file.  If it's a symlink already,\n * we want to throw it out and replace it.  If it's not a symlink, we\n * need to trash it so we can create one.\n */\n#if defined(_WIN32)\nextern int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)\n#ifdef __clang__\n  __attribute__((unavailable(\"no symlinks on Windows\")));\n#else\n  __attribute__((error(\"no symlinks on Windows\")));\n#endif\n#else\nstatic int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)\n{\n    struct stat dstStat;\n    char linkBuf[PATH_MAX+1];\n    int statResult, nameLen;\n\n    assert(options & COPY_NO_DEREFERENCE);\n    DBUG((\"--- copying symlink '%s' to '%s'\\n\", src, dst));\n\n    /* NOTE: we use lstat() here */\n    statResult = lstat(dst, &dstStat);\n    if (statResult == 0 && !S_ISREG(dstStat.st_mode)\n                         && !S_ISLNK(dstStat.st_mode)\n\t\t\t\t\t\t )\n    {\n        fprintf(stderr,\n            \"acp: destination '%s' exists and is not regular or symlink\\n\",\n            dst);\n        return -1;\n    }\n\n    if (statResult == 0) {\n        if (isSameFile(pSrcStat, &dstStat)) {\n            fprintf(stderr, \"acp: '%s' and '%s' are the same file\\n\",\n                src, dst);\n            return -1;\n        }\n        if (options & COPY_UPDATE_ONLY) {\n            if (!isSourceNewer(pSrcStat, &dstStat)) {\n                DBUG((\"---  source is not newer: '%s'\\n\", src));\n                printNotNewerMsg(src, dst, options);\n                return 0;\n            }\n        }\n    }\n\n    /* extract the symlink contents */\n    nameLen = readlink(src, linkBuf, sizeof(linkBuf)-1);\n    if (nameLen <= 0) {\n        fprintf(stderr, \"acp: unable to read symlink '%s': %s\\n\",\n            src, strerror(errno));\n        return -1;\n    }\n    linkBuf[nameLen] = '\\0';\n    DBUG((\"--- creating symlink file '%s' (--> %s)\\n\", dst, linkBuf));\n\n    if (statResult == 0) {\n        DBUG((\"---  removing '%s'\\n\", dst));\n        if (unlink(dst) != 0) {\n            fprintf(stderr, \"acp: unable to remove '%s': %s\\n\",\n                dst, strerror(errno));\n            return -1;\n        }\n    }\n\n    if (symlink(linkBuf, dst) != 0) {\n        fprintf(stderr, \"acp: unable to create symlink '%s' [%s]: %s\\n\",\n            dst, linkBuf, strerror(errno));\n        return -1;\n    }\n\n    /*\n     * There's no way to set the file date or access permissions, but\n     * it is possible to set the owner.\n     */\n    if (options & COPY_PERMISSIONS) {\n        if (lchown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0)\n            DBUG((\"---  lchown failed: %s\\n\", strerror(errno)));\n    }\n\n    printCopyMsg(src, dst, options);\n\n    return 0;\n}\n#endif\n\n/*\n * Copy the contents of one directory to another.  Both \"src\" and \"dst\"\n * must be directories.  We will create \"dst\" if it does not exist.\n */\nint copyDirectory(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)\n{\n    int retVal = 0;\n    struct stat dstStat;\n    DIR* dir;\n    int cc, statResult;\n\n    DBUG((\"--- copy dir '%s' to '%s'\\n\", src, dst));\n\n    statResult = stat(dst, &dstStat);\n    if (statResult == 0 && !S_ISDIR(dstStat.st_mode)) {\n        fprintf(stderr,\n            \"acp: destination '%s' exists and is not a directory\\n\", dst);\n        return -1;\n    } else if (statResult != 0 && errno != ENOENT) {\n        fprintf(stderr, \"acp: unable to stat destination '%s'\\n\", dst);\n        return -1;\n    }\n\n    if (statResult == 0) {\n        if (isSameFile(pSrcStat, &dstStat)) {\n            fprintf(stderr,\n                \"acp: cannot copy directory into itself ('%s' and '%s')\\n\",\n                src, dst);\n            return -1;\n        }\n    } else {\n        DBUG((\"---  creating dir '%s'\\n\", dst));\n        cc = mkdir(dst, 0755);\n        if (cc != 0) {\n            fprintf(stderr, \"acp: unable to create directory '%s': %s\\n\",\n                dst, strerror(errno));\n            return -1;\n        }\n\n        /* only print on mkdir */\n        printCopyMsg(src, dst, options);\n    }\n\n    /*\n     * Open the directory, and plow through its contents.\n     */\n    dir = opendir(src);\n    if (dir == NULL) {\n        fprintf(stderr, \"acp: unable to open directory '%s': %s\\n\",\n            src, strerror(errno));\n        return -1;\n    }\n\n    while (1) {\n        struct dirent* ent;\n        char* srcFile;\n        char* dstFile;\n        int srcLen, dstLen, nameLen;\n\n        ent = readdir(dir);\n        if (ent == NULL)\n            break;\n\n        if (strcmp(ent->d_name, \".\") == 0 ||\n            strcmp(ent->d_name, \"..\") == 0)\n        {\n            continue;\n        }\n\n        nameLen = strlen(ent->d_name);\n        srcLen = strlen(src);\n        dstLen = strlen(dst);\n\n        srcFile = malloc(srcLen +1 + nameLen +1);\n        memcpy(srcFile, src, srcLen);\n        srcFile[srcLen] = FSSEP;\n        memcpy(srcFile + srcLen+1, ent->d_name, nameLen +1);\n\n        dstFile = malloc(dstLen +1 + nameLen +1);\n        memcpy(dstFile, dst, dstLen);\n        dstFile[dstLen] = FSSEP;\n        memcpy(dstFile + dstLen+1, ent->d_name, nameLen +1);\n\n        if (copyFileRecursive(srcFile, dstFile, false, options) != 0)\n            retVal = -1;        /* note failure and keep going */\n\n        free(srcFile);\n        free(dstFile);\n    }\n    closedir(dir);\n\n    setPermissions(dst, pSrcStat, options);\n\n    return retVal;\n}\n\n/*\n * Do the actual copy.  This is called recursively from copyDirectory().\n *\n * \"dst\" should only be a directory if \"src\" is also a directory.\n *\n * Returns 0 on success.\n */\nstatic int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options)\n{\n    char* srcExe = NULL;\n    char* dstExe = NULL;\n    char* dstDir = NULL;\n    struct stat srcStat;\n    int retVal = 0;\n    int statResult, statErrno;\n    (void)isCmdLine;\n\n    /*\n     * Stat the source file.  If it doesn't exist, fail.\n     */\n    if (options & COPY_NO_DEREFERENCE)\n        statResult = lstat(src, &srcStat);\n    else\n        statResult = stat(src, &srcStat);\n    statErrno = errno;        /* preserve across .exe attempt */\n\n    if (statResult < 0) {\n        if (statErrno == ENOENT)\n            fprintf(stderr, \"acp: file '%s' does not exist\\n\", src);\n        else\n            fprintf(stderr, \"acp: unable to stat '%s': %s\\n\",\n                src, strerror(statErrno));\n        retVal = -1;\n        goto bail;\n    }\n\n    /*\n     * If \"src\" is a directory, ignore it if \"recursive\" isn't set.\n     *\n     * We want to create \"dst\" as a directory (or verify that it already\n     * exists as a directory), and then copy its contents.\n     */\n    if (S_ISDIR(srcStat.st_mode)) {\n        if (!(options & COPY_RECURSIVE)) {\n            fprintf(stderr, \"acp: omitting directory '%s'\\n\", src);\n        } else {\n            retVal = copyDirectory(src, dst, &srcStat, options);\n        }\n#if !defined(_WIN32)\n    } else if (S_ISLNK(srcStat.st_mode)) {\n        retVal = copySymlink(src, dst, &srcStat, options);\n#endif\n    } else if (S_ISREG(srcStat.st_mode)) {\n        retVal = copyRegular(src, dst, &srcStat, options);\n    } else {\n        fprintf(stderr, \"acp: skipping unusual file '%s' (mode=0%o)\\n\",\n            src, srcStat.st_mode);\n        retVal = -1;\n    }\n\nbail:\n    free(srcExe);\n    free(dstExe);\n    free(dstDir);\n    return retVal;\n}\n\nint copyFile(const char* src, const char* dst, unsigned int options)\n{\n    return copyFileRecursive(src, dst, true, options);\n}\n\n\n"
  },
  {
    "path": "tools/libhost/include/host/CopyFile.h",
    "content": "#ifndef _HOST_COPYFILE_H\n#define _HOST_COPYFILE_H\n\n#include <stdbool.h>\n#include <sys/stat.h>\n\n#if __cplusplus\nextern \"C\" {\n#endif\n\n// command line options\nenum {\n    COPY_NO_DEREFERENCE = 0x00010000, // copy symlink link instead of target\n    COPY_TRY_EXE        = 0x00020000, // on Win32, try adding '.exe' to filename\n    COPY_FORCE          = 0x00040000, // override access permissions\n    COPY_PERMISSIONS    = 0x00080000, // preserve mode, ownership, timestamps\n    COPY_TIMESTAMPS     = 0x00100000, // preserve mode, ownership, timestamps\n    COPY_RECURSIVE      = 0x00200000, // copy directories\n    COPY_UPDATE_ONLY    = 0x00400000, // only copy if source file is newer\n    COPY_VERBOSE_MASK   = 0x000000ff  // talk lots\n};\n\nint copyFile(const char* src, const char* dst, unsigned int options);\n\n#if __cplusplus\n} // extern \"C\"\n#endif\n\n#endif // _HOST_COPYFILE_H\n\n"
  },
  {
    "path": "tools/list_files.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import List\nfrom glob import glob\nfrom pathlib import Path\nfrom os.path import join, relpath\nfrom itertools import chain\nimport argparse\n\nclass FileLister:\n    def __init__(self, args) -> None:\n        self.out_file = args.out_file\n\n        self.folder_dir = args.dir\n        self.extensions = [e if e.startswith(\".\") else \".\" + e for e in args.extensions]\n        self.root = args.root\n        self.files_list : List[str] = list()\n        self.classes = args.classes\n\n    def get_files(self) -> None:\n        \"\"\"Get all files directory in the input directory including the files in the subdirectories\n\n        Recursively finds all files in the input directory.\n        Set file_list as a list of file directory strings,\n        which do not include directories but only files.\n        List is sorted in alphabetical order of the file directories.\n\n        Args:\n            dir: Directory to get the files. String.\n\n        Raises:\n            FileNotFoundError: An error occurred accessing the non-existing directory\n        \"\"\"\n\n        if not dir_exists(self.folder_dir):\n            raise FileNotFoundError(f\"Directory {self.folder_dir} does not exist\")\n\n        if self.folder_dir[:-2] != \"**\":\n            self.folder_dir = join(self.folder_dir, \"**\")\n\n        self.files_list = list()\n        for file in sorted(glob(self.folder_dir, recursive=True)):\n            if Path(file).is_file():\n                if self.root:\n                    file = join(self.root, relpath(file, self.folder_dir[:-2]))\n                self.files_list.append(file)\n\n\n    def list(self) -> None:\n        self.get_files()\n        self.files_list = [f for f in self.files_list if not self.extensions or Path(f).suffix in self.extensions]\n\n        # If files_list is as below:\n        # A/B/C.java\n        # A/B/D.java\n        # A/B/E.txt\n        # --classes flag converts files_list in the following format:\n        # A/B/C.class\n        # A/B/C$*.class\n        # A/B/D.class\n        # A/B/D$*.class\n        # Additional `$*`-suffixed line is appended after each line\n        # to take multiple top level classes in a single java file into account.\n        # Note that non-java files in files_list are filtered out.\n        if self.classes:\n            self.files_list = list(chain.from_iterable([\n                (class_files := str(Path(ff).with_suffix(\".class\")),\n                 class_files.replace(\".class\", \"$*.class\"))\n                 for ff in self.files_list if ff.endswith(\".java\")\n            ]))\n\n        self.write()\n\n    def write(self) -> None:\n        if self.out_file == \"\":\n            pprint(self.files_list)\n        else:\n            write_lines(self.out_file, self.files_list)\n\n###\n# Helper functions\n###\ndef pprint(l: List[str]) -> None:\n    for line in l:\n        print(line)\n\ndef dir_exists(dir: str) -> bool:\n    return Path(dir).exists()\n\ndef write_lines(out_file: str, lines: List[str]) -> None:\n    with open(out_file, \"w+\") as f:\n        f.writelines(line + '\\n' for line in lines)\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n    parser.add_argument('dir', action='store', type=str,\n                        help=\"directory to list all subdirectory files\")\n    parser.add_argument('--out', dest='out_file',\n                        action='store', default=\"\", type=str,\n                        help=\"optional directory to write subdirectory files. If not set, will print to console\")\n    parser.add_argument('--root', dest='root',\n                        action='store', default=\"\", type=str,\n                        help=\"optional directory to replace the root directories of output.\")\n    parser.add_argument('--extensions', nargs='*', default=list(), dest='extensions',\n                        help=\"Extensions to include in the output. If not set, all files are included\")\n    parser.add_argument('--classes', dest='classes', action=argparse.BooleanOptionalAction,\n                        help=\"Optional flag. If passed, outputs a list of pattern of class files \\\n                                that will be produced by compiling java files in the input dir. \\\n                                Non-java files in the input directory will be ignored.\")\n\n    args = parser.parse_args()\n\n    file_lister = FileLister(args)\n    file_lister.list()\n"
  },
  {
    "path": "tools/lunchable",
    "content": "#!/bin/bash\n\n# TODO: Currently only checks trunk_staging. Should check trunk_staging first,\n#       then use the product-specfic releases. Only applies to -c though.\n\nfunction Help() {\ncat <<@EOF@\nUsage: lunchable [options]\n\nLists products that have no functioning lunch combo.\n\noptions:\n-c    prints all failing lunch combos for all targets;\n-w    why? Prints the error message after each failed lunch combo. Only\n      works with -c\n\n@EOF@\n}\n\ncomplete=0\nwhy=0\nwhile getopts \"cwh\" option; do\n  case $option in\n    c)\n      complete=1;;\n    w)\n      why=1;;\n    h)\n      Help\n      exit;;\n  esac\ndone\n\n# Getting all named products can fail if we haven't lunched anything\nsource $(pwd)/build/envsetup.sh &> /dev/null\nall_named_products=( $(get_build_var all_named_products 2> /dev/null) )\nif [[ $? -ne 0 ]]; then\n  echo \"get_build_var all_named_products failed. Lunch something first?\" >&2\n  exit 1\nfi\ntotal_products=${#all_named_products[@]}\ncurrent_product=0\n\nfor product in \"${all_named_products[@]}\"; do\n  (( current_product += 1 ))\n  single_pass=0\n  printf \" Checking ${current_product}/${total_products} \\r\" >&2\n  for release in trunk_staging; do\n    for variant in eng user userdebug; do\n      lunchcombo=\"${product}-${release}-${variant}\"\n      lunch_error=\"$(lunch $lunchcombo 2>&1 > /dev/null)\"\n      if [[ $? -ne 0 ]]; then\n        # Lunch failed\n        if [[ $complete -eq 1 ]]; then\n          echo -e \"${product} : ${lunchcombo}\"\n          if [[ $why -eq 1 ]]; then\n            echo -e \"$(sed 's/^/    /g' <<<$lunch_error)\"\n          fi\n        fi\n      elif [[ $complete -ne 1 ]]; then\n        single_pass=1\n        break # skip variant\n      fi\n    done\n    if [[ $single_pass -eq 1 ]]; then\n      break # skip release\n    fi\n  done\n  if [[ $complete -eq 0 ]] && [[ $single_pass -eq 0 ]]; then\n    echo \"${product}\"\n  fi\ndone\n"
  },
  {
    "path": "tools/merge-event-log-tags.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nMerge together zero or more event-logs-tags files to produce a single\noutput file, stripped of comments.  Checks that no tag numbers conflict\nand fails if they do.\n\"\"\"\n\nfrom io import StringIO\nimport argparse\nimport sys\n\nimport event_log_tags\n\nerrors = []\nwarnings = []\n\nparser = argparse.ArgumentParser(description=__doc__)\nparser.add_argument('-o', dest='output_file')\nparser.add_argument('files', nargs='*')\nargs = parser.parse_args()\n\n# Restrictions on tags:\n#\n#   Tag names must be unique.  (If the tag number and description are\n#   also the same, a warning is issued instead of an error.)\n#\n#   Explicit tag numbers must be unique.  (If the tag name is also the\n#   same, no error is issued because the above rule will issue a\n#   warning or error.)\n\nby_tagname = {}\nby_tagnum = {}\n\nfor fn in args.files:\n  tagfile = event_log_tags.TagFile(fn)\n\n  for t in tagfile.tags:\n    tagnum = t.tagnum\n    tagname = t.tagname\n    description = t.description\n\n    if t.tagname in by_tagname:\n      orig = by_tagname[t.tagname]\n\n      if (t.tagnum == orig.tagnum and\n          t.description == orig.description):\n        # if the name and description are identical, issue a warning\n        # instead of failing (to make it easier to move tags between\n        # projects without breaking the build).\n        tagfile.AddWarning(\"tag \\\"%s\\\" (%s) duplicated in %s:%d\" %\n                           (t.tagname, t.tagnum, orig.filename, orig.linenum),\n                           linenum=t.linenum)\n      else:\n        tagfile.AddError(\n            \"tag name \\\"%s\\\" used by conflicting tag %s from %s:%d\" %\n            (t.tagname, orig.tagnum, orig.filename, orig.linenum),\n            linenum=t.linenum)\n      continue\n\n    if t.tagnum in by_tagnum:\n      orig = by_tagnum[t.tagnum]\n\n      if t.tagname != orig.tagname:\n        tagfile.AddError(\n            \"tag number %d used by conflicting tag \\\"%s\\\" from %s:%d\" %\n            (t.tagnum, orig.tagname, orig.filename, orig.linenum),\n            linenum=t.linenum)\n        continue\n\n    by_tagname[t.tagname] = t\n    by_tagnum[t.tagnum] = t\n\n  errors.extend(tagfile.errors)\n  warnings.extend(tagfile.warnings)\n\nif errors:\n  for fn, ln, msg in errors:\n    print(\"%s:%d: error: %s\" % (fn, ln, msg), file=sys.stderr)\n  sys.exit(1)\n\nif warnings:\n  for fn, ln, msg in warnings:\n    print(\"%s:%d: warning: %s\" % (fn, ln, msg), file=sys.stderr)\n\n# by_tagnum should be complete now; we've assigned numbers to all tags.\n\nbuffer = StringIO()\nfor n, t in sorted(by_tagnum.items()):\n  if t.description:\n    buffer.write(\"%d %s %s\\n\" % (t.tagnum, t.tagname, t.description))\n  else:\n    buffer.write(\"%d %s\\n\" % (t.tagnum, t.tagname))\n\nevent_log_tags.WriteOutput(args.output_file, buffer)\n"
  },
  {
    "path": "tools/missing_soong_module_info.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2016 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nimport os\nimport sys\n\ndef main():\n    try:\n        product_out = os.environ[\"ANDROID_PRODUCT_OUT\"]\n    except KeyError:\n        sys.stderr.write(\"Can't get ANDROID_PRODUCT_OUT. Run lunch first.\\n\")\n        sys.exit(1)\n\n    filename = os.path.join(product_out, \"module-info.json\")\n    try:\n        with open(filename) as f:\n            modules = json.load(f)\n    except FileNotFoundError:\n        sys.stderr.write(f\"File not found: {filename}\\n\")\n        sys.exit(1)\n    except json.JSONDecodeError:\n        sys.stderr.write(f\"Invalid json: {filename}\\n\")\n        return None\n\n    classes = {}\n\n    for name, info in modules.items():\n        make = info.get(\"make\")\n        make_gen = info.get(\"make_generated_module_info\")\n        if not make and make_gen:\n            classes.setdefault(frozenset(info.get(\"class\")), []).append(name)\n\n    for cl, names in classes.items():\n        print(\" \".join(cl))\n        for name in names:\n            print(\" \", name)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "tools/mk2bp_catalog.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"\nCommand to print info about makefiles remaining to be converted to soong.\n\nSee usage / argument parsing below for commandline options.\n\"\"\"\n\nimport argparse\nimport csv\nimport itertools\nimport json\nimport os\nimport re\nimport sys\n\nDIRECTORY_PATTERNS = [x.split(\"/\") for x in (\n  \"device/*\",\n  \"frameworks/*\",\n  \"hardware/*\",\n  \"packages/*\",\n  \"vendor/*\",\n  \"*\",\n)]\n\ndef match_directory_group(pattern, filename):\n  match = []\n  filename = filename.split(\"/\")\n  if len(filename) < len(pattern):\n    return None\n  for i in range(len(pattern)):\n    pattern_segment = pattern[i]\n    filename_segment = filename[i]\n    if pattern_segment == \"*\" or pattern_segment == filename_segment:\n      match.append(filename_segment)\n    else:\n      return None\n  if match:\n    return os.path.sep.join(match)\n  else:\n    return None\n\ndef directory_group(filename):\n  for pattern in DIRECTORY_PATTERNS:\n    match = match_directory_group(pattern, filename)\n    if match:\n      return match\n  return os.path.dirname(filename)\n\nclass Analysis(object):\n  def __init__(self, filename, line_matches):\n    self.filename = filename;\n    self.line_matches = line_matches\n\ndef analyze_lines(filename, lines, func):\n  line_matches = []\n  for i in range(len(lines)):\n    line = lines[i]\n    stripped = line.strip()\n    if stripped.startswith(\"#\"):\n      continue\n    if func(stripped):\n      line_matches.append((i+1, line))\n  if line_matches:\n    return Analysis(filename, line_matches);\n\ndef analyze_has_conditional(line):\n  return (line.startswith(\"ifeq\") or line.startswith(\"ifneq\")\n          or line.startswith(\"ifdef\") or line.startswith(\"ifndef\"))\n\nNORMAL_INCLUDES = [re.compile(pattern) for pattern in (\n  \"include \\$+\\(CLEAR_VARS\\)\", # These are in defines which are tagged separately\n  \"include \\$+\\(BUILD_.*\\)\",\n  \"include \\$\\(call first-makefiles-under, *\\$\\(LOCAL_PATH\\)\\)\",\n  \"include \\$\\(call all-subdir-makefiles\\)\",\n  \"include \\$\\(all-subdir-makefiles\\)\",\n  \"include \\$\\(call all-makefiles-under, *\\$\\(LOCAL_PATH\\)\\)\",\n  \"include \\$\\(call all-makefiles-under, *\\$\\(call my-dir\\).*\\)\",\n  \"include \\$\\(BUILD_SYSTEM\\)/base_rules.mk\", # called out separately\n  \"include \\$\\(call all-named-subdir-makefiles,.*\\)\",\n  \"include \\$\\(subdirs\\)\",\n)]\ndef analyze_has_wacky_include(line):\n  if not (line.startswith(\"include\") or line.startswith(\"-include\")\n          or line.startswith(\"sinclude\")):\n    return False\n  for matcher in NORMAL_INCLUDES:\n    if matcher.fullmatch(line):\n      return False\n  return True\n\nBASE_RULES_RE = re.compile(\"include \\$\\(BUILD_SYSTEM\\)/base_rules.mk\")\n\nclass Analyzer(object):\n  def __init__(self, title, func):\n    self.title = title;\n    self.func = func\n\n\nANALYZERS = (\n  Analyzer(\"ifeq / ifneq\", analyze_has_conditional),\n  Analyzer(\"Wacky Includes\", analyze_has_wacky_include),\n  Analyzer(\"Calls base_rules\", lambda line: BASE_RULES_RE.fullmatch(line)),\n  Analyzer(\"Calls define\", lambda line: line.startswith(\"define \")),\n  Analyzer(\"Has ../\", lambda line: \"../\" in line),\n  Analyzer(\"dist-for-&#8203;goals\", lambda line: \"dist-for-goals\" in line),\n  Analyzer(\".PHONY\", lambda line: \".PHONY\" in line),\n  Analyzer(\"render-&#8203;script\", lambda line: \".rscript\" in line),\n  Analyzer(\"vts src\", lambda line: \".vts\" in line),\n  Analyzer(\"COPY_&#8203;HEADERS\", lambda line: \"LOCAL_COPY_HEADERS\" in line),\n)\n\nclass Summary(object):\n  def __init__(self):\n    self.makefiles = dict()\n    self.directories = dict()\n\n  def Add(self, makefile):\n    self.makefiles[makefile.filename] = makefile\n    self.directories.setdefault(directory_group(makefile.filename), []).append(makefile)\n\nclass Makefile(object):\n  def __init__(self, filename):\n    self.filename = filename\n\n    # Analyze the file\n    with open(filename, \"r\", errors=\"ignore\") as f:\n      try:\n        lines = f.readlines()\n      except UnicodeDecodeError as ex:\n        sys.stderr.write(\"Filename: %s\\n\" % filename)\n        raise ex\n    lines = [line.strip() for line in lines]\n\n    self.analyses = dict([(analyzer, analyze_lines(filename, lines, analyzer.func)) for analyzer\n        in ANALYZERS])\n\ndef find_android_mk():\n  cwd = os.getcwd()\n  for root, dirs, files in os.walk(cwd):\n    for filename in files:\n      if filename == \"Android.mk\":\n        yield os.path.join(root, filename)[len(cwd) + 1:]\n    for ignore in (\".git\", \".repo\"):\n      if ignore in dirs:\n        dirs.remove(ignore)\n\ndef is_aosp(dirname):\n  for d in (\"device/sample\", \"hardware/interfaces\", \"hardware/libhardware\",\n          \"hardware/ril\"):\n    if dirname.startswith(d):\n      return True\n  for d in (\"device/\", \"hardware/\", \"vendor/\"):\n    if dirname.startswith(d):\n      return False\n  return True\n\ndef is_google(dirname):\n  for d in (\"device/google\",\n            \"hardware/google\",\n            \"test/sts\",\n            \"vendor/auto\",\n            \"vendor/google\",\n            \"vendor/unbundled_google\",\n            \"vendor/widevine\",\n            \"vendor/xts\"):\n    if dirname.startswith(d):\n      return True\n  return False\n\ndef is_clean(makefile):\n  for analysis in makefile.analyses.values():\n    if analysis:\n      return False\n  return True\n\ndef clean_and_only_blocked_by_clean(soong, all_makefiles, makefile):\n  if not is_clean(makefile):\n    return False\n  modules = soong.reverse_makefiles[makefile.filename]\n  for module in modules:\n    for dep in soong.transitive_deps(module):\n      for filename in soong.makefiles.get(dep, []):\n        m = all_makefiles.get(filename)\n        if m and not is_clean(m):\n          return False\n  return True\n\nclass Annotations(object):\n  def __init__(self):\n    self.entries = []\n    self.count = 0\n\n  def Add(self, makefiles, modules):\n    self.entries.append((makefiles, modules))\n    self.count += 1\n    return self.count-1\n\nclass SoongData(object):\n  def __init__(self, reader):\n    \"\"\"Read the input file and store the modules and dependency mappings.\n    \"\"\"\n    self.problems = dict()\n    self.deps = dict()\n    self.reverse_deps = dict()\n    self.module_types = dict()\n    self.makefiles = dict()\n    self.reverse_makefiles = dict()\n    self.installed = dict()\n    self.reverse_installed = dict()\n    self.modules = set()\n\n    for (module, module_type, problem, dependencies, makefiles, installed) in reader:\n      self.modules.add(module)\n      makefiles = [f for f in makefiles.strip().split(' ') if f != \"\"]\n      self.module_types[module] = module_type\n      self.problems[module] = problem\n      self.deps[module] = [d for d in dependencies.strip().split(' ') if d != \"\"]\n      for dep in self.deps[module]:\n        if not dep in self.reverse_deps:\n          self.reverse_deps[dep] = []\n        self.reverse_deps[dep].append(module)\n      self.makefiles[module] = makefiles\n      for f in makefiles:\n        self.reverse_makefiles.setdefault(f, []).append(module)\n      for f in installed.strip().split(' '):\n        self.installed[f] = module\n        self.reverse_installed.setdefault(module, []).append(f)\n\n  def transitive_deps(self, module):\n    results = set()\n    def traverse(module):\n      for dep in self.deps.get(module, []):\n        if not dep in results:\n          results.add(dep)\n          traverse(module)\n    traverse(module)\n    return results\n\n  def contains_unblocked_modules(self, filename):\n    for m in self.reverse_makefiles[filename]:\n      if len(self.deps[m]) == 0:\n        return True\n    return False\n\n  def contains_blocked_modules(self, filename):\n    for m in self.reverse_makefiles[filename]:\n      if len(self.deps[m]) > 0:\n        return True\n    return False\n\ndef count_deps(depsdb, module, seen):\n  \"\"\"Based on the depsdb, count the number of transitive dependencies.\n\n  You can pass in an reversed dependency graph to count the number of\n  modules that depend on the module.\"\"\"\n  count = 0\n  seen.append(module)\n  if module in depsdb:\n    for dep in depsdb[module]:\n      if dep in seen:\n        continue\n      count += 1 + count_deps(depsdb, dep, seen)\n  return count\n\nOTHER_PARTITON = \"_other\"\nHOST_PARTITON = \"_host\"\n\ndef get_partition_from_installed(HOST_OUT_ROOT, PRODUCT_OUT, filename):\n  host_prefix = HOST_OUT_ROOT + \"/\"\n  device_prefix = PRODUCT_OUT + \"/\"\n\n  if filename.startswith(host_prefix):\n    return HOST_PARTITON\n\n  elif filename.startswith(device_prefix):\n    index = filename.find(\"/\", len(device_prefix))\n    if index < 0:\n      return OTHER_PARTITON\n    return filename[len(device_prefix):index]\n\n  return OTHER_PARTITON\n\ndef format_module_link(module):\n  return \"<a class='ModuleLink' href='#module_%s'>%s</a>\" % (module, module)\n\ndef format_module_list(modules):\n  return \"\".join([\"<div>%s</div>\" % format_module_link(m) for m in modules])\n\ndef print_analysis_header(link, title):\n  print(\"\"\"\n    <a name=\"%(link)s\"></a>\n    <h2>%(title)s</h2>\n    <table>\n      <tr>\n        <th class=\"RowTitle\">Directory</th>\n        <th class=\"Count\">Total</th>\n        <th class=\"Count Clean\">Easy</th>\n        <th class=\"Count Clean\">Unblocked Clean</th>\n        <th class=\"Count Unblocked\">Unblocked</th>\n        <th class=\"Count Blocked\">Blocked</th>\n        <th class=\"Count Clean\">Clean</th>\n  \"\"\" % {\n    \"link\": link,\n    \"title\": title\n  })\n  for analyzer in ANALYZERS:\n    print(\"\"\"<th class=\"Count Warning\">%s</th>\"\"\" % analyzer.title)\n  print(\"      </tr>\")\n\n# get all modules in $(PRODUCT_PACKAGE) and the corresponding deps\ndef get_module_product_packages_plus_deps(initial_modules, result, soong_data):\n  for module in initial_modules:\n    if module in result:\n      continue\n    result.add(module)\n    if module in soong_data.deps:\n      get_module_product_packages_plus_deps(soong_data.deps[module], result, soong_data)\n\ndef main():\n  parser = argparse.ArgumentParser(description=\"Info about remaining Android.mk files.\")\n  parser.add_argument(\"--device\", type=str, required=True,\n                      help=\"TARGET_DEVICE\")\n  parser.add_argument(\"--product-packages\", type=argparse.FileType('r'),\n                      default=None,\n                      help=\"PRODUCT_PACKAGES\")\n  parser.add_argument(\"--title\", type=str,\n                      help=\"page title\")\n  parser.add_argument(\"--codesearch\", type=str,\n                      default=\"https://cs.android.com/android/platform/superproject/+/master:\",\n                      help=\"page title\")\n  parser.add_argument(\"--out-dir\", type=str,\n                      default=None,\n                      help=\"Equivalent of $OUT_DIR, which will also be checked if\"\n                        + \" --out-dir is unset. If neither is set, default is\"\n                        + \" 'out'.\")\n  parser.add_argument(\"--mode\", type=str,\n                      default=\"html\",\n                      help=\"output format: csv or html\")\n\n  args = parser.parse_args()\n\n  # Guess out directory name\n  if not args.out_dir:\n    args.out_dir = os.getenv(\"OUT_DIR\", \"out\")\n  while args.out_dir.endswith(\"/\") and len(args.out_dir) > 1:\n    args.out_dir = args.out_dir[:-1]\n\n  TARGET_DEVICE = args.device\n  global HOST_OUT_ROOT\n  HOST_OUT_ROOT = args.out_dir + \"/host\"\n  global PRODUCT_OUT\n  PRODUCT_OUT = args.out_dir + \"/target/product/%s\" % TARGET_DEVICE\n\n  # Read target information\n  # TODO: Pull from configurable location. This is also slightly different because it's\n  # only a single build, where as the tree scanning we do below is all Android.mk files.\n  with open(\"%s/obj/PACKAGING/soong_conversion_intermediates/soong_conv_data\"\n      % PRODUCT_OUT, \"r\", errors=\"ignore\") as csvfile:\n    soong = SoongData(csv.reader(csvfile))\n\n  # Read the makefiles\n  all_makefiles = dict()\n  for filename, modules in soong.reverse_makefiles.items():\n    if filename.startswith(args.out_dir + \"/\"):\n      continue\n    all_makefiles[filename] = Makefile(filename)\n\n  # Get all the modules in $(PRODUCT_PACKAGES) and the correspoding deps\n  product_package_modules_plus_deps = set()\n  if args.product_packages:\n    product_package_top_modules = args.product_packages.read().strip().split('\\n')\n    get_module_product_packages_plus_deps(product_package_top_modules, product_package_modules_plus_deps, soong)\n\n  if args.mode == \"html\":\n    HtmlProcessor(args=args, soong=soong, all_makefiles=all_makefiles,\n        product_packages_modules=product_package_modules_plus_deps).execute()\n  elif args.mode == \"csv\":\n    CsvProcessor(args=args, soong=soong, all_makefiles=all_makefiles,\n        product_packages_modules=product_package_modules_plus_deps).execute()\n\nclass HtmlProcessor(object):\n  def __init__(self, args, soong, all_makefiles, product_packages_modules):\n    self.args = args\n    self.soong = soong\n    self.all_makefiles = all_makefiles\n    self.product_packages_modules = product_packages_modules\n    self.annotations = Annotations()\n\n  def execute(self):\n    if self.args.title:\n      page_title = self.args.title\n    else:\n      page_title = \"Remaining Android.mk files\"\n\n    # Which modules are installed where\n    modules_by_partition = dict()\n    partitions = set()\n    for installed, module in self.soong.installed.items():\n      if len(self.product_packages_modules) > 0 and module not in self.product_packages_modules:\n        continue\n      partition = get_partition_from_installed(HOST_OUT_ROOT, PRODUCT_OUT, installed)\n      modules_by_partition.setdefault(partition, []).append(module)\n      partitions.add(partition)\n\n    print(\"\"\"\n    <html>\n      <head>\n        <title>%(page_title)s</title>\n        <style type=\"text/css\">\n          body, table {\n            font-family: Roboto, sans-serif;\n            font-size: 9pt;\n          }\n          body {\n            margin: 0;\n            padding: 0;\n            display: flex;\n            flex-direction: column;\n            height: 100vh;\n          }\n          #container {\n            flex: 1;\n            display: flex;\n            flex-direction: row;\n            overflow: hidden;\n          }\n          #tables {\n            padding: 0 20px 40px 20px;\n            overflow: scroll;\n            flex: 2 2 600px;\n          }\n          #details {\n            display: none;\n            overflow: scroll;\n            flex: 1 1 650px;\n            padding: 0 20px 0 20px;\n          }\n          h1 {\n            margin: 16px 0 16px 20px;\n          }\n          h2 {\n            margin: 12px 0 4px 0;\n          }\n          .RowTitle {\n            text-align: left;\n            width: 200px;\n            min-width: 200px;\n          }\n          .Count {\n            text-align: center;\n            width: 60px;\n            min-width: 60px;\n            max-width: 60px;\n          }\n          th.Clean,\n          th.Unblocked {\n            background-color: #1e8e3e;\n          }\n          th.Blocked {\n            background-color: #d93025;\n          }\n          th.Warning {\n            background-color: #e8710a;\n          }\n          th {\n            background-color: #1a73e8;\n            color: white;\n            font-weight: bold;\n          }\n          td.Unblocked {\n            background-color: #81c995;\n          }\n          td.Blocked {\n            background-color: #f28b82;\n          }\n          td, th {\n            padding: 2px 4px;\n            border-right: 2px solid white;\n          }\n          tr.TotalRow td {\n            background-color: white;\n            border-right-color: white;\n          }\n          tr.AospDir td {\n            background-color: #e6f4ea;\n            border-right-color: #e6f4ea;\n          }\n          tr.GoogleDir td {\n            background-color: #e8f0fe;\n            border-right-color: #e8f0fe;\n          }\n          tr.PartnerDir td {\n            background-color: #fce8e6;\n            border-right-color: #fce8e6;\n          }\n          table {\n            border-spacing: 0;\n            border-collapse: collapse;\n          }\n          div.Makefile {\n            margin: 12px 0 0 0;\n          }\n          div.Makefile:first {\n            margin-top: 0;\n          }\n          div.FileModules {\n            padding: 4px 0 0 20px;\n          }\n          td.LineNo {\n            vertical-align: baseline;\n            padding: 6px 0 0 20px;\n            width: 50px;\n            vertical-align: baseline;\n          }\n          td.LineText {\n            vertical-align: baseline;\n            font-family: monospace;\n            padding: 6px 0 0 0;\n          }\n          a.CsLink {\n            font-family: monospace;\n          }\n          div.Help {\n            width: 550px;\n          }\n          table.HelpColumns tr {\n            border-bottom: 2px solid white;\n          }\n          .ModuleName {\n            vertical-align: baseline;\n            padding: 6px 0 0 20px;\n            width: 275px;\n          }\n          .ModuleDeps {\n            vertical-align: baseline;\n            padding: 6px 0 0 0;\n          }\n          table#Modules td {\n            vertical-align: baseline;\n          }\n          tr.Alt {\n            background-color: #ececec;\n          }\n          tr.Alt td {\n            border-right-color: #ececec;\n          }\n          .AnalysisCol {\n            width: 300px;\n            padding: 2px;\n            line-height: 21px;\n          }\n          .Analysis {\n            color: white;\n            font-weight: bold;\n            background-color: #e8710a;\n            border-radius: 6px;\n            margin: 4px;\n            padding: 2px 6px;\n            white-space: nowrap;\n          }\n          .Nav {\n            margin: 4px 0 16px 20px;\n          }\n          .NavSpacer {\n            display: inline-block;\n            width: 6px;\n          }\n          .ModuleDetails {\n            margin-top: 20px;\n          }\n          .ModuleDetails td {\n            vertical-align: baseline;\n          }\n        </style>\n      </head>\n      <body>\n        <h1>%(page_title)s</h1>\n        <div class=\"Nav\">\n          <a href='#help'>Help</a>\n          <span class='NavSpacer'></span><span class='NavSpacer'> </span>\n          Partitions:\n    \"\"\" % {\n      \"page_title\": page_title,\n    })\n    for partition in sorted(partitions):\n      print(\"<a href='#partition_%s'>%s</a><span class='NavSpacer'></span>\" % (partition, partition))\n\n    print(\"\"\"\n          <span class='NavSpacer'></span><span class='NavSpacer'> </span>\n          <a href='#summary'>Overall Summary</a>\n        </div>\n        <div id=\"container\">\n          <div id=\"tables\">\n          <a name=\"help\"></a>\n          <div class=\"Help\">\n            <p>\n            This page analyzes the remaining Android.mk files in the Android Source tree.\n            <p>\n            The modules are first broken down by which of the device filesystem partitions\n            they are installed to. This also includes host tools and testcases which don't\n            actually reside in their own partition but convenitely group together.\n            <p>\n            The makefiles for each partition are further are grouped into a set of directories\n            aritrarily picked to break down the problem size by owners.\n            <ul style=\"width: 300px\">\n              <li style=\"background-color: #e6f4ea\">AOSP directories are colored green.</li>\n              <li style=\"background-color: #e8f0fe\">Google directories are colored blue.</li>\n              <li style=\"background-color: #fce8e6\">Other partner directories are colored red.</li>\n            </ul>\n            Each of the makefiles are scanned for issues that are likely to come up during\n            conversion to soong.  Clicking the number in each cell shows additional information,\n            including the line that triggered the warning.\n            <p>\n            <table class=\"HelpColumns\">\n              <tr>\n                <th>Total</th>\n                <td>The total number of makefiles in this each directory.</td>\n              </tr>\n              <tr>\n                <th class=\"Clean\">Easy</th>\n                <td>The number of makefiles that have no warnings themselves, and also\n                    none of their dependencies have warnings either.</td>\n              </tr>\n              <tr>\n                <th class=\"Clean\">Unblocked Clean</th>\n                <td>The number of makefiles that are both Unblocked and Clean.</td>\n              </tr>\n\n              <tr>\n                <th class=\"Unblocked\">Unblocked</th>\n                <td>Makefiles containing one or more modules that don't have any\n                    additional dependencies pending before conversion.</td>\n              </tr>\n              <tr>\n                <th class=\"Blocked\">Blocked</th>\n                <td>Makefiles containiong one or more modules which <i>do</i> have\n                    additional prerequesite depenedencies that are not yet converted.</td>\n              </tr>\n              <tr>\n                <th class=\"Clean\">Clean</th>\n                <td>The number of makefiles that have none of the following warnings.</td>\n              </tr>\n              <tr>\n                <th class=\"Warning\">ifeq / ifneq</th>\n                <td>Makefiles that use <code>ifeq</code> or <code>ifneq</code>. i.e.\n                conditionals.</td>\n              </tr>\n              <tr>\n                <th class=\"Warning\">Wacky Includes</th>\n                <td>Makefiles that <code>include</code> files other than the standard build-system\n                    defined template and macros.</td>\n              </tr>\n              <tr>\n                <th class=\"Warning\">Calls base_rules</th>\n                <td>Makefiles that include base_rules.mk directly.</td>\n              </tr>\n              <tr>\n                <th class=\"Warning\">Calls define</th>\n                <td>Makefiles that define their own macros. Some of these are easy to convert\n                    to soong <code>defaults</code>, but others are complex.</td>\n              </tr>\n              <tr>\n                <th class=\"Warning\">Has ../</th>\n                <td>Makefiles containing the string \"../\" outside of a comment. These likely\n                    access files outside their directories.</td>\n              </tr>\n              <tr>\n                <th class=\"Warning\">dist-for-goals</th>\n                <td>Makefiles that call <code>dist-for-goals</code> directly.</td>\n              </tr>\n              <tr>\n                <th class=\"Warning\">.PHONY</th>\n                <td>Makefiles that declare .PHONY targets.</td>\n              </tr>\n              <tr>\n                <th class=\"Warning\">renderscript</th>\n                <td>Makefiles defining targets that depend on <code>.rscript</code> source files.</td>\n              </tr>\n              <tr>\n                <th class=\"Warning\">vts src</th>\n                <td>Makefiles defining targets that depend on <code>.vts</code> source files.</td>\n              </tr>\n              <tr>\n                <th class=\"Warning\">COPY_HEADERS</th>\n                <td>Makefiles using LOCAL_COPY_HEADERS.</td>\n              </tr>\n            </table>\n            <p>\n            Following the list of directories is a list of the modules that are installed on\n            each partition. Potential issues from their makefiles are listed, as well as the\n            total number of dependencies (both blocking that module and blocked by that module)\n            and the list of direct dependencies.  Note: The number is the number of all transitive\n            dependencies and the list of modules is only the direct dependencies.\n          </div>\n    \"\"\")\n\n    overall_summary = Summary()\n\n    # For each partition\n    for partition in sorted(partitions):\n      modules = modules_by_partition[partition]\n\n      makefiles = set(itertools.chain.from_iterable(\n          [self.soong.makefiles[module] for module in modules]))\n\n      # Read makefiles\n      summary = Summary()\n      for filename in makefiles:\n        makefile = self.all_makefiles.get(filename)\n        if makefile:\n          summary.Add(makefile)\n          overall_summary.Add(makefile)\n\n      # Categorize directories by who is responsible\n      aosp_dirs = []\n      google_dirs = []\n      partner_dirs = []\n      for dirname in sorted(summary.directories.keys()):\n        if is_aosp(dirname):\n          aosp_dirs.append(dirname)\n        elif is_google(dirname):\n          google_dirs.append(dirname)\n        else:\n          partner_dirs.append(dirname)\n\n      print_analysis_header(\"partition_\" + partition, partition)\n\n      for dirgroup, rowclass in [(aosp_dirs, \"AospDir\"),\n                                 (google_dirs, \"GoogleDir\"),\n                                 (partner_dirs, \"PartnerDir\"),]:\n        for dirname in dirgroup:\n          self.print_analysis_row(summary, modules,\n                               dirname, rowclass, summary.directories[dirname])\n\n      self.print_analysis_row(summary, modules,\n                           \"Total\", \"TotalRow\",\n                           set(itertools.chain.from_iterable(summary.directories.values())))\n      print(\"\"\"\n        </table>\n      \"\"\")\n\n      module_details = [(count_deps(self.soong.deps, m, []),\n                         -count_deps(self.soong.reverse_deps, m, []), m)\n                 for m in modules]\n      module_details.sort()\n      module_details = [m[2] for m in module_details]\n      print(\"\"\"\n        <table class=\"ModuleDetails\">\"\"\")\n      print(\"<tr>\")\n      print(\"  <th>Module Name</th>\")\n      print(\"  <th>Issues</th>\")\n      print(\"  <th colspan='2'>Blocked By</th>\")\n      print(\"  <th colspan='2'>Blocking</th>\")\n      print(\"</tr>\")\n      altRow = True\n      for module in module_details:\n        analyses = set()\n        for filename in self.soong.makefiles[module]:\n          makefile = summary.makefiles.get(filename)\n          if makefile:\n            for analyzer, analysis in makefile.analyses.items():\n              if analysis:\n                analyses.add(analyzer.title)\n\n        altRow = not altRow\n        print(\"<tr class='%s'>\" % (\"Alt\" if altRow else \"\",))\n        print(\"  <td><a name='module_%s'></a>%s</td>\" % (module, module))\n        print(\"  <td class='AnalysisCol'>%s</td>\" % \" \".join([\"<span class='Analysis'>%s</span>\" % title\n            for title in analyses]))\n        print(\"  <td>%s</td>\" % count_deps(self.soong.deps, module, []))\n        print(\"  <td>%s</td>\" % format_module_list(self.soong.deps.get(module, [])))\n        print(\"  <td>%s</td>\" % count_deps(self.soong.reverse_deps, module, []))\n        print(\"  <td>%s</td>\" % format_module_list(self.soong.reverse_deps.get(module, [])))\n        print(\"</tr>\")\n      print(\"\"\"</table>\"\"\")\n\n    print_analysis_header(\"summary\", \"Overall Summary\")\n\n    modules = [module for installed, module in self.soong.installed.items()]\n    self.print_analysis_row(overall_summary, modules,\n                         \"All Makefiles\", \"TotalRow\",\n                         set(itertools.chain.from_iterable(overall_summary.directories.values())))\n    print(\"\"\"\n        </table>\n    \"\"\")\n\n    print(\"\"\"\n      <script type=\"text/javascript\">\n      function close_details() {\n        document.getElementById('details').style.display = 'none';\n      }\n\n      class LineMatch {\n        constructor(lineno, text) {\n          this.lineno = lineno;\n          this.text = text;\n        }\n      }\n\n      class Analysis {\n        constructor(filename, modules, line_matches) {\n          this.filename = filename;\n          this.modules = modules;\n          this.line_matches = line_matches;\n        }\n      }\n\n      class Module {\n        constructor(deps) {\n          this.deps = deps;\n        }\n      }\n\n      function make_module_link(module) {\n        var a = document.createElement('a');\n        a.className = 'ModuleLink';\n        a.innerText = module;\n        a.href = '#module_' + module;\n        return a;\n      }\n\n      function update_details(id) {\n        document.getElementById('details').style.display = 'block';\n\n        var analyses = ANALYSIS[id];\n\n        var details = document.getElementById(\"details_data\");\n        while (details.firstChild) {\n            details.removeChild(details.firstChild);\n        }\n\n        for (var i=0; i<analyses.length; i++) {\n          var analysis = analyses[i];\n\n          var makefileDiv = document.createElement('div');\n          makefileDiv.className = 'Makefile';\n          details.appendChild(makefileDiv);\n\n          var fileA = document.createElement('a');\n          makefileDiv.appendChild(fileA);\n          fileA.className = 'CsLink';\n          fileA.href = '%(codesearch)s' + analysis.filename;\n          fileA.innerText = analysis.filename;\n          fileA.target = \"_blank\";\n\n          if (analysis.modules.length > 0) {\n            var moduleTable = document.createElement('table');\n            details.appendChild(moduleTable);\n\n            for (var j=0; j<analysis.modules.length; j++) {\n              var moduleRow = document.createElement('tr');\n              moduleTable.appendChild(moduleRow);\n\n              var moduleNameCell = document.createElement('td');\n              moduleRow.appendChild(moduleNameCell);\n              moduleNameCell.className = 'ModuleName';\n              moduleNameCell.appendChild(make_module_link(analysis.modules[j]));\n\n              var moduleData = MODULE_DATA[analysis.modules[j]];\n              console.log(moduleData);\n\n              var depCell = document.createElement('td');\n              moduleRow.appendChild(depCell);\n\n              if (moduleData.deps.length == 0) {\n                depCell.className = 'ModuleDeps Unblocked';\n                depCell.innerText = 'UNBLOCKED';\n              } else {\n                depCell.className = 'ModuleDeps Blocked';\n\n                for (var k=0; k<moduleData.deps.length; k++) {\n                  depCell.appendChild(make_module_link(moduleData.deps[k]));\n                  depCell.appendChild(document.createElement('br'));\n                }\n              }\n            }\n          }\n\n          if (analysis.line_matches.length > 0) {\n            var lineTable = document.createElement('table');\n            details.appendChild(lineTable);\n\n            for (var j=0; j<analysis.line_matches.length; j++) {\n              var line_match = analysis.line_matches[j];\n\n              var lineRow = document.createElement('tr');\n              lineTable.appendChild(lineRow);\n\n              var linenoCell = document.createElement('td');\n              lineRow.appendChild(linenoCell);\n              linenoCell.className = 'LineNo';\n\n              var linenoA = document.createElement('a');\n              linenoCell.appendChild(linenoA);\n              linenoA.className = 'CsLink';\n              linenoA.href = '%(codesearch)s' + analysis.filename\n                  + ';l=' + line_match.lineno;\n              linenoA.innerText = line_match.lineno;\n              linenoA.target = \"_blank\";\n\n              var textCell = document.createElement('td');\n              lineRow.appendChild(textCell);\n              textCell.className = 'LineText';\n              textCell.innerText = line_match.text;\n            }\n          }\n        }\n      }\n\n      var ANALYSIS = [\n      \"\"\" % {\n          \"codesearch\": self.args.codesearch,\n      })\n    for entry, mods in self.annotations.entries:\n      print(\"  [\")\n      for analysis in entry:\n        print(\"    new Analysis('%(filename)s', %(modules)s, [%(line_matches)s]),\" % {\n          \"filename\": analysis.filename,\n          #\"modules\": json.dumps([m for m in mods if m in filename in self.soong.makefiles[m]]),\n          \"modules\": json.dumps(\n              [m for m in self.soong.reverse_makefiles[analysis.filename] if m in mods]),\n          \"line_matches\": \", \".join([\n              \"new LineMatch(%d, %s)\" % (lineno, json.dumps(text))\n              for lineno, text in analysis.line_matches]),\n        })\n      print(\"  ],\")\n    print(\"\"\"\n      ];\n      var MODULE_DATA = {\n    \"\"\")\n    for module in self.soong.modules:\n      print(\"      '%(name)s': new Module(%(deps)s),\" % {\n        \"name\": module,\n        \"deps\": json.dumps(self.soong.deps[module]),\n      })\n    print(\"\"\"\n      };\n      </script>\n\n    \"\"\")\n\n    print(\"\"\"\n        </div> <!-- id=tables -->\n        <div id=\"details\">\n          <div style=\"text-align: right;\">\n            <a href=\"javascript:close_details();\">\n              <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/></svg>\n            </a>\n          </div>\n          <div id=\"details_data\"></div>\n        </div>\n      </body>\n    </html>\n    \"\"\")\n\n  def traverse_ready_makefiles(self, summary, makefiles):\n    return [Analysis(makefile.filename, []) for makefile in makefiles\n        if clean_and_only_blocked_by_clean(self.soong, self.all_makefiles, makefile)]\n\n  def print_analysis_row(self, summary, modules, rowtitle, rowclass, makefiles):\n    all_makefiles = [Analysis(makefile.filename, []) for makefile in makefiles]\n    clean_makefiles = [Analysis(makefile.filename, []) for makefile in makefiles\n        if is_clean(makefile)]\n    easy_makefiles = self.traverse_ready_makefiles(summary, makefiles)\n    unblocked_clean_makefiles = [Analysis(makefile.filename, []) for makefile in makefiles\n        if (self.soong.contains_unblocked_modules(makefile.filename)\n            and is_clean(makefile))]\n    unblocked_makefiles = [Analysis(makefile.filename, []) for makefile in makefiles\n        if self.soong.contains_unblocked_modules(makefile.filename)]\n    blocked_makefiles = [Analysis(makefile.filename, []) for makefile in makefiles\n        if self.soong.contains_blocked_modules(makefile.filename)]\n\n    print(\"\"\"\n      <tr class=\"%(rowclass)s\">\n        <td class=\"RowTitle\">%(rowtitle)s</td>\n        <td class=\"Count\">%(makefiles)s</td>\n        <td class=\"Count\">%(easy)s</td>\n        <td class=\"Count\">%(unblocked_clean)s</td>\n        <td class=\"Count\">%(unblocked)s</td>\n        <td class=\"Count\">%(blocked)s</td>\n        <td class=\"Count\">%(clean)s</td>\n    \"\"\" % {\n      \"rowclass\": rowclass,\n      \"rowtitle\": rowtitle,\n      \"makefiles\": self.make_annotation_link(all_makefiles, modules),\n      \"unblocked\": self.make_annotation_link(unblocked_makefiles, modules),\n      \"blocked\": self.make_annotation_link(blocked_makefiles, modules),\n      \"clean\": self.make_annotation_link(clean_makefiles, modules),\n      \"unblocked_clean\": self.make_annotation_link(unblocked_clean_makefiles, modules),\n      \"easy\": self.make_annotation_link(easy_makefiles, modules),\n    })\n\n    for analyzer in ANALYZERS:\n      analyses = [m.analyses.get(analyzer) for m in makefiles if m.analyses.get(analyzer)]\n      print(\"\"\"<td class=\"Count\">%s</td>\"\"\"\n          % self.make_annotation_link(analyses, modules))\n\n    print(\"      </tr>\")\n\n  def make_annotation_link(self, analysis, modules):\n    if analysis:\n      return \"<a href='javascript:update_details(%d)'>%s</a>\" % (\n        self.annotations.Add(analysis, modules),\n        len(analysis)\n      )\n    else:\n      return \"\";\n\nclass CsvProcessor(object):\n  def __init__(self, args, soong, all_makefiles, product_packages_modules):\n    self.args = args\n    self.soong = soong\n    self.all_makefiles = all_makefiles\n    self.product_packages_modules = product_packages_modules\n\n  def execute(self):\n    csvout = csv.writer(sys.stdout)\n\n    # Title row\n    row = [\"Filename\", \"Module\", \"Partitions\", \"Easy\", \"Unblocked Clean\", \"Unblocked\",\n           \"Blocked\", \"Clean\"]\n    for analyzer in ANALYZERS:\n      row.append(analyzer.title)\n    csvout.writerow(row)\n\n    # Makefile & module data\n    for filename in sorted(self.all_makefiles.keys()):\n      makefile = self.all_makefiles[filename]\n      for module in self.soong.reverse_makefiles[filename]:\n        if len(self.product_packages_modules) > 0 and module not in self.product_packages_modules:\n          continue\n        row = [filename, module]\n        # Partitions\n        row.append(\";\".join(sorted(set([get_partition_from_installed(HOST_OUT_ROOT, PRODUCT_OUT,\n                                         installed)\n                                        for installed\n                                        in self.soong.reverse_installed.get(module, [])]))))\n        # Easy\n        row.append(1\n            if clean_and_only_blocked_by_clean(self.soong, self.all_makefiles, makefile)\n            else \"\")\n        # Unblocked Clean\n        row.append(1\n            if (self.soong.contains_unblocked_modules(makefile.filename) and is_clean(makefile))\n            else \"\")\n        # Unblocked\n        row.append(1 if self.soong.contains_unblocked_modules(makefile.filename) else \"\")\n        # Blocked\n        row.append(1 if self.soong.contains_blocked_modules(makefile.filename) else \"\")\n        # Clean\n        row.append(1 if is_clean(makefile) else \"\")\n        # Analysis\n        for analyzer in ANALYZERS:\n          row.append(1 if makefile.analyses.get(analyzer) else \"\")\n        # Write results\n        csvout.writerow(row)\n\nif __name__ == \"__main__\":\n  main()\n\n"
  },
  {
    "path": "tools/mk2bp_partition.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"\nThe complete list of the remaining Make files in each partition for all lunch targets\n\nHow to run?\npython3 $(path-to-file)/mk2bp_partition.py\n\"\"\"\n\nfrom pathlib import Path\n\nimport csv\nimport datetime\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport time\n\ndef get_top():\n  path = '.'\n  while not os.path.isfile(os.path.join(path, 'build/soong/soong_ui.bash')):\n    if os.path.abspath(path) == '/':\n      sys.exit('Could not find android source tree root.')\n    path = os.path.join(path, '..')\n  return os.path.abspath(path)\n\n# get the values of a build variable\ndef get_build_var(variable, product, build_variant):\n  \"\"\"Returns the result of the shell command get_build_var.\"\"\"\n  env = {\n      **os.environ,\n      'TARGET_PRODUCT': product if product else '',\n      'TARGET_BUILD_VARIANT': build_variant if build_variant else '',\n  }\n  return subprocess.run([\n      'build/soong/soong_ui.bash',\n      '--dumpvar-mode',\n      variable\n  ], check=True, capture_output=True, env=env, text=True).stdout.strip()\n\ndef get_make_file_partitions():\n    lunch_targets = set(get_build_var(\"all_named_products\", \"\", \"\").split())\n    total_lunch_targets = len(lunch_targets)\n    makefile_by_partition = dict()\n    partitions = set()\n    current_count = 0\n    start_time = time.time()\n    # cannot run command `m lunch_target`\n    broken_targets = {\"mainline_sdk\", \"ndk\"}\n    for lunch_target in sorted(lunch_targets):\n        current_count += 1\n        current_time = time.time()\n        print (current_count, \"/\", total_lunch_targets, lunch_target, datetime.timedelta(seconds=current_time - start_time))\n        if lunch_target in broken_targets:\n            continue\n        installed_product_out = get_build_var(\"PRODUCT_OUT\", lunch_target, \"userdebug\")\n        filename = os.path.join(installed_product_out, \"mk2bp_remaining.csv\")\n        copy_filename = os.path.join(installed_product_out, lunch_target + \"_mk2bp_remaining.csv\")\n        # only generate if not exists\n        if not os.path.exists(copy_filename):\n            bash_cmd = \"bash build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=\" + lunch_target\n            bash_cmd += \" TARGET_BUILD_VARIANT=userdebug \" + filename\n            subprocess.run(bash_cmd, shell=True, text=True, check=True, stdout=subprocess.DEVNULL)\n            # generate a copied .csv file, to avoid possible overwritings\n            with open(copy_filename, \"w\") as file:\n                shutil.copyfile(filename, copy_filename)\n\n        # open mk2bp_remaining.csv file\n        with open(copy_filename, \"r\") as csvfile:\n            reader = csv.reader(csvfile, delimiter=\",\", quotechar='\"')\n            # bypass the header row\n            next(reader, None)\n            for row in reader:\n                # read partition information\n                partition = row[2]\n                makefile_by_partition.setdefault(partition, set()).add(row[0])\n                partitions.add(partition)\n\n    # write merged make file list for each partition into a csv file\n    installed_path = Path(installed_product_out).parents[0].as_posix()\n    csv_path = installed_path + \"/mk2bp_partition.csv\"\n    with open(csv_path, \"wt\") as csvfile:\n        writer = csv.writer(csvfile, delimiter=\",\")\n        count_makefile = 0\n        for partition in sorted(partitions):\n            number_file = len(makefile_by_partition[partition])\n            count_makefile += number_file\n            writer.writerow([partition, number_file])\n            for makefile in sorted(makefile_by_partition[partition]):\n                writer.writerow([makefile])\n        row = [\"The total count of make files is \", count_makefile]\n        writer.writerow(row)\n\ndef main():\n    os.chdir(get_top())\n    get_make_file_partitions()\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "tools/normalize_path.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"\nNormalize and output paths from arguments, or stdin if no arguments provided.\n\"\"\"\n\nimport os.path\nimport sys\n\nif len(sys.argv) > 1:\n  for p in sys.argv[1:]:\n    print(os.path.normpath(p))\n  sys.exit(0)\n\nfor line in sys.stdin:\n  print(os.path.normpath(line.strip()))\n"
  },
  {
    "path": "tools/otatools_package/Android.bp",
    "content": "// Copyright (C) 2025 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\njava_genrule_host {\n    name: \"otatools_package_dep_jars\",\n    tools: [\"soong_zip\"],\n    compile_multilib: \"first\",\n    cmd: \"mkdir -p $(genDir)/framework && \" +\n        \"cp $(in) $(genDir)/framework && \" +\n        \"$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)/framework\",\n    srcs: [\n        \":apksigner\",\n        \":boot_signer\",\n        \":signapk\",\n        \":verity_signer\",\n    ],\n    out: [\"otatools_package_dep_jars.zip\"],\n}\n\ncc_genrule {\n    name: \"otatools_package_dep_libs\",\n    host_supported: true,\n    device_supported: false,\n    compile_multilib: \"first\",\n    tools: [\"soong_zip\"],\n    cmd: \"mkdir -p $(genDir)/$$CC_MULTILIB &&\" +\n        \"cp $(in) $(genDir)/$$CC_MULTILIB && \" +\n        \"$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)/$$CC_MULTILIB\",\n    srcs: [\n        \":libbase\",\n        \":libbrillo\",\n        \":libbrillo-stream\",\n        \":libc++\",\n        \"//external/libchrome:libchrome\",\n        \":libconscrypt_openjdk_jni\",\n        \":libcrypto\",\n        \":libcrypto_utils\",\n        \":libcutils\",\n        \":libevent\",\n        \":libext2_blkid\",\n        \":libext2_com_err\",\n        \":libext2_e2p\",\n        \":libext2_quota\",\n        \":libext2_uuid\",\n        \":libext2fs\",\n        \":libext4_utils\",\n        \":libfec\",\n        \":libhidl-gen-utils\",\n        \":libhidlmetadata\",\n        \":libicui18n\",\n        \":libicuuc\",\n        \":liblog\",\n        \":liblp\",\n        \":liblz4\",\n        \":libpcre2\",\n        \":libprocessgroup\",\n        \":libprotobuf-cpp-lite\",\n        \":libselinux\",\n        \":libsparse\",\n        \":libsqlite\",\n        \":libsquashfs_utils\",\n        \":libssl\",\n        \":libz\",\n        \":libziparchive\",\n    ],\n    out: [\"otatools_package_dep_libs.zip\"],\n}\n\ncc_genrule {\n    name: \"otatools_package_dep_bins\",\n    host_supported: true,\n    device_supported: false,\n    compile_multilib: \"first\",\n    tools: [\n        \"apksigner\",\n        \"boot_signer\",\n        \"merge_zips\",\n        \"signapk\",\n        \"verity_signer\",\n    ],\n    cmd: \"mkdir -p $(genDir)/bin && \" +\n        \"cp $(in) $(genDir)/bin && \" +\n        \"cp $(location apksigner) $(location boot_signer) $(location merge_zips) $(location signapk) $(location verity_signer) $(genDir)/bin && \" +\n        \"$(location :soong_zip) -o $(out) -C $(genDir) -D $(genDir)/bin\",\n    srcs: [\n        \":aapt2\",\n        \":add_img_to_target_files\",\n        \":apex_compression_tool\",\n        \":apexd_host\",\n        \":apexer\",\n        \":append2simg\",\n        \":avbtool\",\n        \":blk_alloc_to_base_fs\",\n        \":brillo_update_payload\",\n        \":brotli\",\n        \":bsdiff\",\n        \":build_image\",\n        \":build_super_image\",\n        \":build_verity_metadata\",\n        \":build_verity_tree\",\n        \":care_map_generator\",\n        \":check_ota_package_signature\",\n        \":check_target_files_signatures\",\n        \":check_target_files_vintf\",\n        \":checkvintf\",\n        \":create_brick_ota\",\n        \":deapexer\",\n        \":debugfs_static\",\n        \":delta_generator\",\n        \":e2fsck\",\n        \":e2fsdroid\",\n        \":fc_sort\",\n        \":fec\",\n        \":fs_config\",\n        \":fsck.erofs\",\n        \":fsck.f2fs\",\n        \":generate_verity_key\",\n        \":host_init_verifier\",\n        \":img2simg\",\n        \":img_from_target_files\",\n        \":initrd_bootconfig\",\n        \":lpmake\",\n        \":lpunpack\",\n        \":lz4\",\n        \":make_f2fs\",\n        \":make_f2fs_casefold\",\n        \":merge_ota\",\n        \":merge_target_files\",\n        \"//device/generic/goldfish:mk_combined_img\",\n        \":mkbootfs\",\n        \":mkbootimg\",\n        \":mke2fs\",\n        \":mkf2fsuserimg\",\n        \":mkfs.erofs\",\n        \":mksquashfs\",\n        \":mksquashfsimage\",\n        \":mkuserimg_mke2fs\",\n        \":ota_extractor\",\n        \":ota_from_target_files\",\n        \":repack_bootimg\",\n        \":resize2fs\",\n        \":secilc\",\n        \":sefcontext_compile\",\n        \":sgdisk\",\n        \":shflags\",\n        \":sign_apex\",\n        \":sign_target_files_apks\",\n        \":sign_virt_apex\",\n        \":simg2img\",\n        \":sload_f2fs\",\n        \":soong_zip\",\n        \":toybox\",\n        \":tune2fs\",\n        \":unpack_bootimg\",\n        \":update_device\",\n        \":validate_target_files\",\n        \":verity_verifier\",\n        \":zip2zip\",\n        \":zipalign\",\n        \":zucchini\",\n    ] + select(soong_config_variable(\"otatools\", \"use_build_mixed_kernels_ramdisk\"), {\n        true: [\":build_mixed_kernels_ramdisk_host\"],\n        default: [],\n    }) + select(soong_config_variable(\"otatools\", \"use_bootable_deprecated_ota_applypatch\"), {\n        true: [\n            \":imgdiff\",\n            \":update_host_simulator\",\n        ],\n        default: [],\n    }),\n    out: [\"otatools_package_dep_bins.zip\"],\n}\n\njava_genrule_host {\n    name: \"otatools-package\",\n    tools: [\"merge_zips\"],\n    compile_multilib: \"first\",\n    cmd: \"$(location merge_zips) $(out) $(in)\",\n    srcs: [\n        \":otatools_package_cert_files\",\n        \":otatools_package_dep_bins\",\n        \":otatools_package_dep_jars\",\n        \":otatools_package_dep_libs\",\n        \":otatools_package_releasetools\",\n    ],\n    out: [\"otatools.zip\"],\n    dist: {\n        targets: [\n            \"droidcore\",\n            \"otatools-package\",\n        ],\n    },\n}\n\notatools_package_cert_files {\n    name: \"otatools_package_cert_files\",\n}\n"
  },
  {
    "path": "tools/perf/benchmarks",
    "content": "#!/usr/bin/env python3\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport sys\nif __name__ == \"__main__\":\n    sys.dont_write_bytecode = True\n\nimport argparse\nimport dataclasses\nimport datetime\nimport json\nimport os\nimport pathlib\nimport random\nimport re\nimport shutil\nimport subprocess\nimport time\nimport uuid\nfrom typing import Optional\n\nimport pretty\nimport utils\n\n\nclass FatalError(Exception):\n    def __init__(self):\n        pass\n\n\nclass OptionsError(Exception):\n    def __init__(self, message):\n        self.message = message\n\n\n@dataclasses.dataclass(frozen=True)\nclass Lunch:\n    \"Lunch combination\"\n\n    target_product: str\n    \"TARGET_PRODUCT\"\n\n    target_release: str\n    \"TARGET_RELEASE\"\n\n    target_build_variant: str\n    \"TARGET_BUILD_VARIANT\"\n\n    def ToDict(self):\n        return {\n            \"TARGET_PRODUCT\": self.target_product,\n            \"TARGET_RELEASE\": self.target_release,\n            \"TARGET_BUILD_VARIANT\": self.target_build_variant,\n        }\n\n    def Combine(self):\n        return f\"{self.target_product}-{self.target_release}-{self.target_build_variant}\"\n\n\n@dataclasses.dataclass(frozen=True)\nclass Change:\n    \"A change that we make to the tree, and how to undo it\"\n    label: str\n    \"String to print in the log when the change is made\"\n\n    change: callable\n    \"Function to change the source tree\"\n\n    undo: callable\n    \"Function to revert the source tree to its previous condition in the most minimal way possible.\"\n\n_DUMPVARS_VARS=[\n    \"COMMON_LUNCH_CHOICES\",\n    \"HOST_PREBUILT_TAG\",\n    \"print\",\n    \"PRODUCT_OUT\",\n    \"report_config\",\n    \"TARGET_ARCH\",\n    \"TARGET_BUILD_VARIANT\",\n    \"TARGET_DEVICE\",\n    \"TARGET_PRODUCT\",\n]\n\n_DUMPVARS_ABS_VARS =[\n    \"ANDROID_CLANG_PREBUILTS\",\n    \"ANDROID_JAVA_HOME\",\n    \"ANDROID_JAVA_TOOLCHAIN\",\n    \"ANDROID_PREBUILTS\",\n    \"HOST_OUT\",\n    \"HOST_OUT_EXECUTABLES\",\n    \"HOST_OUT_TESTCASES\",\n    \"OUT_DIR\",\n    \"print\",\n    \"PRODUCT_OUT\",\n    \"SOONG_HOST_OUT\",\n    \"SOONG_HOST_OUT_EXECUTABLES\",\n    \"TARGET_OUT_TESTCASES\",\n]\n\n@dataclasses.dataclass(frozen=True)\nclass Benchmark:\n    \"Something we measure\"\n\n    id: str\n    \"Short ID for the benchmark, for the command line\"\n\n    title: str\n    \"Title for reports\"\n\n    change: Change\n    \"Source tree modification for the benchmark that will be measured\"\n\n    dumpvars: Optional[bool] = False\n    \"If specified, soong will run in dumpvars mode rather than build-mode.\"\n\n    modules: Optional[list[str]] = None\n    \"Build modules to build on soong command line\"\n\n    preroll: Optional[int] = 0\n    \"Number of times to run the build command to stabilize\"\n\n    postroll: Optional[int] = 3\n    \"Number of times to run the build command after reverting the action to stabilize\"\n\n    def build_description(self):\n      \"Short description of the benchmark's Soong invocation.\"\n      if self.dumpvars:\n        return \"dumpvars\"\n      elif self.modules:\n        return \" \".join(self.modules)\n      return \"\"\n\n\n    def soong_command(self, root):\n      \"Command line args to soong_ui for this benchmark.\"\n      if self.dumpvars:\n          return [\n              \"--dumpvars-mode\",\n              f\"--vars=\\\"{' '.join(_DUMPVARS_VARS)}\\\"\",\n              f\"--abs-vars=\\\"{' '.join(_DUMPVARS_ABS_VARS)}\\\"\",\n              \"--var-prefix=var_cache_\",\n              \"--abs-var-prefix=abs_var_cache_\",\n          ]\n      elif self.modules:\n          return [\n              \"--build-mode\",\n              \"--all-modules\",\n              f\"--dir={root}\",\n              \"--skip-metrics-upload\",\n          ] + self.modules\n      else:\n          raise Exception(\"Benchmark must specify dumpvars or modules\")\n\n\n@dataclasses.dataclass(frozen=True)\nclass FileSnapshot:\n    \"Snapshot of a file's contents.\"\n\n    filename: str\n    \"The file that was snapshottened\"\n\n    contents: str\n    \"The contents of the file\"\n\n    def write(self):\n        \"Write the contents back to the file\"\n        with open(self.filename, \"w\") as f:\n            f.write(self.contents)\n\n\ndef Snapshot(filename):\n    \"\"\"Return a FileSnapshot with the file's current contents.\"\"\"\n    with open(filename) as f:\n        contents = f.read()\n    return FileSnapshot(filename, contents)\n\n\ndef Clean():\n    \"\"\"Remove the out directory.\"\"\"\n    def remove_out():\n        out_dir = utils.get_out_dir()\n        #only remove actual contents, in case out is a symlink (as is the case for cog)\n        if os.path.exists(out_dir):\n          for filename in os.listdir(out_dir):\n              p = os.path.join(out_dir, filename)\n              if os.path.isfile(p) or os.path.islink(p):\n                  os.remove(p)\n              elif os.path.isdir(p):\n                  shutil.rmtree(p)\n    return Change(label=\"Remove out\", change=remove_out, undo=lambda: None)\n\n\ndef CleanNinja():\n    \"\"\"Remove the out directory, and then run lunch to initialize soong\"\"\"\n    def clean_ninja():\n        returncode = subprocess.call(\"rm out/*.ninja out/soong/*.ninja\", shell=True)\n        if returncode != 0:\n            report_error(f\"Build failed: {' '.join(cmd)}\")\n            raise FatalError()\n    return Change(label=\"Remove ninja files\", change=clean_ninja, undo=lambda: None)\n\n\ndef NoChange():\n    \"\"\"No change to the source tree.\"\"\"\n    return Change(label=\"No change\", change=lambda: None, undo=lambda: None)\n\n\ndef Create(filename):\n    \"Create an action to create `filename`. The parent directory must exist.\"\n    def create():\n        with open(filename, \"w\") as f:\n            pass\n    def delete():\n        os.remove(filename)\n    return Change(\n                label=f\"Create {filename}\",\n                change=create,\n                undo=delete,\n            )\n\n\ndef Modify(filename, contents, before=None):\n    \"\"\"Create an action to modify `filename` by appending the result of `contents`\n    before the last instances of `before` in the file.\n\n    Raises an error if `before` doesn't appear in the file.\n    \"\"\"\n    orig = Snapshot(filename)\n    if before:\n        index = orig.contents.rfind(before)\n        if index < 0:\n            report_error(f\"{filename}: Unable to find string '{before}' for modify operation.\")\n            raise FatalError()\n    else:\n        index = len(orig.contents)\n    modified = FileSnapshot(filename, orig.contents[:index] + contents() + orig.contents[index:])\n    if False:\n        print(f\"Modify: {filename}\")\n        x = orig.contents.replace(\"\\n\", \"\\n   ORIG\")\n        print(f\"   ORIG {x}\")\n        x = modified.contents.replace(\"\\n\", \"\\n   MODIFIED\")\n        print(f\"   MODIFIED {x}\")\n\n    return Change(\n            label=\"Modify \" + filename,\n            change=lambda: modified.write(),\n            undo=lambda: orig.write()\n        )\n\ndef ChangePublicApi():\n    change = AddJavaField(\"frameworks/base/core/java/android/provider/Settings.java\",\n                 \"@android.annotation.SuppressLint(\\\"UnflaggedApi\\\") public\")\n    orig_current_text = Snapshot(\"frameworks/base/core/api/current.txt\")\n\n    def undo():\n        change.undo()\n        orig_current_text.write()\n\n    return Change(\n        label=change.label,\n        change=change.change,\n        undo=lambda: undo()\n    )\n\ndef AddJavaField(filename, prefix):\n    return Modify(filename,\n                  lambda: f\"{prefix} static final int BENCHMARK = {random.randint(0, 1000000)};\\n\",\n                  before=\"}\")\n\n\ndef Comment(prefix, suffix=\"\"):\n    return lambda: prefix + \" \" + str(uuid.uuid4()) + suffix\n\n\nclass BenchmarkReport():\n    \"Information about a run of the benchmark\"\n\n    lunch: Lunch\n    \"lunch combo\"\n\n    benchmark: Benchmark\n    \"The benchmark object.\"\n\n    iteration: int\n    \"Which iteration of the benchmark\"\n\n    log_dir: str\n    \"Path the the log directory, relative to the root of the reports directory\"\n\n    preroll_duration_ns: [int]\n    \"Durations of the in nanoseconds.\"\n\n    duration_ns: int\n    \"Duration of the measured portion of the benchmark in nanoseconds.\"\n\n    postroll_duration_ns: [int]\n    \"Durations of the postrolls in nanoseconds.\"\n\n    complete: bool\n    \"Whether the benchmark made it all the way through the postrolls.\"\n\n    def __init__(self, lunch, benchmark, iteration, log_dir):\n        self.lunch = lunch\n        self.benchmark = benchmark\n        self.iteration = iteration\n        self.log_dir = log_dir\n        self.preroll_duration_ns = []\n        self.duration_ns = -1\n        self.postroll_duration_ns = []\n        self.complete = False\n\n    def ToDict(self):\n        return {\n            \"lunch\": self.lunch.ToDict(),\n            \"id\": self.benchmark.id,\n            \"title\": self.benchmark.title,\n            \"modules\": self.benchmark.modules,\n            \"dumpvars\": self.benchmark.dumpvars,\n            \"change\": self.benchmark.change.label,\n            \"iteration\": self.iteration,\n            \"log_dir\": self.log_dir,\n            \"preroll_duration_ns\": self.preroll_duration_ns,\n            \"duration_ns\": self.duration_ns,\n            \"postroll_duration_ns\": self.postroll_duration_ns,\n            \"complete\": self.complete,\n        }\n\nclass Runner():\n    \"\"\"Runs the benchmarks.\"\"\"\n\n    def __init__(self, options):\n        self._options = options\n        self._reports = []\n        self._complete = False\n\n    def Run(self):\n        \"\"\"Run all of the user-selected benchmarks.\"\"\"\n\n        # With `--list`, just list the benchmarks available.\n        if self._options.List():\n            print(\" \".join(self._options.BenchmarkIds()))\n            return\n\n        # Clean out the log dir or create it if necessary\n        prepare_log_dir(self._options.LogDir())\n\n        try:\n            for lunch in self._options.Lunches():\n                print(lunch)\n                for benchmark in self._options.Benchmarks():\n                    for iteration in range(self._options.Iterations()):\n                        self._run_benchmark(lunch, benchmark, iteration)\n            self._complete = True\n        finally:\n            self._write_summary()\n\n\n    def _run_benchmark(self, lunch, benchmark, iteration):\n        \"\"\"Run a single benchmark.\"\"\"\n        benchmark_log_subdir = self._benchmark_log_dir(lunch, benchmark, iteration)\n        benchmark_log_dir = self._options.LogDir().joinpath(benchmark_log_subdir)\n\n        sys.stderr.write(f\"STARTING BENCHMARK: {benchmark.id}\\n\")\n        sys.stderr.write(f\"             lunch: {lunch.Combine()}\\n\")\n        sys.stderr.write(f\"         iteration: {iteration}\\n\")\n        sys.stderr.write(f\" benchmark_log_dir: {benchmark_log_dir}\\n\")\n\n        report = BenchmarkReport(lunch, benchmark, iteration, benchmark_log_subdir)\n        self._reports.append(report)\n\n        # Preroll builds\n        for i in range(benchmark.preroll):\n            ns = self._run_build(lunch, benchmark_log_dir.joinpath(f\"pre_{i}\"), benchmark)\n            report.preroll_duration_ns.append(ns)\n\n        sys.stderr.write(f\"PERFORMING CHANGE: {benchmark.change.label}\\n\")\n        if not self._options.DryRun():\n            benchmark.change.change()\n        try:\n\n            # Measured build\n            ns = self._run_build(lunch, benchmark_log_dir.joinpath(\"measured\"), benchmark)\n            report.duration_ns = ns\n\n            dist_one = self._options.DistOne()\n            if dist_one:\n                # If we're disting just one benchmark, save the logs and we can stop here.\n                self._dist(utils.get_dist_dir(), benchmark.dumpvars)\n            else:\n                self._dist(benchmark_log_dir, benchmark.dumpvars, store_metrics_only=True)\n                # Postroll builds\n                for i in range(benchmark.postroll):\n                    ns = self._run_build(lunch, benchmark_log_dir.joinpath(f\"post_{i}\"),\n                                         benchmark)\n                    report.postroll_duration_ns.append(ns)\n\n        finally:\n            # Always undo, even if we crashed or the build failed and we stopped.\n            sys.stderr.write(f\"UNDOING CHANGE: {benchmark.change.label}\\n\")\n            if not self._options.DryRun():\n                benchmark.change.undo()\n\n        self._write_summary()\n        sys.stderr.write(f\"FINISHED BENCHMARK: {benchmark.id}\\n\")\n\n    def _benchmark_log_dir(self, lunch, benchmark, iteration):\n        \"\"\"Construct the log directory fir a benchmark run.\"\"\"\n        path = f\"{lunch.Combine()}/{benchmark.id}\"\n        # Zero pad to the correct length for correct alpha sorting\n        path += (\"/%0\" + str(len(str(self._options.Iterations()))) + \"d\") % iteration\n        return path\n\n    def _run_build(self, lunch, build_log_dir, benchmark):\n        \"\"\"Builds the modules.  Saves interesting log files to log_dir.  Raises FatalError\n        if the build fails.\n        \"\"\"\n        sys.stderr.write(f\"STARTING BUILD {benchmark.build_description()} Logs to: {build_log_dir}\\n\")\n\n        before_ns = time.perf_counter_ns()\n        if not self._options.DryRun():\n            cmd = [\n                \"build/soong/soong_ui.bash\",\n            ] + benchmark.soong_command(self._options.root)\n            env = dict(os.environ)\n            env[\"TARGET_PRODUCT\"] = lunch.target_product\n            env[\"TARGET_RELEASE\"] = lunch.target_release\n            env[\"TARGET_BUILD_VARIANT\"] = lunch.target_build_variant\n            returncode = subprocess.call(cmd, env=env)\n            if returncode != 0:\n                report_error(f\"Build failed: {' '.join(cmd)}\")\n                raise FatalError()\n\n        after_ns = time.perf_counter_ns()\n\n        # TODO: Copy some log files.\n\n        sys.stderr.write(f\"FINISHED BUILD {benchmark.build_description()}\\n\")\n\n        return after_ns - before_ns\n\n    def _dist(self, dist_dir, dumpvars, store_metrics_only=False):\n        out_dir = utils.get_out_dir()\n        dest_dir = dist_dir.joinpath(\"logs\")\n        os.makedirs(dest_dir, exist_ok=True)\n        basenames = [\n            \"soong_build_metrics.pb\",\n            \"soong_metrics\",\n        ]\n        if not store_metrics_only:\n            basenames.extend([\n                \"build.trace.gz\",\n                \"soong.log\",\n            ])\n        if dumpvars:\n            basenames = ['dumpvars-'+b for b in basenames]\n        for base in basenames:\n            src = out_dir.joinpath(base)\n            if src.exists():\n                sys.stderr.write(f\"DIST: copied {src} to {dest_dir}\\n\")\n                shutil.copy(src, dest_dir)\n\n    def _write_summary(self):\n        # Write the results, even if the build failed or we crashed, including\n        # whether we finished all of the benchmarks.\n        data = {\n            \"start_time\": self._options.Timestamp().isoformat(),\n            \"branch\": self._options.Branch(),\n            \"tag\": self._options.Tag(),\n            \"benchmarks\": [report.ToDict() for report in self._reports],\n            \"complete\": self._complete,\n        }\n        with open(self._options.LogDir().joinpath(\"summary.json\"), \"w\", encoding=\"utf-8\") as f:\n            json.dump(data, f, indent=2, sort_keys=True)\n\n\ndef benchmark_table(benchmarks):\n    rows = [(\"ID\", \"DESCRIPTION\", \"REBUILD\"),]\n    rows += [(benchmark.id, benchmark.title, benchmark.build_description()) for benchmark in\n             benchmarks]\n    return rows\n\n\ndef prepare_log_dir(directory):\n    if os.path.exists(directory):\n        # If it exists and isn't a directory, fail.\n        if not os.path.isdir(directory):\n            report_error(f\"Log directory already exists but isn't a directory: {directory}\")\n            raise FatalError()\n        # Make sure the directory is empty. Do this rather than deleting it to handle\n        # symlinks cleanly.\n        for filename in os.listdir(directory):\n            entry = os.path.join(directory, filename)\n            if os.path.isdir(entry):\n                shutil.rmtree(entry)\n            else:\n                os.unlink(entry)\n    else:\n        # Create it\n        os.makedirs(directory)\n\n\nclass Options():\n    def __init__(self):\n        self._had_error = False\n\n        # Wall time clock when we started\n        self._timestamp = datetime.datetime.now(datetime.timezone.utc)\n\n        # Move to the root of the tree right away. Everything must happen from there.\n        self.root = utils.get_root()\n        if not self.root:\n            report_error(\"Unable to find root of tree from cwd.\")\n            raise FatalError()\n        os.chdir(self.root)\n\n        # Initialize the Benchmarks. Note that this pre-loads all of the files, etc.\n        # Doing all that here forces us to fail fast if one of them can't load a required\n        # file, at the cost of a small startup speed. Don't make this do something slow\n        # like scan the whole tree.\n        self._init_benchmarks()\n\n        # Argument parsing\n        epilog = f\"\"\"\nbenchmarks:\n{pretty.FormatTable(benchmark_table(self._benchmarks), prefix=\"  \")}\n\"\"\"\n\n        parser = argparse.ArgumentParser(\n                prog=\"benchmarks\",\n                allow_abbrev=False, # Don't let people write unsupportable scripts.\n                formatter_class=argparse.RawDescriptionHelpFormatter,\n                epilog=epilog,\n                description=\"Run build system performance benchmarks.\")\n        self.parser = parser\n\n        parser.add_argument(\"--log-dir\",\n                            help=\"Directory for logs. Default is $TOP/../benchmarks/.\")\n        parser.add_argument(\"--dated-logs\", action=\"store_true\",\n                            help=\"Append timestamp to log dir.\")\n        parser.add_argument(\"-n\", action=\"store_true\", dest=\"dry_run\",\n                            help=\"Dry run. Don't run the build commands but do everything else.\")\n        parser.add_argument(\"--tag\",\n                            help=\"Variant of the run, for when there are multiple perf runs.\")\n        parser.add_argument(\"--lunch\", nargs=\"*\",\n                            help=\"Lunch combos to test\")\n        parser.add_argument(\"--iterations\", type=int, default=1,\n                            help=\"Number of iterations of each test to run.\")\n        parser.add_argument(\"--branch\", type=str,\n                            help=\"Specify branch. Otherwise a guess will be made based on repo.\")\n        parser.add_argument(\"--benchmark\", nargs=\"*\", default=[b.id for b in self._benchmarks],\n                            metavar=\"BENCHMARKS\",\n                            help=\"Benchmarks to run.  Default suite will be run if omitted.\")\n        parser.add_argument(\"--list\", action=\"store_true\",\n                            help=\"list the available benchmarks.  No benchmark is run.\")\n        parser.add_argument(\"--dist-one\", action=\"store_true\",\n                            help=\"Copy logs and metrics to the given dist dir. Requires that only\"\n                                + \" one benchmark be supplied. Postroll steps will be skipped.\")\n\n        self._args = parser.parse_args()\n\n        self._branch = self._branch()\n        self._log_dir = self._log_dir()\n        self._lunches = self._lunches()\n\n        # Validate the benchmark ids\n        all_ids = [benchmark.id for benchmark in self._benchmarks]\n        bad_ids = [id for id in self._args.benchmark if id not in all_ids]\n        if bad_ids:\n            for id in bad_ids:\n                self._error(f\"Invalid benchmark: {id}\")\n\n        # --dist-one requires that only one benchmark be supplied\n        if self._args.dist_one and len(self.Benchmarks()) != 1:\n            self._error(\"--dist-one requires exactly one --benchmark.\")\n\n        if self._had_error:\n            raise FatalError()\n\n    def Timestamp(self):\n        return self._timestamp\n\n    def _branch(self):\n        \"\"\"Return the branch, either from the command line or by guessing from repo.\"\"\"\n        if self._args.branch:\n            return self._args.branch\n        try:\n            branch = subprocess.check_output(f\"cd {self.root}/.repo/manifests\"\n                        + \" && git rev-parse --abbrev-ref --symbolic-full-name @{u}\",\n                    shell=True, encoding=\"utf-8\")\n            return branch.strip().split(\"/\")[-1]\n        except subprocess.CalledProcessError as ex:\n            report_error(\"Can't get branch from .repo dir. Specify --branch argument\")\n            report_error(str(ex))\n            raise FatalError()\n\n    def Branch(self):\n        return self._branch\n\n    def _log_dir(self):\n        \"The log directory to use, based on the current options\"\n        if self._args.log_dir:\n            d = pathlib.Path(self._args.log_dir).resolve().absolute()\n        else:\n            d = self.root.joinpath(\"..\", utils.DEFAULT_REPORT_DIR)\n        if self._args.dated_logs:\n            d = d.joinpath(self._timestamp.strftime('%Y-%m-%d'))\n        d = d.joinpath(self._branch)\n        if self._args.tag:\n            d = d.joinpath(self._args.tag)\n        return d.resolve().absolute()\n\n    def LogDir(self):\n        return self._log_dir\n\n    def Benchmarks(self):\n        return [b for b in self._benchmarks if b.id in self._args.benchmark]\n\n    def Tag(self):\n        return self._args.tag\n\n    def DryRun(self):\n        return self._args.dry_run\n\n    def List(self):\n        return self._args.list\n\n    def BenchmarkIds(self) :\n        return [benchmark.id for benchmark in self._benchmarks]\n\n    def _lunches(self):\n        def parse_lunch(lunch):\n            parts = lunch.split(\"-\")\n            if len(parts) != 3:\n                raise OptionsError(f\"Invalid lunch combo: {lunch}\")\n            return Lunch(parts[0], parts[1], parts[2])\n        # If they gave lunch targets on the command line use that\n        if self._args.lunch:\n            result = []\n            # Split into Lunch objects\n            for lunch in self._args.lunch:\n                try:\n                    result.append(parse_lunch(lunch))\n                except OptionsError as ex:\n                    self._error(ex.message)\n            return result\n        # Use whats in the environment\n        product = os.getenv(\"TARGET_PRODUCT\")\n        release = os.getenv(\"TARGET_RELEASE\")\n        variant = os.getenv(\"TARGET_BUILD_VARIANT\")\n        if (not product) or (not release) or (not variant):\n            # If they didn't give us anything, fail rather than guessing. There's no good\n            # default for AOSP.\n            self._error(\"No lunch combo specified. Either pass --lunch argument or run lunch.\")\n            return []\n        return [Lunch(product, release, variant),]\n\n    def Lunches(self):\n        return self._lunches\n\n    def Iterations(self):\n        return self._args.iterations\n\n    def DistOne(self):\n        return self._args.dist_one\n\n    def _init_benchmarks(self):\n        \"\"\"Initialize the list of benchmarks.\"\"\"\n        # Assumes that we've already chdired to the root of the tree.\n        self._benchmarks = [\n            Benchmark(\n                      id=\"full_lunch\",\n                      title=\"Lunch from clean out\",\n                      change=Clean(),\n                      dumpvars=True,\n                      preroll=0,\n                      postroll=0,\n            ),\n            Benchmark(\n                      id=\"noop_lunch\",\n                      title=\"Lunch with no change\",\n                      change=NoChange(),\n                      dumpvars=True,\n                      preroll=1,\n                      postroll=0,\n            ),\n            Benchmark(id=\"full\",\n                      title=\"Full build\",\n                      change=Clean(),\n                      modules=[\"droid\"],\n                      preroll=0,\n                      postroll=3,\n                      ),\n            Benchmark(id=\"nochange\",\n                      title=\"No change\",\n                      change=NoChange(),\n                      modules=[\"droid\"],\n                      preroll=2,\n                      postroll=3,\n                      ),\n            Benchmark(id=\"unreferenced\",\n                      title=\"Create unreferenced file\",\n                      change=Create(\"bionic/unreferenced.txt\"),\n                      modules=[\"droid\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"modify_bp\",\n                      title=\"Modify Android.bp\",\n                      change=Modify(\"bionic/libc/Android.bp\", Comment(\"//\")),\n                      modules=[\"droid\"],\n                      preroll=1,\n                      postroll=3,\n                      ),\n            Benchmark(id=\"full_analysis\",\n                      title=\"Full Analysis\",\n                      change=CleanNinja(),\n                      modules=[\"nothing\"],\n                      preroll=1,\n                      postroll=3,\n                      ),\n            Benchmark(id=\"modify_stdio\",\n                      title=\"Modify stdio.cpp\",\n                      change=Modify(\"bionic/libc/stdio/stdio.cpp\", Comment(\"//\")),\n                      modules=[\"libc\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"modify_adbd\",\n                      title=\"Modify adbd\",\n                      change=Modify(\"packages/modules/adb/daemon/main.cpp\", Comment(\"//\")),\n                      modules=[\"adbd\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"services_private_field\",\n                      title=\"Add private field to ActivityManagerService.java\",\n                      change=AddJavaField(\"frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java\",\n                                          \"private\"),\n                      modules=[\"services\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"services_public_field\",\n                      title=\"Add public field to ActivityManagerService.java\",\n                      change=AddJavaField(\"frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java\",\n                                          \"/** @hide */ public\"),\n                      modules=[\"services\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"services_api\",\n                      title=\"Add API to ActivityManagerService.javaa\",\n                      change=AddJavaField(\"frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java\",\n                                          \"@android.annotation.SuppressLint(\\\"UnflaggedApi\\\") public\"),\n                      modules=[\"services\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"framework_private_field\",\n                      title=\"Add private field to Settings.java\",\n                      change=AddJavaField(\"frameworks/base/core/java/android/provider/Settings.java\",\n                                          \"private\"),\n                      modules=[\"framework-minus-apex\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"framework_public_field\",\n                      title=\"Add public field to Settings.java\",\n                      change=AddJavaField(\"frameworks/base/core/java/android/provider/Settings.java\",\n                                          \"/** @hide */ public\"),\n                      modules=[\"framework-minus-apex\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"framework_api\",\n                      title=\"Add API to Settings.java\",\n                      change=ChangePublicApi(),\n                      modules=[\"api-stubs-docs-non-updatable-update-current-api\", \"framework-minus-apex\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"modify_framework_resource\",\n                      title=\"Modify framework resource\",\n                      change=Modify(\"frameworks/base/core/res/res/values/config.xml\",\n                                    lambda: str(uuid.uuid4()),\n                                    before=\"</string>\"),\n                      modules=[\"framework-minus-apex\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"add_framework_resource\",\n                      title=\"Add framework resource\",\n                      change=Modify(\"frameworks/base/core/res/res/values/config.xml\",\n                                    lambda: f\"<string name=\\\"BENCHMARK\\\">{uuid.uuid4()}</string>\",\n                                    before=\"</resources>\"),\n                      modules=[\"framework-minus-apex\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"add_systemui_field\",\n                      title=\"Add SystemUI field\",\n                      change=AddJavaField(\"frameworks/base/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java\",\n                                    \"public\"),\n                      modules=[\"SystemUI\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"add_systemui_field_with_tests\",\n                      title=\"Add SystemUI field with tests\",\n                      change=AddJavaField(\"frameworks/base/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java\",\n                                    \"public\"),\n                      modules=[\"SystemUiRavenTests\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"systemui_flicker_add_log_call\",\n                      title=\"Add a Log call to flicker\",\n                      change=Modify(\"platform_testing/libraries/flicker/src/android/tools/flicker/FlickerServiceResultsCollector.kt\",\n                                    lambda: f'Log.v(LOG_TAG, \"BENCHMARK = {random.randint(0, 1000000)}\");\\n',\n                                    before=\"Log.v(LOG_TAG,\"),\n                      modules=[\"WMShellFlickerTestsPip\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n            Benchmark(id=\"systemui_core_add_log_call\",\n                      title=\"Add a Log call SystemUIApplication\",\n                      change=Modify(\"frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java\",\n                                    lambda: f'Log.v(TAG, \"BENCHMARK = {random.randint(0, 1000000)}\");\\n',\n                                    before=\"Log.wtf(TAG,\"),\n                      modules=[\"SystemUI-core\"],\n                      preroll=1,\n                      postroll=2,\n                      ),\n        ]\n\n    def _error(self, message):\n        report_error(message)\n        self._had_error = True\n\n\ndef report_error(message):\n    sys.stderr.write(f\"error: {message}\\n\")\n\n\ndef main(argv):\n    try:\n        options = Options()\n        runner = Runner(options)\n        runner.Run()\n    except FatalError:\n        sys.stderr.write(f\"FAILED\\n\")\n        sys.exit(1)\n\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n"
  },
  {
    "path": "tools/perf/format_benchmarks",
    "content": "#!/usr/bin/env python3\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport sys\nif __name__ == \"__main__\":\n    sys.dont_write_bytecode = True\n\nimport argparse\nimport dataclasses\nimport datetime\nimport json\nimport os\nimport pathlib\nimport statistics\nimport zoneinfo\nimport csv\n\nimport pretty\nimport utils\n\n# TODO:\n# - Flag if the last postroll build was more than 15 seconds or something. That's\n#   an indicator that something is amiss.\n# - Add a mode to print all of the values for multi-iteration runs\n# - Add a flag to reorder the tags\n# - Add a flag to reorder the headers in order to show grouping more clearly.\n\n\ndef FindSummaries(args):\n    def find_summaries(directory):\n        return [str(p.resolve()) for p in pathlib.Path(directory).glob(\"**/summary.json\")]\n    if not args:\n        # If they didn't give an argument, use the default dir\n        root = utils.get_root()\n        if not root:\n            return []\n        return find_summaries(root.joinpath(\"..\", utils.DEFAULT_REPORT_DIR))\n    results = list()\n    for arg in args:\n        if os.path.isfile(arg):\n            # If it's a file add that\n            results.append(arg)\n        elif os.path.isdir(arg):\n            # If it's a directory, find all of the files there\n            results += find_summaries(arg)\n        else:\n            sys.stderr.write(f\"Invalid summary argument: {arg}\\n\")\n            sys.exit(1)\n    return sorted(list(results))\n\n\ndef LoadSummary(filename):\n    with open(filename) as f:\n        return json.load(f)\n\n# Columns:\n#   Date\n#   Branch\n#   Tag\n#   --\n#   Lunch\n# Rows:\n#   Benchmark\n\ndef lunch_str(d):\n    \"Convert a lunch dict to a string\"\n    return f\"{d['TARGET_PRODUCT']}-{d['TARGET_RELEASE']}-{d['TARGET_BUILD_VARIANT']}\"\n\ndef group_by(l, key):\n    \"Return a list of tuples, grouped by key, sorted by key\"\n    result = {}\n    for item in l:\n        result.setdefault(key(item), []).append(item)\n    return [(k, v) for k, v in result.items()]\n\n\nclass Table:\n    def __init__(self, row_title, fixed_titles=[]):\n        self._data = {}\n        self._rows = []\n        self._cols = []\n        self._fixed_cols = {}\n        self._titles = [row_title] + fixed_titles\n\n    def Set(self, column_key, row_key, data):\n        self._data[(column_key, row_key)] = data\n        if not column_key in self._cols:\n            self._cols.append(column_key)\n        if not row_key in self._rows:\n            self._rows.append(row_key)\n\n    def SetFixedCol(self, row_key, columns):\n        self._fixed_cols[row_key] = columns\n\n    def Write(self, out, fmt):\n        table = []\n        # Expand the column items\n        for row in zip(*self._cols):\n            if row.count(row[0]) == len(row):\n                continue\n            table.append([\"\"] * len(self._titles) + [col for col in row])\n        if table:\n            # Update the last row of the header with title and add separator\n            for i in range(len(self._titles)):\n                table[len(table)-1][i] = self._titles[i]\n            if fmt == \"table\":\n                table.append(pretty.SEPARATOR)\n        # Populate the data\n        for row in self._rows:\n            table.append([str(row)]\n                         + self._fixed_cols[row]\n                         + [str(self._data.get((col, row), \"\")) for col in self._cols])\n        if fmt == \"csv\":\n            csv.writer(sys.stdout, quoting=csv.QUOTE_MINIMAL).writerows(table)\n        else:\n            out.write(pretty.FormatTable(table, alignments=\"LL\"))\n\n\ndef format_duration_sec(ns, fmt_sec):\n    \"Format a duration in ns to second precision\"\n    sec = round(ns / 1000000000)\n    if fmt_sec:\n        return f\"{sec}\"\n    else:\n        h, sec = divmod(sec, 60*60)\n        m, sec = divmod(sec, 60)\n        result = \"\"\n        if h > 0:\n            result += f\"{h:2d}h \"\n        if h > 0 or m > 0:\n            result += f\"{m:2d}m \"\n        return result + f\"{sec:2d}s\"\n\n\ndef main(argv):\n    parser = argparse.ArgumentParser(\n            prog=\"format_benchmarks\",\n            allow_abbrev=False, # Don't let people write unsupportable scripts.\n            description=\"Print analysis tables for benchmarks\")\n\n    parser.add_argument(\"--csv\", action=\"store_true\",\n                        help=\"Print in CSV instead of table.\")\n\n    parser.add_argument(\"--sec\", action=\"store_true\",\n                        help=\"Print in seconds instead of minutes and seconds\")\n\n    parser.add_argument(\"--tags\", nargs=\"*\",\n                        help=\"The tags to print, in order.\")\n\n    parser.add_argument(\"summaries\", nargs=\"*\",\n                        help=\"A summary.json file or a directory in which to look for summaries.\")\n\n    args = parser.parse_args()\n\n    # Load the summaries\n    summaries = [(s, LoadSummary(s)) for s in FindSummaries(args.summaries)]\n\n    # Convert to MTV time\n    for filename, s in summaries:\n        dt = datetime.datetime.fromisoformat(s[\"start_time\"])\n        dt = dt.astimezone(zoneinfo.ZoneInfo(\"America/Los_Angeles\"))\n        s[\"datetime\"] = dt\n        s[\"date\"] = datetime.date(dt.year, dt.month, dt.day)\n\n    # Filter out tags we don't want\n    if args.tags:\n        summaries = [(f, s) for f, s in summaries if s.get(\"tag\", \"\") in args.tags]\n\n    # If they supplied tags, sort in that order, otherwise sort by tag\n    if args.tags:\n        tagsort = lambda tag: args.tags.index(tag)\n    else:\n        tagsort = lambda tag: tag\n\n    # Sort the summaries\n    summaries.sort(key=lambda s: (s[1][\"date\"], s[1][\"branch\"], tagsort(s[1][\"tag\"])))\n\n    # group the benchmarks by column and iteration\n    def bm_key(b):\n        return (\n            lunch_str(b[\"lunch\"]),\n        )\n    for filename, summary in summaries:\n        summary[\"columns\"] = [(key, group_by(bms, lambda b: b[\"id\"])) for key, bms\n                              in group_by(summary[\"benchmarks\"], bm_key)]\n\n    # Build the table\n    table = Table(\"Benchmark\", [\"Rebuild\"])\n    for filename, summary in summaries:\n        for key, column in summary[\"columns\"]:\n            for id, cell in column:\n                duration_ns = statistics.median([b[\"duration_ns\"] for b in cell])\n                modules = cell[0][\"modules\"]\n                if not modules:\n                    modules = [\"---\"]\n                table.SetFixedCol(cell[0][\"title\"], [\" \".join(modules)])\n                table.Set(tuple([summary[\"date\"].strftime(\"%Y-%m-%d\"),\n                                 summary[\"branch\"],\n                                 summary[\"tag\"]]\n                                + list(key)),\n                          cell[0][\"title\"], format_duration_sec(duration_ns, args.sec))\n\n    table.Write(sys.stdout, \"csv\" if args.csv else \"table\")\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n\n"
  },
  {
    "path": "tools/perf/pretty.py",
    "content": "# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Formatting utilities\n\nclass Sentinel():\n    pass\n\nSEPARATOR = Sentinel()\n\ndef FormatTable(data, prefix=\"\", alignments=[]):\n    \"\"\"Pretty print a table.\n\n    Prefixes each row with `prefix`.\n    \"\"\"\n    if not data:\n        return \"\"\n    widths = [max([len(x) if x else 0 for x in col]) for col\n              in zip(*[d for d in data if not isinstance(d, Sentinel)])]\n    result = \"\"\n    colsep = \"  \"\n    for row in data:\n        result += prefix\n        if row == SEPARATOR:\n            for w in widths:\n                result += \"-\" * w\n                result += colsep\n            result += \"\\n\"\n        else:\n            for i in range(len(row)):\n                cell = row[i] if row[i] else \"\"\n                if i >= len(alignments) or alignments[i] == \"R\":\n                    result += \" \" * (widths[i] - len(cell))\n                result += cell\n                if i < len(alignments) and alignments[i] == \"L\":\n                    result += \" \" * (widths[i] - len(cell))\n                result += colsep\n            result += \"\\n\"\n    return result\n\n\n"
  },
  {
    "path": "tools/perf/utils.py",
    "content": "# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport pathlib\n\nDEFAULT_REPORT_DIR = \"benchmarks\"\n\ndef get_root():\n    top_dir = os.environ.get(\"ANDROID_BUILD_TOP\")\n    d = pathlib.Path.cwd()\n    # with cog, someone may have a new workspace and new source tree top, but\n    # not run lunch yet, resulting in a misleading ANDROID_BUILD_TOP value\n    if top_dir and d.is_relative_to(top_dir):\n        return pathlib.Path(top_dir).resolve()\n    while True:\n        if d.joinpath(\"build\", \"soong\", \"soong_ui.bash\").exists():\n            return d.resolve().absolute()\n        d = d.parent\n        if d == pathlib.Path(\"/\"):\n            return None\n\ndef get_dist_dir():\n    dist_dir = os.getenv(\"DIST_DIR\")\n    if dist_dir:\n        return pathlib.Path(dist_dir).resolve()\n    return get_out_dir().joinpath(\"dist\")\n\ndef get_out_dir():\n    out_dir = os.getenv(\"OUT_DIR\")\n    if not out_dir:\n        out_dir = \"out\"\n    return pathlib.Path(out_dir).resolve()\n"
  },
  {
    "path": "tools/post_process_props.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport sys\n\nfrom uffd_gc_utils import should_enable_uffd_gc\n\n# Usage: post_process_props.py file.prop [disallowed_key, ...]\n# Disallowed keys are removed from the property file, if present\n\n# See PROP_VALUE_MAX in system_properties.h.\n# The constant in system_properties.h includes the terminating NUL,\n# so we decrease the value by 1 here.\nPROP_VALUE_MAX = 91\n\n# Put the modifications that you need to make into the */build.prop into this\n# function.\ndef mangle_build_prop(prop_list, kernel_version_file_for_uffd_gc):\n  # If ro.debuggable is 1, then enable adb on USB by default\n  # (this is for userdebug builds)\n  if prop_list.get_value(\"ro.debuggable\") == \"1\":\n    val = prop_list.get_value(\"persist.sys.usb.config\")\n    if \"adb\" not in val:\n      if val == \"\":\n        val = \"adb\"\n      else:\n        val = val + \",adb\"\n      prop_list.put(\"persist.sys.usb.config\", val)\n  if prop_list.get_value(\"ro.dalvik.vm.enable_uffd_gc\") == \"default\":\n    assert kernel_version_file_for_uffd_gc != \"\"\n    enable_uffd_gc = should_enable_uffd_gc(kernel_version_file_for_uffd_gc)\n    prop_list.put(\"ro.dalvik.vm.enable_uffd_gc\",\n                  \"true\" if enable_uffd_gc else \"false\")\n\ndef validate_grf_props(prop_list):\n  \"\"\"Validate GRF properties if exist.\n\n  If ro.board.first_api_level is defined, check if its value is valid.\n\n  Returns:\n    True if the GRF properties are valid.\n  \"\"\"\n  grf_api_level = prop_list.get_value(\"ro.board.first_api_level\")\n  board_api_level = prop_list.get_value(\"ro.board.api_level\")\n\n  if grf_api_level and board_api_level:\n    grf_api_level = int(grf_api_level)\n    board_api_level = int(board_api_level)\n    if board_api_level < grf_api_level:\n      sys.stderr.write(\"error: ro.board.api_level(%d) must not be less than \"\n                       \"ro.board.first_api_level(%d)\\n\"\n                       % (board_api_level, grf_api_level))\n      return False\n\n  return True\n\ndef validate(prop_list):\n  \"\"\"Validate the properties.\n\n  If the value of a sysprop exceeds the max limit (91), it's an error, unless\n  the sysprop is a read-only one.\n\n  Checks if there is no optional prop assignments.\n\n  Returns:\n    True if nothing is wrong.\n  \"\"\"\n  check_pass = True\n  for p in prop_list.get_all_props():\n    if len(p.value) > PROP_VALUE_MAX and not p.name.startswith(\"ro.\"):\n      check_pass = False\n      sys.stderr.write(\"error: %s cannot exceed %d bytes: \" %\n                       (p.name, PROP_VALUE_MAX))\n      sys.stderr.write(\"%s (%d)\\n\" % (p.value, len(p.value)))\n\n    if p.is_optional():\n      check_pass = False\n      sys.stderr.write(\"error: found unresolved optional prop assignment:\\n\")\n      sys.stderr.write(str(p) + \"\\n\")\n\n  return check_pass\n\ndef override_optional_props(prop_list, allow_dup=False):\n  \"\"\"Override a?=b with a=c, if the latter exists\n\n  Overriding is done by deleting a?=b\n  When there are a?=b and a?=c, then only the last one survives\n  When there are a=b and a=c, then it's an error.\n\n  Returns:\n    True if the override was successful\n  \"\"\"\n  success = True\n  for name in prop_list.get_all_names():\n    props = prop_list.get_props(name)\n    optional_props = [p for p in props if p.is_optional()]\n    overriding_props = [p for p in props if not p.is_optional()]\n    if len(overriding_props) > 1:\n      # duplicated props are allowed when the all have the same value\n      if all(overriding_props[0].value == p.value for p in overriding_props):\n        for p in optional_props:\n          p.delete(\"overridden by %s\" % str(overriding_props[0]))\n        continue\n      # or if dup is explicitly allowed for compat reason\n      if allow_dup:\n        # this could left one or more optional props unresolved.\n        # Convert them into non-optional because init doesn't understand ?=\n        # syntax\n        for p in optional_props:\n          p.optional = False\n        continue\n\n      success = False\n      sys.stderr.write(\"error: found duplicate sysprop assignments:\\n\")\n      for p in overriding_props:\n        sys.stderr.write(\"%s\\n\" % str(p))\n    elif len(overriding_props) == 1:\n      for p in optional_props:\n        p.delete(\"overridden by %s\" % str(overriding_props[0]))\n    else:\n      if len(optional_props) > 1:\n        for p in optional_props[:-1]:\n          p.delete(\"overridden by %s\" % str(optional_props[-1]))\n      # Make the last optional one as non-optional\n      optional_props[-1].optional = False\n\n  return success\n\nclass Prop:\n\n  def __init__(self, name, value, optional=False, comment=None):\n    self.name = name.strip()\n    self.value = value.strip()\n    if comment != None:\n      self.comments = [comment]\n    else:\n      self.comments = []\n    self.optional = optional\n\n  @staticmethod\n  def from_line(line):\n    line = line.rstrip('\\n')\n    if line.startswith(\"#\"):\n      return Prop(\"\", \"\", comment=line)\n    elif \"?=\" in line:\n      name, value = line.split(\"?=\", 1)\n      return Prop(name, value, optional=True)\n    elif \"=\" in line:\n      name, value = line.split(\"=\", 1)\n      return Prop(name, value, optional=False)\n    else:\n      # don't fail on invalid line\n      # TODO(jiyong) make this a hard error\n      return Prop(\"\", \"\", comment=line)\n\n  def is_comment(self):\n    return bool(self.comments and not self.name)\n\n  def is_optional(self):\n    return (not self.is_comment()) and self.optional\n\n  def make_as_comment(self):\n    # Prepend \"#\" to the last line which is the prop assignment\n    if not self.is_comment():\n      assignment = str(self).rsplit(\"\\n\", 1)[-1]\n      self.comments.append(\"#\" + assignment)\n      self.name = \"\"\n      self.value = \"\"\n\n  def delete(self, reason):\n    self.comments.append(\"# Removed by post_process_props.py because \" + reason)\n    self.make_as_comment()\n\n  def __str__(self):\n    assignment = []\n    if not self.is_comment():\n      operator = \"?=\" if self.is_optional() else \"=\"\n      assignment.append(self.name + operator + self.value)\n    return \"\\n\".join(self.comments + assignment)\n\nclass PropList:\n\n  def __init__(self, filename):\n    with open(filename) as f:\n      self.props = [Prop.from_line(l)\n                    for l in f.readlines() if l.strip() != \"\"]\n\n  def get_all_props(self):\n    return [p for p in self.props if not p.is_comment()]\n\n  def get_all_names(self):\n    return set([p.name for p in self.get_all_props()])\n\n  def get_props(self, name):\n    return [p for p in self.get_all_props() if p.name == name]\n\n  def get_value(self, name):\n    # Caution: only the value of the first sysprop having the name is returned.\n    return next((p.value for p in self.props if p.name == name), \"\")\n\n  def put(self, name, value):\n    # Note: when there is an optional prop for the name, its value isn't changed.\n    # Instead a new non-optional prop is appended, which will override the\n    # optional prop. Otherwise, the new value might be overridden by an existing\n    # non-optional prop of the same name.\n    index = next((i for i,p in enumerate(self.props)\n                  if p.name == name and not p.is_optional()), -1)\n    if index == -1:\n      self.props.append(Prop(name, value,\n                             comment=\"# Auto-added by post_process_props.py\"))\n    else:\n      self.props[index].comments.append(\n          \"# Value overridden by post_process_props.py. Original value: %s\" %\n          self.props[index].value)\n      self.props[index].value = value\n\n  def write(self, filename):\n    with open(filename, 'w+') as f:\n      for p in self.props:\n        f.write(str(p) + \"\\n\")\n\ndef main(argv):\n  parser = argparse.ArgumentParser(description=\"Post-process build.prop file\")\n  parser.add_argument(\"--allow-dup\", dest=\"allow_dup\", action=\"store_true\",\n                      default=False)\n  parser.add_argument(\"filename\")\n  parser.add_argument(\"disallowed_keys\", metavar=\"KEY\", type=str, nargs=\"*\")\n  parser.add_argument(\"--sdk-version\", type=int, required=True)\n  parser.add_argument(\"--kernel-version-file-for-uffd-gc\", required=True)\n  args = parser.parse_args()\n\n  if not args.filename.endswith(\"/build.prop\"):\n    sys.stderr.write(\"bad command line: \" + str(argv) + \"\\n\")\n    sys.exit(1)\n\n  props = PropList(args.filename)\n  mangle_build_prop(props, args.kernel_version_file_for_uffd_gc)\n  if not override_optional_props(props, args.allow_dup):\n    sys.exit(1)\n  if not validate_grf_props(props):\n    sys.exit(1)\n  if not validate(props):\n    sys.exit(1)\n\n  # Drop any disallowed keys\n  for key in args.disallowed_keys:\n    for p in props.get_props(key):\n      p.delete(\"%s is a disallowed key\" % key)\n\n  props.write(args.filename)\n\nif __name__ == \"__main__\":\n  main(sys.argv)\n"
  },
  {
    "path": "tools/post_process_props_unittest.xml",
    "content": "<configuration description=\"Config to run post_process_props_unittest\">\n    <test class=\"com.android.tradefed.testtype.python.PythonBinaryHostTest\" >\n        <option name=\"par-file-name\" value=\"post_process_props_unittest\" />\n        <option name=\"test-timeout\" value=\"1m\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "tools/print_module_licenses.sh",
    "content": "#!/bin/sh\nfind . -name MODULE_LICENSE_\\* | sed 's/\\/MODULE_LICENSE_/\\ /' | sed 's/\\.\\///' | awk '{ print $2 \" \" $1; }' | sort\n"
  },
  {
    "path": "tools/product_config/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\njava_defaults {\n    name: \"product-config-defaults\",\n    srcs: [\"src/**/*.java\"],\n}\n\njava_binary_host {\n    name: \"product-config\",\n    defaults: [\"product-config-defaults\"],\n    manifest: \"MANIFEST.MF\"\n}\n\njava_test_host {\n    name: \"product-config-test\",\n    defaults: [\"product-config-defaults\"],\n    srcs: [\n        \"test/**/*.java\",\n    ],\n    static_libs: [\n        \"junit\"\n    ],\n    manifest: \"TEST_MANIFEST.MF\",\n    test_suites: [\"general-tests\"]\n}\n"
  },
  {
    "path": "tools/product_config/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nMain-Class: com.android.build.config.Main\n"
  },
  {
    "path": "tools/product_config/TEST_MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nMain-Class: com.android.build.config.TestRunner\n"
  },
  {
    "path": "tools/product_config/inherit_tree.py",
    "content": "#!/usr/bin/env python3\n\n#\n# Run from the root of the tree, after product-config has been run to see\n# the product inheritance hierarchy for the current lunch target.\n#\n\nimport csv\nimport sys\n\ndef PrintNodes(graph, node, prefix):\n  sys.stdout.write(\"%s%s\" % (prefix, node))\n  children = graph.get(node, [])\n  if children:\n    sys.stdout.write(\" {\\n\")\n    for child in sorted(graph.get(node, [])):\n      PrintNodes(graph, child, prefix + \"  \")\n    sys.stdout.write(\"%s}\\n\" % prefix);\n  else:\n    sys.stdout.write(\"\\n\")\n\ndef main(argv):\n  if len(argv) != 2:\n    print(\"usage: inherit_tree.py out/$TARGET_PRODUCT-$TARGET_BUILD_VARIANT/dumpconfig.csv\")\n    sys.exit(1)\n\n  root = None\n  graph = {}\n  with open(argv[1], newline='') as csvfile:\n    for line in csv.reader(csvfile):\n      if not root:\n        # Look for PRODUCTS\n        if len(line) < 3 or line[0] != \"phase\" or line[1] != \"PRODUCTS\":\n          continue\n        root = line[2]\n      else:\n        # Everything else\n        if len(line) < 3 or line[0] != \"inherit\":\n          continue\n        graph.setdefault(line[1], list()).append(line[2])\n\n  PrintNodes(graph, root, \"\")\n\n\nif __name__ == \"__main__\":\n  main(sys.argv)\n\n# vim: set expandtab ts=2 sw=2 sts=2:\n\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/CommandException.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\n/**\n * Exception to indicate that a fatal error has occurred.  Throwing this\n * will cause errors to be printed, cleanup to occur, and the command to\n * exit with a failure code.\n *\n * These are user errors. Throwing other exceptions will result in\n * the stack trace being shown.\n */\npublic class CommandException extends RuntimeException {\n    public CommandException() {\n        super();\n    }\n\n    public CommandException(String message) {\n        super(message);\n    }\n\n    public CommandException(String message, Throwable chain) {\n        super(message, chain);\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/ConfigBase.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\n/**\n * Common parts between MakeConfig and the to-be-added GenericConfig, BazelConfig and SoongConfig.\n */\npublic class ConfigBase {\n    protected String mPhase;\n    protected List<String> mRootNodes;\n\n    /**\n     * State of the make varaible environment from before the first config file.\n     */\n    protected Map<String, Str> mInitialVariables = new HashMap();\n\n    /**\n     * State of the make varaible environment from after the first config file.\n     */\n    protected Map<String, Str> mFinalVariables = new HashMap();\n\n\n    /**\n     * The variables that are handled specially.\n     */\n    protected final TreeMap<String, VarType> mProductVars = new TreeMap();\n\n    public void setPhase(String phase) {\n        mPhase = phase;\n    }\n\n    public String getPhase() {\n        return mPhase;\n    }\n\n    public void setRootNodes(List<String> filenames) {\n        mRootNodes = new ArrayList(filenames);\n    }\n\n    public List<String> getRootNodes() {\n        return mRootNodes;\n    }\n\n    public void addProductVar(String name, VarType type) {\n        mProductVars.put(name, type);\n    }\n\n    public TreeMap<String, VarType> getProductVars() {\n        return mProductVars;\n    }\n\n    public VarType getVarType(String name) {\n        final VarType t = mProductVars.get(name);\n        if (t != null) {\n            return t;\n        } else {\n            return VarType.UNKNOWN;\n        }\n    }\n\n    public boolean isProductVar(String name) {\n        return mProductVars.get(name) != null;\n    }\n\n    /**\n     * Return the state the make variable environment from before the first config file.\n     */\n    public Map<String, Str> getInitialVariables() {\n        return mInitialVariables;\n    }\n\n    /**\n     * Return the state the make variable environment from before the first config file.\n     */\n    public Map<String, Str> getFinalVariables() {\n        return mFinalVariables;\n    }\n\n    /**\n     * Copy common base class fields from that to this.\n     */\n    public void copyFrom(ConfigBase that) {\n        setPhase(that.getPhase());\n        setRootNodes(that.getRootNodes());\n        for (Map.Entry<String, VarType> entry: that.getProductVars().entrySet()) {\n            addProductVar(entry.getKey(), entry.getValue());\n        }\n        mInitialVariables = new HashMap(that.getInitialVariables());\n        mFinalVariables = new HashMap(that.getFinalVariables());\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/ConvertMakeToGenericConfig.java",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Converts a MakeConfig into a Generic config by applying heuristics about\n * the types of variable assignments that we do.\n */\npublic class ConvertMakeToGenericConfig {\n    private final Errors mErrors;\n\n    public ConvertMakeToGenericConfig(Errors errors) {\n        mErrors = errors;\n    }\n\n    public GenericConfig convert(Map<String, MakeConfig> make) {\n        final GenericConfig result = new GenericConfig();\n\n        final MakeConfig products = make.get(\"PRODUCTS\");\n        if (products == null) {\n            mErrors.ERROR_DUMPCONFIG.add(\"Could not find PRODUCTS phase in dumpconfig output.\");\n            return null;\n        }\n\n        // Base class fields\n        result.copyFrom(products);\n\n        // Each file\n        for (MakeConfig.ConfigFile f: products.getConfigFiles()) {\n            final GenericConfig.ConfigFile genericFile\n                    = new GenericConfig.ConfigFile(f.getFilename());\n            result.addConfigFile(genericFile);\n\n            final List<MakeConfig.Block> blocks = f.getBlocks();\n\n            // Some assertions:\n            // TODO: Include better context for these errors.\n            // There should always be at least a BEGIN and an AFTER, so assert this.\n            if (blocks.size() < 2) {\n                throw new RuntimeException(\"expected at least blocks.size() >= 2. Actcual size: \"\n                        + blocks.size());\n            }\n            if (blocks.get(0).getBlockType() != MakeConfig.BlockType.BEFORE) {\n                throw new RuntimeException(\"expected first block to be BEFORE\");\n            }\n            if (blocks.get(blocks.size() - 1).getBlockType() != MakeConfig.BlockType.AFTER) {\n                throw new RuntimeException(\"expected first block to be AFTER\");\n            }\n            // Everything in between should be an INHERIT block.\n            for (int index = 1; index < blocks.size() - 1; index++) {\n                if (blocks.get(index).getBlockType() != MakeConfig.BlockType.INHERIT) {\n                    throw new RuntimeException(\"expected INHERIT at block \" + index);\n                }\n            }\n\n            // Each block represents a snapshot of the interpreter variable state (minus a few big\n            // sets of variables which we don't export because they're used in the internals\n            // of node_fns.mk, so we know they're not necessary here). The first (BEFORE) one\n            // is everything that is set before the file is included, so it forms the base\n            // for everything else.\n            MakeConfig.Block prevBlock = blocks.get(0);\n\n            for (int index = 1; index < blocks.size(); index++) {\n                final MakeConfig.Block block = blocks.get(index);\n                for (final Map.Entry<String, Str> entry: block.getVars().entrySet()) {\n                    final String varName = entry.getKey();\n                    final GenericConfig.Assign assign = convertAssignment(block.getBlockType(),\n                            block.getInheritedFile(), products.getVarType(varName), varName,\n                            entry.getValue(), prevBlock.getVar(varName));\n                    if (assign != null) {\n                        genericFile.addStatement(assign);\n                    }\n                }\n                // Handle variables that are in prevBlock but not block -- they were\n                // deleted. Is this even possible, or do they show up as \"\"?  We will\n                // treat them as positive assigments to empty string\n                for (String prevName: prevBlock.getVars().keySet()) {\n                    if (!block.getVars().containsKey(prevName)) {\n                        genericFile.addStatement(\n                                new GenericConfig.Assign(prevName, new Str(\"\")));\n                    }\n                }\n                if (block.getBlockType() == MakeConfig.BlockType.INHERIT) {\n                    genericFile.addStatement(\n                            new GenericConfig.Inherit(block.getInheritedFile()));\n                }\n                // For next iteration\n                prevBlock = block;\n            }\n        }\n\n        // Overwrite the final variables with the ones that come from the PRODUCTS-EXPAND phase.\n        // Drop the ones that were newly defined between the two phases, but leave values\n        // that were modified between.  We do need to reproduce that logic in this tool.\n        final MakeConfig expand = make.get(\"PRODUCT-EXPAND\");\n        if (expand == null) {\n            mErrors.ERROR_DUMPCONFIG.add(\"Could not find PRODUCT-EXPAND phase in dumpconfig\"\n                    + \" output.\");\n            return null;\n        }\n        final Map<String, Str> productsFinal = products.getFinalVariables();\n        final Map<String, Str> expandInitial = expand.getInitialVariables();\n        final Map<String, Str> expandFinal = expand.getFinalVariables();\n        final Map<String, Str> finalFinal = result.getFinalVariables();\n        finalFinal.clear();\n        for (Map.Entry<String, Str> var: expandFinal.entrySet()) {\n            final String varName = var.getKey();\n            if (expandInitial.containsKey(varName) && !productsFinal.containsKey(varName)) {\n                continue;\n            }\n            finalFinal.put(varName, var.getValue());\n        }\n\n        return result;\n    }\n\n    /**\n     * Converts one variable from a MakeConfig Block into a GenericConfig Assignment.\n     */\n    GenericConfig.Assign convertAssignment(MakeConfig.BlockType blockType, Str inheritedFile,\n            VarType varType, String varName, Str varVal, Str prevVal) {\n        if (prevVal == null) {\n            // New variable.\n            return new GenericConfig.Assign(varName, varVal);\n        } else if (!varVal.equals(prevVal)) {\n            // The value changed from the last block.\n            if (varVal.length() == 0) {\n                // It was set to empty\n                return new GenericConfig.Assign(varName, varVal);\n            } else {\n                // Product vars have the @inherit processing. Other vars we\n                // will just ignore and put in one section at the end, based\n                // on the difference between the BEFORE and AFTER blocks.\n                if (varType == VarType.UNKNOWN) {\n                    if (blockType == MakeConfig.BlockType.AFTER) {\n                        // For UNKNOWN variables, we don't worry about the\n                        // intermediate steps, just take the final value.\n                        return new GenericConfig.Assign(varName, varVal);\n                    } else {\n                        return null;\n                    }\n                } else {\n                    return convertInheritedVar(blockType, inheritedFile,\n                            varName, varVal, prevVal);\n                }\n            }\n        } else {\n            // Variable not touched\n            return null;\n        }\n    }\n\n    /**\n     * Handle the special inherited values, where the inherit-product puts in the\n     * @inherit:... markers, adding Statements to the ConfigFile.\n     */\n    GenericConfig.Assign convertInheritedVar(MakeConfig.BlockType blockType, Str inheritedFile,\n            String varName, Str varVal, Str prevVal) {\n        String varText = varVal.toString();\n        String prevText = prevVal.toString().trim();\n        if (blockType == MakeConfig.BlockType.INHERIT) {\n            // inherit-product appends @inherit:... so drop that.\n            final String marker = \"@inherit:\" + inheritedFile;\n            if (varText.endsWith(marker)) {\n                varText = varText.substring(0, varText.length() - marker.length()).trim();\n            } else {\n                mErrors.ERROR_IMPROPER_PRODUCT_VAR_MARKER.add(varVal.getPosition(),\n                        \"Variable didn't end with marker \\\"\" + marker + \"\\\": \" + varText);\n            }\n        }\n\n        if (!varText.equals(prevText)) {\n            // If the variable value was actually changed.\n            final ArrayList<String> words = split(varText, prevText);\n            if (words.size() == 0) {\n                // Pure Assignment, none of the previous value is present.\n                return new GenericConfig.Assign(varName, new Str(varVal.getPosition(), varText));\n            } else {\n                // Self referential value (prepend, append, both).\n                if (words.size() > 2) {\n                    // This is indicative of a construction that might not be quite\n                    // what we want.  The above code will do something that works if it was\n                    // of the form \"VAR := a $(VAR) b $(VAR) c\", but if the original code\n                    // something else this won't work. This doesn't happen in AOSP, but\n                    // it's a theoretically possibility, so someone might do it.\n                    mErrors.WARNING_VARIABLE_RECURSION.add(varVal.getPosition(),\n                            \"Possible unsupported variable recursion: \"\n                                + varName + \" = \" + varVal + \" (prev=\" + prevVal + \")\");\n                }\n                return new GenericConfig.Assign(varName, Str.toList(varVal.getPosition(), words));\n            }\n        } else {\n            // Variable not touched\n            return null;\n        }\n    }\n\n    /**\n     * Split 'haystack' on occurrences of 'needle'. Trims each string of whitespace\n     * to preserve make list semantics.\n     */\n    private static ArrayList<String> split(String haystack, String needle) {\n        final ArrayList<String> result = new ArrayList();\n        final int needleLen = needle.length();\n        if (needleLen == 0) {\n            return result;\n        }\n        int start = 0;\n        int end;\n        while ((end = haystack.indexOf(needle, start)) >= 0) {\n            result.add(haystack.substring(start, end).trim());\n            start = end + needleLen;\n        }\n        result.add(haystack.substring(start).trim());\n        return result;\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/CsvParser.java",
    "content": "\n/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.android.build.config;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * A CSV parser.\n */\npublic class CsvParser {\n    /**\n     * Internal string buffer grows by this amount.\n     */\n    private static final int CHUNK_SIZE = 64 * 1024;\n\n    /**\n     * Error parsing.\n     */\n    public static class ParseException extends Exception {\n        private int mLine;\n        private int mColumn;\n\n        public ParseException(int line, int column, String message) {\n            super(message);\n            mLine = line;\n            mColumn = column;\n        }\n\n        /**\n         * Line number in source file.\n         */\n        public int getLine() {\n            return mLine;\n        }\n\n        /**\n         * Column in source file.\n         */\n        public int getColumn() {\n            return mColumn;\n        }\n    }\n\n    public static class Line {\n        private final int mLineNumber;\n        private final List<String> mFields;\n\n        Line(int lineno, List<String> fields) {\n            mLineNumber = lineno;\n            mFields = fields;\n        }\n\n        public int getLine() {\n            return mLineNumber;\n        }\n\n        public List<String> getFields() {\n            return mFields;\n        }\n    }\n\n    // Parser States\n    private static final int STATE_START_LINE = 0;\n    private static final int STATE_START_FIELD = 1;\n    private static final int STATE_INSIDE_QUOTED_FIELD = 2;\n    private static final int STATE_FIRST_QUOTATION_MARK = 3;\n    private static final int STATE_INSIDE_UNQUOTED_FIELD = 4;\n    private static final int STATE_DONE = 5;\n\n    // Parser Actions\n    private static final int ACTION_APPEND_CHAR = 1;\n    private static final int ACTION_FIELD_COMPLETE = 2;\n    private static final int ACTION_LINE_COMPLETE = 4;\n\n    /**\n     * Constructor.\n     */\n    private CsvParser() {\n    }\n\n    /**\n     * Reads CSV and returns a list of Line objects.\n     *\n     * Handles newlines inside fields quoted with double quotes (\").\n     *\n     * Doesn't report blank lines, but does include empty fields.\n     */\n    public static List<Line> parse(Reader reader)\n            throws ParseException, IOException {\n        ArrayList<Line> result = new ArrayList();\n        int line = 1;\n        int column = 1;\n        int pos = 0;\n        char[] buf = new char[CHUNK_SIZE];\n        HashMap<String,String> stringPool = new HashMap();\n        ArrayList<String> fields = new ArrayList();\n\n        int state = STATE_START_LINE;\n        while (state != STATE_DONE) {\n            int c = reader.read();\n            int action = 0;\n\n            if (state == STATE_START_LINE) {\n                if (c <= 0) {\n                    // No data, skip ACTION_LINE_COMPLETE.\n                    state = STATE_DONE;\n                } else if (c == '\"') {\n                    state = STATE_INSIDE_QUOTED_FIELD;\n                } else if (c == ',') {\n                    action = ACTION_FIELD_COMPLETE;\n                    state = STATE_START_FIELD;\n                } else if (c == '\\n') {\n                    // Consume the newline, state stays STATE_START_LINE.\n                } else {\n                    action = ACTION_APPEND_CHAR;\n                    state = STATE_INSIDE_UNQUOTED_FIELD;\n                }\n            } else if (state == STATE_START_FIELD) {\n                if (c <= 0) {\n                    // Field will be empty\n                    action = ACTION_FIELD_COMPLETE | ACTION_LINE_COMPLETE;\n                    state = STATE_DONE;\n                } else if (c == '\"') {\n                    state = STATE_INSIDE_QUOTED_FIELD;\n                } else if (c == ',') {\n                    action = ACTION_FIELD_COMPLETE;\n                    state = STATE_START_FIELD;\n                } else if (c == '\\n') {\n                    action = ACTION_FIELD_COMPLETE | ACTION_LINE_COMPLETE;\n                    state = STATE_START_LINE;\n                } else {\n                    action = ACTION_APPEND_CHAR;\n                    state = STATE_INSIDE_UNQUOTED_FIELD;\n                }\n            } else if (state == STATE_INSIDE_QUOTED_FIELD) {\n                if (c <= 0) {\n                    throw new ParseException(line, column,\n                            \"Bad input: End of input inside quoted field.\");\n                } else if (c == '\"') {\n                    state = STATE_FIRST_QUOTATION_MARK;\n                } else {\n                    action = ACTION_APPEND_CHAR;\n                }\n            } else if (state == STATE_FIRST_QUOTATION_MARK) {\n                if (c <= 0) {\n                    action = ACTION_FIELD_COMPLETE | ACTION_LINE_COMPLETE;\n                    state = STATE_DONE;\n                } else if (c == '\"') {\n                    action = ACTION_APPEND_CHAR;\n                    state = STATE_INSIDE_QUOTED_FIELD;\n                } else if (c == ',') {\n                    action = ACTION_FIELD_COMPLETE;\n                    state = STATE_START_FIELD;\n                } else if (c == '\\n') {\n                    action = ACTION_FIELD_COMPLETE | ACTION_LINE_COMPLETE;\n                    state = STATE_START_LINE;\n                } else {\n                    throw new ParseException(line, column,\n                            \"Bad input: Character after field ended or unquoted '\\\"'.\");\n                }\n            } else if (state == STATE_INSIDE_UNQUOTED_FIELD) {\n                if (c <= 0) {\n                    action = ACTION_FIELD_COMPLETE | ACTION_LINE_COMPLETE;\n                    state = STATE_DONE;\n                } else if (c == ',') {\n                    action = ACTION_FIELD_COMPLETE;\n                    state = STATE_START_FIELD;\n                } else if (c == '\\n') {\n                    action = ACTION_FIELD_COMPLETE | ACTION_LINE_COMPLETE;\n                    state = STATE_START_LINE;\n                } else {\n                    action = ACTION_APPEND_CHAR;\n                }\n            }\n\n            if ((action & ACTION_APPEND_CHAR) != 0) {\n                // Reallocate buffer if necessary. Hopefully not often because CHUNK_SIZE is big.\n                if (pos >= buf.length) {\n                    char[] old = buf;\n                    buf = new char[old.length + CHUNK_SIZE];\n                    System.arraycopy(old, 0, buf, 0, old.length);\n                }\n                // Store the character\n                buf[pos] = (char)c;\n                pos++;\n            }\n            if ((action & ACTION_FIELD_COMPLETE) != 0) {\n                // A lot of the strings are duplicated, so pool them to reduce peak memory\n                // usage. This could be made slightly better by having a custom key class\n                // that does the lookup without making a new String that gets immediately\n                // thrown away.\n                String field = new String(buf, 0, pos);\n                final String cached = stringPool.get(field);\n                if (cached == null) {\n                    stringPool.put(field, field);\n                } else {\n                    field = cached;\n                }\n                fields.add(field);\n                pos = 0;\n            }\n            if ((action & ACTION_LINE_COMPLETE) != 0) {\n                // Only report lines with any contents\n                if (fields.size() > 0) {\n                    result.add(new Line(line, fields));\n                    fields = new ArrayList();\n                }\n            }\n\n            if (c == '\\n') {\n                line++;\n                column = 1;\n            } else {\n                column++;\n            }\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/DumpConfigParser.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.android.build.config;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/**\n * Parses the output of ckati building build/make/core/dumpconfig.mk.\n *\n * The format is as follows:\n *   - All processed lines are colon (':') separated fields.\n *   - Lines before the dumpconfig_version line are dropped for forward compatibility\n *   - Lines where the first field is config_var describe variables declared in makefiles\n *     (implemented by the dump-config-vals macro)\n *          Field   Description\n *          0       \"config_var\" row type\n *          1       Product makefile being processed\n *          2       The variable name\n *          3       The value of the variable\n *          4       The location of the variable, as best tracked by kati\n */\npublic class DumpConfigParser {\n    private static final boolean DEBUG = false;\n\n    private final Errors mErrors;\n    private final String mFilename;\n    private final Reader mReader;\n\n    private final Map<String,MakeConfig> mResults = new HashMap();\n\n    private static final Pattern LIST_SEPARATOR = Pattern.compile(\"\\\\s+\");\n\n    /**\n     * Constructor.\n     */\n    private DumpConfigParser(Errors errors, String filename, Reader reader) {\n        mErrors = errors;\n        mFilename = filename;\n        mReader = reader;\n    }\n\n    /**\n     * Parse the text into a map of the phase names to MakeConfig objects.\n     */\n    public static Map<String,MakeConfig> parse(Errors errors, String filename, Reader reader)\n            throws CsvParser.ParseException, IOException {\n        DumpConfigParser parser = new DumpConfigParser(errors, filename, reader);\n        parser.parseImpl();\n        return parser.mResults;\n    }\n\n    /**\n     * Parse the input.\n     */\n    private void parseImpl() throws CsvParser.ParseException, IOException {\n        final List<CsvParser.Line> lines = CsvParser.parse(mReader);\n        final int lineCount = lines.size();\n        int index = 0;\n\n        int dumpconfigVersion = 0;\n\n        // Ignore lines until until we get a dumpconfig_version line for forward compatibility.\n        // In a previous life, this loop parsed from all of kati's stdout, not just the file\n        // that dumpconfig.mk writes, but it's harmless to leave this loop in.  It gives us a\n        // little bit of flexibility which we probably won't need anyway, this tool probably\n        // won't diverge from dumpconfig.mk anyway.\n        for (; index < lineCount; index++) {\n            final CsvParser.Line line = lines.get(index);\n            final List<String> fields = line.getFields();\n\n            if (matchLineType(line, \"dumpconfig_version\", 1)) {\n                try {\n                    dumpconfigVersion = Integer.parseInt(fields.get(1));\n                } catch (NumberFormatException ex) {\n                    mErrors.WARNING_DUMPCONFIG.add(\n                            new Position(mFilename, line.getLine()),\n                            \"Couldn't parse dumpconfig_version: \" + fields.get(1));\n                }\n                break;\n            }\n        }\n\n        // If we never saw dumpconfig_version, there's a problem with the command, so stop.\n        if (dumpconfigVersion == 0) {\n            mErrors.ERROR_DUMPCONFIG.fatal(\n                    new Position(mFilename),\n                    \"Never saw a valid dumpconfig_version line.\");\n        }\n\n        // Any lines before the start signal will be dropped. We create garbage objects\n        // here to avoid having to check for null everywhere.\n        MakeConfig makeConfig = new MakeConfig();\n        MakeConfig.ConfigFile configFile = new MakeConfig.ConfigFile(\"<ignored>\");\n        MakeConfig.Block block = new MakeConfig.Block(MakeConfig.BlockType.UNSET);\n        Map<String, Str> initialVariables = new HashMap();\n        Map<String, Str> finalVariables = new HashMap();\n\n        // Number of \"phases\" we've seen so far.\n        for (; index < lineCount; index++) {\n            final CsvParser.Line line = lines.get(index);\n            final List<String> fields = line.getFields();\n            final String lineType = fields.get(0);\n\n            if (matchLineType(line, \"phase\", 2)) {\n                // Start the new one\n                makeConfig = new MakeConfig();\n                makeConfig.setPhase(fields.get(1));\n                makeConfig.setRootNodes(splitList(fields.get(2)));\n                // If there is a duplicate phase of the same name, continue parsing, but\n                // don't add it.  Emit a warning.\n                if (!mResults.containsKey(makeConfig.getPhase())) {\n                    mResults.put(makeConfig.getPhase(), makeConfig);\n                } else {\n                    mErrors.WARNING_DUMPCONFIG.add(\n                            new Position(mFilename, line.getLine()),\n                            \"Duplicate phase: \" + makeConfig.getPhase()\n                                + \". This one will be dropped.\");\n                }\n                initialVariables = makeConfig.getInitialVariables();\n                finalVariables = makeConfig.getFinalVariables();\n\n                if (DEBUG) {\n                    System.out.println(\"PHASE:\");\n                    System.out.println(\"  \" + makeConfig.getPhase());\n                    System.out.println(\"  \" + makeConfig.getRootNodes());\n                }\n            } else if (matchLineType(line, \"var\", 2)) {\n                final VarType type = \"list\".equals(fields.get(1)) ? VarType.LIST : VarType.SINGLE;\n                makeConfig.addProductVar(fields.get(2), type);\n\n                if (DEBUG) {\n                    System.out.println(\"  VAR: \" + type + \" \" + fields.get(2));\n                }\n            } else if (matchLineType(line, \"import\", 1)) {\n                final List<String> importStack = splitList(fields.get(1));\n                if (importStack.size() == 0) {\n                    mErrors.WARNING_DUMPCONFIG.add(\n                            new Position(mFilename, line.getLine()),\n                            \"'import' line with empty include stack.\");\n                    continue;\n                }\n\n                // The beginning of importing a new file.\n                configFile = new MakeConfig.ConfigFile(importStack.get(0));\n                if (makeConfig.addConfigFile(configFile) != null) {\n                    mErrors.WARNING_DUMPCONFIG.add(\n                            new Position(mFilename, line.getLine()),\n                            \"Duplicate file imported in section: \" + configFile.getFilename());\n                }\n                // We expect a Variable block next.\n                block = new MakeConfig.Block(MakeConfig.BlockType.BEFORE);\n                configFile.addBlock(block);\n\n                if (DEBUG) {\n                    System.out.println(\"  IMPORT: \" + configFile.getFilename());\n                }\n            } else if (matchLineType(line, \"inherit\", 2)) {\n                final String currentFile = fields.get(1);\n                final String inheritedFile = fields.get(2);\n                if (!configFile.getFilename().equals(currentFile)) {\n                    mErrors.WARNING_DUMPCONFIG.add(\n                            new Position(mFilename, line.getLine()),\n                            \"Unexpected current file in 'inherit' line '\" + currentFile\n                                + \"' while processing '\" + configFile.getFilename() + \"'\");\n                    continue;\n                }\n\n                // There is already a file in progress, so add another var block to that.\n                block = new MakeConfig.Block(MakeConfig.BlockType.INHERIT);\n                // TODO: Make dumpconfig.mk also output a Position for inherit-product\n                block.setInheritedFile(new Str(inheritedFile));\n                configFile.addBlock(block);\n\n                if (DEBUG) {\n                    System.out.println(\"  INHERIT: \" + inheritedFile);\n                }\n            } else if (matchLineType(line, \"imported\", 1)) {\n                final List<String> importStack = splitList(fields.get(1));\n                if (importStack.size() == 0) {\n                    mErrors.WARNING_DUMPCONFIG.add(\n                            new Position(mFilename, line.getLine()),\n                            \"'imported' line with empty include stack.\");\n                    continue;\n                }\n                final String currentFile = importStack.get(0);\n                if (!configFile.getFilename().equals(currentFile)) {\n                    mErrors.WARNING_DUMPCONFIG.add(\n                            new Position(mFilename, line.getLine()),\n                            \"Unexpected current file in 'imported' line '\" + currentFile\n                                + \"' while processing '\" + configFile.getFilename() + \"'\");\n                    continue;\n                }\n\n                // There is already a file in progress, so add another var block to that.\n                // This will be the last one, but will check that after parsing.\n                block = new MakeConfig.Block(MakeConfig.BlockType.AFTER);\n                configFile.addBlock(block);\n\n                if (DEBUG) {\n                    System.out.println(\"  AFTER: \" + currentFile);\n                }\n            } else if (matchLineType(line, \"val\", 5)) {\n                final String productMakefile = fields.get(1);\n                final String blockTypeString = fields.get(2);\n                final String varName = fields.get(3);\n                final String varValue = fields.get(4);\n                final Position pos = Position.parse(fields.get(5));\n                final Str str = new Str(pos, varValue);\n\n                if (blockTypeString.equals(\"initial\")) {\n                    initialVariables.put(varName, str);\n                } else if (blockTypeString.equals(\"final\")) {\n                    finalVariables.put(varName, str);\n                } else {\n                    if (!productMakefile.equals(configFile.getFilename())) {\n                        mErrors.WARNING_DUMPCONFIG.add(\n                                new Position(mFilename, line.getLine()),\n                                \"Mismatched 'val' product makefile.\"\n                                    + \" Expected: \" + configFile.getFilename()\n                                    + \" Saw: \" + productMakefile);\n                        continue;\n                    }\n\n                    final MakeConfig.BlockType blockType = parseBlockType(line, blockTypeString);\n                    if (blockType == null) {\n                        continue;\n                    }\n                    if (blockType != block.getBlockType()) {\n                        mErrors.WARNING_DUMPCONFIG.add(\n                                new Position(mFilename, line.getLine()),\n                                \"Mismatched 'val' block type.\"\n                                    + \" Expected: \" + block.getBlockType()\n                                    + \" Saw: \" + blockType);\n                    }\n\n                    // Add the variable to the block in progress\n                    block.addVar(varName, str);\n                }\n            } else {\n                if (DEBUG) {\n                    System.out.print(\"# \");\n                    for (int d = 0; d < fields.size(); d++) {\n                        System.out.print(fields.get(d));\n                        if (d != fields.size() - 1) {\n                            System.out.print(\",\");\n                        }\n                    }\n                    System.out.println();\n                }\n            }\n        }\n    }\n\n    /**\n     * Return true if the line type matches 'lineType' and there are at least 'fieldCount'\n     * fields (not including the first field which is the line type).\n     */\n    private boolean matchLineType(CsvParser.Line line, String lineType, int fieldCount) {\n        final List<String> fields = line.getFields();\n        if (!lineType.equals(fields.get(0))) {\n            return false;\n        }\n        if (fields.size() < (fieldCount + 1)) {\n            mErrors.WARNING_DUMPCONFIG.add(new Position(mFilename, line.getLine()),\n                    fields.get(0) + \" line has \" + fields.size() + \" fields. Expected at least \"\n                    + (fieldCount + 1) + \" fields.\");\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * Split a string with space separated items (i.e. the make list format) into a List<String>.\n     */\n    private static List<String> splitList(String text) {\n        // Arrays.asList returns a fixed-length List, so we copy it into an ArrayList to not\n        // propagate that surprise detail downstream.\n        return new ArrayList(Arrays.asList(LIST_SEPARATOR.split(text.trim())));\n    }\n\n    /**\n     * Parse a BockType or issue a warning if it can't be parsed.\n     */\n    private MakeConfig.BlockType parseBlockType(CsvParser.Line line, String text) {\n        if (\"before\".equals(text)) {\n            return MakeConfig.BlockType.BEFORE;\n        } else if (\"inherit\".equals(text)) {\n            return MakeConfig.BlockType.INHERIT;\n        } else if (\"after\".equals(text)) {\n            return MakeConfig.BlockType.AFTER;\n        } else {\n            mErrors.WARNING_DUMPCONFIG.add(\n                    new Position(mFilename, line.getLine()),\n                    \"Invalid block type: \" + text);\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/ErrorReporter.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.lang.reflect.Field;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Base class for reporting errors.\n */\npublic class ErrorReporter {\n    /**\n     * List of Entries that have occurred.\n     */\n    // Also used as the lock for this object.\n    private final ArrayList<Entry> mEntries = new ArrayList();\n\n    /**\n     * The categories that are for this Errors object.\n     */\n    private Map<Integer, Category> mCategories;\n\n    /**\n     * Whether there has been a warning or an error yet.\n     */\n    private boolean mHadWarningOrError;\n\n    /**\n     * Whether there has been an error yet.\n     */\n    private boolean mHadError;\n\n    public static class FatalException extends RuntimeException {\n        FatalException(String message) {\n            super(message);\n        }\n\n        FatalException(String message, Throwable chain) {\n            super(message, chain);\n        }\n    }\n\n    /**\n     * Whether errors are errors, warnings or hidden.\n     */\n    public static enum Level {\n        HIDDEN(\"hidden\"),\n        WARNING(\"warning\"),\n        ERROR(\"error\");\n\n        private final String mLabel;\n\n        Level(String label) {\n            mLabel = label;\n        }\n\n        String getLabel() {\n            return mLabel;\n        }\n    }\n\n    /**\n     * The available error codes.\n     */\n    public class Category {\n        private final int mCode;\n        private boolean mIsLevelSettable;\n        private Level mLevel;\n        private String mHelp;\n\n        /**\n         * Construct a Category object.\n         */\n        public Category(int code, boolean isLevelSettable, Level level, String help) {\n            if (!isLevelSettable && level != Level.ERROR) {\n                throw new RuntimeException(\"Don't have WARNING or HIDDEN without isLevelSettable\");\n            }\n            mCode = code;\n            mIsLevelSettable = isLevelSettable;\n            mLevel = level;\n            mHelp = help;\n        }\n\n        /**\n         * Get the numeric code for the Category, which can be used to set the level.\n         */\n        public int getCode() {\n            return mCode;\n        }\n\n        /**\n         * Get whether the level of this Category can be changed.\n         */\n        public boolean isLevelSettable() {\n            return mIsLevelSettable;\n        }\n\n        /**\n         * Set the level of this category.\n         */\n        public void setLevel(Level level) {\n            if (!mIsLevelSettable) {\n                throw new RuntimeException(\"Can't set level for error \" + mCode);\n            }\n            mLevel = level;\n        }\n\n        /**\n         * Return the level, including any overrides.\n         */\n        public Level getLevel() {\n            return mLevel;\n        }\n\n        /**\n         * Return the category's help text.\n         */\n        public String getHelp() {\n            return mHelp;\n        }\n\n        /**\n         * Add an error with no source position.\n         */\n        public void add(String message) {\n            ErrorReporter.this.add(this, false, new Position(), message);\n        }\n\n        /**\n         * Add an error.\n         */\n        public void add(Position pos, String message) {\n            ErrorReporter.this.add(this, false, pos, message);\n        }\n\n        /**\n         * Add an error with no source position, and throw a FatalException, stopping processing\n         * immediately.\n         */\n        public void fatal(String message) {\n            ErrorReporter.this.add(this, true, new Position(), message);\n        }\n\n        /**\n         * Add an error, and throw a FatalException, stopping processing immediately.\n         */\n        public void fatal(Position pos, String message) {\n            ErrorReporter.this.add(this, true, pos, message);\n        }\n    }\n\n    /**\n     * An instance of an error happening.\n     */\n    public static class Entry {\n        private final Category mCategory;\n        private final Position mPosition;\n        private final String mMessage;\n\n        Entry(Category category, Position position, String message) {\n            mCategory = category;\n            mPosition = position;\n            mMessage = message;\n        }\n\n        public Category getCategory() {\n            return mCategory;\n        }\n\n        public Position getPosition() {\n            return mPosition;\n        }\n\n        public String getMessage() {\n            return mMessage;\n        }\n\n        @Override\n        public String toString() {\n            return mPosition\n                    + \"[\" + mCategory.getLevel().getLabel() + \" \" + mCategory.getCode() + \"] \"\n                    + mMessage;\n        }\n    }\n\n    private void initLocked() {\n        if (mCategories == null) {\n            HashMap<Integer, Category> categories = new HashMap();\n            for (Field field: getClass().getFields()) {\n                if (Category.class.isAssignableFrom(field.getType())) {\n                    Category category = null;\n                    try {\n                        category = (Category)field.get(this);\n                    } catch (IllegalAccessException ex) {\n                        // Wrap and rethrow, this is always on this class, so it's\n                        // our programming error if this happens.\n                        throw new RuntimeException(\"Categories on Errors should be public.\", ex);\n                    }\n                    Category prev = categories.put(category.getCode(), category);\n                    if (prev != null) {\n                        throw new RuntimeException(\"Duplicate categories with code \"\n                                + category.getCode());\n                    }\n                }\n            }\n            mCategories = Collections.unmodifiableMap(categories);\n        }\n    }\n\n    /**\n     * Returns a map of the category codes to the categories.\n     */\n    public Map<Integer, Category> getCategories() {\n        synchronized (mEntries) {\n            initLocked();\n            return mCategories;\n        }\n    }\n\n    /**\n     * Add an error.\n     */\n    private void add(Category category, boolean fatal, Position pos, String message) {\n        synchronized (mEntries) {\n            initLocked();\n            if (mCategories.get(category.getCode()) != category) {\n                throw new RuntimeException(\"Errors.Category used from the wrong Errors object.\");\n            }\n            final Entry entry = new Entry(category, pos, message);\n            mEntries.add(entry);\n            final Level level = category.getLevel();\n            if (level == Level.WARNING || level == Level.ERROR) {\n                mHadWarningOrError = true;\n            }\n            if (level == Level.ERROR) {\n                mHadError = true;\n            }\n            if (fatal) {\n                throw new FatalException(entry.toString());\n            }\n        }\n    }\n\n    /**\n     * Returns whether there has been a warning or an error yet.\n     */\n    public boolean hadWarningOrError() {\n        synchronized (mEntries) {\n            return mHadWarningOrError;\n        }\n    }\n\n    /**\n     * Returns whether there has been an error yet.\n     */\n    public boolean hadError() {\n        synchronized (mEntries) {\n            return mHadError;\n        }\n    }\n\n    /**\n     * Returns a list of all entries that were added.\n     */\n    public List<Entry> getEntries() {\n        synchronized (mEntries) {\n            return new ArrayList<Entry>(mEntries);\n        }\n    }\n\n    /**\n     * Prints the errors.\n     */\n    public void printErrors(PrintStream out) {\n        synchronized (mEntries) {\n            for (Entry entry: mEntries) {\n                if (entry.getCategory().getLevel() == Level.HIDDEN) {\n                    continue;\n                }\n                out.println(entry.toString());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/Errors.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.lang.reflect.Field;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Error constants and error reporting.\n * <p>\n * <b>Naming Convention:</b>\n * <ul>\n *  <li>ERROR_ for Categories with isLevelSettable false and Level.ERROR\n *  <li>WARNING_ for Categories with isLevelSettable true and default WARNING or HIDDEN\n *  <li>Don't have isLevelSettable true and not ERROR. (The constructor asserts this).\n * </ul>\n */\npublic class Errors extends ErrorReporter {\n\n    public final Category ERROR_COMMAND_LINE = new Category(1, false, Level.ERROR,\n            \"Error on the command line.\");\n\n    public final Category WARNING_UNKNOWN_COMMAND_LINE_ERROR = new Category(2, true, Level.HIDDEN,\n            \"Passing unknown errors on the command line.  Hidden by default for\\n\"\n            + \"forward compatibility.\");\n\n    public final Category ERROR_KATI = new Category(3, false, Level.ERROR,\n            \"Error executing or reading from Kati.\");\n\n    public final Category WARNING_DUMPCONFIG = new Category(4, true, Level.WARNING,\n            \"Anomaly parsing the output of kati and dumpconfig.mk.\");\n\n    public final Category ERROR_DUMPCONFIG = new Category(5, false, Level.ERROR,\n            \"Error parsing the output of kati and dumpconfig.mk.\");\n\n    public final Category WARNING_VARIABLE_RECURSION = new Category(6, true, Level.WARNING,\n            \"Possible unsupported variable recursion.\");\n\n    // This could be a warning, but it's very likely that the data is corrupted somehow\n    // if we're seeing this.\n    public final Category ERROR_IMPROPER_PRODUCT_VAR_MARKER = new Category(7, true, Level.ERROR,\n            \"Bad input from dumpvars causing corrupted product variables.\");\n\n    public final Category ERROR_MISSING_CONFIG_FILE = new Category(8, true, Level.ERROR,\n            \"Unable to find config file.\");\n\n    public final Category ERROR_INFINITE_RECURSION = new Category(9, true, Level.ERROR,\n            \"A file tries to inherit-product from itself or its own inherited products.\");\n\n    // TODO: This will become obsolete when it is possible to have starlark-based product\n    // config files.\n    public final Category WARNING_DIFFERENT_FROM_KATI = new Category(1000, true, Level.WARNING,\n            \"The cross-check with the original kati implementation failed.\");\n\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/FlatConfig.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\n/**\n * Flattened configuration -- set of variables after all assignments and inherits have\n * been executed.\n */\npublic class FlatConfig extends ConfigBase {\n\n    private final TreeMap<String, Value> mValues = new TreeMap();\n\n    public TreeMap<String, Value> getValues() {\n        return mValues;\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/FlattenConfig.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.regex.Pattern;\n\npublic class FlattenConfig {\n    private static final Pattern RE_SPACE = Pattern.compile(\"\\\\p{Space}+\");\n    private static final String PRODUCTS_PREFIX = \"PRODUCTS\";\n\n    private final Errors mErrors;\n    private final GenericConfig mGenericConfig;\n    private final Map<String, GenericConfig.ConfigFile> mGenericConfigs;\n    private final FlatConfig mResult = new FlatConfig();\n    private final Map<String, Value> mVariables;\n    /**\n     * Files that have been visited, to prevent infinite recursion. There are no\n     * conditionals at this point in the processing, so we don't need a stack, just\n     * a single set.\n     */\n    private final Set<Str> mStack = new HashSet();\n\n\n    private FlattenConfig(Errors errors, GenericConfig genericConfig) {\n        mErrors = errors;\n        mGenericConfig = genericConfig;\n        mGenericConfigs = genericConfig.getFiles();\n        mVariables = mResult.getValues();\n\n        // Base class fields\n        mResult.copyFrom(genericConfig);\n    }\n\n    /**\n     * Flatten a GenericConfig to a FlatConfig.\n     *\n     * Makes three passes through the genericConfig, one to flatten the single variables,\n     * one to flatten the list variables, and one to flatten the unknown variables. Each\n     * has a slightly different algorithm.\n     */\n    public static FlatConfig flatten(Errors errors, GenericConfig genericConfig) {\n        final FlattenConfig flattener = new FlattenConfig(errors, genericConfig);\n        return flattener.flattenImpl();\n    }\n\n    private FlatConfig flattenImpl() {\n        final List<String> rootNodes = mGenericConfig.getRootNodes();\n        if (rootNodes.size() == 0) {\n            mErrors.ERROR_DUMPCONFIG.add(\"No root nodes in PRODUCTS phase.\");\n            return null;\n        } else if (rootNodes.size() != 1) {\n            final StringBuilder msg = new StringBuilder(\n                    \"Ignoring extra root nodes in PRODUCTS phase. All nodes are:\");\n            for (final String rn: rootNodes) {\n                msg.append(' ');\n                msg.append(rn);\n            }\n            mErrors.WARNING_DUMPCONFIG.add(msg.toString());\n        }\n        final String root = rootNodes.get(0);\n\n        // TODO: Do we need to worry about the initial state of variables? Anything\n        // that from the product config\n\n        flattenListVars(root);\n        flattenSingleVars(root);\n        flattenUnknownVars(root);\n        flattenInheritsFrom(root);\n\n        setDefaultKnownVars();\n\n        // TODO: This only supports the single product mode of import-nodes, which is all the\n        // real build does. m product-graph and friends will have to be rewritten.\n        mVariables.put(\"PRODUCTS\", new Value(VarType.UNKNOWN, new Str(root)));\n\n        return mResult;\n    }\n\n    interface AssignCallback {\n        void onAssignStatement(GenericConfig.Assign assign);\n    }\n\n    interface InheritCallback {\n        void onInheritStatement(GenericConfig.Inherit assign);\n    }\n\n    /**\n     * Do a bunch of validity checks, and then iterate through each of the statements\n     * in the given file.  For Assignments, the callback is only called for variables\n     * matching varType.\n     *\n     * Adds makefiles which have been traversed to the 'seen' set, and will not traverse\n     * into an inherit statement if its makefile has already been seen.\n     */\n    private void forEachStatement(Str filename, VarType varType, Set<String> seen,\n            AssignCallback assigner, InheritCallback inheriter) {\n        if (mStack.contains(filename)) {\n            mErrors.ERROR_INFINITE_RECURSION.add(filename.getPosition(),\n                    \"File is already in the inherit-product stack: \" + filename);\n            return;\n        }\n\n        mStack.add(filename);\n        try {\n            final GenericConfig.ConfigFile genericFile = mGenericConfigs.get(filename.toString());\n\n            if (genericFile == null) {\n                mErrors.ERROR_MISSING_CONFIG_FILE.add(filename.getPosition(),\n                        \"Unable to find config file: \" + filename);\n                return;\n            }\n\n            for (final GenericConfig.Statement statement: genericFile.getStatements()) {\n                if (statement instanceof GenericConfig.Assign) {\n                    if (assigner != null) {\n                        final GenericConfig.Assign assign = (GenericConfig.Assign)statement;\n                        final String varName = assign.getName();\n\n                        // Assert that we're not stomping on another variable, which\n                        // really should be impossible at this point.\n                        assertVarType(filename, varName);\n\n                        if (mGenericConfig.getVarType(varName) == varType) {\n                            assigner.onAssignStatement(assign);\n                        }\n                    }\n                } else if (statement instanceof GenericConfig.Inherit) {\n                    if (inheriter != null) {\n                        final GenericConfig.Inherit inherit = (GenericConfig.Inherit)statement;\n                        if (seen != null) {\n                            if (seen.contains(inherit.getFilename().toString())) {\n                                continue;\n                            }\n                            seen.add(inherit.getFilename().toString());\n                        }\n                        inheriter.onInheritStatement(inherit);\n                    }\n                }\n            }\n        } finally {\n            // Also executes after return statements, so we always remove this.\n            mStack.remove(filename);\n        }\n    }\n\n    /**\n     * Call 'inheriter' for each child of 'filename' in alphabetical order.\n     */\n    private void forEachInheritAlpha(final Str filename, VarType varType, Set<String> seen,\n            InheritCallback inheriter) {\n        final TreeMap<Str, GenericConfig.Inherit> alpha = new TreeMap();\n        forEachStatement(filename, varType, null, null,\n                (inherit) -> {\n                    alpha.put(inherit.getFilename(), inherit);\n                });\n        for (final GenericConfig.Inherit inherit: alpha.values()) {\n            // Handle 'seen' here where we actaully call back, not before, so that\n            // the proper traversal order is preserved.\n            if (seen != null) {\n                if (seen.contains(inherit.getFilename().toString())) {\n                    continue;\n                }\n                seen.add(inherit.getFilename().toString());\n            }\n            inheriter.onInheritStatement(inherit);\n        }\n    }\n\n    /**\n     * Traverse the inheritance hierarchy, setting list-value product config variables.\n     */\n    private void flattenListVars(final String filename) {\n        Map<String, Value> vars = flattenListVars(new Str(filename), new HashSet());\n        // Add the result of the recursion to mVariables. We know there will be\n        // no collisions because this function only handles list variables.\n        for (Map.Entry<String, Value> entry: vars.entrySet()) {\n            mVariables.put(entry.getKey(), entry.getValue());\n        }\n    }\n\n    /**\n     * Return the variables defined, recursively, by 'filename.' The 'seen' set\n     * accumulates which nodes have been visited, as each is only done once.\n     *\n     * This convoluted algorithm isn't ideal, but it matches what is in node_fns.mk.\n     */\n    private Map<String, Value> flattenListVars(final Str filename, Set<String> seen) {\n        Map<String, Value> result = new HashMap();\n\n        // Recurse into our children first in alphabetical order, building a map of\n        // that filename to its flattened values.  The order matters here because\n        // we will only look at each child once, and when a file appears multiple\n        // times, its variables must have the right set, based on whether it's been\n        // seen before. This preserves the order from node_fns.mk.\n\n        // Child filename --> { varname --> value }\n        final Map<Str, Map<String, Value>> children = new HashMap();\n        forEachInheritAlpha(filename, VarType.LIST, seen,\n                (inherit) -> {\n                    final Str child = inherit.getFilename();\n                    children.put(child, flattenListVars(child, seen));\n                });\n\n        // Now, traverse the values again in the original source order to concatenate the values.\n        // Note that the contcatenation order is *different* from the inherit order above.\n        forEachStatement(filename, VarType.LIST, null,\n                (assign) -> {\n                    assignToListVar(result, assign.getName(), assign.getValue());\n                },\n                (inherit) -> {\n                    final Map<String, Value> child = children.get(inherit.getFilename());\n                    // child == null happens if this node has been visited before.\n                    if (child != null) {\n                        for (Map.Entry<String, Value> entry: child.entrySet()) {\n                            final String varName = entry.getKey();\n                            final Value varVal = entry.getValue();\n                            appendToListVar(result, varName, varVal.getList());\n                        }\n                    }\n                });\n\n        return result;\n    }\n\n    /**\n     * Traverse the inheritance hierarchy, setting single-value product config variables.\n     */\n    private void flattenSingleVars(final String filename) {\n        flattenSingleVars(new Str(filename), new HashSet(), new HashSet());\n    }\n\n    private void flattenSingleVars(final Str filename, Set<String> seen1, Set<String> seen2) {\n        // flattenSingleVars has two loops.  The first sets all variables that are\n        // defined for *this* file.  The second traverses through the inheritance,\n        // to fill in values that weren't defined in this file.  The first appearance of\n        // the variable is the one that wins.\n\n        forEachStatement(filename, VarType.SINGLE, seen1,\n                (assign) -> {\n                    final String varName = assign.getName();\n                    Value v = mVariables.get(varName);\n                    // Only take the first value that we see for single variables.\n                    Value value = mVariables.get(varName);\n                    if (!mVariables.containsKey(varName)) {\n                        final List<Str> valueList = assign.getValue();\n                        // There should never be more than one item in this list, because\n                        // SINGLE values should never be appended to.\n                        if (valueList.size() != 1) {\n                            final StringBuilder positions = new StringBuilder(\"[\");\n                            for (Str s: valueList) {\n                                positions.append(s.getPosition());\n                            }\n                            positions.append(\" ]\");\n                            throw new RuntimeException(\"Value list found for SINGLE variable \"\n                                    + varName + \" size=\" + valueList.size()\n                                    + \"positions=\" + positions.toString());\n                        }\n                        mVariables.put(varName,\n                                new Value(VarType.SINGLE,\n                                    valueList.get(0)));\n                    }\n                }, null);\n\n        forEachInheritAlpha(filename, VarType.SINGLE, seen2,\n                (inherit) -> {\n                    flattenSingleVars(inherit.getFilename(), seen1, seen2);\n                });\n    }\n\n    /**\n     * Traverse the inheritance hierarchy and flatten the values\n     */\n    private void flattenUnknownVars(String filename) {\n        flattenUnknownVars(new Str(filename), new HashSet());\n    }\n\n    private void flattenUnknownVars(final Str filename, Set<String> seen) {\n        // flattenUnknownVars has two loops: First to attempt to set the variable from\n        // this file, and then a second loop to handle the inheritance.  This is odd\n        // but it matches the order the files are included in node_fns.mk. The last appearance\n        // of the value is the one that wins.\n\n        forEachStatement(filename, VarType.UNKNOWN, null,\n                (assign) -> {\n                    // Overwrite the current value with whatever is now in the file.\n                    mVariables.put(assign.getName(),\n                            new Value(VarType.UNKNOWN,\n                                flattenAssignList(assign, new Str(\"\"))));\n                }, null);\n\n        forEachInheritAlpha(filename, VarType.UNKNOWN, seen,\n                (inherit) -> {\n                    flattenUnknownVars(inherit.getFilename(), seen);\n                });\n    }\n\n    String prefix = \"\";\n\n    /**\n     * Sets the PRODUCTS.<filename>.INHERITS_FROM variables.\n     */\n    private void flattenInheritsFrom(final String filename) {\n        flattenInheritsFrom(new Str(filename));\n    }\n\n    /**\n     * This flatten function, unlike the others visits all of the nodes regardless\n     * of whether they have been seen before, because that's what the make code does.\n     */\n    private void flattenInheritsFrom(final Str filename) {\n        // Recurse, and gather the list our chlidren\n        final TreeSet<Str> children = new TreeSet();\n        forEachStatement(filename, VarType.LIST, null, null,\n                (inherit) -> {\n                    children.add(inherit.getFilename());\n                    flattenInheritsFrom(inherit.getFilename());\n                });\n\n        final String varName = \"PRODUCTS.\" + filename + \".INHERITS_FROM\";\n        if (children.size() > 0) {\n            // Build the space separated list.\n            boolean first = true;\n            final StringBuilder val = new StringBuilder();\n            for (Str child: children) {\n                if (first) {\n                    first = false;\n                } else {\n                    val.append(' ');\n                }\n                val.append(child);\n            }\n            mVariables.put(varName, new Value(VarType.UNKNOWN, new Str(val.toString())));\n        } else {\n            // Clear whatever flattenUnknownVars happened to have put in.\n            mVariables.remove(varName);\n        }\n    }\n\n    /**\n     * Throw an exception if there's an existing variable with a different type.\n     */\n    private void assertVarType(Str filename, String varName) {\n        if (mGenericConfig.getVarType(varName) == VarType.UNKNOWN) {\n            final Value prevValue = mVariables.get(varName);\n            if (prevValue != null\n                    && prevValue.getVarType() != VarType.UNKNOWN) {\n                throw new RuntimeException(\"Mismatched var types:\"\n                        + \" filename=\" + filename\n                        + \" varType=\" + mGenericConfig.getVarType(varName)\n                        + \" varName=\" + varName\n                        + \" prevValue=\" + Value.debugString(prevValue));\n            }\n        }\n    }\n\n    /**\n     * Depending on whether the assignment is prepending, appending, setting, etc.,\n     * update the value.  We can infer which of those operations it is by the length\n     * and contents of the values. Each value in the list was originally separated\n     * by the previous value.\n     */\n    private void assignToListVar(Map<String, Value> vars, String varName, List<Str> items) {\n        final Value value = vars.get(varName);\n        final List<Str> orig = value == null ? new ArrayList() : value.getList();\n        final List<Str> result = new ArrayList();\n        if (items.size() > 0) {\n            for (int i = 0; i < items.size(); i++) {\n                if (i != 0) {\n                    result.addAll(orig);\n                }\n                final Str item = items.get(i);\n                addWords(result, item);\n            }\n        }\n        vars.put(varName, new Value(result));\n    }\n\n    /**\n     * Appends all of the words in in 'items' to an entry in vars keyed by 'varName',\n     * creating one if necessary.\n     */\n    private static void appendToListVar(Map<String, Value> vars, String varName, List<Str> items) {\n        Value value = vars.get(varName);\n        if (value == null) {\n            value = new Value(new ArrayList());\n            vars.put(varName, value);\n        }\n        final List<Str> out = value.getList();\n        for (Str item: items) {\n            addWords(out, item);\n        }\n    }\n\n    /**\n     * Split 'item' on spaces, and add each of them as a word to 'out'.\n     */\n    private static void addWords(List<Str> out, Str item) {\n        for (String word: RE_SPACE.split(item.toString().trim())) {\n            if (word.length() > 0) {\n                out.add(new Str(item.getPosition(), word));\n            }\n        }\n    }\n\n    /**\n     * Flatten the list of strings in an Assign statement, using the previous value\n     * as a separator.\n     */\n    private Str flattenAssignList(GenericConfig.Assign assign, Str previous) {\n        final StringBuilder result = new StringBuilder();\n        Position position = previous.getPosition();\n        final List<Str> list = assign.getValue();\n        final int size = list.size();\n        for (int i = 0; i < size; i++) {\n            final Str item = list.get(i);\n            result.append(item.toString());\n            if (i != size - 1) {\n                result.append(previous);\n            }\n            final Position pos = item.getPosition();\n            if (pos != null && pos.getFile() != null) {\n                position = pos;\n            }\n        }\n        return new Str(position, result.toString());\n    }\n\n    /**\n     * Make sure that each of the product config variables has a default value.\n     */\n    private void setDefaultKnownVars() {\n        for (Map.Entry<String, VarType> entry: mGenericConfig.getProductVars().entrySet()) {\n            final String varName = entry.getKey();\n            final VarType varType = entry.getValue();\n\n            final Value val = mVariables.get(varName);\n            if (val == null) {\n                mVariables.put(varName, new Value(varType));\n            }\n        }\n\n\n        // TODO: These two for now as well, until we can rewrite the enforce packages exist\n        // handling.\n        if (!mVariables.containsKey(\"PRODUCT_ENFORCE_PACKAGES_EXIST\")) {\n            mVariables.put(\"PRODUCT_ENFORCE_PACKAGES_EXIST\", new Value(VarType.UNKNOWN));\n        }\n        if (!mVariables.containsKey(\"PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST\")) {\n            mVariables.put(\"PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST\", new Value(VarType.UNKNOWN));\n        }\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/GenericConfig.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\n/**\n * Language-agnostic representation of a configuration statement.\n */\npublic class GenericConfig extends ConfigBase {\n    /**\n     * The config files that were imported in this config pass.\n     */\n    protected final TreeMap<String, ConfigFile> mConfigFiles = new TreeMap();\n\n    /**\n     * A configuration file.\n     */\n    public static class ConfigFile {\n        /**\n         * The name of the file, relative to the tree root.\n         */\n        private final String mFilename;\n\n        /**\n         * Sections of variable definitions and import statements. Product config\n         * files will always have at least one block.\n         */\n        private final ArrayList<Statement> mStatements = new ArrayList();\n\n        public ConfigFile(String filename) {\n            mFilename = filename;\n        }\n\n        public String getFilename() {\n            return mFilename;\n        }\n\n        public void addStatement(Statement statement) {\n            mStatements.add(statement);\n        }\n\n        public ArrayList<Statement> getStatements() {\n            return mStatements;\n        }\n    }\n\n    /**\n     * Base class for statements that appear in config files.\n     */\n    public static class Statement {\n    }\n\n    /**\n     * A variable assignment.\n     */\n    public static class Assign extends Statement {\n        private final String mVarName;\n        private final List<Str> mValue;\n\n        /**\n         * Assignment of a single value\n         */\n        public Assign(String varName, Str value) {\n            mVarName = varName;\n            mValue = new ArrayList();\n            mValue.add(value);\n        }\n\n        /**\n         * Assignment referencing a previous value.\n         *   VAR := $(1) $(VAR) $(2) $(VAR) $(3)\n         */\n        public Assign(String varName, List<Str> value) {\n            mVarName = varName;\n            mValue = value;\n        }\n\n        public String getName() {\n            return mVarName;\n        }\n\n        public List<Str> getValue() {\n            return mValue;\n        }\n    }\n\n    /**\n     * An $(inherit-product FILENAME) statement\n     */\n    public static class Inherit extends Statement {\n        private final Str mFilename;\n\n        public Inherit(Str filename) {\n            mFilename = filename;\n        }\n\n        public Str getFilename() {\n            return mFilename;\n        }\n    }\n\n    /**\n     * Adds the given config file. Returns any one previously added, or null.\n     */\n    public ConfigFile addConfigFile(ConfigFile file) {\n        return mConfigFiles.put(file.getFilename(), file);\n    }\n\n    public TreeMap<String, ConfigFile> getFiles() {\n        return mConfigFiles;\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/Kati.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.util.Map;\n\n/**\n * Wrapper for invoking kati.\n */\npublic interface Kati {\n    public Map<String, MakeConfig> loadProductConfig();\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/KatiCommand.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic interface KatiCommand {\n    public static class KatiException extends Exception {\n        private String mStderr;\n\n        public KatiException(List<String> cmd, String stderr) {\n            super(\"Error running kati: \" + Arrays.toString(cmd.toArray()));\n            mStderr = stderr;\n        }\n\n        public String getStderr() {\n            return mStderr;\n        }\n    }\n\n    /**\n     * Run kati directly. Returns stdout data.\n     *\n     * @throws KatiException if there is an error. KatiException will contain\n     * the stderr from the kati invocation.\n     */\n    public String run(String[] args) throws KatiException;\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/KatiCommandImpl.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.nio.charset.StandardCharsets;\n\npublic class KatiCommandImpl implements KatiCommand {\n    final Errors mErrors;\n    final Options mOptions;\n\n    /**\n     * Runnable that consumes all of an InputStream until EOF, writes the contents\n     * into a StringBuilder, and then closes the stream.\n     */\n    class OutputReader implements Runnable {\n        private final InputStream mStream;\n        private final StringBuilder mOutput;\n\n        OutputReader(InputStream stream, StringBuilder output) {\n            mStream = stream;\n            mOutput = output;\n        }\n\n        @Override\n        public void run() {\n            final char[] buf = new char[16*1024];\n            final InputStreamReader reader = new InputStreamReader(mStream, StandardCharsets.UTF_8);\n            try {\n                int amt;\n                while ((amt = reader.read(buf, 0, buf.length)) >= 0) {\n                    mOutput.append(buf, 0, amt);\n                }\n            } catch (IOException ex) {\n                mErrors.ERROR_KATI.add(\"Error reading from kati: \" + ex.getMessage());\n            } finally {\n                try {\n                    reader.close();\n                } catch (IOException ex) {\n                    // Close doesn't throw\n                }\n            }\n        }\n    }\n\n    public KatiCommandImpl(Errors errors, Options options) {\n        mErrors = errors;\n        mOptions = options;\n    }\n\n    /**\n     * Run kati directly. Returns stdout data.\n     *\n     * @throws KatiException if there is an error. KatiException will contain\n     * the stderr from the kati invocation.\n     */\n    public String run(String[] args) throws KatiException {\n        final ArrayList<String> cmd = new ArrayList();\n        cmd.add(mOptions.getCKatiBin());\n        for (String arg: args) {\n            cmd.add(arg);\n        }\n\n        final ProcessBuilder builder = new ProcessBuilder(cmd);\n        builder.redirectOutput(ProcessBuilder.Redirect.PIPE);\n        builder.redirectError(ProcessBuilder.Redirect.PIPE);\n\n        Process process = null;\n\n        try {\n            process = builder.start();\n        } catch (IOException ex) {\n            throw new KatiException(cmd, \"IOException running process: \" + ex.getMessage());\n        }\n\n        final StringBuilder stdout = new StringBuilder();\n        final Thread stdoutThread = new Thread(new OutputReader(process.getInputStream(), stdout),\n                \"kati_stdout_reader\");\n        stdoutThread.start();\n\n        final StringBuilder stderr = new StringBuilder();\n        final Thread stderrThread = new Thread(new OutputReader(process.getErrorStream(), stderr),\n                \"kati_stderr_reader\");\n        stderrThread.start();\n\n        int returnCode = waitForProcess(process);\n        joinThread(stdoutThread);\n        joinThread(stderrThread);\n\n        if (returnCode != 0) {\n            throw new KatiException(cmd, stderr.toString());\n        }\n\n        return stdout.toString();\n    }\n\n    /**\n     * Wrap Process.waitFor() because it throws InterruptedException.\n     */\n    private static int waitForProcess(Process proc) {\n        while (true) {\n            try {\n                return proc.waitFor();\n            } catch (InterruptedException ex) {\n            }\n        }\n    }\n\n    /**\n     * Wrap Thread.join() because it throws InterruptedException.\n     */\n    private static void joinThread(Thread thread) {\n        while (true) {\n            try {\n                thread.join();\n                return;\n            } catch (InterruptedException ex) {\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/KatiImpl.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.nio.file.Paths;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class KatiImpl implements Kati {\n    // Subdirectory inside out for config stuff.\n    private static final String CONFIG_SUBDIR = \"config\";\n\n    private final Errors mErrors;\n    private final Options mOptions;\n    private final KatiCommand mCommand;\n\n    // TODO: Do we need to consider the whole or a greater subset of the\n    // environment (or a hash of it?). In theory product-variant is enough, but we know\n    // people use stuff from the environment, even though we're trying to get rid of that.\n    private String getWorkDirPath() {\n        return Paths.get(mOptions.getOutDir(), CONFIG_SUBDIR,\n                mOptions.getProduct() + '-' + mOptions.getVariant()).toString();\n    }\n\n    private String getDumpConfigCsvPath() {\n        return Paths.get(getWorkDirPath(), \"dumpconfig.csv\").toString();\n    }\n\n    public KatiImpl(Errors errors, Options options) {\n        this(errors, options, new KatiCommandImpl(errors, options));\n    }\n\n    // VisibleForTesting\n    public KatiImpl(Errors errors, Options options, KatiCommand command) {\n        mErrors = errors;\n        mOptions = options;\n        mCommand = command;\n    }\n\n    @Override\n    public Map<String, MakeConfig> loadProductConfig() {\n        final String csvPath = getDumpConfigCsvPath();\n        try {\n            File workDir = new File(getWorkDirPath());\n\n            if ((workDir.exists() && !workDir.isDirectory()) || !workDir.mkdirs()) {\n                mErrors.ERROR_KATI.add(\"Unable to create directory: \" + workDir);\n                return null; // TODO: throw exception?\n            }\n\n            String out = mCommand.run(new String[] {\n                    \"-f\", \"build/make/core/dumpconfig.mk\",\n                    \"DUMPCONFIG_FILE=\" + csvPath\n                });\n\n            if (!out.contains(\"***DONE***\")) {\n                mErrors.ERROR_KATI.add(\n                        \"Unknown error with kati, but it didn't print ***DONE*** message\");\n                return null; // TODO: throw exception?\n            }\n            // TODO: Check that output was good.\n        } catch (KatiCommand.KatiException ex) {\n            mErrors.ERROR_KATI.add(\"Error running kati:\\n\" + ex.getStderr());\n            return null;\n        }\n\n        if (!(new File(csvPath)).canRead()) {\n            mErrors.ERROR_KATI.add(\"Kati ran but did not create \" + csvPath);\n            return null;\n        }\n\n        try (FileReader reader = new FileReader(csvPath)) {\n            Map<String, MakeConfig> makeConfigs = DumpConfigParser.parse(mErrors, csvPath, reader);\n\n            if (makeConfigs.size() == 0) {\n                // TODO: Issue error?\n                return null;\n            }\n\n            return makeConfigs;\n        } catch (CsvParser.ParseException ex) {\n            mErrors.ERROR_KATI.add(new Position(csvPath, ex.getLine()),\n                    \"Unable to parse output of dumpconfig.mk: \" + ex.getMessage());\n            return null; // TODO: throw exception?\n        } catch (IOException ex) {\n            System.out.println(ex);\n            mErrors.ERROR_KATI.add(\"Unable to read \" + csvPath + \": \" + ex.getMessage());\n            return null; // TODO: throw exception?\n        }\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/Main.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\n\npublic class Main {\n    private final Errors mErrors;\n    private final Options mOptions;\n\n    public Main(Errors errors, Options options) {\n        mErrors = errors;\n        mOptions = options;\n    }\n\n    void run() {\n        // TODO: Check the build environment to make sure we're running in a real\n        // build environment, e.g. actually inside a source tree, with TARGET_PRODUCT\n        // and TARGET_BUILD_VARIANT defined, etc.\n        Kati kati = new KatiImpl(mErrors, mOptions);\n        Map<String, MakeConfig> makeConfigs = kati.loadProductConfig();\n        if (makeConfigs == null || mErrors.hadError()) {\n            return;\n        }\n        if (false) {\n            for (MakeConfig makeConfig: (new TreeMap<String, MakeConfig>(makeConfigs)).values()) {\n                System.out.println();\n                System.out.println(\"=======================================\");\n                System.out.println(\"PRODUCT CONFIG FILES : \" + makeConfig.getPhase());\n                System.out.println(\"=======================================\");\n                makeConfig.printToStream(System.out);\n            }\n        }\n\n        ConvertMakeToGenericConfig m2g = new ConvertMakeToGenericConfig(mErrors);\n        GenericConfig generic = m2g.convert(makeConfigs);\n        if (false) {\n            System.out.println(\"======================\");\n            System.out.println(\"REGENERATED MAKE FILES\");\n            System.out.println(\"======================\");\n            MakeWriter.write(System.out, generic, 0);\n        }\n\n        // TODO: Lookup shortened name as used in PRODUCT_NAME / TARGET_PRODUCT\n        FlatConfig flat = FlattenConfig.flatten(mErrors, generic);\n        if (false) {\n            System.out.println(\"=======================\");\n            System.out.println(\"FLATTENED VARIABLE LIST\");\n            System.out.println(\"=======================\");\n            MakeWriter.write(System.out, flat, 0);\n        }\n\n        OutputChecker checker = new OutputChecker(flat);\n        checker.reportErrors(mErrors);\n\n        // TODO: Run kati and extract the variables and convert all that into starlark files.\n\n        // TODO: Run starlark with all the generated ones and the hand written ones.\n\n        // TODO: Get the variables that were defined in starlark and use that to write\n        // out the make, soong and bazel input files.\n    }\n\n    public static void main(String[] args) {\n        Errors errors = new Errors();\n        int exitCode = 0;\n\n        try {\n            Options options = Options.parse(errors, args, System.getenv());\n            if (errors.hadError()) {\n                Options.printHelp(System.err);\n                System.err.println();\n                throw new CommandException();\n            }\n\n            switch (options.getAction()) {\n                case DEFAULT:\n                    (new Main(errors, options)).run();\n                    return;\n                case HELP:\n                    Options.printHelp(System.out);\n                    return;\n            }\n        } catch (CommandException | Errors.FatalException ex) {\n            // These are user errors, so don't show a stack trace\n            exitCode = 1;\n        } catch (Throwable ex) {\n            // These are programming errors in the code of this tool, so print the exception.\n            // We'll try to print this.  If it's something unrecoverable, then we'll hope\n            // for the best. We will still print the errors below, because they can be useful\n            // for debugging.\n            ex.printStackTrace(System.err);\n            System.err.println();\n            exitCode = 1;\n        } finally {\n            // Print errors and warnings\n            errors.printErrors(System.err);\n            if (errors.hadError()) {\n                exitCode = 1;\n            }\n            System.exit(exitCode);\n        }\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/MakeConfig.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\npublic class MakeConfig extends ConfigBase {\n    /**\n     * The config files that were imported in this config pass.\n     */\n    protected final ArrayList<ConfigFile> mConfigFiles = new ArrayList();\n\n    public enum BlockType {\n        UNSET,\n        BEFORE,\n        INHERIT,\n        AFTER\n    }\n\n    public static class ConfigFile {\n        /**\n         * The name of the file, relative to the tree root.\n         */\n        private final String mFilename;\n\n        /**\n         * Sections of variable definitions and import statements. Product config\n         * files will always have at least one block.\n         */\n        private final ArrayList<Block> mBlocks = new ArrayList();\n\n        public ConfigFile(String filename) {\n            mFilename = filename;\n        }\n\n        public String getFilename() {\n            return mFilename;\n        }\n\n        public void addBlock(Block block) {\n            mBlocks.add(block);\n        }\n\n        public ArrayList<Block> getBlocks() {\n            return mBlocks;\n        }\n    }\n\n    /**\n     * A set of variables that were defined.\n     */\n    public static class Block {\n        private final BlockType mBlockType;\n        private final TreeMap<String, Str> mValues = new TreeMap();\n        private Str mInheritedFile;\n\n        public Block(BlockType blockType) {\n            mBlockType = blockType;\n        }\n\n        public BlockType getBlockType() {\n            return mBlockType;\n        }\n\n        public void addVar(String varName, Str varValue) {\n            mValues.put(varName, varValue);\n        }\n\n        public Str getVar(String varName) {\n            return mValues.get(varName);\n        }\n\n        public TreeMap<String, Str> getVars() {\n            return mValues;\n        }\n\n        public void setInheritedFile(Str filename) {\n            mInheritedFile = filename;\n        }\n\n        public Str getInheritedFile() {\n            return mInheritedFile;\n        }\n    }\n\n    /**\n     * Adds the given config file. Returns any one previously added, or null.\n     */\n    public ConfigFile addConfigFile(ConfigFile file) {\n        ConfigFile prev = null;\n        for (ConfigFile f: mConfigFiles) {\n            if (f.getFilename().equals(file.getFilename())) {\n                prev = f;\n                break;\n            }\n        }\n        mConfigFiles.add(file);\n        return prev;\n    }\n\n    public List<ConfigFile> getConfigFiles() {\n        return mConfigFiles;\n    }\n\n    public void printToStream(PrintStream out) {\n        out.println(\"MakeConfig {\");\n        out.println(\"  phase: \" + mPhase);\n        out.println(\"  rootNodes: \" + mRootNodes);\n        out.print(\"  singleVars: [ \");\n        for (Map.Entry<String,VarType> entry: mProductVars.entrySet()) {\n            if (entry.getValue() == VarType.SINGLE) {\n                out.print(entry.getKey());\n                out.print(\" \");\n            }\n        }\n        out.println(\"]\");\n        out.print(\"  listVars: [ \");\n        for (Map.Entry<String,VarType> entry: mProductVars.entrySet()) {\n            if (entry.getValue() == VarType.LIST) {\n                out.print(entry.getKey());\n                out.print(\" \");\n            }\n        }\n        out.println(\"]\");\n        out.println(\"  configFiles: [\");\n        for (final ConfigFile configFile: mConfigFiles) {\n            out.println(\"    ConfigFile {\");\n            out.println(\"      filename: \" + configFile.getFilename());\n            out.println(\"      blocks: [\");\n            for (Block block: configFile.getBlocks()) {\n                out.println(\"        Block {\");\n                out.println(\"          type: \" + block.getBlockType());\n                if (block.getBlockType() == BlockType.INHERIT) {\n                    out.println(\"          inherited: \" + block.getInheritedFile());\n                }\n                out.println(\"          values: {\");\n                for (Map.Entry<String,Str> var: block.getVars().entrySet()) {\n                    if (!var.getKey().equals(\"PRODUCT_PACKAGES\")) {\n                        continue;\n                    }\n                    out.println(\"            \" + var.getKey() + \": \" + var.getValue());\n                }\n                out.println(\"          }\");\n                out.println(\"        }\");\n            }\n            out.println(\"      ]\");\n            out.println(\"    }\");\n        }\n        out.println(\"  ] // configFiles\");\n        out.println(\"} // MakeConfig\");\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/MakeWriter.java",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.io.PrintStream;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\npublic class MakeWriter {\n    public static final int FLAG_WRITE_HEADER = 1;\n    public static final int FLAG_WRITE_ANNOTATIONS = 1 << 1;\n\n    private final boolean mWriteHeader;\n    private final boolean mWriteAnnotations;\n\n    public static void write(PrintStream out, GenericConfig config, int flags) {\n        (new MakeWriter(flags)).writeGeneric(out, config);\n    }\n\n    public static void write(PrintStream out, FlatConfig config, int flags) {\n        (new MakeWriter(flags)).writeFlat(out, config);\n    }\n\n\n    private MakeWriter(int flags) {\n        mWriteHeader = (flags & FLAG_WRITE_HEADER) != 0;\n        mWriteAnnotations = (flags & FLAG_WRITE_ANNOTATIONS) != 0;\n    }\n\n    private void writeGeneric(PrintStream out, GenericConfig config) {\n        for (GenericConfig.ConfigFile file: config.getFiles().values()) {\n            out.println(\"---------------------------------------------------------\");\n            out.println(\"FILE: \" + file.getFilename());\n            out.println(\"---------------------------------------------------------\");\n            writeFile(out, config, file);\n            out.println();\n        }\n        out.println(\"---------------------------------------------------------\");\n        out.println(\"VARIABLES TOUCHED BY MAKE BASED CONFIG:\");\n        out.println(\"---------------------------------------------------------\");\n        writeStrVars(out, OutputChecker.getModifiedVars(config.getInitialVariables(),\n                                          config.getFinalVariables()), config);\n    }\n\n    private void writeFile(PrintStream out, GenericConfig config, GenericConfig.ConfigFile file) {\n        if (mWriteHeader) {\n            out.println(\"# This file is generated by the product_config tool\");\n        }\n        for (GenericConfig.Statement statement: file.getStatements()) {\n            if (statement instanceof GenericConfig.Assign) {\n                writeAssign(out, config, (GenericConfig.Assign)statement);\n            } else if (statement instanceof GenericConfig.Inherit) {\n                writeInherit(out, (GenericConfig.Inherit)statement);\n            } else {\n                throw new RuntimeException(\"Unexpected Statement: \" + statement);\n            }\n        }\n    }\n\n    private void writeAssign(PrintStream out, GenericConfig config,\n            GenericConfig.Assign statement) {\n        final List<Str> values = statement.getValue();\n        final int size = values.size();\n        final String varName = statement.getName();\n        Position pos = null;\n        if (size == 0) {\n            return;\n        } else if (size == 1) {\n            // Plain :=\n            final Str value = values.get(0);\n            out.print(varName + \" := \" + value);\n            pos = value.getPosition();\n        } else if (size == 2 && values.get(0).toString().length() == 0) {\n            // Plain +=\n            final Str value = values.get(1);\n            out.print(varName + \" += \" + value);\n            pos = value.getPosition();\n        } else {\n            // Write it out the long way\n            out.print(varName + \" := \" + values.get(0));\n            for (int i = 1; i < size; i++) {\n                out.print(\"$(\" + varName + \") \" + values.get(i));\n                pos = values.get(i).getPosition();\n            }\n        }\n        if (mWriteAnnotations) {\n            out.print(\"  # \" + config.getVarType(varName) + \" \" + pos);\n        }\n        out.println();\n    }\n\n    private void writeInherit(PrintStream out, GenericConfig.Inherit statement) {\n        final Str filename = statement.getFilename();\n        out.print(\"$(call inherit-product \" + filename + \")\");\n        if (mWriteAnnotations) {\n            out.print(\"  # \" + filename.getPosition());\n        }\n        out.println();\n    }\n\n    private static class Var {\n        Var(String name, Str val) {\n            this.name = name;\n            this.val = val;\n        }\n        final String name;\n        final Str val;\n    }\n\n    private static void writeStrVars(PrintStream out, Map<String, Str> vars, ConfigBase config) {\n        // Sort by file name and var name\n        TreeMap<String, Var> sorted = new TreeMap();\n        for (Map.Entry<String, Str> entry: vars.entrySet()) {\n            sorted.put(entry.getValue().getPosition().toString() + \" \" + entry.getKey(),\n                    new Var(entry.getKey(), entry.getValue()));\n        }\n        // Print it\n        for (Var var: sorted.values()) {\n            out.println(var.val.getPosition() + var.name + \" := \" + var.val);\n        }\n    }\n\n    private void writeFlat(PrintStream out, FlatConfig config) {\n        // TODO: Print positions.\n        for (Map.Entry<String, Value> entry: config.getValues().entrySet()) {\n            out.print(entry.getKey());\n            out.print(\" := \");\n\n            final Value value = entry.getValue();\n            if (value.getVarType() == VarType.LIST) {\n                final List<Str> list = value.getList();\n                final int size = list.size();\n                for (int i = 0; i < size; i++) {\n                    out.print(list.get(i).toString());\n                    if (i != size - 1) {\n                        out.print(\" \\\\\\n        \");\n                    }\n                }\n            } else {\n                out.print(value.getStr().toString());\n            }\n            out.println();\n        }\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/Options.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.io.PrintStream;\nimport java.util.Map;\nimport java.util.TreeMap;\n\npublic class Options {\n    public enum Action {\n        DEFAULT,\n        HELP\n    }\n\n    private Action mAction = Action.DEFAULT;\n\n    private String mProduct;\n    private String mVariant;\n    private String mOutDir;\n    private String mCKatiBin;\n\n    public Action getAction() {\n        return mAction;\n    }\n\n    public String getProduct() {\n        return mProduct;\n    }\n\n    public String getVariant() {\n        return mVariant;\n    }\n\n    public String getOutDir() {\n        return mOutDir != null ? mOutDir : \"out\";\n    }\n\n    public String getCKatiBin() {\n        return mCKatiBin;\n    }\n\n    public static void printHelp(PrintStream out) {\n        out.println(\"usage: product_config\");\n        out.println();\n        out.println(\"REQUIRED FLAGS\");\n        out.println(\"  --ckati_bin CKATI        Kati binary to use.\");\n        out.println();\n        out.println(\"OPTIONAL FLAGS\");\n        out.println(\"  --hide ERROR_ID          Suppress this error.\");\n        out.println(\"  --error ERROR_ID         Make this ERROR_ID a fatal error.\");\n        out.println(\"  --help -h                This message.\");\n        out.println(\"  --warning ERROR_ID       Make this ERROR_ID a warning.\");\n        out.println();\n        out.println(\"REQUIRED ENVIRONMENT\");\n        out.println(\"  TARGET_PRODUCT           Product to build from lunch command.\");\n        out.println(\"  TARGET_BUILD_VARIANT     Build variant from lunch command.\");\n        out.println();\n        out.println(\"OPTIONAL ENVIRONMENT\");\n        out.println(\"  OUT_DIR                  Build output directory. Defaults to \\\"out\\\".\");\n        out.println();\n        out.println(\"ERRORS\");\n        out.println(\"  The following are the errors that can be controlled on the\");\n        out.println(\"  commandline with the --hide --warning --error flags.\");\n\n        TreeMap<Integer,Errors.Category> sorted = new TreeMap((new Errors()).getCategories());\n\n        for (final Errors.Category category: sorted.values()) {\n            if (category.isLevelSettable()) {\n                out.println(String.format(\"    %-3d      %s\", category.getCode(),\n                category.getHelp().replace(\"\\n\", \"\\n             \")));\n            }\n        }\n    }\n\n    static class Parser {\n        private static class ParseException extends Exception {\n            public ParseException(String message) {\n                super(message);\n            }\n        }\n\n        private Errors mErrors;\n        private String[] mArgs;\n        private Map<String,String> mEnv;\n        private Options mResult = new Options();\n        private int mIndex;\n        private boolean mSkipRequiredArgValidation;\n\n        public Parser(Errors errors, String[] args, Map<String,String> env) {\n            mErrors = errors;\n            mArgs = args;\n            mEnv = env;\n        }\n\n        public Options parse() {\n            // Args\n            try {\n                while (mIndex < mArgs.length) {\n                    final String arg = mArgs[mIndex];\n\n                    if (\"--ckati_bin\".equals(arg)) {\n                        mResult.mCKatiBin = requireNextStringArg(arg);\n                    } else if (\"--hide\".equals(arg)) {\n                        handleErrorCode(arg, Errors.Level.HIDDEN);\n                    } else if (\"--error\".equals(arg)) {\n                        handleErrorCode(arg, Errors.Level.ERROR);\n                    } else if (\"--help\".equals(arg) || \"-h\".equals(arg)) {\n                        // Help overrides all other commands if there isn't an error, but\n                        // we will stop here.\n                        if (!mErrors.hadError()) {\n                            mResult.mAction = Action.HELP;\n                        }\n                        return mResult;\n                    } else if (\"--warning\".equals(arg)) {\n                        handleErrorCode(arg, Errors.Level.WARNING);\n                    } else {\n                        throw new ParseException(\"Unknown command line argument: \" + arg);\n                    }\n\n                    mIndex++;\n                }\n            } catch (ParseException ex) {\n                mErrors.ERROR_COMMAND_LINE.add(ex.getMessage());\n            }\n\n            // Environment\n            mResult.mProduct = mEnv.get(\"TARGET_PRODUCT\");\n            mResult.mVariant = mEnv.get(\"TARGET_BUILD_VARIANT\");\n            mResult.mOutDir = mEnv.get(\"OUT_DIR\");\n\n            validateArgs();\n\n            return mResult;\n        }\n\n        /**\n         * For testing; don't generate errors about missing arguments\n         */\n        public void setSkipRequiredArgValidation() {\n            mSkipRequiredArgValidation = true;\n        }\n\n        private void validateArgs() {\n            if (!mSkipRequiredArgValidation) {\n                if (mResult.mCKatiBin == null || \"\".equals(mResult.mCKatiBin)) {\n                    addMissingArgError(\"--ckati_bin\");\n                }\n                if (mResult.mProduct == null) {\n                    addMissingEnvError(\"TARGET_PRODUCT\");\n                }\n                if (mResult.mVariant == null) {\n                    addMissingEnvError(\"TARGET_BUILD_VARIANT\");\n                }\n            }\n        }\n\n        private void addMissingArgError(String argName) {\n            mErrors.ERROR_COMMAND_LINE.add(\"Required command line argument missing: \"\n                    + argName);\n        }\n\n        private void addMissingEnvError(String envName) {\n            mErrors.ERROR_COMMAND_LINE.add(\"Required environment variable missing: \"\n                    + envName);\n        }\n\n        private String getNextNonFlagArg() {\n            if (mIndex == mArgs.length - 1) {\n                return null;\n            }\n            if (mArgs[mIndex + 1].startsWith(\"-\")) {\n                return null;\n            }\n            mIndex++;\n            return mArgs[mIndex];\n        }\n\n        private String requireNextStringArg(String arg) throws ParseException {\n            final String val = getNextNonFlagArg();\n            if (val == null) {\n                throw new ParseException(arg + \" requires a string argument.\");\n            }\n            return val;\n        }\n\n        private int requireNextNumberArg(String arg) throws ParseException {\n            final String val = getNextNonFlagArg();\n            if (val == null) {\n                throw new ParseException(arg + \" requires a numeric argument.\");\n            }\n            try {\n                return Integer.parseInt(val);\n            } catch (NumberFormatException ex) {\n                throw new ParseException(arg + \" requires a numeric argument. found: \" + val);\n            }\n        }\n\n        private void handleErrorCode(String arg, Errors.Level level) throws ParseException {\n            final int code = requireNextNumberArg(arg);\n            final Errors.Category category = mErrors.getCategories().get(code);\n            if (category == null) {\n                mErrors.WARNING_UNKNOWN_COMMAND_LINE_ERROR.add(\"Unknown error code: \" + code);\n                return;\n            }\n            if (!category.isLevelSettable()) {\n                mErrors.ERROR_COMMAND_LINE.add(\"Can't set level for error \" + code);\n                return;\n            }\n            category.setLevel(level);\n        }\n    }\n\n    /**\n     * Parse the arguments and return an options object.\n     * <p>\n     * Updates errors with the hidden / warning / error levels.\n     * <p>\n     * Adds errors encountered to Errors object.\n     */\n    public static Options parse(Errors errors, String[] args, Map<String, String> env) {\n        return (new Parser(errors, args, env)).parse();\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/OutputChecker.java",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.util.Arrays;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\n/**\n * Compares the make-based configuration as reported by dumpconfig.mk\n * with what was computed from the new tool.\n */\npublic class OutputChecker {\n    // Differences that we know about, either know issues to be fixed or intentional.\n    private static final RegexSet IGNORED_VARIABLES = new RegexSet(\n            // TODO: Rewrite the enforce packages exist logic into this tool.\n            \"PRODUCT_ENFORCE_PACKAGES_EXIST\",\n            \"PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST\",\n            \"PRODUCTS\\\\..*\\\\.PRODUCT_ENFORCE_PACKAGES_EXIST\",\n            \"PRODUCTS\\\\..*\\\\.PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST\",\n\n            // This is generated by this tool, but comes later in the make build system.\n            \"INTERNAL_PRODUCT\",\n\n            // This can be set temporarily by product_config.mk\n            \".KATI_ALLOW_RULES\"\n            );\n\n    private final FlatConfig mConfig;\n    private final TreeMap<String, Variable> mVariables;\n\n    /**\n     * Represents the before and after state of a variable.\n     */\n    public static class Variable {\n        public final String name;\n        public final VarType type;\n        public final Str original;\n        public final Value updated;\n\n        public Variable(String name, VarType type, Str original) {\n            this(name, type, original, null);\n        }\n\n        public Variable(String name, VarType type, Str original, Value updated) {\n            this.name = name;\n            this.type = type;\n            this.original = original;\n            this.updated = updated;\n        }\n\n        /**\n         * Return copy of this Variable with the updated field also set.\n         */\n        public Variable addUpdated(Value updated) {\n            return new Variable(name, type, original, updated);\n        }\n\n        /**\n         * Return whether normalizedOriginal and normalizedUpdate are equal.\n         */\n        public boolean isSame() {\n            final Str normalizedOriginal = Value.normalize(original);\n            final Str normalizedUpdated = Value.normalize(updated);\n            if (normalizedOriginal == null && normalizedUpdated == null) {\n                return true;\n            } else if (normalizedOriginal != null) {\n                return normalizedOriginal.equals(normalizedUpdated);\n            } else {\n                return false;\n            }\n        }\n    }\n\n    /**\n     * Construct OutputChecker with the config it will check.\n     */\n    public OutputChecker(FlatConfig config) {\n        mConfig = config;\n        mVariables = getVariables(config);\n    }\n\n    /**\n     * Add a WARNING_DIFFERENT_FROM_KATI for each of the variables which have changed.\n     */\n    public void reportErrors(Errors errors) {\n        for (Variable var: getDifferences()) {\n            if (IGNORED_VARIABLES.matches(var.name)) {\n                continue;\n            }\n            errors.WARNING_DIFFERENT_FROM_KATI.add(\"product_config processing differs from\"\n                    + \" kati processing for \" + var.type + \" variable \" + var.name + \".\\n\"\n                    + \"original: \"\n                    + Value.oneLinePerWord(var.original, \"<null>\") + \"\\n\"\n                    + \"updated: \"\n                    + Value.oneLinePerWord(var.updated, \"<null>\"));\n        }\n    }\n\n    /**\n     * Get the Variables that are different between the normalized form of the original\n     * and updated.  If one is null and the other is not, even if one is an empty string,\n     * the values are considered different.\n     */\n    public List<Variable> getDifferences() {\n        final ArrayList<Variable> result = new ArrayList();\n        for (Variable var: mVariables.values()) {\n            if (!var.isSame()) {\n                result.add(var);\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Get all of the variables for this config.\n     *\n     * VisibleForTesting\n     */\n    static TreeMap<String, Variable> getVariables(FlatConfig config) {\n        final TreeMap<String, Variable> result = new TreeMap();\n\n        // Add the original values to mAll\n        for (Map.Entry<String, Str> entry: getModifiedVars(config.getInitialVariables(),\n                    config.getFinalVariables()).entrySet()) {\n            final String name = entry.getKey();\n            result.put(name, new Variable(name, config.getVarType(name), entry.getValue()));\n        }\n\n        // Add the updated values to mAll\n        for (Map.Entry<String, Value> entry: config.getValues().entrySet()) {\n            final String name = entry.getKey();\n            final Value value = entry.getValue();\n            Variable var = result.get(name);\n            if (var == null) {\n                result.put(name, new Variable(name, config.getVarType(name), null, value));\n            } else {\n                result.put(name, var.addUpdated(value));\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Get the entries that are different in the two maps.\n     */\n    public static Map<String, Str> getModifiedVars(Map<String, Str> before,\n            Map<String, Str> after) {\n        final HashMap<String, Str> result = new HashMap();\n\n        // Entries that were added or changed.\n        for (Map.Entry<String, Str> afterEntry: after.entrySet()) {\n            final String varName = afterEntry.getKey();\n            final Str afterValue = afterEntry.getValue();\n            final Str beforeValue = before.get(varName);\n            if (beforeValue == null || !beforeValue.equals(afterValue)) {\n                result.put(varName, afterValue);\n            }\n        }\n\n        // removed Entries that were removed, we just treat them as empty string\n        for (Map.Entry<String, Str> beforeEntry: before.entrySet()) {\n            final String varName = beforeEntry.getKey();\n            if (!after.containsKey(varName)) {\n                result.put(varName, new Str(\"\"));\n            }\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/Position.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Position in a source file.\n */\npublic class Position implements Comparable<Position> {\n    /**\n     * Sentinel line number for when there is no known line number.\n     */\n    public static final int NO_LINE = -1;\n\n    private static final Pattern REGEX = Pattern.compile(\"([^:]*)(?::(\\\\d)*)?:?\\\\s*\");\n    public static final String UNKNOWN = \"<unknown>\";\n\n    private final String mFile;\n    private final int mLine;\n\n    public Position() {\n        mFile = null;\n        mLine = NO_LINE;\n    }\n\n    public Position(String file) {\n        mFile = file;\n        mLine = NO_LINE;\n    }\n\n    public Position(String file, int line) {\n        if (line < NO_LINE) {\n            throw new IllegalArgumentException(\"Negative line number. file=\" + file\n                    + \" line=\" + line);\n        }\n        mFile = file;\n        mLine = line;\n    }\n\n    public int compareTo(Position that) {\n        int result = mFile.compareTo(that.mFile);\n        if (result != 0) {\n            return result;\n        }\n        return mLine - that.mLine;\n    }\n\n    public String getFile() {\n        return mFile;\n    }\n\n    public int getLine() {\n        return mLine;\n    }\n\n    /**\n     * Return a Position object from a string containing <filename>:<line>, or the default\n     * Position(null, NO_LINE) if the string can't be parsed.\n     */\n    public static Position parse(String str) {\n        final Matcher m = REGEX.matcher(str);\n        if (!m.matches()) {\n            return new Position();\n        }\n        String filename = m.group(1);\n        if (filename.length() == 0 || UNKNOWN.equals(filename)) {\n            filename = null;\n        }\n        String lineString = m.group(2);\n        int line;\n        if (lineString == null || lineString.length() == 0) {\n            line = NO_LINE;\n        } else {\n            try {\n                line = Integer.parseInt(lineString);\n            } catch (NumberFormatException ex) {\n                line = NO_LINE;\n            }\n        }\n        return new Position(filename, line);\n    }\n\n    @Override\n    public String toString() {\n      if (mFile == null && mLine == NO_LINE) {\n        return \"\";\n      } else if (mFile == null && mLine != NO_LINE) {\n        return UNKNOWN + \":\" + mLine + \": \";\n      } else if (mFile != null && mLine == NO_LINE) {\n        return mFile + \": \";\n      } else { // if (mFile != null && mLine != NO_LINE)\n        return mFile + ':' + mLine + \": \";\n      }\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/RegexSet.java",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.util.regex.Pattern;\n\n/**\n * Returns whether a string matches one of a set of presupplied regexes.\n */\npublic class RegexSet {\n    private final Pattern[] mPatterns;\n\n    public RegexSet(String... patterns) {\n        mPatterns = new Pattern[patterns.length];\n        for (int i = 0; i < patterns.length; i++) {\n            mPatterns[i] = Pattern.compile(patterns[i]);\n        }\n    }\n\n    public boolean matches(String s) {\n        for (Pattern p: mPatterns) {\n            if (p.matcher(s).matches()) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/Str.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * A String and a Position, where it came from in source code.\n */\npublic class Str implements Comparable<Str> {\n    private String mValue;\n    private Position mPosition;\n\n    public Str(String s) {\n        mValue = s;\n        mPosition = new Position();\n    }\n\n    public Str(Position pos, String s) {\n        mValue = s;\n        mPosition = pos;\n    }\n\n    public int length() {\n        return mValue.length();\n    }\n\n    @Override\n    public String toString() {\n        return mValue;\n    }\n\n    public Position getPosition() {\n        return mPosition;\n    }\n\n    /**\n     * Str is equal if the string value is equal, regardless of whether the position\n     * is the same.\n     */\n    @Override\n    public boolean equals(Object o) {\n        if (!(o instanceof Str)) {\n            return false;\n        }\n        final Str that = (Str)o;\n        return mValue.equals(that.mValue);\n    }\n\n    @Override\n    public int hashCode() {\n        return mValue.hashCode();\n    }\n\n    @Override\n    public int compareTo(Str that) {\n        return this.mValue.compareTo(that.mValue);\n    }\n\n    public static ArrayList<Str> toList(Position pos, List<String> list) {\n        final ArrayList<Str> result = new ArrayList(list.size());\n        for (String s: list) {\n            result.add(new Str(pos, s));\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/Value.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Pattern;\nimport java.util.regex.Matcher;\n\n/**\n * Class to hold the two types of variables we support, strings and lists of strings.\n */\npublic class Value {\n    private static final Pattern SPACES = Pattern.compile(\"\\\\s+\");\n\n    private final VarType mVarType;\n    private final Str mStr;\n    private final ArrayList<Str> mList;\n\n    /**\n     * Construct an appropriately typed empty value.\n     */\n    public Value(VarType varType) {\n        mVarType = varType;\n        if (varType == VarType.LIST) {\n            mStr = null;\n            mList = new ArrayList();\n            mList.add(new Str(\"\"));\n        } else {\n            mStr = new Str(\"\");\n            mList = null;\n        }\n    }\n\n    public Value(VarType varType, Str str) {\n        mVarType = varType;\n        mStr = str;\n        mList = null;\n    }\n\n    public Value(List<Str> list) {\n        mVarType = VarType.LIST;\n        mStr = null;\n        mList = new ArrayList(list);\n    }\n\n    public VarType getVarType() {\n        return mVarType;\n    }\n\n    public Str getStr() {\n        return mStr;\n    }\n\n    public List<Str> getList() {\n        return mList;\n    }\n\n    /**\n     * Normalize a string that is behaving as a list.\n     */\n    public static String normalize(String str) {\n        if (str == null) {\n            return null;\n        }\n        return SPACES.matcher(str.trim()).replaceAll(\" \").trim();\n    }\n\n    /**\n     * Normalize a string that is behaving as a list.\n     */\n    public static Str normalize(Str str) {\n        if (str == null) {\n            return null;\n        }\n        return new Str(str.getPosition(), normalize(str.toString()));\n    }\n\n    /**\n     * Normalize a this Value into the same format as normalize(Str).\n     */\n    public static Str normalize(Value val) {\n        if (val == null) {\n            return null;\n        }\n        if (val.mStr != null) {\n            return normalize(val.mStr);\n        }\n\n        if (val.mList.size() == 0) {\n            return new Str(\"\");\n        }\n\n        StringBuilder result = new StringBuilder();\n        final int size = val.mList.size();\n        boolean first = true;\n        for (int i = 0; i < size; i++) {\n            String s = val.mList.get(i).toString().trim();\n            if (s.length() > 0) {\n                if (!first) {\n                    result.append(\" \");\n                } else {\n                    first = false;\n                }\n                result.append(s);\n            }\n        }\n\n        // Just use the first item's position.\n        return new Str(val.mList.get(0).getPosition(), result.toString());\n    }\n\n    /**\n     * Put each word in 'str' on its own line in make format. If 'val' is null,\n     * 'nullValue' is returned.\n     */\n    public static String oneLinePerWord(Value val, String nullValue) {\n        if (val == null) {\n            return nullValue;\n        }\n        final String s = normalize(val).toString();\n        final Matcher m = SPACES.matcher(s);\n        final StringBuilder result = new StringBuilder();\n        if (s.length() > 0 && (val.mVarType == VarType.LIST || m.find())) {\n            result.append(\"\\\\\\n  \");\n        }\n        result.append(m.replaceAll(\" \\\\\\\\\\n  \"));\n        return result.toString();\n    }\n\n    /**\n     * Put each word in 'str' on its own line in make format. If 'str' is null,\n     * nullValue is returned.\n     */\n    public static String oneLinePerWord(Str str, String nullValue) {\n        if (str == null) {\n            return nullValue;\n        }\n        final Matcher m = SPACES.matcher(normalize(str.toString()));\n        final StringBuilder result = new StringBuilder();\n        if (m.find()) {\n            result.append(\"\\\\\\n  \");\n        }\n        result.append(m.replaceAll(\" \\\\\\\\\\n  \"));\n        return result.toString();\n    }\n\n    /**\n     * Return a string representing this value with detailed debugging information.\n     */\n    public static String debugString(Value val) {\n        if (val == null) {\n            return \"null\";\n        }\n\n        final StringBuilder str = new StringBuilder(\"Value(\");\n        if (val.mStr != null) {\n            str.append(\"mStr=\");\n            str.append(\"\\\"\");\n            str.append(val.mStr.toString());\n            str.append(\"\\\"\");\n            if (false) {\n                str.append(\" (\");\n                str.append(val.mStr.getPosition().toString());\n                str.append(\")\");\n            }\n        }\n        if (val.mList != null) {\n            str.append(\"mList=\");\n            str.append(\"[\");\n            for (Str s: val.mList) {\n                str.append(\" \\\"\");\n                str.append(s.toString());\n                if (false) {\n                    str.append(\"\\\" (\");\n                    str.append(s.getPosition().toString());\n                    str.append(\")\");\n                } else {\n                    str.append(\"\\\"\");\n                }\n            }\n            str.append(\" ]\");\n        }\n        str.append(\")\");\n        return str.toString();\n    }\n\n    /**\n     * Get the Positions of all of the parts of this Value.\n     */\n    public List<Position> getPositions() {\n        List<Position> result = new ArrayList();\n        if (mStr != null) {\n            result.add(mStr.getPosition());\n        }\n        if (mList != null) {\n            for (Str str: mList) {\n                result.add(str.getPosition());\n            }\n        }\n        return result;\n    }\n}\n\n"
  },
  {
    "path": "tools/product_config/src/com/android/build/config/VarType.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\n/**\n * Whether a product config variable is a list or single-value variable.\n */\npublic enum VarType {\n    /**\n     * A product config variable that is a list of space separated strings.\n     * These are defined by _product_single_value_vars in product.mk.\n     */\n    LIST,\n\n    /**\n     * A product config varaible that is a single string.\n     * These are defined by _product_list_vars in product.mk.\n     */\n    SINGLE,\n\n    /**\n     * A variable that is given the special product config handling but is\n     * nonetheless defined by product config makefiles.\n     */\n    UNKNOWN\n}\n\n"
  },
  {
    "path": "tools/product_config/test/com/android/build/config/CsvParserTest.java",
    "content": "\n/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.android.build.config;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.StringReader;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * Test for CSV parser class.\n */\npublic class CsvParserTest {\n    public String listsToStrings(String[] expected, List<String> actual) {\n        return \"expected=\" + Arrays.toString(expected)\n                + \" actual=\" + Arrays.toString(actual.toArray());\n    }\n\n    public void assertLineEquals(CsvParser.Line actual, int lineno, String... fields) {\n        if (actual.getLine() != lineno) {\n            throw new RuntimeException(\"lineno mismatch: expected=\" + lineno\n                    + \" actual=\" + actual.getLine());\n        }\n        if (fields.length != actual.getFields().size()) {\n            throw new RuntimeException(\"getFields().size() mismatch: expected=\" + fields.length\n                    + \" actual=\" + actual.getFields().size()\n                    + \" values: \" + listsToStrings(fields, actual.getFields()));\n        }\n        for (int i = 0; i < fields.length; i++) {\n            if (!fields[i].equals(actual.getFields().get(i))) {\n                throw new RuntimeException(\"getFields().get(\" + i + \") mismatch: expected=\"\n                        + fields[i] + \" actual=\" + actual.getFields().get(i)\n                        + \" values: \" + listsToStrings(fields, actual.getFields()));\n\n            }\n        }\n    }\n\n    @Test\n    public void testEmptyString() throws Exception {\n        List<CsvParser.Line> lines = CsvParser.parse(new StringReader(\n                    \"\"));\n\n        Assert.assertEquals(0, lines.size());\n    }\n\n    @Test\n    public void testLexerOneCharacter() throws Exception {\n        List<CsvParser.Line> lines = CsvParser.parse(new StringReader(\n                    \"a\"));\n\n        Assert.assertEquals(1, lines.size());\n        assertLineEquals(lines.get(0), 1, \"a\");\n    }\n\n    @Test\n    public void testLexerTwoFieldsNoNewline() throws Exception {\n        List<CsvParser.Line> lines = CsvParser.parse(new StringReader(\n                    \"a,b\"));\n\n        Assert.assertEquals(1, lines.size());\n        assertLineEquals(lines.get(0), 1, \"a\", \"b\");\n    }\n\n    @Test\n    public void testLexerTwoFieldsNewline() throws Exception {\n        List<CsvParser.Line> lines = CsvParser.parse(new StringReader(\n                    \"a,b\\n\"));\n\n        Assert.assertEquals(1, lines.size());\n        assertLineEquals(lines.get(0), 1, \"a\", \"b\");\n    }\n\n    @Test\n    public void testEndsWithTwoNewlines() throws Exception {\n        List<CsvParser.Line> lines = CsvParser.parse(new StringReader(\n                    \"a,b\\n\\n\"));\n\n        Assert.assertEquals(1, lines.size());\n        assertLineEquals(lines.get(0), 1, \"a\", \"b\");\n    }\n\n    @Test\n    public void testOnlyNewlines() throws Exception {\n        List<CsvParser.Line> lines = CsvParser.parse(new StringReader(\n                    \"\\n\\n\\n\\n\"));\n\n        Assert.assertEquals(0, lines.size());\n    }\n\n\n    @Test\n    public void testLexerComplex() throws Exception {\n        List<CsvParser.Line> lines = CsvParser.parse(new StringReader(\n                    \",\\\"ab\\\"\\\"\\nc\\\",,de\\n\"\n                    + \"fg,\\n\"\n                    + \"\\n\"\n                    + \",\\n\"\n                    + \"hijk\"));\n\n        Assert.assertEquals(4, lines.size());\n        assertLineEquals(lines.get(0), 2, \"\", \"ab\\\"\\nc\", \"\", \"de\");\n        assertLineEquals(lines.get(1), 3, \"fg\", \"\");\n        assertLineEquals(lines.get(2), 5, \"\", \"\");\n        assertLineEquals(lines.get(3), 6, \"hijk\");\n    }\n\n    @Test\n    public void testEndInsideQuoted() throws Exception {\n        try {\n            List<CsvParser.Line> lines = CsvParser.parse(new StringReader(\n                        \"\\\"asd\"));\n            throw new RuntimeException(\"Didn't throw ParseException\");\n        } catch (CsvParser.ParseException ex) {\n            System.out.println(\"Caught: \" + ex);\n        }\n    }\n\n    @Test\n    public void testCharacterAfterQuotedField() throws Exception {\n        try {\n            List<CsvParser.Line> lines = CsvParser.parse(new StringReader(\n                        \"\\\"\\\"a\"));\n            throw new RuntimeException(\"Didn't throw ParseException\");\n        } catch (CsvParser.ParseException ex) {\n            System.out.println(\"Caught: \" + ex);\n        }\n    }\n}\n\n"
  },
  {
    "path": "tools/product_config/test/com/android/build/config/ErrorReporterTest.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.HashSet;\nimport java.util.List;\n\npublic class ErrorReporterTest {\n    /**\n     * Test that errors can be recorded and retrieved.\n     */\n    @Test\n    public void testAdding() {\n        TestErrors errors = new TestErrors();\n\n        errors.ERROR.add(new Position(\"a\", 12), \"Errrororrrr\");\n\n        Assert.assertTrue(errors.hadWarningOrError());\n        Assert.assertTrue(errors.hadError());\n\n        List<TestErrors.Entry> entries = errors.getEntries();\n        Assert.assertEquals(1, entries.size());\n\n        TestErrors.Entry entry = entries.get(0);\n        Assert.assertEquals(errors.ERROR, entry.getCategory());\n        Assert.assertEquals(\"a\", entry.getPosition().getFile());\n        Assert.assertEquals(12, entry.getPosition().getLine());\n        Assert.assertEquals(\"Errrororrrr\", entry.getMessage());\n\n        Assert.assertNotEquals(\"\", errors.getErrorMessages());\n    }\n\n    /**\n     * Test that not adding an error doesn't record errors.\n     */\n    @Test\n    public void testNoError() {\n        TestErrors errors = new TestErrors();\n\n        Assert.assertFalse(errors.hadWarningOrError());\n        Assert.assertFalse(errors.hadError());\n        Assert.assertEquals(\"\", errors.getErrorMessages());\n    }\n\n    /**\n     * Test that not adding a warning doesn't record errors.\n     */\n    @Test\n    public void testWarning() {\n        TestErrors errors = new TestErrors();\n\n        errors.WARNING.add(\"Waaaaarninggggg\");\n\n        Assert.assertTrue(errors.hadWarningOrError());\n        Assert.assertFalse(errors.hadError());\n        Assert.assertNotEquals(\"\", errors.getErrorMessages());\n    }\n\n    /**\n     * Test that hidden warnings don't report.\n     */\n    @Test\n    public void testHidden() {\n        TestErrors errors = new TestErrors();\n\n        errors.HIDDEN.add(\"Hidddeennn\");\n\n        Assert.assertFalse(errors.hadWarningOrError());\n        Assert.assertFalse(errors.hadError());\n        Assert.assertEquals(\"\", errors.getErrorMessages());\n    }\n\n    /**\n     * Test changing an error level.\n     */\n    @Test\n    public void testSetLevel() {\n        TestErrors errors = new TestErrors();\n        Assert.assertEquals(TestErrors.Level.ERROR, errors.ERROR.getLevel());\n\n        errors.ERROR.setLevel(TestErrors.Level.WARNING);\n\n        Assert.assertEquals(TestErrors.Level.WARNING, errors.ERROR.getLevel());\n    }\n\n    /**\n     * Test that changing a fixed error fails.\n     */\n    @Test\n    public void testSetLevelFails() {\n        TestErrors errors = new TestErrors();\n        Assert.assertEquals(TestErrors.Level.ERROR, errors.ERROR_FIXED.getLevel());\n\n        boolean exceptionThrown = false;\n        try {\n            errors.ERROR_FIXED.setLevel(TestErrors.Level.WARNING);\n        } catch (RuntimeException ex) {\n            exceptionThrown = true;\n        }\n\n        Assert.assertTrue(exceptionThrown);\n        Assert.assertEquals(TestErrors.Level.ERROR, errors.ERROR_FIXED.getLevel());\n    }\n}\n"
  },
  {
    "path": "tools/product_config/test/com/android/build/config/OptionsTest.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.HashMap;\n\npublic class OptionsTest {\n\n    private Options parse(Errors errors, String[] args) {\n        final HashMap<String, String> env = new HashMap();\n        env.put(\"TARGET_PRODUCT\", \"test_product\");\n        env.put(\"TARGET_BUILD_VARIANT\", \"user\");\n        final Options.Parser parser = new Options.Parser(errors, args, env);\n        parser.setSkipRequiredArgValidation();\n        return parser.parse();\n    }\n\n    @Test\n    public void testErrorMissingLast() {\n        final Errors errors = new Errors();\n\n        final Options options = parse(errors, new String[] {\n                    \"--error\"\n                });\n\n        Assert.assertNotEquals(\"\", TestErrors.getErrorMessages(errors));\n        Assert.assertEquals(Options.Action.DEFAULT, options.getAction());\n        TestErrors.assertHasEntry(errors.ERROR_COMMAND_LINE, errors);\n    }\n\n    @Test\n    public void testErrorMissingNotLast() {\n        final Errors errors = new Errors();\n\n        final Options options = parse(errors, new String[] {\n                    \"--error\", \"--warning\", \"2\"\n                });\n\n        Assert.assertNotEquals(\"\", TestErrors.getErrorMessages(errors));\n        Assert.assertEquals(Options.Action.DEFAULT, options.getAction());\n        TestErrors.assertHasEntry(errors.ERROR_COMMAND_LINE, errors);\n    }\n\n    @Test\n    public void testErrorNotNumeric() {\n        final Errors errors = new Errors();\n\n        final Options options = parse(errors, new String[] {\n                    \"--error\", \"notgood\"\n                });\n\n        Assert.assertNotEquals(\"\", TestErrors.getErrorMessages(errors));\n        Assert.assertEquals(Options.Action.DEFAULT, options.getAction());\n        TestErrors.assertHasEntry(errors.ERROR_COMMAND_LINE, errors);\n    }\n\n    @Test\n    public void testErrorInvalidError() {\n        final Errors errors = new Errors();\n\n        final Options options = parse(errors, new String[] {\n                    \"--error\", \"50000\"\n                });\n\n        Assert.assertEquals(\"\", TestErrors.getErrorMessages(errors));\n        Assert.assertEquals(Options.Action.DEFAULT, options.getAction());\n        TestErrors.assertHasEntry(errors.WARNING_UNKNOWN_COMMAND_LINE_ERROR, errors);\n    }\n\n    @Test\n    public void testErrorOne() {\n        final Errors errors = new Errors();\n\n        final Options options = parse(errors, new String[] {\n                    \"--error\", \"2\"\n                });\n\n        Assert.assertEquals(\"\", TestErrors.getErrorMessages(errors));\n        Assert.assertEquals(Options.Action.DEFAULT, options.getAction());\n        Assert.assertFalse(errors.hadWarningOrError());\n    }\n\n    @Test\n    public void testWarningOne() {\n        final Errors errors = new Errors();\n\n        final Options options = parse(errors, new String[] {\n                    \"--warning\", \"2\"\n                });\n\n        Assert.assertEquals(\"\", TestErrors.getErrorMessages(errors));\n        Assert.assertEquals(Options.Action.DEFAULT, options.getAction());\n        Assert.assertFalse(errors.hadWarningOrError());\n    }\n\n    @Test\n    public void testHideOne() {\n        final Errors errors = new Errors();\n\n        final Options options = parse(errors, new String[] {\n                    \"--hide\", \"2\"\n                });\n\n        Assert.assertEquals(\"\", TestErrors.getErrorMessages(errors));\n        Assert.assertEquals(Options.Action.DEFAULT, options.getAction());\n        Assert.assertFalse(errors.hadWarningOrError());\n    }\n\n    @Test\n    public void testEnv() {\n        final Errors errors = new Errors();\n\n        final Options options = parse(errors, new String[0]);\n\n        Assert.assertEquals(\"test_product\", options.getProduct());\n        Assert.assertEquals(\"user\", options.getVariant());\n        Assert.assertFalse(errors.hadWarningOrError());\n    }\n}\n\n"
  },
  {
    "path": "tools/product_config/test/com/android/build/config/PositionTest.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.HashMap;\n\npublic class PositionTest {\n\n    @Test\n    public void testParseEmpty() {\n        final Position pos = Position.parse(\"\");\n\n        Assert.assertEquals(null, pos.getFile());\n        Assert.assertEquals(Position.NO_LINE, pos.getLine());\n    }\n\n    @Test\n    public void testParseOnlyFile() {\n        final Position pos = Position.parse(\"asdf\");\n\n        Assert.assertEquals(\"asdf\", pos.getFile());\n        Assert.assertEquals(Position.NO_LINE, pos.getLine());\n    }\n\n    @Test\n    public void testParseBoth() {\n        final Position pos = Position.parse(\"asdf:1\");\n\n        Assert.assertEquals(\"asdf\", pos.getFile());\n        Assert.assertEquals(1, pos.getLine());\n    }\n\n    @Test\n    public void testParseEndsWithColon() {\n        final Position pos = Position.parse(\"asdf:\");\n\n        Assert.assertEquals(\"asdf\", pos.getFile());\n        Assert.assertEquals(Position.NO_LINE, pos.getLine());\n    }\n\n    @Test\n    public void testParseEndsWithSpace() {\n        final Position pos = Position.parse(\"asdf: \");\n\n        Assert.assertEquals(\"asdf\", pos.getFile());\n        Assert.assertEquals(Position.NO_LINE, pos.getLine());\n    }\n\n\n}\n\n"
  },
  {
    "path": "tools/product_config/test/com/android/build/config/TestErrors.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * Errors for testing.\n */\npublic class TestErrors extends ErrorReporter {\n\n    public static final int ERROR_CODE = 1;\n\n    public final Category ERROR = new Category(ERROR_CODE, true, Level.ERROR,\n            \"An error.\");\n\n    public static final int WARNING_CODE = 2;\n\n    public final Category WARNING = new Category(WARNING_CODE, true, Level.WARNING,\n            \"A warning.\");\n\n    public static final int HIDDEN_CODE = 3;\n\n    public final Category HIDDEN = new Category(HIDDEN_CODE, true, Level.HIDDEN,\n            \"A hidden warning.\");\n\n    public static final int ERROR_FIXED_CODE = 4;\n\n    public final Category ERROR_FIXED = new Category(ERROR_FIXED_CODE, false, Level.ERROR,\n            \"An error that can't have its level changed.\");\n\n    public void assertHasEntry(Errors.Category category) {\n        assertHasEntry(category, this);\n    }\n\n    public String getErrorMessages() {\n        return getErrorMessages(this);\n    }\n\n    public static void assertHasEntry(Errors.Category category, ErrorReporter errors) {\n        StringBuilder found = new StringBuilder();\n        for (Errors.Entry entry: errors.getEntries()) {\n            if (entry.getCategory() == category) {\n                return;\n            }\n            found.append(' ');\n            found.append(entry.getCategory().getCode());\n        }\n        throw new AssertionError(\"No error category \" + category.getCode() + \" found.\"\n                + \" Found category codes were:\" + found);\n    }\n\n    public static String getErrorMessages(ErrorReporter errors) {\n        final ByteArrayOutputStream stream = new ByteArrayOutputStream();\n        try {\n            errors.printErrors(new PrintStream(stream, true, StandardCharsets.UTF_8.name()));\n        } catch (UnsupportedEncodingException ex) {\n            // utf-8 is always supported\n        }\n        return new String(stream.toByteArray(), StandardCharsets.UTF_8);\n    }\n}\n\n"
  },
  {
    "path": "tools/product_config/test/com/android/build/config/TestRunner.java",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.build.config;\n\nimport org.junit.runner.Description;\nimport org.junit.runner.JUnitCore;\nimport org.junit.runner.Result;\nimport org.junit.runner.notification.Failure;\nimport org.junit.runner.notification.RunListener;\n\npublic class TestRunner {\n    public static void main(String[] args) {\n        JUnitCore junit = new JUnitCore();\n\n        junit.addListener(new RunListener() {\n                    @Override\n                    public void testStarted(Description description) {\n                        System.out.println(\"\\nSTARTING: \" + description.getDisplayName());\n                    }\n\n                    @Override\n                    public void testFailure(Failure failure) {\n                        System.out.println(\"FAILED: \"\n                                + failure.getDescription().getDisplayName());\n                        System.out.println(failure.getTrace());\n                    }\n                });\n        Result result = junit.run(CsvParserTest.class,\n                                  ErrorReporterTest.class,\n                                  OptionsTest.class,\n                                  PositionTest.class);\n        if (!result.wasSuccessful()) {\n            System.out.println(\"\\n*** FAILED ***\");\n        }\n    }\n}\n\n"
  },
  {
    "path": "tools/product_config/test.sh",
    "content": "#!/bin/bash\n\n#\n# This script runs the full set of tests for product config:\n# 1. Build the product-config tool.\n# 2. Run the unit tests.\n# 3. Run the product config for every product available in the current\n#    source tree, for each of user, userdebug and eng.\n#       - To restrict which products or variants are run, set the\n#         PRODUCTS or VARIANTS environment variables.\n#       - Products for which the make based product config fails are\n#         skipped.\n#\n\n# The PRODUCTS variable is used by the build, and setting it in the environment\n# interferes with that, so unset it.  (That should probably be fixed)\nproducts=$PRODUCTS\nvariants=$VARIANTS\nunset PRODUCTS\nunset VARIANTS\n\n# Don't use lunch from the user's shell\nunset TARGET_PRODUCT\nunset TARGET_BUILD_VARIANT\n\nfunction die() {\n    format=$1\n    shift\n    printf \"$format\\nStopping...\\n\" $@ >&2\n    exit 1;\n}\n\n[[ -f build/make/envsetup.sh ]] || die \"Run this script from the root of the tree.\"\n: ${products:=$(build/soong/soong_ui.bash --dumpvar-mode all_named_products | sed -e \"s/ /\\n/g\" | sort -u )}\n: ${variants:=\"user userdebug eng\"}\n: ${CKATI_BIN:=prebuilts/build-tools/$(build/soong/soong_ui.bash --dumpvar-mode HOST_PREBUILT_TAG)/bin/ckati}\n\nfunction if_signal_exit() {\n    [[ $1 -lt 128 ]] || exit $1\n}\n\nbuild/soong/soong_ui.bash --build-mode --all-modules --dir=\"$(pwd)\" product-config-test product-config \\\n    || die \"Build failed.\"\n\necho\necho Running unit tests\njava -jar out/host/linux-x86/testcases/product-config-test/product-config-test.jar\nunit_tests=$?\nif_signal_exit $unit_tests\n\nfailed_baseline_checks=\nfor product in $products ; do\n    for variant in $variants ; do\n        echo\n        echo \"Checking: lunch $product-$variant\"\n\n        TARGET_PRODUCT=$product \\\n            TARGET_BUILD_VARIANT=$variant \\\n            build/soong/soong_ui.bash --dumpvar-mode TARGET_PRODUCT &> /dev/null\n        exit_status=$?\n        if_signal_exit $exit_status\n        if [ $exit_status -ne 0 ] ; then\n            echo \"*** Combo fails with make, skipping product-config test run for $product-$variant\"\n        else\n            rm -rf out/config/$product-$variant\n            TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant product-config \\\n                            --ckati_bin $CKATI_BIN \\\n                            --error 1000\n            exit_status=$?\n            if_signal_exit $exit_status\n            if [ $exit_status -ne 0 ] ; then\n                failed_baseline_checks=\"$failed_baseline_checks $product-$variant\"\n            fi\n            if [ \"$CHECK_FOR_RULES\" != \"\" ] ; then\n                # This is a little bit of sleight of hand for good output formatting at the\n                # expense of speed. We've already run the command once without\n                # ALLOW_RULES_IN_PRODUCT_CONFIG, so we know it passes there. We run it again\n                # with ALLOW_RULES_IN_PRODUCT_CONFIG=error to see if it fails, but that will\n                # cause it to only print the first error. But we want to see all of them,\n                # so if it fails we run it a third time with ALLOW_RULES_IN_PRODUCT_CONFIG=warning,\n                # so we can see all the warnings.\n                TARGET_PRODUCT=$product \\\n                    TARGET_BUILD_VARIANT=$variant \\\n                    ALLOW_RULES_IN_PRODUCT_CONFIG=error \\\n                    build/soong/soong_ui.bash --dumpvar-mode TARGET_PRODUCT &> /dev/null\n                exit_status=$?\n                if_signal_exit $exit_status\n                if [ $exit_status -ne 0 ] ; then\n                    TARGET_PRODUCT=$product \\\n                        TARGET_BUILD_VARIANT=$variant \\\n                        ALLOW_RULES_IN_PRODUCT_CONFIG=warning \\\n                        build/soong/soong_ui.bash --dumpvar-mode TARGET_PRODUCT > /dev/null\n                    failed_rule_checks=\"$failed_rule_checks $product-$variant\"\n                fi\n            fi\n        fi\n    done\ndone\n\necho\necho\necho \"------------------------------\"\necho SUMMARY\necho \"------------------------------\"\n\necho -n \"Unit tests        \"\nif [ $unit_tests -eq 0 ] ; then echo PASSED ; else echo FAILED ; fi\n\necho -n \"Baseline checks   \"\nif [ \"$failed_baseline_checks\" = \"\" ] ; then echo PASSED ; else echo FAILED ; fi\nfor combo in $failed_baseline_checks ; do\n    echo \"                   ... $combo\"\ndone\n\necho -n \"Rules checks      \"\nif [ \"$failed_rule_checks\" = \"\" ] ; then echo PASSED ; else echo FAILED ; fi\nfor combo in $failed_rule_checks ; do\n    echo \"                   ... $combo\"\ndone\n\n"
  },
  {
    "path": "tools/protos/Android.bp",
    "content": "// Copyright 2023 Google Inc. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\npython_library_host {\n    name: \"metadata_file_proto_py\",\n    srcs: [\n        \"metadata_file.proto\",\n    ],\n    proto: {\n        canonical_path_from_root: false,\n    },\n}\n"
  },
  {
    "path": "tools/protos/metadata_file.proto",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage metadata_file;\n\n// Proto definition of METADATA files of packages in AOSP codebase.\nmessage Metadata {\n  // Name of the package.\n  optional string name = 1;\n\n  // A short description (a few lines) of the package.\n  // Example: \"Handles location lookups, throttling, batching, etc.\"\n  optional string description = 2;\n\n  // Specifies additional data about third-party packages.\n  optional ThirdParty third_party = 3;\n}\n\nmessage ThirdParty {\n  // URL(s) associated with the package.\n  //\n  // At a minimum, all packages must specify a URL which identifies where it\n  // came from, containing a type of: ARCHIVE, GIT or OTHER. Typically,\n  // a package should contain only a single URL from these types.  Occasionally,\n  // a package may be broken across multiple archive files for whatever reason,\n  // in which case having multiple ARCHIVE URLs is okay.  However, this should\n  // not be used to combine different logical packages that are versioned and\n  // possibly licensed differently.\n  repeated URL url = 1;\n\n  // The package version.  In order of preference, this should contain:\n  //  - If the package comes from Git or another source control system,\n  //    a specific tag or revision in source control, such as \"r123\" or\n  //    \"58e27d2\".  This MUST NOT be a mutable ref such as a branch name.\n  //  - a released package version such as \"1.0\", \"2.3-beta\", etc.\n  //  - the date the package was retrieved, formatted as \"As of YYYY-MM-DD\".\n  optional string version = 2;\n\n  // The date of the change in which the package was last upgraded from\n  // upstream.\n  // This should only identify package upgrades from upstream, not local\n  // modifications. This may identify the date of either the original or\n  // merged change.\n  //\n  // Note: this is NOT the date that this version of the package was released\n  // externally.\n  optional Date last_upgrade_date = 3;\n\n  // License type that identifies how the package may be used.\n  optional LicenseType license_type = 4;\n\n  // An additional note explaining the licensing of this package.  This is most\n  // commonly used with commercial license.\n  optional string license_note = 5;\n\n  // Description of local changes that have been made to the package.  This does\n  // not need to (and in most cases should not) attempt to include an exhaustive\n  // list of all changes, but may instead direct readers to review the local\n  // commit history, a collection of patch files, a separate README.md (or\n  // similar) document, etc.\n  // Note: Use of this field to store IDs of advisories fixed with a backported\n  // patch is deprecated, use \"security.mitigated_security_patch\" instead.\n  optional string local_modifications = 6;\n\n  // Security related metadata including risk category and any special\n  // instructions for using the package, as determined by an ISE-TPS review.\n  optional Security security = 7;\n\n  // The type of directory this metadata represents.\n  optional DirectoryType type = 8 [default = PACKAGE];\n\n  // The homepage for the package. This will eventually replace\n  // `url { type: HOMEPAGE }`\n  optional string homepage = 9;\n\n  // SBOM information of the package. It is mandatory for prebuilt packages.\n  oneof sbom {\n    // Reference to external SBOM document provided as URL.\n    SBOMRef sbom_ref = 10;\n  }\n\n  // Identifiers for the package.\n  repeated Identifier identifier = 11;\n}\n\n// URL associated with a third-party package.\nmessage URL {\n  enum Type {\n    // The homepage for the package. For example, \"https://bazel.io/\". This URL\n    // is optional, but encouraged to help disambiguate similarly named packages\n    // or to get more information about the package. This is especially helpful\n    // when no other URLs provide human readable resources (such as git:// or\n    // sso:// URLs).\n    HOMEPAGE = 1;\n\n    // The URL of the archive containing the source code for the package, for\n    // example a zip or tgz file.\n    ARCHIVE = 2;\n\n    // The URL of the upstream git repository this package is retrieved from.\n    // For example:\n    //  - https://github.com/git/git.git\n    //  - git://git.kernel.org/pub/scm/git/git.git\n    //\n    // Use of a git URL requires that the package \"version\" value must specify a\n    // specific git tag or revision.\n    GIT = 3;\n\n    // The URL of the upstream SVN repository this package is retrieved from.\n    // For example:\n    //  - http://llvm.org/svn/llvm-project/llvm/\n    //\n    // Use of an SVN URL requires that the package \"version\" value must specify\n    // a specific SVN tag or revision.\n    SVN = 4;\n\n    // The URL of the upstream mercurial repository this package is retrieved\n    // from. For example:\n    //   - https://mercurial-scm.org/repo/evolve\n    //\n    // Use of a mercurial URL requires that the package \"version\" value must\n    // specify a specific tag or revision.\n    HG = 5;\n\n    // The URL of the upstream darcs repository this package is retrieved\n    // from. For example:\n    //   - https://hub.darcs.net/hu.dwim/hu.dwim.util\n    //\n    // Use of a DARCS URL requires that the package \"version\" value must\n    // specify a specific tag or revision.\n    DARCS = 6;\n\n    PIPER = 7;\n\n    // A URL that does not fit any other type. This may also indicate that the\n    // source code was received via email or some other out-of-band way. This is\n    // most commonly used with commercial software received directly from the\n    // vendor. In the case of email, the URL value can be used to provide\n    // additional information about how it was received.\n    OTHER = 8;\n\n    // The URL identifying where the local copy of the package source code can\n    // be found.\n    //\n    // Typically, the metadata files describing a package reside in the same\n    // directory as the source code for the package. In a few rare cases where\n    // they are separate, the LOCAL_SOURCE URL identifies where to find the\n    // source code. This only describes where to find the local copy of the\n    // source; there should always be an additional URL describing where the\n    // package was retrieved from.\n    //\n    // Examples:\n    //  - https://android.googlesource.com/platform/external/apache-http/\n    LOCAL_SOURCE = 9;\n  }\n\n  // The type of resource this URL identifies.\n  optional Type type = 1;\n\n  // The actual URL value.  URLs should be absolute and start with 'http://' or\n  // 'https://' (or occasionally 'git://' or 'ftp://' where appropriate).\n  optional string value = 2;\n}\n\n// License type that identifies how the packages may be used.\nenum LicenseType {\n  BY_EXCEPTION_ONLY = 1;\n  NOTICE = 2;\n  PERMISSIVE = 3;\n  RECIPROCAL = 4;\n  RESTRICTED_IF_STATICALLY_LINKED = 5;\n  RESTRICTED = 6;\n  UNENCUMBERED = 7;\n}\n\n// Identifies security related metadata including risk category and any special\n// instructions for using the package.\nmessage Security {\n  // Security risk category for a package, as determined by an ISE-TPS review.\n  enum Category {\n    CATEGORY_UNSPECIFIED = 0;\n\n    // Package should only be used in a sandboxed environment.\n    // Package should have restricted visibility.\n    SANDBOXED_ONLY = 1;\n\n    // Package should not be used to process user content. It is considered\n    // safe to use to process trusted data only. Package should have restricted\n    // visibility.\n    TRUSTED_DATA_ONLY = 2;\n\n    // Package is considered safe to use.\n    REVIEWED_AND_SECURE = 3;\n  }\n\n  // Identifies the security risk category for the package.  This will be\n  // provided by the ISE-TPS team as the result of a security review of the\n  // package.\n  optional Category category = 1;\n\n  // An additional security note for the package.\n  optional string note = 2;\n\n  // Text tag to categorize the package. It's currently used by security to:\n  // - to disable OSV (https://osv.dev)\n  // support via the `OSV:disable` tag\n  // - to attach CPE to their corresponding packages, for vulnerability\n  // monitoring:\n  //\n  // Please do document your usecase here should you want to add one.\n  repeated string tag = 3;\n\n  // ID of advisories fixed with a mitigated patch, for example CVE-2018-1111.\n  repeated string mitigated_security_patch = 4;\n}\n\nenum DirectoryType {\n  UNDEFINED = 0;\n\n  // This directory represents a package.\n  PACKAGE = 1;\n\n  // This directory is designed to organize multiple third-party PACKAGE\n  // directories.\n  GROUP = 2;\n\n  // This directory contains several PACKAGE directories representing\n  // different versions of the same third-party project.\n  VERSIONS = 3;\n}\n\n// Represents a whole or partial calendar date, such as a birthday. The time of\n// day and time zone are either specified elsewhere or are insignificant. The\n// date is relative to the Gregorian Calendar. This can represent one of the\n// following:\n//\n// * A full date, with non-zero year, month, and day values.\n// * A month and day, with a zero year (for example, an anniversary).\n// * A year on its own, with a zero month and a zero day.\n// * A year and month, with a zero day (for example, a credit card expiration\n//   date).\nmessage Date {\n  // Year of the date. Must be from 1 to 9999, or 0 to specify a date without\n  // a year.\n  optional int32 year = 1;\n  // Month of a year. Must be from 1 to 12, or 0 to specify a year without a\n  // month and day.\n  optional int32 month = 2;\n  // Day of a month. Must be from 1 to 31 and valid for the year and month, or 0\n  // to specify a year by itself or a year and month where the day isn't\n  // significant.\n  optional int32 day = 3;\n}\n\n// Reference to external SBOM document and element corresponding to the package.\n// See https://spdx.github.io/spdx-spec/v2.3/document-creation-information/#66-external-document-references-field\nmessage SBOMRef {\n  // The URL that points to the SBOM document of the upstream package of this\n  // third_party package.\n  optional string url = 1;\n  // Checksum of the SBOM document the url field points to.\n  // Format: e.g. SHA1:<checksum>, or any algorithm defined in\n  // https://spdx.github.io/spdx-spec/v2.3/file-information/#8.4\n  optional string checksum = 2;\n  // SPDXID of the upstream package/file defined in the SBOM document the url field points to.\n  // Format: SPDXRef-[a-zA-Z0-9.-]+, see\n  // https://spdx.github.io/spdx-spec/v2.3/package-information/#72-package-spdx-identifier-field or\n  // https://spdx.github.io/spdx-spec/v2.3/file-information/#82-file-spdx-identifier-field\n  optional string element_id = 3;\n}\n\n// Identifier for a third-party package.\n// See go/tp-metadata-id.\nmessage Identifier {\n  // The type of the identifier. Either an \"ecosystem\" value from\n  // https://ossf.github.io/osv-schema/#affectedpackage-field such as \"Go\",\n  // \"npm\" or \"PyPI\". The \"value\" and \"version\" fields follow the same rules as\n  // defined in the OSV spec.\n\n  // Or one of:\n  //  - \"Git\": The \"value\" field is the URL of the upstream git repository this\n  //  package is retrieved from.\n  //  For example:\n  //   - https://github.com/git/git\n  //   - git://git.kernel.org/pub/scm/git/git\n  //\n  //  Use of a git URL requires that the package \"version\" value must specify a\n  //  specific git tag or revision. This must not be a branch name.\n  //\n  //  - \"SVN\": The \"value\" field is the URL of the upstream SVN repository this\n  //  package is retrieved from.\n  //  For example:\n  //   - http://llvm.org/svn/llvm-project/llvm/\n  //\n  //  Use of an SVN URL requires that the package \"version\" value must specify\n  //  a specific SVN tag or revision. This must not be a branch name.\n  //\n  //  - \"Hg\": The \"value\" field is the URL of the upstream mercurial repository\n  //  this package is retrieved from.\n  //  For example:\n  //   - https://mercurial-scm.org/repo/evolve\n  //\n  //  Use of a mercurial URL requires that the package \"version\" value must\n  //  specify a specific tag or revision. This must not be a branch name.\n  //\n  //  - \"Darcs\": the \"value\" field is the URL of the upstream darcs repository\n  //  this package is retrieved from.\n  //  For example:\n  //   - https://hub.darcs.net/hu.dwim/hu.dwim.util\n  //\n  //  Use of a Darcs URL requires that the package \"version\" value must\n  //  specify a specific tag or revision. This must not be a branch name.\n  //\n  //  - \"Piper\": The \"value\" field is the URL of the upstream piper location.\n  //  This is primarily used when a package is being migrated into third_party\n  //  from elsewhere in Piper, or when a package is being newly developed in\n  //  third_party.\n  //\n  //  - \"VCS\": This is a generic fallback for an unlisted VCS system. The\n  // \"value\" field is the URL of the repository for this VCS.\n  //\n  //  - \"Archive\": The \"value\" field is the URL of the archive containing the\n  //  source code for the package, for example a zip or tgz file.\n  //\n  //  - \"PrebuiltByAlphabet\": This type should be used for archives of primarily\n  //  Google-owned source code (may contain non-Google-owned dependencies),\n  //  which has been built using production Google infrastructure, and copied\n  //  into Android. The \"value\" field is the URL of the prebuilt artifact or\n  //  the relative path of the artifact to the root of a package.\n  //  Example:\n  //    identifier {\n  //      type: \"PrebuiltByAlphabet\",\n  //      version: \"1\",\n  //      value: \"v1/arm84_hdpi.apk\",\n  //    }\n  //    identifier {\n  //      type: \"PrebuiltByAlphabet\",\n  //      version: \"2\",\n  //      value: \"v2/x86_64_xhdpi.apk\",\n  //    }\n  //\n  //  - \"LocalSource\": The \"value\" field is the URL identifying where the local\n  //  copy of the package source code can be found.\n  //  Examples:\n  //   - https://android.googlesource.com/platform/external/apache-http/\n  //\n  //  Typically, the metadata files describing a package reside in the same\n  //  directory as the source code for the package. In a few rare cases where\n  //  they are separate, the LocalSource URL identifies where to find the\n  //  source code. This only describes where to find the local copy of the\n  //  source; there should always be an additional URL describing where the\n  //  package was retrieved from.\n  //\n  //  - \"Other\": An identifier that does not fit any other type. This may also\n  //  indicate that the Source code was received via email or some other\n  //  out-of-band way. This is most commonly used with commercial software\n  //  received directly from the Vendor. In the case of email, the \"value\" field\n  //  can be used to provide additional information about how it was received.\n  optional string type = 1;\n\n  // A human readable string to indicate why a third-package package does not\n  // have this identifier type set.\n  // Example:\n  //   identifier {\n  //     type: \"PyPI\"\n  //     omission_reason: \"Only on Git. Not published to PyPI.\"\n  //   }\n  optional string omission_reason = 2;\n\n  // The value of the package identifier as defined by the \"type\".\n  // Example:\n  //  identifier {\n  //    type: \"PyPI\"\n  //    value: \"django\"\n  //    version: \"3.2.8\"\n  //  }\n  optional string value = 3;\n\n  // The version associated with this package as defined by the \"type\".\n  // Example:\n  //  identifier {\n  //    type: \"PyPI\"\n  //    value: \"django\"\n  //    version: \"3.2.8\"\n  //  }\n  optional string version = 4;\n\n  // The closest version associated with this package as defined by the \"type\".\n  // This should only be set by automated infrastructure by applying automated\n  // heuristics, such as the closest git tag or package version from a package\n  // manifest file (e.g. pom.xml).\n  //\n  // For most identifier types, only one of `version` or `closest_version`\n  // should be set (not both). The exception is source repository types such as\n  // \"Git\", where `version` will refer to a git commit, and `closest_version`\n  // refers to a git tag.\n  // Example:\n  //  identifier {\n  //    type: \"Git\",\n  //    value: \"https://github.com/my/repo\"\n  //    version: \"e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e\"\n  //    closest_version: \"v1.4\"\n  //  }\n  optional string closest_version = 5;\n\n  // When `true`, this Identifier represents the location from which the source\n  // code for this package was originally obtained. This should only be set for\n  // *one* Identifier in a third_party package's METADATA.\n\n  // For external packages, this is typically for the Identifier associated\n  // with the version control system or package manager that was used to\n  // check out or download the code.\n  optional bool primary_source = 6;\n}"
  },
  {
    "path": "tools/rbcrun/Android.bp",
    "content": "//\n// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nblueprint_go_binary {\n    name: \"rbcrun\",\n    srcs: [\"rbcrun/rbcrun.go\"],\n    deps: [\"rbcrun-module\"],\n}\n\nbootstrap_go_package {\n    name: \"rbcrun-module\",\n    srcs: [\n        \"host.go\",\n    ],\n    testSrcs: [\n        \"host_test.go\",\n    ],\n    pkgPath: \"rbcrun\",\n    deps: [\n        \"go-starlark-starlark\",\n        \"go-starlark-starlarkjson\",\n        \"go-starlark-starlarkstruct\",\n        \"go-starlark-starlarktest\",\n    ],\n}\n"
  },
  {
    "path": "tools/rbcrun/README.md",
    "content": "# Roboleaf configuration files interpreter\n\nReads and executes Roboleaf product configuration files.\n\n## Usage\n\n`rbcrun` *options* *VAR=value*... [ *file* ]\n\nA Roboleaf configuration file is a Starlark script. Usually it is read from *file*. The option `-c` allows to provide a\nscript directly on the command line. The option `-f` is there to allow the name of a file script to contain (`=`).\n(i.e., `my=file.rbc` sets `my` to `file.rbc`, `-f my=file.rbc` runs the script from `my=file.rbc`).\n\n### Options\n\n`-d` *dir*\\\nRoot directory for load(\"//path\",...)\n\n`-c` *text*\\\nRead script from *text*\n\n`--perf` *file*\\\nGather performance statistics and save it to *file*. Use \\\n`       go tool prof -top`*file*\\\nto show top CPU users\n\n`-f` *file*\\\nFile to run.\n\n## Extensions\n\nThe runner allows Starlark scripts to use the following features that Bazel's Starlark interpreter does not support:\n\n### Load statement URI\n\nStarlark does not define the format of the load statement's first argument.\nThe Roboleaf configuration interpreter supports the format that Bazel uses\n(`\":file\"` or `\"//path:file\"`). In addition, it allows the URI to end with\n`\"|symbol\"` which defines a single variable `symbol` with `None` value if a\nmodule does not exist. Thus,\n\n```\nload(\":mymodule.rbc|init\", mymodule_init=\"init\")\n```\n\nwill load the module `mymodule.rbc` and export a symbol `init` in it as\n`mymodule_init` if `mymodule.rbc` exists. If `mymodule.rbc` is missing,\n`mymodule_init` will be set to `None`\n\n### Predefined Symbols\n\n#### rblf_env\n\nA `struct` containing environment variables. E.g., `rblf_env.USER` is the username when running on Unix.\n\n#### rblf_cli\n\nA `struct` containing the variable set by the interpreter's command line. That is, running\n\n```\nrbcrun FOO=bar myfile.rbc\n```\n\nwill have the value of `rblf_cli.FOO` be `\"bar\"`\n\n### Predefined Functions\n\n#### rblf_file_exists(*file*)\n\nReturns `True`  if *file* exists\n\n#### rblf_find_files(*top*, *file-pattern*, only_files = 0)\n\nReturns all the paths under *top* whose basename matches *pattern* (which is a shell's glob pattern). If *only_files* is\nnot zero, only the paths to the regular files are returned. The returned paths are relative to *top*.\n\n#### rblf_wildcard(*glob*, *top* = None)\n\nExpands *glob*. If *top* is supplied, expands \"*top*/*glob*\", then removes\n\"*top*/\" prefix from the matching file names.\n\n#### rblf_regex(*pattern*, *text*)\n\nReturns *True* if *text* matches *pattern*.\n\n#### rblf_shell(*command*)\n\nRuns `sh -c \"`*command*`\"`, reads its output, converts all newlines into spaces, chops trailing newline returns this\nstring. This is equivalent to Make's\n`shell` builtin function. *This function will be eventually removed*.\n\n#### rblf_log(*arg*,..., sep=' ')\n\nSame as `print` builtin but writes to stderr."
  },
  {
    "path": "tools/rbcrun/go.mod",
    "content": "module rbcrun\n\nrequire go.starlark.net v0.0.0-20201006213952-227f4aabceb5\n\nreplace go.starlark.net => ../../../../external/starlark-go\n\ngo 1.21\n"
  },
  {
    "path": "tools/rbcrun/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "tools/rbcrun/host.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage rbcrun\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"go.starlark.net/starlark\"\n\t\"go.starlark.net/starlarkstruct\"\n)\n\ntype ExecutionMode int\nconst (\n\tExecutionModeRbc ExecutionMode = iota\n\tExecutionModeScl ExecutionMode = iota\n)\n\nconst allowExternalEntrypointKey = \"allowExternalEntrypoint\"\nconst callingFileKey = \"callingFile\"\nconst executionModeKey = \"executionMode\"\nconst shellKey = \"shell\"\n\ntype modentry struct {\n\tglobals starlark.StringDict\n\terr     error\n}\n\nvar moduleCache = make(map[string]*modentry)\n\nvar rbcBuiltins starlark.StringDict = starlark.StringDict{\n\t\"struct\":   starlark.NewBuiltin(\"struct\", starlarkstruct.Make),\n\t// To convert find-copy-subdir and product-copy-files-by pattern\n\t\"rblf_find_files\": starlark.NewBuiltin(\"rblf_find_files\", find),\n\t// To convert makefile's $(shell cmd)\n\t\"rblf_shell\": starlark.NewBuiltin(\"rblf_shell\", shell),\n\t// Output to stderr\n\t\"rblf_log\": starlark.NewBuiltin(\"rblf_log\", log),\n\t// To convert makefile's $(wildcard foo*)\n\t\"rblf_wildcard\": starlark.NewBuiltin(\"rblf_wildcard\", wildcard),\n}\n\nvar sclBuiltins starlark.StringDict = starlark.StringDict{\n\t\"struct\":   starlark.NewBuiltin(\"struct\", starlarkstruct.Make),\n}\n\nfunc isSymlink(filepath string) (bool, error) {\n\tif info, err := os.Lstat(filepath); err == nil {\n\t\treturn info.Mode() & os.ModeSymlink != 0, nil\n\t} else {\n\t\treturn false, err\n\t}\n}\n\n// Takes a module name (the first argument to the load() function) and returns the path\n// it's trying to load, stripping out leading //, and handling leading :s.\nfunc cleanModuleName(moduleName string, callerDir string, allowExternalPaths bool) (string, error) {\n\tif strings.Count(moduleName, \":\") > 1 {\n\t\treturn \"\", fmt.Errorf(\"at most 1 colon must be present in starlark path: %s\", moduleName)\n\t}\n\n\t// We don't have full support for external repositories, but at least support skylib's dicts.\n\tif moduleName == \"@bazel_skylib//lib:dicts.bzl\" {\n\t\treturn \"external/bazel-skylib/lib/dicts.bzl\", nil\n\t}\n\n\tlocalLoad := false\n\tif strings.HasPrefix(moduleName, \"@//\") {\n\t\tmoduleName = moduleName[3:]\n\t} else if strings.HasPrefix(moduleName, \"//\") {\n\t\tmoduleName = moduleName[2:]\n\t} else if strings.HasPrefix(moduleName, \":\") {\n\t\tmoduleName = moduleName[1:]\n\t\tlocalLoad = true\n\t} else if !allowExternalPaths {\n\t\treturn \"\", fmt.Errorf(\"load path must start with // or :\")\n\t}\n\n\tif ix := strings.LastIndex(moduleName, \":\"); ix >= 0 {\n\t\tmoduleName = moduleName[:ix] + string(os.PathSeparator) + moduleName[ix+1:]\n\t}\n\n\tif filepath.Clean(moduleName) != moduleName {\n\t\treturn \"\", fmt.Errorf(\"load path must be clean, found: %s, expected: %s\", moduleName, filepath.Clean(moduleName))\n\t}\n\tif !allowExternalPaths {\n\t\tif strings.HasPrefix(moduleName, \"../\") {\n\t\t\treturn \"\", fmt.Errorf(\"load path must not start with ../: %s\", moduleName)\n\t\t}\n\t\tif strings.HasPrefix(moduleName, \"/\") {\n\t\t\treturn \"\", fmt.Errorf(\"load path starts with /, use // for a absolute path: %s\", moduleName)\n\t\t}\n\t}\n\n\tif localLoad {\n\t\treturn filepath.Join(callerDir, moduleName), nil\n\t}\n\n\treturn moduleName, nil\n}\n\n// loader implements load statement. The format of the loaded module URI is\n//  [//path]:base[|symbol]\n// The file path is $ROOT/path/base if path is present, <caller_dir>/base otherwise.\n// The presence of `|symbol` indicates that the loader should return a single 'symbol'\n// bound to None if file is missing.\nfunc loader(thread *starlark.Thread, module string) (starlark.StringDict, error) {\n\tmode := thread.Local(executionModeKey).(ExecutionMode)\n\tallowExternalEntrypoint := thread.Local(allowExternalEntrypointKey).(bool)\n\tvar defaultSymbol string\n\tmustLoad := true\n\tif mode == ExecutionModeRbc {\n\t\tpipePos := strings.LastIndex(module, \"|\")\n\t\tif pipePos >= 0 {\n\t\t\tmustLoad = false\n\t\t\tdefaultSymbol = module[pipePos+1:]\n\t\t\tmodule = module[:pipePos]\n\t\t}\n\t}\n\tcallingFile := thread.Local(callingFileKey).(string)\n\tmodulePath, err := cleanModuleName(module, filepath.Dir(callingFile), allowExternalEntrypoint)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\te, ok := moduleCache[modulePath]\n\tif e == nil {\n\t\tif ok {\n\t\t\treturn nil, fmt.Errorf(\"cycle in load graph\")\n\t\t}\n\n\t\t// Add a placeholder to indicate \"load in progress\".\n\t\tmoduleCache[modulePath] = nil\n\n\t\t// Decide if we should load.\n\t\tif !mustLoad {\n\t\t\tif _, err := os.Stat(modulePath); err == nil {\n\t\t\t\tmustLoad = true\n\t\t\t}\n\t\t}\n\n\t\t// Load or return default\n\t\tif mustLoad {\n\t\t\tif strings.HasSuffix(callingFile, \".scl\") && !strings.HasSuffix(modulePath, \".scl\") {\n\t\t\t\treturn nil, fmt.Errorf(\".scl files can only load other .scl files: %q loads %q\", callingFile, modulePath)\n\t\t\t}\n\t\t\t// Switch into scl mode from here on\n\t\t\tif strings.HasSuffix(modulePath, \".scl\") {\n\t\t\t\tmode = ExecutionModeScl\n\t\t\t}\n\n\t\t\tif sym, err := isSymlink(modulePath); sym && err == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"symlinks to starlark files are not allowed. Instead, load the target file and re-export its symbols: %s\", modulePath)\n\t\t\t} else if err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tchildThread := &starlark.Thread{Name: \"exec \" + module, Load: thread.Load}\n\t\t\t// Cheating for the sake of testing:\n\t\t\t// propagate starlarktest's Reporter key, otherwise testing\n\t\t\t// the load function may cause panic in starlarktest code.\n\t\t\tconst testReporterKey = \"Reporter\"\n\t\t\tif v := thread.Local(testReporterKey); v != nil {\n\t\t\t\tchildThread.SetLocal(testReporterKey, v)\n\t\t\t}\n\n\t\t\t// Only the entrypoint starlark file allows external loads.\n\t\t\tchildThread.SetLocal(allowExternalEntrypointKey, false)\n\t\t\tchildThread.SetLocal(callingFileKey, modulePath)\n\t\t\tchildThread.SetLocal(executionModeKey, mode)\n\t\t\tchildThread.SetLocal(shellKey, thread.Local(shellKey))\n\t\t\tif mode == ExecutionModeRbc {\n\t\t\t\tglobals, err := starlark.ExecFile(childThread, modulePath, nil, rbcBuiltins)\n\t\t\t\te = &modentry{globals, err}\n\t\t\t} else if mode == ExecutionModeScl {\n\t\t\t\tglobals, err := starlark.ExecFile(childThread, modulePath, nil, sclBuiltins)\n\t\t\t\te = &modentry{globals, err}\n\t\t\t} else {\n\t\t\t\treturn nil, fmt.Errorf(\"unknown executionMode %d\", mode)\n\t\t\t}\n\t\t} else {\n\t\t\te = &modentry{starlark.StringDict{defaultSymbol: starlark.None}, nil}\n\t\t}\n\n\t\t// Update the cache.\n\t\tmoduleCache[modulePath] = e\n\t}\n\treturn e.globals, e.err\n}\n\n// wildcard(pattern, top=None) expands shell's glob pattern. If 'top' is present,\n// the 'top/pattern' is globbed and then 'top/' prefix is removed.\nfunc wildcard(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple,\n\tkwargs []starlark.Tuple) (starlark.Value, error) {\n\tvar pattern string\n\tvar top string\n\n\tif err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &pattern, &top); err != nil {\n\t\treturn starlark.None, err\n\t}\n\n\tvar files []string\n\tvar err error\n\tif top == \"\" {\n\t\tif files, err = filepath.Glob(pattern); err != nil {\n\t\t\treturn starlark.None, err\n\t\t}\n\t} else {\n\t\tprefix := top + string(filepath.Separator)\n\t\tif files, err = filepath.Glob(prefix + pattern); err != nil {\n\t\t\treturn starlark.None, err\n\t\t}\n\t\tfor i := range files {\n\t\t\tfiles[i] = strings.TrimPrefix(files[i], prefix)\n\t\t}\n\t}\n\t// Kati uses glob(3) with no flags, which means it's sorted\n\t// because GLOB_NOSORT is not passed. Go's glob is not\n\t// guaranteed to sort the results.\n\tsort.Strings(files)\n\treturn makeStringList(files), nil\n}\n\n// find(top, pattern, only_files = 0) returns all the paths under 'top'\n// whose basename matches 'pattern' (which is a shell's glob pattern).\n// If 'only_files' is non-zero, only the paths to the regular files are\n// returned. The returned paths are relative to 'top'.\nfunc find(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple,\n\tkwargs []starlark.Tuple) (starlark.Value, error) {\n\tvar top, pattern string\n\tvar onlyFiles int\n\tif err := starlark.UnpackArgs(b.Name(), args, kwargs,\n\t\t\"top\", &top, \"pattern\", &pattern, \"only_files?\", &onlyFiles); err != nil {\n\t\treturn starlark.None, err\n\t}\n\ttop = filepath.Clean(top)\n\tpattern = filepath.Clean(pattern)\n\t// Go's filepath.Walk is slow, consider using OS's find\n\tvar res []string\n\terr := filepath.WalkDir(top, func(path string, d fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\tif d != nil && d.IsDir() {\n\t\t\t\treturn fs.SkipDir\n\t\t\t} else {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\trelPath := strings.TrimPrefix(path, top)\n\t\tif len(relPath) > 0 && relPath[0] == os.PathSeparator {\n\t\t\trelPath = relPath[1:]\n\t\t}\n\t\t// Do not return top-level dir\n\t\tif len(relPath) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tif matched, err := filepath.Match(pattern, d.Name()); err == nil && matched && (onlyFiles == 0 || d.Type().IsRegular()) {\n\t\t\tres = append(res, relPath)\n\t\t}\n\t\treturn nil\n\t})\n\treturn makeStringList(res), err\n}\n\n// shell(command) runs OS shell with given command and returns back\n// its output the same way as Make's $(shell ) function. The end-of-lines\n// (\"\\n\" or \"\\r\\n\") are replaced with \" \" in the result, and the trailing\n// end-of-line is removed.\nfunc shell(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple,\n\tkwargs []starlark.Tuple) (starlark.Value, error) {\n\tvar command string\n\tif err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &command); err != nil {\n\t\treturn starlark.None, err\n\t}\n\tshellPath := thread.Local(shellKey).(string)\n\tif shellPath == \"\" {\n\t\treturn starlark.None,\n\t\t\tfmt.Errorf(\"cannot run shell, /bin/sh is missing (running on Windows?)\")\n\t}\n\tcmd := exec.Command(shellPath, \"-c\", command)\n\t// We ignore command's status\n\tbytes, _ := cmd.Output()\n\toutput := string(bytes)\n\tif strings.HasSuffix(output, \"\\n\") {\n\t\toutput = strings.TrimSuffix(output, \"\\n\")\n\t} else {\n\t\toutput = strings.TrimSuffix(output, \"\\r\\n\")\n\t}\n\n\treturn starlark.String(\n\t\tstrings.ReplaceAll(\n\t\t\tstrings.ReplaceAll(output, \"\\r\\n\", \" \"),\n\t\t\t\"\\n\", \" \")), nil\n}\n\nfunc makeStringList(items []string) *starlark.List {\n\telems := make([]starlark.Value, len(items))\n\tfor i, item := range items {\n\t\telems[i] = starlark.String(item)\n\t}\n\treturn starlark.NewList(elems)\n}\n\nfunc log(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {\n\tsep := \" \"\n\tif err := starlark.UnpackArgs(\"print\", nil, kwargs, \"sep?\", &sep); err != nil {\n\t\treturn nil, err\n\t}\n\tfor i, v := range args {\n\t\tif i > 0 {\n\t\t\tfmt.Fprint(os.Stderr, sep)\n\t\t}\n\t\tif s, ok := starlark.AsString(v); ok {\n\t\t\tfmt.Fprint(os.Stderr, s)\n\t\t} else if b, ok := v.(starlark.Bytes); ok {\n\t\t\tfmt.Fprint(os.Stderr, string(b))\n\t\t} else {\n\t\t\tfmt.Fprintf(os.Stderr, \"%s\", v)\n\t\t}\n\t}\n\n\tfmt.Fprintln(os.Stderr)\n\treturn starlark.None, nil\n}\n\n// Parses, resolves, and executes a Starlark file.\n// filename and src parameters are as for starlark.ExecFile:\n// * filename is the name of the file to execute,\n//   and the name that appears in error messages;\n// * src is an optional source of bytes to use instead of filename\n//   (it can be a string, or a byte array, or an io.Reader instance)\n// Returns the top-level starlark variables, the list of starlark files loaded, and an error\nfunc Run(filename string, src interface{}, mode ExecutionMode, allowExternalEntrypoint bool) (starlark.StringDict, []string, error) {\n\t// NOTE(asmundak): OS-specific. Behave similar to Linux `system` call,\n\t// which always uses /bin/sh to run the command\n\tshellPath := \"/bin/sh\"\n\tif _, err := os.Stat(shellPath); err != nil {\n\t\tshellPath = \"\"\n\t}\n\n\tmainThread := &starlark.Thread{\n\t\tName:  \"main\",\n\t\tPrint: func(_ *starlark.Thread, msg string) {\n\t\t\tif mode == ExecutionModeRbc {\n\t\t\t\t// In rbc mode, rblf_log is used to print to stderr\n\t\t\t\tfmt.Println(msg)\n\t\t\t} else if mode == ExecutionModeScl {\n\t\t\t\tfmt.Fprintln(os.Stderr, msg)\n\t\t\t}\n\t\t},\n\t\tLoad:  loader,\n\t}\n\tfilename, err := filepath.Abs(filename)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif wd, err := os.Getwd(); err == nil {\n\t\tfilename, err = filepath.Rel(wd, filename)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tif !allowExternalEntrypoint && strings.HasPrefix(filename, \"../\") {\n\t\t\treturn nil, nil, fmt.Errorf(\"path could not be made relative to workspace root: %s\", filename)\n\t\t}\n\t} else {\n\t\treturn nil, nil, err\n\t}\n\n\tif sym, err := isSymlink(filename); sym && err == nil {\n\t\treturn nil, nil, fmt.Errorf(\"symlinks to starlark files are not allowed. Instead, load the target file and re-export its symbols: %s\", filename)\n\t} else if err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif mode == ExecutionModeScl && !strings.HasSuffix(filename, \".scl\") {\n\t\treturn nil, nil, fmt.Errorf(\"filename must end in .scl: %s\", filename)\n\t}\n\n\t// Add top-level file to cache for cycle detection purposes\n\tmoduleCache[filename] = nil\n\n\tvar results starlark.StringDict\n\tmainThread.SetLocal(allowExternalEntrypointKey, allowExternalEntrypoint)\n\tmainThread.SetLocal(callingFileKey, filename)\n\tmainThread.SetLocal(executionModeKey, mode)\n\tmainThread.SetLocal(shellKey, shellPath)\n\tif mode == ExecutionModeRbc {\n\t\tresults, err = starlark.ExecFile(mainThread, filename, src, rbcBuiltins)\n\t} else if mode == ExecutionModeScl {\n\t\tresults, err = starlark.ExecFile(mainThread, filename, src, sclBuiltins)\n\t} else {\n\t\treturn results, nil, fmt.Errorf(\"unknown executionMode %d\", mode)\n\t}\n\tloadedStarlarkFiles := make([]string, 0, len(moduleCache))\n\tfor file := range moduleCache {\n\t\tloadedStarlarkFiles = append(loadedStarlarkFiles, file)\n\t}\n\tsort.Strings(loadedStarlarkFiles)\n\n\treturn results, loadedStarlarkFiles, err\n}\n"
  },
  {
    "path": "tools/rbcrun/host_test.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage rbcrun\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.starlark.net/resolve\"\n\t\"go.starlark.net/starlark\"\n\t\"go.starlark.net/starlarktest\"\n)\n\n// In order to use \"assert.star\" from go/starlark.net/starlarktest in the tests,\n// provide:\n//  * load function that handles \"assert.star\"\n//  * starlarktest.DataFile function that finds its location\n\nfunc init() {\n\tstarlarktestSetup()\n}\n\nfunc starlarktestSetup() {\n\tresolve.AllowLambda = true\n\tstarlarktest.DataFile = func(pkgdir, filename string) string {\n\t\t// The caller expects this function to return the path to the\n\t\t// data file. The implementation assumes that the source file\n\t\t// containing the caller and the data file are in the same\n\t\t// directory. It's ugly. Not sure what's the better way.\n\t\t// TODO(asmundak): handle Bazel case\n\t\t_, starlarktestSrcFile, _, _ := runtime.Caller(1)\n\t\tif filepath.Base(starlarktestSrcFile) != \"starlarktest.go\" {\n\t\t\tpanic(fmt.Errorf(\"this function should be called from starlarktest.go, got %s\",\n\t\t\t\tstarlarktestSrcFile))\n\t\t}\n\t\treturn filepath.Join(filepath.Dir(starlarktestSrcFile), filename)\n\t}\n}\n\n// Common setup for the tests: create thread, change to the test directory\nfunc testSetup(t *testing.T) *starlark.Thread {\n\tthread := &starlark.Thread{\n\t\tLoad: func(thread *starlark.Thread, module string) (starlark.StringDict, error) {\n\t\t\tif module == \"assert.star\" {\n\t\t\t\treturn starlarktest.LoadAssertModule()\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"load not implemented\")\n\t\t}}\n\tstarlarktest.SetReporter(thread, t)\n\tif err := os.Chdir(dataDir()); err != nil {\n\t\tt.Fatal(err)\n\t}\n\treturn thread\n}\n\nfunc dataDir() string {\n\t_, thisSrcFile, _, _ := runtime.Caller(0)\n\treturn filepath.Join(filepath.Dir(thisSrcFile), \"testdata\")\n}\n\nfunc exerciseStarlarkTestFile(t *testing.T, starFile string) {\n\t// In order to use \"assert.star\" from go/starlark.net/starlarktest in the tests, provide:\n\t//  * load function that handles \"assert.star\"\n\t//  * starlarktest.DataFile function that finds its location\n\tif err := os.Chdir(dataDir()); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tthread := &starlark.Thread{\n\t\tLoad: func(thread *starlark.Thread, module string) (starlark.StringDict, error) {\n\t\t\tif module == \"assert.star\" {\n\t\t\t\treturn starlarktest.LoadAssertModule()\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"load not implemented\")\n\t\t}}\n\tstarlarktest.SetReporter(thread, t)\n\t_, thisSrcFile, _, _ := runtime.Caller(0)\n\tfilename := filepath.Join(filepath.Dir(thisSrcFile), starFile)\n\tthread.SetLocal(executionModeKey, ExecutionModeRbc)\n\tthread.SetLocal(shellKey, \"/bin/sh\")\n\tif _, err := starlark.ExecFile(thread, filename, nil, rbcBuiltins); err != nil {\n\t\tif err, ok := err.(*starlark.EvalError); ok {\n\t\t\tt.Fatal(err.Backtrace())\n\t\t}\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestFileOps(t *testing.T) {\n\t// TODO(asmundak): convert this to use exerciseStarlarkTestFile\n\tthread := testSetup(t)\n\tif _, err := starlark.ExecFile(thread, \"file_ops.star\", nil, rbcBuiltins); err != nil {\n\t\tif err, ok := err.(*starlark.EvalError); ok {\n\t\t\tt.Fatal(err.Backtrace())\n\t\t}\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestLoad(t *testing.T) {\n\t// TODO(asmundak): convert this to use exerciseStarlarkTestFile\n\tthread := testSetup(t)\n\tthread.Load = func(thread *starlark.Thread, module string) (starlark.StringDict, error) {\n\t\tif module == \"assert.star\" {\n\t\t\treturn starlarktest.LoadAssertModule()\n\t\t} else {\n\t\t\treturn loader(thread, module)\n\t\t}\n\t}\n\tdir := dataDir()\n\tif err := os.Chdir(filepath.Dir(dir)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tthread.SetLocal(allowExternalEntrypointKey, false)\n\tthread.SetLocal(callingFileKey, \"testdata/load.star\")\n\tthread.SetLocal(executionModeKey, ExecutionModeRbc)\n\tif _, err := starlark.ExecFile(thread, \"testdata/load.star\", nil, rbcBuiltins); err != nil {\n\t\tif err, ok := err.(*starlark.EvalError); ok {\n\t\t\tt.Fatal(err.Backtrace())\n\t\t}\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestBzlLoadsScl(t *testing.T) {\n\tmoduleCache = make(map[string]*modentry)\n\tdir := dataDir()\n\tif err := os.Chdir(filepath.Dir(dir)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvars, _, err := Run(\"testdata/bzl_loads_scl.bzl\", nil, ExecutionModeRbc, false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif val, ok := vars[\"foo\"]; !ok {\n\t\tt.Fatalf(\"Failed to load foo variable\")\n\t} else if val.(starlark.String) != \"bar\" {\n\t\tt.Fatalf(\"Expected \\\"bar\\\", got %q\", val)\n\t}\n}\n\nfunc TestNonEntrypointBzlLoadsScl(t *testing.T) {\n\tmoduleCache = make(map[string]*modentry)\n\tdir := dataDir()\n\tif err := os.Chdir(filepath.Dir(dir)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvars, _, err := Run(\"testdata/bzl_loads_scl_2.bzl\", nil, ExecutionModeRbc, false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif val, ok := vars[\"foo\"]; !ok {\n\t\tt.Fatalf(\"Failed to load foo variable\")\n\t} else if val.(starlark.String) != \"bar\" {\n\t\tt.Fatalf(\"Expected \\\"bar\\\", got %q\", val)\n\t}\n}\n\nfunc TestSclLoadsBzl(t *testing.T) {\n\tmoduleCache = make(map[string]*modentry)\n\tdir := dataDir()\n\tif err := os.Chdir(filepath.Dir(dir)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\t_, _, err := Run(\"testdata/scl_incorrectly_loads_bzl.scl\", nil, ExecutionModeScl, false)\n\tif err == nil {\n\t\tt.Fatal(\"Expected failure\")\n\t}\n\tif !strings.Contains(err.Error(), \".scl files can only load other .scl files\") {\n\t\tt.Fatalf(\"Expected error to contain \\\".scl files can only load other .scl files\\\": %q\", err.Error())\n\t}\n}\n\nfunc TestCantLoadSymlink(t *testing.T) {\n\tmoduleCache = make(map[string]*modentry)\n\tdir := dataDir()\n\tif err := os.Chdir(filepath.Dir(dir)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\t_, _, err := Run(\"testdata/test_scl_symlink.scl\", nil, ExecutionModeScl, false)\n\tif err == nil {\n\t\tt.Fatal(\"Expected failure\")\n\t}\n\tif !strings.Contains(err.Error(), \"symlinks to starlark files are not allowed\") {\n\t\tt.Fatalf(\"Expected error to contain \\\"symlinks to starlark files are not allowed\\\": %q\", err.Error())\n\t}\n}\n\nfunc TestShell(t *testing.T) {\n\texerciseStarlarkTestFile(t, \"testdata/shell.star\")\n}\n"
  },
  {
    "path": "tools/rbcrun/rbcrun/rbcrun.go",
    "content": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"rbcrun\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"go.starlark.net/starlark\"\n)\n\nvar (\n\tallowExternalEntrypoint = flag.Bool(\"allow_external_entrypoint\", false, \"allow the entrypoint starlark file to be outside of the source tree\")\n\tmodeFlag  = flag.String(\"mode\", \"\", \"the general behavior of rbcrun. Can be \\\"rbc\\\" or \\\"make\\\". Required.\")\n\trootdir  = flag.String(\"d\", \".\", \"the value of // for load paths\")\n\tperfFile = flag.String(\"perf\", \"\", \"save performance data\")\n\tidentifierRe = regexp.MustCompile(\"[a-zA-Z_][a-zA-Z0-9_]*\")\n)\n\nfunc getEntrypointStarlarkFile() string {\n\tfilename := \"\"\n\n\tfor _, arg := range flag.Args() {\n\t\tif filename == \"\" {\n\t\t\tfilename = arg\n\t\t} else {\n\t\t\tquit(\"only one file can be executed\\n\")\n\t\t}\n\t}\n\tif filename == \"\" {\n\t\tflag.Usage()\n\t\tos.Exit(1)\n\t}\n\treturn filename\n}\n\nfunc getMode() rbcrun.ExecutionMode {\n\tswitch *modeFlag {\n\tcase \"rbc\":\n\t\treturn rbcrun.ExecutionModeRbc\n\tcase \"make\":\n\t\treturn rbcrun.ExecutionModeScl\n\tcase \"\":\n\t\tquit(\"-mode flag is required.\")\n\tdefault:\n\t\tquit(\"Unknown -mode value %q, expected 1 of \\\"rbc\\\", \\\"make\\\"\", *modeFlag)\n\t}\n\treturn rbcrun.ExecutionModeScl\n}\n\nvar makeStringReplacer = strings.NewReplacer(\"#\", \"\\\\#\", \"$\", \"$$\")\n\nfunc cleanStringForMake(s string) (string, error) {\n\tif strings.ContainsAny(s, \"\\\\\\n\") {\n\t\t// \\\\ in make is literally \\\\, not a single \\, so we can't allow them.\n\t\t// \\<newline> in make will produce a space, not a newline.\n\t\treturn \"\", fmt.Errorf(\"starlark strings exported to make cannot contain backslashes or newlines\")\n\t}\n\treturn makeStringReplacer.Replace(s), nil\n}\n\nfunc getValueInMakeFormat(value starlark.Value, allowLists bool) (string, error) {\n\tswitch v := value.(type) {\n\tcase starlark.String:\n\t\tif cleanedValue, err := cleanStringForMake(v.GoString()); err == nil {\n\t\t\treturn cleanedValue, nil\n\t\t} else {\n\t\t\treturn \"\", err\n\t\t}\n\tcase starlark.Int:\n\t\treturn v.String(), nil\n\tcase *starlark.List:\n\t\tif !allowLists {\n\t\t\treturn \"\", fmt.Errorf(\"nested lists are not allowed to be exported from starlark to make, flatten the list in starlark first\")\n\t\t}\n\t\tresult := \"\"\n\t\tfor i := 0; i < v.Len(); i++ {\n\t\t\tvalue, err := getValueInMakeFormat(v.Index(i), false)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\tif i > 0 {\n\t\t\t\tresult += \" \"\n\t\t\t}\n\t\t\tresult += value\n\t\t}\n\t\treturn result, nil\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"only starlark strings, ints, and lists of strings/ints can be exported to make. Please convert all other types in starlark first. Found type: %s\", value.Type())\n\t}\n}\n\nfunc printVarsInMakeFormat(globals starlark.StringDict) error {\n\t// We could just directly export top level variables by name instead of going through\n\t// a variables_to_export_to_make dictionary, but that wouldn't allow for exporting a\n\t// runtime-defined number of variables to make. This can be important because dictionaries\n\t// in make are often represented by a unique variable for every key in the dictionary.\n\tvariablesValue, ok := globals[\"variables_to_export_to_make\"]\n\tif !ok {\n\t\treturn fmt.Errorf(\"expected top-level starlark file to have a \\\"variables_to_export_to_make\\\" variable\")\n\t}\n\tvariables, ok := variablesValue.(*starlark.Dict)\n\tif !ok {\n\t\treturn fmt.Errorf(\"expected variables_to_export_to_make to be a dict, got %s\", variablesValue.Type())\n\t}\n\n\tfor _, varTuple := range variables.Items() {\n\t\tvarNameStarlark, ok := varTuple.Index(0).(starlark.String)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"all keys in variables_to_export_to_make must be strings, but got %q\", varTuple.Index(0).Type())\n\t\t}\n\t\tvarName := varNameStarlark.GoString()\n\t\tif !identifierRe.MatchString(varName) {\n\t\t\treturn fmt.Errorf(\"all variables at the top level starlark file must be valid c identifiers, but got %q\", varName)\n\t\t}\n\t\tif varName == \"LOADED_STARLARK_FILES\" {\n\t\t\treturn fmt.Errorf(\"the name LOADED_STARLARK_FILES is reserved for use by the starlark interpreter\")\n\t\t}\n\t\tvalueMake, err := getValueInMakeFormat(varTuple.Index(1), true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// The :=$= is special Kati syntax that means \"set and make readonly\"\n\t\tfmt.Printf(\"%s :=$= %s\\n\", varName, valueMake)\n\t}\n\treturn nil\n}\n\nfunc main() {\n\tflag.Parse()\n\tfilename := getEntrypointStarlarkFile()\n\tmode := getMode()\n\n\tif os.Chdir(*rootdir) != nil {\n\t\tquit(\"could not chdir to %s\\n\", *rootdir)\n\t}\n\tif *perfFile != \"\" {\n\t\tpprof, err := os.Create(*perfFile)\n\t\tif err != nil {\n\t\t\tquit(\"%s: err\", *perfFile)\n\t\t}\n\t\tdefer pprof.Close()\n\t\tif err := starlark.StartProfile(pprof); err != nil {\n\t\t\tquit(\"%s\\n\", err)\n\t\t}\n\t}\n\tvariables, loadedStarlarkFiles, err := rbcrun.Run(filename, nil, mode, *allowExternalEntrypoint)\n\trc := 0\n\tif *perfFile != \"\" {\n\t\tif err2 := starlark.StopProfile(); err2 != nil {\n\t\t\tfmt.Fprintln(os.Stderr, err2)\n\t\t\trc = 1\n\t\t}\n\t}\n\tif err != nil {\n\t\tif evalErr, ok := err.(*starlark.EvalError); ok {\n\t\t\tquit(\"%s\\n\", evalErr.Backtrace())\n\t\t} else {\n\t\t\tquit(\"%s\\n\", err)\n\t\t}\n\t}\n\tif mode == rbcrun.ExecutionModeScl {\n\t\tif err := printVarsInMakeFormat(variables); err != nil {\n\t\t\tquit(\"%s\\n\", err)\n\t\t}\n\t\tfmt.Printf(\"LOADED_STARLARK_FILES := %s\\n\", strings.Join(loadedStarlarkFiles, \" \"))\n\t}\n\tos.Exit(rc)\n}\n\nfunc quit(format string, s ...interface{}) {\n\tfmt.Fprintf(os.Stderr, format, s...)\n\tos.Exit(2)\n}\n"
  },
  {
    "path": "tools/rbcrun/testdata/bzl_loads_scl.bzl",
    "content": "load(\":test_scl.scl\", _foo = \"foo\")\n\nfoo = _foo\n"
  },
  {
    "path": "tools/rbcrun/testdata/bzl_loads_scl_2.bzl",
    "content": "load(\":bzl_loads_scl.bzl\", _foo = \"foo\")\n\nfoo = _foo\n"
  },
  {
    "path": "tools/rbcrun/testdata/file_ops.star",
    "content": "# Tests file ops builtins\nload(\"assert.star\", \"assert\")\n\ndef test():\n    myname = \"file_ops.star\"\n    files = rblf_wildcard(\"*.star\")\n    assert.true(myname in files, \"expected %s in  %s\" % (myname, files))\n    files = rblf_wildcard(\"*.star\")\n    assert.true(myname in files, \"expected %s in %s\" % (myname, files))\n    files = rblf_wildcard(\"*.xxx\")\n    assert.true(len(files) == 0, \"expansion should be empty but contains %s\" % files)\n    mydir = \"testdata\"\n    myrelname = \"%s/%s\" % (mydir, myname)\n    files = rblf_find_files(\"../\", \"*\")\n    assert.true(mydir in files and myrelname in files, \"expected %s and %s in %s\" % (mydir, myrelname, files))\n    files = rblf_find_files(\"../\", \"*\", only_files=1)\n    assert.true(mydir not in files, \"did not expect %s in %s\" % (mydir, files))\n    assert.true(myrelname in files, \"expected %s  in %s\" % (myrelname, files))\n    files = rblf_find_files(\"../\", \"*.star\")\n    assert.true(myrelname in files, \"expected %s in %s\" % (myrelname, files))\ntest()\n"
  },
  {
    "path": "tools/rbcrun/testdata/load.star",
    "content": "# Test load, simple and conditional\nload(\"assert.star\", \"assert\")\nload(\":module1.star\", test1=\"test\")\nload(\"//testdata:module2.star\", test2=\"test\")\nload(\":module3|test\", test3=\"test\")\n\n\ndef test():\n    assert.eq(test1, \"module1\")\n    assert.eq(test2, \"module2\")\n    assert.eq(test3, None)\n\n\ntest()\n"
  },
  {
    "path": "tools/rbcrun/testdata/module1.star",
    "content": "# Module loaded my load.star\nload(\"assert.star\", \"assert\")\n\n# Make sure that builtins are defined for the loaded module, too\nassert.true(rblf_wildcard(\"testdata/module1.star\"))\nassert.true(not rblf_wildcard(\"testdata/no_such file\"))\ntest = \"module1\"\n"
  },
  {
    "path": "tools/rbcrun/testdata/module2.star",
    "content": "# Module loaded my load.star\ntest = \"module2\"\n"
  },
  {
    "path": "tools/rbcrun/testdata/scl_incorrectly_loads_bzl.scl",
    "content": "load(\":bzl_loads_scl.bzl\", _foo = \"foo\")\n\nfoo = _foo\n"
  },
  {
    "path": "tools/rbcrun/testdata/shell.star",
    "content": "# Tests \"queue\" data type\nload(\"assert.star\", \"assert\")\n\nassert.eq(\"load.star shell.star\", rblf_shell(\"ls -1 shell.star load.star 2>&1\"))\nassert.eq(\"shell.star\", rblf_shell(\"echo shell.sta*\"))\n"
  },
  {
    "path": "tools/rbcrun/testdata/test_scl.scl",
    "content": "\nfoo = \"bar\"\n"
  },
  {
    "path": "tools/record-finalized-flags/.gitignore",
    "content": "Cargo.lock\ntarget/\n"
  },
  {
    "path": "tools/record-finalized-flags/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_defaults {\n    name: \"record-finalized-flags-defaults\",\n    edition: \"2021\",\n    clippy_lints: \"android\",\n    lints: \"android\",\n    srcs: [\"src/main.rs\"],\n    rustlibs: [\n        \"libaconfig_protos\",\n        \"libanyhow\",\n        \"libclap\",\n        \"libregex\",\n    ],\n}\n\nrust_binary_host {\n    name: \"record-finalized-flags\",\n    defaults: [\"record-finalized-flags-defaults\"],\n}\n\nrust_test_host {\n    name: \"record-finalized-flags-test\",\n    defaults: [\"record-finalized-flags-defaults\"],\n    test_suites: [\"general-tests\"],\n}\n"
  },
  {
    "path": "tools/record-finalized-flags/Cargo.toml",
    "content": "# Cargo.toml file to allow rapid development of record-finalized-flags using\n# cargo. Soong is the official Android build system, and the only system\n# guaranteed to support record-finalized-flags. If there is ever any issue with\n# the cargo setup, support for cargo will be dropped and this file removed.\n\n[package]\nname = \"record-finalized-flags\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\naconfig_protos = { path = \"../aconfig/aconfig_protos\" }\nanyhow = { path = \"../../../../external/rust/android-crates-io/crates/anyhow\" }\nclap = { path = \"../../../../external/rust/android-crates-io/crates/clap\", features = [\"derive\"] }\nregex = { path = \"../../../../external/rust/android-crates-io/crates/regex\" }\n"
  },
  {
    "path": "tools/record-finalized-flags/OWNERS",
    "content": "include platform/frameworks/base:/SDK_OWNERS\n"
  },
  {
    "path": "tools/record-finalized-flags/src/api_signature_files.rs",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse anyhow::Result;\nuse regex::Regex;\nuse std::{collections::HashSet, io::Read};\n\nuse crate::FlagId;\n\n/// Grep for all flags used with @FlaggedApi annotations in an API signature file (*current.txt\n/// file).\npub(crate) fn extract_flagged_api_flags<R: Read>(mut reader: R) -> Result<HashSet<FlagId>> {\n    let mut haystack = String::new();\n    reader.read_to_string(&mut haystack)?;\n    let regex = Regex::new(r#\"(?ms)@FlaggedApi\\(\"(.*?)\"\\)\"#).unwrap();\n    let iter = regex.captures_iter(&haystack).map(|cap| cap[1].to_owned());\n    Ok(HashSet::from_iter(iter))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test() {\n        let api_signature_file = include_bytes!(\"../tests/api-signature-file.txt\");\n        let flags = extract_flagged_api_flags(&api_signature_file[..]).unwrap();\n        assert_eq!(\n            flags,\n            HashSet::from_iter(vec![\n                \"record_finalized_flags.test.foo\".to_string(),\n                \"this.flag.is.not.used\".to_string(),\n            ])\n        );\n    }\n}\n"
  },
  {
    "path": "tools/record-finalized-flags/src/finalized_flags.rs",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse anyhow::Result;\nuse std::{collections::HashSet, io::Read};\n\nuse crate::FlagId;\n\n/// Read a list of flag names. The input is expected to be plain text, with each line containing\n/// the name of a single flag.\npub(crate) fn read_finalized_flags<R: Read>(mut reader: R) -> Result<HashSet<FlagId>> {\n    let mut contents = String::new();\n    reader.read_to_string(&mut contents)?;\n    let iter = contents.lines().map(|s| s.to_owned());\n    Ok(HashSet::from_iter(iter))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test() {\n        let input = include_bytes!(\"../tests/finalized-flags.txt\");\n        let flags = read_finalized_flags(&input[..]).unwrap();\n        assert_eq!(\n            flags,\n            HashSet::from_iter(vec![\n                \"record_finalized_flags.test.bar\".to_string(),\n                \"record_finalized_flags.test.baz\".to_string(),\n            ])\n        );\n    }\n}\n"
  },
  {
    "path": "tools/record-finalized-flags/src/flag_values.rs",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nuse aconfig_protos::{ParsedFlagExt, ProtoFlagPermission, ProtoFlagState};\nuse anyhow::{anyhow, Result};\nuse std::{collections::HashSet, io::Read};\n\nuse crate::FlagId;\n\n/// Parse a ProtoParsedFlags binary protobuf blob and return the fully qualified names of flags\n/// that are slated for API finalization (i.e. are both ENABLED and READ_ONLY).\npub(crate) fn get_relevant_flags_from_binary_proto<R: Read>(\n    mut reader: R,\n) -> Result<HashSet<FlagId>> {\n    let mut buffer = Vec::new();\n    reader.read_to_end(&mut buffer)?;\n    let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&buffer)\n        .map_err(|_| anyhow!(\"failed to parse binary proto\"))?;\n    let iter = parsed_flags\n        .parsed_flag\n        .into_iter()\n        .filter(|flag| {\n            flag.state() == ProtoFlagState::ENABLED\n                && flag.permission() == ProtoFlagPermission::READ_ONLY\n        })\n        .map(|flag| flag.fully_qualified_name());\n    Ok(HashSet::from_iter(iter))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_disabled_or_read_write_flags_are_ignored() {\n        let bytes = include_bytes!(\"../tests/flags.protobuf\");\n        let flags = get_relevant_flags_from_binary_proto(&bytes[..]).unwrap();\n        assert_eq!(flags, HashSet::from_iter(vec![\"record_finalized_flags.test.foo\".to_string()]));\n    }\n}\n"
  },
  {
    "path": "tools/record-finalized-flags/src/main.rs",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//! `record-finalized-flags` is a tool to create a snapshot (intended to be stored in\n//! prebuilts/sdk) of the flags used with @FlaggedApi APIs\nuse anyhow::Result;\nuse clap::Parser;\nuse std::{collections::HashSet, fs::File, path::PathBuf};\n\nmod api_signature_files;\nmod finalized_flags;\nmod flag_values;\n\npub(crate) type FlagId = String;\n\nconst ABOUT: &str = \"Create a new prebuilts/sdk/<version>/finalized-flags.txt file\n\nThe prebuilts/sdk/<version>/finalized-flags.txt files list all aconfig flags that have been used\nwith @FlaggedApi annotations on APIs that have been finalized. These files are used to prevent\nflags from being re-used for new, unfinalized, APIs, and by the aconfig code generation.\n\nThis tool works as follows:\n\n  - Read API signature files from source tree (*current.txt files) [--api-signature-file]\n  - Read the current aconfig flag values from source tree [--parsed-flags-file]\n  - Read the previous finalized-flags.txt files from prebuilts/sdk [--finalized-flags-file]\n  - Extract the flags slated for API finalization by scanning through the API signature files for\n    flags that are ENABLED and READ_ONLY\n  - Merge the found flags with the recorded flags from previous API finalizations\n  - Print the set of flags to stdout\n\";\n\n#[derive(Parser, Debug)]\n#[clap(about=ABOUT)]\nstruct Cli {\n    #[arg(long)]\n    parsed_flags_file: PathBuf,\n\n    #[arg(long)]\n    api_signature_file: Vec<PathBuf>,\n\n    #[arg(long)]\n    finalized_flags_file: PathBuf,\n}\n\n/// Filter out the ENABLED and READ_ONLY flags used with @FlaggedApi annotations in the source\n/// tree, and add those flags to the set of previously finalized flags.\nfn calculate_new_finalized_flags(\n    flags_used_with_flaggedapi_annotation: &HashSet<FlagId>,\n    all_flags_to_be_finalized: &HashSet<FlagId>,\n    already_finalized_flags: &HashSet<FlagId>,\n) -> HashSet<FlagId> {\n    let new_flags: HashSet<_> = flags_used_with_flaggedapi_annotation\n        .intersection(all_flags_to_be_finalized)\n        .map(|s| s.to_owned())\n        .collect();\n    already_finalized_flags.union(&new_flags).map(|s| s.to_owned()).collect()\n}\n\nfn main() -> Result<()> {\n    let args = Cli::parse();\n\n    let mut flags_used_with_flaggedapi_annotation = HashSet::new();\n    for path in args.api_signature_file {\n        let file = File::open(path)?;\n        for flag in api_signature_files::extract_flagged_api_flags(file)?.drain() {\n            flags_used_with_flaggedapi_annotation.insert(flag);\n        }\n    }\n\n    let file = File::open(args.parsed_flags_file)?;\n    let all_flags_to_be_finalized = flag_values::get_relevant_flags_from_binary_proto(file)?;\n\n    let file = File::open(args.finalized_flags_file)?;\n    let already_finalized_flags = finalized_flags::read_finalized_flags(file)?;\n\n    let mut new_finalized_flags = Vec::from_iter(calculate_new_finalized_flags(\n        &flags_used_with_flaggedapi_annotation,\n        &all_flags_to_be_finalized,\n        &already_finalized_flags,\n    ));\n    new_finalized_flags.sort();\n\n    println!(\"{}\", new_finalized_flags.join(\"\\n\"));\n\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test() {\n        let input = include_bytes!(\"../tests/api-signature-file.txt\");\n        let flags_used_with_flaggedapi_annotation =\n            api_signature_files::extract_flagged_api_flags(&input[..]).unwrap();\n\n        let input = include_bytes!(\"../tests/flags.protobuf\");\n        let all_flags_to_be_finalized =\n            flag_values::get_relevant_flags_from_binary_proto(&input[..]).unwrap();\n\n        let input = include_bytes!(\"../tests/finalized-flags.txt\");\n        let already_finalized_flags = finalized_flags::read_finalized_flags(&input[..]).unwrap();\n\n        let new_finalized_flags = calculate_new_finalized_flags(\n            &flags_used_with_flaggedapi_annotation,\n            &all_flags_to_be_finalized,\n            &already_finalized_flags,\n        );\n\n        assert_eq!(\n            new_finalized_flags,\n            HashSet::from_iter(vec![\n                \"record_finalized_flags.test.foo\".to_string(),\n                \"record_finalized_flags.test.bar\".to_string(),\n                \"record_finalized_flags.test.baz\".to_string(),\n            ])\n        );\n    }\n}\n"
  },
  {
    "path": "tools/record-finalized-flags/tests/api-signature-file.txt",
    "content": "// Signature format: 2.0\npackage android {\n\n  public final class C {\n    ctor public C();\n  }\n\n  public static final class C.inner {\n    ctor public C.inner();\n    field @FlaggedApi(\"record_finalized_flags.test.foo\") public static final String FOO = \"foo\";\n    field @FlaggedApi(\"this.flag.is.not.used\") public static final String BAR = \"bar\";\n  }\n\n}\n\n"
  },
  {
    "path": "tools/record-finalized-flags/tests/finalized-flags.txt",
    "content": "record_finalized_flags.test.bar\nrecord_finalized_flags.test.baz\n"
  },
  {
    "path": "tools/record-finalized-flags/tests/flags.declarations",
    "content": "package: \"record_finalized_flags.test\"\ncontainer: \"system\"\n\nflag {\n    name: \"foo\"\n    namespace: \"test\"\n    description: \"FIXME\"\n    bug: \"\"\n}\n\nflag {\n    name: \"not_enabled\"\n    namespace: \"test\"\n    description: \"FIXME\"\n    bug: \"\"\n}\n"
  },
  {
    "path": "tools/record-finalized-flags/tests/flags.values",
    "content": "flag_value {\n    package: \"record_finalized_flags.test\"\n    name: \"foo\"\n    state: ENABLED\n    permission: READ_ONLY\n}\n\nflag_value {\n    package: \"record_finalized_flags.test\"\n    name: \"not_enabled\"\n    state: DISABLED\n    permission: READ_ONLY\n}\n"
  },
  {
    "path": "tools/record-finalized-flags/tests/generate-flags-protobuf.sh",
    "content": "#!/bin/bash\naconfig create-cache \\\n    --package record_finalized_flags.test \\\n    --container system \\\n    --declarations flags.declarations \\\n    --values flags.values \\\n    --cache flags.protobuf\n"
  },
  {
    "path": "tools/releasetools/Android.bp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//\n// Module-specific defaults.\n//\n// For module X, if we need to build it both as a library and an executable:\n//  - A default rule `releasetools_X_defaults` is created, which lists `srcs`, `libs` and\n//    `required` properties.\n//  - `python_library_host` and `python_binary_host` are created by listing\n//    `releasetools_X_defaults` in their defaults.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\npython_defaults {\n    name: \"releasetools_add_img_to_target_files_defaults\",\n    srcs: [\n        \"add_img_to_target_files.py\",\n    ],\n    libs: [\n        \"ota_metadata_proto\",\n        \"releasetools_apex_utils\",\n        \"releasetools_build_image\",\n        \"releasetools_build_super_image\",\n        \"releasetools_common\",\n        \"libavbtool\",\n    ],\n    required: [\n        \"care_map_generator\",\n    ],\n}\n\npython_defaults {\n    name: \"releasetools_build_image_defaults\",\n    srcs: [\n        \"build_image.py\",\n    ],\n    libs: [\n        \"releasetools_common\",\n        \"releasetools_fsverity_metadata_generator\",\n        \"releasetools_verity_utils\",\n    ],\n    required: [\n        \"blk_alloc_to_base_fs\",\n        \"e2fsck\",\n        \"fsck.erofs\",\n        \"img2simg\",\n        \"mkfs.erofs\",\n        \"mkuserimg_mke2fs\",\n        \"simg2img\",\n        \"tune2fs\",\n        \"mkf2fsuserimg\",\n        \"fsck.f2fs\",\n    ],\n}\n\npython_defaults {\n    name: \"releasetools_build_super_image_defaults\",\n    srcs: [\n        \"build_super_image.py\",\n    ],\n    libs: [\n        \"releasetools_common\",\n    ],\n}\n\npython_defaults {\n    name: \"releasetools_img_from_target_files_defaults\",\n    srcs: [\n        \"img_from_target_files.py\",\n    ],\n    libs: [\n        \"releasetools_build_super_image\",\n        \"releasetools_common\",\n    ],\n}\n\npython_defaults {\n    name: \"releasetools_check_target_files_vintf_defaults\",\n    srcs: [\n        \"check_target_files_vintf.py\",\n    ],\n    libs: [\n        \"apex_manifest\",\n        \"releasetools_apex_utils\",\n        \"releasetools_common\",\n    ],\n    required: [\n        \"apexd_host\",\n        \"checkvintf\",\n    ],\n}\n\npython_library_host {\n    name: \"ota_metadata_proto\",\n    srcs: [\n        \"ota_metadata.proto\",\n    ],\n    proto: {\n        canonical_path_from_root: false,\n    },\n}\n\ncc_library_static {\n    name: \"ota_metadata_proto_cc\",\n    srcs: [\n        \"ota_metadata.proto\",\n    ],\n    host_supported: true,\n    recovery_available: true,\n    proto: {\n        canonical_path_from_root: false,\n        type: \"lite\",\n        export_proto_headers: true,\n    },\n}\n\njava_library_static {\n    name: \"ota_metadata_proto_java\",\n    host_supported: true,\n    proto: {\n        type: \"nano\",\n    },\n    srcs: [\"ota_metadata.proto\"],\n    sdk_version: \"9\",\n    target: {\n        android: {\n            jarjar_rules: \"jarjar-rules.txt\",\n        },\n        host: {\n            static_libs: [\"libprotobuf-java-nano\"],\n        },\n    },\n    visibility: [\"//frameworks/base:__subpackages__\"],\n}\n\npython_defaults {\n    name: \"releasetools_ota_from_target_files_defaults\",\n    srcs: [\n        \"edify_generator.py\",\n        \"non_ab_ota.py\",\n        \"ota_from_target_files.py\",\n        \"target_files_diff.py\",\n    ],\n    libs: [\n        \"ota_metadata_proto\",\n        \"releasetools_check_target_files_vintf\",\n        \"releasetools_common\",\n        \"releasetools_verity_utils\",\n        \"apex_manifest\",\n        \"care_map_proto_py\",\n        \"ota_utils_lib\",\n    ],\n    required: [\n        \"apexd_host\",\n        \"brillo_update_payload\",\n        \"checkvintf\",\n        \"lz4\",\n        \"toybox\",\n        \"unpack_bootimg\",\n        \"deapexer\",\n    ],\n    target: {\n        darwin: {\n            // required module \"brillo_update_payload\" is disabled on darwin\n            enabled: false,\n        },\n    },\n}\n\n//\n// Host libraries.\n//\n\npython_library_host {\n    name: \"releasetools_add_img_to_target_files\",\n    defaults: [\n        \"releasetools_add_img_to_target_files_defaults\",\n    ],\n}\n\npython_library_host {\n    name: \"releasetools_apex_utils\",\n    srcs: [\n        \"apex_utils.py\",\n    ],\n    libs: [\n        \"apex_manifest\",\n        \"ota_metadata_proto\",\n        \"releasetools_common\",\n    ],\n}\n\npython_library_host {\n    name: \"releasetools_build_image\",\n    defaults: [\n        \"releasetools_build_image_defaults\",\n    ],\n}\n\npython_library_host {\n    name: \"releasetools_build_super_image\",\n    defaults: [\n        \"releasetools_build_super_image_defaults\",\n    ],\n}\n\npython_library_host {\n    name: \"releasetools_check_target_files_vintf\",\n    defaults: [\n        \"releasetools_check_target_files_vintf_defaults\",\n    ],\n}\n\npython_library_host {\n    name: \"releasetools_common\",\n    srcs: [\n        \"blockimgdiff.py\",\n        \"common.py\",\n        \"images.py\",\n        \"rangelib.py\",\n        \"sparse_img.py\",\n    ],\n    data: [\n        \":zip2zip\",\n    ],\n    // Only the tools that are referenced directly are listed as required modules. For example,\n    // `avbtool` is not here, as the script always uses the one from info_dict['avb_avbtool'].\n    required: [\n        \"aapt2\",\n        \"boot_signer\",\n        \"brotli\",\n        \"bsdiff\",\n        \"lz4\",\n        \"mkbootfs\",\n        \"signapk\",\n        \"toybox\",\n        \"unpack_bootimg\",\n    ],\n}\n\npython_library_host {\n    name: \"releasetools_img_from_target_files\",\n    defaults: [\n        \"releasetools_img_from_target_files_defaults\",\n    ],\n}\n\npython_library_host {\n    name: \"releasetools_ota_from_target_files\",\n    defaults: [\n        \"releasetools_ota_from_target_files_defaults\",\n    ],\n}\n\npython_library_host {\n    name: \"releasetools_fsverity_metadata_generator\",\n    srcs: [\n        \"fsverity_metadata_generator.py\",\n    ],\n    libs: [\n        \"fsverity_digests_proto_python\",\n    ],\n}\n\npython_library_host {\n    name: \"releasetools_verity_utils\",\n    srcs: [\n        \"verity_utils.py\",\n    ],\n    required: [\n        \"append2simg\",\n        \"build_verity_metadata\",\n        \"build_verity_tree\",\n        \"fec\",\n    ],\n}\n\n//\n// Host binaries.\n//\n\npython_defaults {\n    name: \"releasetools_binary_defaults\",\n    // TODO (b/140144201) Build imgdiff from releasetools_common\n    required: [\n        \"aapt2\",\n        \"boot_signer\",\n        \"brotli\",\n        \"bsdiff\",\n        \"deapexer\",\n        \"lz4\",\n        \"mkbootfs\",\n        \"signapk\",\n        \"toybox\",\n        \"unpack_bootimg\",\n    ],\n}\n\npython_binary_host {\n    name: \"add_img_to_target_files\",\n    defaults: [\n        \"releasetools_binary_defaults\",\n        \"releasetools_add_img_to_target_files_defaults\",\n    ],\n}\n\npython_library_host {\n    name: \"ota_utils_lib\",\n    srcs: [\n        \"ota_utils.py\",\n        \"payload_signer.py\",\n        \"ota_signing_utils.py\",\n    ],\n    libs: [\n        \"releasetools_common\",\n    ],\n}\n\npython_binary_host {\n    name: \"merge_ota\",\n    srcs: [\n        \"merge_ota.py\",\n    ],\n    libs: [\n        \"ota_metadata_proto\",\n        \"update_payload\",\n        \"care_map_proto_py\",\n        \"releasetools_common\",\n        \"ota_utils_lib\",\n    ],\n}\n\npython_binary_host {\n    name: \"create_brick_ota\",\n    srcs: [\n        \"create_brick_ota.py\",\n    ],\n    libs: [\n        \"ota_utils_lib\",\n    ],\n    required: [\n        \"signapk\",\n    ],\n}\n\npython_binary_host {\n    name: \"build_image\",\n    defaults: [\n        \"releasetools_binary_defaults\",\n        \"releasetools_build_image_defaults\",\n    ],\n}\n\npython_binary_host {\n    name: \"build_super_image\",\n    defaults: [\n        \"releasetools_binary_defaults\",\n        \"releasetools_build_super_image_defaults\",\n    ],\n}\n\npython_binary_host {\n    name: \"check_partition_sizes\",\n    srcs: [\n        \"check_partition_sizes.py\",\n    ],\n    libs: [\n        \"releasetools_common\",\n    ],\n    defaults: [\n        \"releasetools_binary_defaults\",\n    ],\n}\n\npython_binary_host {\n    name: \"check_ota_package_signature\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"check_ota_package_signature.py\",\n    ],\n    libs: [\n        \"releasetools_common\",\n    ],\n    required: [\n        \"delta_generator\",\n    ],\n    target: {\n        darwin: {\n            // required module \"delta_generator\" is disabled on darwin\n            enabled: false,\n        },\n    },\n}\n\npython_binary_host {\n    name: \"check_target_files_signatures\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"check_target_files_signatures.py\",\n    ],\n    libs: [\n        \"releasetools_common\",\n    ],\n    required: [\n        \"aapt2\",\n    ],\n}\n\npython_binary_host {\n    name: \"check_target_files_vintf\",\n    defaults: [\n        \"releasetools_binary_defaults\",\n        \"releasetools_check_target_files_vintf_defaults\",\n    ],\n}\n\npython_binary_host {\n    name: \"img_from_target_files\",\n    defaults: [\n        \"releasetools_binary_defaults\",\n        \"releasetools_img_from_target_files_defaults\",\n    ],\n}\n\npython_defaults {\n    name: \"releasetools_find_shareduid_violation_defaults\",\n    srcs: [\n        \"find_shareduid_violation.py\",\n    ],\n    libs: [\n        \"releasetools_common\",\n    ],\n}\n\npython_binary_host {\n    name: \"find_shareduid_violation\",\n    defaults: [\n        \"releasetools_binary_defaults\",\n        \"releasetools_find_shareduid_violation_defaults\",\n    ],\n}\n\npython_library_host {\n    name: \"releasetools_find_shareduid_violation\",\n    defaults: [\n        \"releasetools_find_shareduid_violation_defaults\",\n    ],\n}\n\npython_binary_host {\n    name: \"make_recovery_patch\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"make_recovery_patch.py\",\n    ],\n    libs: [\n        \"releasetools_common\",\n    ],\n}\n\npython_binary_host {\n    name: \"ota_from_target_files\",\n    defaults: [\n        \"releasetools_binary_defaults\",\n        \"releasetools_ota_from_target_files_defaults\",\n    ],\n}\n\npython_binary_host {\n    name: \"ota_from_raw_img\",\n    srcs: [\n        \"ota_from_raw_img.py\",\n    ],\n    main: \"ota_from_raw_img.py\",\n    defaults: [\n        \"releasetools_binary_defaults\",\n    ],\n    required: [\n        \"delta_generator\",\n    ],\n    libs: [\n        \"ota_metadata_proto\",\n        \"releasetools_common\",\n        \"ota_utils_lib\",\n    ],\n}\n\npython_binary_host {\n    name: \"ota_package_parser\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"ota_package_parser.py\",\n        \"rangelib.py\",\n    ],\n}\n\npython_binary_host {\n    name: \"sparse_img\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"rangelib.py\",\n        \"sparse_img.py\",\n    ],\n}\n\npython_binary_host {\n    name: \"sign_apex\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"sign_apex.py\",\n    ],\n    libs: [\n        \"releasetools_apex_utils\",\n        \"releasetools_common\",\n    ],\n}\n\npython_binary_host {\n    name: \"sign_target_files_apks\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"sign_target_files_apks.py\",\n        \"ota_from_raw_img.py\",\n    ],\n    libs: [\n        \"releasetools_add_img_to_target_files\",\n        \"releasetools_apex_utils\",\n        \"releasetools_common\",\n        \"ota_metadata_proto\",\n        \"ota_utils_lib\",\n        \"update_payload\",\n    ],\n}\n\npython_binary_host {\n    name: \"validate_target_files\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"validate_target_files.py\",\n    ],\n    libs: [\n        \"releasetools_common\",\n    ],\n}\n\npython_binary_host {\n    name: \"verity_utils\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"verity_utils.py\",\n    ],\n    libs: [\n        \"releasetools_common\",\n    ],\n    required: [\n        \"append2simg\",\n        \"build_verity_metadata\",\n        \"build_verity_tree\",\n        \"fec\",\n    ],\n}\n\npython_binary_host {\n    name: \"fsverity_metadata_generator\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"fsverity_metadata_generator.py\",\n    ],\n    required: [\n        \"fsverity\",\n    ],\n}\n\n//\n// Tests.\n//\n\npython_defaults {\n    name: \"releasetools_test_defaults\",\n    srcs: [\n        \"check_ota_package_signature.py\",\n        \"check_partition_sizes.py\",\n        \"check_target_files_signatures.py\",\n        \"make_recovery_patch.py\",\n        \"ota_package_parser.py\",\n        \"sign_apex.py\",\n        \"sign_target_files_apks.py\",\n        \"validate_target_files.py\",\n        \"merge_ota.py\",\n        \":releasetools_merge_sources\",\n        \":releasetools_merge_tests\",\n\n        \"test_*.py\",\n    ],\n    libs: [\n        \"releasetools_add_img_to_target_files\",\n        \"releasetools_apex_utils\",\n        \"releasetools_build_image\",\n        \"releasetools_build_super_image\",\n        \"releasetools_check_target_files_vintf\",\n        \"releasetools_common\",\n        \"releasetools_find_shareduid_violation\",\n        \"releasetools_img_from_target_files\",\n        \"releasetools_ota_from_target_files\",\n        \"releasetools_verity_utils\",\n        \"update_payload\",\n    ],\n    data: [\n        \"testdata/**/*\",\n    ],\n    device_common_data: [\n        \":com.android.apex.compressed.v1\",\n        \":com.android.apex.vendor.foo.with_vintf\",\n    ],\n    target: {\n        darwin: {\n            // libs dep \"releasetools_ota_from_target_files\" is disabled on darwin\n            enabled: false,\n        },\n    },\n    required: [\n        \"apexd_host\",\n        \"deapexer\",\n    ],\n}\n\npython_test_host {\n    name: \"releasetools_test\",\n    defaults: [\"releasetools_test_defaults\"],\n    main: \"test_utils.py\",\n    // Don't use embedded_launcher, atest will try (but may fail) to load libc++.so from\n    // host, because the test executable won't be able to find the needed libs via its\n    // runpath.\n    test_options: {\n        unit_test: true,\n    },\n}\n\ngenrule {\n    name: \"otatools_package_releasetools\",\n    tools: [\"soong_zip\"],\n    srcs: [\"**/*\"],\n    cmd: \"find build/make/tools/releasetools -name '*.pyc' -prune -o \\\\( -type f -o -type l \\\\) -print | sort > $(genDir)/files.txt && \" +\n        \"$(location soong_zip) -o $(out) -C build/make/tools -l $(genDir)/files.txt\",\n    out: [\"otatools_package_releasetools.zip\"],\n}\n"
  },
  {
    "path": "tools/releasetools/OWNERS",
    "content": "elsk@google.com\nnhdo@google.com\nzhangkelvin@google.com\n"
  },
  {
    "path": "tools/releasetools/add_img_to_target_files.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2014 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nGiven a target-files zipfile that does not contain images (ie, does\nnot have an IMAGES/ top-level subdirectory), produce the images and\nadd them to the zipfile.\n\nUsage:  add_img_to_target_files [flag] target_files\n\n  -a  (--add_missing)\n      Build and add missing images to \"IMAGES/\". If this option is\n      not specified, this script will simply exit when \"IMAGES/\"\n      directory exists in the target file.\n\n  -r  (--rebuild_recovery)\n      Rebuild the recovery patch and write it to the system image. Only\n      meaningful when system image needs to be rebuilt and there're separate\n      boot / recovery images.\n\n  --replace_verity_private_key\n      Replace the private key used for verity signing. (same as the option\n      in sign_target_files_apks)\n\n  --replace_verity_public_key\n       Replace the certificate (public key) used for verity verification. (same\n       as the option in sign_target_files_apks)\n\n  --is_signing\n      Skip building & adding the images for \"userdata\" and \"cache\" if we\n      are signing the target files.\n\n  --avb-resolve-rollback-index-location-conflict\n      If provided, resolve the conflict AVB rollback index location when\n      necessary.\n\"\"\"\n\nfrom __future__ import print_function\n\nimport avbtool\nimport datetime\nimport logging\nimport os\nimport shlex\nimport shutil\nimport stat\nimport sys\nimport uuid\nimport tempfile\nimport zipfile\n\nimport build_image\nimport build_super_image\nimport common\nimport verity_utils\nimport ota_metadata_pb2\nimport rangelib\nimport sparse_img\nfrom concurrent.futures import ThreadPoolExecutor\nfrom apex_utils import GetApexInfoFromTargetFiles\nfrom common import ZipDelete, PARTITIONS_WITH_CARE_MAP, ExternalError, RunAndCheckOutput, IsSparseImage, MakeTempFile, ZipWrite\nfrom build_image import FIXED_FILE_TIMESTAMP\n\nif sys.hexversion < 0x02070000:\n  print(\"Python 2.7 or newer is required.\", file=sys.stderr)\n  sys.exit(1)\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\nOPTIONS.add_missing = False\nOPTIONS.rebuild_recovery = False\nOPTIONS.replace_updated_files_list = []\nOPTIONS.is_signing = False\nOPTIONS.avb_resolve_rollback_index_location_conflict = False\n\n\ndef ParseAvbFooter(img_path) -> avbtool.AvbFooter:\n  with open(img_path, 'rb') as fp:\n    fp.seek(-avbtool.AvbFooter.SIZE, os.SEEK_END)\n    data = fp.read(avbtool.AvbFooter.SIZE)\n    return avbtool.AvbFooter(data)\n\n\ndef GetCareMap(which, imgname):\n  \"\"\"Returns the care_map string for the given partition.\n\n  Args:\n    which: The partition name, must be listed in PARTITIONS_WITH_CARE_MAP.\n    imgname: The filename of the image.\n\n  Returns:\n    (which, care_map_ranges): care_map_ranges is the raw string of the care_map\n    RangeSet; or None.\n  \"\"\"\n  assert which in PARTITIONS_WITH_CARE_MAP\n\n  is_sparse_img = IsSparseImage(imgname)\n  unsparsed_image_size = os.path.getsize(imgname)\n\n  # A verified image contains original image + hash tree data + FEC data\n  # + AVB footer, all concatenated together. The caremap specifies a range\n  # of blocks that update_verifier should read on top of dm-verity device\n  # to verify correctness of OTA updates. When reading off of dm-verity device,\n  # the hashtree and FEC part of image isn't available. So caremap should\n  # only contain the original image blocks.\n  try:\n    avbfooter = None\n    if is_sparse_img:\n      with tempfile.NamedTemporaryFile() as tmpfile:\n        img = sparse_img.SparseImage(imgname)\n        unsparsed_image_size = img.total_blocks * img.blocksize\n        for data in img.ReadBlocks(img.total_blocks - 1, 1):\n          tmpfile.write(data)\n        tmpfile.flush()\n        avbfooter = ParseAvbFooter(tmpfile.name)\n    else:\n      avbfooter = ParseAvbFooter(imgname)\n  except LookupError as e:\n    logger.warning(\n        \"Failed to parse avbfooter for partition %s image %s, %s\", which, imgname, e)\n    return None\n\n  image_size = avbfooter.original_image_size\n  assert image_size < unsparsed_image_size, f\"AVB footer's original image size {image_size} is larger than or equal to image size on disk {unsparsed_image_size}, this can't happen because a verified image = original image + hash tree data + FEC data + avbfooter.\"\n  assert image_size > 0\n\n  image_blocks = int(image_size) // 4096 - 1\n  # It's OK for image_blocks to be 0, because care map ranges are inclusive.\n  # So 0-0 means \"just block 0\", which is valid.\n  assert image_blocks >= 0, \"blocks for {} must be non-negative, image size: {}\".format(\n      which, image_size)\n\n  # For sparse images, we will only check the blocks that are listed in the care\n  # map, i.e. the ones with meaningful data.\n  if is_sparse_img:\n    simg = sparse_img.SparseImage(imgname)\n    care_map_ranges = simg.care_map.intersect(\n        rangelib.RangeSet(\"0-{}\".format(image_blocks)))\n\n  # Otherwise for non-sparse images, we read all the blocks in the filesystem\n  # image.\n  else:\n    care_map_ranges = rangelib.RangeSet(\"0-{}\".format(image_blocks))\n\n  return [which, care_map_ranges.to_string_raw()]\n\n\ndef AddCareMapForAbOta(output_file, ab_partitions, image_paths):\n  \"\"\"Generates and adds care_map.pb for a/b partition that has care_map.\n\n  Args:\n    output_file: The output zip file (needs to be already open),\n        or file path to write care_map.pb.\n    ab_partitions: The list of A/B partitions.\n    image_paths: A map from the partition name to the image path.\n  \"\"\"\n  if not output_file:\n    raise ExternalError('Expected output_file for AddCareMapForAbOta')\n\n  care_map_list = []\n  for partition in ab_partitions:\n    partition = partition.strip()\n    if partition not in PARTITIONS_WITH_CARE_MAP:\n      continue\n\n    verity_block_device = \"{}_verity_block_device\".format(partition)\n    avb_hashtree_enable = \"avb_{}_hashtree_enable\".format(partition)\n    if (verity_block_device in OPTIONS.info_dict or\n            OPTIONS.info_dict.get(avb_hashtree_enable) == \"true\"):\n      if partition not in image_paths:\n        logger.warning('Potential partition with care_map missing from images: %s',\n                       partition)\n        continue\n      image_path = image_paths[partition]\n      if not os.path.exists(image_path):\n        raise ExternalError('Expected image at path {}'.format(image_path))\n\n      care_map = GetCareMap(partition, image_path)\n      if not care_map:\n        continue\n      care_map_list += care_map\n\n      # adds fingerprint field to the care_map\n      # TODO(xunchang) revisit the fingerprint calculation for care_map.\n      partition_props = OPTIONS.info_dict.get(partition + \".build.prop\")\n      prop_name_list = [\"ro.{}.build.fingerprint\".format(partition),\n                        \"ro.{}.build.thumbprint\".format(partition)]\n\n      present_props = [x for x in prop_name_list if\n                       partition_props and partition_props.GetProp(x)]\n      if not present_props:\n        logger.warning(\n            \"fingerprint is not present for partition %s\", partition)\n        property_id, fingerprint = \"unknown\", \"unknown\"\n      else:\n        property_id = present_props[0]\n        fingerprint = partition_props.GetProp(property_id)\n      care_map_list += [property_id, fingerprint]\n\n  if not care_map_list:\n    return\n\n  # Converts the list into proto buf message by calling care_map_generator; and\n  # writes the result to a temp file.\n  temp_care_map_text = MakeTempFile(prefix=\"caremap_text-\",\n                                           suffix=\".txt\")\n  with open(temp_care_map_text, 'w') as text_file:\n    text_file.write('\\n'.join(care_map_list))\n\n  temp_care_map = MakeTempFile(prefix=\"caremap-\", suffix=\".pb\")\n  care_map_gen_cmd = [\"care_map_generator\", temp_care_map_text, temp_care_map]\n  RunAndCheckOutput(care_map_gen_cmd)\n\n  if not isinstance(output_file, zipfile.ZipFile):\n    shutil.copy(temp_care_map, output_file)\n    return\n  # output_file is a zip file\n  care_map_path = \"META/care_map.pb\"\n  if care_map_path in output_file.namelist():\n    # Copy the temp file into the OPTIONS.input_tmp dir and update the\n    # replace_updated_files_list used by add_img_to_target_files\n    if not OPTIONS.replace_updated_files_list:\n      OPTIONS.replace_updated_files_list = []\n    shutil.copy(temp_care_map, os.path.join(OPTIONS.input_tmp, care_map_path))\n    OPTIONS.replace_updated_files_list.append(care_map_path)\n  else:\n    ZipWrite(output_file, temp_care_map, arcname=care_map_path)\n\n\nclass OutputFile(object):\n  \"\"\"A helper class to write a generated file to the given dir or zip.\n\n  When generating images, we want the outputs to go into the given zip file, or\n  the given dir.\n\n  Attributes:\n    name: The name of the output file, regardless of the final destination.\n  \"\"\"\n\n  def __init__(self, output_zip, input_dir, *args):\n    # We write the intermediate output file under the given input_dir, even if\n    # the final destination is a zip archive.\n    self.name = os.path.join(input_dir, *args)\n    self._output_zip = output_zip\n    if self._output_zip:\n      self._zip_name = os.path.join(*args)\n\n  def Write(self, compress_type=None):\n    if self._output_zip:\n      common.ZipWrite(self._output_zip, self.name,\n                      self._zip_name, compress_type=compress_type)\n\n\ndef AddSystem(output_zip, recovery_img=None, boot_img=None):\n  \"\"\"Turn the contents of SYSTEM into a system image and store it in\n  output_zip. Returns the name of the system image file.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"system.img\")\n  if os.path.exists(img.name):\n    logger.info(\"system.img already exists; no need to rebuild...\")\n    return img.name\n\n  def output_sink(fn, data):\n    output_file = os.path.join(OPTIONS.input_tmp, \"SYSTEM\", fn)\n    with open(output_file, \"wb\") as ofile:\n      ofile.write(data)\n\n    if output_zip:\n      arc_name = \"SYSTEM/\" + fn\n      if arc_name in output_zip.namelist():\n        OPTIONS.replace_updated_files_list.append(arc_name)\n      else:\n        common.ZipWrite(output_zip, output_file, arc_name)\n\n  board_uses_vendorimage = OPTIONS.info_dict.get(\n      \"board_uses_vendorimage\") == \"true\"\n\n  if (OPTIONS.rebuild_recovery and not board_uses_vendorimage and\n          recovery_img is not None and boot_img is not None):\n    logger.info(\"Building new recovery patch on system at system/vendor\")\n    common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,\n                             boot_img, info_dict=OPTIONS.info_dict)\n\n  block_list = OutputFile(output_zip, OPTIONS.input_tmp,\n                          \"IMAGES\", \"system.map\")\n  CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, \"system\", img,\n              block_list=block_list)\n  return img.name\n\n\ndef AddSystemOther(output_zip):\n  \"\"\"Turn the contents of SYSTEM_OTHER into a system_other image\n  and store it in output_zip.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"system_other.img\")\n  if os.path.exists(img.name):\n    logger.info(\"system_other.img already exists; no need to rebuild...\")\n    return\n\n  CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, \"system_other\", img)\n\n\ndef AddVendor(output_zip, recovery_img=None, boot_img=None):\n  \"\"\"Turn the contents of VENDOR into a vendor image and store in it\n  output_zip.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"vendor.img\")\n  if os.path.exists(img.name):\n    logger.info(\"vendor.img already exists; no need to rebuild...\")\n    return img.name\n\n  def output_sink(fn, data):\n    output_file = os.path.join(OPTIONS.input_tmp, \"VENDOR\", fn)\n    with open(output_file, \"wb\") as ofile:\n      ofile.write(data)\n\n    if output_zip:\n      arc_name = \"VENDOR/\" + fn\n      if arc_name in output_zip.namelist():\n        OPTIONS.replace_updated_files_list.append(arc_name)\n      else:\n        common.ZipWrite(output_zip, output_file, arc_name)\n\n  board_uses_vendorimage = OPTIONS.info_dict.get(\n      \"board_uses_vendorimage\") == \"true\"\n\n  if (OPTIONS.rebuild_recovery and board_uses_vendorimage and\n          recovery_img is not None and boot_img is not None):\n    logger.info(\"Building new recovery patch on vendor\")\n    common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,\n                             boot_img, info_dict=OPTIONS.info_dict)\n\n  block_list = OutputFile(output_zip, OPTIONS.input_tmp,\n                          \"IMAGES\", \"vendor.map\")\n  CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, \"vendor\", img,\n              block_list=block_list)\n  return img.name\n\n\ndef AddProduct(output_zip):\n  \"\"\"Turn the contents of PRODUCT into a product image and store it in\n  output_zip.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"product.img\")\n  if os.path.exists(img.name):\n    logger.info(\"product.img already exists; no need to rebuild...\")\n    return img.name\n\n  block_list = OutputFile(\n      output_zip, OPTIONS.input_tmp, \"IMAGES\", \"product.map\")\n  CreateImage(\n      OPTIONS.input_tmp, OPTIONS.info_dict, \"product\", img,\n      block_list=block_list)\n  return img.name\n\n\ndef AddSystemExt(output_zip):\n  \"\"\"Turn the contents of SYSTEM_EXT into a system_ext image and store it in\n  output_zip.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\",\n                   \"system_ext.img\")\n  if os.path.exists(img.name):\n    logger.info(\"system_ext.img already exists; no need to rebuild...\")\n    return img.name\n\n  block_list = OutputFile(\n      output_zip, OPTIONS.input_tmp, \"IMAGES\", \"system_ext.map\")\n  CreateImage(\n      OPTIONS.input_tmp, OPTIONS.info_dict, \"system_ext\", img,\n      block_list=block_list)\n  return img.name\n\n\ndef AddOdm(output_zip):\n  \"\"\"Turn the contents of ODM into an odm image and store it in output_zip.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"odm.img\")\n  if os.path.exists(img.name):\n    logger.info(\"odm.img already exists; no need to rebuild...\")\n    return img.name\n\n  block_list = OutputFile(\n      output_zip, OPTIONS.input_tmp, \"IMAGES\", \"odm.map\")\n  CreateImage(\n      OPTIONS.input_tmp, OPTIONS.info_dict, \"odm\", img,\n      block_list=block_list)\n  return img.name\n\n\ndef AddVendorDlkm(output_zip):\n  \"\"\"Turn the contents of VENDOR_DLKM into an vendor_dlkm image and store it in output_zip.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"vendor_dlkm.img\")\n  if os.path.exists(img.name):\n    logger.info(\"vendor_dlkm.img already exists; no need to rebuild...\")\n    return img.name\n\n  block_list = OutputFile(\n      output_zip, OPTIONS.input_tmp, \"IMAGES\", \"vendor_dlkm.map\")\n  CreateImage(\n      OPTIONS.input_tmp, OPTIONS.info_dict, \"vendor_dlkm\", img,\n      block_list=block_list)\n  return img.name\n\n\ndef AddOdmDlkm(output_zip):\n  \"\"\"Turn the contents of OdmDlkm into an odm_dlkm image and store it in output_zip.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"odm_dlkm.img\")\n  if os.path.exists(img.name):\n    logger.info(\"odm_dlkm.img already exists; no need to rebuild...\")\n    return img.name\n\n  block_list = OutputFile(\n      output_zip, OPTIONS.input_tmp, \"IMAGES\", \"odm_dlkm.map\")\n  CreateImage(\n      OPTIONS.input_tmp, OPTIONS.info_dict, \"odm_dlkm\", img,\n      block_list=block_list)\n  return img.name\n\n\ndef AddSystemDlkm(output_zip):\n  \"\"\"Turn the contents of SystemDlkm into an system_dlkm image and store it in output_zip.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"system_dlkm.img\")\n  if os.path.exists(img.name):\n    logger.info(\"system_dlkm.img already exists; no need to rebuild...\")\n    return img.name\n\n  block_list = OutputFile(\n      output_zip, OPTIONS.input_tmp, \"IMAGES\", \"system_dlkm.map\")\n  CreateImage(\n      OPTIONS.input_tmp, OPTIONS.info_dict, \"system_dlkm\", img,\n      block_list=block_list)\n  return img.name\n\n\ndef AddDtbo(output_zip):\n  \"\"\"Adds the DTBO image.\n\n  Uses the image under IMAGES/ if it already exists. Otherwise looks for the\n  image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.\n  \"\"\"\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"dtbo.img\")\n  if os.path.exists(img.name):\n    logger.info(\"dtbo.img already exists; no need to rebuild...\")\n    return img.name\n\n  dtbo_prebuilt_path = os.path.join(\n      OPTIONS.input_tmp, \"PREBUILT_IMAGES\", \"dtbo.img\")\n  assert os.path.exists(dtbo_prebuilt_path)\n  os.makedirs(os.path.dirname(img.name), exist_ok=True)\n  shutil.copy(dtbo_prebuilt_path, img.name)\n\n  # AVB-sign the image as needed.\n  if OPTIONS.info_dict.get(\"avb_enable\") == \"true\":\n    # Signing requires +w\n    os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR)\n\n    avbtool = OPTIONS.info_dict[\"avb_avbtool\"]\n    part_size = OPTIONS.info_dict[\"dtbo_size\"]\n    # The AVB hash footer will be replaced if already present.\n    cmd = [avbtool, \"add_hash_footer\", \"--image\", img.name,\n           \"--partition_size\", str(part_size), \"--partition_name\", \"dtbo\"]\n    common.AppendAVBSigningArgs(cmd, \"dtbo\")\n    args = OPTIONS.info_dict.get(\"avb_dtbo_add_hash_footer_args\")\n    if args and args.strip():\n      cmd.extend(shlex.split(args))\n    common.RunAndCheckOutput(cmd)\n\n  img.Write()\n  return img.name\n\n\ndef AddPvmfw(output_zip):\n  \"\"\"Adds the pvmfw image.\n\n  Uses the image under IMAGES/ if it already exists. Otherwise looks for the\n  image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.\n  \"\"\"\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"pvmfw.img\")\n  if os.path.exists(img.name):\n    logger.info(\"pvmfw.img already exists; no need to rebuild...\")\n    return img.name\n\n  pvmfw_prebuilt_path = os.path.join(\n      OPTIONS.input_tmp, \"PREBUILT_IMAGES\", \"pvmfw.img\")\n  assert os.path.exists(pvmfw_prebuilt_path)\n  shutil.copy(pvmfw_prebuilt_path, img.name)\n\n  # AVB-sign the image as needed.\n  if OPTIONS.info_dict.get(\"avb_enable\") == \"true\":\n    # Signing requires +w\n    os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR)\n\n    avbtool = OPTIONS.info_dict[\"avb_avbtool\"]\n    part_size = OPTIONS.info_dict[\"pvmfw_size\"]\n    # The AVB hash footer will be replaced if already present.\n    cmd = [avbtool, \"add_hash_footer\", \"--image\", img.name,\n           \"--partition_size\", str(part_size), \"--partition_name\", \"pvmfw\"]\n    common.AppendAVBSigningArgs(cmd, \"pvmfw\")\n    args = OPTIONS.info_dict.get(\"avb_pvmfw_add_hash_footer_args\")\n    if args and args.strip():\n      cmd.extend(shlex.split(args))\n    common.RunAndCheckOutput(cmd)\n\n  img.Write()\n  return img.name\n\n\ndef AddCustomImages(output_zip, partition_name, image_list):\n  \"\"\"Adds and signs avb custom images as needed in IMAGES/.\n\n  Args:\n    output_zip: The output zip file (needs to be already open), or None to\n        write images to OPTIONS.input_tmp/.\n    partition_name: The custom image partition name.\n    image_list: The image list of the custom image partition.\n\n  Uses the image under IMAGES/ if it already exists. Otherwise looks for the\n  image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.\n\n  Raises:\n    AssertionError: If image can't be found.\n  \"\"\"\n\n  builder = None\n  key_path = OPTIONS.info_dict.get(\"avb_{}_key_path\".format(partition_name))\n  if key_path is not None:\n    algorithm = OPTIONS.info_dict.get(\"avb_{}_algorithm\".format(partition_name))\n    extra_args = OPTIONS.info_dict.get(\n        \"avb_{}_add_hashtree_footer_args\".format(partition_name))\n    partition_size = OPTIONS.info_dict.get(\n        \"avb_{}_partition_size\".format(partition_name))\n\n    builder = verity_utils.CreateCustomImageBuilder(\n        OPTIONS.info_dict, partition_name, partition_size,\n        key_path, algorithm, extra_args)\n\n  for img_name in image_list:\n    custom_image = OutputFile(\n        output_zip, OPTIONS.input_tmp, \"IMAGES\", img_name)\n    if os.path.exists(custom_image.name):\n      continue\n\n    custom_image_prebuilt_path = os.path.join(\n        OPTIONS.input_tmp, \"PREBUILT_IMAGES\", img_name)\n    assert os.path.exists(custom_image_prebuilt_path), \\\n        \"Failed to find %s at %s\" % (img_name, custom_image_prebuilt_path)\n\n    shutil.copy(custom_image_prebuilt_path, custom_image.name)\n\n    if builder is not None:\n      builder.Build(custom_image.name)\n\n    custom_image.Write()\n\n  default = os.path.join(OPTIONS.input_tmp, \"IMAGES\", partition_name + \".img\")\n  assert os.path.exists(default), \\\n      \"Can't find %s for image %s\" % (default, partition_name)\n  return default\n\n\ndef CreateImage(input_dir, info_dict, what, output_file, block_list=None):\n  logger.info(\"creating %s.img...\", what)\n\n  image_props = build_image.ImagePropFromGlobalDict(info_dict, what)\n  image_props[\"timestamp\"] = FIXED_FILE_TIMESTAMP\n\n  if what == \"system\":\n    fs_config_prefix = \"\"\n  else:\n    fs_config_prefix = what + \"_\"\n\n  fs_config = os.path.join(\n      input_dir, \"META/\" + fs_config_prefix + \"filesystem_config.txt\")\n  if not os.path.exists(fs_config):\n    fs_config = None\n\n  # Override values loaded from info_dict.\n  if fs_config:\n    image_props[\"fs_config\"] = fs_config\n  if block_list:\n    image_props[\"block_list\"] = block_list.name\n\n  build_image.BuildImage(\n      os.path.join(input_dir, what.upper()), image_props, output_file.name)\n\n  output_file.Write()\n  if block_list:\n    block_list.Write()\n\n  # Set the '_image_size' for given image size.\n  is_verity_partition = \"verity_block_device\" in image_props\n  verity_supported = (image_props.get(\"avb_enable\") == \"true\")\n  is_avb_enable = image_props.get(\"avb_hashtree_enable\") == \"true\"\n  if verity_supported and (is_verity_partition or is_avb_enable):\n    image_size = image_props.get(\"image_size\")\n    if image_size:\n      image_size_key = what + \"_image_size\"\n      info_dict[image_size_key] = int(image_size)\n\n  use_dynamic_size = (\n      info_dict.get(\"use_dynamic_partition_size\") == \"true\" and\n      what in shlex.split(info_dict.get(\"dynamic_partition_list\", \"\").strip()))\n  if use_dynamic_size:\n    info_dict.update(build_image.GlobalDictFromImageProp(image_props, what))\n\n\ndef AddUserdata(output_zip):\n  \"\"\"Create a userdata image and store it in output_zip.\n\n  In most case we just create and store an empty userdata.img;\n  But the invoker can also request to create userdata.img with real\n  data from the target files, by setting \"userdata_img_with_data=true\"\n  in OPTIONS.info_dict.\n  \"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"userdata.img\")\n  if os.path.exists(img.name):\n    logger.info(\"userdata.img already exists; no need to rebuild...\")\n    return\n\n  # Skip userdata.img if no size.\n  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, \"data\")\n  if not image_props.get(\"partition_size\"):\n    return\n\n  logger.info(\"creating userdata.img...\")\n\n  image_props[\"timestamp\"] = FIXED_FILE_TIMESTAMP\n\n  if OPTIONS.info_dict.get(\"userdata_img_with_data\") == \"true\":\n    user_dir = os.path.join(OPTIONS.input_tmp, \"DATA\")\n  else:\n    user_dir = common.MakeTempDir()\n\n  build_image.BuildImage(user_dir, image_props, img.name)\n\n  common.CheckSize(img.name, \"userdata.img\", OPTIONS.info_dict)\n  # Always use compression for useradata image.\n  # As it's likely huge and consist of lots of 0s.\n  img.Write(zipfile.ZIP_DEFLATED)\n\n\ndef AddVBMeta(output_zip, partitions, name, needed_partitions):\n  \"\"\"Creates a VBMeta image and stores it in output_zip.\n\n  It generates the requested VBMeta image. The requested image could be for\n  top-level or chained VBMeta image, which is determined based on the name.\n\n  Args:\n    output_zip: The output zip file, which needs to be already open.\n    partitions: A dict that's keyed by partition names with image paths as\n        values. Only valid partition names are accepted, as partitions listed\n        in common.AVB_PARTITIONS and custom partitions listed in\n        OPTIONS.info_dict.get(\"avb_custom_images_partition_list\")\n    name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.\n    needed_partitions: Partitions whose descriptors should be included into the\n        generated VBMeta image.\n\n  Returns:\n    Path to the created image.\n\n  Raises:\n    AssertionError: On invalid input args.\n  \"\"\"\n  assert needed_partitions, \"Needed partitions must be specified\"\n\n  img = OutputFile(\n      output_zip, OPTIONS.input_tmp, \"IMAGES\", \"{}.img\".format(name))\n  if os.path.exists(img.name):\n    logger.info(\"%s.img already exists; not rebuilding...\", name)\n    return img.name\n\n  common.BuildVBMeta(img.name, partitions, name, needed_partitions,\n                     OPTIONS.avb_resolve_rollback_index_location_conflict)\n  img.Write()\n  return img.name\n\n\ndef AddCache(output_zip):\n  \"\"\"Create an empty cache image and store it in output_zip.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"cache.img\")\n  if os.path.exists(img.name):\n    logger.info(\"cache.img already exists; no need to rebuild...\")\n    return\n\n  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, \"cache\")\n  # The build system has to explicitly request for cache.img.\n  if \"fs_type\" not in image_props:\n    return\n\n  logger.info(\"creating cache.img...\")\n\n  image_props[\"timestamp\"] = FIXED_FILE_TIMESTAMP\n\n  user_dir = common.MakeTempDir()\n  build_image.BuildImage(user_dir, image_props, img.name)\n\n  common.CheckSize(img.name, \"cache.img\", OPTIONS.info_dict)\n  img.Write()\n\n\ndef CheckAbOtaImages(output_zip, ab_partitions):\n  \"\"\"Checks that all the listed A/B partitions have their images available.\n\n  The images need to be available under IMAGES/ or RADIO/, with the former takes\n  a priority.\n\n  Args:\n    output_zip: The output zip file (needs to be already open), or None to\n        find images in OPTIONS.input_tmp/.\n    ab_partitions: The list of A/B partitions.\n\n  Raises:\n    AssertionError: If it can't find an image.\n  \"\"\"\n  for partition in ab_partitions:\n    img_name = partition + \".img\"\n\n    # Assert that the image is present under IMAGES/ now.\n    if output_zip:\n      # Zip spec says: All slashes MUST be forward slashes.\n      images_path = \"IMAGES/\" + img_name\n      radio_path = \"RADIO/\" + img_name\n      available = (images_path in output_zip.namelist() or\n                   radio_path in output_zip.namelist())\n    else:\n      images_path = os.path.join(OPTIONS.input_tmp, \"IMAGES\", img_name)\n      radio_path = os.path.join(OPTIONS.input_tmp, \"RADIO\", img_name)\n      available = os.path.exists(images_path) or os.path.exists(radio_path)\n\n    assert available, \"Failed to find \" + img_name\n\n\ndef AddPackRadioImages(output_zip, images):\n  \"\"\"Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/.\n\n  Args:\n    output_zip: The output zip file (needs to be already open), or None to\n        write images to OPTIONS.input_tmp/.\n    images: A list of image names.\n\n  Raises:\n    AssertionError: If a listed image can't be found.\n  \"\"\"\n  for image in images:\n    img_name = image.strip()\n    _, ext = os.path.splitext(img_name)\n    if not ext:\n      img_name += \".img\"\n\n    prebuilt_path = os.path.join(OPTIONS.input_tmp, \"IMAGES\", img_name)\n    if os.path.exists(prebuilt_path):\n      logger.info(\"%s already exists, no need to overwrite...\", img_name)\n      continue\n\n    img_radio_path = os.path.join(OPTIONS.input_tmp, \"RADIO\", img_name)\n    assert os.path.exists(img_radio_path), \\\n        \"Failed to find %s at %s\" % (img_name, img_radio_path)\n\n    if output_zip:\n      common.ZipWrite(output_zip, img_radio_path, \"IMAGES/\" + img_name)\n    else:\n      shutil.copy(img_radio_path, prebuilt_path)\n\n\ndef AddSuperEmpty(output_zip):\n  \"\"\"Create a super_empty.img and store it in output_zip.\"\"\"\n\n  img = OutputFile(output_zip, OPTIONS.input_tmp, \"IMAGES\", \"super_empty.img\")\n  if os.path.exists(img.name):\n    logger.info(\"super_empty.img already exists; no need to rebuild...\")\n    return\n  build_super_image.BuildSuperImage(OPTIONS.info_dict, img.name)\n  img.Write()\n\n\ndef AddSuperSplit(output_zip):\n  \"\"\"Create split super_*.img and store it in output_zip.\"\"\"\n\n  outdir = os.path.join(OPTIONS.input_tmp, \"OTA\")\n  built = build_super_image.BuildSuperImage(OPTIONS.input_tmp, outdir)\n\n  if built:\n    for dev in OPTIONS.info_dict['super_block_devices'].strip().split():\n      img = OutputFile(output_zip, OPTIONS.input_tmp, \"OTA\",\n                       \"super_\" + dev + \".img\")\n      img.Write()\n\n\ndef ReplaceUpdatedFiles(zip_filename, files_list):\n  \"\"\"Updates all the ZIP entries listed in files_list.\n\n  For now the list includes META/care_map.pb, and the related files under\n  SYSTEM/ after rebuilding recovery.\n  \"\"\"\n  common.ZipDelete(zip_filename, files_list)\n  output_zip = zipfile.ZipFile(zip_filename, \"a\",\n                               compression=zipfile.ZIP_DEFLATED,\n                               allowZip64=True)\n  for item in files_list:\n    file_path = os.path.join(OPTIONS.input_tmp, item)\n    assert os.path.exists(file_path)\n    common.ZipWrite(output_zip, file_path, arcname=item)\n  common.ZipClose(output_zip)\n\n\ndef HasPartition(partition_name):\n  \"\"\"Determines if the target files archive should build a given partition.\"\"\"\n\n  return ((os.path.isdir(\n      os.path.join(OPTIONS.input_tmp, partition_name.upper())) and\n      OPTIONS.info_dict.get(\n      \"building_{}_image\".format(partition_name)) == \"true\") or\n      os.path.exists(\n      os.path.join(OPTIONS.input_tmp, \"IMAGES\",\n                   \"{}.img\".format(partition_name))))\n\n\ndef AddApexInfo(output_zip):\n  apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp)\n  apex_metadata_proto = ota_metadata_pb2.ApexMetadata()\n  apex_metadata_proto.apex_info.extend(apex_infos)\n  apex_info_bytes = apex_metadata_proto.SerializeToString()\n\n  output_file = os.path.join(OPTIONS.input_tmp, \"META\", \"apex_info.pb\")\n  with open(output_file, \"wb\") as ofile:\n    ofile.write(apex_info_bytes)\n  if output_zip:\n    arc_name = \"META/apex_info.pb\"\n    if arc_name in output_zip.namelist():\n      OPTIONS.replace_updated_files_list.append(arc_name)\n    else:\n      common.ZipWrite(output_zip, output_file, arc_name)\n\n\ndef AddVbmetaDigest(output_zip):\n  \"\"\"Write the vbmeta digest to the output dir and zipfile.\"\"\"\n\n  # Calculate the vbmeta digest and put the result in to META/\n  boot_images = OPTIONS.info_dict.get(\"boot_images\")\n  # Disable the digest calculation if the target_file is used as a container\n  # for boot images. A boot container might contain boot-5.4.img, boot-5.10.img\n  # etc., instead of just a boot.img and will fail in vbmeta digest calculation.\n  boot_container = boot_images and (\n      len(boot_images.split()) >= 2 or boot_images.split()[0] != 'boot.img')\n  if (OPTIONS.info_dict.get(\"avb_enable\") == \"true\" and not boot_container and\n          OPTIONS.info_dict.get(\"avb_building_vbmeta_image\") == \"true\"):\n    avbtool = OPTIONS.info_dict[\"avb_avbtool\"]\n    digest = verity_utils.CalculateVbmetaDigest(OPTIONS.input_tmp, avbtool)\n    vbmeta_digest_txt = os.path.join(OPTIONS.input_tmp, \"META\",\n                                     \"vbmeta_digest.txt\")\n    with open(vbmeta_digest_txt, 'w') as f:\n      f.write(digest)\n    # writes to the output zipfile\n    if output_zip:\n      arc_name = \"META/vbmeta_digest.txt\"\n      if arc_name in output_zip.namelist():\n        OPTIONS.replace_updated_files_list.append(arc_name)\n      else:\n        common.ZipWriteStr(output_zip, arc_name, digest)\n\n\ndef AddImagesToTargetFiles(filename):\n  \"\"\"Creates and adds images (boot/recovery/system/...) to a target_files.zip.\n\n  It works with either a zip file (zip mode), or a directory that contains the\n  files to be packed into a target_files.zip (dir mode). The latter is used when\n  being called from build/make/core/Makefile.\n\n  The images will be created under IMAGES/ in the input target_files.zip.\n\n  Args:\n    filename: the target_files.zip, or the zip root directory.\n  \"\"\"\n  if os.path.isdir(filename):\n    OPTIONS.input_tmp = os.path.abspath(filename)\n  else:\n    OPTIONS.input_tmp = common.UnzipTemp(filename)\n\n  if not OPTIONS.add_missing:\n    if os.path.isdir(os.path.join(OPTIONS.input_tmp, \"IMAGES\")):\n      logger.warning(\"target_files appears to already contain images.\")\n      sys.exit(1)\n\n  OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, repacking=True)\n\n  has_recovery = OPTIONS.info_dict.get(\"no_recovery\") != \"true\"\n  has_boot = OPTIONS.info_dict.get(\"no_boot\") != \"true\"\n  has_init_boot = OPTIONS.info_dict.get(\"init_boot\") == \"true\"\n  has_vendor_boot = OPTIONS.info_dict.get(\"vendor_boot\") == \"true\"\n  has_vendor_kernel_boot = OPTIONS.info_dict.get(\n      \"vendor_kernel_boot\") == \"true\"\n\n  # {vendor,odm,product,system_ext,vendor_dlkm,odm_dlkm, system_dlkm, system, system_other}.img\n  # can be built from source, or  dropped into target_files.zip as a prebuilt blob.\n  has_vendor = HasPartition(\"vendor\")\n  has_odm = HasPartition(\"odm\")\n  has_vendor_dlkm = HasPartition(\"vendor_dlkm\")\n  has_odm_dlkm = HasPartition(\"odm_dlkm\")\n  has_system_dlkm = HasPartition(\"system_dlkm\")\n  has_product = HasPartition(\"product\")\n  has_system_ext = HasPartition(\"system_ext\")\n  has_system = HasPartition(\"system\")\n  has_system_other = HasPartition(\"system_other\")\n  has_userdata = OPTIONS.info_dict.get(\"building_userdata_image\") == \"true\"\n  has_cache = OPTIONS.info_dict.get(\"building_cache_image\") == \"true\"\n\n  # Set up the output destination. It writes to the given directory for dir\n  # mode; otherwise appends to the given ZIP.\n  if os.path.isdir(filename):\n    output_zip = None\n  else:\n    output_zip = zipfile.ZipFile(filename, \"a\",\n                                 compression=zipfile.ZIP_DEFLATED,\n                                 allowZip64=True)\n\n  # Always make input_tmp/IMAGES available, since we may stage boot / recovery\n  # images there even under zip mode. The directory will be cleaned up as part\n  # of OPTIONS.input_tmp.\n  images_dir = os.path.join(OPTIONS.input_tmp, \"IMAGES\")\n  if not os.path.isdir(images_dir):\n    os.makedirs(images_dir)\n\n  # A map between partition names and their paths, which could be used when\n  # generating AVB vbmeta image.\n  partitions = {}\n\n  def banner(s):\n    logger.info(\"\\n\\n++++ %s  ++++\\n\\n\", s)\n\n  boot_image = None\n  if has_boot:\n    banner(\"boot\")\n    boot_images = OPTIONS.info_dict.get(\"boot_images\")\n    if boot_images is None:\n      boot_images = \"boot.img\"\n    for index, b in enumerate(boot_images.split()):\n      # common.GetBootableImage() returns the image directly if present.\n      boot_image = common.GetBootableImage(\n          \"IMAGES/\" + b, b, OPTIONS.input_tmp, \"BOOT\")\n      # boot.img may be unavailable in some targets (e.g. aosp_arm64).\n      if boot_image:\n        boot_image_path = os.path.join(OPTIONS.input_tmp, \"IMAGES\", b)\n        # Although multiple boot images can be generated, include the image\n        # descriptor of only the first boot image in vbmeta\n        if index == 0:\n          partitions['boot'] = boot_image_path\n        if not os.path.exists(boot_image_path):\n          boot_image.WriteToDir(OPTIONS.input_tmp)\n          if output_zip:\n            boot_image.AddToZip(output_zip)\n\n  if has_init_boot:\n    banner(\"init_boot\")\n    init_boot_image = common.GetBootableImage(\n        \"IMAGES/init_boot.img\", \"init_boot.img\", OPTIONS.input_tmp, \"INIT_BOOT\",\n        dev_nodes=True)\n    if init_boot_image:\n      partitions['init_boot'] = os.path.join(\n          OPTIONS.input_tmp, \"IMAGES\", \"init_boot.img\")\n      if not os.path.exists(partitions['init_boot']):\n        init_boot_image.WriteToDir(OPTIONS.input_tmp)\n        if output_zip:\n          init_boot_image.AddToZip(output_zip)\n\n  if has_vendor_boot:\n    banner(\"vendor_boot\")\n    vendor_boot_image = common.GetVendorBootImage(\n        \"IMAGES/vendor_boot.img\", \"vendor_boot.img\", OPTIONS.input_tmp,\n        \"VENDOR_BOOT\")\n    if vendor_boot_image:\n      partitions['vendor_boot'] = os.path.join(OPTIONS.input_tmp, \"IMAGES\",\n                                               \"vendor_boot.img\")\n      if not os.path.exists(partitions['vendor_boot']):\n        vendor_boot_image.WriteToDir(OPTIONS.input_tmp)\n        if output_zip:\n          vendor_boot_image.AddToZip(output_zip)\n\n  if has_vendor_kernel_boot:\n    banner(\"vendor_kernel_boot\")\n    vendor_kernel_boot_image = common.GetVendorKernelBootImage(\n        \"IMAGES/vendor_kernel_boot.img\", \"vendor_kernel_boot.img\", OPTIONS.input_tmp,\n        \"VENDOR_KERNEL_BOOT\")\n    if vendor_kernel_boot_image:\n      partitions['vendor_kernel_boot'] = os.path.join(OPTIONS.input_tmp, \"IMAGES\",\n                                                      \"vendor_kernel_boot.img\")\n      if not os.path.exists(partitions['vendor_kernel_boot']):\n        vendor_kernel_boot_image.WriteToDir(OPTIONS.input_tmp)\n        if output_zip:\n          vendor_kernel_boot_image.AddToZip(output_zip)\n\n  recovery_image = None\n  if has_recovery:\n    banner(\"recovery\")\n    recovery_image = common.GetBootableImage(\n        \"IMAGES/recovery.img\", \"recovery.img\", OPTIONS.input_tmp, \"RECOVERY\")\n    assert recovery_image, \"Failed to create recovery.img.\"\n    partitions['recovery'] = os.path.join(\n        OPTIONS.input_tmp, \"IMAGES\", \"recovery.img\")\n    if not os.path.exists(partitions['recovery']):\n      recovery_image.WriteToDir(OPTIONS.input_tmp)\n      if output_zip:\n        recovery_image.AddToZip(output_zip)\n\n      banner(\"recovery (two-step image)\")\n      # The special recovery.img for two-step package use.\n      recovery_two_step_image = common.GetBootableImage(\n          \"OTA/recovery-two-step.img\", \"recovery-two-step.img\",\n          OPTIONS.input_tmp, \"RECOVERY\", two_step_image=True)\n      assert recovery_two_step_image, \"Failed to create recovery-two-step.img.\"\n      recovery_two_step_image_path = os.path.join(\n          OPTIONS.input_tmp, \"OTA\", \"recovery-two-step.img\")\n      if not os.path.exists(recovery_two_step_image_path):\n        recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)\n        if output_zip:\n          recovery_two_step_image.AddToZip(output_zip)\n\n  def add_partition(partition, has_partition, add_func, add_args):\n    if has_partition:\n      banner(partition)\n      partitions[partition] = add_func(output_zip, *add_args)\n\n  add_partition_calls = (\n      (\"system\", has_system, AddSystem, [recovery_image, boot_image]),\n      (\"vendor\", has_vendor, AddVendor, [recovery_image, boot_image]),\n      (\"product\", has_product, AddProduct, []),\n      (\"system_ext\", has_system_ext, AddSystemExt, []),\n      (\"odm\", has_odm, AddOdm, []),\n      (\"vendor_dlkm\", has_vendor_dlkm, AddVendorDlkm, []),\n      (\"odm_dlkm\", has_odm_dlkm, AddOdmDlkm, []),\n      (\"system_dlkm\", has_system_dlkm, AddSystemDlkm, []),\n      (\"system_other\", has_system_other, AddSystemOther, []),\n  )\n  # If output_zip exists, each add_partition_calls writes bytes to the same output_zip,\n  # which is not thread-safe. So, run them in serial if output_zip exists.\n  if output_zip:\n    for call in add_partition_calls:\n      add_partition(*call)\n  else:\n    with ThreadPoolExecutor(max_workers=len(add_partition_calls)) as executor:\n      for future in [executor.submit(add_partition, *call) for call in add_partition_calls]:\n        future.result()\n\n  AddApexInfo(output_zip)\n\n  if not OPTIONS.is_signing:\n    banner(\"userdata\")\n    AddUserdata(output_zip)\n    banner(\"cache\")\n    AddCache(output_zip)\n\n  add_partition(\"dtbo\",\n                OPTIONS.info_dict.get(\"has_dtbo\") == \"true\", AddDtbo, [])\n  add_partition(\"pvmfw\",\n                OPTIONS.info_dict.get(\"has_pvmfw\") == \"true\", AddPvmfw, [])\n\n  # Custom images.\n  custom_partitions = OPTIONS.info_dict.get(\n      \"custom_images_partition_list\", \"\").strip().split()\n  for partition_name in custom_partitions:\n    partition_name = partition_name.strip()\n    banner(\"custom images for \" + partition_name)\n    image_list = OPTIONS.info_dict.get(\n          \"{}_image_list\".format(partition_name)).split()\n    partitions[partition_name] = AddCustomImages(output_zip, partition_name, image_list)\n\n  avb_custom_partitions = OPTIONS.info_dict.get(\n      \"avb_custom_images_partition_list\", \"\").strip().split()\n  for partition_name in avb_custom_partitions:\n    partition_name = partition_name.strip()\n    banner(\"avb custom images for \" + partition_name)\n    image_list = OPTIONS.info_dict.get(\n          \"avb_{}_image_list\".format(partition_name)).split()\n    partitions[partition_name] = AddCustomImages(output_zip, partition_name, image_list)\n\n  if OPTIONS.info_dict.get(\"avb_enable\") == \"true\":\n    # vbmeta_partitions includes the partitions that should be included into\n    # top-level vbmeta.img, which are the ones that are not included in any\n    # chained VBMeta image plus the chained VBMeta images themselves.\n    # Currently avb_custom_partitions are all chained to VBMeta image.\n    vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(avb_custom_partitions)\n\n    vbmeta_system = OPTIONS.info_dict.get(\"avb_vbmeta_system\", \"\").strip()\n    if vbmeta_system and set(vbmeta_system.split()).intersection(partitions):\n      banner(\"vbmeta_system\")\n      partitions[\"vbmeta_system\"] = AddVBMeta(\n          output_zip, partitions, \"vbmeta_system\", vbmeta_system.split())\n      vbmeta_partitions = [\n          item for item in vbmeta_partitions\n          if item not in vbmeta_system.split()]\n      vbmeta_partitions.append(\"vbmeta_system\")\n\n    vbmeta_vendor = OPTIONS.info_dict.get(\"avb_vbmeta_vendor\", \"\").strip()\n    if vbmeta_vendor and set(vbmeta_vendor.split()).intersection(partitions):\n      banner(\"vbmeta_vendor\")\n      partitions[\"vbmeta_vendor\"] = AddVBMeta(\n          output_zip, partitions, \"vbmeta_vendor\", vbmeta_vendor.split())\n      vbmeta_partitions = [\n          item for item in vbmeta_partitions\n          if item not in vbmeta_vendor.split()]\n      vbmeta_partitions.append(\"vbmeta_vendor\")\n    custom_avb_partitions = OPTIONS.info_dict.get(\n        \"avb_custom_vbmeta_images_partition_list\", \"\").strip().split()\n    if custom_avb_partitions:\n      for avb_part in custom_avb_partitions:\n        partition_name = \"vbmeta_\" + avb_part\n        included_partitions = OPTIONS.info_dict.get(\n            \"avb_vbmeta_{}\".format(avb_part), \"\").strip().split()\n        assert included_partitions, \"Custom vbmeta partition {0} missing avb_vbmeta_{0} prop\".format(\n            avb_part)\n        banner(partition_name)\n        logger.info(\"VBMeta partition {} needs {}\".format(\n            partition_name, included_partitions))\n        partitions[partition_name] = AddVBMeta(\n            output_zip, partitions, partition_name, included_partitions)\n        vbmeta_partitions = [\n            item for item in vbmeta_partitions\n            if item not in included_partitions]\n        vbmeta_partitions.append(partition_name)\n\n    if OPTIONS.info_dict.get(\"avb_building_vbmeta_image\") == \"true\" and set(vbmeta_partitions).intersection(partitions):\n      banner(\"vbmeta\")\n      AddVBMeta(output_zip, partitions, \"vbmeta\", vbmeta_partitions)\n\n  if OPTIONS.info_dict.get(\"use_dynamic_partitions\") == \"true\":\n    if OPTIONS.info_dict.get(\"build_super_empty_partition\") == \"true\":\n      banner(\"super_empty\")\n      AddSuperEmpty(output_zip)\n\n  if OPTIONS.info_dict.get(\"build_super_partition\") == \"true\":\n    if OPTIONS.info_dict.get(\n            \"build_retrofit_dynamic_partitions_ota_package\") == \"true\":\n      banner(\"super split images\")\n      AddSuperSplit(output_zip)\n\n  banner(\"radio\")\n  ab_partitions_txt = os.path.join(OPTIONS.input_tmp, \"META\",\n                                   \"ab_partitions.txt\")\n  if os.path.exists(ab_partitions_txt):\n    with open(ab_partitions_txt) as f:\n      ab_partitions = f.read().splitlines()\n\n    # For devices using A/B update, make sure we have all the needed images\n    # ready under IMAGES/ or RADIO/.\n    CheckAbOtaImages(output_zip, ab_partitions)\n\n    # Generate care_map.pb for ab_partitions, then write this file to\n    # target_files package.\n    output_care_map = os.path.join(OPTIONS.input_tmp, \"META\", \"care_map.pb\")\n    AddCareMapForAbOta(output_zip if output_zip else output_care_map,\n                       ab_partitions, partitions)\n\n  # Radio images that need to be packed into IMAGES/, and product-img.zip.\n  pack_radioimages_txt = os.path.join(\n      OPTIONS.input_tmp, \"META\", \"pack_radioimages.txt\")\n  if os.path.exists(pack_radioimages_txt):\n    with open(pack_radioimages_txt) as f:\n      AddPackRadioImages(output_zip, f.readlines())\n\n  AddVbmetaDigest(output_zip)\n\n  if output_zip:\n    common.ZipClose(output_zip)\n    if OPTIONS.replace_updated_files_list:\n      ReplaceUpdatedFiles(output_zip.filename,\n                          OPTIONS.replace_updated_files_list)\n\n\ndef OptimizeCompressedEntries(zipfile_path):\n  \"\"\"Convert files that do not compress well to uncompressed storage\n\n  EROFS images tend to be compressed already, so compressing them again\n  yields little space savings. Leaving them uncompressed will make\n  downstream tooling's job easier, and save compute time.\n  \"\"\"\n  if not zipfile.is_zipfile(zipfile_path):\n    return\n  entries_to_store = []\n  with tempfile.TemporaryDirectory() as tmpdir:\n    with zipfile.ZipFile(zipfile_path, \"r\", allowZip64=True) as zfp:\n      for zinfo in zfp.filelist:\n        if not zinfo.filename.startswith(\"IMAGES/\") and not zinfo.filename.startswith(\"META\"):\n          continue\n        # Don't try to store userdata.img uncompressed, it's usually huge.\n        if zinfo.filename.endswith(\"userdata.img\"):\n          continue\n        if zinfo.compress_size > zinfo.file_size * 0.80 and zinfo.compress_type != zipfile.ZIP_STORED:\n          entries_to_store.append(zinfo)\n          zfp.extract(zinfo, tmpdir)\n    if len(entries_to_store) == 0:\n      return\n    # Remove these entries, then re-add them as ZIP_STORED\n    ZipDelete(zipfile_path, [entry.filename for entry in entries_to_store])\n    with zipfile.ZipFile(zipfile_path, \"a\", allowZip64=True) as zfp:\n      for entry in entries_to_store:\n        zfp.write(os.path.join(tmpdir, entry.filename),\n                  entry.filename, compress_type=zipfile.ZIP_STORED)\n\n\ndef main(argv):\n  def option_handler(o, a):\n    if o in (\"-a\", \"--add_missing\"):\n      OPTIONS.add_missing = True\n    elif o in (\"-r\", \"--rebuild_recovery\",):\n      OPTIONS.rebuild_recovery = True\n    elif o == \"--replace_verity_private_key\":\n      raise ValueError(\"--replace_verity_private_key is no longer supported,\"\n                       \" please switch to AVB\")\n    elif o == \"--replace_verity_public_key\":\n      raise ValueError(\"--replace_verity_public_key is no longer supported,\"\n                       \" please switch to AVB\")\n    elif o == \"--is_signing\":\n      OPTIONS.is_signing = True\n    elif o == \"--avb_resolve_rollback_index_location_conflict\":\n      OPTIONS.avb_resolve_rollback_index_location_conflict = True\n    else:\n      return False\n    return True\n\n  args = common.ParseOptions(\n      argv, __doc__, extra_opts=\"ar\",\n      extra_long_opts=[\"add_missing\", \"rebuild_recovery\",\n                       \"replace_verity_public_key=\",\n                       \"replace_verity_private_key=\",\n                       \"is_signing\",\n                       \"avb_resolve_rollback_index_location_conflict\"],\n      extra_option_handler=option_handler)\n\n  if len(args) != 1:\n    common.Usage(__doc__)\n    sys.exit(1)\n\n  common.InitLogging()\n\n  AddImagesToTargetFiles(args[0])\n  OptimizeCompressedEntries(args[0])\n  logger.info(\"done.\")\n\n\nif __name__ == '__main__':\n  try:\n    common.CloseInheritedPipes()\n    main(sys.argv[1:])\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/apex_utils.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport logging\nimport os.path\nimport re\nimport shlex\nimport shutil\nimport zipfile\n\nimport apex_manifest\nimport common\nfrom common import UnzipTemp, RunAndCheckOutput, MakeTempFile, OPTIONS\n\nimport ota_metadata_pb2\n\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\n\nAPEX_PAYLOAD_IMAGE = 'apex_payload.img'\n\nAPEX_PUBKEY = 'apex_pubkey'\n\n# Partitions supporting APEXes\nPARTITIONS = ['system', 'system_ext', 'product', 'vendor', 'odm']\n\nclass ApexInfoError(Exception):\n  \"\"\"An Exception raised during Apex Information command.\"\"\"\n\n  def __init__(self, message):\n    Exception.__init__(self, message)\n\n\nclass ApexSigningError(Exception):\n  \"\"\"An Exception raised during Apex Payload signing.\"\"\"\n\n  def __init__(self, message):\n    Exception.__init__(self, message)\n\n\nclass ApexApkSigner(object):\n  \"\"\"Class to sign the apk files and other files in an apex payload image and repack the apex\"\"\"\n\n  def __init__(self, apex_path, key_passwords, codename_to_api_level_map, avbtool=None, sign_tool=None):\n    self.apex_path = apex_path\n    if not key_passwords:\n      self.key_passwords = dict()\n    else:\n      self.key_passwords = key_passwords\n    self.codename_to_api_level_map = codename_to_api_level_map\n    self.debugfs_path = os.path.join(\n        OPTIONS.search_path, \"bin\", \"debugfs_static\")\n    self.fsckerofs_path = os.path.join(\n        OPTIONS.search_path, \"bin\", \"fsck.erofs\")\n    self.avbtool = avbtool if avbtool else \"avbtool\"\n    self.sign_tool = sign_tool\n\n  def ProcessApexFile(self, apk_keys, payload_key, signing_args=None):\n    \"\"\"Scans and signs the payload files and repack the apex\n\n    Args:\n      apk_keys: A dict that holds the signing keys for apk files.\n\n    Returns:\n      The repacked apex file containing the signed apk files.\n    \"\"\"\n    payload_dir = self.ExtractApexPayload(self.apex_path)\n    apk_entries = []\n    for base_dir, _, files in os.walk(payload_dir):\n      apk_entries.extend(os.path.join(base_dir, file) for file in files if file.endswith('.apk'))\n\n    # No need to sign and repack, return the original apex path.\n    if not apk_entries and self.sign_tool is None:\n      logger.info('No apk file to sign in %s', self.apex_path)\n      return self.apex_path\n\n    for entry in apk_entries:\n      apk_name = os.path.basename(entry)\n      if apk_name not in apk_keys:\n        raise ApexSigningError('Failed to find signing keys for apk file {} in'\n                               ' apex {}.  Use \"-e <apkname>=\" to specify a key'\n                               .format(entry, self.apex_path))\n      if not any(dirname in entry for dirname in ['app/', 'priv-app/',\n                                                  'overlay/']):\n        logger.warning('Apk path does not contain the intended directory name:'\n                       ' %s', entry)\n\n    has_signed_content = self.SignContentsInPayload(\n        payload_dir, apk_entries, apk_keys, payload_key, signing_args)\n    if not has_signed_content:\n      logger.info('No contents has been signed in %s', self.apex_path)\n      return self.apex_path\n\n    return self.RepackApexPayload(payload_dir, payload_key, signing_args)\n\n  def ExtractApexPayload(self, apex_path):\n    \"\"\"Extracts the contents of an APEX and returns the directory of the contents\"\"\"\n    if not os.path.exists(self.debugfs_path):\n      raise ApexSigningError(\n          \"Couldn't find location of debugfs_static: \" +\n          \"Path {} does not exist. \".format(self.debugfs_path) +\n          \"Make sure bin/debugfs_static can be found in -p <path>\")\n    if not os.path.exists(self.fsckerofs_path):\n      raise ApexSigningError(\n          \"Couldn't find location of fsck.erofs: \" +\n          \"Path {} does not exist. \".format(self.fsckerofs_path) +\n          \"Make sure bin/fsck.erofs can be found in -p <path>\")\n    payload_dir = common.MakeTempDir()\n    extract_cmd = ['deapexer', '--debugfs_path', self.debugfs_path,\n                   '--fsckerofs_path', self.fsckerofs_path,\n                   'extract',\n                   apex_path, payload_dir]\n    common.RunAndCheckOutput(extract_cmd)\n    return payload_dir\n\n  def SignContentsInPayload(self, payload_dir, apk_entries, apk_keys, payload_key, signing_args):\n    \"\"\"Signs the contents in payload.\"\"\"\n    has_signed_content = False\n    for entry in apk_entries:\n      apk_path = os.path.join(payload_dir, entry)\n      assert os.path.exists(self.apex_path)\n\n      key_name = apk_keys.get(os.path.basename(entry))\n      if key_name in common.SPECIAL_CERT_STRINGS:\n        logger.info('Not signing: %s due to special cert string', apk_path)\n        continue\n\n      logger.info('Signing apk file %s in apex %s', apk_path, self.apex_path)\n      # Rename the unsigned apk and overwrite the original apk path with the\n      # signed apk file.\n      unsigned_apk = common.MakeTempFile()\n      os.rename(apk_path, unsigned_apk)\n      common.SignFile(\n          unsigned_apk, apk_path, key_name, self.key_passwords.get(key_name),\n          codename_to_api_level_map=self.codename_to_api_level_map)\n      has_signed_content = True\n\n    if self.sign_tool:\n      logger.info('Signing payload contents in apex %s with %s', self.apex_path, self.sign_tool)\n      # Pass avbtool to the custom signing tool\n      cmd = [self.sign_tool, '--avbtool', self.avbtool]\n      # Pass signing_args verbatim which will be forwarded to avbtool (e.g. --signing_helper=...)\n      if signing_args:\n        cmd.extend(['--signing_args', '\"{}\"'.format(signing_args)])\n      cmd.extend([payload_key, payload_dir])\n      common.RunAndCheckOutput(cmd)\n      has_signed_content = True\n\n    return has_signed_content\n\n  def RepackApexPayload(self, payload_dir, payload_key, signing_args=None):\n    \"\"\"Rebuilds the apex file with the updated payload directory.\"\"\"\n    apex_dir = common.MakeTempDir()\n    # Extract the apex file and reuse its meta files as repack parameters.\n    common.UnzipToDir(self.apex_path, apex_dir)\n    arguments_dict = {\n        'manifest': os.path.join(apex_dir, 'apex_manifest.pb'),\n        'build_info': os.path.join(apex_dir, 'apex_build_info.pb'),\n        'key': payload_key,\n    }\n    for filename in arguments_dict.values():\n      assert os.path.exists(filename), 'file {} not found'.format(filename)\n\n    # The repack process will add back these files later in the payload image.\n    for name in ['apex_manifest.pb', 'apex_manifest.json', 'lost+found']:\n      path = os.path.join(payload_dir, name)\n      if os.path.isfile(path):\n        os.remove(path)\n      elif os.path.isdir(path):\n        shutil.rmtree(path, ignore_errors=True)\n\n    # TODO(xunchang) the signing process can be improved by using\n    # '--unsigned_payload_only'. But we need to parse the vbmeta earlier for\n    # the signing arguments, e.g. algorithm, salt, etc.\n    payload_img = os.path.join(apex_dir, APEX_PAYLOAD_IMAGE)\n    generate_image_cmd = ['apexer', '--force', '--payload_only',\n                          '--do_not_check_keyname', '--apexer_tool_path',\n                          os.getenv('PATH')]\n    for key, val in arguments_dict.items():\n      generate_image_cmd.extend(['--' + key, val])\n\n    # Add quote to the signing_args as we will pass\n    # --signing_args \"--signing_helper_with_files=%path\" to apexer\n    if signing_args:\n      generate_image_cmd.extend(\n          ['--signing_args', '\"{}\"'.format(signing_args)])\n\n    # optional arguments for apex repacking\n    manifest_json = os.path.join(apex_dir, 'apex_manifest.json')\n    if os.path.exists(manifest_json):\n      generate_image_cmd.extend(['--manifest_json', manifest_json])\n    generate_image_cmd.extend([payload_dir, payload_img])\n    if OPTIONS.verbose:\n      generate_image_cmd.append('-v')\n    common.RunAndCheckOutput(generate_image_cmd)\n\n    # Add the payload image back to the apex file.\n    common.ZipDelete(self.apex_path, APEX_PAYLOAD_IMAGE)\n    with zipfile.ZipFile(self.apex_path, 'a', allowZip64=True) as output_apex:\n      common.ZipWrite(output_apex, payload_img, APEX_PAYLOAD_IMAGE,\n                      compress_type=zipfile.ZIP_STORED)\n    return self.apex_path\n\n\ndef SignApexPayload(avbtool, payload_file, payload_key_path, payload_key_name,\n                    algorithm, salt, hash_algorithm, no_hashtree, signing_args=None):\n  \"\"\"Signs a given payload_file with the payload key.\"\"\"\n  # Add the new footer. Old footer, if any, will be replaced by avbtool.\n  cmd = [avbtool, 'add_hashtree_footer',\n         '--do_not_generate_fec',\n         '--algorithm', algorithm,\n         '--key', payload_key_path,\n         '--prop', 'apex.key:{}'.format(payload_key_name),\n         '--image', payload_file,\n         '--salt', salt,\n         '--hash_algorithm', hash_algorithm]\n  if no_hashtree:\n    cmd.append('--no_hashtree')\n  if signing_args:\n    cmd.extend(shlex.split(signing_args))\n\n  try:\n    common.RunAndCheckOutput(cmd)\n  except common.ExternalError as e:\n    raise ApexSigningError(\n        'Failed to sign APEX payload {} with {}:\\n{}'.format(\n            payload_file, payload_key_path, e))\n\n  # Verify the signed payload image with specified public key.\n  logger.info('Verifying %s', payload_file)\n  VerifyApexPayload(avbtool, payload_file, payload_key_path, no_hashtree)\n\n\ndef VerifyApexPayload(avbtool, payload_file, payload_key, no_hashtree=False):\n  \"\"\"Verifies the APEX payload signature with the given key.\"\"\"\n  cmd = [avbtool, 'verify_image', '--image', payload_file,\n         '--key', payload_key]\n  if no_hashtree:\n    cmd.append('--accept_zeroed_hashtree')\n  try:\n    common.RunAndCheckOutput(cmd)\n  except common.ExternalError as e:\n    raise ApexSigningError(\n        'Failed to validate payload signing for {} with {}:\\n{}'.format(\n            payload_file, payload_key, e))\n\n\ndef ParseApexPayloadInfo(avbtool, payload_path):\n  \"\"\"Parses the APEX payload info.\n\n  Args:\n    avbtool: The AVB tool to use.\n    payload_path: The path to the payload image.\n\n  Raises:\n    ApexInfoError on parsing errors.\n\n  Returns:\n    A dict that contains payload property-value pairs. The dict should at least\n    contain Algorithm, Salt, Tree Size and apex.key.\n  \"\"\"\n  if not os.path.exists(payload_path):\n    raise ApexInfoError('Failed to find image: {}'.format(payload_path))\n\n  cmd = [avbtool, 'info_image', '--image', payload_path]\n  try:\n    output = common.RunAndCheckOutput(cmd)\n  except common.ExternalError as e:\n    raise ApexInfoError(\n        'Failed to get APEX payload info for {}:\\n{}'.format(\n            payload_path, e))\n\n  # Extract the Algorithm / Hash Algorithm / Salt / Prop info / Tree size from\n  # payload (i.e. an image signed with avbtool). For example,\n  # Algorithm:                SHA256_RSA4096\n  PAYLOAD_INFO_PATTERN = (\n      r'^\\s*(?P<key>Algorithm|Hash Algorithm|Salt|Prop|Tree Size)\\:\\s*(?P<value>.*?)$')\n  payload_info_matcher = re.compile(PAYLOAD_INFO_PATTERN)\n\n  payload_info = {}\n  for line in output.split('\\n'):\n    line_info = payload_info_matcher.match(line)\n    if not line_info:\n      continue\n\n    key, value = line_info.group('key'), line_info.group('value')\n\n    if key == 'Prop':\n      # Further extract the property key-value pair, from a 'Prop:' line. For\n      # example,\n      #   Prop: apex.key -> 'com.android.runtime'\n      # Note that avbtool writes single or double quotes around values.\n      PROPERTY_DESCRIPTOR_PATTERN = r'^\\s*(?P<key>.*?)\\s->\\s*(?P<value>.*?)$'\n\n      prop_matcher = re.compile(PROPERTY_DESCRIPTOR_PATTERN)\n      prop = prop_matcher.match(value)\n      if not prop:\n        raise ApexInfoError(\n            'Failed to parse prop string {}'.format(value))\n\n      prop_key, prop_value = prop.group('key'), prop.group('value')\n      if prop_key == 'apex.key':\n        # avbtool dumps the prop value with repr(), which contains single /\n        # double quotes that we don't want.\n        payload_info[prop_key] = prop_value.strip('\\\"\\'')\n\n    else:\n      payload_info[key] = value\n\n  # Validation check.\n  for key in ('Algorithm', 'Salt', 'apex.key', 'Hash Algorithm'):\n    if key not in payload_info:\n      raise ApexInfoError(\n          'Failed to find {} prop in {}'.format(key, payload_path))\n\n  return payload_info\n\n\ndef SignUncompressedApex(avbtool, apex_file, payload_key, container_key,\n                         container_pw, apk_keys, codename_to_api_level_map,\n                         no_hashtree, signing_args=None, sign_tool=None):\n  \"\"\"Signs the current uncompressed APEX with the given payload/container keys.\n\n  Args:\n    apex_file: Uncompressed APEX file.\n    payload_key: The path to payload signing key (w/ extension).\n    container_key: The path to container signing key (w/o extension).\n    container_pw: The matching password of the container_key, or None.\n    apk_keys: A dict that holds the signing keys for apk files.\n    codename_to_api_level_map: A dict that maps from codename to API level.\n    no_hashtree: Don't include hashtree in the signed APEX.\n    signing_args: Additional args to be passed to the payload signer.\n    sign_tool: A tool to sign the contents of the APEX.\n\n  Returns:\n    The path to the signed APEX file.\n  \"\"\"\n  # 1. Extract the apex payload image and sign the files (e.g. APKs). Repack\n  # the apex file after signing.\n  apk_signer = ApexApkSigner(apex_file, container_pw,\n                             codename_to_api_level_map,\n                             avbtool, sign_tool)\n  apex_file = apk_signer.ProcessApexFile(apk_keys, payload_key, signing_args)\n\n  # 2a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given\n  # payload_key.\n  payload_dir = common.MakeTempDir(prefix='apex-payload-')\n  with zipfile.ZipFile(apex_file) as apex_fd:\n    payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)\n    zip_items = apex_fd.namelist()\n\n  payload_info = ParseApexPayloadInfo(avbtool, payload_file)\n  if no_hashtree is None:\n    no_hashtree = payload_info.get(\"Tree Size\", 0) == 0\n  SignApexPayload(\n      avbtool,\n      payload_file,\n      payload_key,\n      payload_info['apex.key'],\n      payload_info['Algorithm'],\n      payload_info['Salt'],\n      payload_info['Hash Algorithm'],\n      no_hashtree,\n      signing_args)\n\n  # 2b. Update the embedded payload public key.\n  payload_public_key = common.ExtractAvbPublicKey(avbtool, payload_key)\n  common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)\n  if APEX_PUBKEY in zip_items:\n    common.ZipDelete(apex_file, APEX_PUBKEY)\n  apex_zip = zipfile.ZipFile(apex_file, 'a', allowZip64=True)\n  common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)\n  common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)\n  common.ZipClose(apex_zip)\n\n  # 3. Sign the APEX container with container_key.\n  signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')\n\n  # Specify the 4K alignment when calling SignApk.\n  extra_signapk_args = OPTIONS.extra_signapk_args[:]\n  extra_signapk_args.extend(['-a', '4096', '--align-file-size'])\n\n  password = container_pw.get(container_key) if container_pw else None\n  common.SignFile(\n      apex_file,\n      signed_apex,\n      container_key,\n      password,\n      codename_to_api_level_map=codename_to_api_level_map,\n      extra_signapk_args=extra_signapk_args)\n\n  return signed_apex\n\n\ndef SignCompressedApex(avbtool, apex_file, payload_key, container_key,\n                       container_pw, apk_keys, codename_to_api_level_map,\n                       no_hashtree, signing_args=None, sign_tool=None):\n  \"\"\"Signs the current compressed APEX with the given payload/container keys.\n\n  Args:\n    apex_file: Raw uncompressed APEX data.\n    payload_key: The path to payload signing key (w/ extension).\n    container_key: The path to container signing key (w/o extension).\n    container_pw: The matching password of the container_key, or None.\n    apk_keys: A dict that holds the signing keys for apk files.\n    codename_to_api_level_map: A dict that maps from codename to API level.\n    no_hashtree: Don't include hashtree in the signed APEX.\n    signing_args: Additional args to be passed to the payload signer.\n\n  Returns:\n    The path to the signed APEX file.\n  \"\"\"\n  debugfs_path = os.path.join(OPTIONS.search_path, 'bin', 'debugfs_static')\n\n  # 1. Decompress original_apex inside compressed apex.\n  original_apex_file = common.MakeTempFile(prefix='original-apex-',\n                                           suffix='.apex')\n  # Decompression target path should not exist\n  os.remove(original_apex_file)\n  common.RunAndCheckOutput(['deapexer', '--debugfs_path', debugfs_path,\n                            'decompress', '--input', apex_file,\n                            '--output', original_apex_file])\n\n  # 2. Sign original_apex\n  signed_original_apex_file = SignUncompressedApex(\n      avbtool,\n      original_apex_file,\n      payload_key,\n      container_key,\n      container_pw,\n      apk_keys,\n      codename_to_api_level_map,\n      no_hashtree,\n      signing_args,\n      sign_tool)\n\n  # 3. Compress signed original apex.\n  compressed_apex_file = common.MakeTempFile(prefix='apex-container-',\n                                             suffix='.capex')\n  common.RunAndCheckOutput(['apex_compression_tool',\n                            'compress',\n                            '--apex_compression_tool_path', os.getenv('PATH'),\n                            '--input', signed_original_apex_file,\n                            '--output', compressed_apex_file])\n\n  # 4. Sign the APEX container with container_key.\n  signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.capex')\n\n  password = container_pw.get(container_key) if container_pw else None\n  common.SignFile(\n      compressed_apex_file,\n      signed_apex,\n      container_key,\n      password,\n      codename_to_api_level_map=codename_to_api_level_map,\n      extra_signapk_args=OPTIONS.extra_signapk_args)\n\n  return signed_apex\n\n\ndef SignApex(avbtool, apex_data, payload_key, container_key, container_pw,\n             apk_keys, codename_to_api_level_map,\n             no_hashtree, signing_args=None, sign_tool=None):\n  \"\"\"Signs the current APEX with the given payload/container keys.\n\n  Args:\n    apex_file: Path to apex file path.\n    payload_key: The path to payload signing key (w/ extension).\n    container_key: The path to container signing key (w/o extension).\n    container_pw: The matching password of the container_key, or None.\n    apk_keys: A dict that holds the signing keys for apk files.\n    codename_to_api_level_map: A dict that maps from codename to API level.\n    no_hashtree: Don't include hashtree in the signed APEX.\n    signing_args: Additional args to be passed to the payload signer.\n\n  Returns:\n    The path to the signed APEX file.\n  \"\"\"\n  apex_file = common.MakeTempFile(prefix='apex-container-', suffix='.apex')\n  with open(apex_file, 'wb') as output_fp:\n    output_fp.write(apex_data)\n\n  debugfs_path = os.path.join(OPTIONS.search_path, 'bin', 'debugfs_static')\n  cmd = ['deapexer', '--debugfs_path', debugfs_path,\n         'info', '--print-type', apex_file]\n\n  try:\n    apex_type = common.RunAndCheckOutput(cmd).strip()\n    if apex_type == 'UNCOMPRESSED':\n      return SignUncompressedApex(\n          avbtool,\n          apex_file,\n          payload_key=payload_key,\n          container_key=container_key,\n          container_pw=container_pw,\n          codename_to_api_level_map=codename_to_api_level_map,\n          no_hashtree=no_hashtree,\n          apk_keys=apk_keys,\n          signing_args=signing_args,\n          sign_tool=sign_tool)\n    elif apex_type == 'COMPRESSED':\n      return SignCompressedApex(\n          avbtool,\n          apex_file,\n          payload_key=payload_key,\n          container_key=container_key,\n          container_pw=container_pw,\n          codename_to_api_level_map=codename_to_api_level_map,\n          no_hashtree=no_hashtree,\n          apk_keys=apk_keys,\n          signing_args=signing_args,\n          sign_tool=sign_tool)\n    else:\n      # TODO(b/172912232): support signing compressed apex\n      raise ApexInfoError('Unsupported apex type {}'.format(apex_type))\n\n  except common.ExternalError as e:\n    raise ApexInfoError(\n        'Failed to get type for {}:\\n{}'.format(apex_file, e))\n\n\ndef GetApexInfoFromTargetFiles(input_file):\n  \"\"\"\n  Get information about APEXes stored in the input_file zip\n\n  Args:\n    input_file: The filename of the target build target-files zip or directory.\n\n  Return:\n    A list of ota_metadata_pb2.ApexInfo() populated using the APEX stored in\n    each partition of the input_file\n  \"\"\"\n\n  # Extract the apex files so that we can run checks on them\n  if not isinstance(input_file, str):\n    raise RuntimeError(\"must pass filepath to target-files zip or directory\")\n  apex_infos = []\n  for partition in PARTITIONS:\n    apex_infos.extend(GetApexInfoForPartition(input_file, partition))\n  return apex_infos\n\n\ndef GetApexInfoForPartition(input_file, partition):\n  apex_subdir = os.path.join(partition.upper(), 'apex')\n  if os.path.isdir(input_file):\n    tmp_dir = input_file\n  else:\n    tmp_dir = UnzipTemp(input_file, [os.path.join(apex_subdir, '*')])\n  target_dir = os.path.join(tmp_dir, apex_subdir)\n\n  # Partial target-files packages for vendor-only builds may not contain\n  # a system apex directory.\n  if not os.path.exists(target_dir):\n    logger.info('No APEX directory at path: %s', target_dir)\n    return []\n\n  apex_infos = []\n\n  debugfs_path = \"debugfs\"\n  if OPTIONS.search_path:\n    debugfs_path = os.path.join(OPTIONS.search_path, \"bin\", \"debugfs_static\")\n\n  deapexer = 'deapexer'\n  if OPTIONS.search_path:\n    deapexer_path = os.path.join(OPTIONS.search_path, \"bin\", \"deapexer\")\n    if os.path.isfile(deapexer_path):\n      deapexer = deapexer_path\n\n  for apex_filename in sorted(os.listdir(target_dir)):\n    apex_filepath = os.path.join(target_dir, apex_filename)\n    if not os.path.isfile(apex_filepath) or \\\n            not zipfile.is_zipfile(apex_filepath):\n      logger.info(\"Skipping %s because it's not a zipfile\", apex_filepath)\n      continue\n    apex_info = ota_metadata_pb2.ApexInfo()\n    # Open the apex file to retrieve information\n    manifest = apex_manifest.fromApex(apex_filepath)\n    apex_info.package_name = manifest.name\n    apex_info.version = manifest.version\n    # Check if the file is compressed or not\n    apex_type = RunAndCheckOutput([\n        deapexer, \"--debugfs_path\", debugfs_path,\n        'info', '--print-type', apex_filepath]).rstrip()\n    if apex_type == 'COMPRESSED':\n      apex_info.is_compressed = True\n    elif apex_type == 'UNCOMPRESSED':\n      apex_info.is_compressed = False\n    else:\n      raise RuntimeError('Not an APEX file: ' + apex_type)\n\n    # Decompress compressed APEX to determine its size\n    if apex_info.is_compressed:\n      decompressed_file_path = MakeTempFile(prefix=\"decompressed-\",\n                                            suffix=\".apex\")\n      # Decompression target path should not exist\n      os.remove(decompressed_file_path)\n      RunAndCheckOutput([deapexer, 'decompress', '--input', apex_filepath,\n                         '--output', decompressed_file_path])\n      apex_info.decompressed_size = os.path.getsize(decompressed_file_path)\n\n    apex_infos.append(apex_info)\n\n  return apex_infos\n"
  },
  {
    "path": "tools/releasetools/blockimgdiff.py",
    "content": "# Copyright (C) 2014 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import print_function\n\nimport array\nimport copy\nimport functools\nimport heapq\nimport itertools\nimport logging\nimport multiprocessing\nimport os\nimport os.path\nimport re\nimport sys\nimport threading\nimport zlib\nfrom collections import deque, namedtuple, OrderedDict\n\nimport common\nfrom images import EmptyImage\nfrom rangelib import RangeSet\n\n__all__ = [\"BlockImageDiff\"]\n\nlogger = logging.getLogger(__name__)\n\n# The tuple contains the style and bytes of a bsdiff|imgdiff patch.\nPatchInfo = namedtuple(\"PatchInfo\", [\"imgdiff\", \"content\"])\n\n\ndef compute_patch(srcfile, tgtfile, imgdiff=False):\n  \"\"\"Calls bsdiff|imgdiff to compute the patch data, returns a PatchInfo.\"\"\"\n  patchfile = common.MakeTempFile(prefix='patch-')\n\n  cmd = ['imgdiff', '-z'] if imgdiff else ['bsdiff']\n  cmd.extend([srcfile, tgtfile, patchfile])\n\n  # Don't dump the bsdiff/imgdiff commands, which are not useful for the case\n  # here, since they contain temp filenames only.\n  proc = common.Run(cmd, verbose=False)\n  output, _ = proc.communicate()\n\n  if proc.returncode != 0:\n    raise ValueError(output)\n\n  with open(patchfile, 'rb') as f:\n    return PatchInfo(imgdiff, f.read())\n\n\nclass Transfer(object):\n  def __init__(self, tgt_name, src_name, tgt_ranges, src_ranges, tgt_sha1,\n               src_sha1, style, by_id):\n    self.tgt_name = tgt_name\n    self.src_name = src_name\n    self.tgt_ranges = tgt_ranges\n    self.src_ranges = src_ranges\n    self.tgt_sha1 = tgt_sha1\n    self.src_sha1 = src_sha1\n    self.style = style\n\n    # We use OrderedDict rather than dict so that the output is repeatable;\n    # otherwise it would depend on the hash values of the Transfer objects.\n    self.goes_before = OrderedDict()\n    self.goes_after = OrderedDict()\n\n    self.stash_before = []\n    self.use_stash = []\n\n    self.id = len(by_id)\n    by_id.append(self)\n\n    self._patch_info = None\n\n  @property\n  def patch_info(self):\n    return self._patch_info\n\n  @patch_info.setter\n  def patch_info(self, info):\n    if info:\n      assert self.style == \"diff\"\n    self._patch_info = info\n\n  def NetStashChange(self):\n    return (sum(sr.size() for (_, sr) in self.stash_before) -\n            sum(sr.size() for (_, sr) in self.use_stash))\n\n  def ConvertToNew(self):\n    assert self.style != \"new\"\n    self.use_stash = []\n    self.style = \"new\"\n    self.src_ranges = RangeSet()\n    self.patch_info = None\n\n  def __str__(self):\n    return (str(self.id) + \": <\" + str(self.src_ranges) + \" \" + self.style +\n            \" to \" + str(self.tgt_ranges) + \">\")\n\n\n@functools.total_ordering\nclass HeapItem(object):\n  def __init__(self, item):\n    self.item = item\n    # Negate the score since python's heap is a min-heap and we want the\n    # maximum score.\n    self.score = -item.score\n\n  def clear(self):\n    self.item = None\n\n  def __bool__(self):\n    return self.item is not None\n\n  # Python 2 uses __nonzero__, while Python 3 uses __bool__.\n  __nonzero__ = __bool__\n\n  # The rest operations are generated by functools.total_ordering decorator.\n  def __eq__(self, other):\n    return self.score == other.score\n\n  def __le__(self, other):\n    return self.score <= other.score\n\n\nclass ImgdiffStats(object):\n  \"\"\"A class that collects imgdiff stats.\n\n  It keeps track of the files that will be applied imgdiff while generating\n  BlockImageDiff. It also logs the ones that cannot use imgdiff, with specific\n  reasons. The stats is only meaningful when imgdiff not being disabled by the\n  caller of BlockImageDiff. In addition, only files with supported types\n  (BlockImageDiff.FileTypeSupportedByImgdiff()) are allowed to be logged.\n  \"\"\"\n\n  USED_IMGDIFF = \"APK files diff'd with imgdiff\"\n  USED_IMGDIFF_LARGE_APK = \"Large APK files split and diff'd with imgdiff\"\n\n  # Reasons for not applying imgdiff on APKs.\n  SKIPPED_NONMONOTONIC = \"Not used imgdiff due to having non-monotonic ranges\"\n  SKIPPED_SHARED_BLOCKS = \"Not used imgdiff due to using shared blocks\"\n  SKIPPED_INCOMPLETE = \"Not used imgdiff due to incomplete RangeSet\"\n\n  # The list of valid reasons, which will also be the dumped order in a report.\n  REASONS = (\n      USED_IMGDIFF,\n      USED_IMGDIFF_LARGE_APK,\n      SKIPPED_NONMONOTONIC,\n      SKIPPED_SHARED_BLOCKS,\n      SKIPPED_INCOMPLETE,\n  )\n\n  def  __init__(self):\n    self.stats = {}\n\n  def Log(self, filename, reason):\n    \"\"\"Logs why imgdiff can or cannot be applied to the given filename.\n\n    Args:\n      filename: The filename string.\n      reason: One of the reason constants listed in REASONS.\n\n    Raises:\n      AssertionError: On unsupported filetypes or invalid reason.\n    \"\"\"\n    assert BlockImageDiff.FileTypeSupportedByImgdiff(filename)\n    assert reason in self.REASONS\n\n    if reason not in self.stats:\n      self.stats[reason] = set()\n    self.stats[reason].add(filename)\n\n  def Report(self):\n    \"\"\"Prints a report of the collected imgdiff stats.\"\"\"\n\n    def print_header(header, separator):\n      logger.info(header)\n      logger.info('%s\\n', separator * len(header))\n\n    print_header('  Imgdiff Stats Report  ', '=')\n    for key in self.REASONS:\n      if key not in self.stats:\n        continue\n      values = self.stats[key]\n      section_header = ' {} (count: {}) '.format(key, len(values))\n      print_header(section_header, '-')\n      logger.info(''.join(['  {}\\n'.format(name) for name in values]))\n\n\nclass BlockImageDiff(object):\n  \"\"\"Generates the diff of two block image objects.\n\n  BlockImageDiff works on two image objects. An image object is anything that\n  provides the following attributes:\n\n     blocksize: the size in bytes of a block, currently must be 4096.\n\n     total_blocks: the total size of the partition/image, in blocks.\n\n     care_map: a RangeSet containing which blocks (in the range [0,\n       total_blocks) we actually care about; i.e. which blocks contain data.\n\n     file_map: a dict that partitions the blocks contained in care_map into\n         smaller domains that are useful for doing diffs on. (Typically a domain\n         is a file, and the key in file_map is the pathname.)\n\n     clobbered_blocks: a RangeSet containing which blocks contain data but may\n         be altered by the FS. They need to be excluded when verifying the\n         partition integrity.\n\n     ReadRangeSet(): a function that takes a RangeSet and returns the data\n         contained in the image blocks of that RangeSet. The data is returned as\n         a list or tuple of strings; concatenating the elements together should\n         produce the requested data. Implementations are free to break up the\n         data into list/tuple elements in any way that is convenient.\n\n     RangeSha1(): a function that returns (as a hex string) the SHA-1 hash of\n         all the data in the specified range.\n\n     TotalSha1(): a function that returns (as a hex string) the SHA-1 hash of\n         all the data in the image (ie, all the blocks in the care_map minus\n         clobbered_blocks, or including the clobbered blocks if\n         include_clobbered_blocks is True).\n\n  When creating a BlockImageDiff, the src image may be None, in which case the\n  list of transfers produced will never read from the original image.\n  \"\"\"\n\n  def __init__(self, tgt, src=None, threads=None, version=4,\n               disable_imgdiff=False):\n    if threads is None:\n      threads = multiprocessing.cpu_count() // 2\n      if threads == 0:\n        threads = 1\n    self.threads = threads\n    self.version = version\n    self.transfers = []\n    self.src_basenames = {}\n    self.src_numpatterns = {}\n    self._max_stashed_size = 0\n    self.touched_src_ranges = RangeSet()\n    self.touched_src_sha1 = None\n    self.disable_imgdiff = disable_imgdiff\n    self.imgdiff_stats = ImgdiffStats() if not disable_imgdiff else None\n\n    assert version in (3, 4)\n\n    self.tgt = tgt\n    if src is None:\n      src = EmptyImage()\n    self.src = src\n\n    # The updater code that installs the patch always uses 4k blocks.\n    assert tgt.blocksize == 4096\n    assert src.blocksize == 4096\n\n    # The range sets in each filemap should comprise a partition of\n    # the care map.\n    self.AssertPartition(src.care_map, src.file_map.values())\n    self.AssertPartition(tgt.care_map, tgt.file_map.values())\n\n  @property\n  def max_stashed_size(self):\n    return self._max_stashed_size\n\n  @staticmethod\n  def FileTypeSupportedByImgdiff(filename):\n    \"\"\"Returns whether the file type is supported by imgdiff.\"\"\"\n    return filename.lower().endswith(('.apk', '.jar', '.zip'))\n\n  def CanUseImgdiff(self, name, tgt_ranges, src_ranges, large_apk=False):\n    \"\"\"Checks whether we can apply imgdiff for the given RangeSets.\n\n    For files in ZIP format (e.g., APKs, JARs, etc.) we would like to use\n    'imgdiff -z' if possible. Because it usually produces significantly smaller\n    patches than bsdiff.\n\n    This is permissible if all of the following conditions hold.\n      - The imgdiff hasn't been disabled by the caller (e.g. squashfs);\n      - The file type is supported by imgdiff;\n      - The source and target blocks are monotonic (i.e. the data is stored with\n        blocks in increasing order);\n      - Both files don't contain shared blocks;\n      - Both files have complete lists of blocks;\n      - We haven't removed any blocks from the source set.\n\n    If all these conditions are satisfied, concatenating all the blocks in the\n    RangeSet in order will produce a valid ZIP file (plus possibly extra zeros\n    in the last block). imgdiff is fine with extra zeros at the end of the file.\n\n    Args:\n      name: The filename to be diff'd.\n      tgt_ranges: The target RangeSet.\n      src_ranges: The source RangeSet.\n      large_apk: Whether this is to split a large APK.\n\n    Returns:\n      A boolean result.\n    \"\"\"\n    if self.disable_imgdiff or not self.FileTypeSupportedByImgdiff(name):\n      return False\n\n    if not tgt_ranges.monotonic or not src_ranges.monotonic:\n      self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_NONMONOTONIC)\n      return False\n\n    if (tgt_ranges.extra.get('uses_shared_blocks') or\n        src_ranges.extra.get('uses_shared_blocks')):\n      self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_SHARED_BLOCKS)\n      return False\n\n    if tgt_ranges.extra.get('incomplete') or src_ranges.extra.get('incomplete'):\n      self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_INCOMPLETE)\n      return False\n\n    reason = (ImgdiffStats.USED_IMGDIFF_LARGE_APK if large_apk\n              else ImgdiffStats.USED_IMGDIFF)\n    self.imgdiff_stats.Log(name, reason)\n    return True\n\n  def Compute(self, prefix):\n    # When looking for a source file to use as the diff input for a\n    # target file, we try:\n    #   1) an exact path match if available, otherwise\n    #   2) a exact basename match if available, otherwise\n    #   3) a basename match after all runs of digits are replaced by\n    #      \"#\" if available, otherwise\n    #   4) we have no source for this target.\n    self.AbbreviateSourceNames()\n    self.FindTransfers()\n\n    self.FindSequenceForTransfers()\n\n    # Ensure the runtime stash size is under the limit.\n    if common.OPTIONS.cache_size is not None:\n      stash_limit = (common.OPTIONS.cache_size *\n                     common.OPTIONS.stash_threshold / self.tgt.blocksize)\n      # Ignore the stash limit and calculate the maximum simultaneously stashed\n      # blocks needed.\n      _, max_stashed_blocks = self.ReviseStashSize(ignore_stash_limit=True)\n\n      # We cannot stash more blocks than the stash limit simultaneously. As a\n      # result, some 'diff' commands will be converted to new; leading to an\n      # unintended large package. To mitigate this issue, we can carefully\n      # choose the transfers for conversion. The number '1024' can be further\n      # tweaked here to balance the package size and build time.\n      if max_stashed_blocks > stash_limit + 1024:\n        self.SelectAndConvertDiffTransfersToNew(\n            max_stashed_blocks - stash_limit)\n        # Regenerate the sequence as the graph has changed.\n        self.FindSequenceForTransfers()\n\n      # Revise the stash size again to keep the size under limit.\n      self.ReviseStashSize()\n\n    # Double-check our work.\n    self.AssertSequenceGood()\n    self.AssertSha1Good()\n\n    self.ComputePatches(prefix)\n    self.WriteTransfers(prefix)\n\n    # Report the imgdiff stats.\n    if not self.disable_imgdiff:\n      self.imgdiff_stats.Report()\n\n  def WriteTransfers(self, prefix):\n    def WriteSplitTransfers(out, style, target_blocks):\n      \"\"\"Limit the size of operand in command 'new' and 'zero' to 1024 blocks.\n\n      This prevents the target size of one command from being too large; and\n      might help to avoid fsync errors on some devices.\"\"\"\n\n      assert style == \"new\" or style == \"zero\"\n      blocks_limit = 1024\n      total = 0\n      while target_blocks:\n        blocks_to_write = target_blocks.first(blocks_limit)\n        out.append(\"%s %s\\n\" % (style, blocks_to_write.to_string_raw()))\n        total += blocks_to_write.size()\n        target_blocks = target_blocks.subtract(blocks_to_write)\n      return total\n\n    out = []\n    total = 0\n\n    # In BBOTA v3+, it uses the hash of the stashed blocks as the stash slot\n    # id. 'stashes' records the map from 'hash' to the ref count. The stash\n    # will be freed only if the count decrements to zero.\n    stashes = {}\n    stashed_blocks = 0\n    max_stashed_blocks = 0\n\n    for xf in self.transfers:\n\n      for _, sr in xf.stash_before:\n        sh = self.src.RangeSha1(sr)\n        if sh in stashes:\n          stashes[sh] += 1\n        else:\n          stashes[sh] = 1\n          stashed_blocks += sr.size()\n          self.touched_src_ranges = self.touched_src_ranges.union(sr)\n          out.append(\"stash %s %s\\n\" % (sh, sr.to_string_raw()))\n\n      if stashed_blocks > max_stashed_blocks:\n        max_stashed_blocks = stashed_blocks\n\n      free_string = []\n      free_size = 0\n\n      #   <# blocks> <src ranges>\n      #     OR\n      #   <# blocks> <src ranges> <src locs> <stash refs...>\n      #     OR\n      #   <# blocks> - <stash refs...>\n\n      size = xf.src_ranges.size()\n      src_str_buffer = [str(size)]\n\n      unstashed_src_ranges = xf.src_ranges\n      mapped_stashes = []\n      for _, sr in xf.use_stash:\n        unstashed_src_ranges = unstashed_src_ranges.subtract(sr)\n        sh = self.src.RangeSha1(sr)\n        sr = xf.src_ranges.map_within(sr)\n        mapped_stashes.append(sr)\n        assert sh in stashes\n        src_str_buffer.append(\"%s:%s\" % (sh, sr.to_string_raw()))\n        stashes[sh] -= 1\n        if stashes[sh] == 0:\n          free_string.append(\"free %s\\n\" % (sh,))\n          free_size += sr.size()\n          stashes.pop(sh)\n\n      if unstashed_src_ranges:\n        src_str_buffer.insert(1, unstashed_src_ranges.to_string_raw())\n        if xf.use_stash:\n          mapped_unstashed = xf.src_ranges.map_within(unstashed_src_ranges)\n          src_str_buffer.insert(2, mapped_unstashed.to_string_raw())\n          mapped_stashes.append(mapped_unstashed)\n          self.AssertPartition(RangeSet(data=(0, size)), mapped_stashes)\n      else:\n        src_str_buffer.insert(1, \"-\")\n        self.AssertPartition(RangeSet(data=(0, size)), mapped_stashes)\n\n      src_str = \" \".join(src_str_buffer)\n\n      # version 3+:\n      #   zero <rangeset>\n      #   new <rangeset>\n      #   erase <rangeset>\n      #   bsdiff patchstart patchlen srchash tgthash <tgt rangeset> <src_str>\n      #   imgdiff patchstart patchlen srchash tgthash <tgt rangeset> <src_str>\n      #   move hash <tgt rangeset> <src_str>\n\n      tgt_size = xf.tgt_ranges.size()\n\n      if xf.style == \"new\":\n        assert xf.tgt_ranges\n        assert tgt_size == WriteSplitTransfers(out, xf.style, xf.tgt_ranges)\n        total += tgt_size\n      elif xf.style == \"move\":\n        assert xf.tgt_ranges\n        assert xf.src_ranges.size() == tgt_size\n        if xf.src_ranges != xf.tgt_ranges:\n          # take into account automatic stashing of overlapping blocks\n          if xf.src_ranges.overlaps(xf.tgt_ranges):\n            temp_stash_usage = stashed_blocks + xf.src_ranges.size()\n            if temp_stash_usage > max_stashed_blocks:\n              max_stashed_blocks = temp_stash_usage\n\n          self.touched_src_ranges = self.touched_src_ranges.union(\n              xf.src_ranges)\n\n          out.append(\"%s %s %s %s\\n\" % (\n              xf.style,\n              xf.tgt_sha1,\n              xf.tgt_ranges.to_string_raw(), src_str))\n          total += tgt_size\n      elif xf.style in (\"bsdiff\", \"imgdiff\"):\n        assert xf.tgt_ranges\n        assert xf.src_ranges\n        # take into account automatic stashing of overlapping blocks\n        if xf.src_ranges.overlaps(xf.tgt_ranges):\n          temp_stash_usage = stashed_blocks + xf.src_ranges.size()\n          if temp_stash_usage > max_stashed_blocks:\n            max_stashed_blocks = temp_stash_usage\n\n        self.touched_src_ranges = self.touched_src_ranges.union(xf.src_ranges)\n\n        out.append(\"%s %d %d %s %s %s %s\\n\" % (\n            xf.style,\n            xf.patch_start, xf.patch_len,\n            xf.src_sha1,\n            xf.tgt_sha1,\n            xf.tgt_ranges.to_string_raw(), src_str))\n        total += tgt_size\n      elif xf.style == \"zero\":\n        assert xf.tgt_ranges\n        to_zero = xf.tgt_ranges.subtract(xf.src_ranges)\n        assert WriteSplitTransfers(out, xf.style, to_zero) == to_zero.size()\n        total += to_zero.size()\n      else:\n        raise ValueError(\"unknown transfer style '%s'\\n\" % xf.style)\n\n      if free_string:\n        out.append(\"\".join(free_string))\n        stashed_blocks -= free_size\n\n      if common.OPTIONS.cache_size is not None:\n        # Validation check: abort if we're going to need more stash space than\n        # the allowed size (cache_size * threshold). There are two purposes\n        # of having a threshold here. a) Part of the cache may have been\n        # occupied by some recovery logs. b) It will buy us some time to deal\n        # with the oversize issue.\n        cache_size = common.OPTIONS.cache_size\n        stash_threshold = common.OPTIONS.stash_threshold\n        max_allowed = cache_size * stash_threshold\n        assert max_stashed_blocks * self.tgt.blocksize <= max_allowed, \\\n               'Stash size %d (%d * %d) exceeds the limit %d (%d * %.2f)' % (\n                   max_stashed_blocks * self.tgt.blocksize, max_stashed_blocks,\n                   self.tgt.blocksize, max_allowed, cache_size,\n                   stash_threshold)\n\n    self.touched_src_sha1 = self.src.RangeSha1(self.touched_src_ranges)\n\n    # Zero out extended blocks as a workaround for bug 20881595.\n    if self.tgt.extended:\n      assert (WriteSplitTransfers(out, \"zero\", self.tgt.extended) ==\n              self.tgt.extended.size())\n      total += self.tgt.extended.size()\n\n    # We erase all the blocks on the partition that a) don't contain useful\n    # data in the new image; b) will not be touched by dm-verity. Out of those\n    # blocks, we erase the ones that won't be used in this update at the\n    # beginning of an update. The rest would be erased at the end. This is to\n    # work around the eMMC issue observed on some devices, which may otherwise\n    # get starving for clean blocks and thus fail the update. (b/28347095)\n    all_tgt = RangeSet(data=(0, self.tgt.total_blocks))\n    all_tgt_minus_extended = all_tgt.subtract(self.tgt.extended)\n    new_dontcare = all_tgt_minus_extended.subtract(self.tgt.care_map)\n\n    erase_first = new_dontcare.subtract(self.touched_src_ranges)\n    if erase_first:\n      out.insert(0, \"erase %s\\n\" % (erase_first.to_string_raw(),))\n\n    erase_last = new_dontcare.subtract(erase_first)\n    if erase_last:\n      out.append(\"erase %s\\n\" % (erase_last.to_string_raw(),))\n\n    out.insert(0, \"%d\\n\" % (self.version,))   # format version number\n    out.insert(1, \"%d\\n\" % (total,))\n    # v3+: the number of stash slots is unused.\n    out.insert(2, \"0\\n\")\n    out.insert(3, str(max_stashed_blocks) + \"\\n\")\n\n    with open(prefix + \".transfer.list\", \"w\") as f:\n      for i in out:\n        f.write(i)\n\n    self._max_stashed_size = max_stashed_blocks * self.tgt.blocksize\n    OPTIONS = common.OPTIONS\n    if OPTIONS.cache_size is not None:\n      max_allowed = OPTIONS.cache_size * OPTIONS.stash_threshold\n      logger.info(\n          \"max stashed blocks: %d  (%d bytes), limit: %d bytes (%.2f%%)\\n\",\n          max_stashed_blocks, self._max_stashed_size, max_allowed,\n          self._max_stashed_size * 100.0 / max_allowed)\n    else:\n      logger.info(\n          \"max stashed blocks: %d  (%d bytes), limit: <unknown>\\n\",\n          max_stashed_blocks, self._max_stashed_size)\n\n  def ReviseStashSize(self, ignore_stash_limit=False):\n    \"\"\" Revises the transfers to keep the stash size within the size limit.\n\n    Iterates through the transfer list and calculates the stash size each\n    transfer generates. Converts the affected transfers to new if we reach the\n    stash limit.\n\n    Args:\n      ignore_stash_limit: Ignores the stash limit and calculates the max\n      simultaneous stashed blocks instead. No change will be made to the\n      transfer list with this flag.\n\n    Return:\n      A tuple of (tgt blocks converted to new, max stashed blocks)\n    \"\"\"\n    logger.info(\"Revising stash size...\")\n    stash_map = {}\n\n    # Create the map between a stash and its def/use points. For example, for a\n    # given stash of (raw_id, sr), stash_map[raw_id] = (sr, def_cmd, use_cmd).\n    for xf in self.transfers:\n      # Command xf defines (stores) all the stashes in stash_before.\n      for stash_raw_id, sr in xf.stash_before:\n        stash_map[stash_raw_id] = (sr, xf)\n\n      # Record all the stashes command xf uses.\n      for stash_raw_id, _ in xf.use_stash:\n        stash_map[stash_raw_id] += (xf,)\n\n    max_allowed_blocks = None\n    if not ignore_stash_limit:\n      # Compute the maximum blocks available for stash based on /cache size and\n      # the threshold.\n      cache_size = common.OPTIONS.cache_size\n      stash_threshold = common.OPTIONS.stash_threshold\n      max_allowed_blocks = cache_size * stash_threshold / self.tgt.blocksize\n\n    # See the comments for 'stashes' in WriteTransfers().\n    stashes = {}\n    stashed_blocks = 0\n    new_blocks = 0\n    max_stashed_blocks = 0\n\n    # Now go through all the commands. Compute the required stash size on the\n    # fly. If a command requires excess stash than available, it deletes the\n    # stash by replacing the command that uses the stash with a \"new\" command\n    # instead.\n    for xf in self.transfers:\n      replaced_cmds = []\n\n      # xf.stash_before generates explicit stash commands.\n      for stash_raw_id, sr in xf.stash_before:\n        # Check the post-command stashed_blocks.\n        stashed_blocks_after = stashed_blocks\n        sh = self.src.RangeSha1(sr)\n        if sh not in stashes:\n          stashed_blocks_after += sr.size()\n\n        if max_allowed_blocks and stashed_blocks_after > max_allowed_blocks:\n          # We cannot stash this one for a later command. Find out the command\n          # that will use this stash and replace the command with \"new\".\n          use_cmd = stash_map[stash_raw_id][2]\n          replaced_cmds.append(use_cmd)\n          logger.info(\"%10d  %9s  %s\", sr.size(), \"explicit\", use_cmd)\n        else:\n          # Update the stashes map.\n          if sh in stashes:\n            stashes[sh] += 1\n          else:\n            stashes[sh] = 1\n          stashed_blocks = stashed_blocks_after\n          max_stashed_blocks = max(max_stashed_blocks, stashed_blocks)\n\n      # \"move\" and \"diff\" may introduce implicit stashes in BBOTA v3. Prior to\n      # ComputePatches(), they both have the style of \"diff\".\n      if xf.style == \"diff\":\n        assert xf.tgt_ranges and xf.src_ranges\n        if xf.src_ranges.overlaps(xf.tgt_ranges):\n          if (max_allowed_blocks and\n              stashed_blocks + xf.src_ranges.size() > max_allowed_blocks):\n            replaced_cmds.append(xf)\n            logger.info(\"%10d  %9s  %s\", xf.src_ranges.size(), \"implicit\", xf)\n          else:\n            # The whole source ranges will be stashed for implicit stashes.\n            max_stashed_blocks = max(max_stashed_blocks,\n                                     stashed_blocks + xf.src_ranges.size())\n\n      # Replace the commands in replaced_cmds with \"new\"s.\n      for cmd in replaced_cmds:\n        # It no longer uses any commands in \"use_stash\". Remove the def points\n        # for all those stashes.\n        for stash_raw_id, sr in cmd.use_stash:\n          def_cmd = stash_map[stash_raw_id][1]\n          assert (stash_raw_id, sr) in def_cmd.stash_before\n          def_cmd.stash_before.remove((stash_raw_id, sr))\n\n        # Add up blocks that violates space limit and print total number to\n        # screen later.\n        new_blocks += cmd.tgt_ranges.size()\n        cmd.ConvertToNew()\n\n      # xf.use_stash may generate free commands.\n      for _, sr in xf.use_stash:\n        sh = self.src.RangeSha1(sr)\n        assert sh in stashes\n        stashes[sh] -= 1\n        if stashes[sh] == 0:\n          stashed_blocks -= sr.size()\n          stashes.pop(sh)\n\n    num_of_bytes = new_blocks * self.tgt.blocksize\n    logger.info(\n        \"  Total %d blocks (%d bytes) are packed as new blocks due to \"\n        \"insufficient cache size. Maximum blocks stashed simultaneously: %d\",\n        new_blocks, num_of_bytes, max_stashed_blocks)\n    return new_blocks, max_stashed_blocks\n\n  def ComputePatches(self, prefix):\n    logger.info(\"Reticulating splines...\")\n    diff_queue = []\n    patch_num = 0\n    with open(prefix + \".new.dat\", \"wb\") as new_f:\n      for index, xf in enumerate(self.transfers):\n        if xf.style == \"zero\":\n          tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize\n          logger.info(\n              \"%10d %10d (%6.2f%%) %7s %s %s\", tgt_size, tgt_size, 100.0,\n              xf.style, xf.tgt_name, str(xf.tgt_ranges))\n\n        elif xf.style == \"new\":\n          self.tgt.WriteRangeDataToFd(xf.tgt_ranges, new_f)\n          tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize\n          logger.info(\n              \"%10d %10d (%6.2f%%) %7s %s %s\", tgt_size, tgt_size, 100.0,\n              xf.style, xf.tgt_name, str(xf.tgt_ranges))\n\n        elif xf.style == \"diff\":\n          # We can't compare src and tgt directly because they may have\n          # the same content but be broken up into blocks differently, eg:\n          #\n          #    [\"he\", \"llo\"]  vs  [\"h\", \"ello\"]\n          #\n          # We want those to compare equal, ideally without having to\n          # actually concatenate the strings (these may be tens of\n          # megabytes).\n          if xf.src_sha1 == xf.tgt_sha1:\n            # These are identical; we don't need to generate a patch,\n            # just issue copy commands on the device.\n            xf.style = \"move\"\n            xf.patch_info = None\n            tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize\n            if xf.src_ranges != xf.tgt_ranges:\n              logger.info(\n                  \"%10d %10d (%6.2f%%) %7s %s %s (from %s)\", tgt_size, tgt_size,\n                  100.0, xf.style,\n                  xf.tgt_name if xf.tgt_name == xf.src_name else (\n                      xf.tgt_name + \" (from \" + xf.src_name + \")\"),\n                  str(xf.tgt_ranges), str(xf.src_ranges))\n          else:\n            if xf.patch_info:\n              # We have already generated the patch (e.g. during split of large\n              # APKs or reduction of stash size)\n              imgdiff = xf.patch_info.imgdiff\n            else:\n              imgdiff = self.CanUseImgdiff(\n                  xf.tgt_name, xf.tgt_ranges, xf.src_ranges)\n            xf.style = \"imgdiff\" if imgdiff else \"bsdiff\"\n            diff_queue.append((index, imgdiff, patch_num))\n            patch_num += 1\n\n        else:\n          assert False, \"unknown style \" + xf.style\n\n    patches = self.ComputePatchesForInputList(diff_queue, False)\n\n    offset = 0\n    with open(prefix + \".patch.dat\", \"wb\") as patch_fd:\n      for index, patch_info, _ in patches:\n        xf = self.transfers[index]\n        xf.patch_len = len(patch_info.content)\n        xf.patch_start = offset\n        offset += xf.patch_len\n        patch_fd.write(patch_info.content)\n\n        tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize\n        logger.info(\n            \"%10d %10d (%6.2f%%) %7s %s %s %s\", xf.patch_len, tgt_size,\n            xf.patch_len * 100.0 / tgt_size, xf.style,\n            xf.tgt_name if xf.tgt_name == xf.src_name else (\n                xf.tgt_name + \" (from \" + xf.src_name + \")\"),\n            xf.tgt_ranges, xf.src_ranges)\n\n  def AssertSha1Good(self):\n    \"\"\"Check the SHA-1 of the src & tgt blocks in the transfer list.\n\n    Double check the SHA-1 value to avoid the issue in b/71908713, where\n    SparseImage.RangeSha1() messed up with the hash calculation in multi-thread\n    environment. That specific problem has been fixed by protecting the\n    underlying generator function 'SparseImage._GetRangeData()' with lock.\n    \"\"\"\n    for xf in self.transfers:\n      tgt_sha1 = self.tgt.RangeSha1(xf.tgt_ranges)\n      assert xf.tgt_sha1 == tgt_sha1\n      if xf.style == \"diff\":\n        src_sha1 = self.src.RangeSha1(xf.src_ranges)\n        assert xf.src_sha1 == src_sha1\n\n  def AssertSequenceGood(self):\n    # Simulate the sequences of transfers we will output, and check that:\n    # - we never read a block after writing it, and\n    # - we write every block we care about exactly once.\n\n    # Start with no blocks having been touched yet.\n    touched = array.array(\"B\", b\"\\0\" * self.tgt.total_blocks)\n\n    # Imagine processing the transfers in order.\n    for xf in self.transfers:\n      # Check that the input blocks for this transfer haven't yet been touched.\n\n      x = xf.src_ranges\n      for _, sr in xf.use_stash:\n        x = x.subtract(sr)\n\n      for s, e in x:\n        # Source image could be larger. Don't check the blocks that are in the\n        # source image only. Since they are not in 'touched', and won't ever\n        # be touched.\n        for i in range(s, min(e, self.tgt.total_blocks)):\n          assert touched[i] == 0\n\n      # Check that the output blocks for this transfer haven't yet\n      # been touched, and touch all the blocks written by this\n      # transfer.\n      for s, e in xf.tgt_ranges:\n        for i in range(s, e):\n          assert touched[i] == 0\n          touched[i] = 1\n\n    # Check that we've written every target block.\n    for s, e in self.tgt.care_map:\n      for i in range(s, e):\n        assert touched[i] == 1\n\n  def FindSequenceForTransfers(self):\n    \"\"\"Finds a sequence for the given transfers.\n\n     The goal is to minimize the violation of order dependencies between these\n     transfers, so that fewer blocks are stashed when applying the update.\n    \"\"\"\n\n    # Clear the existing dependency between transfers\n    for xf in self.transfers:\n      xf.goes_before = OrderedDict()\n      xf.goes_after = OrderedDict()\n\n      xf.stash_before = []\n      xf.use_stash = []\n\n    # Find the ordering dependencies among transfers (this is O(n^2)\n    # in the number of transfers).\n    self.GenerateDigraph()\n    # Find a sequence of transfers that satisfies as many ordering\n    # dependencies as possible (heuristically).\n    self.FindVertexSequence()\n    # Fix up the ordering dependencies that the sequence didn't\n    # satisfy.\n    self.ReverseBackwardEdges()\n    self.ImproveVertexSequence()\n\n  def ImproveVertexSequence(self):\n    logger.info(\"Improving vertex order...\")\n\n    # At this point our digraph is acyclic; we reversed any edges that\n    # were backwards in the heuristically-generated sequence.  The\n    # previously-generated order is still acceptable, but we hope to\n    # find a better order that needs less memory for stashed data.\n    # Now we do a topological sort to generate a new vertex order,\n    # using a greedy algorithm to choose which vertex goes next\n    # whenever we have a choice.\n\n    # Make a copy of the edge set; this copy will get destroyed by the\n    # algorithm.\n    for xf in self.transfers:\n      xf.incoming = xf.goes_after.copy()\n      xf.outgoing = xf.goes_before.copy()\n\n    L = []   # the new vertex order\n\n    # S is the set of sources in the remaining graph; we always choose\n    # the one that leaves the least amount of stashed data after it's\n    # executed.\n    S = [(u.NetStashChange(), u.order, u) for u in self.transfers\n         if not u.incoming]\n    heapq.heapify(S)\n\n    while S:\n      _, _, xf = heapq.heappop(S)\n      L.append(xf)\n      for u in xf.outgoing:\n        del u.incoming[xf]\n        if not u.incoming:\n          heapq.heappush(S, (u.NetStashChange(), u.order, u))\n\n    # if this fails then our graph had a cycle.\n    assert len(L) == len(self.transfers)\n\n    self.transfers = L\n    for i, xf in enumerate(L):\n      xf.order = i\n\n  def ReverseBackwardEdges(self):\n    \"\"\"Reverse unsatisfying edges and compute pairs of stashed blocks.\n\n    For each transfer, make sure it properly stashes the blocks it touches and\n    will be used by later transfers. It uses pairs of (stash_raw_id, range) to\n    record the blocks to be stashed. 'stash_raw_id' is an id that uniquely\n    identifies each pair. Note that for the same range (e.g. RangeSet(\"1-5\")),\n    it is possible to have multiple pairs with different 'stash_raw_id's. Each\n    'stash_raw_id' will be consumed by one transfer. In BBOTA v3+, identical\n    blocks will be written to the same stash slot in WriteTransfers().\n    \"\"\"\n\n    logger.info(\"Reversing backward edges...\")\n    in_order = 0\n    out_of_order = 0\n    stash_raw_id = 0\n    stash_size = 0\n\n    for xf in self.transfers:\n      for u in xf.goes_before.copy():\n        # xf should go before u\n        if xf.order < u.order:\n          # it does, hurray!\n          in_order += 1\n        else:\n          # it doesn't, boo.  modify u to stash the blocks that it\n          # writes that xf wants to read, and then require u to go\n          # before xf.\n          out_of_order += 1\n\n          overlap = xf.src_ranges.intersect(u.tgt_ranges)\n          assert overlap\n\n          u.stash_before.append((stash_raw_id, overlap))\n          xf.use_stash.append((stash_raw_id, overlap))\n          stash_raw_id += 1\n          stash_size += overlap.size()\n\n          # reverse the edge direction; now xf must go after u\n          del xf.goes_before[u]\n          del u.goes_after[xf]\n          xf.goes_after[u] = None    # value doesn't matter\n          u.goes_before[xf] = None\n\n    logger.info(\n        \"  %d/%d dependencies (%.2f%%) were violated; %d source blocks \"\n        \"stashed.\", out_of_order, in_order + out_of_order,\n        (out_of_order * 100.0 / (in_order + out_of_order)) if (\n            in_order + out_of_order) else 0.0,\n        stash_size)\n\n  def FindVertexSequence(self):\n    logger.info(\"Finding vertex sequence...\")\n\n    # This is based on \"A Fast & Effective Heuristic for the Feedback\n    # Arc Set Problem\" by P. Eades, X. Lin, and W.F. Smyth.  Think of\n    # it as starting with the digraph G and moving all the vertices to\n    # be on a horizontal line in some order, trying to minimize the\n    # number of edges that end up pointing to the left.  Left-pointing\n    # edges will get removed to turn the digraph into a DAG.  In this\n    # case each edge has a weight which is the number of source blocks\n    # we'll lose if that edge is removed; we try to minimize the total\n    # weight rather than just the number of edges.\n\n    # Make a copy of the edge set; this copy will get destroyed by the\n    # algorithm.\n    for xf in self.transfers:\n      xf.incoming = xf.goes_after.copy()\n      xf.outgoing = xf.goes_before.copy()\n      xf.score = sum(xf.outgoing.values()) - sum(xf.incoming.values())\n\n    # We use an OrderedDict instead of just a set so that the output\n    # is repeatable; otherwise it would depend on the hash values of\n    # the transfer objects.\n    G = OrderedDict()\n    for xf in self.transfers:\n      G[xf] = None\n    s1 = deque()  # the left side of the sequence, built from left to right\n    s2 = deque()  # the right side of the sequence, built from right to left\n\n    heap = []\n    for xf in self.transfers:\n      xf.heap_item = HeapItem(xf)\n      heap.append(xf.heap_item)\n    heapq.heapify(heap)\n\n    # Use OrderedDict() instead of set() to preserve the insertion order. Need\n    # to use 'sinks[key] = None' to add key into the set. sinks will look like\n    # { key1: None, key2: None, ... }.\n    sinks = OrderedDict.fromkeys(u for u in G if not u.outgoing)\n    sources = OrderedDict.fromkeys(u for u in G if not u.incoming)\n\n    def adjust_score(iu, delta):\n      iu.score += delta\n      iu.heap_item.clear()\n      iu.heap_item = HeapItem(iu)\n      heapq.heappush(heap, iu.heap_item)\n\n    while G:\n      # Put all sinks at the end of the sequence.\n      while sinks:\n        new_sinks = OrderedDict()\n        for u in sinks:\n          if u not in G:\n            continue\n          s2.appendleft(u)\n          del G[u]\n          for iu in u.incoming:\n            adjust_score(iu, -iu.outgoing.pop(u))\n            if not iu.outgoing:\n              new_sinks[iu] = None\n        sinks = new_sinks\n\n      # Put all the sources at the beginning of the sequence.\n      while sources:\n        new_sources = OrderedDict()\n        for u in sources:\n          if u not in G:\n            continue\n          s1.append(u)\n          del G[u]\n          for iu in u.outgoing:\n            adjust_score(iu, +iu.incoming.pop(u))\n            if not iu.incoming:\n              new_sources[iu] = None\n        sources = new_sources\n\n      if not G:\n        break\n\n      # Find the \"best\" vertex to put next.  \"Best\" is the one that\n      # maximizes the net difference in source blocks saved we get by\n      # pretending it's a source rather than a sink.\n\n      while True:\n        u = heapq.heappop(heap)\n        if u and u.item in G:\n          u = u.item\n          break\n\n      s1.append(u)\n      del G[u]\n      for iu in u.outgoing:\n        adjust_score(iu, +iu.incoming.pop(u))\n        if not iu.incoming:\n          sources[iu] = None\n\n      for iu in u.incoming:\n        adjust_score(iu, -iu.outgoing.pop(u))\n        if not iu.outgoing:\n          sinks[iu] = None\n\n    # Now record the sequence in the 'order' field of each transfer,\n    # and by rearranging self.transfers to be in the chosen sequence.\n\n    new_transfers = []\n    for x in itertools.chain(s1, s2):\n      x.order = len(new_transfers)\n      new_transfers.append(x)\n      del x.incoming\n      del x.outgoing\n\n    self.transfers = new_transfers\n\n  def GenerateDigraph(self):\n    logger.info(\"Generating digraph...\")\n\n    # Each item of source_ranges will be:\n    #   - None, if that block is not used as a source,\n    #   - an ordered set of transfers.\n    source_ranges = []\n    for b in self.transfers:\n      for s, e in b.src_ranges:\n        if e > len(source_ranges):\n          source_ranges.extend([None] * (e-len(source_ranges)))\n        for i in range(s, e):\n          if source_ranges[i] is None:\n            source_ranges[i] = OrderedDict.fromkeys([b])\n          else:\n            source_ranges[i][b] = None\n\n    for a in self.transfers:\n      intersections = OrderedDict()\n      for s, e in a.tgt_ranges:\n        for i in range(s, e):\n          if i >= len(source_ranges):\n            break\n          # Add all the Transfers in source_ranges[i] to the (ordered) set.\n          if source_ranges[i] is not None:\n            for j in source_ranges[i]:\n              intersections[j] = None\n\n      for b in intersections:\n        if a is b:\n          continue\n\n        # If the blocks written by A are read by B, then B needs to go before A.\n        i = a.tgt_ranges.intersect(b.src_ranges)\n        if i:\n          if b.src_name == \"__ZERO\":\n            # the cost of removing source blocks for the __ZERO domain\n            # is (nearly) zero.\n            size = 0\n          else:\n            size = i.size()\n          b.goes_before[a] = size\n          a.goes_after[b] = size\n\n  def ComputePatchesForInputList(self, diff_queue, compress_target):\n    \"\"\"Returns a list of patch information for the input list of transfers.\n\n      Args:\n        diff_queue: a list of transfers with style 'diff'\n        compress_target: If True, compresses the target ranges of each\n            transfers; and save the size.\n\n      Returns:\n        A list of (transfer order, patch_info, compressed_size) tuples.\n    \"\"\"\n\n    if not diff_queue:\n      return []\n\n    if self.threads > 1:\n      logger.info(\"Computing patches (using %d threads)...\", self.threads)\n    else:\n      logger.info(\"Computing patches...\")\n\n    diff_total = len(diff_queue)\n    patches = [None] * diff_total\n    error_messages = []\n\n    # Using multiprocessing doesn't give additional benefits, due to the\n    # pattern of the code. The diffing work is done by subprocess.call, which\n    # already runs in a separate process (not affected much by the GIL -\n    # Global Interpreter Lock). Using multiprocess also requires either a)\n    # writing the diff input files in the main process before forking, or b)\n    # reopening the image file (SparseImage) in the worker processes. Doing\n    # neither of them further improves the performance.\n    lock = threading.Lock()\n\n    def diff_worker():\n      while True:\n        with lock:\n          if not diff_queue:\n            return\n          xf_index, imgdiff, patch_index = diff_queue.pop()\n          xf = self.transfers[xf_index]\n\n        message = []\n        compressed_size = None\n\n        patch_info = xf.patch_info\n        if not patch_info:\n          src_file = common.MakeTempFile(prefix=\"src-\")\n          with open(src_file, \"wb\") as fd:\n            self.src.WriteRangeDataToFd(xf.src_ranges, fd)\n\n          tgt_file = common.MakeTempFile(prefix=\"tgt-\")\n          with open(tgt_file, \"wb\") as fd:\n            self.tgt.WriteRangeDataToFd(xf.tgt_ranges, fd)\n\n          try:\n            patch_info = compute_patch(src_file, tgt_file, imgdiff)\n          except ValueError as e:\n            message.append(\n                \"Failed to generate %s for %s: tgt=%s, src=%s:\\n%s\" % (\n                    \"imgdiff\" if imgdiff else \"bsdiff\",\n                    xf.tgt_name if xf.tgt_name == xf.src_name else\n                    xf.tgt_name + \" (from \" + xf.src_name + \")\",\n                    xf.tgt_ranges, xf.src_ranges, e.message))\n\n        if compress_target:\n          tgt_data = self.tgt.ReadRangeSet(xf.tgt_ranges)\n          try:\n            # Compresses with the default level\n            compress_obj = zlib.compressobj(6, zlib.DEFLATED, -zlib.MAX_WBITS)\n            compressed_data = (compress_obj.compress(b\"\".join(tgt_data))\n                               + compress_obj.flush())\n            compressed_size = len(compressed_data)\n          except zlib.error as e:\n            message.append(\n                \"Failed to compress the data in target range {} for {}:\\n\"\n                \"{}\".format(xf.tgt_ranges, xf.tgt_name, e.message))\n\n        if message:\n          with lock:\n            error_messages.extend(message)\n\n        with lock:\n          patches[patch_index] = (xf_index, patch_info, compressed_size)\n\n    threads = [threading.Thread(target=diff_worker)\n               for _ in range(self.threads)]\n    for th in threads:\n      th.start()\n    while threads:\n      threads.pop().join()\n\n    if error_messages:\n      logger.error('ERROR:')\n      logger.error('\\n'.join(error_messages))\n      logger.error('\\n\\n\\n')\n      sys.exit(1)\n\n    return patches\n\n  def SelectAndConvertDiffTransfersToNew(self, violated_stash_blocks):\n    \"\"\"Converts the diff transfers to reduce the max simultaneous stash.\n\n    Since the 'new' data is compressed with deflate, we can select the 'diff'\n    transfers for conversion by comparing its patch size with the size of the\n    compressed data. Ideally, we want to convert the transfers with a small\n    size increase, but using a large number of stashed blocks.\n    \"\"\"\n    TransferSizeScore = namedtuple(\"TransferSizeScore\",\n                                   \"xf, used_stash_blocks, score\")\n\n    logger.info(\"Selecting diff commands to convert to new.\")\n    diff_queue = []\n    for xf in self.transfers:\n      if xf.style == \"diff\" and xf.src_sha1 != xf.tgt_sha1:\n        use_imgdiff = self.CanUseImgdiff(xf.tgt_name, xf.tgt_ranges,\n                                         xf.src_ranges)\n        diff_queue.append((xf.order, use_imgdiff, len(diff_queue)))\n\n    # Remove the 'move' transfers, and compute the patch & compressed size\n    # for the remaining.\n    result = self.ComputePatchesForInputList(diff_queue, True)\n\n    conversion_candidates = []\n    for xf_index, patch_info, compressed_size in result:\n      xf = self.transfers[xf_index]\n      if not xf.patch_info:\n        xf.patch_info = patch_info\n\n      size_ratio = len(xf.patch_info.content) * 100.0 / compressed_size\n      diff_style = \"imgdiff\" if xf.patch_info.imgdiff else \"bsdiff\"\n      logger.info(\"%s, target size: %d blocks, style: %s, patch size: %d,\"\n                  \" compression_size: %d, ratio %.2f%%\", xf.tgt_name,\n                  xf.tgt_ranges.size(), diff_style,\n                  len(xf.patch_info.content), compressed_size, size_ratio)\n\n      used_stash_blocks = sum(sr.size() for _, sr in xf.use_stash)\n      # Convert the transfer to new if the compressed size is smaller or equal.\n      # We don't need to maintain the stash_before lists here because the\n      # graph will be regenerated later.\n      if len(xf.patch_info.content) >= compressed_size:\n        # Add the transfer to the candidate list with negative score. And it\n        # will be converted later.\n        conversion_candidates.append(TransferSizeScore(xf, used_stash_blocks,\n                                                       -1))\n      elif used_stash_blocks > 0:\n        # This heuristic represents the size increase in the final package to\n        # remove per unit of stashed data.\n        score = ((compressed_size - len(xf.patch_info.content)) * 100.0\n                 / used_stash_blocks)\n        conversion_candidates.append(TransferSizeScore(xf, used_stash_blocks,\n                                                       score))\n    # Transfers with lower score (i.e. less expensive to convert) will be\n    # converted first.\n    conversion_candidates.sort(key=lambda x: x.score)\n\n    # TODO(xunchang), improve the logic to find the transfers to convert, e.g.\n    # convert the ones that contribute to the max stash, run ReviseStashSize\n    # multiple times etc.\n    removed_stashed_blocks = 0\n    for xf, used_stash_blocks, _ in conversion_candidates:\n      logger.info(\"Converting %s to new\", xf.tgt_name)\n      xf.ConvertToNew()\n      removed_stashed_blocks += used_stash_blocks\n      # Experiments show that we will get a smaller package size if we remove\n      # slightly more stashed blocks than the violated stash blocks.\n      if removed_stashed_blocks >= violated_stash_blocks:\n        break\n\n    logger.info(\"Removed %d stashed blocks\", removed_stashed_blocks)\n\n  def FindTransfers(self):\n    \"\"\"Parse the file_map to generate all the transfers.\"\"\"\n\n    def AddSplitTransfersWithFixedSizeChunks(tgt_name, src_name, tgt_ranges,\n                                             src_ranges, style, by_id):\n      \"\"\"Add one or multiple Transfer()s by splitting large files.\n\n      For BBOTA v3, we need to stash source blocks for resumable feature.\n      However, with the growth of file size and the shrink of the cache\n      partition source blocks are too large to be stashed. If a file occupies\n      too many blocks, we split it into smaller pieces by getting multiple\n      Transfer()s.\n\n      The downside is that after splitting, we may increase the package size\n      since the split pieces don't align well. According to our experiments,\n      1/8 of the cache size as the per-piece limit appears to be optimal.\n      Compared to the fixed 1024-block limit, it reduces the overall package\n      size by 30% for volantis, and 20% for angler and bullhead.\"\"\"\n\n      pieces = 0\n      while (tgt_ranges.size() > max_blocks_per_transfer and\n             src_ranges.size() > max_blocks_per_transfer):\n        tgt_split_name = \"%s-%d\" % (tgt_name, pieces)\n        src_split_name = \"%s-%d\" % (src_name, pieces)\n        tgt_first = tgt_ranges.first(max_blocks_per_transfer)\n        src_first = src_ranges.first(max_blocks_per_transfer)\n\n        Transfer(tgt_split_name, src_split_name, tgt_first, src_first,\n                 self.tgt.RangeSha1(tgt_first), self.src.RangeSha1(src_first),\n                 style, by_id)\n\n        tgt_ranges = tgt_ranges.subtract(tgt_first)\n        src_ranges = src_ranges.subtract(src_first)\n        pieces += 1\n\n      # Handle remaining blocks.\n      if tgt_ranges.size() or src_ranges.size():\n        # Must be both non-empty.\n        assert tgt_ranges.size() and src_ranges.size()\n        tgt_split_name = \"%s-%d\" % (tgt_name, pieces)\n        src_split_name = \"%s-%d\" % (src_name, pieces)\n        Transfer(tgt_split_name, src_split_name, tgt_ranges, src_ranges,\n                 self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),\n                 style, by_id)\n\n    def AddSplitTransfers(tgt_name, src_name, tgt_ranges, src_ranges, style,\n                          by_id):\n      \"\"\"Find all the zip files and split the others with a fixed chunk size.\n\n      This function will construct a list of zip archives, which will later be\n      split by imgdiff to reduce the final patch size. For the other files,\n      we will plainly split them based on a fixed chunk size with the potential\n      patch size penalty.\n      \"\"\"\n\n      assert style == \"diff\"\n\n      # Change nothing for small files.\n      if (tgt_ranges.size() <= max_blocks_per_transfer and\n          src_ranges.size() <= max_blocks_per_transfer):\n        Transfer(tgt_name, src_name, tgt_ranges, src_ranges,\n                 self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),\n                 style, by_id)\n        return\n\n      # Split large APKs with imgdiff, if possible. We're intentionally checking\n      # file types one more time (CanUseImgdiff() checks that as well), before\n      # calling the costly RangeSha1()s.\n      if (self.FileTypeSupportedByImgdiff(tgt_name) and\n          self.tgt.RangeSha1(tgt_ranges) != self.src.RangeSha1(src_ranges)):\n        if self.CanUseImgdiff(tgt_name, tgt_ranges, src_ranges, True):\n          large_apks.append((tgt_name, src_name, tgt_ranges, src_ranges))\n          return\n\n      AddSplitTransfersWithFixedSizeChunks(tgt_name, src_name, tgt_ranges,\n                                           src_ranges, style, by_id)\n\n    def AddTransfer(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id,\n                    split=False):\n      \"\"\"Wrapper function for adding a Transfer().\"\"\"\n\n      # We specialize diff transfers only (which covers bsdiff/imgdiff/move);\n      # otherwise add the Transfer() as is.\n      if style != \"diff\" or not split:\n        Transfer(tgt_name, src_name, tgt_ranges, src_ranges,\n                 self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),\n                 style, by_id)\n        return\n\n      # Handle .odex files specially to analyze the block-wise difference. If\n      # most of the blocks are identical with only few changes (e.g. header),\n      # we will patch the changed blocks only. This avoids stashing unchanged\n      # blocks while patching. We limit the analysis to files without size\n      # changes only. This is to avoid sacrificing the OTA generation cost too\n      # much.\n      if (tgt_name.split(\".\")[-1].lower() == 'odex' and\n          tgt_ranges.size() == src_ranges.size()):\n\n        # 0.5 threshold can be further tuned. The tradeoff is: if only very\n        # few blocks remain identical, we lose the opportunity to use imgdiff\n        # that may have better compression ratio than bsdiff.\n        crop_threshold = 0.5\n\n        tgt_skipped = RangeSet()\n        src_skipped = RangeSet()\n        tgt_size = tgt_ranges.size()\n        tgt_changed = 0\n        for src_block, tgt_block in zip(src_ranges.next_item(),\n                                        tgt_ranges.next_item()):\n          src_rs = RangeSet(str(src_block))\n          tgt_rs = RangeSet(str(tgt_block))\n          if self.src.ReadRangeSet(src_rs) == self.tgt.ReadRangeSet(tgt_rs):\n            tgt_skipped = tgt_skipped.union(tgt_rs)\n            src_skipped = src_skipped.union(src_rs)\n          else:\n            tgt_changed += tgt_rs.size()\n\n          # Terminate early if no clear sign of benefits.\n          if tgt_changed > tgt_size * crop_threshold:\n            break\n\n        if tgt_changed < tgt_size * crop_threshold:\n          assert tgt_changed + tgt_skipped.size() == tgt_size\n          logger.info(\n              '%10d %10d (%6.2f%%) %s', tgt_skipped.size(), tgt_size,\n              tgt_skipped.size() * 100.0 / tgt_size, tgt_name)\n          AddSplitTransfers(\n              \"%s-skipped\" % (tgt_name,),\n              \"%s-skipped\" % (src_name,),\n              tgt_skipped, src_skipped, style, by_id)\n\n          # Intentionally change the file extension to avoid being imgdiff'd as\n          # the files are no longer in their original format.\n          tgt_name = \"%s-cropped\" % (tgt_name,)\n          src_name = \"%s-cropped\" % (src_name,)\n          tgt_ranges = tgt_ranges.subtract(tgt_skipped)\n          src_ranges = src_ranges.subtract(src_skipped)\n\n          # Possibly having no changed blocks.\n          if not tgt_ranges:\n            return\n\n      # Add the transfer(s).\n      AddSplitTransfers(\n          tgt_name, src_name, tgt_ranges, src_ranges, style, by_id)\n\n    def ParseAndValidateSplitInfo(patch_size, tgt_ranges, src_ranges,\n                                  split_info):\n      \"\"\"Parse the split_info and return a list of info tuples.\n\n      Args:\n        patch_size: total size of the patch file.\n        tgt_ranges: Ranges of the target file within the original image.\n        src_ranges: Ranges of the source file within the original image.\n        split_info format:\n          imgdiff version#\n          count of pieces\n          <patch_size_1> <tgt_size_1> <src_ranges_1>\n          ...\n          <patch_size_n> <tgt_size_n> <src_ranges_n>\n\n      Returns:\n        [patch_start, patch_len, split_tgt_ranges, split_src_ranges]\n      \"\"\"\n\n      version = int(split_info[0])\n      assert version == 2\n      count = int(split_info[1])\n      assert len(split_info) - 2 == count\n\n      split_info_list = []\n      patch_start = 0\n      tgt_remain = copy.deepcopy(tgt_ranges)\n      # each line has the format <patch_size>, <tgt_size>, <src_ranges>\n      for line in split_info[2:]:\n        info = line.split()\n        assert len(info) == 3\n        patch_length = int(info[0])\n\n        split_tgt_size = int(info[1])\n        assert split_tgt_size % 4096 == 0\n        assert split_tgt_size // 4096 <= tgt_remain.size()\n        split_tgt_ranges = tgt_remain.first(split_tgt_size // 4096)\n        tgt_remain = tgt_remain.subtract(split_tgt_ranges)\n\n        # Find the split_src_ranges within the image file from its relative\n        # position in file.\n        split_src_indices = RangeSet.parse_raw(info[2])\n        split_src_ranges = RangeSet()\n        for r in split_src_indices:\n          curr_range = src_ranges.first(r[1]).subtract(src_ranges.first(r[0]))\n          assert not split_src_ranges.overlaps(curr_range)\n          split_src_ranges = split_src_ranges.union(curr_range)\n\n        split_info_list.append((patch_start, patch_length,\n                                split_tgt_ranges, split_src_ranges))\n        patch_start += patch_length\n\n      # Check that the sizes of all the split pieces add up to the final file\n      # size for patch and target.\n      assert tgt_remain.size() == 0\n      assert patch_start == patch_size\n      return split_info_list\n\n    def SplitLargeApks():\n      \"\"\"Split the large apks files.\n\n      Example: Chrome.apk will be split into\n        src-0: Chrome.apk-0, tgt-0: Chrome.apk-0\n        src-1: Chrome.apk-1, tgt-1: Chrome.apk-1\n        ...\n\n      After the split, the target pieces are continuous and block aligned; and\n      the source pieces are mutually exclusive. During the split, we also\n      generate and save the image patch between src-X & tgt-X. This patch will\n      be valid because the block ranges of src-X & tgt-X will always stay the\n      same afterwards; but there's a chance we don't use the patch if we\n      convert the \"diff\" command into \"new\" or \"move\" later.\n      \"\"\"\n\n      while True:\n        with transfer_lock:\n          if not large_apks:\n            return\n          tgt_name, src_name, tgt_ranges, src_ranges = large_apks.pop(0)\n\n        src_file = common.MakeTempFile(prefix=\"src-\")\n        tgt_file = common.MakeTempFile(prefix=\"tgt-\")\n        with open(src_file, \"wb\") as src_fd:\n          self.src.WriteRangeDataToFd(src_ranges, src_fd)\n        with open(tgt_file, \"wb\") as tgt_fd:\n          self.tgt.WriteRangeDataToFd(tgt_ranges, tgt_fd)\n\n        patch_file = common.MakeTempFile(prefix=\"patch-\")\n        patch_info_file = common.MakeTempFile(prefix=\"split_info-\")\n        cmd = [\"imgdiff\", \"-z\",\n               \"--block-limit={}\".format(max_blocks_per_transfer),\n               \"--split-info=\" + patch_info_file,\n               src_file, tgt_file, patch_file]\n        proc = common.Run(cmd)\n        imgdiff_output, _ = proc.communicate()\n        assert proc.returncode == 0, \\\n            \"Failed to create imgdiff patch between {} and {}:\\n{}\".format(\n                src_name, tgt_name, imgdiff_output)\n\n        with open(patch_info_file) as patch_info:\n          lines = patch_info.readlines()\n\n        patch_size_total = os.path.getsize(patch_file)\n        split_info_list = ParseAndValidateSplitInfo(patch_size_total,\n                                                    tgt_ranges, src_ranges,\n                                                    lines)\n        for index, (patch_start, patch_length, split_tgt_ranges,\n                    split_src_ranges) in enumerate(split_info_list):\n          with open(patch_file, 'rb') as f:\n            f.seek(patch_start)\n            patch_content = f.read(patch_length)\n\n          split_src_name = \"{}-{}\".format(src_name, index)\n          split_tgt_name = \"{}-{}\".format(tgt_name, index)\n          split_large_apks.append((split_tgt_name,\n                                   split_src_name,\n                                   split_tgt_ranges,\n                                   split_src_ranges,\n                                   patch_content))\n\n    logger.info(\"Finding transfers...\")\n\n    large_apks = []\n    split_large_apks = []\n    cache_size = common.OPTIONS.cache_size\n    split_threshold = 0.125\n    assert cache_size is not None\n    max_blocks_per_transfer = int(cache_size * split_threshold /\n                                  self.tgt.blocksize)\n    empty = RangeSet()\n    for tgt_fn, tgt_ranges in sorted(self.tgt.file_map.items()):\n      if tgt_fn == \"__ZERO\":\n        # the special \"__ZERO\" domain is all the blocks not contained\n        # in any file and that are filled with zeros.  We have a\n        # special transfer style for zero blocks.\n        src_ranges = self.src.file_map.get(\"__ZERO\", empty)\n        AddTransfer(tgt_fn, \"__ZERO\", tgt_ranges, src_ranges,\n                    \"zero\", self.transfers)\n        continue\n\n      elif tgt_fn == \"__COPY\":\n        # \"__COPY\" domain includes all the blocks not contained in any\n        # file and that need to be copied unconditionally to the target.\n        AddTransfer(tgt_fn, None, tgt_ranges, empty, \"new\", self.transfers)\n        continue\n\n      elif tgt_fn == \"__HASHTREE\":\n        continue\n\n      elif tgt_fn in self.src.file_map:\n        # Look for an exact pathname match in the source.\n        AddTransfer(tgt_fn, tgt_fn, tgt_ranges, self.src.file_map[tgt_fn],\n                    \"diff\", self.transfers, True)\n        continue\n\n      b = os.path.basename(tgt_fn)\n      if b in self.src_basenames:\n        # Look for an exact basename match in the source.\n        src_fn = self.src_basenames[b]\n        AddTransfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],\n                    \"diff\", self.transfers, True)\n        continue\n\n      b = re.sub(\"[0-9]+\", \"#\", b)\n      if b in self.src_numpatterns:\n        # Look for a 'number pattern' match (a basename match after\n        # all runs of digits are replaced by \"#\").  (This is useful\n        # for .so files that contain version numbers in the filename\n        # that get bumped.)\n        src_fn = self.src_numpatterns[b]\n        AddTransfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],\n                    \"diff\", self.transfers, True)\n        continue\n\n      AddTransfer(tgt_fn, None, tgt_ranges, empty, \"new\", self.transfers)\n\n    transfer_lock = threading.Lock()\n    threads = [threading.Thread(target=SplitLargeApks)\n               for _ in range(self.threads)]\n    for th in threads:\n      th.start()\n    while threads:\n      threads.pop().join()\n\n    # Sort the split transfers for large apks to generate a determinate package.\n    split_large_apks.sort()\n    for (tgt_name, src_name, tgt_ranges, src_ranges,\n         patch) in split_large_apks:\n      transfer_split = Transfer(tgt_name, src_name, tgt_ranges, src_ranges,\n                                self.tgt.RangeSha1(tgt_ranges),\n                                self.src.RangeSha1(src_ranges),\n                                \"diff\", self.transfers)\n      transfer_split.patch_info = PatchInfo(True, patch)\n\n  def AbbreviateSourceNames(self):\n    for k in self.src.file_map.keys():\n      b = os.path.basename(k)\n      self.src_basenames[b] = k\n      b = re.sub(\"[0-9]+\", \"#\", b)\n      self.src_numpatterns[b] = k\n\n  @staticmethod\n  def AssertPartition(total, seq):\n    \"\"\"Assert that all the RangeSets in 'seq' form a partition of the\n    'total' RangeSet (ie, they are nonintersecting and their union\n    equals 'total').\"\"\"\n\n    so_far = RangeSet()\n    for i in seq:\n      assert not so_far.overlaps(i)\n      so_far = so_far.union(i)\n    assert so_far == total\n"
  },
  {
    "path": "tools/releasetools/build_image.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2011 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nBuilds output_image from the given input_directory, properties_file,\nand writes the image to target_output_directory.\n\nUsage:  build_image input_directory properties_file output_image \\\\\n            target_output_directory\n\"\"\"\n\nimport datetime\n\nimport argparse\nimport glob\nimport logging\nimport os\nimport os.path\nimport re\nimport shlex\nimport shutil\nimport sys\nimport uuid\nimport tempfile\n\nimport common\nimport verity_utils\n\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\nBLOCK_SIZE = common.BLOCK_SIZE\nBYTES_IN_MB = 1024 * 1024\n\n# Use a fixed timestamp (01/01/2009 00:00:00 UTC) for files when packaging\n# images. (b/24377993, b/80600931)\nFIXED_FILE_TIMESTAMP = int((\n    datetime.datetime(2009, 1, 1, 0, 0, 0, 0, datetime.UTC) -\n    datetime.datetime.fromtimestamp(0, datetime.UTC)).total_seconds())\n\n\nclass BuildImageError(Exception):\n  \"\"\"An Exception raised during image building.\"\"\"\n\n  def __init__(self, message):\n    Exception.__init__(self, message)\n\n\ndef GetDiskUsage(path):\n  \"\"\"Returns the number of bytes that \"path\" occupies on host.\n\n  Args:\n    path: The directory or file to calculate size on.\n\n  Returns:\n    The number of bytes based on a 1K block_size.\n  \"\"\"\n  cmd = [\"du\", \"-b\", \"-k\", \"-s\", path]\n  output = common.RunAndCheckOutput(cmd, verbose=False)\n  return int(output.split()[0]) * 1024\n\n\ndef GetInodeUsage(path):\n  \"\"\"Returns the number of inodes that \"path\" occupies on host.\n\n  Args:\n    path: The directory or file to calculate inode number on.\n\n  Returns:\n    The number of inodes used.\n  \"\"\"\n  cmd = [\"find\", path, \"-print\"]\n  output = common.RunAndCheckOutput(cmd, verbose=False)\n  # increase by > 6% as number of files and directories is not whole picture.\n  inodes = output.count('\\n')\n  spare_inodes = inodes * 6 // 100\n  min_spare_inodes = 12\n  if spare_inodes < min_spare_inodes:\n    spare_inodes = min_spare_inodes\n  return inodes + spare_inodes\n\n\ndef GetFilesystemCharacteristics(fs_type, image_path, sparse_image=True):\n  \"\"\"Returns various filesystem characteristics of \"image_path\".\n\n  Args:\n    image_path: The file to analyze.\n    sparse_image: Image is sparse\n\n  Returns:\n    The characteristics dictionary.\n  \"\"\"\n  unsparse_image_path = image_path\n  if sparse_image:\n    unsparse_image_path = UnsparseImage(image_path, replace=False)\n\n  if fs_type.startswith(\"ext\"):\n    cmd = [\"tune2fs\", \"-l\", unsparse_image_path]\n  elif fs_type.startswith(\"f2fs\"):\n    cmd = [\"fsck.f2fs\", \"-l\", unsparse_image_path]\n\n  try:\n    output = common.RunAndCheckOutput(cmd, verbose=False)\n  finally:\n    if sparse_image:\n      os.remove(unsparse_image_path)\n  fs_dict = {}\n  for line in output.splitlines():\n    fields = line.split(\":\")\n    if len(fields) == 2:\n      fs_dict[fields[0].strip()] = fields[1].strip()\n  return fs_dict\n\n\ndef UnsparseImage(sparse_image_path, replace=True):\n  img_dir = os.path.dirname(sparse_image_path)\n  unsparse_image_path = \"unsparse_\" + os.path.basename(sparse_image_path)\n  unsparse_image_path = os.path.join(img_dir, unsparse_image_path)\n  if os.path.exists(unsparse_image_path):\n    if replace:\n      os.unlink(unsparse_image_path)\n    else:\n      return unsparse_image_path\n  inflate_command = [\"simg2img\", sparse_image_path, unsparse_image_path]\n  try:\n    common.RunAndCheckOutput(inflate_command)\n  except:\n    os.remove(unsparse_image_path)\n    raise\n  return unsparse_image_path\n\n\ndef ConvertBlockMapToBaseFs(block_map_file):\n  base_fs_file = common.MakeTempFile(prefix=\"script_gen_\", suffix=\".base_fs\")\n  convert_command = [\"blk_alloc_to_base_fs\", block_map_file, base_fs_file]\n  common.RunAndCheckOutput(convert_command)\n  return base_fs_file\n\n\ndef SetUpInDirAndFsConfig(origin_in, prop_dict):\n  \"\"\"Returns the in_dir and fs_config that should be used for image building.\n\n  When building system.img for all targets, it creates and returns a staged dir\n  that combines the contents of /system (i.e. in the given in_dir) and root.\n\n  Args:\n    origin_in: Path to the input directory.\n    prop_dict: A property dict that contains info like partition size. Values\n        may be updated.\n\n  Returns:\n    A tuple of in_dir and fs_config that should be used to build the image.\n  \"\"\"\n  fs_config = prop_dict.get(\"fs_config\")\n\n  if prop_dict[\"mount_point\"] == \"system_other\":\n    prop_dict[\"mount_point\"] = \"system\"\n    return origin_in, fs_config\n\n  if prop_dict[\"mount_point\"] != \"system\":\n    return origin_in, fs_config\n\n  if \"first_pass\" in prop_dict:\n    prop_dict[\"mount_point\"] = \"/\"\n    return prop_dict[\"first_pass\"]\n\n  # Construct a staging directory of the root file system.\n  in_dir = common.MakeTempDir()\n  root_dir = prop_dict.get(\"root_dir\")\n  if root_dir:\n    shutil.rmtree(in_dir)\n    shutil.copytree(root_dir, in_dir, symlinks=True)\n  in_dir_system = os.path.join(in_dir, \"system\")\n  shutil.rmtree(in_dir_system, ignore_errors=True)\n  shutil.copytree(origin_in, in_dir_system, symlinks=True)\n\n  # Change the mount point to \"/\".\n  prop_dict[\"mount_point\"] = \"/\"\n  if fs_config:\n    # We need to merge the fs_config files of system and root.\n    merged_fs_config = common.MakeTempFile(\n        prefix=\"merged_fs_config\", suffix=\".txt\")\n    with open(merged_fs_config, \"w\") as fw:\n      if \"root_fs_config\" in prop_dict:\n        with open(prop_dict[\"root_fs_config\"]) as fr:\n          fw.writelines(fr.readlines())\n      with open(fs_config) as fr:\n        fw.writelines(fr.readlines())\n    fs_config = merged_fs_config\n  prop_dict[\"first_pass\"] = (in_dir, fs_config)\n  return in_dir, fs_config\n\n\ndef CheckHeadroom(ext4fs_output, prop_dict):\n  \"\"\"Checks if there's enough headroom space available.\n\n  Headroom is the reserved space on system image (via PRODUCT_SYSTEM_HEADROOM),\n  which is useful for devices with low disk space that have system image\n  variation between builds. The 'partition_headroom' in prop_dict is the size\n  in bytes, while the numbers in 'ext4fs_output' are for 4K-blocks.\n\n  Args:\n    ext4fs_output: The output string from mke2fs command.\n    prop_dict: The property dict.\n\n  Raises:\n    AssertionError: On invalid input.\n    BuildImageError: On check failure.\n  \"\"\"\n  assert ext4fs_output is not None\n  assert prop_dict.get('fs_type', '').startswith('ext4')\n  assert 'partition_headroom' in prop_dict\n  assert 'mount_point' in prop_dict\n\n  ext4fs_stats = re.compile(\n      r'Created filesystem with .* (?P<used_blocks>[0-9]+)/'\n      r'(?P<total_blocks>[0-9]+) blocks')\n  last_line = ext4fs_output.strip().split('\\n')[-1]\n  m = ext4fs_stats.match(last_line)\n  used_blocks = int(m.groupdict().get('used_blocks'))\n  total_blocks = int(m.groupdict().get('total_blocks'))\n  headroom_blocks = int(prop_dict['partition_headroom']) // BLOCK_SIZE\n  adjusted_blocks = total_blocks - headroom_blocks\n  if used_blocks > adjusted_blocks:\n    mount_point = prop_dict[\"mount_point\"]\n    raise BuildImageError(\n        \"Error: Not enough room on {} (total: {} blocks, used: {} blocks, \"\n        \"headroom: {} blocks, available: {} blocks)\".format(\n            mount_point, total_blocks, used_blocks, headroom_blocks,\n            adjusted_blocks))\n\n\ndef CalculateSizeAndReserved(prop_dict, size):\n  fs_type = prop_dict.get(\"fs_type\", \"\")\n  partition_headroom = int(prop_dict.get(\"partition_headroom\", 0))\n  # If not specified, give us 16MB margin for GetDiskUsage error ...\n  reserved_size = int(prop_dict.get(\n      \"partition_reserved_size\", BYTES_IN_MB * 16))\n\n  if fs_type == \"erofs\":\n    reserved_size = int(prop_dict.get(\"partition_reserved_size\", 0))\n    if reserved_size == 0:\n      # give .3% margin or a minimum size for AVB footer\n      return max(size * 1003 // 1000, 256 * 1024)\n\n  if fs_type.startswith(\"ext4\") and partition_headroom > reserved_size:\n    reserved_size = partition_headroom\n\n  return int(size * 1.1) + reserved_size\n\n\ndef BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config):\n  \"\"\"Builds a pure image for the files under in_dir and writes it to out_file.\n\n  Args:\n    in_dir: Path to input directory.\n    prop_dict: A property dict that contains info like partition size. Values\n        will be updated with computed values.\n    out_file: The output image file.\n    target_out: Path to the TARGET_OUT directory as in Makefile. It actually\n        points to the /system directory under PRODUCT_OUT. fs_config (the one\n        under system/core/libcutils) reads device specific FS config files from\n        there.\n    fs_config: The fs_config file that drives the prototype\n\n  Raises:\n    BuildImageError: On build image failures.\n  \"\"\"\n  build_command = []\n  fs_type = prop_dict.get(\"fs_type\", \"\")\n  run_fsck = None\n  needs_projid = prop_dict.get(\"needs_projid\", 0)\n  needs_casefold = prop_dict.get(\"needs_casefold\", 0)\n  needs_compress = prop_dict.get(\"needs_compress\", 0)\n\n  disable_sparse = \"disable_sparse\" in prop_dict\n  manual_sparse = False\n\n  if fs_type.startswith(\"ext\"):\n    build_command = [prop_dict[\"ext_mkuserimg\"]]\n    if \"extfs_sparse_flag\" in prop_dict and not disable_sparse:\n      build_command.append(prop_dict[\"extfs_sparse_flag\"])\n      run_fsck = RunE2fsck\n    build_command.extend([in_dir, out_file, fs_type,\n                          prop_dict[\"mount_point\"]])\n    build_command.append(prop_dict[\"image_size\"])\n    if \"journal_size\" in prop_dict:\n      build_command.extend([\"-j\", prop_dict[\"journal_size\"]])\n    if \"timestamp\" in prop_dict:\n      build_command.extend([\"-T\", str(prop_dict[\"timestamp\"])])\n    if fs_config:\n      build_command.extend([\"-C\", fs_config])\n    if target_out:\n      build_command.extend([\"-D\", target_out])\n    if \"block_list\" in prop_dict:\n      build_command.extend([\"-B\", prop_dict[\"block_list\"]])\n    if \"base_fs_file\" in prop_dict:\n      base_fs_file = ConvertBlockMapToBaseFs(prop_dict[\"base_fs_file\"])\n      build_command.extend([\"-d\", base_fs_file])\n    build_command.extend([\"-L\", prop_dict[\"mount_point\"]])\n    if \"extfs_inode_count\" in prop_dict:\n      build_command.extend([\"-i\", prop_dict[\"extfs_inode_count\"]])\n    if \"extfs_rsv_pct\" in prop_dict:\n      build_command.extend([\"-M\", prop_dict[\"extfs_rsv_pct\"]])\n    if \"flash_erase_block_size\" in prop_dict:\n      build_command.extend([\"-e\", prop_dict[\"flash_erase_block_size\"]])\n    if \"flash_logical_block_size\" in prop_dict:\n      build_command.extend([\"-o\", prop_dict[\"flash_logical_block_size\"]])\n    # Specify UUID and hash_seed if using mke2fs.\n    if os.path.basename(prop_dict[\"ext_mkuserimg\"]) == \"mkuserimg_mke2fs\":\n      if \"uuid\" in prop_dict:\n        build_command.extend([\"-U\", prop_dict[\"uuid\"]])\n      if \"hash_seed\" in prop_dict:\n        build_command.extend([\"-S\", prop_dict[\"hash_seed\"]])\n    if prop_dict.get(\"ext4_share_dup_blocks\") == \"true\":\n      build_command.append(\"-c\")\n    if (needs_projid):\n      build_command.extend([\"--inode_size\", \"512\"])\n    else:\n      build_command.extend([\"--inode_size\", \"256\"])\n    if \"selinux_fc\" in prop_dict:\n      build_command.append(prop_dict[\"selinux_fc\"])\n  elif fs_type.startswith(\"erofs\"):\n    build_command = [\"mkfs.erofs\"]\n\n    compressor = None\n    if \"erofs_default_compressor\" in prop_dict:\n      compressor = prop_dict[\"erofs_default_compressor\"]\n    if \"erofs_compressor\" in prop_dict:\n      compressor = prop_dict[\"erofs_compressor\"]\n    if compressor and compressor != \"none\":\n      build_command.extend([\"-z\", compressor])\n\n    compress_hints = None\n    if \"erofs_default_compress_hints\" in prop_dict:\n      compress_hints = prop_dict[\"erofs_default_compress_hints\"]\n    if \"erofs_compress_hints\" in prop_dict:\n      compress_hints = prop_dict[\"erofs_compress_hints\"]\n    if compress_hints:\n      build_command.extend([\"--compress-hints\", compress_hints])\n\n    build_command.extend([\"-b\", prop_dict.get(\"erofs_blocksize\", \"4096\")])\n\n    build_command.extend([\"--mount-point\", prop_dict[\"mount_point\"]])\n    if target_out:\n      build_command.extend([\"--product-out\", target_out])\n    if fs_config:\n      build_command.extend([\"--fs-config-file\", fs_config])\n    if \"selinux_fc\" in prop_dict:\n      build_command.extend([\"--file-contexts\", prop_dict[\"selinux_fc\"]])\n    if \"timestamp\" in prop_dict:\n      build_command.extend([\"-T\", str(prop_dict[\"timestamp\"])])\n    if \"uuid\" in prop_dict:\n      build_command.extend([\"-U\", prop_dict[\"uuid\"]])\n    if \"block_list\" in prop_dict:\n      build_command.extend([\"--block-list-file\", prop_dict[\"block_list\"]])\n    if \"erofs_pcluster_size\" in prop_dict:\n      build_command.extend([\"-C\", prop_dict[\"erofs_pcluster_size\"]])\n    if \"erofs_share_dup_blocks\" in prop_dict:\n      build_command.extend([\"--chunksize\", \"4096\"])\n    if \"erofs_use_legacy_compression\" in prop_dict:\n      build_command.extend([\"-E\", \"legacy-compress\"])\n\n    build_command.extend([out_file, in_dir])\n    if \"erofs_sparse_flag\" in prop_dict and not disable_sparse:\n      manual_sparse = True\n\n    run_fsck = RunErofsFsck\n  elif fs_type.startswith(\"squash\"):\n    build_command = [\"mksquashfsimage\"]\n    build_command.extend([in_dir, out_file])\n    if \"squashfs_sparse_flag\" in prop_dict and not disable_sparse:\n      build_command.extend([prop_dict[\"squashfs_sparse_flag\"]])\n    build_command.extend([\"-m\", prop_dict[\"mount_point\"]])\n    if target_out:\n      build_command.extend([\"-d\", target_out])\n    if fs_config:\n      build_command.extend([\"-C\", fs_config])\n    if \"selinux_fc\" in prop_dict:\n      build_command.extend([\"-c\", prop_dict[\"selinux_fc\"]])\n    if \"block_list\" in prop_dict:\n      build_command.extend([\"-B\", prop_dict[\"block_list\"]])\n    if \"squashfs_block_size\" in prop_dict:\n      build_command.extend([\"-b\", prop_dict[\"squashfs_block_size\"]])\n    if \"squashfs_compressor\" in prop_dict:\n      build_command.extend([\"-z\", prop_dict[\"squashfs_compressor\"]])\n    if \"squashfs_compressor_opt\" in prop_dict:\n      build_command.extend([\"-zo\", prop_dict[\"squashfs_compressor_opt\"]])\n    if prop_dict.get(\"squashfs_disable_4k_align\") == \"true\":\n      build_command.extend([\"-a\"])\n  elif fs_type.startswith(\"f2fs\"):\n    build_command = [\"mkf2fsuserimg\"]\n    build_command.extend([out_file, prop_dict[\"image_size\"]])\n    if \"f2fs_sparse_flag\" in prop_dict and not disable_sparse:\n      build_command.extend([prop_dict[\"f2fs_sparse_flag\"]])\n    if fs_config:\n      build_command.extend([\"-C\", fs_config])\n    build_command.extend([\"-f\", in_dir])\n    if target_out:\n      build_command.extend([\"-D\", target_out])\n    if \"selinux_fc\" in prop_dict:\n      build_command.extend([\"-s\", prop_dict[\"selinux_fc\"]])\n    build_command.extend([\"-t\", prop_dict[\"mount_point\"]])\n    if \"timestamp\" in prop_dict:\n      build_command.extend([\"-T\", str(prop_dict[\"timestamp\"])])\n    if \"block_list\" in prop_dict:\n      build_command.extend([\"-B\", prop_dict[\"block_list\"]])\n    build_command.extend([\"-L\", prop_dict[\"mount_point\"]])\n    if (needs_projid):\n      build_command.append(\"--prjquota\")\n    if (needs_casefold):\n      build_command.append(\"--casefold\")\n    if (needs_compress or prop_dict.get(\"f2fs_compress\") == \"true\"):\n      build_command.append(\"--compression\")\n    if \"ro_mount_point\" in prop_dict:\n      build_command.append(\"--readonly\")\n    if (prop_dict.get(\"f2fs_compress\") == \"true\"):\n      build_command.append(\"--sldc\")\n      if (prop_dict.get(\"f2fs_sldc_flags\") == None):\n        build_command.append(str(0))\n      else:\n        sldc_flags_str = prop_dict.get(\"f2fs_sldc_flags\")\n        sldc_flags = sldc_flags_str.split()\n        build_command.append(str(len(sldc_flags)))\n        build_command.extend(sldc_flags)\n    f2fs_blocksize = prop_dict.get(\"f2fs_blocksize\", \"4096\")\n    build_command.extend([\"-b\", f2fs_blocksize])\n  else:\n    raise BuildImageError(\n        \"Error: unknown filesystem type: {}\".format(fs_type))\n\n  try:\n    mkfs_output = common.RunAndCheckOutput(build_command)\n  except:\n    try:\n      du = GetDiskUsage(in_dir)\n      du_str = \"{} bytes ({} MB)\".format(du, du // BYTES_IN_MB)\n    # Suppress any errors from GetDiskUsage() to avoid hiding the real errors\n    # from common.RunAndCheckOutput().\n    except Exception:  # pylint: disable=broad-except\n      logger.exception(\"Failed to compute disk usage with du\")\n      du_str = \"unknown\"\n    print(\n        \"Out of space? Out of inodes? The tree size of {} is {}, \"\n        \"with reserved space of {} bytes ({} MB).\".format(\n            in_dir, du_str,\n            int(prop_dict.get(\"partition_reserved_size\", 0)),\n            int(prop_dict.get(\"partition_reserved_size\", 0)) // BYTES_IN_MB))\n    if (\"image_size\" in prop_dict and \"partition_size\" in prop_dict):\n      print(\n          \"The max image size for filesystem files is {} bytes ({} MB), \"\n          \"out of a total partition size of {} bytes ({} MB).\".format(\n              int(prop_dict[\"image_size\"]),\n              int(prop_dict[\"image_size\"]) // BYTES_IN_MB,\n              int(prop_dict[\"partition_size\"]),\n              int(prop_dict[\"partition_size\"]) // BYTES_IN_MB))\n    raise\n\n  if run_fsck and prop_dict.get(\"skip_fsck\") != \"true\":\n    run_fsck(out_file)\n\n  if manual_sparse:\n    temp_file = out_file + \".sparse\"\n    img2simg_argv = [\"img2simg\", out_file, temp_file]\n    common.RunAndCheckOutput(img2simg_argv)\n    os.rename(temp_file, out_file)\n\n  return mkfs_output\n\n\ndef RunE2fsck(out_file):\n  unsparse_image = UnsparseImage(out_file, replace=False)\n\n  # Run e2fsck on the inflated image file\n  e2fsck_command = [\"e2fsck\", \"-f\", \"-n\", unsparse_image]\n  try:\n    common.RunAndCheckOutput(e2fsck_command)\n  finally:\n    os.remove(unsparse_image)\n\n\ndef RunErofsFsck(out_file):\n  fsck_command = [\"fsck.erofs\", \"--extract\", out_file]\n  try:\n    common.RunAndCheckOutput(fsck_command)\n  except:\n    print(\"Check failed for EROFS image {}\".format(out_file))\n    raise\n\n\ndef SetUUIDIfNotExist(image_props):\n\n  # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and\n  # build fingerprint). Also use the legacy build id, because the vbmeta digest\n  # isn't available at this point.\n  what = image_props[\"mount_point\"]\n  fingerprint = image_props.get(\"fingerprint\", \"\")\n  uuid_seed = what + \"-\" + fingerprint\n  logger.info(\"Using fingerprint %s for partition %s\", fingerprint, what)\n  image_props[\"uuid\"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))\n  hash_seed = \"hash_seed-\" + uuid_seed\n  image_props[\"hash_seed\"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))\n\n\ndef BuildImage(in_dir, prop_dict, out_file, target_out=None):\n  \"\"\"Builds an image for the files under in_dir and writes it to out_file.\n\n  Args:\n    in_dir: Path to input directory.\n    prop_dict: A property dict that contains info like partition size. Values\n        will be updated with computed values.\n    out_file: The output image file.\n    target_out: Path to the TARGET_OUT directory as in Makefile. It actually\n        points to the /system directory under PRODUCT_OUT. fs_config (the one\n        under system/core/libcutils) reads device specific FS config files from\n        there.\n\n  Raises:\n    BuildImageError: On build image failures.\n  \"\"\"\n  in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)\n  SetUUIDIfNotExist(prop_dict)\n\n  build_command = []\n  fs_type = prop_dict.get(\"fs_type\", \"\")\n\n  fs_spans_partition = True\n  if fs_type.startswith(\"squash\") or fs_type.startswith(\"erofs\"):\n    fs_spans_partition = False\n  elif fs_type.startswith(\"f2fs\") and prop_dict.get(\"f2fs_compress\") == \"true\":\n    fs_spans_partition = False\n\n  # Get a builder for creating an image that's to be verified by Verified Boot,\n  # or None if not applicable.\n  verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)\n\n  disable_sparse = \"disable_sparse\" in prop_dict\n  mkfs_output = None\n  if (prop_dict.get(\"use_dynamic_partition_size\") == \"true\" and\n          \"partition_size\" not in prop_dict):\n    # If partition_size is not defined, use output of `du' + reserved_size.\n    # For compressed file system, it's better to use the compressed size to avoid wasting space.\n    if fs_type.startswith(\"erofs\"):\n      mkfs_output = BuildImageMkfs(\n          in_dir, prop_dict, out_file, target_out, fs_config)\n      if \"erofs_sparse_flag\" in prop_dict and not disable_sparse:\n        image_path = UnsparseImage(out_file, replace=False)\n        size = GetDiskUsage(image_path)\n        os.remove(image_path)\n      else:\n        size = GetDiskUsage(out_file)\n    else:\n      size = GetDiskUsage(in_dir)\n    logger.info(\n        \"The tree size of %s is %d MB.\", in_dir, size // BYTES_IN_MB)\n    size = CalculateSizeAndReserved(prop_dict, size)\n    # Round this up to a multiple of 4K so that avbtool works\n    size = common.RoundUpTo4K(size)\n    if fs_type.startswith(\"ext\"):\n      prop_dict[\"partition_size\"] = str(size)\n      prop_dict[\"image_size\"] = str(size)\n      if \"extfs_inode_count\" not in prop_dict:\n        prop_dict[\"extfs_inode_count\"] = str(GetInodeUsage(in_dir))\n      logger.info(\n          \"First Pass based on estimates of %d MB and %s inodes.\",\n          size // BYTES_IN_MB, prop_dict[\"extfs_inode_count\"])\n      BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)\n      sparse_image = False\n      if \"extfs_sparse_flag\" in prop_dict and not disable_sparse:\n        sparse_image = True\n      fs_dict = GetFilesystemCharacteristics(fs_type, out_file, sparse_image)\n      os.remove(out_file)\n      block_size = int(fs_dict.get(\"Block size\", \"4096\"))\n      free_size = int(fs_dict.get(\"Free blocks\", \"0\")) * block_size\n      reserved_size = int(prop_dict.get(\"partition_reserved_size\", 0))\n      partition_headroom = int(fs_dict.get(\"partition_headroom\", 0))\n      if fs_type.startswith(\"ext4\") and partition_headroom > reserved_size:\n        reserved_size = partition_headroom\n      if free_size <= reserved_size:\n        logger.info(\n            \"Not worth reducing image %d <= %d.\", free_size, reserved_size)\n      else:\n        size -= free_size\n        size += reserved_size\n        if reserved_size == 0:\n          # add .3% margin\n          size = size * 1003 // 1000\n        # Use a minimum size, otherwise we will fail to calculate an AVB footer\n        # or fail to construct an ext4 image.\n        size = max(size, 256 * 1024)\n        if block_size <= 4096:\n          size = common.RoundUpTo4K(size)\n        else:\n          size = ((size + block_size - 1) // block_size) * block_size\n      extfs_inode_count = prop_dict[\"extfs_inode_count\"]\n      inodes = int(fs_dict.get(\"Inode count\", extfs_inode_count))\n      inodes -= int(fs_dict.get(\"Free inodes\", \"0\"))\n      # add .2% margin or 1 inode, whichever is greater\n      spare_inodes = inodes * 2 // 1000\n      min_spare_inodes = 1\n      if spare_inodes < min_spare_inodes:\n        spare_inodes = min_spare_inodes\n      inodes += spare_inodes\n      prop_dict[\"extfs_inode_count\"] = str(inodes)\n      prop_dict[\"partition_size\"] = str(size)\n      logger.info(\n          \"Allocating %d Inodes for %s.\", inodes, out_file)\n    elif fs_type.startswith(\"f2fs\") and prop_dict.get(\"f2fs_compress\") == \"true\":\n      prop_dict[\"partition_size\"] = str(size)\n      prop_dict[\"image_size\"] = str(size)\n      BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)\n      sparse_image = False\n      if \"f2fs_sparse_flag\" in prop_dict and not disable_sparse:\n        sparse_image = True\n      fs_dict = GetFilesystemCharacteristics(fs_type, out_file, sparse_image)\n      os.remove(out_file)\n      block_count = int(fs_dict.get(\"block_count\", \"0\"))\n      log_blocksize = int(fs_dict.get(\"log_blocksize\", \"12\"))\n      size = block_count << log_blocksize\n      prop_dict[\"partition_size\"] = str(size)\n    if verity_image_builder:\n      size = verity_image_builder.CalculateDynamicPartitionSize(size)\n    prop_dict[\"partition_size\"] = str(size)\n    logger.info(\n        \"Allocating %d MB for %s\", size // BYTES_IN_MB, out_file)\n\n  prop_dict[\"image_size\"] = prop_dict[\"partition_size\"]\n\n  # Adjust the image size to make room for the hashes if this is to be verified.\n  if verity_image_builder:\n    max_image_size = verity_image_builder.CalculateMaxImageSize()\n    prop_dict[\"image_size\"] = str(max_image_size)\n\n  if not mkfs_output:\n    mkfs_output = BuildImageMkfs(\n        in_dir, prop_dict, out_file, target_out, fs_config)\n\n  # Update the image (eg filesystem size). This can be different eg if mkfs\n  # rounds the requested size down due to alignment.\n  prop_dict[\"image_size\"] = common.sparse_img.GetImagePartitionSize(out_file)\n\n  # Check if there's enough headroom space available for ext4 image.\n  if \"partition_headroom\" in prop_dict and fs_type.startswith(\"ext4\"):\n    CheckHeadroom(mkfs_output, prop_dict)\n\n  if not fs_spans_partition and verity_image_builder:\n    verity_image_builder.PadSparseImage(out_file)\n\n  # Create the verified image if this is to be verified.\n  if verity_image_builder:\n    verity_image_builder.Build(out_file)\n\n\ndef TryParseFingerprint(glob_dict: dict):\n  for (key, val) in glob_dict.items():\n    if not key.endswith(\"_add_hashtree_footer_args\") and not key.endswith(\"_add_hash_footer_args\"):\n      continue\n    for arg in shlex.split(val):\n      m = re.match(r\"^com\\.android\\.build\\.\\w+\\.fingerprint:\", arg)\n      if m is None:\n        continue\n      fingerprint = arg[len(m.group()):]\n      glob_dict[\"fingerprint\"] = fingerprint\n      return\n\ndef TryParseFingerprintAndTimestamp(glob_dict):\n  \"\"\"Helper function that parses fingerprint and timestamp from the global dictionary.\n\n  Args:\n    glob_dict: the global dictionary from the build system.\n  \"\"\"\n  TryParseFingerprint(glob_dict)\n\n  # Set fixed timestamp for building the OTA package.\n  if \"use_fixed_timestamp\" in glob_dict:\n    glob_dict[\"timestamp\"] = FIXED_FILE_TIMESTAMP\n  if \"build.prop\" in glob_dict:\n    timestamp = glob_dict[\"build.prop\"].GetProp(\"ro.build.date.utc\")\n    if timestamp:\n      glob_dict[\"timestamp\"] = timestamp\n\ndef ImagePropFromGlobalDict(glob_dict, mount_point):\n  \"\"\"Build an image property dictionary from the global dictionary.\n\n  Args:\n    glob_dict: the global dictionary from the build system.\n    mount_point: such as \"system\", \"data\" etc.\n  \"\"\"\n  d = {}\n  TryParseFingerprintAndTimestamp(glob_dict)\n\n  def copy_prop(src_p, dest_p):\n    \"\"\"Copy a property from the global dictionary.\n\n    Args:\n      src_p: The source property in the global dictionary.\n      dest_p: The destination property.\n    Returns:\n      True if property was found and copied, False otherwise.\n    \"\"\"\n    if src_p in glob_dict:\n      d[dest_p] = str(glob_dict[src_p])\n      return True\n    return False\n\n  common_props = (\n      \"extfs_sparse_flag\",\n      \"erofs_default_compressor\",\n      \"erofs_default_compress_hints\",\n      \"erofs_pcluster_size\",\n      \"erofs_blocksize\",\n      \"erofs_share_dup_blocks\",\n      \"erofs_sparse_flag\",\n      \"erofs_use_legacy_compression\",\n      \"squashfs_sparse_flag\",\n      \"system_f2fs_compress\",\n      \"system_f2fs_sldc_flags\",\n      \"f2fs_sparse_flag\",\n      \"f2fs_blocksize\",\n      \"skip_fsck\",\n      \"ext_mkuserimg\",\n      \"avb_enable\",\n      \"avb_avbtool\",\n      \"use_dynamic_partition_size\",\n      \"fingerprint\",\n      \"timestamp\",\n  )\n  for p in common_props:\n    copy_prop(p, p)\n\n  ro_mount_points = set([\n      \"odm\",\n      \"odm_dlkm\",\n      \"oem\",\n      \"product\",\n      \"system\",\n      \"system_dlkm\",\n      \"system_ext\",\n      \"system_other\",\n      \"vendor\",\n      \"vendor_dlkm\",\n  ])\n\n  # Tuple layout: (readonly, specific prop, general prop)\n  fmt_props = (\n      # Generic first, then specific file type.\n      (False, \"fs_type\", \"fs_type\"),\n      (False, \"{}_fs_type\", \"fs_type\"),\n\n      # Ordering for these doesn't matter.\n      (False, \"{}_selinux_fc\", \"selinux_fc\"),\n      (False, \"{}_size\", \"partition_size\"),\n      (True, \"avb_{}_add_hashtree_footer_args\", \"avb_add_hashtree_footer_args\"),\n      (True, \"avb_{}_algorithm\", \"avb_algorithm\"),\n      (True, \"avb_{}_hashtree_enable\", \"avb_hashtree_enable\"),\n      (True, \"avb_{}_key_path\", \"avb_key_path\"),\n      (True, \"avb_{}_salt\", \"avb_salt\"),\n      (True, \"erofs_use_legacy_compression\", \"erofs_use_legacy_compression\"),\n      (True, \"ext4_share_dup_blocks\", \"ext4_share_dup_blocks\"),\n      (True, \"{}_base_fs_file\", \"base_fs_file\"),\n      (True, \"{}_disable_sparse\", \"disable_sparse\"),\n      (True, \"{}_erofs_compressor\", \"erofs_compressor\"),\n      (True, \"{}_erofs_compress_hints\", \"erofs_compress_hints\"),\n      (True, \"{}_erofs_pcluster_size\", \"erofs_pcluster_size\"),\n      (True, \"{}_erofs_blocksize\", \"erofs_blocksize\"),\n      (True, \"{}_erofs_share_dup_blocks\", \"erofs_share_dup_blocks\"),\n      (True, \"{}_extfs_inode_count\", \"extfs_inode_count\"),\n      (True, \"{}_f2fs_compress\", \"f2fs_compress\"),\n      (True, \"{}_f2fs_sldc_flags\", \"f2fs_sldc_flags\"),\n      (True, \"{}_f2fs_blocksize\", \"f2fs_block_size\"),\n      (True, \"{}_reserved_size\", \"partition_reserved_size\"),\n      (True, \"{}_squashfs_block_size\", \"squashfs_block_size\"),\n      (True, \"{}_squashfs_compressor\", \"squashfs_compressor\"),\n      (True, \"{}_squashfs_compressor_opt\", \"squashfs_compressor_opt\"),\n      (True, \"{}_squashfs_disable_4k_align\", \"squashfs_disable_4k_align\"),\n      (True, \"{}_verity_block_device\", \"verity_block_device\"),\n  )\n\n  # Translate prefixed properties into generic ones.\n  if mount_point == \"data\":\n    prefix = \"userdata\"\n  else:\n    prefix = mount_point\n\n  for readonly, src_prop, dest_prop in fmt_props:\n    if readonly and mount_point not in ro_mount_points:\n      continue\n\n    if src_prop == \"fs_type\":\n      # This property is legacy and only used on a few partitions. b/202600377\n      allowed_partitions = set([\"system\", \"system_other\", \"data\", \"oem\"])\n      if mount_point not in allowed_partitions:\n        continue\n\n    if (mount_point == \"system_other\") and (dest_prop != \"partition_size\"):\n      # Propagate system properties to system_other. They'll get overridden\n      # after as needed.\n      copy_prop(src_prop.format(\"system\"), dest_prop)\n\n    copy_prop(src_prop.format(prefix), dest_prop)\n\n  # Set prefixed properties that need a default value.\n  if mount_point in ro_mount_points:\n    prop = \"{}_journal_size\".format(prefix)\n    if not copy_prop(prop, \"journal_size\"):\n      d[\"journal_size\"] = \"0\"\n\n    prop = \"{}_extfs_rsv_pct\".format(prefix)\n    if not copy_prop(prop, \"extfs_rsv_pct\"):\n      d[\"extfs_rsv_pct\"] = \"0\"\n\n    d[\"ro_mount_point\"] = \"1\"\n\n  # Copy partition-specific properties.\n  d[\"mount_point\"] = mount_point\n  if mount_point == \"system\":\n    copy_prop(\"system_headroom\", \"partition_headroom\")\n    copy_prop(\"root_dir\", \"root_dir\")\n    copy_prop(\"root_fs_config\", \"root_fs_config\")\n  elif mount_point == \"data\":\n    # Copy the generic fs type first, override with specific one if available.\n    copy_prop(\"flash_logical_block_size\", \"flash_logical_block_size\")\n    copy_prop(\"flash_erase_block_size\", \"flash_erase_block_size\")\n    copy_prop(\"needs_casefold\", \"needs_casefold\")\n    copy_prop(\"needs_projid\", \"needs_projid\")\n    copy_prop(\"needs_compress\", \"needs_compress\")\n  d[\"partition_name\"] = mount_point\n  return d\n\n\ndef LoadGlobalDict(filename):\n  \"\"\"Load \"name=value\" pairs from filename\"\"\"\n  d = {}\n  f = open(filename)\n  for line in f:\n    line = line.strip()\n    if not line or line.startswith(\"#\"):\n      continue\n    k, v = line.split(\"=\", 1)\n    d[k] = v\n  f.close()\n  return d\n\n\ndef GlobalDictFromImageProp(image_prop, mount_point):\n  d = {}\n\n  def copy_prop(src_p, dest_p):\n    if src_p in image_prop:\n      d[dest_p] = image_prop[src_p]\n      return True\n    return False\n\n  if mount_point == \"system\":\n    copy_prop(\"partition_size\", \"system_size\")\n  elif mount_point == \"system_other\":\n    copy_prop(\"partition_size\", \"system_other_size\")\n  elif mount_point == \"vendor\":\n    copy_prop(\"partition_size\", \"vendor_size\")\n  elif mount_point == \"odm\":\n    copy_prop(\"partition_size\", \"odm_size\")\n  elif mount_point == \"vendor_dlkm\":\n    copy_prop(\"partition_size\", \"vendor_dlkm_size\")\n  elif mount_point == \"odm_dlkm\":\n    copy_prop(\"partition_size\", \"odm_dlkm_size\")\n  elif mount_point == \"system_dlkm\":\n    copy_prop(\"partition_size\", \"system_dlkm_size\")\n  elif mount_point == \"product\":\n    copy_prop(\"partition_size\", \"product_size\")\n  elif mount_point == \"system_ext\":\n    copy_prop(\"partition_size\", \"system_ext_size\")\n  return d\n\n\ndef BuildVBMeta(in_dir, glob_dict, output_path):\n  \"\"\"Creates a VBMeta image.\n\n  It generates the requested VBMeta image. The requested image could be for\n  top-level or chained VBMeta image, which is determined based on the name.\n\n  Args:\n    output_path: Path to generated vbmeta.img\n    partitions: A dict that's keyed by partition names with image paths as\n        values. Only valid partition names are accepted, as partitions listed\n        in common.AVB_PARTITIONS and custom partitions listed in\n        OPTIONS.info_dict.get(\"avb_custom_images_partition_list\")\n    name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.\n    needed_partitions: Partitions whose descriptors should be included into the\n        generated VBMeta image.\n\n  Returns:\n    Path to the created image.\n\n  Raises:\n    AssertionError: On invalid input args.\n  \"\"\"\n  vbmeta_partitions = common.AVB_PARTITIONS[:]\n  name = os.path.basename(output_path).rstrip(\".img\")\n  vbmeta_system = glob_dict.get(\"avb_vbmeta_system\", \"\").strip()\n  vbmeta_vendor = glob_dict.get(\"avb_vbmeta_vendor\", \"\").strip()\n  if \"vbmeta_system\" in name:\n    vbmeta_partitions = vbmeta_system.split()\n  elif \"vbmeta_vendor\" in name:\n    vbmeta_partitions = vbmeta_vendor.split()\n  else:\n    if vbmeta_system:\n      vbmeta_partitions = [\n          item for item in vbmeta_partitions\n          if item not in vbmeta_system.split()]\n      vbmeta_partitions.append(\"vbmeta_system\")\n\n    if vbmeta_vendor:\n      vbmeta_partitions = [\n          item for item in vbmeta_partitions\n          if item not in vbmeta_vendor.split()]\n      vbmeta_partitions.append(\"vbmeta_vendor\")\n\n  partitions = {part: os.path.join(in_dir, part + \".img\")\n                for part in vbmeta_partitions}\n  partitions = {part: path for (part, path) in partitions.items() if os.path.exists(path)}\n  common.BuildVBMeta(output_path, partitions, name, vbmeta_partitions)\n\n\ndef BuildImageOrVBMeta(input_directory, target_out, glob_dict, image_properties, out_file):\n  try:\n    if \"vbmeta\" in os.path.basename(out_file):\n      OPTIONS.info_dict = glob_dict\n      BuildVBMeta(input_directory, glob_dict, out_file)\n    else:\n      BuildImage(input_directory, image_properties, out_file, target_out)\n  except:\n    logger.error(\"Failed to build %s from %s\", out_file, input_directory)\n    raise\n\n\ndef CopyInputDirectory(src, dst, filter_file):\n  with open(filter_file, 'r') as f:\n    for line in f:\n      line = line.strip()\n      if not line:\n        return\n      if line != os.path.normpath(line):\n        sys.exit(f\"{line}: not normalized\")\n      if line.startswith(\"../\") or line.startswith('/'):\n        sys.exit(f\"{line}: escapes staging directory by starting with ../ or /\")\n      full_src = os.path.join(src, line)\n      full_dst = os.path.join(dst, line)\n      if os.path.isdir(full_src):\n        os.makedirs(full_dst, exist_ok=True)\n      else:\n        os.makedirs(os.path.dirname(full_dst), exist_ok=True)\n        os.link(full_src, full_dst, follow_symlinks=False)\n\n\ndef main(argv):\n  parser = argparse.ArgumentParser(\n    description=\"Builds output_image from the given input_directory and properties_file, and \"\n    \"writes the image to target_output_directory.\")\n  parser.add_argument(\"--input-directory-filter-file\",\n    help=\"the path to a file that contains a list of all files in the input_directory. If this \"\n    \"option is provided, all files under the input_directory that are not listed in this file will \"\n    \"be deleted before building the image. This is to work around the fact that building a module \"\n    \"will install in by default, so there could be files in the input_directory that are not \"\n    \"actually supposed to be part of the partition. The paths in this file must be relative to \"\n    \"input_directory.\")\n  parser.add_argument(\"input_directory\",\n    help=\"the staging directory to be converted to an image file\")\n  parser.add_argument(\"properties_file\",\n    help=\"a file containing the 'global dictionary' of properties that affect how the image is \"\n    \"built\")\n  parser.add_argument(\"out_file\",\n    help=\"the output file to write\")\n  parser.add_argument(\"target_out\",\n    help=\"the path to $(TARGET_OUT). Certain tools will use this to look through multiple staging \"\n    \"directories for fs config files.\")\n  parser.add_argument(\"-v\", action=\"store_true\",\n                      help=\"Enable verbose logging\", dest=\"verbose\")\n  args = parser.parse_args()\n  if args.verbose:\n    OPTIONS.verbose = True\n\n  common.InitLogging()\n\n  glob_dict = LoadGlobalDict(args.properties_file)\n  if \"mount_point\" in glob_dict:\n    # The caller knows the mount point and provides a dictionary needed by\n    # BuildImage().\n    image_properties = glob_dict\n    TryParseFingerprintAndTimestamp(image_properties)\n  else:\n    image_filename = os.path.basename(args.out_file)\n    mount_point = \"\"\n    if image_filename == \"system.img\":\n      mount_point = \"system\"\n    elif image_filename == \"system_other.img\":\n      mount_point = \"system_other\"\n    elif image_filename == \"userdata.img\":\n      mount_point = \"data\"\n    elif image_filename == \"cache.img\":\n      mount_point = \"cache\"\n    elif image_filename == \"vendor.img\":\n      mount_point = \"vendor\"\n    elif image_filename == \"odm.img\":\n      mount_point = \"odm\"\n    elif image_filename == \"vendor_dlkm.img\":\n      mount_point = \"vendor_dlkm\"\n    elif image_filename == \"odm_dlkm.img\":\n      mount_point = \"odm_dlkm\"\n    elif image_filename == \"system_dlkm.img\":\n      mount_point = \"system_dlkm\"\n    elif image_filename == \"oem.img\":\n      mount_point = \"oem\"\n    elif image_filename == \"product.img\":\n      mount_point = \"product\"\n    elif image_filename == \"system_ext.img\":\n      mount_point = \"system_ext\"\n    elif \"vbmeta\" in image_filename:\n      mount_point = \"vbmeta\"\n    else:\n      logger.error(\"Unknown image file name %s\", image_filename)\n      sys.exit(1)\n\n    if \"vbmeta\" != mount_point:\n      image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)\n\n  if args.input_directory_filter_file and not os.environ.get(\"BUILD_BROKEN_INCORRECT_PARTITION_IMAGES\"):\n    with tempfile.TemporaryDirectory(dir=os.path.dirname(args.input_directory)) as new_input_directory:\n      CopyInputDirectory(args.input_directory, new_input_directory, args.input_directory_filter_file)\n      BuildImageOrVBMeta(new_input_directory, args.target_out, glob_dict, image_properties, args.out_file)\n  else:\n    BuildImageOrVBMeta(args.input_directory, args.target_out, glob_dict, image_properties, args.out_file)\n\n\nif __name__ == '__main__':\n  try:\n    main(sys.argv[1:])\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/build_super_image.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nUsage: build_super_image input_file output_dir_or_file\n\ninput_file: one of the following:\n  - directory containing extracted target files. It will load info from\n    META/misc_info.txt and build full super image / split images using source\n    images from IMAGES/.\n  - target files package. Same as above, but extracts the archive before\n    building super image.\n  - a dictionary file containing input arguments to build. Check\n    `dump-super-image-info' for details.\n    In addition:\n    - If source images should be included in the output image (for super.img\n      and super split images), a list of \"*_image\" should be paths of each\n      source images.\n\noutput_dir_or_file:\n    If a single super image is built (for super_empty.img, or super.img for\n    launch devices), this argument is the output file.\n    If a collection of split images are built (for retrofit devices), this\n    argument is the output directory.\n\"\"\"\n\nfrom __future__ import print_function\n\nimport logging\nimport os.path\nimport shlex\nimport sys\nimport zipfile\n\nimport common\nimport sparse_img\n\nif sys.hexversion < 0x02070000:\n  print(\"Python 2.7 or newer is required.\", file=sys.stderr)\n  sys.exit(1)\n\nlogger = logging.getLogger(__name__)\n\n\nUNZIP_PATTERN = [\"IMAGES/*\", \"META/*\", \"*/build.prop\"]\n\n\ndef GetArgumentsForImage(partition, group, image=None):\n  image_size = sparse_img.GetImagePartitionSize(image) if image else 0\n\n  cmd = [\"--partition\",\n         \"{}:readonly:{}:{}\".format(partition, image_size, group)]\n  if image:\n    cmd += [\"--image\", \"{}={}\".format(partition, image)]\n\n  return cmd\n\n\ndef BuildSuperImageFromDict(info_dict, output):\n\n  cmd = [info_dict[\"lpmake\"],\n         \"--metadata-size\", \"65536\",\n         \"--super-name\", info_dict[\"super_metadata_device\"]]\n\n  ab_update = info_dict.get(\"ab_update\") == \"true\"\n  virtual_ab = info_dict.get(\"virtual_ab\") == \"true\"\n  virtual_ab_retrofit = info_dict.get(\"virtual_ab_retrofit\") == \"true\"\n  retrofit = info_dict.get(\"dynamic_partition_retrofit\") == \"true\"\n  block_devices = shlex.split(info_dict.get(\"super_block_devices\", \"\").strip())\n  groups = shlex.split(info_dict.get(\"super_partition_groups\", \"\").strip())\n\n  if ab_update and retrofit:\n    cmd += [\"--metadata-slots\", \"2\"]\n  elif ab_update:\n    cmd += [\"--metadata-slots\", \"3\"]\n  else:\n    cmd += [\"--metadata-slots\", \"2\"]\n\n  if ab_update and retrofit:\n    cmd.append(\"--auto-slot-suffixing\")\n  if virtual_ab and not virtual_ab_retrofit:\n    cmd.append(\"--virtual-ab\")\n\n  for device in block_devices:\n    size = info_dict[\"super_{}_device_size\".format(device)]\n    cmd += [\"--device\", \"{}:{}\".format(device, size)]\n\n  append_suffix = ab_update and not retrofit\n  has_image = False\n  for group in groups:\n    group_size = info_dict[\"super_{}_group_size\".format(group)]\n    if append_suffix:\n      cmd += [\"--group\", \"{}_a:{}\".format(group, group_size),\n              \"--group\", \"{}_b:{}\".format(group, group_size)]\n    else:\n      cmd += [\"--group\", \"{}:{}\".format(group, group_size)]\n\n    partition_list = shlex.split(\n        info_dict[\"super_{}_partition_list\".format(group)].strip())\n\n    for partition in partition_list:\n      image = info_dict.get(\"{}_image\".format(partition))\n      if image:\n        has_image = True\n\n      if not append_suffix:\n        cmd += GetArgumentsForImage(partition, group, image)\n        continue\n\n      # For A/B devices, super partition always contains sub-partitions in\n      # the _a slot, because this image should only be used for\n      # bootstrapping / initializing the device. When flashing the image,\n      # bootloader fastboot should always mark _a slot as bootable.\n      cmd += GetArgumentsForImage(partition + \"_a\", group + \"_a\", image)\n\n      other_image = None\n      if partition == \"system\" and \"system_other_image\" in info_dict:\n        other_image = info_dict[\"system_other_image\"]\n        has_image = True\n\n      cmd += GetArgumentsForImage(partition + \"_b\", group + \"_b\", other_image)\n\n  if info_dict.get(\"build_non_sparse_super_partition\") != \"true\":\n    cmd.append(\"--sparse\")\n\n  cmd += [\"--output\", output]\n\n  common.RunAndCheckOutput(cmd)\n\n  if retrofit and has_image:\n    logger.info(\"Done writing images to directory %s\", output)\n  else:\n    logger.info(\"Done writing image %s\", output)\n\n  return True\n\n\ndef BuildSuperImageFromExtractedTargetFiles(inp, out):\n  info_dict = common.LoadInfoDict(inp)\n  partition_list = shlex.split(\n      info_dict.get(\"dynamic_partition_list\", \"\").strip())\n\n  if \"system\" in partition_list:\n    image_path = os.path.join(inp, \"IMAGES\", \"system_other.img\")\n    if os.path.isfile(image_path):\n      info_dict[\"system_other_image\"] = image_path\n\n  missing_images = []\n  for partition in partition_list:\n    image_path = os.path.join(inp, \"IMAGES\", \"{}.img\".format(partition))\n    if not os.path.isfile(image_path):\n      missing_images.append(image_path)\n    else:\n      info_dict[\"{}_image\".format(partition)] = image_path\n  if missing_images:\n    logger.warning(\"Skip building super image because the following \"\n                   \"images are missing from target files:\\n%s\",\n                   \"\\n\".join(missing_images))\n    return False\n  return BuildSuperImageFromDict(info_dict, out)\n\n\ndef BuildSuperImageFromTargetFiles(inp, out):\n  input_tmp = common.UnzipTemp(inp, UNZIP_PATTERN)\n  return BuildSuperImageFromExtractedTargetFiles(input_tmp, out)\n\n\ndef BuildSuperImage(inp, out):\n\n  if isinstance(inp, dict):\n    logger.info(\"Building super image from info dict...\")\n    return BuildSuperImageFromDict(inp, out)\n\n  if isinstance(inp, str):\n    if os.path.isdir(inp):\n      logger.info(\"Building super image from extracted target files...\")\n      return BuildSuperImageFromExtractedTargetFiles(inp, out)\n\n    if zipfile.is_zipfile(inp):\n      logger.info(\"Building super image from target files...\")\n      return BuildSuperImageFromTargetFiles(inp, out)\n\n    if os.path.isfile(inp):\n      logger.info(\"Building super image from info dict...\")\n      return BuildSuperImageFromDict(common.LoadDictionaryFromFile(inp), out)\n\n  raise ValueError(\"{} is not a dictionary or a valid path\".format(inp))\n\n\ndef main(argv):\n\n  args = common.ParseOptions(argv, __doc__)\n\n  if len(args) != 2:\n    common.Usage(__doc__)\n    sys.exit(1)\n\n  common.InitLogging()\n\n  BuildSuperImage(args[0], args[1])\n\n\nif __name__ == \"__main__\":\n  try:\n    common.CloseInheritedPipes()\n    main(sys.argv[1:])\n  except common.ExternalError:\n    logger.exception(\"\\n   ERROR:\\n\")\n    sys.exit(1)\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/care_map_pb2.py",
    "content": "# -*- coding: utf-8 -*-\n# Generated by the protocol buffer compiler.  DO NOT EDIT!\n# source: bootable/recovery/update_verifier/care_map.proto\n\nimport sys\n_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import message as _message\nfrom google.protobuf import reflection as _reflection\nfrom google.protobuf import symbol_database as _symbol_database\n# @@protoc_insertion_point(imports)\n\n_sym_db = _symbol_database.Default()\n\n\n\n\nDESCRIPTOR = _descriptor.FileDescriptor(\n  name='bootable/recovery/update_verifier/care_map.proto',\n  package='recovery_update_verifier',\n  syntax='proto3',\n  serialized_options=_b('H\\003'),\n  serialized_pb=_b('\\n0bootable/recovery/update_verifier/care_map.proto\\x12\\x18recovery_update_verifier\\\"\\x9e\\x01\\n\\x07\\x43\\x61reMap\\x12\\x43\\n\\npartitions\\x18\\x01 \\x03(\\x0b\\x32/.recovery_update_verifier.CareMap.PartitionInfo\\x1aN\\n\\rPartitionInfo\\x12\\x0c\\n\\x04name\\x18\\x01 \\x01(\\t\\x12\\x0e\\n\\x06ranges\\x18\\x02 \\x01(\\t\\x12\\n\\n\\x02id\\x18\\x03 \\x01(\\t\\x12\\x13\\n\\x0b\\x66ingerprint\\x18\\x04 \\x01(\\tB\\x02H\\x03\\x62\\x06proto3')\n)\n\n\n\n\n_CAREMAP_PARTITIONINFO = _descriptor.Descriptor(\n  name='PartitionInfo',\n  full_name='recovery_update_verifier.CareMap.PartitionInfo',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='name', full_name='recovery_update_verifier.CareMap.PartitionInfo.name', index=0,\n      number=1, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='ranges', full_name='recovery_update_verifier.CareMap.PartitionInfo.ranges', index=1,\n      number=2, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='id', full_name='recovery_update_verifier.CareMap.PartitionInfo.id', index=2,\n      number=3, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='fingerprint', full_name='recovery_update_verifier.CareMap.PartitionInfo.fingerprint', index=3,\n      number=4, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto3',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=159,\n  serialized_end=237,\n)\n\n_CAREMAP = _descriptor.Descriptor(\n  name='CareMap',\n  full_name='recovery_update_verifier.CareMap',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='partitions', full_name='recovery_update_verifier.CareMap.partitions', index=0,\n      number=1, type=11, cpp_type=10, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[_CAREMAP_PARTITIONINFO, ],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto3',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=79,\n  serialized_end=237,\n)\n\n_CAREMAP_PARTITIONINFO.containing_type = _CAREMAP\n_CAREMAP.fields_by_name['partitions'].message_type = _CAREMAP_PARTITIONINFO\nDESCRIPTOR.message_types_by_name['CareMap'] = _CAREMAP\n_sym_db.RegisterFileDescriptor(DESCRIPTOR)\n\nCareMap = _reflection.GeneratedProtocolMessageType('CareMap', (_message.Message,), {\n\n  'PartitionInfo' : _reflection.GeneratedProtocolMessageType('PartitionInfo', (_message.Message,), {\n    'DESCRIPTOR' : _CAREMAP_PARTITIONINFO,\n    '__module__' : 'bootable.recovery.update_verifier.care_map_pb2'\n    # @@protoc_insertion_point(class_scope:recovery_update_verifier.CareMap.PartitionInfo)\n    })\n  ,\n  'DESCRIPTOR' : _CAREMAP,\n  '__module__' : 'bootable.recovery.update_verifier.care_map_pb2'\n  # @@protoc_insertion_point(class_scope:recovery_update_verifier.CareMap)\n  })\n_sym_db.RegisterMessage(CareMap)\n_sym_db.RegisterMessage(CareMap.PartitionInfo)\n\n\nDESCRIPTOR._options = None\n# @@protoc_insertion_point(module_scope)\n"
  },
  {
    "path": "tools/releasetools/check_ota_package_signature.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2016 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nVerify a given OTA package with the specifed certificate.\n\"\"\"\n\nfrom __future__ import print_function\n\nimport argparse\nimport logging\nimport re\nimport subprocess\nimport sys\nimport zipfile\nfrom hashlib import sha1\nfrom hashlib import sha256\n\nimport common\n\nlogger = logging.getLogger(__name__)\n\n\ndef CertUsesSha256(cert):\n  \"\"\"Check if the cert uses SHA-256 hashing algorithm.\"\"\"\n\n  cmd = ['openssl', 'x509', '-text', '-noout', '-in', cert]\n  cert_dump = common.RunAndCheckOutput(cmd, stdout=subprocess.PIPE)\n\n  algorithm = re.search(r'Signature Algorithm: ([a-zA-Z0-9]+)', cert_dump)\n  assert algorithm, \"Failed to identify the signature algorithm.\"\n\n  assert not algorithm.group(1).startswith('ecdsa'), (\n      'This script doesn\\'t support verifying ECDSA signed package yet.')\n\n  return algorithm.group(1).startswith('sha256')\n\n\ndef VerifyPackage(cert, package):\n  \"\"\"Verify the given package with the certificate.\n\n  (Comments from bootable/recovery/verifier.cpp:)\n\n  An archive with a whole-file signature will end in six bytes:\n\n    (2-byte signature start) $ff $ff (2-byte comment size)\n\n  (As far as the ZIP format is concerned, these are part of the\n  archive comment.) We start by reading this footer, this tells\n  us how far back from the end we have to start reading to find\n  the whole comment.\n  \"\"\"\n\n  print('Package: %s' % (package,))\n  print('Certificate: %s' % (cert,))\n\n  # Read in the package.\n  with open(package, 'rb') as package_file:\n    package_bytes = package_file.read()\n\n  length = len(package_bytes)\n  assert length >= 6, \"Not big enough to contain footer.\"\n\n  footer = bytearray(package_bytes[-6:])\n  assert footer[2] == 0xff and footer[3] == 0xff, \"Footer is wrong.\"\n\n  signature_start_from_end = (footer[1] << 8) + footer[0]\n  assert signature_start_from_end > 6, \"Signature start is in the footer.\"\n\n  signature_start = length - signature_start_from_end\n\n  # Determine how much of the file is covered by the signature. This is\n  # everything except the signature data and length, which includes all of the\n  # EOCD except for the comment length field (2 bytes) and the comment data.\n  comment_len = (footer[5] << 8) + footer[4]\n  signed_len = length - comment_len - 2\n\n  print('Package length: %d' % (length,))\n  print('Comment length: %d' % (comment_len,))\n  print('Signed data length: %d' % (signed_len,))\n  print('Signature start: %d' % (signature_start,))\n\n  use_sha256 = CertUsesSha256(cert)\n  print('Use SHA-256: %s' % (use_sha256,))\n\n  h = sha256() if use_sha256 else sha1()\n  h.update(package_bytes[:signed_len])\n  package_digest = h.hexdigest().lower()\n\n  print('Digest: %s' % (package_digest,))\n\n  # Get the signature from the input package.\n  signature = package_bytes[signature_start:-6]\n  sig_file = common.MakeTempFile(prefix='sig-')\n  with open(sig_file, 'wb') as f:\n    f.write(signature)\n\n  # Parse the signature and get the hash.\n  cmd = ['openssl', 'asn1parse', '-inform', 'DER', '-in', sig_file]\n  sig = common.RunAndCheckOutput(cmd, stdout=subprocess.PIPE)\n\n  digest_line = sig.rstrip().split('\\n')[-1]\n  digest_string = digest_line.split(':')[3]\n  digest_file = common.MakeTempFile(prefix='digest-')\n  with open(digest_file, 'wb') as f:\n    f.write(bytearray.fromhex(digest_string))\n\n  # Verify the digest by outputing the decrypted result in ASN.1 structure.\n  decrypted_file = common.MakeTempFile(prefix='decrypted-')\n  cmd = ['openssl', 'rsautl', '-verify', '-certin', '-inkey', cert,\n         '-in', digest_file, '-out', decrypted_file]\n  common.RunAndCheckOutput(cmd, stdout=subprocess.PIPE)\n\n  # Parse the output ASN.1 structure.\n  cmd = ['openssl', 'asn1parse', '-inform', 'DER', '-in', decrypted_file]\n  decrypted_output = common.RunAndCheckOutput(cmd, stdout=subprocess.PIPE)\n\n  digest_line = decrypted_output.rstrip().split('\\n')[-1]\n  digest_string = digest_line.split(':')[3].lower()\n\n  # Verify that the two digest strings match.\n  assert package_digest == digest_string, \"Verification failed.\"\n\n  # Verified successfully upon reaching here.\n  print('\\nWhole package signature VERIFIED\\n')\n\n\ndef VerifyAbOtaPayload(cert, package):\n  \"\"\"Verifies the payload and metadata signatures in an A/B OTA payload.\"\"\"\n  package_zip = zipfile.ZipFile(package, 'r', allowZip64=True)\n  if 'payload.bin' not in package_zip.namelist():\n    common.ZipClose(package_zip)\n    return\n\n  print('Verifying A/B OTA payload signatures...')\n\n  # Dump pubkey from the certificate.\n  pubkey = common.MakeTempFile(prefix=\"key-\", suffix=\".pem\")\n  with open(pubkey, 'w') as pubkey_fp:\n    pubkey_fp.write(common.ExtractPublicKey(cert))\n\n  package_dir = common.MakeTempDir(prefix='package-')\n\n  # Signature verification with delta_generator.\n  payload_file = package_zip.extract('payload.bin', package_dir)\n  cmd = ['delta_generator',\n         '--in_file=' + payload_file,\n         '--public_key=' + pubkey]\n  common.RunAndCheckOutput(cmd)\n  common.ZipClose(package_zip)\n\n  # Verified successfully upon reaching here.\n  print('\\nPayload signatures VERIFIED\\n\\n')\n\n\ndef main():\n  parser = argparse.ArgumentParser()\n  parser.add_argument('certificate', help='The certificate to be used.')\n  parser.add_argument('package', help='The OTA package to be verified.')\n  args = parser.parse_args()\n\n  common.InitLogging()\n\n  VerifyPackage(args.certificate, args.package)\n  VerifyAbOtaPayload(args.certificate, args.package)\n\n\nif __name__ == '__main__':\n  try:\n    main()\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/check_partition_sizes.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nCheck dynamic partition sizes.\n\nusage: check_partition_sizes [info.txt]\n\nCheck dump-super-partitions-info procedure for expected keys in info.txt. In\naddition, *_image (e.g. system_image, vendor_image, etc.) must be defined for\neach partition in dynamic_partition_list.\n\nExit code is 0 if successful and non-zero if any failures.\n\"\"\"\n\nfrom __future__ import print_function\n\nimport logging\nimport sys\n\nimport common\nimport sparse_img\n\nif sys.hexversion < 0x02070000:\n  print(\"Python 2.7 or newer is required.\", file=sys.stderr)\n  sys.exit(1)\n\nlogger = logging.getLogger(__name__)\n\n\nclass Expression(object):\n  def __init__(self, desc, expr, value=None):\n    # Human-readable description\n    self.desc = str(desc)\n    # Numeric expression\n    self.expr = str(expr)\n    # Value of expression\n    self.value = int(expr) if value is None else value\n\n  def CheckLe(self, other, level=logging.ERROR):\n    format_args = (self.desc, other.desc, self.expr, self.value,\n                   other.expr, other.value)\n    if self.value <= other.value:\n      logger.info(\"%s is less than or equal to %s:\\n%s == %d <= %s == %d\",\n                  *format_args)\n    else:\n      msg = \"{} is greater than {}:\\n{} == {} > {} == {}\".format(*format_args)\n      if level == logging.ERROR:\n        raise RuntimeError(msg)\n      else:\n        logger.log(level, msg)\n\n  def CheckLt(self, other, level=logging.ERROR):\n    format_args = (self.desc, other.desc, self.expr, self.value,\n                   other.expr, other.value)\n    if self.value < other.value:\n      logger.info(\"%s is less than %s:\\n%s == %d < %s == %d\",\n                  *format_args)\n    else:\n      msg = \"{} is greater than or equal to {}:\\n{} == {} >= {} == {}\".format(\n          *format_args)\n      if level == logging.ERROR:\n        raise RuntimeError(msg)\n      else:\n        logger.log(level, msg)\n\n  def CheckEq(self, other):\n    format_args = (self.desc, other.desc, self.expr, self.value,\n                   other.expr, other.value)\n    if self.value == other.value:\n      logger.info(\"%s equals %s:\\n%s == %d == %s == %d\", *format_args)\n    else:\n      raise RuntimeError(\"{} does not equal {}:\\n{} == {} != {} == {}\".format(\n          *format_args))\n\n\n# A/B feature flags\nclass DeviceType(object):\n  NONE = 0\n  AB = 1\n  RVAB = 2 # retrofit Virtual-A/B\n  VAB = 3\n\n  @staticmethod\n  def Get(info_dict):\n    if info_dict.get(\"ab_update\") != \"true\":\n      return DeviceType.NONE\n    if info_dict.get(\"virtual_ab_retrofit\") == \"true\":\n      return DeviceType.RVAB\n    if info_dict.get(\"virtual_ab\") == \"true\":\n      return DeviceType.VAB\n    return DeviceType.AB\n\n\n# Dynamic partition feature flags\nclass Dap(object):\n  NONE = 0\n  RDAP = 1\n  DAP = 2\n\n  @staticmethod\n  def Get(info_dict):\n    if info_dict.get(\"use_dynamic_partitions\") != \"true\":\n      return Dap.NONE\n    if info_dict.get(\"dynamic_partition_retrofit\") == \"true\":\n      return Dap.RDAP\n    return Dap.DAP\n\n\nclass DynamicPartitionSizeChecker(object):\n  def __init__(self, info_dict):\n    if \"super_partition_size\" in info_dict:\n      if \"super_partition_warn_limit\" not in info_dict:\n        info_dict[\"super_partition_warn_limit\"] = \\\n            int(info_dict[\"super_partition_size\"]) * 95 // 100\n      if \"super_partition_error_limit\" not in info_dict:\n        info_dict[\"super_partition_error_limit\"] = \\\n            int(info_dict[\"super_partition_size\"])\n    self.info_dict = info_dict\n\n  def _ReadSizeOfPartition(self, name):\n    # Tests uses *_image_size instead (to avoid creating empty sparse images\n    # on disk)\n    if name + \"_image_size\" in self.info_dict:\n      return int(self.info_dict[name + \"_image_size\"])\n    return sparse_img.GetImagePartitionSize(self.info_dict[name + \"_image\"])\n\n  # Round result to BOARD_SUPER_PARTITION_ALIGNMENT\n  def _RoundPartitionSize(self, size):\n    alignment = self.info_dict.get(\"super_partition_alignment\")\n    if alignment is None:\n      return size\n    return (size + alignment - 1) // alignment * alignment\n\n  def _CheckSuperPartitionSize(self):\n    info_dict = self.info_dict\n    super_block_devices = \\\n        info_dict.get(\"super_block_devices\", \"\").strip().split()\n    size_list = [int(info_dict.get(\"super_{}_device_size\".format(b), \"0\"))\n                 for b in super_block_devices]\n    sum_size = Expression(\"sum of super partition block device sizes\",\n                          \"+\".join(str(size) for size in size_list),\n                          sum(size_list))\n    super_partition_size = Expression(\"BOARD_SUPER_PARTITION_SIZE\",\n                                      info_dict[\"super_partition_size\"])\n    sum_size.CheckEq(super_partition_size)\n\n  def _CheckSumOfPartitionSizes(self, max_size, partition_names,\n                                warn_size=None, error_size=None):\n    partition_size_list = [self._RoundPartitionSize(\n        self._ReadSizeOfPartition(p)) for p in partition_names]\n    sum_size = Expression(\"sum of sizes of {}\".format(partition_names),\n                          \"+\".join(str(size) for size in partition_size_list),\n                          sum(partition_size_list))\n    sum_size.CheckLe(max_size)\n    if error_size:\n      sum_size.CheckLe(error_size)\n    if warn_size:\n      sum_size.CheckLe(warn_size, level=logging.WARNING)\n\n  def _NumDeviceTypesInSuper(self):\n    slot = DeviceType.Get(self.info_dict)\n    dap = Dap.Get(self.info_dict)\n\n    if dap == Dap.NONE:\n      raise RuntimeError(\"check_partition_sizes should only be executed on \"\n                         \"builds with dynamic partitions enabled\")\n\n    # Retrofit dynamic partitions: 1 slot per \"super\", 2 \"super\"s on the device\n    if dap == Dap.RDAP:\n      if slot != DeviceType.AB:\n        raise RuntimeError(\"Device with retrofit dynamic partitions must use \"\n                           \"regular (non-Virtual) A/B\")\n      return 1\n\n    # Launch DAP: 1 super on the device\n    assert dap == Dap.DAP\n\n    # DAP + A/B: 2 slots in super\n    if slot == DeviceType.AB:\n      return 2\n\n    # DAP + retrofit Virtual A/B: same as A/B\n    if slot == DeviceType.RVAB:\n      return 2\n\n    # DAP + Launch Virtual A/B: 1 *real* slot in super (2 virtual slots)\n    if slot == DeviceType.VAB:\n      return 1\n\n    # DAP + non-A/B: 1 slot in super\n    assert slot == DeviceType.NONE\n    return 1\n\n  def _CheckAllPartitionSizes(self):\n    info_dict = self.info_dict\n    num_slots = self._NumDeviceTypesInSuper()\n    size_limit_suffix = (\" / %d\" % num_slots) if num_slots > 1 else \"\"\n\n    # Check sum(all partitions) <= super partition (/ 2 for A/B devices launched\n    # with dynamic partitions)\n    if \"super_partition_size\" in info_dict and \\\n        \"dynamic_partition_list\" in info_dict:\n      max_size = Expression(\n          \"BOARD_SUPER_PARTITION_SIZE{}\".format(size_limit_suffix),\n          int(info_dict[\"super_partition_size\"]) // num_slots)\n      warn_limit = Expression(\n          \"BOARD_SUPER_PARTITION_WARN_LIMIT{}\".format(size_limit_suffix),\n          int(info_dict[\"super_partition_warn_limit\"]) // num_slots)\n      error_limit = Expression(\n          \"BOARD_SUPER_PARTITION_ERROR_LIMIT{}\".format(size_limit_suffix),\n          int(info_dict[\"super_partition_error_limit\"]) // num_slots)\n      partitions_in_super = info_dict[\"dynamic_partition_list\"].strip().split()\n      # In the vab case, factory OTA will allocate space on super to install\n      # the system_other partition. So add system_other to the partition list.\n      if DeviceType.Get(self.info_dict) == DeviceType.VAB and (\n          \"system_other_image\" in info_dict or\n          \"system_other_image_size\" in info_dict):\n        partitions_in_super.append(\"system_other\")\n      self._CheckSumOfPartitionSizes(max_size, partitions_in_super,\n                                     warn_limit, error_limit)\n\n    groups = info_dict.get(\"super_partition_groups\", \"\").strip().split()\n\n    # For each group, check sum(partitions in group) <= group size\n    for group in groups:\n      if \"super_{}_group_size\".format(group) in info_dict and \\\n          \"super_{}_partition_list\".format(group) in info_dict:\n        group_size = Expression(\n            \"BOARD_{}_SIZE\".format(group),\n            int(info_dict[\"super_{}_group_size\".format(group)]))\n        self._CheckSumOfPartitionSizes(\n            group_size,\n            info_dict[\"super_{}_partition_list\".format(group)].strip().split())\n\n    # Check sum(all group sizes) <= super partition (/ 2 for A/B devices\n    # launched with dynamic partitions)\n    if \"super_partition_size\" in info_dict:\n      group_size_list = [int(info_dict.get(\n          \"super_{}_group_size\".format(group), 0)) for group in groups]\n      sum_size = Expression(\"sum of sizes of {}\".format(groups),\n                            \"+\".join(str(size) for size in group_size_list),\n                            sum(group_size_list))\n      max_size = Expression(\n          \"BOARD_SUPER_PARTITION_SIZE{}\".format(size_limit_suffix),\n          int(info_dict[\"super_partition_size\"]) // num_slots)\n      # Retrofit DAP will build metadata as part of super image.\n      if Dap.Get(info_dict) == Dap.RDAP:\n        sum_size.CheckLe(max_size)\n        return\n\n      sum_size.CheckLt(max_size)\n      # Display a warning if group size + 1M >= super size\n      minimal_metadata_size = 1024 * 1024  # 1MiB\n      sum_size_plus_metadata = Expression(\n          \"sum of sizes of {} plus 1M metadata\".format(groups),\n          \"+\".join(str(size) for size in\n                   group_size_list + [minimal_metadata_size]),\n          sum(group_size_list) + minimal_metadata_size)\n      sum_size_plus_metadata.CheckLe(max_size, level=logging.WARNING)\n\n  def Run(self):\n    self._CheckAllPartitionSizes()\n    if self.info_dict.get(\"dynamic_partition_retrofit\") == \"true\":\n      self._CheckSuperPartitionSize()\n\n\ndef CheckPartitionSizes(inp):\n  if isinstance(inp, str):\n    info_dict = common.LoadDictionaryFromFile(inp)\n    return DynamicPartitionSizeChecker(info_dict).Run()\n  if isinstance(inp, dict):\n    return DynamicPartitionSizeChecker(inp).Run()\n  raise ValueError(\"{} is not a dictionary or a valid path\".format(inp))\n\n\ndef main(argv):\n  args = common.ParseOptions(argv, __doc__)\n  if len(args) != 1:\n    common.Usage(__doc__)\n    sys.exit(1)\n  common.InitLogging()\n  CheckPartitionSizes(args[0])\n\n\nif __name__ == \"__main__\":\n  try:\n    common.CloseInheritedPipes()\n    main(sys.argv[1:])\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/check_target_files_signatures.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nCheck the signatures of all APKs in a target_files .zip file.  With\n-c, compare the signatures of each package to the ones in a separate\ntarget_files (usually a previously distributed build for the same\ndevice) and flag any changes.\n\nUsage:  check_target_file_signatures [flags] target_files\n\n  -c  (--compare_with)  <other_target_files>\n      Look for compatibility problems between the two sets of target\n      files (eg., packages whose keys have changed).\n\n  -l  (--local_cert_dirs)  <dir,dir,...>\n      Comma-separated list of top-level directories to scan for\n      .x509.pem files.  Defaults to \"vendor,build\".  Where cert files\n      can be found that match APK signatures, the filename will be\n      printed as the cert name, otherwise a hash of the cert plus its\n      subject string will be printed instead.\n\n  -t  (--text)\n      Dump the certificate information for both packages in comparison\n      mode (this output is normally suppressed).\n\n\"\"\"\n\nfrom __future__ import print_function\n\nimport logging\nimport os\nimport os.path\nimport re\nimport subprocess\nimport sys\nimport zipfile\n\nimport common\n\nif sys.hexversion < 0x02070000:\n  print(\"Python 2.7 or newer is required.\", file=sys.stderr)\n  sys.exit(1)\n\n\nlogger = logging.getLogger(__name__)\n\n\nOPTIONS = common.OPTIONS\n\nOPTIONS.text = False\nOPTIONS.compare_with = None\nOPTIONS.local_cert_dirs = (\"vendor\", \"build\")\n\nPROBLEMS = []\nPROBLEM_PREFIX = []\n\n\ndef AddProblem(msg):\n  logger.error(msg)\n  PROBLEMS.append(\" \".join(PROBLEM_PREFIX) + \" \" + msg)\n\n\ndef Push(msg):\n  PROBLEM_PREFIX.append(msg)\n\n\ndef Pop():\n  PROBLEM_PREFIX.pop()\n\n\ndef Banner(msg):\n  print(\"-\" * 70)\n  print(\"  \", msg)\n  print(\"-\" * 70)\n\n\ndef GetCertSubject(cert):\n  p = common.Run([\"openssl\", \"x509\", \"-inform\", \"DER\", \"-text\"],\n                 stdin=subprocess.PIPE,\n                 stdout=subprocess.PIPE,\n                 universal_newlines=False)\n  out, err = p.communicate(cert)\n  if err and not err.strip():\n    return \"(error reading cert subject)\"\n  for line in out.decode().split(\"\\n\"):\n    line = line.strip()\n    if line.startswith(\"Subject:\"):\n      return line[8:].strip()\n  return \"(unknown cert subject)\"\n\n\nclass CertDB(object):\n\n  def __init__(self):\n    self.certs = {}\n\n  def Add(self, cert_digest, subject, name=None):\n    if cert_digest in self.certs:\n      if name:\n        self.certs[cert_digest] = self.certs[cert_digest] + \",\" + name\n    else:\n      if name is None:\n        name = \"unknown cert %s (%s)\" % (cert_digest[:12], subject)\n      self.certs[cert_digest] = name\n\n  def Get(self, cert_digest):\n    \"\"\"Return the name for a given cert digest.\"\"\"\n    return self.certs.get(cert_digest, None)\n\n  def FindLocalCerts(self):\n    to_load = []\n    for top in OPTIONS.local_cert_dirs:\n      for dirpath, _, filenames in os.walk(top):\n        certs = [os.path.join(dirpath, i)\n                 for i in filenames if i.endswith(\".x509.pem\")]\n        if certs:\n          to_load.extend(certs)\n\n    for i in to_load:\n      with open(i) as f:\n        cert = common.ParseCertificate(f.read())\n      name, _ = os.path.splitext(i)\n      name, _ = os.path.splitext(name)\n\n      cert_sha1 = common.sha1(cert).hexdigest()\n      cert_subject = GetCertSubject(cert)\n      self.Add(cert_sha1, cert_subject, name)\n\n\nALL_CERTS = CertDB()\n\n\ndef CertFromPKCS7(data, filename):\n  \"\"\"Read the cert out of a PKCS#7-format file (which is what is\n  stored in a signed .apk).\"\"\"\n  Push(filename + \":\")\n  try:\n    p = common.Run([\"openssl\", \"pkcs7\",\n                    \"-inform\", \"DER\",\n                    \"-outform\", \"PEM\",\n                    \"-print_certs\"],\n                   stdin=subprocess.PIPE,\n                   stdout=subprocess.PIPE,\n                   universal_newlines=False)\n    out, err = p.communicate(data)\n    if err and not err.strip():\n      AddProblem(\"error reading cert:\\n\" + err.decode())\n      return None\n\n    cert = common.ParseCertificate(out.decode())\n    if not cert:\n      AddProblem(\"error parsing cert output\")\n      return None\n    return cert\n  finally:\n    Pop()\n\n\nclass APK(object):\n\n  def __init__(self, full_filename, filename):\n    self.filename = filename\n    self.cert_digests = frozenset()\n    self.shared_uid = None\n    self.package = None\n\n    Push(filename+\":\")\n    try:\n      self.RecordCerts(full_filename)\n      self.ReadManifest(full_filename)\n    finally:\n      Pop()\n\n  def ReadCertsDeprecated(self, full_filename):\n    print(\"reading certs in deprecated way for {}\".format(full_filename))\n    cert_digests = set()\n    with zipfile.ZipFile(full_filename) as apk:\n      for info in apk.infolist():\n        filename = info.filename\n        if (filename.startswith(\"META-INF/\") and\n                info.filename.endswith((\".DSA\", \".RSA\"))):\n          pkcs7 = apk.read(filename)\n          cert = CertFromPKCS7(pkcs7, filename)\n          if not cert:\n            continue\n          cert_sha1 = common.sha1(cert).hexdigest()\n          cert_subject = GetCertSubject(cert)\n          ALL_CERTS.Add(cert_sha1, cert_subject)\n          cert_digests.add(cert_sha1)\n    if not cert_digests:\n      AddProblem(\"No signature found\")\n      return\n    self.cert_digests = frozenset(cert_digests)\n\n  def RecordCerts(self, full_filename):\n    \"\"\"Parse and save the signature of an apk file.\"\"\"\n\n    # Dump the cert info with apksigner\n    cmd = [\"apksigner\", \"verify\", \"--print-certs\", full_filename]\n    p = common.Run(cmd, stdout=subprocess.PIPE)\n    output, _ = p.communicate()\n    if p.returncode != 0:\n      self.ReadCertsDeprecated(full_filename)\n      return\n\n    # Sample output:\n    # Signer #1 certificate DN: ...\n    # Signer #1 certificate SHA-256 digest: ...\n    # Signer #1 certificate SHA-1 digest: ...\n    # Signer (minSdkVersion=24, maxSdkVersion=32) certificate SHA-256 digest: 56be132b780656fe2444cd34326eb5d7aac91d2096abf0fe673a99270622ec87\n    # Signer (minSdkVersion=24, maxSdkVersion=32) certificate SHA-1 digest: 19da94896ce4078c38ca695701f1dec741ec6d67\n    # ...\n    certs_info = {}\n    certificate_regex = re.compile(\n        r\"(Signer (?:#[0-9]+|\\(.*\\))) (certificate .*):(.*)\")\n    for line in output.splitlines():\n      m = certificate_regex.match(line)\n      if not m:\n        continue\n      signer, key, val = m.group(1), m.group(2), m.group(3)\n      if certs_info.get(signer):\n        certs_info[signer].update({key.strip(): val.strip()})\n      else:\n        certs_info.update({signer: {key.strip(): val.strip()}})\n    if not certs_info:\n      AddProblem(\"Failed to parse cert info\")\n      return\n\n    cert_digests = set()\n    for signer, props in certs_info.items():\n      subject = props.get(\"certificate DN\")\n      digest = props.get(\"certificate SHA-1 digest\")\n      if not subject or not digest:\n        AddProblem(\"Failed to parse cert subject or digest\")\n        return\n      ALL_CERTS.Add(digest, subject)\n      cert_digests.add(digest)\n    self.cert_digests = frozenset(cert_digests)\n\n  def ReadManifest(self, full_filename):\n    p = common.Run([\"aapt2\", \"dump\", \"xmltree\", full_filename, \"--file\",\n                    \"AndroidManifest.xml\"],\n                   stdout=subprocess.PIPE)\n    manifest, err = p.communicate()\n    if err:\n      AddProblem(\"failed to read manifest \" + full_filename)\n      return\n\n    self.shared_uid = None\n    self.package = None\n\n    for line in manifest.split(\"\\n\"):\n      line = line.strip()\n      m = re.search(r'A: (\\S*?)(?:\\(0x[0-9a-f]+\\))?=\"(.*?)\" \\(Raw', line)\n      if m:\n        name = m.group(1)\n        if name == \"android:sharedUserId\":\n          if self.shared_uid is not None:\n            AddProblem(\"multiple sharedUserId declarations \" + full_filename)\n          self.shared_uid = m.group(2)\n        elif name == \"package\":\n          if self.package is not None:\n            AddProblem(\"multiple package declarations \" + full_filename)\n          self.package = m.group(2)\n\n    if self.package is None:\n      AddProblem(\"no package declaration \" + full_filename)\n\n\nclass TargetFiles(object):\n  def __init__(self):\n    self.max_pkg_len = 30\n    self.max_fn_len = 20\n    self.apks = None\n    self.apks_by_basename = None\n    self.certmap = None\n\n  def LoadZipFile(self, filename):\n    # First read the APK certs file to figure out whether there are compressed\n    # APKs in the archive. If we do have compressed APKs in the archive, then we\n    # must decompress them individually before we perform any analysis.\n\n    # This is the list of wildcards of files we extract from |filename|.\n    apk_extensions = ['*.apk', '*.apex']\n\n    with zipfile.ZipFile(filename, \"r\") as input_zip:\n      self.certmap, compressed_extension = common.ReadApkCerts(input_zip)\n    if compressed_extension:\n      apk_extensions.append('*.apk' + compressed_extension)\n\n    d = common.UnzipTemp(filename, apk_extensions)\n    self.apks = {}\n    self.apks_by_basename = {}\n    for dirpath, _, filenames in os.walk(d):\n      for fn in filenames:\n        # Decompress compressed APKs before we begin processing them.\n        if compressed_extension and fn.endswith(compressed_extension):\n          # First strip the compressed extension from the file.\n          uncompressed_fn = fn[:-len(compressed_extension)]\n\n          # Decompress the compressed file to the output file.\n          common.Gunzip(os.path.join(dirpath, fn),\n                        os.path.join(dirpath, uncompressed_fn))\n\n          # Finally, delete the compressed file and use the uncompressed file\n          # for further processing. Note that the deletion is not strictly\n          # required, but is done here to ensure that we're not using too much\n          # space in the temporary directory.\n          os.remove(os.path.join(dirpath, fn))\n          fn = uncompressed_fn\n\n        if fn.endswith(('.apk', '.apex')):\n          fullname = os.path.join(dirpath, fn)\n          displayname = fullname[len(d)+1:]\n          apk = APK(fullname, displayname)\n          self.apks[apk.filename] = apk\n          self.apks_by_basename[os.path.basename(apk.filename)] = apk\n          if apk.package:\n            self.max_pkg_len = max(self.max_pkg_len, len(apk.package))\n          self.max_fn_len = max(self.max_fn_len, len(apk.filename))\n\n  def CheckSharedUids(self):\n    \"\"\"Look for any instances where packages signed with different\n    certs request the same sharedUserId.\"\"\"\n    apks_by_uid = {}\n    for apk in self.apks.values():\n      if apk.shared_uid:\n        apks_by_uid.setdefault(apk.shared_uid, []).append(apk)\n\n    for uid in sorted(apks_by_uid):\n      apks = apks_by_uid[uid]\n      for apk in apks[1:]:\n        if apk.certs != apks[0].certs:\n          break\n      else:\n        # all packages have the same set of certs; this uid is fine.\n        continue\n\n      AddProblem(\"different cert sets for packages with uid %s\" % (uid,))\n\n      print(\"uid %s is shared by packages with different cert sets:\" % (uid,))\n      for apk in apks:\n        print(\"%-*s  [%s]\" % (self.max_pkg_len, apk.package, apk.filename))\n        for digest in apk.cert_digests:\n          print(\"   \", ALL_CERTS.Get(digest))\n      print()\n\n  def CheckExternalSignatures(self):\n    for apk_filename, certname in self.certmap.items():\n      if certname == \"EXTERNAL\":\n        # Apps marked EXTERNAL should be signed with the test key\n        # during development, then manually re-signed after\n        # predexopting.  Consider it an error if this app is now\n        # signed with any key that is present in our tree.\n        apk = self.apks_by_basename[apk_filename]\n        signed_with_external = False\n        for digest in apk.cert_digests:\n          name = ALL_CERTS.Get(digest)\n          if name and name.startswith(\"unknown \"):\n            signed_with_external = True\n\n        if not signed_with_external:\n          Push(apk.filename)\n          AddProblem(\"hasn't been signed with EXTERNAL cert\")\n          Pop()\n\n  def PrintCerts(self):\n    \"\"\"Display a table of packages grouped by cert.\"\"\"\n    by_digest = {}\n    for apk in self.apks.values():\n      for digest in apk.cert_digests:\n        if apk.package:\n          by_digest.setdefault(digest, []).append((apk.package, apk))\n\n    order = [(-len(v), k) for (k, v) in by_digest.items()]\n    order.sort()\n\n    for _, digest in order:\n      print(\"%s:\" % (ALL_CERTS.Get(digest),))\n      apks = by_digest[digest]\n      apks.sort(key=lambda x: x[0])\n      for i in range(1, len(apks)):\n        pkgname, apk = apks[i]\n        if pkgname == apks[i-1][0]:\n          print(\"Both {} and {} have same package name {}\".format(\n              apk.filename, apks[i-1][1].filename, pkgname))\n      for _, apk in apks:\n        if apk.shared_uid:\n          print(\"  %-*s  %-*s  [%s]\" % (self.max_fn_len, apk.filename,\n                                        self.max_pkg_len, apk.package,\n                                        apk.shared_uid))\n        else:\n          print(\"  %-*s  %s\" % (self.max_fn_len, apk.filename, apk.package))\n      print()\n\n  def CompareWith(self, other):\n    \"\"\"Look for instances where a given package that exists in both\n    self and other have different certs.\"\"\"\n\n    all_apks = set(self.apks.keys())\n    all_apks.update(other.apks.keys())\n\n    max_pkg_len = max(self.max_pkg_len, other.max_pkg_len)\n\n    by_digestpair = {}\n\n    for i in all_apks:\n      if i in self.apks:\n        if i in other.apks:\n          # in both; should have same set of certs\n          if self.apks[i].cert_digests != other.apks[i].cert_digests:\n            by_digestpair.setdefault((other.apks[i].cert_digests,\n                                      self.apks[i].cert_digests), []).append(i)\n        else:\n          print(\"%s [%s]: new APK (not in comparison target_files)\" % (\n              i, self.apks[i].filename))\n      else:\n        if i in other.apks:\n          print(\"%s [%s]: removed APK (only in comparison target_files)\" % (\n              i, other.apks[i].filename))\n\n    if by_digestpair:\n      AddProblem(\"some APKs changed certs\")\n      Banner(\"APK signing differences\")\n      for (old, new), packages in sorted(by_digestpair.items()):\n        for i, o in enumerate(old):\n          if i == 0:\n            print(\"was\", ALL_CERTS.Get(o))\n          else:\n            print(\"   \", ALL_CERTS.Get(o))\n        for i, n in enumerate(new):\n          if i == 0:\n            print(\"now\", ALL_CERTS.Get(n))\n          else:\n            print(\"   \", ALL_CERTS.Get(n))\n        for i in sorted(packages):\n          old_fn = other.apks[i].filename\n          new_fn = self.apks[i].filename\n          if old_fn == new_fn:\n            print(\"  %-*s  [%s]\" % (max_pkg_len, i, old_fn))\n          else:\n            print(\"  %-*s  [was: %s; now: %s]\" % (max_pkg_len, i,\n                                                  old_fn, new_fn))\n        print()\n\n\ndef main(argv):\n  def option_handler(o, a):\n    if o in (\"-c\", \"--compare_with\"):\n      OPTIONS.compare_with = a\n    elif o in (\"-l\", \"--local_cert_dirs\"):\n      OPTIONS.local_cert_dirs = [i.strip() for i in a.split(\",\")]\n    elif o in (\"-t\", \"--text\"):\n      OPTIONS.text = True\n    else:\n      return False\n    return True\n\n  args = common.ParseOptions(argv, __doc__,\n                             extra_opts=\"c:l:t\",\n                             extra_long_opts=[\"compare_with=\",\n                                              \"local_cert_dirs=\"],\n                             extra_option_handler=option_handler)\n\n  if len(args) != 1:\n    common.Usage(__doc__)\n    sys.exit(1)\n\n  common.InitLogging()\n\n  ALL_CERTS.FindLocalCerts()\n\n  Push(\"input target_files:\")\n  try:\n    target_files = TargetFiles()\n    target_files.LoadZipFile(args[0])\n  finally:\n    Pop()\n\n  compare_files = None\n  if OPTIONS.compare_with:\n    Push(\"comparison target_files:\")\n    try:\n      compare_files = TargetFiles()\n      compare_files.LoadZipFile(OPTIONS.compare_with)\n    finally:\n      Pop()\n\n  if OPTIONS.text or not compare_files:\n    Banner(\"target files\")\n    target_files.PrintCerts()\n  target_files.CheckSharedUids()\n  target_files.CheckExternalSignatures()\n  if compare_files:\n    if OPTIONS.text:\n      Banner(\"comparison files\")\n      compare_files.PrintCerts()\n    target_files.CompareWith(compare_files)\n\n  if PROBLEMS:\n    print(\"%d problem(s) found:\\n\" % (len(PROBLEMS),))\n    for p in PROBLEMS:\n      print(p)\n    return 1\n\n  return 0\n\n\nif __name__ == '__main__':\n  try:\n    r = main(sys.argv[1:])\n    sys.exit(r)\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/check_target_files_vintf.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nCheck VINTF compatibility from a target files package.\n\nUsage: check_target_files_vintf target_files\n\ntarget_files can be a ZIP file or an extracted target files directory.\n\"\"\"\n\nimport json\nimport logging\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport zipfile\n\nimport apex_utils\nimport common\nfrom apex_manifest import ParseApexManifest\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\n\n# Keys are paths that VINTF searches. Must keep in sync with libvintf's search\n# paths (VintfObject.cpp).\n# These paths are stored in different directories in target files package, so\n# we have to search for the correct path and tell checkvintf to remap them.\n# Look for TARGET_COPY_OUT_* variables in board_config.mk for possible paths for\n# each partition.\nDIR_SEARCH_PATHS = {\n    '/system': ('SYSTEM',),\n    '/vendor': ('VENDOR', 'SYSTEM/vendor'),\n    '/product': ('PRODUCT', 'SYSTEM/product'),\n    '/odm': ('ODM', 'VENDOR/odm', 'SYSTEM/vendor/odm'),\n    '/system_ext': ('SYSTEM_EXT', 'SYSTEM/system_ext'),\n    # vendor_dlkm, odm_dlkm, and system_dlkm does not have VINTF files.\n}\n\nUNZIP_PATTERN = ['META/*', '*/build.prop']\n\n\ndef GetDirmap(input_tmp):\n  dirmap = {}\n  for device_path, target_files_rel_paths in DIR_SEARCH_PATHS.items():\n    for target_files_rel_path in target_files_rel_paths:\n      target_files_path = os.path.join(input_tmp, target_files_rel_path)\n      if os.path.isdir(target_files_path):\n        dirmap[device_path] = target_files_path\n        break\n    if device_path not in dirmap:\n      raise ValueError(\"Can't determine path for device path \" + device_path +\n                       \". Searched the following:\" +\n                       (\"\\n\".join(target_files_rel_paths)))\n  return dirmap\n\n\ndef GetArgsForSkus(info_dict):\n  odm_skus = info_dict.get('vintf_odm_manifest_skus', '').strip().split()\n  if info_dict.get('vintf_include_empty_odm_sku', '') == \"true\" or not odm_skus:\n    odm_skus += ['']\n\n  vendor_skus = info_dict.get('vintf_vendor_manifest_skus', '').strip().split()\n  if info_dict.get('vintf_include_empty_vendor_sku', '') == \"true\" or \\\n      not vendor_skus:\n    vendor_skus += ['']\n\n  return [['--property', 'ro.boot.product.hardware.sku=' + odm_sku,\n           '--property', 'ro.boot.product.vendor.sku=' + vendor_sku]\n          for odm_sku in odm_skus for vendor_sku in vendor_skus]\n\n\ndef GetArgsForShippingApiLevel(info_dict):\n  shipping_api_level = info_dict['vendor.build.prop'].GetProp(\n      'ro.product.first_api_level')\n  if not shipping_api_level:\n    logger.warning('Cannot determine ro.product.first_api_level')\n    return []\n  return ['--property', 'ro.product.first_api_level=' + shipping_api_level]\n\n\ndef GetArgsForKernel(input_tmp):\n  version_path = os.path.join(input_tmp, 'META/kernel_version.txt')\n  config_path = os.path.join(input_tmp, 'META/kernel_configs.txt')\n\n  if not os.path.isfile(version_path) or not os.path.isfile(config_path):\n    logger.info('Skipping kernel config checks because '\n                'PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS is not set')\n    return []\n\n  return ['--kernel', '{}:{}'.format(version_path, config_path)]\n\n\ndef CheckVintfFromExtractedTargetFiles(input_tmp, info_dict=None):\n  \"\"\"\n  Checks VINTF metadata of an extracted target files directory.\n\n  Args:\n    inp: path to the directory that contains the extracted target files archive.\n    info_dict: The build-time info dict. If None, it will be loaded from inp.\n\n  Returns:\n    True if VINTF check is skipped or compatible, False if incompatible. Raise\n    a RuntimeError if any error occurs.\n  \"\"\"\n\n  if info_dict is None:\n    info_dict = common.LoadInfoDict(input_tmp)\n\n  if info_dict.get('vintf_enforce') != 'true':\n    logger.warning('PRODUCT_ENFORCE_VINTF_MANIFEST is not set, skipping checks')\n    return True\n\n\n  dirmap = GetDirmap(input_tmp)\n\n  # Simulate apexd with target-files.\n  # add a mapping('/apex' => ${input_tmp}/APEX) to dirmap\n  PrepareApexDirectory(input_tmp, dirmap)\n\n  args_for_skus = GetArgsForSkus(info_dict)\n  shipping_api_level_args = GetArgsForShippingApiLevel(info_dict)\n  kernel_args = GetArgsForKernel(input_tmp)\n\n  common_command = [\n      'checkvintf',\n      '--check-compat',\n  ]\n\n  for device_path, real_path in sorted(dirmap.items()):\n    common_command += ['--dirmap', '{}:{}'.format(device_path, real_path)]\n  common_command += kernel_args\n  common_command += shipping_api_level_args\n\n  success = True\n  for sku_args in args_for_skus:\n    command = common_command + sku_args\n    proc = common.Run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    out, err = proc.communicate()\n    last_out_line = out.split()[-1] if out != \"\" else out\n    if proc.returncode == 0:\n      logger.info(\"Command `%s` returns 'compatible'\", ' '.join(command))\n    elif last_out_line.strip() == \"INCOMPATIBLE\":\n      logger.info(\"Command `%s` returns 'incompatible'\", ' '.join(command))\n      success = False\n    else:\n      raise common.ExternalError(\n          \"Failed to run command '{}' (exit code {}):\\nstdout:{}\\nstderr:{}\"\n          .format(' '.join(command), proc.returncode, out, err))\n    logger.info(\"stdout: %s\", out)\n    logger.info(\"stderr: %s\", err)\n\n  return success\n\n\ndef GetVintfFileList():\n  \"\"\"\n  Returns a list of VINTF metadata files that should be read from a target files\n  package before executing checkvintf.\n  \"\"\"\n  def PathToPatterns(path):\n    if path[-1] == '/':\n      path += '**'\n\n    # Loop over all the entries in DIR_SEARCH_PATHS and find one where the key\n    # is a prefix of path. In order to get find the correct prefix, sort the\n    # entries by decreasing length of their keys, so that we check if longer\n    # strings are prefixes before shorter strings. This is so that keys that\n    # are substrings of other keys (like /system vs /system_ext) are checked\n    # later, and we don't mistakenly mark a path that starts with /system_ext\n    # as starting with only /system.\n    for device_path, target_files_rel_paths in sorted(DIR_SEARCH_PATHS.items(), key=lambda i: len(i[0]), reverse=True):\n      if path.startswith(device_path):\n        suffix = path[len(device_path):]\n        return [rel_path + suffix for rel_path in target_files_rel_paths]\n    raise RuntimeError('Unrecognized path from checkvintf --dump-file-list: ' +\n                       path)\n\n  out = common.RunAndCheckOutput(['checkvintf', '--dump-file-list'])\n  paths = out.strip().split('\\n')\n  paths = sum((PathToPatterns(path) for path in paths if path), [])\n  return paths\n\ndef GetVintfApexUnzipPatterns():\n  \"\"\" Build unzip pattern for APEXes. \"\"\"\n  patterns = []\n  for target_files_rel_paths in DIR_SEARCH_PATHS.values():\n    for target_files_rel_path in target_files_rel_paths:\n      patterns.append(os.path.join(target_files_rel_path,\"apex/*\"))\n\n  return patterns\n\n\ndef PrepareApexDirectory(inp, dirmap):\n  \"\"\" Prepare /apex directory before running checkvintf\n\n  Apex binaries do not support dirmaps, in order to use these binaries we\n  need to move the APEXes from the extracted target file archives to the\n  expected device locations.\n\n  This simulates how apexd activates APEXes.\n  1. create {inp}/APEX which is treated as a \"/apex\" on device.\n  2. invoke apexd_host with APEXes.\n  \"\"\"\n\n  apex_dir = common.MakeTempDir('APEX')\n  # checkvintf needs /apex dirmap\n  dirmap['/apex'] = apex_dir\n\n  # Always create /apex directory for dirmap\n  os.makedirs(apex_dir, exist_ok=True)\n\n  # Invoke apexd_host to activate APEXes for checkvintf\n  apex_host = os.path.join(OPTIONS.search_path, 'bin', 'apexd_host')\n  cmd = [apex_host, '--tool_path', OPTIONS.search_path]\n  cmd += ['--apex_path', dirmap['/apex']]\n  for p in apex_utils.PARTITIONS:\n    if '/' + p in dirmap:\n      cmd += ['--' + p + '_path', dirmap['/' + p]]\n  common.RunAndCheckOutput(cmd)\n\n\ndef CheckVintfFromTargetFiles(inp, info_dict=None):\n  \"\"\"\n  Checks VINTF metadata of a target files zip.\n\n  Args:\n    inp: path to the target files archive.\n    info_dict: The build-time info dict. If None, it will be loaded from inp.\n\n  Returns:\n    True if VINTF check is skipped or compatible, False if incompatible. Raise\n    a RuntimeError if any error occurs.\n  \"\"\"\n  input_tmp = common.UnzipTemp(inp, GetVintfFileList() + GetVintfApexUnzipPatterns() + UNZIP_PATTERN)\n  return CheckVintfFromExtractedTargetFiles(input_tmp, info_dict)\n\n\ndef CheckVintf(inp, info_dict=None):\n  \"\"\"\n  Checks VINTF metadata of a target files zip or extracted target files\n  directory.\n\n  Args:\n    inp: path to the (possibly extracted) target files archive.\n    info_dict: The build-time info dict. If None, it will be loaded from inp.\n\n  Returns:\n    True if VINTF check is skipped or compatible, False if incompatible. Raise\n    a RuntimeError if any error occurs.\n  \"\"\"\n  if os.path.isdir(inp):\n    logger.info('Checking VINTF compatibility extracted target files...')\n    return CheckVintfFromExtractedTargetFiles(inp, info_dict)\n\n  if zipfile.is_zipfile(inp):\n    logger.info('Checking VINTF compatibility target files...')\n    return CheckVintfFromTargetFiles(inp, info_dict)\n\n  raise ValueError('{} is not a valid directory or zip file'.format(inp))\n\ndef CheckVintfIfTrebleEnabled(target_files, target_info):\n  \"\"\"Checks compatibility info of the input target files.\n\n  Metadata used for compatibility verification is retrieved from target_zip.\n\n  Compatibility should only be checked for devices that have enabled\n  Treble support.\n\n  Args:\n    target_files: Path to zip file containing the source files to be included\n        for OTA. Can also be the path to extracted directory.\n    target_info: The BuildInfo instance that holds the target build info.\n  \"\"\"\n\n  # Will only proceed if the target has enabled the Treble support (as well as\n  # having a /vendor partition).\n  if not HasTrebleEnabled(target_files, target_info):\n    return\n\n  # Skip adding the compatibility package as a workaround for b/114240221. The\n  # compatibility will always fail on devices without qualified kernels.\n  if OPTIONS.skip_compatibility_check:\n    return\n\n  if not CheckVintf(target_files, target_info):\n    raise RuntimeError(\"VINTF compatibility check failed\")\n\ndef HasTrebleEnabled(target_files, target_info):\n  def HasVendorPartition(target_files):\n    if os.path.isdir(target_files):\n      return os.path.isdir(os.path.join(target_files, \"VENDOR\"))\n    if zipfile.is_zipfile(target_files):\n      return HasPartition(zipfile.ZipFile(target_files, allowZip64=True), \"vendor\")\n    raise ValueError(\"Unknown target_files argument\")\n\n  return (HasVendorPartition(target_files) and\n          target_info.GetBuildProp(\"ro.treble.enabled\") == \"true\")\n\n\ndef HasPartition(target_files_zip, partition):\n  try:\n    target_files_zip.getinfo(partition.upper() + \"/\")\n    return True\n  except KeyError:\n    return False\n\n\ndef main(argv):\n  args = common.ParseOptions(argv, __doc__)\n  if len(args) != 1:\n    common.Usage(__doc__)\n    sys.exit(1)\n  common.InitLogging()\n  if not CheckVintf(args[0]):\n    sys.exit(1)\n\n\nif __name__ == '__main__':\n  try:\n    common.CloseInheritedPipes()\n    main(sys.argv[1:])\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/common.py",
    "content": "# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import print_function\n\nimport base64\nimport collections\nimport copy\nimport datetime\nimport errno\nimport fnmatch\nimport getopt\nimport getpass\nimport gzip\nimport importlib.util\nimport json\nimport logging\nimport logging.config\nimport os\nimport platform\nimport re\nimport shlex\nimport shutil\nimport subprocess\nimport stat\nimport sys\nimport tempfile\nimport threading\nimport time\nimport zipfile\n\nfrom typing import Iterable, Callable\nfrom dataclasses import dataclass\nfrom hashlib import sha1, sha256\n\nimport images\nimport sparse_img\nfrom blockimgdiff import BlockImageDiff\n\nlogger = logging.getLogger(__name__)\n\n\n@dataclass\nclass OptionHandler:\n  extra_long_opts: Iterable[str]\n  handler: Callable\n\nclass Options(object):\n\n  def __init__(self):\n    # Set up search path, in order to find framework/ and lib64/. At the time of\n    # running this function, user-supplied search path (`--path`) hasn't been\n    # available. So the value set here is the default, which might be overridden\n    # by commandline flag later.\n    exec_path = os.path.realpath(sys.argv[0])\n    if exec_path.endswith('.py'):\n      script_name = os.path.basename(exec_path)\n      # logger hasn't been initialized yet at this point. Use print to output\n      # warnings.\n      print(\n          'Warning: releasetools script should be invoked as hermetic Python '\n          'executable -- build and run `{}` directly.'.format(\n              script_name[:-3]),\n          file=sys.stderr)\n    self.search_path = os.path.dirname(os.path.dirname(exec_path))\n\n    self.signapk_path = \"framework/signapk.jar\"  # Relative to search_path\n    if not os.path.exists(os.path.join(self.search_path, self.signapk_path)):\n      if \"ANDROID_HOST_OUT\" in os.environ:\n        self.search_path = os.environ[\"ANDROID_HOST_OUT\"]\n    self.signapk_shared_library_path = \"lib64\"   # Relative to search_path\n    self.extra_signapk_args = []\n    self.aapt2_path = \"aapt2\"\n    self.java_path = \"java\"  # Use the one on the path by default.\n    self.java_args = [\"-Xmx4096m\"]  # The default JVM args.\n    self.android_jar_path = None\n    self.public_key_suffix = \".x509.pem\"\n    self.private_key_suffix = \".pk8\"\n    # use otatools built boot_signer by default\n    self.verbose = False\n    self.tempfiles = []\n    self.device_specific = None\n    self.extras = {}\n    self.info_dict = None\n    self.source_info_dict = None\n    self.target_info_dict = None\n    self.worker_threads = None\n    # Stash size cannot exceed cache_size * threshold.\n    self.cache_size = None\n    self.stash_threshold = 0.8\n    self.logfile = None\n\n\nOPTIONS = Options()\n\n# The block size that's used across the releasetools scripts.\nBLOCK_SIZE = 4096\n\n# Values for \"certificate\" in apkcerts that mean special things.\nSPECIAL_CERT_STRINGS = (\"PRESIGNED\", \"EXTERNAL\")\n\n# The partitions allowed to be signed by AVB (Android Verified Boot 2.0). Note\n# that system_other is not in the list because we don't want to include its\n# descriptor into vbmeta.img. When adding a new entry here, the\n# AVB_FOOTER_ARGS_BY_PARTITION in sign_target_files_apks need to be updated\n# accordingly.\nAVB_PARTITIONS = ('boot', 'init_boot', 'dtbo', 'odm', 'product', 'pvmfw',\n                  'recovery', 'system', 'system_ext', 'vendor', 'vendor_boot',\n                  'vendor_kernel_boot', 'vendor_dlkm', 'odm_dlkm',\n                  'system_dlkm')\n\n# Chained VBMeta partitions.\nAVB_VBMETA_PARTITIONS = ('vbmeta_system', 'vbmeta_vendor')\n\n# avbtool arguments name\nAVB_ARG_NAME_INCLUDE_DESC_FROM_IMG = '--include_descriptors_from_image'\nAVB_ARG_NAME_CHAIN_PARTITION = '--chain_partition'\n\n# Partitions that should have their care_map added to META/care_map.pb\nPARTITIONS_WITH_CARE_MAP = [\n    'system',\n    'vendor',\n    'product',\n    'system_ext',\n    'odm',\n    'vendor_dlkm',\n    'odm_dlkm',\n    'system_dlkm',\n]\n\n# Partitions with a build.prop file\nPARTITIONS_WITH_BUILD_PROP = PARTITIONS_WITH_CARE_MAP + ['boot', 'init_boot']\n\n# See sysprop.mk. If file is moved, add new search paths here; don't remove\n# existing search paths.\nRAMDISK_BUILD_PROP_REL_PATHS = ['system/etc/ramdisk/build.prop']\n\n\n@dataclass\nclass AvbChainedPartitionArg:\n  \"\"\"The required arguments for avbtool --chain_partition.\"\"\"\n  partition: str\n  rollback_index_location: int\n  pubkey_path: str\n\n  def to_string(self):\n    \"\"\"Convert to string command arguments.\"\"\"\n    return '{}:{}:{}'.format(\n        self.partition, self.rollback_index_location, self.pubkey_path)\n\n\nclass ErrorCode(object):\n  \"\"\"Define error_codes for failures that happen during the actual\n  update package installation.\n\n  Error codes 0-999 are reserved for failures before the package\n  installation (i.e. low battery, package verification failure).\n  Detailed code in 'bootable/recovery/error_code.h' \"\"\"\n\n  SYSTEM_VERIFICATION_FAILURE = 1000\n  SYSTEM_UPDATE_FAILURE = 1001\n  SYSTEM_UNEXPECTED_CONTENTS = 1002\n  SYSTEM_NONZERO_CONTENTS = 1003\n  SYSTEM_RECOVER_FAILURE = 1004\n  VENDOR_VERIFICATION_FAILURE = 2000\n  VENDOR_UPDATE_FAILURE = 2001\n  VENDOR_UNEXPECTED_CONTENTS = 2002\n  VENDOR_NONZERO_CONTENTS = 2003\n  VENDOR_RECOVER_FAILURE = 2004\n  OEM_PROP_MISMATCH = 3000\n  FINGERPRINT_MISMATCH = 3001\n  THUMBPRINT_MISMATCH = 3002\n  OLDER_BUILD = 3003\n  DEVICE_MISMATCH = 3004\n  BAD_PATCH_FILE = 3005\n  INSUFFICIENT_CACHE_SPACE = 3006\n  TUNE_PARTITION_FAILURE = 3007\n  APPLY_PATCH_FAILURE = 3008\n\n\nclass ExternalError(RuntimeError):\n  pass\n\n\ndef InitLogging():\n  DEFAULT_LOGGING_CONFIG = {\n      'version': 1,\n      'disable_existing_loggers': False,\n      'formatters': {\n          'standard': {\n              'format':\n                  '%(asctime)s - %(filename)s - %(levelname)-8s: %(message)s',\n              'datefmt': '%Y-%m-%d %H:%M:%S',\n          },\n      },\n      'handlers': {\n          'default': {\n              'class': 'logging.StreamHandler',\n              'formatter': 'standard',\n              'level': 'WARNING',\n          },\n      },\n      'loggers': {\n          '': {\n              'handlers': ['default'],\n              'propagate': True,\n              'level': 'NOTSET',\n          }\n      }\n  }\n  env_config = os.getenv('LOGGING_CONFIG')\n  if env_config:\n    with open(env_config) as f:\n      config = json.load(f)\n  else:\n    config = DEFAULT_LOGGING_CONFIG\n\n    # Increase the logging level for verbose mode.\n    if OPTIONS.verbose:\n      config = copy.deepcopy(config)\n      config['handlers']['default']['level'] = 'INFO'\n\n    if OPTIONS.logfile:\n      config = copy.deepcopy(config)\n      config['handlers']['logfile'] = {\n          'class': 'logging.FileHandler',\n          'formatter': 'standard',\n          'level': 'INFO',\n          'mode': 'w',\n          'filename': OPTIONS.logfile,\n      }\n      config['loggers']['']['handlers'].append('logfile')\n\n  logging.config.dictConfig(config)\n\n\ndef FindHostToolPath(tool_name):\n  \"\"\"Finds the path to the host tool.\n\n  Args:\n    tool_name: name of the tool to find\n  Returns:\n    path to the tool if found under the same directory as this binary is located at. If not found,\n    tool_name is returned.\n  \"\"\"\n  my_dir = os.path.dirname(os.path.realpath(sys.argv[0]))\n  tool_path = os.path.join(my_dir, tool_name)\n  if os.path.exists(tool_path):\n    return tool_path\n\n  return tool_name\n\n\ndef Run(args, verbose=None, **kwargs):\n  \"\"\"Creates and returns a subprocess.Popen object.\n\n  Args:\n    args: The command represented as a list of strings.\n    verbose: Whether the commands should be shown. Default to the global\n        verbosity if unspecified.\n    kwargs: Any additional args to be passed to subprocess.Popen(), such as env,\n        stdin, etc. stdout and stderr will default to subprocess.PIPE and\n        subprocess.STDOUT respectively unless caller specifies any of them.\n        universal_newlines will default to True, as most of the users in\n        releasetools expect string output.\n\n  Returns:\n    A subprocess.Popen object.\n  \"\"\"\n  if 'stdout' not in kwargs and 'stderr' not in kwargs:\n    kwargs['stdout'] = subprocess.PIPE\n    kwargs['stderr'] = subprocess.STDOUT\n  if 'universal_newlines' not in kwargs:\n    kwargs['universal_newlines'] = True\n\n  if args:\n    # Make a copy of args in case client relies on the content of args later.\n    args = args[:]\n    args[0] = FindHostToolPath(args[0])\n\n  if verbose is None:\n    verbose = OPTIONS.verbose\n\n  # Don't log any if caller explicitly says so.\n  if verbose:\n    logger.info(\"  Running: \\\"%s\\\"\", \" \".join(args))\n  return subprocess.Popen(args, **kwargs)\n\n\ndef RunAndCheckOutput(args, verbose=None, **kwargs):\n  \"\"\"Runs the given command and returns the output.\n\n  Args:\n    args: The command represented as a list of strings.\n    verbose: Whether the commands should be shown. Default to the global\n        verbosity if unspecified.\n    kwargs: Any additional args to be passed to subprocess.Popen(), such as env,\n        stdin, etc. stdout and stderr will default to subprocess.PIPE and\n        subprocess.STDOUT respectively unless caller specifies any of them.\n\n  Returns:\n    The output string.\n\n  Raises:\n    ExternalError: On non-zero exit from the command.\n  \"\"\"\n  if verbose is None:\n    verbose = OPTIONS.verbose\n  proc = Run(args, verbose=verbose, **kwargs)\n  output, _ = proc.communicate()\n  if output is None:\n    output = \"\"\n  # Don't log any if caller explicitly says so.\n  if verbose:\n    logger.info(\"%s\", output.rstrip())\n  if proc.returncode != 0:\n    raise ExternalError(\n        \"Failed to run command '{}' (exit code {}):\\n{}\".format(\n            args, proc.returncode, output))\n  return output\n\n\ndef RoundUpTo4K(value):\n  rounded_up = value + 4095\n  return rounded_up - (rounded_up % 4096)\n\n\ndef CloseInheritedPipes():\n  \"\"\" Gmake in MAC OS has file descriptor (PIPE) leak. We close those fds\n  before doing other work.\"\"\"\n  if platform.system() != \"Darwin\":\n    return\n  for d in range(3, 1025):\n    try:\n      stat = os.fstat(d)\n      if stat is not None:\n        pipebit = stat[0] & 0x1000\n        if pipebit != 0:\n          os.close(d)\n    except OSError:\n      pass\n\n\nclass BuildInfo(object):\n  \"\"\"A class that holds the information for a given build.\n\n  This class wraps up the property querying for a given source or target build.\n  It abstracts away the logic of handling OEM-specific properties, and caches\n  the commonly used properties such as fingerprint.\n\n  There are two types of info dicts: a) build-time info dict, which is generated\n  at build time (i.e. included in a target_files zip); b) OEM info dict that is\n  specified at package generation time (via command line argument\n  '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not\n  having \"oem_fingerprint_properties\" in build-time info dict), all the queries\n  would be answered based on build-time info dict only. Otherwise if using\n  OEM-specific properties, some of them will be calculated from two info dicts.\n\n  Users can query properties similarly as using a dict() (e.g. info['fstab']),\n  or to query build properties via GetBuildProp() or GetPartitionBuildProp().\n\n  Attributes:\n    info_dict: The build-time info dict.\n    is_ab: Whether it's a build that uses A/B OTA.\n    oem_dicts: A list of OEM dicts.\n    oem_props: A list of OEM properties that should be read from OEM dicts; None\n        if the build doesn't use any OEM-specific property.\n    fingerprint: The fingerprint of the build, which would be calculated based\n        on OEM properties if applicable.\n    device: The device name, which could come from OEM dicts if applicable.\n  \"\"\"\n\n  _RO_PRODUCT_RESOLVE_PROPS = [\"ro.product.brand\", \"ro.product.device\",\n                               \"ro.product.manufacturer\", \"ro.product.model\",\n                               \"ro.product.name\"]\n  _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_CURRENT = [\n      \"product\", \"odm\", \"vendor\", \"system_ext\", \"system\"]\n  _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_ANDROID_10 = [\n      \"product\", \"product_services\", \"odm\", \"vendor\", \"system\"]\n  _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_LEGACY = []\n\n  # The length of vbmeta digest to append to the fingerprint\n  _VBMETA_DIGEST_SIZE_USED = 8\n\n  def __init__(self, info_dict, oem_dicts=None, use_legacy_id=False):\n    \"\"\"Initializes a BuildInfo instance with the given dicts.\n\n    Note that it only wraps up the given dicts, without making copies.\n\n    Arguments:\n      info_dict: The build-time info dict.\n      oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note\n          that it always uses the first dict to calculate the fingerprint or the\n          device name. The rest would be used for asserting OEM properties only\n          (e.g. one package can be installed on one of these devices).\n      use_legacy_id: Use the legacy build id to construct the fingerprint. This\n          is used when we need a BuildInfo class, while the vbmeta digest is\n          unavailable.\n\n    Raises:\n      ValueError: On invalid inputs.\n    \"\"\"\n    self.info_dict = info_dict\n    self.oem_dicts = oem_dicts\n\n    self._is_ab = info_dict.get(\"ab_update\") == \"true\"\n    self.use_legacy_id = use_legacy_id\n\n    # Skip _oem_props if oem_dicts is None to use BuildInfo in\n    # sign_target_files_apks\n    if self.oem_dicts:\n      self._oem_props = info_dict.get(\"oem_fingerprint_properties\")\n    else:\n      self._oem_props = None\n\n    def check_fingerprint(fingerprint):\n      if (\" \" in fingerprint or any(ord(ch) > 127 for ch in fingerprint)):\n        raise ValueError(\n            'Invalid build fingerprint: \"{}\". See the requirement in Android CDD '\n            \"3.2.2. Build Parameters.\".format(fingerprint))\n\n    self._partition_fingerprints = {}\n    for partition in PARTITIONS_WITH_BUILD_PROP:\n      try:\n        fingerprint = self.CalculatePartitionFingerprint(partition)\n        check_fingerprint(fingerprint)\n        self._partition_fingerprints[partition] = fingerprint\n      except ExternalError:\n        continue\n    if \"system\" in self._partition_fingerprints:\n      # system_other is not included in PARTITIONS_WITH_BUILD_PROP, but does\n      # need a fingerprint when creating the image.\n      self._partition_fingerprints[\n          \"system_other\"] = self._partition_fingerprints[\"system\"]\n\n    # These two should be computed only after setting self._oem_props.\n    self._device = self.GetOemProperty(\"ro.product.device\")\n    self._fingerprint = self.CalculateFingerprint()\n    check_fingerprint(self._fingerprint)\n\n  @property\n  def is_ab(self):\n    return self._is_ab\n\n  @property\n  def device(self):\n    return self._device\n\n  @property\n  def fingerprint(self):\n    return self._fingerprint\n\n  @property\n  def is_vabc(self):\n    return self.info_dict.get(\"virtual_ab_compression\") == \"true\"\n\n  @property\n  def is_android_r(self):\n    system_prop = self.info_dict.get(\"system.build.prop\")\n    return system_prop and system_prop.GetProp(\"ro.build.version.release\") == \"11\"\n\n  @property\n  def is_release_key(self):\n    system_prop = self.info_dict.get(\"build.prop\")\n    return system_prop and system_prop.GetProp(\"ro.build.tags\") == \"release-key\"\n\n  @property\n  def vabc_compression_param(self):\n    return self.get(\"virtual_ab_compression_method\", \"\")\n\n  @property\n  def vabc_cow_version(self):\n    return self.get(\"virtual_ab_cow_version\", \"\")\n\n  @property\n  def vendor_api_level(self):\n    vendor_prop = self.info_dict.get(\"vendor.build.prop\")\n    if not vendor_prop:\n      return -1\n\n    props = [\n        \"ro.board.first_api_level\",\n        \"ro.product.first_api_level\",\n    ]\n    for prop in props:\n      value = vendor_prop.GetProp(prop)\n      try:\n        return int(value)\n      except:\n        pass\n    return -1\n\n  @property\n  def is_vabc_xor(self):\n    vendor_prop = self.info_dict.get(\"vendor.build.prop\")\n    vabc_xor_enabled = vendor_prop and \\\n        vendor_prop.GetProp(\"ro.virtual_ab.compression.xor.enabled\") == \"true\"\n    return vabc_xor_enabled\n\n  @property\n  def vendor_suppressed_vabc(self):\n    vendor_prop = self.info_dict.get(\"vendor.build.prop\")\n    vabc_suppressed = vendor_prop and \\\n        vendor_prop.GetProp(\"ro.vendor.build.dont_use_vabc\")\n    return vabc_suppressed and vabc_suppressed.lower() == \"true\"\n\n  @property\n  def oem_props(self):\n    return self._oem_props\n\n  def __getitem__(self, key):\n    return self.info_dict[key]\n\n  def __setitem__(self, key, value):\n    self.info_dict[key] = value\n\n  def get(self, key, default=None):\n    return self.info_dict.get(key, default)\n\n  def items(self):\n    return self.info_dict.items()\n\n  def _GetRawBuildProp(self, prop, partition):\n    prop_file = '{}.build.prop'.format(\n        partition) if partition else 'build.prop'\n    partition_props = self.info_dict.get(prop_file)\n    if not partition_props:\n      return None\n    return partition_props.GetProp(prop)\n\n  def GetPartitionBuildProp(self, prop, partition):\n    \"\"\"Returns the inquired build property for the provided partition.\"\"\"\n\n    # Boot image and init_boot image uses ro.[product.]bootimage instead of boot.\n    # This comes from the generic ramdisk\n    prop_partition = \"bootimage\" if partition == \"boot\" or partition == \"init_boot\" else partition\n\n    # If provided a partition for this property, only look within that\n    # partition's build.prop.\n    if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:\n      prop = prop.replace(\"ro.product\", \"ro.product.{}\".format(prop_partition))\n    else:\n      prop = prop.replace(\"ro.\", \"ro.{}.\".format(prop_partition))\n\n    prop_val = self._GetRawBuildProp(prop, partition)\n    if prop_val is not None:\n      return prop_val\n    raise ExternalError(\"couldn't find %s in %s.build.prop\" %\n                        (prop, partition))\n\n  def GetBuildProp(self, prop):\n    \"\"\"Returns the inquired build property from the standard build.prop file.\"\"\"\n    if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:\n      return self._ResolveRoProductBuildProp(prop)\n\n    if prop == \"ro.build.id\":\n      return self._GetBuildId()\n\n    prop_val = self._GetRawBuildProp(prop, None)\n    if prop_val is not None:\n      return prop_val\n\n    raise ExternalError(\"couldn't find %s in build.prop\" % (prop,))\n\n  def _ResolveRoProductBuildProp(self, prop):\n    \"\"\"Resolves the inquired ro.product.* build property\"\"\"\n    prop_val = self._GetRawBuildProp(prop, None)\n    if prop_val:\n      return prop_val\n\n    default_source_order = self._GetRoProductPropsDefaultSourceOrder()\n    source_order_val = self._GetRawBuildProp(\n        \"ro.product.property_source_order\", None)\n    if source_order_val:\n      source_order = source_order_val.split(\",\")\n    else:\n      source_order = default_source_order\n\n    # Check that all sources in ro.product.property_source_order are valid\n    if any([x not in default_source_order for x in source_order]):\n      raise ExternalError(\n          \"Invalid ro.product.property_source_order '{}'\".format(source_order))\n\n    for source_partition in source_order:\n      source_prop = prop.replace(\n          \"ro.product\", \"ro.product.{}\".format(source_partition), 1)\n      prop_val = self._GetRawBuildProp(source_prop, source_partition)\n      if prop_val:\n        return prop_val\n\n    raise ExternalError(\"couldn't resolve {}\".format(prop))\n\n  def _GetRoProductPropsDefaultSourceOrder(self):\n    # NOTE: refer to CDDs and android.os.Build.VERSION for the definition and\n    # values of these properties for each Android release.\n    android_codename = self._GetRawBuildProp(\"ro.build.version.codename\", None)\n    if android_codename == \"REL\":\n      android_version = self._GetRawBuildProp(\"ro.build.version.release\", None)\n      if android_version == \"10\":\n        return BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_ANDROID_10\n      # NOTE: float() conversion of android_version will have rounding error.\n      # We are checking for \"9\" or less, and using \"< 10\" is well outside of\n      # possible floating point rounding.\n      try:\n        android_version_val = float(android_version)\n      except ValueError:\n        android_version_val = 0\n      if android_version_val < 10:\n        return BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_LEGACY\n    return BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_CURRENT\n\n  def _GetPlatformVersion(self):\n    version_sdk = self.GetBuildProp(\"ro.build.version.sdk\")\n    # init code switches to version_release_or_codename (see b/158483506). After\n    # API finalization, release_or_codename will be the same as release. This\n    # is the best effort to support pre-S dev stage builds.\n    if int(version_sdk) >= 30:\n      try:\n        return self.GetBuildProp(\"ro.build.version.release_or_codename\")\n      except ExternalError:\n        logger.warning('Failed to find ro.build.version.release_or_codename')\n\n    return self.GetBuildProp(\"ro.build.version.release\")\n\n  def _GetBuildId(self):\n    build_id = self._GetRawBuildProp(\"ro.build.id\", None)\n    if build_id:\n      return build_id\n\n    legacy_build_id = self.GetBuildProp(\"ro.build.legacy.id\")\n    if not legacy_build_id:\n      raise ExternalError(\"Couldn't find build id in property file\")\n\n    if self.use_legacy_id:\n      return legacy_build_id\n\n    # Append the top 8 chars of vbmeta digest to the existing build id. The\n    # logic needs to match the one in init, so that OTA can deliver correctly.\n    avb_enable = self.info_dict.get(\"avb_enable\") == \"true\"\n    if not avb_enable:\n      raise ExternalError(\"AVB isn't enabled when using legacy build id\")\n\n    vbmeta_digest = self.info_dict.get(\"vbmeta_digest\")\n    if not vbmeta_digest:\n      raise ExternalError(\"Vbmeta digest isn't provided when using legacy build\"\n                          \" id\")\n    if len(vbmeta_digest) < self._VBMETA_DIGEST_SIZE_USED:\n      raise ExternalError(\"Invalid vbmeta digest \" + vbmeta_digest)\n\n    digest_prefix = vbmeta_digest[:self._VBMETA_DIGEST_SIZE_USED]\n    return legacy_build_id + '.' + digest_prefix\n\n  def _GetPartitionPlatformVersion(self, partition):\n    try:\n      return self.GetPartitionBuildProp(\"ro.build.version.release_or_codename\",\n                                        partition)\n    except ExternalError:\n      return self.GetPartitionBuildProp(\"ro.build.version.release\",\n                                        partition)\n\n  def GetOemProperty(self, key):\n    if self.oem_props is not None and key in self.oem_props:\n      return self.oem_dicts[0][key]\n    return self.GetBuildProp(key)\n\n  def GetPartitionFingerprint(self, partition):\n    return self._partition_fingerprints.get(partition, None)\n\n  def CalculatePartitionFingerprint(self, partition):\n    try:\n      return self.GetPartitionBuildProp(\"ro.build.fingerprint\", partition)\n    except ExternalError:\n      return \"{}/{}/{}:{}/{}/{}:{}/{}\".format(\n          self.GetPartitionBuildProp(\"ro.product.brand\", partition),\n          self.GetPartitionBuildProp(\"ro.product.name\", partition),\n          self.GetPartitionBuildProp(\"ro.product.device\", partition),\n          self._GetPartitionPlatformVersion(partition),\n          self.GetPartitionBuildProp(\"ro.build.id\", partition),\n          self.GetPartitionBuildProp(\n              \"ro.build.version.incremental\", partition),\n          self.GetPartitionBuildProp(\"ro.build.type\", partition),\n          self.GetPartitionBuildProp(\"ro.build.tags\", partition))\n\n  def CalculateFingerprint(self):\n    if self.oem_props is None:\n      try:\n        return self.GetBuildProp(\"ro.build.fingerprint\")\n      except ExternalError:\n        return \"{}/{}/{}:{}/{}/{}:{}/{}\".format(\n            self.GetBuildProp(\"ro.product.brand\"),\n            self.GetBuildProp(\"ro.product.name\"),\n            self.GetBuildProp(\"ro.product.device\"),\n            self._GetPlatformVersion(),\n            self.GetBuildProp(\"ro.build.id\"),\n            self.GetBuildProp(\"ro.build.version.incremental\"),\n            self.GetBuildProp(\"ro.build.type\"),\n            self.GetBuildProp(\"ro.build.tags\"))\n    return \"%s/%s/%s:%s\" % (\n        self.GetOemProperty(\"ro.product.brand\"),\n        self.GetOemProperty(\"ro.product.name\"),\n        self.GetOemProperty(\"ro.product.device\"),\n        self.GetBuildProp(\"ro.build.thumbprint\"))\n\n  def WriteMountOemScript(self, script):\n    assert self.oem_props is not None\n    recovery_mount_options = self.info_dict.get(\"recovery_mount_options\")\n    script.Mount(\"/oem\", recovery_mount_options)\n\n  def WriteDeviceAssertions(self, script, oem_no_mount):\n    # Read the property directly if not using OEM properties.\n    if not self.oem_props:\n      script.AssertDevice(self.device)\n      return\n\n    # Otherwise assert OEM properties.\n    if not self.oem_dicts:\n      raise ExternalError(\n          \"No OEM file provided to answer expected assertions\")\n\n    for prop in self.oem_props.split():\n      values = []\n      for oem_dict in self.oem_dicts:\n        if prop in oem_dict:\n          values.append(oem_dict[prop])\n      if not values:\n        raise ExternalError(\n            \"The OEM file is missing the property %s\" % (prop,))\n      script.AssertOemProperty(prop, values, oem_no_mount)\n\n\ndef DoesInputFileContain(input_file, fn):\n  \"\"\"Check whether the input target_files.zip contain an entry `fn`\"\"\"\n  if isinstance(input_file, zipfile.ZipFile):\n    return fn in input_file.namelist()\n  elif zipfile.is_zipfile(input_file):\n    with zipfile.ZipFile(input_file, \"r\", allowZip64=True) as zfp:\n      return fn in zfp.namelist()\n  else:\n    if not os.path.isdir(input_file):\n      raise ValueError(\n          \"Invalid input_file, accepted inputs are ZipFile object, path to .zip file on disk, or path to extracted directory. Actual: \" + input_file)\n    path = os.path.join(input_file, *fn.split(\"/\"))\n    return os.path.exists(path)\n\n\ndef ReadBytesFromInputFile(input_file, fn):\n  \"\"\"Reads the bytes of fn from input zipfile or directory.\"\"\"\n  if isinstance(input_file, zipfile.ZipFile):\n    return input_file.read(fn)\n  elif zipfile.is_zipfile(input_file):\n    with zipfile.ZipFile(input_file, \"r\", allowZip64=True) as zfp:\n      return zfp.read(fn)\n  else:\n    if not os.path.isdir(input_file):\n      raise ValueError(\n          \"Invalid input_file, accepted inputs are ZipFile object, path to .zip file on disk, or path to extracted directory. Actual: \" + input_file)\n    path = os.path.join(input_file, *fn.split(\"/\"))\n    try:\n      with open(path, \"rb\") as f:\n        return f.read()\n    except IOError as e:\n      if e.errno == errno.ENOENT:\n        raise KeyError(fn)\n\n\ndef ReadFromInputFile(input_file, fn):\n  \"\"\"Reads the str contents of fn from input zipfile or directory.\"\"\"\n  return ReadBytesFromInputFile(input_file, fn).decode()\n\n\ndef WriteBytesToInputFile(input_file, fn, data):\n  \"\"\"Write bytes |data| contents to fn of input zipfile or directory.\"\"\"\n  if isinstance(input_file, zipfile.ZipFile):\n    with input_file.open(fn, \"w\") as entry_fp:\n      return entry_fp.write(data)\n  elif zipfile.is_zipfile(input_file):\n    with zipfile.ZipFile(input_file, \"r\", allowZip64=True) as zfp:\n      with zfp.open(fn, \"w\") as entry_fp:\n        return entry_fp.write(data)\n  else:\n    if not os.path.isdir(input_file):\n      raise ValueError(\n          \"Invalid input_file, accepted inputs are ZipFile object, path to .zip file on disk, or path to extracted directory. Actual: \" + input_file)\n    path = os.path.join(input_file, *fn.split(\"/\"))\n    try:\n      with open(path, \"wb\") as f:\n        return f.write(data)\n    except IOError as e:\n      if e.errno == errno.ENOENT:\n        raise KeyError(fn)\n\n\ndef WriteToInputFile(input_file, fn, str: str):\n  \"\"\"Write str content to fn of input file or directory\"\"\"\n  return WriteBytesToInputFile(input_file, fn, str.encode())\n\n\ndef ExtractFromInputFile(input_file, fn):\n  \"\"\"Extracts the contents of fn from input zipfile or directory into a file.\"\"\"\n  if isinstance(input_file, zipfile.ZipFile):\n    tmp_file = MakeTempFile(os.path.basename(fn))\n    with open(tmp_file, 'wb') as f:\n      f.write(input_file.read(fn))\n    return tmp_file\n  elif zipfile.is_zipfile(input_file):\n    with zipfile.ZipFile(input_file, \"r\", allowZip64=True) as zfp:\n      tmp_file = MakeTempFile(os.path.basename(fn))\n      with open(tmp_file, \"wb\") as fp:\n        fp.write(zfp.read(fn))\n      return tmp_file\n  else:\n    if not os.path.isdir(input_file):\n      raise ValueError(\n          \"Invalid input_file, accepted inputs are ZipFile object, path to .zip file on disk, or path to extracted directory. Actual: \" + input_file)\n    file = os.path.join(input_file, *fn.split(\"/\"))\n    if not os.path.exists(file):\n      raise KeyError(fn)\n    return file\n\n\nclass RamdiskFormat(object):\n  LZ4 = 1\n  GZ = 2\n\n\ndef GetRamdiskFormat(info_dict):\n  if info_dict.get('lz4_ramdisks') == 'true':\n    ramdisk_format = RamdiskFormat.LZ4\n  else:\n    ramdisk_format = RamdiskFormat.GZ\n  return ramdisk_format\n\n\ndef LoadInfoDict(input_file, repacking=False):\n  \"\"\"Loads the key/value pairs from the given input target_files.\n\n  It reads `META/misc_info.txt` file in the target_files input, does validation\n  checks and returns the parsed key/value pairs for to the given build. It's\n  usually called early when working on input target_files files, e.g. when\n  generating OTAs, or signing builds. Note that the function may be called\n  against an old target_files file (i.e. from past dessert releases). So the\n  property parsing needs to be backward compatible.\n\n  In a `META/misc_info.txt`, a few properties are stored as links to the files\n  in the PRODUCT_OUT directory. It works fine with the build system. However,\n  they are no longer available when (re)generating images from target_files zip.\n  When `repacking` is True, redirect these properties to the actual files in the\n  unzipped directory.\n\n  Args:\n    input_file: The input target_files file, which could be an open\n        zipfile.ZipFile instance, or a str for the dir that contains the files\n        unzipped from a target_files file.\n    repacking: Whether it's trying repack an target_files file after loading the\n        info dict (default: False). If so, it will rewrite a few loaded\n        properties (e.g. selinux_fc, root_dir) to point to the actual files in\n        target_files file. When doing repacking, `input_file` must be a dir.\n\n  Returns:\n    A dict that contains the parsed key/value pairs.\n\n  Raises:\n    AssertionError: On invalid input arguments.\n    ValueError: On malformed input values.\n  \"\"\"\n  if repacking:\n    assert isinstance(input_file, str), \\\n        \"input_file must be a path str when doing repacking\"\n\n  def read_helper(fn):\n    return ReadFromInputFile(input_file, fn)\n\n  try:\n    d = LoadDictionaryFromLines(read_helper(\"META/misc_info.txt\").split(\"\\n\"))\n  except KeyError:\n    raise ValueError(\"Failed to find META/misc_info.txt in input target-files\")\n\n  if \"recovery_api_version\" not in d:\n    raise ValueError(\"Failed to find 'recovery_api_version'\")\n  if \"fstab_version\" not in d:\n    raise ValueError(\"Failed to find 'fstab_version'\")\n\n  if repacking:\n    # \"selinux_fc\" properties should point to the file_contexts files\n    # (file_contexts.bin) under META/.\n    for key in d:\n      if key.endswith(\"selinux_fc\"):\n        fc_basename = os.path.basename(d[key])\n        fc_config = os.path.join(input_file, \"META\", fc_basename)\n        assert os.path.exists(fc_config), \"{} does not exist\".format(fc_config)\n\n        d[key] = fc_config\n\n    # Similarly we need to redirect \"root_dir\", and \"root_fs_config\".\n    d[\"root_dir\"] = os.path.join(input_file, \"ROOT\")\n    d[\"root_fs_config\"] = os.path.join(\n        input_file, \"META\", \"root_filesystem_config.txt\")\n\n    partitions = [\"system\", \"vendor\", \"system_ext\", \"product\", \"odm\",\n                  \"vendor_dlkm\", \"odm_dlkm\", \"system_dlkm\"]\n    # Redirect {partition}_base_fs_file for each of the named partitions.\n    for part_name in partitions:\n      key_name = part_name + \"_base_fs_file\"\n      if key_name not in d:\n        continue\n      basename = os.path.basename(d[key_name])\n      base_fs_file = os.path.join(input_file, \"META\", basename)\n      if os.path.exists(base_fs_file):\n        d[key_name] = base_fs_file\n      else:\n        logger.warning(\n            \"Failed to find %s base fs file: %s\", part_name, base_fs_file)\n        del d[key_name]\n\n    # Redirecting helper for optional properties like erofs_compress_hints\n    def redirect_file(prop, filename):\n      if prop not in d:\n        return\n      config_file = os.path.join(input_file, \"META/\" + filename)\n      if os.path.exists(config_file):\n        d[prop] = config_file\n      else:\n        logger.warning(\n            \"Failed to find %s fro %s\", filename, prop)\n        del d[prop]\n\n    # Redirect erofs_[default_]compress_hints files\n    redirect_file(\"erofs_default_compress_hints\",\n                  \"erofs_default_compress_hints.txt\")\n    for part in partitions:\n      redirect_file(part + \"_erofs_compress_hints\",\n                    part + \"_erofs_compress_hints.txt\")\n\n  def makeint(key):\n    if key in d:\n      d[key] = int(d[key], 0)\n\n  makeint(\"recovery_api_version\")\n  makeint(\"blocksize\")\n  makeint(\"system_size\")\n  makeint(\"vendor_size\")\n  makeint(\"userdata_size\")\n  makeint(\"cache_size\")\n  makeint(\"recovery_size\")\n  makeint(\"fstab_version\")\n\n  boot_images = \"boot.img\"\n  if \"boot_images\" in d:\n    boot_images = d[\"boot_images\"]\n  for b in boot_images.split():\n    makeint(b.replace(\".img\", \"_size\"))\n\n  # Load recovery fstab if applicable.\n  d[\"fstab\"] = _FindAndLoadRecoveryFstab(d, input_file, read_helper)\n  ramdisk_format = GetRamdiskFormat(d)\n\n  # Tries to load the build props for all partitions with care_map, including\n  # system and vendor.\n  for partition in PARTITIONS_WITH_BUILD_PROP:\n    partition_prop = \"{}.build.prop\".format(partition)\n    d[partition_prop] = PartitionBuildProps.FromInputFile(\n        input_file, partition, ramdisk_format=ramdisk_format)\n  d[\"build.prop\"] = d[\"system.build.prop\"]\n\n  if d.get(\"avb_enable\") == \"true\":\n    build_info = BuildInfo(d, use_legacy_id=True)\n    # Set up the salt for partitions without build.prop\n    if build_info.fingerprint:\n      if \"fingerprint\" not in d:\n        d[\"fingerprint\"] = build_info.fingerprint\n      if \"avb_salt\" not in d:\n        d[\"avb_salt\"] = sha256(build_info.fingerprint.encode()).hexdigest()\n    # Set the vbmeta digest if exists\n    try:\n      d[\"vbmeta_digest\"] = read_helper(\"META/vbmeta_digest.txt\").rstrip()\n    except KeyError:\n      pass\n\n  try:\n    d[\"ab_partitions\"] = read_helper(\"META/ab_partitions.txt\").split(\"\\n\")\n  except KeyError:\n    logger.warning(\"Can't find META/ab_partitions.txt\")\n  return d\n\n\ndef LoadListFromFile(file_path):\n  with open(file_path) as f:\n    return f.read().splitlines()\n\n\ndef LoadDictionaryFromFile(file_path):\n  lines = LoadListFromFile(file_path)\n  return LoadDictionaryFromLines(lines)\n\n\ndef LoadDictionaryFromLines(lines):\n  d = {}\n  for line in lines:\n    line = line.strip()\n    if not line or line.startswith(\"#\"):\n      continue\n    if \"=\" in line:\n      name, value = line.split(\"=\", 1)\n      d[name] = value\n  return d\n\n\nclass PartitionBuildProps(object):\n  \"\"\"The class holds the build prop of a particular partition.\n\n  This class loads the build.prop and holds the build properties for a given\n  partition. It also partially recognizes the 'import' statement in the\n  build.prop; and calculates alternative values of some specific build\n  properties during runtime.\n\n  Attributes:\n    input_file: a zipped target-file or an unzipped target-file directory.\n    partition: name of the partition.\n    props_allow_override: a list of build properties to search for the\n        alternative values during runtime.\n    build_props: a dict of build properties for the given partition.\n    prop_overrides: a set of props that are overridden by import.\n    placeholder_values: A dict of runtime variables' values to replace the\n        placeholders in the build.prop file. We expect exactly one value for\n        each of the variables.\n    ramdisk_format: If name is \"boot\", the format of ramdisk inside the\n        boot image. Otherwise, its value is ignored.\n        Use lz4 to decompress by default. If its value is gzip, use gzip.\n  \"\"\"\n\n  def __init__(self, input_file, name, placeholder_values=None):\n    self.input_file = input_file\n    self.partition = name\n    self.props_allow_override = [props.format(name) for props in [\n        'ro.product.{}.brand', 'ro.product.{}.name', 'ro.product.{}.device']]\n    self.build_props = {}\n    self.prop_overrides = set()\n    self.placeholder_values = {}\n    if placeholder_values:\n      self.placeholder_values = copy.deepcopy(placeholder_values)\n\n  @staticmethod\n  def FromDictionary(name, build_props):\n    \"\"\"Constructs an instance from a build prop dictionary.\"\"\"\n\n    props = PartitionBuildProps(\"unknown\", name)\n    props.build_props = build_props.copy()\n    return props\n\n  @staticmethod\n  def FromInputFile(input_file, name, placeholder_values=None, ramdisk_format=RamdiskFormat.LZ4):\n    \"\"\"Loads the build.prop file and builds the attributes.\"\"\"\n\n    if name in (\"boot\", \"init_boot\"):\n      data = PartitionBuildProps._ReadBootPropFile(\n          input_file, name, ramdisk_format=ramdisk_format)\n    else:\n      data = PartitionBuildProps._ReadPartitionPropFile(input_file, name)\n\n    props = PartitionBuildProps(input_file, name, placeholder_values)\n    props._LoadBuildProp(data)\n    return props\n\n  @staticmethod\n  def _ReadBootPropFile(input_file, partition_name, ramdisk_format):\n    \"\"\"\n    Read build.prop for boot image from input_file.\n    Return empty string if not found.\n    \"\"\"\n    image_path = 'IMAGES/' + partition_name + '.img'\n    try:\n      boot_img = ExtractFromInputFile(input_file, image_path)\n    except KeyError:\n      logger.warning('Failed to read %s', image_path)\n      return ''\n    prop_file = GetBootImageBuildProp(boot_img, ramdisk_format=ramdisk_format)\n    if prop_file is None:\n      return ''\n    with open(prop_file, \"r\") as f:\n      return f.read()\n\n  @staticmethod\n  def _ReadPartitionPropFile(input_file, name):\n    \"\"\"\n    Read build.prop for name from input_file.\n    Return empty string if not found.\n    \"\"\"\n    data = ''\n    for prop_file in ['{}/etc/build.prop'.format(name.upper()),\n                      '{}/build.prop'.format(name.upper())]:\n      try:\n        data = ReadFromInputFile(input_file, prop_file)\n        break\n      except KeyError:\n        logger.warning('Failed to read %s', prop_file)\n    if data == '':\n      logger.warning(\"Failed to read build.prop for partition {}\".format(name))\n    return data\n\n  @staticmethod\n  def FromBuildPropFile(name, build_prop_file):\n    \"\"\"Constructs an instance from a build prop file.\"\"\"\n\n    props = PartitionBuildProps(\"unknown\", name)\n    with open(build_prop_file) as f:\n      props._LoadBuildProp(f.read())\n    return props\n\n  def _LoadBuildProp(self, data):\n    for line in data.split('\\n'):\n      line = line.strip()\n      if not line or line.startswith(\"#\"):\n        continue\n      if line.startswith(\"import\"):\n        overrides = self._ImportParser(line)\n        duplicates = self.prop_overrides.intersection(overrides.keys())\n        if duplicates:\n          raise ValueError('prop {} is overridden multiple times'.format(\n              ','.join(duplicates)))\n        self.prop_overrides = self.prop_overrides.union(overrides.keys())\n        self.build_props.update(overrides)\n      elif \"=\" in line:\n        name, value = line.split(\"=\", 1)\n        if name in self.prop_overrides:\n          raise ValueError('prop {} is set again after overridden by import '\n                           'statement'.format(name))\n        self.build_props[name] = value\n\n  def _ImportParser(self, line):\n    \"\"\"Parses the build prop in a given import statement.\"\"\"\n\n    tokens = line.split()\n    if tokens[0] != 'import' or (len(tokens) != 2 and len(tokens) != 3):\n      raise ValueError('Unrecognized import statement {}'.format(line))\n\n    if len(tokens) == 3:\n      logger.info(\"Import %s from %s, skip\", tokens[2], tokens[1])\n      return {}\n\n    import_path = tokens[1]\n    if not re.match(r'^/{}/.*\\.prop$'.format(self.partition), import_path):\n      logger.warn('Unrecognized import path {}'.format(line))\n      return {}\n\n    # We only recognize a subset of import statement that the init process\n    # supports. And we can loose the restriction based on how the dynamic\n    # fingerprint is used in practice. The placeholder format should be\n    # ${placeholder}, and its value should be provided by the caller through\n    # the placeholder_values.\n    for prop, value in self.placeholder_values.items():\n      prop_place_holder = '${{{}}}'.format(prop)\n      if prop_place_holder in import_path:\n        import_path = import_path.replace(prop_place_holder, value)\n    if '$' in import_path:\n      logger.info('Unresolved place holder in import path %s', import_path)\n      return {}\n\n    import_path = import_path.replace('/{}'.format(self.partition),\n                                      self.partition.upper())\n    logger.info('Parsing build props override from %s', import_path)\n\n    lines = ReadFromInputFile(self.input_file, import_path).split('\\n')\n    d = LoadDictionaryFromLines(lines)\n    return {key: val for key, val in d.items()\n            if key in self.props_allow_override}\n\n  def __getstate__(self):\n    state = self.__dict__.copy()\n    # Don't pickle baz\n    if \"input_file\" in state and isinstance(state[\"input_file\"], zipfile.ZipFile):\n      state[\"input_file\"] = state[\"input_file\"].filename\n    return state\n\n  def GetProp(self, prop):\n    return self.build_props.get(prop)\n\n\ndef LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path):\n  class Partition(object):\n    def __init__(self, mount_point, fs_type, device, length, context, slotselect):\n      self.mount_point = mount_point\n      self.fs_type = fs_type\n      self.device = device\n      self.length = length\n      self.context = context\n      self.slotselect = slotselect\n\n  try:\n    data = read_helper(recovery_fstab_path)\n  except KeyError:\n    logger.warning(\"Failed to find %s\", recovery_fstab_path)\n    data = \"\"\n\n  assert fstab_version == 2\n\n  d = {}\n  for line in data.split(\"\\n\"):\n    line = line.strip()\n    if not line or line.startswith(\"#\"):\n      continue\n\n    # <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>\n    pieces = line.split()\n    if len(pieces) != 5:\n      raise ValueError(\"malformed recovery.fstab line: \\\"%s\\\"\" % (line,))\n\n    # Ignore entries that are managed by vold.\n    options = pieces[4]\n    if \"voldmanaged=\" in options:\n      continue\n\n    # It's a good line, parse it.\n    length = 0\n    slotselect = False\n    options = options.split(\",\")\n    for i in options:\n      if i.startswith(\"length=\"):\n        length = int(i[7:])\n      elif i == \"slotselect\":\n        slotselect = True\n      else:\n        # Ignore all unknown options in the unified fstab.\n        continue\n\n    mount_flags = pieces[3]\n    # Honor the SELinux context if present.\n    context = None\n    for i in mount_flags.split(\",\"):\n      if i.startswith(\"context=\"):\n        context = i\n\n    mount_point = pieces[1]\n    d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2],\n                               device=pieces[0], length=length, context=context,\n                               slotselect=slotselect)\n\n  return d\n\n\ndef _FindAndLoadRecoveryFstab(info_dict, input_file, read_helper):\n  \"\"\"Finds the path to recovery fstab and loads its contents.\"\"\"\n  # recovery fstab is only meaningful when installing an update via recovery\n  # (i.e. non-A/B OTA). Skip loading fstab if device used A/B OTA.\n  if info_dict.get('ab_update') == 'true' and \\\n     info_dict.get(\"allow_non_ab\") != \"true\":\n    return None\n\n  # We changed recovery.fstab path in Q, from ../RAMDISK/etc/recovery.fstab to\n  # ../RAMDISK/system/etc/recovery.fstab. This function has to handle both\n  # cases, since it may load the info_dict from an old build (e.g. when\n  # generating incremental OTAs from that build).\n  if info_dict.get('no_recovery') != 'true':\n    recovery_fstab_path = 'RECOVERY/RAMDISK/system/etc/recovery.fstab'\n    if not DoesInputFileContain(input_file, recovery_fstab_path):\n      recovery_fstab_path = 'RECOVERY/RAMDISK/etc/recovery.fstab'\n    return LoadRecoveryFSTab(\n        read_helper, info_dict['fstab_version'], recovery_fstab_path)\n\n  if info_dict.get('recovery_as_boot') == 'true':\n    recovery_fstab_path = 'BOOT/RAMDISK/system/etc/recovery.fstab'\n    if not DoesInputFileContain(input_file, recovery_fstab_path):\n      recovery_fstab_path = 'BOOT/RAMDISK/etc/recovery.fstab'\n    return LoadRecoveryFSTab(\n        read_helper, info_dict['fstab_version'], recovery_fstab_path)\n\n  return None\n\n\ndef DumpInfoDict(d):\n  for k, v in sorted(d.items()):\n    logger.info(\"%-25s = (%s) %s\", k, type(v).__name__, v)\n\n\ndef MergeDynamicPartitionInfoDicts(framework_dict, vendor_dict):\n  \"\"\"Merges dynamic partition info variables.\n\n  Args:\n    framework_dict: The dictionary of dynamic partition info variables from the\n      partial framework target files.\n    vendor_dict: The dictionary of dynamic partition info variables from the\n      partial vendor target files.\n\n  Returns:\n    The merged dynamic partition info dictionary.\n  \"\"\"\n\n  def uniq_concat(a, b):\n    combined = set(a.split())\n    combined.update(set(b.split()))\n    combined = [item.strip() for item in combined if item.strip()]\n    return \" \".join(sorted(combined))\n\n  if (framework_dict.get(\"use_dynamic_partitions\") !=\n          \"true\") or (vendor_dict.get(\"use_dynamic_partitions\") != \"true\"):\n    raise ValueError(\"Both dictionaries must have use_dynamic_partitions=true\")\n\n  merged_dict = {\"use_dynamic_partitions\": \"true\"}\n  # For keys-value pairs that are the same, copy to merged dict\n  for key in vendor_dict.keys():\n    if key in framework_dict and framework_dict[key] == vendor_dict[key]:\n      merged_dict[key] = vendor_dict[key]\n\n  merged_dict[\"dynamic_partition_list\"] = uniq_concat(\n      framework_dict.get(\"dynamic_partition_list\", \"\"),\n      vendor_dict.get(\"dynamic_partition_list\", \"\"))\n\n  # Super block devices are defined by the vendor dict.\n  if \"super_block_devices\" in vendor_dict:\n    merged_dict[\"super_block_devices\"] = vendor_dict[\"super_block_devices\"]\n    for block_device in merged_dict[\"super_block_devices\"].split():\n      key = \"super_%s_device_size\" % block_device\n      if key not in vendor_dict:\n        raise ValueError(\"Vendor dict does not contain required key %s.\" % key)\n      merged_dict[key] = vendor_dict[key]\n\n  # Partition groups and group sizes are defined by the vendor dict because\n  # these values may vary for each board that uses a shared system image.\n  merged_dict[\"super_partition_groups\"] = vendor_dict[\"super_partition_groups\"]\n  for partition_group in merged_dict[\"super_partition_groups\"].split():\n    # Set the partition group's size using the value from the vendor dict.\n    key = \"super_%s_group_size\" % partition_group\n    if key not in vendor_dict:\n      raise ValueError(\"Vendor dict does not contain required key %s.\" % key)\n    merged_dict[key] = vendor_dict[key]\n\n    # Set the partition group's partition list using a concatenation of the\n    # framework and vendor partition lists.\n    key = \"super_%s_partition_list\" % partition_group\n    merged_dict[key] = uniq_concat(\n        framework_dict.get(key, \"\"), vendor_dict.get(key, \"\"))\n  # in the case that vendor is on s build, but is taking a v3 -> v3 vabc ota, we want to fallback to v2\n  if \"vabc_cow_version\" not in vendor_dict or \"vabc_cow_version\" not in framework_dict:\n    merged_dict[\"vabc_cow_version\"] = '2'\n  else:\n    merged_dict[\"vabc_cow_version\"] = min(vendor_dict[\"vabc_cow_version\"], framework_dict[\"vabc_cow_version\"])\n  # Various other flags should be copied from the vendor dict, if defined.\n  for key in (\"virtual_ab\", \"virtual_ab_retrofit\", \"lpmake\",\n              \"super_metadata_device\", \"super_partition_error_limit\",\n              \"super_partition_size\"):\n    if key in vendor_dict.keys():\n      merged_dict[key] = vendor_dict[key]\n\n  return merged_dict\n\n\ndef PartitionMapFromTargetFiles(target_files_dir):\n  \"\"\"Builds a map from partition -> path within an extracted target files directory.\"\"\"\n  # Keep possible_subdirs in sync with build/make/core/board_config.mk.\n  possible_subdirs = {\n      \"system\": [\"SYSTEM\"],\n      \"vendor\": [\"VENDOR\", \"SYSTEM/vendor\"],\n      \"product\": [\"PRODUCT\", \"SYSTEM/product\"],\n      \"system_ext\": [\"SYSTEM_EXT\", \"SYSTEM/system_ext\"],\n      \"odm\": [\"ODM\", \"VENDOR/odm\", \"SYSTEM/vendor/odm\"],\n      \"vendor_dlkm\": [\n          \"VENDOR_DLKM\", \"VENDOR/vendor_dlkm\", \"SYSTEM/vendor/vendor_dlkm\"\n      ],\n      \"odm_dlkm\": [\"ODM_DLKM\", \"VENDOR/odm_dlkm\", \"SYSTEM/vendor/odm_dlkm\"],\n      \"system_dlkm\": [\"SYSTEM_DLKM\", \"SYSTEM/system_dlkm\"],\n  }\n  partition_map = {}\n  for partition, subdirs in possible_subdirs.items():\n    for subdir in subdirs:\n      if os.path.exists(os.path.join(target_files_dir, subdir)):\n        partition_map[partition] = subdir\n        break\n  return partition_map\n\n\ndef SharedUidPartitionViolations(uid_dict, partition_groups):\n  \"\"\"Checks for APK sharedUserIds that cross partition group boundaries.\n\n  This uses a single or merged build's shareduid_violation_modules.json\n  output file, as generated by find_shareduid_violation.py or\n  core/tasks/find-shareduid-violation.mk.\n\n  An error is defined as a sharedUserId that is found in a set of partitions\n  that span more than one partition group.\n\n  Args:\n    uid_dict: A dictionary created by using the standard json module to read a\n      complete shareduid_violation_modules.json file.\n    partition_groups: A list of groups, where each group is a list of\n      partitions.\n\n  Returns:\n    A list of error messages.\n  \"\"\"\n  errors = []\n  for uid, partitions in uid_dict.items():\n    found_in_groups = [\n        group for group in partition_groups\n        if set(partitions.keys()) & set(group)\n    ]\n    if len(found_in_groups) > 1:\n      errors.append(\n          \"APK sharedUserId \\\"%s\\\" found across partition groups in partitions \\\"%s\\\"\"\n          % (uid, \",\".join(sorted(partitions.keys()))))\n  return errors\n\n\ndef RunVendoredHostInitVerifier(product_out, partition_map):\n  \"\"\"Runs vendor host_init_verifier on the init rc files within selected partitions.\n\n  host_init_verifier searches the etc/init path within each selected partition.\n\n  Args:\n    product_out: PRODUCT_OUT directory, containing partition directories.\n    partition_map: A map of partition name -> relative path within product_out.\n  \"\"\"\n  return RunHostInitVerifier(\n      product_out,\n      partition_map,\n      tool=os.path.join(OPTIONS.vendor_otatools, 'bin', 'host_init_verifier'))\n\n\ndef RunHostInitVerifier(product_out, partition_map, tool=\"host_init_verifier\"):\n  \"\"\"Runs host_init_verifier on the init rc files within partitions.\n\n  host_init_verifier searches the etc/init path within each partition.\n\n  Args:\n    product_out: PRODUCT_OUT directory, containing partition directories.\n    partition_map: A map of partition name -> relative path within product_out.\n    tool: Full path to host_init_verifier or binary name\n  \"\"\"\n  allowed_partitions = (\"system\", \"system_ext\", \"product\", \"vendor\", \"odm\")\n  cmd = [tool]\n  for partition, path in partition_map.items():\n    if partition not in allowed_partitions:\n      raise ExternalError(\"Unable to call host_init_verifier for partition %s\" %\n                          partition)\n    cmd.extend([\"--out_%s\" % partition, os.path.join(product_out, path)])\n    # Add --property-contexts if the file exists on the partition.\n    property_contexts = \"%s_property_contexts\" % (\n        \"plat\" if partition == \"system\" else partition)\n    property_contexts_path = os.path.join(product_out, path, \"etc\", \"selinux\",\n                                          property_contexts)\n    if os.path.exists(property_contexts_path):\n      cmd.append(\"--property-contexts=%s\" % property_contexts_path)\n    # Add the passwd file if the file exists on the partition.\n    passwd_path = os.path.join(product_out, path, \"etc\", \"passwd\")\n    if os.path.exists(passwd_path):\n      cmd.extend([\"-p\", passwd_path])\n  return RunAndCheckOutput(cmd)\n\n\ndef AppendAVBSigningArgs(cmd, partition, avb_salt=None):\n  \"\"\"Append signing arguments for avbtool.\"\"\"\n  # e.g., \"--key path/to/signing_key --algorithm SHA256_RSA4096\"\n  key_path = ResolveAVBSigningPathArgs(\n      OPTIONS.info_dict.get(\"avb_\" + partition + \"_key_path\"))\n  algorithm = OPTIONS.info_dict.get(\"avb_\" + partition + \"_algorithm\")\n  if key_path and algorithm:\n    cmd.extend([\"--key\", key_path, \"--algorithm\", algorithm])\n  if avb_salt is None:\n    avb_salt = OPTIONS.info_dict.get(\"avb_salt\")\n  # make_vbmeta_image doesn't like \"--salt\" (and it's not needed).\n  if avb_salt and not partition.startswith(\"vbmeta\"):\n    cmd.extend([\"--salt\", avb_salt])\n\n\ndef ResolveAVBSigningPathArgs(split_args):\n\n  def ResolveBinaryPath(path):\n    if os.path.exists(path):\n      return path\n    if OPTIONS.search_path:\n      new_path = os.path.join(OPTIONS.search_path, path)\n      if os.path.exists(new_path):\n        return new_path\n    raise ExternalError(\n        \"Failed to find {}\".format(path))\n\n  if not split_args:\n    return split_args\n\n  if isinstance(split_args, list):\n    for index, arg in enumerate(split_args[:-1]):\n      if arg == '--signing_helper':\n        signing_helper_path = split_args[index + 1]\n        split_args[index + 1] = ResolveBinaryPath(signing_helper_path)\n        break\n  elif isinstance(split_args, str):\n    split_args = ResolveBinaryPath(split_args)\n\n  return split_args\n\n\ndef GetAvbPartitionArg(partition, image, info_dict=None):\n  \"\"\"Returns the VBMeta arguments for one partition.\n\n  It sets up the VBMeta argument by including the partition descriptor from the\n  given 'image', or by configuring the partition as a chained partition.\n\n  Args:\n    partition: The name of the partition (e.g. \"system\").\n    image: The path to the partition image.\n    info_dict: A dict returned by common.LoadInfoDict(). Will use\n        OPTIONS.info_dict if None has been given.\n\n  Returns:\n    A list of VBMeta arguments for one partition.\n  \"\"\"\n  if info_dict is None:\n    info_dict = OPTIONS.info_dict\n\n  # Check if chain partition is used.\n  key_path = info_dict.get(\"avb_\" + partition + \"_key_path\")\n  if not key_path:\n    return [AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG, image]\n\n  # For a non-A/B device, we don't chain /recovery nor include its descriptor\n  # into vbmeta.img. The recovery image will be configured on an independent\n  # boot chain, to be verified with AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION.\n  # See details at\n  # https://android.googlesource.com/platform/external/avb/+/master/README.md#booting-into-recovery.\n  if info_dict.get(\"ab_update\") != \"true\" and partition == \"recovery\":\n    return []\n\n  # Otherwise chain the partition into vbmeta.\n  chained_partition_arg = GetAvbChainedPartitionArg(partition, info_dict)\n  return [AVB_ARG_NAME_CHAIN_PARTITION, chained_partition_arg]\n\n\ndef GetAvbPartitionsArg(partitions,\n                        resolve_rollback_index_location_conflict=False,\n                        info_dict=None):\n  \"\"\"Returns the VBMeta arguments for all AVB partitions.\n\n  It sets up the VBMeta argument by calling GetAvbPartitionArg of all\n  partitions.\n\n  Args:\n    partitions: A dict of all AVB partitions.\n    resolve_rollback_index_location_conflict: If true, resolve conflicting avb\n        rollback index locations by assigning the smallest unused value.\n    info_dict: A dict returned by common.LoadInfoDict().\n\n  Returns:\n    A list of VBMeta arguments for all partitions.\n  \"\"\"\n  # An AVB partition will be linked into a vbmeta partition by either\n  # AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG or AVB_ARG_NAME_CHAIN_PARTITION, there\n  # should be no other cases.\n  valid_args = {\n      AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG: [],\n      AVB_ARG_NAME_CHAIN_PARTITION: []\n  }\n\n  for partition, path in sorted(partitions.items()):\n    avb_partition_arg = GetAvbPartitionArg(partition, path, info_dict)\n    if not avb_partition_arg:\n      continue\n    arg_name, arg_value = avb_partition_arg\n    assert arg_name in valid_args\n    valid_args[arg_name].append(arg_value)\n\n  # Copy the arguments for non-chained AVB partitions directly without\n  # intervention.\n  avb_args = []\n  for image in valid_args[AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG]:\n    avb_args.extend([AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG, image])\n\n  # Handle chained AVB partitions. The rollback index location might be\n  # adjusted if two partitions use the same value. This may happen when mixing\n  # a shared system image with other vendor images.\n  used_index_loc = set()\n  for chained_partition_arg in valid_args[AVB_ARG_NAME_CHAIN_PARTITION]:\n    if resolve_rollback_index_location_conflict:\n      while chained_partition_arg.rollback_index_location in used_index_loc:\n        chained_partition_arg.rollback_index_location += 1\n\n    used_index_loc.add(chained_partition_arg.rollback_index_location)\n    avb_args.extend([AVB_ARG_NAME_CHAIN_PARTITION,\n                     chained_partition_arg.to_string()])\n\n  return avb_args\n\n\ndef GetAvbChainedPartitionArg(partition, info_dict, key=None):\n  \"\"\"Constructs and returns the arg to build or verify a chained partition.\n\n  Args:\n    partition: The partition name.\n    info_dict: The info dict to look up the key info and rollback index\n        location.\n    key: The key to be used for building or verifying the partition. Defaults to\n        the key listed in info_dict.\n\n  Returns:\n    An AvbChainedPartitionArg object with rollback_index_location and\n    pubkey_path that can be used to build or verify vbmeta image.\n  \"\"\"\n  if key is None:\n    key = info_dict[\"avb_\" + partition + \"_key_path\"]\n  key = ResolveAVBSigningPathArgs(key)\n  pubkey_path = ExtractAvbPublicKey(info_dict[\"avb_avbtool\"], key)\n  rollback_index_location = info_dict[\n      \"avb_\" + partition + \"_rollback_index_location\"]\n  return AvbChainedPartitionArg(\n      partition=partition,\n      rollback_index_location=int(rollback_index_location),\n      pubkey_path=pubkey_path)\n\n\ndef BuildVBMeta(image_path, partitions, name, needed_partitions,\n                resolve_rollback_index_location_conflict=False):\n  \"\"\"Creates a VBMeta image.\n\n  It generates the requested VBMeta image. The requested image could be for\n  top-level or chained VBMeta image, which is determined based on the name.\n\n  Args:\n    image_path: The output path for the new VBMeta image.\n    partitions: A dict that's keyed by partition names with image paths as\n        values. Only valid partition names are accepted, as partitions listed\n        in common.AVB_PARTITIONS and custom partitions listed in\n        OPTIONS.info_dict.get(\"avb_custom_images_partition_list\")\n    name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.\n    needed_partitions: Partitions whose descriptors should be included into the\n        generated VBMeta image.\n    resolve_rollback_index_location_conflict: If true, resolve conflicting avb\n        rollback index locations by assigning the smallest unused value.\n\n  Raises:\n    AssertionError: On invalid input args.\n  \"\"\"\n  avbtool = OPTIONS.info_dict[\"avb_avbtool\"]\n  cmd = [avbtool, \"make_vbmeta_image\", \"--output\", image_path]\n  AppendAVBSigningArgs(cmd, name)\n\n  custom_partitions = OPTIONS.info_dict.get(\n      \"avb_custom_images_partition_list\", \"\").strip().split()\n  custom_avb_partitions = [\"vbmeta_\" + part for part in OPTIONS.info_dict.get(\n      \"avb_custom_vbmeta_images_partition_list\", \"\").strip().split()]\n\n  avb_partitions = {}\n  for partition, path in sorted(partitions.items()):\n    if partition not in needed_partitions:\n      continue\n    assert (partition in AVB_PARTITIONS or\n            partition in AVB_VBMETA_PARTITIONS or\n            partition in custom_avb_partitions or\n            partition in custom_partitions), \\\n        'Unknown partition: {}'.format(partition)\n    assert os.path.exists(path), \\\n        'Failed to find {} for {}'.format(path, partition)\n    avb_partitions[partition] = path\n  cmd.extend(GetAvbPartitionsArg(avb_partitions,\n                                 resolve_rollback_index_location_conflict))\n\n  args = OPTIONS.info_dict.get(\"avb_{}_args\".format(name))\n  if args and args.strip():\n    split_args = shlex.split(args)\n    for index, arg in enumerate(split_args[:-1]):\n      # Check that the image file exists. Some images might be defined\n      # as a path relative to source tree, which may not be available at the\n      # same location when running this script (we have the input target_files\n      # zip only). For such cases, we additionally scan other locations (e.g.\n      # IMAGES/, RADIO/, etc) before bailing out.\n      if arg == AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG:\n        chained_image = split_args[index + 1]\n        if os.path.exists(chained_image):\n          continue\n        found = False\n        for dir_name in ['IMAGES', 'RADIO', 'PREBUILT_IMAGES']:\n          alt_path = os.path.join(\n              OPTIONS.input_tmp, dir_name, os.path.basename(chained_image))\n          if os.path.exists(alt_path):\n            split_args[index + 1] = alt_path\n            found = True\n            break\n        assert found, 'Failed to find {}'.format(chained_image)\n\n    split_args = ResolveAVBSigningPathArgs(split_args)\n    cmd.extend(split_args)\n\n  RunAndCheckOutput(cmd)\n\n\ndef _MakeRamdisk(sourcedir, fs_config_file=None,\n                 dev_node_file=None,\n                 ramdisk_format=RamdiskFormat.GZ):\n  ramdisk_img = tempfile.NamedTemporaryFile()\n\n  cmd = [\"mkbootfs\"]\n\n  if fs_config_file and os.access(fs_config_file, os.F_OK):\n    cmd.extend([\"-f\", fs_config_file])\n\n  if dev_node_file and os.access(dev_node_file, os.F_OK):\n    cmd.extend([\"-n\", dev_node_file])\n\n  cmd.append(os.path.join(sourcedir, \"RAMDISK\"))\n\n  p1 = Run(cmd, stdout=subprocess.PIPE)\n  if ramdisk_format == RamdiskFormat.LZ4:\n    p2 = Run([\"lz4\", \"-l\", \"-12\", \"--favor-decSpeed\"], stdin=p1.stdout,\n             stdout=ramdisk_img.file.fileno())\n  elif ramdisk_format == RamdiskFormat.GZ:\n    p2 = Run([\"gzip\"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())\n  else:\n    raise ValueError(\"Only support lz4 or gzip ramdisk format.\")\n\n  p2.wait()\n  p1.wait()\n  assert p1.returncode == 0, \"mkbootfs of %s ramdisk failed\" % (sourcedir,)\n  assert p2.returncode == 0, \"compression of %s ramdisk failed\" % (sourcedir,)\n\n  return ramdisk_img\n\n\ndef _BuildBootableImage(image_name, sourcedir, fs_config_file,\n                        dev_node_file=None, info_dict=None,\n                        has_ramdisk=False, two_step_image=False):\n  \"\"\"Build a bootable image from the specified sourcedir.\n\n  Take a kernel, cmdline, and optionally a ramdisk directory from the input (in\n  'sourcedir'), and turn them into a boot image. 'two_step_image' indicates if\n  we are building a two-step special image (i.e. building a recovery image to\n  be loaded into /boot in two-step OTAs).\n\n  Return the image data, or None if sourcedir does not appear to contains files\n  for building the requested image.\n  \"\"\"\n\n  if info_dict is None:\n    info_dict = OPTIONS.info_dict\n\n  # \"boot\" or \"recovery\", without extension.\n  partition_name = os.path.basename(sourcedir).lower()\n\n  kernel = None\n  if partition_name == \"recovery\":\n    if info_dict.get(\"exclude_kernel_from_recovery_image\") == \"true\":\n      logger.info(\"Excluded kernel binary from recovery image.\")\n    else:\n      kernel = \"kernel\"\n  elif partition_name == \"init_boot\":\n    pass\n  else:\n    kernel = image_name.replace(\"boot\", \"kernel\")\n    kernel = kernel.replace(\".img\", \"\")\n  if kernel and not os.access(os.path.join(sourcedir, kernel), os.F_OK):\n    return None\n\n  kernel_path = os.path.join(sourcedir, kernel) if kernel else None\n\n  if has_ramdisk and not os.access(os.path.join(sourcedir, \"RAMDISK\"), os.F_OK):\n    return None\n\n  img = tempfile.NamedTemporaryFile()\n\n  if has_ramdisk:\n    ramdisk_format = GetRamdiskFormat(info_dict)\n    ramdisk_img = _MakeRamdisk(sourcedir, fs_config_file, dev_node_file,\n                               ramdisk_format=ramdisk_format)\n\n  # use MKBOOTIMG from environ, or \"mkbootimg\" if empty or not set\n  mkbootimg = os.getenv('MKBOOTIMG') or \"mkbootimg\"\n\n  cmd = [mkbootimg]\n  if kernel_path is not None:\n    cmd.extend([\"--kernel\", kernel_path])\n\n  fn = os.path.join(sourcedir, \"second\")\n  if os.access(fn, os.F_OK):\n    cmd.append(\"--second\")\n    cmd.append(fn)\n\n  fn = os.path.join(sourcedir, \"dtb\")\n  if os.access(fn, os.F_OK):\n    cmd.append(\"--dtb\")\n    cmd.append(fn)\n\n  fn = os.path.join(sourcedir, \"cmdline\")\n  if os.access(fn, os.F_OK):\n    cmd.append(\"--cmdline\")\n    cmd.append(open(fn).read().rstrip(\"\\n\"))\n\n  fn = os.path.join(sourcedir, \"base\")\n  if os.access(fn, os.F_OK):\n    cmd.append(\"--base\")\n    cmd.append(open(fn).read().rstrip(\"\\n\"))\n\n  fn = os.path.join(sourcedir, \"pagesize\")\n  if os.access(fn, os.F_OK):\n    cmd.append(\"--pagesize\")\n    cmd.append(open(fn).read().rstrip(\"\\n\"))\n\n  if partition_name == \"recovery\":\n    args = info_dict.get(\"recovery_mkbootimg_args\")\n    if not args:\n      # Fall back to \"mkbootimg_args\" for recovery image\n      # in case \"recovery_mkbootimg_args\" is not set.\n      args = info_dict.get(\"mkbootimg_args\")\n  elif partition_name == \"init_boot\":\n    args = info_dict.get(\"mkbootimg_init_args\")\n  else:\n    args = info_dict.get(\"mkbootimg_args\")\n  if args and args.strip():\n    cmd.extend(shlex.split(args))\n\n  args = info_dict.get(\"mkbootimg_version_args\")\n  if args and args.strip():\n    cmd.extend(shlex.split(args))\n\n  if has_ramdisk:\n    cmd.extend([\"--ramdisk\", ramdisk_img.name])\n\n  cmd.extend([\"--output\", img.name])\n\n  if partition_name == \"recovery\":\n    if info_dict.get(\"include_recovery_dtbo\") == \"true\":\n      fn = os.path.join(sourcedir, \"recovery_dtbo\")\n      cmd.extend([\"--recovery_dtbo\", fn])\n    if info_dict.get(\"include_recovery_acpio\") == \"true\":\n      fn = os.path.join(sourcedir, \"recovery_acpio\")\n      cmd.extend([\"--recovery_acpio\", fn])\n\n  RunAndCheckOutput(cmd)\n\n  # AVB: if enabled, calculate and add hash to boot.img or recovery.img.\n  if info_dict.get(\"avb_enable\") == \"true\":\n    avbtool = info_dict[\"avb_avbtool\"]\n    if partition_name == \"recovery\":\n      part_size = info_dict[\"recovery_size\"]\n    else:\n      part_size = info_dict[image_name.replace(\".img\", \"_size\")]\n    cmd = [avbtool, \"add_hash_footer\", \"--image\", img.name,\n           \"--partition_size\", str(part_size), \"--partition_name\",\n           partition_name]\n    salt = None\n    if kernel_path is not None:\n      with open(kernel_path, \"rb\") as fp:\n        salt = sha256(fp.read()).hexdigest()\n    AppendAVBSigningArgs(cmd, partition_name, salt)\n    args = info_dict.get(\"avb_\" + partition_name + \"_add_hash_footer_args\")\n    if args and args.strip():\n      split_args = ResolveAVBSigningPathArgs(shlex.split(args))\n      cmd.extend(split_args)\n    RunAndCheckOutput(cmd)\n\n  img.seek(os.SEEK_SET, 0)\n  data = img.read()\n\n  if has_ramdisk:\n    ramdisk_img.close()\n  img.close()\n\n  return data\n\n\ndef _SignBootableImage(image_path, prebuilt_name, partition_name,\n                       info_dict=None):\n  \"\"\"Performs AVB signing for a prebuilt boot.img.\n\n  Args:\n    image_path: The full path of the image, e.g., /path/to/boot.img.\n    prebuilt_name: The prebuilt image name, e.g., boot.img, boot-5.4-gz.img,\n        boot-5.10.img, recovery.img or init_boot.img.\n    partition_name: The partition name, e.g., 'boot', 'init_boot' or 'recovery'.\n    info_dict: The information dict read from misc_info.txt.\n  \"\"\"\n  if info_dict is None:\n    info_dict = OPTIONS.info_dict\n\n  # AVB: if enabled, calculate and add hash to boot.img or recovery.img.\n  if info_dict.get(\"avb_enable\") == \"true\":\n    avbtool = info_dict[\"avb_avbtool\"]\n    if partition_name == \"recovery\":\n      part_size = info_dict[\"recovery_size\"]\n    else:\n      part_size = info_dict[prebuilt_name.replace(\".img\", \"_size\")]\n\n    cmd = [avbtool, \"add_hash_footer\", \"--image\", image_path,\n           \"--partition_size\", str(part_size), \"--partition_name\",\n           partition_name]\n    # Use sha256 of the kernel as salt for reproducible builds\n    with tempfile.TemporaryDirectory() as tmpdir:\n      RunAndCheckOutput([\"unpack_bootimg\", \"--boot_img\", image_path, \"--out\", tmpdir])\n      for filename in [\"kernel\", \"ramdisk\", \"vendor_ramdisk00\"]:\n        path = os.path.join(tmpdir, filename)\n        if os.path.exists(path) and os.path.getsize(path):\n          print(\"Using {} as salt for avb footer of {}\".format(\n              filename, partition_name))\n          with open(path, \"rb\") as fp:\n            salt = sha256(fp.read()).hexdigest()\n            break\n    AppendAVBSigningArgs(cmd, partition_name, salt)\n    args = info_dict.get(\"avb_\" + partition_name + \"_add_hash_footer_args\")\n    if args and args.strip():\n      split_args = ResolveAVBSigningPathArgs(shlex.split(args))\n      cmd.extend(split_args)\n    RunAndCheckOutput(cmd)\n\n\ndef HasRamdisk(partition_name, info_dict=None):\n  \"\"\"Returns true/false to see if a bootable image should have a ramdisk.\n\n  Args:\n    partition_name: The partition name, e.g., 'boot', 'init_boot' or 'recovery'.\n    info_dict: The information dict read from misc_info.txt.\n  \"\"\"\n  if info_dict is None:\n    info_dict = OPTIONS.info_dict\n\n  if partition_name != \"boot\":\n    return True  # init_boot.img or recovery.img has a ramdisk.\n\n  if info_dict.get(\"recovery_as_boot\") == \"true\":\n    return True  # the recovery-as-boot boot.img has a RECOVERY ramdisk.\n\n  if info_dict.get(\"gki_boot_image_without_ramdisk\") == \"true\":\n    return False  # A GKI boot.img has no ramdisk since Android-13.\n\n  if info_dict.get(\"init_boot\") == \"true\":\n    # The ramdisk is moved to the init_boot.img, so there is NO\n    # ramdisk in the boot.img or boot-<kernel version>.img.\n    return False\n\n  return True\n\n\ndef GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,\n                     info_dict=None, two_step_image=False,\n                     dev_nodes=False):\n  \"\"\"Return a File object with the desired bootable image.\n\n  Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',\n  otherwise look for it under 'unpack_dir'/IMAGES, otherwise construct it from\n  the source files in 'unpack_dir'/'tree_subdir'.\"\"\"\n\n  if info_dict is None:\n    info_dict = OPTIONS.info_dict\n\n  prebuilt_path = os.path.join(unpack_dir, \"BOOTABLE_IMAGES\", prebuilt_name)\n  if os.path.exists(prebuilt_path):\n    logger.info(\"using prebuilt %s from BOOTABLE_IMAGES...\", prebuilt_name)\n    return File.FromLocalFile(name, prebuilt_path)\n\n  prebuilt_path = os.path.join(unpack_dir, \"IMAGES\", prebuilt_name)\n  if os.path.exists(prebuilt_path):\n    logger.info(\"using prebuilt %s from IMAGES...\", prebuilt_name)\n    return File.FromLocalFile(name, prebuilt_path)\n\n  partition_name = tree_subdir.lower()\n  prebuilt_path = os.path.join(unpack_dir, \"PREBUILT_IMAGES\", prebuilt_name)\n  if os.path.exists(prebuilt_path):\n    logger.info(\"Re-signing prebuilt %s from PREBUILT_IMAGES...\", prebuilt_name)\n    signed_img = MakeTempFile()\n    shutil.copy(prebuilt_path, signed_img)\n    _SignBootableImage(signed_img, prebuilt_name, partition_name, info_dict)\n    return File.FromLocalFile(name, signed_img)\n\n  logger.info(\"building image from target_files %s...\", tree_subdir)\n\n  has_ramdisk = HasRamdisk(partition_name, info_dict)\n\n  fs_config = \"META/\" + tree_subdir.lower() + \"_filesystem_config.txt\"\n  data = _BuildBootableImage(prebuilt_name, os.path.join(unpack_dir, tree_subdir),\n                             os.path.join(unpack_dir, fs_config),\n                             os.path.join(unpack_dir, 'META/ramdisk_node_list')\n                             if dev_nodes else None,\n                             info_dict, has_ramdisk, two_step_image)\n  if data:\n    return File(name, data)\n  return None\n\n\ndef _BuildVendorBootImage(sourcedir, fs_config_file, partition_name, info_dict=None):\n  \"\"\"Build a vendor boot image from the specified sourcedir.\n\n  Take a ramdisk, dtb, and vendor_cmdline from the input (in 'sourcedir'), and\n  turn them into a vendor boot image.\n\n  Return the image data, or None if sourcedir does not appear to contains files\n  for building the requested image.\n  \"\"\"\n\n  if info_dict is None:\n    info_dict = OPTIONS.info_dict\n\n  img = tempfile.NamedTemporaryFile()\n\n  ramdisk_format = GetRamdiskFormat(info_dict)\n  ramdisk_img = _MakeRamdisk(sourcedir, fs_config_file=fs_config_file, ramdisk_format=ramdisk_format)\n\n  # use MKBOOTIMG from environ, or \"mkbootimg\" if empty or not set\n  mkbootimg = os.getenv('MKBOOTIMG') or \"mkbootimg\"\n\n  cmd = [mkbootimg]\n\n  fn = os.path.join(sourcedir, \"dtb\")\n  if os.access(fn, os.F_OK):\n    has_vendor_kernel_boot = (info_dict.get(\n        \"vendor_kernel_boot\", \"\").lower() == \"true\")\n\n    # Pack dtb into vendor_kernel_boot if building vendor_kernel_boot.\n    # Otherwise pack dtb into vendor_boot.\n    if not has_vendor_kernel_boot or partition_name == \"vendor_kernel_boot\":\n      cmd.append(\"--dtb\")\n      cmd.append(fn)\n\n  fn = os.path.join(sourcedir, \"vendor_cmdline\")\n  if os.access(fn, os.F_OK):\n    cmd.append(\"--vendor_cmdline\")\n    cmd.append(open(fn).read().rstrip(\"\\n\"))\n\n  fn = os.path.join(sourcedir, \"base\")\n  if os.access(fn, os.F_OK):\n    cmd.append(\"--base\")\n    cmd.append(open(fn).read().rstrip(\"\\n\"))\n\n  fn = os.path.join(sourcedir, \"pagesize\")\n  if os.access(fn, os.F_OK):\n    cmd.append(\"--pagesize\")\n    cmd.append(open(fn).read().rstrip(\"\\n\"))\n\n  args = info_dict.get(\"mkbootimg_args\")\n  if args and args.strip():\n    cmd.extend(shlex.split(args))\n\n  args = info_dict.get(\"mkbootimg_version_args\")\n  if args and args.strip():\n    cmd.extend(shlex.split(args))\n\n  cmd.extend([\"--vendor_ramdisk\", ramdisk_img.name])\n  cmd.extend([\"--vendor_boot\", img.name])\n\n  fn = os.path.join(sourcedir, \"vendor_bootconfig\")\n  if os.access(fn, os.F_OK):\n    cmd.append(\"--vendor_bootconfig\")\n    cmd.append(fn)\n\n  ramdisk_fragment_imgs = []\n  fn = os.path.join(sourcedir, \"vendor_ramdisk_fragments\")\n  if os.access(fn, os.F_OK):\n    ramdisk_fragments = shlex.split(open(fn).read().rstrip(\"\\n\"))\n    for ramdisk_fragment in ramdisk_fragments:\n      fn = os.path.join(sourcedir, \"RAMDISK_FRAGMENTS\",\n                        ramdisk_fragment, \"mkbootimg_args\")\n      cmd.extend(shlex.split(open(fn).read().rstrip(\"\\n\")))\n      fn = os.path.join(sourcedir, \"RAMDISK_FRAGMENTS\",\n                        ramdisk_fragment, \"prebuilt_ramdisk\")\n      # Use prebuilt image if found, else create ramdisk from supplied files.\n      if os.access(fn, os.F_OK):\n        ramdisk_fragment_pathname = fn\n      else:\n        ramdisk_fragment_root = os.path.join(\n            sourcedir, \"RAMDISK_FRAGMENTS\", ramdisk_fragment)\n        ramdisk_fragment_img = _MakeRamdisk(ramdisk_fragment_root,\n                                            ramdisk_format=ramdisk_format)\n        ramdisk_fragment_imgs.append(ramdisk_fragment_img)\n        ramdisk_fragment_pathname = ramdisk_fragment_img.name\n      cmd.extend([\"--vendor_ramdisk_fragment\", ramdisk_fragment_pathname])\n\n  RunAndCheckOutput(cmd)\n\n  # AVB: if enabled, calculate and add hash.\n  if info_dict.get(\"avb_enable\") == \"true\":\n    avbtool = info_dict[\"avb_avbtool\"]\n    part_size = info_dict[f'{partition_name}_size']\n    cmd = [avbtool, \"add_hash_footer\", \"--image\", img.name,\n           \"--partition_size\", str(part_size), \"--partition_name\", partition_name]\n    AppendAVBSigningArgs(cmd, partition_name)\n    args = info_dict.get(f'avb_{partition_name}_add_hash_footer_args')\n    if args and args.strip():\n      split_args = ResolveAVBSigningPathArgs(shlex.split(args))\n      cmd.extend(split_args)\n    RunAndCheckOutput(cmd)\n\n  img.seek(os.SEEK_SET, 0)\n  data = img.read()\n\n  for f in ramdisk_fragment_imgs:\n    f.close()\n  ramdisk_img.close()\n  img.close()\n\n  return data\n\n\ndef GetVendorBootImage(name, prebuilt_name, unpack_dir, tree_subdir,\n                       info_dict=None):\n  \"\"\"Return a File object with the desired vendor boot image.\n\n  Look for it under 'unpack_dir'/IMAGES, otherwise construct it from\n  the source files in 'unpack_dir'/'tree_subdir'.\"\"\"\n\n  prebuilt_path = os.path.join(unpack_dir, \"IMAGES\", prebuilt_name)\n  if os.path.exists(prebuilt_path):\n    logger.info(\"using prebuilt %s from IMAGES...\", prebuilt_name)\n    return File.FromLocalFile(name, prebuilt_path)\n\n  logger.info(\"building image from target_files %s...\", tree_subdir)\n\n  if info_dict is None:\n    info_dict = OPTIONS.info_dict\n\n  fs_config = \"META/\" + tree_subdir.lower() + \"_filesystem_config.txt\"\n  data = _BuildVendorBootImage(\n      os.path.join(unpack_dir, tree_subdir), os.path.join(unpack_dir, fs_config), \"vendor_boot\", info_dict)\n  if data:\n    return File(name, data)\n  return None\n\n\ndef GetVendorKernelBootImage(name, prebuilt_name, unpack_dir, tree_subdir,\n                             info_dict=None):\n  \"\"\"Return a File object with the desired vendor kernel boot image.\n\n  Look for it under 'unpack_dir'/IMAGES, otherwise construct it from\n  the source files in 'unpack_dir'/'tree_subdir'.\"\"\"\n\n  prebuilt_path = os.path.join(unpack_dir, \"IMAGES\", prebuilt_name)\n  if os.path.exists(prebuilt_path):\n    logger.info(\"using prebuilt %s from IMAGES...\", prebuilt_name)\n    return File.FromLocalFile(name, prebuilt_path)\n\n  logger.info(\"building image from target_files %s...\", tree_subdir)\n\n  if info_dict is None:\n    info_dict = OPTIONS.info_dict\n\n  data = _BuildVendorBootImage(\n      os.path.join(unpack_dir, tree_subdir), None, \"vendor_kernel_boot\", info_dict)\n  if data:\n    return File(name, data)\n  return None\n\n\ndef Gunzip(in_filename, out_filename):\n  \"\"\"Gunzips the given gzip compressed file to a given output file.\"\"\"\n  with gzip.open(in_filename, \"rb\") as in_file, \\\n          open(out_filename, \"wb\") as out_file:\n    shutil.copyfileobj(in_file, out_file)\n\n\ndef UnzipSingleFile(input_zip: zipfile.ZipFile, info: zipfile.ZipInfo, dirname: str):\n  # According to https://stackoverflow.com/questions/434641/how-do-i-set-permissions-attributes-on-a-file-in-a-zip-file-using-pythons-zip/6297838#6297838\n  # higher bits of |external_attr| are unix file permission and types\n  unix_filetype = info.external_attr >> 16\n  file_perm = unix_filetype & 0o777\n\n  def CheckMask(a, mask):\n    return (a & mask) == mask\n\n  def IsSymlink(a):\n    return CheckMask(a, stat.S_IFLNK)\n\n  def IsDir(a):\n    return CheckMask(a, stat.S_IFDIR)\n  # python3.11 zipfile implementation doesn't handle symlink correctly\n  if not IsSymlink(unix_filetype):\n    target = input_zip.extract(info, dirname)\n    # We want to ensure that the file is at least read/writable by owner and readable by all users\n    if IsDir(unix_filetype):\n      os.chmod(target, file_perm | 0o755)\n    else:\n      os.chmod(target, file_perm | 0o644)\n    return target\n  if dirname is None:\n    dirname = os.getcwd()\n  target = os.path.join(dirname, info.filename)\n  os.makedirs(os.path.dirname(target), exist_ok=True)\n  if os.path.exists(target):\n    os.unlink(target)\n  os.symlink(input_zip.read(info).decode(), target)\n  return target\n\n\ndef UnzipToDir(filename, dirname, patterns=None):\n  \"\"\"Unzips the archive to the given directory.\n\n  Args:\n    filename: The name of the zip file to unzip.\n    dirname: Where the unziped files will land.\n    patterns: Files to unzip from the archive. If omitted, will unzip the entire\n        archvie. Non-matching patterns will be filtered out. If there's no match\n        after the filtering, no file will be unzipped.\n  \"\"\"\n  with zipfile.ZipFile(filename, allowZip64=True, mode=\"r\") as input_zip:\n    # Filter out non-matching patterns. unzip will complain otherwise.\n    entries = input_zip.infolist()\n    # b/283033491\n    # Per https://en.wikipedia.org/wiki/ZIP_(file_format)#Central_directory_file_header\n    # In zip64 mode, central directory record's header_offset field might be\n    # set to 0xFFFFFFFF if header offset is > 2^32. In this case, the extra\n    # fields will contain an 8 byte little endian integer at offset 20\n    # to indicate the actual local header offset.\n    # As of python3.11, python does not handle zip64 central directories\n    # correctly, so we will manually do the parsing here.\n\n    # ZIP64 central directory extra field has two required fields:\n    # 2 bytes header ID and 2 bytes size field. Thes two require fields have\n    # a total size of 4 bytes. Then it has three other 8 bytes field, followed\n    # by a 4 byte disk number field. The last disk number field is not required\n    # to be present, but if it is present, the total size of extra field will be\n    # divisible by 8(because 2+2+4+8*n is always going to be multiple of 8)\n    # Most extra fields are optional, but when they appear, their must appear\n    # in the order defined by zip64 spec. Since file header offset is the 2nd\n    # to last field in zip64 spec, it will only be at last 8 bytes or last 12-4\n    # bytes, depending on whether disk number is present.\n    for entry in entries:\n      if entry.header_offset == 0xFFFFFFFF:\n        if len(entry.extra) % 8 == 0:\n          entry.header_offset = int.from_bytes(entry.extra[-12:-4], \"little\")\n        else:\n          entry.header_offset = int.from_bytes(entry.extra[-8:], \"little\")\n    if patterns is not None:\n      filtered = [info for info in entries if any(\n          [fnmatch.fnmatch(info.filename, p) for p in patterns])]\n\n      # There isn't any matching files. Don't unzip anything.\n      if not filtered:\n        return\n      for info in filtered:\n        UnzipSingleFile(input_zip, info, dirname)\n    else:\n      for info in entries:\n        UnzipSingleFile(input_zip, info, dirname)\n\n\ndef UnzipTemp(filename, patterns=None):\n  \"\"\"Unzips the given archive into a temporary directory and returns the name.\n\n  Args:\n    filename: If filename is of the form \"foo.zip+bar.zip\", unzip foo.zip into\n    a temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.\n\n    patterns: Files to unzip from the archive. If omitted, will unzip the entire\n    archvie.\n\n  Returns:\n    The name of the temporary directory.\n  \"\"\"\n\n  tmp = MakeTempDir(prefix=\"targetfiles-\")\n  m = re.match(r\"^(.*[.]zip)\\+(.*[.]zip)$\", filename, re.IGNORECASE)\n  if m:\n    UnzipToDir(m.group(1), tmp, patterns)\n    UnzipToDir(m.group(2), os.path.join(tmp, \"BOOTABLE_IMAGES\"), patterns)\n    filename = m.group(1)\n  else:\n    UnzipToDir(filename, tmp, patterns)\n\n  return tmp\n\n\ndef GetUserImage(which, tmpdir, input_zip,\n                 info_dict=None,\n                 allow_shared_blocks=None,\n                 reset_file_map=False):\n  \"\"\"Returns an Image object suitable for passing to BlockImageDiff.\n\n  This function loads the specified image from the given path. If the specified\n  image is sparse, it also performs additional processing for OTA purpose. For\n  example, it always adds block 0 to clobbered blocks list. It also detects\n  files that cannot be reconstructed from the block list, for whom we should\n  avoid applying imgdiff.\n\n  Args:\n    which: The partition name.\n    tmpdir: The directory that contains the prebuilt image and block map file.\n    input_zip: The target-files ZIP archive.\n    info_dict: The dict to be looked up for relevant info.\n    allow_shared_blocks: If image is sparse, whether having shared blocks is\n        allowed. If none, it is looked up from info_dict.\n    reset_file_map: If true and image is sparse, reset file map before returning\n        the image.\n  Returns:\n    A Image object. If it is a sparse image and reset_file_map is False, the\n    image will have file_map info loaded.\n  \"\"\"\n  if info_dict is None:\n    info_dict = LoadInfoDict(input_zip)\n\n  is_sparse = IsSparseImage(os.path.join(tmpdir, \"IMAGES\", which + \".img\"))\n\n  # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain\n  # shared blocks (i.e. some blocks will show up in multiple files' block\n  # list). We can only allocate such shared blocks to the first \"owner\", and\n  # disable imgdiff for all later occurrences.\n  if allow_shared_blocks is None:\n    allow_shared_blocks = info_dict.get(\"ext4_share_dup_blocks\") == \"true\"\n\n  if is_sparse:\n    img = GetSparseImage(which, tmpdir, input_zip, allow_shared_blocks)\n    if reset_file_map:\n      img.ResetFileMap()\n    return img\n  return GetNonSparseImage(which, tmpdir)\n\n\ndef GetNonSparseImage(which, tmpdir):\n  \"\"\"Returns a Image object suitable for passing to BlockImageDiff.\n\n  This function loads the specified non-sparse image from the given path.\n\n  Args:\n    which: The partition name.\n    tmpdir: The directory that contains the prebuilt image and block map file.\n  Returns:\n    A Image object.\n  \"\"\"\n  path = os.path.join(tmpdir, \"IMAGES\", which + \".img\")\n  mappath = os.path.join(tmpdir, \"IMAGES\", which + \".map\")\n\n  # The image and map files must have been created prior to calling\n  # ota_from_target_files.py (since LMP).\n  assert os.path.exists(path) and os.path.exists(mappath)\n\n  return images.FileImage(path)\n\n\ndef GetSparseImage(which, tmpdir, input_zip, allow_shared_blocks):\n  \"\"\"Returns a SparseImage object suitable for passing to BlockImageDiff.\n\n  This function loads the specified sparse image from the given path, and\n  performs additional processing for OTA purpose. For example, it always adds\n  block 0 to clobbered blocks list. It also detects files that cannot be\n  reconstructed from the block list, for whom we should avoid applying imgdiff.\n\n  Args:\n    which: The partition name, e.g. \"system\", \"vendor\".\n    tmpdir: The directory that contains the prebuilt image and block map file.\n    input_zip: The target-files ZIP archive.\n    allow_shared_blocks: Whether having shared blocks is allowed.\n  Returns:\n    A SparseImage object, with file_map info loaded.\n  \"\"\"\n  path = os.path.join(tmpdir, \"IMAGES\", which + \".img\")\n  mappath = os.path.join(tmpdir, \"IMAGES\", which + \".map\")\n\n  # The image and map files must have been created prior to calling\n  # ota_from_target_files.py (since LMP).\n  assert os.path.exists(path) and os.path.exists(mappath)\n\n  # In ext4 filesystems, block 0 might be changed even being mounted R/O. We add\n  # it to clobbered_blocks so that it will be written to the target\n  # unconditionally. Note that they are still part of care_map. (Bug: 20939131)\n  clobbered_blocks = \"0\"\n\n  image = sparse_img.SparseImage(\n      path, mappath, clobbered_blocks, allow_shared_blocks=allow_shared_blocks)\n\n  # block.map may contain less blocks, because mke2fs may skip allocating blocks\n  # if they contain all zeros. We can't reconstruct such a file from its block\n  # list. Tag such entries accordingly. (Bug: 65213616)\n  for entry in image.file_map:\n    # Skip artificial names, such as \"__ZERO\", \"__NONZERO-1\".\n    if not entry.startswith('/'):\n      continue\n\n    # \"/system/framework/am.jar\" => \"SYSTEM/framework/am.jar\". Note that the\n    # filename listed in system.map may contain an additional leading slash\n    # (i.e. \"//system/framework/am.jar\"). Using lstrip to get consistent\n    # results.\n    # And handle another special case, where files not under /system\n    # (e.g. \"/sbin/charger\") are packed under ROOT/ in a target_files.zip.\n    arcname = entry.lstrip('/')\n    if which == 'system' and not arcname.startswith('system'):\n      arcname = 'ROOT/' + arcname\n    else:\n      arcname = arcname.replace(which, which.upper(), 1)\n\n    assert arcname in input_zip.namelist(), \\\n        \"Failed to find the ZIP entry for {}\".format(entry)\n\n    info = input_zip.getinfo(arcname)\n    ranges = image.file_map[entry]\n\n    # If a RangeSet has been tagged as using shared blocks while loading the\n    # image, check the original block list to determine its completeness. Note\n    # that the 'incomplete' flag would be tagged to the original RangeSet only.\n    if ranges.extra.get('uses_shared_blocks'):\n      ranges = ranges.extra['uses_shared_blocks']\n\n    if RoundUpTo4K(info.file_size) > ranges.size() * 4096:\n      ranges.extra['incomplete'] = True\n\n  return image\n\n\ndef GetKeyPasswords(keylist):\n  \"\"\"Given a list of keys, prompt the user to enter passwords for\n  those which require them.  Return a {key: password} dict.  password\n  will be None if the key has no password.\"\"\"\n\n  no_passwords = []\n  need_passwords = []\n  key_passwords = {}\n  devnull = open(\"/dev/null\", \"w+b\")\n\n  # sorted() can't compare strings to None, so convert Nones to strings\n  for k in sorted(keylist, key=lambda x: x if x is not None else \"\"):\n    # We don't need a password for things that aren't really keys.\n    if k in SPECIAL_CERT_STRINGS or k is None:\n      no_passwords.append(k)\n      continue\n\n    p = Run([\"openssl\", \"pkcs8\", \"-in\", k+OPTIONS.private_key_suffix,\n             \"-inform\", \"DER\", \"-nocrypt\"],\n            stdin=devnull.fileno(),\n            stdout=devnull.fileno(),\n            stderr=subprocess.STDOUT)\n    p.communicate()\n    if p.returncode == 0:\n      # Definitely an unencrypted key.\n      no_passwords.append(k)\n    else:\n      p = Run([\"openssl\", \"pkcs8\", \"-in\", k+OPTIONS.private_key_suffix,\n               \"-inform\", \"DER\", \"-passin\", \"pass:\"],\n              stdin=devnull.fileno(),\n              stdout=devnull.fileno(),\n              stderr=subprocess.PIPE)\n      _, stderr = p.communicate()\n      if p.returncode == 0:\n        # Encrypted key with empty string as password.\n        key_passwords[k] = ''\n      elif stderr.startswith('Error decrypting key'):\n        # Definitely encrypted key.\n        # It would have said \"Error reading key\" if it didn't parse correctly.\n        need_passwords.append(k)\n      else:\n        # Potentially, a type of key that openssl doesn't understand.\n        # We'll let the routines in signapk.jar handle it.\n        no_passwords.append(k)\n  devnull.close()\n\n  key_passwords.update(PasswordManager().GetPasswords(need_passwords))\n  key_passwords.update(dict.fromkeys(no_passwords))\n  return key_passwords\n\n\ndef GetMinSdkVersion(apk_name):\n  \"\"\"Gets the minSdkVersion declared in the APK.\n\n  It calls OPTIONS.aapt2_path to query the embedded minSdkVersion from the given\n  APK file. This can be both a decimal number (API Level) or a codename.\n\n  Args:\n    apk_name: The APK filename.\n\n  Returns:\n    The parsed SDK version string.\n\n  Raises:\n    ExternalError: On failing to obtain the min SDK version.\n  \"\"\"\n  proc = Run(\n      [OPTIONS.aapt2_path, \"dump\", \"badging\", apk_name], stdout=subprocess.PIPE,\n      stderr=subprocess.PIPE)\n  stdoutdata, stderrdata = proc.communicate()\n  if proc.returncode != 0:\n    raise ExternalError(\n        \"Failed to obtain minSdkVersion for {}: aapt2 return code {}:\\n{}\\n{}\".format(\n            apk_name, proc.returncode, stdoutdata, stderrdata))\n\n  is_split_apk = False\n  for line in stdoutdata.split(\"\\n\"):\n    # See b/353837347 , split APKs do not have sdk version defined,\n    # so we default to 21 as split APKs are only supported since SDK\n    # 21.\n    if (re.search(r\"split=[\\\"'].*[\\\"']\", line)):\n      is_split_apk = True\n    # Due to ag/24161708, looking for lines such as minSdkVersion:'23',minSdkVersion:'M'\n    # or sdkVersion:'23', sdkVersion:'M'.\n    m = re.match(r'(?:minSdkVersion|sdkVersion):\\'([^\\']*)\\'', line)\n    if m:\n      return m.group(1)\n  if is_split_apk:\n    logger.info(\"%s is a split APK, it does not have minimum SDK version\"\n                \" defined. Defaulting to 21 because split APK isn't supported\"\n                \" before that.\", apk_name)\n    return 21\n  raise ExternalError(\"No minSdkVersion returned by aapt2 for apk: {}\".format(apk_name))\n\n\ndef GetMinSdkVersionInt(apk_name, codename_to_api_level_map):\n  \"\"\"Returns the minSdkVersion declared in the APK as a number (API Level).\n\n  If minSdkVersion is set to a codename, it is translated to a number using the\n  provided map.\n\n  Args:\n    apk_name: The APK filename.\n\n  Returns:\n    The parsed SDK version number.\n\n  Raises:\n    ExternalError: On failing to get the min SDK version number.\n  \"\"\"\n  version = GetMinSdkVersion(apk_name)\n  try:\n    return int(version)\n  except ValueError:\n    # Not a decimal number.\n    #\n    # It could be either a straight codename, e.g.\n    #     UpsideDownCake\n    #\n    # Or a codename with API fingerprint SHA, e.g.\n    #     UpsideDownCake.e7d3947f14eb9dc4fec25ff6c5f8563e\n    #\n    # Extract the codename and try and map it to a version number.\n    split = version.split(\".\")\n    codename = split[0]\n    if codename in codename_to_api_level_map:\n      return codename_to_api_level_map[codename]\n    raise ExternalError(\n        \"Unknown codename: '{}' from minSdkVersion: '{}'. Known codenames: {}\".format(\n            codename, version, codename_to_api_level_map))\n\n\ndef SignFile(input_name, output_name, key, password, min_api_level=None,\n             codename_to_api_level_map=None, whole_file=False,\n             extra_signapk_args=None):\n  \"\"\"Sign the input_name zip/jar/apk, producing output_name.  Use the\n  given key and password (the latter may be None if the key does not\n  have a password.\n\n  If whole_file is true, use the \"-w\" option to SignApk to embed a\n  signature that covers the whole file in the archive comment of the\n  zip file.\n\n  min_api_level is the API Level (int) of the oldest platform this file may end\n  up on. If not specified for an APK, the API Level is obtained by interpreting\n  the minSdkVersion attribute of the APK's AndroidManifest.xml.\n\n  codename_to_api_level_map is needed to translate the codename which may be\n  encountered as the APK's minSdkVersion.\n\n  Caller may optionally specify extra args to be passed to SignApk, which\n  defaults to OPTIONS.extra_signapk_args if omitted.\n  \"\"\"\n  if codename_to_api_level_map is None:\n    codename_to_api_level_map = {}\n  if extra_signapk_args is None:\n    extra_signapk_args = OPTIONS.extra_signapk_args\n\n  java_library_path = os.path.join(\n      OPTIONS.search_path, OPTIONS.signapk_shared_library_path)\n\n  cmd = ([OPTIONS.java_path] + OPTIONS.java_args +\n         [\"-Djava.library.path=\" + java_library_path,\n          \"-jar\", os.path.join(OPTIONS.search_path, OPTIONS.signapk_path)] +\n         extra_signapk_args)\n  if whole_file:\n    cmd.append(\"-w\")\n\n  min_sdk_version = min_api_level\n  if min_sdk_version is None:\n    if not whole_file:\n      min_sdk_version = GetMinSdkVersionInt(\n          input_name, codename_to_api_level_map)\n  if min_sdk_version is not None:\n    cmd.extend([\"--min-sdk-version\", str(min_sdk_version)])\n\n  cmd.extend([key + OPTIONS.public_key_suffix,\n              key + OPTIONS.private_key_suffix,\n              input_name, output_name])\n\n  proc = Run(cmd, stdin=subprocess.PIPE)\n  if password is not None:\n    password += \"\\n\"\n  stdoutdata, _ = proc.communicate(password)\n  if proc.returncode != 0:\n    raise ExternalError(\n        \"Failed to run {}: return code {}:\\n{}\".format(cmd,\n                                                       proc.returncode, stdoutdata))\n\n\ndef CheckSize(data, target, info_dict):\n  \"\"\"Checks the data string passed against the max size limit.\n\n  For non-AVB images, raise exception if the data is too big. Print a warning\n  if the data is nearing the maximum size.\n\n  For AVB images, the actual image size should be identical to the limit.\n\n  Args:\n    data: A string that contains all the data for the partition.\n    target: The partition name. The \".img\" suffix is optional.\n    info_dict: The dict to be looked up for relevant info.\n  \"\"\"\n  if target.endswith(\".img\"):\n    target = target[:-4]\n  mount_point = \"/\" + target\n\n  fs_type = None\n  limit = None\n  if info_dict[\"fstab\"]:\n    if mount_point == \"/userdata\":\n      mount_point = \"/data\"\n    p = info_dict[\"fstab\"][mount_point]\n    fs_type = p.fs_type\n    device = p.device\n    if \"/\" in device:\n      device = device[device.rfind(\"/\")+1:]\n    limit = info_dict.get(device + \"_size\", 0)\n    if isinstance(limit, str):\n      limit = int(limit, 0)\n  if not fs_type or not limit:\n    return\n\n  size = len(data)\n  # target could be 'userdata' or 'cache'. They should follow the non-AVB image\n  # path.\n  if info_dict.get(\"avb_enable\") == \"true\" and target in AVB_PARTITIONS:\n    if size != limit:\n      raise ExternalError(\n          \"Mismatching image size for %s: expected %d actual %d\" % (\n              target, limit, size))\n  else:\n    pct = float(size) * 100.0 / limit\n    msg = \"%s size (%d) is %.2f%% of limit (%d)\" % (target, size, pct, limit)\n    if pct >= 99.0:\n      raise ExternalError(msg)\n\n    if pct >= 95.0:\n      logger.warning(\"\\n  WARNING: %s\\n\", msg)\n    else:\n      logger.info(\"  %s\", msg)\n\n\ndef ReadApkCerts(tf_zip):\n  \"\"\"Parses the APK certs info from a given target-files zip.\n\n  Given a target-files ZipFile, parses the META/apkcerts.txt entry and returns a\n  tuple with the following elements: (1) a dictionary that maps packages to\n  certs (based on the \"certificate\" and \"private_key\" attributes in the file;\n  (2) a string representing the extension of compressed APKs in the target files\n  (e.g \".gz\", \".bro\").\n\n  Args:\n    tf_zip: The input target_files ZipFile (already open).\n\n  Returns:\n    (certmap, ext): certmap is a dictionary that maps packages to certs; ext is\n        the extension string of compressed APKs (e.g. \".gz\"), or None if there's\n        no compressed APKs.\n  \"\"\"\n  certmap = {}\n  compressed_extension = None\n\n  # META/apkcerts.txt contains the info for _all_ the packages known at build\n  # time. Filter out the ones that are not installed.\n  installed_files = set()\n  for name in tf_zip.namelist():\n    basename = os.path.basename(name)\n    if basename:\n      installed_files.add(basename)\n\n  for line in tf_zip.read('META/apkcerts.txt').decode().split('\\n'):\n    line = line.strip()\n    if not line:\n      continue\n    m = re.match(\n        r'^name=\"(?P<NAME>.*)\"\\s+certificate=\"(?P<CERT>.*)\"\\s+'\n        r'private_key=\"(?P<PRIVKEY>.*?)\"(\\s+compressed=\"(?P<COMPRESSED>.*?)\")?'\n        r'(\\s+partition=\"(?P<PARTITION>.*?)\")?$',\n        line)\n    if not m:\n      continue\n\n    matches = m.groupdict()\n    cert = matches[\"CERT\"]\n    privkey = matches[\"PRIVKEY\"]\n    name = matches[\"NAME\"]\n    this_compressed_extension = matches[\"COMPRESSED\"]\n\n    public_key_suffix_len = len(OPTIONS.public_key_suffix)\n    private_key_suffix_len = len(OPTIONS.private_key_suffix)\n    if cert in SPECIAL_CERT_STRINGS and not privkey:\n      certmap[name] = cert\n    elif (cert.endswith(OPTIONS.public_key_suffix) and\n          privkey.endswith(OPTIONS.private_key_suffix) and\n          cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):\n      certmap[name] = cert[:-public_key_suffix_len]\n    else:\n      raise ValueError(\"Failed to parse line from apkcerts.txt:\\n\" + line)\n\n    if not this_compressed_extension:\n      continue\n\n    # Only count the installed files.\n    filename = name + '.' + this_compressed_extension\n    if filename not in installed_files:\n      continue\n\n    # Make sure that all the values in the compression map have the same\n    # extension. We don't support multiple compression methods in the same\n    # system image.\n    if compressed_extension:\n      if this_compressed_extension != compressed_extension:\n        raise ValueError(\n            \"Multiple compressed extensions: {} vs {}\".format(\n                compressed_extension, this_compressed_extension))\n    else:\n      compressed_extension = this_compressed_extension\n\n  return (certmap,\n          (\".\" + compressed_extension) if compressed_extension else None)\n\n\nCOMMON_DOCSTRING = \"\"\"\nGlobal options\n\n  -p  (--path) <dir>\n      Prepend <dir>/bin to the list of places to search for binaries run by this\n      script, and expect to find jars in <dir>/framework.\n\n  -s  (--device_specific) <file>\n      Path to the Python module containing device-specific releasetools code.\n\n  -x  (--extra) <key=value>\n      Add a key/value pair to the 'extras' dict, which device-specific extension\n      code may look at.\n\n  -v  (--verbose)\n      Show command lines being executed.\n\n  -h  (--help)\n      Display this usage message and exit.\n\n  --logfile <file>\n      Put verbose logs to specified file (regardless of --verbose option.)\n\"\"\"\n\n\ndef Usage(docstring):\n  print(docstring.rstrip(\"\\n\"))\n  print(COMMON_DOCSTRING)\n\n\ndef ParseOptions(argv,\n                 docstring,\n                 extra_opts=\"\", extra_long_opts=(),\n                 extra_option_handler: Iterable[OptionHandler] = None):\n  \"\"\"Parse the options in argv and return any arguments that aren't\n  flags.  docstring is the calling module's docstring, to be displayed\n  for errors and -h.  extra_opts and extra_long_opts are for flags\n  defined by the caller, which are processed by passing them to\n  extra_option_handler.\"\"\"\n  extra_long_opts = list(extra_long_opts)\n  if not isinstance(extra_option_handler, Iterable):\n    extra_option_handler = [extra_option_handler]\n\n  for handler in extra_option_handler:\n    if isinstance(handler, OptionHandler):\n      extra_long_opts.extend(handler.extra_long_opts)\n\n  try:\n    opts, args = getopt.getopt(\n        argv, \"hvp:s:x:\" + extra_opts,\n        [\"help\", \"verbose\", \"path=\", \"signapk_path=\",\n         \"signapk_shared_library_path=\", \"extra_signapk_args=\", \"aapt2_path=\",\n         \"java_path=\", \"java_args=\", \"android_jar_path=\", \"public_key_suffix=\",\n         \"private_key_suffix=\", \"boot_signer_path=\", \"boot_signer_args=\",\n         \"verity_signer_path=\", \"verity_signer_args=\", \"device_specific=\",\n         \"extra=\", \"logfile=\"] + list(extra_long_opts))\n  except getopt.GetoptError as err:\n    Usage(docstring)\n    print(\"**\", str(err), \"**\")\n    sys.exit(2)\n\n  for o, a in opts:\n    if o in (\"-h\", \"--help\"):\n      Usage(docstring)\n      sys.exit()\n    elif o in (\"-v\", \"--verbose\"):\n      OPTIONS.verbose = True\n    elif o in (\"-p\", \"--path\"):\n      OPTIONS.search_path = a\n    elif o in (\"--signapk_path\",):\n      OPTIONS.signapk_path = a\n    elif o in (\"--signapk_shared_library_path\",):\n      OPTIONS.signapk_shared_library_path = a\n    elif o in (\"--extra_signapk_args\",):\n      OPTIONS.extra_signapk_args = shlex.split(a)\n    elif o in (\"--aapt2_path\",):\n      OPTIONS.aapt2_path = a\n    elif o in (\"--java_path\",):\n      OPTIONS.java_path = a\n    elif o in (\"--java_args\",):\n      OPTIONS.java_args = shlex.split(a)\n    elif o in (\"--android_jar_path\",):\n      OPTIONS.android_jar_path = a\n    elif o in (\"--public_key_suffix\",):\n      OPTIONS.public_key_suffix = a\n    elif o in (\"--private_key_suffix\",):\n      OPTIONS.private_key_suffix = a\n    elif o in (\"--boot_signer_path\",):\n      raise ValueError(\n          \"--boot_signer_path is no longer supported, please switch to AVB\")\n    elif o in (\"--boot_signer_args\",):\n      raise ValueError(\n          \"--boot_signer_args is no longer supported, please switch to AVB\")\n    elif o in (\"--verity_signer_path\",):\n      raise ValueError(\n          \"--verity_signer_path is no longer supported, please switch to AVB\")\n    elif o in (\"--verity_signer_args\",):\n      raise ValueError(\n          \"--verity_signer_args is no longer supported, please switch to AVB\")\n    elif o in (\"-s\", \"--device_specific\"):\n      OPTIONS.device_specific = a\n    elif o in (\"-x\", \"--extra\"):\n      key, value = a.split(\"=\", 1)\n      OPTIONS.extras[key] = value\n    elif o in (\"--logfile\",):\n      OPTIONS.logfile = a\n    else:\n      if extra_option_handler is None:\n        raise ValueError(\"unknown option \\\"%s\\\"\" % (o,))\n      success = False\n      for handler in extra_option_handler:\n        if isinstance(handler, OptionHandler):\n          if handler.handler(o, a):\n            success = True\n            break\n        elif handler(o, a):\n          success = True\n          break\n      if not success:\n        raise ValueError(\"unknown option \\\"%s\\\"\" % (o,))\n\n\n  if OPTIONS.search_path:\n    os.environ[\"PATH\"] = (os.path.join(OPTIONS.search_path, \"bin\") +\n                          os.pathsep + os.environ[\"PATH\"])\n\n  return args\n\n\ndef MakeTempFile(prefix='tmp', suffix=''):\n  \"\"\"Make a temp file and add it to the list of things to be deleted\n  when Cleanup() is called.  Return the filename.\"\"\"\n  fd, fn = tempfile.mkstemp(prefix=prefix, suffix=suffix)\n  os.close(fd)\n  OPTIONS.tempfiles.append(fn)\n  return fn\n\n\ndef MakeTempDir(prefix='tmp', suffix=''):\n  \"\"\"Makes a temporary dir that will be cleaned up with a call to Cleanup().\n\n  Returns:\n    The absolute pathname of the new directory.\n  \"\"\"\n  dir_name = tempfile.mkdtemp(suffix=suffix, prefix=prefix)\n  OPTIONS.tempfiles.append(dir_name)\n  return dir_name\n\n\ndef Cleanup():\n  for i in OPTIONS.tempfiles:\n    if not os.path.exists(i):\n      continue\n    if os.path.isdir(i):\n      shutil.rmtree(i, ignore_errors=True)\n    else:\n      os.remove(i)\n  del OPTIONS.tempfiles[:]\n\n\nclass PasswordManager(object):\n  def __init__(self):\n    self.editor = os.getenv(\"EDITOR\")\n    self.pwfile = os.getenv(\"ANDROID_PW_FILE\")\n\n  def GetPasswords(self, items):\n    \"\"\"Get passwords corresponding to each string in 'items',\n    returning a dict.  (The dict may have keys in addition to the\n    values in 'items'.)\n\n    Uses the passwords in $ANDROID_PW_FILE if available, letting the\n    user edit that file to add more needed passwords.  If no editor is\n    available, or $ANDROID_PW_FILE isn't define, prompts the user\n    interactively in the ordinary way.\n    \"\"\"\n\n    current = self.ReadFile()\n\n    first = True\n    while True:\n      missing = []\n      for i in items:\n        if i not in current or not current[i]:\n          missing.append(i)\n      # Are all the passwords already in the file?\n      if not missing:\n        return current\n\n      for i in missing:\n        current[i] = \"\"\n\n      if not first:\n        print(\"key file %s still missing some passwords.\" % (self.pwfile,))\n        if sys.version_info[0] >= 3:\n          raw_input = input  # pylint: disable=redefined-builtin\n        answer = raw_input(\"try to edit again? [y]> \").strip()\n        if answer and answer[0] not in 'yY':\n          raise RuntimeError(\"key passwords unavailable\")\n      first = False\n\n      current = self.UpdateAndReadFile(current)\n\n  def PromptResult(self, current):  # pylint: disable=no-self-use\n    \"\"\"Prompt the user to enter a value (password) for each key in\n    'current' whose value is fales.  Returns a new dict with all the\n    values.\n    \"\"\"\n    result = {}\n    for k, v in sorted(current.items()):\n      if v:\n        result[k] = v\n      else:\n        while True:\n          result[k] = getpass.getpass(\n              \"Enter password for %s key> \" % k).strip()\n          if result[k]:\n            break\n    return result\n\n  def UpdateAndReadFile(self, current):\n    if not self.editor or not self.pwfile:\n      return self.PromptResult(current)\n\n    f = open(self.pwfile, \"w\")\n    os.chmod(self.pwfile, 0o600)\n    f.write(\"# Enter key passwords between the [[[ ]]] brackets.\\n\")\n    f.write(\"# (Additional spaces are harmless.)\\n\\n\")\n\n    first_line = None\n    sorted_list = sorted([(not v, k, v) for (k, v) in current.items()])\n    for i, (_, k, v) in enumerate(sorted_list):\n      f.write(\"[[[  %s  ]]] %s\\n\" % (v, k))\n      if not v and first_line is None:\n        # position cursor on first line with no password.\n        first_line = i + 4\n    f.close()\n\n    RunAndCheckOutput([self.editor, \"+%d\" % (first_line,), self.pwfile])\n\n    return self.ReadFile()\n\n  def ReadFile(self):\n    result = {}\n    if self.pwfile is None:\n      return result\n    try:\n      f = open(self.pwfile, \"r\")\n      for line in f:\n        line = line.strip()\n        if not line or line[0] == '#':\n          continue\n        m = re.match(r\"^\\[\\[\\[\\s*(.*?)\\s*\\]\\]\\]\\s*(\\S+)$\", line)\n        if not m:\n          logger.warning(\"Failed to parse password file: %s\", line)\n        else:\n          result[m.group(2)] = m.group(1)\n      f.close()\n    except IOError as e:\n      if e.errno != errno.ENOENT:\n        logger.exception(\"Error reading password file:\")\n    return result\n\n\ndef ZipWrite(zip_file, filename, arcname=None, perms=0o644,\n             compress_type=None):\n\n  # http://b/18015246\n  # Python 2.7's zipfile implementation wrongly thinks that zip64 is required\n  # for files larger than 2GiB. We can work around this by adjusting their\n  # limit. Note that `zipfile.writestr()` will not work for strings larger than\n  # 2GiB. The Python interpreter sometimes rejects strings that large (though\n  # it isn't clear to me exactly what circumstances cause this).\n  # `zipfile.write()` must be used directly to work around this.\n  #\n  # This mess can be avoided if we port to python3.\n  saved_zip64_limit = zipfile.ZIP64_LIMIT\n  zipfile.ZIP64_LIMIT = (1 << 32) - 1\n\n  if compress_type is None:\n    compress_type = zip_file.compression\n  if arcname is None:\n    arcname = filename\n\n  saved_stat = os.stat(filename)\n\n  try:\n    # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the\n    # file to be zipped and reset it when we're done.\n    os.chmod(filename, perms)\n\n    # Use a fixed timestamp so the output is repeatable.\n    # Note: Use of fromtimestamp without specifying a timezone here is\n    # intentional. zip stores datetimes in local time without a time zone\n    # attached, so we need \"epoch\" but in the local time zone to get 2009/01/01\n    # in the zip archive.\n    local_epoch = datetime.datetime.fromtimestamp(0)\n    timestamp = (datetime.datetime(2009, 1, 1) - local_epoch).total_seconds()\n    os.utime(filename, (timestamp, timestamp))\n\n    zip_file.write(filename, arcname=arcname, compress_type=compress_type)\n  finally:\n    os.chmod(filename, saved_stat.st_mode)\n    os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))\n    zipfile.ZIP64_LIMIT = saved_zip64_limit\n\n\ndef ZipWriteStr(zip_file: zipfile.ZipFile, zinfo_or_arcname, data, perms=None,\n                compress_type=None):\n  \"\"\"Wrap zipfile.writestr() function to work around the zip64 limit.\n\n  Even with the ZIP64_LIMIT workaround, it won't allow writing a string\n  longer than 2GiB. It gives 'OverflowError: size does not fit in an int'\n  when calling crc32(bytes).\n\n  But it still works fine to write a shorter string into a large zip file.\n  We should use ZipWrite() whenever possible, and only use ZipWriteStr()\n  when we know the string won't be too long.\n  \"\"\"\n\n  saved_zip64_limit = zipfile.ZIP64_LIMIT\n  zipfile.ZIP64_LIMIT = (1 << 32) - 1\n\n  if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):\n    zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname)\n    zinfo.compress_type = zip_file.compression\n    if perms is None:\n      perms = 0o100644\n  else:\n    zinfo = zinfo_or_arcname\n    # Python 2 and 3 behave differently when calling ZipFile.writestr() with\n    # zinfo.external_attr being 0. Python 3 uses `0o600 << 16` as the value for\n    # such a case (since\n    # https://github.com/python/cpython/commit/18ee29d0b870caddc0806916ca2c823254f1a1f9),\n    # which seems to make more sense. Otherwise the entry will have 0o000 as the\n    # permission bits. We follow the logic in Python 3 to get consistent\n    # behavior between using the two versions.\n    if not zinfo.external_attr:\n      zinfo.external_attr = 0o600 << 16\n\n  # If compress_type is given, it overrides the value in zinfo.\n  if compress_type is not None:\n    zinfo.compress_type = compress_type\n\n  # If perms is given, it has a priority.\n  if perms is not None:\n    # If perms doesn't set the file type, mark it as a regular file.\n    if perms & 0o770000 == 0:\n      perms |= 0o100000\n    zinfo.external_attr = perms << 16\n\n  # Use a fixed timestamp so the output is repeatable.\n  zinfo.date_time = (2009, 1, 1, 0, 0, 0)\n\n  zip_file.writestr(zinfo, data)\n  zipfile.ZIP64_LIMIT = saved_zip64_limit\n\ndef ZipExclude(input_zip, output_zip, entries, force=False):\n  \"\"\"Deletes entries from a ZIP file.\n\n  Args:\n    zip_filename: The name of the ZIP file.\n    entries: The name of the entry, or the list of names to be deleted.\n  \"\"\"\n  if isinstance(entries, str):\n    entries = [entries]\n  # If list is empty, nothing to do\n  if not entries:\n    shutil.copy(input_zip, output_zip)\n    return\n\n  with zipfile.ZipFile(input_zip, 'r') as zin:\n    if not force and len(set(zin.namelist()).intersection(entries)) == 0:\n      raise ExternalError(\n          \"Failed to delete zip entries, name not matched: %s\" % entries)\n\n    fd, new_zipfile = tempfile.mkstemp(dir=os.path.dirname(input_zip))\n    os.close(fd)\n    cmd = [\"zip2zip\", \"-i\", input_zip, \"-o\", new_zipfile]\n    for entry in entries:\n      cmd.append(\"-x\")\n      cmd.append(entry)\n    RunAndCheckOutput(cmd)\n  os.replace(new_zipfile, output_zip)\n\n\ndef ZipDelete(zip_filename, entries, force=False):\n  \"\"\"Deletes entries from a ZIP file.\n\n  Args:\n    zip_filename: The name of the ZIP file.\n    entries: The name of the entry, or the list of names to be deleted.\n  \"\"\"\n  if isinstance(entries, str):\n    entries = [entries]\n  # If list is empty, nothing to do\n  if not entries:\n    return\n\n  ZipExclude(zip_filename, zip_filename, entries, force)\n\n\ndef ZipClose(zip_file):\n  # http://b/18015246\n  # zipfile also refers to ZIP64_LIMIT during close() when it writes out the\n  # central directory.\n  saved_zip64_limit = zipfile.ZIP64_LIMIT\n  zipfile.ZIP64_LIMIT = (1 << 32) - 1\n\n  zip_file.close()\n\n  zipfile.ZIP64_LIMIT = saved_zip64_limit\n\n\nclass DeviceSpecificParams(object):\n  module = None\n\n  def __init__(self, **kwargs):\n    \"\"\"Keyword arguments to the constructor become attributes of this\n    object, which is passed to all functions in the device-specific\n    module.\"\"\"\n    for k, v in kwargs.items():\n      setattr(self, k, v)\n    self.extras = OPTIONS.extras\n\n    if self.module is None:\n      path = OPTIONS.device_specific\n      if not path:\n        return\n      try:\n        if os.path.isdir(path):\n          path = os.path.join(path, \"releasetools\")\n          if os.path.isdir(path):\n            path = os.path.join(path, \"__init__.py\")\n        if not os.path.exists(path) and os.path.exists(path + \".py\"):\n          path = path + \".py\"\n        spec = importlib.util.spec_from_file_location(\"device_specific\", path)\n        if not spec:\n          raise FileNotFoundError(path)\n        logger.info(\"loaded device-specific extensions from %s\", path)\n        module = importlib.util.module_from_spec(spec)\n        spec.loader.exec_module(module)\n        self.module = module\n      except (ImportError, FileNotFoundError):\n        logger.info(\"unable to load device-specific module; assuming none\")\n\n  def _DoCall(self, function_name, *args, **kwargs):\n    \"\"\"Call the named function in the device-specific module, passing\n    the given args and kwargs.  The first argument to the call will be\n    the DeviceSpecific object itself.  If there is no module, or the\n    module does not define the function, return the value of the\n    'default' kwarg (which itself defaults to None).\"\"\"\n    if self.module is None or not hasattr(self.module, function_name):\n      return kwargs.get(\"default\")\n    return getattr(self.module, function_name)(*((self,) + args), **kwargs)\n\n  def FullOTA_Assertions(self):\n    \"\"\"Called after emitting the block of assertions at the top of a\n    full OTA package.  Implementations can add whatever additional\n    assertions they like.\"\"\"\n    return self._DoCall(\"FullOTA_Assertions\")\n\n  def FullOTA_InstallBegin(self):\n    \"\"\"Called at the start of full OTA installation.\"\"\"\n    return self._DoCall(\"FullOTA_InstallBegin\")\n\n  def FullOTA_GetBlockDifferences(self):\n    \"\"\"Called during full OTA installation and verification.\n    Implementation should return a list of BlockDifference objects describing\n    the update on each additional partitions.\n    \"\"\"\n    return self._DoCall(\"FullOTA_GetBlockDifferences\")\n\n  def FullOTA_InstallEnd(self):\n    \"\"\"Called at the end of full OTA installation; typically this is\n    used to install the image for the device's baseband processor.\"\"\"\n    return self._DoCall(\"FullOTA_InstallEnd\")\n\n  def IncrementalOTA_Assertions(self):\n    \"\"\"Called after emitting the block of assertions at the top of an\n    incremental OTA package.  Implementations can add whatever\n    additional assertions they like.\"\"\"\n    return self._DoCall(\"IncrementalOTA_Assertions\")\n\n  def IncrementalOTA_VerifyBegin(self):\n    \"\"\"Called at the start of the verification phase of incremental\n    OTA installation; additional checks can be placed here to abort\n    the script before any changes are made.\"\"\"\n    return self._DoCall(\"IncrementalOTA_VerifyBegin\")\n\n  def IncrementalOTA_VerifyEnd(self):\n    \"\"\"Called at the end of the verification phase of incremental OTA\n    installation; additional checks can be placed here to abort the\n    script before any changes are made.\"\"\"\n    return self._DoCall(\"IncrementalOTA_VerifyEnd\")\n\n  def IncrementalOTA_InstallBegin(self):\n    \"\"\"Called at the start of incremental OTA installation (after\n    verification is complete).\"\"\"\n    return self._DoCall(\"IncrementalOTA_InstallBegin\")\n\n  def IncrementalOTA_GetBlockDifferences(self):\n    \"\"\"Called during incremental OTA installation and verification.\n    Implementation should return a list of BlockDifference objects describing\n    the update on each additional partitions.\n    \"\"\"\n    return self._DoCall(\"IncrementalOTA_GetBlockDifferences\")\n\n  def IncrementalOTA_InstallEnd(self):\n    \"\"\"Called at the end of incremental OTA installation; typically\n    this is used to install the image for the device's baseband\n    processor.\"\"\"\n    return self._DoCall(\"IncrementalOTA_InstallEnd\")\n\n  def VerifyOTA_Assertions(self):\n    return self._DoCall(\"VerifyOTA_Assertions\")\n\n\nclass File(object):\n  def __init__(self, name, data, compress_size=None):\n    self.name = name\n    self.data = data\n    self.size = len(data)\n    self.compress_size = compress_size or self.size\n    self.sha1 = sha1(data).hexdigest()\n\n  @classmethod\n  def FromLocalFile(cls, name, diskname):\n    f = open(diskname, \"rb\")\n    data = f.read()\n    f.close()\n    return File(name, data)\n\n  def WriteToTemp(self):\n    t = tempfile.NamedTemporaryFile()\n    t.write(self.data)\n    t.flush()\n    return t\n\n  def WriteToDir(self, d):\n    output_path = os.path.join(d, self.name)\n    os.makedirs(os.path.dirname(output_path), exist_ok=True)\n    with open(output_path, \"wb\") as fp:\n      fp.write(self.data)\n\n  def AddToZip(self, z, compression=None):\n    ZipWriteStr(z, self.name, self.data, compress_type=compression)\n\n\nDIFF_PROGRAM_BY_EXT = {\n    \".gz\": \"imgdiff\",\n    \".zip\": [\"imgdiff\", \"-z\"],\n    \".jar\": [\"imgdiff\", \"-z\"],\n    \".apk\": [\"imgdiff\", \"-z\"],\n    \".img\": \"imgdiff\",\n}\n\n\nclass Difference(object):\n  def __init__(self, tf, sf, diff_program=None):\n    self.tf = tf\n    self.sf = sf\n    self.patch = None\n    self.diff_program = diff_program\n\n  def ComputePatch(self):\n    \"\"\"Compute the patch (as a string of data) needed to turn sf into\n    tf.  Returns the same tuple as GetPatch().\"\"\"\n\n    tf = self.tf\n    sf = self.sf\n\n    if self.diff_program:\n      diff_program = self.diff_program\n    else:\n      ext = os.path.splitext(tf.name)[1]\n      diff_program = DIFF_PROGRAM_BY_EXT.get(ext, \"bsdiff\")\n\n    ttemp = tf.WriteToTemp()\n    stemp = sf.WriteToTemp()\n\n    ext = os.path.splitext(tf.name)[1]\n\n    try:\n      ptemp = tempfile.NamedTemporaryFile()\n      if isinstance(diff_program, list):\n        cmd = copy.copy(diff_program)\n      else:\n        cmd = [diff_program]\n      cmd.append(stemp.name)\n      cmd.append(ttemp.name)\n      cmd.append(ptemp.name)\n      p = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n      err = []\n\n      def run():\n        _, e = p.communicate()\n        if e:\n          err.append(e)\n      th = threading.Thread(target=run)\n      th.start()\n      th.join(timeout=300)   # 5 mins\n      if th.is_alive():\n        logger.warning(\"diff command timed out\")\n        p.terminate()\n        th.join(5)\n        if th.is_alive():\n          p.kill()\n          th.join()\n\n      if p.returncode != 0:\n        logger.warning(\"Failure running %s:\\n%s\\n\", cmd, \"\".join(err))\n        self.patch = None\n        return None, None, None\n      diff = ptemp.read()\n    finally:\n      ptemp.close()\n      stemp.close()\n      ttemp.close()\n\n    self.patch = diff\n    return self.tf, self.sf, self.patch\n\n  def GetPatch(self):\n    \"\"\"Returns a tuple of (target_file, source_file, patch_data).\n\n    patch_data may be None if ComputePatch hasn't been called, or if\n    computing the patch failed.\n    \"\"\"\n    return self.tf, self.sf, self.patch\n\n\ndef ComputeDifferences(diffs):\n  \"\"\"Call ComputePatch on all the Difference objects in 'diffs'.\"\"\"\n  logger.info(\"%d diffs to compute\", len(diffs))\n\n  # Do the largest files first, to try and reduce the long-pole effect.\n  by_size = [(i.tf.size, i) for i in diffs]\n  by_size.sort(reverse=True)\n  by_size = [i[1] for i in by_size]\n\n  lock = threading.Lock()\n  diff_iter = iter(by_size)   # accessed under lock\n\n  def worker():\n    try:\n      lock.acquire()\n      for d in diff_iter:\n        lock.release()\n        start = time.time()\n        d.ComputePatch()\n        dur = time.time() - start\n        lock.acquire()\n\n        tf, sf, patch = d.GetPatch()\n        if sf.name == tf.name:\n          name = tf.name\n        else:\n          name = \"%s (%s)\" % (tf.name, sf.name)\n        if patch is None:\n          logger.error(\"patching failed! %40s\", name)\n        else:\n          logger.info(\n              \"%8.2f sec %8d / %8d bytes (%6.2f%%) %s\", dur, len(patch),\n              tf.size, 100.0 * len(patch) / tf.size, name)\n      lock.release()\n    except Exception:\n      logger.exception(\"Failed to compute diff from worker\")\n      raise\n\n  # start worker threads; wait for them all to finish.\n  threads = [threading.Thread(target=worker)\n             for i in range(OPTIONS.worker_threads)]\n  for th in threads:\n    th.start()\n  while threads:\n    threads.pop().join()\n\n\nclass BlockDifference(object):\n  def __init__(self, partition, tgt, src=None, check_first_block=False,\n               version=None, disable_imgdiff=False):\n    self.tgt = tgt\n    self.src = src\n    self.partition = partition\n    self.check_first_block = check_first_block\n    self.disable_imgdiff = disable_imgdiff\n\n    if version is None:\n      version = max(\n          int(i) for i in\n          OPTIONS.info_dict.get(\"blockimgdiff_versions\", \"1\").split(\",\"))\n    assert version >= 3\n    self.version = version\n\n    b = BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,\n                       version=self.version,\n                       disable_imgdiff=self.disable_imgdiff)\n    self.path = os.path.join(MakeTempDir(), partition)\n    b.Compute(self.path)\n    self._required_cache = b.max_stashed_size\n    self.touched_src_ranges = b.touched_src_ranges\n    self.touched_src_sha1 = b.touched_src_sha1\n\n    # On devices with dynamic partitions, for new partitions,\n    # src is None but OPTIONS.source_info_dict is not.\n    if OPTIONS.source_info_dict is None:\n      is_dynamic_build = OPTIONS.info_dict.get(\n          \"use_dynamic_partitions\") == \"true\"\n      is_dynamic_source = False\n    else:\n      is_dynamic_build = OPTIONS.source_info_dict.get(\n          \"use_dynamic_partitions\") == \"true\"\n      is_dynamic_source = partition in shlex.split(\n          OPTIONS.source_info_dict.get(\"dynamic_partition_list\", \"\").strip())\n\n    is_dynamic_target = partition in shlex.split(\n        OPTIONS.info_dict.get(\"dynamic_partition_list\", \"\").strip())\n\n    # For dynamic partitions builds, check partition list in both source\n    # and target build because new partitions may be added, and existing\n    # partitions may be removed.\n    is_dynamic = is_dynamic_build and (is_dynamic_source or is_dynamic_target)\n\n    if is_dynamic:\n      self.device = 'map_partition(\"%s\")' % partition\n    else:\n      if OPTIONS.source_info_dict is None:\n        _, device_expr = GetTypeAndDeviceExpr(\"/\" + partition,\n                                              OPTIONS.info_dict)\n      else:\n        _, device_expr = GetTypeAndDeviceExpr(\"/\" + partition,\n                                              OPTIONS.source_info_dict)\n      self.device = device_expr\n\n  @property\n  def required_cache(self):\n    return self._required_cache\n\n  def WriteScript(self, script, output_zip, progress=None,\n                  write_verify_script=False):\n    if not self.src:\n      # write the output unconditionally\n      script.Print(\"Patching %s image unconditionally...\" % (self.partition,))\n    else:\n      script.Print(\"Patching %s image after verification.\" % (self.partition,))\n\n    if progress:\n      script.ShowProgress(progress, 0)\n    self._WriteUpdate(script, output_zip)\n\n    if write_verify_script:\n      self.WritePostInstallVerifyScript(script)\n\n  def WriteStrictVerifyScript(self, script):\n    \"\"\"Verify all the blocks in the care_map, including clobbered blocks.\n\n    This differs from the WriteVerifyScript() function: a) it prints different\n    error messages; b) it doesn't allow half-way updated images to pass the\n    verification.\"\"\"\n\n    partition = self.partition\n    script.Print(\"Verifying %s...\" % (partition,))\n    ranges = self.tgt.care_map\n    ranges_str = ranges.to_string_raw()\n    script.AppendExtra(\n        'range_sha1(%s, \"%s\") == \"%s\" && ui_print(\"    Verified.\") || '\n        'ui_print(\"%s has unexpected contents.\");' % (\n            self.device, ranges_str,\n            self.tgt.TotalSha1(include_clobbered_blocks=True),\n            self.partition))\n    script.AppendExtra(\"\")\n\n  def WriteVerifyScript(self, script, touched_blocks_only=False):\n    partition = self.partition\n\n    # full OTA\n    if not self.src:\n      script.Print(\"Image %s will be patched unconditionally.\" % (partition,))\n\n    # incremental OTA\n    else:\n      if touched_blocks_only:\n        ranges = self.touched_src_ranges\n        expected_sha1 = self.touched_src_sha1\n      else:\n        ranges = self.src.care_map.subtract(self.src.clobbered_blocks)\n        expected_sha1 = self.src.TotalSha1()\n\n      # No blocks to be checked, skipping.\n      if not ranges:\n        return\n\n      ranges_str = ranges.to_string_raw()\n      script.AppendExtra(\n          'if (range_sha1(%s, \"%s\") == \"%s\" || block_image_verify(%s, '\n          'package_extract_file(\"%s.transfer.list\"), \"%s.new.dat\", '\n          '\"%s.patch.dat\")) then' % (\n              self.device, ranges_str, expected_sha1,\n              self.device, partition, partition, partition))\n      script.Print('Verified %s image...' % (partition,))\n      script.AppendExtra('else')\n\n      if self.version >= 4:\n\n        # Bug: 21124327\n        # When generating incrementals for the system and vendor partitions in\n        # version 4 or newer, explicitly check the first block (which contains\n        # the superblock) of the partition to see if it's what we expect. If\n        # this check fails, give an explicit log message about the partition\n        # having been remounted R/W (the most likely explanation).\n        if self.check_first_block:\n          script.AppendExtra('check_first_block(%s);' % (self.device,))\n\n        # If version >= 4, try block recovery before abort update\n        if partition == \"system\":\n          code = ErrorCode.SYSTEM_RECOVER_FAILURE\n        else:\n          code = ErrorCode.VENDOR_RECOVER_FAILURE\n        script.AppendExtra((\n            'ifelse (block_image_recover({device}, \"{ranges}\") && '\n            'block_image_verify({device}, '\n            'package_extract_file(\"{partition}.transfer.list\"), '\n            '\"{partition}.new.dat\", \"{partition}.patch.dat\"), '\n            'ui_print(\"{partition} recovered successfully.\"), '\n            'abort(\"E{code}: {partition} partition fails to recover\"));\\n'\n            'endif;').format(device=self.device, ranges=ranges_str,\n                             partition=partition, code=code))\n\n      # Abort the OTA update. Note that the incremental OTA cannot be applied\n      # even if it may match the checksum of the target partition.\n      # a) If version < 3, operations like move and erase will make changes\n      #    unconditionally and damage the partition.\n      # b) If version >= 3, it won't even reach here.\n      else:\n        if partition == \"system\":\n          code = ErrorCode.SYSTEM_VERIFICATION_FAILURE\n        else:\n          code = ErrorCode.VENDOR_VERIFICATION_FAILURE\n        script.AppendExtra((\n            'abort(\"E%d: %s partition has unexpected contents\");\\n'\n            'endif;') % (code, partition))\n\n  def WritePostInstallVerifyScript(self, script):\n    partition = self.partition\n    script.Print('Verifying the updated %s image...' % (partition,))\n    # Unlike pre-install verification, clobbered_blocks should not be ignored.\n    ranges = self.tgt.care_map\n    ranges_str = ranges.to_string_raw()\n    script.AppendExtra(\n        'if range_sha1(%s, \"%s\") == \"%s\" then' % (\n            self.device, ranges_str,\n            self.tgt.TotalSha1(include_clobbered_blocks=True)))\n\n    # Bug: 20881595\n    # Verify that extended blocks are really zeroed out.\n    if self.tgt.extended:\n      ranges_str = self.tgt.extended.to_string_raw()\n      script.AppendExtra(\n          'if range_sha1(%s, \"%s\") == \"%s\" then' % (\n              self.device, ranges_str,\n              self._HashZeroBlocks(self.tgt.extended.size())))\n      script.Print('Verified the updated %s image.' % (partition,))\n      if partition == \"system\":\n        code = ErrorCode.SYSTEM_NONZERO_CONTENTS\n      else:\n        code = ErrorCode.VENDOR_NONZERO_CONTENTS\n      script.AppendExtra(\n          'else\\n'\n          '  abort(\"E%d: %s partition has unexpected non-zero contents after '\n          'OTA update\");\\n'\n          'endif;' % (code, partition))\n    else:\n      script.Print('Verified the updated %s image.' % (partition,))\n\n    if partition == \"system\":\n      code = ErrorCode.SYSTEM_UNEXPECTED_CONTENTS\n    else:\n      code = ErrorCode.VENDOR_UNEXPECTED_CONTENTS\n\n    script.AppendExtra(\n        'else\\n'\n        '  abort(\"E%d: %s partition has unexpected contents after OTA '\n        'update\");\\n'\n        'endif;' % (code, partition))\n\n  def _WriteUpdate(self, script, output_zip):\n    ZipWrite(output_zip,\n             '{}.transfer.list'.format(self.path),\n             '{}.transfer.list'.format(self.partition))\n\n    # For full OTA, compress the new.dat with brotli with quality 6 to reduce\n    # its size. Quailty 9 almost triples the compression time but doesn't\n    # further reduce the size too much. For a typical 1.8G system.new.dat\n    #                       zip  | brotli(quality 6)  | brotli(quality 9)\n    #   compressed_size:    942M | 869M (~8% reduced) | 854M\n    #   compression_time:   75s  | 265s               | 719s\n    #   decompression_time: 15s  | 25s                | 25s\n\n    if not self.src:\n      brotli_cmd = ['brotli', '--quality=6',\n                    '--output={}.new.dat.br'.format(self.path),\n                    '{}.new.dat'.format(self.path)]\n      print(\"Compressing {}.new.dat with brotli\".format(self.partition))\n      RunAndCheckOutput(brotli_cmd)\n\n      new_data_name = '{}.new.dat.br'.format(self.partition)\n      ZipWrite(output_zip,\n               '{}.new.dat.br'.format(self.path),\n               new_data_name,\n               compress_type=zipfile.ZIP_STORED)\n    else:\n      new_data_name = '{}.new.dat'.format(self.partition)\n      ZipWrite(output_zip, '{}.new.dat'.format(self.path), new_data_name)\n\n    ZipWrite(output_zip,\n             '{}.patch.dat'.format(self.path),\n             '{}.patch.dat'.format(self.partition),\n             compress_type=zipfile.ZIP_STORED)\n\n    if self.partition == \"system\":\n      code = ErrorCode.SYSTEM_UPDATE_FAILURE\n    else:\n      code = ErrorCode.VENDOR_UPDATE_FAILURE\n\n    call = ('block_image_update({device}, '\n            'package_extract_file(\"{partition}.transfer.list\"), '\n            '\"{new_data_name}\", \"{partition}.patch.dat\") ||\\n'\n            '  abort(\"E{code}: Failed to update {partition} image.\");'.format(\n                device=self.device, partition=self.partition,\n                new_data_name=new_data_name, code=code))\n    script.AppendExtra(script.WordWrap(call))\n\n  def _HashBlocks(self, source, ranges):  # pylint: disable=no-self-use\n    data = source.ReadRangeSet(ranges)\n    ctx = sha1()\n\n    for p in data:\n      ctx.update(p)\n\n    return ctx.hexdigest()\n\n  def _HashZeroBlocks(self, num_blocks):  # pylint: disable=no-self-use\n    \"\"\"Return the hash value for all zero blocks.\"\"\"\n    zero_block = '\\x00' * 4096\n    ctx = sha1()\n    for _ in range(num_blocks):\n      ctx.update(zero_block)\n\n    return ctx.hexdigest()\n\n\n# Expose these two classes to support vendor-specific scripts\nDataImage = images.DataImage\nEmptyImage = images.EmptyImage\n\n\n# map recovery.fstab's fs_types to mount/format \"partition types\"\nPARTITION_TYPES = {\n    \"ext4\": \"EMMC\",\n    \"emmc\": \"EMMC\",\n    \"f2fs\": \"EMMC\",\n    \"squashfs\": \"EMMC\",\n    \"erofs\": \"EMMC\"\n}\n\n\ndef GetTypeAndDevice(mount_point, info, check_no_slot=True):\n  \"\"\"\n  Use GetTypeAndDeviceExpr whenever possible. This function is kept for\n  backwards compatibility. It aborts if the fstab entry has slotselect option\n  (unless check_no_slot is explicitly set to False).\n  \"\"\"\n  fstab = info[\"fstab\"]\n  if fstab:\n    if check_no_slot:\n      assert not fstab[mount_point].slotselect, \\\n          \"Use GetTypeAndDeviceExpr instead\"\n    return (PARTITION_TYPES[fstab[mount_point].fs_type],\n            fstab[mount_point].device)\n  raise KeyError\n\n\ndef GetTypeAndDeviceExpr(mount_point, info):\n  \"\"\"\n  Return the filesystem of the partition, and an edify expression that evaluates\n  to the device at runtime.\n  \"\"\"\n  fstab = info[\"fstab\"]\n  if fstab:\n    p = fstab[mount_point]\n    device_expr = '\"%s\"' % fstab[mount_point].device\n    if p.slotselect:\n      device_expr = 'add_slot_suffix(%s)' % device_expr\n    return (PARTITION_TYPES[fstab[mount_point].fs_type], device_expr)\n  raise KeyError\n\n\ndef GetEntryForDevice(fstab, device):\n  \"\"\"\n  Returns:\n    The first entry in fstab whose device is the given value.\n  \"\"\"\n  if not fstab:\n    return None\n  for mount_point in fstab:\n    if fstab[mount_point].device == device:\n      return fstab[mount_point]\n  return None\n\n\ndef ParseCertificate(data):\n  \"\"\"Parses and converts a PEM-encoded certificate into DER-encoded.\n\n  This gives the same result as `openssl x509 -in <filename> -outform DER`.\n\n  Returns:\n    The decoded certificate bytes.\n  \"\"\"\n  cert_buffer = []\n  save = False\n  for line in data.split(\"\\n\"):\n    if \"--END CERTIFICATE--\" in line:\n      break\n    if save:\n      cert_buffer.append(line)\n    if \"--BEGIN CERTIFICATE--\" in line:\n      save = True\n  cert = base64.b64decode(\"\".join(cert_buffer))\n  return cert\n\n\ndef ExtractPublicKey(cert):\n  \"\"\"Extracts the public key (PEM-encoded) from the given certificate file.\n\n  Args:\n    cert: The certificate filename.\n\n  Returns:\n    The public key string.\n\n  Raises:\n    AssertionError: On non-zero return from 'openssl'.\n  \"\"\"\n  # The behavior with '-out' is different between openssl 1.1 and openssl 1.0.\n  # While openssl 1.1 writes the key into the given filename followed by '-out',\n  # openssl 1.0 (both of 1.0.1 and 1.0.2) doesn't. So we collect the output from\n  # stdout instead.\n  cmd = ['openssl', 'x509', '-pubkey', '-noout', '-in', cert]\n  proc = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n  pubkey, stderrdata = proc.communicate()\n  assert proc.returncode == 0, \\\n      'Failed to dump public key from certificate: %s\\n%s' % (cert, stderrdata)\n  return pubkey\n\n\ndef ExtractAvbPublicKey(avbtool, key):\n  \"\"\"Extracts the AVB public key from the given public or private key.\n\n  Args:\n    avbtool: The AVB tool to use.\n    key: The input key file, which should be PEM-encoded public or private key.\n\n  Returns:\n    The path to the extracted AVB public key file.\n  \"\"\"\n  output = MakeTempFile(prefix='avb-', suffix='.avbpubkey')\n  RunAndCheckOutput(\n      [avbtool, 'extract_public_key', \"--key\", key, \"--output\", output])\n  return output\n\n\ndef MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,\n                      info_dict=None):\n  \"\"\"Generates the recovery-from-boot patch and writes the script to output.\n\n  Most of the space in the boot and recovery images is just the kernel, which is\n  identical for the two, so the resulting patch should be efficient. Add it to\n  the output zip, along with a shell script that is run from init.rc on first\n  boot to actually do the patching and install the new recovery image.\n\n  Args:\n    input_dir: The top-level input directory of the target-files.zip.\n    output_sink: The callback function that writes the result.\n    recovery_img: File object for the recovery image.\n    boot_img: File objects for the boot image.\n    info_dict: A dict returned by common.LoadInfoDict() on the input\n        target_files. Will use OPTIONS.info_dict if None has been given.\n  \"\"\"\n  if info_dict is None:\n    info_dict = OPTIONS.info_dict\n\n  full_recovery_image = info_dict.get(\"full_recovery_image\") == \"true\"\n  board_uses_vendorimage = info_dict.get(\"board_uses_vendorimage\") == \"true\"\n\n  if board_uses_vendorimage:\n    # In this case, the output sink is rooted at VENDOR\n    recovery_img_path = \"etc/recovery.img\"\n    recovery_resource_dat_path = \"VENDOR/etc/recovery-resource.dat\"\n    sh_dir = \"bin\"\n  else:\n    # In this case the output sink is rooted at SYSTEM\n    recovery_img_path = \"vendor/etc/recovery.img\"\n    recovery_resource_dat_path = \"SYSTEM/vendor/etc/recovery-resource.dat\"\n    sh_dir = \"vendor/bin\"\n\n  if full_recovery_image:\n    output_sink(recovery_img_path, recovery_img.data)\n\n  else:\n    include_recovery_dtbo = info_dict.get(\"include_recovery_dtbo\") == \"true\"\n    include_recovery_acpio = info_dict.get(\"include_recovery_acpio\") == \"true\"\n    path = os.path.join(input_dir, recovery_resource_dat_path)\n    # Use bsdiff to handle mismatching entries (Bug: 72731506)\n    if include_recovery_dtbo or include_recovery_acpio:\n      diff_program = [\"bsdiff\"]\n      bonus_args = \"\"\n      assert not os.path.exists(path)\n    else:\n      diff_program = [\"imgdiff\"]\n      if os.path.exists(path):\n        diff_program.append(\"-b\")\n        diff_program.append(path)\n        bonus_args = \"--bonus /vendor/etc/recovery-resource.dat\"\n      else:\n        bonus_args = \"\"\n\n    d = Difference(recovery_img, boot_img, diff_program=diff_program)\n    _, _, patch = d.ComputePatch()\n    output_sink(\"recovery-from-boot.p\", patch)\n\n  try:\n    # The following GetTypeAndDevice()s need to use the path in the target\n    # info_dict instead of source_info_dict.\n    boot_type, boot_device = GetTypeAndDevice(\"/boot\", info_dict,\n                                              check_no_slot=False)\n    recovery_type, recovery_device = GetTypeAndDevice(\"/recovery\", info_dict,\n                                                      check_no_slot=False)\n  except KeyError:\n    return\n\n  if full_recovery_image:\n\n    # Note that we use /vendor to refer to the recovery resources. This will\n    # work for a separate vendor partition mounted at /vendor or a\n    # /system/vendor subdirectory on the system partition, for which init will\n    # create a symlink from /vendor to /system/vendor.\n\n    sh = \"\"\"#!/vendor/bin/sh\nif ! applypatch --check %(type)s:%(device)s:%(size)d:%(sha1)s; then\n  applypatch \\\\\n          --flash /vendor/etc/recovery.img \\\\\n          --target %(type)s:%(device)s:%(size)d:%(sha1)s && \\\\\n      log -t recovery \"Installing new recovery image: succeeded\" || \\\\\n      log -t recovery \"Installing new recovery image: failed\"\nelse\n  log -t recovery \"Recovery image already installed\"\nfi\n\"\"\" % {'type': recovery_type,\n       'device': recovery_device,\n       'sha1': recovery_img.sha1,\n       'size': recovery_img.size}\n  else:\n    sh = \"\"\"#!/vendor/bin/sh\nif ! applypatch --check %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then\n  applypatch %(bonus_args)s \\\\\n          --patch /vendor/recovery-from-boot.p \\\\\n          --source %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s \\\\\n          --target %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s && \\\\\n      log -t recovery \"Installing new recovery image: succeeded\" || \\\\\n      log -t recovery \"Installing new recovery image: failed\"\nelse\n  log -t recovery \"Recovery image already installed\"\nfi\n\"\"\" % {'boot_size': boot_img.size,\n       'boot_sha1': boot_img.sha1,\n       'recovery_size': recovery_img.size,\n       'recovery_sha1': recovery_img.sha1,\n       'boot_type': boot_type,\n       'boot_device': boot_device + '$(getprop ro.boot.slot_suffix)',\n       'recovery_type': recovery_type,\n       'recovery_device': recovery_device + '$(getprop ro.boot.slot_suffix)',\n       'bonus_args': bonus_args}\n\n  # The install script location moved from /system/etc to /system/bin in the L\n  # release. In the R release it is in VENDOR/bin or SYSTEM/vendor/bin.\n  sh_location = os.path.join(sh_dir, \"install-recovery.sh\")\n\n  logger.info(\"putting script in %s\", sh_location)\n\n  output_sink(sh_location, sh.encode())\n\n\nclass DynamicPartitionUpdate(object):\n  def __init__(self, src_group=None, tgt_group=None, progress=None,\n               block_difference=None):\n    self.src_group = src_group\n    self.tgt_group = tgt_group\n    self.progress = progress\n    self.block_difference = block_difference\n\n  @property\n  def src_size(self):\n    if not self.block_difference:\n      return 0\n    return DynamicPartitionUpdate._GetSparseImageSize(self.block_difference.src)\n\n  @property\n  def tgt_size(self):\n    if not self.block_difference:\n      return 0\n    return DynamicPartitionUpdate._GetSparseImageSize(self.block_difference.tgt)\n\n  @staticmethod\n  def _GetSparseImageSize(img):\n    if not img:\n      return 0\n    return img.blocksize * img.total_blocks\n\n\nclass DynamicGroupUpdate(object):\n  def __init__(self, src_size=None, tgt_size=None):\n    # None: group does not exist. 0: no size limits.\n    self.src_size = src_size\n    self.tgt_size = tgt_size\n\n\nclass DynamicPartitionsDifference(object):\n  def __init__(self, info_dict, block_diffs, progress_dict=None,\n               source_info_dict=None):\n    if progress_dict is None:\n      progress_dict = {}\n\n    self._remove_all_before_apply = False\n    if source_info_dict is None:\n      self._remove_all_before_apply = True\n      source_info_dict = {}\n\n    block_diff_dict = collections.OrderedDict(\n        [(e.partition, e) for e in block_diffs])\n\n    assert len(block_diff_dict) == len(block_diffs), \\\n        \"Duplicated BlockDifference object for {}\".format(\n            [partition for partition, count in\n             collections.Counter(e.partition for e in block_diffs).items()\n             if count > 1])\n\n    self._partition_updates = collections.OrderedDict()\n\n    for p, block_diff in block_diff_dict.items():\n      self._partition_updates[p] = DynamicPartitionUpdate()\n      self._partition_updates[p].block_difference = block_diff\n\n    for p, progress in progress_dict.items():\n      if p in self._partition_updates:\n        self._partition_updates[p].progress = progress\n\n    tgt_groups = shlex.split(info_dict.get(\n        \"super_partition_groups\", \"\").strip())\n    src_groups = shlex.split(source_info_dict.get(\n        \"super_partition_groups\", \"\").strip())\n\n    for g in tgt_groups:\n      for p in shlex.split(info_dict.get(\n              \"super_%s_partition_list\" % g, \"\").strip()):\n        assert p in self._partition_updates, \\\n            \"{} is in target super_{}_partition_list but no BlockDifference \" \\\n            \"object is provided.\".format(p, g)\n        self._partition_updates[p].tgt_group = g\n\n    for g in src_groups:\n      for p in shlex.split(source_info_dict.get(\n              \"super_%s_partition_list\" % g, \"\").strip()):\n        assert p in self._partition_updates, \\\n            \"{} is in source super_{}_partition_list but no BlockDifference \" \\\n            \"object is provided.\".format(p, g)\n        self._partition_updates[p].src_group = g\n\n    target_dynamic_partitions = set(shlex.split(info_dict.get(\n        \"dynamic_partition_list\", \"\").strip()))\n    block_diffs_with_target = set(p for p, u in self._partition_updates.items()\n                                  if u.tgt_size)\n    assert block_diffs_with_target == target_dynamic_partitions, \\\n        \"Target Dynamic partitions: {}, BlockDifference with target: {}\".format(\n            list(target_dynamic_partitions), list(block_diffs_with_target))\n\n    source_dynamic_partitions = set(shlex.split(source_info_dict.get(\n        \"dynamic_partition_list\", \"\").strip()))\n    block_diffs_with_source = set(p for p, u in self._partition_updates.items()\n                                  if u.src_size)\n    assert block_diffs_with_source == source_dynamic_partitions, \\\n        \"Source Dynamic partitions: {}, BlockDifference with source: {}\".format(\n            list(source_dynamic_partitions), list(block_diffs_with_source))\n\n    if self._partition_updates:\n      logger.info(\"Updating dynamic partitions %s\",\n                  self._partition_updates.keys())\n\n    self._group_updates = collections.OrderedDict()\n\n    for g in tgt_groups:\n      self._group_updates[g] = DynamicGroupUpdate()\n      self._group_updates[g].tgt_size = int(info_dict.get(\n          \"super_%s_group_size\" % g, \"0\").strip())\n\n    for g in src_groups:\n      if g not in self._group_updates:\n        self._group_updates[g] = DynamicGroupUpdate()\n      self._group_updates[g].src_size = int(source_info_dict.get(\n          \"super_%s_group_size\" % g, \"0\").strip())\n\n    self._Compute()\n\n  def WriteScript(self, script, output_zip, write_verify_script=False):\n    script.Comment('--- Start patching dynamic partitions ---')\n    for p, u in self._partition_updates.items():\n      if u.src_size and u.tgt_size and u.src_size > u.tgt_size:\n        script.Comment('Patch partition %s' % p)\n        u.block_difference.WriteScript(script, output_zip, progress=u.progress,\n                                       write_verify_script=False)\n\n    op_list_path = MakeTempFile()\n    with open(op_list_path, 'w') as f:\n      for line in self._op_list:\n        f.write('{}\\n'.format(line))\n\n    ZipWrite(output_zip, op_list_path, \"dynamic_partitions_op_list\")\n\n    script.Comment('Update dynamic partition metadata')\n    script.AppendExtra('assert(update_dynamic_partitions('\n                       'package_extract_file(\"dynamic_partitions_op_list\")));')\n\n    if write_verify_script:\n      for p, u in self._partition_updates.items():\n        if u.src_size and u.tgt_size and u.src_size > u.tgt_size:\n          u.block_difference.WritePostInstallVerifyScript(script)\n          script.AppendExtra('unmap_partition(\"%s\");' % p)  # ignore errors\n\n    for p, u in self._partition_updates.items():\n      if u.tgt_size and u.src_size <= u.tgt_size:\n        script.Comment('Patch partition %s' % p)\n        u.block_difference.WriteScript(script, output_zip, progress=u.progress,\n                                       write_verify_script=write_verify_script)\n        if write_verify_script:\n          script.AppendExtra('unmap_partition(\"%s\");' % p)  # ignore errors\n\n    script.Comment('--- End patching dynamic partitions ---')\n\n  def _Compute(self):\n    self._op_list = list()\n\n    def append(line):\n      self._op_list.append(line)\n\n    def comment(line):\n      self._op_list.append(\"# %s\" % line)\n\n    if self._remove_all_before_apply:\n      comment('Remove all existing dynamic partitions and groups before '\n              'applying full OTA')\n      append('remove_all_groups')\n\n    for p, u in self._partition_updates.items():\n      if u.src_group and not u.tgt_group:\n        append('remove %s' % p)\n\n    for p, u in self._partition_updates.items():\n      if u.src_group and u.tgt_group and u.src_group != u.tgt_group:\n        comment('Move partition %s from %s to default' % (p, u.src_group))\n        append('move %s default' % p)\n\n    for p, u in self._partition_updates.items():\n      if u.src_size and u.tgt_size and u.src_size > u.tgt_size:\n        comment('Shrink partition %s from %d to %d' %\n                (p, u.src_size, u.tgt_size))\n        append('resize %s %s' % (p, u.tgt_size))\n\n    for g, u in self._group_updates.items():\n      if u.src_size is not None and u.tgt_size is None:\n        append('remove_group %s' % g)\n      if (u.src_size is not None and u.tgt_size is not None and\n              u.src_size > u.tgt_size):\n        comment('Shrink group %s from %d to %d' % (g, u.src_size, u.tgt_size))\n        append('resize_group %s %d' % (g, u.tgt_size))\n\n    for g, u in self._group_updates.items():\n      if u.src_size is None and u.tgt_size is not None:\n        comment('Add group %s with maximum size %d' % (g, u.tgt_size))\n        append('add_group %s %d' % (g, u.tgt_size))\n      if (u.src_size is not None and u.tgt_size is not None and\n              u.src_size < u.tgt_size):\n        comment('Grow group %s from %d to %d' % (g, u.src_size, u.tgt_size))\n        append('resize_group %s %d' % (g, u.tgt_size))\n\n    for p, u in self._partition_updates.items():\n      if u.tgt_group and not u.src_group:\n        comment('Add partition %s to group %s' % (p, u.tgt_group))\n        append('add %s %s' % (p, u.tgt_group))\n\n    for p, u in self._partition_updates.items():\n      if u.tgt_size and u.src_size < u.tgt_size:\n        comment('Grow partition %s from %d to %d' %\n                (p, u.src_size, u.tgt_size))\n        append('resize %s %d' % (p, u.tgt_size))\n\n    for p, u in self._partition_updates.items():\n      if u.src_group and u.tgt_group and u.src_group != u.tgt_group:\n        comment('Move partition %s from default to %s' %\n                (p, u.tgt_group))\n        append('move %s %s' % (p, u.tgt_group))\n\n\ndef GetBootImageBuildProp(boot_img, ramdisk_format=RamdiskFormat.LZ4):\n  \"\"\"\n  Get build.prop from ramdisk within the boot image\n\n  Args:\n    boot_img: the boot image file. Ramdisk must be compressed with lz4 or gzip format.\n\n  Return:\n    An extracted file that stores properties in the boot image.\n  \"\"\"\n  tmp_dir = MakeTempDir('boot_', suffix='.img')\n  try:\n    RunAndCheckOutput(['unpack_bootimg', '--boot_img',\n                      boot_img, '--out', tmp_dir])\n    ramdisk = os.path.join(tmp_dir, 'ramdisk')\n    if not os.path.isfile(ramdisk):\n      logger.warning('Unable to get boot image timestamp: no ramdisk in boot')\n      return None\n    uncompressed_ramdisk = os.path.join(tmp_dir, 'uncompressed_ramdisk')\n    if ramdisk_format == RamdiskFormat.LZ4:\n      RunAndCheckOutput(['lz4', '-d', ramdisk, uncompressed_ramdisk])\n    elif ramdisk_format == RamdiskFormat.GZ:\n      with open(ramdisk, 'rb') as input_stream:\n        with open(uncompressed_ramdisk, 'wb') as output_stream:\n          p2 = Run(['gzip', '-d'], stdin=input_stream.fileno(),\n                   stdout=output_stream.fileno())\n          p2.wait()\n    else:\n      logger.error('Only support lz4 or gzip ramdisk format.')\n      return None\n\n    abs_uncompressed_ramdisk = os.path.abspath(uncompressed_ramdisk)\n    extracted_ramdisk = MakeTempDir('extracted_ramdisk')\n    # Use \"toybox cpio\" instead of \"cpio\" because the latter invokes cpio from\n    # the host environment.\n    RunAndCheckOutput(['toybox', 'cpio', '-F', abs_uncompressed_ramdisk, '-i'],\n                      cwd=extracted_ramdisk)\n\n    for search_path in RAMDISK_BUILD_PROP_REL_PATHS:\n      prop_file = os.path.join(extracted_ramdisk, search_path)\n      if os.path.isfile(prop_file):\n        return prop_file\n      logger.warning(\n          'Unable to get boot image timestamp: no %s in ramdisk', search_path)\n\n    return None\n\n  except ExternalError as e:\n    logger.warning('Unable to get boot image build props: %s', e)\n    return None\n\n\ndef GetBootImageTimestamp(boot_img):\n  \"\"\"\n  Get timestamp from ramdisk within the boot image\n\n  Args:\n    boot_img: the boot image file. Ramdisk must be compressed with lz4 format.\n\n  Return:\n    An integer that corresponds to the timestamp of the boot image, or None\n    if file has unknown format. Raise exception if an unexpected error has\n    occurred.\n  \"\"\"\n  prop_file = GetBootImageBuildProp(boot_img)\n  if not prop_file:\n    return None\n\n  props = PartitionBuildProps.FromBuildPropFile('boot', prop_file)\n  if props is None:\n    return None\n\n  try:\n    timestamp = props.GetProp('ro.bootimage.build.date.utc')\n    if timestamp:\n      return int(timestamp)\n    logger.warning(\n        'Unable to get boot image timestamp: ro.bootimage.build.date.utc is undefined')\n    return None\n\n  except ExternalError as e:\n    logger.warning('Unable to get boot image timestamp: %s', e)\n    return None\n\n\ndef IsSparseImage(filepath):\n  if not os.path.exists(filepath):\n    return False\n  with open(filepath, 'rb') as fp:\n    # Magic for android sparse image format\n    # https://source.android.com/devices/bootloader/images\n    return fp.read(4) == b'\\x3A\\xFF\\x26\\xED'\n\n\ndef UnsparseImage(filepath, target_path=None):\n  if not IsSparseImage(filepath):\n    return\n  if target_path is None:\n    tmp_img = MakeTempFile(suffix=\".img\")\n    RunAndCheckOutput([\"simg2img\", filepath, tmp_img])\n    os.rename(tmp_img, filepath)\n  else:\n    RunAndCheckOutput([\"simg2img\", filepath, target_path])\n\n\ndef ParseUpdateEngineConfig(path: str):\n  \"\"\"Parse the update_engine config stored in file `path`\n  Args\n    path: Path to update_engine_config.txt file in target_files\n\n  Returns\n    A tuple of (major, minor) version number . E.g. (2, 8)\n  \"\"\"\n  with open(path, \"r\") as fp:\n    # update_engine_config.txt is only supposed to contain two lines,\n    # PAYLOAD_MAJOR_VERSION and PAYLOAD_MINOR_VERSION. 1024 should be more than\n    # sufficient. If the length is more than that, something is wrong.\n    data = fp.read(1024)\n    major = re.search(r\"PAYLOAD_MAJOR_VERSION=(\\d+)\", data)\n    if not major:\n      raise ValueError(\n          f\"{path} is an invalid update_engine config, missing PAYLOAD_MAJOR_VERSION {data}\")\n    minor = re.search(r\"PAYLOAD_MINOR_VERSION=(\\d+)\", data)\n    if not minor:\n      raise ValueError(\n          f\"{path} is an invalid update_engine config, missing PAYLOAD_MINOR_VERSION {data}\")\n    return (int(major.group(1)), int(minor.group(1)))\n"
  },
  {
    "path": "tools/releasetools/create_brick_ota.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport argparse\nfrom pathlib import Path\nimport zipfile\nfrom typing import List\nimport common\nimport tempfile\nimport shutil\n\nPARTITIONS_TO_WIPE = [\"/dev/block/by-name/vbmeta\",\n                      \"/dev/block/by-name/vbmeta_a\",\n                      \"/dev/block/by-name/vbmeta_b\",\n                      \"/dev/block/by-name/vbmeta_system_a\",\n                      \"/dev/block/by-name/vbmeta_system_b\",\n                      \"/dev/block/by-name/boot\",\n                      \"/dev/block/by-name/boot_a\",\n                      \"/dev/block/by-name/boot_b\",\n                      \"/dev/block/by-name/vendor_boot\",\n                      \"/dev/block/by-name/vendor_boot_a\",\n                      \"/dev/block/by-name/vendor_boot_b\",\n                      \"/dev/block/by-name/init_boot_a\",\n                      \"/dev/block/by-name/init_boot_b\",\n                      \"/dev/block/by-name/metadata\",\n                      \"/dev/block/by-name/super\",\n                      \"/dev/block/by-name/userdata\"]\n\n\ndef CreateBrickOta(product_name: str, output_path: Path, extra_wipe_partitions: str, serialno: str):\n  partitions_to_wipe = PARTITIONS_TO_WIPE\n  if extra_wipe_partitions is not None:\n    partitions_to_wipe = PARTITIONS_TO_WIPE + extra_wipe_partitions.split(\",\")\n  ota_metadata = [\"ota-type=BRICK\", \"post-timestamp=9999999999\",\n                  \"pre-device=\" + product_name]\n  if serialno is not None:\n      ota_metadata.append(\"serialno=\" + serialno)\n  # recovery requiers product name to be a | separated list\n  product_name = product_name.replace(\",\", \"|\")\n  with zipfile.ZipFile(output_path, \"w\") as zfp:\n    zfp.writestr(\"recovery.wipe\", \"\\n\".join(partitions_to_wipe))\n    zfp.writestr(\"payload.bin\", \"\")\n    zfp.writestr(\"META-INF/com/android/metadata\", \"\\n\".join(\n        ota_metadata))\n\n\ndef main(argv):\n  parser = argparse.ArgumentParser(description='Android Brick OTA generator')\n  parser.add_argument('otafile', metavar='PAYLOAD', type=str,\n                      help='The output OTA package file.')\n  parser.add_argument('--product', type=str,\n                      help='The product name of the device, for example, bramble, redfin.', required=True)\n  parser.add_argument('--serialno', type=str,\n                      help='The serial number of devices that are allowed to install this OTA package. This can be a | separated list.')\n  parser.add_argument('--extra_wipe_partitions', type=str,\n                      help='Additional partitions on device which should be wiped.')\n  parser.add_argument('-v', action=\"store_true\",\n                      help=\"Enable verbose logging\", dest=\"verbose\")\n  parser.add_argument('--package_key', type=str,\n                      help='Paths to private key for signing payload')\n  parser.add_argument('--search_path', type=str,\n                      help='Search path for framework/signapk.jar')\n  parser.add_argument('--private_key_suffix', type=str,\n                      help='Suffix to be appended to package_key path', default=\".pk8\")\n  args = parser.parse_args(argv[1:])\n  if args.search_path:\n    common.OPTIONS.search_path = args.search_path\n  if args.verbose:\n    common.OPTIONS.verbose = args.verbose\n  CreateBrickOta(args.product, args.otafile,\n                 args.extra_wipe_partitions, args.serialno)\n  if args.package_key:\n    common.OPTIONS.private_key_suffix = args.private_key_suffix\n    with tempfile.NamedTemporaryFile() as tmpfile:\n      common.SignFile(args.otafile, tmpfile.name,\n                      args.package_key, None, whole_file=True)\n      shutil.copy(tmpfile.name, args.otafile)\n\n\nif __name__ == \"__main__\":\n  import sys\n  main(sys.argv)\n"
  },
  {
    "path": "tools/releasetools/edify_generator.py",
    "content": "# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\n\nimport common\n\nclass EdifyGenerator(object):\n  \"\"\"Class to generate scripts in the 'edify' recovery script language\n  used from donut onwards.\"\"\"\n\n  def __init__(self, version, info, fstab=None):\n    self.script = []\n    self.mounts = set()\n    self._required_cache = 0\n    self.version = version\n    self.info = info\n    if fstab is None:\n      self.fstab = self.info.get(\"fstab\", None)\n    else:\n      self.fstab = fstab\n\n  @property\n  def required_cache(self):\n    \"\"\"Return the minimum cache size to apply the update.\"\"\"\n    return self._required_cache\n\n  @staticmethod\n  def WordWrap(cmd, linelen=80):\n    \"\"\"'cmd' should be a function call with null characters after each\n    parameter (eg, \"somefun(foo,\\0bar,\\0baz)\").  This function wraps cmd\n    to a given line length, replacing nulls with spaces and/or newlines\n    to format it nicely.\"\"\"\n    indent = cmd.index(\"(\")+1\n    out = []\n    first = True\n    x = re.compile(\"^(.{,%d})\\0\" % (linelen-indent,))\n    while True:\n      if not first:\n        out.append(\" \" * indent)\n      first = False\n      m = x.search(cmd)\n      if not m:\n        parts = cmd.split(\"\\0\", 1)\n        out.append(parts[0]+\"\\n\")\n        if len(parts) == 1:\n          break\n        else:\n          cmd = parts[1]\n          continue\n      out.append(m.group(1)+\"\\n\")\n      cmd = cmd[m.end():]\n\n    return \"\".join(out).replace(\"\\0\", \" \").rstrip(\"\\n\")\n\n  def AppendScript(self, other):\n    \"\"\"Append the contents of another script (which should be created\n    with temporary=True) to this one.\"\"\"\n    self.script.extend(other.script)\n\n  def AssertOemProperty(self, name, values, oem_no_mount):\n    \"\"\"Assert that a property on the OEM paritition matches allowed values.\"\"\"\n    if not name:\n      raise ValueError(\"must specify an OEM property\")\n    if not values:\n      raise ValueError(\"must specify the OEM value\")\n\n    if oem_no_mount:\n      get_prop_command = 'getprop(\"%s\")' % name\n    else:\n      get_prop_command = 'file_getprop(\"/oem/oem.prop\", \"%s\")' % name\n\n    cmd = ''\n    for value in values:\n      cmd += '%s == \"%s\" || ' % (get_prop_command, value)\n    cmd += (\n        'abort(\"E{code}: This package expects the value \\\\\"{values}\\\\\" for '\n        '\\\\\"{name}\\\\\"; this has value \\\\\"\" + '\n        '{get_prop_command} + \"\\\\\".\");').format(\n            code=common.ErrorCode.OEM_PROP_MISMATCH,\n            get_prop_command=get_prop_command, name=name,\n            values='\\\\\" or \\\\\"'.join(values))\n    self.script.append(cmd)\n\n  def AssertSomeFingerprint(self, *fp):\n    \"\"\"Assert that the current recovery build fingerprint is one of *fp.\"\"\"\n    if not fp:\n      raise ValueError(\"must specify some fingerprints\")\n    cmd = (' ||\\n    '.join([('getprop(\"ro.build.fingerprint\") == \"%s\"') % i\n                             for i in fp]) +\n           ' ||\\n    abort(\"E%d: Package expects build fingerprint of %s; '\n           'this device has \" + getprop(\"ro.build.fingerprint\") + \".\");') % (\n               common.ErrorCode.FINGERPRINT_MISMATCH, \" or \".join(fp))\n    self.script.append(cmd)\n\n  def AssertSomeThumbprint(self, *fp):\n    \"\"\"Assert that the current recovery build thumbprint is one of *fp.\"\"\"\n    if not fp:\n      raise ValueError(\"must specify some thumbprints\")\n    cmd = (' ||\\n    '.join([('getprop(\"ro.build.thumbprint\") == \"%s\"') % i\n                             for i in fp]) +\n           ' ||\\n    abort(\"E%d: Package expects build thumbprint of %s; this '\n           'device has \" + getprop(\"ro.build.thumbprint\") + \".\");') % (\n               common.ErrorCode.THUMBPRINT_MISMATCH, \" or \".join(fp))\n    self.script.append(cmd)\n\n  def AssertFingerprintOrThumbprint(self, fp, tp):\n    \"\"\"Assert that the current recovery build fingerprint is fp, or thumbprint\n       is tp.\"\"\"\n    cmd = ('getprop(\"ro.build.fingerprint\") == \"{fp}\" ||\\n'\n           '    getprop(\"ro.build.thumbprint\") == \"{tp}\" ||\\n'\n           '    abort(\"Package expects build fingerprint of {fp} or '\n           'thumbprint of {tp}; this device has a fingerprint of \" '\n           '+ getprop(\"ro.build.fingerprint\") + \" and a thumbprint of \" '\n           '+ getprop(\"ro.build.thumbprint\") + \".\");').format(fp=fp, tp=tp)\n    self.script.append(cmd)\n\n  def AssertOlderBuild(self, timestamp, timestamp_text):\n    \"\"\"Assert that the build on the device is older (or the same as)\n    the given timestamp.\"\"\"\n    self.script.append(\n        ('(!less_than_int(%s, getprop(\"ro.build.date.utc\"))) || '\n         'abort(\"E%d: Can\\'t install this package (%s) over newer '\n         'build (\" + getprop(\"ro.build.date\") + \").\");') % (\n             timestamp, common.ErrorCode.OLDER_BUILD, timestamp_text))\n\n  def AssertDevice(self, device):\n    \"\"\"Assert that the device identifier is the given string.\"\"\"\n    cmd = ('getprop(\"ro.product.device\") == \"%s\" || '\n           'abort(\"E%d: This package is for \\\\\"%s\\\\\" devices; '\n           'this is a \\\\\"\" + getprop(\"ro.product.device\") + \"\\\\\".\");') % (\n               device, common.ErrorCode.DEVICE_MISMATCH, device)\n    self.script.append(cmd)\n\n  def AssertSomeBootloader(self, *bootloaders):\n    \"\"\"Asert that the bootloader version is one of *bootloaders.\"\"\"\n    cmd = (\"assert(\" +\n           \" ||\\0\".join(['getprop(\"ro.bootloader\") == \"%s\"' % (b,)\n                         for b in bootloaders]) +\n           \");\")\n    self.script.append(self.WordWrap(cmd))\n\n  def ShowProgress(self, frac, dur):\n    \"\"\"Update the progress bar, advancing it over 'frac' over the next\n    'dur' seconds.  'dur' may be zero to advance it via SetProgress\n    commands instead of by time.\"\"\"\n    self.script.append(\"show_progress(%f, %d);\" % (frac, int(dur)))\n\n  def SetProgress(self, frac):\n    \"\"\"Set the position of the progress bar within the chunk defined\n    by the most recent ShowProgress call.  'frac' should be in\n    [0,1].\"\"\"\n    self.script.append(\"set_progress(%f);\" % (frac,))\n\n  def PatchCheck(self, filename, *sha1):  # pylint: disable=unused-argument\n    \"\"\"Checks that the given partition has the desired checksum.\n\n    The call to this function is being deprecated in favor of\n    PatchPartitionCheck(). It will try to parse and handle the old format,\n    unless the format is unknown.\n    \"\"\"\n    tokens = filename.split(':')\n    assert len(tokens) == 6 and tokens[0] == 'EMMC', \\\n        \"Failed to handle unknown format. Use PatchPartitionCheck() instead.\"\n    source = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[2], tokens[3])\n    target = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[4], tokens[5])\n    self.PatchPartitionCheck(target, source)\n\n  def PatchPartitionCheck(self, target, source):\n    \"\"\"Checks whether updater can patch the given partitions.\n\n    It checks the checksums of the given partitions. If none of them matches the\n    expected checksum, updater will additionally look for a backup on /cache.\n    \"\"\"\n    self._CheckSecondTokenNotSlotSuffixed(target, \"PatchPartitionExprCheck\")\n    self._CheckSecondTokenNotSlotSuffixed(source, \"PatchPartitionExprCheck\")\n    self.PatchPartitionExprCheck('\"%s\"' % target, '\"%s\"' % source)\n\n  def PatchPartitionExprCheck(self, target_expr, source_expr):\n    \"\"\"Checks whether updater can patch the given partitions.\n\n    It checks the checksums of the given partitions. If none of them matches the\n    expected checksum, updater will additionally look for a backup on /cache.\n\n    Args:\n      target_expr: an Edify expression that serves as the target arg to\n        patch_partition. Must be evaluated to a string in the form of\n        foo:bar:baz:quux\n      source_expr: an Edify expression that serves as the source arg to\n        patch_partition. Must be evaluated to a string in the form of\n        foo:bar:baz:quux\n    \"\"\"\n    self.script.append(self.WordWrap((\n        'patch_partition_check({target},\\0{source}) ||\\n    abort('\n        'concat(\"E{code}: \\\\\"\",{target},\"\\\\\" or \\\\\"\",{source},\"\\\\\" has '\n        'unexpected contents.\"));').format(\n            target=target_expr,\n            source=source_expr,\n            code=common.ErrorCode.BAD_PATCH_FILE)))\n\n  def CacheFreeSpaceCheck(self, amount):\n    \"\"\"Check that there's at least 'amount' space that can be made\n    available on /cache.\"\"\"\n    self._required_cache = max(self._required_cache, amount)\n    self.script.append(('apply_patch_space(%d) || abort(\"E%d: Not enough free '\n                        'space on /cache to apply patches.\");') % (\n                            amount,\n                            common.ErrorCode.INSUFFICIENT_CACHE_SPACE))\n\n  def Mount(self, mount_point, mount_options_by_format=\"\"):\n    \"\"\"Mount the partition with the given mount_point.\n      mount_options_by_format:\n      [fs_type=option[,option]...[|fs_type=option[,option]...]...]\n      where option is optname[=optvalue]\n      E.g. ext4=barrier=1,nodelalloc,errors=panic|f2fs=errors=recover\n    \"\"\"\n    fstab = self.fstab\n    if fstab:\n      p = fstab[mount_point]\n      mount_dict = {}\n      if mount_options_by_format is not None:\n        for option in mount_options_by_format.split(\"|\"):\n          if \"=\" in option:\n            key, value = option.split(\"=\", 1)\n            mount_dict[key] = value\n      mount_flags = mount_dict.get(p.fs_type, \"\")\n      if p.context is not None:\n        mount_flags = p.context + (\",\" + mount_flags if mount_flags else \"\")\n      self.script.append('mount(\"%s\", \"%s\", %s, \"%s\", \"%s\");' % (\n          p.fs_type, common.PARTITION_TYPES[p.fs_type],\n          self._GetSlotSuffixDeviceForEntry(p),\n          p.mount_point, mount_flags))\n      self.mounts.add(p.mount_point)\n\n  def Comment(self, comment):\n    \"\"\"Write a comment into the update script.\"\"\"\n    self.script.append(\"\")\n    for i in comment.split(\"\\n\"):\n      self.script.append(\"# \" + i)\n    self.script.append(\"\")\n\n  def Print(self, message):\n    \"\"\"Log a message to the screen (if the logs are visible).\"\"\"\n    self.script.append('ui_print(\"%s\");' % (message,))\n\n  def TunePartition(self, partition, *options):\n    fstab = self.fstab\n    if fstab:\n      p = fstab[partition]\n      if p.fs_type not in (\"ext2\", \"ext3\", \"ext4\"):\n        raise ValueError(\"Partition %s cannot be tuned\\n\" % (partition,))\n    self.script.append(\n        'tune2fs(' + \"\".join(['\"%s\", ' % (i,) for i in options]) +\n        '%s) || abort(\"E%d: Failed to tune partition %s\");' % (\n            self._GetSlotSuffixDeviceForEntry(p),\n            common.ErrorCode.TUNE_PARTITION_FAILURE, partition))\n\n  def FormatPartition(self, partition):\n    \"\"\"Format the given partition, specified by its mount point (eg,\n    \"/system\").\"\"\"\n\n    fstab = self.fstab\n    if fstab:\n      p = fstab[partition]\n      self.script.append('format(\"%s\", \"%s\", %s, \"%s\", \"%s\");' %\n                         (p.fs_type, common.PARTITION_TYPES[p.fs_type],\n                          self._GetSlotSuffixDeviceForEntry(p),\n                          p.length, p.mount_point))\n\n  def WipeBlockDevice(self, partition):\n    if partition not in (\"/system\", \"/vendor\"):\n      raise ValueError((\"WipeBlockDevice doesn't work on %s\\n\") % (partition,))\n    fstab = self.fstab\n    size = self.info.get(partition.lstrip(\"/\") + \"_size\", None)\n    device = self._GetSlotSuffixDeviceForEntry(fstab[partition])\n\n    self.script.append('wipe_block_device(%s, %s);' % (device, size))\n\n  def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs):\n    \"\"\"Apply binary patches (in *patchpairs) to the given srcfile to\n    produce tgtfile (which may be \"-\" to indicate overwriting the\n    source file.\n\n    This edify function is being deprecated in favor of PatchPartition(). It\n    will try to redirect calls to PatchPartition() if possible. On unknown /\n    invalid inputs, raises an exception.\n    \"\"\"\n    tokens = srcfile.split(':')\n    assert (len(tokens) == 6 and tokens[0] == 'EMMC' and tgtfile == '-' and\n            len(patchpairs) == 2), \\\n        \"Failed to handle unknown format. Use PatchPartition() instead.\"\n\n    # Also validity check the args.\n    assert tokens[3] == patchpairs[0], \\\n        \"Found mismatching values for source SHA-1: {} vs {}\".format(\n            tokens[3], patchpairs[0])\n    assert int(tokens[4]) == tgtsize, \\\n        \"Found mismatching values for target size: {} vs {}\".format(\n            tokens[4], tgtsize)\n    assert tokens[5] == tgtsha1, \\\n        \"Found mismatching values for target SHA-1: {} vs {}\".format(\n            tokens[5], tgtsha1)\n\n    source = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[2], tokens[3])\n    target = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[4], tokens[5])\n    patch = patchpairs[1]\n    self.PatchPartition(target, source, patch)\n\n  def PatchPartition(self, target, source, patch):\n    \"\"\"\n    Applies the patch to the source partition and writes it to target.\n\n    Args:\n      target: the target arg to patch_partition. Must be in the form of\n        foo:bar:baz:quux\n      source: the source arg to patch_partition. Must be in the form of\n        foo:bar:baz:quux\n      patch: the patch arg to patch_partition. Must be an unquoted string.\n    \"\"\"\n    self._CheckSecondTokenNotSlotSuffixed(target, \"PatchPartitionExpr\")\n    self._CheckSecondTokenNotSlotSuffixed(source, \"PatchPartitionExpr\")\n    self.PatchPartitionExpr('\"%s\"' % target, '\"%s\"' % source, '\"%s\"' % patch)\n\n  def PatchPartitionExpr(self, target_expr, source_expr, patch_expr):\n    \"\"\"\n    Applies the patch to the source partition and writes it to target.\n\n    Args:\n      target_expr: an Edify expression that serves as the target arg to\n        patch_partition. Must be evaluated to a string in the form of\n        foo:bar:baz:quux\n      source_expr: an Edify expression that serves as the source arg to\n        patch_partition. Must be evaluated to a string in the form of\n        foo:bar:baz:quux\n      patch_expr: an Edify expression that serves as the patch arg to\n        patch_partition. Must be evaluated to a string.\n    \"\"\"\n    self.script.append(self.WordWrap((\n        'patch_partition({target},\\0{source},\\0'\n        'package_extract_file({patch})) ||\\n'\n        '    abort(concat('\n        '        \"E{code}: Failed to apply patch to \",{source}));').format(\n            target=target_expr,\n            source=source_expr,\n            patch=patch_expr,\n            code=common.ErrorCode.APPLY_PATCH_FAILURE)))\n\n  def _GetSlotSuffixDeviceForEntry(self, entry=None):\n    \"\"\"\n    Args:\n      entry: the fstab entry of device \"foo\"\n    Returns:\n      An edify expression. Caller must not quote result.\n      If foo is slot suffixed, it returns\n        'add_slot_suffix(\"foo\")'\n      Otherwise it returns\n        '\"foo\"' (quoted)\n    \"\"\"\n    assert entry is not None\n    if entry.slotselect:\n      return 'add_slot_suffix(\"%s\")' % entry.device\n    return '\"%s\"' % entry.device\n\n  def _CheckSecondTokenNotSlotSuffixed(self, s, fn):\n    lst = s.split(':')\n    assert(len(lst) == 4), \"{} does not contain 4 tokens\".format(s)\n    if self.fstab:\n      entry = common.GetEntryForDevice(self.fstab, lst[1])\n      if entry is not None:\n        assert not entry.slotselect, \\\n          \"Use %s because %s is slot suffixed\" % (fn, lst[1])\n\n  def WriteRawImage(self, mount_point, fn, mapfn=None):\n    \"\"\"Write the given package file into the partition for the given\n    mount point.\"\"\"\n\n    fstab = self.fstab\n    if fstab:\n      p = fstab[mount_point]\n      partition_type = common.PARTITION_TYPES[p.fs_type]\n      device = self._GetSlotSuffixDeviceForEntry(p)\n      args = {'device': device, 'fn': fn}\n      if partition_type == \"EMMC\":\n        if mapfn:\n          args[\"map\"] = mapfn\n          self.script.append(\n              'package_extract_file(\"%(fn)s\", %(device)s, \"%(map)s\");' % args)\n        else:\n          self.script.append(\n              'package_extract_file(\"%(fn)s\", %(device)s);' % args)\n      else:\n        raise ValueError(\n            \"don't know how to write \\\"%s\\\" partitions\" % p.fs_type)\n\n  def AppendExtra(self, extra):\n    \"\"\"Append text verbatim to the output script.\"\"\"\n    self.script.append(extra)\n\n  def Unmount(self, mount_point):\n    self.script.append('unmount(\"%s\");' % mount_point)\n    self.mounts.remove(mount_point)\n\n  def UnmountAll(self):\n    for p in sorted(self.mounts):\n      self.script.append('unmount(\"%s\");' % (p,))\n    self.mounts = set()\n\n  def AddToZip(self, input_zip, output_zip, input_path=None):\n    \"\"\"Write the accumulated script to the output_zip file.  input_zip\n    is used as the source for the 'updater' binary needed to run\n    script.  If input_path is not None, it will be used as a local\n    path for the binary instead of input_zip.\"\"\"\n\n    self.UnmountAll()\n\n    common.ZipWriteStr(output_zip, \"META-INF/com/google/android/updater-script\",\n                       \"\\n\".join(self.script) + \"\\n\")\n\n    if input_path is None:\n      data = input_zip.read(\"OTA/bin/updater\")\n    else:\n      data = open(input_path, \"rb\").read()\n    common.ZipWriteStr(output_zip, \"META-INF/com/google/android/update-binary\",\n                       data, perms=0o755)\n"
  },
  {
    "path": "tools/releasetools/find_shareduid_violation.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\"\"\"Find APK sharedUserId violators.\n\nUsage: find_shareduid_violation [args]\n\n  --product_out\n    PRODUCT_OUT directory\n\n  --aapt\n    Path to aapt or aapt2\n\n  --copy_out_system\n    TARGET_COPY_OUT_SYSTEM\n\n  --copy_out_vendor_\n    TARGET_COPY_OUT_VENDOR\n\n  --copy_out_product\n    TARGET_COPY_OUT_PRODUCT\n\n  --copy_out_system_ext\n    TARGET_COPY_OUT_SYSTEM_EXT\n\"\"\"\n\nimport json\nimport logging\nimport os\nimport re\nimport subprocess\nimport sys\n\nfrom collections import defaultdict\nfrom glob import glob\n\nimport common\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\nOPTIONS.product_out = os.environ.get(\"PRODUCT_OUT\")\nOPTIONS.aapt = \"aapt2\"\nOPTIONS.copy_out_system = \"system\"\nOPTIONS.copy_out_vendor = \"vendor\"\nOPTIONS.copy_out_product = \"product\"\nOPTIONS.copy_out_system_ext = \"system_ext\"\n\n\ndef execute(cmd):\n  p = subprocess.Popen(\n      cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n  out, err = map(lambda b: b.decode(\"utf-8\"), p.communicate())\n  return p.returncode == 0, out, err\n\n\ndef make_aapt_cmds(aapt, apk):\n  return [\n      aapt + \" dump \" + apk + \" --file AndroidManifest.xml\",\n      aapt + \" dump xmltree \" + apk + \" --file AndroidManifest.xml\"\n  ]\n\n\ndef extract_shared_uid(aapt, apk):\n  for cmd in make_aapt_cmds(aapt, apk):\n    success, manifest, error_msg = execute(cmd)\n    if success:\n      break\n  else:\n    logger.error(error_msg)\n    sys.exit()\n\n  pattern = re.compile(r\"sharedUserId.*=\\\"([^\\\"]*)\")\n\n  for line in manifest.split(\"\\n\"):\n    match = pattern.search(line)\n    if match:\n      return match.group(1)\n  return None\n\n\ndef FindShareduidViolation(product_out, partition_map, aapt=\"aapt2\"):\n  \"\"\"Find sharedUserId violators in the given partitions.\n\n  Args:\n    product_out: The base directory containing the partition directories.\n    partition_map: A map of partition name -> directory name.\n    aapt: The name of the aapt binary. Defaults to aapt2.\n\n  Returns:\n    A string containing a JSON object describing the shared UIDs.\n  \"\"\"\n  shareduid_app_dict = defaultdict(lambda: defaultdict(list))\n\n  for part, location in partition_map.items():\n    for f in glob(os.path.join(product_out, location, \"*\", \"*\", \"*.apk\")):\n      apk_file = os.path.basename(f)\n      shared_uid = extract_shared_uid(aapt, f)\n\n      if shared_uid is None:\n        continue\n      shareduid_app_dict[shared_uid][part].append(apk_file)\n\n  # Only output sharedUserId values that appear in >1 partition.\n  output = {}\n  for uid, partitions in shareduid_app_dict.items():\n    if len(partitions) > 1:\n      output[uid] = shareduid_app_dict[uid]\n\n  return json.dumps(output, indent=2, sort_keys=True)\n\n\ndef main():\n  common.InitLogging()\n\n  def option_handler(o, a):\n    if o == \"--product_out\":\n      OPTIONS.product_out = a\n    elif o == \"--aapt\":\n      OPTIONS.aapt = a\n    elif o == \"--copy_out_system\":\n      OPTIONS.copy_out_system = a\n    elif o == \"--copy_out_vendor\":\n      OPTIONS.copy_out_vendor = a\n    elif o == \"--copy_out_product\":\n      OPTIONS.copy_out_product = a\n    elif o == \"--copy_out_system_ext\":\n      OPTIONS.copy_out_system_ext = a\n    else:\n      return False\n    return True\n\n  args = common.ParseOptions(\n      sys.argv[1:],\n      __doc__,\n      extra_long_opts=[\n          \"product_out=\",\n          \"aapt=\",\n          \"copy_out_system=\",\n          \"copy_out_vendor=\",\n          \"copy_out_product=\",\n          \"copy_out_system_ext=\",\n      ],\n      extra_option_handler=option_handler)\n\n  if args:\n    common.Usage(__doc__)\n    sys.exit(1)\n\n  partition_map = {\n      \"system\": OPTIONS.copy_out_system,\n      \"vendor\": OPTIONS.copy_out_vendor,\n      \"product\": OPTIONS.copy_out_product,\n      \"system_ext\": OPTIONS.copy_out_system_ext,\n  }\n\n  print(\n      FindShareduidViolation(OPTIONS.product_out, partition_map, OPTIONS.aapt))\n\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "tools/releasetools/fsverity_metadata_generator.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright 2021 Google Inc. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\n`fsverity_metadata_generator` generates fsverity metadata and signature to a\ncontainer file\n\nThis actually is a simple wrapper around the `fsverity` program. A file is\nsigned by the program which produces the PKCS#7 signature file, merkle tree file\n, and the fsverity_descriptor file. Then the files are packed into a single\noutput file so that the information about the signing stays together.\n\nCurrently, the output of this script is used by `fd_server` which is the host-\nside backend of an authfs filesystem. `fd_server` uses this file in case when\nthe underlying filesystem (ext4, etc.) on the device doesn't support the\nfsverity feature natively in which case the information is read directly from\nthe filesystem using ioctl.\n\"\"\"\n\nimport argparse\nimport os\nimport re\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\nfrom struct import *\n\nclass TempDirectory(object):\n  def __enter__(self):\n    self.name = tempfile.mkdtemp()\n    return self.name\n\n  def __exit__(self, *unused):\n    shutil.rmtree(self.name)\n\nclass FSVerityMetadataGenerator:\n  def __init__(self, fsverity_path):\n    self._fsverity_path = fsverity_path\n\n    # Default values for some properties\n    self.set_hash_alg(\"sha256\")\n    self.set_signature('none')\n\n  def set_key_format(self, key_format):\n    self._key_format = key_format\n\n  def set_key(self, key):\n    self._key = key\n\n  def set_cert(self, cert):\n    self._cert = cert\n\n  def set_hash_alg(self, hash_alg):\n    self._hash_alg = hash_alg\n\n  def set_signature(self, signature):\n    self._signature = signature\n\n  def _raw_signature(pkcs7_sig_file):\n    \"\"\" Extracts raw signature from DER formatted PKCS#7 detached signature file\n\n    Do that by parsing the ASN.1 tree to get the location of the signature\n    in the file and then read the portion.\n    \"\"\"\n\n    # Note: there seems to be no public python API (even in 3p modules) that\n    # provides direct access to the raw signature at this moment. So, `openssl\n    # asn1parse` commandline tool is used instead.\n    cmd = ['openssl', 'asn1parse']\n    cmd.extend(['-inform', 'DER'])\n    cmd.extend(['-in', pkcs7_sig_file])\n    out = subprocess.check_output(cmd, universal_newlines=True)\n\n    # The signature is the last element in the tree\n    last_line = out.splitlines()[-1]\n    m = re.search('(\\d+):.*hl=\\s*(\\d+)\\s*l=\\s*(\\d+)\\s*.*OCTET STRING', last_line)\n    if not m:\n      raise RuntimeError(\"Failed to parse asn1parse output: \" + out)\n    offset = int(m.group(1))\n    header_len = int(m.group(2))\n    size = int(m.group(3))\n    with open(pkcs7_sig_file, 'rb') as f:\n      f.seek(offset + header_len)\n      return f.read(size)\n\n  def digest(self, input_file):\n    cmd = [self._fsverity_path, 'digest', input_file]\n    cmd.extend(['--compact'])\n    cmd.extend(['--hash-alg', self._hash_alg])\n    out = subprocess.check_output(cmd, universal_newlines=True).strip()\n    return bytes(bytearray.fromhex(out))\n\n  def generate(self, input_file, output_file):\n    if self._signature != 'none':\n      if not self._key:\n        raise RuntimeError(\"key must be specified.\")\n      if not self._cert:\n        raise RuntimeError(\"cert must be specified.\")\n\n    with TempDirectory() as temp_dir:\n      self._do_generate(input_file, output_file, temp_dir)\n\n  def _do_generate(self, input_file, output_file, work_dir):\n    # temporary files\n    desc_file = os.path.join(work_dir, 'desc')\n    merkletree_file = os.path.join(work_dir, 'merkletree')\n    sig_file = os.path.join(work_dir, 'signature')\n\n    # run the fsverity util to create the temporary files\n    cmd = [self._fsverity_path]\n    if self._signature == 'none':\n      cmd.append('digest')\n      cmd.append(input_file)\n    else:\n      cmd.append('sign')\n      cmd.append(input_file)\n      cmd.append(sig_file)\n\n      # If key is DER, convert DER private key to PEM\n      if self._key_format == 'der':\n        pem_key = os.path.join(work_dir, 'key.pem')\n        key_cmd = ['openssl', 'pkcs8']\n        key_cmd.extend(['-inform', 'DER'])\n        key_cmd.extend(['-in', self._key])\n        key_cmd.extend(['-nocrypt'])\n        key_cmd.extend(['-out', pem_key])\n        subprocess.check_call(key_cmd)\n      else:\n        pem_key = self._key\n\n      cmd.extend(['--key', pem_key])\n      cmd.extend(['--cert', self._cert])\n    cmd.extend(['--hash-alg', self._hash_alg])\n    cmd.extend(['--block-size', '4096'])\n    cmd.extend(['--out-merkle-tree', merkletree_file])\n    cmd.extend(['--out-descriptor', desc_file])\n    subprocess.check_call(cmd, stdout=open(os.devnull, 'w'))\n\n    with open(output_file, 'wb') as out:\n      # 1. version\n      out.write(pack('<I', 1))\n\n      # 2. fsverity_descriptor\n      with open(desc_file, 'rb') as f:\n        out.write(f.read())\n\n      # 3. signature\n      SIG_TYPE_NONE = 0\n      SIG_TYPE_PKCS7 = 1\n      SIG_TYPE_RAW = 2\n      if self._signature == 'raw':\n        out.write(pack('<I', SIG_TYPE_RAW))\n        sig = self._raw_signature(sig_file)\n        out.write(pack('<I', len(sig)))\n        out.write(sig)\n      elif self._signature == 'pkcs7':\n        with open(sig_file, 'rb') as f:\n          out.write(pack('<I', SIG_TYPE_PKCS7))\n          sig = f.read()\n          out.write(pack('<I', len(sig)))\n          out.write(sig)\n      else:\n        out.write(pack('<I', SIG_TYPE_NONE))\n        out.write(pack('<I', 0))\n\n      # 4. merkle tree\n      with open(merkletree_file, 'rb') as f:\n        # merkle tree is placed at the next nearest page boundary to make\n        # mmapping possible\n        out.seek(next_page(out.tell()))\n        out.write(f.read())\n\ndef next_page(n):\n  \"\"\" Returns the next nearest page boundary from `n` \"\"\"\n  PAGE_SIZE = 4096\n  return (n + PAGE_SIZE - 1) // PAGE_SIZE * PAGE_SIZE\n\nif __name__ == '__main__':\n  p = argparse.ArgumentParser()\n  p.add_argument(\n      '--output',\n      help='output file. If omitted, print to <INPUT>.fsv_meta',\n      metavar='output',\n      default=None)\n  p.add_argument(\n      'input',\n      help='input file to be signed')\n  p.add_argument(\n      '--key-format',\n      choices=['pem', 'der'],\n      default='der',\n      help='format of the input key. Default is der')\n  p.add_argument(\n      '--key',\n      help='PKCS#8 private key file')\n  p.add_argument(\n      '--cert',\n      help='x509 certificate file in PEM format')\n  p.add_argument(\n      '--hash-alg',\n      help='hash algorithm to use to build the merkle tree',\n      choices=['sha256', 'sha512'],\n      default='sha256')\n  p.add_argument(\n      '--signature',\n      help='format for signature',\n      choices=['none', 'raw', 'pkcs7'],\n      default='none')\n  p.add_argument(\n      '--fsverity-path',\n      help='path to the fsverity program',\n      required=True)\n  args = p.parse_args(sys.argv[1:])\n\n  output_file = args.output\n  if not output_file:\n    output_file = input_file + '.fsv_meta'\n\n  # remove the output file first, as switching between a file and a symlink can be complicated\n  try:\n    os.remove(output_file)\n  except FileNotFoundError:\n    pass\n\n  if os.path.islink(args.input):\n    target = os.readlink(args.input) + '.fsv_meta'\n    os.symlink(target, output_file)\n    sys.exit(0)\n\n  generator = FSVerityMetadataGenerator(args.fsverity_path)\n  generator.set_signature(args.signature)\n  if args.signature == 'none':\n    if args.key or args.cert:\n      raise ValueError(\"When signature is none, key and cert can't be set\")\n  else:\n    if not args.key or not args.cert:\n      raise ValueError(\"To generate signature, key and cert must be set\")\n    generator.set_key(args.key)\n    generator.set_cert(args.cert)\n  generator.set_key_format(args.key_format)\n  generator.set_hash_alg(args.hash_alg)\n  generator.generate(args.input, output_file)\n"
  },
  {
    "path": "tools/releasetools/images.py",
    "content": "# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific\n\nimport os\nimport threading\nfrom hashlib import sha1\n\nfrom rangelib import RangeSet\n\n__all__ = [\"EmptyImage\", \"DataImage\", \"FileImage\"]\n\n\nclass Image(object):\n  def RangeSha1(self, ranges):\n    raise NotImplementedError\n\n  def ReadRangeSet(self, ranges):\n    raise NotImplementedError\n\n  def TotalSha1(self, include_clobbered_blocks=False):\n    raise NotImplementedError\n\n  def WriteRangeDataToFd(self, ranges, fd):\n    raise NotImplementedError\n\n\nclass EmptyImage(Image):\n  \"\"\"A zero-length image.\"\"\"\n\n  def __init__(self):\n    self.blocksize = 4096\n    self.care_map = RangeSet()\n    self.clobbered_blocks = RangeSet()\n    self.extended = RangeSet()\n    self.total_blocks = 0\n    self.file_map = {}\n    self.hashtree_info = None\n\n  def RangeSha1(self, ranges):\n    return sha1().hexdigest()\n\n  def ReadRangeSet(self, ranges):\n    return ()\n\n  def TotalSha1(self, include_clobbered_blocks=False):\n    # EmptyImage always carries empty clobbered_blocks, so\n    # include_clobbered_blocks can be ignored.\n    assert self.clobbered_blocks.size() == 0\n    return sha1().hexdigest()\n\n  def WriteRangeDataToFd(self, ranges, fd):\n    raise ValueError(\"Can't write data from EmptyImage to file\")\n\n\nclass DataImage(Image):\n  \"\"\"An image wrapped around a single string of data.\"\"\"\n\n  def __init__(self, data, trim=False, pad=False):\n    self.data = data\n    self.blocksize = 4096\n\n    assert not (trim and pad)\n\n    partial = len(self.data) % self.blocksize\n    padded = False\n    if partial > 0:\n      if trim:\n        self.data = self.data[:-partial]\n      elif pad:\n        self.data += '\\0' * (self.blocksize - partial)\n        padded = True\n      else:\n        raise ValueError((\"data for DataImage must be multiple of %d bytes \"\n                          \"unless trim or pad is specified\") %\n                         (self.blocksize,))\n\n    assert len(self.data) % self.blocksize == 0\n\n    self.total_blocks = len(self.data) // self.blocksize\n    self.care_map = RangeSet(data=(0, self.total_blocks))\n    # When the last block is padded, we always write the whole block even for\n    # incremental OTAs. Because otherwise the last block may get skipped if\n    # unchanged for an incremental, but would fail the post-install\n    # verification if it has non-zero contents in the padding bytes.\n    # Bug: 23828506\n    if padded:\n      clobbered_blocks = [self.total_blocks-1, self.total_blocks]\n    else:\n      clobbered_blocks = []\n    self.clobbered_blocks = clobbered_blocks\n    self.extended = RangeSet()\n\n    zero_blocks = []\n    nonzero_blocks = []\n    reference = '\\0' * self.blocksize\n\n    for i in range(self.total_blocks-1 if padded else self.total_blocks):\n      d = self.data[i*self.blocksize : (i+1)*self.blocksize]\n      if d == reference:\n        zero_blocks.append(i)\n        zero_blocks.append(i+1)\n      else:\n        nonzero_blocks.append(i)\n        nonzero_blocks.append(i+1)\n\n    assert zero_blocks or nonzero_blocks or clobbered_blocks\n\n    self.file_map = dict()\n    if zero_blocks:\n      self.file_map[\"__ZERO\"] = RangeSet(data=zero_blocks)\n    if nonzero_blocks:\n      self.file_map[\"__NONZERO\"] = RangeSet(data=nonzero_blocks)\n    if clobbered_blocks:\n      self.file_map[\"__COPY\"] = RangeSet(data=clobbered_blocks)\n\n  def _GetRangeData(self, ranges):\n    for s, e in ranges:\n      yield self.data[s*self.blocksize:e*self.blocksize]\n\n  def RangeSha1(self, ranges):\n    h = sha1()\n    for data in self._GetRangeData(ranges): # pylint: disable=not-an-iterable\n      h.update(data)\n    return h.hexdigest()\n\n  def ReadRangeSet(self, ranges):\n    return list(self._GetRangeData(ranges))\n\n  def TotalSha1(self, include_clobbered_blocks=False):\n    if not include_clobbered_blocks:\n      return self.RangeSha1(self.care_map.subtract(self.clobbered_blocks))\n    return sha1(self.data).hexdigest()\n\n  def WriteRangeDataToFd(self, ranges, fd):\n    for data in self._GetRangeData(ranges): # pylint: disable=not-an-iterable\n      fd.write(data)\n\n\nclass FileImage(Image):\n  \"\"\"An image wrapped around a raw image file.\"\"\"\n\n  def __init__(self, path):\n    self.path = path\n    self.blocksize = 4096\n    self._file_size = os.path.getsize(self.path)\n    self._file = open(self.path, 'rb')\n\n    if self._file_size % self.blocksize != 0:\n      raise ValueError(\"Size of file %s must be multiple of %d bytes, but is %d\"\n                       % self.path, self.blocksize, self._file_size)\n\n    self.total_blocks = self._file_size // self.blocksize\n    self.care_map = RangeSet(data=(0, self.total_blocks))\n    self.clobbered_blocks = RangeSet()\n    self.extended = RangeSet()\n\n    self.generator_lock = threading.Lock()\n\n    zero_blocks = []\n    nonzero_blocks = []\n    reference = '\\0' * self.blocksize\n\n    for i in range(self.total_blocks):\n      d = self._file.read(self.blocksize)\n      if d == reference:\n        zero_blocks.append(i)\n        zero_blocks.append(i+1)\n      else:\n        nonzero_blocks.append(i)\n        nonzero_blocks.append(i+1)\n\n    assert zero_blocks or nonzero_blocks\n\n    self.file_map = {}\n    if zero_blocks:\n      self.file_map[\"__ZERO\"] = RangeSet(data=zero_blocks)\n    if nonzero_blocks:\n      self.file_map[\"__NONZERO\"] = RangeSet(data=nonzero_blocks)\n\n  def __del__(self):\n    self._file.close()\n\n  def _GetRangeData(self, ranges):\n    # Use a lock to protect the generator so that we will not run two\n    # instances of this generator on the same object simultaneously.\n    with self.generator_lock:\n      for s, e in ranges:\n        self._file.seek(s * self.blocksize)\n        for _ in range(s, e):\n          yield self._file.read(self.blocksize)\n\n  def RangeSha1(self, ranges):\n    h = sha1()\n    for data in self._GetRangeData(ranges): # pylint: disable=not-an-iterable\n      h.update(data)\n    return h.hexdigest()\n\n  def ReadRangeSet(self, ranges):\n    return list(self._GetRangeData(ranges))\n\n  def TotalSha1(self, include_clobbered_blocks=False):\n    assert not self.clobbered_blocks\n    return self.RangeSha1(self.care_map)\n\n  def WriteRangeDataToFd(self, ranges, fd):\n    for data in self._GetRangeData(ranges): # pylint: disable=not-an-iterable\n      fd.write(data)\n"
  },
  {
    "path": "tools/releasetools/img_from_target_files.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nGiven an input target-files, produces an image zipfile suitable for use with\n'fastboot update'.\n\nUsage:  img_from_target_files [flags] input_target_files output_image_zip\n\ninput_target_files: Path to the input target_files zip.\n\nFlags:\n  -z  (--bootable_zip)\n      Include only the bootable images (eg 'boot' and 'recovery') in\n      the output.\n\n  --additional <filespec>\n      Include an additional entry into the generated zip file. The filespec is\n      in a format that's accepted by zip2zip (e.g.\n      'OTA/android-info.txt:android-info.txt', to copy `OTA/android-info.txt`\n      from input_file into output_file as `android-info.txt`. Refer to the\n      `filespec` arg in zip2zip's help message). The option can be repeated to\n      include multiple entries.\n\n  --exclude <filespec>\n      Don't include these files. If the file is in --additional and --exclude,\n      the file will not be included.\n\n\"\"\"\n\nfrom __future__ import print_function\n\nimport logging\nimport os\nimport sys\nimport zipfile\n\nimport common\nfrom build_super_image import BuildSuperImage\n\nif sys.hexversion < 0x02070000:\n  print('Python 2.7 or newer is required.', file=sys.stderr)\n  sys.exit(1)\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\n\nOPTIONS.additional_entries = []\nOPTIONS.excluded_entries = []\nOPTIONS.bootable_only = False\nOPTIONS.put_super = None\nOPTIONS.put_bootloader = None\nOPTIONS.dynamic_partition_list = None\nOPTIONS.super_device_list = None\nOPTIONS.retrofit_dap = None\nOPTIONS.build_super = None\nOPTIONS.sparse_userimages = None\nOPTIONS.use_fastboot_info = True\nOPTIONS.build_super_image = None\n\n\ndef LoadOptions(input_file):\n  \"\"\"Loads information from input_file to OPTIONS.\n\n  Args:\n    input_file: Path to the input target_files zip file.\n  \"\"\"\n  with zipfile.ZipFile(input_file) as input_zip:\n    info = OPTIONS.info_dict = common.LoadInfoDict(input_zip)\n\n  OPTIONS.put_super = info.get('super_image_in_update_package') == 'true'\n  OPTIONS.put_bootloader = info.get('bootloader_in_update_package') == 'true'\n  OPTIONS.dynamic_partition_list = info.get('dynamic_partition_list',\n                                            '').strip().split()\n  OPTIONS.super_device_list = info.get('super_block_devices',\n                                       '').strip().split()\n  OPTIONS.retrofit_dap = info.get('dynamic_partition_retrofit') == 'true'\n  OPTIONS.build_super = info.get('build_super_partition') == 'true'\n  OPTIONS.sparse_userimages = bool(info.get('extfs_sparse_flag'))\n\n\ndef CopyZipEntries(input_file, output_file, entries):\n  \"\"\"Copies ZIP entries between input and output files.\n\n  Args:\n    input_file: Path to the input target_files zip.\n    output_file: Output filename.\n    entries: A list of entries to copy, in a format that's accepted by zip2zip\n        (e.g. 'OTA/android-info.txt:android-info.txt', which copies\n        `OTA/android-info.txt` from input_file into output_file as\n        `android-info.txt`. Refer to the `filespec` arg in zip2zip's help\n        message).\n  \"\"\"\n  logger.info('Writing %d entries to archive...', len(entries))\n  cmd = ['zip2zip', '-i', input_file, '-o', output_file]\n  cmd.extend(entries)\n  common.RunAndCheckOutput(cmd)\n\n\ndef LocatePartitionEntry(partition_name, namelist):\n  for subdir in [\"IMAGES\", \"PREBUILT_IMAGES\", \"RADIO\"]:\n    entry_name = os.path.join(subdir, partition_name + \".img\")\n    if entry_name in namelist:\n      return entry_name\n\n\ndef EntriesForUserImages(input_file):\n  \"\"\"Returns the user images entries to be copied.\n\n  Args:\n    input_file: Path to the input target_files zip file.\n  \"\"\"\n  dynamic_images = [p + '.img' for p in OPTIONS.dynamic_partition_list]\n\n  # Filter out system_other for launch DAP devices because it is in super image.\n  if not OPTIONS.retrofit_dap and 'system' in OPTIONS.dynamic_partition_list:\n    dynamic_images.append('system_other.img')\n\n  entries = [\n      'OTA/android-info.txt:android-info.txt',\n  ]\n  if OPTIONS.use_fastboot_info:\n    entries.append('META/fastboot-info.txt:fastboot-info.txt')\n  ab_partitions = []\n  with zipfile.ZipFile(input_file) as input_zip:\n    namelist = input_zip.namelist()\n    if \"META/ab_partitions.txt\" in namelist:\n      ab_partitions = input_zip.read(\n          \"META/ab_partitions.txt\").decode().strip().split()\n  if 'PREBUILT_IMAGES/kernel_16k' in namelist:\n    entries.append('PREBUILT_IMAGES/kernel_16k:kernel_16k')\n  if 'PREBUILT_IMAGES/ramdisk_16k.img' in namelist:\n    entries.append('PREBUILT_IMAGES/ramdisk_16k.img:ramdisk_16k.img')\n\n  visited_partitions = set(OPTIONS.dynamic_partition_list)\n  for image_path in [name for name in namelist if name.startswith('IMAGES/')]:\n    image = os.path.basename(image_path)\n    if OPTIONS.bootable_only and image not in ('boot.img', 'recovery.img', 'bootloader', 'init_boot.img'):\n      continue\n    if not image.endswith('.img') and image != 'bootloader':\n      continue\n    if image == 'bootloader' and not OPTIONS.put_bootloader:\n      continue\n    # Filter out super_empty and the images that are already in super partition.\n    if OPTIONS.put_super:\n      if image == 'super_empty.img':\n        continue\n      if image in dynamic_images:\n        continue\n    partition_name = image.rstrip(\".img\")\n    visited_partitions.add(partition_name)\n    entries.append('{}:{}'.format(image_path, image))\n  for part in [part for part in ab_partitions if part not in visited_partitions]:\n    entry = LocatePartitionEntry(part, namelist)\n    image = os.path.basename(entry)\n    if entry is not None:\n      entries.append('{}:{}'.format(entry, image))\n  return entries\n\n\ndef EntriesForSplitSuperImages(input_file):\n  \"\"\"Returns the entries for split super images.\n\n  This is only done for retrofit dynamic partition devices.\n\n  Args:\n    input_file: Path to the input target_files zip file.\n  \"\"\"\n  with zipfile.ZipFile(input_file) as input_zip:\n    namelist = input_zip.namelist()\n  entries = []\n  for device in OPTIONS.super_device_list:\n    image = 'OTA/super_{}.img'.format(device)\n    assert image in namelist, 'Failed to find {}'.format(image)\n    entries.append('{}:{}'.format(image, os.path.basename(image)))\n  return entries\n\n\ndef RebuildAndWriteSuperImages(input_file, output_file):\n  \"\"\"Builds and writes super images to the output file.\"\"\"\n  logger.info('Building super image...')\n\n  # We need files under IMAGES/, OTA/, META/ for img_from_target_files.py.\n  # However, common.LoadInfoDict() may read additional files under BOOT/,\n  # RECOVERY/ and ROOT/. So unzip everything from the target_files.zip.\n  input_tmp = common.UnzipTemp(input_file)\n\n  super_file = common.MakeTempFile('super_', '.img')\n\n  # Allow overriding the BUILD_SUPER_IMAGE binary\n  if OPTIONS.build_super_image:\n    command = [OPTIONS.build_super_image, input_tmp, super_file]\n    common.RunAndCheckOutput(command)\n  else:\n    BuildSuperImage(input_tmp, super_file)\n\n  logger.info('Writing super.img to archive...')\n  with zipfile.ZipFile(\n          output_file, 'a', compression=zipfile.ZIP_DEFLATED,\n          allowZip64=True) as output_zip:\n    common.ZipWrite(output_zip, super_file, 'super.img')\n\n\ndef ImgFromTargetFiles(input_file, output_file):\n  \"\"\"Creates an image archive from the input target_files zip.\n\n  Args:\n    input_file: Path to the input target_files zip.\n    output_file: Output filename.\n\n  Raises:\n    ValueError: On invalid input.\n  \"\"\"\n  if not os.path.exists(input_file):\n    raise ValueError('%s is not exist' % input_file)\n\n  if not zipfile.is_zipfile(input_file):\n    raise ValueError('%s is not a valid zipfile' % input_file)\n\n  logger.info('Building image zip from target files zip.')\n\n  LoadOptions(input_file)\n\n  # Entries to be copied into the output file.\n  entries = EntriesForUserImages(input_file)\n\n  # Only for devices that retrofit dynamic partitions there're split super\n  # images available in the target_files.zip.\n  rebuild_super = False\n  if OPTIONS.build_super and OPTIONS.put_super:\n    if OPTIONS.retrofit_dap:\n      entries += EntriesForSplitSuperImages(input_file)\n    else:\n      rebuild_super = True\n\n  # Any additional entries provided by caller.\n  entries += OPTIONS.additional_entries\n\n  # Remove any excluded entries\n  entries = [e for e in entries if e not in OPTIONS.excluded_entries]\n\n  CopyZipEntries(input_file, output_file, entries)\n\n  if rebuild_super:\n    RebuildAndWriteSuperImages(input_file, output_file)\n\n\ndef main(argv):\n\n  def option_handler(o, a):\n    if o in ('-z', '--bootable_zip'):\n      OPTIONS.bootable_only = True\n    elif o == '--additional':\n      OPTIONS.additional_entries.append(a)\n    elif o == '--exclude':\n      OPTIONS.excluded_entries.append(a)\n    elif o == '--build_super_image':\n      OPTIONS.build_super_image = a\n    else:\n      return False\n    return True\n\n  args = common.ParseOptions(argv, __doc__,\n                             extra_opts='z',\n                             extra_long_opts=[\n                                 'additional=',\n                                 'exclude=',\n                                 'bootable_zip',\n                                 'build_super_image=',\n                             ],\n                             extra_option_handler=option_handler)\n  if len(args) != 2:\n    common.Usage(__doc__)\n    sys.exit(1)\n\n  common.InitLogging()\n\n  ImgFromTargetFiles(args[0], args[1])\n\n  logger.info('done.')\n\n\nif __name__ == '__main__':\n  try:\n    common.CloseInheritedPipes()\n    main(sys.argv[1:])\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/jarjar-rules.txt",
    "content": "rule com.google.protobuf.nano.** com.android.framework.protobuf.nano.@1\n"
  },
  {
    "path": "tools/releasetools/make_recovery_patch.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2014 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import print_function\n\nimport logging\nimport os\nimport sys\n\nimport common\n\nif sys.hexversion < 0x02070000:\n  print(\"Python 2.7 or newer is required.\", file=sys.stderr)\n  sys.exit(1)\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\n\n\ndef main(argv):\n  args = common.ParseOptions(argv, __doc__)\n  input_dir, output_dir = args\n\n  common.InitLogging()\n\n  OPTIONS.info_dict = common.LoadInfoDict(input_dir)\n\n  recovery_img = common.GetBootableImage(\"recovery.img\", \"recovery.img\",\n                                         input_dir, \"RECOVERY\")\n  boot_img = common.GetBootableImage(\"boot.img\", \"boot.img\",\n                                     input_dir, \"BOOT\")\n\n  if not recovery_img or not boot_img:\n    sys.exit(0)\n\n  board_uses_vendorimage = OPTIONS.info_dict.get(\n      \"board_uses_vendorimage\") == \"true\"\n\n  if board_uses_vendorimage:\n    target_files_dir = \"VENDOR\"\n  else:\n    target_files_dir = \"SYSTEM\"\n\n  def output_sink(fn, data):\n    with open(os.path.join(output_dir, target_files_dir,\n                           *fn.split(\"/\")), \"wb\") as f:\n      f.write(data)\n\n  common.MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img)\n\n\nif __name__ == '__main__':\n  main(sys.argv[1:])\n"
  },
  {
    "path": "tools/releasetools/merge/Android.bp",
    "content": "// Copyright (C) 2022 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nfilegroup {\n    name: \"releasetools_merge_sources\",\n    srcs: [\n        \"merge_compatibility_checks.py\",\n        \"merge_dexopt.py\",\n        \"merge_meta.py\",\n        \"merge_target_files.py\",\n        \"merge_utils.py\",\n    ],\n}\n\nfilegroup {\n    name: \"releasetools_merge_tests\",\n    srcs: [\n        \"test_merge_compatibility_checks.py\",\n        \"test_merge_meta.py\",\n        \"test_merge_utils.py\",\n    ],\n}\n\npython_binary_host {\n    name: \"merge_target_files\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\":releasetools_merge_sources\"],\n    libs: [\n        \"releasetools_add_img_to_target_files\",\n        \"releasetools_build_super_image\",\n        \"releasetools_check_target_files_vintf\",\n        \"releasetools_common\",\n        \"releasetools_find_shareduid_violation\",\n        \"releasetools_img_from_target_files\",\n        \"releasetools_ota_from_target_files\",\n    ],\n    required: [\n        \"apexd_host\",\n        \"checkvintf\",\n        \"host_init_verifier\",\n        \"secilc\",\n    ],\n    target: {\n        darwin: {\n            // libs dep \"releasetools_ota_from_target_files\" is disabled on darwin\n            enabled: false,\n        },\n    },\n}\n\npython_binary_host {\n    name: \"merge_builds\",\n    defaults: [\"releasetools_binary_defaults\"],\n    srcs: [\n        \"merge_builds.py\",\n    ],\n    libs: [\n        \"releasetools_build_super_image\",\n        \"releasetools_common\",\n    ],\n}\n"
  },
  {
    "path": "tools/releasetools/merge/OWNERS",
    "content": "deyaoren@google.com\nhaamed@google.com\njgalmes@google.com\nrseymour@google.com\n"
  },
  {
    "path": "tools/releasetools/merge/merge_builds.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n# use this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations under\n# the License.\n#\n\"\"\"Merges two non-dist partial builds together.\n\nGiven two partial builds, a framework build and a vendor build, merge the builds\ntogether so that the images can be flashed using 'fastboot flashall'.\n\nTo support both DAP and non-DAP vendor builds with a single framework partial\nbuild, the framework partial build should always be built with DAP enabled. The\nvendor partial build determines whether the merged result supports DAP.\n\nThis script does not require builds to be built with 'make dist'.\nThis script regenerates super_empty.img and vbmeta.img if necessary. Other\nimages are assumed to not require regeneration.\n\nUsage: merge_builds.py [args]\n\n  --framework_images comma_separated_image_list\n      Comma-separated list of image names that should come from the framework\n      build.\n\n  --product_out_framework product_out_framework_path\n      Path to out/target/product/<framework build>.\n\n  --product_out_vendor product_out_vendor_path\n      Path to out/target/product/<vendor build>.\n\n  --build_vbmeta\n      If provided, vbmeta.img will be regenerated in out/target/product/<vendor\n      build>.\n\n  --framework_misc_info_keys\n      The optional path to a newline-separated config file containing keys to\n      obtain from the framework instance of misc_info.txt, used for creating\n      vbmeta.img. The remaining keys come from the vendor instance.\n\n  --avb_resolve_rollback_index_location_conflict\n      If provided, resolve the conflict AVB rollback index location when\n      necessary.\n\"\"\"\nfrom __future__ import print_function\n\nimport logging\nimport os\nimport sys\n\nimport build_super_image\nimport common\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\nOPTIONS.framework_images = (\"system\",)\nOPTIONS.product_out_framework = None\nOPTIONS.product_out_vendor = None\nOPTIONS.build_vbmeta = False\nOPTIONS.framework_misc_info_keys = None\nOPTIONS.avb_resolve_rollback_index_location_conflict = False\n\n\ndef CreateImageSymlinks():\n  for image in OPTIONS.framework_images:\n    image_path = os.path.join(OPTIONS.product_out_framework, \"%s.img\" % image)\n    symlink_path = os.path.join(OPTIONS.product_out_vendor, \"%s.img\" % image)\n    if os.path.exists(symlink_path):\n      if os.path.islink(symlink_path):\n        os.remove(symlink_path)\n      else:\n        raise ValueError(\"Attempting to overwrite built image: %s\" %\n                         symlink_path)\n    os.symlink(image_path, symlink_path)\n\n\ndef BuildSuperEmpty():\n  framework_dict = common.LoadDictionaryFromFile(\n      os.path.join(OPTIONS.product_out_framework, \"misc_info.txt\"))\n  vendor_dict = common.LoadDictionaryFromFile(\n      os.path.join(OPTIONS.product_out_vendor, \"misc_info.txt\"))\n  # Regenerate super_empty.img if both partial builds enable DAP. If only the\n  # the vendor build enables DAP, the vendor build's existing super_empty.img\n  # will be reused. If only the framework build should enable DAP, super_empty\n  # should be included in the --framework_images flag to copy the existing\n  # super_empty.img from the framework build.\n  if (framework_dict.get(\"use_dynamic_partitions\") == \"true\") and (\n      vendor_dict.get(\"use_dynamic_partitions\") == \"true\"):\n    logger.info(\"Building super_empty.img.\")\n    merged_dict = dict(vendor_dict)\n    merged_dict.update(\n        common.MergeDynamicPartitionInfoDicts(\n            framework_dict=framework_dict, vendor_dict=vendor_dict))\n    output_super_empty_path = os.path.join(OPTIONS.product_out_vendor,\n                                           \"super_empty.img\")\n    build_super_image.BuildSuperImage(merged_dict, output_super_empty_path)\n\n\ndef BuildVBMeta():\n  logger.info(\"Building vbmeta.img.\")\n\n  framework_dict = common.LoadDictionaryFromFile(\n      os.path.join(OPTIONS.product_out_framework, \"misc_info.txt\"))\n  vendor_dict = common.LoadDictionaryFromFile(\n      os.path.join(OPTIONS.product_out_vendor, \"misc_info.txt\"))\n  merged_dict = dict(vendor_dict)\n  if OPTIONS.framework_misc_info_keys:\n    for key in common.LoadListFromFile(OPTIONS.framework_misc_info_keys):\n      merged_dict[key] = framework_dict[key]\n\n  # Build vbmeta.img using partitions in product_out_vendor.\n  partitions = {}\n  for partition in common.AVB_PARTITIONS:\n    partition_path = os.path.join(OPTIONS.product_out_vendor,\n                                  \"%s.img\" % partition)\n    if os.path.exists(partition_path):\n      partitions[partition] = partition_path\n\n  # vbmeta_partitions includes the partitions that should be included into\n  # top-level vbmeta.img, which are the ones that are not included in any\n  # chained VBMeta image plus the chained VBMeta images themselves.\n  vbmeta_partitions = common.AVB_PARTITIONS[:]\n  for partition in common.AVB_VBMETA_PARTITIONS:\n    chained_partitions = merged_dict.get(\"avb_%s\" % partition, \"\").strip()\n    if chained_partitions:\n      partitions[partition] = os.path.join(OPTIONS.product_out_vendor,\n                                           \"%s.img\" % partition)\n      vbmeta_partitions = [\n          item for item in vbmeta_partitions\n          if item not in chained_partitions.split()\n      ]\n      vbmeta_partitions.append(partition)\n\n  output_vbmeta_path = os.path.join(OPTIONS.product_out_vendor, \"vbmeta.img\")\n  OPTIONS.info_dict = merged_dict\n  common.BuildVBMeta(output_vbmeta_path, partitions, \"vbmeta\",\n                     vbmeta_partitions,\n                     OPTIONS.avb_resolve_rollback_index_location_conflict)\n\n\ndef MergeBuilds():\n  CreateImageSymlinks()\n  BuildSuperEmpty()\n  if OPTIONS.build_vbmeta:\n    BuildVBMeta()\n\n\ndef main():\n  common.InitLogging()\n\n  def option_handler(o, a):\n    if o == \"--framework_images\":\n      OPTIONS.framework_images = [i.strip() for i in a.split(\",\")]\n    elif o == \"--product_out_framework\":\n      OPTIONS.product_out_framework = a\n    elif o == \"--product_out_vendor\":\n      OPTIONS.product_out_vendor = a\n    elif o == \"--build_vbmeta\":\n      OPTIONS.build_vbmeta = True\n    elif o == \"--framework_misc_info_keys\":\n      OPTIONS.framework_misc_info_keys = a\n    elif o == \"--avb_resolve_rollback_index_location_conflict\":\n      OPTIONS.avb_resolve_rollback_index_location_conflict = True\n    else:\n      return False\n    return True\n\n  args = common.ParseOptions(\n      sys.argv[1:],\n      __doc__,\n      extra_long_opts=[\n          \"framework_images=\",\n          \"product_out_framework=\",\n          \"product_out_vendor=\",\n          \"build_vbmeta\",\n          \"framework_misc_info_keys=\",\n          \"avb_resolve_rollback_index_location_conflict\"\n      ],\n      extra_option_handler=option_handler)\n\n  if (args or OPTIONS.product_out_framework is None or\n      OPTIONS.product_out_vendor is None):\n    common.Usage(__doc__)\n    sys.exit(1)\n\n  MergeBuilds()\n\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "tools/releasetools/merge/merge_compatibility_checks.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n# use this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations under\n# the License.\n#\n\"\"\"Compatibility checks that should be performed on merged target_files.\"\"\"\n\nimport json\nimport logging\nimport os\nfrom xml.etree import ElementTree\n\nimport apex_utils\nimport check_target_files_vintf\nimport common\nimport find_shareduid_violation\n\nlogger = logging.getLogger(__name__)\nOPTIONS = common.OPTIONS\n\n\ndef CheckCompatibility(target_files_dir, partition_map):\n  \"\"\"Runs various compatibility checks.\n\n  Returns a possibly-empty list of error messages.\n  \"\"\"\n  errors = []\n\n  errors.extend(CheckVintf(target_files_dir))\n  errors.extend(CheckShareduidViolation(target_files_dir, partition_map))\n  errors.extend(CheckApexDuplicatePackages(target_files_dir, partition_map))\n\n  # The remaining checks only use the following partitions:\n  partition_map = {\n      partition: path\n      for partition, path in partition_map.items()\n      if partition in ('system', 'system_ext', 'product', 'vendor', 'odm')\n  }\n\n  errors.extend(CheckInitRcFiles(target_files_dir, partition_map))\n  errors.extend(CheckCombinedSepolicy(target_files_dir, partition_map))\n\n  return errors\n\n\ndef CheckVintf(target_files_dir):\n  \"\"\"Check for any VINTF issues using check_vintf.\"\"\"\n  errors = []\n  try:\n    if not check_target_files_vintf.CheckVintf(target_files_dir):\n      errors.append('Incompatible VINTF.')\n  except RuntimeError as err:\n    errors.append(str(err))\n  return errors\n\n\ndef CheckShareduidViolation(target_files_dir, partition_map):\n  \"\"\"Check for any APK sharedUserId violations across partition sets.\n\n  Writes results to META/shareduid_violation_modules.json to help\n  with followup debugging.\n  \"\"\"\n  errors = []\n  violation = find_shareduid_violation.FindShareduidViolation(\n      target_files_dir, partition_map)\n  shareduid_violation_modules = os.path.join(\n      target_files_dir, 'META', 'shareduid_violation_modules.json')\n  with open(shareduid_violation_modules, 'w') as f:\n    # Write the output to a file to enable debugging.\n    f.write(violation)\n\n    # Check for violations across the partition sets.\n    shareduid_errors = common.SharedUidPartitionViolations(\n        json.loads(violation),\n        [OPTIONS.framework_partition_set, OPTIONS.vendor_partition_set])\n    if shareduid_errors:\n      for error in shareduid_errors:\n        errors.append('APK sharedUserId error: %s' % error)\n      errors.append('See APK sharedUserId violations file: %s' %\n                    shareduid_violation_modules)\n  return errors\n\n\ndef CheckInitRcFiles(target_files_dir, partition_map):\n  \"\"\"Check for any init.rc issues using host_init_verifier.\"\"\"\n  try:\n    vendor_partitions = set()\n    if OPTIONS.vendor_otatools:\n      vendor_partitions = {\"vendor\", \"odm\"}\n      common.RunVendoredHostInitVerifier(\n          product_out=target_files_dir,\n          partition_map={p: partition_map[p] for p in vendor_partitions})\n\n    common.RunHostInitVerifier(\n        product_out=target_files_dir,\n        partition_map={\n            p: partition_map[p]\n            for p in partition_map.keys() - vendor_partitions\n        })\n  except RuntimeError as err:\n    return [str(err)]\n  return []\n\n\ndef CheckCombinedSepolicy(target_files_dir, partition_map, execute=True):\n  \"\"\"Uses secilc to compile a split sepolicy file.\n\n  Depends on various */etc/selinux/* and */etc/vintf/* files within partitions.\n  \"\"\"\n  errors = []\n\n  def get_file(partition, path):\n    if partition not in partition_map:\n      logger.warning('Cannot load SEPolicy files for missing partition %s',\n                     partition)\n      return None\n    file_path = os.path.join(target_files_dir, partition_map[partition], path)\n    if os.path.exists(file_path):\n      return file_path\n    return None\n\n  # Load the kernel sepolicy version from the FCM. This is normally provided\n  # directly to selinux.cpp as a build flag, but is also available in this file.\n  fcm_file = get_file('system', 'etc/vintf/compatibility_matrix.device.xml')\n  if not fcm_file:\n    errors.append('Missing required file for loading sepolicy: '\n                  '/system/etc/vintf/compatibility_matrix.device.xml')\n    return errors\n  kernel_sepolicy_version = ElementTree.parse(fcm_file).getroot().find(\n      'sepolicy/kernel-sepolicy-version').text\n\n  # Load the vendor's plat sepolicy version. This is the version used for\n  # locating sepolicy mapping files.\n  vendor_plat_version_file = get_file('vendor',\n                                      'etc/selinux/plat_sepolicy_vers.txt')\n  if not vendor_plat_version_file:\n    errors.append('Missing required sepolicy file %s' %\n                  vendor_plat_version_file)\n    return errors\n  with open(vendor_plat_version_file) as f:\n    vendor_plat_version = f.read().strip()\n\n  # Use the same flags and arguments as selinux.cpp OpenSplitPolicy().\n  cmd = ['secilc', '-m', '-M', 'true', '-G', '-N']\n  cmd.extend(['-c', kernel_sepolicy_version])\n  cmd.extend(['-o', os.path.join(target_files_dir, 'META/combined_sepolicy')])\n  cmd.extend(['-f', '/dev/null'])\n\n  required_policy_files = (\n      ('system', 'etc/selinux/plat_sepolicy.cil'),\n      ('system', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),\n      ('vendor', 'etc/selinux/vendor_sepolicy.cil'),\n      ('vendor', 'etc/selinux/plat_pub_versioned.cil'),\n  )\n  for policy in (map(lambda partition_and_path: get_file(*partition_and_path),\n                     required_policy_files)):\n    if not policy:\n      errors.append('Missing required sepolicy file %s' % policy)\n      return errors\n    cmd.append(policy)\n\n  optional_policy_files = (\n      ('system', 'etc/selinux/mapping/%s.compat.cil' % vendor_plat_version),\n      ('system_ext', 'etc/selinux/system_ext_sepolicy.cil'),\n      ('system_ext', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),\n      ('product', 'etc/selinux/product_sepolicy.cil'),\n      ('product', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),\n      ('odm', 'etc/selinux/odm_sepolicy.cil'),\n  )\n  for policy in (map(lambda partition_and_path: get_file(*partition_and_path),\n                     optional_policy_files)):\n    if policy:\n      cmd.append(policy)\n\n  try:\n    if execute:\n      common.RunAndCheckOutput(cmd)\n    else:\n      return cmd\n  except RuntimeError as err:\n    errors.append(str(err))\n\n  return errors\n\n\ndef CheckApexDuplicatePackages(target_files_dir, partition_map):\n  \"\"\"Checks if the same APEX package name is provided by multiple partitions.\"\"\"\n  errors = []\n\n  apex_packages = set()\n  for partition in partition_map.keys():\n    try:\n      apex_info = apex_utils.GetApexInfoForPartition(\n          target_files_dir, partition)\n    except RuntimeError as err:\n      errors.append(str(err))\n      apex_info = []\n    partition_apex_packages = set([info.package_name for info in apex_info])\n    duplicates = apex_packages.intersection(partition_apex_packages)\n    if duplicates:\n      errors.append(\n          'Duplicate APEX package_names found in multiple partitions: %s' %\n          ' '.join(duplicates))\n    apex_packages.update(partition_apex_packages)\n\n  return errors\n"
  },
  {
    "path": "tools/releasetools/merge/merge_dexopt.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n# use this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations under\n# the License.\n#\n\"\"\"Generates dexopt files for vendor apps, from a merged target_files.\n\nExpects items in OPTIONS prepared by merge_target_files.py.\n\"\"\"\n\nimport glob\nimport json\nimport logging\nimport os\nimport shutil\nimport subprocess\n\nimport common\nimport merge_utils\n\nlogger = logging.getLogger(__name__)\nOPTIONS = common.OPTIONS\n\n\ndef MergeDexopt(temp_dir, output_target_files_dir):\n  \"\"\"If needed, generates dexopt files for vendor apps.\n\n  Args:\n    temp_dir: Location containing an 'output' directory where target files have\n      been extracted, e.g. <temp_dir>/output/SYSTEM, <temp_dir>/output/IMAGES,\n      etc.\n    output_target_files_dir: The name of a directory that will be used to create\n      the output target files package after all the special cases are processed.\n  \"\"\"\n  # Load vendor and framework META/misc_info.txt.\n  if (OPTIONS.vendor_misc_info.get('building_with_vsdk') != 'true' or\n      OPTIONS.framework_dexpreopt_tools is None or\n      OPTIONS.framework_dexpreopt_config is None or\n      OPTIONS.vendor_dexpreopt_config is None):\n    return\n\n  logger.info('applying dexpreopt')\n\n  # The directory structure to apply dexpreopt is:\n  #\n  # <temp_dir>/\n  #     framework_meta/\n  #         META/\n  #     vendor_meta/\n  #         META/\n  #     output/\n  #         SYSTEM/\n  #         VENDOR/\n  #         IMAGES/\n  #         <other items extracted from system and vendor target files>\n  #     tools/\n  #         <contents of dexpreopt_tools.zip>\n  #     system_config/\n  #         <contents of system dexpreopt_config.zip>\n  #     vendor_config/\n  #         <contents of vendor dexpreopt_config.zip>\n  #     system -> output/SYSTEM\n  #     vendor -> output/VENDOR\n  #     apex/ (extracted updatable APEX)\n  #         <apex 1>/\n  #             ...\n  #         <apex 2>/\n  #             ...\n  #         ...\n  #     out/dex2oat_result/vendor/\n  #         <app>\n  #             oat/arm64/\n  #                 package.vdex\n  #                 package.odex\n  #         <priv-app>\n  #             oat/arm64/\n  #                 package.vdex\n  #                 package.odex\n  dexpreopt_tools_files_temp_dir = os.path.join(temp_dir, 'tools')\n  dexpreopt_framework_config_files_temp_dir = os.path.join(\n      temp_dir, 'system_config')\n  dexpreopt_vendor_config_files_temp_dir = os.path.join(temp_dir,\n                                                        'vendor_config')\n\n  merge_utils.ExtractItems(\n      input_zip=OPTIONS.framework_dexpreopt_tools,\n      output_dir=dexpreopt_tools_files_temp_dir,\n      extract_item_list=('*',))\n  merge_utils.ExtractItems(\n      input_zip=OPTIONS.framework_dexpreopt_config,\n      output_dir=dexpreopt_framework_config_files_temp_dir,\n      extract_item_list=('*',))\n  merge_utils.ExtractItems(\n      input_zip=OPTIONS.vendor_dexpreopt_config,\n      output_dir=dexpreopt_vendor_config_files_temp_dir,\n      extract_item_list=('*',))\n\n  os.symlink(\n      os.path.join(output_target_files_dir, 'SYSTEM'),\n      os.path.join(temp_dir, 'system'))\n  os.symlink(\n      os.path.join(output_target_files_dir, 'VENDOR'),\n      os.path.join(temp_dir, 'vendor'))\n\n  # Extract APEX.\n  logging.info('extracting APEX')\n  apex_extract_root_dir = os.path.join(temp_dir, 'apex')\n  os.makedirs(apex_extract_root_dir)\n\n  command = [\n      'apexd_host',\n      '--system_path',\n      os.path.join(temp_dir, 'system'),\n      '--apex_path',\n      apex_extract_root_dir,\n  ]\n  logging.info('    running %s', command)\n  subprocess.check_call(command)\n\n  # Modify system config to point to the tools that have been extracted.\n  # Absolute or .. paths are not allowed  by the dexpreopt_gen tool in\n  # dexpreopt_soong.config.\n  dexpreopt_framework_soon_config = os.path.join(\n      dexpreopt_framework_config_files_temp_dir, 'dexpreopt_soong.config')\n  with open(dexpreopt_framework_soon_config, 'w') as f:\n    dexpreopt_soong_config = {\n        'Profman': 'tools/profman',\n        'Dex2oat': 'tools/dex2oatd',\n        'Aapt': 'tools/aapt2',\n        'SoongZip': 'tools/soong_zip',\n        'Zip2zip': 'tools/zip2zip',\n        'ManifestCheck': 'tools/manifest_check',\n        'ConstructContext': 'tools/construct_context',\n    }\n    json.dump(dexpreopt_soong_config, f)\n\n  # TODO(b/188179859): Make *dex location configurable to vendor or system_other.\n  use_system_other_odex = False\n\n  if use_system_other_odex:\n    dex_img = 'SYSTEM_OTHER'\n  else:\n    dex_img = 'VENDOR'\n    # Open vendor_filesystem_config to append the items generated by dexopt.\n    vendor_file_system_config = open(\n        os.path.join(temp_dir, 'output', 'META',\n                     'vendor_filesystem_config.txt'), 'a')\n\n  # Dexpreopt vendor apps.\n  dexpreopt_config_suffix = '_dexpreopt.config'\n  for config in glob.glob(\n      os.path.join(dexpreopt_vendor_config_files_temp_dir,\n                   '*' + dexpreopt_config_suffix)):\n    app = os.path.basename(config)[:-len(dexpreopt_config_suffix)]\n    logging.info('dexpreopt config: %s %s', config, app)\n\n    apk_dir = 'app'\n    apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')\n    if not os.path.exists(apk_path):\n      apk_dir = 'priv-app'\n      apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')\n      if not os.path.exists(apk_path):\n        logging.warning(\n            'skipping dexpreopt for %s, no apk found in vendor/app '\n            'or vendor/priv-app', app)\n        continue\n\n    # Generate dexpreopting script. Note 'out_dir' is not the output directory\n    # where the script is generated, but the OUT_DIR at build time referenced\n    # in the dexpreot config files, e.g., \"out/.../core-oj.jar\", so the tool knows\n    # how to adjust the path.\n    command = [\n        os.path.join(dexpreopt_tools_files_temp_dir, 'dexpreopt_gen'),\n        '-global',\n        os.path.join(dexpreopt_framework_config_files_temp_dir,\n                     'dexpreopt.config'),\n        '-global_soong',\n        os.path.join(dexpreopt_framework_config_files_temp_dir,\n                     'dexpreopt_soong.config'),\n        '-module',\n        config,\n        '-dexpreopt_script',\n        'dexpreopt_app.sh',\n        '-out_dir',\n        'out',\n        '-base_path',\n        '.',\n        '--uses_target_files',\n    ]\n\n    # Run the command from temp_dir so all tool paths are its descendants.\n    logging.info('running %s', command)\n    subprocess.check_call(command, cwd=temp_dir)\n\n    # Call the generated script.\n    command = ['sh', 'dexpreopt_app.sh', apk_path]\n    logging.info('running %s', command)\n    subprocess.check_call(command, cwd=temp_dir)\n\n    # Output files are in:\n    #\n    # <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.vdex\n    # <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.odex\n    # <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.vdex\n    # <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.odex\n    #\n    # Copy the files to their destination. The structure of system_other is:\n    #\n    # system_other/\n    #     system-other-odex-marker\n    #     system/\n    #         app/\n    #             <app>/oat/arm64/\n    #                 <app>.odex\n    #                 <app>.vdex\n    #             ...\n    #         priv-app/\n    #             <app>/oat/arm64/\n    #                 <app>.odex\n    #                 <app>.vdex\n    #             ...\n\n    # TODO(b/188179859): Support for other architectures.\n    arch = 'arm64'\n\n    dex_destination = os.path.join(temp_dir, 'output', dex_img, apk_dir, app,\n                                   'oat', arch)\n    os.makedirs(dex_destination)\n    dex2oat_path = os.path.join(temp_dir, 'out', 'dex2oat_result', 'vendor',\n                                apk_dir, app, 'oat', arch)\n    shutil.copy(\n        os.path.join(dex2oat_path, 'package.vdex'),\n        os.path.join(dex_destination, app + '.vdex'))\n    shutil.copy(\n        os.path.join(dex2oat_path, 'package.odex'),\n        os.path.join(dex_destination, app + '.odex'))\n\n    # Append entries to vendor_file_system_config.txt, such as:\n    #\n    # vendor/app/<app>/oat 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0\n    # vendor/app/<app>/oat/arm64 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0\n    # vendor/app/<app>/oat/arm64/<app>.odex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0\n    # vendor/app/<app>/oat/arm64/<app>.vdex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0\n    if not use_system_other_odex:\n      vendor_app_prefix = 'vendor/' + apk_dir + '/' + app + '/oat'\n      selabel = 'selabel=u:object_r:vendor_app_file:s0 capabilities=0x0'\n      vendor_file_system_config.writelines([\n          vendor_app_prefix + ' 0 2000 755 ' + selabel + '\\n',\n          vendor_app_prefix + '/' + arch + ' 0 2000 755 ' + selabel + '\\n',\n          vendor_app_prefix + '/' + arch + '/' + app + '.odex 0 0 644 ' +\n          selabel + '\\n',\n          vendor_app_prefix + '/' + arch + '/' + app + '.vdex 0 0 644 ' +\n          selabel + '\\n',\n      ])\n\n  if not use_system_other_odex:\n    vendor_file_system_config.close()\n    # Delete vendor.img so that it will be regenerated.\n    # TODO(b/188179859): Rebuilding a vendor image in GRF mode (e.g., T(framework)\n    #                    and S(vendor) may require logic similar to that in\n    #                    rebuild_image_with_sepolicy.\n    vendor_img = os.path.join(output_target_files_dir, 'IMAGES', 'vendor.img')\n    if os.path.exists(vendor_img):\n      logging.info('Deleting %s', vendor_img)\n      os.remove(vendor_img)\n"
  },
  {
    "path": "tools/releasetools/merge/merge_meta.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n# use this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations under\n# the License.\n#\n\"\"\"Functions for merging META/* files from partial builds.\n\nExpects items in OPTIONS prepared by merge_target_files.py.\n\"\"\"\n\nimport logging\nimport os\nimport re\nimport shutil\n\nimport build_image\nimport common\nimport merge_utils\nimport sparse_img\nimport verity_utils\nfrom ota_utils import ParseUpdateEngineConfig\n\nfrom common import ExternalError\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\n\n# In apexkeys.txt or apkcerts.txt, we will find partition tags on each entry in\n# the file. We use these partition tags to filter the entries in those files\n# from the two different target files packages to produce a merged apexkeys.txt\n# or apkcerts.txt file. A partition tag (e.g., for the product partition) looks\n# like this: 'partition=\"product\"'. We use the group syntax grab the value of\n# the tag. We use non-greedy matching in case there are other fields on the\n# same line.\n\nPARTITION_TAG_PATTERN = re.compile(r'partition=\"(.*?)\"')\n\n# The sorting algorithm for apexkeys.txt and apkcerts.txt does not include the\n# \".apex\" or \".apk\" suffix, so we use the following pattern to extract a key.\n\nMODULE_KEY_PATTERN = re.compile(r'name=\"(.+)\\.(apex|apk)\"')\n\n\ndef MergeUpdateEngineConfig(framework_meta_dir, vendor_meta_dir,\n                            merged_meta_dir):\n  \"\"\"Merges META/update_engine_config.txt.\n\n  The output is the configuration for maximum compatibility.\n  \"\"\"\n  _CONFIG_NAME = 'update_engine_config.txt'\n  framework_config_path = os.path.join(framework_meta_dir, _CONFIG_NAME)\n  vendor_config_path = os.path.join(vendor_meta_dir, _CONFIG_NAME)\n  merged_config_path = os.path.join(merged_meta_dir, _CONFIG_NAME)\n\n  if os.path.exists(framework_config_path):\n    framework_config = ParseUpdateEngineConfig(framework_config_path)\n    vendor_config = ParseUpdateEngineConfig(vendor_config_path)\n    # Copy older config to merged target files for maximum compatibility\n    # update_engine in system partition is from system side, but\n    # update_engine_sideload in recovery is from vendor side.\n    if framework_config < vendor_config:\n      shutil.copy(framework_config_path, merged_config_path)\n    else:\n      shutil.copy(vendor_config_path, merged_config_path)\n  else:\n    if not OPTIONS.allow_partial_ab:\n      raise FileNotFoundError(framework_config_path)\n    shutil.copy(vendor_config_path, merged_config_path)\n\n\ndef MergeMetaFiles(temp_dir, merged_dir, framework_partitions):\n  \"\"\"Merges various files in META/*.\"\"\"\n\n  framework_meta_dir = os.path.join(temp_dir, 'framework_meta', 'META')\n  merge_utils.CollectTargetFiles(\n      input_zipfile_or_dir=OPTIONS.framework_target_files,\n      output_dir=os.path.dirname(framework_meta_dir),\n      item_list=('META/*',))\n\n  vendor_meta_dir = os.path.join(temp_dir, 'vendor_meta', 'META')\n  merge_utils.CollectTargetFiles(\n      input_zipfile_or_dir=OPTIONS.vendor_target_files,\n      output_dir=os.path.dirname(vendor_meta_dir),\n      item_list=('META/*',))\n\n  merged_meta_dir = os.path.join(merged_dir, 'META')\n\n  # Merge META/misc_info.txt into OPTIONS.merged_misc_info,\n  # but do not write it yet. The following functions may further\n  # modify this dict.\n  OPTIONS.merged_misc_info = MergeMiscInfo(\n      framework_meta_dir=framework_meta_dir,\n      vendor_meta_dir=vendor_meta_dir,\n      merged_meta_dir=merged_meta_dir)\n\n  CopyNamedFileContexts(\n      framework_meta_dir=framework_meta_dir,\n      vendor_meta_dir=vendor_meta_dir,\n      merged_meta_dir=merged_meta_dir)\n\n  if OPTIONS.merged_misc_info.get('use_dynamic_partitions') == 'true':\n    MergeDynamicPartitionsInfo(\n        framework_meta_dir=framework_meta_dir,\n        vendor_meta_dir=vendor_meta_dir,\n        merged_meta_dir=merged_meta_dir)\n\n  if OPTIONS.merged_misc_info.get('ab_update') == 'true':\n    MergeAbPartitions(\n        framework_meta_dir=framework_meta_dir,\n        vendor_meta_dir=vendor_meta_dir,\n        merged_meta_dir=merged_meta_dir,\n        framework_partitions=framework_partitions)\n    UpdateCareMapImageSizeProps(images_dir=os.path.join(merged_dir, 'IMAGES'))\n\n  for file_name in ('apkcerts.txt', 'apexkeys.txt'):\n    MergePackageKeys(\n        framework_meta_dir=framework_meta_dir,\n        vendor_meta_dir=vendor_meta_dir,\n        merged_meta_dir=merged_meta_dir,\n        file_name=file_name)\n\n  if OPTIONS.merged_misc_info.get('ab_update') == 'true':\n    MergeUpdateEngineConfig(\n        framework_meta_dir, vendor_meta_dir, merged_meta_dir)\n\n  # Write the now-finalized OPTIONS.merged_misc_info.\n  merge_utils.WriteSortedData(\n      data=OPTIONS.merged_misc_info,\n      path=os.path.join(merged_meta_dir, 'misc_info.txt'))\n\n\ndef MergeAbPartitions(framework_meta_dir, vendor_meta_dir, merged_meta_dir,\n                      framework_partitions):\n  \"\"\"Merges META/ab_partitions.txt.\n\n  The output contains the union of the partition names.\n  \"\"\"\n  framework_ab_partitions = []\n  framework_ab_config = os.path.join(framework_meta_dir, 'ab_partitions.txt')\n  if os.path.exists(framework_ab_config):\n    with open(framework_ab_config) as f:\n      # Filter out some partitions here to support the case that the\n      # ab_partitions.txt of framework-target-files has non-framework\n      # partitions. This case happens when we use a complete merged target\n      # files package as the framework-target-files.\n      framework_ab_partitions.extend([\n          partition\n          for partition in f.read().splitlines()\n          if partition in framework_partitions\n      ])\n  else:\n    if not OPTIONS.allow_partial_ab:\n      raise FileNotFoundError(framework_ab_config)\n    logger.info('Use partial AB because framework ab_partitions.txt does not '\n                'exist.')\n\n  with open(os.path.join(vendor_meta_dir, 'ab_partitions.txt')) as f:\n    vendor_ab_partitions = f.read().splitlines()\n\n  merge_utils.WriteSortedData(\n      data=set(framework_ab_partitions + vendor_ab_partitions),\n      path=os.path.join(merged_meta_dir, 'ab_partitions.txt'))\n\n\ndef MergeMiscInfo(framework_meta_dir, vendor_meta_dir, merged_meta_dir):\n  \"\"\"Merges META/misc_info.txt.\n\n  The output contains a combination of key=value pairs from both inputs.\n  Most pairs are taken from the vendor input, while some are taken from\n  the framework input.\n  \"\"\"\n\n  OPTIONS.framework_misc_info = common.LoadDictionaryFromFile(\n      os.path.join(framework_meta_dir, 'misc_info.txt'))\n  OPTIONS.vendor_misc_info = common.LoadDictionaryFromFile(\n      os.path.join(vendor_meta_dir, 'misc_info.txt'))\n\n  # Merged misc info is a combination of vendor misc info plus certain values\n  # from the framework misc info.\n\n  merged_dict = OPTIONS.vendor_misc_info\n  for key in OPTIONS.framework_misc_info_keys:\n    if key in OPTIONS.framework_misc_info:\n      merged_dict[key] = OPTIONS.framework_misc_info[key]\n\n  # If AVB is enabled then ensure that we build vbmeta.img.\n  # Partial builds with AVB enabled may set PRODUCT_BUILD_VBMETA_IMAGE=false to\n  # skip building an incomplete vbmeta.img.\n  if merged_dict.get('avb_enable') == 'true':\n    merged_dict['avb_building_vbmeta_image'] = 'true'\n\n  return merged_dict\n\n\ndef MergeDynamicPartitionsInfo(framework_meta_dir, vendor_meta_dir,\n                               merged_meta_dir):\n  \"\"\"Merge META/dynamic_partitions_info.txt.\"\"\"\n  framework_dynamic_partitions_dict = common.LoadDictionaryFromFile(\n      os.path.join(framework_meta_dir, 'dynamic_partitions_info.txt'))\n  vendor_dynamic_partitions_dict = common.LoadDictionaryFromFile(\n      os.path.join(vendor_meta_dir, 'dynamic_partitions_info.txt'))\n\n  merged_dynamic_partitions_dict = common.MergeDynamicPartitionInfoDicts(\n      framework_dict=framework_dynamic_partitions_dict,\n      vendor_dict=vendor_dynamic_partitions_dict)\n\n  merge_utils.WriteSortedData(\n      data=merged_dynamic_partitions_dict,\n      path=os.path.join(merged_meta_dir, 'dynamic_partitions_info.txt'))\n\n  # Merge misc info keys used for Dynamic Partitions.\n  OPTIONS.merged_misc_info.update(merged_dynamic_partitions_dict)\n  # Ensure that add_img_to_target_files rebuilds super split images for\n  # devices that retrofit dynamic partitions. This flag may have been set to\n  # false in the partial builds to prevent duplicate building of super.img.\n  OPTIONS.merged_misc_info['build_super_partition'] = 'true'\n\n\ndef MergePackageKeys(framework_meta_dir, vendor_meta_dir, merged_meta_dir,\n                     file_name):\n  \"\"\"Merges APK/APEX key list files.\"\"\"\n\n  if file_name not in ('apkcerts.txt', 'apexkeys.txt'):\n    raise ExternalError(\n        'Unexpected file_name provided to merge_package_keys_txt: %s',\n        file_name)\n\n  def read_helper(d):\n    temp = {}\n    with open(os.path.join(d, file_name)) as f:\n      for line in f.read().splitlines():\n        line = line.strip()\n        if line:\n          name_search = MODULE_KEY_PATTERN.search(line.split()[0])\n          temp[name_search.group(1)] = line\n    return temp\n\n  framework_dict = read_helper(framework_meta_dir)\n  vendor_dict = read_helper(vendor_meta_dir)\n  merged_dict = {}\n\n  def filter_into_merged_dict(item_dict, partition_set):\n    for key, value in item_dict.items():\n      tag_search = PARTITION_TAG_PATTERN.search(value)\n\n      if tag_search is None:\n        raise ValueError('Entry missing partition tag: %s' % value)\n\n      partition_tag = tag_search.group(1)\n\n      if partition_tag in partition_set:\n        if key in merged_dict:\n          if OPTIONS.allow_duplicate_apkapex_keys:\n            # TODO(b/150582573) Always raise on duplicates.\n            logger.warning('Duplicate key %s' % key)\n            continue\n          else:\n            raise ValueError('Duplicate key %s' % key)\n\n        merged_dict[key] = value\n\n  # Prioritize framework keys first.\n  # Duplicate keys from vendor are an error, or ignored.\n  filter_into_merged_dict(framework_dict, OPTIONS.framework_partition_set)\n  filter_into_merged_dict(vendor_dict, OPTIONS.vendor_partition_set)\n\n  # The following code is similar to WriteSortedData, but different enough\n  # that we couldn't use that function. We need the output to be sorted by the\n  # basename of the apex/apk (without the \".apex\" or \".apk\" suffix). This\n  # allows the sort to be consistent with the framework/vendor input data and\n  # eases comparison of input data with merged data.\n  with open(os.path.join(merged_meta_dir, file_name), 'w') as output:\n    for key, value in sorted(merged_dict.items()):\n      output.write(value + '\\n')\n\n\ndef CopyNamedFileContexts(framework_meta_dir, vendor_meta_dir, merged_meta_dir):\n  \"\"\"Creates named copies of each partial build's file_contexts.bin.\n\n  Used when regenerating images from the partial build.\n  \"\"\"\n\n  def copy_fc_file(source_dir, file_name):\n    for name in (file_name, 'file_contexts.bin'):\n      fc_path = os.path.join(source_dir, name)\n      if os.path.exists(fc_path):\n        shutil.copyfile(fc_path, os.path.join(merged_meta_dir, file_name))\n        return\n    raise ValueError('Missing file_contexts file from %s: %s', source_dir,\n                     file_name)\n\n  copy_fc_file(framework_meta_dir, 'framework_file_contexts.bin')\n  copy_fc_file(vendor_meta_dir, 'vendor_file_contexts.bin')\n\n  # Replace <image>_selinux_fc values with framework or vendor file_contexts.bin\n  # depending on which dictionary the key came from.\n  # Only the file basename is required because all selinux_fc properties are\n  # replaced with the full path to the file under META/ when misc_info.txt is\n  # loaded from target files for repacking. See common.py LoadInfoDict().\n  for key in OPTIONS.vendor_misc_info:\n    if key.endswith('_selinux_fc'):\n      OPTIONS.merged_misc_info[key] = 'vendor_file_contexts.bin'\n  for key in OPTIONS.framework_misc_info:\n    if key.endswith('_selinux_fc'):\n      OPTIONS.merged_misc_info[key] = 'framework_file_contexts.bin'\n\n\ndef UpdateCareMapImageSizeProps(images_dir):\n  \"\"\"Sets <partition>_image_size props in misc_info.\n\n  add_images_to_target_files uses these props to generate META/care_map.pb.\n  Regenerated images will have this property set during regeneration.\n\n  However, images copied directly from input partial target files packages\n  need this value calculated here.\n  \"\"\"\n  for partition in common.PARTITIONS_WITH_CARE_MAP:\n    image_path = os.path.join(images_dir, '{}.img'.format(partition))\n    if os.path.exists(image_path):\n      partition_size = sparse_img.GetImagePartitionSize(image_path)\n      image_props = build_image.ImagePropFromGlobalDict(\n          OPTIONS.merged_misc_info, partition)\n      verity_image_builder = verity_utils.CreateVerityImageBuilder(image_props)\n      image_size = verity_image_builder.CalculateMaxImageSize(partition_size)\n      OPTIONS.merged_misc_info['{}_image_size'.format(partition)] = image_size\n"
  },
  {
    "path": "tools/releasetools/merge/merge_target_files.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n# use this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations under\n# the License.\n#\n\"\"\"This script merges two partial target files packages.\n\nOne input package contains framework files, and the other contains vendor files.\n\nThis script produces a complete, merged target files package:\n  - This package can be used to generate a flashable IMG package.\n    See --output-img.\n  - This package can be used to generate an OTA package. See --output-ota.\n  - The merged package is checked for compatibility between the two inputs.\n\nUsage: merge_target_files [args]\n\n  --framework-target-files framework-target-files-package\n      The input target files package containing framework bits. This is a zip\n      archive or a directory.\n\n  --framework-item-list framework-item-list-file\n      The optional path to a newline-separated config file of items that\n      are extracted as-is from the framework target files package.\n\n  --framework-misc-info-keys framework-misc-info-keys-file\n      The optional path to a newline-separated config file of keys to\n      extract from the framework META/misc_info.txt file.\n\n  --vendor-target-files vendor-target-files-package\n      The input target files package containing vendor bits. This is a zip\n      archive or a directory.\n\n  --vendor-item-list vendor-item-list-file\n      The optional path to a newline-separated config file of items that\n      are extracted as-is from the vendor target files package.\n\n  --boot-image-dir-path\n      The input boot image directory path. This path contains IMAGES/boot.img\n      file.\n\n  --output-target-files output-target-files-package\n      If provided, the output merged target files package. Also a zip archive.\n\n  --output-dir output-directory\n      If provided, the destination directory for saving merged files. Requires\n      the --output-item-list flag.\n      Can be provided alongside --output-target-files, or by itself.\n\n  --output-item-list output-item-list-file.\n      The optional path to a newline-separated config file that specifies the\n      file patterns to copy into the --output-dir. Required if providing\n      the --output-dir flag.\n\n  --output-ota output-ota-package\n      The output ota package. This is a zip archive. Use of this flag may\n      require passing the --path common flag; see common.py.\n\n  --output-img output-img-package\n      The output img package, suitable for use with 'fastboot update'. Use of\n      this flag may require passing the --path common flag; see common.py.\n\n  --output-super-empty output-super-empty-image\n      If provided, creates a super_empty.img file from the merged target\n      files package and saves it at this path.\n\n  --rebuild_recovery\n      Copy the recovery image used by non-A/B devices, used when\n      regenerating vendor images with --rebuild-sepolicy.\n\n  --allow-duplicate-apkapex-keys\n      If provided, duplicate APK/APEX keys are ignored and the value from the\n      framework is used.\n\n  --rebuild-sepolicy\n      If provided, rebuilds odm.img or vendor.img to include merged sepolicy\n      files. If odm is present then odm is preferred.\n\n  --vendor-otatools otatools.zip or directory\n      If provided, use these otatools when recompiling the odm or vendor\n      image to include sepolicy.\n\n  --keep-tmp\n      Keep tempoary files for debugging purposes.\n\n  --avb-resolve-rollback-index-location-conflict\n      If provided, resolve the conflict AVB rollback index location when\n      necessary.\n\n  --allow-partial-ab\n      If provided, allow merging non-AB framework target files with AB vendor\n      target files, which means that only the vendor has AB partitions.\n\n  The following only apply when using the VSDK to perform dexopt on vendor apps:\n\n  --framework-dexpreopt-config\n      If provided, the location of framwework's dexpreopt_config.zip.\n\n  --framework-dexpreopt-tools\n      if provided, the location of framework's dexpreopt_tools.zip.\n\n  --vendor-dexpreopt-config\n      If provided, the location of vendor's dexpreopt_config.zip.\n\"\"\"\n\nimport logging\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport zipfile\n\nimport add_img_to_target_files\nimport build_image\nimport build_super_image\nimport common\nimport img_from_target_files\nimport merge_compatibility_checks\nimport merge_dexopt\nimport merge_meta\nimport merge_utils\nimport ota_from_target_files\n\nfrom common import ExternalError\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\n# Always turn on verbose logging.\nOPTIONS.verbose = True\nOPTIONS.framework_target_files = None\nOPTIONS.framework_item_list = []\nOPTIONS.framework_misc_info_keys = []\nOPTIONS.vendor_target_files = None\nOPTIONS.vendor_item_list = []\nOPTIONS.boot_image_dir_path = None\nOPTIONS.output_target_files = None\nOPTIONS.output_dir = None\nOPTIONS.output_item_list = []\nOPTIONS.output_ota = None\nOPTIONS.output_img = None\nOPTIONS.output_super_empty = None\nOPTIONS.rebuild_recovery = False\n# TODO(b/150582573): Remove this option.\nOPTIONS.allow_duplicate_apkapex_keys = False\nOPTIONS.vendor_otatools = None\nOPTIONS.rebuild_sepolicy = False\nOPTIONS.keep_tmp = False\nOPTIONS.avb_resolve_rollback_index_location_conflict = False\nOPTIONS.allow_partial_ab = False\nOPTIONS.framework_dexpreopt_config = None\nOPTIONS.framework_dexpreopt_tools = None\nOPTIONS.vendor_dexpreopt_config = None\n\n\ndef move_only_exists(source, destination):\n  \"\"\"Judge whether the file exists and then move the file.\"\"\"\n\n  if os.path.exists(source):\n    shutil.move(source, destination)\n\n\ndef remove_file_if_exists(file_name):\n  \"\"\"Remove the file if it exists and skip otherwise.\"\"\"\n\n  try:\n    os.remove(file_name)\n  except FileNotFoundError:\n    pass\n\n\ndef include_extra_in_list(item_list):\n  \"\"\"\n  1. Include all `META/*` files in the item list.\n\n  To ensure that `AddImagesToTargetFiles` can still be used with vendor item\n  list that do not specify all of the required META/ files, those files should\n  be included by default. This preserves the backward compatibility of\n  `rebuild_image_with_sepolicy`.\n\n  2. Include `SYSTEM/build.prop` file in the item list.\n\n  To ensure that `AddImagesToTargetFiles` for GRF vendor images, can still\n  access SYSTEM/build.prop to pass GetPartitionFingerprint check in BuildInfo\n  constructor.\n  \"\"\"\n  if not item_list:\n    return None\n  return list(item_list) + ['META/*'] + ['SYSTEM/build.prop']\n\n\ndef create_merged_package(temp_dir):\n  \"\"\"Merges two target files packages into one target files structure.\n\n  Returns:\n    Path to merged package under temp directory.\n  \"\"\"\n  # Extract \"as is\" items from the input framework and vendor partial target\n  # files packages directly into the output temporary directory, since these\n  # items do not need special case processing.\n\n  output_target_files_temp_dir = os.path.join(temp_dir, 'output')\n  merge_utils.CollectTargetFiles(\n      input_zipfile_or_dir=OPTIONS.framework_target_files,\n      output_dir=output_target_files_temp_dir,\n      item_list=OPTIONS.framework_item_list)\n  merge_utils.CollectTargetFiles(\n      input_zipfile_or_dir=OPTIONS.vendor_target_files,\n      output_dir=output_target_files_temp_dir,\n      item_list=OPTIONS.vendor_item_list)\n\n  if OPTIONS.boot_image_dir_path:\n    merge_utils.CollectTargetFiles(\n        input_zipfile_or_dir=OPTIONS.boot_image_dir_path,\n        output_dir=output_target_files_temp_dir,\n        item_list=['IMAGES/boot.img'])\n\n  # Perform special case processing on META/* items.\n  # After this function completes successfully, all the files we need to create\n  # the output target files package are in place.\n  merge_meta.MergeMetaFiles(\n      temp_dir=temp_dir,\n      merged_dir=output_target_files_temp_dir,\n      framework_partitions=OPTIONS.framework_partition_set)\n\n  merge_dexopt.MergeDexopt(\n      temp_dir=temp_dir, output_target_files_dir=output_target_files_temp_dir)\n\n  return output_target_files_temp_dir\n\n\ndef generate_missing_images(target_files_dir):\n  \"\"\"Generate any missing images from target files.\"\"\"\n\n  # Regenerate IMAGES in the target directory.\n\n  add_img_args = [\n      '--verbose',\n      '--add_missing',\n  ]\n  if OPTIONS.rebuild_recovery:\n    add_img_args.append('--rebuild_recovery')\n  if OPTIONS.avb_resolve_rollback_index_location_conflict:\n    add_img_args.append('--avb_resolve_rollback_index_location_conflict')\n  add_img_args.append(target_files_dir)\n\n  add_img_to_target_files.main(add_img_args)\n\n\ndef rebuild_image_with_sepolicy(target_files_dir):\n  \"\"\"Rebuilds odm.img or vendor.img to include merged sepolicy files.\n\n  If odm is present then odm is preferred -- otherwise vendor is used.\n  \"\"\"\n  partition = 'vendor'\n  if os.path.exists(os.path.join(target_files_dir, 'ODM')):\n    partition = 'odm'\n  partition_img = '{}.img'.format(partition)\n  partition_map = '{}.map'.format(partition)\n\n  logger.info('Recompiling %s using the merged sepolicy files.', partition_img)\n\n  # Copy the combined SEPolicy file and framework hashes to the image that is\n  # being rebuilt.\n  def copy_selinux_file(input_path, output_filename):\n    input_filename = os.path.join(target_files_dir, input_path)\n    if not os.path.exists(input_filename):\n      input_filename = input_filename.replace('SYSTEM_EXT/',\n                                              'SYSTEM/system_ext/') \\\n          .replace('PRODUCT/', 'SYSTEM/product/')\n      if not os.path.exists(input_filename):\n        logger.info('Skipping copy_selinux_file for %s', input_filename)\n        return\n    shutil.copy(\n        input_filename,\n        os.path.join(target_files_dir, partition.upper(), 'etc/selinux',\n                     output_filename))\n\n  copy_selinux_file('META/combined_sepolicy', 'precompiled_sepolicy')\n  copy_selinux_file('SYSTEM/etc/selinux/plat_sepolicy_and_mapping.sha256',\n                    'precompiled_sepolicy.plat_sepolicy_and_mapping.sha256')\n  copy_selinux_file(\n      'SYSTEM_EXT/etc/selinux/system_ext_sepolicy_and_mapping.sha256',\n      'precompiled_sepolicy.system_ext_sepolicy_and_mapping.sha256')\n  copy_selinux_file('PRODUCT/etc/selinux/product_sepolicy_and_mapping.sha256',\n                    'precompiled_sepolicy.product_sepolicy_and_mapping.sha256')\n\n  if not OPTIONS.vendor_otatools:\n    # Remove the partition from the merged target-files archive. It will be\n    # rebuilt later automatically by generate_missing_images().\n    remove_file_if_exists(\n        os.path.join(target_files_dir, 'IMAGES', partition_img))\n    return\n\n  # TODO(b/192253131): Remove the need for vendor_otatools by fixing\n  # backwards-compatibility issues when compiling images across releases.\n  if not OPTIONS.vendor_target_files:\n    raise ValueError(\n        'Expected vendor_target_files if vendor_otatools is not None.')\n  logger.info(\n      '%s recompilation will be performed using the vendor otatools.zip',\n      partition_img)\n\n  # Unzip the vendor build's target-files archive.\n  vendor_target_files_dir = common.MakeTempDir(\n      prefix='merge_target_files_vendor_target_files_')\n  merge_utils.CollectTargetFiles(\n      input_zipfile_or_dir=OPTIONS.vendor_target_files,\n      output_dir=vendor_target_files_dir,\n      item_list=include_extra_in_list(OPTIONS.vendor_item_list))\n\n  # Copy the partition contents from the merged target-files archive to the\n  # vendor target-files archive.\n  shutil.rmtree(os.path.join(vendor_target_files_dir, partition.upper()))\n  shutil.copytree(\n      os.path.join(target_files_dir, partition.upper()),\n      os.path.join(vendor_target_files_dir, partition.upper()),\n      symlinks=True)\n\n  # Delete then rebuild the partition.\n  remove_file_if_exists(\n      os.path.join(vendor_target_files_dir, 'IMAGES', partition_img))\n  rebuild_partition_command = [\n      os.path.join(OPTIONS.vendor_otatools, 'bin', 'add_img_to_target_files'),\n      '--verbose',\n      '--add_missing',\n  ]\n  if OPTIONS.rebuild_recovery:\n    rebuild_partition_command.append('--rebuild_recovery')\n  rebuild_partition_command.append(vendor_target_files_dir)\n  logger.info('Recompiling %s: %s', partition_img,\n              ' '.join(rebuild_partition_command))\n  common.RunAndCheckOutput(rebuild_partition_command, verbose=True)\n\n  # Move the newly-created image to the merged target files dir.\n  if not os.path.exists(os.path.join(target_files_dir, 'IMAGES')):\n    os.makedirs(os.path.join(target_files_dir, 'IMAGES'))\n  shutil.move(\n      os.path.join(vendor_target_files_dir, 'IMAGES', partition_img),\n      os.path.join(target_files_dir, 'IMAGES', partition_img))\n  move_only_exists(\n      os.path.join(vendor_target_files_dir, 'IMAGES', partition_map),\n      os.path.join(target_files_dir, 'IMAGES', partition_map))\n\n  def copy_recovery_file(filename):\n    for subdir in ('VENDOR', 'SYSTEM/vendor'):\n      source = os.path.join(vendor_target_files_dir, subdir, filename)\n      if os.path.exists(source):\n        dest = os.path.join(target_files_dir, subdir, filename)\n        shutil.copy(source, dest)\n        return\n    logger.info('Skipping copy_recovery_file for %s, file not found', filename)\n\n  if OPTIONS.rebuild_recovery:\n    copy_recovery_file('etc/recovery.img')\n    copy_recovery_file('bin/install-recovery.sh')\n    copy_recovery_file('recovery-from-boot.p')\n\n\ndef generate_super_empty_image(target_dir, output_super_empty):\n  \"\"\"Generates super_empty image from target package.\n\n  Args:\n    target_dir: Path to the target file package which contains misc_info.txt for\n      detailed information for super image.\n    output_super_empty: If provided, copies a super_empty.img file from the\n      target files package to this path.\n  \"\"\"\n  # Create super_empty.img using the merged misc_info.txt.\n\n  misc_info_txt = os.path.join(target_dir, 'META', 'misc_info.txt')\n\n  use_dynamic_partitions = common.LoadDictionaryFromFile(misc_info_txt).get(\n      'use_dynamic_partitions')\n\n  if use_dynamic_partitions != 'true' and output_super_empty:\n    raise ValueError(\n        'Building super_empty.img requires use_dynamic_partitions=true.')\n  elif use_dynamic_partitions == 'true':\n    super_empty_img = os.path.join(target_dir, 'IMAGES', 'super_empty.img')\n    build_super_image_args = [\n        misc_info_txt,\n        super_empty_img,\n    ]\n    build_super_image.main(build_super_image_args)\n\n    # Copy super_empty.img to the user-provided output_super_empty location.\n    if output_super_empty:\n      shutil.copyfile(super_empty_img, output_super_empty)\n\n\ndef create_target_files_archive(output_zip, source_dir, temp_dir):\n  \"\"\"Creates a target_files zip archive from the input source dir.\n\n  Args:\n    output_zip: The name of the zip archive target files package.\n    source_dir: The target directory contains package to be archived.\n    temp_dir: Path to temporary directory for any intermediate files.\n  \"\"\"\n  output_target_files_list = os.path.join(temp_dir, 'output.list')\n  output_target_files_meta_dir = os.path.join(source_dir, 'META')\n\n  def files_from_path(target_path, extra_args=None):\n    \"\"\"Gets files under the given path and return a sorted list.\"\"\"\n    find_command = ['find', target_path] + (extra_args or [])\n    find_process = common.Run(\n        find_command, stdout=subprocess.PIPE, verbose=False)\n    return common.RunAndCheckOutput(['sort'],\n                                    stdin=find_process.stdout,\n                                    verbose=False)\n\n  # META content appears first in the zip. This is done by the\n  # standard build system for optimized extraction of those files,\n  # so we do the same step for merged target_files.zips here too.\n  meta_content = files_from_path(output_target_files_meta_dir)\n  other_content = files_from_path(\n      source_dir,\n      ['-path', output_target_files_meta_dir, '-prune', '-o', '-print'])\n\n  with open(output_target_files_list, 'w') as f:\n    f.write(meta_content)\n    f.write(other_content)\n\n  command = [\n      'soong_zip',\n      '-d',\n      '-o',\n      os.path.abspath(output_zip),\n      '-C',\n      source_dir,\n      '-r',\n      output_target_files_list,\n  ]\n\n  logger.info('creating %s', output_zip)\n  common.RunAndCheckOutput(command, verbose=True)\n  logger.info('finished creating %s', output_zip)\n\n\ndef merge_target_files(temp_dir):\n  \"\"\"Merges two target files packages together.\n\n  This function uses framework and vendor target files packages as input,\n  performs various file extractions, special case processing, and finally\n  creates a merged zip archive as output.\n\n  Args:\n    temp_dir: The name of a directory we use when we extract items from the\n      input target files packages, and also a scratch directory that we use for\n      temporary files.\n  \"\"\"\n\n  logger.info('starting: merge framework %s and vendor %s into output %s',\n              OPTIONS.framework_target_files, OPTIONS.vendor_target_files,\n              OPTIONS.output_target_files)\n\n  output_target_files_temp_dir = create_merged_package(temp_dir)\n\n  partition_map = common.PartitionMapFromTargetFiles(\n      output_target_files_temp_dir)\n\n  compatibility_errors = merge_compatibility_checks.CheckCompatibility(\n      target_files_dir=output_target_files_temp_dir,\n      partition_map=partition_map)\n  if compatibility_errors:\n    for error in compatibility_errors:\n      logger.error(error)\n    raise ExternalError(\n        'Found incompatibilities in the merged target files package.')\n\n  # Include the compiled policy in an image if requested.\n  if OPTIONS.rebuild_sepolicy:\n    rebuild_image_with_sepolicy(output_target_files_temp_dir)\n\n  generate_missing_images(output_target_files_temp_dir)\n\n  generate_super_empty_image(output_target_files_temp_dir,\n                             OPTIONS.output_super_empty)\n\n  # Finally, create the output target files zip archive and/or copy the\n  # output items to the output target files directory.\n\n  if OPTIONS.output_dir:\n    merge_utils.CopyItems(output_target_files_temp_dir, OPTIONS.output_dir,\n                          OPTIONS.output_item_list)\n\n  if not OPTIONS.output_target_files:\n    return\n\n  create_target_files_archive(OPTIONS.output_target_files,\n                              output_target_files_temp_dir, temp_dir)\n\n  # Create the IMG package from the merged target files package.\n  if OPTIONS.output_img:\n    img_from_target_files.main(\n        [OPTIONS.output_target_files, OPTIONS.output_img])\n\n  # Create the OTA package from the merged target files package.\n\n  if OPTIONS.output_ota:\n    ota_from_target_files.main(\n        [OPTIONS.output_target_files, OPTIONS.output_ota])\n\n\ndef main():\n  \"\"\"The main function.\n\n  Process command line arguments, then call merge_target_files to\n  perform the heavy lifting.\n  \"\"\"\n\n  common.InitLogging()\n\n  def option_handler(o, a):\n    if o == '--system-target-files':\n      logger.warning(\n          '--system-target-files has been renamed to --framework-target-files')\n      OPTIONS.framework_target_files = a\n    elif o == '--framework-target-files':\n      OPTIONS.framework_target_files = a\n    elif o == '--system-item-list':\n      logger.warning(\n          '--system-item-list has been renamed to --framework-item-list')\n      OPTIONS.framework_item_list = a\n    elif o == '--framework-item-list':\n      OPTIONS.framework_item_list = a\n    elif o == '--system-misc-info-keys':\n      logger.warning('--system-misc-info-keys has been renamed to '\n                     '--framework-misc-info-keys')\n      OPTIONS.framework_misc_info_keys = a\n    elif o == '--framework-misc-info-keys':\n      OPTIONS.framework_misc_info_keys = a\n    elif o == '--other-target-files':\n      logger.warning(\n          '--other-target-files has been renamed to --vendor-target-files')\n      OPTIONS.vendor_target_files = a\n    elif o == '--vendor-target-files':\n      OPTIONS.vendor_target_files = a\n    elif o == '--other-item-list':\n      logger.warning('--other-item-list has been renamed to --vendor-item-list')\n      OPTIONS.vendor_item_list = a\n    elif o == '--vendor-item-list':\n      OPTIONS.vendor_item_list = a\n    elif o == '--boot-image-dir-path':\n      OPTIONS.boot_image_dir_path = a\n    elif o == '--output-target-files':\n      OPTIONS.output_target_files = a\n    elif o == '--output-dir':\n      OPTIONS.output_dir = a\n    elif o == '--output-item-list':\n      OPTIONS.output_item_list = a\n    elif o == '--output-ota':\n      OPTIONS.output_ota = a\n    elif o == '--output-img':\n      OPTIONS.output_img = a\n    elif o == '--output-super-empty':\n      OPTIONS.output_super_empty = a\n    elif o == '--rebuild_recovery' or o == '--rebuild-recovery':\n      OPTIONS.rebuild_recovery = True\n    elif o == '--allow-duplicate-apkapex-keys':\n      OPTIONS.allow_duplicate_apkapex_keys = True\n    elif o == '--vendor-otatools':\n      OPTIONS.vendor_otatools = a\n    elif o == '--rebuild-sepolicy':\n      OPTIONS.rebuild_sepolicy = True\n    elif o == '--keep-tmp':\n      OPTIONS.keep_tmp = True\n    elif o == '--avb-resolve-rollback-index-location-conflict':\n      OPTIONS.avb_resolve_rollback_index_location_conflict = True\n    elif o == '--allow-partial-ab':\n      OPTIONS.allow_partial_ab = True\n    elif o == '--framework-dexpreopt-config':\n      OPTIONS.framework_dexpreopt_config = a\n    elif o == '--framework-dexpreopt-tools':\n      OPTIONS.framework_dexpreopt_tools = a\n    elif o == '--vendor-dexpreopt-config':\n      OPTIONS.vendor_dexpreopt_config = a\n    else:\n      return False\n    return True\n\n  args = common.ParseOptions(\n      sys.argv[1:],\n      __doc__,\n      extra_long_opts=[\n          'system-target-files=',\n          'framework-target-files=',\n          'system-item-list=',\n          'framework-item-list=',\n          'system-misc-info-keys=',\n          'framework-misc-info-keys=',\n          'other-target-files=',\n          'vendor-target-files=',\n          'other-item-list=',\n          'vendor-item-list=',\n          'boot-image-dir-path=',\n          'output-target-files=',\n          'output-dir=',\n          'output-item-list=',\n          'output-ota=',\n          'output-img=',\n          'output-super-empty=',\n          'framework-dexpreopt-config=',\n          'framework-dexpreopt-tools=',\n          'vendor-dexpreopt-config=',\n          'rebuild_recovery',\n          'rebuild-recovery',\n          'allow-duplicate-apkapex-keys',\n          'vendor-otatools=',\n          'rebuild-sepolicy',\n          'keep-tmp',\n          'avb-resolve-rollback-index-location-conflict',\n          'allow-partial-ab',\n      ],\n      extra_option_handler=option_handler)\n\n  # pylint: disable=too-many-boolean-expressions\n  if (args or OPTIONS.framework_target_files is None or\n      OPTIONS.vendor_target_files is None or\n      (OPTIONS.output_target_files is None and OPTIONS.output_dir is None) or\n      (OPTIONS.output_dir is not None and not OPTIONS.output_item_list) or\n      (OPTIONS.rebuild_recovery and not OPTIONS.rebuild_sepolicy)):\n    common.Usage(__doc__)\n    sys.exit(1)\n\n  framework_namelist = merge_utils.GetTargetFilesItems(\n      OPTIONS.framework_target_files)\n  vendor_namelist = merge_utils.GetTargetFilesItems(\n      OPTIONS.vendor_target_files)\n\n  if OPTIONS.framework_item_list:\n    OPTIONS.framework_item_list = common.LoadListFromFile(\n        OPTIONS.framework_item_list)\n  else:\n    OPTIONS.framework_item_list = merge_utils.InferItemList(\n        input_namelist=framework_namelist, framework=True)\n  OPTIONS.framework_partition_set = merge_utils.ItemListToPartitionSet(\n      OPTIONS.framework_item_list)\n\n  if OPTIONS.framework_misc_info_keys:\n    OPTIONS.framework_misc_info_keys = common.LoadListFromFile(\n        OPTIONS.framework_misc_info_keys)\n  else:\n    OPTIONS.framework_misc_info_keys = merge_utils.InferFrameworkMiscInfoKeys(\n        input_namelist=framework_namelist)\n\n  if OPTIONS.vendor_item_list:\n    OPTIONS.vendor_item_list = common.LoadListFromFile(OPTIONS.vendor_item_list)\n  else:\n    OPTIONS.vendor_item_list = merge_utils.InferItemList(\n        input_namelist=vendor_namelist, framework=False)\n  OPTIONS.vendor_partition_set = merge_utils.ItemListToPartitionSet(\n      OPTIONS.vendor_item_list)\n\n  if OPTIONS.output_item_list:\n    OPTIONS.output_item_list = common.LoadListFromFile(OPTIONS.output_item_list)\n\n  if OPTIONS.vendor_otatools and zipfile.is_zipfile(OPTIONS.vendor_otatools):\n    vendor_otatools_dir = common.MakeTempDir(\n        prefix='merge_target_files_vendor_otatools_')\n    common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)\n    OPTIONS.vendor_otatools = vendor_otatools_dir\n\n  if not merge_utils.ValidateConfigLists():\n    sys.exit(1)\n\n  temp_dir = common.MakeTempDir(prefix='merge_target_files_')\n  try:\n    merge_target_files(temp_dir)\n  finally:\n    if OPTIONS.keep_tmp:\n      logger.info('Keeping temp_dir %s', temp_dir)\n    else:\n      common.Cleanup()\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tools/releasetools/merge/merge_utils.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n# use this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations under\n# the License.\n#\n\"\"\"Common utility functions shared by merge_* scripts.\n\nExpects items in OPTIONS prepared by merge_target_files.py.\n\"\"\"\n\nimport fnmatch\nimport logging\nimport os\nimport re\nimport shutil\nimport zipfile\n\nimport common\n\nlogger = logging.getLogger(__name__)\nOPTIONS = common.OPTIONS\n\n\ndef ExtractItems(input_zip, output_dir, extract_item_list):\n  \"\"\"Extracts items in extract_item_list from a zip to a dir.\"\"\"\n\n  # Filter the extract_item_list to remove any items that do not exist in the\n  # zip file. Otherwise, the extraction step will fail.\n\n  with zipfile.ZipFile(input_zip, allowZip64=True) as input_zipfile:\n    input_namelist = input_zipfile.namelist()\n\n  filtered_extract_item_list = []\n  for pattern in extract_item_list:\n    if fnmatch.filter(input_namelist, pattern):\n      filtered_extract_item_list.append(pattern)\n\n  common.UnzipToDir(input_zip, output_dir, filtered_extract_item_list)\n\n\ndef CopyItems(from_dir, to_dir, copy_item_list):\n  \"\"\"Copies the items in copy_item_list from source to destination directory.\n\n  copy_item_list may include files and directories. Will copy the matched\n  files and create the matched directories.\n\n  Args:\n    from_dir: The source directory.\n    to_dir: The destination directory.\n    copy_item_list: Items to be copied.\n  \"\"\"\n  item_paths = []\n  for root, dirs, files in os.walk(from_dir):\n    item_paths.extend(\n        os.path.relpath(path=os.path.join(root, item_name), start=from_dir)\n        for item_name in files + dirs)\n\n  filtered = set()\n  for pattern in copy_item_list:\n    filtered.update(fnmatch.filter(item_paths, pattern))\n\n  for item in filtered:\n    original_path = os.path.join(from_dir, item)\n    copied_path = os.path.join(to_dir, item)\n    copied_parent_path = os.path.dirname(copied_path)\n    if not os.path.exists(copied_parent_path):\n      os.makedirs(copied_parent_path)\n    if os.path.islink(original_path):\n      os.symlink(os.readlink(original_path), copied_path)\n    elif os.path.isdir(original_path):\n      if not os.path.exists(copied_path):\n        os.makedirs(copied_path)\n    else:\n      shutil.copyfile(original_path, copied_path)\n\n\ndef GetTargetFilesItems(target_files_zipfile_or_dir):\n  \"\"\"Gets a list of target files items.\"\"\"\n  if zipfile.is_zipfile(target_files_zipfile_or_dir):\n    with zipfile.ZipFile(target_files_zipfile_or_dir, allowZip64=True) as fz:\n      return fz.namelist()\n  elif os.path.isdir(target_files_zipfile_or_dir):\n    item_list = []\n    for root, dirs, files in os.walk(target_files_zipfile_or_dir):\n      item_list.extend(\n          os.path.relpath(path=os.path.join(root, item),\n                          start=target_files_zipfile_or_dir)\n          for item in dirs + files)\n    return item_list\n  else:\n    raise ValueError('Target files should be either zipfile or directory.')\n\n\ndef CollectTargetFiles(input_zipfile_or_dir, output_dir, item_list=None):\n  \"\"\"Extracts input zipfile or copy input directory to output directory.\n\n  Extracts the input zipfile if `input_zipfile_or_dir` is a zip archive, or\n  copies the items if `input_zipfile_or_dir` is a directory.\n\n  Args:\n    input_zipfile_or_dir: The input target files, could be either a zipfile to\n      extract or a directory to copy.\n    output_dir: The output directory that the input files are either extracted\n      or copied.\n    item_list: Files to be extracted or copied. Will extract or copy all files\n      if omitted.\n  \"\"\"\n  patterns = item_list if item_list else ('*',)\n  if zipfile.is_zipfile(input_zipfile_or_dir):\n    ExtractItems(input_zipfile_or_dir, output_dir, patterns)\n  elif os.path.isdir(input_zipfile_or_dir):\n    CopyItems(input_zipfile_or_dir, output_dir, patterns)\n  else:\n    raise ValueError('Target files should be either zipfile or directory.')\n\n\ndef WriteSortedData(data, path):\n  \"\"\"Writes the sorted contents of either a list or dict to file.\n\n  This function sorts the contents of the list or dict and then writes the\n  resulting sorted contents to a file specified by path.\n\n  Args:\n    data: The list or dict to sort and write.\n    path: Path to the file to write the sorted values to. The file at path will\n      be overridden if it exists.\n  \"\"\"\n  with open(path, 'w') as output:\n    for entry in sorted(data):\n      out_str = '{}={}\\n'.format(entry, data[entry]) if isinstance(\n          data, dict) else '{}\\n'.format(entry)\n      output.write(out_str)\n\n\ndef ValidateConfigLists():\n  \"\"\"Performs validations on the merge config lists.\n\n  Returns:\n    False if a validation fails, otherwise true.\n  \"\"\"\n  has_error = False\n\n  # Check that partitions only come from one input.\n  framework_partitions = ItemListToPartitionSet(OPTIONS.framework_item_list)\n  vendor_partitions = ItemListToPartitionSet(OPTIONS.vendor_item_list)\n  from_both = framework_partitions.intersection(vendor_partitions)\n  if from_both:\n    logger.error(\n        'Cannot extract items from the same partition in both the '\n        'framework and vendor builds. Please ensure only one merge config '\n        'item list (or inferred list) includes each partition: %s' %\n        ','.join(from_both))\n    has_error = True\n\n  if any([\n      key in OPTIONS.framework_misc_info_keys\n      for key in ('dynamic_partition_list', 'super_partition_groups')\n  ]):\n    logger.error('Dynamic partition misc info keys should come from '\n                 'the vendor instance of META/misc_info.txt.')\n    has_error = True\n\n  return not has_error\n\n\n# In an item list (framework or vendor), we may see entries that select whole\n# partitions. Such an entry might look like this 'SYSTEM/*' (e.g., for the\n# system partition). The following regex matches this and extracts the\n# partition name.\n\n_PARTITION_ITEM_PATTERN = re.compile(r'^([A-Z_]+)/.*$')\n_IMAGE_PARTITION_PATTERN = re.compile(r'^IMAGES/(.*)\\.img$')\n_PREBUILT_IMAGE_PARTITION_PATTERN = re.compile(r'^PREBUILT_IMAGES/(.*)\\.img$')\n\n\ndef ItemListToPartitionSet(item_list):\n  \"\"\"Converts a target files item list to a partition set.\n\n  The item list contains items that might look like 'SYSTEM/*' or 'VENDOR/*' or\n  'OTA/android-info.txt'. Items that end in '/*' are assumed to match entire\n  directories where 'SYSTEM' or 'VENDOR' is a directory name that identifies the\n  contents of a partition of the same name. Other items in the list, such as the\n  'OTA' example contain metadata. This function iterates such a list, returning\n  a set that contains the partition entries.\n\n  Args:\n    item_list: A list of items in a target files package.\n\n  Returns:\n    A set of partitions extracted from the list of items.\n  \"\"\"\n\n  partition_set = set()\n\n  for item in item_list:\n    for pattern in (_PARTITION_ITEM_PATTERN, _IMAGE_PARTITION_PATTERN, _PREBUILT_IMAGE_PARTITION_PATTERN):\n      partition_match = pattern.search(item.strip())\n      if partition_match:\n        partition = partition_match.group(1).lower()\n        # These directories in target-files are not actual partitions.\n        if partition not in ('meta', 'images', 'prebuilt_images'):\n          partition_set.add(partition)\n\n  return partition_set\n\n\n# Partitions that are grabbed from the framework partial build by default.\n_FRAMEWORK_PARTITIONS = {\n    'system', 'product', 'system_ext', 'system_other', 'root',\n    'vbmeta_system', 'pvmfw'\n}\n\n\ndef InferItemList(input_namelist, framework):\n  item_set = set()\n\n  # Some META items are always grabbed from partial builds directly.\n  # Others are combined in merge_meta.py.\n  if framework:\n    item_set.update([\n        'META/liblz4.so',\n        'META/postinstall_config.txt',\n        'META/zucchini_config.txt',\n    ])\n  else:  # vendor\n    item_set.update([\n        'META/kernel_configs.txt',\n        'META/kernel_version.txt',\n        'META/otakeys.txt',\n        'META/pack_radioimages.txt',\n        'META/releasetools.py',\n    ])\n\n  # Grab a set of items for the expected partitions in the partial build.\n  seen_partitions = []\n  for namelist in input_namelist:\n    if namelist.endswith('/'):\n      continue\n\n    partition = namelist.split('/')[0].lower()\n\n    # META items are grabbed above, or merged later.\n    if partition == 'meta':\n      continue\n\n    if partition in ('images', 'prebuilt_images'):\n      image_partition, extension = os.path.splitext(os.path.basename(namelist))\n      if image_partition == 'vbmeta':\n        # Always regenerate vbmeta.img since it depends on hash information\n        # from both builds.\n        continue\n      if extension in ('.img', '.map'):\n        # Include image files in IMAGES/* if the partition comes from\n        # the expected set.\n        if (framework and image_partition in _FRAMEWORK_PARTITIONS) or (\n            not framework and image_partition not in _FRAMEWORK_PARTITIONS):\n          item_set.add(namelist)\n      elif not framework:\n        # Include all miscellaneous non-image files in IMAGES/* from\n        # the vendor build.\n        item_set.add(namelist)\n      continue\n\n    # Skip already-visited partitions.\n    if partition in seen_partitions:\n      continue\n    seen_partitions.append(partition)\n\n    if (framework and partition in _FRAMEWORK_PARTITIONS) or (\n        not framework and partition not in _FRAMEWORK_PARTITIONS):\n      fs_config_prefix = '' if partition == 'system' else '%s_' % partition\n      item_set.update([\n          '%s/*' % partition.upper(),\n          'META/%sfilesystem_config.txt' % fs_config_prefix,\n      ])\n\n  return sorted(item_set)\n\n\ndef InferFrameworkMiscInfoKeys(input_namelist):\n  keys = [\n      'ab_update',\n      'avb_vbmeta_system',\n      'avb_vbmeta_system_algorithm',\n      'avb_vbmeta_system_key_path',\n      'avb_vbmeta_system_rollback_index_location',\n      'default_system_dev_certificate',\n  ]\n\n  for partition in _FRAMEWORK_PARTITIONS:\n    for partition_dir in ('%s/' % partition.upper(), 'SYSTEM/%s/' % partition):\n      if partition_dir in input_namelist:\n        fs_type_prefix = '' if partition == 'system' else '%s_' % partition\n        keys.extend([\n            'avb_%s_hashtree_enable' % partition,\n            'avb_%s_add_hashtree_footer_args' % partition,\n            '%s_disable_sparse' % partition,\n            'building_%s_image' % partition,\n            '%sfs_type' % fs_type_prefix,\n        ])\n\n  return sorted(keys)\n"
  },
  {
    "path": "tools/releasetools/merge/test_merge_compatibility_checks.py",
    "content": "#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport os.path\nimport shutil\n\nimport common\nimport merge_compatibility_checks\nimport merge_target_files\nimport test_utils\n\n\nclass MergeCompatibilityChecksTest(test_utils.ReleaseToolsTestCase):\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n    self.partition_map = {\n        'system': 'system',\n        'system_ext': 'system_ext',\n        'product': 'product',\n        'vendor': 'vendor',\n        'odm': 'odm',\n    }\n    self.OPTIONS = merge_target_files.OPTIONS\n    self.OPTIONS.framework_partition_set = set(\n        ['product', 'system', 'system_ext'])\n    self.OPTIONS.vendor_partition_set = set(['odm', 'vendor'])\n\n  def test_CheckCombinedSepolicy(self):\n    product_out_dir = common.MakeTempDir()\n\n    def write_temp_file(path, data=''):\n      full_path = os.path.join(product_out_dir, path)\n      if not os.path.exists(os.path.dirname(full_path)):\n        os.makedirs(os.path.dirname(full_path))\n      with open(full_path, 'w') as f:\n        f.write(data)\n\n    write_temp_file(\n        'system/etc/vintf/compatibility_matrix.device.xml', \"\"\"\n      <compatibility-matrix>\n        <sepolicy>\n          <kernel-sepolicy-version>30</kernel-sepolicy-version>\n        </sepolicy>\n      </compatibility-matrix>\"\"\")\n    write_temp_file('vendor/etc/selinux/plat_sepolicy_vers.txt', '30.0')\n\n    write_temp_file('system/etc/selinux/plat_sepolicy.cil')\n    write_temp_file('system/etc/selinux/mapping/30.0.cil')\n    write_temp_file('product/etc/selinux/mapping/30.0.cil')\n    write_temp_file('vendor/etc/selinux/vendor_sepolicy.cil')\n    write_temp_file('vendor/etc/selinux/plat_pub_versioned.cil')\n\n    cmd = merge_compatibility_checks.CheckCombinedSepolicy(\n        product_out_dir, self.partition_map, execute=False)\n    self.assertEqual(' '.join(cmd),\n                     ('secilc -m -M true -G -N -c 30 '\n                      '-o {OTP}/META/combined_sepolicy -f /dev/null '\n                      '{OTP}/system/etc/selinux/plat_sepolicy.cil '\n                      '{OTP}/system/etc/selinux/mapping/30.0.cil '\n                      '{OTP}/vendor/etc/selinux/vendor_sepolicy.cil '\n                      '{OTP}/vendor/etc/selinux/plat_pub_versioned.cil '\n                      '{OTP}/product/etc/selinux/mapping/30.0.cil').format(\n                          OTP=product_out_dir))\n\n  def _copy_apex(self, source, output_dir, partition):\n    shutil.copy(\n        source,\n        os.path.join(output_dir, partition, 'apex', os.path.basename(source)))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckApexDuplicatePackages(self):\n    output_dir = common.MakeTempDir()\n    os.makedirs(os.path.join(output_dir, 'SYSTEM/apex'))\n    os.makedirs(os.path.join(output_dir, 'VENDOR/apex'))\n\n    self._copy_apex(\n        os.path.join(self.testdata_dir, 'has_apk.apex'), output_dir, 'SYSTEM')\n    self._copy_apex(\n        os.path.join(test_utils.get_current_dir(),\n                     'com.android.apex.compressed.v1.capex'), output_dir,\n        'VENDOR')\n    self.assertEqual(\n        len(\n            merge_compatibility_checks.CheckApexDuplicatePackages(\n                output_dir, self.partition_map)), 0)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckApexDuplicatePackages_RaisesOnPackageInMultiplePartitions(self):\n    output_dir = common.MakeTempDir()\n    os.makedirs(os.path.join(output_dir, 'SYSTEM/apex'))\n    os.makedirs(os.path.join(output_dir, 'VENDOR/apex'))\n\n    same_apex_package = os.path.join(self.testdata_dir, 'has_apk.apex')\n    self._copy_apex(same_apex_package, output_dir, 'SYSTEM')\n    self._copy_apex(same_apex_package, output_dir, 'VENDOR')\n    self.assertEqual(\n        merge_compatibility_checks.CheckApexDuplicatePackages(\n            output_dir, self.partition_map)[0],\n        'Duplicate APEX package_names found in multiple partitions: com.android.wifi'\n    )\n"
  },
  {
    "path": "tools/releasetools/merge/test_merge_meta.py",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport os.path\nimport shutil\n\nimport common\nimport merge_meta\nimport merge_target_files\nimport test_utils\n\n\nclass MergeMetaTest(test_utils.ReleaseToolsTestCase):\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n    self.OPTIONS = merge_target_files.OPTIONS\n    self.OPTIONS.framework_partition_set = set(\n        ['product', 'system', 'system_ext'])\n    self.OPTIONS.vendor_partition_set = set(['odm', 'vendor'])\n\n  def test_MergePackageKeys_ReturnsTrueIfNoConflicts(self):\n    output_meta_dir = common.MakeTempDir()\n\n    framework_meta_dir = common.MakeTempDir()\n    os.symlink(\n        os.path.join(self.testdata_dir, 'apexkeys_framework.txt'),\n        os.path.join(framework_meta_dir, 'apexkeys.txt'))\n\n    vendor_meta_dir = common.MakeTempDir()\n    os.symlink(\n        os.path.join(self.testdata_dir, 'apexkeys_vendor.txt'),\n        os.path.join(vendor_meta_dir, 'apexkeys.txt'))\n\n    merge_meta.MergePackageKeys(framework_meta_dir, vendor_meta_dir,\n                                output_meta_dir, 'apexkeys.txt')\n\n    merged_entries = []\n    merged_path = os.path.join(self.testdata_dir, 'apexkeys_merge.txt')\n\n    with open(merged_path) as f:\n      merged_entries = f.read().split('\\n')\n\n    output_entries = []\n    output_path = os.path.join(output_meta_dir, 'apexkeys.txt')\n\n    with open(output_path) as f:\n      output_entries = f.read().split('\\n')\n\n    return self.assertEqual(merged_entries, output_entries)\n\n  def test_MergePackageKeys_ReturnsFalseIfConflictsPresent(self):\n    output_meta_dir = common.MakeTempDir()\n\n    framework_meta_dir = common.MakeTempDir()\n    os.symlink(\n        os.path.join(self.testdata_dir, 'apexkeys_framework.txt'),\n        os.path.join(framework_meta_dir, 'apexkeys.txt'))\n\n    conflict_meta_dir = common.MakeTempDir()\n    os.symlink(\n        os.path.join(self.testdata_dir, 'apexkeys_framework_conflict.txt'),\n        os.path.join(conflict_meta_dir, 'apexkeys.txt'))\n\n    self.assertRaises(ValueError, merge_meta.MergePackageKeys,\n                      framework_meta_dir, conflict_meta_dir, output_meta_dir,\n                      'apexkeys.txt')\n\n  def test_MergePackageKeys_HandlesApkCertsSyntax(self):\n    output_meta_dir = common.MakeTempDir()\n\n    framework_meta_dir = common.MakeTempDir()\n    os.symlink(\n        os.path.join(self.testdata_dir, 'apkcerts_framework.txt'),\n        os.path.join(framework_meta_dir, 'apkcerts.txt'))\n\n    vendor_meta_dir = common.MakeTempDir()\n    os.symlink(\n        os.path.join(self.testdata_dir, 'apkcerts_vendor.txt'),\n        os.path.join(vendor_meta_dir, 'apkcerts.txt'))\n\n    merge_meta.MergePackageKeys(framework_meta_dir, vendor_meta_dir,\n                                output_meta_dir, 'apkcerts.txt')\n\n    merged_entries = []\n    merged_path = os.path.join(self.testdata_dir, 'apkcerts_merge.txt')\n\n    with open(merged_path) as f:\n      merged_entries = f.read().split('\\n')\n\n    output_entries = []\n    output_path = os.path.join(output_meta_dir, 'apkcerts.txt')\n\n    with open(output_path) as f:\n      output_entries = f.read().split('\\n')\n\n    return self.assertEqual(merged_entries, output_entries)\n"
  },
  {
    "path": "tools/releasetools/merge/test_merge_utils.py",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport os.path\n\nimport common\nimport merge_target_files\nimport merge_utils\nimport test_utils\n\n\nclass MergeUtilsTest(test_utils.ReleaseToolsTestCase):\n\n  def setUp(self):\n    self.OPTIONS = merge_target_files.OPTIONS\n\n  def test_CopyItems_CopiesItemsMatchingPatterns(self):\n\n    def createEmptyFile(path):\n      if not os.path.exists(os.path.dirname(path)):\n        os.makedirs(os.path.dirname(path))\n      open(path, 'a').close()\n      return path\n\n    def createEmptyFolder(path):\n      os.makedirs(path)\n      return path\n\n    def createSymLink(source, dest):\n      os.symlink(source, dest)\n      return dest\n\n    def getRelPaths(start, filepaths):\n      return set(\n          os.path.relpath(path=filepath, start=start)\n          for filepath in filepaths)\n\n    input_dir = common.MakeTempDir()\n    output_dir = common.MakeTempDir()\n    expected_copied_items = []\n    actual_copied_items = []\n    patterns = ['*.cpp', 'subdir/*.txt', 'subdir/empty_dir']\n\n    # Create various files and empty directories that we expect to get copied\n    # because they match one of the patterns.\n    expected_copied_items.extend([\n        createEmptyFile(os.path.join(input_dir, 'a.cpp')),\n        createEmptyFile(os.path.join(input_dir, 'b.cpp')),\n        createEmptyFile(os.path.join(input_dir, 'subdir', 'c.txt')),\n        createEmptyFile(os.path.join(input_dir, 'subdir', 'd.txt')),\n        createEmptyFile(\n            os.path.join(input_dir, 'subdir', 'subsubdir', 'e.txt')),\n        createEmptyFolder(os.path.join(input_dir, 'subdir', 'empty_dir')),\n        createSymLink('a.cpp', os.path.join(input_dir, 'a_link.cpp')),\n    ])\n    # Create some more files that we expect to not get copied.\n    createEmptyFile(os.path.join(input_dir, 'a.h'))\n    createEmptyFile(os.path.join(input_dir, 'b.h'))\n    createEmptyFile(os.path.join(input_dir, 'subdir', 'subsubdir', 'f.gif'))\n    createSymLink('a.h', os.path.join(input_dir, 'a_link.h'))\n\n    # Copy items.\n    merge_utils.CopyItems(input_dir, output_dir, patterns)\n\n    # Assert the actual copied items match the ones we expected.\n    for root_dir, dirs, files in os.walk(output_dir):\n      actual_copied_items.extend(\n          os.path.join(root_dir, filename) for filename in files)\n      for dirname in dirs:\n        dir_path = os.path.join(root_dir, dirname)\n        if not os.listdir(dir_path):\n          actual_copied_items.append(dir_path)\n    self.assertEqual(\n        getRelPaths(output_dir, actual_copied_items),\n        getRelPaths(input_dir, expected_copied_items))\n    self.assertEqual(\n        os.readlink(os.path.join(output_dir, 'a_link.cpp')), 'a.cpp')\n\n  def test_ValidateConfigLists_ReturnsFalseIfSharedExtractedPartition(self):\n    self.OPTIONS.system_item_list = [\n        'SYSTEM/*',\n    ]\n    self.OPTIONS.vendor_item_list = [\n        'SYSTEM/my_system_file',\n        'VENDOR/*',\n    ]\n    self.OPTIONS.vendor_item_list.append('SYSTEM/my_system_file')\n    self.assertFalse(merge_utils.ValidateConfigLists())\n\n  def test_ValidateConfigLists_ReturnsFalseIfSharedExtractedPartitionImage(\n      self):\n    self.OPTIONS.system_item_list = [\n        'SYSTEM/*',\n    ]\n    self.OPTIONS.vendor_item_list = [\n        'IMAGES/system.img',\n        'VENDOR/*',\n    ]\n    self.assertFalse(merge_utils.ValidateConfigLists())\n\n  def test_ValidateConfigLists_ReturnsFalseIfBadSystemMiscInfoKeys(self):\n    for bad_key in ['dynamic_partition_list', 'super_partition_groups']:\n      self.OPTIONS.framework_misc_info_keys = [bad_key]\n      self.assertFalse(merge_utils.ValidateConfigLists())\n\n  def test_ItemListToPartitionSet(self):\n    item_list = [\n        'IMAGES/system_ext.img',\n        'META/apexkeys.txt',\n        'META/apkcerts.txt',\n        'META/filesystem_config.txt',\n        'PRODUCT/*',\n        'SYSTEM/*',\n        'SYSTEM/system_ext/*',\n    ]\n    partition_set = merge_utils.ItemListToPartitionSet(item_list)\n    self.assertEqual(set(['product', 'system', 'system_ext']), partition_set)\n\n  def test_InferItemList_Framework(self):\n    zip_namelist = [\n        'IMAGES/product.img',\n        'IMAGES/product.map',\n        'IMAGES/system.img',\n        'IMAGES/system.map',\n        'SYSTEM/my_system_file',\n        'PRODUCT/my_product_file',\n        # Device does not use a separate system_ext partition.\n        'SYSTEM/system_ext/system_ext_file',\n    ]\n\n    item_list = merge_utils.InferItemList(zip_namelist, framework=True)\n\n    expected_framework_item_list = [\n        'IMAGES/product.img',\n        'IMAGES/product.map',\n        'IMAGES/system.img',\n        'IMAGES/system.map',\n        'META/filesystem_config.txt',\n        'META/liblz4.so',\n        'META/postinstall_config.txt',\n        'META/product_filesystem_config.txt',\n        'META/zucchini_config.txt',\n        'PRODUCT/*',\n        'SYSTEM/*',\n    ]\n\n    self.assertEqual(item_list, expected_framework_item_list)\n\n  def test_InferItemList_Vendor(self):\n    zip_namelist = [\n        'VENDOR/my_vendor_file',\n        'ODM/my_odm_file',\n        'IMAGES/odm.img',\n        'IMAGES/odm.map',\n        'IMAGES/vendor.img',\n        'IMAGES/vendor.map',\n        'IMAGES/my_custom_image.img',\n        'IMAGES/my_custom_file.txt',\n        'IMAGES/vbmeta.img',\n        'CUSTOM_PARTITION/my_custom_file',\n        # Leftover framework pieces that shouldn't be grabbed.\n        'IMAGES/system.img',\n        'SYSTEM/system_file',\n    ]\n\n    item_list = merge_utils.InferItemList(zip_namelist, framework=False)\n\n    expected_vendor_item_list = [\n        'CUSTOM_PARTITION/*',\n        'IMAGES/my_custom_file.txt',\n        'IMAGES/my_custom_image.img',\n        'IMAGES/odm.img',\n        'IMAGES/odm.map',\n        'IMAGES/vendor.img',\n        'IMAGES/vendor.map',\n        'META/custom_partition_filesystem_config.txt',\n        'META/kernel_configs.txt',\n        'META/kernel_version.txt',\n        'META/odm_filesystem_config.txt',\n        'META/otakeys.txt',\n        'META/pack_radioimages.txt',\n        'META/releasetools.py',\n        'META/vendor_filesystem_config.txt',\n        'ODM/*',\n        'VENDOR/*',\n    ]\n    self.assertEqual(item_list, expected_vendor_item_list)\n\n  def test_InferFrameworkMiscInfoKeys(self):\n    zip_namelist = [\n        'PRODUCT/',\n        'SYSTEM/',\n        'SYSTEM/system_ext/',\n    ]\n\n    keys = merge_utils.InferFrameworkMiscInfoKeys(zip_namelist)\n\n    expected_keys = [\n        'ab_update',\n        'avb_product_add_hashtree_footer_args',\n        'avb_product_hashtree_enable',\n        'avb_system_add_hashtree_footer_args',\n        'avb_system_ext_add_hashtree_footer_args',\n        'avb_system_ext_hashtree_enable',\n        'avb_system_hashtree_enable',\n        'avb_vbmeta_system',\n        'avb_vbmeta_system_algorithm',\n        'avb_vbmeta_system_key_path',\n        'avb_vbmeta_system_rollback_index_location',\n        'building_product_image',\n        'building_system_ext_image',\n        'building_system_image',\n        'default_system_dev_certificate',\n        'fs_type',\n        'product_disable_sparse',\n        'product_fs_type',\n        'system_disable_sparse',\n        'system_ext_disable_sparse',\n        'system_ext_fs_type',\n    ]\n    self.assertEqual(keys, expected_keys)\n"
  },
  {
    "path": "tools/releasetools/merge_ota.py",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport logging\nimport struct\nimport sys\nimport update_payload\nimport tempfile\nimport zipfile\nimport os\nimport care_map_pb2\n\nimport common\nfrom typing import BinaryIO, List\nfrom update_metadata_pb2 import DeltaArchiveManifest, DynamicPartitionMetadata, DynamicPartitionGroup\nfrom ota_metadata_pb2 import OtaMetadata\nfrom update_payload import Payload\n\nfrom payload_signer import PayloadSigner\nfrom ota_utils import PayloadGenerator, METADATA_PROTO_NAME, FinalizeMetadata\nfrom ota_signing_utils import AddSigningArgumentParse\n\nlogger = logging.getLogger(__name__)\n\nCARE_MAP_ENTRY = \"care_map.pb\"\nAPEX_INFO_ENTRY = \"apex_info.pb\"\n\n\ndef WriteDataBlob(payload: Payload, outfp: BinaryIO, read_size=1024*64):\n  for i in range(0, payload.total_data_length, read_size):\n    blob = payload.ReadDataBlob(\n        i, min(i+read_size, payload.total_data_length)-i)\n    outfp.write(blob)\n\n\ndef ConcatBlobs(payloads: List[Payload], outfp: BinaryIO):\n  for payload in payloads:\n    WriteDataBlob(payload, outfp)\n\n\ndef TotalDataLength(partitions):\n  for partition in reversed(partitions):\n    for op in reversed(partition.operations):\n      if op.data_length > 0:\n        return op.data_offset + op.data_length\n  return 0\n\n\ndef ExtendPartitionUpdates(partitions, new_partitions):\n  prefix_blob_length = TotalDataLength(partitions)\n  partitions.extend(new_partitions)\n  for part in partitions[-len(new_partitions):]:\n    for op in part.operations:\n      if op.HasField(\"data_length\") and op.data_length != 0:\n        op.data_offset += prefix_blob_length\n\n\nclass DuplicatePartitionError(ValueError):\n  pass\n\n\ndef MergeDynamicPartitionGroups(groups: List[DynamicPartitionGroup], new_groups: List[DynamicPartitionGroup]):\n  new_groups = {new_group.name: new_group for new_group in new_groups}\n  for group in groups:\n    if group.name not in new_groups:\n      continue\n    new_group = new_groups[group.name]\n    common_partitions = set(group.partition_names).intersection(\n        set(new_group.partition_names))\n    if len(common_partitions) != 0:\n      raise DuplicatePartitionError(\n          f\"Old group and new group should not have any intersections, {group.partition_names}, {new_group.partition_names}, common partitions: {common_partitions}\")\n    group.partition_names.extend(new_group.partition_names)\n    group.size = max(new_group.size, group.size)\n    del new_groups[group.name]\n  for new_group in new_groups.values():\n    groups.append(new_group)\n\n\ndef MergeDynamicPartitionMetadata(metadata: DynamicPartitionMetadata, new_metadata: DynamicPartitionMetadata):\n  MergeDynamicPartitionGroups(metadata.groups, new_metadata.groups)\n  metadata.snapshot_enabled &= new_metadata.snapshot_enabled\n  metadata.vabc_enabled &= new_metadata.vabc_enabled\n  assert metadata.vabc_compression_param == new_metadata.vabc_compression_param, f\"{metadata.vabc_compression_param} vs. {new_metadata.vabc_compression_param}\"\n  metadata.cow_version = max(metadata.cow_version, new_metadata.cow_version)\n\n\ndef MergeManifests(payloads: List[Payload]) -> DeltaArchiveManifest:\n  if len(payloads) == 0:\n    return None\n  if len(payloads) == 1:\n    return payloads[0].manifest\n\n  output_manifest = DeltaArchiveManifest()\n  output_manifest.block_size = payloads[0].manifest.block_size\n  output_manifest.partial_update = True\n  output_manifest.dynamic_partition_metadata.snapshot_enabled = payloads[\n      0].manifest.dynamic_partition_metadata.snapshot_enabled\n  output_manifest.dynamic_partition_metadata.vabc_enabled = payloads[\n      0].manifest.dynamic_partition_metadata.vabc_enabled\n  output_manifest.dynamic_partition_metadata.vabc_compression_param = payloads[\n      0].manifest.dynamic_partition_metadata.vabc_compression_param\n  apex_info = {}\n  for payload in payloads:\n    manifest = payload.manifest\n    assert manifest.block_size == output_manifest.block_size\n    output_manifest.minor_version = max(\n        output_manifest.minor_version, manifest.minor_version)\n    output_manifest.max_timestamp = max(\n        output_manifest.max_timestamp, manifest.max_timestamp)\n    output_manifest.apex_info.extend(manifest.apex_info)\n    for apex in manifest.apex_info:\n      apex_info[apex.package_name] = apex\n    ExtendPartitionUpdates(output_manifest.partitions, manifest.partitions)\n    try:\n      MergeDynamicPartitionMetadata(\n          output_manifest.dynamic_partition_metadata, manifest.dynamic_partition_metadata)\n    except DuplicatePartitionError:\n      logger.error(\n          \"OTA %s has duplicate partition with some of the previous OTAs\", payload.name)\n      raise\n\n  for apex_name in sorted(apex_info.keys()):\n    output_manifest.apex_info.extend(apex_info[apex_name])\n\n  return output_manifest\n\n\ndef MergePayloads(payloads: List[Payload]):\n  with tempfile.NamedTemporaryFile(prefix=\"payload_blob\") as tmpfile:\n    ConcatBlobs(payloads, tmpfile)\n\n\ndef MergeCareMap(paths: List[str]):\n  care_map = care_map_pb2.CareMap()\n  for path in paths:\n    with zipfile.ZipFile(path, \"r\", allowZip64=True) as zfp:\n      if CARE_MAP_ENTRY in zfp.namelist():\n        care_map_bytes = zfp.read(CARE_MAP_ENTRY)\n        partial_care_map = care_map_pb2.CareMap()\n        partial_care_map.ParseFromString(care_map_bytes)\n        care_map.partitions.extend(partial_care_map.partitions)\n  if len(care_map.partitions) == 0:\n    return b\"\"\n  return care_map.SerializeToString()\n\n\ndef WriteHeaderAndManifest(manifest: DeltaArchiveManifest, fp: BinaryIO):\n  __MAGIC = b\"CrAU\"\n  __MAJOR_VERSION = 2\n  manifest_bytes = manifest.SerializeToString()\n  fp.write(struct.pack(f\">4sQQL\", __MAGIC,\n           __MAJOR_VERSION, len(manifest_bytes), 0))\n  fp.write(manifest_bytes)\n\n\ndef AddOtaMetadata(input_ota, metadata_ota, output_ota, package_key, pw):\n  with zipfile.ZipFile(metadata_ota, 'r') as zfp:\n    metadata = OtaMetadata()\n    metadata.ParseFromString(zfp.read(METADATA_PROTO_NAME))\n    FinalizeMetadata(metadata, input_ota, output_ota,\n                     package_key=package_key, pw=pw)\n    return output_ota\n\n\ndef CheckOutput(output_ota):\n  payload = update_payload.Payload(output_ota)\n  payload.CheckOpDataHash()\n\n\ndef CheckDuplicatePartitions(payloads: List[Payload]):\n  partition_to_ota = {}\n  for payload in payloads:\n    for group in payload.manifest.dynamic_partition_metadata.groups:\n      for part in group.partition_names:\n        if part in partition_to_ota:\n          raise DuplicatePartitionError(\n              f\"OTA {partition_to_ota[part].name} and {payload.name} have duplicating partition {part}\")\n        partition_to_ota[part] = payload\n\n\ndef ApexInfo(file_paths):\n  if len(file_paths) > 1:\n    logger.info(\"More than one target file specified, will ignore \"\n                \"apex_info.pb (if any)\")\n    return None\n  with zipfile.ZipFile(file_paths[0], \"r\", allowZip64=True) as zfp:\n    if APEX_INFO_ENTRY in zfp.namelist():\n      apex_info_bytes = zfp.read(APEX_INFO_ENTRY)\n      return apex_info_bytes\n  return None\n\n\ndef main(argv):\n  parser = argparse.ArgumentParser(description='Merge multiple partial OTAs')\n  parser.add_argument('packages', type=str, nargs='+',\n                      help='Paths to OTA packages to merge')\n  parser.add_argument('--output', type=str,\n                      help='Paths to output merged ota', required=True)\n  parser.add_argument('--metadata_ota', type=str,\n                      help='Output zip will use build metadata from this OTA package, if unspecified, use the last OTA package in merge list')\n  parser.add_argument('-v', action=\"store_true\",\n                      help=\"Enable verbose logging\", dest=\"verbose\")\n  AddSigningArgumentParse(parser)\n\n  parser.epilog = ('This tool can also be used to resign a regular OTA. For a single regular OTA, '\n                   'apex_info.pb will be written to output. When merging multiple OTAs, '\n                   'apex_info.pb will not be written.')\n  args = parser.parse_args(argv[1:])\n  file_paths = args.packages\n\n  common.OPTIONS.verbose = args.verbose\n  if args.verbose:\n    logger.setLevel(logging.INFO)\n\n  logger.info(args)\n  if args.java_path:\n    common.OPTIONS.java_path = args.java_path\n\n  if args.search_path:\n    common.OPTIONS.search_path = args.search_path\n\n  if args.signapk_path:\n    common.OPTIONS.signapk_path = args.signapk_path\n\n  if args.extra_signapk_args:\n    common.OPTIONS.extra_signapk_args = args.extra_signapk_args\n\n  if args.signapk_shared_library_path:\n    common.OPTIONS.signapk_shared_library_path = args.signapk_shared_library_path\n\n  metadata_ota = args.packages[-1]\n  if args.metadata_ota is not None:\n    metadata_ota = args.metadata_ota\n    assert os.path.exists(metadata_ota)\n\n  payloads = [Payload(path) for path in file_paths]\n\n  CheckDuplicatePartitions(payloads)\n\n  merged_manifest = MergeManifests(payloads)\n\n  # Get signing keys\n  key_passwords = common.GetKeyPasswords([args.package_key])\n\n  apex_info_bytes = ApexInfo(file_paths)\n\n  with tempfile.NamedTemporaryFile() as unsigned_payload:\n    WriteHeaderAndManifest(merged_manifest, unsigned_payload)\n    ConcatBlobs(payloads, unsigned_payload)\n    unsigned_payload.flush()\n\n    generator = PayloadGenerator()\n    generator.payload_file = unsigned_payload.name\n    logger.info(\"Payload size: %d\", os.path.getsize(generator.payload_file))\n\n    if args.package_key:\n      logger.info(\"Signing payload...\")\n      # TODO: remove OPTIONS when no longer used as fallback in payload_signer\n      common.OPTIONS.payload_signer_args = None\n      common.OPTIONS.payload_signer_maximum_signature_size = None\n      signer = PayloadSigner(args.package_key, args.private_key_suffix,\n                             key_passwords[args.package_key],\n                             payload_signer=args.payload_signer,\n                             payload_signer_args=args.payload_signer_args,\n                             payload_signer_maximum_signature_size=args.payload_signer_maximum_signature_size)\n      generator.payload_file = unsigned_payload.name\n      generator.Sign(signer)\n\n    logger.info(\"Payload size: %d\", os.path.getsize(generator.payload_file))\n\n    logger.info(\"Writing to %s\", args.output)\n\n    key_passwords = common.GetKeyPasswords([args.package_key])\n    with tempfile.NamedTemporaryFile(prefix=\"signed_ota\", suffix=\".zip\") as signed_ota:\n      with zipfile.ZipFile(signed_ota, \"w\") as zfp:\n        generator.WriteToZip(zfp)\n        care_map_bytes = MergeCareMap(args.packages)\n        if care_map_bytes:\n          common.ZipWriteStr(zfp, CARE_MAP_ENTRY, care_map_bytes)\n        if apex_info_bytes:\n          logger.info(\"Writing %s\", APEX_INFO_ENTRY)\n          common.ZipWriteStr(zfp, APEX_INFO_ENTRY, apex_info_bytes)\n      AddOtaMetadata(signed_ota.name, metadata_ota,\n                     args.output, args.package_key, key_passwords[args.package_key])\n  return 0\n\n\nif __name__ == '__main__':\n  logging.basicConfig()\n  sys.exit(main(sys.argv))\n"
  },
  {
    "path": "tools/releasetools/non_ab_ota.py",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport collections\nimport logging\nimport os\nimport zipfile\n\nimport common\nimport edify_generator\nimport verity_utils\nfrom check_target_files_vintf import CheckVintfIfTrebleEnabled, HasPartition\nfrom common import OPTIONS\nfrom ota_utils import UNZIP_PATTERN, FinalizeMetadata, GetPackageMetadata, PropertyFiles\nimport subprocess\n\nlogger = logging.getLogger(__name__)\n\n\ndef GetBlockDifferences(target_zip, source_zip, target_info, source_info,\n                        device_specific):\n  \"\"\"Returns a ordered dict of block differences with partition name as key.\"\"\"\n\n  def GetIncrementalBlockDifferenceForPartition(name):\n    if not HasPartition(source_zip, name):\n      raise RuntimeError(\n          \"can't generate incremental that adds {}\".format(name))\n\n    partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip,\n                                        info_dict=source_info,\n                                        allow_shared_blocks=allow_shared_blocks)\n\n    partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,\n                                        info_dict=target_info,\n                                        allow_shared_blocks=allow_shared_blocks)\n\n    # Check the first block of the source system partition for remount R/W only\n    # if the filesystem is ext4.\n    partition_source_info = source_info[\"fstab\"][\"/\" + name]\n    check_first_block = partition_source_info.fs_type == \"ext4\"\n    # Disable imgdiff because it relies on zlib to produce stable output\n    # across different versions, which is often not the case.\n    return common.BlockDifference(name, partition_tgt, partition_src,\n                                  check_first_block,\n                                  version=blockimgdiff_version,\n                                  disable_imgdiff=True)\n\n  if source_zip:\n    # See notes in common.GetUserImage()\n    allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == \"true\" or\n                           target_info.get('ext4_share_dup_blocks') == \"true\")\n    blockimgdiff_version = max(\n        int(i) for i in target_info.get(\n            \"blockimgdiff_versions\", \"1\").split(\",\"))\n    assert blockimgdiff_version >= 3\n\n  block_diff_dict = collections.OrderedDict()\n  partition_names = [\"system\", \"vendor\", \"product\", \"odm\", \"system_ext\",\n                     \"vendor_dlkm\", \"odm_dlkm\", \"system_dlkm\"]\n  for partition in partition_names:\n    if not HasPartition(target_zip, partition):\n      continue\n    # Full OTA update.\n    if not source_zip:\n      tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip,\n                                info_dict=target_info,\n                                reset_file_map=True)\n      block_diff_dict[partition] = common.BlockDifference(partition, tgt,\n                                                          src=None)\n    # Incremental OTA update.\n    else:\n      block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition(\n          partition)\n  assert \"system\" in block_diff_dict\n\n  # Get the block diffs from the device specific script. If there is a\n  # duplicate block diff for a partition, ignore the diff in the generic script\n  # and use the one in the device specific script instead.\n  if source_zip:\n    device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()\n    function_name = \"IncrementalOTA_GetBlockDifferences\"\n  else:\n    device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()\n    function_name = \"FullOTA_GetBlockDifferences\"\n\n  if device_specific_diffs:\n    assert all(isinstance(diff, common.BlockDifference)\n               for diff in device_specific_diffs), \\\n        \"{} is not returning a list of BlockDifference objects\".format(\n            function_name)\n    for diff in device_specific_diffs:\n      if diff.partition in block_diff_dict:\n        logger.warning(\"Duplicate block difference found. Device specific block\"\n                       \" diff for partition '%s' overrides the one in generic\"\n                       \" script.\", diff.partition)\n      block_diff_dict[diff.partition] = diff\n\n  return block_diff_dict\n\n\ndef WriteFullOTAPackage(input_zip, output_file):\n  target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)\n\n  # We don't know what version it will be installed on top of. We expect the API\n  # just won't change very often. Similarly for fstab, it might have changed in\n  # the target build.\n  target_api_version = target_info[\"recovery_api_version\"]\n  script = edify_generator.EdifyGenerator(target_api_version, target_info)\n\n  if target_info.oem_props and not OPTIONS.oem_no_mount:\n    target_info.WriteMountOemScript(script)\n\n  metadata = GetPackageMetadata(target_info)\n\n  if not OPTIONS.no_signing:\n    staging_file = common.MakeTempFile(suffix='.zip')\n  else:\n    staging_file = output_file\n\n  output_zip = zipfile.ZipFile(\n      staging_file, \"w\", compression=zipfile.ZIP_DEFLATED)\n\n  device_specific = common.DeviceSpecificParams(\n      input_zip=input_zip,\n      input_version=target_api_version,\n      output_zip=output_zip,\n      script=script,\n      input_tmp=OPTIONS.input_tmp,\n      metadata=metadata,\n      info_dict=OPTIONS.info_dict)\n\n  assert HasRecoveryPatch(input_zip, info_dict=OPTIONS.info_dict)\n\n  # Assertions (e.g. downgrade check, device properties check).\n  ts = target_info.GetBuildProp(\"ro.build.date.utc\")\n  ts_text = target_info.GetBuildProp(\"ro.build.date\")\n  script.AssertOlderBuild(ts, ts_text)\n\n  target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)\n  device_specific.FullOTA_Assertions()\n\n  block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None,\n                                        target_info=target_info,\n                                        source_info=None,\n                                        device_specific=device_specific)\n\n  # Two-step package strategy (in chronological order, which is *not*\n  # the order in which the generated script has things):\n  #\n  # if stage is not \"2/3\" or \"3/3\":\n  #    write recovery image to boot partition\n  #    set stage to \"2/3\"\n  #    reboot to boot partition and restart recovery\n  # else if stage is \"2/3\":\n  #    write recovery image to recovery partition\n  #    set stage to \"3/3\"\n  #    reboot to recovery partition and restart recovery\n  # else:\n  #    (stage must be \"3/3\")\n  #    set stage to \"\"\n  #    do normal full package installation:\n  #       wipe and install system, boot image, etc.\n  #       set up system to update recovery partition on first boot\n  #    complete script normally\n  #    (allow recovery to mark itself finished and reboot)\n\n  recovery_img = common.GetBootableImage(\"recovery.img\", \"recovery.img\",\n                                         OPTIONS.input_tmp, \"RECOVERY\")\n  if OPTIONS.two_step:\n    if not target_info.get(\"multistage_support\"):\n      assert False, \"two-step packages not supported by this build\"\n    fs = target_info[\"fstab\"][\"/misc\"]\n    assert fs.fs_type.upper() == \"EMMC\", \\\n        \"two-step packages only supported on devices with EMMC /misc partitions\"\n    bcb_dev = {\"bcb_dev\": fs.device}\n    common.ZipWriteStr(output_zip, \"recovery.img\", recovery_img.data)\n    script.AppendExtra(\"\"\"\nif get_stage(\"%(bcb_dev)s\") == \"2/3\" then\n\"\"\" % bcb_dev)\n\n    # Stage 2/3: Write recovery image to /recovery (currently running /boot).\n    script.Comment(\"Stage 2/3\")\n    script.WriteRawImage(\"/recovery\", \"recovery.img\")\n    script.AppendExtra(\"\"\"\nset_stage(\"%(bcb_dev)s\", \"3/3\");\nreboot_now(\"%(bcb_dev)s\", \"recovery\");\nelse if get_stage(\"%(bcb_dev)s\") == \"3/3\" then\n\"\"\" % bcb_dev)\n\n    # Stage 3/3: Make changes.\n    script.Comment(\"Stage 3/3\")\n\n  # Dump fingerprints\n  script.Print(\"Target: {}\".format(target_info.fingerprint))\n\n  device_specific.FullOTA_InstallBegin()\n\n  # All other partitions as well as the data wipe use 10% of the progress, and\n  # the update of the system partition takes the remaining progress.\n  system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1\n  if OPTIONS.wipe_user_data:\n    system_progress -= 0.1\n  progress_dict = {partition: 0.1 for partition in block_diff_dict}\n  progress_dict[\"system\"] = system_progress\n\n  if target_info.get('use_dynamic_partitions') == \"true\":\n    # Use empty source_info_dict to indicate that all partitions / groups must\n    # be re-added.\n    dynamic_partitions_diff = common.DynamicPartitionsDifference(\n        info_dict=OPTIONS.info_dict,\n        block_diffs=block_diff_dict.values(),\n        progress_dict=progress_dict)\n    dynamic_partitions_diff.WriteScript(script, output_zip,\n                                        write_verify_script=OPTIONS.verify)\n  else:\n    for block_diff in block_diff_dict.values():\n      block_diff.WriteScript(script, output_zip,\n                             progress=progress_dict.get(block_diff.partition),\n                             write_verify_script=OPTIONS.verify)\n\n  CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info)\n\n  boot_img = common.GetBootableImage(\n      \"boot.img\", \"boot.img\", OPTIONS.input_tmp, \"BOOT\")\n  common.CheckSize(boot_img.data, \"boot.img\", target_info)\n  common.ZipWriteStr(output_zip, \"boot.img\", boot_img.data)\n\n  script.WriteRawImage(\"/boot\", \"boot.img\")\n\n  script.ShowProgress(0.1, 10)\n  device_specific.FullOTA_InstallEnd()\n\n  if OPTIONS.extra_script is not None:\n    script.AppendExtra(OPTIONS.extra_script)\n\n  script.UnmountAll()\n\n  if OPTIONS.wipe_user_data:\n    script.ShowProgress(0.1, 10)\n    script.FormatPartition(\"/data\")\n\n  if OPTIONS.two_step:\n    script.AppendExtra(\"\"\"\nset_stage(\"%(bcb_dev)s\", \"\");\n\"\"\" % bcb_dev)\n    script.AppendExtra(\"else\\n\")\n\n    # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.\n    script.Comment(\"Stage 1/3\")\n    _WriteRecoveryImageToBoot(script, output_zip)\n\n    script.AppendExtra(\"\"\"\nset_stage(\"%(bcb_dev)s\", \"2/3\");\nreboot_now(\"%(bcb_dev)s\", \"\");\nendif;\nendif;\n\"\"\" % bcb_dev)\n\n  script.SetProgress(1)\n  script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)\n  metadata.required_cache = script.required_cache\n\n  # We haven't written the metadata entry, which will be done in\n  # FinalizeMetadata.\n  common.ZipClose(output_zip)\n\n  needed_property_files = (\n      NonAbOtaPropertyFiles(),\n  )\n  FinalizeMetadata(metadata, staging_file, output_file,\n                   needed_property_files, package_key=OPTIONS.package_key)\n\n\ndef WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):\n  target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)\n  source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)\n\n  target_api_version = target_info[\"recovery_api_version\"]\n  source_api_version = source_info[\"recovery_api_version\"]\n  if source_api_version == 0:\n    logger.warning(\n        \"Generating edify script for a source that can't install it.\")\n\n  script = edify_generator.EdifyGenerator(\n      source_api_version, target_info, fstab=source_info[\"fstab\"])\n\n  if target_info.oem_props or source_info.oem_props:\n    if not OPTIONS.oem_no_mount:\n      source_info.WriteMountOemScript(script)\n\n  metadata = GetPackageMetadata(target_info, source_info)\n\n  if not OPTIONS.no_signing:\n    staging_file = common.MakeTempFile(suffix='.zip')\n  else:\n    staging_file = output_file\n\n  output_zip = zipfile.ZipFile(\n      staging_file, \"w\", compression=zipfile.ZIP_DEFLATED)\n\n  device_specific = common.DeviceSpecificParams(\n      source_zip=source_zip,\n      source_version=source_api_version,\n      source_tmp=OPTIONS.source_tmp,\n      target_zip=target_zip,\n      target_version=target_api_version,\n      target_tmp=OPTIONS.target_tmp,\n      output_zip=output_zip,\n      script=script,\n      metadata=metadata,\n      info_dict=source_info)\n\n  source_boot = common.GetBootableImage(\n      \"/tmp/boot.img\", \"boot.img\", OPTIONS.source_tmp, \"BOOT\", source_info)\n  target_boot = common.GetBootableImage(\n      \"/tmp/boot.img\", \"boot.img\", OPTIONS.target_tmp, \"BOOT\", target_info)\n  updating_boot = (not OPTIONS.two_step and\n                   (source_boot.data != target_boot.data))\n\n  target_recovery = common.GetBootableImage(\n      \"/tmp/recovery.img\", \"recovery.img\", OPTIONS.target_tmp, \"RECOVERY\")\n\n  block_diff_dict = GetBlockDifferences(target_zip=target_zip,\n                                        source_zip=source_zip,\n                                        target_info=target_info,\n                                        source_info=source_info,\n                                        device_specific=device_specific)\n\n  CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info)\n\n  # Assertions (e.g. device properties check).\n  target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)\n  device_specific.IncrementalOTA_Assertions()\n\n  # Two-step incremental package strategy (in chronological order,\n  # which is *not* the order in which the generated script has\n  # things):\n  #\n  # if stage is not \"2/3\" or \"3/3\":\n  #    do verification on current system\n  #    write recovery image to boot partition\n  #    set stage to \"2/3\"\n  #    reboot to boot partition and restart recovery\n  # else if stage is \"2/3\":\n  #    write recovery image to recovery partition\n  #    set stage to \"3/3\"\n  #    reboot to recovery partition and restart recovery\n  # else:\n  #    (stage must be \"3/3\")\n  #    perform update:\n  #       patch system files, etc.\n  #       force full install of new boot image\n  #       set up system to update recovery partition on first boot\n  #    complete script normally\n  #    (allow recovery to mark itself finished and reboot)\n\n  if OPTIONS.two_step:\n    if not source_info.get(\"multistage_support\"):\n      assert False, \"two-step packages not supported by this build\"\n    fs = source_info[\"fstab\"][\"/misc\"]\n    assert fs.fs_type.upper() == \"EMMC\", \\\n        \"two-step packages only supported on devices with EMMC /misc partitions\"\n    bcb_dev = {\"bcb_dev\": fs.device}\n    common.ZipWriteStr(output_zip, \"recovery.img\", target_recovery.data)\n    script.AppendExtra(\"\"\"\nif get_stage(\"%(bcb_dev)s\") == \"2/3\" then\n\"\"\" % bcb_dev)\n\n    # Stage 2/3: Write recovery image to /recovery (currently running /boot).\n    script.Comment(\"Stage 2/3\")\n    script.AppendExtra(\"sleep(20);\\n\")\n    script.WriteRawImage(\"/recovery\", \"recovery.img\")\n    script.AppendExtra(\"\"\"\nset_stage(\"%(bcb_dev)s\", \"3/3\");\nreboot_now(\"%(bcb_dev)s\", \"recovery\");\nelse if get_stage(\"%(bcb_dev)s\") != \"3/3\" then\n\"\"\" % bcb_dev)\n\n    # Stage 1/3: (a) Verify the current system.\n    script.Comment(\"Stage 1/3\")\n\n  # Dump fingerprints\n  script.Print(\"Source: {}\".format(source_info.fingerprint))\n  script.Print(\"Target: {}\".format(target_info.fingerprint))\n\n  script.Print(\"Verifying current system...\")\n\n  device_specific.IncrementalOTA_VerifyBegin()\n\n  WriteFingerprintAssertion(script, target_info, source_info)\n\n  # Check the required cache size (i.e. stashed blocks).\n  required_cache_sizes = [diff.required_cache for diff in\n                          block_diff_dict.values()]\n  if updating_boot:\n    boot_type, boot_device_expr = common.GetTypeAndDeviceExpr(\"/boot\",\n                                                              source_info)\n    d = common.Difference(target_boot, source_boot, \"bsdiff\")\n    _, _, d = d.ComputePatch()\n    if d is None:\n      include_full_boot = True\n      common.ZipWriteStr(output_zip, \"boot.img\", target_boot.data)\n    else:\n      include_full_boot = False\n\n      logger.info(\n          \"boot      target: %d  source: %d  diff: %d\", target_boot.size,\n          source_boot.size, len(d))\n\n      common.ZipWriteStr(output_zip, \"boot.img.p\", d)\n\n      target_expr = 'concat(\"{}:\",{},\":{}:{}\")'.format(\n          boot_type, boot_device_expr, target_boot.size, target_boot.sha1)\n      source_expr = 'concat(\"{}:\",{},\":{}:{}\")'.format(\n          boot_type, boot_device_expr, source_boot.size, source_boot.sha1)\n      script.PatchPartitionExprCheck(target_expr, source_expr)\n\n      required_cache_sizes.append(target_boot.size)\n\n  if required_cache_sizes:\n    script.CacheFreeSpaceCheck(max(required_cache_sizes))\n\n  # Verify the existing partitions.\n  for diff in block_diff_dict.values():\n    diff.WriteVerifyScript(script, touched_blocks_only=True)\n\n  device_specific.IncrementalOTA_VerifyEnd()\n\n  if OPTIONS.two_step:\n    # Stage 1/3: (b) Write recovery image to /boot.\n    _WriteRecoveryImageToBoot(script, output_zip)\n\n    script.AppendExtra(\"\"\"\nset_stage(\"%(bcb_dev)s\", \"2/3\");\nreboot_now(\"%(bcb_dev)s\", \"\");\nelse\n\"\"\" % bcb_dev)\n\n    # Stage 3/3: Make changes.\n    script.Comment(\"Stage 3/3\")\n\n  script.Comment(\"---- start making changes here ----\")\n\n  device_specific.IncrementalOTA_InstallBegin()\n\n  progress_dict = {partition: 0.1 for partition in block_diff_dict}\n  progress_dict[\"system\"] = 1 - len(block_diff_dict) * 0.1\n\n  if OPTIONS.source_info_dict.get(\"use_dynamic_partitions\") == \"true\":\n    if OPTIONS.target_info_dict.get(\"use_dynamic_partitions\") != \"true\":\n      raise RuntimeError(\n          \"can't generate incremental that disables dynamic partitions\")\n    dynamic_partitions_diff = common.DynamicPartitionsDifference(\n        info_dict=OPTIONS.target_info_dict,\n        source_info_dict=OPTIONS.source_info_dict,\n        block_diffs=block_diff_dict.values(),\n        progress_dict=progress_dict)\n    dynamic_partitions_diff.WriteScript(\n        script, output_zip, write_verify_script=OPTIONS.verify)\n  else:\n    for block_diff in block_diff_dict.values():\n      block_diff.WriteScript(script, output_zip,\n                             progress=progress_dict.get(block_diff.partition),\n                             write_verify_script=OPTIONS.verify)\n\n  if OPTIONS.two_step:\n    common.ZipWriteStr(output_zip, \"boot.img\", target_boot.data)\n    script.WriteRawImage(\"/boot\", \"boot.img\")\n    logger.info(\"writing full boot image (forced by two-step mode)\")\n\n  if not OPTIONS.two_step:\n    if updating_boot:\n      if include_full_boot:\n        logger.info(\"boot image changed; including full.\")\n        script.Print(\"Installing boot image...\")\n        script.WriteRawImage(\"/boot\", \"boot.img\")\n      else:\n        # Produce the boot image by applying a patch to the current\n        # contents of the boot partition, and write it back to the\n        # partition.\n        logger.info(\"boot image changed; including patch.\")\n        script.Print(\"Patching boot image...\")\n        script.ShowProgress(0.1, 10)\n        target_expr = 'concat(\"{}:\",{},\":{}:{}\")'.format(\n            boot_type, boot_device_expr, target_boot.size, target_boot.sha1)\n        source_expr = 'concat(\"{}:\",{},\":{}:{}\")'.format(\n            boot_type, boot_device_expr, source_boot.size, source_boot.sha1)\n        script.PatchPartitionExpr(target_expr, source_expr, '\"boot.img.p\"')\n    else:\n      logger.info(\"boot image unchanged; skipping.\")\n\n  # Do device-specific installation (eg, write radio image).\n  device_specific.IncrementalOTA_InstallEnd()\n\n  if OPTIONS.extra_script is not None:\n    script.AppendExtra(OPTIONS.extra_script)\n\n  if OPTIONS.wipe_user_data:\n    script.Print(\"Erasing user data...\")\n    script.FormatPartition(\"/data\")\n\n  if OPTIONS.two_step:\n    script.AppendExtra(\"\"\"\nset_stage(\"%(bcb_dev)s\", \"\");\nendif;\nendif;\n\"\"\" % bcb_dev)\n\n  script.SetProgress(1)\n  # For downgrade OTAs, we prefer to use the update-binary in the source\n  # build that is actually newer than the one in the target build.\n  if OPTIONS.downgrade:\n    script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)\n  else:\n    script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)\n  metadata.required_cache = script.required_cache\n\n  # We haven't written the metadata entry yet, which will be handled in\n  # FinalizeMetadata().\n  common.ZipClose(output_zip)\n\n  # Sign the generated zip package unless no_signing is specified.\n  needed_property_files = (\n      NonAbOtaPropertyFiles(),\n  )\n  FinalizeMetadata(metadata, staging_file, output_file,\n                   needed_property_files, package_key=OPTIONS.package_key)\n\n\ndef GenerateNonAbOtaPackage(target_file, output_file, source_file=None):\n  \"\"\"Generates a non-A/B OTA package.\"\"\"\n  # Check the loaded info dicts first.\n  if OPTIONS.info_dict.get(\"no_recovery\") == \"true\":\n    raise common.ExternalError(\n        \"--- target build has specified no recovery ---\")\n\n  # Non-A/B OTAs rely on /cache partition to store temporary files.\n  cache_size = OPTIONS.info_dict.get(\"cache_size\")\n  if cache_size is None:\n    logger.warning(\"--- can't determine the cache partition size ---\")\n  OPTIONS.cache_size = cache_size\n\n  if OPTIONS.extra_script is not None:\n    with open(OPTIONS.extra_script) as fp:\n      OPTIONS.extra_script = fp.read()\n\n  if OPTIONS.extracted_input is not None:\n    OPTIONS.input_tmp = OPTIONS.extracted_input\n  else:\n    if not os.path.isdir(target_file):\n      logger.info(\"unzipping target target-files...\")\n      OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)\n    else:\n      OPTIONS.input_tmp = target_file\n      tmpfile = common.MakeTempFile(suffix=\".zip\")\n      os.unlink(tmpfile)\n      common.RunAndCheckOutput(\n          [\"zip\", tmpfile, \"-r\", \".\", \"-0\"], cwd=target_file)\n      assert zipfile.is_zipfile(tmpfile)\n      target_file = tmpfile\n\n  OPTIONS.target_tmp = OPTIONS.input_tmp\n\n  # If the caller explicitly specified the device-specific extensions path via\n  # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it\n  # is present in the target target_files. Otherwise, take the path of the file\n  # from 'tool_extensions' in the info dict and look for that in the local\n  # filesystem, relative to the current directory.\n  if OPTIONS.device_specific is None:\n    from_input = os.path.join(OPTIONS.input_tmp, \"META\", \"releasetools.py\")\n    if os.path.exists(from_input):\n      logger.info(\"(using device-specific extensions from target_files)\")\n      OPTIONS.device_specific = from_input\n    else:\n      OPTIONS.device_specific = OPTIONS.info_dict.get(\"tool_extensions\")\n\n  if OPTIONS.device_specific is not None:\n    OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)\n\n  # Generate a full OTA.\n  if source_file is None:\n    with zipfile.ZipFile(target_file) as input_zip:\n      WriteFullOTAPackage(\n          input_zip,\n          output_file)\n\n  # Generate an incremental OTA.\n  else:\n    logger.info(\"unzipping source target-files...\")\n    OPTIONS.source_tmp = common.UnzipTemp(\n        OPTIONS.incremental_source, UNZIP_PATTERN)\n    with zipfile.ZipFile(target_file) as input_zip, \\\n            zipfile.ZipFile(source_file) as source_zip:\n      WriteBlockIncrementalOTAPackage(\n          input_zip,\n          source_zip,\n          output_file)\n\n\ndef WriteFingerprintAssertion(script, target_info, source_info):\n  source_oem_props = source_info.oem_props\n  target_oem_props = target_info.oem_props\n\n  if source_oem_props is None and target_oem_props is None:\n    script.AssertSomeFingerprint(\n        source_info.fingerprint, target_info.fingerprint)\n  elif source_oem_props is not None and target_oem_props is not None:\n    script.AssertSomeThumbprint(\n        target_info.GetBuildProp(\"ro.build.thumbprint\"),\n        source_info.GetBuildProp(\"ro.build.thumbprint\"))\n  elif source_oem_props is None and target_oem_props is not None:\n    script.AssertFingerprintOrThumbprint(\n        source_info.fingerprint,\n        target_info.GetBuildProp(\"ro.build.thumbprint\"))\n  else:\n    script.AssertFingerprintOrThumbprint(\n        target_info.fingerprint,\n        source_info.GetBuildProp(\"ro.build.thumbprint\"))\n\n\nclass NonAbOtaPropertyFiles(PropertyFiles):\n  \"\"\"The property-files for non-A/B OTA.\n\n  For non-A/B OTA, the property-files string contains the info for METADATA\n  entry, with which a system updater can be fetched the package metadata prior\n  to downloading the entire package.\n  \"\"\"\n\n  def __init__(self):\n    super(NonAbOtaPropertyFiles, self).__init__()\n    self.name = 'ota-property-files'\n\n\ndef _WriteRecoveryImageToBoot(script, output_zip):\n  \"\"\"Find and write recovery image to /boot in two-step OTA.\n\n  In two-step OTAs, we write recovery image to /boot as the first step so that\n  we can reboot to there and install a new recovery image to /recovery.\n  A special \"recovery-two-step.img\" will be preferred, which encodes the correct\n  path of \"/boot\". Otherwise the device may show \"device is corrupt\" message\n  when booting into /boot.\n\n  Fall back to using the regular recovery.img if the two-step recovery image\n  doesn't exist. Note that rebuilding the special image at this point may be\n  infeasible, because we don't have the desired boot signer and keys when\n  calling ota_from_target_files.py.\n  \"\"\"\n\n  recovery_two_step_img_name = \"recovery-two-step.img\"\n  recovery_two_step_img_path = os.path.join(\n      OPTIONS.input_tmp, \"OTA\", recovery_two_step_img_name)\n  if os.path.exists(recovery_two_step_img_path):\n    common.ZipWrite(\n        output_zip,\n        recovery_two_step_img_path,\n        arcname=recovery_two_step_img_name)\n    logger.info(\n        \"two-step package: using %s in stage 1/3\", recovery_two_step_img_name)\n    script.WriteRawImage(\"/boot\", recovery_two_step_img_name)\n  else:\n    logger.info(\"two-step package: using recovery.img in stage 1/3\")\n    # The \"recovery.img\" entry has been written into package earlier.\n    script.WriteRawImage(\"/boot\", \"recovery.img\")\n\n\ndef HasRecoveryPatch(target_files_zip, info_dict):\n  board_uses_vendorimage = info_dict.get(\"board_uses_vendorimage\") == \"true\"\n\n  if board_uses_vendorimage:\n    target_files_dir = \"VENDOR\"\n  else:\n    target_files_dir = \"SYSTEM/vendor\"\n\n  patch = \"%s/recovery-from-boot.p\" % target_files_dir\n  img = \"%s/etc/recovery.img\" % target_files_dir\n\n  namelist = target_files_zip.namelist()\n  return patch in namelist or img in namelist\n"
  },
  {
    "path": "tools/releasetools/ota_from_raw_img.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nGiven a series of .img files, produces an OTA package that installs thoese images\n\"\"\"\n\nimport sys\nimport os\nimport argparse\nimport subprocess\nimport tempfile\nimport logging\nimport zipfile\n\nimport common\nfrom payload_signer import PayloadSigner\nfrom ota_utils import PayloadGenerator\nfrom ota_signing_utils import AddSigningArgumentParse\n\n\nlogger = logging.getLogger(__name__)\n\n\ndef ResolveBinaryPath(filename, search_path):\n  if not search_path:\n    return filename\n  if not os.path.exists(search_path):\n    return filename\n  path = os.path.join(search_path, \"bin\", filename)\n  if os.path.exists(path):\n    return path\n  path = os.path.join(search_path, filename)\n  if os.path.exists(path):\n    return path\n  return path\n\n\ndef main(argv):\n  parser = argparse.ArgumentParser(\n      prog=argv[0], description=\"Given a series of .img files, produces a full OTA package that installs thoese images\")\n  parser.add_argument(\"images\", nargs=\"+\", type=str,\n                      help=\"List of images to generate OTA\")\n  parser.add_argument(\"--partition_names\", nargs='?', type=str,\n                      help=\"Partition names to install the images, default to basename of the image(no file name extension)\")\n  parser.add_argument('--output', type=str,\n                      help='Paths to output merged ota', required=True)\n  parser.add_argument('--max_timestamp', type=int,\n                      help='Maximum build timestamp allowed to install this OTA')\n  parser.add_argument(\"-v\", action=\"store_true\",\n                      help=\"Enable verbose logging\", dest=\"verbose\")\n  AddSigningArgumentParse(parser)\n\n  args = parser.parse_args(argv[1:])\n  if args.verbose:\n    logger.setLevel(logging.INFO)\n  logger.info(args)\n  old_imgs = [\"\"] * len(args.images)\n  for (i, img) in enumerate(args.images):\n    if \":\" in img:\n      old_imgs[i], args.images[i] = img.split(\":\", maxsplit=1)\n\n  if not args.partition_names:\n    args.partition_names = [os.path.splitext(os.path.basename(path))[\n        0] for path in args.images]\n  else:\n    args.partition_names = args.partition_names.split(\",\")\n  with tempfile.NamedTemporaryFile() as unsigned_payload, tempfile.NamedTemporaryFile() as dynamic_partition_info_file:\n    dynamic_partition_info_file.writelines(\n        [b\"virtual_ab=true\\n\", b\"super_partition_groups=\\n\"])\n    dynamic_partition_info_file.flush()\n    cmd = [ResolveBinaryPath(\"delta_generator\", args.search_path)]\n    cmd.append(\"--partition_names=\" + \":\".join(args.partition_names))\n    cmd.append(\"--dynamic_partition_info_file=\" +\n               dynamic_partition_info_file.name)\n    cmd.append(\"--old_partitions=\" + \":\".join(old_imgs))\n    cmd.append(\"--new_partitions=\" + \":\".join(args.images))\n    cmd.append(\"--out_file=\" + unsigned_payload.name)\n    cmd.append(\"--is_partial_update\")\n    if args.max_timestamp:\n      cmd.append(\"--max_timestamp=\" + str(args.max_timestamp))\n      cmd.append(\"--partition_timestamps=boot:\" + str(args.max_timestamp))\n    logger.info(\"Running %s\", cmd)\n\n    subprocess.check_call(cmd)\n    generator = PayloadGenerator()\n    generator.payload_file = unsigned_payload.name\n    logger.info(\"Payload size: %d\", os.path.getsize(generator.payload_file))\n\n    # Get signing keys\n    key_passwords = common.GetKeyPasswords([args.package_key])\n\n    if args.package_key:\n      logger.info(\"Signing payload...\")\n      signer = PayloadSigner(args.package_key, args.private_key_suffix,\n                             key_passwords[args.package_key],\n                             payload_signer=args.payload_signer,\n                             payload_signer_args=args.payload_signer_args,\n                             payload_signer_maximum_signature_size=args.payload_signer_maximum_signature_size)\n      generator.payload_file = unsigned_payload.name\n      generator.Sign(signer)\n\n    logger.info(\"Payload size: %d\", os.path.getsize(generator.payload_file))\n\n    logger.info(\"Writing to %s\", args.output)\n    with zipfile.ZipFile(args.output, \"w\") as zfp:\n      generator.WriteToZip(zfp)\n\n\nif __name__ == \"__main__\":\n  logging.basicConfig()\n  main(sys.argv)\n"
  },
  {
    "path": "tools/releasetools/ota_from_target_files.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nGiven a target-files zipfile, produces an OTA package that installs that build.\nAn incremental OTA is produced if -i is given, otherwise a full OTA is produced.\n\nUsage:  ota_from_target_files [options] input_target_files output_ota_package\n\nCommon options that apply to both of non-A/B and A/B OTAs\n\n  --downgrade\n      Intentionally generate an incremental OTA that updates from a newer build\n      to an older one (e.g. downgrading from P preview back to O MR1).\n      \"ota-downgrade=yes\" will be set in the package metadata file. A data wipe\n      will always be enforced when using this flag, so \"ota-wipe=yes\" will also\n      be included in the metadata file. The update-binary in the source build\n      will be used in the OTA package, unless --binary flag is specified. Please\n      also check the comment for --override_timestamp below.\n\n  -i  (--incremental_from) <file>\n      Generate an incremental OTA using the given target-files zip as the\n      starting build.\n\n  -k  (--package_key) <key>\n      Key to use to sign the package (default is the value of\n      default_system_dev_certificate from the input target-files's\n      META/misc_info.txt, or \"build/make/target/product/security/testkey\" if\n      that value is not specified).\n\n      For incremental OTAs, the default value is based on the source\n      target-file, not the target build.\n\n  --override_timestamp\n      Intentionally generate an incremental OTA that updates from a newer build\n      to an older one (based on timestamp comparison), by setting the downgrade\n      flag in the package metadata. This differs from --downgrade flag, as we\n      don't enforce a data wipe with this flag. Because we know for sure this is\n      NOT an actual downgrade case, but two builds happen to be cut in a reverse\n      order (e.g. from two branches). A legit use case is that we cut a new\n      build C (after having A and B), but want to enfore an update path of A ->\n      C -> B. Specifying --downgrade may not help since that would enforce a\n      data wipe for C -> B update.\n\n      We used to set a fake timestamp in the package metadata for this flow. But\n      now we consolidate the two cases (i.e. an actual downgrade, or a downgrade\n      based on timestamp) with the same \"ota-downgrade=yes\" flag, with the\n      difference being whether \"ota-wipe=yes\" is set.\n\n  --wipe_user_data\n      Generate an OTA package that will wipe the user data partition when\n      installed.\n\n  --retrofit_dynamic_partitions\n      Generates an OTA package that updates a device to support dynamic\n      partitions (default False). This flag is implied when generating\n      an incremental OTA where the base build does not support dynamic\n      partitions but the target build does. For A/B, when this flag is set,\n      --skip_postinstall is implied.\n\n  --skip_compatibility_check\n      Skip checking compatibility of the input target files package.\n\n  --output_metadata_path\n      Write a copy of the metadata to a separate file. Therefore, users can\n      read the post build fingerprint without extracting the OTA package.\n\n  --force_non_ab\n      This flag can only be set on an A/B device that also supports non-A/B\n      updates. Implies --two_step.\n      If set, generate that non-A/B update package.\n      If not set, generates A/B package for A/B device and non-A/B package for\n      non-A/B device.\n\n  -o  (--oem_settings) <main_file[,additional_files...]>\n      Comma separated list of files used to specify the expected OEM-specific\n      properties on the OEM partition of the intended device. Multiple expected\n      values can be used by providing multiple files. Only the first dict will\n      be used to compute fingerprint, while the rest will be used to assert\n      OEM-specific properties.\n\nNon-A/B OTA specific options\n\n  -b  (--binary) <file>\n      Use the given binary as the update-binary in the output package, instead\n      of the binary in the build's target_files. Use for development only.\n\n  --block\n      Generate a block-based OTA for non-A/B device. We have deprecated the\n      support for file-based OTA since O. Block-based OTA will be used by\n      default for all non-A/B devices. Keeping this flag here to not break\n      existing callers.\n\n  -e  (--extra_script) <file>\n      Insert the contents of file at the end of the update script.\n\n  --full_bootloader\n      Similar to --full_radio. When generating an incremental OTA, always\n      include a full copy of bootloader image.\n\n  --full_radio\n      When generating an incremental OTA, always include a full copy of radio\n      image. This option is only meaningful when -i is specified, because a full\n      radio is always included in a full OTA if applicable.\n\n  --log_diff <file>\n      Generate a log file that shows the differences in the source and target\n      builds for an incremental package. This option is only meaningful when -i\n      is specified.\n\n  --oem_no_mount\n      For devices with OEM-specific properties but without an OEM partition, do\n      not mount the OEM partition in the updater-script. This should be very\n      rarely used, since it's expected to have a dedicated OEM partition for\n      OEM-specific properties. Only meaningful when -o is specified.\n\n  --stash_threshold <float>\n      Specify the threshold that will be used to compute the maximum allowed\n      stash size (defaults to 0.8).\n\n  -t  (--worker_threads) <int>\n      Specify the number of worker-threads that will be used when generating\n      patches for incremental updates (defaults to 3).\n\n  --verify\n      Verify the checksums of the updated system and vendor (if any) partitions.\n      Non-A/B incremental OTAs only.\n\n  -2  (--two_step)\n      Generate a 'two-step' OTA package, where recovery is updated first, so\n      that any changes made to the system partition are done using the new\n      recovery (new kernel, etc.).\n\nA/B OTA specific options\n\n  --disable_fec_computation\n      Disable the on device FEC data computation for incremental updates. OTA will be larger but installation will be faster.\n\n  --include_secondary\n      Additionally include the payload for secondary slot images (default:\n      False). Only meaningful when generating A/B OTAs.\n\n      By default, an A/B OTA package doesn't contain the images for the\n      secondary slot (e.g. system_other.img). Specifying this flag allows\n      generating a separate payload that will install secondary slot images.\n\n      Such a package needs to be applied in a two-stage manner, with a reboot\n      in-between. During the first stage, the updater applies the primary\n      payload only. Upon finishing, it reboots the device into the newly updated\n      slot. It then continues to install the secondary payload to the inactive\n      slot, but without switching the active slot at the end (needs the matching\n      support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).\n\n      Due to the special install procedure, the secondary payload will be always\n      generated as a full payload.\n\n  --payload_signer <signer>\n      Specify the signer when signing the payload and metadata for A/B OTAs.\n      By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign\n      with the package private key. If the private key cannot be accessed\n      directly, a payload signer that knows how to do that should be specified.\n      The signer will be supplied with \"-inkey <path_to_key>\",\n      \"-in <input_file>\" and \"-out <output_file>\" parameters.\n\n  --payload_signer_args <args>\n      Specify the arguments needed for payload signer.\n\n  --payload_signer_maximum_signature_size <signature_size>\n      The maximum signature size (in bytes) that would be generated by the given\n      payload signer. Only meaningful when custom payload signer is specified\n      via '--payload_signer'.\n      If the signer uses a RSA key, this should be the number of bytes to\n      represent the modulus. If it uses an EC key, this is the size of a\n      DER-encoded ECDSA signature.\n\n  --payload_signer_key_size <key_size>\n      Deprecated. Use the '--payload_signer_maximum_signature_size' instead.\n\n  --boot_variable_file <path>\n      A file that contains the possible values of ro.boot.* properties. It's\n      used to calculate the possible runtime fingerprints when some\n      ro.product.* properties are overridden by the 'import' statement.\n      The file expects one property per line, and each line has the following\n      format: 'prop_name=value1,value2'. e.g. 'ro.boot.product.sku=std,pro'\n      The path specified can either be relative to the current working directory\n      or the path to a file inside of input_target_files.\n\n  --skip_postinstall\n      Skip the postinstall hooks when generating an A/B OTA package (default:\n      False). Note that this discards ALL the hooks, including non-optional\n      ones. Should only be used if caller knows it's safe to do so (e.g. all the\n      postinstall work is to dexopt apps and a data wipe will happen immediately\n      after). Only meaningful when generating A/B OTAs.\n\n  --partial \"<PARTITION> [<PARTITION>[...]]\"\n      Generate partial updates, overriding ab_partitions list with the given\n      list. Specify --partial= without partition list to let tooling auto detect\n      partial partition list.\n\n  --custom_image <custom_partition=custom_image>\n      Use the specified custom_image to update custom_partition when generating\n      an A/B OTA package. e.g. \"--custom_image oem=oem.img --custom_image\n      cus=cus_test.img\"\n\n  --disable_vabc\n      Disable Virtual A/B Compression, for builds that have compression enabled\n      by default.\n\n  --vabc_downgrade\n      Don't disable Virtual A/B Compression for downgrading OTAs.\n      For VABC downgrades, we must finish merging before doing data wipe, and\n      since data wipe is required for downgrading OTA, this might cause long\n      wait time in recovery.\n\n  --enable_vabc_xor\n      Enable the VABC xor feature. Will reduce space requirements for OTA, but OTA installation will be slower.\n\n  --force_minor_version\n      Override the update_engine minor version for delta generation.\n\n  --compressor_types\n      A colon ':' separated list of compressors. Allowed values are bz2 and brotli.\n\n  --enable_zucchini\n      Whether to enable to zucchini feature. Will generate smaller OTA but uses more memory, OTA generation will take longer.\n\n  --enable_puffdiff\n      Whether to enable to puffdiff feature. Will generate smaller OTA but uses more memory, OTA generation will take longer.\n\n  --enable_lz4diff\n      Whether to enable lz4diff feature. Will generate smaller OTA for EROFS but\n      uses more memory.\n\n  --spl_downgrade\n      Force generate an SPL downgrade OTA. Only needed if target build has an\n      older SPL.\n\n  --vabc_compression_param\n      Compression algorithm to be used for VABC. Available options: gz, lz4, zstd, brotli, none. \n      Compression level can be specified by appending \",$LEVEL\" to option. \n      e.g. --vabc_compression_param=gz,9 specifies level 9 compression with gz algorithm\n\n  --security_patch_level\n      Override the security patch level in target files\n\n  --max_threads\n      Specify max number of threads allowed when generating A/B OTA\n\n  --vabc_cow_version\n      Specify the VABC cow version to be used\n\n  --compression_factor\n      Specify the maximum block size to be compressed at once during OTA. supported options: 4k, 8k, 16k, 32k, 64k, 128k, 256k\n\n  --full_ota_partitions\n      Specify list of partitions should be updated in full OTA fashion, even if\n      an incremental OTA is about to be generated\n\"\"\"\n\nfrom __future__ import print_function\n\nimport logging\nimport multiprocessing\nimport os\nimport os.path\nimport re\nimport shutil\nimport subprocess\nimport sys\nimport zipfile\n\nimport care_map_pb2\nimport common\nimport ota_utils\nimport payload_signer\nfrom ota_utils import (VABC_COMPRESSION_PARAM_SUPPORT, FinalizeMetadata, GetPackageMetadata,\n                       PayloadGenerator, SECURITY_PATCH_LEVEL_PROP_NAME, ExtractTargetFiles, CopyTargetFilesDir, TARGET_FILES_IMAGES_SUBDIR)\nfrom common import DoesInputFileContain, IsSparseImage\nimport target_files_diff\nfrom non_ab_ota import GenerateNonAbOtaPackage\nfrom payload_signer import PayloadSigner\n\nif sys.hexversion < 0x02070000:\n  print(\"Python 2.7 or newer is required.\", file=sys.stderr)\n  sys.exit(1)\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = ota_utils.OPTIONS\nOPTIONS.verify = False\nOPTIONS.patch_threshold = 0.95\nOPTIONS.wipe_user_data = False\nOPTIONS.extra_script = None\nOPTIONS.worker_threads = multiprocessing.cpu_count() // 2\nif OPTIONS.worker_threads == 0:\n  OPTIONS.worker_threads = 1\nOPTIONS.two_step = False\nOPTIONS.include_secondary = False\nOPTIONS.block_based = True\nOPTIONS.updater_binary = None\nOPTIONS.oem_dicts = None\nOPTIONS.oem_source = None\nOPTIONS.oem_no_mount = False\nOPTIONS.full_radio = False\nOPTIONS.full_bootloader = False\n# Stash size cannot exceed cache_size * threshold.\nOPTIONS.cache_size = None\nOPTIONS.stash_threshold = 0.8\nOPTIONS.log_diff = None\nOPTIONS.extracted_input = None\nOPTIONS.skip_postinstall = False\nOPTIONS.skip_compatibility_check = False\nOPTIONS.disable_fec_computation = False\nOPTIONS.disable_verity_computation = False\nOPTIONS.partial = None\nOPTIONS.custom_images = {}\nOPTIONS.disable_vabc = False\nOPTIONS.spl_downgrade = False\nOPTIONS.vabc_downgrade = False\nOPTIONS.enable_vabc_xor = True\nOPTIONS.force_minor_version = None\nOPTIONS.compressor_types = None\nOPTIONS.enable_zucchini = False\nOPTIONS.enable_puffdiff = None\nOPTIONS.enable_lz4diff = False\nOPTIONS.vabc_compression_param = None\nOPTIONS.security_patch_level = None\nOPTIONS.max_threads = None\nOPTIONS.vabc_cow_version = None\nOPTIONS.compression_factor = None\nOPTIONS.full_ota_partitions = None\n\n\nPOSTINSTALL_CONFIG = 'META/postinstall_config.txt'\nDYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'\nMISC_INFO = 'META/misc_info.txt'\nAB_PARTITIONS = 'META/ab_partitions.txt'\n\n# Files to be unzipped for target diffing purpose.\nTARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*',\n                                'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*',\n                                'VENDOR_DLKM/*', 'ODM_DLKM/*', 'SYSTEM_DLKM/*']\nRETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]\n\n# Images to be excluded from secondary payload. We essentially only keep\n# 'system_other' and bootloader partitions.\nSECONDARY_PAYLOAD_SKIPPED_IMAGES = [\n    'boot', 'dtbo', 'modem', 'odm', 'odm_dlkm', 'product', 'radio', 'recovery',\n    'system_dlkm', 'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor',\n    'vendor', 'vendor_boot']\n\n\ndef _LoadOemDicts(oem_source):\n  \"\"\"Returns the list of loaded OEM properties dict.\"\"\"\n  if not oem_source:\n    return None\n\n  oem_dicts = []\n  for oem_file in oem_source:\n    oem_dicts.append(common.LoadDictionaryFromFile(oem_file))\n  return oem_dicts\n\ndef ModifyKeyvalueList(content: str, key: str, value: str):\n  \"\"\" Update update the key value list with specified key and value\n  Args:\n    content: The string content of dynamic_partitions_info.txt. Each line\n      should be a key valur pair, where string before the first '=' are keys,\n      remaining parts are values.\n    key: the key of the key value pair to modify\n    value: the new value to replace with\n\n  Returns:\n    Updated content of the key value list\n  \"\"\"\n  output_list = []\n  for line in content.splitlines():\n    if line.startswith(key+\"=\"):\n      continue\n    output_list.append(line)\n  output_list.append(\"{}={}\".format(key, value))\n  return \"\\n\".join(output_list)\n\ndef ModifyVABCCompressionParam(content, algo):\n  \"\"\" Update update VABC Compression Param in dynamic_partitions_info.txt\n  Args:\n    content: The string content of dynamic_partitions_info.txt\n    algo: The compression algorithm should be used for VABC. See\n          https://cs.android.com/android/platform/superproject/+/master:system/core/fs_mgr/libsnapshot/cow_writer.cpp;l=127;bpv=1;bpt=1?q=CowWriter::ParseOptions&sq=\n  Returns:\n    Updated content of dynamic_partitions_info.txt , with custom compression algo\n  \"\"\"\n  return ModifyKeyvalueList(content, \"virtual_ab_compression_method\", algo)\n\n\ndef UpdatesInfoForSpecialUpdates(content, partitions_filter,\n                                 delete_keys=None):\n  \"\"\" Updates info file for secondary payload generation, partial update, etc.\n\n    Scan each line in the info file, and remove the unwanted partitions from\n    the dynamic partition list in the related properties. e.g.\n    \"super_google_dynamic_partitions_partition_list=system vendor product\"\n    will become \"super_google_dynamic_partitions_partition_list=system\".\n\n  Args:\n    content: The content of the input info file. e.g. misc_info.txt.\n    partitions_filter: A function to filter the desired partitions from a given\n      list\n    delete_keys: A list of keys to delete in the info file\n\n  Returns:\n    A string of the updated info content.\n  \"\"\"\n\n  output_list = []\n  # The suffix in partition_list variables that follows the name of the\n  # partition group.\n  list_suffix = 'partition_list'\n  for line in content.splitlines():\n    if line.startswith('#') or '=' not in line:\n      output_list.append(line)\n      continue\n    key, value = line.strip().split('=', 1)\n\n    if delete_keys and key in delete_keys:\n      pass\n    elif key.endswith(list_suffix):\n      partitions = value.split()\n      # TODO for partial update, partitions in the same group must be all\n      # updated or all omitted\n      partitions = filter(partitions_filter, partitions)\n      output_list.append('{}={}'.format(key, ' '.join(partitions)))\n    else:\n      output_list.append(line)\n  return '\\n'.join(output_list)\n\n\ndef GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):\n  \"\"\"Returns a target-files.zip file for generating secondary payload.\n\n  Although the original target-files.zip already contains secondary slot\n  images (i.e. IMAGES/system_other.img), we need to rename the files to the\n  ones without _other suffix. Note that we cannot instead modify the names in\n  META/ab_partitions.txt, because there are no matching partitions on device.\n\n  For the partitions that don't have secondary images, the ones for primary\n  slot will be used. This is to ensure that we always have valid boot, vbmeta,\n  bootloader images in the inactive slot.\n\n  After writing system_other to inactive slot's system partiiton,\n  PackageManagerService will read `ro.cp_system_other_odex`, and set\n  `sys.cppreopt` to \"requested\". Then, according to\n  system/extras/cppreopts/cppreopts.rc , init will mount system_other at\n  /postinstall, and execute `cppreopts` to copy optimized APKs from\n  /postinstall to /data .\n\n  Args:\n    input_file: The input target-files.zip file.\n    skip_postinstall: Whether to skip copying the postinstall config file.\n\n  Returns:\n    The filename of the target-files.zip for generating secondary payload.\n  \"\"\"\n\n  def GetInfoForSecondaryImages(info_file):\n    \"\"\"Updates info file for secondary payload generation.\"\"\"\n    with open(info_file) as f:\n      content = f.read()\n    # Remove virtual_ab flag from secondary payload so that OTA client\n    # don't use snapshots for secondary update\n    delete_keys = ['virtual_ab', \"virtual_ab_retrofit\"]\n    return UpdatesInfoForSpecialUpdates(\n        content, lambda p: p not in SECONDARY_PAYLOAD_SKIPPED_IMAGES,\n        delete_keys)\n\n  target_file = common.MakeTempFile(prefix=\"targetfiles-\", suffix=\".zip\")\n  target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)\n\n  fileslist = []\n  for (root, dirs, files) in os.walk(input_file):\n    root = root.lstrip(input_file).lstrip(\"/\")\n    fileslist.extend([os.path.join(root, d) for d in dirs])\n    fileslist.extend([os.path.join(root, d) for d in files])\n\n  input_tmp = input_file\n  for filename in fileslist:\n    unzipped_file = os.path.join(input_tmp, *filename.split('/'))\n    if filename == 'IMAGES/system_other.img':\n      common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')\n\n    # Primary images and friends need to be skipped explicitly.\n    elif filename in ('IMAGES/system.img',\n                      'IMAGES/system.map'):\n      pass\n\n    # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES.\n    elif filename.startswith(('IMAGES/', 'RADIO/')):\n      image_name = os.path.basename(filename)\n      if image_name not in ['{}.img'.format(partition) for partition in\n                            SECONDARY_PAYLOAD_SKIPPED_IMAGES]:\n        common.ZipWrite(target_zip, unzipped_file, arcname=filename)\n\n    # Skip copying the postinstall config if requested.\n    elif skip_postinstall and filename == POSTINSTALL_CONFIG:\n      pass\n\n    elif filename.startswith('META/'):\n      # Remove the unnecessary partitions for secondary images from the\n      # ab_partitions file.\n      if filename == AB_PARTITIONS:\n        with open(unzipped_file) as f:\n          partition_list = f.read().splitlines()\n        partition_list = [partition for partition in partition_list if partition\n                          and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]\n        common.ZipWriteStr(target_zip, filename,\n                           '\\n'.join(partition_list))\n      # Remove the unnecessary partitions from the dynamic partitions list.\n      elif (filename == 'META/misc_info.txt' or\n            filename == DYNAMIC_PARTITION_INFO):\n        modified_info = GetInfoForSecondaryImages(unzipped_file)\n        common.ZipWriteStr(target_zip, filename, modified_info)\n      else:\n        common.ZipWrite(target_zip, unzipped_file, arcname=filename)\n\n  common.ZipClose(target_zip)\n\n  return target_file\n\n\ndef GetTargetFilesZipWithoutPostinstallConfig(input_file):\n  \"\"\"Returns a target-files.zip that's not containing postinstall_config.txt.\n\n  This allows brillo_update_payload script to skip writing all the postinstall\n  hooks in the generated payload. The input target-files.zip file will be\n  duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't\n  contain the postinstall_config.txt entry, the input file will be returned.\n\n  Args:\n    input_file: The input target-files.zip filename.\n\n  Returns:\n    The filename of target-files.zip that doesn't contain postinstall config.\n  \"\"\"\n  config_path = os.path.join(input_file, POSTINSTALL_CONFIG)\n  if os.path.exists(config_path):\n    os.unlink(config_path)\n  return input_file\n\n\ndef ParseInfoDict(target_file_path):\n  return common.LoadInfoDict(target_file_path)\n\ndef ModifyTargetFilesDynamicPartitionInfo(input_file, key, value):\n  \"\"\"Returns a target-files.zip with a custom VABC compression param.\n  Args:\n    input_file: The input target-files.zip path\n    vabc_compression_param: Custom Virtual AB Compression algorithm\n\n  Returns:\n    The path to modified target-files.zip\n  \"\"\"\n  if os.path.isdir(input_file):\n    dynamic_partition_info_path = os.path.join(\n        input_file, *DYNAMIC_PARTITION_INFO.split(\"/\"))\n    with open(dynamic_partition_info_path, \"r\") as fp:\n      dynamic_partition_info = fp.read()\n    dynamic_partition_info = ModifyKeyvalueList(\n        dynamic_partition_info, key, value)\n    with open(dynamic_partition_info_path, \"w\") as fp:\n      fp.write(dynamic_partition_info)\n    return input_file\n\n  target_file = common.MakeTempFile(prefix=\"targetfiles-\", suffix=\".zip\")\n  shutil.copyfile(input_file, target_file)\n  common.ZipDelete(target_file, DYNAMIC_PARTITION_INFO)\n  with zipfile.ZipFile(input_file, 'r', allowZip64=True) as zfp:\n    dynamic_partition_info = zfp.read(DYNAMIC_PARTITION_INFO).decode()\n    dynamic_partition_info = ModifyKeyvalueList(\n        dynamic_partition_info, key, value)\n    with zipfile.ZipFile(target_file, \"a\", allowZip64=True) as output_zip:\n      output_zip.writestr(DYNAMIC_PARTITION_INFO, dynamic_partition_info)\n  return target_file\n\ndef GetTargetFilesZipForCustomVABCCompression(input_file, vabc_compression_param):\n  \"\"\"Returns a target-files.zip with a custom VABC compression param.\n  Args:\n    input_file: The input target-files.zip path\n    vabc_compression_param: Custom Virtual AB Compression algorithm\n\n  Returns:\n    The path to modified target-files.zip\n  \"\"\"\n  return ModifyTargetFilesDynamicPartitionInfo(input_file, \"virtual_ab_compression_method\", vabc_compression_param)\n\n\ndef GetTargetFilesZipForPartialUpdates(input_file, ab_partitions):\n  \"\"\"Returns a target-files.zip for partial ota update package generation.\n\n  This function modifies ab_partitions list with the desired partitions before\n  calling the brillo_update_payload script. It also cleans up the reference to\n  the excluded partitions in the info file, e.g. misc_info.txt.\n\n  Args:\n    input_file: The input target-files.zip filename.\n    ab_partitions: A list of partitions to include in the partial update\n\n  Returns:\n    The filename of target-files.zip used for partial ota update.\n  \"\"\"\n\n  original_ab_partitions = common.ReadFromInputFile(input_file, AB_PARTITIONS)\n\n  unrecognized_partitions = [partition for partition in ab_partitions if\n                             partition not in original_ab_partitions]\n  if unrecognized_partitions:\n    raise ValueError(\"Unrecognized partitions when generating partial updates\",\n                     unrecognized_partitions)\n\n  logger.info(\"Generating partial updates for %s\", ab_partitions)\n  for subdir in [\"IMAGES\", \"RADIO\", \"PREBUILT_IMAGES\"]:\n    image_dir = os.path.join(subdir)\n    if not os.path.exists(image_dir):\n      continue\n    for filename in os.listdir(image_dir):\n      filepath = os.path.join(image_dir, filename)\n      if filename.endswith(\".img\"):\n        partition_name = filename.removesuffix(\".img\")\n        if partition_name not in ab_partitions:\n          os.unlink(filepath)\n\n  common.WriteToInputFile(input_file, 'META/ab_partitions.txt',\n                          '\\n'.join(ab_partitions))\n  CARE_MAP_ENTRY = \"META/care_map.pb\"\n  if DoesInputFileContain(input_file, CARE_MAP_ENTRY):\n    caremap = care_map_pb2.CareMap()\n    caremap.ParseFromString(\n        common.ReadBytesFromInputFile(input_file, CARE_MAP_ENTRY))\n    filtered = [\n        part for part in caremap.partitions if part.name in ab_partitions]\n    del caremap.partitions[:]\n    caremap.partitions.extend(filtered)\n    common.WriteBytesToInputFile(input_file, CARE_MAP_ENTRY,\n                                 caremap.SerializeToString())\n\n  for info_file in ['META/misc_info.txt', DYNAMIC_PARTITION_INFO]:\n    if not DoesInputFileContain(input_file, info_file):\n      logger.warning('Cannot find %s in input zipfile', info_file)\n      continue\n\n    content = common.ReadFromInputFile(input_file, info_file)\n    modified_info = UpdatesInfoForSpecialUpdates(\n        content, lambda p: p in ab_partitions)\n    if OPTIONS.vabc_compression_param and info_file == DYNAMIC_PARTITION_INFO:\n      modified_info = ModifyVABCCompressionParam(\n          modified_info, OPTIONS.vabc_compression_param)\n    common.WriteToInputFile(input_file, info_file, modified_info)\n\n  def IsInPartialList(postinstall_line: str):\n    idx = postinstall_line.find(\"=\")\n    if idx < 0:\n      return False\n    key = postinstall_line[:idx]\n    logger.info(\"%s %s\", key, ab_partitions)\n    for part in ab_partitions:\n      if key.endswith(\"_\" + part):\n        return True\n    return False\n\n  if common.DoesInputFileContain(input_file, POSTINSTALL_CONFIG):\n    postinstall_config = common.ReadFromInputFile(\n        input_file, POSTINSTALL_CONFIG)\n    postinstall_config = [\n        line for line in postinstall_config.splitlines() if IsInPartialList(line)]\n    if postinstall_config:\n      postinstall_config = \"\\n\".join(postinstall_config)\n      common.WriteToInputFile(\n          input_file, POSTINSTALL_CONFIG, postinstall_config)\n    else:\n      os.unlink(os.path.join(input_file, POSTINSTALL_CONFIG))\n\n  return input_file\n\n\ndef GetTargetFilesZipForRetrofitDynamicPartitions(input_file,\n                                                  super_block_devices,\n                                                  dynamic_partition_list):\n  \"\"\"Returns a target-files.zip for retrofitting dynamic partitions.\n\n  This allows brillo_update_payload to generate an OTA based on the exact\n  bits on the block devices. Postinstall is disabled.\n\n  Args:\n    input_file: The input target-files.zip filename.\n    super_block_devices: The list of super block devices\n    dynamic_partition_list: The list of dynamic partitions\n\n  Returns:\n    The filename of target-files.zip with *.img replaced with super_*.img for\n    each block device in super_block_devices.\n  \"\"\"\n  assert super_block_devices, \"No super_block_devices are specified.\"\n\n  replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)\n             for dev in super_block_devices}\n\n  # Remove partitions from META/ab_partitions.txt that is in\n  # dynamic_partition_list but not in super_block_devices so that\n  # brillo_update_payload won't generate update for those logical partitions.\n  ab_partitions_lines = common.ReadFromInputFile(\n      input_file, AB_PARTITIONS).split(\"\\n\")\n  ab_partitions = [line.strip() for line in ab_partitions_lines]\n  # Assert that all super_block_devices are in ab_partitions\n  super_device_not_updated = [partition for partition in super_block_devices\n                              if partition not in ab_partitions]\n  assert not super_device_not_updated, \\\n      \"{} is in super_block_devices but not in {}\".format(\n          super_device_not_updated, AB_PARTITIONS)\n  # ab_partitions -= (dynamic_partition_list - super_block_devices)\n  to_delete = [AB_PARTITIONS]\n\n  # Always skip postinstall for a retrofit update.\n  to_delete += [POSTINSTALL_CONFIG]\n\n  # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this\n  # is a regular update on devices without dynamic partitions support.\n  to_delete += [DYNAMIC_PARTITION_INFO]\n\n  # Remove the existing partition images as well as the map files.\n  to_delete += list(replace.values())\n  to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]\n  for item in to_delete:\n    os.unlink(os.path.join(input_file, item))\n\n  # Write super_{foo}.img as {foo}.img.\n  for src, dst in replace.items():\n    assert DoesInputFileContain(input_file, src), \\\n        'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)\n    source_path = os.path.join(input_file, *src.split(\"/\"))\n    target_path = os.path.join(input_file, *dst.split(\"/\"))\n    os.rename(source_path, target_path)\n\n  # Write new ab_partitions.txt file\n  new_ab_partitions = os.path.join(input_file, AB_PARTITIONS)\n  with open(new_ab_partitions, 'w') as f:\n    for partition in ab_partitions:\n      if (partition in dynamic_partition_list and\n              partition not in super_block_devices):\n        logger.info(\"Dropping %s from ab_partitions.txt\", partition)\n        continue\n      f.write(partition + \"\\n\")\n\n  return input_file\n\n\ndef GetTargetFilesZipForCustomImagesUpdates(input_file, custom_images: dict):\n  \"\"\"Returns a target-files.zip for custom partitions update.\n\n  This function modifies ab_partitions list with the desired custom partitions\n  and puts the custom images into the target target-files.zip.\n\n  Args:\n    input_file: The input target-files extracted directory\n    custom_images: A map of custom partitions and custom images.\n\n  Returns:\n    The extracted dir of a target-files.zip which has renamed the custom images\n    in the IMAGES/ to their partition names.\n  \"\"\"\n  for custom_image in custom_images.values():\n    if not os.path.exists(os.path.join(input_file, \"IMAGES\", custom_image)):\n      raise ValueError(\"Specified custom image {} not found in target files {}, available images are {}\",\n                       custom_image, input_file, os.listdir(os.path.join(input_file, \"IMAGES\")))\n\n  for custom_partition, custom_image in custom_images.items():\n    default_custom_image = '{}.img'.format(custom_partition)\n    if default_custom_image != custom_image:\n      src = os.path.join(input_file, 'IMAGES', custom_image)\n      dst = os.path.join(input_file, 'IMAGES', default_custom_image)\n      os.rename(src, dst)\n\n  return input_file\n\n\ndef GeneratePartitionTimestampFlags(partition_state):\n  partition_timestamps = [\n      part.partition_name + \":\" + part.version\n      for part in partition_state]\n  return [\"--partition_timestamps\", \",\".join(partition_timestamps)]\n\n\ndef GeneratePartitionTimestampFlagsDowngrade(\n        pre_partition_state, post_partition_state):\n  assert pre_partition_state is not None\n  partition_timestamps = {}\n  for part in post_partition_state:\n    partition_timestamps[part.partition_name] = part.version\n  for part in pre_partition_state:\n    if part.partition_name in partition_timestamps:\n      partition_timestamps[part.partition_name] = \\\n          max(part.version, partition_timestamps[part.partition_name])\n  return [\n      \"--partition_timestamps\",\n      \",\".join([key + \":\" + val for (key, val)\n                in partition_timestamps.items()])\n  ]\n\n\ndef SupportsMainlineGkiUpdates(target_file):\n  \"\"\"Return True if the build supports MainlineGKIUpdates.\n\n  This function scans the product.img file in IMAGES/ directory for\n  pattern |*/apex/com.android.gki.*.apex|. If there are files\n  matching this pattern, conclude that build supports mainline\n  GKI and return True\n\n  Args:\n    target_file: Path to a target_file.zip, or an extracted directory\n  Return:\n    True if thisb uild supports Mainline GKI Updates.\n  \"\"\"\n  if target_file is None:\n    return False\n  if os.path.isfile(target_file):\n    target_file = common.UnzipTemp(target_file, [\"IMAGES/product.img\"])\n  if not os.path.isdir(target_file):\n    assert os.path.isdir(target_file), \\\n        \"{} must be a path to zip archive or dir containing extracted\"\\\n        \" target_files\".format(target_file)\n  image_file = os.path.join(target_file, \"IMAGES\", \"product.img\")\n\n  if not os.path.isfile(image_file):\n    return False\n\n  if IsSparseImage(image_file):\n    # Unsparse the image\n    tmp_img = common.MakeTempFile(suffix=\".img\")\n    subprocess.check_output([\"simg2img\", image_file, tmp_img])\n    image_file = tmp_img\n\n  cmd = [\"debugfs_static\", \"-R\", \"ls -p /apex\", image_file]\n  output = subprocess.check_output(cmd).decode()\n\n  pattern = re.compile(r\"com\\.android\\.gki\\..*\\.apex\")\n  return pattern.search(output) is not None\n\n\ndef ExtractOrCopyTargetFiles(target_file):\n  if os.path.isdir(target_file):\n    return CopyTargetFilesDir(target_file)\n  else:\n    return ExtractTargetFiles(target_file)\n\n\ndef ValidateCompressionParam(target_info):\n  vabc_compression_param = OPTIONS.vabc_compression_param\n  if vabc_compression_param:\n    minimum_api_level_required = VABC_COMPRESSION_PARAM_SUPPORT[vabc_compression_param.split(\",\")[0]]\n    if target_info.vendor_api_level < minimum_api_level_required:\n      raise ValueError(\"Specified VABC compression param {} is only supported for API level >= {}, device is on API level {}\".format(\n          vabc_compression_param, minimum_api_level_required, target_info.vendor_api_level))\n\n\ndef GenerateAbOtaPackage(target_file, output_file, source_file=None):\n  \"\"\"Generates an Android OTA package that has A/B update payload.\"\"\"\n  # If input target_files are directories, create a copy so that we can modify\n  # them directly\n  target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)\n  if OPTIONS.disable_vabc and target_info.is_release_key:\n    raise ValueError(\"Disabling VABC on release-key builds is not supported.\")\n\n  ValidateCompressionParam(target_info)\n  vabc_compression_param = target_info.vabc_compression_param\n\n  target_file = ExtractOrCopyTargetFiles(target_file)\n  if source_file is not None:\n    source_file = ExtractOrCopyTargetFiles(source_file)\n  # Stage the output zip package for package signing.\n  if not OPTIONS.no_signing:\n    staging_file = common.MakeTempFile(suffix='.zip')\n  else:\n    staging_file = output_file\n  output_zip = zipfile.ZipFile(staging_file, \"w\",\n                               compression=zipfile.ZIP_DEFLATED,\n                               allowZip64=True)\n\n  if source_file is not None:\n    source_file = ExtractTargetFiles(source_file)\n    if OPTIONS.full_ota_partitions:\n      for partition in OPTIONS.full_ota_partitions:\n        for subdir in TARGET_FILES_IMAGES_SUBDIR:\n          image_path = os.path.join(source_file, subdir, partition + \".img\")\n          if os.path.exists(image_path):\n            logger.info(\n                \"Ignoring source image %s for partition %s because it is configured to use full OTA\", image_path, partition)\n            os.remove(image_path)\n    assert \"ab_partitions\" in OPTIONS.source_info_dict, \\\n        \"META/ab_partitions.txt is required for ab_update.\"\n    assert \"ab_partitions\" in OPTIONS.target_info_dict, \\\n        \"META/ab_partitions.txt is required for ab_update.\"\n    target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)\n    source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)\n    # If source supports VABC, delta_generator/update_engine will attempt to\n    # use VABC. This dangerous, as the target build won't have snapuserd to\n    # serve I/O request when device boots. Therefore, disable VABC if source\n    # build doesn't supports it.\n    if not source_info.is_vabc or not target_info.is_vabc:\n      logger.info(\"Either source or target does not support VABC, disabling.\")\n      OPTIONS.disable_vabc = True\n    if OPTIONS.vabc_compression_param is None and \\\n            source_info.vabc_compression_param != target_info.vabc_compression_param:\n      logger.info(\"Source build and target build use different compression methods {} vs {}, default to source builds parameter {}\".format(\n          source_info.vabc_compression_param, target_info.vabc_compression_param, source_info.vabc_compression_param))\n      vabc_compression_param = source_info.vabc_compression_param\n    # Virtual AB Cow version 3 is introduced in Android U with improved memory\n    # and install time performance. All OTA's with\n    # both the source build and target build with VIRTUAL_AB_COW_VERSION = 3\n    # can support the new format. Otherwise, fallback on older versions\n    if not OPTIONS.vabc_cow_version:\n      if not source_info.vabc_cow_version or not target_info.vabc_cow_version:\n        logger.info(\"Source or Target doesn't have VABC_COW_VERSION specified, default to version 2\")\n        OPTIONS.vabc_cow_version = 2\n      elif source_info.vabc_cow_version != target_info.vabc_cow_version:\n        logger.info(\"Source and Target have different cow VABC_COW_VERSION specified, default to minimum version\")\n        OPTIONS.vabc_cow_version = min(source_info.vabc_cow_version, target_info.vabc_cow_version)\n\n    # Virtual AB Compression was introduced in Androd S.\n    # Later, we backported VABC to Android R. But verity support was not\n    # backported, so if VABC is used and we are on Android R, disable\n    # verity computation.\n    if not OPTIONS.disable_vabc and source_info.is_android_r:\n      OPTIONS.disable_verity_computation = True\n      OPTIONS.disable_fec_computation = True\n\n  else:\n    assert \"ab_partitions\" in OPTIONS.info_dict, \\\n        \"META/ab_partitions.txt is required for ab_update.\"\n    source_info = None\n    if not OPTIONS.vabc_cow_version:\n      if not target_info.vabc_cow_version:\n          OPTIONS.vabc_cow_version = 2\n      elif target_info.vabc_cow_version >= \"3\" and target_info.vendor_api_level < 35:\n        logger.warning(\n              \"This full OTA is configured to use VABC cow version\"\n              \" 3 which is supported since\"\n              \" Android API level 35, but device is \"\n              \"launched with {} . If this full OTA is\"\n              \" served to a device running old build, OTA might fail due to \"\n              \"unsupported vabc cow version. For safety, version 2 is used because \"\n              \"it's supported since day 1.\".format(\n                  target_info.vendor_api_level))\n        OPTIONS.vabc_cow_version = 2\n    if OPTIONS.vabc_compression_param is None and vabc_compression_param:\n      minimum_api_level_required = VABC_COMPRESSION_PARAM_SUPPORT[\n          vabc_compression_param]\n      if target_info.vendor_api_level < minimum_api_level_required:\n        logger.warning(\n            \"This full OTA is configured to use VABC compression algorithm\"\n            \" {}, which is supported since\"\n            \" Android API level {}, but device is \"\n            \"launched with {} . If this full OTA is\"\n            \" served to a device running old build, OTA might fail due to \"\n            \"unsupported compression parameter. For safety, gz is used because \"\n            \"it's supported since day 1.\".format(\n                vabc_compression_param,\n                minimum_api_level_required,\n                target_info.vendor_api_level))\n        vabc_compression_param = \"gz\"\n\n  if OPTIONS.partial == []:\n    logger.info(\n        \"Automatically detecting partial partition list from input target files.\")\n    OPTIONS.partial = target_info.get(\n        \"partial_ota_update_partitions_list\").split()\n    assert OPTIONS.partial, \"Input target_file does not have\"\n    \" partial_ota_update_partitions_list defined, failed to auto detect partial\"\n    \" partition list. Please specify list of partitions to update manually via\"\n    \" --partial=a,b,c , or generate a complete OTA by removing the --partial\"\n    \" option\"\n    OPTIONS.partial.sort()\n    if source_info:\n      source_partial_list = source_info.get(\n          \"partial_ota_update_partitions_list\").split()\n      if source_partial_list:\n        source_partial_list.sort()\n        if source_partial_list != OPTIONS.partial:\n          logger.warning(\"Source build and target build have different partial partition lists. Source: %s, target: %s, taking the intersection.\",\n                         source_partial_list, OPTIONS.partial)\n          OPTIONS.partial = list(\n              set(OPTIONS.partial) and set(source_partial_list))\n          OPTIONS.partial.sort()\n    logger.info(\"Automatically deduced partial partition list: %s\",\n                OPTIONS.partial)\n\n  if target_info.vendor_suppressed_vabc:\n    logger.info(\"Vendor suppressed VABC. Disabling\")\n    OPTIONS.disable_vabc = True\n\n  # Both source and target build need to support VABC XOR for us to use it.\n  # Source build's update_engine must be able to write XOR ops, and target\n  # build's snapuserd must be able to interpret XOR ops.\n  if not target_info.is_vabc_xor or OPTIONS.disable_vabc or \\\n          (source_info is not None and not source_info.is_vabc_xor):\n    logger.info(\"VABC XOR Not supported, disabling\")\n    OPTIONS.enable_vabc_xor = False\n\n  if OPTIONS.vabc_compression_param == \"none\":\n    logger.info(\n        \"VABC Compression algorithm is set to 'none', disabling VABC xor\")\n    OPTIONS.enable_vabc_xor = False\n\n  if OPTIONS.enable_vabc_xor:\n    api_level = -1\n    if source_info is not None:\n      api_level = source_info.vendor_api_level\n    if api_level == -1:\n      api_level = target_info.vendor_api_level\n\n    # XOR is only supported on T and higher.\n    if api_level < 33:\n      logger.error(\"VABC XOR not supported on this vendor, disabling\")\n      OPTIONS.enable_vabc_xor = False\n\n  if OPTIONS.vabc_compression_param:\n    vabc_compression_param = OPTIONS.vabc_compression_param\n\n  additional_args = []\n\n  # Prepare custom images.\n  if OPTIONS.custom_images:\n    if source_file is not None:\n      source_file = GetTargetFilesZipForCustomImagesUpdates(\n           source_file, OPTIONS.custom_images)\n    target_file = GetTargetFilesZipForCustomImagesUpdates(\n        target_file, OPTIONS.custom_images)\n\n  if OPTIONS.retrofit_dynamic_partitions:\n    target_file = GetTargetFilesZipForRetrofitDynamicPartitions(\n        target_file, target_info.get(\"super_block_devices\").strip().split(),\n        target_info.get(\"dynamic_partition_list\").strip().split())\n  elif OPTIONS.partial:\n    target_file = GetTargetFilesZipForPartialUpdates(target_file,\n                                                     OPTIONS.partial)\n  if vabc_compression_param != target_info.vabc_compression_param:\n    target_file = GetTargetFilesZipForCustomVABCCompression(\n        target_file, vabc_compression_param)\n  if OPTIONS.vabc_cow_version:\n    target_file = ModifyTargetFilesDynamicPartitionInfo(target_file, \"virtual_ab_cow_version\", OPTIONS.vabc_cow_version)\n  if OPTIONS.compression_factor:\n    target_file = ModifyTargetFilesDynamicPartitionInfo(target_file, \"virtual_ab_compression_factor\", OPTIONS.compression_factor)\n  if OPTIONS.skip_postinstall:\n    target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)\n  # Target_file may have been modified, reparse ab_partitions\n  target_info.info_dict['ab_partitions'] = common.ReadFromInputFile(target_file,\n                                                                    AB_PARTITIONS).strip().split(\"\\n\")\n\n  from check_target_files_vintf import CheckVintfIfTrebleEnabled\n  CheckVintfIfTrebleEnabled(target_file, target_info)\n\n  # Allow boot_variable_file to also exist in target-files\n  if OPTIONS.boot_variable_file:\n    if not os.path.isfile(OPTIONS.boot_variable_file):\n      OPTIONS.boot_variable_file = os.path.join(target_file, OPTIONS.boot_variable_file)\n  # Metadata to comply with Android OTA package format.\n  metadata = GetPackageMetadata(target_info, source_info)\n  # Generate payload.\n  payload = PayloadGenerator(\n      wipe_user_data=OPTIONS.wipe_user_data, minor_version=OPTIONS.force_minor_version, is_partial_update=OPTIONS.partial, spl_downgrade=OPTIONS.spl_downgrade)\n\n  partition_timestamps_flags = []\n  # Enforce a max timestamp this payload can be applied on top of.\n  if OPTIONS.downgrade:\n    # When generating ota between merged target-files, partition build date can\n    # decrease in target, at the same time as ro.build.date.utc increases,\n    # so always pick largest value.\n    max_timestamp = max(source_info.GetBuildProp(\"ro.build.date.utc\"),\n        str(metadata.postcondition.timestamp))\n    partition_timestamps_flags = GeneratePartitionTimestampFlagsDowngrade(\n        metadata.precondition.partition_state,\n        metadata.postcondition.partition_state\n    )\n  else:\n    max_timestamp = str(metadata.postcondition.timestamp)\n    partition_timestamps_flags = GeneratePartitionTimestampFlags(\n        metadata.postcondition.partition_state)\n\n  if not ota_utils.IsZucchiniCompatible(source_file, target_file):\n    logger.warning(\n        \"Builds doesn't support zucchini, or source/target don't have compatible zucchini versions. Disabling zucchini.\")\n    OPTIONS.enable_zucchini = False\n\n  security_patch_level = target_info.GetBuildProp(\n      \"ro.build.version.security_patch\")\n  if OPTIONS.security_patch_level is not None:\n    security_patch_level = OPTIONS.security_patch_level\n\n  additional_args += [\"--security_patch_level\", security_patch_level]\n\n  if OPTIONS.max_threads:\n    additional_args += [\"--max_threads\", OPTIONS.max_threads]\n\n  additional_args += [\"--enable_zucchini=\" +\n                      str(OPTIONS.enable_zucchini).lower()]\n  if OPTIONS.enable_puffdiff is not None:\n    additional_args += [\"--enable_puffdiff=\" +\n                        str(OPTIONS.enable_puffdiff).lower()]\n\n  if not ota_utils.IsLz4diffCompatible(source_file, target_file):\n    logger.warning(\n        \"Source build doesn't support lz4diff, or source/target don't have compatible lz4diff versions. Disabling lz4diff.\")\n    OPTIONS.enable_lz4diff = False\n\n  additional_args += [\"--enable_lz4diff=\" +\n                      str(OPTIONS.enable_lz4diff).lower()]\n\n  env_override = {}\n  if source_file and OPTIONS.enable_lz4diff:\n    liblz4_path = os.path.join(source_file, \"META\", \"liblz4.so\")\n    assert os.path.exists(\n        liblz4_path), \"liblz4.so not found in META/ dir of target file {}\".format(liblz4_path)\n    logger.info(\"Enabling lz4diff %s\", liblz4_path)\n    erofs_compression_param = OPTIONS.target_info_dict.get(\n        \"erofs_default_compressor\")\n    assert erofs_compression_param is not None, \"'erofs_default_compressor' not found in META/misc_info.txt of target build. This is required to enable lz4diff.\"\n    additional_args += [\"--erofs_compression_param\", erofs_compression_param]\n    env_override[\"LD_PRELOAD\"] = liblz4_path + \\\n        \":\" + os.environ.get(\"LD_PRELOAD\", \"\")\n\n  if OPTIONS.disable_vabc:\n    additional_args += [\"--disable_vabc=true\"]\n  if OPTIONS.enable_vabc_xor:\n    additional_args += [\"--enable_vabc_xor=true\"]\n  if OPTIONS.compressor_types:\n    additional_args += [\"--compressor_types\", OPTIONS.compressor_types]\n  additional_args += [\"--max_timestamp\", max_timestamp]\n\n  env = dict(os.environ)\n  if env_override:\n    logger.info(\"Using environment variables %s\", env_override)\n    env.update(env_override)\n  payload.Generate(\n      target_file,\n      source_file,\n      additional_args + partition_timestamps_flags,\n      env=env\n  )\n\n  # Sign the payload.\n  pw = OPTIONS.key_passwords[OPTIONS.package_key]\n  payload_signer = PayloadSigner(\n      OPTIONS.package_key, OPTIONS.private_key_suffix,\n      pw, OPTIONS.payload_signer)\n  payload.Sign(payload_signer)\n\n  # Write the payload into output zip.\n  payload.WriteToZip(output_zip)\n\n  # Generate and include the secondary payload that installs secondary images\n  # (e.g. system_other.img).\n  if OPTIONS.include_secondary:\n    # We always include a full payload for the secondary slot, even when\n    # building an incremental OTA. See the comments for \"--include_secondary\".\n    secondary_target_file = GetTargetFilesZipForSecondaryImages(\n        target_file, OPTIONS.skip_postinstall)\n    secondary_payload = PayloadGenerator(secondary=True)\n    secondary_payload.Generate(secondary_target_file,\n                               additional_args=[\"--max_timestamp\",\n                                                max_timestamp])\n    secondary_payload.Sign(payload_signer)\n    secondary_payload.WriteToZip(output_zip)\n\n  # If dm-verity is supported for the device, copy contents of care_map\n  # into A/B OTA package.\n  if target_info.get(\"avb_enable\") == \"true\":\n    # Adds care_map if either the protobuf format or the plain text one exists.\n    for care_map_name in [\"care_map.pb\", \"care_map.txt\"]:\n      if not DoesInputFileContain(target_file, \"META/\" + care_map_name):\n        continue\n      care_map_data = common.ReadBytesFromInputFile(\n          target_file, \"META/\" + care_map_name)\n      # In order to support streaming, care_map needs to be packed as\n      # ZIP_STORED.\n      common.ZipWriteStr(output_zip, care_map_name, care_map_data,\n                         compress_type=zipfile.ZIP_STORED)\n      # break here to avoid going into else when care map has been handled\n      break\n    else:\n      logger.warning(\"Cannot find care map file in target_file package\")\n\n  # Add the source apex version for incremental ota updates, and write the\n  # result apex info to the ota package.\n  ota_apex_info = ota_utils.ConstructOtaApexInfo(target_file, source_file)\n  if ota_apex_info is not None:\n    common.ZipWriteStr(output_zip, \"apex_info.pb\", ota_apex_info,\n                       compress_type=zipfile.ZIP_STORED)\n\n  # We haven't written the metadata entry yet, which will be handled in\n  # FinalizeMetadata().\n  common.ZipClose(output_zip)\n\n  FinalizeMetadata(metadata, staging_file, output_file,\n                   package_key=OPTIONS.package_key)\n\n\ndef main(argv):\n\n  def option_handler(o, a: str):\n    if o in (\"-i\", \"--incremental_from\"):\n      OPTIONS.incremental_source = a\n    elif o == \"--full_radio\":\n      OPTIONS.full_radio = True\n    elif o == \"--full_bootloader\":\n      OPTIONS.full_bootloader = True\n    elif o == \"--wipe_user_data\":\n      OPTIONS.wipe_user_data = True\n    elif o == \"--downgrade\":\n      OPTIONS.downgrade = True\n      OPTIONS.wipe_user_data = True\n    elif o == \"--override_timestamp\":\n      OPTIONS.downgrade = True\n    elif o in (\"-o\", \"--oem_settings\"):\n      OPTIONS.oem_source = a.split(',')\n    elif o == \"--oem_no_mount\":\n      OPTIONS.oem_no_mount = True\n    elif o in (\"-e\", \"--extra_script\"):\n      OPTIONS.extra_script = a\n    elif o in (\"-t\", \"--worker_threads\"):\n      if a.isdigit():\n        OPTIONS.worker_threads = int(a)\n      else:\n        raise ValueError(\"Cannot parse value %r for option %r - only \"\n                         \"integers are allowed.\" % (a, o))\n    elif o in (\"-2\", \"--two_step\"):\n      OPTIONS.two_step = True\n    elif o == \"--include_secondary\":\n      OPTIONS.include_secondary = True\n    elif o == \"--no_signing\":\n      OPTIONS.no_signing = True\n    elif o == \"--verify\":\n      OPTIONS.verify = True\n    elif o == \"--block\":\n      OPTIONS.block_based = True\n    elif o in (\"-b\", \"--binary\"):\n      OPTIONS.updater_binary = a\n    elif o == \"--stash_threshold\":\n      try:\n        OPTIONS.stash_threshold = float(a)\n      except ValueError:\n        raise ValueError(\"Cannot parse value %r for option %r - expecting \"\n                         \"a float\" % (a, o))\n    elif o == \"--log_diff\":\n      OPTIONS.log_diff = a\n    elif o == \"--extracted_input_target_files\":\n      OPTIONS.extracted_input = a\n    elif o == \"--skip_postinstall\":\n      OPTIONS.skip_postinstall = True\n    elif o == \"--retrofit_dynamic_partitions\":\n      OPTIONS.retrofit_dynamic_partitions = True\n    elif o == \"--skip_compatibility_check\":\n      OPTIONS.skip_compatibility_check = True\n    elif o == \"--output_metadata_path\":\n      OPTIONS.output_metadata_path = a\n    elif o == \"--disable_fec_computation\":\n      OPTIONS.disable_fec_computation = True\n    elif o == \"--disable_verity_computation\":\n      OPTIONS.disable_verity_computation = True\n    elif o == \"--force_non_ab\":\n      OPTIONS.force_non_ab = True\n    elif o == \"--boot_variable_file\":\n      OPTIONS.boot_variable_file = a\n    elif o == \"--partial\":\n      if a:\n        partitions = a.split()\n        if not partitions:\n          raise ValueError(\"Cannot parse partitions in {}\".format(a))\n      else:\n        partitions = []\n      OPTIONS.partial = partitions\n    elif o == \"--custom_image\":\n      custom_partition, custom_image = a.split(\"=\")\n      OPTIONS.custom_images[custom_partition] = custom_image\n    elif o == \"--disable_vabc\":\n      OPTIONS.disable_vabc = True\n    elif o == \"--spl_downgrade\":\n      OPTIONS.spl_downgrade = True\n      OPTIONS.wipe_user_data = True\n    elif o == \"--vabc_downgrade\":\n      OPTIONS.vabc_downgrade = True\n    elif o == \"--enable_vabc_xor\":\n      assert a.lower() in [\"true\", \"false\"]\n      OPTIONS.enable_vabc_xor = a.lower() != \"false\"\n    elif o == \"--force_minor_version\":\n      OPTIONS.force_minor_version = a\n    elif o == \"--compressor_types\":\n      OPTIONS.compressor_types = a\n    elif o == \"--enable_zucchini\":\n      assert a.lower() in [\"true\", \"false\"]\n      OPTIONS.enable_zucchini = a.lower() != \"false\"\n    elif o == \"--enable_puffdiff\":\n      assert a.lower() in [\"true\", \"false\"]\n      OPTIONS.enable_puffdiff = a.lower() != \"false\"\n    elif o == \"--enable_lz4diff\":\n      assert a.lower() in [\"true\", \"false\"]\n      OPTIONS.enable_lz4diff = a.lower() != \"false\"\n    elif o == \"--vabc_compression_param\":\n      words = a.split(\",\")\n      assert len(words) >= 1 and len(words) <= 2\n      OPTIONS.vabc_compression_param = a.lower()\n      if len(words) == 2:\n        if not words[1].lstrip(\"-\").isdigit():\n          raise ValueError(\"Cannot parse value %r for option $COMPRESSION_LEVEL - only \"\n                           \"integers are allowed.\" % words[1])\n    elif o == \"--security_patch_level\":\n      OPTIONS.security_patch_level = a\n    elif o in (\"--max_threads\"):\n      if a.isdigit():\n        OPTIONS.max_threads = a\n      else:\n        raise ValueError(\"Cannot parse value %r for option %r - only \"\n                         \"integers are allowed.\" % (a, o))\n    elif o in (\"--compression_factor\"):\n        values = [\"4k\", \"8k\", \"16k\", \"32k\", \"64k\", \"128k\", \"256k\"]\n        if a[:-1].isdigit() and a in values and a.endswith(\"k\"):\n            OPTIONS.compression_factor = str(int(a[:-1]) * 1024)\n        else:\n            raise ValueError(\"Please specify value from following options: 4k, 8k, 16k, 32k, 64k, 128k\", \"256k\")\n\n    elif o == \"--vabc_cow_version\":\n      if a.isdigit():\n        OPTIONS.vabc_cow_version = a\n      else:\n        raise ValueError(\"Cannot parse value %r for option %r - only \"\n                         \"integers are allowed.\" % (a, o))\n    elif o == \"--full_ota_partitions\":\n      OPTIONS.full_ota_partitions = set(\n          a.strip().strip(\"\\\"\").strip(\"'\").split(\",\"))\n    else:\n      return False\n    return True\n\n  args = common.ParseOptions(argv, __doc__,\n                             extra_opts=\"b:k:i:d:e:t:2o:\",\n                             extra_long_opts=[\n                                 \"incremental_from=\",\n                                 \"full_radio\",\n                                 \"full_bootloader\",\n                                 \"wipe_user_data\",\n                                 \"downgrade\",\n                                 \"override_timestamp\",\n                                 \"extra_script=\",\n                                 \"worker_threads=\",\n                                 \"two_step\",\n                                 \"include_secondary\",\n                                 \"no_signing\",\n                                 \"block\",\n                                 \"binary=\",\n                                 \"oem_settings=\",\n                                 \"oem_no_mount\",\n                                 \"verify\",\n                                 \"stash_threshold=\",\n                                 \"log_diff=\",\n                                 \"extracted_input_target_files=\",\n                                 \"skip_postinstall\",\n                                 \"retrofit_dynamic_partitions\",\n                                 \"skip_compatibility_check\",\n                                 \"output_metadata_path=\",\n                                 \"disable_fec_computation\",\n                                 \"disable_verity_computation\",\n                                 \"force_non_ab\",\n                                 \"boot_variable_file=\",\n                                 \"partial=\",\n                                 \"custom_image=\",\n                                 \"disable_vabc\",\n                                 \"spl_downgrade\",\n                                 \"vabc_downgrade\",\n                                 \"enable_vabc_xor=\",\n                                 \"force_minor_version=\",\n                                 \"compressor_types=\",\n                                 \"enable_zucchini=\",\n                                 \"enable_puffdiff=\",\n                                 \"enable_lz4diff=\",\n                                 \"vabc_compression_param=\",\n                                 \"security_patch_level=\",\n                                 \"max_threads=\",\n                                 \"vabc_cow_version=\",\n                                 \"compression_factor=\",\n                                 \"full_ota_partitions=\",\n                             ], extra_option_handler=[option_handler, payload_signer.signer_options])\n  common.InitLogging()\n\n  if len(args) != 2:\n    common.Usage(__doc__)\n    sys.exit(1)\n\n  # Load the build info dicts from the zip directly or the extracted input\n  # directory. We don't need to unzip the entire target-files zips, because they\n  # won't be needed for A/B OTAs (brillo_update_payload does that on its own).\n  # When loading the info dicts, we don't need to provide the second parameter\n  # to common.LoadInfoDict(). Specifying the second parameter allows replacing\n  # some properties with their actual paths, such as 'selinux_fc',\n  # 'ramdisk_dir', which won't be used during OTA generation.\n  if OPTIONS.extracted_input is not None:\n    OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)\n  else:\n    OPTIONS.info_dict = common.LoadInfoDict(args[0])\n\n  if OPTIONS.wipe_user_data:\n    if not OPTIONS.vabc_downgrade:\n      logger.info(\"Detected downgrade/datawipe OTA.\"\n                  \"When wiping userdata, VABC OTA makes the user \"\n                  \"wait in recovery mode for merge to finish. Disable VABC by \"\n                  \"default. If you really want to do VABC downgrade, pass \"\n                  \"--vabc_downgrade\")\n      OPTIONS.disable_vabc = True\n    # We should only allow downgrading incrementals (as opposed to full).\n    # Otherwise the device may go back from arbitrary build with this full\n    # OTA package.\n  if OPTIONS.incremental_source is None and OPTIONS.downgrade:\n    raise ValueError(\"Cannot generate downgradable full OTAs\")\n\n  # TODO(xunchang) for retrofit and partial updates, maybe we should rebuild the\n  # target-file and reload the info_dict. So the info will be consistent with\n  # the modified target-file.\n\n  logger.info(\"--- target info ---\")\n  common.DumpInfoDict(OPTIONS.info_dict)\n\n  # Load the source build dict if applicable.\n  if OPTIONS.incremental_source is not None:\n    OPTIONS.target_info_dict = OPTIONS.info_dict\n    OPTIONS.source_info_dict = ParseInfoDict(OPTIONS.incremental_source)\n\n    logger.info(\"--- source info ---\")\n    common.DumpInfoDict(OPTIONS.source_info_dict)\n\n  if OPTIONS.partial:\n    OPTIONS.info_dict['ab_partitions'] = \\\n        list(\n        set(OPTIONS.info_dict['ab_partitions']) & set(OPTIONS.partial)\n    )\n    if OPTIONS.source_info_dict:\n      OPTIONS.source_info_dict['ab_partitions'] = \\\n          list(\n          set(OPTIONS.source_info_dict['ab_partitions']) &\n          set(OPTIONS.partial)\n      )\n\n  # Load OEM dicts if provided.\n  OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)\n\n  # Assume retrofitting dynamic partitions when base build does not set\n  # use_dynamic_partitions but target build does.\n  if (OPTIONS.source_info_dict and\n      OPTIONS.source_info_dict.get(\"use_dynamic_partitions\") != \"true\" and\n          OPTIONS.target_info_dict.get(\"use_dynamic_partitions\") == \"true\"):\n    if OPTIONS.target_info_dict.get(\"dynamic_partition_retrofit\") != \"true\":\n      raise common.ExternalError(\n          \"Expect to generate incremental OTA for retrofitting dynamic \"\n          \"partitions, but dynamic_partition_retrofit is not set in target \"\n          \"build.\")\n    logger.info(\"Implicitly generating retrofit incremental OTA.\")\n    OPTIONS.retrofit_dynamic_partitions = True\n\n  # Skip postinstall for retrofitting dynamic partitions.\n  if OPTIONS.retrofit_dynamic_partitions:\n    OPTIONS.skip_postinstall = True\n\n  ab_update = OPTIONS.info_dict.get(\"ab_update\") == \"true\"\n  allow_non_ab = OPTIONS.info_dict.get(\"allow_non_ab\") == \"true\"\n  if OPTIONS.force_non_ab:\n    assert allow_non_ab,\\\n        \"--force_non_ab only allowed on devices that supports non-A/B\"\n    assert ab_update, \"--force_non_ab only allowed on A/B devices\"\n\n  generate_ab = not OPTIONS.force_non_ab and ab_update\n\n  # Use the default key to sign the package if not specified with package_key.\n  # package_keys are needed on ab_updates, so always define them if an\n  # A/B update is getting created.\n  if not OPTIONS.no_signing or generate_ab:\n    if OPTIONS.package_key is None:\n      OPTIONS.package_key = OPTIONS.info_dict.get(\n          \"default_system_dev_certificate\",\n          \"build/make/target/product/security/testkey\")\n    # Get signing keys\n    OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])\n\n    # Only check for existence of key file if using the default signer.\n    # Because the custom signer might not need the key file AT all.\n    # b/191704641\n    if not OPTIONS.payload_signer:\n      private_key_path = OPTIONS.package_key + OPTIONS.private_key_suffix\n      if not os.path.exists(private_key_path):\n        raise common.ExternalError(\n            \"Private key {} doesn't exist. Make sure you passed the\"\n            \" correct key path through -k option\".format(\n                private_key_path)\n        )\n      signapk_abs_path = os.path.join(\n          OPTIONS.search_path, OPTIONS.signapk_path)\n      if not os.path.exists(signapk_abs_path):\n        raise common.ExternalError(\n            \"Failed to find sign apk binary {} in search path {}. Make sure the correct search path is passed via -p\".format(OPTIONS.signapk_path, OPTIONS.search_path))\n\n  if OPTIONS.source_info_dict:\n    source_build_prop = OPTIONS.source_info_dict[\"build.prop\"]\n    target_build_prop = OPTIONS.target_info_dict[\"build.prop\"]\n    source_spl = source_build_prop.GetProp(SECURITY_PATCH_LEVEL_PROP_NAME)\n    target_spl = target_build_prop.GetProp(SECURITY_PATCH_LEVEL_PROP_NAME)\n    is_spl_downgrade = target_spl < source_spl\n    if is_spl_downgrade and target_build_prop.GetProp(\"ro.build.tags\") == \"release-keys\":\n      raise common.ExternalError(\n          \"Target security patch level {} is older than source SPL {} \"\n          \"A locked bootloader will reject SPL downgrade no matter \"\n          \"what(even if data wipe is done), so SPL downgrade on any \"\n          \"release-keys build is not allowed.\".format(target_spl, source_spl))\n\n    logger.info(\"SPL downgrade on %s\",\n                target_build_prop.GetProp(\"ro.build.tags\"))\n    if is_spl_downgrade and not OPTIONS.spl_downgrade and not OPTIONS.downgrade:\n      raise common.ExternalError(\n          \"Target security patch level {} is older than source SPL {} applying \"\n          \"such OTA will likely cause device fail to boot. Pass --spl_downgrade \"\n          \"to override this check. This script expects security patch level to \"\n          \"be in format yyyy-mm-dd (e.x. 2021-02-05). It's possible to use \"\n          \"separators other than -, so as long as it's used consistenly across \"\n          \"all SPL dates\".format(target_spl, source_spl))\n    elif not is_spl_downgrade and OPTIONS.spl_downgrade:\n      raise ValueError(\"--spl_downgrade specified but no actual SPL downgrade\"\n                       \" detected. Please only pass in this flag if you want a\"\n                       \" SPL downgrade. Target SPL: {} Source SPL: {}\"\n                       .format(target_spl, source_spl))\n  if generate_ab:\n    GenerateAbOtaPackage(\n        target_file=args[0],\n        output_file=args[1],\n        source_file=OPTIONS.incremental_source)\n\n  else:\n    GenerateNonAbOtaPackage(\n        target_file=args[0],\n        output_file=args[1],\n        source_file=OPTIONS.incremental_source)\n\n  # Post OTA generation works.\n  if OPTIONS.incremental_source is not None and OPTIONS.log_diff:\n    logger.info(\"Generating diff logs...\")\n    logger.info(\"Unzipping target-files for diffing...\")\n    target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)\n    source_dir = common.UnzipTemp(\n        OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)\n\n    with open(OPTIONS.log_diff, 'w') as out_file:\n      target_files_diff.recursiveDiff(\n          '', source_dir, target_dir, out_file)\n\n  logger.info(\"done.\")\n\n\nif __name__ == '__main__':\n  try:\n    common.CloseInheritedPipes()\n    main(sys.argv[1:])\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/ota_metadata.proto",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// If you change this file,\n// Please update ota_metadata_pb2.py by executing\n// protoc ota_metadata.proto --python_out\n// $ANDROID_BUILD_TOP/build/tools/releasetools\n\nsyntax = \"proto3\";\n\npackage build.tools.releasetools;\noption optimize_for = LITE_RUNTIME;\noption java_package = \"android.ota\";\noption java_outer_classname = \"OtaPackageMetadata\";\n\n// The build information of a particular partition on the device.\nmessage PartitionState {\n  string partition_name = 1;\n  repeated string device = 2;\n  repeated string build = 3;\n  // The version string of the partition. It's usually timestamp if present.\n  // One known exception is the boot image, who uses the kmi version, e.g.\n  // 5.4.42-android12-0\n  string version = 4;\n\n  // TODO(xunchang), revisit other necessary fields, e.g. security_patch_level.\n}\n\n// The build information on the device. The bytes of the running images are thus\n// inferred from the device state. For more information of the meaning of each\n// subfield, check\n// https://source.android.com/compatibility/android-cdd#3_2_2_build_parameters\nmessage DeviceState {\n  // device name. i.e. ro.product.device; if the field has multiple values, it\n  // means the ota package supports multiple devices. This usually happens when\n  // we use the same image to support multiple skus.\n  repeated string device = 1;\n  // device fingerprint. Up to R build, the value reads from\n  // ro.build.fingerprint.\n  repeated string build = 2;\n  // A value that specify a version of the android build.\n  string build_incremental = 3;\n  // The timestamp when the build is generated.\n  int64 timestamp = 4;\n  // The version of the currently-executing Android system.\n  string sdk_level = 5;\n  // A value indicating the security patch level of a build.\n  string security_patch_level = 6;\n\n  // The detailed state of each partition. For partial updates or devices with\n  // mixed build of partitions, some of the above fields may left empty. And the\n  // client will rely on the information of specific partitions to target the\n  // update.\n  repeated PartitionState partition_state = 7;\n}\n\nmessage ApexInfo {\n  string package_name = 1;\n  int64 version = 2;\n  bool is_compressed = 3;\n  int64 decompressed_size = 4;\n  // Used in OTA\n  int64 source_version = 5;\n}\n\n// Just a container to hold repeated apex_info, so that we can easily serialize\n// a list of apex_info to string.\nmessage ApexMetadata {\n  repeated ApexInfo apex_info = 1;\n}\n\n// The metadata of an OTA package. It contains the information of the package\n// and prerequisite to install the update correctly.\nmessage OtaMetadata {\n  enum OtaType {\n    UNKNOWN = 0;\n    AB = 1;\n    BLOCK = 2;\n    BRICK = 3;\n  };\n  OtaType type = 1;\n  // True if we need to wipe after the update.\n  bool wipe = 2;\n  // True if the timestamp of the post build is older than the pre build.\n  bool downgrade = 3;\n  // A map of name:content of property files, e.g. ota-property-files.\n  map<string, string> property_files = 4;\n\n  // The required device state in order to install the package.\n  DeviceState precondition = 5;\n  // The expected device state after the update.\n  DeviceState postcondition = 6;\n\n  // True if the ota that updates a device to support dynamic partitions, where\n  // the source build doesn't support it.\n  bool retrofit_dynamic_partitions = 7;\n  // The required size of the cache partition, only valid for non-A/B update.\n  int64 required_cache = 8;\n\n  // True iff security patch level downgrade is permitted on this OTA.\n  bool spl_downgrade = 9;\n}\n"
  },
  {
    "path": "tools/releasetools/ota_metadata_pb2.py",
    "content": "# -*- coding: utf-8 -*-\n# Generated by the protocol buffer compiler.  DO NOT EDIT!\n# source: ota_metadata.proto\n\nimport sys\n_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import message as _message\nfrom google.protobuf import reflection as _reflection\nfrom google.protobuf import symbol_database as _symbol_database\n# @@protoc_insertion_point(imports)\n\n_sym_db = _symbol_database.Default()\n\n\n\n\nDESCRIPTOR = _descriptor.FileDescriptor(\n  name='ota_metadata.proto',\n  package='build.tools.releasetools',\n  syntax='proto3',\n  serialized_options=_b('\\n\\013android.otaB\\022OtaPackageMetadataH\\003'),\n  serialized_pb=_b('\\n\\x12ota_metadata.proto\\x12\\x18\\x62uild.tools.releasetools\\\"X\\n\\x0ePartitionState\\x12\\x16\\n\\x0epartition_name\\x18\\x01 \\x01(\\t\\x12\\x0e\\n\\x06\\x64\\x65vice\\x18\\x02 \\x03(\\t\\x12\\r\\n\\x05\\x62uild\\x18\\x03 \\x03(\\t\\x12\\x0f\\n\\x07version\\x18\\x04 \\x01(\\t\\\"\\xce\\x01\\n\\x0b\\x44\\x65viceState\\x12\\x0e\\n\\x06\\x64\\x65vice\\x18\\x01 \\x03(\\t\\x12\\r\\n\\x05\\x62uild\\x18\\x02 \\x03(\\t\\x12\\x19\\n\\x11\\x62uild_incremental\\x18\\x03 \\x01(\\t\\x12\\x11\\n\\ttimestamp\\x18\\x04 \\x01(\\x03\\x12\\x11\\n\\tsdk_level\\x18\\x05 \\x01(\\t\\x12\\x1c\\n\\x14security_patch_level\\x18\\x06 \\x01(\\t\\x12\\x41\\n\\x0fpartition_state\\x18\\x07 \\x03(\\x0b\\x32(.build.tools.releasetools.PartitionState\\\"{\\n\\x08\\x41pexInfo\\x12\\x14\\n\\x0cpackage_name\\x18\\x01 \\x01(\\t\\x12\\x0f\\n\\x07version\\x18\\x02 \\x01(\\x03\\x12\\x15\\n\\ris_compressed\\x18\\x03 \\x01(\\x08\\x12\\x19\\n\\x11\\x64\\x65\\x63ompressed_size\\x18\\x04 \\x01(\\x03\\x12\\x16\\n\\x0esource_version\\x18\\x05 \\x01(\\x03\\\"E\\n\\x0c\\x41pexMetadata\\x12\\x35\\n\\tapex_info\\x18\\x01 \\x03(\\x0b\\x32\\\".build.tools.releasetools.ApexInfo\\\"\\xf8\\x03\\n\\x0bOtaMetadata\\x12;\\n\\x04type\\x18\\x01 \\x01(\\x0e\\x32-.build.tools.releasetools.OtaMetadata.OtaType\\x12\\x0c\\n\\x04wipe\\x18\\x02 \\x01(\\x08\\x12\\x11\\n\\tdowngrade\\x18\\x03 \\x01(\\x08\\x12P\\n\\x0eproperty_files\\x18\\x04 \\x03(\\x0b\\x32\\x38.build.tools.releasetools.OtaMetadata.PropertyFilesEntry\\x12;\\n\\x0cprecondition\\x18\\x05 \\x01(\\x0b\\x32%.build.tools.releasetools.DeviceState\\x12<\\n\\rpostcondition\\x18\\x06 \\x01(\\x0b\\x32%.build.tools.releasetools.DeviceState\\x12#\\n\\x1bretrofit_dynamic_partitions\\x18\\x07 \\x01(\\x08\\x12\\x16\\n\\x0erequired_cache\\x18\\x08 \\x01(\\x03\\x12\\x15\\n\\rspl_downgrade\\x18\\t \\x01(\\x08\\x1a\\x34\\n\\x12PropertyFilesEntry\\x12\\x0b\\n\\x03key\\x18\\x01 \\x01(\\t\\x12\\r\\n\\x05value\\x18\\x02 \\x01(\\t:\\x02\\x38\\x01\\\"4\\n\\x07OtaType\\x12\\x0b\\n\\x07UNKNOWN\\x10\\x00\\x12\\x06\\n\\x02\\x41\\x42\\x10\\x01\\x12\\t\\n\\x05\\x42LOCK\\x10\\x02\\x12\\t\\n\\x05\\x42RICK\\x10\\x03\\x42#\\n\\x0b\\x61ndroid.otaB\\x12OtaPackageMetadataH\\x03\\x62\\x06proto3')\n)\n\n\n\n_OTAMETADATA_OTATYPE = _descriptor.EnumDescriptor(\n  name='OtaType',\n  full_name='build.tools.releasetools.OtaMetadata.OtaType',\n  filename=None,\n  file=DESCRIPTOR,\n  values=[\n    _descriptor.EnumValueDescriptor(\n      name='UNKNOWN', index=0, number=0,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='AB', index=1, number=1,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='BLOCK', index=2, number=2,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='BRICK', index=3, number=3,\n      serialized_options=None,\n      type=None),\n  ],\n  containing_type=None,\n  serialized_options=None,\n  serialized_start=996,\n  serialized_end=1048,\n)\n_sym_db.RegisterEnumDescriptor(_OTAMETADATA_OTATYPE)\n\n\n_PARTITIONSTATE = _descriptor.Descriptor(\n  name='PartitionState',\n  full_name='build.tools.releasetools.PartitionState',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='partition_name', full_name='build.tools.releasetools.PartitionState.partition_name', index=0,\n      number=1, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='device', full_name='build.tools.releasetools.PartitionState.device', index=1,\n      number=2, type=9, cpp_type=9, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='build', full_name='build.tools.releasetools.PartitionState.build', index=2,\n      number=3, type=9, cpp_type=9, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='version', full_name='build.tools.releasetools.PartitionState.version', index=3,\n      number=4, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto3',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=48,\n  serialized_end=136,\n)\n\n\n_DEVICESTATE = _descriptor.Descriptor(\n  name='DeviceState',\n  full_name='build.tools.releasetools.DeviceState',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='device', full_name='build.tools.releasetools.DeviceState.device', index=0,\n      number=1, type=9, cpp_type=9, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='build', full_name='build.tools.releasetools.DeviceState.build', index=1,\n      number=2, type=9, cpp_type=9, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='build_incremental', full_name='build.tools.releasetools.DeviceState.build_incremental', index=2,\n      number=3, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='timestamp', full_name='build.tools.releasetools.DeviceState.timestamp', index=3,\n      number=4, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='sdk_level', full_name='build.tools.releasetools.DeviceState.sdk_level', index=4,\n      number=5, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='security_patch_level', full_name='build.tools.releasetools.DeviceState.security_patch_level', index=5,\n      number=6, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='partition_state', full_name='build.tools.releasetools.DeviceState.partition_state', index=6,\n      number=7, type=11, cpp_type=10, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto3',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=139,\n  serialized_end=345,\n)\n\n\n_APEXINFO = _descriptor.Descriptor(\n  name='ApexInfo',\n  full_name='build.tools.releasetools.ApexInfo',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='package_name', full_name='build.tools.releasetools.ApexInfo.package_name', index=0,\n      number=1, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='version', full_name='build.tools.releasetools.ApexInfo.version', index=1,\n      number=2, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='is_compressed', full_name='build.tools.releasetools.ApexInfo.is_compressed', index=2,\n      number=3, type=8, cpp_type=7, label=1,\n      has_default_value=False, default_value=False,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='decompressed_size', full_name='build.tools.releasetools.ApexInfo.decompressed_size', index=3,\n      number=4, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='source_version', full_name='build.tools.releasetools.ApexInfo.source_version', index=4,\n      number=5, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto3',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=347,\n  serialized_end=470,\n)\n\n\n_APEXMETADATA = _descriptor.Descriptor(\n  name='ApexMetadata',\n  full_name='build.tools.releasetools.ApexMetadata',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='apex_info', full_name='build.tools.releasetools.ApexMetadata.apex_info', index=0,\n      number=1, type=11, cpp_type=10, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto3',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=472,\n  serialized_end=541,\n)\n\n\n_OTAMETADATA_PROPERTYFILESENTRY = _descriptor.Descriptor(\n  name='PropertyFilesEntry',\n  full_name='build.tools.releasetools.OtaMetadata.PropertyFilesEntry',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='key', full_name='build.tools.releasetools.OtaMetadata.PropertyFilesEntry.key', index=0,\n      number=1, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='value', full_name='build.tools.releasetools.OtaMetadata.PropertyFilesEntry.value', index=1,\n      number=2, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=_b(\"\").decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=_b('8\\001'),\n  is_extendable=False,\n  syntax='proto3',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=942,\n  serialized_end=994,\n)\n\n_OTAMETADATA = _descriptor.Descriptor(\n  name='OtaMetadata',\n  full_name='build.tools.releasetools.OtaMetadata',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='type', full_name='build.tools.releasetools.OtaMetadata.type', index=0,\n      number=1, type=14, cpp_type=8, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='wipe', full_name='build.tools.releasetools.OtaMetadata.wipe', index=1,\n      number=2, type=8, cpp_type=7, label=1,\n      has_default_value=False, default_value=False,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='downgrade', full_name='build.tools.releasetools.OtaMetadata.downgrade', index=2,\n      number=3, type=8, cpp_type=7, label=1,\n      has_default_value=False, default_value=False,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='property_files', full_name='build.tools.releasetools.OtaMetadata.property_files', index=3,\n      number=4, type=11, cpp_type=10, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='precondition', full_name='build.tools.releasetools.OtaMetadata.precondition', index=4,\n      number=5, type=11, cpp_type=10, label=1,\n      has_default_value=False, default_value=None,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='postcondition', full_name='build.tools.releasetools.OtaMetadata.postcondition', index=5,\n      number=6, type=11, cpp_type=10, label=1,\n      has_default_value=False, default_value=None,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='retrofit_dynamic_partitions', full_name='build.tools.releasetools.OtaMetadata.retrofit_dynamic_partitions', index=6,\n      number=7, type=8, cpp_type=7, label=1,\n      has_default_value=False, default_value=False,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='required_cache', full_name='build.tools.releasetools.OtaMetadata.required_cache', index=7,\n      number=8, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='spl_downgrade', full_name='build.tools.releasetools.OtaMetadata.spl_downgrade', index=8,\n      number=9, type=8, cpp_type=7, label=1,\n      has_default_value=False, default_value=False,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[_OTAMETADATA_PROPERTYFILESENTRY, ],\n  enum_types=[\n    _OTAMETADATA_OTATYPE,\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto3',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=544,\n  serialized_end=1048,\n)\n\n_DEVICESTATE.fields_by_name['partition_state'].message_type = _PARTITIONSTATE\n_APEXMETADATA.fields_by_name['apex_info'].message_type = _APEXINFO\n_OTAMETADATA_PROPERTYFILESENTRY.containing_type = _OTAMETADATA\n_OTAMETADATA.fields_by_name['type'].enum_type = _OTAMETADATA_OTATYPE\n_OTAMETADATA.fields_by_name['property_files'].message_type = _OTAMETADATA_PROPERTYFILESENTRY\n_OTAMETADATA.fields_by_name['precondition'].message_type = _DEVICESTATE\n_OTAMETADATA.fields_by_name['postcondition'].message_type = _DEVICESTATE\n_OTAMETADATA_OTATYPE.containing_type = _OTAMETADATA\nDESCRIPTOR.message_types_by_name['PartitionState'] = _PARTITIONSTATE\nDESCRIPTOR.message_types_by_name['DeviceState'] = _DEVICESTATE\nDESCRIPTOR.message_types_by_name['ApexInfo'] = _APEXINFO\nDESCRIPTOR.message_types_by_name['ApexMetadata'] = _APEXMETADATA\nDESCRIPTOR.message_types_by_name['OtaMetadata'] = _OTAMETADATA\n_sym_db.RegisterFileDescriptor(DESCRIPTOR)\n\nPartitionState = _reflection.GeneratedProtocolMessageType('PartitionState', (_message.Message,), {\n  'DESCRIPTOR' : _PARTITIONSTATE,\n  '__module__' : 'ota_metadata_pb2'\n  # @@protoc_insertion_point(class_scope:build.tools.releasetools.PartitionState)\n  })\n_sym_db.RegisterMessage(PartitionState)\n\nDeviceState = _reflection.GeneratedProtocolMessageType('DeviceState', (_message.Message,), {\n  'DESCRIPTOR' : _DEVICESTATE,\n  '__module__' : 'ota_metadata_pb2'\n  # @@protoc_insertion_point(class_scope:build.tools.releasetools.DeviceState)\n  })\n_sym_db.RegisterMessage(DeviceState)\n\nApexInfo = _reflection.GeneratedProtocolMessageType('ApexInfo', (_message.Message,), {\n  'DESCRIPTOR' : _APEXINFO,\n  '__module__' : 'ota_metadata_pb2'\n  # @@protoc_insertion_point(class_scope:build.tools.releasetools.ApexInfo)\n  })\n_sym_db.RegisterMessage(ApexInfo)\n\nApexMetadata = _reflection.GeneratedProtocolMessageType('ApexMetadata', (_message.Message,), {\n  'DESCRIPTOR' : _APEXMETADATA,\n  '__module__' : 'ota_metadata_pb2'\n  # @@protoc_insertion_point(class_scope:build.tools.releasetools.ApexMetadata)\n  })\n_sym_db.RegisterMessage(ApexMetadata)\n\nOtaMetadata = _reflection.GeneratedProtocolMessageType('OtaMetadata', (_message.Message,), {\n\n  'PropertyFilesEntry' : _reflection.GeneratedProtocolMessageType('PropertyFilesEntry', (_message.Message,), {\n    'DESCRIPTOR' : _OTAMETADATA_PROPERTYFILESENTRY,\n    '__module__' : 'ota_metadata_pb2'\n    # @@protoc_insertion_point(class_scope:build.tools.releasetools.OtaMetadata.PropertyFilesEntry)\n    })\n  ,\n  'DESCRIPTOR' : _OTAMETADATA,\n  '__module__' : 'ota_metadata_pb2'\n  # @@protoc_insertion_point(class_scope:build.tools.releasetools.OtaMetadata)\n  })\n_sym_db.RegisterMessage(OtaMetadata)\n_sym_db.RegisterMessage(OtaMetadata.PropertyFilesEntry)\n\n\nDESCRIPTOR._options = None\n_OTAMETADATA_PROPERTYFILESENTRY._options = None\n# @@protoc_insertion_point(module_scope)\n"
  },
  {
    "path": "tools/releasetools/ota_package_parser.py",
    "content": "#!/usr/bin/env python\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport logging\nimport sys\nimport traceback\nimport zipfile\n\nfrom rangelib import RangeSet\n\nclass Stash(object):\n  \"\"\"Build a map to track stashed blocks during update simulation.\"\"\"\n\n  def __init__(self):\n    self.blocks_stashed = 0\n    self.overlap_blocks_stashed = 0\n    self.max_stash_needed = 0\n    self.current_stash_size = 0\n    self.stash_map = {}\n\n  def StashBlocks(self, SHA1, blocks):\n    if SHA1 in self.stash_map:\n      logging.info(\"already stashed {}: {}\".format(SHA1, blocks))\n      return\n    self.blocks_stashed += blocks.size()\n    self.current_stash_size += blocks.size()\n    self.max_stash_needed = max(self.current_stash_size, self.max_stash_needed)\n    self.stash_map[SHA1] = blocks\n\n  def FreeBlocks(self, SHA1):\n    assert self.stash_map.has_key(SHA1), \"stash {} not found\".format(SHA1)\n    self.current_stash_size -= self.stash_map[SHA1].size()\n    del self.stash_map[SHA1]\n\n  def HandleOverlapBlocks(self, SHA1, blocks):\n    self.StashBlocks(SHA1, blocks)\n    self.overlap_blocks_stashed += blocks.size()\n    self.FreeBlocks(SHA1)\n\n\nclass OtaPackageParser(object):\n  \"\"\"Parse a block-based OTA package.\"\"\"\n\n  def __init__(self, package):\n    self.package = package\n    self.new_data_size = 0\n    self.patch_data_size = 0\n    self.block_written = 0\n    self.block_stashed = 0\n\n  @staticmethod\n  def GetSizeString(size):\n    assert size >= 0\n    base = 1024.0\n    if size <= base:\n      return \"{} bytes\".format(size)\n    for units in ['K', 'M', 'G']:\n      if size <= base * 1024 or units == 'G':\n        return \"{:.1f}{}\".format(size / base, units)\n      base *= 1024\n\n  def ParseTransferList(self, name):\n    \"\"\"Simulate the transfer commands and calculate the amout of I/O.\"\"\"\n\n    logging.info(\"\\nSimulating commands in '{}':\".format(name))\n    lines = self.package.read(name).strip().splitlines()\n    assert len(lines) >= 4, \"{} is too short; Transfer list expects at least\" \\\n        \"4 lines, it has {}\".format(name, len(lines))\n    assert int(lines[0]) >= 3\n    logging.info(\"(version: {})\".format(lines[0]))\n\n    blocks_written = 0\n    my_stash = Stash()\n    for line in lines[4:]:\n      cmd_list = line.strip().split(\" \")\n      cmd_name = cmd_list[0]\n      try:\n        if cmd_name == \"new\" or cmd_name == \"zero\":\n          assert len(cmd_list) == 2, \"command format error: {}\".format(line)\n          target_range = RangeSet.parse_raw(cmd_list[1])\n          blocks_written += target_range.size()\n        elif cmd_name == \"move\":\n          # Example:  move <onehash> <tgt_range> <src_blk_count> <src_range>\n          # [<loc_range> <stashed_blocks>]\n          assert len(cmd_list) >= 5, \"command format error: {}\".format(line)\n          target_range = RangeSet.parse_raw(cmd_list[2])\n          blocks_written += target_range.size()\n          if cmd_list[4] == '-':\n            continue\n          SHA1 = cmd_list[1]\n          source_range = RangeSet.parse_raw(cmd_list[4])\n          if target_range.overlaps(source_range):\n            my_stash.HandleOverlapBlocks(SHA1, source_range)\n        elif cmd_name == \"bsdiff\" or cmd_name == \"imgdiff\":\n          # Example:  bsdiff <offset> <len> <src_hash> <tgt_hash> <tgt_range>\n          # <src_blk_count> <src_range> [<loc_range> <stashed_blocks>]\n          assert len(cmd_list) >= 8, \"command format error: {}\".format(line)\n          target_range = RangeSet.parse_raw(cmd_list[5])\n          blocks_written += target_range.size()\n          if cmd_list[7] == '-':\n            continue\n          source_SHA1 = cmd_list[3]\n          source_range = RangeSet.parse_raw(cmd_list[7])\n          if target_range.overlaps(source_range):\n            my_stash.HandleOverlapBlocks(source_SHA1, source_range)\n        elif cmd_name == \"stash\":\n          assert len(cmd_list) == 3, \"command format error: {}\".format(line)\n          SHA1 = cmd_list[1]\n          source_range = RangeSet.parse_raw(cmd_list[2])\n          my_stash.StashBlocks(SHA1, source_range)\n        elif cmd_name == \"free\":\n          assert len(cmd_list) == 2, \"command format error: {}\".format(line)\n          SHA1 = cmd_list[1]\n          my_stash.FreeBlocks(SHA1)\n      except:\n        logging.error(\"failed to parse command in: \" + line)\n        raise\n\n    self.block_written += blocks_written\n    self.block_stashed += my_stash.blocks_stashed\n\n    logging.info(\"blocks written: {}  (expected: {})\".format(\n        blocks_written, lines[1]))\n    logging.info(\"max blocks stashed simultaneously: {}  (expected: {})\".\n        format(my_stash.max_stash_needed, lines[3]))\n    logging.info(\"total blocks stashed: {}\".format(my_stash.blocks_stashed))\n    logging.info(\"blocks stashed implicitly: {}\".format(\n        my_stash.overlap_blocks_stashed))\n\n  def PrintDataInfo(self, partition):\n    logging.info(\"\\nReading data info for {} partition:\".format(partition))\n    new_data = self.package.getinfo(partition + \".new.dat\")\n    patch_data = self.package.getinfo(partition + \".patch.dat\")\n    logging.info(\"{:<40}{:<40}\".format(new_data.filename, patch_data.filename))\n    logging.info(\"{:<40}{:<40}\".format(\n          \"compress_type: \" + str(new_data.compress_type),\n          \"compress_type: \" + str(patch_data.compress_type)))\n    logging.info(\"{:<40}{:<40}\".format(\n          \"compressed_size: \" + OtaPackageParser.GetSizeString(\n              new_data.compress_size),\n          \"compressed_size: \" + OtaPackageParser.GetSizeString(\n              patch_data.compress_size)))\n    logging.info(\"{:<40}{:<40}\".format(\n        \"file_size: \" + OtaPackageParser.GetSizeString(new_data.file_size),\n        \"file_size: \" + OtaPackageParser.GetSizeString(patch_data.file_size)))\n\n    self.new_data_size += new_data.file_size\n    self.patch_data_size += patch_data.file_size\n\n  def AnalyzePartition(self, partition):\n    assert partition in (\"system\", \"vendor\")\n    assert partition + \".new.dat\" in self.package.namelist()\n    assert partition + \".patch.dat\" in self.package.namelist()\n    assert partition + \".transfer.list\" in self.package.namelist()\n\n    self.PrintDataInfo(partition)\n    self.ParseTransferList(partition + \".transfer.list\")\n\n  def PrintMetadata(self):\n    metadata_path = \"META-INF/com/android/metadata\"\n    logging.info(\"\\nMetadata info:\")\n    metadata_info = {}\n    for line in self.package.read(metadata_path).strip().splitlines():\n      index = line.find(\"=\")\n      metadata_info[line[0 : index].strip()] = line[index + 1:].strip()\n    assert metadata_info.get(\"ota-type\") == \"BLOCK\"\n    assert \"pre-device\" in metadata_info\n    logging.info(\"device: {}\".format(metadata_info[\"pre-device\"]))\n    if \"pre-build\" in metadata_info:\n      logging.info(\"pre-build: {}\".format(metadata_info[\"pre-build\"]))\n    assert \"post-build\" in metadata_info\n    logging.info(\"post-build: {}\".format(metadata_info[\"post-build\"]))\n\n  def Analyze(self):\n    logging.info(\"Analyzing ota package: \" + self.package.filename)\n    self.PrintMetadata()\n    assert \"system.new.dat\" in self.package.namelist()\n    self.AnalyzePartition(\"system\")\n    if \"vendor.new.dat\" in self.package.namelist():\n      self.AnalyzePartition(\"vendor\")\n\n    #TODO Add analysis of other partitions(e.g. bootloader, boot, radio)\n\n    BLOCK_SIZE = 4096\n    logging.info(\"\\nOTA package analyzed:\")\n    logging.info(\"new data size (uncompressed): \" +\n        OtaPackageParser.GetSizeString(self.new_data_size))\n    logging.info(\"patch data size (uncompressed): \" +\n        OtaPackageParser.GetSizeString(self.patch_data_size))\n    logging.info(\"total data written: \" +\n        OtaPackageParser.GetSizeString(self.block_written * BLOCK_SIZE))\n    logging.info(\"total data stashed: \" +\n        OtaPackageParser.GetSizeString(self.block_stashed * BLOCK_SIZE))\n\n\ndef main(argv):\n  parser = argparse.ArgumentParser(description='Analyze an OTA package.')\n  parser.add_argument(\"ota_package\", help='Path of the OTA package.')\n  args = parser.parse_args(argv)\n\n  logging_format = '%(message)s'\n  logging.basicConfig(level=logging.INFO, format=logging_format)\n\n  try:\n    with zipfile.ZipFile(args.ota_package, 'r', allowZip64=True) as package:\n      package_parser = OtaPackageParser(package)\n      package_parser.Analyze()\n  except:\n    logging.error(\"Failed to read \" + args.ota_package)\n    traceback.print_exc()\n    sys.exit(1)\n\n\nif __name__ == '__main__':\n  main(sys.argv[1:])\n"
  },
  {
    "path": "tools/releasetools/ota_signing_utils.py",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport shlex\n\n\ndef ParseSignerArgs(args):\n  if args is None:\n    return None\n  return shlex.split(args)\n\n\ndef AddSigningArgumentParse(parser: argparse.ArgumentParser):\n  parser.add_argument('--java_path', type=str,\n                      help='Path to JVM if other than default')\n  parser.add_argument('--package_key', type=str,\n                      help='Paths to private key for signing payload')\n  parser.add_argument('--search_path', '--path', type=str,\n                      help='Search path for framework/signapk.jar')\n  parser.add_argument('--signapk_path', type=str,\n                      help='Path to signapk.jar, relative to search_path')\n  parser.add_argument('--extra_signapk_args', type=ParseSignerArgs,\n                      help='Extra arguments for signapk.jar')\n  parser.add_argument('--signapk_shared_library_path', type=str,\n                      help='Path to lib64 libraries used by signapk.jar')\n  parser.add_argument('--payload_signer', type=str,\n                      help='Path to custom payload signer')\n  parser.add_argument('--payload_signer_args', type=ParseSignerArgs,\n                      help='Arguments for payload signer if necessary')\n  parser.add_argument('--payload_signer_maximum_signature_size', type=str,\n                      help='Maximum signature size (in bytes) that would be '\n                      'generated by the given payload signer')\n  parser.add_argument('--private_key_suffix', type=str,\n                      help='Suffix to be appended to package_key path', default=\".pk8\")\n"
  },
  {
    "path": "tools/releasetools/ota_utils.py",
    "content": "# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport copy\nimport itertools\nimport logging\nimport os\nimport shutil\nimport struct\nimport zipfile\n\nimport ota_metadata_pb2\nimport common\nimport fnmatch\nfrom common import (ZipDelete, DoesInputFileContain, ReadBytesFromInputFile, OPTIONS, MakeTempFile,\n                    ZipWriteStr, BuildInfo, LoadDictionaryFromFile,\n                    SignFile, PARTITIONS_WITH_BUILD_PROP, PartitionBuildProps,\n                    GetRamdiskFormat, ParseUpdateEngineConfig)\nimport payload_signer\nfrom payload_signer import PayloadSigner, AddSigningArgumentParse, GeneratePayloadProperties\n\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS.no_signing = False\nOPTIONS.force_non_ab = False\nOPTIONS.wipe_user_data = False\nOPTIONS.downgrade = False\nOPTIONS.key_passwords = {}\nOPTIONS.incremental_source = None\nOPTIONS.retrofit_dynamic_partitions = False\nOPTIONS.output_metadata_path = None\nOPTIONS.boot_variable_file = None\n\nMETADATA_NAME = 'META-INF/com/android/metadata'\nMETADATA_PROTO_NAME = 'META-INF/com/android/metadata.pb'\nUNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*',\n                 'RADIO/*', '*/build.prop', '*/default.prop', '*/build.default', \"*/etc/vintf/*\"]\nSECURITY_PATCH_LEVEL_PROP_NAME = \"ro.build.version.security_patch\"\nTARGET_FILES_IMAGES_SUBDIR = [\"IMAGES\", \"PREBUILT_IMAGES\", \"RADIO\"]\n\n\n# Key is the compression algorithm, value is minimum API level required to\n# use this compression algorithm for VABC OTA on device.\nVABC_COMPRESSION_PARAM_SUPPORT = {\n    \"gz\": 31,\n    \"brotli\": 31,\n    \"none\": 31,\n    # lz4 support is added in Android U\n    \"lz4\": 34,\n    # zstd support is added in Android V\n    \"zstd\": 35,\n}\n\n\ndef FinalizeMetadata(metadata, input_file, output_file, needed_property_files=None, package_key=None, pw=None):\n  \"\"\"Finalizes the metadata and signs an A/B OTA package.\n\n  In order to stream an A/B OTA package, we need 'ota-streaming-property-files'\n  that contains the offsets and sizes for the ZIP entries. An example\n  property-files string is as follows.\n\n    \"payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379\"\n\n  OTA server can pass down this string, in addition to the package URL, to the\n  system update client. System update client can then fetch individual ZIP\n  entries (ZIP_STORED) directly at the given offset of the URL.\n\n  Args:\n    metadata: The metadata dict for the package.\n    input_file: The input ZIP filename that doesn't contain the package METADATA\n        entry yet.\n    output_file: The final output ZIP filename.\n    needed_property_files: The list of PropertyFiles' to be generated. Default is [AbOtaPropertyFiles(), StreamingPropertyFiles()]\n    package_key: The key used to sign this OTA package\n    pw: Password for the package_key\n  \"\"\"\n  no_signing = package_key is None\n\n  if needed_property_files is None:\n    # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers\n    # all the info of the latter. However, system updaters and OTA servers need to\n    # take time to switch to the new flag. We keep both of the flags for\n    # P-timeframe, and will remove StreamingPropertyFiles in later release.\n    needed_property_files = (\n        AbOtaPropertyFiles(),\n        StreamingPropertyFiles(),\n    )\n\n  def ComputeAllPropertyFiles(input_file, needed_property_files):\n    # Write the current metadata entry with placeholders.\n    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:\n      for property_files in needed_property_files:\n        metadata.property_files[property_files.name] = property_files.Compute(\n            input_zip)\n\n    ZipDelete(input_file, [METADATA_NAME, METADATA_PROTO_NAME], True)\n    with zipfile.ZipFile(input_file, 'a', allowZip64=True) as output_zip:\n      WriteMetadata(metadata, output_zip)\n\n    if no_signing:\n      return input_file\n\n    prelim_signing = MakeTempFile(suffix='.zip')\n    SignOutput(input_file, prelim_signing, package_key, pw)\n    return prelim_signing\n\n  def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):\n    with zipfile.ZipFile(prelim_signing, 'r', allowZip64=True) as prelim_signing_zip:\n      for property_files in needed_property_files:\n        metadata.property_files[property_files.name] = property_files.Finalize(\n            prelim_signing_zip,\n            len(metadata.property_files[property_files.name]))\n\n  # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP\n  # entries, as well as padding the entry headers. We do a preliminary signing\n  # (with an incomplete metadata entry) to allow that to happen. Then compute\n  # the ZIP entry offsets, write back the final metadata and do the final\n  # signing.\n  prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)\n  try:\n    FinalizeAllPropertyFiles(prelim_signing, needed_property_files)\n  except PropertyFiles.InsufficientSpaceException:\n    # Even with the preliminary signing, the entry orders may change\n    # dramatically, which leads to insufficiently reserved space during the\n    # first call to ComputeAllPropertyFiles(). In that case, we redo all the\n    # preliminary signing works, based on the already ordered ZIP entries, to\n    # address the issue.\n    prelim_signing = ComputeAllPropertyFiles(\n        prelim_signing, needed_property_files)\n    FinalizeAllPropertyFiles(prelim_signing, needed_property_files)\n\n  # Replace the METADATA entry.\n  ZipDelete(prelim_signing, [METADATA_NAME, METADATA_PROTO_NAME])\n  with zipfile.ZipFile(prelim_signing, 'a', allowZip64=True) as output_zip:\n    WriteMetadata(metadata, output_zip)\n\n  # Re-sign the package after updating the metadata entry.\n  if no_signing:\n    logger.info(f\"Signing disabled for output file {output_file}\")\n    shutil.copy(prelim_signing, output_file)\n  else:\n    logger.info(\n        f\"Signing the output file {output_file} with key {package_key}\")\n    SignOutput(prelim_signing, output_file, package_key, pw)\n\n  # Reopen the final signed zip to double check the streaming metadata.\n  with zipfile.ZipFile(output_file, allowZip64=True) as output_zip:\n    for property_files in needed_property_files:\n      property_files.Verify(\n          output_zip, metadata.property_files[property_files.name].strip())\n\n  # If requested, dump the metadata to a separate file.\n  output_metadata_path = OPTIONS.output_metadata_path\n  if output_metadata_path:\n    WriteMetadata(metadata, output_metadata_path)\n\n\ndef WriteMetadata(metadata_proto, output):\n  \"\"\"Writes the metadata to the zip archive or a file.\n\n  Args:\n    metadata_proto: The metadata protobuf for the package.\n    output: A ZipFile object or a string of the output file path. If a string\n      path is given, the metadata in the protobuf format will be written to\n      {output}.pb, e.g. ota_metadata.pb\n  \"\"\"\n\n  metadata_dict = BuildLegacyOtaMetadata(metadata_proto)\n  legacy_metadata = \"\".join([\"%s=%s\\n\" % kv for kv in\n                             sorted(metadata_dict.items())])\n  if isinstance(output, zipfile.ZipFile):\n    ZipWriteStr(output, METADATA_PROTO_NAME, metadata_proto.SerializeToString(),\n                compress_type=zipfile.ZIP_STORED)\n    ZipWriteStr(output, METADATA_NAME, legacy_metadata,\n                compress_type=zipfile.ZIP_STORED)\n    return\n\n  with open('{}.pb'.format(output), 'wb') as f:\n    f.write(metadata_proto.SerializeToString())\n  with open(output, 'w') as f:\n    f.write(legacy_metadata)\n\n\ndef UpdateDeviceState(device_state, build_info, boot_variable_values,\n                      is_post_build):\n  \"\"\"Update the fields of the DeviceState proto with build info.\"\"\"\n\n  def UpdatePartitionStates(partition_states):\n    \"\"\"Update the per-partition state according to its build.prop\"\"\"\n    if not build_info.is_ab:\n      return\n    build_info_set = ComputeRuntimeBuildInfos(build_info,\n                                              boot_variable_values)\n    assert \"ab_partitions\" in build_info.info_dict,\\\n        \"ab_partitions property required for ab update.\"\n    ab_partitions = set(build_info.info_dict.get(\"ab_partitions\"))\n\n    # delta_generator will error out on unused timestamps,\n    # so only generate timestamps for dynamic partitions\n    # used in OTA update.\n    for partition in sorted(set(PARTITIONS_WITH_BUILD_PROP) & ab_partitions):\n      partition_prop = build_info.info_dict.get(\n          '{}.build.prop'.format(partition))\n      # Skip if the partition is missing, or it doesn't have a build.prop\n      if not partition_prop or not partition_prop.build_props:\n        continue\n\n      partition_state = partition_states.add()\n      partition_state.partition_name = partition\n      # Update the partition's runtime device names and fingerprints\n      partition_devices = set()\n      partition_fingerprints = set()\n      for runtime_build_info in build_info_set:\n        partition_devices.add(\n            runtime_build_info.GetPartitionBuildProp('ro.product.device',\n                                                     partition))\n        partition_fingerprints.add(\n            runtime_build_info.GetPartitionFingerprint(partition))\n\n      partition_state.device.extend(sorted(partition_devices))\n      partition_state.build.extend(sorted(partition_fingerprints))\n\n      # TODO(xunchang) set the boot image's version with kmi. Note the boot\n      # image doesn't have a file map.\n      partition_state.version = build_info.GetPartitionBuildProp(\n          'ro.build.date.utc', partition)\n\n  # TODO(xunchang), we can save a call to ComputeRuntimeBuildInfos.\n  build_devices, build_fingerprints = \\\n      CalculateRuntimeDevicesAndFingerprints(build_info, boot_variable_values)\n  device_state.device.extend(sorted(build_devices))\n  device_state.build.extend(sorted(build_fingerprints))\n  device_state.build_incremental = build_info.GetBuildProp(\n      'ro.build.version.incremental')\n\n  UpdatePartitionStates(device_state.partition_state)\n\n  if is_post_build:\n    device_state.sdk_level = build_info.GetBuildProp(\n        'ro.build.version.sdk')\n    device_state.security_patch_level = build_info.GetBuildProp(\n        'ro.build.version.security_patch')\n    # Use the actual post-timestamp, even for a downgrade case.\n    device_state.timestamp = int(build_info.GetBuildProp('ro.build.date.utc'))\n\n\ndef GetPackageMetadata(target_info, source_info=None):\n  \"\"\"Generates and returns the metadata proto.\n\n  It generates a ota_metadata protobuf that contains the info to be written\n  into an OTA package (META-INF/com/android/metadata.pb). It also handles the\n  detection of downgrade / data wipe based on the global options.\n\n  Args:\n    target_info: The BuildInfo instance that holds the target build info.\n    source_info: The BuildInfo instance that holds the source build info, or\n        None if generating full OTA.\n\n  Returns:\n    A protobuf to be written into package metadata entry.\n  \"\"\"\n  assert isinstance(target_info, BuildInfo)\n  assert source_info is None or isinstance(source_info, BuildInfo)\n\n  boot_variable_values = {}\n  if OPTIONS.boot_variable_file:\n    d = LoadDictionaryFromFile(OPTIONS.boot_variable_file)\n    for key, values in d.items():\n      boot_variable_values[key] = [val.strip() for val in values.split(',')]\n\n  metadata_proto = ota_metadata_pb2.OtaMetadata()\n  # TODO(xunchang) some fields, e.g. post-device isn't necessary. We can\n  # consider skipping them if they aren't used by clients.\n  UpdateDeviceState(metadata_proto.postcondition, target_info,\n                    boot_variable_values, True)\n\n  if target_info.is_ab and not OPTIONS.force_non_ab:\n    metadata_proto.type = ota_metadata_pb2.OtaMetadata.AB\n    metadata_proto.required_cache = 0\n  else:\n    metadata_proto.type = ota_metadata_pb2.OtaMetadata.BLOCK\n    # cache requirement will be updated by the non-A/B codes.\n\n  if OPTIONS.wipe_user_data:\n    metadata_proto.wipe = True\n\n  if OPTIONS.retrofit_dynamic_partitions:\n    metadata_proto.retrofit_dynamic_partitions = True\n\n  is_incremental = source_info is not None\n  if is_incremental:\n    UpdateDeviceState(metadata_proto.precondition, source_info,\n                      boot_variable_values, False)\n  else:\n    metadata_proto.precondition.device.extend(\n        metadata_proto.postcondition.device)\n\n  # Detect downgrades and set up downgrade flags accordingly.\n  if is_incremental:\n    HandleDowngradeMetadata(metadata_proto, target_info, source_info)\n\n  return metadata_proto\n\n\ndef BuildLegacyOtaMetadata(metadata_proto):\n  \"\"\"Converts the metadata proto to a legacy metadata dict.\n\n  This metadata dict is used to build the legacy metadata text file for\n  backward compatibility. We won't add new keys to the legacy metadata format.\n  If new information is needed, we should add it as a new field in OtaMetadata\n  proto definition.\n  \"\"\"\n\n  separator = '|'\n\n  metadata_dict = {}\n  if metadata_proto.type == ota_metadata_pb2.OtaMetadata.AB:\n    metadata_dict['ota-type'] = 'AB'\n  elif metadata_proto.type == ota_metadata_pb2.OtaMetadata.BLOCK:\n    metadata_dict['ota-type'] = 'BLOCK'\n  if metadata_proto.wipe:\n    metadata_dict['ota-wipe'] = 'yes'\n  if metadata_proto.retrofit_dynamic_partitions:\n    metadata_dict['ota-retrofit-dynamic-partitions'] = 'yes'\n  if metadata_proto.downgrade:\n    metadata_dict['ota-downgrade'] = 'yes'\n\n  metadata_dict['ota-required-cache'] = str(metadata_proto.required_cache)\n\n  post_build = metadata_proto.postcondition\n  metadata_dict['post-build'] = separator.join(post_build.build)\n  metadata_dict['post-build-incremental'] = post_build.build_incremental\n  metadata_dict['post-sdk-level'] = post_build.sdk_level\n  metadata_dict['post-security-patch-level'] = post_build.security_patch_level\n  metadata_dict['post-timestamp'] = str(post_build.timestamp)\n\n  pre_build = metadata_proto.precondition\n  metadata_dict['pre-device'] = separator.join(pre_build.device)\n  # incremental updates\n  if len(pre_build.build) != 0:\n    metadata_dict['pre-build'] = separator.join(pre_build.build)\n    metadata_dict['pre-build-incremental'] = pre_build.build_incremental\n\n  if metadata_proto.spl_downgrade:\n    metadata_dict['spl-downgrade'] = 'yes'\n  metadata_dict.update(metadata_proto.property_files)\n\n  return metadata_dict\n\n\ndef HandleDowngradeMetadata(metadata_proto, target_info, source_info):\n  # Only incremental OTAs are allowed to reach here.\n  assert OPTIONS.incremental_source is not None\n\n  # used for logging upon errors\n  log_downgrades = []\n  log_upgrades = []\n\n  post_timestamp = target_info.GetBuildProp(\"ro.build.date.utc\")\n  pre_timestamp = source_info.GetBuildProp(\"ro.build.date.utc\")\n  if int(post_timestamp) < int(pre_timestamp):\n    logger.info(f\"ro.build.date.utc pre timestamp: {pre_timestamp}, \"\n                f\"post timestamp: {post_timestamp}. Downgrade detected.\")\n    log_downgrades.append(f\"ro.build.date.utc pre: {pre_timestamp} post: {post_timestamp}\")\n  else:\n    logger.info(f\"ro.build.date.utc pre timestamp: {pre_timestamp}, \"\n                f\"post timestamp: {post_timestamp}.\")\n    log_upgrades.append(f\"ro.build.date.utc pre: {pre_timestamp} post: {post_timestamp}\")\n\n  # When merging system and vendor target files, it is not enough\n  # to check ro.build.date.utc, the timestamp for each partition must\n  # be checked.\n  if source_info.is_ab:\n    ab_partitions = set(source_info.get(\"ab_partitions\"))\n    for partition in sorted(set(PARTITIONS_WITH_BUILD_PROP) & ab_partitions):\n\n      partition_prop = source_info.get('{}.build.prop'.format(partition))\n      # Skip if the partition is missing, or it doesn't have a build.prop\n      if not partition_prop or not partition_prop.build_props:\n        continue\n      partition_prop = target_info.get('{}.build.prop'.format(partition))\n      # Skip if the partition is missing, or it doesn't have a build.prop\n      if not partition_prop or not partition_prop.build_props:\n        continue\n\n      post_timestamp = target_info.GetPartitionBuildProp(\n        'ro.build.date.utc', partition)\n      pre_timestamp = source_info.GetPartitionBuildProp(\n        'ro.build.date.utc', partition)\n      if int(post_timestamp) < int(pre_timestamp):\n        logger.info(f\"Partition {partition} pre timestamp: {pre_timestamp}, \"\n                    f\"post time: {post_timestamp}. Downgrade detected.\")\n        log_downgrades.append(f\"{partition} pre: {pre_timestamp} post: {post_timestamp}\")\n      else:\n        logger.info(f\"Partition {partition} pre timestamp: {pre_timestamp}, \"\n                    f\"post timestamp: {post_timestamp}.\")\n        log_upgrades.append(f\"{partition} pre: {pre_timestamp} post: {post_timestamp}\")\n\n  if OPTIONS.spl_downgrade:\n    metadata_proto.spl_downgrade = True\n\n  if OPTIONS.downgrade:\n    if len(log_downgrades) == 0:\n      raise RuntimeError(\n          \"--downgrade or --override_timestamp specified but no downgrade \"\n          \"detected. Current values for ro.build.date.utc: \" + ', '.join(log_upgrades))\n    metadata_proto.downgrade = True\n  else:\n    if len(log_downgrades) != 0:\n      raise RuntimeError(\n          \"Downgrade detected based on timestamp check in ro.build.date.utc. \"\n          \"Need to specify --override_timestamp OR --downgrade to allow \"\n          \"building the incremental. Downgrades detected for: \"\n          + ', '.join(log_downgrades))\n\ndef ComputeRuntimeBuildInfos(default_build_info, boot_variable_values):\n  \"\"\"Returns a set of build info objects that may exist during runtime.\"\"\"\n\n  build_info_set = {default_build_info}\n  if not boot_variable_values:\n    return build_info_set\n\n  # Calculate all possible combinations of the values for the boot variables.\n  keys = boot_variable_values.keys()\n  value_list = boot_variable_values.values()\n  combinations = [dict(zip(keys, values))\n                  for values in itertools.product(*value_list)]\n  for placeholder_values in combinations:\n    # Reload the info_dict as some build properties may change their values\n    # based on the value of ro.boot* properties.\n    info_dict = copy.deepcopy(default_build_info.info_dict)\n    for partition in PARTITIONS_WITH_BUILD_PROP:\n      partition_prop_key = \"{}.build.prop\".format(partition)\n      input_file = info_dict[partition_prop_key].input_file\n      ramdisk = GetRamdiskFormat(info_dict)\n      if isinstance(input_file, zipfile.ZipFile):\n        with zipfile.ZipFile(input_file.filename, allowZip64=True) as input_zip:\n          info_dict[partition_prop_key] = \\\n              PartitionBuildProps.FromInputFile(input_zip, partition,\n                                                placeholder_values,\n                                                ramdisk)\n      else:\n        info_dict[partition_prop_key] = \\\n            PartitionBuildProps.FromInputFile(input_file, partition,\n                                              placeholder_values,\n                                              ramdisk)\n    info_dict[\"build.prop\"] = info_dict[\"system.build.prop\"]\n    build_info_set.add(BuildInfo(info_dict, default_build_info.oem_dicts))\n\n  return build_info_set\n\n\ndef CalculateRuntimeDevicesAndFingerprints(default_build_info,\n                                           boot_variable_values):\n  \"\"\"Returns a tuple of sets for runtime devices and fingerprints\"\"\"\n\n  device_names = set()\n  fingerprints = set()\n  build_info_set = ComputeRuntimeBuildInfos(default_build_info,\n                                            boot_variable_values)\n  for runtime_build_info in build_info_set:\n    device_names.add(runtime_build_info.device)\n    fingerprints.add(runtime_build_info.fingerprint)\n  return device_names, fingerprints\n\n\ndef GetZipEntryOffset(zfp, entry_info):\n  \"\"\"Get offset to a beginning of a particular zip entry\n  Args:\n    fp: zipfile.ZipFile\n    entry_info: zipfile.ZipInfo\n\n  Returns:\n    (offset, size) tuple\n  \"\"\"\n  # Don't use len(entry_info.extra). Because that returns size of extra\n  # fields in central directory. We need to look at local file directory,\n  # as these two might have different sizes.\n\n  # We cannot work with zipfile.ZipFile instances, we need a |fp| for the underlying file.\n  zfp = zfp.fp\n  zfp.seek(entry_info.header_offset)\n  data = zfp.read(zipfile.sizeFileHeader)\n  fheader = struct.unpack(zipfile.structFileHeader, data)\n  # Last two fields of local file header are filename length and\n  # extra length\n  filename_len = fheader[-2]\n  extra_len = fheader[-1]\n  offset = entry_info.header_offset\n  offset += zipfile.sizeFileHeader\n  offset += filename_len + extra_len\n  size = entry_info.file_size\n  return (offset, size)\n\n\nclass PropertyFiles(object):\n  \"\"\"A class that computes the property-files string for an OTA package.\n\n  A property-files string is a comma-separated string that contains the\n  offset/size info for an OTA package. The entries, which must be ZIP_STORED,\n  can be fetched directly with the package URL along with the offset/size info.\n  These strings can be used for streaming A/B OTAs, or allowing an updater to\n  download package metadata entry directly, without paying the cost of\n  downloading entire package.\n\n  Computing the final property-files string requires two passes. Because doing\n  the whole package signing (with signapk.jar) will possibly reorder the ZIP\n  entries, which may in turn invalidate earlier computed ZIP entry offset/size\n  values.\n\n  This class provides functions to be called for each pass. The general flow is\n  as follows.\n\n    property_files = PropertyFiles()\n    # The first pass, which writes placeholders before doing initial signing.\n    property_files.Compute()\n    SignOutput()\n\n    # The second pass, by replacing the placeholders with actual data.\n    property_files.Finalize()\n    SignOutput()\n\n  And the caller can additionally verify the final result.\n\n    property_files.Verify()\n  \"\"\"\n\n  def __init__(self):\n    self.name = None\n    self.required = ()\n    self.optional = ()\n\n  def Compute(self, input_zip):\n    \"\"\"Computes and returns a property-files string with placeholders.\n\n    We reserve extra space for the offset and size of the metadata entry itself,\n    although we don't know the final values until the package gets signed.\n\n    Args:\n      input_zip: The input ZIP file.\n\n    Returns:\n      A string with placeholders for the metadata offset/size info, e.g.\n      \"payload.bin:679:343,payload_properties.txt:378:45,metadata:        \".\n    \"\"\"\n    return self.GetPropertyFilesString(input_zip, reserve_space=True)\n\n  class InsufficientSpaceException(Exception):\n    pass\n\n  def Finalize(self, input_zip, reserved_length):\n    \"\"\"Finalizes a property-files string with actual METADATA offset/size info.\n\n    The input ZIP file has been signed, with the ZIP entries in the desired\n    place (signapk.jar will possibly reorder the ZIP entries). Now we compute\n    the ZIP entry offsets and construct the property-files string with actual\n    data. Note that during this process, we must pad the property-files string\n    to the reserved length, so that the METADATA entry size remains the same.\n    Otherwise the entries' offsets and sizes may change again.\n\n    Args:\n      input_zip: The input ZIP file.\n      reserved_length: The reserved length of the property-files string during\n          the call to Compute(). The final string must be no more than this\n          size.\n\n    Returns:\n      A property-files string including the metadata offset/size info, e.g.\n      \"payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379  \".\n\n    Raises:\n      InsufficientSpaceException: If the reserved length is insufficient to hold\n          the final string.\n    \"\"\"\n    result = self.GetPropertyFilesString(input_zip, reserve_space=False)\n    if len(result) > reserved_length:\n      raise self.InsufficientSpaceException(\n          'Insufficient reserved space: reserved={}, actual={}'.format(\n              reserved_length, len(result)))\n\n    result += ' ' * (reserved_length - len(result))\n    return result\n\n  def Verify(self, input_zip, expected):\n    \"\"\"Verifies the input ZIP file contains the expected property-files string.\n\n    Args:\n      input_zip: The input ZIP file.\n      expected: The property-files string that's computed from Finalize().\n\n    Raises:\n      AssertionError: On finding a mismatch.\n    \"\"\"\n    actual = self.GetPropertyFilesString(input_zip)\n    assert actual == expected, \\\n        \"Mismatching streaming metadata: {} vs {}.\".format(actual, expected)\n\n  def GetPropertyFilesString(self, zip_file, reserve_space=False):\n    \"\"\"\n    Constructs the property-files string per request.\n\n    Args:\n      zip_file: The input ZIP file.\n      reserved_length: The reserved length of the property-files string.\n\n    Returns:\n      A property-files string including the metadata offset/size info, e.g.\n      \"payload.bin:679:343,payload_properties.txt:378:45,metadata:     \".\n    \"\"\"\n\n    def ComputeEntryOffsetSize(name):\n      \"\"\"Computes the zip entry offset and size.\"\"\"\n      info = zip_file.getinfo(name)\n      (offset, size) = GetZipEntryOffset(zip_file, info)\n      return '%s:%d:%d' % (os.path.basename(name), offset, size)\n\n    tokens = []\n    tokens.extend(self._GetPrecomputed(zip_file))\n    for entry in self.required:\n      tokens.append(ComputeEntryOffsetSize(entry))\n    for entry in self.optional:\n      if entry in zip_file.namelist():\n        tokens.append(ComputeEntryOffsetSize(entry))\n\n    # 'META-INF/com/android/metadata' is required. We don't know its actual\n    # offset and length (as well as the values for other entries). So we reserve\n    # 15-byte as a placeholder ('offset:length'), which is sufficient to cover\n    # the space for metadata entry. Because 'offset' allows a max of 10-digit\n    # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the\n    # reserved space serves the metadata entry only.\n    if reserve_space:\n      tokens.append('metadata:' + ' ' * 15)\n      tokens.append('metadata.pb:' + ' ' * 15)\n    else:\n      tokens.append(ComputeEntryOffsetSize(METADATA_NAME))\n      if METADATA_PROTO_NAME in zip_file.namelist():\n        tokens.append(ComputeEntryOffsetSize(METADATA_PROTO_NAME))\n\n    return ','.join(tokens)\n\n  def _GetPrecomputed(self, input_zip):\n    \"\"\"Computes the additional tokens to be included into the property-files.\n\n    This applies to tokens without actual ZIP entries, such as\n    payload_metadata.bin. We want to expose the offset/size to updaters, so\n    that they can download the payload metadata directly with the info.\n\n    Args:\n      input_zip: The input zip file.\n\n    Returns:\n      A list of strings (tokens) to be added to the property-files string.\n    \"\"\"\n    # pylint: disable=no-self-use\n    # pylint: disable=unused-argument\n    return []\n\n\ndef SignOutput(temp_zip_name, output_zip_name, package_key=None, pw=None):\n  if package_key is None:\n    package_key = OPTIONS.package_key\n  if pw is None and OPTIONS.key_passwords:\n    pw = OPTIONS.key_passwords[package_key]\n\n  SignFile(temp_zip_name, output_zip_name, package_key, pw,\n           whole_file=True)\n\n\ndef ConstructOtaApexInfo(target_zip, source_file=None):\n  \"\"\"If applicable, add the source version to the apex info.\"\"\"\n\n  def _ReadApexInfo(input_zip):\n    if not DoesInputFileContain(input_zip, \"META/apex_info.pb\"):\n      logger.warning(\"target_file doesn't contain apex_info.pb %s\", input_zip)\n      return None\n    return ReadBytesFromInputFile(input_zip, \"META/apex_info.pb\")\n\n  target_apex_string = _ReadApexInfo(target_zip)\n  # Return early if the target apex info doesn't exist or is empty.\n  if not target_apex_string:\n    return target_apex_string\n\n  # If the source apex info isn't available, just return the target info\n  if not source_file:\n    return target_apex_string\n\n  source_apex_string = _ReadApexInfo(source_file)\n  if not source_apex_string:\n    return target_apex_string\n\n  source_apex_proto = ota_metadata_pb2.ApexMetadata()\n  source_apex_proto.ParseFromString(source_apex_string)\n  source_apex_versions = {apex.package_name: apex.version for apex in\n                          source_apex_proto.apex_info}\n\n  # If the apex package is available in the source build, initialize the source\n  # apex version.\n  target_apex_proto = ota_metadata_pb2.ApexMetadata()\n  target_apex_proto.ParseFromString(target_apex_string)\n  for target_apex in target_apex_proto.apex_info:\n    name = target_apex.package_name\n    if name in source_apex_versions:\n      target_apex.source_version = source_apex_versions[name]\n\n  return target_apex_proto.SerializeToString()\n\n\ndef IsLz4diffCompatible(source_file: str, target_file: str):\n  \"\"\"Check whether lz4diff versions in two builds are compatible\n\n  Args:\n    source_file: Path to source build's target_file.zip\n    target_file: Path to target build's target_file.zip\n\n  Returns:\n    bool true if and only if lz4diff versions are compatible\n  \"\"\"\n  if source_file is None or target_file is None:\n    return False\n  # Right now we enable lz4diff as long as source build has liblz4.so.\n  # In the future we might introduce version system to lz4diff as well.\n  if zipfile.is_zipfile(source_file):\n    with zipfile.ZipFile(source_file, \"r\") as zfp:\n      return \"META/liblz4.so\" in zfp.namelist()\n  else:\n    assert os.path.isdir(source_file)\n    return os.path.exists(os.path.join(source_file, \"META\", \"liblz4.so\"))\n\n\ndef IsZucchiniCompatible(source_file: str, target_file: str):\n  \"\"\"Check whether zucchini versions in two builds are compatible\n\n  Args:\n    source_file: Path to source build's target_file.zip\n    target_file: Path to target build's target_file.zip\n\n  Returns:\n    bool true if and only if zucchini versions are compatible\n  \"\"\"\n  if source_file is None or target_file is None:\n    return False\n  assert os.path.exists(source_file)\n  assert os.path.exists(target_file)\n\n  assert zipfile.is_zipfile(source_file) or os.path.isdir(source_file)\n  assert zipfile.is_zipfile(target_file) or os.path.isdir(target_file)\n  _ZUCCHINI_CONFIG_ENTRY_NAME = \"META/zucchini_config.txt\"\n\n  def ReadEntry(path, entry):\n    # Read an entry inside a .zip file or extracted dir of .zip file\n    if zipfile.is_zipfile(path):\n      with zipfile.ZipFile(path, \"r\", allowZip64=True) as zfp:\n        if entry in zfp.namelist():\n          return zfp.read(entry).decode()\n    else:\n      entry_path = os.path.join(path, entry)\n      if os.path.exists(entry_path):\n        with open(entry_path, \"r\") as fp:\n          return fp.read()\n    return False\n  sourceEntry = ReadEntry(source_file, _ZUCCHINI_CONFIG_ENTRY_NAME)\n  targetEntry = ReadEntry(target_file, _ZUCCHINI_CONFIG_ENTRY_NAME)\n  return sourceEntry and targetEntry and sourceEntry == targetEntry\n\n\ndef ExtractTargetFiles(path: str):\n  if os.path.isdir(path):\n    logger.info(\"target files %s is already extracted\", path)\n    return path\n  extracted_dir = common.MakeTempDir(\"target_files\")\n  logger.info(f\"Extracting target files {path} to {extracted_dir}\")\n  common.UnzipToDir(path, extracted_dir, UNZIP_PATTERN + [\"\"])\n  for subdir in TARGET_FILES_IMAGES_SUBDIR:\n    image_dir = os.path.join(extracted_dir, subdir)\n    if not os.path.exists(image_dir):\n      continue\n    for filename in os.listdir(image_dir):\n      if not filename.endswith(\".img\"):\n        continue\n      common.UnsparseImage(os.path.join(image_dir, filename))\n\n  return extracted_dir\n\n\ndef LocatePartitionPath(target_files_dir: str, partition: str, allow_empty):\n  for subdir in TARGET_FILES_IMAGES_SUBDIR:\n    path = os.path.join(target_files_dir, subdir, partition + \".img\")\n    if os.path.exists(path):\n      return path\n  if allow_empty:\n    return \"\"\n  raise common.ExternalError(\n      \"Partition {} not found in target files {}\".format(partition, target_files_dir))\n\n\ndef GetPartitionImages(target_files_dir: str, ab_partitions, allow_empty=True):\n  assert os.path.isdir(target_files_dir)\n  return \":\".join([LocatePartitionPath(target_files_dir, partition, allow_empty) for partition in ab_partitions])\n\n\ndef LocatePartitionMap(target_files_dir: str, partition: str):\n  for subdir in TARGET_FILES_IMAGES_SUBDIR:\n    path = os.path.join(target_files_dir, subdir, partition + \".map\")\n    if os.path.exists(path):\n      return path\n  return \"\"\n\n\ndef GetPartitionMaps(target_files_dir: str, ab_partitions):\n  assert os.path.isdir(target_files_dir)\n  return \":\".join([LocatePartitionMap(target_files_dir, partition) for partition in ab_partitions])\n\n\nclass PayloadGenerator(object):\n  \"\"\"Manages the creation and the signing of an A/B OTA Payload.\"\"\"\n\n  PAYLOAD_BIN = payload_signer.PAYLOAD_BIN\n  PAYLOAD_PROPERTIES_TXT = payload_signer.PAYLOAD_PROPERTIES_TXT\n  SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'\n  SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'\n\n  def __init__(self, secondary=False, wipe_user_data=False, minor_version=None, is_partial_update=False, spl_downgrade=False):\n    \"\"\"Initializes a Payload instance.\n\n    Args:\n      secondary: Whether it's generating a secondary payload (default: False).\n    \"\"\"\n    self.payload_file = None\n    self.payload_properties = None\n    self.secondary = secondary\n    self.wipe_user_data = wipe_user_data\n    self.minor_version = minor_version\n    self.is_partial_update = is_partial_update\n    self.spl_downgrade = spl_downgrade\n\n  def _Run(self, cmd, **kwargs):  # pylint: disable=no-self-use\n    # Don't pipe (buffer) the output if verbose is set. Let\n    # brillo_update_payload write to stdout/stderr directly, so its progress can\n    # be monitored.\n    if OPTIONS.verbose:\n      common.RunAndCheckOutput(cmd, stdout=None, stderr=None, **kwargs)\n    else:\n      common.RunAndCheckOutput(cmd, **kwargs)\n\n  def Generate(self, target_file, source_file=None, additional_args=None, **kwargs):\n    \"\"\"Generates a payload from the given target-files zip(s).\n\n    Args:\n      target_file: The filename of the target build target-files zip.\n      source_file: The filename of the source build target-files zip; or None if\n          generating a full OTA.\n      additional_args: A list of additional args that should be passed to\n          delta_generator binary; or None.\n      kwargs: Any additional args to pass to subprocess.Popen\n    \"\"\"\n    if additional_args is None:\n      additional_args = []\n\n    payload_file = common.MakeTempFile(prefix=\"payload-\", suffix=\".bin\")\n    target_dir = ExtractTargetFiles(target_file)\n    cmd = [\"delta_generator\",\n           \"--out_file\", payload_file]\n    with open(os.path.join(target_dir, \"META\", \"ab_partitions.txt\"), \"r\") as fp:\n      ab_partitions = fp.read().strip().splitlines()\n    cmd.extend([\"--partition_names\", \":\".join(ab_partitions)])\n    cmd.extend(\n        [\"--new_partitions\", GetPartitionImages(target_dir, ab_partitions, False)])\n    cmd.extend(\n        [\"--new_mapfiles\", GetPartitionMaps(target_dir, ab_partitions)])\n    if source_file is not None:\n      source_dir = ExtractTargetFiles(source_file)\n      cmd.extend(\n          [\"--old_partitions\", GetPartitionImages(source_dir, ab_partitions, True)])\n      cmd.extend(\n          [\"--old_mapfiles\", GetPartitionMaps(source_dir, ab_partitions)])\n\n      if OPTIONS.disable_fec_computation:\n        cmd.extend([\"--disable_fec_computation=true\"])\n      if OPTIONS.disable_verity_computation:\n        cmd.extend([\"--disable_verity_computation=true\"])\n    postinstall_config = os.path.join(\n        target_dir, \"META\", \"postinstall_config.txt\")\n\n    if os.path.exists(postinstall_config):\n      cmd.extend([\"--new_postinstall_config_file\", postinstall_config])\n    dynamic_partition_info = os.path.join(\n        target_dir, \"META\", \"dynamic_partitions_info.txt\")\n\n    if os.path.exists(dynamic_partition_info):\n      cmd.extend([\"--dynamic_partition_info_file\", dynamic_partition_info])\n\n    apex_info = os.path.join(\n        target_dir, \"META\", \"apex_info.pb\")\n    if os.path.exists(apex_info):\n      cmd.extend([\"--apex_info_file\", apex_info])\n\n    major_version, minor_version = ParseUpdateEngineConfig(\n        os.path.join(target_dir, \"META\", \"update_engine_config.txt\"))\n    if source_file:\n      major_version, minor_version = ParseUpdateEngineConfig(\n          os.path.join(source_dir, \"META\", \"update_engine_config.txt\"))\n    if self.minor_version:\n      minor_version = self.minor_version\n    cmd.extend([\"--major_version\", str(major_version)])\n    if source_file is not None or self.is_partial_update:\n      cmd.extend([\"--minor_version\", str(minor_version)])\n    if self.is_partial_update:\n      cmd.extend([\"--is_partial_update=true\"])\n    cmd.extend(additional_args)\n    self._Run(cmd, **kwargs)\n\n    self.payload_file = payload_file\n    self.payload_properties = None\n\n  def Sign(self, payload_signer):\n    \"\"\"Generates and signs the hashes of the payload and metadata.\n\n    Args:\n      payload_signer: A PayloadSigner() instance that serves the signing work.\n\n    Raises:\n      AssertionError: On any failure when calling brillo_update_payload script.\n    \"\"\"\n    assert isinstance(payload_signer, PayloadSigner)\n\n    signed_payload_file = payload_signer.SignPayload(self.payload_file)\n\n    self.payload_file = signed_payload_file\n\n  def WriteToZip(self, output_zip):\n    \"\"\"Writes the payload to the given zip.\n\n    Args:\n      output_zip: The output ZipFile instance.\n    \"\"\"\n    assert self.payload_file is not None\n    # 4. Dump the signed payload properties.\n    properties_file = GeneratePayloadProperties(self.payload_file)\n\n\n    with open(properties_file, \"a\") as f:\n      if self.wipe_user_data:\n        f.write(\"POWERWASH=1\\n\")\n      if self.secondary:\n        f.write(\"SWITCH_SLOT_ON_REBOOT=0\\n\")\n      if self.spl_downgrade:\n        f.write(\"SPL_DOWNGRADE=1\\n\")\n\n\n    self.payload_properties = properties_file\n\n    if self.secondary:\n      payload_arcname = PayloadGenerator.SECONDARY_PAYLOAD_BIN\n      payload_properties_arcname = PayloadGenerator.SECONDARY_PAYLOAD_PROPERTIES_TXT\n    else:\n      payload_arcname = PayloadGenerator.PAYLOAD_BIN\n      payload_properties_arcname = PayloadGenerator.PAYLOAD_PROPERTIES_TXT\n\n    # Add the signed payload file and properties into the zip. In order to\n    # support streaming, we pack them as ZIP_STORED. So these entries can be\n    # read directly with the offset and length pairs.\n    common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,\n                    compress_type=zipfile.ZIP_STORED)\n    common.ZipWrite(output_zip, self.payload_properties,\n                    arcname=payload_properties_arcname,\n                    compress_type=zipfile.ZIP_STORED)\n\n\nclass StreamingPropertyFiles(PropertyFiles):\n  \"\"\"A subclass for computing the property-files for streaming A/B OTAs.\"\"\"\n\n  def __init__(self):\n    super(StreamingPropertyFiles, self).__init__()\n    self.name = 'ota-streaming-property-files'\n    self.required = (\n        # payload.bin and payload_properties.txt must exist.\n        'payload.bin',\n        'payload_properties.txt',\n    )\n    self.optional = (\n        # apex_info.pb isn't directly used in the update flow\n        'apex_info.pb',\n        # care_map is available only if dm-verity is enabled.\n        'care_map.pb',\n        'care_map.txt',\n        # compatibility.zip is available only if target supports Treble.\n        'compatibility.zip',\n    )\n\n\nclass AbOtaPropertyFiles(StreamingPropertyFiles):\n  \"\"\"The property-files for A/B OTA that includes payload_metadata.bin info.\n\n  Since P, we expose one more token (aka property-file), in addition to the ones\n  for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.\n  'payload_metadata.bin' is the header part of a payload ('payload.bin'), which\n  doesn't exist as a separate ZIP entry, but can be used to verify if the\n  payload can be applied on the given device.\n\n  For backward compatibility, we keep both of the 'ota-streaming-property-files'\n  and the newly added 'ota-property-files' in P. The new token will only be\n  available in 'ota-property-files'.\n  \"\"\"\n\n  def __init__(self):\n    super(AbOtaPropertyFiles, self).__init__()\n    self.name = 'ota-property-files'\n\n  def _GetPrecomputed(self, input_zip):\n    offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)\n    return ['payload_metadata.bin:{}:{}'.format(offset, size)]\n\n  @staticmethod\n  def _GetPayloadMetadataOffsetAndSize(input_zip):\n    \"\"\"Computes the offset and size of the payload metadata for a given package.\n\n    (From system/update_engine/update_metadata.proto)\n    A delta update file contains all the deltas needed to update a system from\n    one specific version to another specific version. The update format is\n    represented by this struct pseudocode:\n\n    struct delta_update_file {\n      char magic[4] = \"CrAU\";\n      uint64 file_format_version;\n      uint64 manifest_size;  // Size of protobuf DeltaArchiveManifest\n\n      // Only present if format_version > 1:\n      uint32 metadata_signature_size;\n\n      // The Bzip2 compressed DeltaArchiveManifest\n      char manifest[metadata_signature_size];\n\n      // The signature of the metadata (from the beginning of the payload up to\n      // this location, not including the signature itself). This is a\n      // serialized Signatures message.\n      char medatada_signature_message[metadata_signature_size];\n\n      // Data blobs for files, no specific format. The specific offset\n      // and length of each data blob is recorded in the DeltaArchiveManifest.\n      struct {\n        char data[];\n      } blobs[];\n\n      // These two are not signed:\n      uint64 payload_signatures_message_size;\n      char payload_signatures_message[];\n    };\n\n    'payload-metadata.bin' contains all the bytes from the beginning of the\n    payload, till the end of 'medatada_signature_message'.\n    \"\"\"\n    payload_info = input_zip.getinfo('payload.bin')\n    (payload_offset, payload_size) = GetZipEntryOffset(input_zip, payload_info)\n\n    # Read the underlying raw zipfile at specified offset\n    payload_fp = input_zip.fp\n    payload_fp.seek(payload_offset)\n    header_bin = payload_fp.read(24)\n\n    # network byte order (big-endian)\n    header = struct.unpack(\"!IQQL\", header_bin)\n\n    # 'CrAU'\n    magic = header[0]\n    assert magic == 0x43724155, \"Invalid magic: {:x}, computed offset {}\" \\\n        .format(magic, payload_offset)\n\n    manifest_size = header[2]\n    metadata_signature_size = header[3]\n    metadata_total = 24 + manifest_size + metadata_signature_size\n    assert metadata_total <= payload_size\n\n    return (payload_offset, metadata_total)\n\n\ndef Fnmatch(filename, pattersn):\n  return any([fnmatch.fnmatch(filename, pat) for pat in pattersn])\n\n\ndef CopyTargetFilesDir(input_dir):\n  output_dir = common.MakeTempDir(\"target_files\")\n\n  def SymlinkIfNotSparse(src, dst):\n    if common.IsSparseImage(src):\n      return common.UnsparseImage(src, dst)\n    else:\n      return os.symlink(os.path.realpath(src), dst)\n\n  for subdir in TARGET_FILES_IMAGES_SUBDIR:\n    if not os.path.exists(os.path.join(input_dir, subdir)):\n      continue\n    shutil.copytree(os.path.join(input_dir, subdir), os.path.join(\n        output_dir, subdir), dirs_exist_ok=True, copy_function=SymlinkIfNotSparse)\n  shutil.copytree(os.path.join(input_dir, \"META\"), os.path.join(\n      output_dir, \"META\"), dirs_exist_ok=True)\n\n  for (dirpath, _, filenames) in os.walk(input_dir):\n    for filename in filenames:\n      path = os.path.join(dirpath, filename)\n      relative_path = path.removeprefix(input_dir).removeprefix(\"/\")\n      if not Fnmatch(relative_path, UNZIP_PATTERN):\n        continue\n      target_path = os.path.join(\n          output_dir, relative_path)\n      if os.path.exists(target_path):\n        continue\n      os.makedirs(os.path.dirname(target_path), exist_ok=True)\n      shutil.copy(path, target_path)\n  return output_dir\n"
  },
  {
    "path": "tools/releasetools/payload_signer.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport common\nimport logging\nimport shlex\nimport argparse\nimport tempfile\nimport zipfile\nimport shutil\nfrom common import OPTIONS, OptionHandler\nfrom ota_signing_utils import AddSigningArgumentParse\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS.payload_signer = None\nOPTIONS.payload_signer_args = []\nOPTIONS.payload_signer_maximum_signature_size = None\nOPTIONS.package_key = None\n\nPAYLOAD_BIN = 'payload.bin'\nPAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'\n\nclass SignerOptions(OptionHandler):\n\n  @staticmethod\n  def ParseOptions(o, a):\n    if o in (\"-k\", \"--package_key\"):\n      OPTIONS.package_key = a\n    elif o == \"--payload_signer\":\n      OPTIONS.payload_signer = a\n    elif o == \"--payload_signer_args\":\n      OPTIONS.payload_signer_args = shlex.split(a)\n    elif o == \"--payload_signer_maximum_signature_size\":\n      OPTIONS.payload_signer_maximum_signature_size = a\n    elif o == \"--payload_signer_key_size\":\n      # TODO(xunchang) remove this option after cleaning up the callers.\n      logger.warning(\"The option '--payload_signer_key_size' is deprecated.\"\n                      \" Use '--payload_signer_maximum_signature_size' instead.\")\n      OPTIONS.payload_signer_maximum_signature_size = a\n    else:\n      return False\n    return True\n\n  def __init__(self):\n    super().__init__(\n      [\"payload_signer=\",\n       \"package_key=\",\n       \"payload_signer_args=\",\n       \"payload_signer_maximum_signature_size=\",\n       \"payload_signer_key_size=\"],\n       SignerOptions.ParseOptions\n    )\n\n\nsigner_options = SignerOptions()\n\n\nclass PayloadSigner(object):\n  \"\"\"A class that wraps the payload signing works.\n\n  When generating a Payload, hashes of the payload and metadata files will be\n  signed with the device key, either by calling an external payload signer or\n  by calling openssl with the package key. This class provides a unified\n  interface, so that callers can just call PayloadSigner.Sign().\n\n  If an external payload signer has been specified (OPTIONS.payload_signer), it\n  calls the signer with the provided args (OPTIONS.payload_signer_args). Note\n  that the signing key should be provided as part of the payload_signer_args.\n  Otherwise without an external signer, it uses the package key\n  (OPTIONS.package_key) and calls openssl for the signing works.\n  \"\"\"\n\n  def __init__(self, package_key=None, private_key_suffix=None, pw=None, payload_signer=None,\n               payload_signer_args=None, payload_signer_maximum_signature_size=None):\n    if package_key is None:\n      package_key = OPTIONS.package_key\n    if private_key_suffix is None:\n      private_key_suffix = OPTIONS.private_key_suffix\n    if payload_signer_args is None:\n      payload_signer_args = OPTIONS.payload_signer_args\n    if payload_signer_maximum_signature_size is None:\n      payload_signer_maximum_signature_size = OPTIONS.payload_signer_maximum_signature_size\n\n    if payload_signer is None:\n      # Prepare the payload signing key.\n      private_key = package_key + private_key_suffix\n\n      cmd = [\"openssl\", \"pkcs8\", \"-in\", private_key, \"-inform\", \"DER\"]\n      cmd.extend([\"-passin\", \"pass:\" + pw] if pw else [\"-nocrypt\"])\n      signing_key = common.MakeTempFile(prefix=\"key-\", suffix=\".key\")\n      cmd.extend([\"-out\", signing_key])\n      common.RunAndCheckOutput(cmd, verbose=True)\n\n      self.signer = \"openssl\"\n      self.signer_args = [\"pkeyutl\", \"-sign\", \"-inkey\", signing_key,\n                          \"-pkeyopt\", \"digest:sha256\"]\n      self.maximum_signature_size = self._GetMaximumSignatureSizeInBytes(\n          signing_key)\n    else:\n      self.signer = payload_signer\n      self.signer_args = payload_signer_args\n      if payload_signer_maximum_signature_size:\n        self.maximum_signature_size = int(\n            payload_signer_maximum_signature_size)\n      else:\n        # The legacy config uses RSA2048 keys.\n        logger.warning(\"The maximum signature size for payload signer is not\"\n                       \" set, default to 256 bytes.\")\n        self.maximum_signature_size = 256\n\n  @staticmethod\n  def _GetMaximumSignatureSizeInBytes(signing_key):\n    out_signature_size_file = common.MakeTempFile(\"signature_size\")\n    cmd = [\"delta_generator\", \"--out_maximum_signature_size_file={}\".format(\n        out_signature_size_file), \"--private_key={}\".format(signing_key)]\n    common.RunAndCheckOutput(cmd, verbose=True)\n    with open(out_signature_size_file) as f:\n      signature_size = f.read().rstrip()\n    logger.info(\"%s outputs the maximum signature size: %s\", cmd[0],\n                signature_size)\n    return int(signature_size)\n\n  @staticmethod\n  def _Run(cmd):\n    common.RunAndCheckOutput(cmd, stdout=None, stderr=None)\n\n  def SignPayload(self, unsigned_payload):\n\n    # 1. Generate hashes of the payload and metadata files.\n    payload_sig_file = common.MakeTempFile(prefix=\"sig-\", suffix=\".bin\")\n    metadata_sig_file = common.MakeTempFile(prefix=\"sig-\", suffix=\".bin\")\n    cmd = [\"delta_generator\",\n           \"--in_file=\" + unsigned_payload,\n           \"--signature_size=\" + str(self.maximum_signature_size),\n           \"--out_metadata_hash_file=\" + metadata_sig_file,\n           \"--out_hash_file=\" + payload_sig_file]\n    self._Run(cmd)\n\n    # 2. Sign the hashes.\n    signed_payload_sig_file = self.SignHashFile(payload_sig_file)\n    signed_metadata_sig_file = self.SignHashFile(metadata_sig_file)\n\n    # 3. Insert the signatures back into the payload file.\n    signed_payload_file = common.MakeTempFile(prefix=\"signed-payload-\",\n                                              suffix=\".bin\")\n    cmd = [\"delta_generator\",\n           \"--in_file=\" + unsigned_payload,\n           \"--out_file=\" + signed_payload_file,\n           \"--signature_size=\" + str(self.maximum_signature_size),\n           \"--metadata_signature_file=\" + signed_metadata_sig_file,\n           \"--payload_signature_file=\" + signed_payload_sig_file]\n    self._Run(cmd)\n    return signed_payload_file\n\n  def SignHashFile(self, in_file):\n    \"\"\"Signs the given input file. Returns the output filename.\"\"\"\n    out_file = common.MakeTempFile(prefix=\"signed-\", suffix=\".bin\")\n    cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]\n    common.RunAndCheckOutput(cmd)\n    return out_file\n\ndef GeneratePayloadProperties(payload_file):\n    properties_file = common.MakeTempFile(prefix=\"payload-properties-\",\n                                          suffix=\".txt\")\n    cmd = [\"delta_generator\",\n           \"--in_file=\" + payload_file,\n           \"--properties_file=\" + properties_file]\n    common.RunAndCheckOutput(cmd)\n    return properties_file\n\ndef SignOtaPackage(input_path, output_path):\n  payload_signer = PayloadSigner(\n      OPTIONS.package_key, OPTIONS.private_key_suffix,\n      None, OPTIONS.payload_signer, OPTIONS.payload_signer_args)\n  common.ZipExclude(input_path, output_path, [PAYLOAD_BIN, PAYLOAD_PROPERTIES_TXT])\n  with tempfile.NamedTemporaryFile() as unsigned_payload, zipfile.ZipFile(input_path, \"r\", allowZip64=True) as zfp:\n    with zfp.open(\"payload.bin\") as payload_fp:\n      shutil.copyfileobj(payload_fp, unsigned_payload)\n    signed_payload = payload_signer.SignPayload(unsigned_payload.name)\n    properties_file = GeneratePayloadProperties(signed_payload)\n    with zipfile.ZipFile(output_path, \"a\", compression=zipfile.ZIP_STORED, allowZip64=True) as output_zfp:\n      common.ZipWrite(output_zfp, signed_payload, PAYLOAD_BIN)\n      common.ZipWrite(output_zfp, properties_file, PAYLOAD_PROPERTIES_TXT)\n\n\ndef main(argv):\n  parser = argparse.ArgumentParser(\n      prog=argv[0], description=\"Given a series of .img files, produces a full OTA package that installs thoese images\")\n  parser.add_argument(\"input_ota\", type=str,\n                      help=\"Input OTA for signing\")\n  parser.add_argument('output_ota', type=str,\n                      help='Output OTA for the signed package')\n  parser.add_argument(\"-v\", action=\"store_true\",\n                      help=\"Enable verbose logging\", dest=\"verbose\")\n  AddSigningArgumentParse(parser)\n  args = parser.parse_args(argv[1:])\n  input_ota = args.input_ota\n  output_ota = args.output_ota\n  if args.verbose:\n    OPTIONS.verbose = True\n  common.InitLogging()\n  if args.package_key:\n    OPTIONS.package_key = args.package_key\n  logger.info(\"Re-signing OTA package {}\".format(input_ota))\n  SignOtaPackage(input_ota, output_ota)\n\nif __name__ == \"__main__\":\n  import sys\n  main(sys.argv)"
  },
  {
    "path": "tools/releasetools/pylintrc",
    "content": "[MASTER]\n\n# Specify a configuration file.\n#rcfile=\n\n# Python code to execute, usually for sys.path manipulation such as\n# pygtk.require().\n#init-hook=\n\n# Profiled execution.\nprofile=no\n\n# Add files or directories to the blacklist. They should be base names, not\n# paths.\nignore=CVS\n\n# Pickle collected data for later comparisons.\npersistent=yes\n\n# List of plugins (as comma separated values of python modules names) to load,\n# usually to register additional checkers.\nload-plugins=\n\n# Use multiple processes to speed up Pylint.\njobs=1\n\n# Allow loading of arbitrary C extensions. Extensions are imported into the\n# active Python interpreter and may run arbitrary code.\nunsafe-load-any-extension=no\n\n# A comma-separated list of package or module names from where C extensions may\n# be loaded. Extensions are loading into the active Python interpreter and may\n# run arbitrary code\nextension-pkg-whitelist=\n\n# Allow optimization of some AST trees. This will activate a peephole AST\n# optimizer, which will apply various small optimizations. For instance, it can\n# be used to obtain the result of joining multiple strings with the addition\n# operator. Joining a lot of strings can lead to a maximum recursion error in\n# Pylint and this flag can prevent that. It has one side effect, the resulting\n# AST will be different than the one from reality.\noptimize-ast=no\n\n\n[MESSAGES CONTROL]\n\n# Only show warnings with the listed confidence levels. Leave empty to show\n# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED\nconfidence=\n\n# Enable the message, report, category or checker with the given id(s). You can\n# either give multiple identifier separated by comma (,) or put this option\n# multiple time. See also the \"--disable\" option for examples.\n#enable=\n\n# Disable the message, report, category or checker with the given id(s). You\n# can either give multiple identifiers separated by comma (,) or put this\n# option multiple times (only on the command line, not in the configuration\n# file where it should appear only once).You can also use \"--disable=all\" to\n# disable everything first and then reenable specific checks. For example, if\n# you want to run only the similarities checker, you can use \"--disable=all\n# --enable=similarities\". If you want to run only the classes checker, but have\n# no Warning level messages displayed, use\"--disable=all --enable=classes\n# --disable=W\"\ndisable=invalid-name,missing-docstring,too-many-branches,too-many-locals,too-many-arguments,too-many-statements,duplicate-code,too-few-public-methods,too-many-instance-attributes,too-many-lines,too-many-public-methods,locally-disabled,fixme,not-callable\n\n\n[REPORTS]\n\n# Set the output format. Available formats are text, parseable, colorized, msvs\n# (visual studio) and html. You can also give a reporter class, eg\n# mypackage.mymodule.MyReporterClass.\noutput-format=text\n\n# Put messages in a separate file for each module / package specified on the\n# command line instead of printing them on stdout. Reports (if any) will be\n# written in a file name \"pylint_global.[txt|html]\".\nfiles-output=no\n\n# Tells whether to display a full report or only the messages\nreports=yes\n\n# Python expression which should return a note less than 10 (10 is the highest\n# note). You have access to the variables errors warning, statement which\n# respectively contain the number of errors / warnings messages and the total\n# number of statements analyzed. This is used by the global evaluation report\n# (RP0004).\nevaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)\n\n# Add a comment according to your evaluation note. This is used by the global\n# evaluation report (RP0004).\ncomment=no\n\n# Template used to display messages. This is a python new-style format string\n# used to format the message information. See doc for all details\n#msg-template=\n\n\n[SIMILARITIES]\n\n# Minimum lines number of a similarity.\nmin-similarity-lines=4\n\n# Ignore comments when computing similarities.\nignore-comments=yes\n\n# Ignore docstrings when computing similarities.\nignore-docstrings=yes\n\n# Ignore imports when computing similarities.\nignore-imports=no\n\n\n[TYPECHECK]\n\n# Tells whether missing members accessed in mixin class should be ignored. A\n# mixin class is detected if its name ends with \"mixin\" (case insensitive).\nignore-mixin-members=yes\n\n# List of module names for which member attributes should not be checked\n# (useful for modules/projects where namespaces are manipulated during runtime\n# and thus existing member attributes cannot be deduced by static analysis\nignored-modules=\n\n# List of classes names for which member attributes should not be checked\n# (useful for classes with attributes dynamically set).\nignored-classes=SQLObject\n\n# When zope mode is activated, add a predefined set of Zope acquired attributes\n# to generated-members.\nzope=no\n\n# List of members which are set dynamically and missed by pylint inference\n# system, and so shouldn't trigger E0201 when accessed. Python regular\n# expressions are accepted.\ngenerated-members=REQUEST,acl_users,aq_parent\n\n\n[MISCELLANEOUS]\n\n# List of note tags to take in consideration, separated by a comma.\nnotes=FIXME,XXX,TODO\n\n\n[BASIC]\n\n# List of builtins function names that should not be used, separated by a comma\nbad-functions=map,filter,input\n\n# Good variable names which should always be accepted, separated by a comma\ngood-names=i,j,k,ex,Run,_\n\n# Bad variable names which should always be refused, separated by a comma\nbad-names=foo,bar,baz,toto,tutu,tata\n\n# Colon-delimited sets of names that determine each other's naming style when\n# the name regexes allow several styles.\nname-group=\n\n# Include a hint for the correct naming format with invalid-name\ninclude-naming-hint=no\n\n# Regular expression matching correct function names\nfunction-rgx=[a-z_][a-z0-9_]{2,30}$\n\n# Naming hint for function names\nfunction-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct variable names\nvariable-rgx=[a-z_][a-z0-9_]{2,30}$\n\n# Naming hint for variable names\nvariable-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct constant names\nconst-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$\n\n# Naming hint for constant names\nconst-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$\n\n# Regular expression matching correct attribute names\nattr-rgx=[a-z_][a-z0-9_]{2,30}$\n\n# Naming hint for attribute names\nattr-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct argument names\nargument-rgx=[a-z_][a-z0-9_]{2,30}$\n\n# Naming hint for argument names\nargument-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct class attribute names\nclass-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$\n\n# Naming hint for class attribute names\nclass-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$\n\n# Regular expression matching correct inline iteration names\ninlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$\n\n# Naming hint for inline iteration names\ninlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$\n\n# Regular expression matching correct class names\nclass-rgx=[A-Z_][a-zA-Z0-9]+$\n\n# Naming hint for class names\nclass-name-hint=[A-Z_][a-zA-Z0-9]+$\n\n# Regular expression matching correct module names\nmodule-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$\n\n# Naming hint for module names\nmodule-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$\n\n# Regular expression matching correct method names\nmethod-rgx=[a-z_][a-z0-9_]{2,30}$\n\n# Naming hint for method names\nmethod-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression which should only match function or class names that do\n# not require a docstring.\nno-docstring-rgx=__.*__\n\n# Minimum line length for functions/classes that require docstrings, shorter\n# ones are exempt.\ndocstring-min-length=-1\n\n\n[SPELLING]\n\n# Spelling dictionary name. Available dictionaries: none. To make it working\n# install python-enchant package.\nspelling-dict=\n\n# List of comma separated words that should not be checked.\nspelling-ignore-words=\n\n# A path to a file that contains private dictionary; one word per line.\nspelling-private-dict-file=\n\n# Tells whether to store unknown words to indicated private dictionary in\n# --spelling-private-dict-file option instead of raising a message.\nspelling-store-unknown-words=no\n\n\n[FORMAT]\n\n# Maximum number of characters on a single line.\nmax-line-length=80\n\n# Regexp for a line that is allowed to be longer than the limit.\nignore-long-lines=^\\s*(# )?<?https?://\\S+>?$\n\n# Allow the body of an if to be on the same line as the test if there is no\n# else.\nsingle-line-if-stmt=no\n\n# List of optional constructs for which whitespace checking is disabled\nno-space-check=trailing-comma,dict-separator\n\n# Maximum number of lines in a module\nmax-module-lines=1000\n\n# String used as indentation unit. This is usually \" \" (4 spaces) or \"\\t\" (1\n# tab).\nindent-string='  '\n\n# Number of spaces of indent required inside a hanging or continued line.\nindent-after-paren=4\n\n# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.\nexpected-line-ending-format=LF\n\n\n[LOGGING]\n\n# Logging modules to check that the string format arguments are in logging\n# function parameter format\nlogging-modules=logging\n\n\n[VARIABLES]\n\n# Tells whether we should check for unused import in __init__ files.\ninit-import=no\n\n# A regular expression matching the name of dummy variables (i.e. expectedly\n# not used).\ndummy-variables-rgx=_$|dummy\n\n# List of additional names supposed to be defined in builtins. Remember that\n# you should avoid to define new builtins when possible.\nadditional-builtins=\n\n# List of strings which can identify a callback function by name. A callback\n# name must start or end with one of those strings.\ncallbacks=cb_,_cb\n\n\n[DESIGN]\n\n# Maximum number of arguments for function / method\nmax-args=5\n\n# Argument names that match this expression will be ignored. Default to name\n# with leading underscore\nignored-argument-names=_.*\n\n# Maximum number of locals for function / method body\nmax-locals=15\n\n# Maximum number of return / yield for function / method body\nmax-returns=6\n\n# Maximum number of branch for function / method body\nmax-branches=12\n\n# Maximum number of statements in function / method body\nmax-statements=50\n\n# Maximum number of parents for a class (see R0901).\nmax-parents=7\n\n# Maximum number of attributes for a class (see R0902).\nmax-attributes=7\n\n# Minimum number of public methods for a class (see R0903).\nmin-public-methods=2\n\n# Maximum number of public methods for a class (see R0904).\nmax-public-methods=20\n\n\n[IMPORTS]\n\n# Deprecated modules which should not be used, separated by a comma\ndeprecated-modules=regsub,TERMIOS,Bastion,rexec\n\n# Create a graph of every (i.e. internal and external) dependencies in the\n# given file (report RP0402 must not be disabled)\nimport-graph=\n\n# Create a graph of external dependencies in the given file (report RP0402 must\n# not be disabled)\next-import-graph=\n\n# Create a graph of internal dependencies in the given file (report RP0402 must\n# not be disabled)\nint-import-graph=\n\n\n[CLASSES]\n\n# List of method names used to declare (i.e. assign) instance attributes.\ndefining-attr-methods=__init__,__new__,setUp\n\n# List of valid names for the first argument in a class method.\nvalid-classmethod-first-arg=cls\n\n# List of valid names for the first argument in a metaclass class method.\nvalid-metaclass-classmethod-first-arg=mcs\n\n# List of member names, which should be excluded from the protected access\n# warning.\nexclude-protected=_asdict,_fields,_replace,_source,_make\n\n\n[EXCEPTIONS]\n\n# Exceptions that will emit a warning when being caught. Defaults to\n# \"Exception\"\novergeneral-exceptions=Exception\n"
  },
  {
    "path": "tools/releasetools/rangelib.py",
    "content": "# Copyright (C) 2014 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import print_function\n\nimport heapq\nimport itertools\n\n\n__all__ = [\"RangeSet\"]\n\n\nclass RangeSet(object):\n  \"\"\"A RangeSet represents a set of non-overlapping ranges on integers.\n\n  Attributes:\n    monotonic: Whether the input has all its integers in increasing order.\n    extra: A dict that can be used by the caller, e.g. to store info that's\n        only meaningful to caller.\n  \"\"\"\n\n  def __init__(self, data=None):\n    self.monotonic = False\n    self._extra = {}\n    if isinstance(data, str):\n      self._parse_internal(data)\n    elif data:\n      assert len(data) % 2 == 0\n      self.data = tuple(self._remove_pairs(data))\n      self.monotonic = all(x < y for x, y in zip(self.data, self.data[1:]))\n    else:\n      self.data = ()\n\n  def __iter__(self):\n    for i in range(0, len(self.data), 2):\n      yield self.data[i:i+2]\n\n  def __eq__(self, other):\n    return self.data == other.data\n\n  def __ne__(self, other):\n    return self.data != other.data\n\n  def __bool__(self):\n    return bool(self.data)\n\n  # Python 2 uses __nonzero__, while Python 3 uses __bool__.\n  __nonzero__ = __bool__\n\n  def __str__(self):\n    if not self.data:\n      return \"empty\"\n    else:\n      return self.to_string()\n\n  def __repr__(self):\n    return '<RangeSet(\"' + self.to_string() + '\")>'\n\n  @property\n  def extra(self):\n    return self._extra\n\n  @classmethod\n  def parse(cls, text):\n    \"\"\"Parses a text string into a RangeSet.\n\n    The input text string consists of a space-separated list of blocks and\n    ranges, e.g. \"10-20 30 35-40\". Ranges are interpreted to include both their\n    ends (so the above example represents 18 individual blocks). Returns a\n    RangeSet object.\n\n    If the input has all its blocks in increasing order, then the 'monotonic'\n    attribute of the returned RangeSet will be set to True. For example the\n    input \"10-20 30\" is monotonic, but the input \"15-20 30 10-14\" is not, even\n    though they represent the same set of blocks (and the two RangeSets will\n    compare equal with ==).\n    \"\"\"\n    return cls(text)\n\n  @classmethod\n  def parse_raw(cls, text):\n    \"\"\"Parse a string generated by RangeSet.to_string_raw().\n\n    >>> RangeSet.parse_raw(RangeSet(\"0-9\").to_string_raw())\n    <RangeSet(\"0-9\")>\n    \"\"\"\n\n    raw = [int(i) for i in text.split(',')]\n    assert raw[0] == len(raw[1:]), \"Invalid raw string.\"\n\n    return cls(data=raw[1:])\n\n  def _parse_internal(self, text):\n    data = []\n    last = -1\n    monotonic = True\n    for p in text.split():\n      if \"-\" in p:\n        s, e = (int(x) for x in p.split(\"-\"))\n        data.append(s)\n        data.append(e+1)\n        if last <= s <= e:\n          last = e\n        else:\n          monotonic = False\n      else:\n        s = int(p)\n        data.append(s)\n        data.append(s+1)\n        if last <= s:\n          last = s+1\n        else:\n          monotonic = False\n    data.sort()\n    self.data = tuple(self._remove_pairs(data))\n    self.monotonic = monotonic\n\n  @staticmethod\n  def _remove_pairs(source):\n    \"\"\"Remove consecutive duplicate items to simplify the result.\n\n    [1, 2, 2, 5, 5, 10] will become [1, 10].\"\"\"\n    last = None\n    for i in source:\n      if i == last:\n        last = None\n      else:\n        if last is not None:\n          yield last\n        last = i\n    if last is not None:\n      yield last\n\n  def to_string(self):\n    out = []\n    for i in range(0, len(self.data), 2):\n      s, e = self.data[i:i+2]\n      if e == s+1:\n        out.append(str(s))\n      else:\n        out.append(str(s) + \"-\" + str(e-1))\n    return \" \".join(out)\n\n  def to_string_raw(self):\n    assert self.data\n    return str(len(self.data)) + \",\" + \",\".join(str(i) for i in self.data)\n\n  def union(self, other):\n    \"\"\"Return a new RangeSet representing the union of this RangeSet\n    with the argument.\n\n    >>> RangeSet(\"10-19 30-34\").union(RangeSet(\"18-29\"))\n    <RangeSet(\"10-34\")>\n    >>> RangeSet(\"10-19 30-34\").union(RangeSet(\"22 32\"))\n    <RangeSet(\"10-19 22 30-34\")>\n    \"\"\"\n    out = []\n    z = 0\n    for p, d in heapq.merge(zip(self.data, itertools.cycle((+1, -1))),\n                            zip(other.data, itertools.cycle((+1, -1)))):\n      if (z == 0 and d == 1) or (z == 1 and d == -1):\n        out.append(p)\n      z += d\n    return RangeSet(data=out)\n\n  def intersect(self, other):\n    \"\"\"Return a new RangeSet representing the intersection of this\n    RangeSet with the argument.\n\n    >>> RangeSet(\"10-19 30-34\").intersect(RangeSet(\"18-32\"))\n    <RangeSet(\"18-19 30-32\")>\n    >>> RangeSet(\"10-19 30-34\").intersect(RangeSet(\"22-28\"))\n    <RangeSet(\"\")>\n    \"\"\"\n    out = []\n    z = 0\n    for p, d in heapq.merge(zip(self.data, itertools.cycle((+1, -1))),\n                            zip(other.data, itertools.cycle((+1, -1)))):\n      if (z == 1 and d == 1) or (z == 2 and d == -1):\n        out.append(p)\n      z += d\n    return RangeSet(data=out)\n\n  def subtract(self, other):\n    \"\"\"Return a new RangeSet representing subtracting the argument\n    from this RangeSet.\n\n    >>> RangeSet(\"10-19 30-34\").subtract(RangeSet(\"18-32\"))\n    <RangeSet(\"10-17 33-34\")>\n    >>> RangeSet(\"10-19 30-34\").subtract(RangeSet(\"22-28\"))\n    <RangeSet(\"10-19 30-34\")>\n    \"\"\"\n\n    out = []\n    z = 0\n    for p, d in heapq.merge(zip(self.data, itertools.cycle((+1, -1))),\n                            zip(other.data, itertools.cycle((-1, +1)))):\n      if (z == 0 and d == 1) or (z == 1 and d == -1):\n        out.append(p)\n      z += d\n    return RangeSet(data=out)\n\n  def overlaps(self, other):\n    \"\"\"Returns true if the argument has a nonempty overlap with this\n    RangeSet.\n\n    >>> RangeSet(\"10-19 30-34\").overlaps(RangeSet(\"18-32\"))\n    True\n    >>> RangeSet(\"10-19 30-34\").overlaps(RangeSet(\"22-28\"))\n    False\n    \"\"\"\n\n    # This is like intersect, but we can stop as soon as we discover the\n    # output is going to be nonempty.\n    z = 0\n    for _, d in heapq.merge(zip(self.data, itertools.cycle((+1, -1))),\n                            zip(other.data, itertools.cycle((+1, -1)))):\n      if (z == 1 and d == 1) or (z == 2 and d == -1):\n        return True\n      z += d\n    return False\n\n  def size(self):\n    \"\"\"Returns the total size of the RangeSet (ie, how many integers\n    are in the set).\n\n    >>> RangeSet(\"10-19 30-34\").size()\n    15\n    \"\"\"\n\n    total = 0\n    for i, p in enumerate(self.data):\n      if i % 2:\n        total += p\n      else:\n        total -= p\n    return total\n\n  def map_within(self, other):\n    \"\"\"'other' should be a subset of 'self'.  Returns a RangeSet\n    representing what 'other' would get translated to if the integers\n    of 'self' were translated down to be contiguous starting at zero.\n\n    >>> RangeSet(\"0-9\").map_within(RangeSet(\"3-4\"))\n    <RangeSet(\"3-4\")>\n    >>> RangeSet(\"10-19\").map_within(RangeSet(\"13-14\"))\n    <RangeSet(\"3-4\")>\n    >>> RangeSet(\"10-19 30-39\").map_within(RangeSet(\"17-19 30-32\"))\n    <RangeSet(\"7-12\")>\n    >>> RangeSet(\"10-19 30-39\").map_within(RangeSet(\"12-13 17-19 30-32\"))\n    <RangeSet(\"2-3 7-12\")>\n    \"\"\"\n\n    out = []\n    offset = 0\n    start = None\n    for p, d in heapq.merge(zip(self.data, itertools.cycle((-5, +5))),\n                            zip(other.data, itertools.cycle((-1, +1)))):\n      if d == -5:\n        start = p\n      elif d == +5:\n        offset += p-start\n        start = None\n      else:\n        out.append(offset + p - start)\n    return RangeSet(data=out)\n\n  def extend(self, n):\n    \"\"\"Extend the RangeSet by 'n' blocks.\n\n    The lower bound is guaranteed to be non-negative.\n\n    >>> RangeSet(\"0-9\").extend(1)\n    <RangeSet(\"0-10\")>\n    >>> RangeSet(\"10-19\").extend(15)\n    <RangeSet(\"0-34\")>\n    >>> RangeSet(\"10-19 30-39\").extend(4)\n    <RangeSet(\"6-23 26-43\")>\n    >>> RangeSet(\"10-19 30-39\").extend(10)\n    <RangeSet(\"0-49\")>\n    \"\"\"\n    out = self\n    for i in range(0, len(self.data), 2):\n      s, e = self.data[i:i+2]\n      s1 = max(0, s - n)\n      e1 = e + n\n      out = out.union(RangeSet(str(s1) + \"-\" + str(e1-1)))\n    return out\n\n  def first(self, n):\n    \"\"\"Return the RangeSet that contains at most the first 'n' integers.\n\n    >>> RangeSet(\"0-9\").first(1)\n    <RangeSet(\"0\")>\n    >>> RangeSet(\"10-19\").first(5)\n    <RangeSet(\"10-14\")>\n    >>> RangeSet(\"10-19\").first(15)\n    <RangeSet(\"10-19\")>\n    >>> RangeSet(\"10-19 30-39\").first(3)\n    <RangeSet(\"10-12\")>\n    >>> RangeSet(\"10-19 30-39\").first(15)\n    <RangeSet(\"10-19 30-34\")>\n    >>> RangeSet(\"10-19 30-39\").first(30)\n    <RangeSet(\"10-19 30-39\")>\n    >>> RangeSet(\"0-9\").first(0)\n    <RangeSet(\"\")>\n    \"\"\"\n\n    if self.size() <= n:\n      return self\n\n    out = []\n    for s, e in self:\n      if e - s >= n:\n        out += (s, s+n)\n        break\n      else:\n        out += (s, e)\n        n -= e - s\n    return RangeSet(data=out)\n\n  def next_item(self):\n    \"\"\"Return the next integer represented by the RangeSet.\n\n    >>> list(RangeSet(\"0-9\").next_item())\n    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n    >>> list(RangeSet(\"10-19 3-5\").next_item())\n    [3, 4, 5, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n    >>> list(RangeSet(\"10-19 3 5 7\").next_item())\n    [3, 5, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n    \"\"\"\n    for s, e in self:\n      for element in range(s, e):\n        yield element\n\n\nif __name__ == \"__main__\":\n  import doctest\n  doctest.testmod()\n"
  },
  {
    "path": "tools/releasetools/sign_apex.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nSigns a standalone APEX file.\n\nUsage:  sign_apex [flags] input_apex_file output_apex_file\n\n  --avbtool <avbtool>\n      Optional flag that specifies the AVB tool to use. Defaults to `avbtool`.\n\n  --container_key <key>\n      Mandatory flag that specifies the container signing key.\n\n  --payload_key <key>\n      Mandatory flag that specifies the payload signing key.\n\n  --payload_extra_args <args>\n      Optional flag that specifies any extra args to be passed to payload signer\n      (e.g. --payload_extra_args=\"--signing_helper_with_files /path/to/helper\").\n\n  -e  (--extra_apks)  <name,name,...=key>\n      Add extra APK name/key pairs. This is useful to sign the apk files in the\n      apex payload image.\n\n  --codename_to_api_level_map Q:29,R:30,...\n      A Mapping of codename to api level.  This is useful to provide sdk targeting\n      information to APK Signer.\n\n  --sign_tool <sign_tool>\n      Optional flag that specifies a custom signing tool for the contents of the apex.\n\n  --container_pw <name1=passwd,name2=passwd>\n      A mapping of key_name to password\n\"\"\"\n\nimport logging\nimport shutil\nimport re\nimport sys\n\nimport apex_utils\nimport common\n\nlogger = logging.getLogger(__name__)\n\n\ndef SignApexFile(avbtool, apex_file, payload_key, container_key, no_hashtree,\n                 apk_keys=None, signing_args=None, codename_to_api_level_map=None, sign_tool=None, container_pw=None):\n  \"\"\"Signs the given apex file.\"\"\"\n  with open(apex_file, 'rb') as input_fp:\n    apex_data = input_fp.read()\n\n  return apex_utils.SignApex(\n      avbtool,\n      apex_data,\n      payload_key=payload_key,\n      container_key=container_key,\n      container_pw=container_pw,\n      codename_to_api_level_map=codename_to_api_level_map,\n      no_hashtree=no_hashtree,\n      apk_keys=apk_keys,\n      signing_args=signing_args,\n      sign_tool=sign_tool)\n\n\ndef main(argv):\n\n  options = {}\n\n  def option_handler(o, a):\n    if o == '--avbtool':\n      options['avbtool'] = a\n    elif o == '--container_key':\n      # Strip the suffix if any, as common.SignFile expects no suffix.\n      DEFAULT_CONTAINER_KEY_SUFFIX = '.x509.pem'\n      if a.endswith(DEFAULT_CONTAINER_KEY_SUFFIX):\n        a = a[:-len(DEFAULT_CONTAINER_KEY_SUFFIX)]\n      options['container_key'] = a\n    elif o == '--payload_key':\n      options['payload_key'] = a\n    elif o == '--payload_extra_args':\n      options['payload_extra_args'] = a\n    elif o == '--codename_to_api_level_map':\n      versions = a.split(\",\")\n      for v in versions:\n        key, value = v.split(\":\")\n        if 'codename_to_api_level_map' not in options:\n          options['codename_to_api_level_map'] = {}\n        options['codename_to_api_level_map'].update({key: value})\n    elif o in (\"-e\", \"--extra_apks\"):\n      names, key = a.split(\"=\")\n      names = names.split(\",\")\n      for n in names:\n        if 'extra_apks' not in options:\n          options['extra_apks'] = {}\n        options['extra_apks'].update({n: key})\n    elif o == '--sign_tool':\n      options['sign_tool'] = a\n    elif o == '--container_pw':\n      passwords = {}\n      pairs = a.split()\n      for pair in pairs:\n        if \"=\" not in pair:\n          continue\n        tokens = pair.split(\"=\", maxsplit=1)\n        passwords[tokens[0].strip()] = tokens[1].strip()\n      options['container_pw'] = passwords\n    else:\n      return False\n    return True\n\n  args = common.ParseOptions(\n      argv, __doc__,\n      extra_opts='e:',\n      extra_long_opts=[\n          'avbtool=',\n          'codename_to_api_level_map=',\n          'container_key=',\n          'payload_extra_args=',\n          'payload_key=',\n          'extra_apks=',\n          'sign_tool=',\n          'container_pw=',\n      ],\n      extra_option_handler=option_handler)\n\n  if (len(args) != 2 or 'container_key' not in options or\n      'payload_key' not in options):\n    common.Usage(__doc__)\n    sys.exit(1)\n\n  common.InitLogging()\n\n  signed_apex = SignApexFile(\n      options.get('avbtool', 'avbtool'),\n      args[0],\n      options['payload_key'],\n      options['container_key'],\n      no_hashtree=False,\n      apk_keys=options.get('extra_apks', {}),\n      signing_args=options.get('payload_extra_args'),\n      codename_to_api_level_map=options.get(\n          'codename_to_api_level_map', {}),\n      sign_tool=options.get('sign_tool', None),\n      container_pw=options.get('container_pw'),\n  )\n  shutil.copyfile(signed_apex, args[1])\n  logger.info(\"done.\")\n\n\nif __name__ == '__main__':\n  try:\n    main(sys.argv[1:])\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/sign_target_files_apks.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nSigns all the APK files in a target-files zipfile, producing a new\ntarget-files zip.\n\nUsage:  sign_target_files_apks [flags] input_target_files output_target_files\n\n  -e  (--extra_apks)  <name,name,...=key>\n      Add extra APK/APEX name/key pairs as though they appeared in apkcerts.txt\n      or apexkeys.txt (so mappings specified by -k and -d are applied). Keys\n      specified in -e override any value for that app contained in the\n      apkcerts.txt file, or the container key for an APEX. Option may be\n      repeated to give multiple extra packages.\n\n  --extra_apex_payload_key <name,name,...=key>\n      Add a mapping for APEX package name to payload signing key, which will\n      override the default payload signing key in apexkeys.txt. Note that the\n      container key should be overridden via the `--extra_apks` flag above.\n      Option may be repeated for multiple APEXes.\n\n  --skip_apks_with_path_prefix  <prefix>\n      Skip signing an APK if it has the matching prefix in its path. The prefix\n      should be matching the entry name, which has partition names in upper\n      case, e.g. \"VENDOR/app/\", or \"SYSTEM_OTHER/preloads/\". Option may be\n      repeated to give multiple prefixes.\n\n  -k  (--key_mapping)  <src_key=dest_key>\n      Add a mapping from the key name as specified in apkcerts.txt (the\n      src_key) to the real key you wish to sign the package with\n      (dest_key).  Option may be repeated to give multiple key\n      mappings.\n\n  -d  (--default_key_mappings)  <dir>\n      Set up the following key mappings:\n\n        $devkey/devkey    ==>  $dir/releasekey\n        $devkey/testkey   ==>  $dir/releasekey\n        $devkey/media     ==>  $dir/media\n        $devkey/shared    ==>  $dir/shared\n        $devkey/platform  ==>  $dir/platform\n\n      where $devkey is the directory part of the value of\n      default_system_dev_certificate from the input target-files's\n      META/misc_info.txt.  (Defaulting to \"build/make/target/product/security\"\n      if the value is not present in misc_info.\n\n      -d and -k options are added to the set of mappings in the order\n      in which they appear on the command line.\n\n  -o  (--replace_ota_keys)\n      Replace the certificate (public key) used by OTA package verification\n      with the ones specified in the input target_files zip (in the\n      META/otakeys.txt file). Key remapping (-k and -d) is performed on the\n      keys. For A/B devices, the payload verification key will be replaced\n      as well. If there're multiple OTA keys, only the first one will be used\n      for payload verification.\n\n  -t  (--tag_changes)  <+tag>,<-tag>,...\n      Comma-separated list of changes to make to the set of tags (in\n      the last component of the build fingerprint).  Prefix each with\n      '+' or '-' to indicate whether that tag should be added or\n      removed.  Changes are processed in the order they appear.\n      Default value is \"-test-keys,-dev-keys,+release-keys\".\n\n  --replace_verity_private_key <key>\n      Replace the private key used for verity signing. It expects a filename\n      WITHOUT the extension (e.g. verity_key).\n\n  --replace_verity_public_key <key>\n      Replace the certificate (public key) used for verity verification. The\n      key file replaces the one at BOOT/RAMDISK/verity_key. It expects the key\n      filename WITH the extension (e.g. verity_key.pub).\n\n  --replace_verity_keyid <path_to_X509_PEM_cert_file>\n      Replace the veritykeyid in BOOT/cmdline of input_target_file_zip\n      with keyid of the cert pointed by <path_to_X509_PEM_cert_file>.\n\n  --remove_avb_public_keys <key1>,<key2>,...\n      Remove AVB public keys from the first-stage ramdisk. The key file to\n      remove is located at either of the following dirs:\n        - BOOT/RAMDISK/avb/ or\n        - BOOT/RAMDISK/first_stage_ramdisk/avb/\n      The second dir will be used for lookup if BOARD_USES_RECOVERY_AS_BOOT is\n      set to true.\n\n  --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta,\n         vbmeta_system,vbmeta_vendor}_algorithm <algorithm>\n  --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta,\n         vbmeta_system,vbmeta_vendor}_key <key>\n      Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign\n      the specified image. Otherwise it uses the existing values in info dict.\n\n  --avb_{apex,init_boot,boot,recovery,system,system_other,vendor,dtbo,vbmeta,\n         vbmeta_system,vbmeta_vendor}_extra_args <args>\n      Specify any additional args that are needed to AVB-sign the image\n      (e.g. \"--signing_helper /path/to/helper\"). The args will be appended to\n      the existing ones in info dict.\n\n  --avb_extra_custom_image_key <partition=key>\n  --avb_extra_custom_image_algorithm <partition=algorithm>\n      Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign\n      the specified custom images mounted on the partition. Otherwise it uses\n      the existing values in info dict.\n\n  --avb_extra_custom_image_extra_args <partition=extra_args>\n      Specify any additional args that are needed to AVB-sign the custom images\n      mounted on the partition (e.g. \"--signing_helper /path/to/helper\"). The\n      args will be appended to the existing ones in info dict.\n\n  --gki_signing_algorithm <algorithm>\n  --gki_signing_key <key>\n  --gki_signing_extra_args <args>\n      DEPRECATED Does nothing.\n\n  --android_jar_path <path>\n      Path to the android.jar to repack the apex file.\n\n  --allow_gsi_debug_sepolicy\n      Allow the existence of the file 'userdebug_plat_sepolicy.cil' under\n      (/system/system_ext|/system_ext)/etc/selinux.\n      If not set, error out when the file exists.\n\n  --override_apk_keys <path>\n      Replace all APK keys with this private key\n\n  --override_apex_keys <path>\n      Replace all APEX keys with this private key\n\n  -k  (--package_key) <key>\n      Key to use to sign the package (default is the value of\n      default_system_dev_certificate from the input target-files's\n      META/misc_info.txt, or \"build/make/target/product/security/testkey\" if\n      that value is not specified).\n\n      For incremental OTAs, the default value is based on the source\n      target-file, not the target build.\n\n  --payload_signer <signer>\n      Specify the signer when signing the payload and metadata for A/B OTAs.\n      By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign\n      with the package private key. If the private key cannot be accessed\n      directly, a payload signer that knows how to do that should be specified.\n      The signer will be supplied with \"-inkey <path_to_key>\",\n      \"-in <input_file>\" and \"-out <output_file>\" parameters.\n\n  --payload_signer_args <args>\n      Specify the arguments needed for payload signer.\n\n  --payload_signer_maximum_signature_size <signature_size>\n      The maximum signature size (in bytes) that would be generated by the given\n      payload signer. Only meaningful when custom payload signer is specified\n      via '--payload_signer'.\n      If the signer uses a RSA key, this should be the number of bytes to\n      represent the modulus. If it uses an EC key, this is the size of a\n      DER-encoded ECDSA signature.\n\"\"\"\n\nfrom __future__ import print_function\n\nimport base64\nimport copy\nimport errno\nimport gzip\nimport io\nimport itertools\nimport logging\nimport os\nimport re\nimport shutil\nimport stat\nimport sys\nimport shlex\nimport tempfile\nimport zipfile\nfrom xml.etree import ElementTree\n\nimport add_img_to_target_files\nimport ota_from_raw_img\nimport apex_utils\nimport common\nimport payload_signer\nimport update_payload\nfrom payload_signer import SignOtaPackage, PAYLOAD_BIN\n\n\nif sys.hexversion < 0x02070000:\n  print(\"Python 2.7 or newer is required.\", file=sys.stderr)\n  sys.exit(1)\n\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\n\nOPTIONS.extra_apks = {}\nOPTIONS.extra_apex_payload_keys = {}\nOPTIONS.skip_apks_with_path_prefix = set()\nOPTIONS.key_map = {}\nOPTIONS.rebuild_recovery = False\nOPTIONS.replace_ota_keys = False\nOPTIONS.remove_avb_public_keys = None\nOPTIONS.tag_changes = (\"-test-keys\", \"-dev-keys\", \"+release-keys\")\nOPTIONS.avb_keys = {}\nOPTIONS.avb_algorithms = {}\nOPTIONS.avb_extra_args = {}\nOPTIONS.android_jar_path = None\nOPTIONS.vendor_partitions = set()\nOPTIONS.vendor_otatools = None\nOPTIONS.allow_gsi_debug_sepolicy = False\nOPTIONS.override_apk_keys = None\nOPTIONS.override_apex_keys = None\nOPTIONS.input_tmp = None\n\n\nAVB_FOOTER_ARGS_BY_PARTITION = {\n    'boot': 'avb_boot_add_hash_footer_args',\n    'init_boot': 'avb_init_boot_add_hash_footer_args',\n    'dtbo': 'avb_dtbo_add_hash_footer_args',\n    'product': 'avb_product_add_hashtree_footer_args',\n    'recovery': 'avb_recovery_add_hash_footer_args',\n    'system': 'avb_system_add_hashtree_footer_args',\n    'system_dlkm': \"avb_system_dlkm_add_hashtree_footer_args\",\n    'system_ext': 'avb_system_ext_add_hashtree_footer_args',\n    'system_other': 'avb_system_other_add_hashtree_footer_args',\n    'odm': 'avb_odm_add_hashtree_footer_args',\n    'odm_dlkm': 'avb_odm_dlkm_add_hashtree_footer_args',\n    'pvmfw': 'avb_pvmfw_add_hash_footer_args',\n    'vendor': 'avb_vendor_add_hashtree_footer_args',\n    'vendor_boot': 'avb_vendor_boot_add_hash_footer_args',\n    'vendor_kernel_boot': 'avb_vendor_kernel_boot_add_hash_footer_args',\n    'vendor_dlkm': \"avb_vendor_dlkm_add_hashtree_footer_args\",\n    'vbmeta': 'avb_vbmeta_args',\n    'vbmeta_system': 'avb_vbmeta_system_args',\n    'vbmeta_vendor': 'avb_vbmeta_vendor_args',\n}\n\n\n# Check that AVB_FOOTER_ARGS_BY_PARTITION is in sync with AVB_PARTITIONS.\nfor partition in common.AVB_PARTITIONS:\n  if partition not in AVB_FOOTER_ARGS_BY_PARTITION:\n    raise RuntimeError(\"Missing {} in AVB_FOOTER_ARGS\".format(partition))\n\n# Partitions that can be regenerated after signing using a separate\n# vendor otatools package.\nALLOWED_VENDOR_PARTITIONS = set([\"vendor\", \"odm\"])\n\n\ndef IsApexFile(filename):\n  return filename.endswith(\".apex\") or filename.endswith(\".capex\")\n\n\ndef IsOtaPackage(fp):\n  with zipfile.ZipFile(fp) as zfp:\n    if not PAYLOAD_BIN in zfp.namelist():\n      return False\n    with zfp.open(PAYLOAD_BIN, \"r\") as payload:\n      magic = payload.read(4)\n      return magic == b\"CrAU\"\n\n\ndef IsEntryOtaPackage(input_zip, filename):\n  with input_zip.open(filename, \"r\") as fp:\n    external_attr = input_zip.getinfo(filename).external_attr\n    if stat.S_ISLNK(external_attr >> 16):\n      return IsEntryOtaPackage(input_zip,\n          os.path.join(os.path.dirname(filename), fp.read().decode()))\n    return IsOtaPackage(fp)\n\n\ndef GetApexFilename(filename):\n  name = os.path.basename(filename)\n  # Replace the suffix for compressed apex\n  if name.endswith(\".capex\"):\n    return name.replace(\".capex\", \".apex\")\n  return name\n\n\ndef GetApkCerts(certmap):\n  if OPTIONS.override_apk_keys is not None:\n    for apk in certmap.keys():\n      certmap[apk] = OPTIONS.override_apk_keys\n\n  # apply the key remapping to the contents of the file\n  for apk, cert in certmap.items():\n    certmap[apk] = OPTIONS.key_map.get(cert, cert)\n\n  # apply all the -e options, overriding anything in the file\n  for apk, cert in OPTIONS.extra_apks.items():\n    if not cert:\n      cert = \"PRESIGNED\"\n    certmap[apk] = OPTIONS.key_map.get(cert, cert)\n\n  return certmap\n\n\ndef GetApexKeys(keys_info, key_map):\n  \"\"\"Gets APEX payload and container signing keys by applying the mapping rules.\n\n  Presigned payload / container keys will be set accordingly.\n\n  Args:\n    keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,\n        container_key, sign_tool).\n    key_map: A dict that overrides the keys, specified via command-line input.\n\n  Returns:\n    A dict that contains the updated APEX key mapping, which should be used for\n    the current signing.\n\n  Raises:\n    AssertionError: On invalid container / payload key overrides.\n  \"\"\"\n  if OPTIONS.override_apex_keys is not None:\n    for apex in keys_info.keys():\n      keys_info[apex] = (OPTIONS.override_apex_keys, keys_info[apex][1], keys_info[apex][2])\n\n  if OPTIONS.override_apk_keys is not None:\n    key = key_map.get(OPTIONS.override_apk_keys, OPTIONS.override_apk_keys)\n    for apex in keys_info.keys():\n      keys_info[apex] = (keys_info[apex][0], key, keys_info[apex][2])\n\n  # Apply all the --extra_apex_payload_key options to override the payload\n  # signing keys in the given keys_info.\n  for apex, key in OPTIONS.extra_apex_payload_keys.items():\n    if not key:\n      key = 'PRESIGNED'\n    if apex not in keys_info:\n      logger.warning('Failed to find %s in target_files; Ignored', apex)\n      continue\n    keys_info[apex] = (key, keys_info[apex][1], keys_info[apex][2])\n\n  # Apply the key remapping to container keys.\n  for apex, (payload_key, container_key, sign_tool) in keys_info.items():\n    keys_info[apex] = (payload_key, key_map.get(container_key, container_key), sign_tool)\n\n  # Apply all the --extra_apks options to override the container keys.\n  for apex, key in OPTIONS.extra_apks.items():\n    # Skip non-APEX containers.\n    if apex not in keys_info:\n      continue\n    if not key:\n      key = 'PRESIGNED'\n    keys_info[apex] = (keys_info[apex][0], key_map.get(key, key), keys_info[apex][2])\n\n  # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the\n  # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload\n  # (overridden via commandline) indicates a config error, which should not be\n  # allowed.\n  for apex, (payload_key, container_key, sign_tool) in keys_info.items():\n    if container_key != 'PRESIGNED':\n      continue\n    if apex in OPTIONS.extra_apex_payload_keys:\n      payload_override = OPTIONS.extra_apex_payload_keys[apex]\n      assert payload_override == '', \\\n          (\"Invalid APEX key overrides: {} has PRESIGNED container but \"\n           \"non-PRESIGNED payload key {}\").format(apex, payload_override)\n    if payload_key != 'PRESIGNED':\n      print(\n          \"Setting {} payload as PRESIGNED due to PRESIGNED container\".format(\n              apex))\n    keys_info[apex] = ('PRESIGNED', 'PRESIGNED', None)\n\n  return keys_info\n\n\ndef GetMicrodroidVbmetaKey(virt_apex_path, avbtool_path):\n  \"\"\"Extracts the AVB public key from microdroid_vbmeta.img within a virt apex.\n\n  Args:\n    virt_apex_path: The path to the com.android.virt.apex file.\n    avbtool_path: The path to the avbtool executable.\n\n  Returns:\n    The AVB public key (bytes).\n  \"\"\"\n  # Creates an ApexApkSigner to extract microdroid_vbmeta.img.\n  # No need to set key_passwords/codename_to_api_level_map since\n  # we won't do signing here.\n  apex_signer = apex_utils.ApexApkSigner(\n      virt_apex_path,\n      None,  # key_passwords\n      None)  # codename_to_api_level_map\n  payload_dir = apex_signer.ExtractApexPayload(virt_apex_path)\n  microdroid_vbmeta_image = os.path.join(\n      payload_dir, 'etc', 'fs', 'microdroid_vbmeta.img')\n\n  # Extracts the avb public key from microdroid_vbmeta.img.\n  with tempfile.NamedTemporaryFile() as microdroid_pubkey:\n    common.RunAndCheckOutput([\n        avbtool_path, 'info_image',\n        '--image', microdroid_vbmeta_image,\n        '--output_pubkey', microdroid_pubkey.name])\n    with open(microdroid_pubkey.name, 'rb') as f:\n      return f.read()\n\n\ndef GetApkFileInfo(filename, compressed_extension, skipped_prefixes):\n  \"\"\"Returns the APK info based on the given filename.\n\n  Checks if the given filename (with path) looks like an APK file, by taking the\n  compressed extension into consideration. If it appears to be an APK file,\n  further checks if the APK file should be skipped when signing, based on the\n  given path prefixes.\n\n  Args:\n    filename: Path to the file.\n    compressed_extension: The extension string of compressed APKs (e.g. \".gz\"),\n        or None if there's no compressed APKs.\n    skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.\n\n  Returns:\n    (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the\n    given filename is an APK file. is_compressed indicates whether the APK file\n    is compressed (only meaningful when is_apk is True). should_be_skipped\n    indicates whether the filename matches any of the given prefixes to be\n    skipped.\n\n  Raises:\n    AssertionError: On invalid compressed_extension or skipped_prefixes inputs.\n  \"\"\"\n  assert compressed_extension is None or compressed_extension.startswith('.'), \\\n      \"Invalid compressed_extension arg: '{}'\".format(compressed_extension)\n\n  # skipped_prefixes should be one of set/list/tuple types. Other types such as\n  # str shouldn't be accepted.\n  assert isinstance(skipped_prefixes, (set, list, tuple)), \\\n      \"Invalid skipped_prefixes input type: {}\".format(type(skipped_prefixes))\n\n  compressed_apk_extension = (\n      \".apk\" + compressed_extension if compressed_extension else None)\n  is_apk = (filename.endswith(\".apk\") or\n            (compressed_apk_extension and\n             filename.endswith(compressed_apk_extension)))\n  if not is_apk:\n    return (False, False, False)\n\n  is_compressed = (compressed_apk_extension and\n                   filename.endswith(compressed_apk_extension))\n  should_be_skipped = filename.startswith(tuple(skipped_prefixes))\n  return (True, is_compressed, should_be_skipped)\n\n\ndef CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,\n                                 compressed_extension, apex_keys):\n  \"\"\"Checks that all the APKs and APEXes have keys specified.\n\n  Args:\n    input_tf_zip: An open target_files zip file.\n    known_keys: A set of APKs and APEXes that have known signing keys.\n    compressed_extension: The extension string of compressed APKs, such as\n        '.gz', or None if there's no compressed APKs.\n    apex_keys: A dict that contains the key mapping from APEX name to\n        (payload_key, container_key, sign_tool).\n\n  Raises:\n    AssertionError: On finding unknown APKs and APEXes.\n  \"\"\"\n  unknown_files = []\n  for info in input_tf_zip.infolist():\n    # Handle APEXes on all partitions\n    if IsApexFile(info.filename):\n      name = GetApexFilename(info.filename)\n      if name not in known_keys:\n        unknown_files.append(name)\n      continue\n\n    # And APKs.\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)\n    if not is_apk or should_be_skipped:\n      continue\n\n    name = os.path.basename(info.filename)\n    if is_compressed:\n      name = name[:-len(compressed_extension)]\n    if name not in known_keys:\n      unknown_files.append(name)\n\n  assert not unknown_files, \\\n      (\"No key specified for:\\n  {}\\n\"\n       \"Use '-e <apkname>=' to specify a key (which may be an empty string to \"\n       \"not sign this apk).\".format(\"\\n  \".join(unknown_files)))\n\n  # For all the APEXes, double check that we won't have an APEX that has only\n  # one of the payload / container keys set. Note that non-PRESIGNED container\n  # with PRESIGNED payload could be allowed but currently unsupported. It would\n  # require changing SignApex implementation.\n  if not apex_keys:\n    return\n\n  invalid_apexes = []\n  for info in input_tf_zip.infolist():\n    if not IsApexFile(info.filename):\n      continue\n\n    name = GetApexFilename(info.filename)\n\n    (payload_key, container_key, _) = apex_keys[name]\n    if ((payload_key in common.SPECIAL_CERT_STRINGS and\n         container_key not in common.SPECIAL_CERT_STRINGS) or\n        (payload_key not in common.SPECIAL_CERT_STRINGS and\n         container_key in common.SPECIAL_CERT_STRINGS)):\n      invalid_apexes.append(\n          \"{}: payload_key {}, container_key {}\".format(\n              name, payload_key, container_key))\n\n  assert not invalid_apexes, \\\n      \"Invalid APEX keys specified:\\n  {}\\n\".format(\n          \"\\n  \".join(invalid_apexes))\n\n\ndef SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,\n            is_compressed, apk_name):\n  unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)\n  unsigned.write(data)\n  unsigned.flush()\n\n  if is_compressed:\n    uncompressed = tempfile.NamedTemporaryFile()\n    with gzip.open(unsigned.name, \"rb\") as in_file, \\\n            open(uncompressed.name, \"wb\") as out_file:\n      shutil.copyfileobj(in_file, out_file)\n\n    # Finally, close the \"unsigned\" file (which is gzip compressed), and then\n    # replace it with the uncompressed version.\n    #\n    # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,\n    # we could just gzip / gunzip in-memory buffers instead.\n    unsigned.close()\n    unsigned = uncompressed\n\n  signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)\n\n  # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's\n  # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK\n  # didn't change, we don't want its signature to change due to the switch\n  # from SHA-1 to SHA-256.\n  # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion\n  # is 18 or higher. For pre-N builds we disable this mechanism by pretending\n  # that the APK's minSdkVersion is 1.\n  # For N+ builds, we let APK signer rely on the APK's minSdkVersion to\n  # determine whether to use SHA-256.\n  min_api_level = None\n  if platform_api_level > 23:\n    # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's\n    # minSdkVersion attribute\n    min_api_level = None\n  else:\n    # Force APK signer to use SHA-1\n    min_api_level = 1\n\n  common.SignFile(unsigned.name, signed.name, keyname, pw,\n                  min_api_level=min_api_level,\n                  codename_to_api_level_map=codename_to_api_level_map)\n\n  data = None\n  if is_compressed:\n    # Recompress the file after it has been signed.\n    compressed = tempfile.NamedTemporaryFile()\n    with open(signed.name, \"rb\") as in_file, \\\n            gzip.open(compressed.name, \"wb\") as out_file:\n      shutil.copyfileobj(in_file, out_file)\n\n    data = compressed.read()\n    compressed.close()\n  else:\n    data = signed.read()\n\n  unsigned.close()\n  signed.close()\n\n  return data\n\n\n\ndef IsBuildPropFile(filename):\n  return filename in (\n      \"SYSTEM/etc/prop.default\",\n      \"BOOT/RAMDISK/prop.default\",\n      \"RECOVERY/RAMDISK/prop.default\",\n\n      \"VENDOR_BOOT/RAMDISK/default.prop\",\n      \"VENDOR_BOOT/RAMDISK/prop.default\",\n\n      # ROOT/default.prop is a legacy path, but may still exist for upgrading\n      # devices that don't support `property_overrides_split_enabled`.\n      \"ROOT/default.prop\",\n\n      # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist\n      # as a symlink in the current code. So it's a no-op here. Keeping the\n      # path here for clarity.\n      # Some build props might be stored under path\n      # VENDOR_BOOT/RAMDISK_FRAGMENTS/recovery/RAMDISK/default.prop, and\n      # default.prop can be a symbolic link to prop.default, so overwrite all\n      # files that ends with build.prop, default.prop or prop.default\n      \"RECOVERY/RAMDISK/default.prop\") or \\\n        filename.endswith(\"build.prop\") or \\\n        filename.endswith(\"/default.prop\") or \\\n        filename.endswith(\"/prop.default\")\n\n\ndef GetOtaSigningArgs():\n  args = []\n  if OPTIONS.package_key:\n    args.extend([\"--package_key\", OPTIONS.package_key])\n  if OPTIONS.payload_signer:\n    args.extend([\"--payload_signer=\" + OPTIONS.payload_signer])\n  if OPTIONS.payload_signer_args:\n    args.extend([\"--payload_signer_args=\" + shlex.join(OPTIONS.payload_signer_args)])\n  if OPTIONS.search_path:\n    args.extend([\"--search_path\", OPTIONS.search_path])\n  if OPTIONS.payload_signer_maximum_signature_size:\n    args.extend([\"--payload_signer_maximum_signature_size\",\n                OPTIONS.payload_signer_maximum_signature_size])\n  if OPTIONS.private_key_suffix:\n    args.extend([\"--private_key_suffix\", OPTIONS.private_key_suffix])\n  return args\n\n\ndef RegenerateKernelPartitions(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info):\n  \"\"\"Re-generate boot and dtbo partitions using new signing configuration\"\"\"\n  files_to_unzip = [\n      \"PREBUILT_IMAGES/*\", \"BOOTABLE_IMAGES/*.img\", \"*/boot_16k.img\", \"*/dtbo_16k.img\"]\n  if OPTIONS.input_tmp is None:\n    OPTIONS.input_tmp = common.UnzipTemp(input_tf_zip.filename, files_to_unzip)\n  else:\n    common.UnzipToDir(input_tf_zip.filename, OPTIONS.input_tmp, files_to_unzip)\n  unzip_dir = OPTIONS.input_tmp\n  os.makedirs(os.path.join(unzip_dir, \"IMAGES\"), exist_ok=True)\n\n  boot_image = common.GetBootableImage(\n      \"IMAGES/boot.img\", \"boot.img\", unzip_dir, \"BOOT\", misc_info)\n  if boot_image:\n    boot_image.WriteToDir(unzip_dir)\n    boot_image = os.path.join(unzip_dir, boot_image.name)\n    common.ZipWrite(output_tf_zip, boot_image, \"IMAGES/boot.img\",\n                    compress_type=zipfile.ZIP_STORED)\n  if misc_info.get(\"has_dtbo\") == \"true\":\n    add_img_to_target_files.AddDtbo(output_tf_zip)\n  return unzip_dir\n\n\ndef RegenerateBootOTA(input_tf_zip: zipfile.ZipFile, filename, input_ota):\n  with input_tf_zip.open(filename, \"r\") as in_fp:\n    payload = update_payload.Payload(in_fp)\n  is_incremental = any([part.HasField('old_partition_info')\n                        for part in payload.manifest.partitions])\n  is_boot_ota = filename.startswith(\n      \"VENDOR/boot_otas/\") or filename.startswith(\"SYSTEM/boot_otas/\")\n  if not is_boot_ota:\n    return\n  is_4k_boot_ota = filename in [\n      \"VENDOR/boot_otas/boot_ota_4k.zip\", \"SYSTEM/boot_otas/boot_ota_4k.zip\"]\n  # Only 4K boot image is re-generated, so if 16K boot ota isn't incremental,\n  # we do not need to re-generate\n  if not is_4k_boot_ota and not is_incremental:\n    return\n\n  timestamp = str(payload.manifest.max_timestamp)\n  partitions = [part.partition_name for part in payload.manifest.partitions]\n  unzip_dir = OPTIONS.input_tmp\n  signed_boot_image = os.path.join(unzip_dir, \"IMAGES\", \"boot.img\")\n  if not os.path.exists(signed_boot_image):\n    logger.warn(\"Need to re-generate boot OTA {} but failed to get signed boot image. 16K dev option will be impacted, after rolling back to 4K user would need to sideload/flash their device to continue receiving OTAs.\")\n    return\n  signed_dtbo_image = os.path.join(unzip_dir, \"IMAGES\", \"dtbo.img\")\n  if \"dtbo\" in partitions and not os.path.exists(signed_dtbo_image):\n    raise ValueError(\n        \"Boot OTA {} has dtbo partition, but no dtbo image found in target files.\".format(filename))\n  if is_incremental:\n    signed_16k_boot_image = os.path.join(\n        unzip_dir, \"IMAGES\", \"boot_16k.img\")\n    signed_16k_dtbo_image = os.path.join(\n        unzip_dir, \"IMAGES\", \"dtbo_16k.img\")\n    if is_4k_boot_ota:\n      if os.path.exists(signed_16k_boot_image):\n        signed_boot_image = signed_16k_boot_image + \":\" + signed_boot_image\n      if os.path.exists(signed_16k_dtbo_image):\n        signed_dtbo_image = signed_16k_dtbo_image + \":\" + signed_dtbo_image\n    else:\n      if os.path.exists(signed_16k_boot_image):\n        signed_boot_image += \":\" + signed_16k_boot_image\n      if os.path.exists(signed_16k_dtbo_image):\n        signed_dtbo_image += \":\" + signed_16k_dtbo_image\n\n  args = [\"ota_from_raw_img\",\n          \"--max_timestamp\", timestamp, \"--output\", input_ota.name]\n  args.extend(GetOtaSigningArgs())\n  if \"dtbo\" in partitions:\n    args.extend([\"--partition_name\", \"boot,dtbo\",\n                signed_boot_image, signed_dtbo_image])\n  else:\n    args.extend([\"--partition_name\", \"boot\", signed_boot_image])\n  logger.info(\n      \"Re-generating boot OTA {} using cmd {}\".format(filename, args))\n  ota_from_raw_img.main(args)\n\n\ndef ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info,\n                       apk_keys, apex_keys, key_passwords,\n                       platform_api_level, codename_to_api_level_map,\n                       compressed_extension):\n  # maxsize measures the maximum filename length, including the ones to be\n  # skipped.\n  try:\n    maxsize = max(\n        [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()\n         if GetApkFileInfo(i.filename, compressed_extension, [])[0]])\n  except ValueError:\n    # Sets this to zero for targets without APK files.\n    maxsize = 0\n\n  # Replace the AVB signing keys, if any.\n  ReplaceAvbSigningKeys(misc_info)\n  OPTIONS.info_dict = misc_info\n\n  # Rewrite the props in AVB signing args.\n  if misc_info.get('avb_enable') == 'true':\n    RewriteAvbProps(misc_info)\n\n  RegenerateKernelPartitions(input_tf_zip, output_tf_zip, misc_info)\n\n  for info in input_tf_zip.infolist():\n    filename = info.filename\n    if filename.startswith(\"IMAGES/\"):\n      continue\n\n    # Skip OTA-specific images (e.g. split super images), which will be\n    # re-generated during signing.\n    if filename.startswith(\"OTA/\") and filename.endswith(\".img\"):\n      continue\n\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)\n    data = input_tf_zip.read(filename)\n    out_info = copy.copy(info)\n\n    if is_apk and should_be_skipped:\n      # Copy skipped APKs verbatim.\n      print(\n          \"NOT signing: %s\\n\"\n          \"        (skipped due to matching prefix)\" % (filename,))\n      common.ZipWriteStr(output_tf_zip, out_info, data)\n\n    # Sign APKs.\n    elif is_apk:\n      name = os.path.basename(filename)\n      if is_compressed:\n        name = name[:-len(compressed_extension)]\n\n      key = apk_keys[name]\n      if key not in common.SPECIAL_CERT_STRINGS:\n        print(\"    signing: %-*s (%s)\" % (maxsize, name, key))\n        signed_data = SignApk(data, key, key_passwords[key], platform_api_level,\n                              codename_to_api_level_map, is_compressed, name)\n        common.ZipWriteStr(output_tf_zip, out_info, signed_data)\n      else:\n        # an APK we're not supposed to sign.\n        print(\n            \"NOT signing: %s\\n\"\n            \"        (skipped due to special cert string)\" % (name,))\n        common.ZipWriteStr(output_tf_zip, out_info, data)\n\n    # Sign bundled APEX files on all partitions\n    elif IsApexFile(filename):\n      name = GetApexFilename(filename)\n\n      payload_key, container_key, sign_tool = apex_keys[name]\n\n      # We've asserted not having a case with only one of them PRESIGNED.\n      if (payload_key not in common.SPECIAL_CERT_STRINGS and\n              container_key not in common.SPECIAL_CERT_STRINGS):\n        print(\"    signing: %-*s container (%s)\" % (\n            maxsize, name, container_key))\n        print(\"           : %-*s payload   (%s)\" % (\n            maxsize, name, payload_key))\n\n        signed_apex = apex_utils.SignApex(\n            misc_info['avb_avbtool'],\n            data,\n            payload_key,\n            container_key,\n            key_passwords,\n            apk_keys,\n            codename_to_api_level_map,\n            no_hashtree=None,  # Let apex_util determine if hash tree is needed\n            signing_args=OPTIONS.avb_extra_args.get('apex'),\n            sign_tool=sign_tool)\n        common.ZipWrite(output_tf_zip, signed_apex, filename)\n\n      else:\n        print(\n            \"NOT signing: %s\\n\"\n            \"        (skipped due to special cert string)\" % (name,))\n        common.ZipWriteStr(output_tf_zip, out_info, data)\n\n    elif filename.endswith(\".zip\") and IsEntryOtaPackage(input_tf_zip, filename):\n      logger.info(\"Re-signing OTA package {}\".format(filename))\n      with tempfile.NamedTemporaryFile() as input_ota, tempfile.NamedTemporaryFile() as output_ota:\n        RegenerateBootOTA(input_tf_zip, filename, input_ota)\n\n        SignOtaPackage(input_ota.name, output_ota.name)\n        common.ZipWrite(output_tf_zip, output_ota.name, filename,\n                        compress_type=zipfile.ZIP_STORED)\n    # System properties.\n    elif IsBuildPropFile(filename):\n      print(\"Rewriting %s:\" % (filename,))\n      if stat.S_ISLNK(info.external_attr >> 16):\n        new_data = data\n      else:\n        new_data = RewriteProps(data.decode())\n      common.ZipWriteStr(output_tf_zip, out_info, new_data)\n\n    # Replace the certs in *mac_permissions.xml (there could be multiple, such\n    # as {system,vendor}/etc/selinux/{plat,vendor}_mac_permissions.xml).\n    elif filename.endswith(\"mac_permissions.xml\"):\n      print(\"Rewriting %s with new keys.\" % (filename,))\n      new_data = ReplaceCerts(data.decode())\n      common.ZipWriteStr(output_tf_zip, out_info, new_data)\n\n    # Ask add_img_to_target_files to rebuild the recovery patch if needed.\n    elif filename in (\"SYSTEM/recovery-from-boot.p\",\n                      \"VENDOR/recovery-from-boot.p\",\n\n                      \"SYSTEM/etc/recovery.img\",\n                      \"VENDOR/etc/recovery.img\",\n\n                      \"SYSTEM/bin/install-recovery.sh\",\n                      \"VENDOR/bin/install-recovery.sh\"):\n      OPTIONS.rebuild_recovery = True\n\n    # Don't copy OTA certs if we're replacing them.\n    # Replacement of update-payload-key.pub.pem was removed in b/116660991.\n    elif OPTIONS.replace_ota_keys and filename.endswith(\"/otacerts.zip\"):\n      pass\n\n    # Skip META/misc_info.txt since we will write back the new values later.\n    elif filename == \"META/misc_info.txt\":\n      pass\n\n    elif (OPTIONS.remove_avb_public_keys and\n          (filename.startswith(\"BOOT/RAMDISK/avb/\") or\n           filename.startswith(\"BOOT/RAMDISK/first_stage_ramdisk/avb/\"))):\n      matched_removal = False\n      for key_to_remove in OPTIONS.remove_avb_public_keys:\n        if filename.endswith(key_to_remove):\n          matched_removal = True\n          print(\"Removing AVB public key from ramdisk: %s\" % filename)\n          break\n      if not matched_removal:\n        # Copy it verbatim if we don't want to remove it.\n        common.ZipWriteStr(output_tf_zip, out_info, data)\n\n    # Skip the vbmeta digest as we will recalculate it.\n    elif filename == \"META/vbmeta_digest.txt\":\n      pass\n\n    # Skip the care_map as we will regenerate the system/vendor images.\n    elif filename in [\"META/care_map.pb\", \"META/care_map.txt\"]:\n      pass\n\n    # Skip apex_info.pb because we sign/modify apexes\n    elif filename == \"META/apex_info.pb\":\n      pass\n\n    # Updates system_other.avbpubkey in /product/etc/.\n    elif filename in (\n        \"PRODUCT/etc/security/avb/system_other.avbpubkey\",\n        \"SYSTEM/product/etc/security/avb/system_other.avbpubkey\"):\n      # Only update system_other's public key, if the corresponding signing\n      # key is specified via --avb_system_other_key.\n      signing_key = OPTIONS.avb_keys.get(\"system_other\")\n      if signing_key:\n        public_key = common.ExtractAvbPublicKey(\n            misc_info['avb_avbtool'], signing_key)\n        print(\"    Rewriting AVB public key of system_other in /product\")\n        common.ZipWrite(output_tf_zip, public_key, filename)\n\n    # Updates pvmfw embedded public key with the virt APEX payload key.\n    elif filename == \"PREBUILT_IMAGES/pvmfw.img\":\n      # Find the path of the virt APEX in the target files.\n      namelist = input_tf_zip.namelist()\n      apex_gen = (f for f in namelist if IsApexFile(f))\n      virt_apex_re = re.compile(\"^.*com\\.([^\\.]+\\.)?android\\.virt\\.apex$\")\n      virt_apex_path = next(\n        (a for a in apex_gen if virt_apex_re.match(a)), None)\n      if not virt_apex_path:\n        print(\"Removing %s from ramdisk: virt APEX not found\" % filename)\n      else:\n        print(\"Replacing %s embedded key with %s key\" % (filename,\n                                                         virt_apex_path))\n        # Get the current and new embedded keys.\n        virt_apex = GetApexFilename(virt_apex_path)\n        payload_key, container_key, sign_tool = apex_keys[virt_apex]\n\n        # b/384813199: handles the pre-signed com.android.virt.apex in GSI.\n        if payload_key == 'PRESIGNED':\n          with tempfile.NamedTemporaryFile() as virt_apex_temp_file:\n            virt_apex_temp_file.write(input_tf_zip.read(virt_apex_path))\n            virt_apex_temp_file.flush()\n            new_pubkey = GetMicrodroidVbmetaKey(virt_apex_temp_file.name,\n                                                misc_info['avb_avbtool'])\n        else:\n          new_pubkey_path = common.ExtractAvbPublicKey(\n              misc_info['avb_avbtool'], payload_key)\n          with open(new_pubkey_path, 'rb') as f:\n            new_pubkey = f.read()\n\n        pubkey_info = copy.copy(\n            input_tf_zip.getinfo(\"PREBUILT_IMAGES/pvmfw_embedded.avbpubkey\"))\n        old_pubkey = input_tf_zip.read(pubkey_info.filename)\n        # Validate the keys and image.\n        if len(old_pubkey) != len(new_pubkey):\n          raise common.ExternalError(\"pvmfw embedded public key size mismatch\")\n        pos = data.find(old_pubkey)\n        if pos == -1:\n          raise common.ExternalError(\"pvmfw embedded public key not found\")\n        # Replace the key and copy new files.\n        new_data = data[:pos] + new_pubkey + data[pos+len(old_pubkey):]\n        common.ZipWriteStr(output_tf_zip, out_info, new_data)\n        common.ZipWriteStr(output_tf_zip, pubkey_info, new_pubkey)\n    elif filename == \"PREBUILT_IMAGES/pvmfw_embedded.avbpubkey\":\n      pass\n\n    # Should NOT sign boot-debug.img.\n    elif filename in (\n        \"BOOT/RAMDISK/force_debuggable\",\n        \"BOOT/RAMDISK/first_stage_ramdisk/force_debuggable\"):\n      raise common.ExternalError(\"debuggable boot.img cannot be signed\")\n\n    # Should NOT sign userdebug sepolicy file.\n    elif filename in (\n        \"SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil\",\n        \"SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil\"):\n      if not OPTIONS.allow_gsi_debug_sepolicy:\n        raise common.ExternalError(\"debug sepolicy shouldn't be included\")\n      else:\n        # Copy it verbatim if we allow the file to exist.\n        common.ZipWriteStr(output_tf_zip, out_info, data)\n\n    # Sign microdroid_vendor.img.\n    elif filename == \"VENDOR/etc/avf/microdroid/microdroid_vendor.img\":\n      vendor_key = OPTIONS.avb_keys.get(\"vendor\")\n      vendor_algorithm = OPTIONS.avb_algorithms.get(\"vendor\")\n      with tempfile.NamedTemporaryFile() as image:\n        image.write(data)\n        image.flush()\n        ReplaceKeyInAvbHashtreeFooter(image, vendor_key, vendor_algorithm,\n            misc_info)\n        common.ZipWrite(output_tf_zip, image.name, filename)\n    # A non-APK file; copy it verbatim.\n    else:\n      try:\n        entry = output_tf_zip.getinfo(filename)\n        if output_tf_zip.read(entry) != data:\n          logger.warn(\n              \"Output zip contains duplicate entries for %s with different contents\", filename)\n        continue\n      except KeyError:\n        common.ZipWriteStr(output_tf_zip, out_info, data)\n\n  if OPTIONS.replace_ota_keys:\n    ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)\n\n\n  # Write back misc_info with the latest values.\n  ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)\n\n# Parse string output of `avbtool info_image`.\ndef ParseAvbInfo(info_raw):\n  # line_matcher is for parsing each output line of `avbtool info_image`.\n  # example string input: \"      Hash Algorithm:        sha1\"\n  # example matched input: (\"      \", \"Hash Algorithm\", \"sha1\")\n  line_matcher = re.compile(r'^(\\s*)([^:]+):\\s*(.*)$')\n  # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`.\n  # example string input: \"example_prop_key -> 'example_prop_value'\"\n  # example matched output: (\"example_prop_key\", \"example_prop_value\")\n  prop_matcher = re.compile(r\"(.+)\\s->\\s'(.+)'\")\n  info = {}\n  indent_stack = [[-1, info]]\n  for line_info_raw in info_raw.split('\\n'):\n    # Parse the line\n    line_info_parsed = line_matcher.match(line_info_raw)\n    if not line_info_parsed:\n      continue\n    indent = len(line_info_parsed.group(1))\n    key = line_info_parsed.group(2).strip()\n    value = line_info_parsed.group(3).strip()\n\n    # Pop indentation stack\n    while indent <= indent_stack[-1][0]:\n      del indent_stack[-1]\n\n    # Insert information into 'info'.\n    cur_info = indent_stack[-1][1]\n    if value == \"\":\n      if key == \"Descriptors\":\n        empty_list = []\n        cur_info[key] = empty_list\n        indent_stack.append([indent, empty_list])\n      else:\n        empty_dict = {}\n        cur_info.append({key:empty_dict})\n        indent_stack.append([indent, empty_dict])\n    elif key == \"Prop\":\n      prop_parsed = prop_matcher.match(value)\n      if not prop_parsed:\n        raise ValueError(\n            \"Failed to parse prop while getting avb information.\")\n      cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}})\n    else:\n      cur_info[key] = value\n  return info\n\ndef ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info):\n  # Get avb information about the image by parsing avbtool info_image.\n  def GetAvbInfo(avbtool, image_name):\n    # Get information with raw string by `avbtool info_image`.\n    info_raw = common.RunAndCheckOutput([\n      avbtool, 'info_image',\n      '--image', image_name\n    ])\n    return ParseAvbInfo(info_raw)\n\n  # Get hashtree descriptor from info\n  def GetAvbHashtreeDescriptor(avb_info):\n    hashtree_descriptors = tuple(filter(lambda x: \"Hashtree descriptor\" in x,\n        info.get('Descriptors')))\n    if len(hashtree_descriptors) != 1:\n      raise ValueError(\"The number of hashtree descriptor is not 1.\")\n    return hashtree_descriptors[0][\"Hashtree descriptor\"]\n\n  # Get avb info\n  avbtool = misc_info['avb_avbtool']\n  info = GetAvbInfo(avbtool, image.name)\n  hashtree_descriptor = GetAvbHashtreeDescriptor(info)\n\n  # Generate command\n  cmd = [avbtool, 'add_hashtree_footer',\n    '--key', new_key,\n    '--algorithm', new_algorithm,\n    '--partition_name', hashtree_descriptor.get(\"Partition Name\"),\n    '--partition_size', info.get(\"Image size\").removesuffix(\" bytes\"),\n    '--hash_algorithm', hashtree_descriptor.get(\"Hash Algorithm\"),\n    '--salt', hashtree_descriptor.get(\"Salt\"),\n    '--do_not_generate_fec',\n    '--image', image.name\n  ]\n\n  # Append properties into command\n  props = map(lambda x: x.get(\"Prop\"), filter(lambda x: \"Prop\" in x,\n      info.get('Descriptors')))\n  for prop_wrapped in props:\n    prop = tuple(prop_wrapped.items())\n    if len(prop) != 1:\n      raise ValueError(\"The number of property is not 1.\")\n    cmd.append('--prop')\n    cmd.append(prop[0][0] + ':' + prop[0][1])\n\n  # Replace Hashtree Footer with new key\n  common.RunAndCheckOutput(cmd)\n\n  # Check root digest is not changed\n  new_info = GetAvbInfo(avbtool, image.name)\n  new_hashtree_descriptor = GetAvbHashtreeDescriptor(info)\n  root_digest = hashtree_descriptor.get(\"Root Digest\")\n  new_root_digest = new_hashtree_descriptor.get(\"Root Digest\")\n  assert root_digest == new_root_digest, \\\n      (\"Root digest in hashtree descriptor shouldn't be changed. Old: {}, New: \"\n       \"{}\").format(root_digest, new_root_digest)\n\ndef ReplaceCerts(data):\n  \"\"\"Replaces all the occurences of X.509 certs with the new ones.\n\n  The mapping info is read from OPTIONS.key_map. Non-existent certificate will\n  be skipped. After the replacement, it additionally checks for duplicate\n  entries, which would otherwise fail the policy loading code in\n  frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.\n\n  Args:\n    data: Input string that contains a set of X.509 certs.\n\n  Returns:\n    A string after the replacement.\n\n  Raises:\n    AssertionError: On finding duplicate entries.\n  \"\"\"\n  for old, new in OPTIONS.key_map.items():\n    if OPTIONS.verbose:\n      print(\"    Replacing %s.x509.pem with %s.x509.pem\" % (old, new))\n\n    try:\n      with open(old + \".x509.pem\") as old_fp:\n        old_cert16 = base64.b16encode(\n            common.ParseCertificate(old_fp.read())).decode().lower()\n      with open(new + \".x509.pem\") as new_fp:\n        new_cert16 = base64.b16encode(\n            common.ParseCertificate(new_fp.read())).decode().lower()\n    except IOError as e:\n      if OPTIONS.verbose or e.errno != errno.ENOENT:\n        print(\"    Error accessing %s: %s.\\nSkip replacing %s.x509.pem with \"\n              \"%s.x509.pem.\" % (e.filename, e.strerror, old, new))\n      continue\n\n    # Only match entire certs.\n    pattern = \"\\\\b\" + old_cert16 + \"\\\\b\"\n    (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)\n\n    if OPTIONS.verbose:\n      print(\"    Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem\" % (\n          num, old, new))\n\n  # Verify that there're no duplicate entries after the replacement. Note that\n  # it's only checking entries with global seinfo at the moment (i.e. ignoring\n  # the ones with inner packages). (Bug: 69479366)\n  root = ElementTree.fromstring(data)\n  signatures = [signer.attrib['signature']\n                for signer in root.findall('signer')]\n  assert len(signatures) == len(set(signatures)), \\\n      \"Found duplicate entries after cert replacement: {}\".format(data)\n\n  return data\n\n\ndef EditTags(tags):\n  \"\"\"Applies the edits to the tag string as specified in OPTIONS.tag_changes.\n\n  Args:\n    tags: The input string that contains comma-separated tags.\n\n  Returns:\n    The updated tags (comma-separated and sorted).\n  \"\"\"\n  tags = set(tags.split(\",\"))\n  for ch in OPTIONS.tag_changes:\n    if ch[0] == \"-\":\n      tags.discard(ch[1:])\n    elif ch[0] == \"+\":\n      tags.add(ch[1:])\n  return \",\".join(sorted(tags))\n\n\ndef RewriteProps(data):\n  \"\"\"Rewrites the system properties in the given string.\n\n  Each property is expected in 'key=value' format. The properties that contain\n  build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling\n  EditTags().\n\n  Args:\n    data: Input string, separated by newlines.\n\n  Returns:\n    The string with modified properties.\n  \"\"\"\n  output = []\n  for line in data.split(\"\\n\"):\n    line = line.strip()\n    original_line = line\n    if line and line[0] != '#' and \"=\" in line:\n      key, value = line.split(\"=\", 1)\n      if (key.startswith(\"ro.\") and\n              key.endswith((\".build.fingerprint\", \".build.thumbprint\"))):\n        pieces = value.split(\"/\")\n        pieces[-1] = EditTags(pieces[-1])\n        value = \"/\".join(pieces)\n      elif key == \"ro.bootimage.build.fingerprint\":\n        pieces = value.split(\"/\")\n        pieces[-1] = EditTags(pieces[-1])\n        value = \"/\".join(pieces)\n      elif key == \"ro.build.description\":\n        pieces = value.split()\n        assert pieces[-1].endswith(\"-keys\")\n        pieces[-1] = EditTags(pieces[-1])\n        value = \" \".join(pieces)\n      elif key.startswith(\"ro.\") and key.endswith(\".build.tags\"):\n        value = EditTags(value)\n      elif key == \"ro.build.display.id\":\n        # change, eg, \"JWR66N dev-keys\" to \"JWR66N\"\n        value = value.split()\n        if len(value) > 1 and value[-1].endswith(\"-keys\"):\n          value.pop()\n        value = \" \".join(value)\n      line = key + \"=\" + value\n    if line != original_line:\n      print(\"  replace: \", original_line)\n      print(\"     with: \", line)\n    output.append(line)\n  return \"\\n\".join(output) + \"\\n\"\n\n\ndef WriteOtacerts(output_zip, filename, keys):\n  \"\"\"Constructs a zipfile from given keys; and writes it to output_zip.\n\n  Args:\n    output_zip: The output target_files zip.\n    filename: The archive name in the output zip.\n    keys: A list of public keys to use during OTA package verification.\n  \"\"\"\n  temp_file = io.BytesIO()\n  certs_zip = zipfile.ZipFile(temp_file, \"w\", allowZip64=True)\n  for k in keys:\n    common.ZipWrite(certs_zip, k)\n  common.ZipClose(certs_zip)\n  common.ZipWriteStr(output_zip, filename, temp_file.getvalue())\n\n\ndef ReplaceOtaKeys(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info):\n  try:\n    keylist = input_tf_zip.read(\"META/otakeys.txt\").decode().split()\n  except KeyError:\n    raise common.ExternalError(\"can't read META/otakeys.txt from input\")\n\n  extra_ota_keys_info = misc_info.get(\"extra_ota_keys\")\n  if extra_ota_keys_info:\n    extra_ota_keys = [OPTIONS.key_map.get(k, k) + \".x509.pem\"\n                      for k in extra_ota_keys_info.split()]\n    print(\"extra ota key(s): \" + \", \".join(extra_ota_keys))\n  else:\n    extra_ota_keys = []\n  for k in extra_ota_keys:\n    if not os.path.isfile(k):\n      raise common.ExternalError(k + \" does not exist or is not a file\")\n\n  extra_recovery_keys_info = misc_info.get(\"extra_recovery_keys\")\n  if extra_recovery_keys_info:\n    extra_recovery_keys = [OPTIONS.key_map.get(k, k) + \".x509.pem\"\n                           for k in extra_recovery_keys_info.split()]\n    print(\"extra recovery-only key(s): \" + \", \".join(extra_recovery_keys))\n  else:\n    extra_recovery_keys = []\n  for k in extra_recovery_keys:\n    if not os.path.isfile(k):\n      raise common.ExternalError(k + \" does not exist or is not a file\")\n\n  mapped_keys = []\n  for k in keylist:\n    m = re.match(r\"^(.*)\\.x509\\.pem$\", k)\n    if not m:\n      raise common.ExternalError(\n          \"can't parse \\\"%s\\\" from META/otakeys.txt\" % (k,))\n    k = m.group(1)\n    mapped_keys.append(OPTIONS.key_map.get(k, k) + \".x509.pem\")\n\n  if mapped_keys:\n    print(\"using:\\n   \", \"\\n   \".join(mapped_keys))\n    print(\"for OTA package verification\")\n  else:\n    devkey = misc_info.get(\"default_system_dev_certificate\",\n                           \"build/make/target/product/security/testkey\")\n    mapped_devkey = OPTIONS.key_map.get(devkey, devkey)\n    if mapped_devkey != devkey:\n      misc_info[\"default_system_dev_certificate\"] = mapped_devkey\n    mapped_keys.append(mapped_devkey + \".x509.pem\")\n    print(\"META/otakeys.txt has no keys; using %s for OTA package\"\n          \" verification.\" % (mapped_keys[0],))\n  for k in mapped_keys:\n    if not os.path.isfile(k):\n      raise common.ExternalError(k + \" does not exist or is not a file\")\n\n  otacerts = [info\n              for info in input_tf_zip.infolist()\n              if info.filename.endswith(\"/otacerts.zip\")]\n  for info in otacerts:\n    if info.filename.startswith((\"BOOT/\", \"RECOVERY/\", \"VENDOR_BOOT/\")):\n      extra_keys = extra_recovery_keys\n    else:\n      extra_keys = extra_ota_keys\n    print(\"Rewriting OTA key:\", info.filename, mapped_keys + extra_keys)\n    WriteOtacerts(output_tf_zip, info.filename, mapped_keys + extra_keys)\n\n\ndef ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):\n  \"\"\"Replaces META/misc_info.txt.\n\n  Only writes back the ones in the original META/misc_info.txt. Because the\n  current in-memory dict contains additional items computed at runtime.\n  \"\"\"\n  misc_info_old = common.LoadDictionaryFromLines(\n      input_zip.read('META/misc_info.txt').decode().split('\\n'))\n  items = []\n  for key in sorted(misc_info):\n    if key in misc_info_old:\n      items.append('%s=%s' % (key, misc_info[key]))\n  common.ZipWriteStr(output_zip, \"META/misc_info.txt\", '\\n'.join(items))\n\n\ndef ReplaceAvbSigningKeys(misc_info):\n  \"\"\"Replaces the AVB signing keys.\"\"\"\n\n  def ReplaceAvbPartitionSigningKey(partition):\n    key = OPTIONS.avb_keys.get(partition)\n    if not key:\n      return\n\n    algorithm = OPTIONS.avb_algorithms.get(partition)\n    assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)\n\n    print('Replacing AVB signing key for %s with \"%s\" (%s)' % (\n        partition, key, algorithm))\n    misc_info['avb_' + partition + '_algorithm'] = algorithm\n    misc_info['avb_' + partition + '_key_path'] = key\n\n    extra_args = OPTIONS.avb_extra_args.get(partition)\n    if extra_args:\n      print('Setting extra AVB signing args for %s to \"%s\"' % (\n          partition, extra_args))\n      args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(\n          partition,\n          # custom partition\n          \"avb_{}_add_hashtree_footer_args\".format(partition))\n      misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)\n\n  for partition in AVB_FOOTER_ARGS_BY_PARTITION:\n    ReplaceAvbPartitionSigningKey(partition)\n\n  for custom_partition in misc_info.get(\n          \"avb_custom_images_partition_list\", \"\").strip().split():\n    ReplaceAvbPartitionSigningKey(custom_partition)\n\n\ndef RewriteAvbProps(misc_info):\n  \"\"\"Rewrites the props in AVB signing args.\"\"\"\n  for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():\n    args = misc_info.get(args_key)\n    if not args:\n      continue\n\n    tokens = []\n    changed = False\n    for token in args.split():\n      fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)\n      if not token.startswith(fingerprint_key):\n        tokens.append(token)\n        continue\n      prefix, tag = token.rsplit('/', 1)\n      tokens.append('{}/{}'.format(prefix, EditTags(tag)))\n      changed = True\n\n    if changed:\n      result = ' '.join(tokens)\n      print('Rewriting AVB prop for {}:\\n'.format(partition))\n      print('  replace: {}'.format(args))\n      print('     with: {}'.format(result))\n      misc_info[args_key] = result\n\n\ndef BuildKeyMap(misc_info, key_mapping_options):\n  for s, d in key_mapping_options:\n    if s is None:   # -d option\n      devkey = misc_info.get(\"default_system_dev_certificate\",\n                             \"build/make/target/product/security/testkey\")\n      devkeydir = os.path.dirname(devkey)\n\n      OPTIONS.key_map.update({\n          devkeydir + \"/testkey\":  d + \"/releasekey\",\n          devkeydir + \"/devkey\":   d + \"/releasekey\",\n          devkeydir + \"/media\":    d + \"/media\",\n          devkeydir + \"/shared\":   d + \"/shared\",\n          devkeydir + \"/platform\": d + \"/platform\",\n          devkeydir + \"/networkstack\": d + \"/networkstack\",\n          devkeydir + \"/sdk_sandbox\": d + \"/sdk_sandbox\",\n      })\n    else:\n      OPTIONS.key_map[s] = d\n\n\ndef GetApiLevelAndCodename(input_tf_zip):\n  data = input_tf_zip.read(\"SYSTEM/build.prop\").decode()\n  api_level = None\n  codename = None\n  for line in data.split(\"\\n\"):\n    line = line.strip()\n    if line and line[0] != '#' and \"=\" in line:\n      key, value = line.split(\"=\", 1)\n      key = key.strip()\n      if key == \"ro.build.version.sdk\":\n        api_level = int(value.strip())\n      elif key == \"ro.build.version.codename\":\n        codename = value.strip()\n\n  if api_level is None:\n    raise ValueError(\"No ro.build.version.sdk in SYSTEM/build.prop\")\n  if codename is None:\n    raise ValueError(\"No ro.build.version.codename in SYSTEM/build.prop\")\n\n  return (api_level, codename)\n\n\ndef GetCodenameToApiLevelMap(input_tf_zip):\n  data = input_tf_zip.read(\"SYSTEM/build.prop\").decode()\n  api_level = None\n  codenames = None\n  for line in data.split(\"\\n\"):\n    line = line.strip()\n    if line and line[0] != '#' and \"=\" in line:\n      key, value = line.split(\"=\", 1)\n      key = key.strip()\n      if key == \"ro.build.version.sdk\":\n        api_level = int(value.strip())\n      elif key == \"ro.build.version.all_codenames\":\n        codenames = value.strip().split(\",\")\n\n  if api_level is None:\n    raise ValueError(\"No ro.build.version.sdk in SYSTEM/build.prop\")\n  if codenames is None:\n    raise ValueError(\"No ro.build.version.all_codenames in SYSTEM/build.prop\")\n\n  result = {}\n  for codename in codenames:\n    codename = codename.strip()\n    if codename:\n      result[codename] = api_level\n  return result\n\n\ndef ReadApexKeysInfo(tf_zip):\n  \"\"\"Parses the APEX keys info from a given target-files zip.\n\n  Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a\n  dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a\n  tuple of (payload_key, container_key, sign_tool).\n\n  Args:\n    tf_zip: The input target_files ZipFile (already open).\n\n  Returns:\n    (payload_key, container_key, sign_tool):\n      - payload_key contains the path to the payload signing key\n      - container_key contains the path to the container signing key\n      - sign_tool is an apex-specific signing tool for its payload contents\n  \"\"\"\n  keys = {}\n  for line in tf_zip.read('META/apexkeys.txt').decode().split('\\n'):\n    line = line.strip()\n    if not line:\n      continue\n    matches = re.match(\n        r'^name=\"(?P<NAME>.*)\"\\s+'\n        r'public_key=\"(?P<PAYLOAD_PUBLIC_KEY>.*)\"\\s+'\n        r'private_key=\"(?P<PAYLOAD_PRIVATE_KEY>.*)\"\\s+'\n        r'container_certificate=\"(?P<CONTAINER_CERT>.*)\"\\s+'\n        r'container_private_key=\"(?P<CONTAINER_PRIVATE_KEY>.*?)\"'\n        r'(\\s+partition=\"(?P<PARTITION>.*?)\")?'\n        r'(\\s+sign_tool=\"(?P<SIGN_TOOL>.*?)\")?$',\n        line)\n    if not matches:\n      continue\n\n    name = matches.group('NAME')\n    payload_private_key = matches.group(\"PAYLOAD_PRIVATE_KEY\")\n\n    def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):\n      pubkey_suffix_len = len(pubkey_suffix)\n      privkey_suffix_len = len(privkey_suffix)\n      return (pubkey.endswith(pubkey_suffix) and\n              privkey.endswith(privkey_suffix) and\n              pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])\n\n    # Check the container key names, as we'll carry them without the\n    # extensions. This doesn't apply to payload keys though, which we will use\n    # full names only.\n    container_cert = matches.group(\"CONTAINER_CERT\")\n    container_private_key = matches.group(\"CONTAINER_PRIVATE_KEY\")\n    if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':\n      container_key = 'PRESIGNED'\n    elif CompareKeys(\n            container_cert, OPTIONS.public_key_suffix,\n            container_private_key, OPTIONS.private_key_suffix):\n      container_key = container_cert[:-len(OPTIONS.public_key_suffix)]\n    else:\n      raise ValueError(\"Failed to parse container keys: \\n{}\".format(line))\n\n    sign_tool = matches.group(\"SIGN_TOOL\")\n    keys[name] = (payload_private_key, container_key, sign_tool)\n\n  return keys\n\n\ndef BuildVendorPartitions(output_zip_path):\n  \"\"\"Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools.\"\"\"\n  if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):\n    logger.warning(\"Allowed --vendor_partitions: %s\",\n                   \",\".join(ALLOWED_VENDOR_PARTITIONS))\n    OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(\n        OPTIONS.vendor_partitions)\n\n  logger.info(\"Building vendor partitions using vendor otatools.\")\n  vendor_tempdir = common.UnzipTemp(output_zip_path, [\n      \"META/*\",\n      \"SYSTEM/build.prop\",\n      \"RECOVERY/*\",\n      \"BOOT/*\",\n      \"OTA/\",\n  ] + [\"{}/*\".format(p.upper()) for p in OPTIONS.vendor_partitions])\n\n  # Disable various partitions that build based on misc_info fields.\n  # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using\n  # vendor otatools. These other partitions will be rebuilt using the main\n  # otatools if necessary.\n  vendor_misc_info_path = os.path.join(vendor_tempdir, \"META/misc_info.txt\")\n  vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)\n  # Ignore if not rebuilding recovery\n  if not OPTIONS.rebuild_recovery:\n    vendor_misc_info[\"no_boot\"] = \"true\"  # boot\n    vendor_misc_info[\"vendor_boot\"] = \"false\"  # vendor_boot\n    vendor_misc_info[\"no_recovery\"] = \"true\"  # recovery\n    vendor_misc_info[\"avb_enable\"] = \"false\"  # vbmeta\n\n  vendor_misc_info[\"has_dtbo\"] = \"false\"  # dtbo\n  vendor_misc_info[\"has_pvmfw\"] = \"false\"  # pvmfw\n  vendor_misc_info[\"avb_custom_images_partition_list\"] = \"\"  # avb custom images\n  vendor_misc_info[\"avb_building_vbmeta_image\"] = \"false\" # skip building vbmeta\n  vendor_misc_info[\"custom_images_partition_list\"] = \"\"  # custom images\n  vendor_misc_info[\"use_dynamic_partitions\"] = \"false\"  # super_empty\n  vendor_misc_info[\"build_super_partition\"] = \"false\"  # super split\n  vendor_misc_info[\"avb_vbmeta_system\"] = \"\"  # skip building vbmeta_system\n  with open(vendor_misc_info_path, \"w\") as output:\n    for key in sorted(vendor_misc_info):\n      output.write(\"{}={}\\n\".format(key, vendor_misc_info[key]))\n\n  # Disable system partition by a placeholder of IMAGES/system.img,\n  # instead of removing SYSTEM folder.\n  # Because SYSTEM/build.prop is still needed for:\n  #   add_img_to_target_files.CreateImage ->\n  #   common.BuildInfo ->\n  #   common.BuildInfo.CalculateFingerprint\n  vendor_images_path = os.path.join(vendor_tempdir, \"IMAGES\")\n  if not os.path.exists(vendor_images_path):\n    os.makedirs(vendor_images_path)\n  with open(os.path.join(vendor_images_path, \"system.img\"), \"w\") as output:\n    pass\n\n  # Disable care_map.pb as not all ab_partitions are available when\n  # vendor otatools regenerates vendor images.\n  if os.path.exists(os.path.join(vendor_tempdir, \"META/ab_partitions.txt\")):\n    os.remove(os.path.join(vendor_tempdir, \"META/ab_partitions.txt\"))\n  # Disable RADIO images\n  if os.path.exists(os.path.join(vendor_tempdir, \"META/pack_radioimages.txt\")):\n    os.remove(os.path.join(vendor_tempdir, \"META/pack_radioimages.txt\"))\n\n  # Build vendor images using vendor otatools.\n  # Accept either a zip file or extracted directory.\n  if os.path.isfile(OPTIONS.vendor_otatools):\n    vendor_otatools_dir = common.MakeTempDir(prefix=\"vendor_otatools_\")\n    common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)\n  else:\n    vendor_otatools_dir = OPTIONS.vendor_otatools\n  cmd = [\n      os.path.join(vendor_otatools_dir, \"bin\", \"add_img_to_target_files\"),\n      \"--is_signing\",\n      \"--add_missing\",\n      \"--verbose\",\n      vendor_tempdir,\n  ]\n  if OPTIONS.rebuild_recovery:\n    cmd.insert(4, \"--rebuild_recovery\")\n\n  common.RunAndCheckOutput(cmd, verbose=True)\n\n  logger.info(\"Writing vendor partitions to output archive.\")\n  with zipfile.ZipFile(\n      output_zip_path, \"a\", compression=zipfile.ZIP_DEFLATED,\n      allowZip64=True) as output_zip:\n    for p in OPTIONS.vendor_partitions:\n      img_file_path = \"IMAGES/{}.img\".format(p)\n      map_file_path = \"IMAGES/{}.map\".format(p)\n      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, img_file_path), img_file_path)\n      if os.path.exists(os.path.join(vendor_tempdir, map_file_path)):\n        common.ZipWrite(output_zip, os.path.join(vendor_tempdir, map_file_path), map_file_path)\n    # copy recovery.img, boot.img, recovery patch & install.sh\n    if OPTIONS.rebuild_recovery:\n      recovery_img = \"IMAGES/recovery.img\"\n      boot_img = \"IMAGES/boot.img\"\n      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_img), recovery_img)\n      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, boot_img), boot_img)\n      recovery_patch_path = \"VENDOR/recovery-from-boot.p\"\n      recovery_sh_path = \"VENDOR/bin/install-recovery.sh\"\n      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_patch_path), recovery_patch_path)\n      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_sh_path), recovery_sh_path)\n\n\ndef main(argv):\n\n  key_mapping_options = []\n\n  def option_handler(o, a):\n    if o in (\"-e\", \"--extra_apks\"):\n      names, key = a.split(\"=\")\n      names = names.split(\",\")\n      for n in names:\n        OPTIONS.extra_apks[n] = key\n    elif o == \"--extra_apex_payload_key\":\n      apex_names, key = a.split(\"=\")\n      for name in apex_names.split(\",\"):\n        OPTIONS.extra_apex_payload_keys[name] = key\n    elif o == \"--skip_apks_with_path_prefix\":\n      # Check the prefix, which must be in all upper case.\n      prefix = a.split('/')[0]\n      if not prefix or prefix != prefix.upper():\n        raise ValueError(\"Invalid path prefix '%s'\" % (a,))\n      OPTIONS.skip_apks_with_path_prefix.add(a)\n    elif o in (\"-d\", \"--default_key_mappings\"):\n      key_mapping_options.append((None, a))\n    elif o in (\"-k\", \"--key_mapping\"):\n      key_mapping_options.append(a.split(\"=\", 1))\n    elif o in (\"-o\", \"--replace_ota_keys\"):\n      OPTIONS.replace_ota_keys = True\n    elif o in (\"-t\", \"--tag_changes\"):\n      new = []\n      for i in a.split(\",\"):\n        i = i.strip()\n        if not i or i[0] not in \"-+\":\n          raise ValueError(\"Bad tag change '%s'\" % (i,))\n        new.append(i[0] + i[1:].strip())\n      OPTIONS.tag_changes = tuple(new)\n    elif o == \"--replace_verity_public_key\":\n      raise ValueError(\"--replace_verity_public_key is no longer supported,\"\n                       \" please switch to AVB\")\n    elif o == \"--replace_verity_private_key\":\n      raise ValueError(\"--replace_verity_private_key is no longer supported,\"\n                       \" please switch to AVB\")\n    elif o == \"--replace_verity_keyid\":\n      raise ValueError(\"--replace_verity_keyid is no longer supported, please\"\n                       \" switch to AVB\")\n    elif o == \"--remove_avb_public_keys\":\n      OPTIONS.remove_avb_public_keys = a.split(\",\")\n    elif o == \"--avb_vbmeta_key\":\n      OPTIONS.avb_keys['vbmeta'] = a\n    elif o == \"--avb_vbmeta_algorithm\":\n      OPTIONS.avb_algorithms['vbmeta'] = a\n    elif o == \"--avb_vbmeta_extra_args\":\n      OPTIONS.avb_extra_args['vbmeta'] = a\n    elif o == \"--avb_boot_key\":\n      OPTIONS.avb_keys['boot'] = a\n    elif o == \"--avb_boot_algorithm\":\n      OPTIONS.avb_algorithms['boot'] = a\n    elif o == \"--avb_boot_extra_args\":\n      OPTIONS.avb_extra_args['boot'] = a\n    elif o == \"--avb_dtbo_key\":\n      OPTIONS.avb_keys['dtbo'] = a\n    elif o == \"--avb_dtbo_algorithm\":\n      OPTIONS.avb_algorithms['dtbo'] = a\n    elif o == \"--avb_dtbo_extra_args\":\n      OPTIONS.avb_extra_args['dtbo'] = a\n    elif o == \"--avb_init_boot_key\":\n      OPTIONS.avb_keys['init_boot'] = a\n    elif o == \"--avb_init_boot_algorithm\":\n      OPTIONS.avb_algorithms['init_boot'] = a\n    elif o == \"--avb_init_boot_extra_args\":\n      OPTIONS.avb_extra_args['init_boot'] = a\n    elif o == \"--avb_recovery_key\":\n      OPTIONS.avb_keys['recovery'] = a\n    elif o == \"--avb_recovery_algorithm\":\n      OPTIONS.avb_algorithms['recovery'] = a\n    elif o == \"--avb_recovery_extra_args\":\n      OPTIONS.avb_extra_args['recovery'] = a\n    elif o == \"--avb_system_key\":\n      OPTIONS.avb_keys['system'] = a\n    elif o == \"--avb_system_algorithm\":\n      OPTIONS.avb_algorithms['system'] = a\n    elif o == \"--avb_system_extra_args\":\n      OPTIONS.avb_extra_args['system'] = a\n    elif o == \"--avb_system_other_key\":\n      OPTIONS.avb_keys['system_other'] = a\n    elif o == \"--avb_system_other_algorithm\":\n      OPTIONS.avb_algorithms['system_other'] = a\n    elif o == \"--avb_system_other_extra_args\":\n      OPTIONS.avb_extra_args['system_other'] = a\n    elif o == \"--avb_vendor_key\":\n      OPTIONS.avb_keys['vendor'] = a\n    elif o == \"--avb_vendor_algorithm\":\n      OPTIONS.avb_algorithms['vendor'] = a\n    elif o == \"--avb_vendor_extra_args\":\n      OPTIONS.avb_extra_args['vendor'] = a\n    elif o == \"--avb_vbmeta_system_key\":\n      OPTIONS.avb_keys['vbmeta_system'] = a\n    elif o == \"--avb_vbmeta_system_algorithm\":\n      OPTIONS.avb_algorithms['vbmeta_system'] = a\n    elif o == \"--avb_vbmeta_system_extra_args\":\n      OPTIONS.avb_extra_args['vbmeta_system'] = a\n    elif o == \"--avb_vbmeta_vendor_key\":\n      OPTIONS.avb_keys['vbmeta_vendor'] = a\n    elif o == \"--avb_vbmeta_vendor_algorithm\":\n      OPTIONS.avb_algorithms['vbmeta_vendor'] = a\n    elif o == \"--avb_vbmeta_vendor_extra_args\":\n      OPTIONS.avb_extra_args['vbmeta_vendor'] = a\n    elif o == \"--avb_apex_extra_args\":\n      OPTIONS.avb_extra_args['apex'] = a\n    elif o == \"--avb_extra_custom_image_key\":\n      partition, key = a.split(\"=\")\n      OPTIONS.avb_keys[partition] = key\n    elif o == \"--avb_extra_custom_image_algorithm\":\n      partition, algorithm = a.split(\"=\")\n      OPTIONS.avb_algorithms[partition] = algorithm\n    elif o == \"--avb_extra_custom_image_extra_args\":\n      # Setting the maxsplit parameter to one, which will return a list with\n      # two elements. e.g., the second '=' should not be splitted for\n      # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.\n      partition, extra_args = a.split(\"=\", 1)\n      OPTIONS.avb_extra_args[partition] = extra_args\n    elif o == \"--vendor_otatools\":\n      OPTIONS.vendor_otatools = a\n    elif o == \"--vendor_partitions\":\n      OPTIONS.vendor_partitions = set(a.split(\",\"))\n    elif o == \"--allow_gsi_debug_sepolicy\":\n      OPTIONS.allow_gsi_debug_sepolicy = True\n    elif o == \"--override_apk_keys\":\n      OPTIONS.override_apk_keys = a\n    elif o == \"--override_apex_keys\":\n      OPTIONS.override_apex_keys = a\n    elif o in (\"--gki_signing_key\",  \"--gki_signing_algorithm\",  \"--gki_signing_extra_args\"):\n      print(f\"{o} is deprecated and does nothing\")\n    else:\n      return False\n    return True\n\n  args = common.ParseOptions(\n      argv, __doc__,\n      extra_opts=\"e:d:k:ot:\",\n      extra_long_opts=[\n          \"extra_apks=\",\n          \"extra_apex_payload_key=\",\n          \"skip_apks_with_path_prefix=\",\n          \"default_key_mappings=\",\n          \"key_mapping=\",\n          \"replace_ota_keys\",\n          \"tag_changes=\",\n          \"replace_verity_public_key=\",\n          \"replace_verity_private_key=\",\n          \"replace_verity_keyid=\",\n          \"remove_avb_public_keys=\",\n          \"avb_apex_extra_args=\",\n          \"avb_vbmeta_algorithm=\",\n          \"avb_vbmeta_key=\",\n          \"avb_vbmeta_extra_args=\",\n          \"avb_boot_algorithm=\",\n          \"avb_boot_key=\",\n          \"avb_boot_extra_args=\",\n          \"avb_dtbo_algorithm=\",\n          \"avb_dtbo_key=\",\n          \"avb_dtbo_extra_args=\",\n          \"avb_init_boot_algorithm=\",\n          \"avb_init_boot_key=\",\n          \"avb_init_boot_extra_args=\",\n          \"avb_recovery_algorithm=\",\n          \"avb_recovery_key=\",\n          \"avb_recovery_extra_args=\",\n          \"avb_system_algorithm=\",\n          \"avb_system_key=\",\n          \"avb_system_extra_args=\",\n          \"avb_system_other_algorithm=\",\n          \"avb_system_other_key=\",\n          \"avb_system_other_extra_args=\",\n          \"avb_vendor_algorithm=\",\n          \"avb_vendor_key=\",\n          \"avb_vendor_extra_args=\",\n          \"avb_vbmeta_system_algorithm=\",\n          \"avb_vbmeta_system_key=\",\n          \"avb_vbmeta_system_extra_args=\",\n          \"avb_vbmeta_vendor_algorithm=\",\n          \"avb_vbmeta_vendor_key=\",\n          \"avb_vbmeta_vendor_extra_args=\",\n          \"avb_extra_custom_image_key=\",\n          \"avb_extra_custom_image_algorithm=\",\n          \"avb_extra_custom_image_extra_args=\",\n          \"gki_signing_key=\",\n          \"gki_signing_algorithm=\",\n          \"gki_signing_extra_args=\",\n          \"vendor_partitions=\",\n          \"vendor_otatools=\",\n          \"allow_gsi_debug_sepolicy\",\n          \"override_apk_keys=\",\n          \"override_apex_keys=\",\n      ],\n      extra_option_handler=[option_handler, payload_signer.signer_options])\n\n  if len(args) != 2:\n    common.Usage(__doc__)\n    sys.exit(1)\n\n  common.InitLogging()\n\n  input_zip = zipfile.ZipFile(args[0], \"r\", allowZip64=True)\n  output_zip = zipfile.ZipFile(args[1], \"w\",\n                               compression=zipfile.ZIP_DEFLATED,\n                               allowZip64=True)\n\n  misc_info = common.LoadInfoDict(input_zip)\n  if OPTIONS.package_key is None:\n      OPTIONS.package_key = misc_info.get(\n          \"default_system_dev_certificate\",\n          \"build/make/target/product/security/testkey\")\n\n  BuildKeyMap(misc_info, key_mapping_options)\n\n  apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)\n  apk_keys = GetApkCerts(apk_keys_info)\n\n  apex_keys_info = ReadApexKeysInfo(input_zip)\n  apex_keys = GetApexKeys(apex_keys_info, apk_keys)\n\n  # TODO(xunchang) check for the apks inside the apex files, and abort early if\n  # the keys are not available.\n  CheckApkAndApexKeysAvailable(\n      input_zip,\n      set(apk_keys.keys()) | set(apex_keys.keys()),\n      compressed_extension,\n      apex_keys)\n\n  key_passwords = common.GetKeyPasswords(\n      set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))\n  platform_api_level, _ = GetApiLevelAndCodename(input_zip)\n  codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)\n\n  ProcessTargetFiles(input_zip, output_zip, misc_info,\n                     apk_keys, apex_keys, key_passwords,\n                     platform_api_level, codename_to_api_level_map,\n                     compressed_extension)\n\n  common.ZipClose(input_zip)\n  common.ZipClose(output_zip)\n\n  if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:\n    BuildVendorPartitions(args[1])\n\n  # Skip building userdata.img and cache.img when signing the target files.\n  new_args = [\"--is_signing\", \"--add_missing\", \"--verbose\"]\n  # add_img_to_target_files builds the system image from scratch, so the\n  # recovery patch is guaranteed to be regenerated there.\n  if OPTIONS.rebuild_recovery:\n    new_args.append(\"--rebuild_recovery\")\n  new_args.append(args[1])\n  add_img_to_target_files.main(new_args)\n\n  print(\"done.\")\n\n\nif __name__ == '__main__':\n  try:\n    main(sys.argv[1:])\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/sparse_img.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2014 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import print_function\n\nimport argparse\nimport bisect\nimport logging\nimport os\nimport struct\nimport threading\nfrom hashlib import sha1\n\nimport rangelib\n\nlogger = logging.getLogger(__name__)\n\n\nclass SparseImage(object):\n  \"\"\"Wraps a sparse image file into an image object.\n\n  Wraps a sparse image file (and optional file map and clobbered_blocks) into\n  an image object suitable for passing to BlockImageDiff. file_map contains\n  the mapping between files and their blocks. clobbered_blocks contains the set\n  of blocks that should be always written to the target regardless of the old\n  contents (i.e. copying instead of patching). clobbered_blocks should be in\n  the form of a string like \"0\" or \"0 1-5 8\".\n  \"\"\"\n\n  def __init__(self, simg_fn, file_map_fn=None, clobbered_blocks=None,\n               mode=\"rb\", build_map=True, allow_shared_blocks=False):\n    self.simg_f = f = open(simg_fn, mode)\n\n    header_bin = f.read(28)\n    header = struct.unpack(\"<I4H4I\", header_bin)\n\n    magic = header[0]\n    major_version = header[1]\n    minor_version = header[2]\n    file_hdr_sz = header[3]\n    chunk_hdr_sz = header[4]\n    self.blocksize = blk_sz = header[5]\n    self.total_blocks = total_blks = header[6]\n    self.total_chunks = total_chunks = header[7]\n\n    if magic != 0xED26FF3A:\n      raise ValueError(\"Magic should be 0xED26FF3A but is 0x%08X\" % (magic,))\n    if major_version != 1 or minor_version != 0:\n      raise ValueError(\"I know about version 1.0, but this is version %u.%u\" %\n                       (major_version, minor_version))\n    if file_hdr_sz != 28:\n      raise ValueError(\"File header size was expected to be 28, but is %u.\" %\n                       (file_hdr_sz,))\n    if chunk_hdr_sz != 12:\n      raise ValueError(\"Chunk header size was expected to be 12, but is %u.\" %\n                       (chunk_hdr_sz,))\n\n    logger.info(\n        \"Total of %u %u-byte output blocks in %u input chunks.\", total_blks,\n        blk_sz, total_chunks)\n\n    if not build_map:\n      return\n\n    pos = 0   # in blocks\n    care_data = []\n    self.offset_map = offset_map = []\n    self.clobbered_blocks = rangelib.RangeSet(data=clobbered_blocks)\n\n    for _ in range(total_chunks):\n      header_bin = f.read(12)\n      header = struct.unpack(\"<2H2I\", header_bin)\n      chunk_type = header[0]\n      chunk_sz = header[2]\n      total_sz = header[3]\n      data_sz = total_sz - 12\n\n      if chunk_type == 0xCAC1:\n        if data_sz != (chunk_sz * blk_sz):\n          raise ValueError(\n              \"Raw chunk input size (%u) does not match output size (%u)\" %\n              (data_sz, chunk_sz * blk_sz))\n        else:\n          care_data.append(pos)\n          care_data.append(pos + chunk_sz)\n          offset_map.append((pos, chunk_sz, f.tell(), None))\n          pos += chunk_sz\n          f.seek(data_sz, os.SEEK_CUR)\n\n      elif chunk_type == 0xCAC2:\n        fill_data = f.read(4)\n        care_data.append(pos)\n        care_data.append(pos + chunk_sz)\n        offset_map.append((pos, chunk_sz, None, fill_data))\n        pos += chunk_sz\n\n      elif chunk_type == 0xCAC3:\n        if data_sz != 0:\n          raise ValueError(\"Don't care chunk input size is non-zero (%u)\" %\n                           (data_sz))\n\n        pos += chunk_sz\n\n      elif chunk_type == 0xCAC4:\n        raise ValueError(\"CRC32 chunks are not supported\")\n\n      else:\n        raise ValueError(\"Unknown chunk type 0x%04X not supported\" %\n                         (chunk_type,))\n\n    self.generator_lock = threading.Lock()\n\n    self.care_map = rangelib.RangeSet(care_data)\n    self.offset_index = [i[0] for i in offset_map]\n\n    # Bug: 20881595\n    # Introduce extended blocks as a workaround for the bug. dm-verity may\n    # touch blocks that are not in the care_map due to block device\n    # read-ahead. It will fail if such blocks contain non-zeroes. We zero out\n    # the extended blocks explicitly to avoid dm-verity failures. 512 blocks\n    # are the maximum read-ahead we configure for dm-verity block devices.\n    extended = self.care_map.extend(512)\n    all_blocks = rangelib.RangeSet(data=(0, self.total_blocks))\n    extended = extended.intersect(all_blocks).subtract(self.care_map)\n    self.extended = extended\n\n    if file_map_fn:\n      self.LoadFileBlockMap(file_map_fn, self.clobbered_blocks,\n                            allow_shared_blocks)\n    else:\n      self.file_map = {\"__DATA\": self.care_map}\n\n  def AppendFillChunk(self, data, blocks):\n    f = self.simg_f\n\n    # Append a fill chunk\n    f.seek(0, os.SEEK_END)\n    f.write(struct.pack(\"<2H3I\", 0xCAC2, 0, blocks, 16, data))\n\n    # Update the sparse header\n    self.total_blocks += blocks\n    self.total_chunks += 1\n\n    f.seek(16, os.SEEK_SET)\n    f.write(struct.pack(\"<2I\", self.total_blocks, self.total_chunks))\n\n  def RangeSha1(self, ranges):\n    h = sha1()\n    for data in self._GetRangeData(ranges):\n      h.update(data)\n    return h.hexdigest()\n\n  def ReadRangeSet(self, ranges):\n    return [d for d in self._GetRangeData(ranges)]\n\n  def ReadBlocks(self, start=0, num_blocks=None):\n    if num_blocks is None:\n      num_blocks = self.total_blocks\n    return self._GetRangeData([(start, start + num_blocks)])\n\n  def TotalSha1(self, include_clobbered_blocks=False):\n    \"\"\"Return the SHA-1 hash of all data in the 'care' regions.\n\n    If include_clobbered_blocks is True, it returns the hash including the\n    clobbered_blocks.\"\"\"\n    ranges = self.care_map\n    if not include_clobbered_blocks:\n      ranges = ranges.subtract(self.clobbered_blocks)\n    return self.RangeSha1(ranges)\n\n  def WriteRangeDataToFd(self, ranges, fd):\n    for data in self._GetRangeData(ranges):\n      fd.write(data)\n\n  def _GetRangeData(self, ranges):\n    \"\"\"Generator that produces all the image data in 'ranges'.  The\n    number of individual pieces returned is arbitrary (and in\n    particular is not necessarily equal to the number of ranges in\n    'ranges'.\n\n    Use a lock to protect the generator so that we will not run two\n    instances of this generator on the same object simultaneously.\"\"\"\n\n    f = self.simg_f\n    with self.generator_lock:\n      for s, e in ranges:\n        to_read = e-s\n        idx = bisect.bisect_right(self.offset_index, s) - 1\n        chunk_start, chunk_len, filepos, fill_data = self.offset_map[idx]\n\n        # for the first chunk we may be starting partway through it.\n        remain = chunk_len - (s - chunk_start)\n        this_read = min(remain, to_read)\n        if filepos is not None:\n          p = filepos + ((s - chunk_start) * self.blocksize)\n          f.seek(p, os.SEEK_SET)\n          yield f.read(this_read * self.blocksize)\n        else:\n          yield fill_data * (this_read * (self.blocksize >> 2))\n        to_read -= this_read\n\n        while to_read > 0:\n          # continue with following chunks if this range spans multiple chunks.\n          idx += 1\n          chunk_start, chunk_len, filepos, fill_data = self.offset_map[idx]\n          this_read = min(chunk_len, to_read)\n          if filepos is not None:\n            f.seek(filepos, os.SEEK_SET)\n            yield f.read(this_read * self.blocksize)\n          else:\n            yield fill_data * (this_read * (self.blocksize >> 2))\n          to_read -= this_read\n\n  def LoadFileBlockMap(self, fn, clobbered_blocks, allow_shared_blocks):\n    \"\"\"Loads the given block map file.\n\n    Args:\n      fn: The filename of the block map file.\n      clobbered_blocks: A RangeSet instance for the clobbered blocks.\n      allow_shared_blocks: Whether having shared blocks is allowed.\n    \"\"\"\n    remaining = self.care_map\n    self.file_map = out = {}\n\n    with open(fn) as f:\n      for line in f:\n        fn, ranges_text = line.rstrip().split(None, 1)\n        raw_ranges = rangelib.RangeSet.parse(ranges_text)\n\n        # Note: e2fsdroid records holes in the extent tree as \"0\" blocks.\n        # This causes confusion because clobbered_blocks always includes\n        # the superblock (physical block #0). Since the 0 blocks here do\n        # not represent actual physical blocks, remove them from the set.\n        ranges = raw_ranges.subtract(rangelib.RangeSet(\"0\"))\n        # b/150334561 we need to perserve the monotonic property of the raw\n        # range. Otherwise, the validation script will read the blocks with\n        # wrong order when pulling files from the image.\n        ranges.monotonic = raw_ranges.monotonic\n        ranges.extra['text_str'] = ranges_text\n\n        if allow_shared_blocks:\n          # Find the shared blocks that have been claimed by others. If so, tag\n          # the entry so that we can skip applying imgdiff on this file.\n          shared_blocks = ranges.subtract(remaining)\n          if shared_blocks:\n            non_shared = ranges.subtract(shared_blocks)\n            if not non_shared:\n              continue\n\n            # Put the non-shared RangeSet as the value in the block map, which\n            # has a copy of the original RangeSet.\n            non_shared.extra['uses_shared_blocks'] = ranges\n            ranges = non_shared\n\n        out[fn] = ranges\n        assert ranges.size() == ranges.intersect(remaining).size()\n\n        # Currently we assume that blocks in clobbered_blocks are not part of\n        # any file.\n        assert not clobbered_blocks.overlaps(ranges)\n        remaining = remaining.subtract(ranges)\n\n    remaining = remaining.subtract(clobbered_blocks)\n\n    # For all the remaining blocks in the care_map (ie, those that\n    # aren't part of the data for any file nor part of the clobbered_blocks),\n    # divide them into blocks that are all zero and blocks that aren't.\n    # (Zero blocks are handled specially because (1) there are usually\n    # a lot of them and (2) bsdiff handles files with long sequences of\n    # repeated bytes especially poorly.)\n\n    zero_blocks = []\n    nonzero_blocks = []\n    reference = '\\0' * self.blocksize\n\n    # Workaround for bug 23227672. For squashfs, we don't have a system.map. So\n    # the whole system image will be treated as a single file. But for some\n    # unknown bug, the updater will be killed due to OOM when writing back the\n    # patched image to flash (observed on lenok-userdebug MEA49). Prior to\n    # getting a real fix, we evenly divide the non-zero blocks into smaller\n    # groups (currently 1024 blocks or 4MB per group).\n    # Bug: 23227672\n    MAX_BLOCKS_PER_GROUP = 1024\n    nonzero_groups = []\n\n    f = self.simg_f\n    for s, e in remaining:\n      for b in range(s, e):\n        idx = bisect.bisect_right(self.offset_index, b) - 1\n        chunk_start, _, filepos, fill_data = self.offset_map[idx]\n        if filepos is not None:\n          filepos += (b-chunk_start) * self.blocksize\n          f.seek(filepos, os.SEEK_SET)\n          data = f.read(self.blocksize)\n        else:\n          if fill_data == reference[:4]:   # fill with all zeros\n            data = reference\n          else:\n            data = None\n\n        if data == reference:\n          zero_blocks.append(b)\n          zero_blocks.append(b+1)\n        else:\n          nonzero_blocks.append(b)\n          nonzero_blocks.append(b+1)\n\n          if len(nonzero_blocks) >= MAX_BLOCKS_PER_GROUP:\n            nonzero_groups.append(nonzero_blocks)\n            # Clear the list.\n            nonzero_blocks = []\n\n    if nonzero_blocks:\n      nonzero_groups.append(nonzero_blocks)\n      nonzero_blocks = []\n\n    assert zero_blocks or nonzero_groups or clobbered_blocks\n\n    if zero_blocks:\n      out[\"__ZERO\"] = rangelib.RangeSet(data=zero_blocks)\n    if nonzero_groups:\n      for i, blocks in enumerate(nonzero_groups):\n        out[\"__NONZERO-%d\" % i] = rangelib.RangeSet(data=blocks)\n    if clobbered_blocks:\n      out[\"__COPY\"] = clobbered_blocks\n\n  def ResetFileMap(self):\n    \"\"\"Throw away the file map and treat the entire image as\n    undifferentiated data.\"\"\"\n    self.file_map = {\"__DATA\": self.care_map}\n\n\ndef GetImagePartitionSize(img):\n  try:\n    simg = SparseImage(img, build_map=False)\n    return simg.blocksize * simg.total_blocks\n  except ValueError:\n    return os.path.getsize(img)\n\n\nif __name__ == '__main__':\n  parser = argparse.ArgumentParser()\n  parser.add_argument('image')\n  parser.add_argument('--get_partition_size', action='store_true',\n                      help='Return partition size of the image')\n  args = parser.parse_args()\n  if args.get_partition_size:\n    print(GetImagePartitionSize(args.image))\n"
  },
  {
    "path": "tools/releasetools/target_files_diff.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# Finds differences between two target files packages\n#\n\nfrom __future__ import print_function\n\nimport argparse\nimport contextlib\nimport os\nimport re\nimport subprocess\nimport sys\nimport tempfile\n\ndef ignore(name):\n  \"\"\"\n  Files to ignore when diffing\n\n  These are packages that we're already diffing elsewhere,\n  or files that we expect to be different for every build,\n  or known problems.\n  \"\"\"\n\n  # We're looking at the files that make the images, so no need to search them\n  if name in ['IMAGES']:\n    return True\n  # These are packages of the recovery partition, which we're already diffing\n  if name in ['SYSTEM/etc/recovery-resource.dat',\n              'SYSTEM/recovery-from-boot.p']:\n    return True\n\n  # These files are just the BUILD_NUMBER, and will always be different\n  if name in ['BOOT/RAMDISK/selinux_version',\n              'RECOVERY/RAMDISK/selinux_version']:\n    return True\n\n  return False\n\n\ndef rewrite_build_property(original, new):\n  \"\"\"\n  Rewrite property files to remove values known to change for every build\n  \"\"\"\n\n  skipped = ['ro.bootimage.build.date=',\n             'ro.bootimage.build.date.utc=',\n             'ro.bootimage.build.fingerprint=',\n             'ro.build.id=',\n             'ro.build.display.id=',\n             'ro.build.version.incremental=',\n             'ro.build.date=',\n             'ro.build.date.utc=',\n             'ro.build.host=',\n             'ro.build.user=',\n             'ro.build.description=',\n             'ro.build.fingerprint=',\n             'ro.vendor.build.date=',\n             'ro.vendor.build.date.utc=',\n             'ro.vendor.build.fingerprint=']\n\n  for line in original:\n    skip = False\n    for s in skipped:\n      if line.startswith(s):\n        skip = True\n        break\n    if not skip:\n      new.write(line.encode())\n\n\ndef trim_install_recovery(original, new):\n  \"\"\"\n  Rewrite the install-recovery script to remove the hash of the recovery\n  partition.\n  \"\"\"\n  for line in original:\n    new.write(re.sub(r'[0-9a-f]{40}', '0'*40, line).encode())\n\ndef sort_file(original, new):\n  \"\"\"\n  Sort the file. Some OTA metadata files are not in a deterministic order\n  currently.\n  \"\"\"\n  lines = original.readlines()\n  lines.sort()\n  for line in lines:\n    new.write(line.encode())\n\n# Map files to the functions that will modify them for diffing\nREWRITE_RULES = {\n    'BOOT/RAMDISK/default.prop': rewrite_build_property,\n    'RECOVERY/RAMDISK/default.prop': rewrite_build_property,\n    'SYSTEM/build.prop': rewrite_build_property,\n    'VENDOR/build.prop': rewrite_build_property,\n\n    'SYSTEM/bin/install-recovery.sh': trim_install_recovery,\n\n    'META/boot_filesystem_config.txt': sort_file,\n    'META/filesystem_config.txt': sort_file,\n    'META/recovery_filesystem_config.txt': sort_file,\n    'META/vendor_filesystem_config.txt': sort_file,\n}\n\n@contextlib.contextmanager\ndef preprocess(name, filename):\n  \"\"\"\n  Optionally rewrite files before diffing them, to remove known-variable\n  information.\n  \"\"\"\n  if name in REWRITE_RULES:\n    with tempfile.NamedTemporaryFile() as newfp:\n      with open(filename, 'r') as oldfp:\n        REWRITE_RULES[name](oldfp, newfp)\n      newfp.flush()\n      yield newfp.name\n  else:\n    yield filename\n\ndef diff(name, file1, file2, out_file):\n  \"\"\"\n  Diff a file pair with diff, running preprocess() on the arguments first.\n  \"\"\"\n  with preprocess(name, file1) as f1:\n    with preprocess(name, file2) as f2:\n      proc = subprocess.Popen(['diff', f1, f2], stdout=subprocess.PIPE,\n                              stderr=subprocess.STDOUT)\n      (stdout, _) = proc.communicate()\n      if proc.returncode == 0:\n        return\n      stdout = stdout.strip()\n      if stdout == 'Binary files %s and %s differ' % (f1, f2):\n        print(\"%s: Binary files differ\" % name, file=out_file)\n      else:\n        for line in stdout.strip().split(b'\\n'):\n          print(\"%s: %s\" % (name, line), file=out_file)\n\ndef recursiveDiff(prefix, dir1, dir2, out_file):\n  \"\"\"\n  Recursively diff two directories, checking metadata then calling diff()\n  \"\"\"\n  list1 = sorted(os.listdir(dir1))\n  list2 = sorted(os.listdir(dir2))\n\n  for entry in list1:\n    name = os.path.join(prefix, entry)\n    name1 = os.path.join(dir1, entry)\n    name2 = os.path.join(dir2, entry)\n\n    if ignore(name):\n      continue\n\n    if entry in list2:\n      if os.path.islink(name1) and os.path.islink(name2):\n        link1 = os.readlink(name1)\n        link2 = os.readlink(name2)\n        if link1 != link2:\n          print(\"%s: Symlinks differ: %s vs %s\" % (name, link1, link2),\n                file=out_file)\n        continue\n      elif os.path.islink(name1) or os.path.islink(name2):\n        print(\"%s: File types differ, skipping compare\" % name, file=out_file)\n        continue\n\n      stat1 = os.stat(name1)\n      stat2 = os.stat(name2)\n      type1 = stat1.st_mode & ~0o777\n      type2 = stat2.st_mode & ~0o777\n\n      if type1 != type2:\n        print(\"%s: File types differ, skipping compare\" % name, file=out_file)\n        continue\n\n      if stat1.st_mode != stat2.st_mode:\n        print(\"%s: Modes differ: %o vs %o\" %\n            (name, stat1.st_mode, stat2.st_mode), file=out_file)\n\n      if os.path.isdir(name1):\n        recursiveDiff(name, name1, name2, out_file)\n      elif os.path.isfile(name1):\n        diff(name, name1, name2, out_file)\n      else:\n        print(\"%s: Unknown file type, skipping compare\" % name, file=out_file)\n    else:\n      print(\"%s: Only in base package\" % name, file=out_file)\n\n  for entry in list2:\n    name = os.path.join(prefix, entry)\n    name1 = os.path.join(dir1, entry)\n    name2 = os.path.join(dir2, entry)\n\n    if ignore(name):\n      continue\n\n    if entry not in list1:\n      print(\"%s: Only in new package\" % name, file=out_file)\n\ndef main():\n  parser = argparse.ArgumentParser()\n  parser.add_argument('dir1', help='The base target files package (extracted)')\n  parser.add_argument('dir2', help='The new target files package (extracted)')\n  parser.add_argument('--output',\n      help='The output file, otherwise it prints to stdout')\n  args = parser.parse_args()\n\n  if args.output:\n    out_file = open(args.output, 'w')\n  else:\n    out_file = sys.stdout\n\n  recursiveDiff('', args.dir1, args.dir2, out_file)\n\n  if args.output:\n    out_file.close()\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tools/releasetools/test_add_img_to_target_files.py",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport os\nimport os.path\nimport tempfile\nimport zipfile\n\nimport common\nimport test_utils\nfrom add_img_to_target_files import (\n    AddPackRadioImages,\n    AddCareMapForAbOta, GetCareMap,\n    CheckAbOtaImages)\nfrom rangelib import RangeSet\n\n\nOPTIONS = common.OPTIONS\n\n\nclass AddImagesToTargetFilesTest(test_utils.ReleaseToolsTestCase):\n\n  def setUp(self):\n    OPTIONS.input_tmp = common.MakeTempDir()\n\n  @staticmethod\n  def _create_images(images, prefix):\n    \"\"\"Creates images under OPTIONS.input_tmp/prefix.\"\"\"\n    path = os.path.join(OPTIONS.input_tmp, prefix)\n    if not os.path.exists(path):\n      os.mkdir(path)\n\n    for image in images:\n      image_path = os.path.join(path, image + '.img')\n      with open(image_path, 'wb') as image_fp:\n        image_fp.write(image.encode())\n\n    images_path = os.path.join(OPTIONS.input_tmp, 'IMAGES')\n    if not os.path.exists(images_path):\n      os.mkdir(images_path)\n    return images, images_path\n\n  def test_CheckAbOtaImages_imageExistsUnderImages(self):\n    \"\"\"Tests the case with existing images under IMAGES/.\"\"\"\n    images, _ = self._create_images(['aboot', 'xbl'], 'IMAGES')\n    CheckAbOtaImages(None, images)\n\n  def test_CheckAbOtaImages_imageExistsUnderRadio(self):\n    \"\"\"Tests the case with some image under RADIO/.\"\"\"\n    images, _ = self._create_images(['system', 'vendor'], 'IMAGES')\n    radio_path = os.path.join(OPTIONS.input_tmp, 'RADIO')\n    if not os.path.exists(radio_path):\n      os.mkdir(radio_path)\n    with open(os.path.join(radio_path, 'modem.img'), 'wb') as image_fp:\n      image_fp.write('modem'.encode())\n    CheckAbOtaImages(None, images + ['modem'])\n\n  def test_CheckAbOtaImages_missingImages(self):\n    images, _ = self._create_images(['aboot', 'xbl'], 'RADIO')\n    self.assertRaises(\n        AssertionError, CheckAbOtaImages, None, images + ['baz'])\n\n  def test_AddPackRadioImages(self):\n    images, images_path = self._create_images(['foo', 'bar'], 'RADIO')\n    AddPackRadioImages(None, images)\n\n    for image in images:\n      self.assertTrue(\n          os.path.exists(os.path.join(images_path, image + '.img')))\n\n  def test_AddPackRadioImages_with_suffix(self):\n    images, images_path = self._create_images(['foo', 'bar'], 'RADIO')\n    images_with_suffix = [image + '.img' for image in images]\n    AddPackRadioImages(None, images_with_suffix)\n\n    for image in images:\n      self.assertTrue(\n          os.path.exists(os.path.join(images_path, image + '.img')))\n\n  def test_AddPackRadioImages_zipOutput(self):\n    images, _ = self._create_images(['foo', 'bar'], 'RADIO')\n\n    # Set up the output zip.\n    output_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:\n      AddPackRadioImages(output_zip, images)\n\n    with zipfile.ZipFile(output_file, 'r', allowZip64=True) as verify_zip:\n      for image in images:\n        self.assertIn('IMAGES/' + image + '.img', verify_zip.namelist())\n\n  def test_AddPackRadioImages_imageExists(self):\n    images, images_path = self._create_images(['foo', 'bar'], 'RADIO')\n\n    # Additionally create images under IMAGES/ so that they should be skipped.\n    images, images_path = self._create_images(['foo', 'bar'], 'IMAGES')\n\n    AddPackRadioImages(None, images)\n\n    for image in images:\n      self.assertTrue(\n          os.path.exists(os.path.join(images_path, image + '.img')))\n\n  def test_AddPackRadioImages_missingImages(self):\n    images, _ = self._create_images(['foo', 'bar'], 'RADIO')\n    AddPackRadioImages(None, images)\n\n    self.assertRaises(AssertionError, AddPackRadioImages, None,\n                      images + ['baz'])\n\n  @staticmethod\n  def _test_AddCareMapForAbOta():\n    \"\"\"Helper function to set up the test for test_AddCareMapForAbOta().\"\"\"\n    OPTIONS.info_dict = {\n        'system_verity_block_device': '/dev/block/system',\n        'vendor_verity_block_device': '/dev/block/vendor',\n        'system.build.prop': common.PartitionBuildProps.FromDictionary(\n            'system', {\n                'ro.system.build.fingerprint':\n                'google/sailfish/12345:user/dev-keys'}\n        ),\n        'vendor.build.prop': common.PartitionBuildProps.FromDictionary(\n            'vendor', {\n                'ro.vendor.build.fingerprint':\n                'google/sailfish/678:user/dev-keys'}\n        ),\n    }\n\n    # Prepare the META/ folder.\n    meta_path = os.path.join(OPTIONS.input_tmp, 'META')\n    if not os.path.exists(meta_path):\n      os.mkdir(meta_path)\n\n    system_image = test_utils.construct_sparse_image([\n        (0xCAC1, 6),\n        (0xCAC3, 4),\n        (0xCAC1, 6)], \"system\")\n    vendor_image = test_utils.construct_sparse_image([\n        (0xCAC2, 10)], \"vendor\")\n\n    image_paths = {\n        'system': system_image,\n        'vendor': vendor_image,\n    }\n    return image_paths\n\n  def _verifyCareMap(self, expected, file_name):\n    \"\"\"Parses the care_map.pb; and checks the content in plain text.\"\"\"\n    text_file = common.MakeTempFile(prefix=\"caremap-\", suffix=\".txt\")\n\n    # Calls an external binary to convert the proto message.\n    cmd = [\"care_map_generator\", \"--parse_proto\", file_name, text_file]\n    common.RunAndCheckOutput(cmd)\n\n    with open(text_file) as verify_fp:\n      plain_text = verify_fp.read()\n    self.assertEqual('\\n'.join(expected), plain_text)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AddCareMapForAbOta(self):\n    image_paths = self._test_AddCareMapForAbOta()\n\n    care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')\n    AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)\n\n    expected = ['system', RangeSet(\"0-5 10-15\").to_string_raw(),\n                \"ro.system.build.fingerprint\",\n                \"google/sailfish/12345:user/dev-keys\",\n                'vendor', RangeSet(\"0-9\").to_string_raw(),\n                \"ro.vendor.build.fingerprint\",\n                \"google/sailfish/678:user/dev-keys\"]\n\n    self._verifyCareMap(expected, care_map_file)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AddCareMapForAbOta_withNonCareMapPartitions(self):\n    \"\"\"Partitions without care_map should be ignored.\"\"\"\n    image_paths = self._test_AddCareMapForAbOta()\n\n    care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')\n    AddCareMapForAbOta(\n        care_map_file, ['boot', 'system', 'vendor', 'vbmeta'], image_paths)\n\n    expected = ['system', RangeSet(\"0-5 10-15\").to_string_raw(),\n                \"ro.system.build.fingerprint\",\n                \"google/sailfish/12345:user/dev-keys\",\n                'vendor', RangeSet(\"0-9\").to_string_raw(),\n                \"ro.vendor.build.fingerprint\",\n                \"google/sailfish/678:user/dev-keys\"]\n\n    self._verifyCareMap(expected, care_map_file)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AddCareMapForAbOta_withAvb(self):\n    \"\"\"Tests the case for device using AVB.\"\"\"\n    image_paths = self._test_AddCareMapForAbOta()\n    OPTIONS.info_dict = {\n        'avb_system_hashtree_enable': 'true',\n        'avb_vendor_hashtree_enable': 'true',\n        'system.build.prop': common.PartitionBuildProps.FromDictionary(\n            'system', {\n                'ro.system.build.fingerprint':\n                'google/sailfish/12345:user/dev-keys'}\n        ),\n        'vendor.build.prop': common.PartitionBuildProps.FromDictionary(\n            'vendor', {\n                'ro.vendor.build.fingerprint':\n                'google/sailfish/678:user/dev-keys'}\n        ),\n    }\n\n    care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')\n    AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)\n\n    expected = ['system', RangeSet(\"0-5 10-15\").to_string_raw(),\n                \"ro.system.build.fingerprint\",\n                \"google/sailfish/12345:user/dev-keys\",\n                'vendor', RangeSet(\"0-9\").to_string_raw(),\n                \"ro.vendor.build.fingerprint\",\n                \"google/sailfish/678:user/dev-keys\"]\n\n    self._verifyCareMap(expected, care_map_file)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AddCareMapForAbOta_noFingerprint(self):\n    \"\"\"Tests the case for partitions without fingerprint.\"\"\"\n    image_paths = self._test_AddCareMapForAbOta()\n    OPTIONS.info_dict = {\n        'system_verity_block_device': '/dev/block/system',\n        'vendor_verity_block_device': '/dev/block/vendor',\n    }\n\n    care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')\n    AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)\n\n    expected = ['system', RangeSet(\"0-5 10-15\").to_string_raw(), \"unknown\",\n                \"unknown\", 'vendor', RangeSet(\n        \"0-9\").to_string_raw(), \"unknown\",\n        \"unknown\"]\n\n    self._verifyCareMap(expected, care_map_file)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AddCareMapForAbOta_withThumbprint(self):\n    \"\"\"Tests the case for partitions with thumbprint.\"\"\"\n    image_paths = self._test_AddCareMapForAbOta()\n    OPTIONS.info_dict = {\n        'system_verity_block_device': '/dev/block/system',\n        'vendor_verity_block_device': '/dev/block/vendor',\n        'system.build.prop': common.PartitionBuildProps.FromDictionary(\n            'system', {\n                'ro.system.build.thumbprint':\n                'google/sailfish/123:user/dev-keys'}\n        ),\n        'vendor.build.prop': common.PartitionBuildProps.FromDictionary(\n            'vendor', {\n                'ro.vendor.build.thumbprint':\n                'google/sailfish/456:user/dev-keys'}\n        ),\n    }\n\n    care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')\n    AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)\n\n    expected = ['system', RangeSet(\"0-5 10-15\").to_string_raw(),\n                \"ro.system.build.thumbprint\",\n                \"google/sailfish/123:user/dev-keys\",\n                'vendor', RangeSet(\"0-9\").to_string_raw(),\n                \"ro.vendor.build.thumbprint\",\n                \"google/sailfish/456:user/dev-keys\"]\n\n    self._verifyCareMap(expected, care_map_file)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AddCareMapForAbOta_skipPartition(self):\n    image_paths = self._test_AddCareMapForAbOta()\n    test_utils.erase_avb_footer(image_paths[\"vendor\"])\n\n    care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')\n    AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)\n\n    expected = ['system', RangeSet(\"0-5 10-15\").to_string_raw(),\n                \"ro.system.build.fingerprint\",\n                \"google/sailfish/12345:user/dev-keys\"]\n\n    self._verifyCareMap(expected, care_map_file)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AddCareMapForAbOta_skipAllPartitions(self):\n    image_paths = self._test_AddCareMapForAbOta()\n    test_utils.erase_avb_footer(image_paths[\"system\"])\n    test_utils.erase_avb_footer(image_paths[\"vendor\"])\n\n    care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')\n    AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)\n\n    self.assertFalse(os.path.exists(care_map_file))\n\n  def test_AddCareMapForAbOta_verityNotEnabled(self):\n    \"\"\"No care_map.pb should be generated if verity not enabled.\"\"\"\n    image_paths = self._test_AddCareMapForAbOta()\n    OPTIONS.info_dict = {}\n    care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')\n    AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)\n\n    self.assertFalse(os.path.exists(care_map_file))\n\n  def test_AddCareMapForAbOta_missingImageFile(self):\n    \"\"\"Missing image file should be considered fatal.\"\"\"\n    image_paths = self._test_AddCareMapForAbOta()\n    image_paths['vendor'] = ''\n    care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')\n    self.assertRaises(common.ExternalError, AddCareMapForAbOta, care_map_file,\n                      ['system', 'vendor'], image_paths)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AddCareMapForAbOta_zipOutput(self):\n    \"\"\"Tests the case with ZIP output.\"\"\"\n    image_paths = self._test_AddCareMapForAbOta()\n\n    output_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:\n      AddCareMapForAbOta(output_zip, ['system', 'vendor'], image_paths)\n\n    care_map_name = \"META/care_map.pb\"\n    temp_dir = common.MakeTempDir()\n    with zipfile.ZipFile(output_file, 'r', allowZip64=True) as verify_zip:\n      self.assertTrue(care_map_name in verify_zip.namelist())\n      verify_zip.extract(care_map_name, path=temp_dir)\n\n    expected = ['system', RangeSet(\"0-5 10-15\").to_string_raw(),\n                \"ro.system.build.fingerprint\",\n                \"google/sailfish/12345:user/dev-keys\",\n                'vendor', RangeSet(\"0-9\").to_string_raw(),\n                \"ro.vendor.build.fingerprint\",\n                \"google/sailfish/678:user/dev-keys\"]\n    self._verifyCareMap(expected, os.path.join(temp_dir, care_map_name))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AddCareMapForAbOta_zipOutput_careMapEntryExists(self):\n    \"\"\"Tests the case with ZIP output which already has care_map entry.\"\"\"\n    image_paths = self._test_AddCareMapForAbOta()\n\n    output_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:\n      # Create an existing META/care_map.pb entry.\n      common.ZipWriteStr(output_zip, 'META/care_map.pb',\n                         'fake care_map.pb')\n\n      # Request to add META/care_map.pb again.\n      AddCareMapForAbOta(output_zip, ['system', 'vendor'], image_paths)\n\n    # The one under OPTIONS.input_tmp must have been replaced.\n    care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')\n    expected = ['system', RangeSet(\"0-5 10-15\").to_string_raw(),\n                \"ro.system.build.fingerprint\",\n                \"google/sailfish/12345:user/dev-keys\",\n                'vendor', RangeSet(\"0-9\").to_string_raw(),\n                \"ro.vendor.build.fingerprint\",\n                \"google/sailfish/678:user/dev-keys\"]\n\n    self._verifyCareMap(expected, care_map_file)\n\n    # The existing entry should be scheduled to be replaced.\n    self.assertIn('META/care_map.pb', OPTIONS.replace_updated_files_list)\n\n  def test_GetCareMap(self):\n    sparse_image = test_utils.construct_sparse_image([\n        (0xCAC1, 6),\n        (0xCAC3, 4),\n        (0xCAC1, 6)], \"system\")\n    name, care_map = GetCareMap('system', sparse_image)\n    self.assertEqual('system', name)\n    self.assertEqual(RangeSet(\"0-5 10-15\").to_string_raw(), care_map)\n\n  def test_GetCareMap_invalidPartition(self):\n    self.assertRaises(AssertionError, GetCareMap, 'oem', None)\n\n  def test_GetCareMap_nonSparseImage(self):\n    with tempfile.NamedTemporaryFile() as tmpfile:\n      tmpfile.truncate(4096 * 13)\n      test_utils.append_avb_footer(tmpfile.name, \"system\")\n      name, care_map = GetCareMap('system', tmpfile.name)\n      self.assertEqual('system', name)\n      self.assertEqual(RangeSet(\"0-12\").to_string_raw(), care_map)\n"
  },
  {
    "path": "tools/releasetools/test_apex_utils.py",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport re\nimport os\nimport os.path\nimport shutil\nimport zipfile\n\nimport apex_utils\nimport common\nimport test_utils\n\n\nclass ApexUtilsTest(test_utils.ReleaseToolsTestCase):\n\n  # echo \"foo\" | sha256sum\n  SALT = 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n    # The default payload signing key.\n    self.payload_key = os.path.join(self.testdata_dir, 'testkey.key')\n    self.apex_with_apk = os.path.join(self.testdata_dir, 'has_apk.apex')\n\n    common.OPTIONS.search_path = test_utils.get_search_path()\n\n  @staticmethod\n  def _GetTestPayload():\n    payload_file = common.MakeTempFile(prefix='apex-', suffix='.img')\n    with open(payload_file, 'wb') as payload_fp:\n      payload_fp.write(os.urandom(8192))\n    return payload_file\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ParseApexPayloadInfo(self):\n    payload_file = self._GetTestPayload()\n    apex_utils.SignApexPayload(\n        'avbtool', payload_file, self.payload_key, 'testkey', 'SHA256_RSA2048',\n        self.SALT, 'sha256', no_hashtree=True)\n    payload_info = apex_utils.ParseApexPayloadInfo('avbtool', payload_file)\n    self.assertEqual('SHA256_RSA2048', payload_info['Algorithm'])\n    self.assertEqual(self.SALT, payload_info['Salt'])\n    self.assertEqual('testkey', payload_info['apex.key'])\n    self.assertEqual('sha256', payload_info['Hash Algorithm'])\n    self.assertEqual('0 bytes', payload_info['Tree Size'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_SignApexPayload(self):\n    payload_file = self._GetTestPayload()\n    apex_utils.SignApexPayload(\n        'avbtool', payload_file, self.payload_key, 'testkey', 'SHA256_RSA2048',\n        self.SALT, 'sha256', no_hashtree=True)\n    apex_utils.VerifyApexPayload(\n        'avbtool', payload_file, self.payload_key, True)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_SignApexPayload_withHashtree(self):\n    payload_file = self._GetTestPayload()\n    apex_utils.SignApexPayload(\n        'avbtool', payload_file, self.payload_key, 'testkey', 'SHA256_RSA2048',\n        self.SALT, 'sha256', no_hashtree=False)\n    apex_utils.VerifyApexPayload('avbtool', payload_file, self.payload_key)\n    payload_info = apex_utils.ParseApexPayloadInfo('avbtool', payload_file)\n    self.assertEqual('4096 bytes', payload_info['Tree Size'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_SignApexPayload_noHashtree(self):\n    payload_file = self._GetTestPayload()\n    apex_utils.SignApexPayload(\n        'avbtool', payload_file, self.payload_key, 'testkey', 'SHA256_RSA2048',\n        self.SALT, 'sha256', no_hashtree=True)\n    apex_utils.VerifyApexPayload('avbtool', payload_file, self.payload_key,\n                                 no_hashtree=True)\n    payload_info = apex_utils.ParseApexPayloadInfo('avbtool', payload_file)\n    self.assertEqual('0 bytes', payload_info['Tree Size'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_SignApexPayload_withSignerHelper(self):\n    payload_file = self._GetTestPayload()\n    signing_helper = os.path.join(self.testdata_dir, 'signing_helper.sh')\n    os.chmod(signing_helper, 0o700)\n    payload_signer_args = '--signing_helper_with_files {}'.format(\n        signing_helper)\n    apex_utils.SignApexPayload(\n        'avbtool',\n        payload_file,\n        self.payload_key,\n        'testkey', 'SHA256_RSA2048', self.SALT, 'sha256',\n        True,\n        payload_signer_args)\n    apex_utils.VerifyApexPayload(\n        'avbtool', payload_file, self.payload_key, True)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_SignApexPayload_invalidKey(self):\n    self.assertRaises(\n        apex_utils.ApexSigningError,\n        apex_utils.SignApexPayload,\n        'avbtool',\n        self._GetTestPayload(),\n        os.path.join(self.testdata_dir, 'testkey.x509.pem'),\n        'testkey',\n        'SHA256_RSA2048',\n        self.SALT,\n        'sha256',\n        no_hashtree=True)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_VerifyApexPayload_wrongKey(self):\n    payload_file = self._GetTestPayload()\n    apex_utils.SignApexPayload(\n        'avbtool', payload_file, self.payload_key, 'testkey', 'SHA256_RSA2048',\n        self.SALT, 'sha256', True)\n    apex_utils.VerifyApexPayload(\n        'avbtool', payload_file, self.payload_key, True)\n    self.assertRaises(\n        apex_utils.ApexSigningError,\n        apex_utils.VerifyApexPayload,\n        'avbtool',\n        payload_file,\n        os.path.join(self.testdata_dir, 'testkey_with_passwd.key'),\n        no_hashtree=True)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ApexApkSigner_noApkPresent(self):\n    apex_path = os.path.join(self.testdata_dir, 'foo.apex')\n    signer = apex_utils.ApexApkSigner(apex_path, None, None)\n    processed_apex = signer.ProcessApexFile({}, self.payload_key)\n    self.assertEqual(apex_path, processed_apex)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ApexApkSigner_apkKeyNotPresent(self):\n    apex_path = common.MakeTempFile(suffix='.apex')\n    shutil.copy(self.apex_with_apk, apex_path)\n    signer = apex_utils.ApexApkSigner(apex_path, None, None)\n    self.assertRaises(apex_utils.ApexSigningError, signer.ProcessApexFile,\n                      {}, self.payload_key)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ApexApkSigner_signApk(self):\n    apex_path = common.MakeTempFile(suffix='.apex')\n    shutil.copy(self.apex_with_apk, apex_path)\n    signer = apex_utils.ApexApkSigner(apex_path, None, None)\n    apk_keys = {'wifi-service-resources.apk': os.path.join(\n        self.testdata_dir, 'testkey')}\n\n    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')\n    apex_file = signer.ProcessApexFile(apk_keys, self.payload_key)\n    package_name_extract_cmd = ['aapt2', 'dump', 'badging', apex_file]\n    output = common.RunAndCheckOutput(package_name_extract_cmd)\n    for line in output.splitlines():\n      # Sample output from aapt: \"package: name='com.google.android.wifi'\n      # versionCode='1' versionName='' platformBuildVersionName='R'\n      # compileSdkVersion='29' compileSdkVersionCodename='R'\"\n      match = re.search(r\"^package:.* name='([\\w|\\.]+)'\", line, re.IGNORECASE)\n      if match:\n        package_name = match.group(1)\n    self.assertEquals('com.google.android.wifi', package_name)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ApexApkSigner_noAssetDir(self):\n    no_asset = common.MakeTempFile(suffix='.apex')\n    with zipfile.ZipFile(no_asset, 'w', allowZip64=True) as output_zip:\n      with zipfile.ZipFile(self.apex_with_apk, 'r', allowZip64=True) as input_zip:\n        name_list = input_zip.namelist()\n        for name in name_list:\n          if not name.startswith('assets'):\n            output_zip.writestr(name, input_zip.read(name))\n\n    signer = apex_utils.ApexApkSigner(no_asset, None, None)\n    apk_keys = {'wifi-service-resources.apk': os.path.join(\n        self.testdata_dir, 'testkey')}\n\n    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')\n    signer.ProcessApexFile(apk_keys, self.payload_key)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ApexApkSigner_invokesCustomSignTool(self):\n    apex_path = common.MakeTempFile(suffix='.apex')\n    shutil.copy(self.apex_with_apk, apex_path)\n    apk_keys = {'wifi-service-resources.apk': os.path.join(\n        self.testdata_dir, 'testkey')}\n    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')\n\n    # pass `false` as a sign_tool to see the invocation error\n    with self.assertRaises(common.ExternalError) as cm:\n      signer = apex_utils.ApexApkSigner(\n          apex_path, None, None, sign_tool='false')\n      signer.ProcessApexFile(apk_keys, self.payload_key)\n\n    the_exception = cm.exception\n    self.assertIn('Failed to run command \\'[\\'false\\'', str(the_exception))\n"
  },
  {
    "path": "tools/releasetools/test_blockimgdiff.py",
    "content": "#\n# Copyright (C) 2016 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport os\nfrom hashlib import sha1\n\nimport common\nfrom blockimgdiff import BlockImageDiff, HeapItem, ImgdiffStats, Transfer\nfrom images import DataImage, EmptyImage, FileImage\nfrom rangelib import RangeSet\nfrom test_utils import ReleaseToolsTestCase\n\n\nclass HealpItemTest(ReleaseToolsTestCase):\n\n  class Item(object):\n    def __init__(self, score):\n      self.score = score\n\n  def test_init(self):\n    item1 = HeapItem(self.Item(15))\n    item2 = HeapItem(self.Item(20))\n    item3 = HeapItem(self.Item(15))\n    self.assertTrue(item1)\n    self.assertTrue(item2)\n    self.assertTrue(item3)\n\n    self.assertNotEqual(item1, item2)\n    self.assertEqual(item1, item3)\n    # HeapItem uses negated scores.\n    self.assertGreater(item1, item2)\n    self.assertLessEqual(item1, item3)\n    self.assertTrue(item1 <= item3)\n    self.assertFalse(item2 >= item1)\n\n  def test_clear(self):\n    item = HeapItem(self.Item(15))\n    self.assertTrue(item)\n\n    item.clear()\n    self.assertFalse(item)\n\n\nclass BlockImageDiffTest(ReleaseToolsTestCase):\n\n  def test_GenerateDigraphOrder(self):\n    \"\"\"Make sure GenerateDigraph preserves the order.\n\n    t0: <0-5> => <...>\n    t1: <0-7> => <...>\n    t2: <0-4> => <...>\n    t3: <...> => <0-10>\n\n    t0, t1 and t2 must go before t3, i.e. t3.goes_after =\n    { t0:..., t1:..., t2:... }. But the order of t0-t2 must be preserved.\n    \"\"\"\n\n    src = EmptyImage()\n    tgt = EmptyImage()\n    block_image_diff = BlockImageDiff(tgt, src)\n\n    transfers = block_image_diff.transfers\n    t0 = Transfer(\"t1\", \"t1\", RangeSet(\"10-15\"), RangeSet(\"0-5\"), \"t1hash\",\n                  \"t1hash\", \"move\", transfers)\n    t1 = Transfer(\"t2\", \"t2\", RangeSet(\"20-25\"), RangeSet(\"0-7\"), \"t2hash\",\n                  \"t2hash\", \"move\", transfers)\n    t2 = Transfer(\"t3\", \"t3\", RangeSet(\"30-35\"), RangeSet(\"0-4\"), \"t3hash\",\n                  \"t3hash\", \"move\", transfers)\n    t3 = Transfer(\"t4\", \"t4\", RangeSet(\"0-10\"), RangeSet(\"40-50\"), \"t4hash\",\n                  \"t4hash\", \"move\", transfers)\n\n    block_image_diff.GenerateDigraph()\n    t3_goes_after_copy = t3.goes_after.copy()\n\n    # Elements in the set must be in the transfer evaluation order.\n    elements = list(t3_goes_after_copy)\n    self.assertEqual(t0, elements[0])\n    self.assertEqual(t1, elements[1])\n    self.assertEqual(t2, elements[2])\n\n    # Now switch the order of t0, t1 and t2.\n    transfers[0], transfers[1], transfers[2] = (\n        transfers[2], transfers[0], transfers[1])\n    t3.goes_after.clear()\n    t3.goes_before.clear()\n    block_image_diff.GenerateDigraph()\n\n    # The goes_after must be different from last run.\n    self.assertNotEqual(t3_goes_after_copy, t3.goes_after)\n\n    # Assert that each element must agree with the transfer order.\n    elements = list(t3.goes_after)\n    self.assertEqual(t2, elements[0])\n    self.assertEqual(t0, elements[1])\n    self.assertEqual(t1, elements[2])\n\n  def test_ReviseStashSize(self):\n    \"\"\"ReviseStashSize should convert transfers to 'new' commands as needed.\n\n    t1: diff <20-29> => <11-15>\n    t2: diff <11-15> => <20-29>\n    \"\"\"\n\n    src = EmptyImage()\n    tgt = EmptyImage()\n    block_image_diff = BlockImageDiff(tgt, src, version=3)\n\n    transfers = block_image_diff.transfers\n    Transfer(\"t1\", \"t1\", RangeSet(\"11-15\"), RangeSet(\"20-29\"), \"t1hash\",\n             \"t1hash\", \"diff\", transfers)\n    Transfer(\"t2\", \"t2\", RangeSet(\"20-29\"), RangeSet(\"11-15\"), \"t2hash\",\n             \"t2hash\", \"diff\", transfers)\n\n    block_image_diff.GenerateDigraph()\n    block_image_diff.FindVertexSequence()\n    block_image_diff.ReverseBackwardEdges()\n\n    # Sufficient cache to stash 5 blocks (size * 0.8 >= 5).\n    common.OPTIONS.cache_size = 7 * 4096\n    self.assertEqual((0, 5), block_image_diff.ReviseStashSize())\n\n    # Insufficient cache to stash 5 blocks (size * 0.8 < 5).\n    common.OPTIONS.cache_size = 6 * 4096\n    self.assertEqual((10, 0), block_image_diff.ReviseStashSize())\n\n  def test_ReviseStashSize_bug_33687949(self):\n    \"\"\"ReviseStashSize() should \"free\" the used stash _after_ the command.\n\n    t1: diff <1-5> => <11-15>\n    t2: diff <11-15> => <21-25>\n    t3: diff <11-15 30-39> => <1-5 30-39>\n\n    For transfer t3, the used stash \"11-15\" should not be freed until the\n    command finishes. Assume the allowed cache size is 12-block, it should\n    convert the command to 'new' due to insufficient cache (12 < 5 + 10).\n    \"\"\"\n\n    src = EmptyImage()\n    tgt = EmptyImage()\n    block_image_diff = BlockImageDiff(tgt, src, version=3)\n\n    transfers = block_image_diff.transfers\n    t1 = Transfer(\"t1\", \"t1\", RangeSet(\"11-15\"), RangeSet(\"1-5\"), \"t1hash\",\n                  \"t1hash\", \"diff\", transfers)\n    t2 = Transfer(\"t2\", \"t2\", RangeSet(\"21-25\"), RangeSet(\"11-15\"), \"t2hash\",\n                  \"t2hash\", \"diff\", transfers)\n    t3 = Transfer(\"t3\", \"t3\", RangeSet(\"1-5 30-39\"), RangeSet(\"11-15 30-39\"),\n                  \"t3hash\", \"t3hash\", \"diff\", transfers)\n\n    block_image_diff.GenerateDigraph()\n\n    # Instead of calling FindVertexSequence() and ReverseBackwardEdges(), we\n    # just set up the stash_before and use_stash manually. Otherwise it will\n    # reorder the transfer, which makes testing ReviseStashSize() harder.\n    t1.stash_before.append((0, RangeSet(\"11-15\")))\n    t2.use_stash.append((0, RangeSet(\"11-15\")))\n    t1.stash_before.append((1, RangeSet(\"11-15\")))\n    t3.use_stash.append((1, RangeSet(\"11-15\")))\n\n    # Insufficient cache to stash 15 blocks (size * 0.8 < 15).\n    common.OPTIONS.cache_size = 15 * 4096\n    self.assertEqual((15, 5), block_image_diff.ReviseStashSize())\n\n  def test_FileTypeSupportedByImgdiff(self):\n    self.assertTrue(\n        BlockImageDiff.FileTypeSupportedByImgdiff(\n            \"/system/priv-app/Settings/Settings.apk\"))\n    self.assertTrue(\n        BlockImageDiff.FileTypeSupportedByImgdiff(\n            \"/system/framework/am.jar\"))\n    self.assertTrue(\n        BlockImageDiff.FileTypeSupportedByImgdiff(\n            \"/system/etc/security/otacerts.zip\"))\n\n    self.assertFalse(\n        BlockImageDiff.FileTypeSupportedByImgdiff(\n            \"/system/framework/arm/boot.oat\"))\n    self.assertFalse(\n        BlockImageDiff.FileTypeSupportedByImgdiff(\n            \"/system/priv-app/notanapk\"))\n\n  def test_CanUseImgdiff(self):\n    block_image_diff = BlockImageDiff(EmptyImage(), EmptyImage())\n    self.assertTrue(\n        block_image_diff.CanUseImgdiff(\n            \"/system/app/app1.apk\", RangeSet(\"10-15\"), RangeSet(\"0-5\")))\n    self.assertTrue(\n        block_image_diff.CanUseImgdiff(\n            \"/vendor/app/app2.apk\", RangeSet(\"20 25\"), RangeSet(\"30-31\"), True))\n\n    self.assertDictEqual(\n        {\n            ImgdiffStats.USED_IMGDIFF: {\"/system/app/app1.apk\"},\n            ImgdiffStats.USED_IMGDIFF_LARGE_APK: {\"/vendor/app/app2.apk\"},\n        },\n        block_image_diff.imgdiff_stats.stats)\n\n\n  def test_CanUseImgdiff_ineligible(self):\n    # Disabled by caller.\n    block_image_diff = BlockImageDiff(EmptyImage(), EmptyImage(),\n                                      disable_imgdiff=True)\n    self.assertFalse(\n        block_image_diff.CanUseImgdiff(\n            \"/system/app/app1.apk\", RangeSet(\"10-15\"), RangeSet(\"0-5\")))\n\n    # Unsupported file type.\n    block_image_diff = BlockImageDiff(EmptyImage(), EmptyImage())\n    self.assertFalse(\n        block_image_diff.CanUseImgdiff(\n            \"/system/bin/gzip\", RangeSet(\"10-15\"), RangeSet(\"0-5\")))\n\n    # At least one of the ranges is in non-monotonic order.\n    self.assertFalse(\n        block_image_diff.CanUseImgdiff(\n            \"/system/app/app2.apk\", RangeSet(\"10-15\"),\n            RangeSet(\"15-20 30 10-14\")))\n\n    # At least one of the ranges is incomplete.\n    src_ranges = RangeSet(\"0-5\")\n    src_ranges.extra['incomplete'] = True\n    self.assertFalse(\n        block_image_diff.CanUseImgdiff(\n            \"/vendor/app/app4.apk\", RangeSet(\"10-15\"), src_ranges))\n\n    # The stats are correctly logged.\n    self.assertDictEqual(\n        {\n            ImgdiffStats.SKIPPED_NONMONOTONIC: {'/system/app/app2.apk'},\n            ImgdiffStats.SKIPPED_INCOMPLETE: {'/vendor/app/app4.apk'},\n        },\n        block_image_diff.imgdiff_stats.stats)\n\n\nclass ImgdiffStatsTest(ReleaseToolsTestCase):\n\n  def test_Log(self):\n    imgdiff_stats = ImgdiffStats()\n    imgdiff_stats.Log(\"/system/app/app2.apk\", ImgdiffStats.USED_IMGDIFF)\n    self.assertDictEqual(\n        {\n            ImgdiffStats.USED_IMGDIFF: {'/system/app/app2.apk'},\n        },\n        imgdiff_stats.stats)\n\n  def test_Log_invalidInputs(self):\n    imgdiff_stats = ImgdiffStats()\n\n    self.assertRaises(AssertionError, imgdiff_stats.Log, \"/system/bin/gzip\",\n                      ImgdiffStats.USED_IMGDIFF)\n\n    self.assertRaises(AssertionError, imgdiff_stats.Log, \"/system/app/app1.apk\",\n                      \"invalid reason\")\n\n\nclass DataImageTest(ReleaseToolsTestCase):\n\n  def test_read_range_set(self):\n    data = \"file\" + ('\\0' * 4092)\n    image = DataImage(data)\n    self.assertEqual(data, \"\".join(image.ReadRangeSet(image.care_map)))\n\n\nclass FileImageTest(ReleaseToolsTestCase):\n\n  def setUp(self):\n    self.file_path = common.MakeTempFile()\n    self.data = os.urandom(4096 * 4)\n    with open(self.file_path, 'wb') as f:\n      f.write(self.data)\n    self.file = FileImage(self.file_path)\n\n  def test_totalsha1(self):\n    self.assertEqual(sha1(self.data).hexdigest(), self.file.TotalSha1())\n\n  def test_ranges(self):\n    blocksize = self.file.blocksize\n    for s in range(4):\n      for e in range(s, 4):\n        expected_data = self.data[s * blocksize : e * blocksize]\n\n        rs = RangeSet([s, e])\n        data = b''.join(self.file.ReadRangeSet(rs))\n        self.assertEqual(expected_data, data)\n\n        sha1sum = self.file.RangeSha1(rs)\n        self.assertEqual(sha1(expected_data).hexdigest(), sha1sum)\n\n        tmpfile = common.MakeTempFile()\n        with open(tmpfile, 'wb') as f:\n          self.file.WriteRangeDataToFd(rs, f)\n        with open(tmpfile, 'rb') as f:\n          self.assertEqual(expected_data, f.read())\n\n  def test_read_all(self):\n    data = b''.join(self.file.ReadRangeSet(self.file.care_map))\n    self.assertEqual(self.data, data)\n"
  },
  {
    "path": "tools/releasetools/test_build_image.py",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport filecmp\nimport os.path\n\nimport common\nimport test_utils\nfrom build_image import (\n    BuildImageError, CheckHeadroom, GetFilesystemCharacteristics,\n    SetUpInDirAndFsConfig)\n\n\nclass BuildImageTest(test_utils.ReleaseToolsTestCase):\n\n  # Available: 1000 blocks.\n  EXT4FS_OUTPUT = (\n      \"Created filesystem with 2777/129024 inodes and 515099/516099 blocks\")\n\n  def test_CheckHeadroom_SizeUnderLimit(self):\n    # Required headroom: 1000 blocks.\n    prop_dict = {\n        'fs_type' : 'ext4',\n        'partition_headroom' : '4096000',\n        'mount_point' : 'system',\n    }\n    CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict)\n\n  def test_CheckHeadroom_InsufficientHeadroom(self):\n    # Required headroom: 1001 blocks.\n    prop_dict = {\n        'fs_type' : 'ext4',\n        'partition_headroom' : '4100096',\n        'mount_point' : 'system',\n    }\n    self.assertRaises(\n        BuildImageError, CheckHeadroom, self.EXT4FS_OUTPUT, prop_dict)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckHeadroom_WrongFsType(self):\n    prop_dict = {\n        'fs_type' : 'f2fs',\n        'partition_headroom' : '4100096',\n        'mount_point' : 'system',\n    }\n    self.assertRaises(\n        AssertionError, CheckHeadroom, self.EXT4FS_OUTPUT, prop_dict)\n\n  def test_CheckHeadroom_MissingProperties(self):\n    prop_dict = {\n        'fs_type' : 'ext4',\n        'partition_headroom' : '4100096',\n    }\n    self.assertRaises(\n        AssertionError, CheckHeadroom, self.EXT4FS_OUTPUT, prop_dict)\n\n    prop_dict = {\n        'fs_type' : 'ext4',\n        'mount_point' : 'system',\n    }\n    self.assertRaises(\n        AssertionError, CheckHeadroom, self.EXT4FS_OUTPUT, prop_dict)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckHeadroom_WithMke2fsOutput(self):\n    \"\"\"Tests the result parsing from actual call to mke2fs.\"\"\"\n    input_dir = common.MakeTempDir()\n    output_image = common.MakeTempFile(suffix='.img')\n    command = ['mkuserimg_mke2fs', input_dir, output_image, 'ext4',\n               '/system', '409600', '-j', '0']\n    proc = common.Run(command)\n    ext4fs_output, _ = proc.communicate()\n    self.assertEqual(0, proc.returncode)\n\n    prop_dict = {\n        'fs_type' : 'ext4',\n        'partition_headroom' : '40960',\n        'mount_point' : 'system',\n    }\n    CheckHeadroom(ext4fs_output, prop_dict)\n\n    prop_dict = {\n        'fs_type' : 'ext4',\n        'partition_headroom' : '413696',\n        'mount_point' : 'system',\n    }\n    self.assertRaises(BuildImageError, CheckHeadroom, ext4fs_output, prop_dict)\n\n  def test_SetUpInDirAndFsConfig_NonSystem(self):\n    prop_dict = {\n        'fs_config': 'fs-config',\n        'mount_point': 'vendor',\n    }\n    in_dir, fs_config = SetUpInDirAndFsConfig('/path/to/in_dir', prop_dict)\n    self.assertEqual('/path/to/in_dir', in_dir)\n    self.assertEqual('fs-config', fs_config)\n    self.assertEqual('vendor', prop_dict['mount_point'])\n\n  @staticmethod\n  def _gen_fs_config(partition):\n    fs_config = common.MakeTempFile(suffix='.txt')\n    with open(fs_config, 'w') as fs_config_fp:\n      fs_config_fp.write('fs-config-{}\\n'.format(partition))\n    return fs_config\n\n  def test_SetUpInDirAndFsConfig(self):\n    root_dir = common.MakeTempDir()\n    with open(os.path.join(root_dir, 'init'), 'w') as init_fp:\n      init_fp.write('init')\n\n    origin_in = common.MakeTempDir()\n    with open(os.path.join(origin_in, 'file'), 'w') as in_fp:\n      in_fp.write('system-file')\n    os.symlink('../etc', os.path.join(origin_in, 'symlink'))\n\n    fs_config_system = self._gen_fs_config('system')\n\n    prop_dict = {\n        'fs_config': fs_config_system,\n        'mount_point': 'system',\n        'root_dir': root_dir,\n    }\n    in_dir, fs_config = SetUpInDirAndFsConfig(origin_in, prop_dict)\n\n    self.assertTrue(filecmp.cmp(\n        os.path.join(in_dir, 'init'), os.path.join(root_dir, 'init')))\n    self.assertTrue(filecmp.cmp(\n        os.path.join(in_dir, 'system', 'file'),\n        os.path.join(origin_in, 'file')))\n    self.assertTrue(os.path.islink(os.path.join(in_dir, 'system', 'symlink')))\n\n    self.assertTrue(filecmp.cmp(fs_config_system, fs_config))\n    self.assertEqual('/', prop_dict['mount_point'])\n\n  def test_SetUpInDirAndFsConfig_WithRootFsConfig(self):\n    root_dir = common.MakeTempDir()\n    with open(os.path.join(root_dir, 'init'), 'w') as init_fp:\n      init_fp.write('init')\n\n    origin_in = common.MakeTempDir()\n    with open(os.path.join(origin_in, 'file'), 'w') as in_fp:\n      in_fp.write('system-file')\n    os.symlink('../etc', os.path.join(origin_in, 'symlink'))\n\n    fs_config_system = self._gen_fs_config('system')\n    fs_config_root = self._gen_fs_config('root')\n\n    prop_dict = {\n        'fs_config': fs_config_system,\n        'mount_point': 'system',\n        'root_dir': root_dir,\n        'root_fs_config': fs_config_root,\n    }\n    in_dir, fs_config = SetUpInDirAndFsConfig(origin_in, prop_dict)\n\n    self.assertTrue(filecmp.cmp(\n        os.path.join(in_dir, 'init'), os.path.join(root_dir, 'init')))\n    self.assertTrue(filecmp.cmp(\n        os.path.join(in_dir, 'system', 'file'),\n        os.path.join(origin_in, 'file')))\n    self.assertTrue(os.path.islink(os.path.join(in_dir, 'system', 'symlink')))\n\n    with open(fs_config) as fs_config_fp:\n      fs_config_data = fs_config_fp.readlines()\n    self.assertIn('fs-config-system\\n', fs_config_data)\n    self.assertIn('fs-config-root\\n', fs_config_data)\n    self.assertEqual('/', prop_dict['mount_point'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetFilesystemCharacteristics(self):\n    input_dir = common.MakeTempDir()\n    output_image = common.MakeTempFile(suffix='.img')\n    command = ['mkuserimg_mke2fs', input_dir, output_image, 'ext4',\n               '/system', '409600', '-j', '0']\n    proc = common.Run(command)\n    proc.communicate()\n    self.assertEqual(0, proc.returncode)\n\n    output_file = common.MakeTempFile(suffix='.img')\n    cmd = [\"img2simg\", output_image, output_file]\n    p = common.Run(cmd)\n    p.communicate()\n    self.assertEqual(0, p.returncode)\n\n    fs_dict = GetFilesystemCharacteristics('ext4', output_file)\n    self.assertEqual(int(fs_dict['Block size']), 4096)\n    self.assertGreaterEqual(int(fs_dict['Free blocks']), 0) # expect ~88\n    self.assertGreater(int(fs_dict['Inode count']), 0)      # expect ~64\n    self.assertGreaterEqual(int(fs_dict['Free inodes']), 0) # expect ~53\n    self.assertGreater(int(fs_dict['Inode count']), int(fs_dict['Free inodes']))\n"
  },
  {
    "path": "tools/releasetools/test_check_partition_sizes.py",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport common\nimport test_utils\nfrom check_partition_sizes import CheckPartitionSizes\n\nclass CheckPartitionSizesTest(test_utils.ReleaseToolsTestCase):\n  def setUp(self):\n    self.info_dict = common.LoadDictionaryFromLines(\"\"\"\n        use_dynamic_partitions=true\n        ab_update=true\n        super_block_devices=super\n        dynamic_partition_list=system vendor product\n        super_partition_groups=group\n        super_group_partition_list=system vendor product\n        super_partition_size=202\n        super_super_device_size=202\n        super_group_group_size=100\n        system_image_size=50\n        vendor_image_size=20\n        product_image_size=20\n        system_other_image_size=10\n        \"\"\".split(\"\\n\"))\n\n  def test_ab(self):\n    CheckPartitionSizes(self.info_dict)\n\n  def test_non_ab(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        ab_update=false\n        super_partition_size=101\n        super_super_device_size=101\n        \"\"\".split(\"\\n\")))\n    CheckPartitionSizes(self.info_dict)\n\n  def test_non_dap(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        use_dynamic_partitions=false\n        \"\"\".split(\"\\n\")))\n    with self.assertRaises(RuntimeError):\n      CheckPartitionSizes(self.info_dict)\n\n  def test_retrofit_dap(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        dynamic_partition_retrofit=true\n        super_block_devices=system vendor\n        super_system_device_size=75\n        super_vendor_device_size=25\n        super_partition_size=100\n        \"\"\".split(\"\\n\")))\n    CheckPartitionSizes(self.info_dict)\n\n  def test_ab_partition_too_big(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        system_image_size=100\n        \"\"\".split(\"\\n\")))\n    with self.assertRaises(RuntimeError):\n      CheckPartitionSizes(self.info_dict)\n\n  def test_ab_group_too_big(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        super_group_group_size=110\n        \"\"\".split(\"\\n\")))\n    with self.assertRaises(RuntimeError):\n      CheckPartitionSizes(self.info_dict)\n\n  def test_no_image(self):\n    del self.info_dict[\"system_image_size\"]\n    with self.assertRaises(KeyError):\n      CheckPartitionSizes(self.info_dict)\n\n  def test_block_devices_not_match(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        dynamic_partition_retrofit=true\n        super_block_devices=system vendor\n        super_system_device_size=80\n        super_vendor_device_size=25\n        super_partition_size=100\n        \"\"\".split(\"\\n\")))\n    with self.assertRaises(RuntimeError):\n      CheckPartitionSizes(self.info_dict)\n\n  def test_retrofit_vab(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        virtual_ab=true\n        virtual_ab_retrofit=true\n        \"\"\".split(\"\\n\")))\n    CheckPartitionSizes(self.info_dict)\n\n  def test_retrofit_vab_too_big(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        virtual_ab=true\n        virtual_ab_retrofit=true\n        system_image_size=100\n        \"\"\".split(\"\\n\")))\n    with self.assertRaises(RuntimeError):\n      CheckPartitionSizes(self.info_dict)\n\n  def test_vab(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        virtual_ab=true\n        super_partition_size=101\n        super_super_device_size=101\n        \"\"\".split(\"\\n\")))\n    CheckPartitionSizes(self.info_dict)\n\n  def test_vab_too_big(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        virtual_ab=true\n        super_partition_size=100\n        super_super_device_size=100\n        system_image_size=100\n        \"\"\".split(\"\\n\")))\n    with self.assertRaises(RuntimeError):\n      CheckPartitionSizes(self.info_dict)\n\n  def test_vab_too_big_with_system_other(self):\n    self.info_dict.update(common.LoadDictionaryFromLines(\"\"\"\n        virtual_ab=true\n        system_other_image_size=20\n        super_partition_size=101\n        super_super_device_size=101\n        \"\"\".split(\"\\n\")))\n    with self.assertRaises(RuntimeError):\n      CheckPartitionSizes(self.info_dict)\n"
  },
  {
    "path": "tools/releasetools/test_check_target_files_vintf.py",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport os.path\nimport shutil\n\nimport common\nimport test_utils\nfrom check_target_files_vintf import CheckVintf\n\n# A skeleton target files directory structure. This is VINTF compatible.\nSKELETON_TARGET_FILE_STRUCTURE = {\n    # Empty files\n    'PRODUCT/build.prop': '',\n    'PRODUCT/etc/build.prop': '',\n    'VENDOR/etc/build.prop': '',\n    'ODM/build.prop': '',\n    'ODM/etc/build.prop': '',\n    'RECOVERY/RAMDISK/etc/recovery.fstab': '',\n    'SYSTEM/build.prop': '',\n    'SYSTEM/etc/build.prop': '',\n    'SYSTEM_EXT/build.prop': '',\n    'SYSTEM_EXT/etc/build.prop': '',\n\n    # Non-empty files\n    'SYSTEM/etc/vintf/compatibility_matrix.1.xml':\"\"\"\n        <compatibility-matrix version=\"1.0\" level=\"1\" type=\"framework\">\n            <sepolicy>\n                <sepolicy-version>0.0</sepolicy-version>\n                <kernel-sepolicy-version>0</kernel-sepolicy-version>\n            </sepolicy>\n        </compatibility-matrix>\"\"\",\n    'SYSTEM/manifest.xml':\n        '<manifest version=\"1.0\" type=\"framework\"/>',\n    'VENDOR/build.prop': 'ro.product.first_api_level=29\\n',\n    'VENDOR/compatibility_matrix.xml':\n        '<compatibility-matrix version=\"1.0\" type=\"device\" />',\n    'VENDOR/etc/vintf/manifest.xml':\n        '<manifest version=\"1.0\" target-level=\"1\" type=\"device\"/>',\n    'META/misc_info.txt':\n        'recovery_api_version=3\\nfstab_version=2\\nvintf_enforce=true\\n',\n}\n\n\ndef write_string_to_file(content, path, mode='w'):\n  if not os.path.isdir(os.path.dirname(path)):\n    os.makedirs(os.path.dirname(path))\n  with open(path, mode=mode) as f:\n    f.write(content)\n\n\nclass CheckTargetFilesVintfTest(test_utils.ReleaseToolsTestCase):\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n\n  def prepare_test_dir(self, test_delta_rel_path):\n    test_delta_dir = os.path.join(self.testdata_dir, test_delta_rel_path)\n    test_dir = common.MakeTempDir(prefix='check_target_files_vintf')\n\n    # Create a skeleton directory structure of target files\n    for rel_path, content in SKELETON_TARGET_FILE_STRUCTURE.items():\n      write_string_to_file(content, os.path.join(test_dir, rel_path))\n\n    # Overwrite with files from test_delta_rel_path\n    for root, _, files in os.walk(test_delta_dir):\n      rel_root = os.path.relpath(root, test_delta_dir)\n      for f in files:\n        if not f.endswith('.xml'):\n          continue\n        output_file = os.path.join(test_dir, rel_root, f)\n        with open(os.path.join(root, f)) as inp:\n          write_string_to_file(inp.read(), output_file)\n\n    return test_dir\n\n  # Prepare test dir with required HAL for APEX testing\n  def prepare_apex_test_dir(self, test_delta_rel_path):\n    test_dir = self.prepare_test_dir(test_delta_rel_path)\n    write_string_to_file(\n        \"\"\"<compatibility-matrix version=\"1.0\" level=\"1\" type=\"framework\">\n            <hal format=\"aidl\" optional=\"false\" updatable-via-apex=\"true\">\n                <name>android.apex.foo</name>\n                <version>1</version>\n                <interface>\n                    <name>IApex</name>\n                    <instance>default</instance>\n                </interface>\n            </hal>\n            <sepolicy>\n                <sepolicy-version>0.0</sepolicy-version>\n                <kernel-sepolicy-version>0</kernel-sepolicy-version>\n            </sepolicy>\n        </compatibility-matrix>\"\"\",\n        os.path.join(test_dir, 'SYSTEM/etc/vintf/compatibility_matrix.1.xml'))\n\n    return test_dir\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckVintf_skeleton(self):\n    msg = 'vintf check with skeleton target files failed.'\n    test_dir = self.prepare_test_dir('does-not-exist')\n    self.assertTrue(CheckVintf(test_dir), msg=msg)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckVintf_matrix_incompat(self):\n    msg = 'vintf/matrix_incompat should be incompatible because sepolicy ' \\\n          'version fails to match'\n    test_dir = self.prepare_test_dir('vintf/matrix_incompat')\n    self.assertFalse(CheckVintf(test_dir), msg=msg)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckVintf_kernel_compat(self):\n    msg = 'vintf/kernel with 4.14.1 kernel version should be compatible'\n    test_dir = self.prepare_test_dir('vintf/kernel')\n    write_string_to_file('', os.path.join(test_dir, 'META/kernel_configs.txt'))\n    write_string_to_file('4.14.1',\n                         os.path.join(test_dir, 'META/kernel_version.txt'))\n    self.assertTrue(CheckVintf(test_dir), msg=msg)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckVintf_kernel_incompat(self):\n    msg = 'vintf/kernel with 4.14.0 kernel version should be incompatible ' \\\n          'because 4.14.1 kernel version is required'\n    test_dir = self.prepare_test_dir('vintf/kernel')\n    write_string_to_file('', os.path.join(test_dir, 'META/kernel_configs.txt'))\n    write_string_to_file('4.14.0',\n                         os.path.join(test_dir, 'META/kernel_version.txt'))\n    self.assertFalse(CheckVintf(test_dir), msg=msg)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckVintf_sku_compat(self):\n    msg = 'vintf/sku_compat should be compatible because ' \\\n          'ODM/etc/vintf/manifest_sku.xml has the required HALs'\n    test_dir = self.prepare_test_dir('vintf/sku_compat')\n    write_string_to_file('vintf_odm_manifest_skus=sku',\n                         os.path.join(test_dir, 'META/misc_info.txt'), mode='a')\n    self.assertTrue(CheckVintf(test_dir), msg=msg)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckVintf_sku_incompat(self):\n    msg = 'vintf/sku_compat should be compatible because ' \\\n          'ODM/etc/vintf/manifest_sku.xml does not have the required HALs'\n    test_dir = self.prepare_test_dir('vintf/sku_incompat')\n    write_string_to_file('vintf_odm_manifest_skus=sku',\n                         os.path.join(test_dir, 'META/misc_info.txt'), mode='a')\n    self.assertFalse(CheckVintf(test_dir), msg=msg)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckVintf_bad_xml(self):\n    test_dir = self.prepare_test_dir('does-not-exist')\n    write_string_to_file('not an XML',\n                         os.path.join(test_dir, 'VENDOR/etc/vintf/manifest.xml'))\n    # Should raise an error because a file has invalid format.\n    self.assertRaises(common.ExternalError, CheckVintf, test_dir)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckVintf_apex_compat(self):\n    apex_file_name = 'com.android.apex.vendor.foo.with_vintf.apex'\n    msg = 'vintf/apex_compat should be compatible because ' \\\n          'APEX %s has the required HALs' % (apex_file_name)\n    test_dir = self.prepare_apex_test_dir('vintf/apex_compat')\n    # Copy APEX under VENDOR/apex\n    apex_file = os.path.join(test_utils.get_current_dir(), apex_file_name)\n    apex_dir = os.path.join(test_dir, 'VENDOR/apex')\n    os.makedirs(apex_dir)\n    shutil.copy(apex_file, apex_dir)\n    # Should find required HAL via APEX\n    self.assertTrue(CheckVintf(test_dir), msg=msg)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_CheckVintf_apex_incompat(self):\n    msg = 'vintf/apex_incompat should be incompatible because ' \\\n          'no APEX data'\n    test_dir = self.prepare_apex_test_dir('vintf/apex_incompat')\n    # Should not find required HAL\n    self.assertFalse(CheckVintf(test_dir), msg=msg)\n"
  },
  {
    "path": "tools/releasetools/test_common.py",
    "content": "#\n# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport copy\nimport os\nimport subprocess\nimport tempfile\nimport unittest\nimport zipfile\nfrom hashlib import sha1\nfrom typing import BinaryIO\n\nimport common\nimport test_utils\nimport validate_target_files\nfrom images import EmptyImage, DataImage\nfrom rangelib import RangeSet\n\n\nKiB = 1024\nMiB = 1024 * KiB\nGiB = 1024 * MiB\n\n\ndef get_2gb_file():\n  size = int(2 * GiB + 1)\n  block_size = 4 * KiB\n  step_size = 4 * MiB\n  tmpfile = tempfile.NamedTemporaryFile()\n  tmpfile.truncate(size)\n  for _ in range(0, size, step_size):\n    tmpfile.write(os.urandom(block_size))\n    tmpfile.seek(step_size - block_size, os.SEEK_CUR)\n  return tmpfile\n\n\ndef hash_file(filename):\n  sha1_hash = sha1()\n  with open(filename, \"rb\") as fp:\n    for data in iter(lambda: fp.read(4*MiB), b''):\n      sha1_hash.update(data)\n  return sha1_hash\n\n\nclass BuildInfoTest(test_utils.ReleaseToolsTestCase):\n\n  TEST_INFO_FINGERPRINT_DICT = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.brand': 'product-brand',\n              'ro.product.name': 'product-name',\n              'ro.product.device': 'product-device',\n              'ro.build.version.release': 'version-release',\n              'ro.build.id': 'build-id',\n              'ro.build.version.incremental': 'version-incremental',\n              'ro.build.type': 'build-type',\n              'ro.build.tags': 'build-tags',\n              'ro.build.version.sdk': 30,\n          }\n      ),\n  }\n\n  TEST_INFO_DICT = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.device': 'product-device',\n              'ro.product.name': 'product-name',\n              'ro.build.fingerprint': 'build-fingerprint',\n              'ro.build.foo': 'build-foo'}\n      ),\n      'system.build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.system.brand': 'product-brand',\n              'ro.product.system.name': 'product-name',\n              'ro.product.system.device': 'product-device',\n              'ro.system.build.version.release': 'version-release',\n              'ro.system.build.id': 'build-id',\n              'ro.system.build.version.incremental': 'version-incremental',\n              'ro.system.build.type': 'build-type',\n              'ro.system.build.tags': 'build-tags',\n              'ro.system.build.foo': 'build-foo'}\n      ),\n      'vendor.build.prop': common.PartitionBuildProps.FromDictionary(\n          'vendor', {\n              'ro.product.vendor.brand': 'vendor-product-brand',\n              'ro.product.vendor.name': 'vendor-product-name',\n              'ro.product.vendor.device': 'vendor-product-device',\n              'ro.vendor.build.version.release': 'vendor-version-release',\n              'ro.vendor.build.id': 'vendor-build-id',\n              'ro.vendor.build.version.incremental':\n              'vendor-version-incremental',\n              'ro.vendor.build.type': 'vendor-build-type',\n              'ro.vendor.build.tags': 'vendor-build-tags'}\n      ),\n      'property1': 'value1',\n      'property2': 4096,\n  }\n\n  TEST_INFO_DICT_USES_OEM_PROPS = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.name': 'product-name',\n              'ro.build.thumbprint': 'build-thumbprint',\n              'ro.build.bar': 'build-bar'}\n      ),\n      'vendor.build.prop': common.PartitionBuildProps.FromDictionary(\n          'vendor', {\n              'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}\n      ),\n      'property1': 'value1',\n      'property2': 4096,\n      'oem_fingerprint_properties': 'ro.product.device ro.product.brand',\n  }\n\n  TEST_OEM_DICTS = [\n      {\n          'ro.product.brand': 'brand1',\n          'ro.product.device': 'device1',\n      },\n      {\n          'ro.product.brand': 'brand2',\n          'ro.product.device': 'device2',\n      },\n      {\n          'ro.product.brand': 'brand3',\n          'ro.product.device': 'device3',\n      },\n  ]\n\n  TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.build.fingerprint': 'build-fingerprint',\n              'ro.product.property_source_order':\n                  'product,odm,vendor,system_ext,system'}\n      ),\n      'system.build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.system.device': 'system-product-device'}\n      ),\n      'vendor.build.prop': common.PartitionBuildProps.FromDictionary(\n          'vendor', {\n              'ro.product.vendor.device': 'vendor-product-device'}\n      ),\n  }\n\n  TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.build.fingerprint': 'build-fingerprint',\n              'ro.product.property_source_order':\n                  'product,product_services,odm,vendor,system',\n              'ro.build.version.release': '10',\n              'ro.build.version.codename': 'REL'}\n      ),\n      'system.build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.system.device': 'system-product-device'}\n      ),\n      'vendor.build.prop': common.PartitionBuildProps.FromDictionary(\n          'vendor', {\n              'ro.product.vendor.device': 'vendor-product-device'}\n      ),\n  }\n\n  TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.device': 'product-device',\n              'ro.build.fingerprint': 'build-fingerprint',\n              'ro.build.version.release': '9',\n              'ro.build.version.codename': 'REL'}\n      ),\n      'system.build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.system.device': 'system-product-device'}\n      ),\n      'vendor.build.prop': common.PartitionBuildProps.FromDictionary(\n          'vendor', {\n              'ro.product.vendor.device': 'vendor-product-device'}\n      ),\n  }\n\n  def test_init(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)\n    self.assertEqual('product-device', target_info.device)\n    self.assertEqual('build-fingerprint', target_info.fingerprint)\n    self.assertFalse(target_info.is_ab)\n    self.assertIsNone(target_info.oem_props)\n\n  def test_init_with_oem_props(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,\n                                   self.TEST_OEM_DICTS)\n    self.assertEqual('device1', target_info.device)\n    self.assertEqual('brand1/product-name/device1:build-thumbprint',\n                     target_info.fingerprint)\n\n    # Swap the order in oem_dicts, which would lead to different BuildInfo.\n    oem_dicts = copy.copy(self.TEST_OEM_DICTS)\n    oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]\n    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,\n                                   oem_dicts)\n    self.assertEqual('device3', target_info.device)\n    self.assertEqual('brand3/product-name/device3:build-thumbprint',\n                     target_info.fingerprint)\n\n  def test_init_badFingerprint(self):\n    info_dict = copy.deepcopy(self.TEST_INFO_DICT)\n    info_dict['build.prop'].build_props[\n        'ro.build.fingerprint'] = 'bad fingerprint'\n    self.assertRaises(ValueError, common.BuildInfo, info_dict, None)\n\n    info_dict['build.prop'].build_props[\n        'ro.build.fingerprint'] = 'bad\\x80fingerprint'\n    self.assertRaises(ValueError, common.BuildInfo, info_dict, None)\n\n  def test_init_goodFingerprint(self):\n    info_dict = copy.deepcopy(self.TEST_INFO_FINGERPRINT_DICT)\n    build_info = common.BuildInfo(info_dict)\n    self.assertEqual(\n        'product-brand/product-name/product-device:version-release/build-id/'\n        'version-incremental:build-type/build-tags', build_info.fingerprint)\n\n    build_props = info_dict['build.prop'].build_props\n    del build_props['ro.build.id']\n    build_props['ro.build.legacy.id'] = 'legacy-build-id'\n    build_info = common.BuildInfo(info_dict, use_legacy_id=True)\n    self.assertEqual(\n        'product-brand/product-name/product-device:version-release/'\n        'legacy-build-id/version-incremental:build-type/build-tags',\n        build_info.fingerprint)\n\n    self.assertRaises(common.ExternalError, common.BuildInfo, info_dict, None,\n                      False)\n\n    info_dict['avb_enable'] = 'true'\n    info_dict['vbmeta_digest'] = 'abcde12345'\n    build_info = common.BuildInfo(info_dict, use_legacy_id=False)\n    self.assertEqual(\n        'product-brand/product-name/product-device:version-release/'\n        'legacy-build-id.abcde123/version-incremental:build-type/build-tags',\n        build_info.fingerprint)\n\n  def test___getitem__(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)\n    self.assertEqual('value1', target_info['property1'])\n    self.assertEqual(4096, target_info['property2'])\n    self.assertEqual('build-foo',\n                     target_info['build.prop'].GetProp('ro.build.foo'))\n\n  def test___getitem__with_oem_props(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,\n                                   self.TEST_OEM_DICTS)\n    self.assertEqual('value1', target_info['property1'])\n    self.assertEqual(4096, target_info['property2'])\n    self.assertIsNone(target_info['build.prop'].GetProp('ro.build.foo'))\n\n  def test___setitem__(self):\n    target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)\n    self.assertEqual('value1', target_info['property1'])\n    target_info['property1'] = 'value2'\n    self.assertEqual('value2', target_info['property1'])\n\n    self.assertEqual('build-foo',\n                     target_info['build.prop'].GetProp('ro.build.foo'))\n    target_info['build.prop'].build_props['ro.build.foo'] = 'build-bar'\n    self.assertEqual('build-bar',\n                     target_info['build.prop'].GetProp('ro.build.foo'))\n\n  def test_get(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)\n    self.assertEqual('value1', target_info.get('property1'))\n    self.assertEqual(4096, target_info.get('property2'))\n    self.assertEqual(4096, target_info.get('property2', 1024))\n    self.assertEqual(1024, target_info.get('property-nonexistent', 1024))\n    self.assertEqual('build-foo',\n                     target_info.get('build.prop').GetProp('ro.build.foo'))\n\n  def test_get_with_oem_props(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,\n                                   self.TEST_OEM_DICTS)\n    self.assertEqual('value1', target_info.get('property1'))\n    self.assertEqual(4096, target_info.get('property2'))\n    self.assertEqual(4096, target_info.get('property2', 1024))\n    self.assertEqual(1024, target_info.get('property-nonexistent', 1024))\n    self.assertIsNone(target_info.get('build.prop').GetProp('ro.build.foo'))\n\n  def test_items(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)\n    items = target_info.items()\n    self.assertIn(('property1', 'value1'), items)\n    self.assertIn(('property2', 4096), items)\n\n  def test_GetBuildProp(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)\n    self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))\n    self.assertRaises(common.ExternalError, target_info.GetBuildProp,\n                      'ro.build.nonexistent')\n\n  def test_GetBuildProp_with_oem_props(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,\n                                   self.TEST_OEM_DICTS)\n    self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))\n    self.assertRaises(common.ExternalError, target_info.GetBuildProp,\n                      'ro.build.nonexistent')\n\n  def test_GetPartitionFingerprint(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)\n    self.assertEqual(\n        target_info.GetPartitionFingerprint('vendor'),\n        'vendor-product-brand/vendor-product-name/vendor-product-device'\n        ':vendor-version-release/vendor-build-id/vendor-version-incremental'\n        ':vendor-build-type/vendor-build-tags')\n\n  def test_GetPartitionFingerprint_system_other_uses_system(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)\n    self.assertEqual(\n        target_info.GetPartitionFingerprint('system_other'),\n        target_info.GetPartitionFingerprint('system'))\n\n  def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):\n    info_dict = copy.deepcopy(self.TEST_INFO_DICT)\n    info_dict['vendor.build.prop'].build_props[\n        'ro.vendor.build.fingerprint'] = 'vendor:fingerprint'\n    target_info = common.BuildInfo(info_dict, None)\n    self.assertEqual(\n        target_info.GetPartitionFingerprint('vendor'),\n        'vendor:fingerprint')\n\n  def test_WriteMountOemScript(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,\n                                   self.TEST_OEM_DICTS)\n    script_writer = test_utils.MockScriptWriter()\n    target_info.WriteMountOemScript(script_writer)\n    self.assertEqual([('Mount', '/oem', None)], script_writer.lines)\n\n  def test_WriteDeviceAssertions(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)\n    script_writer = test_utils.MockScriptWriter()\n    target_info.WriteDeviceAssertions(script_writer, False)\n    self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)\n\n  def test_WriteDeviceAssertions_with_oem_props(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,\n                                   self.TEST_OEM_DICTS)\n    script_writer = test_utils.MockScriptWriter()\n    target_info.WriteDeviceAssertions(script_writer, False)\n    self.assertEqual(\n        [\n            ('AssertOemProperty', 'ro.product.device',\n             ['device1', 'device2', 'device3'], False),\n            ('AssertOemProperty', 'ro.product.brand',\n             ['brand1', 'brand2', 'brand3'], False),\n        ],\n        script_writer.lines)\n\n  def test_ResolveRoProductProperty_FromVendor(self):\n    info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)\n    info = common.BuildInfo(info_dict, None)\n    self.assertEqual('vendor-product-device',\n                     info.GetBuildProp('ro.product.device'))\n\n  def test_ResolveRoProductProperty_FromSystem(self):\n    info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)\n    del info_dict['vendor.build.prop'].build_props['ro.product.vendor.device']\n    info = common.BuildInfo(info_dict, None)\n    self.assertEqual('system-product-device',\n                     info.GetBuildProp('ro.product.device'))\n\n  def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):\n    info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)\n    info_dict['build.prop'].build_props[\n        'ro.product.property_source_order'] = 'bad-source'\n    with self.assertRaisesRegexp(common.ExternalError,\n                                 'Invalid ro.product.property_source_order'):\n      info = common.BuildInfo(info_dict, None)\n      info.GetBuildProp('ro.product.device')\n\n  def test_ResolveRoProductProperty_Android10PropertySearchOrder(self):\n    info_dict = copy.deepcopy(\n        self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10)\n    info = common.BuildInfo(info_dict, None)\n    self.assertEqual('vendor-product-device',\n                     info.GetBuildProp('ro.product.device'))\n\n  def test_ResolveRoProductProperty_Android9PropertySearchOrder(self):\n    info_dict = copy.deepcopy(\n        self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9)\n    info = common.BuildInfo(info_dict, None)\n    self.assertEqual('product-device',\n                     info.GetBuildProp('ro.product.device'))\n\n\nclass CommonZipTest(test_utils.ReleaseToolsTestCase):\n\n  def _verify(self, zip_file, zip_file_name, arcname, expected_hash,\n              test_file_name=None, expected_stat=None, expected_mode=0o644,\n              expected_compress_type=zipfile.ZIP_STORED):\n    # Verify the stat if present.\n    if test_file_name is not None:\n      new_stat = os.stat(test_file_name)\n      self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))\n      self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))\n\n    # Reopen the zip file to verify.\n    zip_file = zipfile.ZipFile(zip_file_name, \"r\", allowZip64=True)\n\n    # Verify the timestamp.\n    info = zip_file.getinfo(arcname)\n    self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))\n\n    # Verify the file mode.\n    mode = (info.external_attr >> 16) & 0o777\n    self.assertEqual(mode, expected_mode)\n\n    # Verify the compress type.\n    self.assertEqual(info.compress_type, expected_compress_type)\n\n    # Verify the zip contents.\n    entry = zip_file.open(arcname)\n    sha1_hash = sha1()\n    for chunk in iter(lambda: entry.read(4 * MiB), b''):\n      sha1_hash.update(chunk)\n    self.assertEqual(expected_hash, sha1_hash.hexdigest())\n    self.assertIsNone(zip_file.testzip())\n\n  def _test_ZipWrite(self, contents, extra_zipwrite_args=None):\n    with tempfile.NamedTemporaryFile() as test_file:\n      test_file_name = test_file.name\n      for data in contents:\n        test_file.write(bytes(data))\n      return self._test_ZipWriteFile(test_file_name, extra_zipwrite_args)\n\n  def _test_ZipWriteFile(self, test_file_name, extra_zipwrite_args=None):\n    extra_zipwrite_args = dict(extra_zipwrite_args or {})\n\n    test_file = tempfile.NamedTemporaryFile(delete=False)\n    test_file_name = test_file.name\n\n    zip_file = tempfile.NamedTemporaryFile(delete=False)\n    zip_file_name = zip_file.name\n\n    # File names within an archive strip the leading slash.\n    arcname = extra_zipwrite_args.get(\"arcname\", test_file_name)\n    if arcname[0] == \"/\":\n      arcname = arcname[1:]\n    sha1_hash = hash_file(test_file_name)\n\n    zip_file.close()\n    zip_file = zipfile.ZipFile(zip_file_name, \"w\", allowZip64=True)\n\n    try:\n      expected_mode = extra_zipwrite_args.get(\"perms\", 0o644)\n      expected_compress_type = extra_zipwrite_args.get(\"compress_type\",\n                                                       zipfile.ZIP_STORED)\n\n      # Arbitrary timestamp, just to make sure common.ZipWrite() restores\n      # the timestamp after writing.\n      os.utime(test_file_name, (1234567, 1234567))\n      expected_stat = os.stat(test_file_name)\n      common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)\n      common.ZipClose(zip_file)\n\n      self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),\n                   test_file_name, expected_stat, expected_mode,\n                   expected_compress_type)\n    finally:\n      os.remove(zip_file_name)\n\n  def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):\n    extra_args = dict(extra_args or {})\n\n    zip_file = tempfile.NamedTemporaryFile(delete=False)\n    zip_file_name = zip_file.name\n    zip_file.close()\n\n    zip_file = zipfile.ZipFile(zip_file_name, \"w\", allowZip64=True)\n\n    try:\n      expected_compress_type = extra_args.get(\"compress_type\",\n                                              zipfile.ZIP_STORED)\n      if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):\n        arcname = zinfo_or_arcname\n        expected_mode = extra_args.get(\"perms\", 0o644)\n      else:\n        arcname = zinfo_or_arcname.filename\n        if zinfo_or_arcname.external_attr:\n          zinfo_perms = zinfo_or_arcname.external_attr >> 16\n        else:\n          zinfo_perms = 0o600\n        expected_mode = extra_args.get(\"perms\", zinfo_perms)\n\n      common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)\n      common.ZipClose(zip_file)\n\n      self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),\n                   expected_mode=expected_mode,\n                   expected_compress_type=expected_compress_type)\n    finally:\n      os.remove(zip_file_name)\n\n  def _test_ZipWriteStr_large_file(self, large_file: BinaryIO, small, extra_args=None):\n    extra_args = dict(extra_args or {})\n\n    zip_file = tempfile.NamedTemporaryFile(delete=False)\n    zip_file_name = zip_file.name\n\n    test_file_name = large_file.name\n\n    arcname_large = test_file_name\n    arcname_small = \"bar\"\n\n    # File names within an archive strip the leading slash.\n    if arcname_large[0] == \"/\":\n      arcname_large = arcname_large[1:]\n\n    zip_file.close()\n    zip_file = zipfile.ZipFile(zip_file_name, \"w\", allowZip64=True)\n\n    try:\n      sha1_hash = hash_file(test_file_name)\n\n      # Arbitrary timestamp, just to make sure common.ZipWrite() restores\n      # the timestamp after writing.\n      os.utime(test_file_name, (1234567, 1234567))\n      expected_stat = os.stat(test_file_name)\n      expected_mode = 0o644\n      expected_compress_type = extra_args.get(\"compress_type\",\n                                              zipfile.ZIP_STORED)\n\n      common.ZipWrite(zip_file, test_file_name, **extra_args)\n      common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)\n      common.ZipClose(zip_file)\n\n      # Verify the contents written by ZipWrite().\n      self._verify(zip_file, zip_file_name, arcname_large,\n                   sha1_hash.hexdigest(), test_file_name, expected_stat,\n                   expected_mode, expected_compress_type)\n\n      # Verify the contents written by ZipWriteStr().\n      self._verify(zip_file, zip_file_name, arcname_small,\n                   sha1(small).hexdigest(),\n                   expected_compress_type=expected_compress_type)\n    finally:\n      os.remove(zip_file_name)\n\n  def _test_reset_ZIP64_LIMIT(self, func, *args):\n    default_limit = (1 << 31) - 1\n    self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)\n    func(*args)\n    self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)\n\n  def test_ZipWrite(self):\n    file_contents = os.urandom(1024)\n    self._test_ZipWrite(file_contents)\n\n  def test_ZipWrite_with_opts(self):\n    file_contents = os.urandom(1024)\n    self._test_ZipWrite(file_contents, {\n        \"arcname\": \"foobar\",\n        \"perms\": 0o777,\n        \"compress_type\": zipfile.ZIP_DEFLATED,\n    })\n    self._test_ZipWrite(file_contents, {\n        \"arcname\": \"foobar\",\n        \"perms\": 0o700,\n        \"compress_type\": zipfile.ZIP_STORED,\n    })\n\n  def test_ZipWrite_large_file(self):\n    with get_2gb_file() as tmpfile:\n      self._test_ZipWriteFile(tmpfile.name, {\n          \"compress_type\": zipfile.ZIP_DEFLATED,\n      })\n\n  def test_ZipWrite_resets_ZIP64_LIMIT(self):\n    self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, \"\")\n\n  def test_ZipWriteStr(self):\n    random_string = os.urandom(1024)\n    # Passing arcname\n    self._test_ZipWriteStr(\"foo\", random_string)\n\n    # Passing zinfo\n    zinfo = zipfile.ZipInfo(filename=\"foo\")\n    self._test_ZipWriteStr(zinfo, random_string)\n\n    # Timestamp in the zinfo should be overwritten.\n    zinfo.date_time = (2015, 3, 1, 15, 30, 0)\n    self._test_ZipWriteStr(zinfo, random_string)\n\n  def test_ZipWriteStr_with_opts(self):\n    random_string = os.urandom(1024)\n    # Passing arcname\n    self._test_ZipWriteStr(\"foo\", random_string, {\n        \"perms\": 0o700,\n        \"compress_type\": zipfile.ZIP_DEFLATED,\n    })\n    self._test_ZipWriteStr(\"bar\", random_string, {\n        \"compress_type\": zipfile.ZIP_STORED,\n    })\n\n    # Passing zinfo\n    zinfo = zipfile.ZipInfo(filename=\"foo\")\n    self._test_ZipWriteStr(zinfo, random_string, {\n        \"compress_type\": zipfile.ZIP_DEFLATED,\n    })\n    self._test_ZipWriteStr(zinfo, random_string, {\n        \"perms\": 0o600,\n        \"compress_type\": zipfile.ZIP_STORED,\n    })\n    self._test_ZipWriteStr(zinfo, random_string, {\n        \"perms\": 0o000,\n        \"compress_type\": zipfile.ZIP_STORED,\n    })\n\n  def test_ZipWriteStr_large_file(self):\n    # zipfile.writestr() doesn't work when the str size is over 2GiB even with\n    # the workaround. We will only test the case of writing a string into a\n    # large archive.\n    short_string = os.urandom(1024)\n    with get_2gb_file() as large_file:\n      self._test_ZipWriteStr_large_file(large_file, short_string, {\n          \"compress_type\": zipfile.ZIP_DEFLATED,\n      })\n\n  def test_ZipWriteStr_resets_ZIP64_LIMIT(self):\n    self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')\n    zinfo = zipfile.ZipInfo(filename=\"foo\")\n    self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')\n\n  def test_bug21309935(self):\n    zip_file = tempfile.NamedTemporaryFile(delete=False)\n    zip_file_name = zip_file.name\n    zip_file.close()\n\n    try:\n      random_string = os.urandom(1024)\n      zip_file = zipfile.ZipFile(zip_file_name, \"w\", allowZip64=True)\n      # Default perms should be 0o644 when passing the filename.\n      common.ZipWriteStr(zip_file, \"foo\", random_string)\n      # Honor the specified perms.\n      common.ZipWriteStr(zip_file, \"bar\", random_string, perms=0o755)\n      # The perms in zinfo should be untouched.\n      zinfo = zipfile.ZipInfo(filename=\"baz\")\n      zinfo.external_attr = 0o740 << 16\n      common.ZipWriteStr(zip_file, zinfo, random_string)\n      # Explicitly specified perms has the priority.\n      zinfo = zipfile.ZipInfo(filename=\"qux\")\n      zinfo.external_attr = 0o700 << 16\n      common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)\n      common.ZipClose(zip_file)\n\n      self._verify(zip_file, zip_file_name, \"foo\",\n                   sha1(random_string).hexdigest(),\n                   expected_mode=0o644)\n      self._verify(zip_file, zip_file_name, \"bar\",\n                   sha1(random_string).hexdigest(),\n                   expected_mode=0o755)\n      self._verify(zip_file, zip_file_name, \"baz\",\n                   sha1(random_string).hexdigest(),\n                   expected_mode=0o740)\n      self._verify(zip_file, zip_file_name, \"qux\",\n                   sha1(random_string).hexdigest(),\n                   expected_mode=0o400)\n    finally:\n      os.remove(zip_file_name)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ZipDelete(self):\n    zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')\n    output_zip = zipfile.ZipFile(zip_file.name, 'w',\n                                 compression=zipfile.ZIP_DEFLATED)\n    with tempfile.NamedTemporaryFile() as entry_file:\n      entry_file.write(os.urandom(1024))\n      common.ZipWrite(output_zip, entry_file.name, arcname='Test1')\n      common.ZipWrite(output_zip, entry_file.name, arcname='Test2')\n      common.ZipWrite(output_zip, entry_file.name, arcname='Test3')\n      common.ZipClose(output_zip)\n    zip_file.close()\n\n    try:\n      common.ZipDelete(zip_file.name, 'Test2')\n      with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:\n        entries = check_zip.namelist()\n        self.assertTrue('Test1' in entries)\n        self.assertFalse('Test2' in entries)\n        self.assertTrue('Test3' in entries)\n\n      self.assertRaises(\n          common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')\n      with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:\n        entries = check_zip.namelist()\n        self.assertTrue('Test1' in entries)\n        self.assertFalse('Test2' in entries)\n        self.assertTrue('Test3' in entries)\n\n      common.ZipDelete(zip_file.name, ['Test3'])\n      with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:\n        entries = check_zip.namelist()\n        self.assertTrue('Test1' in entries)\n        self.assertFalse('Test2' in entries)\n        self.assertFalse('Test3' in entries)\n\n      common.ZipDelete(zip_file.name, ['Test1', 'Test2'])\n      with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:\n        entries = check_zip.namelist()\n        self.assertFalse('Test1' in entries)\n        self.assertFalse('Test2' in entries)\n        self.assertFalse('Test3' in entries)\n    finally:\n      os.remove(zip_file.name)\n\n  @staticmethod\n  def _test_UnzipTemp_createZipFile():\n    zip_file = common.MakeTempFile(suffix='.zip')\n    output_zip = zipfile.ZipFile(\n        zip_file, 'w', compression=zipfile.ZIP_DEFLATED)\n    contents = os.urandom(1024)\n    with tempfile.NamedTemporaryFile() as entry_file:\n      entry_file.write(contents)\n      common.ZipWrite(output_zip, entry_file.name, arcname='Test1')\n      common.ZipWrite(output_zip, entry_file.name, arcname='Test2')\n      common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')\n      common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')\n      common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')\n      common.ZipClose(output_zip)\n    common.ZipClose(output_zip)\n    return zip_file\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_UnzipTemp(self):\n    zip_file = self._test_UnzipTemp_createZipFile()\n    unzipped_dir = common.UnzipTemp(zip_file)\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_UnzipTemp_withPatterns(self):\n    zip_file = self._test_UnzipTemp_createZipFile()\n\n    unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))\n\n    unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))\n\n    unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))\n\n    unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))\n\n  def test_UnzipTemp_withEmptyPatterns(self):\n    zip_file = self._test_UnzipTemp_createZipFile()\n    unzipped_dir = common.UnzipTemp(zip_file, [])\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_UnzipTemp_withPartiallyMatchingPatterns(self):\n    zip_file = self._test_UnzipTemp_createZipFile()\n    unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))\n    self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))\n\n  def test_UnzipTemp_withNoMatchingPatterns(self):\n    zip_file = self._test_UnzipTemp_createZipFile()\n    unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))\n    self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))\n\n\nclass CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):\n  \"\"\"Tests the APK utils related functions.\"\"\"\n\n  APKCERTS_TXT1 = (\n      'name=\"RecoveryLocalizer.apk\" certificate=\"certs/devkey.x509.pem\"'\n      ' private_key=\"certs/devkey.pk8\"\\n'\n      'name=\"Settings.apk\"'\n      ' certificate=\"build/make/target/product/security/platform.x509.pem\"'\n      ' private_key=\"build/make/target/product/security/platform.pk8\"\\n'\n      'name=\"TV.apk\" certificate=\"PRESIGNED\" private_key=\"\"\\n'\n  )\n\n  APKCERTS_CERTMAP1 = {\n      'RecoveryLocalizer.apk': 'certs/devkey',\n      'Settings.apk': 'build/make/target/product/security/platform',\n      'TV.apk': 'PRESIGNED',\n  }\n\n  APKCERTS_TXT2 = (\n      'name=\"Compressed1.apk\" certificate=\"certs/compressed1.x509.pem\"'\n      ' private_key=\"certs/compressed1.pk8\" compressed=\"gz\"\\n'\n      'name=\"Compressed2a.apk\" certificate=\"certs/compressed2.x509.pem\"'\n      ' private_key=\"certs/compressed2.pk8\" compressed=\"gz\"\\n'\n      'name=\"Compressed2b.apk\" certificate=\"certs/compressed2.x509.pem\"'\n      ' private_key=\"certs/compressed2.pk8\" compressed=\"gz\"\\n'\n      'name=\"Compressed3.apk\" certificate=\"certs/compressed3.x509.pem\"'\n      ' private_key=\"certs/compressed3.pk8\" compressed=\"gz\"\\n'\n  )\n\n  APKCERTS_CERTMAP2 = {\n      'Compressed1.apk': 'certs/compressed1',\n      'Compressed2a.apk': 'certs/compressed2',\n      'Compressed2b.apk': 'certs/compressed2',\n      'Compressed3.apk': 'certs/compressed3',\n  }\n\n  APKCERTS_TXT3 = (\n      'name=\"Compressed4.apk\" certificate=\"certs/compressed4.x509.pem\"'\n      ' private_key=\"certs/compressed4.pk8\" compressed=\"xz\"\\n'\n  )\n\n  APKCERTS_CERTMAP3 = {\n      'Compressed4.apk': 'certs/compressed4',\n  }\n\n  # Test parsing with no optional fields, both optional fields, and only the\n  # partition optional field.\n  APKCERTS_TXT4 = (\n      'name=\"RecoveryLocalizer.apk\" certificate=\"certs/devkey.x509.pem\"'\n      ' private_key=\"certs/devkey.pk8\"\\n'\n      'name=\"Settings.apk\"'\n      ' certificate=\"build/make/target/product/security/platform.x509.pem\"'\n      ' private_key=\"build/make/target/product/security/platform.pk8\"'\n      ' compressed=\"gz\" partition=\"system\"\\n'\n      'name=\"TV.apk\" certificate=\"PRESIGNED\" private_key=\"\"'\n      ' partition=\"product\"\\n'\n  )\n\n  APKCERTS_CERTMAP4 = {\n      'RecoveryLocalizer.apk': 'certs/devkey',\n      'Settings.apk': 'build/make/target/product/security/platform',\n      'TV.apk': 'PRESIGNED',\n  }\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n\n  @staticmethod\n  def _write_apkcerts_txt(apkcerts_txt, additional=None):\n    if additional is None:\n      additional = []\n    target_files = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)\n      for entry in additional:\n        target_files_zip.writestr(entry, '')\n    return target_files\n\n  def test_ReadApkCerts_NoncompressedApks(self):\n    target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      certmap, ext = common.ReadApkCerts(input_zip)\n\n    self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)\n    self.assertIsNone(ext)\n\n  def test_ReadApkCerts_CompressedApks(self):\n    # We have \"installed\" Compressed1.apk.gz only. Note that Compressed3.apk is\n    # not stored in '.gz' format, so it shouldn't be considered as installed.\n    target_files = self._write_apkcerts_txt(\n        self.APKCERTS_TXT2,\n        ['Compressed1.apk.gz', 'Compressed3.apk'])\n\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      certmap, ext = common.ReadApkCerts(input_zip)\n\n    self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)\n    self.assertEqual('.gz', ext)\n\n    # Alternative case with '.xz'.\n    target_files = self._write_apkcerts_txt(\n        self.APKCERTS_TXT3, ['Compressed4.apk.xz'])\n\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      certmap, ext = common.ReadApkCerts(input_zip)\n\n    self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)\n    self.assertEqual('.xz', ext)\n\n  def test_ReadApkCerts_CompressedAndNoncompressedApks(self):\n    target_files = self._write_apkcerts_txt(\n        self.APKCERTS_TXT1 + self.APKCERTS_TXT2,\n        ['Compressed1.apk.gz', 'Compressed3.apk'])\n\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      certmap, ext = common.ReadApkCerts(input_zip)\n\n    certmap_merged = self.APKCERTS_CERTMAP1.copy()\n    certmap_merged.update(self.APKCERTS_CERTMAP2)\n    self.assertDictEqual(certmap_merged, certmap)\n    self.assertEqual('.gz', ext)\n\n  def test_ReadApkCerts_MultipleCompressionMethods(self):\n    target_files = self._write_apkcerts_txt(\n        self.APKCERTS_TXT2 + self.APKCERTS_TXT3,\n        ['Compressed1.apk.gz', 'Compressed4.apk.xz'])\n\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      self.assertRaises(ValueError, common.ReadApkCerts, input_zip)\n\n  def test_ReadApkCerts_MismatchingKeys(self):\n    malformed_apkcerts_txt = (\n        'name=\"App1.apk\" certificate=\"certs/cert1.x509.pem\"'\n        ' private_key=\"certs/cert2.pk8\"\\n'\n    )\n    target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)\n\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      self.assertRaises(ValueError, common.ReadApkCerts, input_zip)\n\n  def test_ReadApkCerts_WithWithoutOptionalFields(self):\n    target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      certmap, ext = common.ReadApkCerts(input_zip)\n\n    self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)\n    self.assertIsNone(ext)\n\n  def test_ExtractPublicKey(self):\n    cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')\n    pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')\n    with open(pubkey) as pubkey_fp:\n      self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))\n\n  def test_ExtractPublicKey_invalidInput(self):\n    wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')\n    self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ExtractAvbPublicKey(self):\n    privkey = os.path.join(self.testdata_dir, 'testkey.key')\n    pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')\n    extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)\n    extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)\n    with open(extracted_from_privkey, 'rb') as privkey_fp, \\\n            open(extracted_from_pubkey, 'rb') as pubkey_fp:\n      self.assertEqual(privkey_fp.read(), pubkey_fp.read())\n\n  def test_ParseCertificate(self):\n    cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')\n\n    cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']\n    proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,\n                      universal_newlines=False)\n    expected, _ = proc.communicate()\n    self.assertEqual(0, proc.returncode)\n\n    with open(cert) as cert_fp:\n      actual = common.ParseCertificate(cert_fp.read())\n    self.assertEqual(expected, actual)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetMinSdkVersion(self):\n    test_app = os.path.join(self.testdata_dir, 'TestApp.apk')\n    self.assertEqual('24', common.GetMinSdkVersion(test_app))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetMinSdkVersion_invalidInput(self):\n    self.assertRaises(\n        common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetMinSdkVersionInt(self):\n    test_app = os.path.join(self.testdata_dir, 'TestApp.apk')\n    self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetMinSdkVersionInt_invalidInput(self):\n    self.assertRaises(\n        common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',\n        {})\n\n\nclass CommonUtilsTest(test_utils.ReleaseToolsTestCase):\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetSparseImage_emptyBlockMapFile(self):\n    target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.write(\n          test_utils.construct_sparse_image([\n              (0xCAC1, 6),\n              (0xCAC3, 3),\n              (0xCAC1, 4)]),\n          arcname='IMAGES/system.img')\n      target_files_zip.writestr('IMAGES/system.map', '')\n      target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))\n      target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))\n\n    tempdir = common.UnzipTemp(target_files)\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)\n\n    self.assertDictEqual(\n        {\n            '__COPY': RangeSet(\"0\"),\n            '__NONZERO-0': RangeSet(\"1-5 9-12\"),\n        },\n        sparse_image.file_map)\n\n  def test_PartitionMapFromTargetFiles(self):\n    target_files_dir = common.MakeTempDir()\n    os.makedirs(os.path.join(target_files_dir, 'SYSTEM'))\n    os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor'))\n    os.makedirs(os.path.join(target_files_dir, 'PRODUCT'))\n    os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'product'))\n    os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor', 'odm'))\n    os.makedirs(os.path.join(target_files_dir, 'VENDOR_DLKM'))\n    partition_map = common.PartitionMapFromTargetFiles(target_files_dir)\n    self.assertDictEqual(\n        partition_map,\n        {\n            'system': 'SYSTEM',\n            'vendor': 'SYSTEM/vendor',\n            # Prefer PRODUCT over SYSTEM/product\n            'product': 'PRODUCT',\n            'odm': 'SYSTEM/vendor/odm',\n            'vendor_dlkm': 'VENDOR_DLKM',\n            # No system_ext or odm_dlkm\n        })\n\n  def test_SharedUidPartitionViolations(self):\n    uid_dict = {\n        'android.uid.phone': {\n            'system': ['system_phone.apk'],\n            'system_ext': ['system_ext_phone.apk'],\n        },\n        'android.uid.wifi': {\n            'vendor': ['vendor_wifi.apk'],\n            'odm': ['odm_wifi.apk'],\n        },\n    }\n    errors = common.SharedUidPartitionViolations(\n        uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])\n    self.assertEqual(errors, [])\n\n  def test_SharedUidPartitionViolations_Violation(self):\n    uid_dict = {\n        'android.uid.phone': {\n            'system': ['system_phone.apk'],\n            'vendor': ['vendor_phone.apk'],\n        },\n    }\n    errors = common.SharedUidPartitionViolations(\n        uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])\n    self.assertIn(\n        ('APK sharedUserId \"android.uid.phone\" found across partition groups '\n         'in partitions \"system,vendor\"'), errors)\n\n  def test_GetSparseImage_missingImageFile(self):\n    self.assertRaises(\n        AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,\n        None, False)\n    self.assertRaises(\n        AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,\n        None, False)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetSparseImage_missingBlockMapFile(self):\n    target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.write(\n          test_utils.construct_sparse_image([\n              (0xCAC1, 6),\n              (0xCAC3, 3),\n              (0xCAC1, 4)]),\n          arcname='IMAGES/system.img')\n      target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))\n      target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))\n\n    tempdir = common.UnzipTemp(target_files)\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      self.assertRaises(\n          AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,\n          False)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetSparseImage_sharedBlocks_notAllowed(self):\n    \"\"\"Tests the case of having overlapping blocks but disallowed.\"\"\"\n    target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.write(\n          test_utils.construct_sparse_image([(0xCAC2, 16)]),\n          arcname='IMAGES/system.img')\n      # Block 10 is shared between two files.\n      target_files_zip.writestr(\n          'IMAGES/system.map',\n          '\\n'.join([\n              '/system/file1 1-5 9-10',\n              '/system/file2 10-12']))\n      target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))\n      target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))\n\n    tempdir = common.UnzipTemp(target_files)\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      self.assertRaises(\n          AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,\n          False)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetSparseImage_sharedBlocks_allowed(self):\n    \"\"\"Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true.\"\"\"\n    target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      # Construct an image with a care_map of \"0-5 9-12\".\n      target_files_zip.write(\n          test_utils.construct_sparse_image([(0xCAC2, 16)]),\n          arcname='IMAGES/system.img')\n      # Block 10 is shared between two files.\n      target_files_zip.writestr(\n          'IMAGES/system.map',\n          '\\n'.join([\n              '/system/file1 1-5 9-10',\n              '/system/file2 10-12']))\n      target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))\n      target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))\n\n    tempdir = common.UnzipTemp(target_files)\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)\n\n    self.assertDictEqual(\n        {\n            '__COPY': RangeSet(\"0\"),\n            '__NONZERO-0': RangeSet(\"6-8 13-15\"),\n            '/system/file1': RangeSet(\"1-5 9-10\"),\n            '/system/file2': RangeSet(\"11-12\"),\n        },\n        sparse_image.file_map)\n\n    # '/system/file2' should be marked with 'uses_shared_blocks', but not with\n    # 'incomplete'.\n    self.assertTrue(\n        sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])\n    self.assertNotIn(\n        'incomplete', sparse_image.file_map['/system/file2'].extra)\n\n    # '/system/file1' will only contain one field -- a copy of the input text.\n    self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))\n\n    # Meta entries should not have any extra tag.\n    self.assertFalse(sparse_image.file_map['__COPY'].extra)\n    self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetSparseImage_incompleteRanges(self):\n    \"\"\"Tests the case of ext4 images with holes.\"\"\"\n    target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.write(\n          test_utils.construct_sparse_image([(0xCAC2, 16)]),\n          arcname='IMAGES/system.img')\n      target_files_zip.writestr(\n          'IMAGES/system.map',\n          '\\n'.join([\n              '/system/file1 1-5 9-10',\n              '/system/file2 11-12']))\n      target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))\n      # '/system/file2' has less blocks listed (2) than actual (3).\n      target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))\n\n    tempdir = common.UnzipTemp(target_files)\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)\n\n    self.assertEqual(\n        '1-5 9-10',\n        sparse_image.file_map['/system/file1'].extra['text_str'])\n    self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):\n    target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.write(\n          test_utils.construct_sparse_image([(0xCAC2, 16)]),\n          arcname='IMAGES/system.img')\n      target_files_zip.writestr(\n          'IMAGES/system.map',\n          '\\n'.join([\n              '//system/file1 1-5 9-10',\n              '//system/file2 11-12',\n              '/system/app/file3 13-15']))\n      target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))\n      # '/system/file2' has less blocks listed (2) than actual (3).\n      target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))\n      # '/system/app/file3' has less blocks listed (3) than actual (4).\n      target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))\n\n    tempdir = common.UnzipTemp(target_files)\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)\n\n    self.assertEqual(\n        '1-5 9-10',\n        sparse_image.file_map['//system/file1'].extra['text_str'])\n    self.assertTrue(\n        sparse_image.file_map['//system/file2'].extra['incomplete'])\n    self.assertTrue(\n        sparse_image.file_map['/system/app/file3'].extra['incomplete'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetSparseImage_systemRootImage_nonSystemFiles(self):\n    target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.write(\n          test_utils.construct_sparse_image([(0xCAC2, 16)]),\n          arcname='IMAGES/system.img')\n      target_files_zip.writestr(\n          'IMAGES/system.map',\n          '\\n'.join([\n              '//system/file1 1-5 9-10',\n              '//init.rc 13-15']))\n      target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))\n      # '/init.rc' has less blocks listed (3) than actual (4).\n      target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))\n\n    tempdir = common.UnzipTemp(target_files)\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)\n\n    self.assertEqual(\n        '1-5 9-10',\n        sparse_image.file_map['//system/file1'].extra['text_str'])\n    self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetSparseImage_fileNotFound(self):\n    target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.write(\n          test_utils.construct_sparse_image([(0xCAC2, 16)]),\n          arcname='IMAGES/system.img')\n      target_files_zip.writestr(\n          'IMAGES/system.map',\n          '\\n'.join([\n              '//system/file1 1-5 9-10',\n              '//system/file2 11-12']))\n      target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))\n\n    tempdir = common.UnzipTemp(target_files)\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:\n      self.assertRaises(\n          AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,\n          False)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetAvbChainedPartitionArg(self):\n    pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')\n    info_dict = {\n        'avb_avbtool': 'avbtool',\n        'avb_system_key_path': pubkey,\n        'avb_system_rollback_index_location': 2,\n    }\n    chained_partition_args = common.GetAvbChainedPartitionArg(\n        'system', info_dict)\n    self.assertEqual('system', chained_partition_args.partition)\n    self.assertEqual(2, chained_partition_args.rollback_index_location)\n    self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetAvbChainedPartitionArg_withPrivateKey(self):\n    key = os.path.join(self.testdata_dir, 'testkey.key')\n    info_dict = {\n        'avb_avbtool': 'avbtool',\n        'avb_product_key_path': key,\n        'avb_product_rollback_index_location': 2,\n    }\n    chained_partition_args = common.GetAvbChainedPartitionArg(\n        'product', info_dict)\n    self.assertEqual('product', chained_partition_args.partition)\n    self.assertEqual(2, chained_partition_args.rollback_index_location)\n    self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):\n    info_dict = {\n        'avb_avbtool': 'avbtool',\n        'avb_system_key_path': 'does-not-exist',\n        'avb_system_rollback_index_location': 2,\n    }\n    pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')\n    chained_partition_args = common.GetAvbChainedPartitionArg(\n        'system', info_dict, pubkey)\n    self.assertEqual('system', chained_partition_args.partition)\n    self.assertEqual(2, chained_partition_args.rollback_index_location)\n    self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetAvbChainedPartitionArg_invalidKey(self):\n    pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')\n    info_dict = {\n        'avb_avbtool': 'avbtool',\n        'avb_system_key_path': pubkey,\n        'avb_system_rollback_index_location': 2,\n    }\n    self.assertRaises(\n        common.ExternalError, common.GetAvbChainedPartitionArg, 'system',\n        info_dict)\n\n  INFO_DICT_DEFAULT = {\n      'recovery_api_version': 3,\n      'fstab_version': 2,\n      'no_recovery': 'true',\n      'recovery_as_boot': 'true',\n  }\n\n  def test_LoadListFromFile(self):\n    file_path = os.path.join(self.testdata_dir,\n                             'merge_config_framework_item_list')\n    contents = common.LoadListFromFile(file_path)\n    expected_contents = [\n        'META/apkcerts.txt',\n        'META/filesystem_config.txt',\n        'META/root_filesystem_config.txt',\n        'META/system_manifest.xml',\n        'META/system_matrix.xml',\n        'META/update_engine_config.txt',\n        'PRODUCT/*',\n        'ROOT/*',\n        'SYSTEM/*',\n    ]\n    self.assertEqual(sorted(contents), sorted(expected_contents))\n\n  @staticmethod\n  def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):\n    target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      info_values = ''.join(\n          ['{}={}\\n'.format(k, v) for k, v in sorted(info_dict.items())])\n      common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)\n      common.ZipWriteStr(target_files_zip, fstab_path,\n                         \"/dev/block/system /system ext4 ro,barrier=1 defaults\")\n      common.ZipWriteStr(\n          target_files_zip, 'META/file_contexts', 'file-contexts')\n    return target_files\n\n  def test_LoadInfoDict(self):\n    target_files = self._test_LoadInfoDict_createTargetFiles(\n        self.INFO_DICT_DEFAULT,\n        'BOOT/RAMDISK/system/etc/recovery.fstab')\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:\n      loaded_dict = common.LoadInfoDict(target_files_zip)\n      self.assertEqual(3, loaded_dict['recovery_api_version'])\n      self.assertEqual(2, loaded_dict['fstab_version'])\n      self.assertIn('/system', loaded_dict['fstab'])\n\n  def test_LoadInfoDict_legacyRecoveryFstabPath(self):\n    target_files = self._test_LoadInfoDict_createTargetFiles(\n        self.INFO_DICT_DEFAULT,\n        'BOOT/RAMDISK/etc/recovery.fstab')\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:\n      loaded_dict = common.LoadInfoDict(target_files_zip)\n      self.assertEqual(3, loaded_dict['recovery_api_version'])\n      self.assertEqual(2, loaded_dict['fstab_version'])\n      self.assertIn('/system', loaded_dict['fstab'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_LoadInfoDict_dirInput(self):\n    target_files = self._test_LoadInfoDict_createTargetFiles(\n        self.INFO_DICT_DEFAULT,\n        'BOOT/RAMDISK/system/etc/recovery.fstab')\n    unzipped = common.UnzipTemp(target_files)\n    loaded_dict = common.LoadInfoDict(unzipped)\n    self.assertEqual(3, loaded_dict['recovery_api_version'])\n    self.assertEqual(2, loaded_dict['fstab_version'])\n    self.assertIn('/system', loaded_dict['fstab'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):\n    target_files = self._test_LoadInfoDict_createTargetFiles(\n        self.INFO_DICT_DEFAULT,\n        'BOOT/RAMDISK/system/etc/recovery.fstab')\n    unzipped = common.UnzipTemp(target_files)\n    loaded_dict = common.LoadInfoDict(unzipped)\n    self.assertEqual(3, loaded_dict['recovery_api_version'])\n    self.assertEqual(2, loaded_dict['fstab_version'])\n    self.assertIn('/system', loaded_dict['fstab'])\n\n  def test_LoadInfoDict_recoveryAsBootFalse(self):\n    info_dict = copy.copy(self.INFO_DICT_DEFAULT)\n    del info_dict['no_recovery']\n    del info_dict['recovery_as_boot']\n    target_files = self._test_LoadInfoDict_createTargetFiles(\n        info_dict,\n        'RECOVERY/RAMDISK/system/etc/recovery.fstab')\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:\n      loaded_dict = common.LoadInfoDict(target_files_zip)\n      self.assertEqual(3, loaded_dict['recovery_api_version'])\n      self.assertEqual(2, loaded_dict['fstab_version'])\n      self.assertNotIn('/', loaded_dict['fstab'])\n      self.assertIn('/system', loaded_dict['fstab'])\n\n  def test_LoadInfoDict_noRecoveryTrue(self):\n    # Device doesn't have a recovery partition at all.\n    info_dict = copy.copy(self.INFO_DICT_DEFAULT)\n    del info_dict['recovery_as_boot']\n    target_files = self._test_LoadInfoDict_createTargetFiles(\n        info_dict,\n        'RECOVERY/RAMDISK/system/etc/recovery.fstab')\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:\n      loaded_dict = common.LoadInfoDict(target_files_zip)\n      self.assertEqual(3, loaded_dict['recovery_api_version'])\n      self.assertEqual(2, loaded_dict['fstab_version'])\n      self.assertIsNone(loaded_dict['fstab'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_LoadInfoDict_missingMetaMiscInfoTxt(self):\n    target_files = self._test_LoadInfoDict_createTargetFiles(\n        self.INFO_DICT_DEFAULT,\n        'BOOT/RAMDISK/system/etc/recovery.fstab')\n    common.ZipDelete(target_files, 'META/misc_info.txt')\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:\n      self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_LoadInfoDict_repacking(self):\n    target_files = self._test_LoadInfoDict_createTargetFiles(\n        self.INFO_DICT_DEFAULT,\n        'BOOT/RAMDISK/system/etc/recovery.fstab')\n    unzipped = common.UnzipTemp(target_files)\n    loaded_dict = common.LoadInfoDict(unzipped, True)\n    self.assertEqual(3, loaded_dict['recovery_api_version'])\n    self.assertEqual(2, loaded_dict['fstab_version'])\n    self.assertIn('/system', loaded_dict['fstab'])\n    self.assertEqual(\n        os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])\n    self.assertEqual(\n        os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),\n        loaded_dict['root_fs_config'])\n\n  def test_LoadInfoDict_repackingWithZipFileInput(self):\n    target_files = self._test_LoadInfoDict_createTargetFiles(\n        self.INFO_DICT_DEFAULT,\n        'BOOT/RAMDISK/system/etc/recovery.fstab')\n    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:\n      self.assertRaises(\n          AssertionError, common.LoadInfoDict, target_files_zip, True)\n\n  def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):\n    framework_dict = {\n        'use_dynamic_partitions': 'true',\n        'super_partition_groups': 'group_a',\n        'dynamic_partition_list': 'system',\n        'super_group_a_partition_list': 'system',\n    }\n    vendor_dict = {\n        'use_dynamic_partitions': 'true',\n        'super_partition_groups': 'group_a group_b',\n        'dynamic_partition_list': 'vendor product',\n        'super_block_devices': 'super',\n        'super_super_device_size': '3000',\n        'super_group_a_partition_list': 'vendor',\n        'super_group_a_group_size': '1000',\n        'super_group_b_partition_list': 'product',\n        'super_group_b_group_size': '2000',\n    }\n    merged_dict = common.MergeDynamicPartitionInfoDicts(\n        framework_dict=framework_dict,\n        vendor_dict=vendor_dict)\n    expected_merged_dict = {\n        'use_dynamic_partitions': 'true',\n        'super_partition_groups': 'group_a group_b',\n        'dynamic_partition_list': 'product system vendor',\n        'super_block_devices': 'super',\n        'super_super_device_size': '3000',\n        'super_group_a_partition_list': 'system vendor',\n        'super_group_a_group_size': '1000',\n        'super_group_b_partition_list': 'product',\n        'super_group_b_group_size': '2000',\n        'vabc_cow_version': '2',\n    }\n    self.assertEqual(merged_dict, expected_merged_dict)\n\n  def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):\n    framework_dict = {\n        'use_dynamic_partitions': 'true',\n        'super_partition_groups': 'group_a',\n        'dynamic_partition_list': 'system',\n        'super_group_a_partition_list': 'system',\n        'super_group_a_group_size': '5000',\n        'vabc_cow_version': '3',\n    }\n    vendor_dict = {\n        'use_dynamic_partitions': 'true',\n        'super_partition_groups': 'group_a group_b',\n        'dynamic_partition_list': 'vendor product',\n        'super_group_a_partition_list': 'vendor',\n        'super_group_a_group_size': '1000',\n        'super_group_b_partition_list': 'product',\n        'super_group_b_group_size': '2000',\n    }\n    merged_dict = common.MergeDynamicPartitionInfoDicts(\n        framework_dict=framework_dict,\n        vendor_dict=vendor_dict)\n    expected_merged_dict = {\n        'use_dynamic_partitions': 'true',\n        'super_partition_groups': 'group_a group_b',\n        'dynamic_partition_list': 'product system vendor',\n        'super_group_a_partition_list': 'system vendor',\n        'super_group_a_group_size': '1000',\n        'super_group_b_partition_list': 'product',\n        'super_group_b_group_size': '2000',\n        'vabc_cow_version': '2',\n    }\n    self.assertEqual(merged_dict, expected_merged_dict)\n\n  def test_GetAvbPartitionArg(self):\n    info_dict = {}\n    cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)\n    self.assertEqual(\n        [common.AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG, '/path/to/system.img'],\n        cmd)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):\n    testdata_dir = test_utils.get_testdata_dir()\n    pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')\n    info_dict = {\n        'avb_avbtool': 'avbtool',\n        'avb_vendor_key_path': pubkey,\n        'avb_vendor_rollback_index_location': 5,\n    }\n    cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)\n    self.assertEqual(2, len(cmd))\n    self.assertEqual(common.AVB_ARG_NAME_CHAIN_PARTITION, cmd[0])\n    chained_partition_args = cmd[1]\n    self.assertEqual('vendor', chained_partition_args.partition)\n    self.assertEqual(5, chained_partition_args.rollback_index_location)\n    self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):\n    testdata_dir = test_utils.get_testdata_dir()\n    pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')\n    info_dict = {\n        'avb_avbtool': 'avbtool',\n        'avb_recovery_key_path': pubkey,\n        'avb_recovery_rollback_index_location': 3,\n    }\n    cmd = common.GetAvbPartitionArg(\n        'recovery', '/path/to/recovery.img', info_dict)\n    self.assertFalse(cmd)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):\n    testdata_dir = test_utils.get_testdata_dir()\n    pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')\n    info_dict = {\n        'ab_update': 'true',\n        'avb_avbtool': 'avbtool',\n        'avb_recovery_key_path': pubkey,\n        'avb_recovery_rollback_index_location': 3,\n    }\n    cmd = common.GetAvbPartitionArg(\n        'recovery', '/path/to/recovery.img', info_dict)\n    self.assertEqual(2, len(cmd))\n    self.assertEqual(common.AVB_ARG_NAME_CHAIN_PARTITION, cmd[0])\n    chained_partition_args = cmd[1]\n    self.assertEqual('recovery', chained_partition_args.partition)\n    self.assertEqual(3, chained_partition_args.rollback_index_location)\n    self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))\n\n\nclass InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):\n  \"\"\"Checks the format of install-recovery.sh.\n\n  Its format should match between common.py and validate_target_files.py.\n  \"\"\"\n\n  def setUp(self):\n    self._tempdir = common.MakeTempDir()\n    # Create a fake dict that contains the fstab info for boot&recovery.\n    self._info = {\"fstab\": {}}\n    fake_fstab = [\n        \"/dev/soc.0/by-name/boot /boot emmc defaults defaults\",\n        \"/dev/soc.0/by-name/recovery /recovery emmc defaults defaults\"]\n    self._info[\"fstab\"] = common.LoadRecoveryFSTab(\"\\n\".join, 2, fake_fstab)\n    # Construct the gzipped recovery.img and boot.img\n    self.recovery_data = bytearray([\n        0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,\n        0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,\n        0x08, 0x00, 0x00, 0x00\n    ])\n    # echo -n \"boot\" | gzip -f | hd\n    self.boot_data = bytearray([\n        0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,\n        0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00\n    ])\n\n  def _out_tmp_sink(self, name, data, prefix=\"SYSTEM\"):\n    loc = os.path.join(self._tempdir, prefix, name)\n    if not os.path.exists(os.path.dirname(loc)):\n      os.makedirs(os.path.dirname(loc))\n    with open(loc, \"wb\") as f:\n      f.write(data)\n\n  def test_full_recovery(self):\n    recovery_image = common.File(\"recovery.img\", self.recovery_data)\n    boot_image = common.File(\"boot.img\", self.boot_data)\n    self._info[\"full_recovery_image\"] = \"true\"\n\n    common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,\n                             recovery_image, boot_image, self._info)\n    validate_target_files.ValidateInstallRecoveryScript(self._tempdir,\n                                                        self._info)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_recovery_from_boot(self):\n    recovery_image = common.File(\"recovery.img\", self.recovery_data)\n    self._out_tmp_sink(\"recovery.img\", recovery_image.data, \"IMAGES\")\n    boot_image = common.File(\"boot.img\", self.boot_data)\n    self._out_tmp_sink(\"boot.img\", boot_image.data, \"IMAGES\")\n\n    common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,\n                             recovery_image, boot_image, self._info)\n    validate_target_files.ValidateInstallRecoveryScript(self._tempdir,\n                                                        self._info)\n    # Validate 'recovery-from-boot' with bonus argument.\n    self._out_tmp_sink(\"etc/recovery-resource.dat\", b\"bonus\", \"SYSTEM\")\n    common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,\n                             recovery_image, boot_image, self._info)\n    validate_target_files.ValidateInstallRecoveryScript(self._tempdir,\n                                                        self._info)\n\n\nclass MockBlockDifference(object):\n\n  def __init__(self, partition, tgt, src=None):\n    self.partition = partition\n    self.tgt = tgt\n    self.src = src\n\n  def WriteScript(self, script, _, progress=None,\n                  write_verify_script=False):\n    if progress:\n      script.AppendExtra(\"progress({})\".format(progress))\n    script.AppendExtra(\"patch({});\".format(self.partition))\n    if write_verify_script:\n      self.WritePostInstallVerifyScript(script)\n\n  def WritePostInstallVerifyScript(self, script):\n    script.AppendExtra(\"verify({});\".format(self.partition))\n\n\nclass FakeSparseImage(object):\n\n  def __init__(self, size):\n    self.blocksize = 4096\n    self.total_blocks = size // 4096\n    assert size % 4096 == 0, \"{} is not a multiple of 4096\".format(size)\n\n\nclass DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):\n\n  @staticmethod\n  def get_op_list(output_path):\n    with zipfile.ZipFile(output_path, allowZip64=True) as output_zip:\n      with output_zip.open('dynamic_partitions_op_list') as op_list:\n        return [line.decode().strip() for line in op_list.readlines()\n                if not line.startswith(b'#')]\n\n  def setUp(self):\n    self.script = test_utils.MockScriptWriter()\n    self.output_path = common.MakeTempFile(suffix='.zip')\n\n  def test_full(self):\n    target_info = common.LoadDictionaryFromLines(\"\"\"\ndynamic_partition_list=system vendor\nsuper_partition_groups=group_foo\nsuper_group_foo_group_size={group_size}\nsuper_group_foo_partition_list=system vendor\n\"\"\".format(group_size=4 * GiB).split(\"\\n\"))\n    block_diffs = [MockBlockDifference(\"system\", FakeSparseImage(3 * GiB)),\n                   MockBlockDifference(\"vendor\", FakeSparseImage(1 * GiB))]\n\n    dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)\n    with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:\n      dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)\n\n    self.assertEqual(str(self.script).strip(), \"\"\"\nassert(update_dynamic_partitions(package_extract_file(\"dynamic_partitions_op_list\")));\npatch(system);\nverify(system);\nunmap_partition(\"system\");\npatch(vendor);\nverify(vendor);\nunmap_partition(\"vendor\");\n\"\"\".strip())\n\n    lines = self.get_op_list(self.output_path)\n\n    remove_all_groups = lines.index(\"remove_all_groups\")\n    add_group = lines.index(\"add_group group_foo 4294967296\")\n    add_vendor = lines.index(\"add vendor group_foo\")\n    add_system = lines.index(\"add system group_foo\")\n    resize_vendor = lines.index(\"resize vendor 1073741824\")\n    resize_system = lines.index(\"resize system 3221225472\")\n\n    self.assertLess(remove_all_groups, add_group,\n                    \"Should add groups after removing all groups\")\n    self.assertLess(add_group, min(add_vendor, add_system),\n                    \"Should add partitions after adding group\")\n    self.assertLess(add_system, resize_system,\n                    \"Should resize system after adding it\")\n    self.assertLess(add_vendor, resize_vendor,\n                    \"Should resize vendor after adding it\")\n\n  def test_inc_groups(self):\n    source_info = common.LoadDictionaryFromLines(\"\"\"\nsuper_partition_groups=group_foo group_bar group_baz\nsuper_group_foo_group_size={group_foo_size}\nsuper_group_bar_group_size={group_bar_size}\n\"\"\".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split(\"\\n\"))\n    target_info = common.LoadDictionaryFromLines(\"\"\"\nsuper_partition_groups=group_foo group_baz group_qux\nsuper_group_foo_group_size={group_foo_size}\nsuper_group_baz_group_size={group_baz_size}\nsuper_group_qux_group_size={group_qux_size}\n\"\"\".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,\n           group_qux_size=1 * GiB).split(\"\\n\"))\n\n    dp_diff = common.DynamicPartitionsDifference(target_info,\n                                                 block_diffs=[],\n                                                 source_info_dict=source_info)\n    with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:\n      dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)\n\n    lines = self.get_op_list(self.output_path)\n\n    removed = lines.index(\"remove_group group_bar\")\n    shrunk = lines.index(\"resize_group group_foo 3221225472\")\n    grown = lines.index(\"resize_group group_baz 4294967296\")\n    added = lines.index(\"add_group group_qux 1073741824\")\n\n    self.assertLess(max(removed, shrunk),\n                    min(grown, added),\n                    \"ops that remove / shrink partitions must precede ops that \"\n                    \"grow / add partitions\")\n\n  def test_incremental(self):\n    source_info = common.LoadDictionaryFromLines(\"\"\"\ndynamic_partition_list=system vendor product system_ext\nsuper_partition_groups=group_foo\nsuper_group_foo_group_size={group_foo_size}\nsuper_group_foo_partition_list=system vendor product system_ext\n\"\"\".format(group_foo_size=4 * GiB).split(\"\\n\"))\n    target_info = common.LoadDictionaryFromLines(\"\"\"\ndynamic_partition_list=system vendor product odm\nsuper_partition_groups=group_foo group_bar\nsuper_group_foo_group_size={group_foo_size}\nsuper_group_foo_partition_list=system vendor odm\nsuper_group_bar_group_size={group_bar_size}\nsuper_group_bar_partition_list=product\n\"\"\".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split(\"\\n\"))\n\n    block_diffs = [MockBlockDifference(\"system\", FakeSparseImage(1536 * MiB),\n                                       src=FakeSparseImage(1024 * MiB)),\n                   MockBlockDifference(\"vendor\", FakeSparseImage(512 * MiB),\n                                       src=FakeSparseImage(1024 * MiB)),\n                   MockBlockDifference(\"product\", FakeSparseImage(1024 * MiB),\n                                       src=FakeSparseImage(1024 * MiB)),\n                   MockBlockDifference(\"system_ext\", None,\n                                       src=FakeSparseImage(1024 * MiB)),\n                   MockBlockDifference(\"odm\", FakeSparseImage(1024 * MiB),\n                                       src=None)]\n\n    dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,\n                                                 source_info_dict=source_info)\n    with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:\n      dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)\n\n    metadata_idx = self.script.lines.index(\n        'assert(update_dynamic_partitions(package_extract_file('\n        '\"dynamic_partitions_op_list\")));')\n    self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)\n    self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))\n    for p in (\"product\", \"system\", \"odm\"):\n      patch_idx = self.script.lines.index(\"patch({});\".format(p))\n      verify_idx = self.script.lines.index(\"verify({});\".format(p))\n      self.assertLess(metadata_idx, patch_idx,\n                      \"Should patch {} after updating metadata\".format(p))\n      self.assertLess(patch_idx, verify_idx,\n                      \"Should verify {} after patching\".format(p))\n\n    self.assertNotIn(\"patch(system_ext);\", self.script.lines)\n\n    lines = self.get_op_list(self.output_path)\n\n    remove = lines.index(\"remove system_ext\")\n    move_product_out = lines.index(\"move product default\")\n    shrink = lines.index(\"resize vendor 536870912\")\n    shrink_group = lines.index(\"resize_group group_foo 3221225472\")\n    add_group_bar = lines.index(\"add_group group_bar 1073741824\")\n    add_odm = lines.index(\"add odm group_foo\")\n    grow_existing = lines.index(\"resize system 1610612736\")\n    grow_added = lines.index(\"resize odm 1073741824\")\n    move_product_in = lines.index(\"move product group_bar\")\n\n    max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)\n    min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)\n\n    self.assertLess(max_idx_move_partition_out_foo, shrink_group,\n                    \"Must shrink group after partitions inside group are shrunk\"\n                    \" / removed\")\n\n    self.assertLess(add_group_bar, move_product_in,\n                    \"Must add partitions to group after group is added\")\n\n    self.assertLess(max_idx_move_partition_out_foo,\n                    min_idx_move_partition_in_foo,\n                    \"Must shrink partitions / remove partitions from group\"\n                    \"before adding / moving partitions into group\")\n\n  def test_remove_partition(self):\n    source_info = common.LoadDictionaryFromLines(\"\"\"\nblockimgdiff_versions=3,4\nuse_dynamic_partitions=true\ndynamic_partition_list=foo\nsuper_partition_groups=group_foo\nsuper_group_foo_group_size={group_foo_size}\nsuper_group_foo_partition_list=foo\n\"\"\".format(group_foo_size=4 * GiB).split(\"\\n\"))\n    target_info = common.LoadDictionaryFromLines(\"\"\"\nblockimgdiff_versions=3,4\nuse_dynamic_partitions=true\nsuper_partition_groups=group_foo\nsuper_group_foo_group_size={group_foo_size}\n\"\"\".format(group_foo_size=4 * GiB).split(\"\\n\"))\n\n    common.OPTIONS.info_dict = target_info\n    common.OPTIONS.target_info_dict = target_info\n    common.OPTIONS.source_info_dict = source_info\n    common.OPTIONS.cache_size = 4 * 4096\n\n    block_diffs = [common.BlockDifference(\"foo\", EmptyImage(),\n                                          src=DataImage(\"source\", pad=True))]\n\n    dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,\n                                                 source_info_dict=source_info)\n    with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:\n      dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)\n\n    self.assertNotIn(\"block_image_update\", str(self.script),\n                     \"Removed partition should not be patched.\")\n\n    lines = self.get_op_list(self.output_path)\n    self.assertEqual(lines, [\"remove foo\"])\n\n\nclass PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):\n  def setUp(self):\n    self.odm_build_prop = [\n        'ro.odm.build.date.utc=1578430045',\n        'ro.odm.build.fingerprint='\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device=coral',\n        'import /odm/etc/build_${ro.boot.product.device_name}.prop',\n    ]\n\n  @staticmethod\n  def _BuildZipFile(entries):\n    input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:\n      for name, content in entries.items():\n        input_zip.writestr(name, content)\n\n    return input_file\n\n  def test_parseBuildProps_noImportStatement(self):\n    build_prop = [\n        'ro.odm.build.date.utc=1578430045',\n        'ro.odm.build.fingerprint='\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device=coral',\n    ]\n    input_file = self._BuildZipFile({\n        'ODM/etc/build.prop': '\\n'.join(build_prop),\n    })\n\n    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:\n      placeholder_values = {\n          'ro.boot.product.device_name': ['std', 'pro']\n      }\n      partition_props = common.PartitionBuildProps.FromInputFile(\n          input_zip, 'odm', placeholder_values)\n\n    self.assertEqual({\n        'ro.odm.build.date.utc': '1578430045',\n        'ro.odm.build.fingerprint':\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device': 'coral',\n    }, partition_props.build_props)\n\n    self.assertEqual(set(), partition_props.prop_overrides)\n\n  def test_parseBuildProps_singleImportStatement(self):\n    build_std_prop = [\n        'ro.product.odm.device=coral',\n        'ro.product.odm.name=product1',\n    ]\n    build_pro_prop = [\n        'ro.product.odm.device=coralpro',\n        'ro.product.odm.name=product2',\n    ]\n\n    input_file = self._BuildZipFile({\n        'ODM/etc/build.prop': '\\n'.join(self.odm_build_prop),\n        'ODM/etc/build_std.prop': '\\n'.join(build_std_prop),\n        'ODM/etc/build_pro.prop': '\\n'.join(build_pro_prop),\n    })\n\n    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:\n      placeholder_values = {\n          'ro.boot.product.device_name': 'std'\n      }\n      partition_props = common.PartitionBuildProps.FromInputFile(\n          input_zip, 'odm', placeholder_values)\n\n    self.assertEqual({\n        'ro.odm.build.date.utc': '1578430045',\n        'ro.odm.build.fingerprint':\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device': 'coral',\n        'ro.product.odm.name': 'product1',\n    }, partition_props.build_props)\n\n    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:\n      placeholder_values = {\n          'ro.boot.product.device_name': 'pro'\n      }\n      partition_props = common.PartitionBuildProps.FromInputFile(\n          input_zip, 'odm', placeholder_values)\n\n    self.assertEqual({\n        'ro.odm.build.date.utc': '1578430045',\n        'ro.odm.build.fingerprint':\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device': 'coralpro',\n        'ro.product.odm.name': 'product2',\n    }, partition_props.build_props)\n\n  def test_parseBuildProps_noPlaceHolders(self):\n    build_prop = copy.copy(self.odm_build_prop)\n    input_file = self._BuildZipFile({\n        'ODM/etc/build.prop': '\\n'.join(build_prop),\n    })\n\n    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:\n      partition_props = common.PartitionBuildProps.FromInputFile(\n          input_zip, 'odm')\n\n    self.assertEqual({\n        'ro.odm.build.date.utc': '1578430045',\n        'ro.odm.build.fingerprint':\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device': 'coral',\n    }, partition_props.build_props)\n\n    self.assertEqual(set(), partition_props.prop_overrides)\n\n  def test_parseBuildProps_multipleImportStatements(self):\n    build_prop = copy.deepcopy(self.odm_build_prop)\n    build_prop.append(\n        'import /odm/etc/build_${ro.boot.product.product_name}.prop')\n\n    build_std_prop = [\n        'ro.product.odm.device=coral',\n    ]\n    build_pro_prop = [\n        'ro.product.odm.device=coralpro',\n    ]\n\n    product1_prop = [\n        'ro.product.odm.name=product1',\n        'ro.product.not_care=not_care',\n    ]\n\n    product2_prop = [\n        'ro.product.odm.name=product2',\n        'ro.product.not_care=not_care',\n    ]\n\n    input_file = self._BuildZipFile({\n        'ODM/etc/build.prop': '\\n'.join(build_prop),\n        'ODM/etc/build_std.prop': '\\n'.join(build_std_prop),\n        'ODM/etc/build_pro.prop': '\\n'.join(build_pro_prop),\n        'ODM/etc/build_product1.prop': '\\n'.join(product1_prop),\n        'ODM/etc/build_product2.prop': '\\n'.join(product2_prop),\n    })\n\n    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:\n      placeholder_values = {\n          'ro.boot.product.device_name': 'std',\n          'ro.boot.product.product_name': 'product1',\n          'ro.boot.product.not_care': 'not_care',\n      }\n      partition_props = common.PartitionBuildProps.FromInputFile(\n          input_zip, 'odm', placeholder_values)\n\n    self.assertEqual({\n        'ro.odm.build.date.utc': '1578430045',\n        'ro.odm.build.fingerprint':\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device': 'coral',\n        'ro.product.odm.name': 'product1'\n    }, partition_props.build_props)\n\n    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:\n      placeholder_values = {\n          'ro.boot.product.device_name': 'pro',\n          'ro.boot.product.product_name': 'product2',\n          'ro.boot.product.not_care': 'not_care',\n      }\n      partition_props = common.PartitionBuildProps.FromInputFile(\n          input_zip, 'odm', placeholder_values)\n\n    self.assertEqual({\n        'ro.odm.build.date.utc': '1578430045',\n        'ro.odm.build.fingerprint':\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device': 'coralpro',\n        'ro.product.odm.name': 'product2'\n    }, partition_props.build_props)\n\n  def test_parseBuildProps_defineAfterOverride(self):\n    build_prop = copy.deepcopy(self.odm_build_prop)\n    build_prop.append('ro.product.odm.device=coral')\n\n    build_std_prop = [\n        'ro.product.odm.device=coral',\n    ]\n    build_pro_prop = [\n        'ro.product.odm.device=coralpro',\n    ]\n\n    input_file = self._BuildZipFile({\n        'ODM/etc/build.prop': '\\n'.join(build_prop),\n        'ODM/etc/build_std.prop': '\\n'.join(build_std_prop),\n        'ODM/etc/build_pro.prop': '\\n'.join(build_pro_prop),\n    })\n\n    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:\n      placeholder_values = {\n          'ro.boot.product.device_name': 'std',\n      }\n\n      self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,\n                        input_zip, 'odm', placeholder_values)\n\n  def test_parseBuildProps_duplicateOverride(self):\n    build_prop = copy.deepcopy(self.odm_build_prop)\n    build_prop.append(\n        'import /odm/etc/build_${ro.boot.product.product_name}.prop')\n\n    build_std_prop = [\n        'ro.product.odm.device=coral',\n        'ro.product.odm.name=product1',\n    ]\n    build_pro_prop = [\n        'ro.product.odm.device=coralpro',\n    ]\n\n    product1_prop = [\n        'ro.product.odm.name=product1',\n    ]\n\n    product2_prop = [\n        'ro.product.odm.name=product2',\n    ]\n\n    input_file = self._BuildZipFile({\n        'ODM/etc/build.prop': '\\n'.join(build_prop),\n        'ODM/etc/build_std.prop': '\\n'.join(build_std_prop),\n        'ODM/etc/build_pro.prop': '\\n'.join(build_pro_prop),\n        'ODM/etc/build_product1.prop': '\\n'.join(product1_prop),\n        'ODM/etc/build_product2.prop': '\\n'.join(product2_prop),\n    })\n\n    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:\n      placeholder_values = {\n          'ro.boot.product.device_name': 'std',\n          'ro.boot.product.product_name': 'product1',\n      }\n      self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,\n                        input_zip, 'odm', placeholder_values)\n\n  def test_partitionBuildProps_fromInputFile_deepcopy(self):\n    build_prop = [\n        'ro.odm.build.date.utc=1578430045',\n        'ro.odm.build.fingerprint='\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device=coral',\n    ]\n    input_file = self._BuildZipFile({\n        'ODM/etc/build.prop': '\\n'.join(build_prop),\n    })\n\n    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:\n      placeholder_values = {\n          'ro.boot.product.device_name': ['std', 'pro']\n      }\n      partition_props = common.PartitionBuildProps.FromInputFile(\n          input_zip, 'odm', placeholder_values)\n\n    copied_props = copy.deepcopy(partition_props)\n    self.assertEqual({\n        'ro.odm.build.date.utc': '1578430045',\n        'ro.odm.build.fingerprint':\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device': 'coral',\n    }, copied_props.build_props)\n\n\nclass DeviceSpecificParamsTest(test_utils.ReleaseToolsTestCase):\n\n  def test_missingSource(self):\n    common.OPTIONS.device_specific = '/does_not_exist'\n    ds = DeviceSpecificParams()\n    self.assertIsNone(ds.module)\n"
  },
  {
    "path": "tools/releasetools/test_merge_ota.py",
    "content": "# Copyright (C) 2008 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport os\nimport tempfile\nimport test_utils\nimport merge_ota\nimport update_payload\nfrom update_metadata_pb2 import DynamicPartitionGroup\nfrom update_metadata_pb2 import DynamicPartitionMetadata\nfrom test_utils import SkipIfExternalToolsUnavailable, ReleaseToolsTestCase\n\n\nclass MergeOtaTest(ReleaseToolsTestCase):\n  def setUp(self) -> None:\n    self.testdata_dir = test_utils.get_testdata_dir()\n    return super().setUp()\n\n  @SkipIfExternalToolsUnavailable()\n  def test_MergeThreeOtas(self):\n    ota1 = os.path.join(self.testdata_dir, \"tuna_vbmeta.zip\")\n    ota2 = os.path.join(self.testdata_dir, \"tuna_vbmeta_system.zip\")\n    ota3 = os.path.join(self.testdata_dir, \"tuna_vbmeta_vendor.zip\")\n    payloads = [update_payload.Payload(ota) for ota in [ota1, ota2, ota3]]\n    with tempfile.NamedTemporaryFile() as output_file:\n      merge_ota.main([\"merge_ota\", \"-v\", ota1, ota2, ota3,\n                     \"--output\", output_file.name])\n      payload = update_payload.Payload(output_file.name)\n      partition_names = [\n          part.partition_name for part in payload.manifest.partitions]\n      self.assertEqual(partition_names, [\n                       \"vbmeta\", \"vbmeta_system\", \"vbmeta_vendor\"])\n      payload.CheckDataHash()\n      for i in range(3):\n        self.assertEqual(payload.manifest.partitions[i].old_partition_info,\n                         payloads[i].manifest.partitions[0].old_partition_info)\n        self.assertEqual(payload.manifest.partitions[i].new_partition_info,\n                         payloads[i].manifest.partitions[0].new_partition_info)\n\n  def test_MergeDAPSnapshotDisabled(self):\n    dap1 = DynamicPartitionMetadata()\n    dap2 = DynamicPartitionMetadata()\n    merged_dap = DynamicPartitionMetadata()\n    dap1.snapshot_enabled = True\n    dap2.snapshot_enabled = False\n    merge_ota.MergeDynamicPartitionMetadata(merged_dap, dap1)\n    merge_ota.MergeDynamicPartitionMetadata(merged_dap, dap2)\n    self.assertFalse(merged_dap.snapshot_enabled)\n\n  def test_MergeDAPSnapshotEnabled(self):\n    dap1 = DynamicPartitionMetadata()\n    dap2 = DynamicPartitionMetadata()\n    merged_dap = DynamicPartitionMetadata()\n    merged_dap.snapshot_enabled = True\n    dap1.snapshot_enabled = True\n    dap2.snapshot_enabled = True\n    merge_ota.MergeDynamicPartitionMetadata(merged_dap, dap1)\n    merge_ota.MergeDynamicPartitionMetadata(merged_dap, dap2)\n    self.assertTrue(merged_dap.snapshot_enabled)\n\n  def test_MergeDAPGroups(self):\n    dap1 = DynamicPartitionMetadata()\n    dap1.groups.append(DynamicPartitionGroup(\n        name=\"abc\", partition_names=[\"a\", \"b\", \"c\"]))\n    dap2 = DynamicPartitionMetadata()\n    dap2.groups.append(DynamicPartitionGroup(\n        name=\"abc\", partition_names=[\"d\", \"e\", \"f\"]))\n    merged_dap = DynamicPartitionMetadata()\n    merge_ota.MergeDynamicPartitionMetadata(merged_dap, dap1)\n    merge_ota.MergeDynamicPartitionMetadata(merged_dap, dap2)\n    self.assertEqual(len(merged_dap.groups), 1)\n    self.assertEqual(merged_dap.groups[0].name, \"abc\")\n    self.assertEqual(merged_dap.groups[0].partition_names, [\n                     \"a\", \"b\", \"c\", \"d\", \"e\", \"f\"])\n"
  },
  {
    "path": "tools/releasetools/test_non_ab_ota.py",
    "content": "#\n# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport copy\nimport zipfile\n\nimport common\nimport test_utils\n\nfrom non_ab_ota import NonAbOtaPropertyFiles, WriteFingerprintAssertion\nfrom test_utils import PropertyFilesTestCase\n\n\nclass NonAbOtaPropertyFilesTest(PropertyFilesTestCase):\n  \"\"\"Additional validity checks specialized for NonAbOtaPropertyFiles.\"\"\"\n  def setUp(self):\n     common.OPTIONS.no_signing = False\n  def test_init(self):\n    property_files = NonAbOtaPropertyFiles()\n    self.assertEqual('ota-property-files', property_files.name)\n    self.assertEqual((), property_files.required)\n    self.assertEqual((), property_files.optional)\n\n  def test_Compute(self):\n    entries = ()\n    zip_file = self.construct_zip_package(entries)\n    property_files = NonAbOtaPropertyFiles()\n    with zipfile.ZipFile(zip_file) as zip_fp:\n      property_files_string = property_files.Compute(zip_fp)\n\n    tokens = self._parse_property_files_string(property_files_string)\n    self.assertEqual(2, len(tokens))\n    self._verify_entries(zip_file, tokens, entries)\n\n  def test_Finalize(self):\n    entries = [\n        'META-INF/com/android/metadata',\n        'META-INF/com/android/metadata.pb',\n    ]\n    zip_file = self.construct_zip_package(entries)\n    property_files = NonAbOtaPropertyFiles()\n    with zipfile.ZipFile(zip_file) as zip_fp:\n      raw_metadata = property_files.GetPropertyFilesString(\n          zip_fp, reserve_space=False)\n      property_files_string = property_files.Finalize(zip_fp, len(raw_metadata))\n    tokens = self._parse_property_files_string(property_files_string)\n\n    self.assertEqual(2, len(tokens))\n    # 'META-INF/com/android/metadata' will be key'd as 'metadata'.\n    entries[0] = 'metadata'\n    entries[1] = 'metadata.pb'\n    self._verify_entries(zip_file, tokens, entries)\n\n  def test_Verify(self):\n    entries = (\n        'META-INF/com/android/metadata',\n        'META-INF/com/android/metadata.pb',\n    )\n    zip_file = self.construct_zip_package(entries)\n    property_files = NonAbOtaPropertyFiles()\n    with zipfile.ZipFile(zip_file) as zip_fp:\n      raw_metadata = property_files.GetPropertyFilesString(\n          zip_fp, reserve_space=False)\n\n      property_files.Verify(zip_fp, raw_metadata)\n\nclass NonAbOTATest(test_utils.ReleaseToolsTestCase):\n  TEST_TARGET_INFO_DICT = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.device': 'product-device',\n              'ro.build.fingerprint': 'build-fingerprint-target',\n              'ro.build.version.incremental': 'build-version-incremental-target',\n              'ro.build.version.sdk': '27',\n              'ro.build.version.security_patch': '2017-12-01',\n              'ro.build.date.utc': '1500000000'}\n      )\n  }\n  TEST_INFO_DICT_USES_OEM_PROPS = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.name': 'product-name',\n              'ro.build.thumbprint': 'build-thumbprint',\n              'ro.build.bar': 'build-bar'}\n      ),\n      'vendor.build.prop': common.PartitionBuildProps.FromDictionary(\n          'vendor', {\n               'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}\n      ),\n      'property1': 'value1',\n      'property2': 4096,\n      'oem_fingerprint_properties': 'ro.product.device ro.product.brand',\n  }\n  TEST_OEM_DICTS = [\n      {\n          'ro.product.brand': 'brand1',\n          'ro.product.device': 'device1',\n      },\n      {\n          'ro.product.brand': 'brand2',\n          'ro.product.device': 'device2',\n      },\n      {\n          'ro.product.brand': 'brand3',\n          'ro.product.device': 'device3',\n      },\n  ]\n  def test_WriteFingerprintAssertion_without_oem_props(self):\n    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)\n    source_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)\n    source_info_dict['build.prop'].build_props['ro.build.fingerprint'] = (\n        'source-build-fingerprint')\n    source_info = common.BuildInfo(source_info_dict, None)\n\n    script_writer = test_utils.MockScriptWriter()\n    WriteFingerprintAssertion(script_writer, target_info, source_info)\n    self.assertEqual(\n        [('AssertSomeFingerprint', 'source-build-fingerprint',\n          'build-fingerprint-target')],\n        script_writer.lines)\n\n  def test_WriteFingerprintAssertion_with_source_oem_props(self):\n    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)\n    source_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,\n                                   self.TEST_OEM_DICTS)\n\n    script_writer = test_utils.MockScriptWriter()\n    WriteFingerprintAssertion(script_writer, target_info, source_info)\n    self.assertEqual(\n        [('AssertFingerprintOrThumbprint', 'build-fingerprint-target',\n          'build-thumbprint')],\n        script_writer.lines)\n\n  def test_WriteFingerprintAssertion_with_target_oem_props(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,\n                                   self.TEST_OEM_DICTS)\n    source_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)\n\n    script_writer = test_utils.MockScriptWriter()\n    WriteFingerprintAssertion(script_writer, target_info, source_info)\n    self.assertEqual(\n        [('AssertFingerprintOrThumbprint', 'build-fingerprint-target',\n          'build-thumbprint')],\n        script_writer.lines)\n\n  def test_WriteFingerprintAssertion_with_both_oem_props(self):\n    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,\n                                   self.TEST_OEM_DICTS)\n    source_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)\n    source_info_dict['build.prop'].build_props['ro.build.thumbprint'] = (\n        'source-build-thumbprint')\n    source_info = common.BuildInfo(source_info_dict, self.TEST_OEM_DICTS)\n\n    script_writer = test_utils.MockScriptWriter()\n    WriteFingerprintAssertion(script_writer, target_info, source_info)\n    self.assertEqual(\n        [('AssertSomeThumbprint', 'build-thumbprint',\n          'source-build-thumbprint')],\n        script_writer.lines)\n"
  },
  {
    "path": "tools/releasetools/test_ota_from_target_files.py",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport copy\nimport os\nimport os.path\nimport tempfile\nimport zipfile\n\nimport common\nimport ota_metadata_pb2\nimport test_utils\nfrom ota_utils import (\n    BuildLegacyOtaMetadata, CalculateRuntimeDevicesAndFingerprints,\n    ConstructOtaApexInfo, FinalizeMetadata, GetPackageMetadata, PropertyFiles, AbOtaPropertyFiles, PayloadGenerator, StreamingPropertyFiles)\nfrom ota_from_target_files import (\n    _LoadOemDicts,\n    GetTargetFilesZipForCustomImagesUpdates,\n    GetTargetFilesZipForPartialUpdates,\n    GetTargetFilesZipForSecondaryImages,\n    GetTargetFilesZipWithoutPostinstallConfig,\n    POSTINSTALL_CONFIG, AB_PARTITIONS)\nfrom apex_utils import GetApexInfoFromTargetFiles\nfrom test_utils import PropertyFilesTestCase\nfrom common import OPTIONS\nfrom payload_signer import PayloadSigner\n\n\ndef construct_target_files(secondary=False, compressedApex=False):\n  \"\"\"Returns a target-files.zip file for generating OTA packages.\"\"\"\n  target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')\n  with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n    # META/update_engine_config.txt\n    target_files_zip.writestr(\n        'META/update_engine_config.txt',\n        \"PAYLOAD_MAJOR_VERSION=2\\nPAYLOAD_MINOR_VERSION=4\\n\")\n\n    # META/postinstall_config.txt\n    target_files_zip.writestr(\n        POSTINSTALL_CONFIG,\n        '\\n'.join([\n            \"RUN_POSTINSTALL_system=true\",\n            \"POSTINSTALL_PATH_system=system/bin/otapreopt_script\",\n            \"FILESYSTEM_TYPE_system=ext4\",\n            \"POSTINSTALL_OPTIONAL_system=true\",\n        ]))\n\n    ab_partitions = [\n        ('IMAGES', 'boot'),\n        ('IMAGES', 'system'),\n        ('IMAGES', 'vendor'),\n        ('RADIO', 'bootloader'),\n        ('RADIO', 'modem'),\n    ]\n    # META/ab_partitions.txt\n    target_files_zip.writestr(\n        'META/ab_partitions.txt',\n        '\\n'.join([partition[1] for partition in ab_partitions]))\n\n    # Create fake images for each of them.\n    for path, partition in ab_partitions:\n      target_files_zip.writestr(\n          '{}/{}.img'.format(path, partition),\n          os.urandom(len(partition)))\n\n    # system_other shouldn't appear in META/ab_partitions.txt.\n    if secondary:\n      target_files_zip.writestr('IMAGES/system_other.img',\n                                os.urandom(len(\"system_other\")))\n\n    if compressedApex:\n      apex_file_name = 'com.android.apex.compressed.v1.capex'\n      apex_file = os.path.join(test_utils.get_current_dir(), apex_file_name)\n      target_files_zip.write(apex_file, 'SYSTEM/apex/' + apex_file_name)\n\n  return target_files\n\n\nclass LoadOemDictsTest(test_utils.ReleaseToolsTestCase):\n\n  def test_NoneDict(self):\n    self.assertIsNone(_LoadOemDicts(None))\n\n  def test_SingleDict(self):\n    dict_file = common.MakeTempFile()\n    with open(dict_file, 'w') as dict_fp:\n      dict_fp.write('abc=1\\ndef=2\\nxyz=foo\\na.b.c=bar\\n')\n\n    oem_dicts = _LoadOemDicts([dict_file])\n    self.assertEqual(1, len(oem_dicts))\n    self.assertEqual('foo', oem_dicts[0]['xyz'])\n    self.assertEqual('bar', oem_dicts[0]['a.b.c'])\n\n  def test_MultipleDicts(self):\n    oem_source = []\n    for i in range(3):\n      dict_file = common.MakeTempFile()\n      with open(dict_file, 'w') as dict_fp:\n        dict_fp.write(\n            'ro.build.index={}\\ndef=2\\nxyz=foo\\na.b.c=bar\\n'.format(i))\n      oem_source.append(dict_file)\n\n    oem_dicts = _LoadOemDicts(oem_source)\n    self.assertEqual(3, len(oem_dicts))\n    for i, oem_dict in enumerate(oem_dicts):\n      self.assertEqual('2', oem_dict['def'])\n      self.assertEqual('foo', oem_dict['xyz'])\n      self.assertEqual('bar', oem_dict['a.b.c'])\n      self.assertEqual('{}'.format(i), oem_dict['ro.build.index'])\n\n\nclass OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):\n  TEST_TARGET_INFO_DICT = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.device': 'product-device',\n              'ro.build.fingerprint': 'build-fingerprint-target',\n              'ro.build.version.incremental': 'build-version-incremental-target',\n              'ro.build.version.sdk': '27',\n              'ro.build.version.security_patch': '2017-12-01',\n              'ro.build.date.utc': '1500000000'}\n      )\n  }\n\n  TEST_SOURCE_INFO_DICT = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.device': 'product-device',\n              'ro.build.fingerprint': 'build-fingerprint-source',\n              'ro.build.version.incremental': 'build-version-incremental-source',\n              'ro.build.version.sdk': '25',\n              'ro.build.version.security_patch': '2016-12-01',\n              'ro.build.date.utc': '1400000000'}\n      )\n  }\n\n  TEST_INFO_DICT_USES_OEM_PROPS = {\n      'build.prop': common.PartitionBuildProps.FromDictionary(\n          'system', {\n              'ro.product.name': 'product-name',\n              'ro.build.thumbprint': 'build-thumbprint',\n              'ro.build.bar': 'build-bar'}\n      ),\n      'vendor.build.prop': common.PartitionBuildProps.FromDictionary(\n          'vendor', {\n              'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}\n      ),\n      'property1': 'value1',\n      'property2': 4096,\n      'oem_fingerprint_properties': 'ro.product.device ro.product.brand',\n  }\n\n  TEST_TARGET_VENDOR_INFO_DICT = common.PartitionBuildProps.FromDictionary(\n    'vendor', {\n      'ro.vendor.build.date.utc' : '87654321',\n      'ro.product.vendor.device':'vendor-device',\n      'ro.vendor.build.fingerprint': 'build-fingerprint-vendor'}\n  )\n\n  TEST_SOURCE_VENDOR_INFO_DICT = common.PartitionBuildProps.FromDictionary(\n    'vendor', {\n      'ro.vendor.build.date.utc' : '12345678',\n      'ro.product.vendor.device':'vendor-device',\n      'ro.vendor.build.fingerprint': 'build-fingerprint-vendor'}\n  )\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n    self.assertTrue(os.path.exists(self.testdata_dir))\n\n    # Reset the global options as in ota_from_target_files.py.\n    common.OPTIONS.incremental_source = None\n    common.OPTIONS.downgrade = False\n    common.OPTIONS.retrofit_dynamic_partitions = False\n    common.OPTIONS.timestamp = False\n    common.OPTIONS.wipe_user_data = False\n    common.OPTIONS.no_signing = False\n    common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')\n    common.OPTIONS.key_passwords = {\n        common.OPTIONS.package_key: None,\n    }\n\n    common.OPTIONS.search_path = test_utils.get_search_path()\n\n  @staticmethod\n  def GetLegacyOtaMetadata(target_info, source_info=None):\n    metadata_proto = GetPackageMetadata(target_info, source_info)\n    return BuildLegacyOtaMetadata(metadata_proto)\n\n  def test_GetPackageMetadata_abOta_full(self):\n    target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)\n    target_info_dict['ab_update'] = 'true'\n    target_info_dict['ab_partitions'] = []\n    target_info = common.BuildInfo(target_info_dict, None)\n    metadata = self.GetLegacyOtaMetadata(target_info)\n    self.assertDictEqual(\n        {\n            'ota-type': 'AB',\n            'ota-required-cache': '0',\n            'post-build': 'build-fingerprint-target',\n            'post-build-incremental': 'build-version-incremental-target',\n            'post-sdk-level': '27',\n            'post-security-patch-level': '2017-12-01',\n            'post-timestamp': '1500000000',\n            'pre-device': 'product-device',\n        },\n        metadata)\n\n  def test_GetPackageMetadata_abOta_incremental(self):\n    target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)\n    target_info_dict['ab_update'] = 'true'\n    target_info_dict['ab_partitions'] = []\n    target_info = common.BuildInfo(target_info_dict, None)\n    source_info = common.BuildInfo(self.TEST_SOURCE_INFO_DICT, None)\n    common.OPTIONS.incremental_source = ''\n    metadata = self.GetLegacyOtaMetadata(target_info, source_info)\n    self.assertDictEqual(\n        {\n            'ota-type': 'AB',\n            'ota-required-cache': '0',\n            'post-build': 'build-fingerprint-target',\n            'post-build-incremental': 'build-version-incremental-target',\n            'post-sdk-level': '27',\n            'post-security-patch-level': '2017-12-01',\n            'post-timestamp': '1500000000',\n            'pre-device': 'product-device',\n            'pre-build': 'build-fingerprint-source',\n            'pre-build-incremental': 'build-version-incremental-source',\n        },\n        metadata)\n\n  def test_GetPackageMetadata_nonAbOta_full(self):\n    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)\n    metadata = self.GetLegacyOtaMetadata(target_info)\n    self.assertDictEqual(\n        {\n            'ota-type': 'BLOCK',\n            'ota-required-cache': '0',\n            'post-build': 'build-fingerprint-target',\n            'post-build-incremental': 'build-version-incremental-target',\n            'post-sdk-level': '27',\n            'post-security-patch-level': '2017-12-01',\n            'post-timestamp': '1500000000',\n            'pre-device': 'product-device',\n        },\n        metadata)\n\n  def test_GetPackageMetadata_nonAbOta_incremental(self):\n    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)\n    source_info = common.BuildInfo(self.TEST_SOURCE_INFO_DICT, None)\n    common.OPTIONS.incremental_source = ''\n    metadata = self.GetLegacyOtaMetadata(target_info, source_info)\n    self.assertDictEqual(\n        {\n            'ota-type': 'BLOCK',\n            'ota-required-cache': '0',\n            'post-build': 'build-fingerprint-target',\n            'post-build-incremental': 'build-version-incremental-target',\n            'post-sdk-level': '27',\n            'post-security-patch-level': '2017-12-01',\n            'post-timestamp': '1500000000',\n            'pre-device': 'product-device',\n            'pre-build': 'build-fingerprint-source',\n            'pre-build-incremental': 'build-version-incremental-source',\n        },\n        metadata)\n\n  def test_GetPackageMetadata_wipe(self):\n    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)\n    common.OPTIONS.wipe_user_data = True\n    metadata = self.GetLegacyOtaMetadata(target_info)\n    self.assertDictEqual(\n        {\n            'ota-type': 'BLOCK',\n            'ota-required-cache': '0',\n            'ota-wipe': 'yes',\n            'post-build': 'build-fingerprint-target',\n            'post-build-incremental': 'build-version-incremental-target',\n            'post-sdk-level': '27',\n            'post-security-patch-level': '2017-12-01',\n            'post-timestamp': '1500000000',\n            'pre-device': 'product-device',\n        },\n        metadata)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetApexInfoFromTargetFiles(self):\n    target_files = construct_target_files(compressedApex=True)\n    apex_infos = GetApexInfoFromTargetFiles(target_files)\n    self.assertEqual(len(apex_infos), 1)\n    self.assertEqual(apex_infos[0].package_name, \"com.android.apex.compressed\")\n    self.assertEqual(apex_infos[0].version, 1)\n    self.assertEqual(apex_infos[0].is_compressed, True)\n    # Compare the decompressed APEX size with the original uncompressed APEX\n    original_apex_name = 'com.android.apex.compressed.v1_original.apex'\n    original_apex_filepath = os.path.join(\n        test_utils.get_current_dir(), original_apex_name)\n    uncompressed_apex_size = os.path.getsize(original_apex_filepath)\n    self.assertEqual(apex_infos[0].decompressed_size, uncompressed_apex_size)\n\n  @staticmethod\n  def construct_tf_with_apex_info(infos):\n    apex_metadata_proto = ota_metadata_pb2.ApexMetadata()\n    apex_metadata_proto.apex_info.extend(infos)\n\n    output = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(output, 'w') as zfp:\n      common.ZipWriteStr(zfp, \"META/apex_info.pb\",\n                         apex_metadata_proto.SerializeToString())\n    return output\n\n  def test_ConstructOtaApexInfo_incremental_package(self):\n    infos = [ota_metadata_pb2.ApexInfo(package_name='com.android.apex.1',\n                                       version=1000, is_compressed=False),\n             ota_metadata_pb2.ApexInfo(package_name='com.android.apex.2',\n                                       version=2000, is_compressed=True)]\n    target_file = self.construct_tf_with_apex_info(infos)\n\n    with zipfile.ZipFile(target_file) as target_zip:\n      info_bytes = ConstructOtaApexInfo(target_zip, source_file=target_file)\n    apex_metadata_proto = ota_metadata_pb2.ApexMetadata()\n    apex_metadata_proto.ParseFromString(info_bytes)\n\n    info_list = apex_metadata_proto.apex_info\n    self.assertEqual(2, len(info_list))\n    self.assertEqual('com.android.apex.1', info_list[0].package_name)\n    self.assertEqual(1000, info_list[0].version)\n    self.assertEqual(1000, info_list[0].source_version)\n\n  def test_GetPackageMetadata_retrofitDynamicPartitions(self):\n    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)\n    common.OPTIONS.retrofit_dynamic_partitions = True\n    metadata = self.GetLegacyOtaMetadata(target_info)\n    self.assertDictEqual(\n        {\n            'ota-retrofit-dynamic-partitions': 'yes',\n            'ota-type': 'BLOCK',\n            'ota-required-cache': '0',\n            'post-build': 'build-fingerprint-target',\n            'post-build-incremental': 'build-version-incremental-target',\n            'post-sdk-level': '27',\n            'post-security-patch-level': '2017-12-01',\n            'post-timestamp': '1500000000',\n            'pre-device': 'product-device',\n        },\n        metadata)\n\n  @staticmethod\n  def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info):\n    (target_info['build.prop'].build_props['ro.build.date.utc'],\n     source_info['build.prop'].build_props['ro.build.date.utc']) = (\n         source_info['build.prop'].build_props['ro.build.date.utc'],\n         target_info['build.prop'].build_props['ro.build.date.utc'])\n\n  @staticmethod\n  def _test_GetPackageMetadata_swapVendorBuildTimestamps(target_info, source_info):\n    (target_info['vendor.build.prop'].build_props['ro.vendor.build.date.utc'],\n     source_info['vendor.build.prop'].build_props['ro.vendor.build.date.utc']) = (\n         source_info['vendor.build.prop'].build_props['ro.vendor.build.date.utc'],\n         target_info['vendor.build.prop'].build_props['ro.vendor.build.date.utc'])\n\n  def test_GetPackageMetadata_unintentionalDowngradeDetected(self):\n    target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)\n    source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)\n    self._test_GetPackageMetadata_swapBuildTimestamps(\n        target_info_dict, source_info_dict)\n\n    target_info = common.BuildInfo(target_info_dict, None)\n    source_info = common.BuildInfo(source_info_dict, None)\n    common.OPTIONS.incremental_source = ''\n    self.assertRaises(RuntimeError, self.GetLegacyOtaMetadata, target_info,\n                      source_info)\n\n  def test_GetPackageMetadata_unintentionalVendorDowngradeDetected(self):\n    target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)\n    target_info_dict['ab_update'] = 'true'\n    target_info_dict['ab_partitions'] = ['vendor']\n    target_info_dict[\"vendor.build.prop\"] = copy.deepcopy(self.TEST_TARGET_VENDOR_INFO_DICT)\n    source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)\n    source_info_dict['ab_update'] = 'true'\n    source_info_dict['ab_partitions'] = ['vendor']\n    source_info_dict[\"vendor.build.prop\"] = copy.deepcopy(self.TEST_SOURCE_VENDOR_INFO_DICT)\n    self._test_GetPackageMetadata_swapVendorBuildTimestamps(\n        target_info_dict, source_info_dict)\n\n    target_info = common.BuildInfo(target_info_dict, None)\n    source_info = common.BuildInfo(source_info_dict, None)\n    common.OPTIONS.incremental_source = ''\n    self.assertRaises(RuntimeError, self.GetLegacyOtaMetadata, target_info,\n                      source_info)\n\n  def test_GetPackageMetadata_downgrade(self):\n    target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)\n    source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)\n    self._test_GetPackageMetadata_swapBuildTimestamps(\n        target_info_dict, source_info_dict)\n\n    target_info = common.BuildInfo(target_info_dict, None)\n    source_info = common.BuildInfo(source_info_dict, None)\n    common.OPTIONS.incremental_source = ''\n    common.OPTIONS.downgrade = True\n    common.OPTIONS.wipe_user_data = True\n    common.OPTIONS.spl_downgrade = True\n    metadata = self.GetLegacyOtaMetadata(target_info, source_info)\n    # Reset spl_downgrade so other tests are unaffected\n    common.OPTIONS.spl_downgrade = False\n\n    self.assertDictEqual(\n        {\n            'ota-downgrade': 'yes',\n            'ota-type': 'BLOCK',\n            'ota-required-cache': '0',\n            'ota-wipe': 'yes',\n            'post-build': 'build-fingerprint-target',\n            'post-build-incremental': 'build-version-incremental-target',\n            'post-sdk-level': '27',\n            'post-security-patch-level': '2017-12-01',\n            'post-timestamp': '1400000000',\n            'pre-device': 'product-device',\n            'pre-build': 'build-fingerprint-source',\n            'pre-build-incremental': 'build-version-incremental-source',\n            'spl-downgrade': 'yes',\n        },\n        metadata)\n\n  def test_GetPackageMetadata_vendorDowngrade(self):\n    target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)\n    target_info_dict['ab_update'] = 'true'\n    target_info_dict['ab_partitions'] = ['vendor']\n    target_info_dict[\"vendor.build.prop\"] = copy.deepcopy(self.TEST_TARGET_VENDOR_INFO_DICT)\n    source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)\n    source_info_dict['ab_update'] = 'true'\n    source_info_dict['ab_partitions'] = ['vendor']\n    source_info_dict[\"vendor.build.prop\"] = copy.deepcopy(self.TEST_SOURCE_VENDOR_INFO_DICT)\n    self._test_GetPackageMetadata_swapVendorBuildTimestamps(\n        target_info_dict, source_info_dict)\n\n    target_info = common.BuildInfo(target_info_dict, None)\n    source_info = common.BuildInfo(source_info_dict, None)\n    common.OPTIONS.incremental_source = ''\n    common.OPTIONS.downgrade = True\n    common.OPTIONS.wipe_user_data = True\n    common.OPTIONS.spl_downgrade = True\n    metadata = self.GetLegacyOtaMetadata(target_info, source_info)\n    # Reset spl_downgrade so other tests are unaffected\n    common.OPTIONS.spl_downgrade = False\n\n    self.assertDictEqual(\n        {\n            'ota-downgrade': 'yes',\n            'ota-type': 'AB',\n            'ota-required-cache': '0',\n            'ota-wipe': 'yes',\n            'post-build': 'build-fingerprint-target',\n            'post-build-incremental': 'build-version-incremental-target',\n            'post-sdk-level': '27',\n            'post-security-patch-level': '2017-12-01',\n            'post-timestamp': '1500000000',\n            'pre-device': 'product-device',\n            'pre-build': 'build-fingerprint-source',\n            'pre-build-incremental': 'build-version-incremental-source',\n            'spl-downgrade': 'yes',\n        },\n        metadata)\n\n    post_build = GetPackageMetadata(target_info, source_info).postcondition\n    self.assertEqual('vendor', post_build.partition_state[0].partition_name)\n    self.assertEqual('12345678', post_build.partition_state[0].version)\n\n    pre_build = GetPackageMetadata(target_info, source_info).precondition\n    self.assertEqual('vendor', pre_build.partition_state[0].partition_name)\n    self.assertEqual('87654321', pre_build.partition_state[0].version)\n\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipForSecondaryImages(self):\n    input_file = construct_target_files(secondary=True)\n    target_file = GetTargetFilesZipForSecondaryImages(input_file)\n\n    with zipfile.ZipFile(target_file) as verify_zip:\n      namelist = verify_zip.namelist()\n      ab_partitions = verify_zip.read('META/ab_partitions.txt').decode()\n\n    self.assertIn('META/ab_partitions.txt', namelist)\n    self.assertIn('IMAGES/system.img', namelist)\n    self.assertIn('RADIO/bootloader.img', namelist)\n    self.assertIn(POSTINSTALL_CONFIG, namelist)\n\n    self.assertNotIn('IMAGES/boot.img', namelist)\n    self.assertNotIn('IMAGES/system_other.img', namelist)\n    self.assertNotIn('IMAGES/system.map', namelist)\n    self.assertNotIn('RADIO/modem.img', namelist)\n\n    expected_ab_partitions = ['system', 'bootloader']\n    self.assertEqual('\\n'.join(expected_ab_partitions), ab_partitions)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipForSecondaryImages_skipPostinstall(self):\n    input_file = construct_target_files(secondary=True)\n    target_file = GetTargetFilesZipForSecondaryImages(\n        input_file, skip_postinstall=True)\n\n    with zipfile.ZipFile(target_file) as verify_zip:\n      namelist = verify_zip.namelist()\n\n    self.assertIn('META/ab_partitions.txt', namelist)\n    self.assertIn('IMAGES/system.img', namelist)\n    self.assertIn('RADIO/bootloader.img', namelist)\n\n    self.assertNotIn('IMAGES/boot.img', namelist)\n    self.assertNotIn('IMAGES/system_other.img', namelist)\n    self.assertNotIn('IMAGES/system.map', namelist)\n    self.assertNotIn('RADIO/modem.img', namelist)\n    self.assertNotIn(POSTINSTALL_CONFIG, namelist)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipForSecondaryImages_withoutRadioImages(self):\n    input_file = construct_target_files(secondary=True)\n    common.ZipDelete(input_file, 'RADIO/bootloader.img')\n    common.ZipDelete(input_file, 'RADIO/modem.img')\n    target_file = GetTargetFilesZipForSecondaryImages(input_file)\n\n    with zipfile.ZipFile(target_file) as verify_zip:\n      namelist = verify_zip.namelist()\n\n    self.assertIn('META/ab_partitions.txt', namelist)\n    self.assertIn('IMAGES/system.img', namelist)\n    self.assertIn(POSTINSTALL_CONFIG, namelist)\n\n    self.assertNotIn('IMAGES/boot.img', namelist)\n    self.assertNotIn('IMAGES/system_other.img', namelist)\n    self.assertNotIn('IMAGES/system.map', namelist)\n    self.assertNotIn('RADIO/bootloader.img', namelist)\n    self.assertNotIn('RADIO/modem.img', namelist)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipForSecondaryImages_dynamicPartitions(self):\n    input_file = construct_target_files(secondary=True)\n    misc_info = '\\n'.join([\n        'use_dynamic_partition_size=true',\n        'use_dynamic_partitions=true',\n        'dynamic_partition_list=system vendor product',\n        'super_partition_groups=google_dynamic_partitions',\n        'super_google_dynamic_partitions_group_size=4873781248',\n        'super_google_dynamic_partitions_partition_list=system vendor product',\n    ])\n    dynamic_partitions_info = '\\n'.join([\n        'super_partition_groups=google_dynamic_partitions',\n        'super_google_dynamic_partitions_group_size=4873781248',\n        'super_google_dynamic_partitions_partition_list=system vendor product',\n    ])\n\n    with zipfile.ZipFile(input_file, 'a', allowZip64=True) as append_zip:\n      common.ZipWriteStr(append_zip, 'META/misc_info.txt', misc_info)\n      common.ZipWriteStr(append_zip, 'META/dynamic_partitions_info.txt',\n                         dynamic_partitions_info)\n\n    target_file = GetTargetFilesZipForSecondaryImages(input_file)\n\n    with zipfile.ZipFile(target_file) as verify_zip:\n      namelist = verify_zip.namelist()\n      updated_misc_info = verify_zip.read('META/misc_info.txt').decode()\n      updated_dynamic_partitions_info = verify_zip.read(\n          'META/dynamic_partitions_info.txt').decode()\n\n    self.assertIn('META/ab_partitions.txt', namelist)\n    self.assertIn('IMAGES/system.img', namelist)\n    self.assertIn(POSTINSTALL_CONFIG, namelist)\n    self.assertIn('META/misc_info.txt', namelist)\n    self.assertIn('META/dynamic_partitions_info.txt', namelist)\n\n    self.assertNotIn('IMAGES/boot.img', namelist)\n    self.assertNotIn('IMAGES/system_other.img', namelist)\n    self.assertNotIn('IMAGES/system.map', namelist)\n\n    # Check the vendor & product are removed from the partitions list.\n    expected_misc_info = misc_info.replace('system vendor product',\n                                           'system')\n    expected_dynamic_partitions_info = dynamic_partitions_info.replace(\n        'system vendor product', 'system')\n    self.assertEqual(expected_misc_info, updated_misc_info)\n    self.assertEqual(expected_dynamic_partitions_info,\n                     updated_dynamic_partitions_info)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipForPartialUpdates_singlePartition(self):\n    input_file = construct_target_files()\n    with zipfile.ZipFile(input_file, 'a', allowZip64=True) as append_zip:\n      common.ZipWriteStr(append_zip, 'IMAGES/system.map', 'fake map')\n\n    target_file = GetTargetFilesZipForPartialUpdates(input_file, ['system'])\n    with zipfile.ZipFile(target_file) as verify_zip:\n      namelist = verify_zip.namelist()\n      ab_partitions = verify_zip.read('META/ab_partitions.txt').decode()\n\n    self.assertIn('META/ab_partitions.txt', namelist)\n    self.assertIn('META/update_engine_config.txt', namelist)\n    self.assertIn('IMAGES/system.img', namelist)\n    self.assertIn('IMAGES/system.map', namelist)\n\n    self.assertNotIn('IMAGES/boot.img', namelist)\n    self.assertNotIn('IMAGES/system_other.img', namelist)\n    self.assertNotIn('RADIO/bootloader.img', namelist)\n    self.assertNotIn('RADIO/modem.img', namelist)\n\n    self.assertEqual('system', ab_partitions)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipForPartialUpdates_unrecognizedPartition(self):\n    input_file = construct_target_files()\n    self.assertRaises(ValueError, GetTargetFilesZipForPartialUpdates,\n                      input_file, ['product'])\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipForPartialUpdates_dynamicPartitions(self):\n    input_file = construct_target_files(secondary=True)\n    misc_info = '\\n'.join([\n        'use_dynamic_partition_size=true',\n        'use_dynamic_partitions=true',\n        'dynamic_partition_list=system vendor product',\n        'super_partition_groups=google_dynamic_partitions',\n        'super_google_dynamic_partitions_group_size=4873781248',\n        'super_google_dynamic_partitions_partition_list=system vendor product',\n    ])\n    dynamic_partitions_info = '\\n'.join([\n        'super_partition_groups=google_dynamic_partitions',\n        'super_google_dynamic_partitions_group_size=4873781248',\n        'super_google_dynamic_partitions_partition_list=system vendor product',\n    ])\n\n    with zipfile.ZipFile(input_file, 'a', allowZip64=True) as append_zip:\n      common.ZipWriteStr(append_zip, 'META/misc_info.txt', misc_info)\n      common.ZipWriteStr(append_zip, 'META/dynamic_partitions_info.txt',\n                         dynamic_partitions_info)\n\n    target_file = GetTargetFilesZipForPartialUpdates(input_file,\n                                                     ['boot', 'system'])\n    with zipfile.ZipFile(target_file) as verify_zip:\n      namelist = verify_zip.namelist()\n      ab_partitions = verify_zip.read('META/ab_partitions.txt').decode()\n      updated_misc_info = verify_zip.read('META/misc_info.txt').decode()\n      updated_dynamic_partitions_info = verify_zip.read(\n          'META/dynamic_partitions_info.txt').decode()\n\n    self.assertIn('META/ab_partitions.txt', namelist)\n    self.assertIn('IMAGES/boot.img', namelist)\n    self.assertIn('IMAGES/system.img', namelist)\n    self.assertIn('META/misc_info.txt', namelist)\n    self.assertIn('META/dynamic_partitions_info.txt', namelist)\n\n    self.assertNotIn('IMAGES/system_other.img', namelist)\n    self.assertNotIn('RADIO/bootloader.img', namelist)\n    self.assertNotIn('RADIO/modem.img', namelist)\n\n    # Check the vendor & product are removed from the partitions list.\n    expected_misc_info = misc_info.replace('system vendor product',\n                                           'system')\n    expected_dynamic_partitions_info = dynamic_partitions_info.replace(\n        'system vendor product', 'system')\n    self.assertEqual(expected_misc_info, updated_misc_info)\n    self.assertEqual(expected_dynamic_partitions_info,\n                     updated_dynamic_partitions_info)\n    self.assertEqual('boot\\nsystem', ab_partitions)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipWithoutPostinstallConfig(self):\n    input_file = construct_target_files()\n    target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)\n    with zipfile.ZipFile(target_file) as verify_zip:\n      self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipWithoutPostinstallConfig_missingEntry(self):\n    input_file = construct_target_files()\n    common.ZipDelete(input_file, POSTINSTALL_CONFIG)\n    target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)\n    with zipfile.ZipFile(target_file) as verify_zip:\n      self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipForCustomImagesUpdates_oemDefaultImage(self):\n    input_file = construct_target_files()\n    with zipfile.ZipFile(input_file, 'a', allowZip64=True) as append_zip:\n      common.ZipWriteStr(append_zip, 'IMAGES/oem.img', 'oem')\n      common.ZipWriteStr(append_zip, 'IMAGES/oem_test.img', 'oem_test')\n\n    target_file = GetTargetFilesZipForCustomImagesUpdates(\n        input_file, {'oem': 'oem.img'})\n\n    with zipfile.ZipFile(target_file) as verify_zip:\n      namelist = verify_zip.namelist()\n      ab_partitions = verify_zip.read('META/ab_partitions.txt').decode()\n      oem_image = verify_zip.read('IMAGES/oem.img').decode()\n\n    self.assertIn('META/ab_partitions.txt', namelist)\n    self.assertEqual('boot\\nsystem\\nvendor\\nbootloader\\nmodem', ab_partitions)\n    self.assertIn('IMAGES/oem.img', namelist)\n    self.assertEqual('oem', oem_image)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetTargetFilesZipForCustomImagesUpdates_oemTestImage(self):\n    input_file = construct_target_files()\n    with zipfile.ZipFile(input_file, 'a', allowZip64=True) as append_zip:\n      common.ZipWriteStr(append_zip, 'IMAGES/oem.img', 'oem')\n      common.ZipWriteStr(append_zip, 'IMAGES/oem_test.img', 'oem_test')\n\n    target_file = GetTargetFilesZipForCustomImagesUpdates(\n        input_file, {'oem': 'oem_test.img'})\n\n    with zipfile.ZipFile(target_file) as verify_zip:\n      namelist = verify_zip.namelist()\n      ab_partitions = verify_zip.read('META/ab_partitions.txt').decode()\n      oem_image = verify_zip.read('IMAGES/oem.img').decode()\n\n    self.assertIn('META/ab_partitions.txt', namelist)\n    self.assertEqual('boot\\nsystem\\nvendor\\nbootloader\\nmodem', ab_partitions)\n    self.assertIn('IMAGES/oem.img', namelist)\n    self.assertEqual('oem_test', oem_image)\n\n  def _test_FinalizeMetadata(self, large_entry=False):\n    entries = [\n        'required-entry1',\n        'required-entry2',\n    ]\n    zip_file = PropertyFilesTest.construct_zip_package(entries)\n    # Add a large entry of 1 GiB if requested.\n    if large_entry:\n      with zipfile.ZipFile(zip_file, 'a', allowZip64=True) as zip_fp:\n        zip_fp.writestr(\n            # Using 'zoo' so that the entry stays behind others after signing.\n            'zoo',\n            'A' * 1024 * 1024 * 1024,\n            zipfile.ZIP_STORED)\n\n    metadata = ota_metadata_pb2.OtaMetadata()\n    output_file = common.MakeTempFile(suffix='.zip')\n    needed_property_files = (\n        TestPropertyFiles(),\n    )\n    FinalizeMetadata(metadata, zip_file, output_file, needed_property_files)\n    self.assertIn('ota-test-property-files', metadata.property_files)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_FinalizeMetadata(self):\n    self._test_FinalizeMetadata()\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_FinalizeMetadata_withNoSigning(self):\n    common.OPTIONS.no_signing = True\n    self._test_FinalizeMetadata()\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_FinalizeMetadata_largeEntry(self):\n    self._test_FinalizeMetadata(large_entry=True)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_FinalizeMetadata_largeEntry_withNoSigning(self):\n    common.OPTIONS.no_signing = True\n    self._test_FinalizeMetadata(large_entry=True)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_FinalizeMetadata_insufficientSpace(self):\n    entries = [\n        'required-entry1',\n        'required-entry2',\n        'optional-entry1',\n        'optional-entry2',\n    ]\n    zip_file = PropertyFilesTest.construct_zip_package(entries)\n    with zipfile.ZipFile(zip_file, 'a', allowZip64=True) as zip_fp:\n      zip_fp.writestr(\n          # 'foo-entry1' will appear ahead of all other entries (in alphabetical\n          # order) after the signing, which will in turn trigger the\n          # InsufficientSpaceException and an automatic retry.\n          'foo-entry1',\n          'A' * 1024 * 1024,\n          zipfile.ZIP_STORED)\n\n    metadata = ota_metadata_pb2.OtaMetadata()\n    needed_property_files = (\n        TestPropertyFiles(),\n    )\n    output_file = common.MakeTempFile(suffix='.zip')\n    FinalizeMetadata(metadata, zip_file, output_file, needed_property_files)\n    self.assertIn('ota-test-property-files', metadata.property_files)\n\n\nclass TestPropertyFiles(PropertyFiles):\n  \"\"\"A class that extends PropertyFiles for testing purpose.\"\"\"\n\n  def __init__(self):\n    super(TestPropertyFiles, self).__init__()\n    self.name = 'ota-test-property-files'\n    self.required = (\n        'required-entry1',\n        'required-entry2',\n    )\n    self.optional = (\n        'optional-entry1',\n        'optional-entry2',\n    )\n\n\nclass PropertyFilesTest(PropertyFilesTestCase):\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Compute(self):\n    entries = (\n        'required-entry1',\n        'required-entry2',\n    )\n    zip_file = self.construct_zip_package(entries)\n    property_files = TestPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      property_files_string = property_files.Compute(zip_fp)\n\n    tokens = self._parse_property_files_string(property_files_string)\n    self.assertEqual(4, len(tokens))\n    self._verify_entries(zip_file, tokens, entries)\n\n  def test_Compute_withOptionalEntries(self):\n    entries = (\n        'required-entry1',\n        'required-entry2',\n        'optional-entry1',\n        'optional-entry2',\n    )\n    zip_file = self.construct_zip_package(entries)\n    property_files = TestPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      property_files_string = property_files.Compute(zip_fp)\n\n    tokens = self._parse_property_files_string(property_files_string)\n    self.assertEqual(6, len(tokens))\n    self._verify_entries(zip_file, tokens, entries)\n\n  def test_Compute_missingRequiredEntry(self):\n    entries = (\n        'required-entry2',\n    )\n    zip_file = self.construct_zip_package(entries)\n    property_files = TestPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      self.assertRaises(KeyError, property_files.Compute, zip_fp)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Finalize(self):\n    entries = [\n        'required-entry1',\n        'required-entry2',\n        'META-INF/com/android/metadata',\n        'META-INF/com/android/metadata.pb',\n    ]\n    zip_file = self.construct_zip_package(entries)\n    property_files = TestPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      raw_metadata = property_files.GetPropertyFilesString(\n          zip_fp, reserve_space=False)\n      streaming_metadata = property_files.Finalize(zip_fp, len(raw_metadata))\n    tokens = self._parse_property_files_string(streaming_metadata)\n\n    self.assertEqual(4, len(tokens))\n    # 'META-INF/com/android/metadata' will be key'd as 'metadata' in the\n    # streaming metadata.\n    entries[2] = 'metadata'\n    entries[3] = 'metadata.pb'\n    self._verify_entries(zip_file, tokens, entries)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Finalize_assertReservedLength(self):\n    entries = (\n        'required-entry1',\n        'required-entry2',\n        'optional-entry1',\n        'optional-entry2',\n        'META-INF/com/android/metadata',\n        'META-INF/com/android/metadata.pb',\n    )\n    zip_file = self.construct_zip_package(entries)\n    property_files = TestPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      # First get the raw metadata string (i.e. without padding space).\n      raw_metadata = property_files.GetPropertyFilesString(\n          zip_fp, reserve_space=False)\n      raw_length = len(raw_metadata)\n\n      # Now pass in the exact expected length.\n      streaming_metadata = property_files.Finalize(zip_fp, raw_length)\n      self.assertEqual(raw_length, len(streaming_metadata))\n\n      # Or pass in insufficient length.\n      self.assertRaises(\n          PropertyFiles.InsufficientSpaceException,\n          property_files.Finalize,\n          zip_fp,\n          raw_length - 1)\n\n      # Or pass in a much larger size.\n      streaming_metadata = property_files.Finalize(\n          zip_fp,\n          raw_length + 20)\n      self.assertEqual(raw_length + 20, len(streaming_metadata))\n      self.assertEqual(' ' * 20, streaming_metadata[raw_length:])\n\n  def test_Verify(self):\n    entries = (\n        'required-entry1',\n        'required-entry2',\n        'optional-entry1',\n        'optional-entry2',\n        'META-INF/com/android/metadata',\n        'META-INF/com/android/metadata.pb',\n    )\n    zip_file = self.construct_zip_package(entries)\n    property_files = TestPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      # First get the raw metadata string (i.e. without padding space).\n      raw_metadata = property_files.GetPropertyFilesString(\n          zip_fp, reserve_space=False)\n\n      # Should pass the test if verification passes.\n      property_files.Verify(zip_fp, raw_metadata)\n\n      # Or raise on verification failure.\n      self.assertRaises(\n          AssertionError, property_files.Verify, zip_fp, raw_metadata + 'x')\n\n\nclass StreamingPropertyFilesTest(PropertyFilesTestCase):\n  \"\"\"Additional validity checks specialized for StreamingPropertyFiles.\"\"\"\n\n  def test_init(self):\n    property_files = StreamingPropertyFiles()\n    self.assertEqual('ota-streaming-property-files', property_files.name)\n    self.assertEqual(\n        (\n            'payload.bin',\n            'payload_properties.txt',\n        ),\n        property_files.required)\n    self.assertEqual(\n        (\n            'apex_info.pb',\n            'care_map.pb',\n            'care_map.txt',\n            'compatibility.zip',\n        ),\n        property_files.optional)\n\n  def test_Compute(self):\n    entries = (\n        'payload.bin',\n        'payload_properties.txt',\n        'care_map.txt',\n        'compatibility.zip',\n    )\n    zip_file = self.construct_zip_package(entries)\n    property_files = StreamingPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      property_files_string = property_files.Compute(zip_fp)\n\n    tokens = self._parse_property_files_string(property_files_string)\n    self.assertEqual(6, len(tokens))\n    self._verify_entries(zip_file, tokens, entries)\n\n  def test_Finalize(self):\n    entries = [\n        'payload.bin',\n        'payload_properties.txt',\n        'care_map.txt',\n        'compatibility.zip',\n        'META-INF/com/android/metadata',\n        'META-INF/com/android/metadata.pb',\n    ]\n    zip_file = self.construct_zip_package(entries)\n    property_files = StreamingPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      raw_metadata = property_files.GetPropertyFilesString(\n          zip_fp, reserve_space=False)\n      streaming_metadata = property_files.Finalize(zip_fp, len(raw_metadata))\n    tokens = self._parse_property_files_string(streaming_metadata)\n\n    self.assertEqual(6, len(tokens))\n    # 'META-INF/com/android/metadata' will be key'd as 'metadata' in the\n    # streaming metadata.\n    entries[4] = 'metadata'\n    entries[5] = 'metadata.pb'\n    self._verify_entries(zip_file, tokens, entries)\n\n  def test_Verify(self):\n    entries = (\n        'payload.bin',\n        'payload_properties.txt',\n        'care_map.txt',\n        'compatibility.zip',\n        'META-INF/com/android/metadata',\n        'META-INF/com/android/metadata.pb',\n    )\n    zip_file = self.construct_zip_package(entries)\n    property_files = StreamingPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      # First get the raw metadata string (i.e. without padding space).\n      raw_metadata = property_files.GetPropertyFilesString(\n          zip_fp, reserve_space=False)\n\n      # Should pass the test if verification passes.\n      property_files.Verify(zip_fp, raw_metadata)\n\n      # Or raise on verification failure.\n      self.assertRaises(\n          AssertionError, property_files.Verify, zip_fp, raw_metadata + 'x')\n\n\nclass AbOtaPropertyFilesTest(PropertyFilesTestCase):\n  \"\"\"Additional validity checks specialized for AbOtaPropertyFiles.\"\"\"\n\n  # The size for payload and metadata signature size.\n  SIGNATURE_SIZE = 256\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n    self.assertTrue(os.path.exists(self.testdata_dir))\n\n    common.OPTIONS.wipe_user_data = False\n    common.OPTIONS.payload_signer = None\n    common.OPTIONS.payload_signer_args = None\n    common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')\n    common.OPTIONS.key_passwords = {\n        common.OPTIONS.package_key: None,\n    }\n\n  def test_init(self):\n    property_files = AbOtaPropertyFiles()\n    self.assertEqual('ota-property-files', property_files.name)\n    self.assertEqual(\n        (\n            'payload.bin',\n            'payload_properties.txt',\n        ),\n        property_files.required)\n    self.assertEqual(\n        (\n            'apex_info.pb',\n            'care_map.pb',\n            'care_map.txt',\n            'compatibility.zip',\n        ),\n        property_files.optional)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetPayloadMetadataOffsetAndSize(self):\n    target_file = construct_target_files()\n    payload = PayloadGenerator()\n    payload.Generate(target_file)\n\n    payload_signer = PayloadSigner()\n    payload.Sign(payload_signer)\n\n    output_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:\n      payload.WriteToZip(output_zip)\n\n    # Find out the payload metadata offset and size.\n    property_files = AbOtaPropertyFiles()\n    with zipfile.ZipFile(output_file) as input_zip:\n      # pylint: disable=protected-access\n      payload_offset, metadata_total = (\n          property_files._GetPayloadMetadataOffsetAndSize(input_zip))\n\n    # The signature proto has the following format (details in\n    #  /platform/system/update_engine/update_metadata.proto):\n    #  message Signature {\n    #    optional uint32 version = 1;\n    #    optional bytes data = 2;\n    #    optional fixed32 unpadded_signature_size = 3;\n    #  }\n    #\n    # According to the protobuf encoding, the tail of the signature message will\n    # be [signature string(256 bytes) + encoding of the fixed32 number 256]. And\n    # 256 is encoded as 'x1d\\x00\\x01\\x00\\x00':\n    # [3 (field number) << 3 | 5 (type) + byte reverse of 0x100 (256)].\n    # Details in (https://developers.google.com/protocol-buffers/docs/encoding)\n    signature_tail_length = self.SIGNATURE_SIZE + 5\n    self.assertGreater(metadata_total, signature_tail_length)\n    with open(output_file, 'rb') as verify_fp:\n      verify_fp.seek(payload_offset + metadata_total - signature_tail_length)\n      metadata_signature_proto_tail = verify_fp.read(signature_tail_length)\n\n    self.assertEqual(b'\\x1d\\x00\\x01\\x00\\x00',\n                     metadata_signature_proto_tail[-5:])\n    metadata_signature = metadata_signature_proto_tail[:-5]\n\n    # Now we extract the metadata hash via brillo_update_payload script, which\n    # will serve as the oracle result.\n    payload_sig_file = common.MakeTempFile(prefix=\"sig-\", suffix=\".bin\")\n    metadata_sig_file = common.MakeTempFile(prefix=\"sig-\", suffix=\".bin\")\n    cmd = ['brillo_update_payload', 'hash',\n           '--unsigned_payload', payload.payload_file,\n           '--signature_size', str(self.SIGNATURE_SIZE),\n           '--metadata_hash_file', metadata_sig_file,\n           '--payload_hash_file', payload_sig_file]\n    proc = common.Run(cmd)\n    stdoutdata, _ = proc.communicate()\n    self.assertEqual(\n        0, proc.returncode,\n        'Failed to run brillo_update_payload:\\n{}'.format(stdoutdata))\n\n    signed_metadata_sig_file = payload_signer.SignHashFile(metadata_sig_file)\n\n    # Finally we can compare the two signatures.\n    with open(signed_metadata_sig_file, 'rb') as verify_fp:\n      self.assertEqual(verify_fp.read(), metadata_signature)\n\n  @staticmethod\n  def construct_zip_package_withValidPayload(with_metadata=False):\n    # Cannot use construct_zip_package() since we need a \"valid\" payload.bin.\n    target_file = construct_target_files()\n    payload = PayloadGenerator()\n    payload.Generate(target_file)\n\n    payload_signer = PayloadSigner()\n    payload.Sign(payload_signer)\n\n    zip_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(zip_file, 'w', allowZip64=True) as zip_fp:\n      # 'payload.bin',\n      payload.WriteToZip(zip_fp)\n\n      # Other entries.\n      entries = ['care_map.txt', 'compatibility.zip']\n\n      # Put META-INF/com/android/metadata if needed.\n      if with_metadata:\n        entries.append('META-INF/com/android/metadata')\n        entries.append('META-INF/com/android/metadata.pb')\n\n      for entry in entries:\n        zip_fp.writestr(\n            entry, entry.replace('.', '-').upper(), zipfile.ZIP_STORED)\n\n    return zip_file\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Compute(self):\n    zip_file = self.construct_zip_package_withValidPayload()\n    property_files = AbOtaPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      property_files_string = property_files.Compute(zip_fp)\n\n    tokens = self._parse_property_files_string(property_files_string)\n    # \"7\" indcludes the four entries above, two metadata entries, and one entry\n    # for payload-metadata.bin.\n    self.assertEqual(7, len(tokens))\n    self._verify_entries(\n        zip_file, tokens, ('care_map.txt', 'compatibility.zip'))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Finalize(self):\n    zip_file = self.construct_zip_package_withValidPayload(with_metadata=True)\n    property_files = AbOtaPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      raw_metadata = property_files.GetPropertyFilesString(\n          zip_fp, reserve_space=False)\n      property_files_string = property_files.Finalize(\n          zip_fp, len(raw_metadata))\n\n    tokens = self._parse_property_files_string(property_files_string)\n    # \"7\" includes the four entries above, two metadata entries, and one entry\n    # for payload-metadata.bin.\n    self.assertEqual(7, len(tokens))\n    self._verify_entries(\n        zip_file, tokens, ('care_map.txt', 'compatibility.zip'))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Verify(self):\n    zip_file = self.construct_zip_package_withValidPayload(with_metadata=True)\n    property_files = AbOtaPropertyFiles()\n    with zipfile.ZipFile(zip_file, 'r', allowZip64=True) as zip_fp:\n      raw_metadata = property_files.GetPropertyFilesString(\n          zip_fp, reserve_space=False)\n\n      property_files.Verify(zip_fp, raw_metadata)\n\n\nclass PayloadSignerTest(test_utils.ReleaseToolsTestCase):\n\n  SIGFILE = 'sigfile.bin'\n  SIGNED_SIGFILE = 'signed-sigfile.bin'\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n    self.assertTrue(os.path.exists(self.testdata_dir))\n\n    common.OPTIONS.payload_signer = None\n    common.OPTIONS.payload_signer_args = []\n    common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')\n    common.OPTIONS.key_passwords = {\n        common.OPTIONS.package_key: None,\n    }\n\n  def _assertFilesEqual(self, file1, file2):\n    with open(file1, 'rb') as fp1, open(file2, 'rb') as fp2:\n      self.assertEqual(fp1.read(), fp2.read())\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_init(self):\n    payload_signer = PayloadSigner()\n    self.assertEqual('openssl', payload_signer.signer)\n    self.assertEqual(256, payload_signer.maximum_signature_size)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_init_withPassword(self):\n    common.OPTIONS.package_key = os.path.join(\n        self.testdata_dir, 'testkey_with_passwd')\n    common.OPTIONS.key_passwords = {\n        common.OPTIONS.package_key: 'foo',\n    }\n    payload_signer = PayloadSigner()\n    self.assertEqual('openssl', payload_signer.signer)\n\n  def test_init_withExternalSigner(self):\n    common.OPTIONS.payload_signer_args = ['arg1', 'arg2']\n    common.OPTIONS.payload_signer_maximum_signature_size = '512'\n    payload_signer = PayloadSigner(\n        OPTIONS.package_key, OPTIONS.private_key_suffix, payload_signer='abc')\n    self.assertEqual('abc', payload_signer.signer)\n    self.assertEqual(['arg1', 'arg2'], payload_signer.signer_args)\n    self.assertEqual(512, payload_signer.maximum_signature_size)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetMaximumSignatureSizeInBytes_512Bytes(self):\n    signing_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')\n    # pylint: disable=protected-access\n    signature_size = PayloadSigner._GetMaximumSignatureSizeInBytes(signing_key)\n    self.assertEqual(512, signature_size)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_GetMaximumSignatureSizeInBytes_ECKey(self):\n    signing_key = os.path.join(self.testdata_dir, 'testkey_EC.key')\n    # pylint: disable=protected-access\n    signature_size = PayloadSigner._GetMaximumSignatureSizeInBytes(signing_key)\n    self.assertEqual(72, signature_size)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Sign(self):\n    payload_signer = PayloadSigner()\n    input_file = os.path.join(self.testdata_dir, self.SIGFILE)\n    signed_file = payload_signer.SignHashFile(input_file)\n\n    verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)\n    self._assertFilesEqual(verify_file, signed_file)\n\n  def test_Sign_withExternalSigner_openssl(self):\n    \"\"\"Uses openssl as the external payload signer.\"\"\"\n    common.OPTIONS.payload_signer_args = [\n        'pkeyutl', '-sign', '-keyform', 'DER', '-inkey',\n        os.path.join(self.testdata_dir, 'testkey.pk8'),\n        '-pkeyopt', 'digest:sha256']\n    payload_signer = PayloadSigner(\n        OPTIONS.package_key, OPTIONS.private_key_suffix, payload_signer=\"openssl\")\n    input_file = os.path.join(self.testdata_dir, self.SIGFILE)\n    signed_file = payload_signer.SignHashFile(input_file)\n\n    verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)\n    self._assertFilesEqual(verify_file, signed_file)\n\n  def test_Sign_withExternalSigner_script(self):\n    \"\"\"Uses testdata/payload_signer.sh as the external payload signer.\"\"\"\n    external_signer = os.path.join(\n        self.testdata_dir, 'payload_signer.sh')\n    os.chmod(external_signer, 0o700)\n    common.OPTIONS.payload_signer_args = [\n        os.path.join(self.testdata_dir, 'testkey.pk8')]\n    payload_signer = PayloadSigner(\n        OPTIONS.package_key, OPTIONS.private_key_suffix, payload_signer=external_signer)\n    input_file = os.path.join(self.testdata_dir, self.SIGFILE)\n    signed_file = payload_signer.SignHashFile(input_file)\n\n    verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)\n    self._assertFilesEqual(verify_file, signed_file)\n\n\nclass PayloadTest(test_utils.ReleaseToolsTestCase):\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n    self.assertTrue(os.path.exists(self.testdata_dir))\n\n    common.OPTIONS.wipe_user_data = False\n    common.OPTIONS.payload_signer = None\n    common.OPTIONS.payload_signer_args = None\n    common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')\n    common.OPTIONS.key_passwords = {\n        common.OPTIONS.package_key: None,\n    }\n\n  @staticmethod\n  def _create_payload_full(secondary=False):\n    target_file = construct_target_files(secondary)\n    payload = PayloadGenerator(secondary, OPTIONS.wipe_user_data)\n    payload.Generate(target_file)\n    return payload\n\n  @staticmethod\n  def _create_payload_incremental():\n    target_file = construct_target_files()\n    source_file = construct_target_files()\n    payload = PayloadGenerator()\n    payload.Generate(target_file, source_file)\n    return payload\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Generate_full(self):\n    payload = self._create_payload_full()\n    self.assertTrue(os.path.exists(payload.payload_file))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Generate_incremental(self):\n    payload = self._create_payload_incremental()\n    self.assertTrue(os.path.exists(payload.payload_file))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Generate_additionalArgs(self):\n    target_file = construct_target_files()\n    source_file = construct_target_files()\n    payload = PayloadGenerator()\n    # This should work the same as calling payload.Generate(target_file,\n    # source_file).\n    payload.Generate(\n        target_file, additional_args=[\"--source_image\", source_file])\n    self.assertTrue(os.path.exists(payload.payload_file))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Generate_invalidInput(self):\n    target_file = construct_target_files()\n    common.ZipDelete(target_file, 'IMAGES/vendor.img')\n    payload = PayloadGenerator()\n    self.assertRaises(common.ExternalError, payload.Generate, target_file)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Sign_full(self):\n    payload = self._create_payload_full()\n    payload.Sign(PayloadSigner())\n\n    output_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:\n      payload.WriteToZip(output_zip)\n\n    import check_ota_package_signature\n    check_ota_package_signature.VerifyAbOtaPayload(\n        os.path.join(self.testdata_dir, 'testkey.x509.pem'),\n        output_file)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Sign_incremental(self):\n    payload = self._create_payload_incremental()\n    payload.Sign(PayloadSigner())\n\n    output_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:\n      payload.WriteToZip(output_zip)\n\n    import check_ota_package_signature\n    check_ota_package_signature.VerifyAbOtaPayload(\n        os.path.join(self.testdata_dir, 'testkey.x509.pem'),\n        output_file)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Sign_withDataWipe(self):\n    common.OPTIONS.wipe_user_data = True\n    payload = self._create_payload_full()\n    payload.Sign(PayloadSigner())\n    with tempfile.NamedTemporaryFile() as fp:\n      with zipfile.ZipFile(fp, \"w\") as zfp:\n        payload.WriteToZip(zfp)\n\n    with open(payload.payload_properties) as properties_fp:\n      self.assertIn(\"POWERWASH=1\", properties_fp.read())\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Sign_secondary(self):\n    payload = self._create_payload_full(secondary=True)\n    payload.Sign(PayloadSigner())\n    with tempfile.NamedTemporaryFile() as fp:\n      with zipfile.ZipFile(fp, \"w\") as zfp:\n        payload.WriteToZip(zfp)\n\n    with open(payload.payload_properties) as properties_fp:\n      self.assertIn(\"SWITCH_SLOT_ON_REBOOT=0\", properties_fp.read())\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_Sign_badSigner(self):\n    \"\"\"Tests that signing failure can be captured.\"\"\"\n    payload = self._create_payload_full()\n    payload_signer = PayloadSigner()\n    payload_signer.signer_args.append('bad-option')\n    self.assertRaises(common.ExternalError, payload.Sign, payload_signer)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_WriteToZip(self):\n    payload = self._create_payload_full()\n    payload.Sign(PayloadSigner())\n\n    output_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:\n      payload.WriteToZip(output_zip)\n\n    with zipfile.ZipFile(output_file) as verify_zip:\n      # First make sure we have the essential entries.\n      namelist = verify_zip.namelist()\n      self.assertIn(PayloadGenerator.PAYLOAD_BIN, namelist)\n      self.assertIn(PayloadGenerator.PAYLOAD_PROPERTIES_TXT, namelist)\n\n      # Then assert these entries are stored.\n      for entry_info in verify_zip.infolist():\n        if entry_info.filename not in (PayloadGenerator.PAYLOAD_BIN,\n                                       PayloadGenerator.PAYLOAD_PROPERTIES_TXT):\n          continue\n        self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_WriteToZip_secondary(self):\n    payload = self._create_payload_full(secondary=True)\n    payload.Sign(PayloadSigner())\n\n    output_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:\n      payload.WriteToZip(output_zip)\n\n    with zipfile.ZipFile(output_file) as verify_zip:\n      # First make sure we have the essential entries.\n      namelist = verify_zip.namelist()\n      self.assertIn(PayloadGenerator.SECONDARY_PAYLOAD_BIN, namelist)\n      self.assertIn(PayloadGenerator.SECONDARY_PAYLOAD_PROPERTIES_TXT, namelist)\n\n      # Then assert these entries are stored.\n      for entry_info in verify_zip.infolist():\n        if entry_info.filename not in (\n                PayloadGenerator.SECONDARY_PAYLOAD_BIN,\n                PayloadGenerator.SECONDARY_PAYLOAD_PROPERTIES_TXT):\n          continue\n        self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)\n\n\nclass RuntimeFingerprintTest(test_utils.ReleaseToolsTestCase):\n  MISC_INFO = [\n      'recovery_api_version=3',\n      'fstab_version=2',\n      'recovery_as_boot=true',\n      'ab_update=true',\n  ]\n\n  BUILD_PROP = [\n      'ro.build.id=build-id',\n      'ro.build.version.incremental=version-incremental',\n      'ro.build.type=build-type',\n      'ro.build.tags=build-tags',\n      'ro.build.version.release=version-release',\n      'ro.build.version.release_or_codename=version-release',\n      'ro.build.version.sdk=30',\n      'ro.build.version.security_patch=2020',\n      'ro.build.date.utc=12345678',\n      'ro.system.build.version.release=version-release',\n      'ro.system.build.id=build-id',\n      'ro.system.build.version.incremental=version-incremental',\n      'ro.system.build.type=build-type',\n      'ro.system.build.tags=build-tags',\n      'ro.system.build.version.sdk=30',\n      'ro.system.build.version.security_patch=2020',\n      'ro.system.build.date.utc=12345678',\n      'ro.product.system.brand=generic',\n      'ro.product.system.name=generic',\n      'ro.product.system.device=generic',\n  ]\n\n  VENDOR_BUILD_PROP = [\n      'ro.vendor.build.version.release=version-release',\n      'ro.vendor.build.id=build-id',\n      'ro.vendor.build.version.incremental=version-incremental',\n      'ro.vendor.build.type=build-type',\n      'ro.vendor.build.tags=build-tags',\n      'ro.vendor.build.version.sdk=30',\n      'ro.vendor.build.version.security_patch=2020',\n      'ro.vendor.build.date.utc=12345678',\n      'ro.product.vendor.brand=vendor-product-brand',\n      'ro.product.vendor.name=vendor-product-name',\n      'ro.product.vendor.device=vendor-product-device'\n  ]\n\n  def setUp(self):\n    common.OPTIONS.oem_dicts = None\n    self.test_dir = common.MakeTempDir()\n    self.writeFiles({'META/misc_info.txt': '\\n'.join(self.MISC_INFO)},\n                    self.test_dir)\n\n  def writeFiles(self, contents_dict, out_dir):\n    for path, content in contents_dict.items():\n      abs_path = os.path.join(out_dir, path)\n      dir_name = os.path.dirname(abs_path)\n      if not os.path.exists(dir_name):\n        os.makedirs(dir_name)\n      with open(abs_path, 'w') as f:\n        f.write(content)\n\n  @staticmethod\n  def constructFingerprint(prefix):\n    return '{}:version-release/build-id/version-incremental:' \\\n           'build-type/build-tags'.format(prefix)\n\n  def test_CalculatePossibleFingerprints_no_dynamic_fingerprint(self):\n    build_prop = copy.deepcopy(self.BUILD_PROP)\n    build_prop.extend([\n        'ro.product.brand=product-brand',\n        'ro.product.name=product-name',\n        'ro.product.device=product-device',\n    ])\n    self.writeFiles({\n        'SYSTEM/build.prop': '\\n'.join(build_prop),\n        'VENDOR/build.prop': '\\n'.join(self.VENDOR_BUILD_PROP),\n    }, self.test_dir)\n\n    build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir))\n    expected = ({'product-device'},\n                {self.constructFingerprint(\n                    'product-brand/product-name/product-device')})\n    self.assertEqual(expected,\n                     CalculateRuntimeDevicesAndFingerprints(build_info, {}))\n\n  def test_CalculatePossibleFingerprints_single_override(self):\n    vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP)\n    vendor_build_prop.extend([\n        'import /vendor/etc/build_${ro.boot.sku_name}.prop',\n    ])\n    self.writeFiles({\n        'SYSTEM/build.prop': '\\n'.join(self.BUILD_PROP),\n        'VENDOR/build.prop': '\\n'.join(vendor_build_prop),\n        'VENDOR/etc/build_std.prop':\n        'ro.product.vendor.name=vendor-product-std',\n        'VENDOR/etc/build_pro.prop':\n        'ro.product.vendor.name=vendor-product-pro',\n    }, self.test_dir)\n\n    build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir))\n    boot_variable_values = {'ro.boot.sku_name': ['std', 'pro']}\n\n    expected = ({'vendor-product-device'}, {\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-name/vendor-product-device'),\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-std/vendor-product-device'),\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-pro/vendor-product-device'),\n    })\n    self.assertEqual(\n        expected, CalculateRuntimeDevicesAndFingerprints(\n            build_info, boot_variable_values))\n\n  def test_CalculatePossibleFingerprints_multiple_overrides(self):\n    vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP)\n    vendor_build_prop.extend([\n        'import /vendor/etc/build_${ro.boot.sku_name}.prop',\n        'import /vendor/etc/build_${ro.boot.device_name}.prop',\n    ])\n    self.writeFiles({\n        'SYSTEM/build.prop': '\\n'.join(self.BUILD_PROP),\n        'VENDOR/build.prop': '\\n'.join(vendor_build_prop),\n        'VENDOR/etc/build_std.prop':\n        'ro.product.vendor.name=vendor-product-std',\n        'VENDOR/etc/build_product1.prop':\n        'ro.product.vendor.device=vendor-device-product1',\n        'VENDOR/etc/build_pro.prop':\n        'ro.product.vendor.name=vendor-product-pro',\n        'VENDOR/etc/build_product2.prop':\n        'ro.product.vendor.device=vendor-device-product2',\n    }, self.test_dir)\n\n    build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir))\n    boot_variable_values = {\n        'ro.boot.sku_name': ['std', 'pro'],\n        'ro.boot.device_name': ['product1', 'product2'],\n    }\n\n    expected_devices = {'vendor-product-device', 'vendor-device-product1',\n                        'vendor-device-product2'}\n    expected_fingerprints = {\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-name/vendor-product-device'),\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-std/vendor-device-product1'),\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-pro/vendor-device-product1'),\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-std/vendor-device-product2'),\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-pro/vendor-device-product2')\n    }\n    self.assertEqual((expected_devices, expected_fingerprints),\n                     CalculateRuntimeDevicesAndFingerprints(\n                         build_info, boot_variable_values))\n\n  def test_GetPackageMetadata_full_package(self):\n    vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP)\n    vendor_build_prop.extend([\n        'import /vendor/etc/build_${ro.boot.sku_name}.prop',\n    ])\n    self.writeFiles({\n        'SYSTEM/build.prop': '\\n'.join(self.BUILD_PROP),\n        'VENDOR/build.prop': '\\n'.join(vendor_build_prop),\n        'VENDOR/etc/build_std.prop':\n        'ro.product.vendor.name=vendor-product-std',\n        'VENDOR/etc/build_pro.prop':\n        'ro.product.vendor.name=vendor-product-pro',\n        AB_PARTITIONS: '\\n'.join(['system', 'vendor']),\n    }, self.test_dir)\n\n    common.OPTIONS.boot_variable_file = common.MakeTempFile()\n    with open(common.OPTIONS.boot_variable_file, 'w') as f:\n      f.write('ro.boot.sku_name=std,pro')\n\n    build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir))\n    metadata_dict = BuildLegacyOtaMetadata(GetPackageMetadata(build_info))\n    self.assertEqual('vendor-product-device', metadata_dict['pre-device'])\n    fingerprints = [\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-name/vendor-product-device'),\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-pro/vendor-product-device'),\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-std/vendor-product-device'),\n    ]\n    self.assertEqual('|'.join(fingerprints), metadata_dict['post-build'])\n\n  def CheckMetadataEqual(self, metadata_dict, metadata_proto):\n    post_build = metadata_proto.postcondition\n    self.assertEqual('|'.join(post_build.build),\n                     metadata_dict['post-build'])\n    self.assertEqual(post_build.build_incremental,\n                     metadata_dict['post-build-incremental'])\n    self.assertEqual(post_build.sdk_level,\n                     metadata_dict['post-sdk-level'])\n    self.assertEqual(post_build.security_patch_level,\n                     metadata_dict['post-security-patch-level'])\n\n    if metadata_proto.type == ota_metadata_pb2.OtaMetadata.AB:\n      ota_type = 'AB'\n    elif metadata_proto.type == ota_metadata_pb2.OtaMetadata.BLOCK:\n      ota_type = 'BLOCK'\n    else:\n      ota_type = ''\n    self.assertEqual(ota_type, metadata_dict['ota-type'])\n    self.assertEqual(metadata_proto.wipe,\n                     metadata_dict.get('ota-wipe') == 'yes')\n    self.assertEqual(metadata_proto.required_cache,\n                     int(metadata_dict.get('ota-required-cache', 0)))\n    self.assertEqual(metadata_proto.retrofit_dynamic_partitions,\n                     metadata_dict.get(\n                         'ota-retrofit-dynamic-partitions') == 'yes')\n\n  def test_GetPackageMetadata_incremental_package(self):\n    vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP)\n    vendor_build_prop.extend([\n        'import /vendor/etc/build_${ro.boot.sku_name}.prop',\n    ])\n    self.writeFiles({\n        'META/misc_info.txt': '\\n'.join(self.MISC_INFO),\n        'META/ab_partitions.txt': '\\n'.join(['system', 'vendor', 'product']),\n        'SYSTEM/build.prop': '\\n'.join(self.BUILD_PROP),\n        'VENDOR/build.prop': '\\n'.join(vendor_build_prop),\n        'VENDOR/etc/build_std.prop':\n        'ro.product.vendor.device=vendor-device-std',\n        'VENDOR/etc/build_pro.prop':\n        'ro.product.vendor.device=vendor-device-pro',\n    }, self.test_dir)\n\n    common.OPTIONS.boot_variable_file = common.MakeTempFile()\n    with open(common.OPTIONS.boot_variable_file, 'w') as f:\n      f.write('ro.boot.sku_name=std,pro')\n\n    source_dir = common.MakeTempDir()\n    source_build_prop = [\n        'ro.build.version.release=source-version-release',\n        'ro.build.id=source-build-id',\n        'ro.build.version.incremental=source-version-incremental',\n        'ro.build.type=build-type',\n        'ro.build.tags=build-tags',\n        'ro.build.version.sdk=29',\n        'ro.build.version.security_patch=2020',\n        'ro.build.date.utc=12340000',\n        'ro.system.build.version.release=source-version-release',\n        'ro.system.build.id=source-build-id',\n        'ro.system.build.version.incremental=source-version-incremental',\n        'ro.system.build.type=build-type',\n        'ro.system.build.tags=build-tags',\n        'ro.system.build.version.sdk=29',\n        'ro.system.build.version.security_patch=2020',\n        'ro.system.build.date.utc=12340000',\n        'ro.product.system.brand=generic',\n        'ro.product.system.name=generic',\n        'ro.product.system.device=generic',\n    ]\n    self.writeFiles({\n        'META/misc_info.txt': '\\n'.join(self.MISC_INFO),\n        'META/ab_partitions.txt': '\\n'.join(['system', 'vendor', 'product']),\n        'SYSTEM/build.prop': '\\n'.join(source_build_prop),\n        'VENDOR/build.prop': '\\n'.join(vendor_build_prop),\n        'VENDOR/etc/build_std.prop':\n        'ro.product.vendor.device=vendor-device-std',\n        'VENDOR/etc/build_pro.prop':\n        'ro.product.vendor.device=vendor-device-pro',\n    }, source_dir)\n    common.OPTIONS.incremental_source = source_dir\n\n    target_info = common.BuildInfo(common.LoadInfoDict(self.test_dir))\n    source_info = common.BuildInfo(common.LoadInfoDict(source_dir))\n\n    metadata_proto = GetPackageMetadata(target_info, source_info)\n    metadata_dict = BuildLegacyOtaMetadata(metadata_proto)\n    self.assertEqual(\n        'vendor-device-pro|vendor-device-std|vendor-product-device',\n        metadata_dict['pre-device'])\n    source_suffix = ':source-version-release/source-build-id/' \\\n                    'source-version-incremental:build-type/build-tags'\n    pre_fingerprints = [\n        'vendor-product-brand/vendor-product-name/vendor-device-pro'\n        '{}'.format(source_suffix),\n        'vendor-product-brand/vendor-product-name/vendor-device-std'\n        '{}'.format(source_suffix),\n        'vendor-product-brand/vendor-product-name/vendor-product-device'\n        '{}'.format(source_suffix),\n    ]\n    self.assertEqual('|'.join(pre_fingerprints), metadata_dict['pre-build'])\n\n    post_fingerprints = [\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-name/vendor-device-pro'),\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-name/vendor-device-std'),\n        self.constructFingerprint(\n            'vendor-product-brand/vendor-product-name/vendor-product-device'),\n    ]\n    self.assertEqual('|'.join(post_fingerprints), metadata_dict['post-build'])\n\n    self.CheckMetadataEqual(metadata_dict, metadata_proto)\n\n    pre_partition_states = metadata_proto.precondition.partition_state\n    self.assertEqual(2, len(pre_partition_states))\n    self.assertEqual('system', pre_partition_states[0].partition_name)\n    self.assertEqual(['generic'], pre_partition_states[0].device)\n    self.assertEqual(['generic/generic/generic{}'.format(source_suffix)],\n                     pre_partition_states[0].build)\n\n    self.assertEqual('vendor', pre_partition_states[1].partition_name)\n    self.assertEqual(['vendor-device-pro', 'vendor-device-std',\n                      'vendor-product-device'], pre_partition_states[1].device)\n    vendor_fingerprints = post_fingerprints\n    self.assertEqual(vendor_fingerprints, pre_partition_states[1].build)\n\n    post_partition_states = metadata_proto.postcondition.partition_state\n    self.assertEqual(2, len(post_partition_states))\n    self.assertEqual('system', post_partition_states[0].partition_name)\n    self.assertEqual(['generic'], post_partition_states[0].device)\n    self.assertEqual([self.constructFingerprint('generic/generic/generic')],\n                     post_partition_states[0].build)\n\n    self.assertEqual('vendor', post_partition_states[1].partition_name)\n    self.assertEqual(['vendor-device-pro', 'vendor-device-std',\n                      'vendor-product-device'], post_partition_states[1].device)\n    self.assertEqual(vendor_fingerprints, post_partition_states[1].build)\n"
  },
  {
    "path": "tools/releasetools/test_ota_utils.py",
    "content": "# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport unittest\nimport io\nimport ota_utils\nimport zipfile\n\n\nclass TestZipEntryOffset(unittest.TestCase):\n  def test_extra_length_differ(self):\n      # This is a magic zip file such that:\n      # 1. It has 1 entry, `file.txt'`\n      # 2. The central directory entry for the entry contains an extra field of#\n      # length 24, while the local file header for the entry contains an extra#\n      # field of length 28.\n      # It is key that the entry contains extra field of different length.\n      # The sole purpose of this test case is make sure our offset computing\n      # logic works in this scenario.\n\n      # This is created by:\n      # touch file.txt\n      # zip -0 test.zip file.txt\n      # Above command may or may not work on all platforms.\n      # Some zip implementation will keep the extra field size consistent.\n      # Some don't\n    magic_zip = b'PK\\x03\\x04\\n\\x00\\x00\\x00\\x00\\x00nY\\xfcR\\x00\\x00\\x00\\x00\\x00\\x00\\x00' +\\\n        b'\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x1c\\x00file.txtUT\\t\\x00\\x03' +\\\n        b'\\xa0s\\x01a\\xa0s\\x01aux\\x0b\\x00\\x01\\x04\\x88\\xc4\\t\\x00\\x04S_\\x01\\x00' +\\\n        b'PK\\x01\\x02\\x1e\\x03\\n\\x00\\x00\\x00\\x00\\x00nY\\xfcR\\x00\\x00\\x00\\x00' +\\\n        b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x18\\x00\\x00\\x00\\x00\\x00' +\\\n        b'\\x00\\x00\\x00\\x00\\x80\\x81\\x00\\x00\\x00\\x00file.txt' +\\\n        b'UT\\x05\\x00\\x03\\xa0s\\x01aux\\x0b\\x00\\x01\\x04\\x88\\xc4\\t\\x00\\x04' +\\\n        b'S_\\x01\\x00PK\\x05\\x06\\x00\\x00\\x00\\x00\\x01\\x00\\x01\\x00N\\x00\\x00' +\\\n        b'\\x00B\\x00\\x00\\x00\\x00\\x00'\n    # Just making sure we concatenated the bytes correctly\n    self.assertEqual(len(magic_zip), 166)\n    fp = io.BytesIO(magic_zip)\n    with zipfile.ZipFile(fp, 'r') as zfp:\n      self.assertGreater(len(zfp.infolist()), 0)\n      zinfo = zfp.getinfo(\"file.txt\")\n      (offset, size) = ota_utils.GetZipEntryOffset(zfp, zinfo)\n      self.assertEqual(size, zinfo.file_size)\n      self.assertEqual(offset, zipfile.sizeFileHeader+len(zinfo.filename) + 28)\n"
  },
  {
    "path": "tools/releasetools/test_rangelib.py",
    "content": "#\n# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nfrom rangelib import RangeSet\nfrom test_utils import ReleaseToolsTestCase\n\n\nclass RangeSetTest(ReleaseToolsTestCase):\n\n  def test_union(self):\n    self.assertEqual(RangeSet(\"10-19 30-34\").union(RangeSet(\"18-29\")),\n                     RangeSet(\"10-34\"))\n    self.assertEqual(RangeSet(\"10-19 30-34\").union(RangeSet(\"22 32\")),\n                     RangeSet(\"10-19 22 30-34\"))\n\n  def test_intersect(self):\n    self.assertEqual(RangeSet(\"10-19 30-34\").intersect(RangeSet(\"18-32\")),\n                     RangeSet(\"18-19 30-32\"))\n    self.assertEqual(RangeSet(\"10-19 30-34\").intersect(RangeSet(\"22-28\")),\n                     RangeSet(\"\"))\n\n  def test_subtract(self):\n    self.assertEqual(RangeSet(\"10-19 30-34\").subtract(RangeSet(\"18-32\")),\n                     RangeSet(\"10-17 33-34\"))\n    self.assertEqual(RangeSet(\"10-19 30-34\").subtract(RangeSet(\"22-28\")),\n                     RangeSet(\"10-19 30-34\"))\n\n  def test_overlaps(self):\n    self.assertTrue(RangeSet(\"10-19 30-34\").overlaps(RangeSet(\"18-32\")))\n    self.assertFalse(RangeSet(\"10-19 30-34\").overlaps(RangeSet(\"22-28\")))\n\n  def test_size(self):\n    self.assertEqual(RangeSet(\"10-19 30-34\").size(), 15)\n    self.assertEqual(RangeSet(\"\").size(), 0)\n\n  def test_map_within(self):\n    self.assertEqual(RangeSet(\"0-9\").map_within(RangeSet(\"3-4\")),\n                     RangeSet(\"3-4\"))\n    self.assertEqual(RangeSet(\"10-19\").map_within(RangeSet(\"13-14\")),\n                     RangeSet(\"3-4\"))\n    self.assertEqual(\n        RangeSet(\"10-19 30-39\").map_within(RangeSet(\"17-19 30-32\")),\n        RangeSet(\"7-12\"))\n    self.assertEqual(\n        RangeSet(\"10-19 30-39\").map_within(RangeSet(\"12-13 17-19 30-32\")),\n        RangeSet(\"2-3 7-12\"))\n\n  def test_first(self):\n    self.assertEqual(RangeSet(\"0-9\").first(1), RangeSet(\"0\"))\n    self.assertEqual(RangeSet(\"10-19\").first(5), RangeSet(\"10-14\"))\n    self.assertEqual(RangeSet(\"10-19\").first(15), RangeSet(\"10-19\"))\n    self.assertEqual(RangeSet(\"10-19 30-39\").first(3), RangeSet(\"10-12\"))\n    self.assertEqual(RangeSet(\"10-19 30-39\").first(15),\n                     RangeSet(\"10-19 30-34\"))\n    self.assertEqual(RangeSet(\"10-19 30-39\").first(30),\n                     RangeSet(\"10-19 30-39\"))\n    self.assertEqual(RangeSet(\"0-9\").first(0), RangeSet(\"\"))\n\n  def test_extend(self):\n    self.assertEqual(RangeSet(\"0-9\").extend(1), RangeSet(\"0-10\"))\n    self.assertEqual(RangeSet(\"10-19\").extend(15), RangeSet(\"0-34\"))\n    self.assertEqual(RangeSet(\"10-19 30-39\").extend(4), RangeSet(\"6-23 26-43\"))\n    self.assertEqual(RangeSet(\"10-19 30-39\").extend(10), RangeSet(\"0-49\"))\n\n  def test_equality(self):\n    self.assertTrue(RangeSet(\"\") == RangeSet(\"\"))\n    self.assertTrue(RangeSet(\"3\") == RangeSet(\"3\"))\n    self.assertTrue(RangeSet(\"3 5\") == RangeSet(\"5 3\"))\n    self.assertTrue(\n        RangeSet(\"10-19 30-39\") == RangeSet(\"30-32 10-14 33-39 15-19\"))\n    self.assertTrue(RangeSet(\"\") != RangeSet(\"3\"))\n    self.assertTrue(RangeSet(\"10-19\") != RangeSet(\"10-19 20\"))\n\n    self.assertFalse(RangeSet(\"\"))\n    self.assertTrue(RangeSet(\"3\"))\n\n  def test_init(self):\n    self.assertIsNotNone(RangeSet(\"\"))\n    self.assertIsNotNone(RangeSet(\"3\"))\n    self.assertIsNotNone(RangeSet(\"3 5\"))\n    self.assertIsNotNone(RangeSet(\"10 19 30-39\"))\n\n    with self.assertRaises(AssertionError):\n      RangeSet(data=[0])\n\n  def test_str(self):\n    self.assertEqual(str(RangeSet(\"0-9\")), \"0-9\")\n    self.assertEqual(str(RangeSet(\"2-10 12\")), \"2-10 12\")\n    self.assertEqual(str(RangeSet(\"11 2-10 12 1 0\")), \"0-12\")\n    self.assertEqual(str(RangeSet(\"\")), \"empty\")\n\n  def test_to_string_raw(self):\n    self.assertEqual(RangeSet(\"0-9\").to_string_raw(), \"2,0,10\")\n    self.assertEqual(RangeSet(\"2-10 12\").to_string_raw(), \"4,2,11,12,13\")\n    self.assertEqual(RangeSet(\"11 2-10 12 1 0\").to_string_raw(), \"2,0,13\")\n\n    with self.assertRaises(AssertionError):\n      RangeSet(\"\").to_string_raw()\n\n  def test_monotonic(self):\n    self.assertTrue(RangeSet(\"0-9\").monotonic)\n    self.assertTrue(RangeSet(\"2-9\").monotonic)\n    self.assertTrue(RangeSet(\"2-9 30 31 35\").monotonic)\n    self.assertTrue(RangeSet(\"\").monotonic)\n    self.assertTrue(RangeSet(\"0-4 5-9\").monotonic)\n    self.assertFalse(RangeSet(\"5-9 0-4\").monotonic)\n    self.assertFalse(RangeSet(\"258768-259211 196604\").monotonic)\n\n    self.assertTrue(RangeSet(data=[0, 10]).monotonic)\n    self.assertTrue(RangeSet(data=[0, 10, 15, 20]).monotonic)\n    self.assertTrue(RangeSet(data=[2, 9, 30, 31, 31, 32, 35, 36]).monotonic)\n    self.assertTrue(RangeSet(data=[0, 5, 5, 10]).monotonic)\n    self.assertFalse(RangeSet(data=[5, 10, 0, 5]).monotonic)\n\n  def test_parse_raw(self):\n    self.assertEqual(\n        RangeSet.parse_raw(RangeSet(\"0-9\").to_string_raw()),\n        RangeSet(\"0-9\"))\n    self.assertEqual(\n        RangeSet.parse_raw(RangeSet(\"2-10 12\").to_string_raw()),\n        RangeSet(\"2-10 12\"))\n    self.assertEqual(\n        RangeSet.parse_raw(RangeSet(\"11 2-10 12 1 0\").to_string_raw()),\n        RangeSet(\"11 2-10 12 1 0\"))\n\n    with self.assertRaises(AssertionError):\n      RangeSet.parse_raw(\"4,0,10\")\n\n  def test_next_item(self):\n    self.assertEqual(\n        list(RangeSet(\"0-9\").next_item()),\n        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n    self.assertEqual(\n        list(RangeSet(\"10-19 3-5\").next_item()),\n        [3, 4, 5, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])\n    self.assertEqual(\n        list(RangeSet(\"10-19 3 5 7\").next_item()),\n        [3, 5, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])\n"
  },
  {
    "path": "tools/releasetools/test_sign_apex.py",
    "content": "#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport os.path\n\nimport common\nimport sign_apex\nimport test_utils\n\n\nclass SignApexTest(test_utils.ReleaseToolsTestCase):\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n    self.assertTrue(os.path.exists(self.testdata_dir))\n\n    common.OPTIONS.search_path = test_utils.get_search_path()\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_SignApexFile(self):\n    foo_apex = os.path.join(self.testdata_dir, 'foo.apex')\n    payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')\n    container_key = os.path.join(self.testdata_dir, 'testkey')\n    signed_foo_apex = sign_apex.SignApexFile(\n        'avbtool',\n        foo_apex,\n        payload_key,\n        container_key,\n        False)\n    self.assertTrue(os.path.exists(signed_foo_apex))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_SignApexWithApk(self):\n    test_apex = os.path.join(self.testdata_dir, 'has_apk.apex')\n    payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')\n    container_key = os.path.join(self.testdata_dir, 'testkey')\n    apk_keys = {'wifi-service-resources.apk': os.path.join(\n        self.testdata_dir, 'testkey')}\n    signed_test_apex = sign_apex.SignApexFile(\n        'avbtool',\n        test_apex,\n        payload_key,\n        container_key,\n        False,\n        apk_keys)\n    self.assertTrue(os.path.exists(signed_test_apex))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_SignCompressedApexFile(self):\n    apex = os.path.join(test_utils.get_current_dir(), 'com.android.apex.compressed.v1.capex')\n    payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')\n    container_key = os.path.join(self.testdata_dir, 'testkey')\n    signed_apex = sign_apex.SignApexFile(\n        'avbtool',\n        apex,\n        payload_key,\n        container_key,\n        False,\n        codename_to_api_level_map={'S': 31, 'Tiramisu' : 32})\n    self.assertTrue(os.path.exists(signed_apex))\n"
  },
  {
    "path": "tools/releasetools/test_sign_target_files_apks.py",
    "content": "#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport base64\nimport io\nimport os.path\nimport zipfile\n\nimport common\nimport test_utils\nfrom sign_target_files_apks import (\n    CheckApkAndApexKeysAvailable, EditTags, GetApkFileInfo, ParseAvbInfo,\n    ReadApexKeysInfo, ReplaceCerts, RewriteAvbProps, RewriteProps,\n    WriteOtacerts)\n\n\nclass SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):\n\n  MAC_PERMISSIONS_XML = \"\"\"<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<policy>\n  <signer signature=\"{}\"><seinfo value=\"platform\"/></signer>\n  <signer signature=\"{}\"><seinfo value=\"media\"/></signer>\n</policy>\"\"\"\n\n  # Note that we test one apex with the partition tag, and another without to\n  # make sure that new OTA tools can process an older target files package that\n  # does not include the partition tag.\n\n  # pylint: disable=line-too-long\n  APEX_KEYS_TXT = \"\"\"name=\"apex.apexd_test.apex\" public_key=\"system/apex/apexd/apexd_testdata/com.android.apex.test_package.avbpubkey\" private_key=\"system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem\" container_certificate=\"build/make/target/product/security/testkey.x509.pem\" container_private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"system\"\nname=\"apex.apexd_test_different_app.apex\" public_key=\"system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey\" private_key=\"system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem\" container_certificate=\"build/make/target/product/security/testkey.x509.pem\" container_private_key=\"build/make/target/product/security/testkey.pk8\"\n\"\"\"\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n\n  def test_EditTags(self):\n    self.assertEqual(EditTags('dev-keys'), ('release-keys'))\n    self.assertEqual(EditTags('test-keys'), ('release-keys'))\n\n    # Multiple tags.\n    self.assertEqual(EditTags('abc,dev-keys,xyz'), ('abc,release-keys,xyz'))\n\n    # Tags are sorted.\n    self.assertEqual(EditTags('xyz,abc,dev-keys,xyz'), ('abc,release-keys,xyz'))\n\n  def test_RewriteAvbProps(self):\n    misc_info = {\n      'avb_boot_add_hash_footer_args':\n          ('--prop com.android.build.boot.os_version:R '\n           '--prop com.android.build.boot.security_patch:2019-09-05'),\n      'avb_init_boot_add_hash_footer_args':\n          ('--prop com.android.build.boot.os_version:R '\n           '--prop com.android.build.boot.security_patch:2019-09-05'),\n      'avb_system_add_hashtree_footer_args':\n          ('--prop com.android.build.system.os_version:R '\n           '--prop com.android.build.system.security_patch:2019-09-05 '\n           '--prop com.android.build.system.fingerprint:'\n           'Android/aosp_taimen/taimen:R/QT/foo:userdebug/test-keys'),\n      'avb_vendor_add_hashtree_footer_args':\n          ('--prop com.android.build.vendor.os_version:R '\n           '--prop com.android.build.vendor.security_patch:2019-09-05 '\n           '--prop com.android.build.vendor.fingerprint:'\n           'Android/aosp_taimen/taimen:R/QT/foo:userdebug/dev-keys'),\n    }\n    expected_dict = {\n      'avb_boot_add_hash_footer_args':\n          ('--prop com.android.build.boot.os_version:R '\n           '--prop com.android.build.boot.security_patch:2019-09-05'),\n      'avb_init_boot_add_hash_footer_args':\n          ('--prop com.android.build.boot.os_version:R '\n           '--prop com.android.build.boot.security_patch:2019-09-05'),\n      'avb_system_add_hashtree_footer_args':\n          ('--prop com.android.build.system.os_version:R '\n           '--prop com.android.build.system.security_patch:2019-09-05 '\n           '--prop com.android.build.system.fingerprint:'\n           'Android/aosp_taimen/taimen:R/QT/foo:userdebug/release-keys'),\n      'avb_vendor_add_hashtree_footer_args':\n          ('--prop com.android.build.vendor.os_version:R '\n           '--prop com.android.build.vendor.security_patch:2019-09-05 '\n           '--prop com.android.build.vendor.fingerprint:'\n           'Android/aosp_taimen/taimen:R/QT/foo:userdebug/release-keys'),\n    }\n    RewriteAvbProps(misc_info)\n    self.assertDictEqual(expected_dict, misc_info)\n\n  def test_RewriteProps(self):\n    props = (\n        ('', ''),\n        ('ro.build.fingerprint=foo/bar/dev-keys',\n         'ro.build.fingerprint=foo/bar/release-keys'),\n        ('ro.build.thumbprint=foo/bar/dev-keys',\n         'ro.build.thumbprint=foo/bar/release-keys'),\n        ('ro.vendor.build.fingerprint=foo/bar/dev-keys',\n         'ro.vendor.build.fingerprint=foo/bar/release-keys'),\n        ('ro.vendor.build.thumbprint=foo/bar/dev-keys',\n         'ro.vendor.build.thumbprint=foo/bar/release-keys'),\n        ('ro.odm.build.fingerprint=foo/bar/test-keys',\n         'ro.odm.build.fingerprint=foo/bar/release-keys'),\n        ('ro.odm.build.thumbprint=foo/bar/test-keys',\n         'ro.odm.build.thumbprint=foo/bar/release-keys'),\n        ('ro.product.build.fingerprint=foo/bar/dev-keys',\n         'ro.product.build.fingerprint=foo/bar/release-keys'),\n        ('ro.product.build.thumbprint=foo/bar/dev-keys',\n         'ro.product.build.thumbprint=foo/bar/release-keys'),\n        ('ro.system_ext.build.fingerprint=foo/bar/test-keys',\n         'ro.system_ext.build.fingerprint=foo/bar/release-keys'),\n        ('ro.system_ext.build.thumbprint=foo/bar/test-keys',\n         'ro.system_ext.build.thumbprint=foo/bar/release-keys'),\n        ('# comment line 1', '# comment line 1'),\n        ('ro.bootimage.build.fingerprint=foo/bar/dev-keys',\n         'ro.bootimage.build.fingerprint=foo/bar/release-keys'),\n        ('ro.build.description='\n         'sailfish-user 8.0.0 OPR6.170623.012 4283428 dev-keys',\n         'ro.build.description='\n         'sailfish-user 8.0.0 OPR6.170623.012 4283428 release-keys'),\n        ('ro.build.tags=dev-keys', 'ro.build.tags=release-keys'),\n        ('ro.build.tags=test-keys', 'ro.build.tags=release-keys'),\n        ('ro.system.build.tags=dev-keys',\n         'ro.system.build.tags=release-keys'),\n        ('ro.vendor.build.tags=dev-keys',\n         'ro.vendor.build.tags=release-keys'),\n        ('ro.odm.build.tags=dev-keys',\n         'ro.odm.build.tags=release-keys'),\n        ('ro.product.build.tags=dev-keys',\n         'ro.product.build.tags=release-keys'),\n        ('ro.system_ext.build.tags=dev-keys',\n         'ro.system_ext.build.tags=release-keys'),\n        ('# comment line 2', '# comment line 2'),\n        ('ro.build.display.id=OPR6.170623.012 dev-keys',\n         'ro.build.display.id=OPR6.170623.012'),\n        ('# comment line 3', '# comment line 3'),\n    )\n\n    # Assert the case for each individual line.\n    for prop, expected in props:\n      self.assertEqual(expected + '\\n', RewriteProps(prop))\n\n    # Concatenate all the input lines.\n    self.assertEqual(\n        '\\n'.join([prop[1] for prop in props]) + '\\n',\n        RewriteProps('\\n'.join([prop[0] for prop in props])))\n\n  def test_ReplaceCerts(self):\n    cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem')\n    with open(cert1_path) as cert1_fp:\n      cert1 = cert1_fp.read()\n    cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem')\n    with open(cert2_path) as cert2_fp:\n      cert2 = cert2_fp.read()\n    cert3_path = os.path.join(self.testdata_dir, 'testkey.x509.pem')\n    with open(cert3_path) as cert3_fp:\n      cert3 = cert3_fp.read()\n\n    # Replace cert1 with cert3.\n    input_xml = self.MAC_PERMISSIONS_XML.format(\n        base64.b16encode(common.ParseCertificate(cert1)).lower(),\n        base64.b16encode(common.ParseCertificate(cert2)).lower())\n\n    output_xml = self.MAC_PERMISSIONS_XML.format(\n        base64.b16encode(common.ParseCertificate(cert3)).lower(),\n        base64.b16encode(common.ParseCertificate(cert2)).lower())\n\n    common.OPTIONS.key_map = {\n        cert1_path[:-9] : cert3_path[:-9],\n    }\n\n    self.assertEqual(output_xml, ReplaceCerts(input_xml))\n\n  def test_ReplaceCerts_duplicateEntries(self):\n    cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem')\n    with open(cert1_path) as cert1_fp:\n      cert1 = cert1_fp.read()\n    cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem')\n    with open(cert2_path) as cert2_fp:\n      cert2 = cert2_fp.read()\n\n    # Replace cert1 with cert2, which leads to duplicate entries.\n    input_xml = self.MAC_PERMISSIONS_XML.format(\n        base64.b16encode(common.ParseCertificate(cert1)).lower(),\n        base64.b16encode(common.ParseCertificate(cert2)).lower())\n\n    common.OPTIONS.key_map = {\n        cert1_path[:-9] : cert2_path[:-9],\n    }\n    self.assertRaises(AssertionError, ReplaceCerts, input_xml)\n\n  def test_ReplaceCerts_skipNonExistentCerts(self):\n    cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem')\n    with open(cert1_path) as cert1_fp:\n      cert1 = cert1_fp.read()\n    cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem')\n    with open(cert2_path) as cert2_fp:\n      cert2 = cert2_fp.read()\n    cert3_path = os.path.join(self.testdata_dir, 'testkey.x509.pem')\n    with open(cert3_path) as cert3_fp:\n      cert3 = cert3_fp.read()\n\n    input_xml = self.MAC_PERMISSIONS_XML.format(\n        base64.b16encode(common.ParseCertificate(cert1)).lower(),\n        base64.b16encode(common.ParseCertificate(cert2)).lower())\n\n    output_xml = self.MAC_PERMISSIONS_XML.format(\n        base64.b16encode(common.ParseCertificate(cert3)).lower(),\n        base64.b16encode(common.ParseCertificate(cert2)).lower())\n\n    common.OPTIONS.key_map = {\n        cert1_path[:-9] : cert3_path[:-9],\n        'non-existent' : cert3_path[:-9],\n        cert2_path[:-9] : 'non-existent',\n    }\n    self.assertEqual(output_xml, ReplaceCerts(input_xml))\n\n  def test_WriteOtacerts(self):\n    certs = [\n        os.path.join(self.testdata_dir, 'platform.x509.pem'),\n        os.path.join(self.testdata_dir, 'media.x509.pem'),\n        os.path.join(self.testdata_dir, 'testkey.x509.pem'),\n    ]\n    entry_name = 'SYSTEM/etc/security/otacerts.zip'\n    output_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:\n      WriteOtacerts(output_zip, entry_name, certs)\n    with zipfile.ZipFile(output_file) as input_zip:\n      self.assertIn(entry_name, input_zip.namelist())\n      otacerts_file = io.BytesIO(input_zip.read(entry_name))\n      with zipfile.ZipFile(otacerts_file) as otacerts_zip:\n        self.assertEqual(3, len(otacerts_zip.namelist()))\n\n  def test_CheckApkAndApexKeysAvailable(self):\n    input_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:\n      input_zip.writestr('SYSTEM/app/App1.apk', \"App1-content\")\n      input_zip.writestr('SYSTEM/app/App2.apk.gz', \"App2-content\")\n\n    apk_key_map = {\n        'App1.apk' : 'key1',\n        'App2.apk' : 'key2',\n        'App3.apk' : 'key3',\n    }\n    with zipfile.ZipFile(input_file) as input_zip:\n      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, {})\n      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.gz', {})\n\n      # 'App2.apk.gz' won't be considered as an APK.\n      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, {})\n      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.xz', {})\n\n      del apk_key_map['App2.apk']\n      self.assertRaises(\n          AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,\n          '.gz', {})\n\n  def test_CheckApkAndApexKeysAvailable_invalidApexKeys(self):\n    input_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:\n      input_zip.writestr('SYSTEM/apex/Apex1.apex', \"Apex1-content\")\n      input_zip.writestr('SYSTEM/apex/Apex2.apex', \"Apex2-content\")\n\n    apk_key_map = {\n        'Apex1.apex' : 'key1',\n        'Apex2.apex' : 'key2',\n        'Apex3.apex' : 'key3',\n    }\n    apex_keys = {\n        'Apex1.apex' : ('payload-key1', 'container-key1', None),\n        'Apex2.apex' : ('payload-key2', 'container-key2', None),\n    }\n    with zipfile.ZipFile(input_file) as input_zip:\n      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)\n\n      # Fine to have both keys as PRESIGNED.\n      apex_keys['Apex2.apex'] = ('PRESIGNED', 'PRESIGNED', None)\n      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)\n\n      # Having only one of them as PRESIGNED is not allowed.\n      apex_keys['Apex2.apex'] = ('payload-key2', 'PRESIGNED', None)\n      self.assertRaises(\n          AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,\n          None, apex_keys)\n\n      apex_keys['Apex2.apex'] = ('PRESIGNED', 'container-key1', None)\n      self.assertRaises(\n          AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,\n          None, apex_keys)\n\n  def test_GetApkFileInfo(self):\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"PRODUCT/apps/Chats.apk\", None, [])\n    self.assertTrue(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertFalse(should_be_skipped)\n\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"PRODUCT/apps/Chats.apk\", None, [])\n    self.assertTrue(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertFalse(should_be_skipped)\n\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"PRODUCT/apps/Chats.dat\", None, [])\n    self.assertFalse(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertFalse(should_be_skipped)\n\n  def test_GetApkFileInfo_withCompressedApks(self):\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"PRODUCT/apps/Chats.apk.gz\", \".gz\", [])\n    self.assertTrue(is_apk)\n    self.assertTrue(is_compressed)\n    self.assertFalse(should_be_skipped)\n\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"PRODUCT/apps/Chats.apk.gz\", \".xz\", [])\n    self.assertFalse(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertFalse(should_be_skipped)\n\n    self.assertRaises(\n        AssertionError, GetApkFileInfo, \"PRODUCT/apps/Chats.apk\", \"\", [])\n\n    self.assertRaises(\n        AssertionError, GetApkFileInfo, \"PRODUCT/apps/Chats.apk\", \"apk\", [])\n\n  def test_GetApkFileInfo_withSkippedPrefixes(self):\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"PRODUCT/preloads/apps/Chats.apk\", None, set())\n    self.assertTrue(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertFalse(should_be_skipped)\n\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"PRODUCT/preloads/apps/Chats.apk\",\n        None,\n        set([\"PRODUCT/preloads/\"]))\n    self.assertTrue(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertTrue(should_be_skipped)\n\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"SYSTEM_OTHER/preloads/apps/Chats.apk\",\n        None,\n        set([\"SYSTEM/preloads/\", \"SYSTEM_OTHER/preloads/\"]))\n    self.assertTrue(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertTrue(should_be_skipped)\n\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"SYSTEM_OTHER/preloads/apps/Chats.apk.gz\",\n        \".gz\",\n        set([\"PRODUCT/prebuilts/\", \"SYSTEM_OTHER/preloads/\"]))\n    self.assertTrue(is_apk)\n    self.assertTrue(is_compressed)\n    self.assertTrue(should_be_skipped)\n\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"SYSTEM_OTHER/preloads/apps/Chats.dat\",\n        None,\n        set([\"SYSTEM_OTHER/preloads/\"]))\n    self.assertFalse(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertFalse(should_be_skipped)\n\n  def test_GetApkFileInfo_checkSkippedPrefixesInput(self):\n    # set\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"SYSTEM_OTHER/preloads/apps/Chats.apk\",\n        None,\n        set([\"SYSTEM_OTHER/preloads/\"]))\n    self.assertTrue(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertTrue(should_be_skipped)\n\n    # tuple\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"SYSTEM_OTHER/preloads/apps/Chats.apk\",\n        None,\n        (\"SYSTEM_OTHER/preloads/\",))\n    self.assertTrue(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertTrue(should_be_skipped)\n\n    # list\n    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(\n        \"SYSTEM_OTHER/preloads/apps/Chats.apk\",\n        None,\n        [\"SYSTEM_OTHER/preloads/\"])\n    self.assertTrue(is_apk)\n    self.assertFalse(is_compressed)\n    self.assertTrue(should_be_skipped)\n\n    # str is invalid.\n    self.assertRaises(\n        AssertionError, GetApkFileInfo, \"SYSTEM_OTHER/preloads/apps/Chats.apk\",\n        None, \"SYSTEM_OTHER/preloads/\")\n\n    # None is invalid.\n    self.assertRaises(\n        AssertionError, GetApkFileInfo, \"SYSTEM_OTHER/preloads/apps/Chats.apk\",\n        None, None)\n\n  def test_ReadApexKeysInfo(self):\n    target_files = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.writestr('META/apexkeys.txt', self.APEX_KEYS_TXT)\n\n    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:\n      keys_info = ReadApexKeysInfo(target_files_zip)\n\n    self.assertEqual({\n        'apex.apexd_test.apex': (\n            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',\n            'build/make/target/product/security/testkey', None),\n        'apex.apexd_test_different_app.apex': (\n            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',\n            'build/make/target/product/security/testkey', None),\n        }, keys_info)\n\n  def test_ReadApexKeysInfo_mismatchingContainerKeys(self):\n    # Mismatching payload public / private keys.\n    apex_keys = self.APEX_KEYS_TXT + (\n        'name=\"apex.apexd_test_different_app2.apex\" '\n        'public_key=\"system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey\" '\n        'private_key=\"system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem\" '\n        'container_certificate=\"build/make/target/product/security/testkey.x509.pem\" '\n        'container_private_key=\"build/make/target/product/security/testkey2.pk8\" '\n        'partition=\"system\"')\n    target_files = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.writestr('META/apexkeys.txt', apex_keys)\n\n    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:\n      self.assertRaises(ValueError, ReadApexKeysInfo, target_files_zip)\n\n  def test_ReadApexKeysInfo_missingPayloadPrivateKey(self):\n    # Invalid lines will be skipped.\n    apex_keys = self.APEX_KEYS_TXT + (\n        'name=\"apex.apexd_test_different_app2.apex\" '\n        'public_key=\"system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey\" '\n        'container_certificate=\"build/make/target/product/security/testkey.x509.pem\" '\n        'container_private_key=\"build/make/target/product/security/testkey.pk8\"')\n    target_files = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.writestr('META/apexkeys.txt', apex_keys)\n\n    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:\n      keys_info = ReadApexKeysInfo(target_files_zip)\n\n    self.assertEqual({\n        'apex.apexd_test.apex': (\n            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',\n            'build/make/target/product/security/testkey', None),\n        'apex.apexd_test_different_app.apex': (\n            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',\n            'build/make/target/product/security/testkey', None),\n        }, keys_info)\n\n  def test_ReadApexKeysInfo_missingPayloadPublicKey(self):\n    # Invalid lines will be skipped.\n    apex_keys = self.APEX_KEYS_TXT + (\n        'name=\"apex.apexd_test_different_app2.apex\" '\n        'private_key=\"system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem\" '\n        'container_certificate=\"build/make/target/product/security/testkey.x509.pem\" '\n        'container_private_key=\"build/make/target/product/security/testkey.pk8\"')\n    target_files = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.writestr('META/apexkeys.txt', apex_keys)\n\n    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:\n      keys_info = ReadApexKeysInfo(target_files_zip)\n\n    self.assertEqual({\n        'apex.apexd_test.apex': (\n            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',\n            'build/make/target/product/security/testkey', None),\n        'apex.apexd_test_different_app.apex': (\n            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',\n            'build/make/target/product/security/testkey', None),\n        }, keys_info)\n\n  def test_ReadApexKeysInfo_presignedKeys(self):\n    apex_keys = self.APEX_KEYS_TXT + (\n        'name=\"apex.apexd_test_different_app2.apex\" '\n        'private_key=\"PRESIGNED\" '\n        'public_key=\"PRESIGNED\" '\n        'container_certificate=\"PRESIGNED\" '\n        'container_private_key=\"PRESIGNED\"')\n    target_files = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.writestr('META/apexkeys.txt', apex_keys)\n\n    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:\n      keys_info = ReadApexKeysInfo(target_files_zip)\n\n    self.assertEqual({\n        'apex.apexd_test.apex': (\n            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',\n            'build/make/target/product/security/testkey', None),\n        'apex.apexd_test_different_app.apex': (\n            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',\n            'build/make/target/product/security/testkey', None),\n        }, keys_info)\n\n  def test_ReadApexKeysInfo_presignedKeys(self):\n    apex_keys = self.APEX_KEYS_TXT + (\n        'name=\"apex.apexd_test_different_app2.apex\" '\n        'private_key=\"PRESIGNED\" '\n        'public_key=\"PRESIGNED\" '\n        'container_certificate=\"PRESIGNED\" '\n        'container_private_key=\"PRESIGNED\"')\n    target_files = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:\n      target_files_zip.writestr('META/apexkeys.txt', apex_keys)\n\n    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:\n      keys_info = ReadApexKeysInfo(target_files_zip)\n\n    self.assertEqual({\n        'apex.apexd_test.apex': (\n            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',\n            'build/make/target/product/security/testkey', None),\n        'apex.apexd_test_different_app.apex': (\n            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',\n            'build/make/target/product/security/testkey', None),\n        }, keys_info)\n\n  def test_ParseAvbInfo(self):\n    avb_info_string = \"\"\"\n    Footer version:           1.0\n    Image size:               9999999 bytes\n    Original image size:      8888888 bytes\n    VBMeta offset:            7777777\n    VBMeta size:              1111 bytes\n    --\n    Minimum libavb version:   1.0\n    Header Block:             222 bytes\n    Authentication Block:     333 bytes\n    Auxiliary Block:          888 bytes\n    Public key (sha1):        abababababababababababababababababababab\n    Algorithm:                SHA256_RSA2048\n    Rollback Index:           0\n    Flags:                    0\n    Rollback Index Location:  0\n    Release String:           'avbtool 1.3.0'\n    Descriptors:\n        Hashtree descriptor:\n          Version of dm-verity:  1\n          Image Size:            8888888 bytes\n          Tree Offset:           8888888\n          Tree Size:             44444 bytes\n          Data Block Size:       4444 bytes\n          Hash Block Size:       4444 bytes\n          FEC num roots:         0\n          FEC offset:            0\n          FEC size:              0 bytes\n          Hash Algorithm:        sha1\n          Partition Name:        partition-name\n          Salt:                  cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\n          Root Digest:           efefefefefefefefefefefefefefefefefef\n          Flags:                 0\n        Prop: prop.key -> 'prop.value'\n    \"\"\"\n\n    self.assertEqual(\n        {\n            'Footer version': '1.0',\n            'Image size': '9999999 bytes',\n            'Original image size': '8888888 bytes',\n            'VBMeta offset': '7777777',\n            'VBMeta size': '1111 bytes',\n            'Minimum libavb version': '1.0',\n            'Header Block': '222 bytes',\n            'Authentication Block': '333 bytes',\n            'Auxiliary Block': '888 bytes',\n            'Public key (sha1)': 'abababababababababababababababababababab',\n            'Algorithm': 'SHA256_RSA2048',\n            'Rollback Index': '0',\n            'Flags': '0',\n            'Rollback Index Location': '0',\n            'Release String': \"'avbtool 1.3.0'\",\n            'Descriptors': [\n                {\n                    'Hashtree descriptor': {\n                        'Version of dm-verity': '1',\n                        'Image Size': '8888888 bytes',\n                        'Tree Offset': '8888888',\n                        'Tree Size': '44444 bytes',\n                        'Data Block Size': '4444 bytes',\n                        'Hash Block Size': '4444 bytes',\n                        'FEC num roots': '0',\n                        'FEC offset': '0',\n                        'FEC size': '0 bytes',\n                        'Hash Algorithm': 'sha1',\n                        'Partition Name': 'partition-name',\n                        'Salt': 'cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd',\n                        'Root Digest': 'efefefefefefefefefefefefefefefefefef',\n                        'Flags': '0',\n                    }\n                },\n                {\n                    'Prop': {\n                        'prop.key': 'prop.value',\n                    }\n                },\n            ],\n        },\n        ParseAvbInfo(avb_info_string),\n    )"
  },
  {
    "path": "tools/releasetools/test_utils.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n\"\"\"\nUtils for running unittests.\n\"\"\"\n\nimport avbtool\nimport logging\nimport os\nimport os.path\nimport re\nimport struct\nimport sys\nimport unittest\nimport zipfile\n\nimport common\n\n# Some test runner doesn't like outputs from stderr.\nlogging.basicConfig(stream=sys.stdout)\n\nALLOWED_TEST_SUBDIRS = ('merge',)\n\n# Use ANDROID_BUILD_TOP as an indicator to tell if the needed tools (e.g.\n# avbtool, mke2fs) are available while running the tests, unless\n# FORCE_RUN_RELEASETOOLS is set to '1'. Not having the required vars means we\n# can't run the tests that require external tools.\nEXTERNAL_TOOLS_UNAVAILABLE = (\n    not os.environ.get('ANDROID_BUILD_TOP') and\n    os.environ.get('FORCE_RUN_RELEASETOOLS') != '1')\n\n\ndef SkipIfExternalToolsUnavailable():\n  \"\"\"Decorator function that allows skipping tests per tools availability.\"\"\"\n  if EXTERNAL_TOOLS_UNAVAILABLE:\n    return unittest.skip('External tools unavailable')\n  return lambda func: func\n\n\ndef get_testdata_dir():\n  \"\"\"Returns the testdata dir, in relative to the script dir.\"\"\"\n  # The script dir is the one we want, which could be different from pwd.\n  current_dir = os.path.dirname(os.path.realpath(__file__))\n  return os.path.join(current_dir, 'testdata')\n\n\ndef get_current_dir():\n  \"\"\"Returns the current dir, relative to the script dir.\"\"\"\n  # The script dir is the one we want, which could be different from pwd.\n  current_dir = os.path.dirname(os.path.realpath(__file__))\n  return current_dir\n\n\ndef get_search_path():\n  \"\"\"Returns the search path that has 'framework/signapk.jar' under.\"\"\"\n\n  def signapk_exists(path):\n    signapk_path = os.path.realpath(\n        os.path.join(path, 'framework', 'signapk.jar'))\n    return os.path.exists(signapk_path)\n\n  # Try with ANDROID_BUILD_TOP first.\n  full_path = os.path.realpath(os.path.join(\n      os.environ.get('ANDROID_BUILD_TOP', ''), 'out', 'host', 'linux-x86'))\n  if signapk_exists(full_path):\n    return full_path\n\n  # Otherwise try going with relative pathes.\n  current_dir = os.path.dirname(os.path.realpath(__file__))\n  for path in (\n      # In relative to 'build/make/tools/releasetools' in the Android source.\n      ['..'] * 4 + ['out', 'host', 'linux-x86'],\n      # Or running the script unpacked from otatools.zip.\n          ['..']):\n    full_path = os.path.realpath(os.path.join(current_dir, *path))\n    if signapk_exists(full_path):\n      return full_path\n  return None\n\n\ndef append_avb_footer(file_path: str, partition_name: str = \"\"):\n  avb = avbtool.AvbTool()\n  try:\n    args = [\"avbtool\", \"add_hashtree_footer\", \"--image\", file_path,\n            \"--partition_name\", partition_name, \"--do_not_generate_fec\"]\n    avb.run(args)\n  except SystemExit:\n    raise ValueError(f\"Failed to append hashtree footer {args}\")\n\n\ndef erase_avb_footer(file_path: str):\n  avb = avbtool.AvbTool()\n  try:\n    args = [\"avbtool\", \"erase_footer\", \"--image\", file_path]\n    avb.run(args)\n  except SystemExit:\n    raise ValueError(f\"Failed to erase hashtree footer {args}\")\n\n\ndef construct_sparse_image(chunks, partition_name: str = \"\"):\n  \"\"\"Returns a sparse image file constructed from the given chunks.\n\n  From system/core/libsparse/sparse_format.h.\n  typedef struct sparse_header {\n    __le32 magic;  // 0xed26ff3a\n    __le16 major_version;  // (0x1) - reject images with higher major versions\n    __le16 minor_version;  // (0x0) - allow images with higer minor versions\n    __le16 file_hdr_sz;  // 28 bytes for first revision of the file format\n    __le16 chunk_hdr_sz;  // 12 bytes for first revision of the file format\n    __le32 blk_sz;  // block size in bytes, must be a multiple of 4 (4096)\n    __le32 total_blks;  // total blocks in the non-sparse output image\n    __le32 total_chunks;  // total chunks in the sparse input image\n    __le32 image_checksum;  // CRC32 checksum of the original data, counting\n                            // \"don't care\" as 0. Standard 802.3 polynomial,\n                            // use a Public Domain table implementation\n  } sparse_header_t;\n\n  typedef struct chunk_header {\n    __le16 chunk_type;  // 0xCAC1 -> raw; 0xCAC2 -> fill;\n                        // 0xCAC3 -> don't care\n    __le16 reserved1;\n    __le32 chunk_sz;  // in blocks in output image\n    __le32 total_sz;  // in bytes of chunk input file including chunk header\n                      // and data\n  } chunk_header_t;\n\n  Args:\n    chunks: A list of chunks to be written. Each entry should be a tuple of\n        (chunk_type, block_number).\n\n  Returns:\n    Filename of the created sparse image.\n  \"\"\"\n  SPARSE_HEADER_MAGIC = 0xED26FF3A\n  SPARSE_HEADER_FORMAT = \"<I4H4I\"\n  CHUNK_HEADER_FORMAT = \"<2H2I\"\n\n  sparse_image = common.MakeTempFile(prefix='sparse-', suffix='.img')\n  with open(sparse_image, 'wb') as fp:\n    fp.write(struct.pack(\n        SPARSE_HEADER_FORMAT, SPARSE_HEADER_MAGIC, 1, 0, 28, 12, 4096,\n        sum(chunk[1] for chunk in chunks),\n        len(chunks), 0))\n\n    for chunk in chunks:\n      data_size = 0\n      if chunk[0] == 0xCAC1:\n        data_size = 4096 * chunk[1]\n      elif chunk[0] == 0xCAC2:\n        data_size = 4\n      elif chunk[0] == 0xCAC3:\n        pass\n      else:\n        assert False, \"Unsupported chunk type: {}\".format(chunk[0])\n\n      fp.write(struct.pack(\n          CHUNK_HEADER_FORMAT, chunk[0], 0, chunk[1], data_size + 12))\n      if data_size != 0:\n        fp.write(os.urandom(data_size))\n\n  append_avb_footer(sparse_image, partition_name)\n  return sparse_image\n\n\nclass MockScriptWriter(object):\n  \"\"\"A class that mocks edify_generator.EdifyGenerator.\n\n  It simply pushes the incoming arguments onto script stack, which is to assert\n  the calls to EdifyGenerator functions.\n  \"\"\"\n\n  def __init__(self, enable_comments=False):\n    self.lines = []\n    self.enable_comments = enable_comments\n\n  def Mount(self, *args):\n    self.lines.append(('Mount',) + args)\n\n  def AssertDevice(self, *args):\n    self.lines.append(('AssertDevice',) + args)\n\n  def AssertOemProperty(self, *args):\n    self.lines.append(('AssertOemProperty',) + args)\n\n  def AssertFingerprintOrThumbprint(self, *args):\n    self.lines.append(('AssertFingerprintOrThumbprint',) + args)\n\n  def AssertSomeFingerprint(self, *args):\n    self.lines.append(('AssertSomeFingerprint',) + args)\n\n  def AssertSomeThumbprint(self, *args):\n    self.lines.append(('AssertSomeThumbprint',) + args)\n\n  def Comment(self, comment):\n    if not self.enable_comments:\n      return\n    self.lines.append('# {}'.format(comment))\n\n  def AppendExtra(self, extra):\n    self.lines.append(extra)\n\n  def __str__(self):\n    return '\\n'.join(self.lines)\n\n\nclass ReleaseToolsTestCase(unittest.TestCase):\n  \"\"\"A common base class for all the releasetools unittests.\"\"\"\n\n  def tearDown(self):\n    common.Cleanup()\n\n\nclass PropertyFilesTestCase(ReleaseToolsTestCase):\n\n  @staticmethod\n  def construct_zip_package(entries):\n    zip_file = common.MakeTempFile(suffix='.zip')\n    with zipfile.ZipFile(zip_file, 'w', allowZip64=True) as zip_fp:\n      for entry in entries:\n        zip_fp.writestr(\n            entry,\n            entry.replace('.', '-').upper(),\n            zipfile.ZIP_STORED)\n    return zip_file\n\n  @staticmethod\n  def _parse_property_files_string(data):\n    result = {}\n    for token in data.split(','):\n      name, info = token.split(':', 1)\n      result[name] = info\n    return result\n\n  def setUp(self):\n    common.OPTIONS.no_signing = False\n\n  def _verify_entries(self, input_file, tokens, entries):\n    for entry in entries:\n      offset, size = map(int, tokens[entry].split(':'))\n      with open(input_file, 'rb') as input_fp:\n        input_fp.seek(offset)\n        if entry == 'metadata':\n          expected = b'META-INF/COM/ANDROID/METADATA'\n        elif entry == 'metadata.pb':\n          expected = b'META-INF/COM/ANDROID/METADATA-PB'\n        else:\n          expected = entry.replace('.', '-').upper().encode()\n        self.assertEqual(expected, input_fp.read(size))\n\n\nif __name__ == '__main__':\n  # We only want to run tests from the top level directory. Unfortunately the\n  # pattern option of unittest.discover, internally using fnmatch, doesn't\n  # provide a good API to filter the test files based on directory. So we do an\n  # os walk and load them manually.\n  test_modules = []\n  base_path = os.path.dirname(os.path.realpath(__file__))\n  test_dirs = [base_path] + [\n      os.path.join(base_path, subdir) for subdir in ALLOWED_TEST_SUBDIRS\n  ]\n  for dirpath, _, files in os.walk(base_path):\n    for fn in files:\n      if dirpath in test_dirs and re.match('test_.*\\\\.py$', fn):\n        test_modules.append(fn[:-3])\n\n  test_suite = unittest.TestLoader().loadTestsFromNames(test_modules)\n\n  # atest needs a verbosity level of >= 2 to correctly parse the result.\n  unittest.TextTestRunner(verbosity=2).run(test_suite)\n"
  },
  {
    "path": "tools/releasetools/test_validate_target_files.py",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n\"\"\"Unittests for validate_target_files.py.\"\"\"\n\nimport os\nimport os.path\nimport shutil\nimport zipfile\n\nimport common\nimport test_utils\nfrom rangelib import RangeSet\nfrom validate_target_files import (ValidateVerifiedBootImages,\n                                   ValidateFileConsistency, CheckBuildPropDuplicity)\nfrom verity_utils import CreateVerityImageBuilder\n\nclass ValidateTargetFilesTest(test_utils.ReleaseToolsTestCase):\n\n  def setUp(self):\n    self.testdata_dir = test_utils.get_testdata_dir()\n\n  def _generate_boot_image(self, output_file):\n    kernel = common.MakeTempFile(prefix='kernel-')\n    with open(kernel, 'wb') as kernel_fp:\n      kernel_fp.write(os.urandom(10))\n\n    cmd = ['mkbootimg', '--kernel', kernel, '-o', output_file]\n    proc = common.Run(cmd)\n    stdoutdata, _ = proc.communicate()\n    self.assertEqual(\n        0, proc.returncode,\n        \"Failed to run mkbootimg: {}\".format(stdoutdata))\n\n    cmd = ['boot_signer', '/boot', output_file,\n           os.path.join(self.testdata_dir, 'testkey.pk8'),\n           os.path.join(self.testdata_dir, 'testkey.x509.pem'), output_file]\n    proc = common.Run(cmd)\n    stdoutdata, _ = proc.communicate()\n    self.assertEqual(\n        0, proc.returncode,\n        \"Failed to sign boot image with boot_signer: {}\".format(stdoutdata))\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ValidateVerifiedBootImages_bootImage(self):\n    input_tmp = common.MakeTempDir()\n    os.mkdir(os.path.join(input_tmp, 'IMAGES'))\n    boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')\n    self._generate_boot_image(boot_image)\n\n    info_dict = {\n        'boot_signer' : 'true',\n    }\n    options = {\n        'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),\n    }\n    ValidateVerifiedBootImages(input_tmp, info_dict, options)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ValidateVerifiedBootImages_bootImage_wrongKey(self):\n    input_tmp = common.MakeTempDir()\n    os.mkdir(os.path.join(input_tmp, 'IMAGES'))\n    boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')\n    self._generate_boot_image(boot_image)\n\n    info_dict = {\n        'boot_signer' : 'true',\n    }\n    options = {\n        'verity_key' : os.path.join(self.testdata_dir, 'verity.x509.pem'),\n    }\n    self.assertRaises(\n        AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,\n        options)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ValidateVerifiedBootImages_bootImage_corrupted(self):\n    input_tmp = common.MakeTempDir()\n    os.mkdir(os.path.join(input_tmp, 'IMAGES'))\n    boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')\n    self._generate_boot_image(boot_image)\n\n    # Corrupt the late byte of the image.\n    with open(boot_image, 'r+b') as boot_fp:\n      boot_fp.seek(-1, os.SEEK_END)\n      last_byte = boot_fp.read(1)\n      last_byte = bytes([255 - ord(last_byte)])\n      boot_fp.seek(-1, os.SEEK_END)\n      boot_fp.write(last_byte)\n\n    info_dict = {\n        'boot_signer' : 'true',\n    }\n    options = {\n        'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),\n    }\n    self.assertRaises(\n        AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,\n        options)\n\n  def _generate_system_image(self, output_file, system_root=None,\n                             file_map=None):\n    prop_dict = {\n        'partition_size': str(1024 * 1024),\n        'verity': 'true',\n        'verity_block_device': '/dev/block/system',\n        'verity_key' : os.path.join(self.testdata_dir, 'testkey'),\n        'verity_fec': \"true\",\n        'verity_signer_cmd': 'verity_signer',\n    }\n    verity_image_builder = CreateVerityImageBuilder(prop_dict)\n    image_size = verity_image_builder.CalculateMaxImageSize()\n\n    # Use an empty root directory.\n    if not system_root:\n      system_root = common.MakeTempDir()\n    cmd = ['mkuserimg_mke2fs', '-s', system_root, output_file, 'ext4',\n           '/system', str(image_size), '-j', '0']\n    if file_map:\n      cmd.extend(['-B', file_map])\n    proc = common.Run(cmd)\n    stdoutdata, _ = proc.communicate()\n    self.assertEqual(\n        0, proc.returncode,\n        \"Failed to create system image with mkuserimg_mke2fs: {}\".format(\n            stdoutdata))\n\n    # Append the verity metadata.\n    verity_image_builder.Build(output_file)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ValidateVerifiedBootImages_systemRootImage(self):\n    input_tmp = common.MakeTempDir()\n    os.mkdir(os.path.join(input_tmp, 'IMAGES'))\n    system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')\n    self._generate_system_image(system_image)\n\n    # Pack the verity key.\n    verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key')\n    os.makedirs(os.path.dirname(verity_key_mincrypt))\n    shutil.copyfile(\n        os.path.join(self.testdata_dir, 'testkey_mincrypt'),\n        verity_key_mincrypt)\n\n    info_dict = {\n        'verity' : 'true',\n    }\n    options = {\n        'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),\n        'verity_key_mincrypt' : verity_key_mincrypt,\n    }\n    ValidateVerifiedBootImages(input_tmp, info_dict, options)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ValidateVerifiedBootImages_nonSystemRootImage(self):\n    input_tmp = common.MakeTempDir()\n    os.mkdir(os.path.join(input_tmp, 'IMAGES'))\n    system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')\n    self._generate_system_image(system_image)\n\n    # Pack the verity key into the root dir in system.img.\n    verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key')\n    os.makedirs(os.path.dirname(verity_key_mincrypt))\n    shutil.copyfile(\n        os.path.join(self.testdata_dir, 'testkey_mincrypt'),\n        verity_key_mincrypt)\n\n    # And a copy in ramdisk.\n    verity_key_ramdisk = os.path.join(\n        input_tmp, 'BOOT', 'RAMDISK', 'verity_key')\n    os.makedirs(os.path.dirname(verity_key_ramdisk))\n    shutil.copyfile(\n        os.path.join(self.testdata_dir, 'testkey_mincrypt'),\n        verity_key_ramdisk)\n\n    info_dict = {\n        'verity' : 'true',\n    }\n    options = {\n        'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),\n        'verity_key_mincrypt' : verity_key_mincrypt,\n    }\n    ValidateVerifiedBootImages(input_tmp, info_dict, options)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ValidateVerifiedBootImages_nonSystemRootImage_mismatchingKeys(self):\n    input_tmp = common.MakeTempDir()\n    os.mkdir(os.path.join(input_tmp, 'IMAGES'))\n    system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')\n    self._generate_system_image(system_image)\n\n    # Pack the verity key into the root dir in system.img.\n    verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key')\n    os.makedirs(os.path.dirname(verity_key_mincrypt))\n    shutil.copyfile(\n        os.path.join(self.testdata_dir, 'testkey_mincrypt'),\n        verity_key_mincrypt)\n\n    # And an invalid copy in ramdisk.\n    verity_key_ramdisk = os.path.join(\n        input_tmp, 'BOOT', 'RAMDISK', 'verity_key')\n    os.makedirs(os.path.dirname(verity_key_ramdisk))\n    shutil.copyfile(\n        os.path.join(self.testdata_dir, 'verity_mincrypt'),\n        verity_key_ramdisk)\n\n    info_dict = {\n        'verity' : 'true',\n    }\n    options = {\n        'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),\n        'verity_key_mincrypt' : verity_key_mincrypt,\n    }\n    self.assertRaises(\n        AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,\n        options)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ValidateFileConsistency_incompleteRange(self):\n    input_tmp = common.MakeTempDir()\n    os.mkdir(os.path.join(input_tmp, 'IMAGES'))\n    system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')\n    system_root = os.path.join(input_tmp, \"SYSTEM\")\n    os.mkdir(system_root)\n\n    # Write test files that contain multiple blocks of zeros, and these zero\n    # blocks will be omitted by kernel. Each test file will occupy one block in\n    # the final system image.\n    with open(os.path.join(system_root, 'a'), 'w') as f:\n      f.write('aaa')\n      f.write('\\0' * 4096 * 3)\n    with open(os.path.join(system_root, 'b'), 'w') as f:\n      f.write('bbb')\n      f.write('\\0' * 4096 * 3)\n\n    raw_file_map = os.path.join(input_tmp, 'IMAGES', 'raw_system.map')\n    self._generate_system_image(system_image, system_root, raw_file_map)\n\n    # Parse the generated file map and update the block ranges for each file.\n    file_map_list = {}\n    image_ranges = RangeSet()\n    with open(raw_file_map) as f:\n      for line in f.readlines():\n        info = line.split()\n        self.assertEqual(2, len(info))\n        image_ranges = image_ranges.union(RangeSet(info[1]))\n        file_map_list[info[0]] = RangeSet(info[1])\n\n    # Add one unoccupied block as the shared block for all test files.\n    mock_shared_block = RangeSet(\"10-20\").subtract(image_ranges).first(1)\n    with open(os.path.join(input_tmp, 'IMAGES', 'system.map'), 'w') as f:\n      for key in sorted(file_map_list.keys()):\n        line = '{} {}\\n'.format(\n            key, file_map_list[key].union(mock_shared_block))\n        f.write(line)\n\n    # Prepare for the target zip file\n    input_file = common.MakeTempFile()\n    all_entries = ['SYSTEM/', 'SYSTEM/b', 'SYSTEM/a', 'IMAGES/',\n                   'IMAGES/system.map', 'IMAGES/system.img']\n    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:\n      for name in all_entries:\n        input_zip.write(os.path.join(input_tmp, name), arcname=name)\n\n    # Expect the validation to pass and both files are skipped due to\n    # 'incomplete' block range.\n    with zipfile.ZipFile(input_file) as input_zip:\n      info_dict = {'extfs_sparse_flag': '-s'}\n      ValidateFileConsistency(input_zip, input_tmp, info_dict)\n\n  @test_utils.SkipIfExternalToolsUnavailable()\n  def test_ValidateFileConsistency_nonMonotonicRanges(self):\n    input_tmp = common.MakeTempDir()\n    os.mkdir(os.path.join(input_tmp, 'IMAGES'))\n    system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')\n    system_root = os.path.join(input_tmp, \"SYSTEM\")\n    os.mkdir(system_root)\n\n    # Write the test file that contain three blocks of 'a', 'b', 'c'.\n    with open(os.path.join(system_root, 'abc'), 'w') as f:\n      f.write('a' * 4096 + 'b' * 4096 + 'c' * 4096)\n    raw_file_map = os.path.join(input_tmp, 'IMAGES', 'raw_system.map')\n    self._generate_system_image(system_image, system_root, raw_file_map)\n\n    # Parse the generated file map and manipulate the block ranges of 'abc' to\n    # be 'cba'.\n    file_map_list = {}\n    with open(raw_file_map) as f:\n      for line in f.readlines():\n        info = line.split()\n        self.assertEqual(2, len(info))\n        ranges = RangeSet(info[1])\n        self.assertTrue(ranges.monotonic)\n        blocks = reversed(list(ranges.next_item()))\n        file_map_list[info[0]] = ' '.join([str(block) for block in blocks])\n\n    # Update the contents of 'abc' to be 'cba'.\n    with open(os.path.join(system_root, 'abc'), 'w') as f:\n      f.write('c' * 4096 + 'b' * 4096 + 'a' * 4096)\n\n    # Update the system.map.\n    with open(os.path.join(input_tmp, 'IMAGES', 'system.map'), 'w') as f:\n      for key in sorted(file_map_list.keys()):\n        f.write('{} {}\\n'.format(key, file_map_list[key]))\n\n    # Get the target zip file.\n    input_file = common.MakeTempFile()\n    all_entries = ['SYSTEM/', 'SYSTEM/abc', 'IMAGES/',\n                   'IMAGES/system.map', 'IMAGES/system.img']\n    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:\n      for name in all_entries:\n        input_zip.write(os.path.join(input_tmp, name), arcname=name)\n\n    with zipfile.ZipFile(input_file) as input_zip:\n      info_dict = {'extfs_sparse_flag': '-s'}\n      ValidateFileConsistency(input_zip, input_tmp, info_dict)\n\n  @staticmethod\n  def make_build_prop(build_prop):\n    input_tmp = common.MakeTempDir()\n    system_dir = os.path.join(input_tmp, 'SYSTEM')\n    os.makedirs(system_dir)\n    prop_file = os.path.join(system_dir, 'build.prop')\n    with open(prop_file, 'w') as output_file:\n      output_file.write(\"\\n\".join(build_prop))\n    return input_tmp\n\n  def test_checkDuplicateProps_noDuplicate(self):\n    build_prop = [\n        'ro.odm.build.date.utc=1578430045',\n        'ro.odm.build.fingerprint='\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device=coral',\n    ]\n    input_tmp = ValidateTargetFilesTest.make_build_prop(build_prop)\n    CheckBuildPropDuplicity(input_tmp)\n\n  def test_checkDuplicateProps_withDuplicate(self):\n    build_prop = [\n        'ro.odm.build.date.utc=1578430045',\n        'ro.odm.build.date.utc=1578430049',\n        'ro.odm.build.fingerprint='\n        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',\n        'ro.product.odm.device=coral',\n    ]\n    input_tmp = ValidateTargetFilesTest.make_build_prop(build_prop)\n\n    self.assertRaises(ValueError, CheckBuildPropDuplicity, input_tmp)\n"
  },
  {
    "path": "tools/releasetools/test_verity_utils.py",
    "content": "#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n\"\"\"Unittests for verity_utils.py.\"\"\"\n\nimport copy\nimport math\nimport os.path\nimport random\n\nimport common\nimport sparse_img\nfrom rangelib import RangeSet\nfrom test_utils import (\n    get_testdata_dir, ReleaseToolsTestCase, SkipIfExternalToolsUnavailable)\nfrom verity_utils import (\n    CalculateVbmetaDigest, CreateVerityImageBuilder)\n\nBLOCK_SIZE = common.BLOCK_SIZE\n\n\nclass VerifiedBootVersion2VerityImageBuilderTest(ReleaseToolsTestCase):\n\n  DEFAULT_PROP_DICT = {\n      'partition_size': str(4096 * 1024),\n      'partition_name': 'system',\n      'avb_avbtool': 'avbtool',\n      'avb_hashtree_enable': 'true',\n      'avb_add_hashtree_footer_args': '',\n  }\n\n  def test_init(self):\n    prop_dict = copy.deepcopy(self.DEFAULT_PROP_DICT)\n    verity_image_builder = CreateVerityImageBuilder(prop_dict)\n    self.assertIsNotNone(verity_image_builder)\n    self.assertEqual(2, verity_image_builder.version)\n\n  def test_init_MissingProps(self):\n    prop_dict = copy.deepcopy(self.DEFAULT_PROP_DICT)\n    del prop_dict['avb_hashtree_enable']\n    verity_image_builder = CreateVerityImageBuilder(prop_dict)\n    self.assertIsNone(verity_image_builder)\n\n  @SkipIfExternalToolsUnavailable()\n  def test_Build(self):\n    prop_dict = copy.deepcopy(self.DEFAULT_PROP_DICT)\n    verity_image_builder = CreateVerityImageBuilder(prop_dict)\n    self.assertIsNotNone(verity_image_builder)\n    self.assertEqual(2, verity_image_builder.version)\n\n    input_dir = common.MakeTempDir()\n    image_dir = common.MakeTempDir()\n    system_image = os.path.join(image_dir, 'system.img')\n    system_image_size = verity_image_builder.CalculateMaxImageSize()\n    cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4', '/system',\n           str(system_image_size), '-j', '0', '-s']\n    common.RunAndCheckOutput(cmd)\n    verity_image_builder.Build(system_image)\n\n    # Additionally make vbmeta image so that we can verify with avbtool.\n    vbmeta_image = os.path.join(image_dir, 'vbmeta.img')\n    cmd = ['avbtool', 'make_vbmeta_image', '--include_descriptors_from_image',\n           system_image, '--output', vbmeta_image]\n    common.RunAndCheckOutput(cmd)\n\n    # Verify the verity metadata.\n    cmd = ['avbtool', 'verify_image', '--image', vbmeta_image]\n    common.RunAndCheckOutput(cmd)\n\n  def _test_CalculateMinPartitionSize_SetUp(self):\n    # To test CalculateMinPartitionSize(), by using 200MB to 2GB image size.\n    #   -  51200 = 200MB * 1024 * 1024 / 4096\n    #   - 524288 = 2GB * 1024 * 1024 * 1024 / 4096\n    image_sizes = [BLOCK_SIZE * random.randint(51200, 524288) + offset\n                   for offset in range(BLOCK_SIZE)]\n\n    prop_dict = {\n        'partition_size': None,\n        'partition_name': 'system',\n        'avb_avbtool': 'avbtool',\n        'avb_hashtree_enable': 'true',\n        'avb_add_hashtree_footer_args': None,\n    }\n    builder = CreateVerityImageBuilder(prop_dict)\n    self.assertEqual(2, builder.version)\n    return image_sizes, builder\n\n  def test_CalculateMinPartitionSize_LinearFooterSize(self):\n    \"\"\"Tests with footer size which is linear to partition size.\"\"\"\n    image_sizes, builder = self._test_CalculateMinPartitionSize_SetUp()\n    for image_size in image_sizes:\n      for ratio in 0.95, 0.56, 0.22:\n        expected_size = common.RoundUpTo4K(int(math.ceil(image_size / ratio)))\n        self.assertEqual(\n            expected_size,\n            builder.CalculateMinPartitionSize(\n                image_size, lambda x, ratio=ratio: int(x * ratio)))\n\n  def test_AVBCalcMinPartitionSize_SlowerGrowthFooterSize(self):\n    \"\"\"Tests with footer size which grows slower than partition size.\"\"\"\n\n    def _SizeCalculator(partition_size):\n      \"\"\"Footer size is the power of 0.95 of partition size.\"\"\"\n      # Minus footer size to return max image size.\n      return partition_size - int(math.pow(partition_size, 0.95))\n\n    image_sizes, builder = self._test_CalculateMinPartitionSize_SetUp()\n    for image_size in image_sizes:\n      min_partition_size = builder.CalculateMinPartitionSize(\n          image_size, _SizeCalculator)\n      # Checks min_partition_size can accommodate image_size.\n      self.assertGreaterEqual(\n          _SizeCalculator(min_partition_size),\n          image_size)\n      # Checks min_partition_size (round to BLOCK_SIZE) is the minimum.\n      self.assertLess(\n          _SizeCalculator(min_partition_size - BLOCK_SIZE),\n          image_size)\n\n  def test_CalculateMinPartitionSize_FasterGrowthFooterSize(self):\n    \"\"\"Tests with footer size which grows faster than partition size.\"\"\"\n\n    def _SizeCalculator(partition_size):\n      \"\"\"Max image size is the power of 0.95 of partition size.\"\"\"\n      # Max image size grows less than partition size, which means\n      # footer size grows faster than partition size.\n      return int(math.pow(partition_size, 0.95))\n\n    image_sizes, builder = self._test_CalculateMinPartitionSize_SetUp()\n    for image_size in image_sizes:\n      min_partition_size = builder.CalculateMinPartitionSize(\n          image_size, _SizeCalculator)\n      # Checks min_partition_size can accommodate image_size.\n      self.assertGreaterEqual(\n          _SizeCalculator(min_partition_size),\n          image_size)\n      # Checks min_partition_size (round to BLOCK_SIZE) is the minimum.\n      self.assertLess(\n          _SizeCalculator(min_partition_size - BLOCK_SIZE),\n          image_size)\n\n  @SkipIfExternalToolsUnavailable()\n  def test_CalculateVbmetaDigest(self):\n    prop_dict = copy.deepcopy(self.DEFAULT_PROP_DICT)\n    verity_image_builder = CreateVerityImageBuilder(prop_dict)\n    self.assertEqual(2, verity_image_builder.version)\n\n    input_dir = common.MakeTempDir()\n    image_dir = common.MakeTempDir()\n    os.mkdir(os.path.join(image_dir, 'IMAGES'))\n    system_image = os.path.join(image_dir, 'IMAGES', 'system.img')\n    system_image_size = verity_image_builder.CalculateMaxImageSize()\n    cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4', '/system',\n           str(system_image_size), '-j', '0', '-s']\n    common.RunAndCheckOutput(cmd)\n    verity_image_builder.Build(system_image)\n\n    # Additionally make vbmeta image\n    vbmeta_image = os.path.join(image_dir, 'IMAGES', 'vbmeta.img')\n    cmd = ['avbtool', 'make_vbmeta_image', '--include_descriptors_from_image',\n           system_image, '--output', vbmeta_image]\n    common.RunAndCheckOutput(cmd)\n\n    # Verify the verity metadata.\n    cmd = ['avbtool', 'verify_image', '--image', vbmeta_image]\n    common.RunAndCheckOutput(cmd)\n    digest = CalculateVbmetaDigest(image_dir, 'avbtool')\n    self.assertIsNotNone(digest)\n"
  },
  {
    "path": "tools/releasetools/testdata/apexkeys_framework.txt",
    "content": "name=\"com.android.conscrypt.apex\" public_key=\"external/conscrypt/apex/com.android.conscrypt.avbpubkey\" private_key=\"external/conscrypt/apex/com.android.conscrypt.pem\" container_certificate=\"external/conscrypt/apex/com.android.conscrypt.x509.pem\" container_private_key=\"external/conscrypt/apex/com.android.conscrypt.pk8\" partition=\"system\"\nname=\"com.android.fake_product.apex\" public_key=\"selected\" private_key=\"selected\" container_certificate=\"selected\" container_private_key=\"selected\" partition=\"product\"\nname=\"com.android.runtime.apex\" public_key=\"bionic/apex/com.android.runtime.avbpubkey\" private_key=\"bionic/apex/com.android.runtime.pem\" container_certificate=\"bionic/apex/com.android.runtime.x509.pem\" container_private_key=\"bionic/apex/com.android.runtime.pk8\" partition=\"system\"\nname=\"com.android.vndk.current.on_vendor.apex\" public_key=\"not_selected\" private_key=\"not_selected\" container_certificate=\"not_selected\" container_private_key=\"not_selected\" partition=\"vendor\"\nname=\"com.android.vndk.v27.apex\" public_key=\"packages/modules/vndk/apex/com.android.vndk.v27.pubkey\" private_key=\"packages/modules/vndk/apex/com.android.vndk.v27.pem\" container_certificate=\"packages/modules/vndk/apex/com.android.vndk.v27.x509.pem\" container_private_key=\"packages/modules/vndk/apex/com.android.vndk.v27.pk8\" partition=\"system_ext\"\nname=\"com.android.vndk.v28.apex\" public_key=\"packages/modules/vndk/apex/com.android.vndk.v28.pubkey\" private_key=\"packages/modules/vndk/apex/com.android.vndk.v28.pem\" container_certificate=\"packages/modules/vndk/apex/com.android.vndk.v28.x509.pem\" container_private_key=\"packages/modules/vndk/apex/com.android.vndk.v28.pk8\" partition=\"system_ext\"\nname=\"com.android.vndk.v29.apex\" public_key=\"packages/modules/vndk/apex/com.android.vndk.v29.pubkey\" private_key=\"packages/modules/vndk/apex/com.android.vndk.v29.pem\" container_certificate=\"packages/modules/vndk/apex/com.android.vndk.v29.x509.pem\" container_private_key=\"packages/modules/vndk/apex/com.android.vndk.v29.pk8\" partition=\"system_ext\"\n"
  },
  {
    "path": "tools/releasetools/testdata/apexkeys_framework_conflict.txt",
    "content": "name=\"com.android.conscrypt.apex\" public_key=\"external/conscrypt/apex/com.android.conscrypt.avbpubkey\" private_key=\"external/conscrypt/apex/com.android.conscrypt.pem\" container_certificate=\"external/conscrypt/apex/com.android.conscrypt.x509.pem\" container_private_key=\"external/conscrypt/apex/com.android.conscrypt.pk8\" partition=\"vendor\"\n"
  },
  {
    "path": "tools/releasetools/testdata/apexkeys_merge.txt",
    "content": "name=\"com.android.conscrypt.apex\" public_key=\"external/conscrypt/apex/com.android.conscrypt.avbpubkey\" private_key=\"external/conscrypt/apex/com.android.conscrypt.pem\" container_certificate=\"external/conscrypt/apex/com.android.conscrypt.x509.pem\" container_private_key=\"external/conscrypt/apex/com.android.conscrypt.pk8\" partition=\"system\"\nname=\"com.android.fake_product.apex\" public_key=\"selected\" private_key=\"selected\" container_certificate=\"selected\" container_private_key=\"selected\" partition=\"product\"\nname=\"com.android.runtime.apex\" public_key=\"bionic/apex/com.android.runtime.avbpubkey\" private_key=\"bionic/apex/com.android.runtime.pem\" container_certificate=\"bionic/apex/com.android.runtime.x509.pem\" container_private_key=\"bionic/apex/com.android.runtime.pk8\" partition=\"system\"\nname=\"com.android.vndk.current.on_vendor.apex\" public_key=\"packages/modules/vndk/apex/com.android.vndk.current.pubkey\" private_key=\"packages/modules/vndk/apex/com.android.vndk.current.pem\" container_certificate=\"packages/modules/vndk/apex/com.android.vndk.current.x509.pem\" container_private_key=\"packages/modules/vndk/apex/com.android.vndk.current.pk8\" partition=\"vendor\"\nname=\"com.android.vndk.v27.apex\" public_key=\"packages/modules/vndk/apex/com.android.vndk.v27.pubkey\" private_key=\"packages/modules/vndk/apex/com.android.vndk.v27.pem\" container_certificate=\"packages/modules/vndk/apex/com.android.vndk.v27.x509.pem\" container_private_key=\"packages/modules/vndk/apex/com.android.vndk.v27.pk8\" partition=\"system_ext\"\nname=\"com.android.vndk.v28.apex\" public_key=\"packages/modules/vndk/apex/com.android.vndk.v28.pubkey\" private_key=\"packages/modules/vndk/apex/com.android.vndk.v28.pem\" container_certificate=\"packages/modules/vndk/apex/com.android.vndk.v28.x509.pem\" container_private_key=\"packages/modules/vndk/apex/com.android.vndk.v28.pk8\" partition=\"system_ext\"\nname=\"com.android.vndk.v29.apex\" public_key=\"packages/modules/vndk/apex/com.android.vndk.v29.pubkey\" private_key=\"packages/modules/vndk/apex/com.android.vndk.v29.pem\" container_certificate=\"packages/modules/vndk/apex/com.android.vndk.v29.x509.pem\" container_private_key=\"packages/modules/vndk/apex/com.android.vndk.v29.pk8\" partition=\"system_ext\"\n"
  },
  {
    "path": "tools/releasetools/testdata/apexkeys_vendor.txt",
    "content": "name=\"com.android.conscrypt.apex\" public_key=\"not_selected\" private_key=\"not_selected\" container_certificate=\"not_selected\" container_private_key=\"not_selected\" partition=\"system\"\nname=\"com.android.fake_product.apex\" public_key=\"not_selected\" private_key=\"not_selected\" container_certificate=\"not_selected\" container_private_key=\"not_selected\" partition=\"product\"\nname=\"com.android.runtime.apex\" public_key=\"not_selected\" private_key=\"not_selected\" container_certificate=\"not_selected\" container_private_key=\"not_selected\" partition=\"system\"\nname=\"com.android.vndk.current.on_vendor.apex\" public_key=\"packages/modules/vndk/apex/com.android.vndk.current.pubkey\" private_key=\"packages/modules/vndk/apex/com.android.vndk.current.pem\" container_certificate=\"packages/modules/vndk/apex/com.android.vndk.current.x509.pem\" container_private_key=\"packages/modules/vndk/apex/com.android.vndk.current.pk8\" partition=\"vendor\"\nname=\"com.android.vndk.v27.apex\" public_key=\"not_selected\" private_key=\"not_selected\" container_certificate=\"not_selected\" container_private_key=\"not_selected\" partition=\"system_ext\"\nname=\"com.android.vndk.v28.apex\" public_key=\"not_selected\" private_key=\"not_selected\" container_certificate=\"not_selected\" container_private_key=\"not_selected\" partition=\"system_ext\"\nname=\"com.android.vndk.v29.apex\" public_key=\"not_selected\" private_key=\"not_selected\" container_certificate=\"not_selected\" container_private_key=\"not_selected\" partition=\"system_ext\"\n"
  },
  {
    "path": "tools/releasetools/testdata/apkcerts_framework.txt",
    "content": "name=\"TestSystem1.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"system\"\nname=\"TestSystem2.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"system\"\nname=\"TestVendor.apk\" certificate=\"not_selected\" private_key=\"not_selected\" partition=\"vendor\"\nname=\"TestOdm.apk\" certificate=\"not_selected\" private_key=\"not_selected\" partition=\"odm\"\nname=\"TestProduct.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"product\"\nname=\"TestSystemExt.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"system_ext\"\n"
  },
  {
    "path": "tools/releasetools/testdata/apkcerts_merge.txt",
    "content": "name=\"TestOdm.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"odm\"\nname=\"TestProduct.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"product\"\nname=\"TestSystem1.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"system\"\nname=\"TestSystem2.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"system\"\nname=\"TestSystemExt.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"system_ext\"\nname=\"TestVendor.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"vendor\"\n"
  },
  {
    "path": "tools/releasetools/testdata/apkcerts_vendor.txt",
    "content": "name=\"TestSystem1.apk\" certificate=\"not_selected\" private_key=\"not_selected\" partition=\"system\"\nname=\"TestSystem2.apk\" certificate=\"not_selected\" private_key=\"not_selected\" partition=\"system\"\nname=\"TestVendor.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"vendor\"\nname=\"TestOdm.apk\" certificate=\"build/make/target/product/security/testkey.x509.pem\" private_key=\"build/make/target/product/security/testkey.pk8\" partition=\"odm\"\nname=\"TestProduct.apk\" certificate=\"not_selected\" private_key=\"not_selected\" partition=\"product\"\nname=\"TestSystemExt.apk\" certificate=\"not_selected\" private_key=\"not_selected\" partition=\"system_ext\"\n"
  },
  {
    "path": "tools/releasetools/testdata/media.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEqDCCA5CgAwIBAgIJAPK5jmEjVyxOMA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD\nVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g\nVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE\nAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe\nFw0wODA0MTUyMzQwNTdaFw0zNTA5MDEyMzQwNTdaMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G\nA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p\nZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI\nhvcNAQEBBQADggENADCCAQgCggEBAK4lDFoW75f8KGmsZRsyF8w2ug6GlkFo1YoE\nn0DOhYZxI6P/tPbZScM88to6BcI+rKpX2AOImxdZvPWefG8hiQriUIW37VaqYmwJ\nie+czTY2LKDo0blgP9TYModnkmzMCQxot3Wuf/MJNMw2nvKFWiZn3wxmf9DHz12O\numVYBnNzA7tiRybquu37cvB+16dqs8uaOBxLfc2AmxQNiR8AITvkAfWNagamHq3D\nqcLxxlZyhbCa4JNCpm+kIer5Ot91c6AowzHXBgGrOvfMhAM+znx3KjpbhrDb6dd3\nw6SKqYAe3O4ngVifRNnkETl5YAV2qZQQuoEJElna2YxsaP94S48CAQOjgfwwgfkw\nHQYDVR0OBBYEFMopPKqLwO0+VC7vQgWiv/K1fk11MIHJBgNVHSMEgcEwgb6AFMop\nPKqLwO0+VC7vQgWiv/K1fk11oYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE\nCBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH\nQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG\nCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAPK5jmEjVyxOMAwGA1Ud\nEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAITelRbV5KhyF6c9qEhwSPUzc6X3\nM/OQ1hvfPMnlJRYlv8qnwxWcriddFyqa4eh21UWBJ6xUL2gpDdUQwAKdj1Hg7hVr\ne3tazbOUJBuOx4t05cQsXK+uFWyvW9GZojonUk2gct6743hGSlM2MLDk0P+34I7L\ncB+ttjecdEZ/bgDG7YiFlTgHkgOHVgB4csjjAHr0I6V6LKs6KChptkxLe9X8GH0K\nfiQVll1ark4Hpt91G0p16Xk8kYphK4HNC2KK7gFo3ETkexDTWTJghJ1q321yfcJE\nRMIh0/nsw2jK0HmZ8rgQW8HyDTjUEGbMFBHCV6lupDSfV0ZWVQfk6AIKGoE=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tools/releasetools/testdata/merge_config_framework_item_list",
    "content": "META/apkcerts.txt\nMETA/filesystem_config.txt\nMETA/root_filesystem_config.txt\nMETA/system_manifest.xml\nMETA/system_matrix.xml\nMETA/update_engine_config.txt\nPRODUCT/*\nROOT/*\nSYSTEM/*\n"
  },
  {
    "path": "tools/releasetools/testdata/payload_signer.sh",
    "content": "#!/bin/sh\n\n# The script will be called with 'payload_signer.sh <key> -in <input> -out <output>'.\nopenssl pkeyutl -sign -keyform DER -inkey $1 -pkeyopt digest:sha256 -in $3 -out $5\n"
  },
  {
    "path": "tools/releasetools/testdata/platform.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEqDCCA5CgAwIBAgIJALOZgIbQVs/6MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD\nVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g\nVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE\nAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe\nFw0wODA0MTUyMjQwNTBaFw0zNTA5MDEyMjQwNTBaMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G\nA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p\nZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI\nhvcNAQEBBQADggENADCCAQgCggEBAJx4BZKsDV04HN6qZezIpgBuNkgMbXIHsSAR\nvlCGOqvitV0Amt9xRtbyICKAx81Ne9smJDuKgGwms0sTdSOkkmgiSQTcAUk+fArP\nGgXIdPabA3tgMJ2QdNJCgOFrrSqHNDYZUer3KkgtCbIEsYdeEqyYwap3PWgAuer9\n5W1Yvtjo2hb5o2AJnDeoNKbf7be2tEoEngeiafzPLFSW8s821k35CjuNjzSjuqtM\n9TNxqydxmzulh1StDFP8FOHbRdUeI0+76TybpO35zlQmE1DsU1YHv2mi/0qgfbX3\n6iANCabBtJ4hQC+J7RGQiTqrWpGA8VLoL4WkV1PPX8GQccXuyCcCAQOjgfwwgfkw\nHQYDVR0OBBYEFE/koLPdnLop9x1yh8Tnw48ghsKZMIHJBgNVHSMEgcEwgb6AFE/k\noLPdnLop9x1yh8Tnw48ghsKZoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE\nCBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH\nQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG\nCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJALOZgIbQVs/6MAwGA1Ud\nEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAFclUbjZOh9z3g9tRp+G2tZwFAAp\nPIigzXzXeLc9r8wZf6t25iEuVsHHYc/EL9cz3lLFCuCIFM78CjtaGkNGBU2Cnx2C\ntCsgSL+ItdFJKe+F9g7dEtctVWV+IuPoXQTIMdYT0Zk4u4mCJH+jISVroS0dao+S\n6h2xw3Mxe6DAN/DRr/ZFrvIkl5+6bnoUvAJccbmBOM7z3fwFlhfPJIRc97QNY4L3\nJ17XOElatuWTG5QhdlxJG3L7aOCA29tYwgKdNHyLMozkPvaosVUz7fvpib1qSN1L\nIC7alMarjdW4OZID2q4u1EYjLk/pvZYTlMYwDlE448/Shebk5INTjLixs1c=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tools/releasetools/testdata/signing_helper.sh",
    "content": "#!/bin/sh\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\ntmpfile=$(mktemp)\ncat $3 | openssl rsautl -sign -inkey $2 -raw > $tmpfile\ncat $tmpfile > $3\nrm $tmpfile\n"
  },
  {
    "path": "tools/releasetools/testdata/testkey.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+O/I7YvBaCZA3\nKrvP7ErTh6DS3cAvjLY2GkAA7NWcXIICsVwWMtMZAMO+Rtk/o3XY7r53Amg8ue2e\nb0D5Wc8gUVEeDQdRZJscz5CTwmC/b/YBWQBSPknWv23hf7ZYjR5HMk/XlmOfrylA\noaZzJyKvLSBu+Zi9cvZlSZObnoOYR8JQJEhjYgYn8JwycV4i1VTQvEqxXkyW3kE7\nRW/8JXgRqI4vDIKehm5SFi2jt0eU7/ju/8f3OGQkLng4DV2QPfwQ+A7kad+EYVI1\ndBGYkNNesWB3o75A7jJQ1fyVg/XQzOKZSki1lrTm3rw0AOrBiXdPudbO+4L2vgip\nkPI9/bVNAgMBAAECggEABGjBSY0Wgw+7rvunlL8mUNbQ7HJFVRTO2FwtZZgXr2MZ\nhFR2DPGqoOa6ortjp6zzO071TS7aGaY5krWDbQQe3+Hinm6w37sUOUu6TyJvOaCv\ntAJLFpzo+zg+pL5gDJdgv0e0QAv1TSszKpNUl1Ct5h+Go+vXFXUHrvtQl4fKBwqA\nefxcd3R4z3p/3Cl2ZYIRz9I7UXUZZYwJE7bDNDz3aFZ1jUoELGmhe1O5w0hJY1q6\nPxuOM9bL60yDn0vu0eiCjaPlHeHyGe9pQ1aQLEuwQz9zpWC01dWPVkLmny7HDygC\nVBsdg8MNlzJQ1WV2en11BH72IqZ59U8pD0xEB7a4BQKBgQDxenfXyYZw4AKcaJlP\nncJmsx/wcgEvWNxiI4etArXES4VIyP2OlSw+q9JbOOpaSk8TJP5PNfUkgTbC4B2y\ngh/AobJP5b7Wn5LrsHc3GY6CzF1i8T4xXQRxnaKWE86SOmZQlEmyCnpyCmfFVuaR\nE8p8CPW/gQLhpxSlQdGZ0bYLiwKBgQDJrJRDdyaI/Isusog/OwKHVGBU6CmRa5tM\ngx+GIlxheqhuDqnBkr0h1kL20Zi80JeG7vKWr+dwfqkEarfdTe+juwlIuQ3MEuXL\nAbsKNuaU1naOqOLm9rjZgRtR7oNLVH5AbkKMaJz1zM6YiMl54FEDX7ZVY8b6q1Kz\nYXT3sGi9hwKBgBsNa0ujagpPLjuzhCllNRgoTRW0z+kr/VSJQnPhb9eT1lS3H6DP\nmWtT+Hb7w1VmKcGtTUg2dUYnq6jdTrZm2YPNGZrV1DFbIyyAUnq7xDlnB7dD64HA\nN/U6gbJqeaPsIvY4BqGJhvorrEBxYdcy7mZC4rUXkOkSvL9exkqDMe/NAoGARaHU\nv0aQg5PO6pyx9kMFqHw1lptiXtdsk4pihAmxI+cZ6IYfjrp/mwNDs7zCo87RwsEV\n+Xlay7iv2tqOCVczerDFj9p1LRUJSoKadfhmvNUfsjoVvfFJ+a9eI3fa1VOjE9P+\nHkSwjR3d50Sza+VLk4Kkje8ZcMtejpkDrdG3GFkCgYBXHqciwlFn5nMPFRe8v426\n6YBiUtzCQCZxDtMeeZYCJslFfjrqPXNUcU/flxWwaikjFsLJEtl7aT3Hpdi5I7T8\nyCYkUWqAAh7twEYTOeG6v/tEa/PmsBjZXPD2zkCp76EQmv3gbvsH4F/nA55gT/GR\n2in6XS/4rHBvjn5gF6MFyg==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tools/releasetools/testdata/testkey.pubkey.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvjvyO2LwWgmQNyq7z+xK\n04eg0t3AL4y2NhpAAOzVnFyCArFcFjLTGQDDvkbZP6N12O6+dwJoPLntnm9A+VnP\nIFFRHg0HUWSbHM+Qk8Jgv2/2AVkAUj5J1r9t4X+2WI0eRzJP15Zjn68pQKGmcyci\nry0gbvmYvXL2ZUmTm56DmEfCUCRIY2IGJ/CcMnFeItVU0LxKsV5Mlt5BO0Vv/CV4\nEaiOLwyCnoZuUhYto7dHlO/47v/H9zhkJC54OA1dkD38EPgO5GnfhGFSNXQRmJDT\nXrFgd6O+QO4yUNX8lYP10MzimUpItZa05t68NADqwYl3T7nWzvuC9r4IqZDyPf21\nTQIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "tools/releasetools/testdata/testkey.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEADCCAuigAwIBAgIJAN/FvjYzGNOKMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g\nVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UE\nAwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe\nFw0xODAxMTgwMDM0NTFaFw00NTA2MDUwMDM0NTFaMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G\nA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHQW5kcm9p\nZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBAL478jti8FoJkDcqu8/sStOHoNLdwC+MtjYa\nQADs1ZxcggKxXBYy0xkAw75G2T+jddjuvncCaDy57Z5vQPlZzyBRUR4NB1FkmxzP\nkJPCYL9v9gFZAFI+Sda/beF/tliNHkcyT9eWY5+vKUChpnMnIq8tIG75mL1y9mVJ\nk5ueg5hHwlAkSGNiBifwnDJxXiLVVNC8SrFeTJbeQTtFb/wleBGoji8Mgp6GblIW\nLaO3R5Tv+O7/x/c4ZCQueDgNXZA9/BD4DuRp34RhUjV0EZiQ016xYHejvkDuMlDV\n/JWD9dDM4plKSLWWtObevDQA6sGJd0+51s77gva+CKmQ8j39tU0CAwEAAaNTMFEw\nHQYDVR0OBBYEFNJPJZDpq6tc/19Z2kxPA2bj9D6UMB8GA1UdIwQYMBaAFNJPJZDp\nq6tc/19Z2kxPA2bj9D6UMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD\nggEBABSUG9qrwV3WcClDJwqkNLN4yeVVYzkRMGA8/XqOiYrW4zh0mKDLfr6OeU1C\nAKwZBLhhql59Po25r4gcwPiTN2DkoCfb3T59XG8J54PAgTQjIAZ3J+mGZplnmuD3\nwj+UGUpPe0qTr33ZPoJfwxVo4RVnOt/UCsIGXch0HS/BIdpechqP0w4rOHUbq6EA\n8UEi5irKSDOU9b/5rD/tX2f4nGwJlKQEHWrsj9LLKlaL7fX36ghoSxN/pBJOhedg\n/VjT6xbaEwfyhC6Zj9av5Xl7UdpYt+rBMroAGenz0OSxKhIphdcx4ZMhvfkBoYG9\nCrupdqe+kUsfg2RlPb5grQ3klMo=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tools/releasetools/testdata/testkey_EC.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGaguGj8Yb1KkqKHd\nISblUsjtOCbzAuVpX81i02sm8FWhRANCAARBnuotwKOsuvjH6iwTDhOAi7Q5pLWz\nxDkZjg2pcfbfi9FFTvLYETas7B2W6fx9PUezUmHTFTDV2JZuMYYFdZOw\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tools/releasetools/testdata/testkey_RSA4096.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQC7y8EH4O8M9aA7\nUhaWLlW5ceQxZi0P7DNOgog/82SIZh1/Vv0S8KCu9LcngK60oIejPU3k9zb0Mpl2\n4OEtupbOq9SV0nyRIp33rs9EJ0zm8keZ2jEfwTubdhE4GumlwkbfYHlMPmpufqOq\nuxKfCOUZk3ZEC5RFDBqVLGrf9m22ITMQwNGj/u/mtAg2UXRGy534eU1evHOFH8tD\nIxXd378m+gRY+bPi6fOkrJgAw6N01NwwsHRxxQsVgp8m1EKpPJ+ARRtP6YYtNsNB\nKTOXqZw18OD7eG7yVONAf7oSOMNRK7qg6ZU6YN/y1k+YZF3D/1HU9THoDXIpBHoW\nR2SpEM63Ua8ilSmx9PfoaDn27VrpjIcq50HqKAXOvclxOCyVD0pRkrryPpevYCVu\nx9/InmW3K4dPiAy0KmmS4ZSLUJnr6Lnkt8C3VxXHqZ4T7MlgrjrO70YqCxeDvYE0\nKL/e/UeJ69nANybkDThhBkxkOC1vvik3VXO4ITPEKCnLHdvdj8rwkjqf3Ex5A0g8\nXaH0l6I72pqxXi8nnU9udLrUEdHUT1KHzjnZBVP6aomcDy5Gnbb6wXTBTkB7fdQB\nnGcqT0DH67PqJE/rCUguVSmX4KGyOVrr5S1GQTg1EmHbF2Kf6P0YJpcVU7PHRE8s\nBisP39wzoE5XsIpn/aKpWdpcfqxjTQIDAQABAoICAQCB/vtyLryLpgPyzFIiR5TD\nuBkUMPyEhybE9ArI6fzvhnBo05h/4d34/iFC0QsesfjygN9I3fBGfjhJWEXH19/I\n1J1l0Ly14taiu3lyXhoXzCLQV3+l0acnaEVnJwoR2jghLLEKnDIkprk42CJ9wDSG\nzdMSK0nJuiU0mfipa/ZqGvU0ZaU49qKuenUs1Jm+3/hMJfvu1ljJEEcuBD2Axv+V\nRYB47vEc5IHpvifCb6rYlviNI7iXgKS5kSAGSuySJgrrSessGCTva3chxhmWpKwj\nksjKioWSbjyZS1FMh8p8h966wLayIJklikCy5tcZc8X7und/gL9DsXuprGX7uky8\n3ZS2cJjiVimkwoROq5VPa+0SBmNJWRBNvRfARiaKkyyidVxIsvjFNDFPQp1jYlzM\nfvGJwgnCiUQP56hvri3irriWN8Le2U8lqQQ7YaDLKcsf6iiMGwfxcK+6E4MUv797\nV5CZXSC7RrPd9wdj6UIqtgSGVUH3BV7kB+fYYfvV15kpj6IXYTxElfAZ2ak3g9sv\nJZ6moKbRN0xSufkDftMGv77cH13v97Iy3Whp8zEPMBnbsdpPp6DpIH8sl2R7O+zr\nuMty6vXw0Ux69LEpz4b4HyD7t65zTSwpou5YhfZt/yRzovawsQIRGlc4E1FJDFjf\ne9LvazMXo/us3T5LNv5pAQKCAQEA5Kq1RJgMlvKOfGaEah1xu65s3I1lGuz+9c1x\ngeYFlta9H2vG1aADUtgmIBjjC+1z4KLD9jrjrwwbqKnisu7/qUOR6Qf8BHFbrMSs\nJ8IMOD3Vw/UVc/8LCjoI4n1XaKYJtOyIxKJtWKAbgtvgVOAGSt47LEOOchXNnAKv\nC3Flak3ADYaUQFLoiwmp6WdSL+uiLisukKNjmYu8vxhg9255p31PB6xixd0raoF/\noDTfgY1fG/OFXvQd+GcjrTJ2Lqk1GtZqau5MEkS5jsKKnPJ6+ozd2t+QVkMrIQER\nWeTtZ7gimJo6QF8uyyG8WqT1qxbO2zV4Nrwak6ozRFhEJdnJrQKCAQEA0j5hY6sr\napIvEUFsK6k1rEb77+1p85eyCOSYZpHEIe0hy89MjMUFA5IKhsXnUqhkiuJURUrD\nVtccWWJt5DUgS6HzJUGjeXo07wkqVz+10l+l+RSHleNBYlbxSpZQtvkKQkISF56c\nbSjLzOGM4RE8NxBdFg6EijwlKlZ8kW1ZJaQv9fuR+QS9DFXSiYUJSDiwLF0F6ogQ\ni1h3RN3RIKYc9kizYqBKaksg8EfQEyJs2Rhl5JrPmdZvDTpSeGRqz66WbdL4gSNv\nud64BYY+Uhec9yH2HDal1l/j1dFbh+Nzs2v4b5TYmCO/zX5GfucUrZaGHbZcovlo\n/abKhURKW/N0IQKCAQEAhz0PCAqFJ6E89AYNulS/tyhp6ecWLN6NzAI9Z34LQDKw\nl6y+ZAnG7XA43DLb1WoSZoDdNPuPPTAEC7SuBvWi7xCvcwrt2hLRDVUkHD9/yqOH\nkeWZUok8lkfMiWdoEtRgWUireuA1m3zVyIcSHiCAmDbm+D7cOEz81ZAgxrvCJyTk\nuRsnAwQF1HVasFgTG5RYzsVrPM/lUCJ89ugMMUp9WLmbzAYARNWRn+QG/1FF/vEF\nlxpnfskSEJ+vUffOPbqFVeIJ/kQBaayLsgsMv9YJNbWqYJBoZRxEnbhr8qaaYgVd\nMLPGT9v7aNgC9fkp8o4CuVLeTkDh1wOKXpl1dI1h4QKCAQEAgfNExxI517lbllLV\nxXblUgLeHkKkxofw50ZEXMGkdUPZK9yJ+Eie/MH796nDfXfQDXgvllTLwJVdVHJe\ncjvUJmuHmnOj06YRqd4EacFbZRjxwa9Kzv6Un3AV3IBki3QLP0EPZcIH9gDNV2ni\nZgr9KRvYLZXznm3mmvCyNkFcZMPDUUuZwk2HfGRfqditEBLZ8dHdokVP1JFtxwdE\nB+Yk6KWvGzrwRBsD1QDOP8V7egR2loKJ5xB/u7Fc4EVRL+U93cwVBd0dZcmf/Oop\nAxzNTIOVV4L/pi3G6ZZp+OhBz5jhCjb6Wa9fEmkGsdGrGlB7vUnGoIWAu6eobg7z\n1zn3gQKCAQBwyViGs7b5HuYQ8UNkvBK9MxUE6h/qHEshbw+QtD/wcdNNpwLTawoW\nJH3bWzD01p1DsbRx/bcV5yaiWDhuslSH2xB6+N1gx2ohg8lPmFhKQiR1OTQ3L603\nY+3h8FNO/c3YPcNr/k4N+tVKPSJvz0NcbkNs9qGUUsiEppVtc17VFAv/yPicV/wP\n0vC4Qw4xitSlIzD1QtPl0HfhA3ZM6fBb0lYx3tpJjmkrWPVjwWhYIAVLD6j7Jark\nNtMW9wSG21atSgWX1jFOiVsu3qzDpMvLXbH2FpAO9t+9GSDcSzfUMQLymWyW1+Dl\nL0rBGoJyEopSbtycAkWEHb/YLolfaTRd\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tools/releasetools/testdata/testkey_with_passwd.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCwaAOHPqgkCmqU\nAFRnJW6LrAuSfy9EzWSRHSkltp811ByMIE0N6/Nttu8ZCL456lzArHNKt/zdoBik\neLB6gN9CTvQ8n4LMdSEmkRl3uXBtOPJuVObJ6ZUILz6L7WofWcr8DT81j2At7nHi\nWg8SkCsFXbFfpjljOlpqUG3Szt+48X8rcgG82s97BuRwNxUgfK1/8QzOiH9fDbMU\nh6XI2jo2VwuBYOsJadJJWOf6oRRHZonrts0FXpV46CXykpLvLT2u5GXg1Pxd7i1K\nv1P8bxZOzVbEVfkL2DnUCtUBAnP98r9UyjQDd4blk4Mwl+mzB5otPTacNzEGhmNK\nEt+HB/cdAgMBAAECggEATsn2IXa7tHUuivHmwLb4O8vY01KY8xrleubSVPTPAUS+\nh1t57ujerbcR7VV5WPay/J9JUyr/9qClwPfioqRikwQek+EOk3ERIF+YR1/8tdvE\nc8DZ337DQIeRYP/l8SCyx4bHH43tADbKiLV+m+TmQhxJt5XPdeE/NtK7andZdwkv\nxEoG9l2aONE4z9pY1x+c1SdDSsq92/iLHLgSkQJmWo+lrfeh6gshXgQgDY8n6rgY\nGsCgSawLphvd8Tvo86CL04l0pWtY1gEW3s6sdYo1YDkpWQzSRCtGm0GlhEt2fyq5\ncoTK2sLHguE7NL5VZo4zlGtM3QBdvRksTO1mJOt6JQKBgQDaT4oGjZp1rtKdObvn\nElaUo5EOyJjmXkRBBndrbiG3078eOqTJHXx45DJUv8hj9+g6vSULiIeFk1FiiMQD\nvcnsBEaGaSc886wXY6TQgIIzvVfzDHGYTuQydiYQbLClH6S28HLqdlZjUIlHwxb9\nwBm8JwmTiVeAEvO8LTzeEqfkLwKBgQDO3He8Ei8XDeqtIK0lzcZ83yw9OGP23/gK\n8GDaf8J+cOtOyYkDlcV0rBNFvE8+TzIpIUlo47b2RSaART3iPSfRJTaySZjKWCVo\ns2A0/zQcrj7GgD2gaHRrgI9bmnWW1j95a9n/6AUEyEIJ6K8tYK819Vl4GAyhNHEQ\nsRbxa69qcwKBgQC5F8jxx2tXLdM6JLIQtzabLZcWTrN8Vh5Od3oWpriF0EzxB02h\nipN3OBsISdZQE+dcrfNTtP0aHo5ZGZX/ihFCP1nAKjVvczXMWtppQRujXHzOABXr\nya+mrQ+Wy2B1j7+qr3DvI0gZSjYqltjOaeon4X04DrEWUHtAZ6Z8rpqUVwKBgQCB\no8mmI/8/A4m/Vmss9fke6P5gn6aGYXah5GPOi6Loevv9NHCZvpMwu2aYnZtMAXX+\nMM5A3fUcAdpPKRXPY2RAvoG42kbXCMbpBwGUNRwDnW/aFySIEu5jMP6m+fYXwc2l\n2uGUb2Q1ywsYCqs+VQl5V3nquaewn5z8SP+H7WTR4QKBgQCO5CRpyNOjEwMxTPR1\nGYUKAEiVtmzknHAxUE6drTgGEZSquAXiau0B5+7+/G5gwqxCLGpnstMByI+dhkR6\n+ybAc/bzb2aoGK4pZf/PuwxQQsHBnG0oaSFU6RZlbVV20j7FZ04+cYnKHwCYkKjN\nDwA1Ae+H+u95raB4vYhk7IzD4A==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tools/releasetools/testdata/testkey_with_passwd.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEADCCAuigAwIBAgIJANefUd3Piu0yMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g\nVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UE\nAwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe\nFw0xODAxMTgwMDI3NDRaFw00NTA2MDUwMDI3NDRaMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G\nA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHQW5kcm9p\nZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBALBoA4c+qCQKapQAVGclbousC5J/L0TNZJEd\nKSW2nzXUHIwgTQ3r82227xkIvjnqXMCsc0q3/N2gGKR4sHqA30JO9Dyfgsx1ISaR\nGXe5cG048m5U5snplQgvPovtah9ZyvwNPzWPYC3uceJaDxKQKwVdsV+mOWM6WmpQ\nbdLO37jxfytyAbzaz3sG5HA3FSB8rX/xDM6If18NsxSHpcjaOjZXC4Fg6wlp0klY\n5/qhFEdmieu2zQVelXjoJfKSku8tPa7kZeDU/F3uLUq/U/xvFk7NVsRV+QvYOdQK\n1QECc/3yv1TKNAN3huWTgzCX6bMHmi09Npw3MQaGY0oS34cH9x0CAwEAAaNTMFEw\nHQYDVR0OBBYEFNsJZ0n9Opeea0rVAzL+1jwkDKzPMB8GA1UdIwQYMBaAFNsJZ0n9\nOpeea0rVAzL+1jwkDKzPMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD\nggEBAJ/bzIzA+NrYwPEv56XKf6Vuj81+M1rTHAsH9PqbOvJT7iM7aU7wAl6vmXAo\nDQtvKoOBMdIXprapwe0quHCQm7PGxg+RRegr+dcTSVJFv1plnODOBOEAVlEfFwuW\nCz0USF2jrNq+4ciH5zPL1a31ONb1rMkxJXQ/tAi0x8m6tZz+jsbE0wO6qB80UmkA\n4WY2Tu/gnAvFpD8plkiU0EKwedBHAcaFFZkQp23MKsVZ3UBqsqzzfXDYV1Oa6rIy\nXIZpI2Gx75pvAb57T2ap/yl0DBEAu7Nmpll0GCsgeJVdy7tS4LNj96Quya3CHWQw\nWNTVuan0KZqwDIm4Xn1oHUFQ9vc=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tools/releasetools/testdata/verity.x509.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIID/TCCAuWgAwIBAgIJAJcPmDkJqolJMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g\nVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UE\nAwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe\nFw0xNDExMDYxOTA3NDBaFw00MjAzMjQxOTA3NDBaMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G\nA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHQW5kcm9p\nZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBAOjreE0vTVSRenuzO9vnaWfk0eQzYab0gqpi\n6xAzi6dmD+ugoEKJmbPiuE5Dwf21isZ9uhUUu0dQM46dK4ocKxMRrcnmGxydFn6o\nfs3ODJMXOkv2gKXL/FdbEPdDbxzdu8z3yk+W67udM/fW7WbaQ3DO0knu+izKak/3\nT41c5uoXmQ81UNtAzRGzGchNVXMmWuTGOkg6U+0I2Td7K8yvUMWhAWPPpKLtVH9r\nAL5TzjYNR92izdKcz3AjRsI3CTjtpiVABGeX0TcjRSuZB7K9EK56HV+OFNS6I1NP\njdD7FIShyGlqqZdUOkAUZYanbpgeT5N7QL6uuqcGpoTOkalu6kkCAwEAAaNQME4w\nHQYDVR0OBBYEFH5DM/m7oArf4O3peeKO0ZIEkrQPMB8GA1UdIwQYMBaAFH5DM/m7\noArf4O3peeKO0ZIEkrQPMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB\nAHO3NSvDE5jFvMehGGtS8BnFYdFKRIglDMc4niWSzhzOVYRH4WajxdtBWc5fx0ix\nNF/+hVKVhP6AIOQa+++sk+HIi7RvioPPbhjcsVlZe7cUEGrLSSveGouQyc+j0+m6\nJF84kszIl5GGNMTnx0XRPO+g8t6h5LWfnVydgZfpGRRg+WHewk1U2HlvTjIceb0N\ndcoJ8WKJAFWdcuE7VIm4w+vF/DYX/A2Oyzr2+QRhmYSv1cusgAeC1tvH4ap+J1Lg\nUnOu5Kh/FqPLLSwNVQp4Bu7b9QFfqK8Moj84bj88NqRGZgDyqzuTrFxn6FW7dmyA\nyttuAJAEAymk1mipd9+zp38=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tools/releasetools/testdata/vintf/kernel/SYSTEM/etc/vintf/compatibility_matrix.1.xml",
    "content": "<compatibility-matrix version=\"1.0\" level=\"1\" type=\"framework\">\n    <kernel version=\"4.14.1\" />\n    <sepolicy>\n        <sepolicy-version>0.0</sepolicy-version>\n        <kernel-sepolicy-version>0</kernel-sepolicy-version>\n    </sepolicy>\n</compatibility-matrix>\n"
  },
  {
    "path": "tools/releasetools/testdata/vintf/matrix_incompat/SYSTEM/etc/vintf/compatibility_matrix.1.xml",
    "content": "<compatibility-matrix version=\"1.0\" level=\"1\" type=\"framework\">\n    <sepolicy>\n        <sepolicy-version>1.0</sepolicy-version>\n        <kernel-sepolicy-version>0</kernel-sepolicy-version>\n    </sepolicy>\n</compatibility-matrix>\n"
  },
  {
    "path": "tools/releasetools/testdata/vintf/sku_compat/ODM/etc/vintf/manifest_sku.xml",
    "content": "<manifest version=\"1.0\" type=\"device\">\n    <hal format=\"hidl\">\n        <name>foo</name>\n        <transport>hwbinder</transport>\n        <fqname>@1.0::IFoo/default</fqname>\n    </hal>\n</manifest>\n"
  },
  {
    "path": "tools/releasetools/testdata/vintf/sku_compat/SYSTEM/etc/vintf/compatibility_matrix.1.xml",
    "content": "<compatibility-matrix version=\"1.0\" level=\"1\" type=\"framework\">\n    <hal format=\"hidl\" optional=\"false\">\n        <name>foo</name>\n        <version>1.0</version>\n        <interface>\n            <name>IFoo</name>\n            <instance>default</instance>\n        </interface>\n    </hal>\n    <sepolicy>\n        <sepolicy-version>0.0</sepolicy-version>\n        <kernel-sepolicy-version>0</kernel-sepolicy-version>\n    </sepolicy>\n</compatibility-matrix>\n"
  },
  {
    "path": "tools/releasetools/testdata/vintf/sku_incompat/ODM/etc/vintf/manifest_sku.xml",
    "content": "<manifest version=\"1.0\" type=\"device\">\n    <hal format=\"hidl\">\n        <name>foo</name>\n        <transport>hwbinder</transport>\n        <fqname>@1.0::IFoo/default</fqname>\n    </hal>\n</manifest>\n"
  },
  {
    "path": "tools/releasetools/testdata/vintf/sku_incompat/SYSTEM/etc/vintf/compatibility_matrix.1.xml",
    "content": "<compatibility-matrix version=\"1.0\" level=\"1\" type=\"framework\">\n    <hal format=\"hidl\" optional=\"false\">\n        <name>foo</name>\n        <version>1.1</version>\n        <interface>\n            <name>IFoo</name>\n            <instance>default</instance>\n        </interface>\n    </hal>\n    <sepolicy>\n        <sepolicy-version>0.0</sepolicy-version>\n        <kernel-sepolicy-version>0</kernel-sepolicy-version>\n    </sepolicy>\n</compatibility-matrix>\n"
  },
  {
    "path": "tools/releasetools/validate_target_files.py",
    "content": "#!/usr/bin/env python\n\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nValidate a given (signed) target_files.zip.\n\nIt performs the following checks to assert the integrity of the input zip.\n\n - It verifies the file consistency between the ones in IMAGES/system.img (read\n   via IMAGES/system.map) and the ones under unpacked folder of SYSTEM/. The\n   same check also applies to the vendor image if present.\n\n - It verifies the install-recovery script consistency, by comparing the\n   checksums in the script against the ones of IMAGES/{boot,recovery}.img.\n\n - It verifies the signed Verified Boot related images, for both of Verified\n   Boot 1.0 and 2.0 (aka AVB).\n\"\"\"\n\nimport argparse\nimport filecmp\nimport logging\nimport os.path\nimport re\nimport zipfile\n\nfrom hashlib import sha1\nfrom common import IsSparseImage\n\nimport common\nimport rangelib\n\n\ndef _ReadFile(file_name, unpacked_name, round_up=False):\n  \"\"\"Constructs and returns a File object. Rounds up its size if needed.\"\"\"\n  assert os.path.exists(unpacked_name)\n  with open(unpacked_name, 'rb') as f:\n    file_data = f.read()\n  file_size = len(file_data)\n  if round_up:\n    file_size_rounded_up = common.RoundUpTo4K(file_size)\n    file_data += b'\\0' * (file_size_rounded_up - file_size)\n  return common.File(file_name, file_data)\n\n\ndef ValidateFileAgainstSha1(input_tmp, file_name, file_path, expected_sha1):\n  \"\"\"Check if the file has the expected SHA-1.\"\"\"\n\n  logging.info('Validating the SHA-1 of %s', file_name)\n  unpacked_name = os.path.join(input_tmp, file_path)\n  assert os.path.exists(unpacked_name)\n  actual_sha1 = _ReadFile(file_name, unpacked_name, False).sha1\n  assert actual_sha1 == expected_sha1, \\\n      'SHA-1 mismatches for {}. actual {}, expected {}'.format(\n          file_name, actual_sha1, expected_sha1)\n\n\ndef ValidateFileConsistency(input_zip, input_tmp, info_dict):\n  \"\"\"Compare the files from image files and unpacked folders.\"\"\"\n\n  def CheckAllFiles(which):\n    logging.info('Checking %s image.', which)\n    path = os.path.join(input_tmp, \"IMAGES\", which + \".img\")\n    if not IsSparseImage(path):\n      logging.info(\"%s is non-sparse image\", which)\n      image = common.GetNonSparseImage(which, input_tmp)\n    else:\n      logging.info(\"%s is sparse image\", which)\n      # Allow having shared blocks when loading the sparse image, because allowing\n      # that doesn't affect the checks below (we will have all the blocks on file,\n      # unless it's skipped due to the holes).\n      image = common.GetSparseImage(which, input_tmp, input_zip, True)\n    prefix = '/' + which\n    for entry in image.file_map:\n      # Skip entries like '__NONZERO-0'.\n      if not entry.startswith(prefix):\n        continue\n\n      # Read the blocks that the file resides. Note that it will contain the\n      # bytes past the file length, which is expected to be padded with '\\0's.\n      ranges = image.file_map[entry]\n\n      # Use the original RangeSet if applicable, which includes the shared\n      # blocks. And this needs to happen before checking the monotonicity flag.\n      if ranges.extra.get('uses_shared_blocks'):\n        file_ranges = ranges.extra['uses_shared_blocks']\n      else:\n        file_ranges = ranges\n\n      incomplete = file_ranges.extra.get('incomplete', False)\n      if incomplete:\n        logging.warning('Skipping %s that has incomplete block list', entry)\n        continue\n\n      # If the file has non-monotonic ranges, read each range in order.\n      if not file_ranges.monotonic:\n        h = sha1()\n        for file_range in file_ranges.extra['text_str'].split(' '):\n          for data in image.ReadRangeSet(rangelib.RangeSet(file_range)):\n            h.update(data)\n        blocks_sha1 = h.hexdigest()\n      else:\n        blocks_sha1 = image.RangeSha1(file_ranges)\n\n      # The filename under unpacked directory, such as SYSTEM/bin/sh.\n      unpacked_name = os.path.join(\n          input_tmp, which.upper(), entry[(len(prefix) + 1):])\n      unpacked_file = _ReadFile(entry, unpacked_name, True)\n      file_sha1 = unpacked_file.sha1\n      assert blocks_sha1 == file_sha1, \\\n          'file: %s, range: %s, blocks_sha1: %s, file_sha1: %s' % (\n              entry, file_ranges, blocks_sha1, file_sha1)\n\n  logging.info('Validating file consistency.')\n\n  # TODO(b/79617342): Validate non-sparse images.\n  if info_dict.get('extfs_sparse_flag') != '-s':\n    logging.warning('Skipped due to target using non-sparse images')\n    return\n\n  # Verify IMAGES/system.img if applicable.\n  # Some targets are system.img-less.\n  if 'IMAGES/system.img' in input_zip.namelist():\n    CheckAllFiles('system')\n\n  # Verify IMAGES/vendor.img if applicable.\n  if 'VENDOR/' in input_zip.namelist():\n    CheckAllFiles('vendor')\n\n  # Not checking IMAGES/system_other.img since it doesn't have the map file.\n\n\ndef ValidateInstallRecoveryScript(input_tmp, info_dict):\n  \"\"\"Validate the SHA-1 embedded in install-recovery.sh.\n\n  install-recovery.sh is written in common.py and has the following format:\n\n  1. full recovery:\n  ...\n  if ! applypatch --check type:device:size:sha1; then\n    applypatch --flash /vendor/etc/recovery.img \\\\\n        type:device:size:sha1 && \\\\\n  ...\n\n  2. recovery from boot:\n  ...\n  if ! applypatch --check type:recovery_device:recovery_size:recovery_sha1; then\n    applypatch [--bonus bonus_args] \\\\\n        --patch /vendor/recovery-from-boot.p \\\\\n        --source type:boot_device:boot_size:boot_sha1 \\\\\n        --target type:recovery_device:recovery_size:recovery_sha1 && \\\\\n  ...\n\n  For full recovery, we want to calculate the SHA-1 of /vendor/etc/recovery.img\n  and compare it against the one embedded in the script. While for recovery\n  from boot, we want to check the SHA-1 for both recovery.img and boot.img\n  under IMAGES/.\n  \"\"\"\n\n  board_uses_vendorimage = info_dict.get(\"board_uses_vendorimage\") == \"true\"\n\n  if board_uses_vendorimage:\n    script_path = 'VENDOR/bin/install-recovery.sh'\n    recovery_img = 'VENDOR/etc/recovery.img'\n  else:\n    script_path = 'SYSTEM/vendor/bin/install-recovery.sh'\n    recovery_img = 'SYSTEM/vendor/etc/recovery.img'\n\n  if not os.path.exists(os.path.join(input_tmp, script_path)):\n    logging.info('%s does not exist in input_tmp', script_path)\n    return\n\n  logging.info('Checking %s', script_path)\n  with open(os.path.join(input_tmp, script_path), 'r') as script:\n    lines = script.read().strip().split('\\n')\n  assert len(lines) >= 10\n  check_cmd = re.search(r'if ! applypatch --check (\\w+:.+:\\w+:\\w+);',\n                        lines[1].strip())\n  check_partition = check_cmd.group(1)\n  assert len(check_partition.split(':')) == 4\n\n  full_recovery_image = info_dict.get(\"full_recovery_image\") == \"true\"\n  if full_recovery_image:\n    assert len(lines) == 10, \"Invalid line count: {}\".format(lines)\n\n    # Expect something like \"EMMC:/dev/block/recovery:28:5f9c..62e3\".\n    target = re.search(r'--target (.+) &&', lines[4].strip())\n    assert target is not None, \\\n        \"Failed to parse target line \\\"{}\\\"\".format(lines[4])\n    flash_partition = target.group(1)\n\n    # Check we have the same recovery target in the check and flash commands.\n    assert check_partition == flash_partition, \\\n        \"Mismatching targets: {} vs {}\".format(\n            check_partition, flash_partition)\n\n    # Validate the SHA-1 of the recovery image.\n    recovery_sha1 = flash_partition.split(':')[3]\n    ValidateFileAgainstSha1(\n        input_tmp, 'recovery.img', recovery_img, recovery_sha1)\n  else:\n    assert len(lines) == 11, \"Invalid line count: {}\".format(lines)\n\n    # --source boot_type:boot_device:boot_size:boot_sha1\n    source = re.search(r'--source (\\w+:.+:\\w+:\\w+) \\\\', lines[4].strip())\n    assert source is not None, \\\n        \"Failed to parse source line \\\"{}\\\"\".format(lines[4])\n\n    source_partition = source.group(1)\n    source_info = source_partition.split(':')\n    assert len(source_info) == 4, \\\n        \"Invalid source partition: {}\".format(source_partition)\n    ValidateFileAgainstSha1(input_tmp, file_name='boot.img',\n                            file_path='IMAGES/boot.img',\n                            expected_sha1=source_info[3])\n\n    # --target recovery_type:recovery_device:recovery_size:recovery_sha1\n    target = re.search(r'--target (\\w+:.+:\\w+:\\w+) && \\\\', lines[5].strip())\n    assert target is not None, \\\n        \"Failed to parse target line \\\"{}\\\"\".format(lines[5])\n    target_partition = target.group(1)\n\n    # Check we have the same recovery target in the check and patch commands.\n    assert check_partition == target_partition, \\\n        \"Mismatching targets: {} vs {}\".format(\n            check_partition, target_partition)\n\n    recovery_info = target_partition.split(':')\n    assert len(recovery_info) == 4, \\\n        \"Invalid target partition: {}\".format(target_partition)\n    ValidateFileAgainstSha1(input_tmp, file_name='recovery.img',\n                            file_path='IMAGES/recovery.img',\n                            expected_sha1=recovery_info[3])\n\n  logging.info('Done checking %s', script_path)\n\n\n# Symlink files in `src` to `dst`, if the files do not\n# already exists in `dst` directory.\ndef symlinkIfNotExists(src, dst):\n  if not os.path.isdir(src):\n    return\n  for filename in os.listdir(src):\n    if os.path.exists(os.path.join(dst, filename)):\n      continue\n    os.symlink(os.path.join(src, filename), os.path.join(dst, filename))\n\n\ndef ValidatePartitionFingerprints(input_tmp, info_dict):\n  build_info = common.BuildInfo(info_dict)\n  # Expected format:\n  #  Prop: com.android.build.vendor.fingerprint -> 'generic/aosp_cf_x86_64_phone/vsoc_x86_64:S/AOSP.MASTER/7335886:userdebug/test-keys'\n  #  Prop: com.android.build.vendor_boot.fingerprint -> 'generic/aosp_cf_x86_64_phone/vsoc_x86_64:S/AOSP.MASTER/7335886:userdebug/test-keys'\n  p = re.compile(\n      r\"Prop: com.android.build.(?P<partition>\\w+).fingerprint -> '(?P<fingerprint>[\\w\\/:\\.-]+)'\")\n  for vbmeta_partition in [\"vbmeta\", \"vbmeta_system\"]:\n    image = os.path.join(input_tmp, \"IMAGES\", vbmeta_partition + \".img\")\n    if not os.path.exists(image):\n      assert vbmeta_partition != \"vbmeta\",\\\n          \"{} is a required partition for AVB.\".format(\n              vbmeta_partition)\n      logging.info(\"vb partition %s not present, skipping\", vbmeta_partition)\n      continue\n\n    output = common.RunAndCheckOutput(\n        [info_dict[\"avb_avbtool\"], \"info_image\", \"--image\", image])\n    matches = p.findall(output)\n    for (partition, fingerprint) in matches:\n      actual_fingerprint = build_info.GetPartitionFingerprint(\n          partition)\n      if actual_fingerprint is None:\n        logging.warning(\n            \"Failed to get fingerprint for partition %s\", partition)\n        continue\n      assert fingerprint == actual_fingerprint, \"Fingerprint mismatch for partition {}, expected: {} actual: {}\".format(\n          partition, fingerprint, actual_fingerprint)\n\n\ndef ValidateVerifiedBootImages(input_tmp, info_dict, options):\n  \"\"\"Validates the Verified Boot related images.\n\n  For Verified Boot 1.0, it verifies the signatures of the bootable images\n  (boot/recovery etc), as well as the dm-verity metadata in system images\n  (system/vendor/product). For Verified Boot 2.0, it calls avbtool to verify\n  vbmeta.img, which in turn verifies all the descriptors listed in vbmeta.\n\n  Args:\n    input_tmp: The top-level directory of unpacked target-files.zip.\n    info_dict: The loaded info dict.\n    options: A dict that contains the user-supplied public keys to be used for\n        image verification. In particular, 'verity_key' is used to verify the\n        bootable images in VB 1.0, and the vbmeta image in VB 2.0, where\n        applicable. 'verity_key_mincrypt' will be used to verify the system\n        images in VB 1.0.\n\n  Raises:\n    AssertionError: On any verification failure.\n  \"\"\"\n  # See bug 159299583\n  # After commit 5277d1015, some images (e.g. acpio.img and tos.img) are no\n  # longer copied from RADIO to the IMAGES folder. But avbtool assumes that\n  # images are in IMAGES folder. So we symlink them.\n  symlinkIfNotExists(os.path.join(input_tmp, \"RADIO\"),\n                     os.path.join(input_tmp, \"IMAGES\"))\n  # Verified boot 1.0 (images signed with boot_signer and verity_signer).\n  if info_dict.get('boot_signer') == 'true':\n    logging.info('Verifying Verified Boot images...')\n\n    # Verify the boot/recovery images (signed with boot_signer), against the\n    # given X.509 encoded pubkey (or falling back to the one in the info_dict if\n    # none given).\n    verity_key = options['verity_key']\n    if verity_key is None:\n      verity_key = info_dict['verity_key'] + '.x509.pem'\n    for image in ('boot.img', 'recovery.img', 'recovery-two-step.img'):\n      if image == 'recovery-two-step.img':\n        image_path = os.path.join(input_tmp, 'OTA', image)\n      else:\n        image_path = os.path.join(input_tmp, 'IMAGES', image)\n      if not os.path.exists(image_path):\n        continue\n\n      cmd = ['boot_signer', '-verify', image_path, '-certificate', verity_key]\n      proc = common.Run(cmd)\n      stdoutdata, _ = proc.communicate()\n      assert proc.returncode == 0, \\\n          'Failed to verify {} with boot_signer:\\n{}'.format(image, stdoutdata)\n      logging.info(\n          'Verified %s with boot_signer (key: %s):\\n%s', image, verity_key,\n          stdoutdata.rstrip())\n\n  # Verify verity signed system images in Verified Boot 1.0. Note that not using\n  # 'elif' here, since 'boot_signer' and 'verity' are not bundled in VB 1.0.\n  if info_dict.get('verity') == 'true':\n    # First verify that the verity key is built into the root image (regardless\n    # of system-as-root).\n    verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key')\n    assert os.path.exists(verity_key_mincrypt), 'Missing verity_key'\n\n    # Verify /verity_key matches the one given via command line, if any.\n    if options['verity_key_mincrypt'] is None:\n      logging.warn(\n          'Skipped checking the content of /verity_key, as the key file not '\n          'provided. Use --verity_key_mincrypt to specify.')\n    else:\n      expected_key = options['verity_key_mincrypt']\n      assert filecmp.cmp(expected_key, verity_key_mincrypt, shallow=False), \\\n          \"Mismatching mincrypt verity key files\"\n      logging.info('Verified the content of /verity_key')\n\n    verity_key_ramdisk = os.path.join(\n        input_tmp, 'BOOT', 'RAMDISK', 'verity_key')\n    assert os.path.exists(\n        verity_key_ramdisk), 'Missing verity_key in ramdisk'\n\n    assert filecmp.cmp(\n        verity_key_mincrypt, verity_key_ramdisk, shallow=False), \\\n        'Mismatching verity_key files in root and ramdisk'\n    logging.info('Verified the content of /verity_key in ramdisk')\n\n    # Then verify the verity signed system/vendor/product images, against the\n    # verity pubkey in mincrypt format.\n    for image in ('system.img', 'vendor.img', 'product.img'):\n      image_path = os.path.join(input_tmp, 'IMAGES', image)\n\n      # We are not checking if the image is actually enabled via info_dict (e.g.\n      # 'system_verity_block_device=...'). Because it's most likely a bug that\n      # skips signing some of the images in signed target-files.zip, while\n      # having the top-level verity flag enabled.\n      if not os.path.exists(image_path):\n        continue\n\n      cmd = ['verity_verifier', image_path, '-mincrypt', verity_key_mincrypt]\n      proc = common.Run(cmd)\n      stdoutdata, _ = proc.communicate()\n      assert proc.returncode == 0, \\\n          'Failed to verify {} with verity_verifier (key: {}):\\n{}'.format(\n              image, verity_key_mincrypt, stdoutdata)\n      logging.info(\n          'Verified %s with verity_verifier (key: %s):\\n%s', image,\n          verity_key_mincrypt, stdoutdata.rstrip())\n\n  # Handle the case of Verified Boot 2.0 (AVB).\n  if info_dict.get(\"avb_building_vbmeta_image\") == \"true\":\n    logging.info('Verifying Verified Boot 2.0 (AVB) images...')\n\n    key = options['verity_key']\n    if key is None:\n      key = info_dict['avb_vbmeta_key_path']\n\n    ValidatePartitionFingerprints(input_tmp, info_dict)\n\n    # avbtool verifies all the images that have descriptors listed in vbmeta.\n    # Using `--follow_chain_partitions` so it would additionally verify chained\n    # vbmeta partitions (e.g. vbmeta_system).\n    image = os.path.join(input_tmp, 'IMAGES', 'vbmeta.img')\n    cmd = [info_dict['avb_avbtool'], 'verify_image', '--image', image,\n           '--follow_chain_partitions']\n\n    # Custom images.\n    custom_partitions = info_dict.get(\n        \"avb_custom_images_partition_list\", \"\").strip().split()\n\n    # Append the args for chained partitions if any.\n    for partition in (common.AVB_PARTITIONS + common.AVB_VBMETA_PARTITIONS +\n                      tuple(custom_partitions)):\n      key_name = 'avb_' + partition + '_key_path'\n      if info_dict.get(key_name) is not None:\n        if info_dict.get('ab_update') != 'true' and partition == 'recovery':\n          continue\n\n        # Use the key file from command line if specified; otherwise fall back\n        # to the one in info dict.\n        key_file = options.get(key_name, info_dict[key_name])\n        chained_partition_arg = common.GetAvbChainedPartitionArg(\n            partition, info_dict, key_file)\n        cmd.extend(['--expected_chain_partition',\n                    chained_partition_arg.to_string()])\n\n    # Handle the boot image with a non-default name, e.g. boot-5.4.img\n    boot_images = info_dict.get(\"boot_images\")\n    if boot_images:\n      # we used the 1st boot image to generate the vbmeta. Rename the filename\n      # to boot.img so that avbtool can find it correctly.\n      first_image_name = boot_images.split()[0]\n      first_image_path = os.path.join(input_tmp, 'IMAGES', first_image_name)\n      assert os.path.isfile(first_image_path)\n      renamed_boot_image_path = os.path.join(input_tmp, 'IMAGES', 'boot.img')\n      os.rename(first_image_path, renamed_boot_image_path)\n\n    proc = common.Run(cmd)\n    stdoutdata, _ = proc.communicate()\n    assert proc.returncode == 0, \\\n        'Failed to verify {} with avbtool (key: {}):\\n{}'.format(\n            image, key, stdoutdata)\n\n    logging.info(\n        'Verified %s with avbtool (key: %s):\\n%s', image, key,\n        stdoutdata.rstrip())\n\n    # avbtool verifies recovery image for non-A/B devices.\n    if (info_dict.get('ab_update') != 'true' and\n            info_dict.get('no_recovery') != 'true'):\n      image = os.path.join(input_tmp, 'IMAGES', 'recovery.img')\n      key = info_dict['avb_recovery_key_path']\n      cmd = [info_dict['avb_avbtool'], 'verify_image', '--image', image,\n             '--key', key]\n      proc = common.Run(cmd)\n      stdoutdata, _ = proc.communicate()\n      assert proc.returncode == 0, \\\n          'Failed to verify {} with avbtool (key: {}):\\n{}'.format(\n              image, key, stdoutdata)\n      logging.info(\n          'Verified %s with avbtool (key: %s):\\n%s', image, key,\n          stdoutdata.rstrip())\n\n\ndef CheckDataInconsistency(lines):\n  build_prop = {}\n  for line in lines:\n    if line.startswith(\"import\") or line.startswith(\"#\"):\n      continue\n    if \"=\" not in line:\n      continue\n\n    key, value = line.rstrip().split(\"=\", 1)\n    if key in build_prop:\n      logging.info(\"Duplicated key found for {}\".format(key))\n      if value != build_prop[key]:\n        logging.error(\"Key {} is defined twice with different values {} vs {}\"\n                      .format(key, value, build_prop[key]))\n        return key\n    build_prop[key] = value\n\n\ndef CheckBuildPropDuplicity(input_tmp):\n  \"\"\"Check all buld.prop files inside directory input_tmp, raise error\n  if they contain duplicates\"\"\"\n\n  if not os.path.isdir(input_tmp):\n    raise ValueError(\"Expect {} to be a directory\".format(input_tmp))\n  for name in os.listdir(input_tmp):\n    if not name.isupper():\n      continue\n    for prop_file in ['build.prop', 'etc/build.prop']:\n      path = os.path.join(input_tmp, name, prop_file)\n      if not os.path.exists(path):\n        continue\n      logging.info(\"Checking {}\".format(path))\n      with open(path, 'r') as fp:\n        dupKey = CheckDataInconsistency(fp.readlines())\n        if dupKey:\n          raise ValueError(\"{} contains duplicate keys for {}\".format(\n              path, dupKey))\n\n\ndef main():\n  parser = argparse.ArgumentParser(\n      description=__doc__,\n      formatter_class=argparse.RawDescriptionHelpFormatter)\n  parser.add_argument(\n      'target_files',\n      help='the input target_files.zip to be validated')\n  parser.add_argument(\n      '--verity_key',\n      help='the verity public key to verify the bootable images (Verified '\n           'Boot 1.0), or the vbmeta image (Verified Boot 2.0, aka AVB), where '\n           'applicable')\n  for partition in common.AVB_PARTITIONS + common.AVB_VBMETA_PARTITIONS:\n    parser.add_argument(\n        '--avb_' + partition + '_key_path',\n        help='the public or private key in PEM format to verify AVB chained '\n             'partition of {}'.format(partition))\n  parser.add_argument(\n      '--verity_key_mincrypt',\n      help='the verity public key in mincrypt format to verify the system '\n           'images, if target using Verified Boot 1.0')\n  args = parser.parse_args()\n\n  # Unprovided args will have 'None' as the value.\n  options = vars(args)\n\n  logging_format = '%(asctime)s - %(filename)s - %(levelname)-8s: %(message)s'\n  date_format = '%Y/%m/%d %H:%M:%S'\n  logging.basicConfig(level=logging.INFO, format=logging_format,\n                      datefmt=date_format)\n\n  logging.info(\"Unzipping the input target_files.zip: %s\", args.target_files)\n  input_tmp = common.UnzipTemp(args.target_files)\n\n  info_dict = common.LoadInfoDict(input_tmp)\n  with zipfile.ZipFile(args.target_files, 'r', allowZip64=True) as input_zip:\n    ValidateFileConsistency(input_zip, input_tmp, info_dict)\n\n  CheckBuildPropDuplicity(input_tmp)\n\n  ValidateInstallRecoveryScript(input_tmp, info_dict)\n\n  ValidateVerifiedBootImages(input_tmp, info_dict, options)\n\n  # TODO: Check if the OTA keys have been properly updated (the ones on /system,\n  # in recovery image).\n\n  logging.info(\"Done.\")\n\n\nif __name__ == '__main__':\n  try:\n    main()\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/releasetools/verity_utils.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nSigns a given image using avbtool\n\nUsage:  verity_utils properties_file output_image\n\"\"\"\n\nfrom __future__ import print_function\n\nimport logging\nimport os.path\nimport shlex\nimport struct\nimport sys\n\nimport common\nimport sparse_img\nfrom rangelib import RangeSet\nfrom hashlib import sha256\n\nlogger = logging.getLogger(__name__)\n\nOPTIONS = common.OPTIONS\nBLOCK_SIZE = common.BLOCK_SIZE\nFIXED_SALT = \"aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7\"\n\n# From external/avb/avbtool.py\nMAX_VBMETA_SIZE = 64 * 1024\nMAX_FOOTER_SIZE = 4096\n\n\nclass BuildVerityImageError(Exception):\n  \"\"\"An Exception raised during verity image building.\"\"\"\n\n  def __init__(self, message):\n    Exception.__init__(self, message)\n\n\ndef CreateVerityImageBuilder(prop_dict):\n  \"\"\"Returns a verity image builder based on the given build properties.\n\n  Args:\n    prop_dict: A dict that contains the build properties. In particular, it will\n        look for verity-related property values.\n\n  Returns:\n    A VerityImageBuilder instance for Verified Boot 1.0 or Verified Boot 2.0; or\n        None if the given build doesn't support Verified Boot.\n  \"\"\"\n  partition_size = prop_dict.get(\"partition_size\")\n  # partition_size could be None at this point, if using dynamic partitions.\n  if partition_size:\n    partition_size = int(partition_size)\n  # Set up the salt (based on fingerprint) that will be used when adding AVB\n  # hash / hashtree footers.\n  salt = prop_dict.get(\"avb_salt\")\n  if salt is None:\n    salt = sha256(prop_dict.get(\"fingerprint\", \"\").encode()).hexdigest()\n\n  # Verified Boot 2.0\n  if (prop_dict.get(\"avb_hash_enable\") == \"true\" or\n      prop_dict.get(\"avb_hashtree_enable\") == \"true\"):\n    # key_path and algorithm are only available when chain partition is used.\n    key_path = prop_dict.get(\"avb_key_path\")\n    algorithm = prop_dict.get(\"avb_algorithm\")\n\n    # Image uses hash footer.\n    if prop_dict.get(\"avb_hash_enable\") == \"true\":\n      return VerifiedBootVersion2VerityImageBuilder(\n          prop_dict[\"partition_name\"],\n          partition_size,\n          VerifiedBootVersion2VerityImageBuilder.AVB_HASH_FOOTER,\n          prop_dict[\"avb_avbtool\"],\n          key_path,\n          algorithm,\n          salt,\n          prop_dict[\"avb_add_hash_footer_args\"])\n\n    # Image uses hashtree footer.\n    return VerifiedBootVersion2VerityImageBuilder(\n        prop_dict[\"partition_name\"],\n        partition_size,\n        VerifiedBootVersion2VerityImageBuilder.AVB_HASHTREE_FOOTER,\n        prop_dict[\"avb_avbtool\"],\n        key_path,\n        algorithm,\n        salt,\n        prop_dict[\"avb_add_hashtree_footer_args\"])\n\n  return None\n\n\nclass VerityImageBuilder(object):\n  \"\"\"A builder that generates an image with verity metadata for Verified Boot.\n\n  A VerityImageBuilder instance handles the works for building an image with\n  verity metadata for supporting Android Verified Boot. This class defines the\n  common interface between Verified Boot 1.0 and Verified Boot 2.0. A matching\n  builder will be returned based on the given build properties.\n\n  More info on the verity image generation can be found at the following link.\n  https://source.android.com/security/verifiedboot/dm-verity#implementation\n  \"\"\"\n\n  def CalculateMaxImageSize(self, partition_size):\n    \"\"\"Calculates the filesystem image size for the given partition size.\"\"\"\n    raise NotImplementedError\n\n  def CalculateDynamicPartitionSize(self, image_size):\n    \"\"\"Calculates and sets the partition size for a dynamic partition.\"\"\"\n    raise NotImplementedError\n\n  def PadSparseImage(self, out_file):\n    \"\"\"Adds padding to the generated sparse image.\"\"\"\n    raise NotImplementedError\n\n  def Build(self, out_file):\n    \"\"\"Builds the verity image and writes it to the given file.\"\"\"\n    raise NotImplementedError\n\n\nclass VerifiedBootVersion2VerityImageBuilder(VerityImageBuilder):\n  \"\"\"A VerityImageBuilder for Verified Boot 2.0.\"\"\"\n\n  AVB_HASH_FOOTER = 1\n  AVB_HASHTREE_FOOTER = 2\n\n  def __init__(self, partition_name, partition_size, footer_type, avbtool,\n               key_path, algorithm, salt, signing_args):\n    self.version = 2\n    self.partition_name = partition_name\n    self.partition_size = partition_size\n    self.footer_type = footer_type\n    self.avbtool = avbtool\n    self.algorithm = algorithm\n    self.key_path = common.ResolveAVBSigningPathArgs(key_path)\n\n    self.salt = salt\n    self.signing_args = signing_args\n    self.image_size = None\n\n  def CalculateMinPartitionSize(self, image_size, size_calculator=None):\n    \"\"\"Calculates min partition size for a given image size.\n\n    This is used when determining the partition size for a dynamic partition,\n    which should be cover the given image size (for filesystem files) as well as\n    the verity metadata size.\n\n    Args:\n      image_size: The size of the image in question.\n      size_calculator: The function to calculate max image size\n          for a given partition size.\n\n    Returns:\n      The minimum partition size required to accommodate the image size.\n    \"\"\"\n    if size_calculator is None:\n      size_calculator = self.CalculateMaxImageSize\n\n    # Use image size as partition size to approximate final partition size.\n    image_ratio = size_calculator(image_size) / float(image_size)\n\n    # Prepare a binary search for the optimal partition size.\n    lo = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE - BLOCK_SIZE\n\n    # Ensure lo is small enough: max_image_size should <= image_size.\n    delta = BLOCK_SIZE\n    max_image_size = size_calculator(lo)\n    while max_image_size > image_size:\n      image_ratio = max_image_size / float(lo)\n      lo = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE - delta\n      delta *= 2\n      max_image_size = size_calculator(lo)\n\n    hi = lo + BLOCK_SIZE\n\n    # Ensure hi is large enough: max_image_size should >= image_size.\n    delta = BLOCK_SIZE\n    max_image_size = size_calculator(hi)\n    while max_image_size < image_size:\n      image_ratio = max_image_size / float(hi)\n      hi = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE + delta\n      delta *= 2\n      max_image_size = size_calculator(hi)\n\n    partition_size = hi\n\n    # Start to binary search.\n    while lo < hi:\n      mid = ((lo + hi) // (2 * BLOCK_SIZE)) * BLOCK_SIZE\n      max_image_size = size_calculator(mid)\n      if max_image_size >= image_size:  # if mid can accommodate image_size\n        if mid < partition_size:  # if a smaller partition size is found\n          partition_size = mid\n        hi = mid\n      else:\n        lo = mid + BLOCK_SIZE\n\n    logger.info(\n        \"CalculateMinPartitionSize(%d): partition_size %d.\", image_size,\n        partition_size)\n\n    return partition_size\n\n  def CalculateDynamicPartitionSize(self, image_size):\n    self.partition_size = self.CalculateMinPartitionSize(image_size)\n    return self.partition_size\n\n  def CalculateMaxImageSize(self, partition_size=None):\n    \"\"\"Calculates max image size for a given partition size.\n\n    Args:\n      partition_size: The partition size, which defaults to self.partition_size\n          if unspecified.\n\n    Returns:\n      The maximum image size.\n\n    Raises:\n      BuildVerityImageError: On error or getting invalid image size.\n    \"\"\"\n    if partition_size is None:\n      partition_size = self.partition_size\n    assert partition_size > 0, \\\n        \"Invalid partition size: {}\".format(partition_size)\n\n    add_footer = (\"add_hash_footer\" if self.footer_type == self.AVB_HASH_FOOTER\n                  else \"add_hashtree_footer\")\n    cmd = [self.avbtool, add_footer, \"--partition_size\",\n           str(partition_size), \"--calc_max_image_size\"]\n    cmd.extend(shlex.split(self.signing_args))\n\n    proc = common.Run(cmd)\n    output, _ = proc.communicate()\n    if proc.returncode != 0:\n      raise BuildVerityImageError(\n          \"Failed to calculate max image size:\\n{}\".format(output))\n    image_size = int(output)\n    if image_size <= 0:\n      raise BuildVerityImageError(\n          \"Invalid max image size: {}\".format(output))\n    self.image_size = image_size\n    return image_size\n\n  def PadSparseImage(self, out_file):\n    # No-op as the padding is taken care of by avbtool.\n    pass\n\n  def Build(self, out_file):\n    \"\"\"Adds dm-verity hashtree and AVB metadata to an image.\n\n    Args:\n      out_file: Path to image to modify.\n    \"\"\"\n    add_footer = (\"add_hash_footer\" if self.footer_type == self.AVB_HASH_FOOTER\n                  else \"add_hashtree_footer\")\n    cmd = [self.avbtool, add_footer,\n           \"--partition_size\", str(self.partition_size),\n           \"--partition_name\", self.partition_name,\n           \"--image\", out_file]\n    if self.key_path and self.algorithm:\n      cmd.extend([\"--key\", self.key_path, \"--algorithm\", self.algorithm])\n    if self.salt:\n      cmd.extend([\"--salt\", self.salt])\n    cmd.extend(shlex.split(self.signing_args))\n\n    proc = common.Run(cmd)\n    output, _ = proc.communicate()\n    if proc.returncode != 0:\n      raise BuildVerityImageError(\"Failed to add AVB footer: {}\".format(output))\n\n\ndef CreateCustomImageBuilder(info_dict, partition_name, partition_size,\n                             key_path, algorithm, signing_args):\n  builder = None\n  if info_dict.get(\"avb_enable\") == \"true\":\n    builder = VerifiedBootVersion2VerityImageBuilder(\n        partition_name,\n        partition_size,\n        VerifiedBootVersion2VerityImageBuilder.AVB_HASHTREE_FOOTER,\n        info_dict.get(\"avb_avbtool\"),\n        key_path,\n        algorithm,\n        # Salt is None because custom images have no fingerprint property to be\n        # used as the salt.\n        None,\n        signing_args)\n\n  return builder\n\n\ndef GetDiskUsage(path):\n  \"\"\"Returns the number of bytes that \"path\" occupies on host.\n\n  Args:\n    path: The directory or file to calculate size on.\n\n  Returns:\n    The number of bytes based on a 1K block_size.\n  \"\"\"\n  cmd = [\"du\", \"-b\", \"-k\", \"-s\", path]\n  output = common.RunAndCheckOutput(cmd, verbose=False)\n  return int(output.split()[0]) * 1024\n\n\ndef CalculateVbmetaDigest(extracted_dir, avbtool):\n  \"\"\"Calculates the vbmeta digest of the images in the extracted target_file\"\"\"\n\n  images_dir = common.MakeTempDir()\n  for name in (\"PREBUILT_IMAGES\", \"RADIO\", \"IMAGES\"):\n    path = os.path.join(extracted_dir, name)\n    if not os.path.exists(path):\n      continue\n\n    # Create symlink for image files under PREBUILT_IMAGES, RADIO and IMAGES,\n    # and put them into one directory.\n    for filename in os.listdir(path):\n      if not filename.endswith(\".img\"):\n        continue\n      symlink_path = os.path.join(images_dir, filename)\n      # The files in latter directory overwrite the existing links\n      common.RunAndCheckOutput(\n        ['ln', '-sf', os.path.join(path, filename), symlink_path])\n\n  cmd = [avbtool, \"calculate_vbmeta_digest\", \"--image\",\n         os.path.join(images_dir, 'vbmeta.img')]\n  return common.RunAndCheckOutput(cmd)\n\n\ndef main(argv):\n  if len(argv) != 2:\n    print(__doc__)\n    sys.exit(1)\n\n  common.InitLogging()\n\n  dict_file = argv[0]\n  out_file = argv[1]\n\n  prop_dict = {}\n  with open(dict_file, 'r') as f:\n    for line in f:\n      line = line.strip()\n      if not line or line.startswith(\"#\"):\n        continue\n      k, v = line.split(\"=\", 1)\n      prop_dict[k] = v\n\n  builder = CreateVerityImageBuilder(prop_dict)\n\n  if \"partition_size\" not in prop_dict:\n    image_size = GetDiskUsage(out_file)\n    # make sure that the image is big enough to hold vbmeta and footer\n    image_size = image_size + (MAX_VBMETA_SIZE + MAX_FOOTER_SIZE)\n    size = builder.CalculateDynamicPartitionSize(image_size)\n    prop_dict[\"partition_size\"] = size\n\n  builder.Build(out_file)\n\n\nif __name__ == '__main__':\n  try:\n    main(sys.argv[1:])\n  finally:\n    common.Cleanup()\n"
  },
  {
    "path": "tools/sbom/Android.bp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\npython_binary_host {\n    name: \"generate-sbom\",\n    srcs: [\n        \"generate-sbom.py\",\n    ],\n    libs: [\n        \"metadata_file_proto_py\",\n        \"libprotobuf-python\",\n        \"sbom_lib\",\n    ],\n}\n\npython_library_host {\n    name: \"compliance_metadata\",\n    srcs: [\n        \"compliance_metadata.py\",\n    ],\n}\n\npython_binary_host {\n    name: \"gen_sbom\",\n    srcs: [\n        \"gen_sbom.py\",\n    ],\n    libs: [\n        \"compliance_metadata\",\n        \"metadata_file_proto_py\",\n        \"libprotobuf-python\",\n        \"sbom_lib\",\n    ],\n}\n\npython_library_host {\n    name: \"sbom_lib\",\n    srcs: [\n        \"sbom_data.py\",\n        \"sbom_writers.py\",\n    ],\n}\n\npython_test_host {\n    name: \"sbom_writers_test\",\n    main: \"sbom_writers_test.py\",\n    srcs: [\n        \"sbom_writers_test.py\",\n    ],\n    data: [\n        \"testdata/*\",\n    ],\n    libs: [\n        \"sbom_lib\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\npython_test_host {\n    name: \"sbom_data_test\",\n    main: \"sbom_data_test.py\",\n    srcs: [\n        \"sbom_data_test.py\",\n    ],\n    libs: [\n        \"sbom_lib\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\npython_binary_host {\n    name: \"generate-sbom-framework_res\",\n    srcs: [\n        \"generate-sbom-framework_res.py\",\n    ],\n    libs: [\n        \"sbom_lib\",\n    ],\n}\n\npython_binary_host {\n    name: \"gen_notice_xml\",\n    srcs: [\n        \"gen_notice_xml.py\",\n    ],\n    libs: [\n        \"compliance_metadata\",\n        \"metadata_file_proto_py\",\n    ],\n}\n"
  },
  {
    "path": "tools/sbom/compliance_metadata.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport sqlite3\n\nclass MetadataDb:\n  def __init__(self, db):\n    self.conn = sqlite3.connect(':memory:')\n    self.conn.row_factory = sqlite3.Row\n    with sqlite3.connect(db) as c:\n      c.backup(self.conn)\n    self.reorg()\n\n  def reorg(self):\n    # package_license table\n    self.conn.execute(\"create table package_license as \"\n                      \"select name as package, pkg_default_applicable_licenses as license \"\n                      \"from modules \"\n                      \"where module_type = 'package' \")\n    cursor = self.conn.execute(\"select package,license from package_license where license like '% %'\")\n    multi_licenses_packages = cursor.fetchall()\n    cursor.close()\n    rows = []\n    for p in multi_licenses_packages:\n      licenses = p['license'].strip().split(' ')\n      for lic in licenses:\n        rows.append((p['package'], lic))\n    self.conn.executemany('insert into package_license values (?, ?)', rows)\n    self.conn.commit()\n\n    self.conn.execute(\"delete from package_license where license like '% %'\")\n    self.conn.commit()\n\n    # module_license table\n    self.conn.execute(\"create table module_license as \"\n                      \"select distinct name as module, package, licenses as license \"\n                      \"from modules \"\n                      \"where licenses != '' \")\n    cursor = self.conn.execute(\"select module,package,license from module_license where license like '% %'\")\n    multi_licenses_modules = cursor.fetchall()\n    cursor.close()\n    rows = []\n    for m in multi_licenses_modules:\n      licenses = m['license'].strip().split(' ')\n      for lic in licenses:\n        rows.append((m['module'], m['package'],lic))\n    self.conn.executemany('insert into module_license values (?, ?, ?)', rows)\n    self.conn.commit()\n\n    self.conn.execute(\"delete from module_license where license like '% %'\")\n    self.conn.commit()\n\n    # module_installed_file table\n    self.conn.execute(\"create table module_installed_file as \"\n                      \"select id as module_id, name as module_name, package, installed_files as installed_file \"\n                      \"from modules \"\n                      \"where installed_files != '' \")\n    cursor = self.conn.execute(\"select module_id, module_name, package, installed_file \"\n                               \"from module_installed_file where installed_file like '% %'\")\n    multi_installed_file_modules = cursor.fetchall()\n    cursor.close()\n    rows = []\n    for m in multi_installed_file_modules:\n      installed_files = m['installed_file'].strip().split(' ')\n      for f in installed_files:\n        rows.append((m['module_id'], m['module_name'], m['package'], f))\n    self.conn.executemany('insert into module_installed_file values (?, ?, ?, ?)', rows)\n    self.conn.commit()\n\n    self.conn.execute(\"delete from module_installed_file where installed_file like '% %'\")\n    self.conn.commit()\n\n    # module_built_file table\n    self.conn.execute(\"create table module_built_file as \"\n                      \"select id as module_id, name as module_name, package, built_files as built_file \"\n                      \"from modules \"\n                      \"where built_files != '' \")\n    cursor = self.conn.execute(\"select module_id, module_name, package, built_file \"\n                               \"from module_built_file where built_file like '% %'\")\n    multi_built_file_modules = cursor.fetchall()\n    cursor.close()\n    rows = []\n    for m in multi_built_file_modules:\n      built_files = m['built_file'].strip().split(' ')\n      for f in built_files:\n        rows.append((m['module_id'], m['module_name'], m['package'], f))\n    self.conn.executemany('insert into module_built_file values (?, ?, ?, ?)', rows)\n    self.conn.commit()\n\n    self.conn.execute(\"delete from module_built_file where built_file like '% %'\")\n    self.conn.commit()\n\n\n    # Indexes\n    self.conn.execute('create index idx_modules_id on modules (id)')\n    self.conn.execute('create index idx_modules_name on modules (name)')\n    self.conn.execute('create index idx_package_licnese_package on package_license (package)')\n    self.conn.execute('create index idx_package_licnese_license on package_license (license)')\n    self.conn.execute('create index idx_module_licnese_module on module_license (module)')\n    self.conn.execute('create index idx_module_licnese_license on module_license (license)')\n    self.conn.execute('create index idx_module_installed_file_module_id on module_installed_file (module_id)')\n    self.conn.execute('create index idx_module_installed_file_installed_file on module_installed_file (installed_file)')\n    self.conn.execute('create index idx_module_built_file_module_id on module_built_file (module_id)')\n    self.conn.execute('create index idx_module_built_file_built_file on module_built_file (built_file)')\n    self.conn.commit()\n\n  def dump_debug_db(self, debug_db):\n    with sqlite3.connect(debug_db) as c:\n      self.conn.backup(c)\n\n  def get_installed_files(self):\n    # Get all records from table make_metadata, which contains all installed files and corresponding make modules' metadata\n    cursor = self.conn.execute('select installed_file, module_path, is_soong_module, is_prebuilt_make_module, product_copy_files, kernel_module_copy_files, is_platform_generated, license_text from make_metadata')\n    rows = cursor.fetchall()\n    cursor.close()\n    installed_files_metadata = []\n    for row in rows:\n      metadata = dict(zip(row.keys(), row))\n      installed_files_metadata.append(metadata)\n    return installed_files_metadata\n\n  def get_installed_file_in_dir(self, dir):\n    dir = dir.removesuffix('/')\n    cursor = self.conn.execute(\n        'select installed_file, module_path, is_soong_module, is_prebuilt_make_module, product_copy_files, '\n        '       kernel_module_copy_files, is_platform_generated, license_text '\n        'from make_metadata '\n        'where installed_file like ?', (dir + '/%',))\n    rows = cursor.fetchall()\n    cursor.close()\n    installed_files_metadata = []\n    for row in rows:\n      metadata = dict(zip(row.keys(), row))\n      installed_files_metadata.append(metadata)\n    return installed_files_metadata\n\n  def get_soong_modules(self):\n    # Get all records from table modules, which contains metadata of all soong modules\n    cursor = self.conn.execute('select name, package, package as module_path, module_type as soong_module_type, built_files, installed_files, static_dep_files, whole_static_dep_files from modules')\n    rows = cursor.fetchall()\n    cursor.close()\n    soong_modules = []\n    for row in rows:\n      soong_module = dict(zip(row.keys(), row))\n      soong_modules.append(soong_module)\n    return soong_modules\n\n  def get_package_licenses(self, package):\n    cursor = self.conn.execute('select m.name, m.package, m.lic_license_text as license_text '\n                               'from package_license pl join modules m on pl.license = m.name '\n                               'where pl.package = ?',\n                               ('//' + package,))\n    rows = cursor.fetchall()\n    licenses = {}\n    for r in rows:\n      licenses[r['name']] = r['license_text']\n    return licenses\n\n  def get_module_licenses(self, module_name, package):\n    licenses = {}\n    # If property \"licenses\" is defined on module\n    cursor = self.conn.execute('select m.name, m.package, m.lic_license_text as license_text '\n                               'from module_license ml join modules m on ml.license = m.name '\n                               'where ml.module = ? and ml.package = ?',\n                               (module_name, package))\n    rows = cursor.fetchall()\n    for r in rows:\n      licenses[r['name']] = r['license_text']\n    if len(licenses) > 0:\n      return licenses\n\n    # Use default package license\n    cursor = self.conn.execute('select m.name, m.package, m.lic_license_text as license_text '\n                               'from package_license pl join modules m on pl.license = m.name '\n                               'where pl.package = ?',\n                               ('//' + package,))\n    rows = cursor.fetchall()\n    for r in rows:\n      licenses[r['name']] = r['license_text']\n    return licenses\n\n  def get_soong_module_of_installed_file(self, installed_file):\n    cursor = self.conn.execute('select name, m.package, m.package as module_path, module_type as soong_module_type, built_files, installed_files, static_dep_files, whole_static_dep_files '\n                               'from modules m join module_installed_file mif on m.id = mif.module_id '\n                               'where mif.installed_file = ?',\n                               (installed_file,))\n    rows = cursor.fetchall()\n    cursor.close()\n    if rows:\n      soong_module = dict(zip(rows[0].keys(), rows[0]))\n      return soong_module\n\n    return None\n\n  def get_soong_module_of_built_file(self, built_file):\n    cursor = self.conn.execute('select name, m.package, m.package as module_path, module_type as soong_module_type, built_files, installed_files, static_dep_files, whole_static_dep_files '\n                               'from modules m join module_built_file mbf on m.id = mbf.module_id '\n                               'where mbf.built_file = ?',\n                               (built_file,))\n    rows = cursor.fetchall()\n    cursor.close()\n    if rows:\n      soong_module = dict(zip(rows[0].keys(), rows[0]))\n      return soong_module\n\n    return None"
  },
  {
    "path": "tools/sbom/gen_notice_xml.py",
    "content": "# !/usr/bin/env python3\n#\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nGenerate NOTICE.xml.gz of a partition.\nUsage example:\n  gen_notice_xml.py --output_file out/soong/.intermediate/.../NOTICE.xml.gz \\\n              --metadata out/soong/compliance-metadata/aosp_cf_x86_64_phone/compliance-metadata.db \\\n              --partition system \\\n              --product_out out/target/vsoc_x86_64 \\\n              --soong_out out/soong\n\"\"\"\n\nimport argparse\nimport compliance_metadata\nimport google.protobuf.text_format as text_format\nimport gzip\nimport hashlib\nimport metadata_file_pb2\nimport os\nimport queue\nimport xml.sax.saxutils\n\n\nFILE_HEADER = '''\\\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<licenses>\n'''\nFILE_FOOTER = '''\\\n</licenses>\n'''\n\n\ndef get_args():\n  parser = argparse.ArgumentParser()\n  parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Print more information.')\n  parser.add_argument('-d', '--debug', action='store_true', default=False, help='Debug mode')\n  parser.add_argument('--output_file', required=True, help='The path of the generated NOTICE.xml.gz file.')\n  parser.add_argument('--partition', required=True, help='The name of partition for which the NOTICE.xml.gz is generated.')\n  parser.add_argument('--metadata', required=True, help='The path of compliance metadata DB file.')\n  parser.add_argument('--product_out', required=True, help='The path of PRODUCT_OUT, e.g. out/target/product/vsoc_x86_64.')\n  parser.add_argument('--soong_out', required=True, help='The path of Soong output directory, e.g. out/soong')\n\n  return parser.parse_args()\n\n\ndef log(*info):\n  if args.verbose:\n    for i in info:\n      print(i)\n\n\ndef new_file_name_tag(file_metadata, package_name, content_id):\n  file_path = file_metadata['installed_file'].removeprefix(args.product_out)\n  lib = 'Android'\n  if package_name:\n    lib = package_name\n  return f'<file-name contentId=\"{content_id}\" lib=\"{lib}\">{file_path}</file-name>\\n'\n\n\ndef new_file_content_tag(content_id, license_text):\n  escaped_license_text = xml.sax.saxutils.escape(license_text, {'\\t': '&#x9;', '\\n': '&#xA;', '\\r': '&#xD;'})\n  return f'<file-content contentId=\"{content_id}\"><![CDATA[{escaped_license_text}]]></file-content>\\n\\n'\n\ndef get_metadata_file_path(file_metadata):\n  \"\"\"Search for METADATA file of a package and return its path.\"\"\"\n  metadata_path = ''\n  if file_metadata['module_path']:\n    metadata_path = file_metadata['module_path']\n  elif file_metadata['kernel_module_copy_files']:\n    metadata_path = os.path.dirname(file_metadata['kernel_module_copy_files'].split(':')[0])\n\n  while metadata_path and not os.path.exists(metadata_path + '/METADATA'):\n    metadata_path = os.path.dirname(metadata_path)\n\n  return metadata_path\n\ndef md5_file_content(filepath):\n  h = hashlib.md5()\n  with open(filepath, 'rb') as f:\n    h.update(f.read())\n  return h.hexdigest()\n\ndef get_transitive_static_dep_modules(installed_file_metadata, db):\n  # Find all transitive static dep files of the installed files\n  q = queue.Queue()\n  if installed_file_metadata['static_dep_files']:\n    for f in installed_file_metadata['static_dep_files'].split(' '):\n      q.put(f)\n  if installed_file_metadata['whole_static_dep_files']:\n    for f in installed_file_metadata['whole_static_dep_files'].split(' '):\n      q.put(f)\n\n  static_dep_files = {}\n  while not q.empty():\n    dep_file = q.get()\n    if dep_file in static_dep_files:\n      # It has been processed\n      continue\n\n    soong_module = db.get_soong_module_of_built_file(dep_file)\n    if not soong_module:\n      continue\n\n    static_dep_files[dep_file] = soong_module\n\n    if soong_module['static_dep_files']:\n      for f in soong_module['static_dep_files'].split(' '):\n        if f not in static_dep_files:\n          q.put(f)\n    if soong_module['whole_static_dep_files']:\n      for f in soong_module['whole_static_dep_files'].split(' '):\n        if f not in static_dep_files:\n          q.put(f)\n\n  return static_dep_files.values()\n\ndef main():\n  global args\n  args = get_args()\n  log('Args:', vars(args))\n\n  global db\n  db = compliance_metadata.MetadataDb(args.metadata)\n  if args.debug:\n    db.dump_debug_db(os.path.dirname(args.output_file) + '/compliance-metadata-debug.db')\n\n  # NOTICE.xml\n  notice_xml_file_path = os.path.dirname(args.output_file) + '/NOTICE.xml'\n  with open(notice_xml_file_path, 'w', encoding=\"utf-8\") as notice_xml_file:\n    notice_xml_file.write(FILE_HEADER)\n\n    all_license_files = {}\n    for metadata in db.get_installed_file_in_dir(args.product_out + '/' + args.partition):\n      soong_module = db.get_soong_module_of_installed_file(metadata['installed_file'])\n      if soong_module:\n        metadata.update(soong_module)\n      else:\n        # For make modules soong_module_type should be empty\n        metadata['soong_module_type'] = ''\n        metadata['static_dep_files'] = ''\n        metadata['whole_static_dep_files'] = ''\n\n      installed_file_metadata_list = [metadata]\n      if args.partition in ('vendor', 'product', 'system_ext'):\n        # For transitive static dependencies of an installed file, make it as if an installed file are\n        # also created from static dependency modules whose licenses are also collected\n        static_dep_modules = get_transitive_static_dep_modules(metadata, db)\n        for dep in static_dep_modules:\n          dep['installed_file'] = metadata['installed_file']\n          installed_file_metadata_list.append(dep)\n\n      for installed_file_metadata in installed_file_metadata_list:\n        package_name = 'Android'\n        licenses = {}\n        if installed_file_metadata['module_path']:\n          metadata_file_path = get_metadata_file_path(installed_file_metadata)\n          if metadata_file_path:\n            proto = metadata_file_pb2.Metadata()\n            with open(metadata_file_path + '/METADATA', 'rt') as f:\n              text_format.Parse(f.read(), proto)\n            if proto.name:\n              package_name = proto.name\n              if proto.third_party and proto.third_party.version:\n                if proto.third_party.version.startswith('v'):\n                  package_name = package_name + '_' + proto.third_party.version\n                else:\n                  package_name = package_name + '_v_' + proto.third_party.version\n            else:\n              package_name = metadata_file_path\n              if metadata_file_path.startswith('external/'):\n                package_name = metadata_file_path.removeprefix('external/')\n\n          # Every license file is in a <file-content> element\n          licenses = db.get_module_licenses(installed_file_metadata.get('name', ''), installed_file_metadata['module_path'])\n\n        # Installed file is from PRODUCT_COPY_FILES\n        elif metadata['product_copy_files']:\n          licenses['unused_name'] = metadata['license_text']\n\n        # Installed file is generated by the platform in builds\n        elif metadata['is_platform_generated']:\n          licenses['unused_name'] = metadata['license_text']\n\n        if licenses:\n          # Each value is a space separated filepath list\n          for license_files in licenses.values():\n            if not license_files:\n              continue\n            for filepath in license_files.split(' '):\n              if filepath not in all_license_files:\n                all_license_files[filepath] = md5_file_content(filepath)\n              md5 = all_license_files[filepath]\n              notice_xml_file.write(new_file_name_tag(installed_file_metadata, package_name, md5))\n\n    # Licenses\n    processed_md5 = []\n    for filepath, md5 in all_license_files.items():\n      if md5 not in processed_md5:\n        processed_md5.append(md5)\n        with open(filepath, 'rt', errors='backslashreplace') as f:\n          notice_xml_file.write(new_file_content_tag(md5, f.read()))\n\n    notice_xml_file.write(FILE_FOOTER)\n\n  # NOTICE.xml.gz\n  with open(notice_xml_file_path, 'rb') as notice_xml_file, gzip.open(args.output_file, 'wb') as gz_file:\n    gz_file.writelines(notice_xml_file)\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tools/sbom/gen_sbom.py",
    "content": "# !/usr/bin/env python3\n#\n# Copyright (C) 2024 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nGenerate the SBOM of the current target product in SPDX format.\nUsage example:\n  gen_sbom.py --output_file out/soong/sbom/aosp_cf_x86_64_phone/sbom.spdx \\\n              --metadata out/soong/metadata/aosp_cf_x86_64_phone/metadata.db \\\n              --product_out out/target/vsoc_x86_64\n              --soong_out out/soong\n              --build_version $(cat out/target/product/vsoc_x86_64/build_fingerprint.txt) \\\n              --product_mfr=Google\n\"\"\"\n\nimport argparse\nimport compliance_metadata\nimport datetime\nimport google.protobuf.text_format as text_format\nimport hashlib\nimport os\nimport pathlib\nimport queue\nimport metadata_file_pb2\nimport sbom_data\nimport sbom_writers\n\n# Package type\nPKG_SOURCE = 'SOURCE'\nPKG_UPSTREAM = 'UPSTREAM'\nPKG_PREBUILT = 'PREBUILT'\n\n# Security tag\nNVD_CPE23 = 'NVD-CPE2.3:'\n\n# Report\nISSUE_NO_METADATA = 'No metadata generated in Make for installed files:'\nISSUE_NO_METADATA_FILE = 'No METADATA file found for installed file:'\nISSUE_METADATA_FILE_INCOMPLETE = 'METADATA file incomplete:'\nISSUE_UNKNOWN_SECURITY_TAG_TYPE = 'Unknown security tag type:'\nISSUE_INSTALLED_FILE_NOT_EXIST = 'Non-existent installed files:'\nISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP = 'No module found for static dependency files:'\nINFO_METADATA_FOUND_FOR_PACKAGE = 'METADATA file found for packages:'\n\nSOONG_PREBUILT_MODULE_TYPES = [\n    'android_app_import',\n    'android_library_import',\n    'cc_prebuilt_binary',\n    'cc_prebuilt_library',\n    'cc_prebuilt_library_headers',\n    'cc_prebuilt_library_shared',\n    'cc_prebuilt_library_static',\n    'cc_prebuilt_object',\n    'dex_import',\n    'java_import',\n    'java_sdk_library_import',\n    'java_system_modules_import',\n    'libclang_rt_prebuilt_library_static',\n    'libclang_rt_prebuilt_library_shared',\n    'llvm_prebuilt_library_static',\n    'ndk_prebuilt_object',\n    'ndk_prebuilt_shared_stl',\n    'nkd_prebuilt_static_stl',\n    'prebuilt_apex',\n    'prebuilt_bootclasspath_fragment',\n    'prebuilt_dsp',\n    'prebuilt_firmware',\n    'prebuilt_kernel_modules',\n    'prebuilt_rfsa',\n    'prebuilt_root',\n    'rust_prebuilt_dylib',\n    'rust_prebuilt_library',\n    'rust_prebuilt_rlib',\n    'vndk_prebuilt_shared',\n]\n\nTHIRD_PARTY_IDENTIFIER_TYPES = [\n    # Types defined in metadata_file.proto\n    'Git',\n    'SVN',\n    'Hg',\n    'Darcs',\n    'Piper',\n    'VCS',\n    'Archive',\n    'PrebuiltByAlphabet',\n    'LocalSource',\n    'Other',\n    # OSV ecosystems defined at https://ossf.github.io/osv-schema/#affectedpackage-field.\n    'Go',\n    'npm',\n    'OSS-Fuzz',\n    'PyPI',\n    'RubyGems',\n    'crates.io',\n    'Hackage',\n    'GHC',\n    'Packagist',\n    'Maven',\n    'NuGet',\n    'Linux',\n    'Debian',\n    'Alpine',\n    'Hex',\n    'Android',\n    'GitHub Actions',\n    'Pub',\n    'ConanCenter',\n    'Rocky Linux',\n    'AlmaLinux',\n    'Bitnami',\n    'Photon OS',\n    'CRAN',\n    'Bioconductor',\n    'SwiftURL'\n]\n\n\ndef get_args():\n  parser = argparse.ArgumentParser()\n  parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Print more information.')\n  parser.add_argument('-d', '--debug', action='store_true', default=False, help='Debug mode')\n  parser.add_argument('--output_file', required=True, help='The generated SBOM file in SPDX format.')\n  parser.add_argument('--metadata', required=True, help='The metadata DB file path.')\n  parser.add_argument('--product_out', required=True, help='The path of PRODUCT_OUT, e.g. out/target/product/vsoc_x86_64.')\n  parser.add_argument('--soong_out', required=True, help='The path of Soong output directory, e.g. out/soong')\n  parser.add_argument('--build_version', required=True, help='The build version.')\n  parser.add_argument('--product_mfr', required=True, help='The product manufacturer.')\n  parser.add_argument('--json', action='store_true', default=False, help='Generated SBOM file in SPDX JSON format')\n\n  return parser.parse_args()\n\n\ndef log(*info):\n  if args.verbose:\n    for i in info:\n      print(i)\n\n\ndef new_package_id(package_name, type):\n  return f'SPDXRef-{type}-{sbom_data.encode_for_spdxid(package_name)}'\n\n\ndef new_file_id(file_path):\n  return f'SPDXRef-{sbom_data.encode_for_spdxid(file_path)}'\n\n\ndef new_license_id(license_name):\n  return f'LicenseRef-{sbom_data.encode_for_spdxid(license_name)}'\n\n\ndef checksum(file_path):\n  h = hashlib.sha1()\n  if os.path.islink(file_path):\n    h.update(os.readlink(file_path).encode('utf-8'))\n  else:\n    with open(file_path, 'rb') as f:\n      h.update(f.read())\n  return f'SHA1: {h.hexdigest()}'\n\n\ndef is_soong_prebuilt_module(file_metadata):\n  return (file_metadata['soong_module_type'] and\n          file_metadata['soong_module_type'] in SOONG_PREBUILT_MODULE_TYPES)\n\n\ndef is_source_package(file_metadata):\n  module_path = file_metadata['module_path']\n  return module_path.startswith('external/') and not is_prebuilt_package(file_metadata)\n\n\ndef is_prebuilt_package(file_metadata):\n  module_path = file_metadata['module_path']\n  if module_path:\n    return (module_path.startswith('prebuilts/') or\n            is_soong_prebuilt_module(file_metadata) or\n            file_metadata['is_prebuilt_make_module'])\n\n  kernel_module_copy_files = file_metadata['kernel_module_copy_files']\n  if kernel_module_copy_files and not kernel_module_copy_files.startswith('ANDROID-GEN:'):\n    return True\n\n  return False\n\n\ndef get_source_package_info(file_metadata, metadata_file_path):\n  \"\"\"Return source package info exists in its METADATA file, currently including name, security tag\n  and external SBOM reference.\n\n  See go/android-spdx and go/android-sbom-gen for more details.\n  \"\"\"\n  if not metadata_file_path:\n    return file_metadata['module_path'], []\n\n  metadata_proto = metadata_file_protos[metadata_file_path]\n  external_refs = []\n  for tag in metadata_proto.third_party.security.tag:\n    if tag.lower().startswith((NVD_CPE23 + 'cpe:2.3:').lower()):\n      external_refs.append(\n          sbom_data.PackageExternalRef(category=sbom_data.PackageExternalRefCategory.SECURITY,\n                                       type=sbom_data.PackageExternalRefType.cpe23Type,\n                                       locator=tag.removeprefix(NVD_CPE23)))\n    elif tag.lower().startswith((NVD_CPE23 + 'cpe:/').lower()):\n      external_refs.append(\n          sbom_data.PackageExternalRef(category=sbom_data.PackageExternalRefCategory.SECURITY,\n                                       type=sbom_data.PackageExternalRefType.cpe22Type,\n                                       locator=tag.removeprefix(NVD_CPE23)))\n\n  if metadata_proto.name:\n    return metadata_proto.name, external_refs\n  else:\n    return os.path.basename(metadata_file_path), external_refs  # return the directory name only as package name\n\n\ndef get_prebuilt_package_name(file_metadata, metadata_file_path):\n  \"\"\"Return name of a prebuilt package, which can be from the METADATA file, metadata file path,\n  module path or kernel module's source path if the installed file is a kernel module.\n\n  See go/android-spdx and go/android-sbom-gen for more details.\n  \"\"\"\n  name = None\n  if metadata_file_path:\n    metadata_proto = metadata_file_protos[metadata_file_path]\n    if metadata_proto.name:\n      name = metadata_proto.name\n    else:\n      name = metadata_file_path\n  elif file_metadata['module_path']:\n    name = file_metadata['module_path']\n  elif file_metadata['kernel_module_copy_files']:\n    src_path = file_metadata['kernel_module_copy_files'].split(':')[0]\n    name = os.path.dirname(src_path)\n\n  return name.removeprefix('prebuilts/').replace('/', '-')\n\n\ndef get_metadata_file_path(file_metadata):\n  \"\"\"Search for METADATA file of a package and return its path.\"\"\"\n  metadata_path = ''\n  if file_metadata['module_path']:\n    metadata_path = file_metadata['module_path']\n  elif file_metadata['kernel_module_copy_files']:\n    metadata_path = os.path.dirname(file_metadata['kernel_module_copy_files'].split(':')[0])\n\n  while metadata_path and not os.path.exists(metadata_path + '/METADATA'):\n    metadata_path = os.path.dirname(metadata_path)\n\n  return metadata_path\n\n\ndef get_package_version(metadata_file_path):\n  \"\"\"Return a package's version in its METADATA file.\"\"\"\n  if not metadata_file_path:\n    return None\n  metadata_proto = metadata_file_protos[metadata_file_path]\n  return metadata_proto.third_party.version\n\n\ndef get_package_homepage(metadata_file_path):\n  \"\"\"Return a package's homepage URL in its METADATA file.\"\"\"\n  if not metadata_file_path:\n    return None\n  metadata_proto = metadata_file_protos[metadata_file_path]\n  if metadata_proto.third_party.homepage:\n    return metadata_proto.third_party.homepage\n  for url in metadata_proto.third_party.url:\n    if url.type == metadata_file_pb2.URL.Type.HOMEPAGE:\n      return url.value\n\n  return None\n\n\ndef get_package_download_location(metadata_file_path):\n  \"\"\"Return a package's code repository URL in its METADATA file.\"\"\"\n  if not metadata_file_path:\n    return None\n  metadata_proto = metadata_file_protos[metadata_file_path]\n  if metadata_proto.third_party.url:\n    urls = sorted(metadata_proto.third_party.url, key=lambda url: url.type)\n    if urls[0].type != metadata_file_pb2.URL.Type.HOMEPAGE:\n      return urls[0].value\n    elif len(urls) > 1:\n      return urls[1].value\n\n  return None\n\n\ndef get_license_text(license_files):\n  license_text = ''\n  for license_file in license_files:\n    if args.debug:\n      license_text += '#### Content from ' + license_file + '\\n'\n    else:\n      license_text += pathlib.Path(license_file).read_text(errors='replace') + '\\n\\n'\n  return license_text\n\n\ndef get_sbom_fragments(installed_file_metadata, metadata_file_path):\n  \"\"\"Return SPDX fragment of source/prebuilt packages, which usually contains a SOURCE/PREBUILT\n  package, a UPSTREAM package and an external SBOM document reference if sbom_ref defined in its\n  METADATA file.\n\n  See go/android-spdx and go/android-sbom-gen for more details.\n  \"\"\"\n  external_doc_ref = None\n  packages = []\n  relationships = []\n  licenses = []\n\n  # Info from METADATA file\n  homepage = get_package_homepage(metadata_file_path)\n  version = get_package_version(metadata_file_path)\n  download_location = get_package_download_location(metadata_file_path)\n\n  lics = db.get_package_licenses(installed_file_metadata['module_path'])\n  if not lics:\n    lics = db.get_package_licenses(metadata_file_path)\n\n  if lics:\n    for license_name, license_files in lics.items():\n      if not license_files:\n        continue\n      license_id = new_license_id(license_name)\n      if license_name not in licenses_text:\n        licenses_text[license_name] = get_license_text(license_files.split(' '))\n      licenses.append(sbom_data.License(id=license_id, name=license_name, text=licenses_text[license_name]))\n\n  if is_source_package(installed_file_metadata):\n    # Source fork packages\n    name, external_refs = get_source_package_info(installed_file_metadata, metadata_file_path)\n    source_package_id = new_package_id(name, PKG_SOURCE)\n    source_package = sbom_data.Package(id=source_package_id, name=name, version=args.build_version,\n                                       download_location=sbom_data.VALUE_NONE,\n                                       supplier='Organization: ' + args.product_mfr,\n                                       external_refs=external_refs)\n\n    upstream_package_id = new_package_id(name, PKG_UPSTREAM)\n    upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version=version,\n                                         supplier=(\n                                               'Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION,\n                                         download_location=download_location)\n    packages += [source_package, upstream_package]\n    relationships.append(sbom_data.Relationship(id1=source_package_id,\n                                                relationship=sbom_data.RelationshipType.VARIANT_OF,\n                                                id2=upstream_package_id))\n\n    for license in licenses:\n      source_package.declared_license_ids.append(license.id)\n      upstream_package.declared_license_ids.append(license.id)\n\n  elif is_prebuilt_package(installed_file_metadata):\n    # Prebuilt fork packages\n    name = get_prebuilt_package_name(installed_file_metadata, metadata_file_path)\n    prebuilt_package_id = new_package_id(name, PKG_PREBUILT)\n    prebuilt_package = sbom_data.Package(id=prebuilt_package_id,\n                                         name=name,\n                                         download_location=sbom_data.VALUE_NONE,\n                                         version=version if version else args.build_version,\n                                         supplier='Organization: ' + args.product_mfr)\n\n    upstream_package_id = new_package_id(name, PKG_UPSTREAM)\n    upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version=version,\n                                         supplier=(\n                                               'Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION,\n                                         download_location=download_location)\n    packages += [prebuilt_package, upstream_package]\n    relationships.append(sbom_data.Relationship(id1=prebuilt_package_id,\n                                                relationship=sbom_data.RelationshipType.VARIANT_OF,\n                                                id2=upstream_package_id))\n    for license in licenses:\n      prebuilt_package.declared_license_ids.append(license.id)\n      upstream_package.declared_license_ids.append(license.id)\n\n  if metadata_file_path:\n    metadata_proto = metadata_file_protos[metadata_file_path]\n    if metadata_proto.third_party.WhichOneof('sbom') == 'sbom_ref':\n      sbom_url = metadata_proto.third_party.sbom_ref.url\n      sbom_checksum = metadata_proto.third_party.sbom_ref.checksum\n      upstream_element_id = metadata_proto.third_party.sbom_ref.element_id\n      if sbom_url and sbom_checksum and upstream_element_id:\n        doc_ref_id = f'DocumentRef-{PKG_UPSTREAM}-{sbom_data.encode_for_spdxid(name)}'\n        external_doc_ref = sbom_data.DocumentExternalReference(id=doc_ref_id,\n                                                               uri=sbom_url,\n                                                               checksum=sbom_checksum)\n        relationships.append(\n            sbom_data.Relationship(id1=upstream_package_id,\n                                   relationship=sbom_data.RelationshipType.VARIANT_OF,\n                                   id2=doc_ref_id + ':' + upstream_element_id))\n\n  return external_doc_ref, packages, relationships, licenses\n\n\ndef save_report(report_file_path, report):\n  with open(report_file_path, 'w', encoding='utf-8') as report_file:\n    for type, issues in report.items():\n      report_file.write(type + '\\n')\n      for issue in issues:\n        report_file.write('\\t' + issue + '\\n')\n      report_file.write('\\n')\n\n\n# Validate the metadata generated by Make for installed files and report if there is no metadata.\ndef installed_file_has_metadata(installed_file_metadata, report):\n  installed_file = installed_file_metadata['installed_file']\n  module_path = installed_file_metadata['module_path']\n  is_soong_module = installed_file_metadata['is_soong_module']\n  product_copy_files = installed_file_metadata['product_copy_files']\n  kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files']\n  is_platform_generated = installed_file_metadata['is_platform_generated']\n\n  if (not module_path and\n      not is_soong_module and\n      not product_copy_files and\n      not kernel_module_copy_files and\n      not is_platform_generated and\n      not installed_file.endswith('.fsv_meta')):\n    report[ISSUE_NO_METADATA].append(installed_file)\n    return False\n\n  return True\n\n\n# Validate identifiers in a package's METADATA.\n# 1) Only known identifier type is allowed\n# 2) Only one identifier's primary_source can be true\ndef validate_package_metadata(metadata_file_path, package_metadata):\n  primary_source_found = False\n  for identifier in package_metadata.third_party.identifier:\n    if identifier.type not in THIRD_PARTY_IDENTIFIER_TYPES:\n      sys.exit(f'Unknown value of third_party.identifier.type in {metadata_file_path}/METADATA: {identifier.type}.')\n    if primary_source_found and identifier.primary_source:\n      sys.exit(\n          f'Field \"primary_source\" is set to true in multiple third_party.identifier in {metadata_file_path}/METADATA.')\n    primary_source_found = identifier.primary_source\n\n\ndef report_metadata_file(metadata_file_path, installed_file_metadata, report):\n  if metadata_file_path:\n    report[INFO_METADATA_FOUND_FOR_PACKAGE].append(\n        'installed_file: {}, module_path: {}, METADATA file: {}'.format(\n            installed_file_metadata['installed_file'],\n            installed_file_metadata['module_path'],\n            metadata_file_path + '/METADATA'))\n\n    package_metadata = metadata_file_pb2.Metadata()\n    with open(metadata_file_path + '/METADATA', 'rt') as f:\n      text_format.Parse(f.read(), package_metadata)\n\n    validate_package_metadata(metadata_file_path, package_metadata)\n\n    if not metadata_file_path in metadata_file_protos:\n      metadata_file_protos[metadata_file_path] = package_metadata\n      if not package_metadata.name:\n        report[ISSUE_METADATA_FILE_INCOMPLETE].append(f'{metadata_file_path}/METADATA does not has \"name\"')\n\n      if not package_metadata.third_party.version:\n        report[ISSUE_METADATA_FILE_INCOMPLETE].append(\n            f'{metadata_file_path}/METADATA does not has \"third_party.version\"')\n\n      for tag in package_metadata.third_party.security.tag:\n        if not tag.startswith(NVD_CPE23):\n          report[ISSUE_UNKNOWN_SECURITY_TAG_TYPE].append(\n              f'Unknown security tag type: {tag} in {metadata_file_path}/METADATA')\n  else:\n    report[ISSUE_NO_METADATA_FILE].append(\n        \"installed_file: {}, module_path: {}\".format(\n            installed_file_metadata['installed_file'], installed_file_metadata['module_path']))\n\n\n# If a file is from a source fork or prebuilt fork package, add its package information to SBOM\ndef add_package_of_file(file_id, file_metadata, doc, report):\n  metadata_file_path = get_metadata_file_path(file_metadata)\n  report_metadata_file(metadata_file_path, file_metadata, report)\n\n  external_doc_ref, pkgs, rels, licenses = get_sbom_fragments(file_metadata, metadata_file_path)\n  if len(pkgs) > 0:\n    if external_doc_ref:\n      doc.add_external_ref(external_doc_ref)\n    for p in pkgs:\n      doc.add_package(p)\n    for rel in rels:\n      doc.add_relationship(rel)\n    fork_package_id = pkgs[0].id  # The first package should be the source/prebuilt fork package\n    doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                id2=fork_package_id))\n    for license in licenses:\n      doc.add_license(license)\n\n\n# Add STATIC_LINK relationship for static dependencies of a file\ndef add_static_deps_of_file(file_id, file_metadata, doc):\n  if not file_metadata['static_dep_files'] and not file_metadata['whole_static_dep_files']:\n    return\n  static_dep_files = []\n  if file_metadata['static_dep_files']:\n    static_dep_files += file_metadata['static_dep_files'].split(' ')\n  if file_metadata['whole_static_dep_files']:\n    static_dep_files += file_metadata['whole_static_dep_files'].split(' ')\n\n  for dep_file in static_dep_files:\n    # Static libs are not shipped on devices, so names are derived from .intermediates paths.\n    doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                relationship=sbom_data.RelationshipType.STATIC_LINK,\n                                                id2=new_file_id(\n                                                  dep_file.removeprefix(args.soong_out + '/.intermediates/'))))\n\n\ndef add_licenses_of_file(file_id, file_metadata, doc):\n  lics = db.get_module_licenses(file_metadata.get('name', ''), file_metadata['module_path'])\n  if lics:\n    file = next(f for f in doc.files if file_id == f.id)\n    for license_name, license_files in lics.items():\n      if not license_files:\n        continue\n      license_id = new_license_id(license_name)\n      file.concluded_license_ids.append(license_id)\n      if license_name not in licenses_text:\n        license_text = get_license_text(license_files.split(' '))\n        licenses_text[license_name] = license_text\n\n      doc.add_license(sbom_data.License(id=license_id, name=license_name, text=licenses_text[license_name]))\n\n\ndef get_all_transitive_static_dep_files_of_installed_files(installed_files_metadata, db, report):\n  # Find all transitive static dep files of all installed files\n  q = queue.Queue()\n  for installed_file_metadata in installed_files_metadata:\n    if installed_file_metadata['static_dep_files']:\n      for f in installed_file_metadata['static_dep_files'].split(' '):\n        q.put(f)\n    if installed_file_metadata['whole_static_dep_files']:\n      for f in installed_file_metadata['whole_static_dep_files'].split(' '):\n        q.put(f)\n\n  all_static_dep_files = {}\n  while not q.empty():\n    dep_file = q.get()\n    if dep_file in all_static_dep_files:\n      # It has been processed\n      continue\n\n    all_static_dep_files[dep_file] = True\n    soong_module = db.get_soong_module_of_built_file(dep_file)\n    if not soong_module:\n      # This should not happen, add to report[ISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP]\n      report[ISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP].append(f)\n      continue\n\n    if soong_module['static_dep_files']:\n      for f in soong_module['static_dep_files'].split(' '):\n        if f not in all_static_dep_files:\n          q.put(f)\n    if soong_module['whole_static_dep_files']:\n      for f in soong_module['whole_static_dep_files'].split(' '):\n        if f not in all_static_dep_files:\n          q.put(f)\n\n  return sorted(all_static_dep_files.keys())\n\n\ndef main():\n  global args\n  args = get_args()\n  log('Args:', vars(args))\n\n  global db\n  db = compliance_metadata.MetadataDb(args.metadata)\n  if args.debug:\n    db.dump_debug_db(os.path.dirname(args.output_file) + '/compliance-metadata-debug.db')\n\n  global metadata_file_protos\n  metadata_file_protos = {}\n  global licenses_text\n  licenses_text = {}\n\n  product_package_id = sbom_data.SPDXID_PRODUCT\n  product_package_name = sbom_data.PACKAGE_NAME_PRODUCT\n  product_package = sbom_data.Package(id=product_package_id,\n                                      name=product_package_name,\n                                      download_location=sbom_data.VALUE_NONE,\n                                      version=args.build_version,\n                                      supplier='Organization: ' + args.product_mfr,\n                                      files_analyzed=True)\n  doc_name = args.build_version\n  doc = sbom_data.Document(name=doc_name,\n                           namespace=f'https://www.google.com/sbom/spdx/android/{doc_name}',\n                           creators=['Organization: ' + args.product_mfr],\n                           describes=product_package_id)\n\n  doc.packages.append(product_package)\n  doc.packages.append(sbom_data.Package(id=sbom_data.SPDXID_PLATFORM,\n                                        name=sbom_data.PACKAGE_NAME_PLATFORM,\n                                        download_location=sbom_data.VALUE_NONE,\n                                        version=args.build_version,\n                                        supplier='Organization: ' + args.product_mfr,\n                                        declared_license_ids=[sbom_data.SPDXID_LICENSE_APACHE]))\n\n  # Report on some issues and information\n  report = {\n      ISSUE_NO_METADATA: [],\n      ISSUE_NO_METADATA_FILE: [],\n      ISSUE_METADATA_FILE_INCOMPLETE: [],\n      ISSUE_UNKNOWN_SECURITY_TAG_TYPE: [],\n      ISSUE_INSTALLED_FILE_NOT_EXIST: [],\n      ISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP: [],\n      INFO_METADATA_FOUND_FOR_PACKAGE: [],\n  }\n\n  # Get installed files and corresponding make modules' metadata if an installed file is from a make module.\n  installed_files_metadata = db.get_installed_files()\n\n  # Find which Soong module an installed file is from and merge metadata from Make and Soong\n  for installed_file_metadata in installed_files_metadata:\n    soong_module = db.get_soong_module_of_installed_file(installed_file_metadata['installed_file'])\n    if soong_module:\n      # Merge soong metadata to make metadata\n      installed_file_metadata.update(soong_module)\n    else:\n      # For make modules soong_module_type should be empty\n      installed_file_metadata['soong_module_type'] = ''\n      installed_file_metadata['static_dep_files'] = ''\n      installed_file_metadata['whole_static_dep_files'] = ''\n\n  # Scan the metadata and create the corresponding package and file records in SPDX\n  for installed_file_metadata in installed_files_metadata:\n    installed_file = installed_file_metadata['installed_file']\n    module_path = installed_file_metadata['module_path']\n    product_copy_files = installed_file_metadata['product_copy_files']\n    kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files']\n    build_output_path = installed_file\n    installed_file = installed_file.removeprefix(args.product_out)\n\n    if not installed_file_has_metadata(installed_file_metadata, report):\n      continue\n    if not (os.path.islink(build_output_path) or os.path.isfile(build_output_path)):\n      report[ISSUE_INSTALLED_FILE_NOT_EXIST].append(installed_file)\n      continue\n\n    file_id = new_file_id(installed_file)\n    sha1 = checksum(build_output_path)\n    f = sbom_data.File(id=file_id, name=installed_file, checksum=sha1)\n    doc.files.append(f)\n    product_package.file_ids.append(file_id)\n\n    if is_source_package(installed_file_metadata) or is_prebuilt_package(installed_file_metadata):\n      add_package_of_file(file_id, installed_file_metadata, doc, report)\n\n    elif module_path or installed_file_metadata['is_platform_generated']:\n      # File from PLATFORM package\n      doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                  relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                  id2=sbom_data.SPDXID_PLATFORM))\n      if installed_file_metadata['is_platform_generated']:\n        f.concluded_license_ids = [sbom_data.SPDXID_LICENSE_APACHE]\n\n    elif product_copy_files:\n      # Format of product_copy_files: <source path>:<dest path>\n      src_path = product_copy_files.split(':')[0]\n      # So far product_copy_files are copied from directory system, kernel, hardware, frameworks and device,\n      # so process them as files from PLATFORM package\n      doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                  relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                  id2=sbom_data.SPDXID_PLATFORM))\n      if installed_file_metadata['license_text']:\n        if installed_file_metadata['license_text'] == 'build/soong/licenses/LICENSE':\n          f.concluded_license_ids = [sbom_data.SPDXID_LICENSE_APACHE]\n\n    elif installed_file.endswith('.fsv_meta'):\n      doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                  relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                  id2=sbom_data.SPDXID_PLATFORM))\n      f.concluded_license_ids = [sbom_data.SPDXID_LICENSE_APACHE]\n\n    elif kernel_module_copy_files.startswith('ANDROID-GEN'):\n      # For the four files generated for _dlkm, _ramdisk partitions\n      doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                  relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                  id2=sbom_data.SPDXID_PLATFORM))\n\n    # Process static dependencies of the installed file\n    add_static_deps_of_file(file_id, installed_file_metadata, doc)\n\n    # Add licenses of the installed file\n    add_licenses_of_file(file_id, installed_file_metadata, doc)\n\n  # Add all static library files to SBOM\n  for dep_file in get_all_transitive_static_dep_files_of_installed_files(installed_files_metadata, db, report):\n    filepath = dep_file.removeprefix(args.soong_out + '/.intermediates/')\n    file_id = new_file_id(filepath)\n    # SHA1 of empty string. Sometimes .a files might not be built.\n    sha1 = 'SHA1: da39a3ee5e6b4b0d3255bfef95601890afd80709'\n    if os.path.islink(dep_file) or os.path.isfile(dep_file):\n      sha1 = checksum(dep_file)\n    doc.files.append(sbom_data.File(id=file_id,\n                                    name=filepath,\n                                    checksum=sha1))\n    file_metadata = {\n        'installed_file': dep_file,\n        'is_prebuilt_make_module': False\n    }\n    soong_module = db.get_soong_module_of_built_file(dep_file)\n    if not soong_module:\n      continue\n    file_metadata.update(soong_module)\n    if is_source_package(file_metadata) or is_prebuilt_package(file_metadata):\n      add_package_of_file(file_id, file_metadata, doc, report)\n    else:\n      # Other static lib files are generated from the platform\n      doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                  relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                  id2=sbom_data.SPDXID_PLATFORM))\n\n    # Add relationships for static deps of static libraries\n    add_static_deps_of_file(file_id, file_metadata, doc)\n\n    # Add licenses of the static lib\n    add_licenses_of_file(file_id, file_metadata, doc)\n\n  # Save SBOM records to output file\n  doc.generate_packages_verification_code()\n  doc.created = datetime.datetime.now(tz=datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')\n  prefix = args.output_file\n  if prefix.endswith('.spdx'):\n    prefix = prefix.removesuffix('.spdx')\n  elif prefix.endswith('.spdx.json'):\n    prefix = prefix.removesuffix('.spdx.json')\n\n  output_file = prefix + '.spdx'\n  with open(output_file, 'w', encoding=\"utf-8\") as file:\n    sbom_writers.TagValueWriter.write(doc, file)\n  if args.json:\n    with open(prefix + '.spdx.json', 'w', encoding=\"utf-8\") as file:\n      sbom_writers.JSONWriter.write(doc, file)\n\n  save_report(prefix + '-gen-report.txt', report)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tools/sbom/generate-sbom-framework_res.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport hashlib\nimport json\nimport sbom_data\nimport sbom_writers\n\n'''\nThis script generates SBOM of framework_res.jar of layoutlib shipped with Android Studio.\n\nThe generated SBOM contains some placeholders which should be substituted by release_layoutlib.sh.\nThe placeholders include: document name, document namespace, organization, created timestamp and \nthe SHA1 checksum of framework_res.jar.\n'''\n\ndef get_args():\n  parser = argparse.ArgumentParser()\n  parser.add_argument('-v', '--verbose', action='store_true', default=False,\n                      help='Print more information.')\n  parser.add_argument('--output_file', required=True,\n                      help='The generated SBOM file in SPDX format.')\n  parser.add_argument('--layoutlib_sbom', required=True,\n                      help='The file path of the SBOM of layoutlib.')\n\n  return parser.parse_args()\n\n\ndef main():\n  global args\n  args = get_args()\n\n  doc = sbom_data.Document(name='<name>',\n                           namespace='<namespace>',\n                           creators=['Organization: <organization>'],\n                           created='<created>')\n\n  filename = 'data/framework_res.jar'\n  file_id = f'SPDXRef-{sbom_data.encode_for_spdxid(filename)}'\n  file = sbom_data.File(id=file_id, name=filename, checksum='SHA1: <checksum>')\n\n  package_name = 'framework_res'\n  package_id = f'SPDXRef-PREBUILT-{sbom_data.encode_for_spdxid(package_name)}'\n  package = sbom_data.Package(id=package_id, name=package_name, version='<package_version>',\n                    download_location=sbom_data.VALUE_NONE,\n                    supplier='Organization: <organization>',\n                    files_analyzed=True,\n                    verification_code='<package_verification_code>')\n  package.file_ids.append(file_id)\n\n  doc.packages.append(package)\n  doc.files.append(file)\n  doc.describes = package_id\n\n  with open(args.layoutlib_sbom, 'r', encoding='utf-8') as f:\n    layoutlib_sbom = json.load(f)\n\n  with open(args.layoutlib_sbom, 'rb') as f:\n    sha1 = hashlib.file_digest(f, 'sha1')\n\n  layoutlib_sbom_namespace = layoutlib_sbom[sbom_writers.PropNames.DOCUMENT_NAMESPACE]\n  external_doc_ref = 'DocumentRef-layoutlib'\n  doc.external_refs = [\n    sbom_data.DocumentExternalReference(external_doc_ref, layoutlib_sbom_namespace,\n                                        f'SHA1: {sha1.hexdigest()}')]\n\n  resource_file_spdxids = []\n  for file in layoutlib_sbom[sbom_writers.PropNames.FILES]:\n    file_path = file[sbom_writers.PropNames.FILE_NAME]\n    if file_path.startswith('data/res/') or file_path.startswith('data/overlays/'):\n      resource_file_spdxids.append(file[sbom_writers.PropNames.SPDXID])\n\n  doc.relationships = [\n    sbom_data.Relationship(package_id, sbom_data.RelationshipType.CONTAINS, file_id)\n  ]\n  for spdxid in resource_file_spdxids:\n    doc.relationships.append(\n      sbom_data.Relationship(file_id, sbom_data.RelationshipType.GENERATED_FROM,\n                             f'{external_doc_ref}:{spdxid}'))\n\n  # write sbom file\n  with open(args.output_file, 'w', encoding='utf-8') as f:\n    sbom_writers.JSONWriter.write(doc, f)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tools/sbom/generate-sbom.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nGenerate the SBOM of the current target product in SPDX format.\nUsage example:\n  generate-sbom.py --output_file out/target/product/vsoc_x86_64/sbom.spdx \\\n                   --metadata out/target/product/vsoc_x86_64/sbom-metadata.csv \\\n                   --build_version $(cat out/target/product/vsoc_x86_64/build_fingerprint.txt) \\\n                   --product_mfr=Google\n\"\"\"\n\nimport argparse\nimport csv\nimport datetime\nimport google.protobuf.text_format as text_format\nimport hashlib\nimport os\nimport metadata_file_pb2\nimport sbom_data\nimport sbom_writers\n\n\n# Package type\nPKG_SOURCE = 'SOURCE'\nPKG_UPSTREAM = 'UPSTREAM'\nPKG_PREBUILT = 'PREBUILT'\n\n# Security tag\nNVD_CPE23 = 'NVD-CPE2.3:'\n\n# Report\nISSUE_NO_METADATA = 'No metadata generated in Make for installed files:'\nISSUE_NO_METADATA_FILE = 'No METADATA file found for installed file:'\nISSUE_METADATA_FILE_INCOMPLETE = 'METADATA file incomplete:'\nISSUE_UNKNOWN_SECURITY_TAG_TYPE = 'Unknown security tag type:'\nISSUE_INSTALLED_FILE_NOT_EXIST = 'Non-exist installed files:'\nINFO_METADATA_FOUND_FOR_PACKAGE = 'METADATA file found for packages:'\n\nSOONG_PREBUILT_MODULE_TYPES = [\n  'android_app_import',\n  'android_library_import',\n  'cc_prebuilt_binary',\n  'cc_prebuilt_library',\n  'cc_prebuilt_library_headers',\n  'cc_prebuilt_library_shared',\n  'cc_prebuilt_library_static',\n  'cc_prebuilt_object',\n  'dex_import',\n  'java_import',\n  'java_sdk_library_import',\n  'java_system_modules_import',\n  'libclang_rt_prebuilt_library_static',\n  'libclang_rt_prebuilt_library_shared',\n  'llvm_prebuilt_library_static',\n  'ndk_prebuilt_object',\n  'ndk_prebuilt_shared_stl',\n  'nkd_prebuilt_static_stl',\n  'prebuilt_apex',\n  'prebuilt_bootclasspath_fragment',\n  'prebuilt_dsp',\n  'prebuilt_firmware',\n  'prebuilt_kernel_modules',\n  'prebuilt_rfsa',\n  'prebuilt_root',\n  'rust_prebuilt_dylib',\n  'rust_prebuilt_library',\n  'rust_prebuilt_rlib',\n  'vndk_prebuilt_shared',\n]\n\nTHIRD_PARTY_IDENTIFIER_TYPES = [\n    # Types defined in metadata_file.proto\n    'Git',\n    'SVN',\n    'Hg',\n    'Darcs',\n    'VCS',\n    'Archive',\n    'PrebuiltByAlphabet',\n    'LocalSource',\n    'Other',\n    # OSV ecosystems defined at https://ossf.github.io/osv-schema/#affectedpackage-field.\n    'Go',\n    'npm',\n    'OSS-Fuzz',\n    'PyPI',\n    'RubyGems',\n    'crates.io',\n    'Hackage',\n    'GHC',\n    'Packagist',\n    'Maven',\n    'NuGet',\n    'Linux',\n    'Debian',\n    'Alpine',\n    'Hex',\n    'Android',\n    'GitHub Actions',\n    'Pub',\n    'ConanCenter',\n    'Rocky Linux',\n    'AlmaLinux',\n    'Bitnami',\n    'Photon OS',\n    'CRAN',\n    'Bioconductor',\n    'SwiftURL'\n]\n\n\ndef get_args():\n  parser = argparse.ArgumentParser()\n  parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Print more information.')\n  parser.add_argument('--output_file', required=True, help='The generated SBOM file in SPDX format.')\n  parser.add_argument('--metadata', required=True, help='The SBOM metadata file path.')\n  parser.add_argument('--build_version', required=True, help='The build version.')\n  parser.add_argument('--product_mfr', required=True, help='The product manufacturer.')\n  parser.add_argument('--module_name', help='The module name. If specified, the generated SBOM is for the module.')\n  parser.add_argument('--json', action='store_true', default=False, help='Generated SBOM file in SPDX JSON format')\n  parser.add_argument('--unbundled_apk', action='store_true', default=False, help='Generate SBOM for unbundled APKs')\n  parser.add_argument('--unbundled_apex', action='store_true', default=False, help='Generate SBOM for unbundled APEXs')\n\n  return parser.parse_args()\n\n\ndef log(*info):\n  if args.verbose:\n    for i in info:\n      print(i)\n\n\ndef new_package_id(package_name, type):\n  return f'SPDXRef-{type}-{sbom_data.encode_for_spdxid(package_name)}'\n\n\ndef new_file_id(file_path):\n  return f'SPDXRef-{sbom_data.encode_for_spdxid(file_path)}'\n\n\ndef checksum(file_path):\n  h = hashlib.sha1()\n  if os.path.islink(file_path):\n    h.update(os.readlink(file_path).encode('utf-8'))\n  else:\n    with open(file_path, 'rb') as f:\n      h.update(f.read())\n  return f'SHA1: {h.hexdigest()}'\n\n\ndef is_soong_prebuilt_module(file_metadata):\n  return (file_metadata['soong_module_type'] and\n          file_metadata['soong_module_type'] in SOONG_PREBUILT_MODULE_TYPES)\n\n\ndef is_source_package(file_metadata):\n  module_path = file_metadata['module_path']\n  return module_path.startswith('external/') and not is_prebuilt_package(file_metadata)\n\n\ndef is_prebuilt_package(file_metadata):\n  module_path = file_metadata['module_path']\n  if module_path:\n    return (module_path.startswith('prebuilts/') or\n            is_soong_prebuilt_module(file_metadata) or\n            file_metadata['is_prebuilt_make_module'])\n\n  kernel_module_copy_files = file_metadata['kernel_module_copy_files']\n  if kernel_module_copy_files and not kernel_module_copy_files.startswith('ANDROID-GEN:'):\n    return True\n\n  return False\n\n\ndef get_source_package_info(file_metadata, metadata_file_path):\n  \"\"\"Return source package info exists in its METADATA file, currently including name, security tag\n  and external SBOM reference.\n\n  See go/android-spdx and go/android-sbom-gen for more details.\n  \"\"\"\n  if not metadata_file_path:\n    return file_metadata['module_path'], []\n\n  metadata_proto = metadata_file_protos[metadata_file_path]\n  external_refs = []\n  for tag in metadata_proto.third_party.security.tag:\n    if tag.lower().startswith((NVD_CPE23 + 'cpe:2.3:').lower()):\n      external_refs.append(\n        sbom_data.PackageExternalRef(category=sbom_data.PackageExternalRefCategory.SECURITY,\n                                     type=sbom_data.PackageExternalRefType.cpe23Type,\n                                     locator=tag.removeprefix(NVD_CPE23)))\n    elif tag.lower().startswith((NVD_CPE23 + 'cpe:/').lower()):\n      external_refs.append(\n        sbom_data.PackageExternalRef(category=sbom_data.PackageExternalRefCategory.SECURITY,\n                                     type=sbom_data.PackageExternalRefType.cpe22Type,\n                                     locator=tag.removeprefix(NVD_CPE23)))\n\n  if metadata_proto.name:\n    return metadata_proto.name, external_refs\n  else:\n    return os.path.basename(metadata_file_path), external_refs  # return the directory name only as package name\n\n\ndef get_prebuilt_package_name(file_metadata, metadata_file_path):\n  \"\"\"Return name of a prebuilt package, which can be from the METADATA file, metadata file path,\n  module path or kernel module's source path if the installed file is a kernel module.\n\n  See go/android-spdx and go/android-sbom-gen for more details.\n  \"\"\"\n  name = None\n  if metadata_file_path:\n    metadata_proto = metadata_file_protos[metadata_file_path]\n    if metadata_proto.name:\n      name = metadata_proto.name\n    else:\n      name = metadata_file_path\n  elif file_metadata['module_path']:\n    name = file_metadata['module_path']\n  elif file_metadata['kernel_module_copy_files']:\n    src_path = file_metadata['kernel_module_copy_files'].split(':')[0]\n    name = os.path.dirname(src_path)\n\n  return name.removeprefix('prebuilts/').replace('/', '-')\n\n\ndef get_metadata_file_path(file_metadata):\n  \"\"\"Search for METADATA file of a package and return its path.\"\"\"\n  metadata_path = ''\n  if file_metadata['module_path']:\n    metadata_path = file_metadata['module_path']\n  elif file_metadata['kernel_module_copy_files']:\n    metadata_path = os.path.dirname(file_metadata['kernel_module_copy_files'].split(':')[0])\n\n  while metadata_path and not os.path.exists(metadata_path + '/METADATA'):\n    metadata_path = os.path.dirname(metadata_path)\n\n  return metadata_path\n\n\ndef get_package_version(metadata_file_path):\n  \"\"\"Return a package's version in its METADATA file.\"\"\"\n  if not metadata_file_path:\n    return None\n  metadata_proto = metadata_file_protos[metadata_file_path]\n  return metadata_proto.third_party.version\n\n\ndef get_package_homepage(metadata_file_path):\n  \"\"\"Return a package's homepage URL in its METADATA file.\"\"\"\n  if not metadata_file_path:\n    return None\n  metadata_proto = metadata_file_protos[metadata_file_path]\n  if metadata_proto.third_party.homepage:\n    return metadata_proto.third_party.homepage\n  for url in metadata_proto.third_party.url:\n    if url.type == metadata_file_pb2.URL.Type.HOMEPAGE:\n      return url.value\n\n  return None\n\n\ndef get_package_download_location(metadata_file_path):\n  \"\"\"Return a package's code repository URL in its METADATA file.\"\"\"\n  if not metadata_file_path:\n    return None\n  metadata_proto = metadata_file_protos[metadata_file_path]\n  if metadata_proto.third_party.url:\n    urls = sorted(metadata_proto.third_party.url, key=lambda url: url.type)\n    if urls[0].type != metadata_file_pb2.URL.Type.HOMEPAGE:\n      return urls[0].value\n    elif len(urls) > 1:\n      return urls[1].value\n\n  return None\n\n\ndef get_sbom_fragments(installed_file_metadata, metadata_file_path):\n  \"\"\"Return SPDX fragment of source/prebuilt packages, which usually contains a SOURCE/PREBUILT\n  package, a UPSTREAM package and an external SBOM document reference if sbom_ref defined in its\n  METADATA file.\n\n  See go/android-spdx and go/android-sbom-gen for more details.\n  \"\"\"\n  external_doc_ref = None\n  packages = []\n  relationships = []\n\n  # Info from METADATA file\n  homepage = get_package_homepage(metadata_file_path)\n  version = get_package_version(metadata_file_path)\n  download_location = get_package_download_location(metadata_file_path)\n\n  if is_source_package(installed_file_metadata):\n    # Source fork packages\n    name, external_refs = get_source_package_info(installed_file_metadata, metadata_file_path)\n    source_package_id = new_package_id(name, PKG_SOURCE)\n    source_package = sbom_data.Package(id=source_package_id, name=name, version=args.build_version,\n                                       download_location=sbom_data.VALUE_NONE,\n                                       supplier='Organization: ' + args.product_mfr,\n                                       external_refs=external_refs)\n\n    upstream_package_id = new_package_id(name, PKG_UPSTREAM)\n    upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version=version,\n                                         supplier=('Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION,\n                                         download_location=download_location)\n    packages += [source_package, upstream_package]\n    relationships.append(sbom_data.Relationship(id1=source_package_id,\n                                                relationship=sbom_data.RelationshipType.VARIANT_OF,\n                                                id2=upstream_package_id))\n  elif is_prebuilt_package(installed_file_metadata):\n    # Prebuilt fork packages\n    name = get_prebuilt_package_name(installed_file_metadata, metadata_file_path)\n    prebuilt_package_id = new_package_id(name, PKG_PREBUILT)\n    prebuilt_package = sbom_data.Package(id=prebuilt_package_id,\n                                         name=name,\n                                         download_location=sbom_data.VALUE_NONE,\n                                         version=version if version else args.build_version,\n                                         supplier='Organization: ' + args.product_mfr)\n\n    upstream_package_id = new_package_id(name, PKG_UPSTREAM)\n    upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version = version,\n                                         supplier=('Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION,\n                                         download_location=download_location)\n    packages += [prebuilt_package, upstream_package]\n    relationships.append(sbom_data.Relationship(id1=prebuilt_package_id,\n                                                relationship=sbom_data.RelationshipType.VARIANT_OF,\n                                                id2=upstream_package_id))\n\n  if metadata_file_path:\n    metadata_proto = metadata_file_protos[metadata_file_path]\n    if metadata_proto.third_party.WhichOneof('sbom') == 'sbom_ref':\n      sbom_url = metadata_proto.third_party.sbom_ref.url\n      sbom_checksum = metadata_proto.third_party.sbom_ref.checksum\n      upstream_element_id = metadata_proto.third_party.sbom_ref.element_id\n      if sbom_url and sbom_checksum and upstream_element_id:\n        doc_ref_id = f'DocumentRef-{PKG_UPSTREAM}-{sbom_data.encode_for_spdxid(name)}'\n        external_doc_ref = sbom_data.DocumentExternalReference(id=doc_ref_id,\n                                                               uri=sbom_url,\n                                                               checksum=sbom_checksum)\n        relationships.append(\n          sbom_data.Relationship(id1=upstream_package_id,\n                                 relationship=sbom_data.RelationshipType.VARIANT_OF,\n                                 id2=doc_ref_id + ':' + upstream_element_id))\n\n  return external_doc_ref, packages, relationships\n\n\ndef save_report(report_file_path, report):\n  with open(report_file_path, 'w', encoding='utf-8') as report_file:\n    for type, issues in report.items():\n      report_file.write(type + '\\n')\n      for issue in issues:\n        report_file.write('\\t' + issue + '\\n')\n      report_file.write('\\n')\n\n\n# Validate the metadata generated by Make for installed files and report if there is no metadata.\ndef installed_file_has_metadata(installed_file_metadata, report):\n  installed_file = installed_file_metadata['installed_file']\n  module_path = installed_file_metadata['module_path']\n  product_copy_files = installed_file_metadata['product_copy_files']\n  kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files']\n  is_platform_generated = installed_file_metadata['is_platform_generated']\n\n  if (not module_path and\n      not product_copy_files and\n      not kernel_module_copy_files and\n      not is_platform_generated and\n      not installed_file.endswith('.fsv_meta')):\n    report[ISSUE_NO_METADATA].append(installed_file)\n    return False\n\n  return True\n\n\n# Validate identifiers in a package's METADATA.\n# 1) Only known identifier type is allowed\n# 2) Only one identifier's primary_source can be true\ndef validate_package_metadata(metadata_file_path, package_metadata):\n  primary_source_found = False\n  for identifier in package_metadata.third_party.identifier:\n    if identifier.type not in THIRD_PARTY_IDENTIFIER_TYPES:\n      sys.exit(f'Unknown value of third_party.identifier.type in {metadata_file_path}/METADATA: {identifier.type}.')\n    if primary_source_found and identifier.primary_source:\n      sys.exit(\n        f'Field \"primary_source\" is set to true in multiple third_party.identifier in {metadata_file_path}/METADATA.')\n    primary_source_found = identifier.primary_source\n\n\ndef report_metadata_file(metadata_file_path, installed_file_metadata, report):\n  if metadata_file_path:\n    report[INFO_METADATA_FOUND_FOR_PACKAGE].append(\n        'installed_file: {}, module_path: {}, METADATA file: {}'.format(\n            installed_file_metadata['installed_file'],\n            installed_file_metadata['module_path'],\n            metadata_file_path + '/METADATA'))\n\n    package_metadata = metadata_file_pb2.Metadata()\n    with open(metadata_file_path + '/METADATA', 'rt') as f:\n      text_format.Parse(f.read(), package_metadata)\n\n    validate_package_metadata(metadata_file_path, package_metadata)\n\n    if not metadata_file_path in metadata_file_protos:\n      metadata_file_protos[metadata_file_path] = package_metadata\n      if not package_metadata.name:\n        report[ISSUE_METADATA_FILE_INCOMPLETE].append(f'{metadata_file_path}/METADATA does not has \"name\"')\n\n      if not package_metadata.third_party.version:\n        report[ISSUE_METADATA_FILE_INCOMPLETE].append(\n            f'{metadata_file_path}/METADATA does not has \"third_party.version\"')\n\n      for tag in package_metadata.third_party.security.tag:\n        if not tag.startswith(NVD_CPE23):\n          report[ISSUE_UNKNOWN_SECURITY_TAG_TYPE].append(\n              f'Unknown security tag type: {tag} in {metadata_file_path}/METADATA')\n  else:\n    report[ISSUE_NO_METADATA_FILE].append(\n        \"installed_file: {}, module_path: {}\".format(\n            installed_file_metadata['installed_file'], installed_file_metadata['module_path']))\n\n\ndef generate_sbom_for_unbundled_apk():\n  with open(args.metadata, newline='') as sbom_metadata_file:\n    reader = csv.DictReader(sbom_metadata_file)\n    doc = sbom_data.Document(name=args.build_version,\n                             namespace=f'https://www.google.com/sbom/spdx/android/{args.build_version}',\n                             creators=['Organization: ' + args.product_mfr])\n    for installed_file_metadata in reader:\n      installed_file = installed_file_metadata['installed_file']\n      if args.output_file != installed_file_metadata['build_output_path'] + '.spdx.json':\n        continue\n\n      module_path = installed_file_metadata['module_path']\n      package_id = new_package_id(module_path, PKG_PREBUILT)\n      package = sbom_data.Package(id=package_id,\n                                  name=module_path,\n                                  version=args.build_version,\n                                  supplier='Organization: ' + args.product_mfr)\n      file_id = new_file_id(installed_file)\n      file = sbom_data.File(id=file_id,\n                            name=installed_file,\n                            checksum=checksum(installed_file_metadata['build_output_path']))\n      relationship = sbom_data.Relationship(id1=file_id,\n                                            relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                            id2=package_id)\n      doc.add_package(package)\n      doc.files.append(file)\n      doc.describes = file_id\n      doc.add_relationship(relationship)\n      doc.created = datetime.datetime.now(tz=datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')\n      break\n\n  with open(args.output_file, 'w', encoding='utf-8') as file:\n    sbom_writers.JSONWriter.write(doc, file)\n  fragment_file = args.output_file.removesuffix('.spdx.json') + '-fragment.spdx'\n  with open(fragment_file, 'w', encoding='utf-8') as file:\n    sbom_writers.TagValueWriter.write(doc, file, fragment=True)\n\n\ndef main():\n  global args\n  args = get_args()\n  log('Args:', vars(args))\n\n  if args.unbundled_apk:\n    generate_sbom_for_unbundled_apk()\n    return\n\n  global metadata_file_protos\n  metadata_file_protos = {}\n\n  product_package_id = sbom_data.SPDXID_PRODUCT\n  product_package_name = sbom_data.PACKAGE_NAME_PRODUCT\n  if args.module_name:\n    # Build SBOM of a module so use the module name instead.\n    product_package_id = f'SPDXRef-{sbom_data.encode_for_spdxid(args.module_name)}'\n    product_package_name = args.module_name\n  product_package = sbom_data.Package(id=product_package_id,\n                                      name=product_package_name,\n                                      download_location=sbom_data.VALUE_NONE,\n                                      version=args.build_version,\n                                      supplier='Organization: ' + args.product_mfr,\n                                      files_analyzed=True)\n  doc_name = args.build_version\n  if args.module_name:\n    doc_name = f'{args.build_version}/{args.module_name}'\n  doc = sbom_data.Document(name=doc_name,\n                           namespace=f'https://www.google.com/sbom/spdx/android/{doc_name}',\n                           creators=['Organization: ' + args.product_mfr],\n                           describes=product_package_id)\n  if not args.unbundled_apex:\n    doc.packages.append(product_package)\n\n  doc.packages.append(sbom_data.Package(id=sbom_data.SPDXID_PLATFORM,\n                                        name=sbom_data.PACKAGE_NAME_PLATFORM,\n                                        download_location=sbom_data.VALUE_NONE,\n                                        version=args.build_version,\n                                        supplier='Organization: ' + args.product_mfr))\n\n  # Report on some issues and information\n  report = {\n    ISSUE_NO_METADATA: [],\n    ISSUE_NO_METADATA_FILE: [],\n    ISSUE_METADATA_FILE_INCOMPLETE: [],\n    ISSUE_UNKNOWN_SECURITY_TAG_TYPE: [],\n    ISSUE_INSTALLED_FILE_NOT_EXIST: [],\n    INFO_METADATA_FOUND_FOR_PACKAGE: [],\n  }\n\n  # Scan the metadata in CSV file and create the corresponding package and file records in SPDX\n  with open(args.metadata, newline='') as sbom_metadata_file:\n    reader = csv.DictReader(sbom_metadata_file)\n    for installed_file_metadata in reader:\n      installed_file = installed_file_metadata['installed_file']\n      module_path = installed_file_metadata['module_path']\n      product_copy_files = installed_file_metadata['product_copy_files']\n      kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files']\n      build_output_path = installed_file_metadata['build_output_path']\n      is_static_lib = installed_file_metadata['is_static_lib']\n\n      if not installed_file_has_metadata(installed_file_metadata, report):\n        continue\n      if not is_static_lib and not (os.path.islink(build_output_path) or os.path.isfile(build_output_path)):\n        # Ignore non-existing static library files for now since they are not shipped on devices.\n        report[ISSUE_INSTALLED_FILE_NOT_EXIST].append(installed_file)\n        continue\n\n      file_id = new_file_id(installed_file)\n      # TODO(b/285453664): Soong should report the information of statically linked libraries to Make.\n      # This happens when a different sanitized version of static libraries is used in linking.\n      # As a workaround, use the following SHA1 checksum for static libraries created by Soong, if .a files could not be\n      # located correctly because Soong doesn't report the information to Make.\n      sha1 = 'SHA1: da39a3ee5e6b4b0d3255bfef95601890afd80709'  # SHA1 of empty string\n      if os.path.islink(build_output_path) or os.path.isfile(build_output_path):\n        sha1 = checksum(build_output_path)\n      doc.files.append(sbom_data.File(id=file_id,\n                                      name=installed_file,\n                                      checksum=sha1))\n\n      if not is_static_lib:\n        if not args.unbundled_apex:\n          product_package.file_ids.append(file_id)\n        elif len(doc.files) > 1:\n            doc.add_relationship(sbom_data.Relationship(doc.files[0].id, sbom_data.RelationshipType.CONTAINS, file_id))\n\n      if is_source_package(installed_file_metadata) or is_prebuilt_package(installed_file_metadata):\n        metadata_file_path = get_metadata_file_path(installed_file_metadata)\n        report_metadata_file(metadata_file_path, installed_file_metadata, report)\n\n        # File from source fork packages or prebuilt fork packages\n        external_doc_ref, pkgs, rels = get_sbom_fragments(installed_file_metadata, metadata_file_path)\n        if len(pkgs) > 0:\n          if external_doc_ref:\n            doc.add_external_ref(external_doc_ref)\n          for p in pkgs:\n            doc.add_package(p)\n          for rel in rels:\n            doc.add_relationship(rel)\n          fork_package_id = pkgs[0].id  # The first package should be the source/prebuilt fork package\n          doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                      relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                      id2=fork_package_id))\n      elif module_path or installed_file_metadata['is_platform_generated']:\n        # File from PLATFORM package\n        doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                    relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                    id2=sbom_data.SPDXID_PLATFORM))\n      elif product_copy_files:\n        # Format of product_copy_files: <source path>:<dest path>\n        src_path = product_copy_files.split(':')[0]\n        # So far product_copy_files are copied from directory system, kernel, hardware, frameworks and device,\n        # so process them as files from PLATFORM package\n        doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                    relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                    id2=sbom_data.SPDXID_PLATFORM))\n      elif installed_file.endswith('.fsv_meta'):\n        # See build/make/core/Makefile:2988\n        doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                    relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                    id2=sbom_data.SPDXID_PLATFORM))\n      elif kernel_module_copy_files.startswith('ANDROID-GEN'):\n        # For the four files generated for _dlkm, _ramdisk partitions\n        # See build/make/core/Makefile:323\n        doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                    relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                    id2=sbom_data.SPDXID_PLATFORM))\n\n      # Process static libraries and whole static libraries the installed file links to\n      static_libs = installed_file_metadata['static_libraries']\n      whole_static_libs = installed_file_metadata['whole_static_libraries']\n      all_static_libs = (static_libs + ' ' + whole_static_libs).strip()\n      if all_static_libs:\n        for lib in all_static_libs.split(' '):\n          doc.add_relationship(sbom_data.Relationship(id1=file_id,\n                                                      relationship=sbom_data.RelationshipType.STATIC_LINK,\n                                                      id2=new_file_id(lib + '.a')))\n\n  if args.unbundled_apex:\n    doc.describes = doc.files[0].id\n\n  # Save SBOM records to output file\n  doc.generate_packages_verification_code()\n  doc.created = datetime.datetime.now(tz=datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')\n  prefix = args.output_file\n  if prefix.endswith('.spdx'):\n    prefix = prefix.removesuffix('.spdx')\n  elif prefix.endswith('.spdx.json'):\n    prefix = prefix.removesuffix('.spdx.json')\n\n  output_file = prefix + '.spdx'\n  if args.unbundled_apex:\n    output_file = prefix + '-fragment.spdx'\n  with open(output_file, 'w', encoding=\"utf-8\") as file:\n    sbom_writers.TagValueWriter.write(doc, file, fragment=args.unbundled_apex)\n  if args.json:\n    with open(prefix + '.spdx.json', 'w', encoding=\"utf-8\") as file:\n      sbom_writers.JSONWriter.write(doc, file)\n\n  save_report(prefix + '-gen-report.txt', report)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tools/sbom/sbom_data.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nDefine data classes that model SBOMs defined by SPDX. The data classes could be\nwritten out to different formats (tagvalue, JSON, etc) of SPDX with corresponding\nwriter utilities.\n\nRrefer to SPDX 2.3 spec: https://spdx.github.io/spdx-spec/v2.3/ and go/android-spdx for details of\nfields in each data class.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import List\nimport hashlib\n\nSPDXID_DOC = 'SPDXRef-DOCUMENT'\nSPDXID_PRODUCT = 'SPDXRef-PRODUCT'\nSPDXID_PLATFORM = 'SPDXRef-PLATFORM'\nSPDXID_LICENSE_APACHE = 'LicenseRef-Android-Apache-2.0'\n\nPACKAGE_NAME_PRODUCT = 'PRODUCT'\nPACKAGE_NAME_PLATFORM = 'PLATFORM'\n\nVALUE_NOASSERTION = 'NOASSERTION'\nVALUE_NONE = 'NONE'\n\n\nclass PackageExternalRefCategory:\n  SECURITY = 'SECURITY'\n  PACKAGE_MANAGER = 'PACKAGE-MANAGER'\n  PERSISTENT_ID = 'PERSISTENT-ID'\n  OTHER = 'OTHER'\n\n\nclass PackageExternalRefType:\n  cpe22Type = 'cpe22Type'\n  cpe23Type = 'cpe23Type'\n\n\n@dataclass(frozen=True)\nclass PackageExternalRef:\n  category: PackageExternalRefCategory\n  type: PackageExternalRefType\n  locator: str\n\n\n@dataclass\nclass Package:\n  name: str\n  id: str\n  version: str = None\n  supplier: str = None\n  download_location: str = None\n  files_analyzed: bool = False\n  verification_code: str = None\n  file_ids: List[str] = field(default_factory=list)\n  external_refs: List[PackageExternalRef] = field(default_factory=list)\n  declared_license_ids: List[str] = field(default_factory=list)\n\n\n@dataclass\nclass File:\n  id: str\n  name: str\n  checksum: str\n  concluded_license_ids: List[str] = field(default_factory=list)\n\n\nclass RelationshipType:\n  DESCRIBES = 'DESCRIBES'\n  VARIANT_OF = 'VARIANT_OF'\n  GENERATED_FROM = 'GENERATED_FROM'\n  CONTAINS = 'CONTAINS'\n  STATIC_LINK = 'STATIC_LINK'\n\n\n@dataclass(frozen=True)\nclass Relationship:\n  id1: str\n  relationship: RelationshipType\n  id2: str\n\n\n@dataclass(frozen=True)\nclass DocumentExternalReference:\n  id: str\n  uri: str\n  checksum: str\n\n\n@dataclass(frozen=True)\nclass License:\n  id: str\n  text: str\n  name: str\n\n\n@dataclass\nclass Document:\n  name: str\n  namespace: str\n  id: str = SPDXID_DOC\n  describes: str = SPDXID_PRODUCT\n  creators: List[str] = field(default_factory=list)\n  created: str = None\n  external_refs: List[DocumentExternalReference] = field(default_factory=list)\n  packages: List[Package] = field(default_factory=list)\n  files: List[File] = field(default_factory=list)\n  relationships: List[Relationship] = field(default_factory=list)\n  licenses: List[License] = field(default_factory=list)\n\n  def add_external_ref(self, external_ref):\n    if not any(external_ref.uri == ref.uri for ref in self.external_refs):\n      self.external_refs.append(external_ref)\n\n  def add_package(self, package):\n    p = next((p for p in self.packages if package.id == p.id), None)\n    if not p:\n      self.packages.append(package)\n    else:\n      for license_id in package.declared_license_ids:\n        if license_id not in p.declared_license_ids:\n          p.declared_license_ids.append(license_id)\n\n  def add_relationship(self, rel):\n    if not any(rel.id1 == r.id1 and rel.id2 == r.id2 and rel.relationship == r.relationship\n               for r in self.relationships):\n      self.relationships.append(rel)\n\n  def add_license(self, license):\n    if not any(license.id == l.id for l in self.licenses):\n      self.licenses.append(license)\n\n  def generate_packages_verification_code(self):\n    for package in self.packages:\n      if not package.file_ids:\n        continue\n\n      checksums = []\n      for file in self.files:\n        if file.id in package.file_ids:\n          checksums.append(file.checksum.split(': ')[1])\n      checksums.sort()\n      h = hashlib.sha1()\n      h.update(''.join(checksums).encode(encoding='utf-8'))\n      package.verification_code = h.hexdigest()\n\ndef encode_for_spdxid(s):\n  \"\"\"Simple encode for string values used in SPDXID which uses the charset of A-Za-Z0-9.-\"\"\"\n  result = ''\n  for c in s:\n    if c.isalnum() or c in '.-':\n      result += c\n    elif c in '_@/':\n      result += '-'\n    else:\n      result += '0x' + c.encode('utf-8').hex()\n\n  return result.lstrip('-')"
  },
  {
    "path": "tools/sbom/sbom_data_test.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport hashlib\nimport unittest\nimport sbom_data\n\nBUILD_FINGER_PRINT = 'build_finger_print'\nSUPPLIER_GOOGLE = 'Organization: Google'\nSUPPLIER_UPSTREAM = 'Organization: upstream'\n\nSPDXID_PREBUILT_PACKAGE1 = 'SPDXRef-PREBUILT-package1'\nSPDXID_PREBUILT_PACKAGE2 = 'SPDXRef-PREBUILT-package2'\nSPDXID_SOURCE_PACKAGE1 = 'SPDXRef-SOURCE-package1'\nSPDXID_UPSTREAM_PACKAGE1 = 'SPDXRef-UPSTREAM-package1'\n\nSPDXID_FILE1 = 'SPDXRef-file1'\nSPDXID_FILE2 = 'SPDXRef-file2'\nSPDXID_FILE3 = 'SPDXRef-file3'\nSPDXID_FILE4 = 'SPDXRef-file4'\n\nSPDXID_LICENSE1 = \"SPDXRef-License-1\"\nSPDXID_LICENSE2 = \"SPDXRef-License-2\"\n\n\nclass SBOMDataTest(unittest.TestCase):\n\n  def setUp(self):\n    # SBOM of a product\n    self.sbom_doc = sbom_data.Document(name='test doc',\n                                       namespace='http://www.google.com/sbom/spdx/android',\n                                       creators=[SUPPLIER_GOOGLE],\n                                       created='2023-03-31T22:17:58Z',\n                                       describes=sbom_data.SPDXID_PRODUCT)\n    self.sbom_doc.add_external_ref(\n        sbom_data.DocumentExternalReference(id='DocumentRef-external_doc_ref',\n                                            uri='external_doc_uri',\n                                            checksum='SHA1: 1234567890'))\n    self.sbom_doc.add_package(\n        sbom_data.Package(id=sbom_data.SPDXID_PRODUCT,\n                          name=sbom_data.PACKAGE_NAME_PRODUCT,\n                          download_location=sbom_data.VALUE_NONE,\n                          supplier=SUPPLIER_GOOGLE,\n                          version=BUILD_FINGER_PRINT,\n                          files_analyzed=True,\n                          verification_code='',\n                          file_ids=[SPDXID_FILE1, SPDXID_FILE2, SPDXID_FILE3, SPDXID_FILE4]))\n\n    self.sbom_doc.add_package(\n        sbom_data.Package(id=sbom_data.SPDXID_PLATFORM,\n                          name=sbom_data.PACKAGE_NAME_PLATFORM,\n                          download_location=sbom_data.VALUE_NONE,\n                          supplier=SUPPLIER_GOOGLE,\n                          version=BUILD_FINGER_PRINT,\n                          ))\n\n    self.sbom_doc.add_package(\n        sbom_data.Package(id=SPDXID_PREBUILT_PACKAGE1,\n                          name='Prebuilt package1',\n                          download_location=sbom_data.VALUE_NONE,\n                          supplier=SUPPLIER_GOOGLE,\n                          version=BUILD_FINGER_PRINT,\n                          ))\n\n    self.sbom_doc.add_package(\n        sbom_data.Package(id=SPDXID_SOURCE_PACKAGE1,\n                          name='Source package1',\n                          download_location=sbom_data.VALUE_NONE,\n                          supplier=SUPPLIER_GOOGLE,\n                          version=BUILD_FINGER_PRINT,\n                          external_refs=[sbom_data.PackageExternalRef(\n                              category=sbom_data.PackageExternalRefCategory.SECURITY,\n                              type=sbom_data.PackageExternalRefType.cpe22Type,\n                              locator='cpe:/a:jsoncpp_project:jsoncpp:1.9.4')]\n                          ))\n\n    self.sbom_doc.add_package(\n        sbom_data.Package(id=SPDXID_UPSTREAM_PACKAGE1,\n                          name='Upstream package1',\n                          supplier=SUPPLIER_UPSTREAM,\n                          version='1.1',\n                          ))\n\n    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_SOURCE_PACKAGE1,\n                                                          relationship=sbom_data.RelationshipType.VARIANT_OF,\n                                                          id2=SPDXID_UPSTREAM_PACKAGE1))\n\n    self.sbom_doc.files.append(\n        sbom_data.File(id=SPDXID_FILE1, name='/bin/file1',\n                       checksum='SHA1: 356a192b7913b04c54574d18c28d46e6395428ab'))  # sha1 hash of 1\n    self.sbom_doc.files.append(\n        sbom_data.File(id=SPDXID_FILE2, name='/bin/file2',\n                       checksum='SHA1: da4b9237bacccdf19c0760cab7aec4a8359010b0'))  # sha1 hash of 2\n    self.sbom_doc.files.append(\n        sbom_data.File(id=SPDXID_FILE3, name='/bin/file3',\n                       checksum='SHA1: 77de68daecd823babbb58edb1c8e14d7106e83bb'))  # sha1 hash of 3\n    self.sbom_doc.files.append(\n        sbom_data.File(id=SPDXID_FILE4, name='file4.a',\n                       checksum='SHA1: 1b6453892473a467d07372d45eb05abc2031647a'))  # sha1 of 4\n\n    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE1,\n                                                          relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                          id2=sbom_data.SPDXID_PLATFORM))\n    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE2,\n                                                          relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                          id2=SPDXID_PREBUILT_PACKAGE1))\n    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE3,\n                                                          relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                          id2=SPDXID_SOURCE_PACKAGE1\n                                                          ))\n    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE1,\n                                                          relationship=sbom_data.RelationshipType.STATIC_LINK,\n                                                          id2=SPDXID_FILE4\n                                                          ))\n\n  def test_package_verification_code(self):\n    checksums = []\n    for file in self.sbom_doc.files:\n      checksums.append(file.checksum.split(': ')[1])\n      checksums.sort()\n    h = hashlib.sha1()\n    h.update(''.join(checksums).encode(encoding='utf-8'))\n    expected_package_verification_code = h.hexdigest()\n\n    self.sbom_doc.generate_packages_verification_code()\n    self.assertEqual(expected_package_verification_code, self.sbom_doc.packages[0].verification_code)\n\n  def test_add_package_(self):\n    self.sbom_doc.add_package(sbom_data.Package(id=SPDXID_PREBUILT_PACKAGE2,\n                                                name='Prebuilt package2',\n                                                download_location=sbom_data.VALUE_NONE,\n                                                supplier=SUPPLIER_GOOGLE,\n                                                version=BUILD_FINGER_PRINT,\n                                                ))\n    p = next((p for p in self.sbom_doc.packages if p.id == SPDXID_PREBUILT_PACKAGE2), None)\n    self.assertNotEqual(p, None)\n    self.assertEqual(p.declared_license_ids, [])\n\n    # Add same package with license 1\n    self.sbom_doc.add_package(sbom_data.Package(id=SPDXID_PREBUILT_PACKAGE2,\n                                                name='Prebuilt package2',\n                                                download_location=sbom_data.VALUE_NONE,\n                                                supplier=SUPPLIER_GOOGLE,\n                                                version=BUILD_FINGER_PRINT,\n                                                declared_license_ids=[SPDXID_LICENSE1]\n                                                ))\n    self.assertEqual(p.declared_license_ids, [SPDXID_LICENSE1])\n\n    # Add same package with license 2\n    self.sbom_doc.add_package(sbom_data.Package(id=SPDXID_PREBUILT_PACKAGE2,\n                                                name='Prebuilt package2',\n                                                download_location=sbom_data.VALUE_NONE,\n                                                supplier=SUPPLIER_GOOGLE,\n                                                version=BUILD_FINGER_PRINT,\n                                                declared_license_ids=[SPDXID_LICENSE2]\n                                                ))\n    self.assertEqual(p.declared_license_ids, [SPDXID_LICENSE1, SPDXID_LICENSE2])\n\n    # Add same package with license 2 again\n    self.sbom_doc.add_package(sbom_data.Package(id=SPDXID_PREBUILT_PACKAGE2,\n                                                name='Prebuilt package2',\n                                                download_location=sbom_data.VALUE_NONE,\n                                                supplier=SUPPLIER_GOOGLE,\n                                                version=BUILD_FINGER_PRINT,\n                                                declared_license_ids=[SPDXID_LICENSE2]\n                                                ))\n    self.assertEqual(p.declared_license_ids, [SPDXID_LICENSE1, SPDXID_LICENSE2])\n\n\nif __name__ == '__main__':\n  unittest.main(verbosity=2)\n"
  },
  {
    "path": "tools/sbom/sbom_writers.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nSerialize objects defined in package sbom_data to SPDX format: tagvalue, JSON.\n\"\"\"\n\nimport json\nimport sbom_data\n\nSPDX_VER = 'SPDX-2.3'\nDATA_LIC = 'CC0-1.0'\n\n\nclass Tags:\n  # Common\n  SPDXID = 'SPDXID'\n  SPDX_VERSION = 'SPDXVersion'\n  DATA_LICENSE = 'DataLicense'\n  DOCUMENT_NAME = 'DocumentName'\n  DOCUMENT_NAMESPACE = 'DocumentNamespace'\n  CREATED = 'Created'\n  CREATOR = 'Creator'\n  EXTERNAL_DOCUMENT_REF = 'ExternalDocumentRef'\n\n  # Package\n  PACKAGE_NAME = 'PackageName'\n  PACKAGE_DOWNLOAD_LOCATION = 'PackageDownloadLocation'\n  PACKAGE_VERSION = 'PackageVersion'\n  PACKAGE_SUPPLIER = 'PackageSupplier'\n  FILES_ANALYZED = 'FilesAnalyzed'\n  PACKAGE_VERIFICATION_CODE = 'PackageVerificationCode'\n  PACKAGE_EXTERNAL_REF = 'ExternalRef'\n  # Package license\n  PACKAGE_LICENSE_CONCLUDED = 'PackageLicenseConcluded'\n  PACKAGE_LICENSE_INFO_FROM_FILES = 'PackageLicenseInfoFromFiles'\n  PACKAGE_LICENSE_DECLARED = 'PackageLicenseDeclared'\n  PACKAGE_LICENSE_COMMENTS = 'PackageLicenseComments'\n\n  # File\n  FILE_NAME = 'FileName'\n  FILE_CHECKSUM = 'FileChecksum'\n  # File license\n  FILE_LICENSE_CONCLUDED = 'LicenseConcluded'\n  FILE_LICENSE_INFO_IN_FILE = 'LicenseInfoInFile'\n  FILE_LICENSE_COMMENTS = 'LicenseComments'\n  FILE_COPYRIGHT_TEXT = 'FileCopyrightText'\n  FILE_NOTICE = 'FileNotice'\n  FILE_ATTRIBUTION_TEXT = 'FileAttributionText'\n\n  # Relationship\n  RELATIONSHIP = 'Relationship'\n\n  # License\n  LICENSE_ID = 'LicenseID'\n  LICENSE_NAME = 'LicenseName'\n  LICENSE_EXTRACTED_TEXT = 'ExtractedText'\n\n\nclass TagValueWriter:\n  @staticmethod\n  def marshal_doc_headers(sbom_doc):\n    headers = [\n      f'{Tags.SPDX_VERSION}: {SPDX_VER}',\n      f'{Tags.DATA_LICENSE}: {DATA_LIC}',\n      f'{Tags.SPDXID}: {sbom_doc.id}',\n      f'{Tags.DOCUMENT_NAME}: {sbom_doc.name}',\n      f'{Tags.DOCUMENT_NAMESPACE}: {sbom_doc.namespace}',\n    ]\n    for creator in sbom_doc.creators:\n      headers.append(f'{Tags.CREATOR}: {creator}')\n    headers.append(f'{Tags.CREATED}: {sbom_doc.created}')\n    for doc_ref in sbom_doc.external_refs:\n      headers.append(\n        f'{Tags.EXTERNAL_DOCUMENT_REF}: {doc_ref.id} {doc_ref.uri} {doc_ref.checksum}')\n    headers.append('')\n    return headers\n\n  @staticmethod\n  def marshal_package(sbom_doc, package, fragment):\n    download_location = sbom_data.VALUE_NOASSERTION\n    if package.download_location:\n      download_location = package.download_location\n    tagvalues = [\n      f'{Tags.PACKAGE_NAME}: {package.name}',\n      f'{Tags.SPDXID}: {package.id}',\n      f'{Tags.PACKAGE_DOWNLOAD_LOCATION}: {download_location}',\n      f'{Tags.FILES_ANALYZED}: {str(package.files_analyzed).lower()}',\n    ]\n    if package.version:\n      tagvalues.append(f'{Tags.PACKAGE_VERSION}: {package.version}')\n    if package.supplier:\n      tagvalues.append(f'{Tags.PACKAGE_SUPPLIER}: {package.supplier}')\n\n    license = sbom_data.VALUE_NOASSERTION\n    if package.declared_license_ids:\n      license = ' OR '.join(package.declared_license_ids)\n    tagvalues.append(f'{Tags.PACKAGE_LICENSE_DECLARED}: {license}')\n\n    if package.verification_code:\n      tagvalues.append(f'{Tags.PACKAGE_VERIFICATION_CODE}: {package.verification_code}')\n    if package.external_refs:\n      for external_ref in package.external_refs:\n        tagvalues.append(\n          f'{Tags.PACKAGE_EXTERNAL_REF}: {external_ref.category} {external_ref.type} {external_ref.locator}')\n\n    tagvalues.append('')\n\n    if package.id == sbom_doc.describes and not fragment:\n      tagvalues.append(\n          f'{Tags.RELATIONSHIP}: {sbom_doc.id} {sbom_data.RelationshipType.DESCRIBES} {sbom_doc.describes}')\n      tagvalues.append('')\n\n    for file in sbom_doc.files:\n      if file.id in package.file_ids:\n        tagvalues += TagValueWriter.marshal_file(file)\n\n    return tagvalues\n\n  @staticmethod\n  def marshal_packages(sbom_doc, fragment):\n    tagvalues = []\n    marshaled_relationships = []\n    i = 0\n    packages = sbom_doc.packages\n    while i < len(packages):\n      if (i + 1 < len(packages)\n          and packages[i].id.startswith('SPDXRef-SOURCE-')\n          and packages[i + 1].id.startswith('SPDXRef-UPSTREAM-')):\n        # Output SOURCE, UPSTREAM packages and their VARIANT_OF relationship together, so they are close to each other\n        # in SBOMs in tagvalue format.\n        tagvalues += TagValueWriter.marshal_package(sbom_doc, packages[i], fragment)\n        tagvalues += TagValueWriter.marshal_package(sbom_doc, packages[i + 1], fragment)\n        rel = next((r for r in sbom_doc.relationships if\n                    r.id1 == packages[i].id and\n                    r.id2 == packages[i + 1].id and\n                    r.relationship == sbom_data.RelationshipType.VARIANT_OF), None)\n        if rel:\n          marshaled_relationships.append(rel)\n          tagvalues.append(TagValueWriter.marshal_relationship(rel))\n          tagvalues.append('')\n\n        i += 2\n      else:\n        tagvalues += TagValueWriter.marshal_package(sbom_doc, packages[i], fragment)\n        i += 1\n\n    return tagvalues, marshaled_relationships\n\n  @staticmethod\n  def marshal_file(file):\n    tagvalues = [\n      f'{Tags.FILE_NAME}: {file.name}',\n      f'{Tags.SPDXID}: {file.id}',\n      f'{Tags.FILE_CHECKSUM}: {file.checksum}',\n    ]\n    license = sbom_data.VALUE_NOASSERTION\n    if file.concluded_license_ids:\n      license = ' OR '.join(file.concluded_license_ids)\n    tagvalues.append(f'{Tags.FILE_LICENSE_CONCLUDED}: {license}')\n    tagvalues.append('')\n\n    return tagvalues\n\n  @staticmethod\n  def marshal_files(sbom_doc, fragment):\n    tagvalues = []\n    files_in_packages = []\n    for package in sbom_doc.packages:\n      files_in_packages += package.file_ids\n    for file in sbom_doc.files:\n      if file.id in files_in_packages:\n        continue\n      tagvalues += TagValueWriter.marshal_file(file)\n      if file.id == sbom_doc.describes and not fragment:\n        # Fragment is not a full SBOM document so the relationship DESCRIBES is not applicable.\n        tagvalues.append(\n            f'{Tags.RELATIONSHIP}: {sbom_doc.id} {sbom_data.RelationshipType.DESCRIBES} {sbom_doc.describes}')\n        tagvalues.append('')\n    return tagvalues\n\n  @staticmethod\n  def marshal_relationship(rel):\n    return f'{Tags.RELATIONSHIP}: {rel.id1} {rel.relationship} {rel.id2}'\n\n  @staticmethod\n  def marshal_relationships(sbom_doc, marshaled_rels):\n    tagvalues = []\n    sorted_rels = sorted(sbom_doc.relationships, key=lambda r: r.id2 + r.id1)\n    for rel in sorted_rels:\n      if any(r.id1 == rel.id1 and r.id2 == rel.id2 and r.relationship == rel.relationship\n             for r in marshaled_rels):\n        continue\n      tagvalues.append(TagValueWriter.marshal_relationship(rel))\n    tagvalues.append('')\n    return tagvalues\n\n  @staticmethod\n  def marshal_license(license):\n    tagvalues = []\n    tagvalues.append(f'{Tags.LICENSE_ID}: {license.id}')\n    tagvalues.append(f'{Tags.LICENSE_NAME}: {license.name}')\n    tagvalues.append(f'{Tags.LICENSE_EXTRACTED_TEXT}: <text>{license.text}</text>')\n    return tagvalues\n\n  @staticmethod\n  def marshal_licenses(sbom_doc):\n    tagvalues = []\n    for license in sbom_doc.licenses:\n      tagvalues += TagValueWriter.marshal_license(license)\n      tagvalues.append('')\n    return tagvalues\n\n  @staticmethod\n  def write(sbom_doc, file, fragment=False):\n    content = []\n    if not fragment:\n      content += TagValueWriter.marshal_doc_headers(sbom_doc)\n    content += TagValueWriter.marshal_files(sbom_doc, fragment)\n    tagvalues, marshaled_relationships = TagValueWriter.marshal_packages(sbom_doc, fragment)\n    content += tagvalues\n    content += TagValueWriter.marshal_relationships(sbom_doc, marshaled_relationships)\n    content += TagValueWriter.marshal_licenses(sbom_doc)\n    file.write('\\n'.join(content))\n\n\nclass PropNames:\n  # Common\n  SPDXID = 'SPDXID'\n  SPDX_VERSION = 'spdxVersion'\n  DATA_LICENSE = 'dataLicense'\n  NAME = 'name'\n  DOCUMENT_NAMESPACE = 'documentNamespace'\n  CREATION_INFO = 'creationInfo'\n  CREATORS = 'creators'\n  CREATED = 'created'\n  EXTERNAL_DOCUMENT_REF = 'externalDocumentRefs'\n  DOCUMENT_DESCRIBES = 'documentDescribes'\n  EXTERNAL_DOCUMENT_ID = 'externalDocumentId'\n  EXTERNAL_DOCUMENT_URI = 'spdxDocument'\n  EXTERNAL_DOCUMENT_CHECKSUM = 'checksum'\n  ALGORITHM = 'algorithm'\n  CHECKSUM_VALUE = 'checksumValue'\n\n  # Package\n  PACKAGES = 'packages'\n  PACKAGE_DOWNLOAD_LOCATION = 'downloadLocation'\n  PACKAGE_VERSION = 'versionInfo'\n  PACKAGE_SUPPLIER = 'supplier'\n  FILES_ANALYZED = 'filesAnalyzed'\n  PACKAGE_VERIFICATION_CODE = 'packageVerificationCode'\n  PACKAGE_VERIFICATION_CODE_VALUE = 'packageVerificationCodeValue'\n  PACKAGE_EXTERNAL_REFS = 'externalRefs'\n  PACKAGE_EXTERNAL_REF_CATEGORY = 'referenceCategory'\n  PACKAGE_EXTERNAL_REF_TYPE = 'referenceType'\n  PACKAGE_EXTERNAL_REF_LOCATOR = 'referenceLocator'\n  PACKAGE_HAS_FILES = 'hasFiles'\n  PACKAGE_LICENSE_DECLARED = 'licenseDeclared'\n\n  # File\n  FILES = 'files'\n  FILE_NAME = 'fileName'\n  FILE_CHECKSUMS = 'checksums'\n  FILE_LICENSE_CONCLUDED = 'licenseConcluded'\n\n  # Relationship\n  RELATIONSHIPS = 'relationships'\n  REL_ELEMENT_ID = 'spdxElementId'\n  REL_RELATED_ELEMENT_ID = 'relatedSpdxElement'\n  REL_TYPE = 'relationshipType'\n\n  # License\n  LICENSES = 'hasExtractedLicensingInfos'\n  LICENSE_ID = 'licenseId'\n  LICENSE_NAME = 'name'\n  LICENSE_EXTRACTED_TEXT = 'extractedText'\n\n\nclass JSONWriter:\n  @staticmethod\n  def marshal_doc_headers(sbom_doc):\n    headers = {\n      PropNames.SPDX_VERSION: SPDX_VER,\n      PropNames.DATA_LICENSE: DATA_LIC,\n      PropNames.SPDXID: sbom_doc.id,\n      PropNames.NAME: sbom_doc.name,\n      PropNames.DOCUMENT_NAMESPACE: sbom_doc.namespace,\n      PropNames.CREATION_INFO: {}\n    }\n    creators = [creator for creator in sbom_doc.creators]\n    headers[PropNames.CREATION_INFO][PropNames.CREATORS] = creators\n    headers[PropNames.CREATION_INFO][PropNames.CREATED] = sbom_doc.created\n    external_refs = []\n    for doc_ref in sbom_doc.external_refs:\n      checksum = doc_ref.checksum.split(': ')\n      external_refs.append({\n        PropNames.EXTERNAL_DOCUMENT_ID: f'{doc_ref.id}',\n        PropNames.EXTERNAL_DOCUMENT_URI: doc_ref.uri,\n        PropNames.EXTERNAL_DOCUMENT_CHECKSUM: {\n          PropNames.ALGORITHM: checksum[0],\n          PropNames.CHECKSUM_VALUE: checksum[1]\n        }\n      })\n    if external_refs:\n      headers[PropNames.EXTERNAL_DOCUMENT_REF] = external_refs\n    headers[PropNames.DOCUMENT_DESCRIBES] = [sbom_doc.describes]\n\n    return headers\n\n  @staticmethod\n  def marshal_packages(sbom_doc):\n    packages = []\n    for p in sbom_doc.packages:\n      package = {\n        PropNames.NAME: p.name,\n        PropNames.SPDXID: p.id,\n        PropNames.PACKAGE_DOWNLOAD_LOCATION: p.download_location if p.download_location else sbom_data.VALUE_NOASSERTION,\n        PropNames.FILES_ANALYZED: p.files_analyzed\n      }\n      if p.version:\n        package[PropNames.PACKAGE_VERSION] = p.version\n      if p.supplier:\n        package[PropNames.PACKAGE_SUPPLIER] = p.supplier\n      package[PropNames.PACKAGE_LICENSE_DECLARED] = sbom_data.VALUE_NOASSERTION\n      if p.declared_license_ids:\n        package[PropNames.PACKAGE_LICENSE_DECLARED] = ' OR '.join(p.declared_license_ids)\n      if p.verification_code:\n        package[PropNames.PACKAGE_VERIFICATION_CODE] = {\n          PropNames.PACKAGE_VERIFICATION_CODE_VALUE: p.verification_code\n        }\n      if p.external_refs:\n        package[PropNames.PACKAGE_EXTERNAL_REFS] = []\n        for ref in p.external_refs:\n          ext_ref = {\n            PropNames.PACKAGE_EXTERNAL_REF_CATEGORY: ref.category,\n            PropNames.PACKAGE_EXTERNAL_REF_TYPE: ref.type,\n            PropNames.PACKAGE_EXTERNAL_REF_LOCATOR: ref.locator,\n          }\n          package[PropNames.PACKAGE_EXTERNAL_REFS].append(ext_ref)\n      if p.file_ids:\n        package[PropNames.PACKAGE_HAS_FILES] = []\n        for file_id in p.file_ids:\n          package[PropNames.PACKAGE_HAS_FILES].append(file_id)\n\n      packages.append(package)\n\n    return {PropNames.PACKAGES: packages}\n\n  @staticmethod\n  def marshal_files(sbom_doc):\n    files = []\n    for f in sbom_doc.files:\n      file = {\n        PropNames.FILE_NAME: f.name,\n        PropNames.SPDXID: f.id\n      }\n      checksum = f.checksum.split(': ')\n      file[PropNames.FILE_CHECKSUMS] = [{\n        PropNames.ALGORITHM: checksum[0],\n        PropNames.CHECKSUM_VALUE: checksum[1],\n      }]\n      file[PropNames.FILE_LICENSE_CONCLUDED] = sbom_data.VALUE_NOASSERTION\n      if f.concluded_license_ids:\n        file[PropNames.FILE_LICENSE_CONCLUDED] = ' OR '.join(f.concluded_license_ids)\n      files.append(file)\n    return {PropNames.FILES: files}\n\n  @staticmethod\n  def marshal_relationships(sbom_doc):\n    relationships = []\n    sorted_rels = sorted(sbom_doc.relationships, key=lambda r: r.relationship + r.id2 + r.id1)\n    for r in sorted_rels:\n      rel = {\n        PropNames.REL_ELEMENT_ID: r.id1,\n        PropNames.REL_RELATED_ELEMENT_ID: r.id2,\n        PropNames.REL_TYPE: r.relationship,\n      }\n      relationships.append(rel)\n\n    return {PropNames.RELATIONSHIPS: relationships}\n\n  @staticmethod\n  def marshal_licenses(sbom_doc):\n    licenses = []\n    for l in sbom_doc.licenses:\n      licenses.append({\n          PropNames.LICENSE_ID: l.id,\n          PropNames.LICENSE_NAME: l.name,\n          PropNames.LICENSE_EXTRACTED_TEXT: f'<text>{l.text}</text>'\n      })\n    return {PropNames.LICENSES: licenses}\n\n  @staticmethod\n  def write(sbom_doc, file):\n    doc = {}\n    doc.update(JSONWriter.marshal_doc_headers(sbom_doc))\n    doc.update(JSONWriter.marshal_packages(sbom_doc))\n    doc.update(JSONWriter.marshal_files(sbom_doc))\n    doc.update(JSONWriter.marshal_relationships(sbom_doc))\n    doc.update(JSONWriter.marshal_licenses(sbom_doc))\n    file.write(json.dumps(doc, indent=4))\n"
  },
  {
    "path": "tools/sbom/sbom_writers_test.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport io\nimport pathlib\nimport unittest\nimport sbom_data\nimport sbom_writers\n\nBUILD_FINGER_PRINT = 'build_finger_print'\nSUPPLIER_GOOGLE = 'Organization: Google'\nSUPPLIER_UPSTREAM = 'Organization: upstream'\n\nSPDXID_PREBUILT_PACKAGE1 = 'SPDXRef-PREBUILT-package1'\nSPDXID_SOURCE_PACKAGE1 = 'SPDXRef-SOURCE-package1'\nSPDXID_UPSTREAM_PACKAGE1 = 'SPDXRef-UPSTREAM-package1'\n\nSPDXID_FILE1 = 'SPDXRef-file1'\nSPDXID_FILE2 = 'SPDXRef-file2'\nSPDXID_FILE3 = 'SPDXRef-file3'\nSPDXID_FILE4 = 'SPDXRef-file4'\n\nSPDXID_LICENSE_1 = 'LicenseRef-Android-License-1'\nSPDXID_LICENSE_2 = 'LicenseRef-Android-License-2'\nSPDXID_LICENSE_3 = 'LicenseRef-Android-License-3'\n\nLICENSE_APACHE_TEXT = \"LICENSE_APACHE\"\nLICENSE1_TEXT = 'LICENSE 1'\nLICENSE2_TEXT = 'LICENSE 2'\nLICENSE3_TEXT = 'LICENSE 3'\n\nclass SBOMWritersTest(unittest.TestCase):\n\n  def setUp(self):\n    # SBOM of a product\n    self.sbom_doc = sbom_data.Document(name='test doc',\n                                       namespace='http://www.google.com/sbom/spdx/android',\n                                       creators=[SUPPLIER_GOOGLE],\n                                       created='2023-03-31T22:17:58Z',\n                                       describes=sbom_data.SPDXID_PRODUCT)\n    self.sbom_doc.add_external_ref(\n      sbom_data.DocumentExternalReference(id='DocumentRef-external_doc_ref',\n                                          uri='external_doc_uri',\n                                          checksum='SHA1: 1234567890'))\n    self.sbom_doc.add_package(\n      sbom_data.Package(id=sbom_data.SPDXID_PRODUCT,\n                        name=sbom_data.PACKAGE_NAME_PRODUCT,\n                        download_location=sbom_data.VALUE_NONE,\n                        supplier=SUPPLIER_GOOGLE,\n                        version=BUILD_FINGER_PRINT,\n                        files_analyzed=True,\n                        verification_code='123456',\n                        file_ids=[SPDXID_FILE1, SPDXID_FILE2, SPDXID_FILE3]))\n\n    self.sbom_doc.add_package(\n      sbom_data.Package(id=sbom_data.SPDXID_PLATFORM,\n                        name=sbom_data.PACKAGE_NAME_PLATFORM,\n                        download_location=sbom_data.VALUE_NONE,\n                        supplier=SUPPLIER_GOOGLE,\n                        version=BUILD_FINGER_PRINT,\n                        declared_license_ids=[sbom_data.SPDXID_LICENSE_APACHE]\n                        ))\n\n    self.sbom_doc.add_package(\n      sbom_data.Package(id=SPDXID_PREBUILT_PACKAGE1,\n                        name='Prebuilt package1',\n                        download_location=sbom_data.VALUE_NONE,\n                        supplier=SUPPLIER_GOOGLE,\n                        version=BUILD_FINGER_PRINT,\n                        declared_license_ids=[SPDXID_LICENSE_1],\n                        ))\n\n    self.sbom_doc.add_package(\n      sbom_data.Package(id=SPDXID_SOURCE_PACKAGE1,\n                        name='Source package1',\n                        download_location=sbom_data.VALUE_NONE,\n                        supplier=SUPPLIER_GOOGLE,\n                        version=BUILD_FINGER_PRINT,\n                        declared_license_ids=[SPDXID_LICENSE_2, SPDXID_LICENSE_3],\n                        external_refs=[sbom_data.PackageExternalRef(\n                          category=sbom_data.PackageExternalRefCategory.SECURITY,\n                          type=sbom_data.PackageExternalRefType.cpe22Type,\n                          locator='cpe:/a:jsoncpp_project:jsoncpp:1.9.4')]\n                        ))\n\n    self.sbom_doc.add_package(\n      sbom_data.Package(id=SPDXID_UPSTREAM_PACKAGE1,\n                        name='Upstream package1',\n                        supplier=SUPPLIER_UPSTREAM,\n                        version='1.1',\n                        declared_license_ids=[SPDXID_LICENSE_2, SPDXID_LICENSE_3],\n                        ))\n\n    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_SOURCE_PACKAGE1,\n                                                          relationship=sbom_data.RelationshipType.VARIANT_OF,\n                                                          id2=SPDXID_UPSTREAM_PACKAGE1))\n\n    self.sbom_doc.files.append(\n      sbom_data.File(id=SPDXID_FILE1, name='/bin/file1', checksum='SHA1: 11111', concluded_license_ids=[sbom_data.SPDXID_LICENSE_APACHE]))\n    self.sbom_doc.files.append(\n      sbom_data.File(id=SPDXID_FILE2, name='/bin/file2', checksum='SHA1: 22222', concluded_license_ids=[SPDXID_LICENSE_1]))\n    self.sbom_doc.files.append(\n      sbom_data.File(id=SPDXID_FILE3, name='/bin/file3', checksum='SHA1: 33333', concluded_license_ids=[SPDXID_LICENSE_2, SPDXID_LICENSE_3]))\n    self.sbom_doc.files.append(\n      sbom_data.File(id=SPDXID_FILE4, name='file4.a', checksum='SHA1: 44444'))\n\n    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE1,\n                                                          relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                          id2=sbom_data.SPDXID_PLATFORM))\n    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE2,\n                                                          relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                          id2=SPDXID_PREBUILT_PACKAGE1))\n    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE3,\n                                                          relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                          id2=SPDXID_SOURCE_PACKAGE1\n                                                          ))\n    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE1,\n                                                          relationship=sbom_data.RelationshipType.STATIC_LINK,\n                                                          id2=SPDXID_FILE4\n                                                          ))\n\n    self.sbom_doc.add_license(sbom_data.License(sbom_data.SPDXID_LICENSE_APACHE, LICENSE_APACHE_TEXT, \"License-Apache\"))\n    self.sbom_doc.add_license(sbom_data.License(SPDXID_LICENSE_1, LICENSE1_TEXT, \"License-1\"))\n    self.sbom_doc.add_license(sbom_data.License(SPDXID_LICENSE_2, LICENSE2_TEXT, \"License-2\"))\n    self.sbom_doc.add_license(sbom_data.License(SPDXID_LICENSE_3, LICENSE3_TEXT, \"License-3\"))\n\n    # SBOM fragment of a APK\n    self.unbundled_sbom_doc = sbom_data.Document(name='test doc',\n                                                 namespace='http://www.google.com/sbom/spdx/android',\n                                                 creators=[SUPPLIER_GOOGLE],\n                                                 created='2023-03-31T22:17:58Z',\n                                                 describes=SPDXID_FILE1)\n\n    self.unbundled_sbom_doc.files.append(\n      sbom_data.File(id=SPDXID_FILE1, name='/bin/file1.apk', checksum='SHA1: 11111'))\n    self.unbundled_sbom_doc.add_package(\n      sbom_data.Package(id=SPDXID_SOURCE_PACKAGE1,\n                        name='Unbundled apk package',\n                        download_location=sbom_data.VALUE_NONE,\n                        supplier=SUPPLIER_GOOGLE,\n                        version=BUILD_FINGER_PRINT))\n    self.unbundled_sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE1,\n                                                                    relationship=sbom_data.RelationshipType.GENERATED_FROM,\n                                                                    id2=SPDXID_SOURCE_PACKAGE1))\n\n  def test_tagvalue_writer(self):\n    with io.StringIO() as output:\n      sbom_writers.TagValueWriter.write(self.sbom_doc, output)\n      expected_output = pathlib.Path('testdata/expected_tagvalue_sbom.spdx').read_text()\n      self.maxDiff = None\n      self.assertEqual(expected_output, output.getvalue())\n\n  def test_tagvalue_writer_doc_describes_file(self):\n    with io.StringIO() as output:\n      self.sbom_doc.describes = SPDXID_FILE4\n      sbom_writers.TagValueWriter.write(self.sbom_doc, output)\n      expected_output = pathlib.Path('testdata/expected_tagvalue_sbom_doc_describes_file.spdx').read_text()\n      self.maxDiff = None\n      self.assertEqual(expected_output, output.getvalue())\n\n  def test_tagvalue_writer_unbundled(self):\n    with io.StringIO() as output:\n      sbom_writers.TagValueWriter.write(self.unbundled_sbom_doc, output, fragment=True)\n      expected_output = pathlib.Path('testdata/expected_tagvalue_sbom_unbundled.spdx').read_text()\n      self.maxDiff = None\n      self.assertEqual(expected_output, output.getvalue())\n\n  def test_json_writer(self):\n    with io.StringIO() as output:\n      sbom_writers.JSONWriter.write(self.sbom_doc, output)\n      expected_output = pathlib.Path('testdata/expected_json_sbom.spdx.json').read_text()\n      self.maxDiff = None\n      self.assertEqual(expected_output, output.getvalue())\n\n\nif __name__ == '__main__':\n  unittest.main(verbosity=2)\n"
  },
  {
    "path": "tools/sbom/testdata/expected_json_sbom.spdx.json",
    "content": "{\n    \"spdxVersion\": \"SPDX-2.3\",\n    \"dataLicense\": \"CC0-1.0\",\n    \"SPDXID\": \"SPDXRef-DOCUMENT\",\n    \"name\": \"test doc\",\n    \"documentNamespace\": \"http://www.google.com/sbom/spdx/android\",\n    \"creationInfo\": {\n        \"creators\": [\n            \"Organization: Google\"\n        ],\n        \"created\": \"2023-03-31T22:17:58Z\"\n    },\n    \"externalDocumentRefs\": [\n        {\n            \"externalDocumentId\": \"DocumentRef-external_doc_ref\",\n            \"spdxDocument\": \"external_doc_uri\",\n            \"checksum\": {\n                \"algorithm\": \"SHA1\",\n                \"checksumValue\": \"1234567890\"\n            }\n        }\n    ],\n    \"documentDescribes\": [\n        \"SPDXRef-PRODUCT\"\n    ],\n    \"packages\": [\n        {\n            \"name\": \"PRODUCT\",\n            \"SPDXID\": \"SPDXRef-PRODUCT\",\n            \"downloadLocation\": \"NONE\",\n            \"filesAnalyzed\": true,\n            \"versionInfo\": \"build_finger_print\",\n            \"supplier\": \"Organization: Google\",\n            \"licenseDeclared\": \"NOASSERTION\",\n            \"packageVerificationCode\": {\n                \"packageVerificationCodeValue\": \"123456\"\n            },\n            \"hasFiles\": [\n                \"SPDXRef-file1\",\n                \"SPDXRef-file2\",\n                \"SPDXRef-file3\"\n            ]\n        },\n        {\n            \"name\": \"PLATFORM\",\n            \"SPDXID\": \"SPDXRef-PLATFORM\",\n            \"downloadLocation\": \"NONE\",\n            \"filesAnalyzed\": false,\n            \"versionInfo\": \"build_finger_print\",\n            \"supplier\": \"Organization: Google\",\n            \"licenseDeclared\": \"LicenseRef-Android-Apache-2.0\"\n        },\n        {\n            \"name\": \"Prebuilt package1\",\n            \"SPDXID\": \"SPDXRef-PREBUILT-package1\",\n            \"downloadLocation\": \"NONE\",\n            \"filesAnalyzed\": false,\n            \"versionInfo\": \"build_finger_print\",\n            \"supplier\": \"Organization: Google\",\n            \"licenseDeclared\": \"LicenseRef-Android-License-1\"\n        },\n        {\n            \"name\": \"Source package1\",\n            \"SPDXID\": \"SPDXRef-SOURCE-package1\",\n            \"downloadLocation\": \"NONE\",\n            \"filesAnalyzed\": false,\n            \"versionInfo\": \"build_finger_print\",\n            \"supplier\": \"Organization: Google\",\n            \"licenseDeclared\": \"LicenseRef-Android-License-2 OR LicenseRef-Android-License-3\",\n            \"externalRefs\": [\n                {\n                    \"referenceCategory\": \"SECURITY\",\n                    \"referenceType\": \"cpe22Type\",\n                    \"referenceLocator\": \"cpe:/a:jsoncpp_project:jsoncpp:1.9.4\"\n                }\n            ]\n        },\n        {\n            \"name\": \"Upstream package1\",\n            \"SPDXID\": \"SPDXRef-UPSTREAM-package1\",\n            \"downloadLocation\": \"NOASSERTION\",\n            \"filesAnalyzed\": false,\n            \"versionInfo\": \"1.1\",\n            \"supplier\": \"Organization: upstream\",\n            \"licenseDeclared\": \"LicenseRef-Android-License-2 OR LicenseRef-Android-License-3\"\n        }\n    ],\n    \"files\": [\n        {\n            \"fileName\": \"/bin/file1\",\n            \"SPDXID\": \"SPDXRef-file1\",\n            \"checksums\": [\n                {\n                    \"algorithm\": \"SHA1\",\n                    \"checksumValue\": \"11111\"\n                }\n            ],\n            \"licenseConcluded\": \"LicenseRef-Android-Apache-2.0\"\n        },\n        {\n            \"fileName\": \"/bin/file2\",\n            \"SPDXID\": \"SPDXRef-file2\",\n            \"checksums\": [\n                {\n                    \"algorithm\": \"SHA1\",\n                    \"checksumValue\": \"22222\"\n                }\n            ],\n            \"licenseConcluded\": \"LicenseRef-Android-License-1\"\n        },\n        {\n            \"fileName\": \"/bin/file3\",\n            \"SPDXID\": \"SPDXRef-file3\",\n            \"checksums\": [\n                {\n                    \"algorithm\": \"SHA1\",\n                    \"checksumValue\": \"33333\"\n                }\n            ],\n            \"licenseConcluded\": \"LicenseRef-Android-License-2 OR LicenseRef-Android-License-3\"\n        },\n        {\n            \"fileName\": \"file4.a\",\n            \"SPDXID\": \"SPDXRef-file4\",\n            \"checksums\": [\n                {\n                    \"algorithm\": \"SHA1\",\n                    \"checksumValue\": \"44444\"\n                }\n            ],\n            \"licenseConcluded\": \"NOASSERTION\"\n        }\n    ],\n    \"relationships\": [\n        {\n            \"spdxElementId\": \"SPDXRef-file1\",\n            \"relatedSpdxElement\": \"SPDXRef-PLATFORM\",\n            \"relationshipType\": \"GENERATED_FROM\"\n        },\n        {\n            \"spdxElementId\": \"SPDXRef-file2\",\n            \"relatedSpdxElement\": \"SPDXRef-PREBUILT-package1\",\n            \"relationshipType\": \"GENERATED_FROM\"\n        },\n        {\n            \"spdxElementId\": \"SPDXRef-file3\",\n            \"relatedSpdxElement\": \"SPDXRef-SOURCE-package1\",\n            \"relationshipType\": \"GENERATED_FROM\"\n        },\n        {\n            \"spdxElementId\": \"SPDXRef-file1\",\n            \"relatedSpdxElement\": \"SPDXRef-file4\",\n            \"relationshipType\": \"STATIC_LINK\"\n        },\n        {\n            \"spdxElementId\": \"SPDXRef-SOURCE-package1\",\n            \"relatedSpdxElement\": \"SPDXRef-UPSTREAM-package1\",\n            \"relationshipType\": \"VARIANT_OF\"\n        }\n    ],\n    \"hasExtractedLicensingInfos\": [\n        {\n            \"licenseId\": \"LicenseRef-Android-Apache-2.0\",\n            \"name\": \"License-Apache\",\n            \"extractedText\": \"<text>LICENSE_APACHE</text>\"\n        },\n        {\n            \"licenseId\": \"LicenseRef-Android-License-1\",\n            \"name\": \"License-1\",\n            \"extractedText\": \"<text>LICENSE 1</text>\"\n        },\n        {\n            \"licenseId\": \"LicenseRef-Android-License-2\",\n            \"name\": \"License-2\",\n            \"extractedText\": \"<text>LICENSE 2</text>\"\n        },\n        {\n            \"licenseId\": \"LicenseRef-Android-License-3\",\n            \"name\": \"License-3\",\n            \"extractedText\": \"<text>LICENSE 3</text>\"\n        }\n    ]\n}"
  },
  {
    "path": "tools/sbom/testdata/expected_tagvalue_sbom.spdx",
    "content": "SPDXVersion: SPDX-2.3\nDataLicense: CC0-1.0\nSPDXID: SPDXRef-DOCUMENT\nDocumentName: test doc\nDocumentNamespace: http://www.google.com/sbom/spdx/android\nCreator: Organization: Google\nCreated: 2023-03-31T22:17:58Z\nExternalDocumentRef: DocumentRef-external_doc_ref external_doc_uri SHA1: 1234567890\n\nFileName: file4.a\nSPDXID: SPDXRef-file4\nFileChecksum: SHA1: 44444\nLicenseConcluded: NOASSERTION\n\nPackageName: PRODUCT\nSPDXID: SPDXRef-PRODUCT\nPackageDownloadLocation: NONE\nFilesAnalyzed: true\nPackageVersion: build_finger_print\nPackageSupplier: Organization: Google\nPackageLicenseDeclared: NOASSERTION\nPackageVerificationCode: 123456\n\nRelationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-PRODUCT\n\nFileName: /bin/file1\nSPDXID: SPDXRef-file1\nFileChecksum: SHA1: 11111\nLicenseConcluded: LicenseRef-Android-Apache-2.0\n\nFileName: /bin/file2\nSPDXID: SPDXRef-file2\nFileChecksum: SHA1: 22222\nLicenseConcluded: LicenseRef-Android-License-1\n\nFileName: /bin/file3\nSPDXID: SPDXRef-file3\nFileChecksum: SHA1: 33333\nLicenseConcluded: LicenseRef-Android-License-2 OR LicenseRef-Android-License-3\n\nPackageName: PLATFORM\nSPDXID: SPDXRef-PLATFORM\nPackageDownloadLocation: NONE\nFilesAnalyzed: false\nPackageVersion: build_finger_print\nPackageSupplier: Organization: Google\nPackageLicenseDeclared: LicenseRef-Android-Apache-2.0\n\nPackageName: Prebuilt package1\nSPDXID: SPDXRef-PREBUILT-package1\nPackageDownloadLocation: NONE\nFilesAnalyzed: false\nPackageVersion: build_finger_print\nPackageSupplier: Organization: Google\nPackageLicenseDeclared: LicenseRef-Android-License-1\n\nPackageName: Source package1\nSPDXID: SPDXRef-SOURCE-package1\nPackageDownloadLocation: NONE\nFilesAnalyzed: false\nPackageVersion: build_finger_print\nPackageSupplier: Organization: Google\nPackageLicenseDeclared: LicenseRef-Android-License-2 OR LicenseRef-Android-License-3\nExternalRef: SECURITY cpe22Type cpe:/a:jsoncpp_project:jsoncpp:1.9.4\n\nPackageName: Upstream package1\nSPDXID: SPDXRef-UPSTREAM-package1\nPackageDownloadLocation: NOASSERTION\nFilesAnalyzed: false\nPackageVersion: 1.1\nPackageSupplier: Organization: upstream\nPackageLicenseDeclared: LicenseRef-Android-License-2 OR LicenseRef-Android-License-3\n\nRelationship: SPDXRef-SOURCE-package1 VARIANT_OF SPDXRef-UPSTREAM-package1\n\nRelationship: SPDXRef-file1 GENERATED_FROM SPDXRef-PLATFORM\nRelationship: SPDXRef-file2 GENERATED_FROM SPDXRef-PREBUILT-package1\nRelationship: SPDXRef-file3 GENERATED_FROM SPDXRef-SOURCE-package1\nRelationship: SPDXRef-file1 STATIC_LINK SPDXRef-file4\n\nLicenseID: LicenseRef-Android-Apache-2.0\nLicenseName: License-Apache\nExtractedText: <text>LICENSE_APACHE</text>\n\nLicenseID: LicenseRef-Android-License-1\nLicenseName: License-1\nExtractedText: <text>LICENSE 1</text>\n\nLicenseID: LicenseRef-Android-License-2\nLicenseName: License-2\nExtractedText: <text>LICENSE 2</text>\n\nLicenseID: LicenseRef-Android-License-3\nLicenseName: License-3\nExtractedText: <text>LICENSE 3</text>\n"
  },
  {
    "path": "tools/sbom/testdata/expected_tagvalue_sbom_doc_describes_file.spdx",
    "content": "SPDXVersion: SPDX-2.3\nDataLicense: CC0-1.0\nSPDXID: SPDXRef-DOCUMENT\nDocumentName: test doc\nDocumentNamespace: http://www.google.com/sbom/spdx/android\nCreator: Organization: Google\nCreated: 2023-03-31T22:17:58Z\nExternalDocumentRef: DocumentRef-external_doc_ref external_doc_uri SHA1: 1234567890\n\nFileName: file4.a\nSPDXID: SPDXRef-file4\nFileChecksum: SHA1: 44444\nLicenseConcluded: NOASSERTION\n\nRelationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-file4\n\nPackageName: PRODUCT\nSPDXID: SPDXRef-PRODUCT\nPackageDownloadLocation: NONE\nFilesAnalyzed: true\nPackageVersion: build_finger_print\nPackageSupplier: Organization: Google\nPackageLicenseDeclared: NOASSERTION\nPackageVerificationCode: 123456\n\nFileName: /bin/file1\nSPDXID: SPDXRef-file1\nFileChecksum: SHA1: 11111\nLicenseConcluded: LicenseRef-Android-Apache-2.0\n\nFileName: /bin/file2\nSPDXID: SPDXRef-file2\nFileChecksum: SHA1: 22222\nLicenseConcluded: LicenseRef-Android-License-1\n\nFileName: /bin/file3\nSPDXID: SPDXRef-file3\nFileChecksum: SHA1: 33333\nLicenseConcluded: LicenseRef-Android-License-2 OR LicenseRef-Android-License-3\n\nPackageName: PLATFORM\nSPDXID: SPDXRef-PLATFORM\nPackageDownloadLocation: NONE\nFilesAnalyzed: false\nPackageVersion: build_finger_print\nPackageSupplier: Organization: Google\nPackageLicenseDeclared: LicenseRef-Android-Apache-2.0\n\nPackageName: Prebuilt package1\nSPDXID: SPDXRef-PREBUILT-package1\nPackageDownloadLocation: NONE\nFilesAnalyzed: false\nPackageVersion: build_finger_print\nPackageSupplier: Organization: Google\nPackageLicenseDeclared: LicenseRef-Android-License-1\n\nPackageName: Source package1\nSPDXID: SPDXRef-SOURCE-package1\nPackageDownloadLocation: NONE\nFilesAnalyzed: false\nPackageVersion: build_finger_print\nPackageSupplier: Organization: Google\nPackageLicenseDeclared: LicenseRef-Android-License-2 OR LicenseRef-Android-License-3\nExternalRef: SECURITY cpe22Type cpe:/a:jsoncpp_project:jsoncpp:1.9.4\n\nPackageName: Upstream package1\nSPDXID: SPDXRef-UPSTREAM-package1\nPackageDownloadLocation: NOASSERTION\nFilesAnalyzed: false\nPackageVersion: 1.1\nPackageSupplier: Organization: upstream\nPackageLicenseDeclared: LicenseRef-Android-License-2 OR LicenseRef-Android-License-3\n\nRelationship: SPDXRef-SOURCE-package1 VARIANT_OF SPDXRef-UPSTREAM-package1\n\nRelationship: SPDXRef-file1 GENERATED_FROM SPDXRef-PLATFORM\nRelationship: SPDXRef-file2 GENERATED_FROM SPDXRef-PREBUILT-package1\nRelationship: SPDXRef-file3 GENERATED_FROM SPDXRef-SOURCE-package1\nRelationship: SPDXRef-file1 STATIC_LINK SPDXRef-file4\n\nLicenseID: LicenseRef-Android-Apache-2.0\nLicenseName: License-Apache\nExtractedText: <text>LICENSE_APACHE</text>\n\nLicenseID: LicenseRef-Android-License-1\nLicenseName: License-1\nExtractedText: <text>LICENSE 1</text>\n\nLicenseID: LicenseRef-Android-License-2\nLicenseName: License-2\nExtractedText: <text>LICENSE 2</text>\n\nLicenseID: LicenseRef-Android-License-3\nLicenseName: License-3\nExtractedText: <text>LICENSE 3</text>\n"
  },
  {
    "path": "tools/sbom/testdata/expected_tagvalue_sbom_unbundled.spdx",
    "content": "FileName: /bin/file1.apk\nSPDXID: SPDXRef-file1\nFileChecksum: SHA1: 11111\nLicenseConcluded: NOASSERTION\n\nPackageName: Unbundled apk package\nSPDXID: SPDXRef-SOURCE-package1\nPackageDownloadLocation: NONE\nFilesAnalyzed: false\nPackageVersion: build_finger_print\nPackageSupplier: Organization: Google\nPackageLicenseDeclared: NOASSERTION\n\nRelationship: SPDXRef-file1 GENERATED_FROM SPDXRef-SOURCE-package1\n"
  },
  {
    "path": "tools/signapk/Android.bp",
    "content": "//\n// Copyright (C) 2008 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n// the signapk tool (a .jar application used to sign packages)\n// ============================================================\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\njava_binary_host {\n    name: \"signapk\",\n    srcs: [\"src/**/*.java\"],\n    manifest: \"SignApk.mf\",\n    static_libs: [\n        \"apksig\",\n        \"bouncycastle-unbundled\",\n        \"bouncycastle-bcpkix-unbundled\",\n        \"conscrypt-unbundled\",\n    ],\n\n    // b/267608166: Prevent target Java 17 so the host-side tool can run in an\n    // environment where JDK 11 is available.\n    java_version: \"11\",\n\n    jni_libs: [\"libconscrypt_openjdk_jni\"],\n\n    // The post-build signing tools need signapk.jar (and its shared libraries,\n    // handled in their own Android.bp files)\n    dist: {\n        tag: \".jar\",\n        targets: [\"droidcore\"],\n    },\n}\n"
  },
  {
    "path": "tools/signapk/OWNERS",
    "content": "cbrubaker@google.com\nmpgroover@google.com\n"
  },
  {
    "path": "tools/signapk/SignApk.mf",
    "content": "Main-Class: com.android.signapk.SignApk\n"
  },
  {
    "path": "tools/signapk/src/com/android/signapk/CountingOutputStream.java",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.signapk;\nimport java.io.OutputStream;\nimport java.io.IOException;\n\nclass CountingOutputStream extends OutputStream {\n    private final OutputStream mBase;\n    private long mWrittenBytes;\n\n    public CountingOutputStream(OutputStream base) {\n        mBase = base;\n    }\n\n    @Override\n    public void close() throws IOException {\n        mBase.close();\n    }\n\n    @Override\n    public void flush() throws IOException {\n        mBase.flush();\n    }\n\n    @Override\n    public void write(byte[] b) throws IOException {\n        mBase.write(b);\n        mWrittenBytes += b.length;\n    }\n\n    @Override\n    public void write(byte[] b, int off, int len) throws IOException {\n        mBase.write(b, off, len);\n        mWrittenBytes += len;\n    }\n\n    @Override\n    public void write(int b) throws IOException {\n        mBase.write(b);\n        mWrittenBytes += 1;\n    }\n\n    public long getWrittenBytes() {\n        return mWrittenBytes;\n    }\n}\n"
  },
  {
    "path": "tools/signapk/src/com/android/signapk/SignApk.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.signapk;\n\nimport org.bouncycastle.asn1.ASN1InputStream;\nimport org.bouncycastle.asn1.ASN1ObjectIdentifier;\nimport org.bouncycastle.asn1.DEROutputStream;\nimport org.bouncycastle.asn1.cms.CMSObjectIdentifiers;\nimport org.bouncycastle.asn1.pkcs.PrivateKeyInfo;\nimport org.bouncycastle.cert.jcajce.JcaCertStore;\nimport org.bouncycastle.cms.CMSException;\nimport org.bouncycastle.cms.CMSSignedData;\nimport org.bouncycastle.cms.CMSSignedDataGenerator;\nimport org.bouncycastle.cms.CMSTypedData;\nimport org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\nimport org.bouncycastle.operator.ContentSigner;\nimport org.bouncycastle.operator.OperatorCreationException;\nimport org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;\nimport org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;\nimport org.conscrypt.OpenSSLProvider;\n\nimport com.android.apksig.ApkSignerEngine;\nimport com.android.apksig.DefaultApkSignerEngine;\nimport com.android.apksig.SigningCertificateLineage;\nimport com.android.apksig.Hints;\nimport com.android.apksig.apk.ApkUtils;\nimport com.android.apksig.apk.MinSdkVersionException;\nimport com.android.apksig.util.DataSink;\nimport com.android.apksig.util.DataSource;\nimport com.android.apksig.util.DataSources;\nimport com.android.apksig.zip.ZipFormatException;\n\nimport java.io.Console;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.DataInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.FilterOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.io.RandomAccessFile;\nimport java.lang.reflect.Constructor;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.security.GeneralSecurityException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.Key;\nimport java.security.KeyFactory;\nimport java.security.KeyStore;\nimport java.security.KeyStoreException;\nimport java.security.KeyStore.PrivateKeyEntry;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.Security;\nimport java.security.UnrecoverableEntryException;\nimport java.security.UnrecoverableKeyException;\nimport java.security.cert.CertificateEncodingException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.TimeZone;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\nimport java.util.jar.JarOutputStream;\nimport java.util.regex.Pattern;\nimport java.util.zip.ZipEntry;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.EncryptedPrivateKeyInfo;\nimport javax.crypto.SecretKeyFactory;\nimport javax.crypto.spec.PBEKeySpec;\n\n/**\n * HISTORICAL NOTE:\n *\n * Prior to the keylimepie release, SignApk ignored the signature\n * algorithm specified in the certificate and always used SHA1withRSA.\n *\n * Starting with JB-MR2, the platform supports SHA256withRSA, so we use\n * the signature algorithm in the certificate to select which to use\n * (SHA256withRSA or SHA1withRSA). Also in JB-MR2, EC keys are supported.\n *\n * Because there are old keys still in use whose certificate actually\n * says \"MD5withRSA\", we treat these as though they say \"SHA1withRSA\"\n * for compatibility with older releases.  This can be changed by\n * altering the getAlgorithm() function below.\n */\n\n\n/**\n * Command line tool to sign JAR files (including APKs and OTA updates) in a way\n * compatible with the mincrypt verifier, using EC or RSA keys and SHA1 or\n * SHA-256 (see historical note). The tool can additionally sign APKs using\n * APK Signature Scheme v2.\n */\nclass SignApk {\n    private static final String OTACERT_NAME = \"META-INF/com/android/otacert\";\n\n    /**\n     * Extensible data block/field header ID used for storing information about alignment of\n     * uncompressed entries as well as for aligning the entries's data. See ZIP appnote.txt section\n     * 4.5 Extensible data fields.\n     */\n    private static final short ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID = (short) 0xd935;\n\n    /**\n     * Minimum size (in bytes) of the extensible data block/field used for alignment of uncompressed\n     * entries.\n     */\n    private static final short ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES = 6;\n\n    // bitmasks for which hash algorithms we need the manifest to include.\n    private static final int USE_SHA1 = 1;\n    private static final int USE_SHA256 = 2;\n\n    /**\n     * Returns the digest algorithm ID (one of {@code USE_SHA1} or {@code USE_SHA256}) to be used\n     * for signing an OTA update package using the private key corresponding to the provided\n     * certificate.\n     */\n    private static int getDigestAlgorithmForOta(X509Certificate cert) {\n        String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);\n        if (\"SHA1WITHRSA\".equals(sigAlg) || \"MD5WITHRSA\".equals(sigAlg)) {\n            // see \"HISTORICAL NOTE\" above.\n            return USE_SHA1;\n        } else if (sigAlg.startsWith(\"SHA256WITH\")) {\n            return USE_SHA256;\n        } else {\n            throw new IllegalArgumentException(\"unsupported signature algorithm \\\"\" + sigAlg +\n                                               \"\\\" in cert [\" + cert.getSubjectDN());\n        }\n    }\n\n    /**\n     * Returns the JCA {@link java.security.Signature} algorithm to be used for signing and OTA\n     * update package using the private key corresponding to the provided certificate and the\n     * provided digest algorithm (see {@code USE_SHA1} and {@code USE_SHA256} constants).\n     */\n    private static String getJcaSignatureAlgorithmForOta(\n            X509Certificate cert, int hash) {\n        String sigAlgDigestPrefix;\n        switch (hash) {\n            case USE_SHA1:\n                sigAlgDigestPrefix = \"SHA1\";\n                break;\n            case USE_SHA256:\n                sigAlgDigestPrefix = \"SHA256\";\n                break;\n            default:\n                throw new IllegalArgumentException(\"Unknown hash ID: \" + hash);\n        }\n\n        String keyAlgorithm = cert.getPublicKey().getAlgorithm();\n        if (\"RSA\".equalsIgnoreCase(keyAlgorithm)) {\n            return sigAlgDigestPrefix + \"withRSA\";\n        } else if (\"EC\".equalsIgnoreCase(keyAlgorithm)) {\n            return sigAlgDigestPrefix + \"withECDSA\";\n        } else {\n            throw new IllegalArgumentException(\"Unsupported key algorithm: \" + keyAlgorithm);\n        }\n    }\n\n    private static X509Certificate readPublicKey(File file)\n        throws IOException, GeneralSecurityException {\n        FileInputStream input = new FileInputStream(file);\n        try {\n            CertificateFactory cf = CertificateFactory.getInstance(\"X.509\");\n            return (X509Certificate) cf.generateCertificate(input);\n        } finally {\n            input.close();\n        }\n    }\n\n    /**\n     * If a console doesn't exist, reads the password from stdin\n     * If a console exists, reads the password from console and returns it as a string.\n     *\n     * @param keyFileName Name of the file containing the private key.  Used to prompt the user.\n     */\n    private static char[] readPassword(String keyFileName) {\n        Console console;\n        if ((console = System.console()) == null) {\n            System.out.print(\n                \"Enter password for \" + keyFileName + \" (password will not be hidden): \");\n            System.out.flush();\n            BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));\n            try {\n                String result = stdin.readLine();\n                return result == null ? null : result.toCharArray();\n            } catch (IOException ex) {\n                return null;\n            }\n        } else {\n            return console.readPassword(\"[%s]\", \"Enter password for \" + keyFileName);\n        }\n    }\n\n    /**\n     * Decrypt an encrypted PKCS#8 format private key.\n     *\n     * Based on ghstark's post on Aug 6, 2006 at\n     * http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949\n     *\n     * @param encryptedPrivateKey The raw data of the private key\n     * @param keyFile The file containing the private key\n     */\n    private static PKCS8EncodedKeySpec decryptPrivateKey(byte[] encryptedPrivateKey, File keyFile)\n        throws GeneralSecurityException {\n        EncryptedPrivateKeyInfo epkInfo;\n        try {\n            epkInfo = new EncryptedPrivateKeyInfo(encryptedPrivateKey);\n        } catch (IOException ex) {\n            // Probably not an encrypted key.\n            return null;\n        }\n\n        SecretKeyFactory skFactory = SecretKeyFactory.getInstance(epkInfo.getAlgName());\n        Key key = skFactory.generateSecret(new PBEKeySpec(readPassword(keyFile.getPath())));\n        Cipher cipher = Cipher.getInstance(epkInfo.getAlgName());\n        cipher.init(Cipher.DECRYPT_MODE, key, epkInfo.getAlgParameters());\n\n        try {\n            return epkInfo.getKeySpec(cipher);\n        } catch (InvalidKeySpecException ex) {\n            System.err.println(\"signapk: Password for \" + keyFile + \" may be bad.\");\n            throw ex;\n        }\n    }\n\n    /** Read a PKCS#8 format private key. */\n    private static PrivateKey readPrivateKey(File file)\n        throws IOException, GeneralSecurityException {\n        DataInputStream input = new DataInputStream(new FileInputStream(file));\n        try {\n            byte[] bytes = new byte[(int) file.length()];\n            input.read(bytes);\n\n            /* Check to see if this is in an EncryptedPrivateKeyInfo structure. */\n            PKCS8EncodedKeySpec spec = decryptPrivateKey(bytes, file);\n            if (spec == null) {\n                spec = new PKCS8EncodedKeySpec(bytes);\n            }\n\n            /*\n             * Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm\n             * OID and use that to construct a KeyFactory.\n             */\n            PrivateKeyInfo pki;\n            try (ASN1InputStream bIn =\n                    new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()))) {\n                pki = PrivateKeyInfo.getInstance(bIn.readObject());\n            }\n            String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();\n\n            return KeyFactory.getInstance(algOid).generatePrivate(spec);\n        } finally {\n            input.close();\n        }\n    }\n\n    private static KeyStore createKeyStore(String keyStoreName, String keyStorePin) throws\n            CertificateException,\n            IOException,\n            KeyStoreException,\n            NoSuchAlgorithmException {\n        KeyStore keyStore = KeyStore.getInstance(keyStoreName);\n        keyStore.load(null, keyStorePin == null ? null : keyStorePin.toCharArray());\n        return keyStore;\n    }\n\n    /** Get a PKCS#11 private key from keyStore */\n    private static PrivateKey loadPrivateKeyFromKeyStore(\n            final KeyStore keyStore, final String keyName)\n            throws CertificateException, KeyStoreException, NoSuchAlgorithmException,\n                    UnrecoverableKeyException, UnrecoverableEntryException {\n        final PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry(keyName, null);\n        if (privateKeyEntry == null) {\n        throw new Error(\n            \"Key \"\n                + keyName\n                + \" not found in the token provided by PKCS11 library!\");\n        }\n        return privateKeyEntry.getPrivateKey();\n    }\n\n    /**\n     * Add a copy of the public key to the archive; this should\n     * exactly match one of the files in\n     * /system/etc/security/otacerts.zip on the device.  (The same\n     * cert can be extracted from the OTA update package's signature\n     * block but this is much easier to get at.)\n     */\n    private static void addOtacert(JarOutputStream outputJar,\n                                   File publicKeyFile,\n                                   long timestamp)\n        throws IOException {\n\n        JarEntry je = new JarEntry(OTACERT_NAME);\n        je.setTime(timestamp);\n        outputJar.putNextEntry(je);\n        FileInputStream input = new FileInputStream(publicKeyFile);\n        byte[] b = new byte[4096];\n        int read;\n        while ((read = input.read(b)) != -1) {\n            outputJar.write(b, 0, read);\n        }\n        input.close();\n    }\n\n\n    /** Sign data and write the digital signature to 'out'. */\n    private static void writeSignatureBlock(\n        CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey, int hash,\n        OutputStream out)\n        throws IOException,\n               CertificateEncodingException,\n               OperatorCreationException,\n               CMSException {\n        ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(1);\n        certList.add(publicKey);\n        JcaCertStore certs = new JcaCertStore(certList);\n\n        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();\n        ContentSigner signer =\n                new JcaContentSignerBuilder(\n                        getJcaSignatureAlgorithmForOta(publicKey, hash))\n                        .build(privateKey);\n        gen.addSignerInfoGenerator(\n            new JcaSignerInfoGeneratorBuilder(\n                new JcaDigestCalculatorProviderBuilder()\n                .build())\n            .setDirectSignature(true)\n            .build(signer, publicKey));\n        gen.addCertificates(certs);\n        CMSSignedData sigData = gen.generate(data, false);\n\n        try (ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded())) {\n            DEROutputStream dos = new DEROutputStream(out);\n            dos.writeObject(asn1.readObject());\n        }\n    }\n\n    /**\n     * Adds ZIP entries which represent the v1 signature (JAR signature scheme).\n     */\n    private static void addV1Signature(\n            ApkSignerEngine apkSigner,\n            ApkSignerEngine.OutputJarSignatureRequest v1Signature,\n            JarOutputStream out,\n            long timestamp) throws IOException {\n        for (ApkSignerEngine.OutputJarSignatureRequest.JarEntry entry\n                : v1Signature.getAdditionalJarEntries()) {\n            String entryName = entry.getName();\n            JarEntry outEntry = new JarEntry(entryName);\n            outEntry.setTime(timestamp);\n            out.putNextEntry(outEntry);\n            byte[] entryData = entry.getData();\n            out.write(entryData);\n            ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =\n                    apkSigner.outputJarEntry(entryName);\n            if (inspectEntryRequest != null) {\n                inspectEntryRequest.getDataSink().consume(entryData, 0, entryData.length);\n                inspectEntryRequest.done();\n            }\n        }\n    }\n\n    /**\n     * Copy all JAR entries from input to output. We set the modification times in the output to a\n     * fixed time, so as to reduce variation in the output file and make incremental OTAs more\n     * efficient.\n     */\n    private static void copyFiles(\n            JarFile in,\n            Pattern ignoredFilenamePattern,\n            ApkSignerEngine apkSigner,\n            JarOutputStream out,\n            CountingOutputStream outCounter,\n            long timestamp,\n            int defaultAlignment) throws IOException {\n        byte[] buffer = new byte[4096];\n        int num;\n\n        List<Hints.PatternWithRange> pinPatterns = extractPinPatterns(in);\n        ArrayList<Hints.ByteRange> pinByteRanges = pinPatterns == null ? null : new ArrayList<>();\n\n        ArrayList<String> names = new ArrayList<String>();\n        for (Enumeration<JarEntry> e = in.entries(); e.hasMoreElements();) {\n            JarEntry entry = e.nextElement();\n            if (entry.isDirectory()) {\n                continue;\n            }\n            String entryName = entry.getName();\n            if ((ignoredFilenamePattern != null)\n                    && (ignoredFilenamePattern.matcher(entryName).matches())) {\n                continue;\n            }\n            if (Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME.equals(entryName)) {\n                continue;  // We regenerate it below.\n            }\n            names.add(entryName);\n        }\n        Collections.sort(names);\n\n        boolean firstEntry = true;\n        long offset = 0L;\n\n        // We do the copy in two passes -- first copying all the\n        // entries that are STORED, then copying all the entries that\n        // have any other compression flag (which in practice means\n        // DEFLATED).  This groups all the stored entries together at\n        // the start of the file and makes it easier to do alignment\n        // on them (since only stored entries are aligned).\n\n        List<String> remainingNames = new ArrayList<>(names.size());\n        for (String name : names) {\n            JarEntry inEntry = in.getJarEntry(name);\n            if (inEntry.getMethod() != JarEntry.STORED) {\n                // Defer outputting this entry until we're ready to output compressed entries.\n                remainingNames.add(name);\n                continue;\n            }\n\n            if (!shouldOutputApkEntry(apkSigner, in, inEntry, buffer)) {\n                continue;\n            }\n\n            // Preserve the STORED method of the input entry.\n            JarEntry outEntry = new JarEntry(inEntry);\n            outEntry.setTime(timestamp);\n            // Discard comment and extra fields of this entry to\n            // simplify alignment logic below and for consistency with\n            // how compressed entries are handled later.\n            outEntry.setComment(null);\n            outEntry.setExtra(null);\n\n            int alignment = getStoredEntryDataAlignment(name, defaultAlignment);\n            // Alignment of the entry's data is achieved by adding a data block to the entry's Local\n            // File Header extra field. The data block contains information about the alignment\n            // value and the necessary padding bytes (0x00) to achieve the alignment.  This works\n            // because the entry's data will be located immediately after the extra field.\n            // See ZIP APPNOTE.txt section \"4.5 Extensible data fields\" for details about the format\n            // of the extra field.\n\n            // 'offset' is the offset into the file at which we expect the entry's data to begin.\n            // This is the value we need to make a multiple of 'alignment'.\n            offset += JarFile.LOCHDR + outEntry.getName().length();\n            if (firstEntry) {\n                // The first entry in a jar file has an extra field of four bytes that you can't get\n                // rid of; any extra data you specify in the JarEntry is appended to these forced\n                // four bytes.  This is JAR_MAGIC in JarOutputStream; the bytes are 0xfeca0000.\n                // See http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6808540\n                // and http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4138619.\n                offset += 4;\n                firstEntry = false;\n            }\n            int extraPaddingSizeBytes = 0;\n            if (alignment > 0) {\n                long paddingStartOffset = offset + ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES;\n                extraPaddingSizeBytes =\n                        (alignment - (int) (paddingStartOffset % alignment)) % alignment;\n            }\n            byte[] extra =\n                    new byte[ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES + extraPaddingSizeBytes];\n            ByteBuffer extraBuf = ByteBuffer.wrap(extra);\n            extraBuf.order(ByteOrder.LITTLE_ENDIAN);\n            extraBuf.putShort(ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID); // Header ID\n            extraBuf.putShort((short) (2 + extraPaddingSizeBytes)); // Data Size\n            extraBuf.putShort((short) alignment);\n            outEntry.setExtra(extra);\n            offset += extra.length;\n\n            long entryHeaderStart = outCounter.getWrittenBytes();\n            out.putNextEntry(outEntry);\n            ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =\n                    (apkSigner != null) ? apkSigner.outputJarEntry(name) : null;\n            DataSink entryDataSink =\n                    (inspectEntryRequest != null) ? inspectEntryRequest.getDataSink() : null;\n\n            long entryDataStart = outCounter.getWrittenBytes();\n            try (InputStream data = in.getInputStream(inEntry)) {\n                while ((num = data.read(buffer)) > 0) {\n                    out.write(buffer, 0, num);\n                    if (entryDataSink != null) {\n                        entryDataSink.consume(buffer, 0, num);\n                    }\n                    offset += num;\n                }\n            }\n            out.closeEntry();\n            out.flush();\n            if (inspectEntryRequest != null) {\n                inspectEntryRequest.done();\n            }\n\n            if (pinPatterns != null) {\n                boolean pinFileHeader = false;\n                for (Hints.PatternWithRange pinPattern : pinPatterns) {\n                    if (!pinPattern.matcher(name).matches()) {\n                        continue;\n                    }\n                    Hints.ByteRange dataRange =\n                        new Hints.ByteRange(\n                            entryDataStart,\n                            outCounter.getWrittenBytes());\n                    Hints.ByteRange pinRange =\n                        pinPattern.ClampToAbsoluteByteRange(dataRange);\n                    if (pinRange != null) {\n                        pinFileHeader = true;\n                        pinByteRanges.add(pinRange);\n                    }\n                }\n                if (pinFileHeader) {\n                    pinByteRanges.add(new Hints.ByteRange(entryHeaderStart,\n                                                          entryDataStart));\n                }\n            }\n        }\n\n        // Copy all the non-STORED entries.  We don't attempt to\n        // maintain the 'offset' variable past this point; we don't do\n        // alignment on these entries.\n\n        for (String name : remainingNames) {\n            JarEntry inEntry = in.getJarEntry(name);\n            if (!shouldOutputApkEntry(apkSigner, in, inEntry, buffer)) {\n                continue;\n            }\n\n            // Create a new entry so that the compressed len is recomputed.\n            JarEntry outEntry = new JarEntry(name);\n            outEntry.setTime(timestamp);\n            long entryHeaderStart = outCounter.getWrittenBytes();\n            out.putNextEntry(outEntry);\n            ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =\n                    (apkSigner != null) ? apkSigner.outputJarEntry(name) : null;\n            DataSink entryDataSink =\n                    (inspectEntryRequest != null) ? inspectEntryRequest.getDataSink() : null;\n\n            long entryDataStart = outCounter.getWrittenBytes();\n            InputStream data = in.getInputStream(inEntry);\n            while ((num = data.read(buffer)) > 0) {\n                out.write(buffer, 0, num);\n                if (entryDataSink != null) {\n                    entryDataSink.consume(buffer, 0, num);\n                }\n            }\n            out.closeEntry();\n            out.flush();\n            if (inspectEntryRequest != null) {\n                inspectEntryRequest.done();\n            }\n\n            if (pinPatterns != null) {\n                boolean pinFileHeader = false;\n                for (Hints.PatternWithRange pinPattern : pinPatterns) {\n                    if (!pinPattern.matcher(name).matches()) {\n                        continue;\n                    }\n                    Hints.ByteRange dataRange =\n                        new Hints.ByteRange(\n                            entryDataStart,\n                            outCounter.getWrittenBytes());\n                    Hints.ByteRange pinRange =\n                        pinPattern.ClampToAbsoluteByteRange(dataRange);\n                    if (pinRange != null) {\n                        pinFileHeader = true;\n                        pinByteRanges.add(pinRange);\n                    }\n                }\n                if (pinFileHeader) {\n                    pinByteRanges.add(new Hints.ByteRange(entryHeaderStart,\n                                                          entryDataStart));\n                }\n            }\n        }\n\n        if (pinByteRanges != null) {\n            // Cover central directory\n            pinByteRanges.add(\n                new Hints.ByteRange(outCounter.getWrittenBytes(),\n                                    Long.MAX_VALUE));\n            addPinByteRanges(out, pinByteRanges, timestamp);\n        }\n    }\n\n    private static List<Hints.PatternWithRange> extractPinPatterns(JarFile in) throws IOException {\n        ZipEntry pinMetaEntry = in.getEntry(Hints.PIN_HINT_ASSET_ZIP_ENTRY_NAME);\n        if (pinMetaEntry == null) {\n            return null;\n        }\n        InputStream pinMetaStream = in.getInputStream(pinMetaEntry);\n        byte[] patternBlob = new byte[(int) pinMetaEntry.getSize()];\n        pinMetaStream.read(patternBlob);\n        return Hints.parsePinPatterns(patternBlob);\n    }\n\n    private static void addPinByteRanges(JarOutputStream outputJar,\n                                         ArrayList<Hints.ByteRange> pinByteRanges,\n                                         long timestamp) throws IOException {\n        JarEntry je = new JarEntry(Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME);\n        je.setTime(timestamp);\n        outputJar.putNextEntry(je);\n        outputJar.write(Hints.encodeByteRangeList(pinByteRanges));\n    }\n\n    private static boolean shouldOutputApkEntry(\n            ApkSignerEngine apkSigner, JarFile inFile, JarEntry inEntry, byte[] tmpbuf)\n                    throws IOException {\n        if (apkSigner == null) {\n            return true;\n        }\n\n        ApkSignerEngine.InputJarEntryInstructions instructions =\n                apkSigner.inputJarEntry(inEntry.getName());\n        ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =\n                instructions.getInspectJarEntryRequest();\n        if (inspectEntryRequest != null) {\n            provideJarEntry(inFile, inEntry, inspectEntryRequest, tmpbuf);\n        }\n        switch (instructions.getOutputPolicy()) {\n            case OUTPUT:\n                return true;\n            case SKIP:\n            case OUTPUT_BY_ENGINE:\n                return false;\n            default:\n                throw new RuntimeException(\n                        \"Unsupported output policy: \" + instructions.getOutputPolicy());\n        }\n    }\n\n    private static void provideJarEntry(\n            JarFile jarFile,\n            JarEntry jarEntry,\n            ApkSignerEngine.InspectJarEntryRequest request,\n            byte[] tmpbuf) throws IOException {\n        DataSink dataSink = request.getDataSink();\n        try (InputStream in = jarFile.getInputStream(jarEntry)) {\n            int chunkSize;\n            while ((chunkSize = in.read(tmpbuf)) > 0) {\n                dataSink.consume(tmpbuf, 0, chunkSize);\n            }\n            request.done();\n        }\n    }\n\n    /**\n     * Returns the multiple (in bytes) at which the provided {@code STORED} entry's data must start\n     * relative to start of file or {@code 0} if alignment of this entry's data is not important.\n     */\n    private static int getStoredEntryDataAlignment(String entryName, int defaultAlignment) {\n        if (defaultAlignment <= 0) {\n            return 0;\n        }\n\n        if (entryName.endsWith(\".so\")) {\n            // Align .so contents to memory page boundary to enable memory-mapped\n            // execution.\n            return 16384;\n        } else {\n            return defaultAlignment;\n        }\n    }\n\n    private static class WholeFileSignerOutputStream extends FilterOutputStream {\n        private boolean closing = false;\n        private ByteArrayOutputStream footer = new ByteArrayOutputStream();\n        private OutputStream tee;\n\n        public WholeFileSignerOutputStream(OutputStream out, OutputStream tee) {\n            super(out);\n            this.tee = tee;\n        }\n\n        public void notifyClosing() {\n            closing = true;\n        }\n\n        public void finish() throws IOException {\n            closing = false;\n\n            byte[] data = footer.toByteArray();\n            if (data.length < 2)\n                throw new IOException(\"Less than two bytes written to footer\");\n            write(data, 0, data.length - 2);\n        }\n\n        public byte[] getTail() {\n            return footer.toByteArray();\n        }\n\n        @Override\n        public void write(byte[] b) throws IOException {\n            write(b, 0, b.length);\n        }\n\n        @Override\n        public void write(byte[] b, int off, int len) throws IOException {\n            if (closing) {\n                // if the jar is about to close, save the footer that will be written\n                footer.write(b, off, len);\n            }\n            else {\n                // write to both output streams. out is the CMSTypedData signer and tee is the file.\n                out.write(b, off, len);\n                tee.write(b, off, len);\n            }\n        }\n\n        @Override\n        public void write(int b) throws IOException {\n            if (closing) {\n                // if the jar is about to close, save the footer that will be written\n                footer.write(b);\n            }\n            else {\n                // write to both output streams. out is the CMSTypedData signer and tee is the file.\n                out.write(b);\n                tee.write(b);\n            }\n        }\n    }\n\n    private static class CMSSigner implements CMSTypedData {\n        private final JarFile inputJar;\n        private final File publicKeyFile;\n        private final X509Certificate publicKey;\n        private final PrivateKey privateKey;\n        private final int hash;\n        private final long timestamp;\n        private final OutputStream outputStream;\n        private final ASN1ObjectIdentifier type;\n        private WholeFileSignerOutputStream signer;\n\n        // Files matching this pattern are not copied to the output.\n        private static final Pattern STRIP_PATTERN =\n                Pattern.compile(\"^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(\"\n                        + Pattern.quote(JarFile.MANIFEST_NAME) + \")$\");\n\n        public CMSSigner(JarFile inputJar, File publicKeyFile,\n                         X509Certificate publicKey, PrivateKey privateKey, int hash,\n                         long timestamp, OutputStream outputStream) {\n            this.inputJar = inputJar;\n            this.publicKeyFile = publicKeyFile;\n            this.publicKey = publicKey;\n            this.privateKey = privateKey;\n            this.hash = hash;\n            this.timestamp = timestamp;\n            this.outputStream = outputStream;\n            this.type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());\n        }\n\n        /**\n         * This should actually return byte[] or something similar, but nothing\n         * actually checks it currently.\n         */\n        @Override\n        public Object getContent() {\n            return this;\n        }\n\n        @Override\n        public ASN1ObjectIdentifier getContentType() {\n            return type;\n        }\n\n        @Override\n        public void write(OutputStream out) throws IOException {\n            try {\n                signer = new WholeFileSignerOutputStream(out, outputStream);\n                CountingOutputStream outputJarCounter = new CountingOutputStream(signer);\n                JarOutputStream outputJar = new JarOutputStream(outputJarCounter);\n\n                copyFiles(inputJar, STRIP_PATTERN, null, outputJar,\n                          outputJarCounter, timestamp, 0);\n                addOtacert(outputJar, publicKeyFile, timestamp);\n\n                signer.notifyClosing();\n                outputJar.close();\n                signer.finish();\n            }\n            catch (Exception e) {\n                throw new IOException(e);\n            }\n        }\n\n        public void writeSignatureBlock(ByteArrayOutputStream temp)\n            throws IOException,\n                   CertificateEncodingException,\n                   OperatorCreationException,\n                   CMSException {\n            SignApk.writeSignatureBlock(this, publicKey, privateKey, hash, temp);\n        }\n\n        public WholeFileSignerOutputStream getSigner() {\n            return signer;\n        }\n    }\n\n    private static void signWholeFile(JarFile inputJar, File publicKeyFile,\n                                      X509Certificate publicKey, PrivateKey privateKey,\n                                      int hash, long timestamp,\n                                      OutputStream outputStream) throws Exception {\n        CMSSigner cmsOut = new CMSSigner(inputJar, publicKeyFile,\n                publicKey, privateKey, hash, timestamp, outputStream);\n\n        ByteArrayOutputStream temp = new ByteArrayOutputStream();\n\n        // put a readable message and a null char at the start of the\n        // archive comment, so that tools that display the comment\n        // (hopefully) show something sensible.\n        // TODO: anything more useful we can put in this message?\n        byte[] message = \"signed by SignApk\".getBytes(StandardCharsets.UTF_8);\n        temp.write(message);\n        temp.write(0);\n\n        cmsOut.writeSignatureBlock(temp);\n\n        byte[] zipData = cmsOut.getSigner().getTail();\n\n        // For a zip with no archive comment, the\n        // end-of-central-directory record will be 22 bytes long, so\n        // we expect to find the EOCD marker 22 bytes from the end.\n        if (zipData[zipData.length-22] != 0x50 ||\n            zipData[zipData.length-21] != 0x4b ||\n            zipData[zipData.length-20] != 0x05 ||\n            zipData[zipData.length-19] != 0x06) {\n            throw new IllegalArgumentException(\"zip data already has an archive comment\");\n        }\n\n        int total_size = temp.size() + 6;\n        if (total_size > 0xffff) {\n            throw new IllegalArgumentException(\"signature is too big for ZIP file comment\");\n        }\n        // signature starts this many bytes from the end of the file\n        int signature_start = total_size - message.length - 1;\n        temp.write(signature_start & 0xff);\n        temp.write((signature_start >> 8) & 0xff);\n        // Why the 0xff bytes?  In a zip file with no archive comment,\n        // bytes [-6:-2] of the file are the little-endian offset from\n        // the start of the file to the central directory.  So for the\n        // two high bytes to be 0xff 0xff, the archive would have to\n        // be nearly 4GB in size.  So it's unlikely that a real\n        // commentless archive would have 0xffs here, and lets us tell\n        // an old signed archive from a new one.\n        temp.write(0xff);\n        temp.write(0xff);\n        temp.write(total_size & 0xff);\n        temp.write((total_size >> 8) & 0xff);\n        temp.flush();\n\n        // Signature verification checks that the EOCD header is the\n        // last such sequence in the file (to avoid minzip finding a\n        // fake EOCD appended after the signature in its scan).  The\n        // odds of producing this sequence by chance are very low, but\n        // let's catch it here if it does.\n        byte[] b = temp.toByteArray();\n        for (int i = 0; i < b.length-3; ++i) {\n            if (b[i] == 0x50 && b[i+1] == 0x4b && b[i+2] == 0x05 && b[i+3] == 0x06) {\n                throw new IllegalArgumentException(\"found spurious EOCD header at \" + i);\n            }\n        }\n\n        outputStream.write(total_size & 0xff);\n        outputStream.write((total_size >> 8) & 0xff);\n        temp.writeTo(outputStream);\n    }\n\n    /**\n     * Tries to load a JSE Provider by class name. This is for custom PrivateKey\n     * types that might be stored in PKCS#11-like storage.\n     */\n    private static void loadProviderIfNecessary(String providerClassName, String providerArg) {\n        if (providerClassName == null) {\n            return;\n        }\n\n        final Class<?> klass;\n        try {\n            final ClassLoader sysLoader = ClassLoader.getSystemClassLoader();\n            if (sysLoader != null) {\n                klass = sysLoader.loadClass(providerClassName);\n            } else {\n                klass = Class.forName(providerClassName);\n            }\n        } catch (ClassNotFoundException e) {\n            e.printStackTrace();\n            System.exit(1);\n            return;\n        }\n\n        Constructor<?> constructor;\n        Object o = null;\n        if (providerArg == null) {\n            try {\n                constructor = klass.getConstructor();\n                o = constructor.newInstance();\n            } catch (ReflectiveOperationException e) {\n                e.printStackTrace();\n                System.err.println(\"Unable to instantiate \" + providerClassName\n                        + \" with a zero-arg constructor\");\n                System.exit(1);\n            }\n        } else {\n            try {\n                constructor = klass.getConstructor(String.class);\n                o = constructor.newInstance(providerArg);\n            } catch (ReflectiveOperationException e) {\n                // This is expected from JDK 9+; the single-arg constructor accepting the\n                // configuration has been replaced with a configure(String) method to be invoked\n                // after instantiating the Provider with the zero-arg constructor.\n                try {\n                    constructor = klass.getConstructor();\n                    o = constructor.newInstance();\n                    // The configure method will return either the modified Provider or a new\n                    // Provider if this one cannot be configured in-place.\n                    o = klass.getMethod(\"configure\", String.class).invoke(o, providerArg);\n                } catch (ReflectiveOperationException roe) {\n                    roe.printStackTrace();\n                    System.err.println(\"Unable to instantiate \" + providerClassName\n                            + \" with the provided argument \" + providerArg);\n                    System.exit(1);\n                }\n            }\n        }\n\n        if (!(o instanceof Provider)) {\n            System.err.println(\"Not a Provider class: \" + providerClassName);\n            System.exit(1);\n        }\n\n        Security.insertProviderAt((Provider) o, 1);\n    }\n\n    private static List<DefaultApkSignerEngine.SignerConfig> createSignerConfigs(\n            PrivateKey[] privateKeys, X509Certificate[] certificates) {\n        if (privateKeys.length != certificates.length) {\n            throw new IllegalArgumentException(\n                    \"The number of private keys must match the number of certificates: \"\n                            + privateKeys.length + \" vs\" + certificates.length);\n        }\n        List<DefaultApkSignerEngine.SignerConfig> signerConfigs = new ArrayList<>();\n        String signerNameFormat = (privateKeys.length == 1) ? \"CERT\" : \"CERT%s\";\n        for (int i = 0; i < privateKeys.length; i++) {\n            String signerName = String.format(Locale.US, signerNameFormat, (i + 1));\n            DefaultApkSignerEngine.SignerConfig signerConfig =\n                    new DefaultApkSignerEngine.SignerConfig.Builder(\n                            signerName,\n                            privateKeys[i],\n                            Collections.singletonList(certificates[i]))\n                            .build();\n            signerConfigs.add(signerConfig);\n        }\n        return signerConfigs;\n    }\n\n    private static class ZipSections {\n        DataSource beforeCentralDir;\n\n        // The following fields are still valid after closing the backing DataSource.\n        long beforeCentralDirSize;\n        ByteBuffer centralDir;\n        ByteBuffer eocd;\n    }\n\n    private static ZipSections findMainZipSections(DataSource apk)\n            throws IOException, ZipFormatException {\n        ApkUtils.ZipSections sections = ApkUtils.findZipSections(apk);\n        long centralDirStartOffset = sections.getZipCentralDirectoryOffset();\n        long centralDirSizeBytes = sections.getZipCentralDirectorySizeBytes();\n        long centralDirEndOffset = centralDirStartOffset + centralDirSizeBytes;\n        long eocdStartOffset = sections.getZipEndOfCentralDirectoryOffset();\n        if (centralDirEndOffset != eocdStartOffset) {\n            throw new ZipFormatException(\n                    \"ZIP Central Directory is not immediately followed by End of Central Directory\"\n                            + \". CD end: \" + centralDirEndOffset\n                            + \", EoCD start: \" + eocdStartOffset);\n        }\n\n        ZipSections result = new ZipSections();\n\n        result.beforeCentralDir = apk.slice(0, centralDirStartOffset);\n        result.beforeCentralDirSize = result.beforeCentralDir.size();\n\n        long centralDirSize = centralDirEndOffset - centralDirStartOffset;\n        if (centralDirSize >= Integer.MAX_VALUE) throw new IndexOutOfBoundsException();\n        result.centralDir = apk.getByteBuffer(centralDirStartOffset, (int)centralDirSize);\n\n        long eocdSize = apk.size() - eocdStartOffset;\n        if (eocdSize >= Integer.MAX_VALUE) throw new IndexOutOfBoundsException();\n        result.eocd = apk.getByteBuffer(eocdStartOffset, (int)eocdSize);\n\n        return result;\n    }\n\n    /**\n     * Returns the API Level corresponding to the APK's minSdkVersion.\n     *\n     * @throws MinSdkVersionException if the API Level cannot be determined from the APK.\n     */\n    private static final int getMinSdkVersion(JarFile apk) throws MinSdkVersionException {\n        JarEntry manifestEntry = apk.getJarEntry(\"AndroidManifest.xml\");\n        if (manifestEntry == null) {\n            throw new MinSdkVersionException(\"No AndroidManifest.xml in APK\");\n        }\n        byte[] manifestBytes;\n        try {\n            try (InputStream manifestIn = apk.getInputStream(manifestEntry)) {\n                manifestBytes = toByteArray(manifestIn);\n            }\n        } catch (IOException e) {\n            throw new MinSdkVersionException(\"Failed to read AndroidManifest.xml\", e);\n        }\n        return ApkUtils.getMinSdkVersionFromBinaryAndroidManifest(ByteBuffer.wrap(manifestBytes));\n    }\n\n    private static byte[] toByteArray(InputStream in) throws IOException {\n        ByteArrayOutputStream result = new ByteArrayOutputStream();\n        byte[] buf = new byte[65536];\n        int chunkSize;\n        while ((chunkSize = in.read(buf)) != -1) {\n            result.write(buf, 0, chunkSize);\n        }\n        return result.toByteArray();\n    }\n\n    private static void usage() {\n        System.err.println(\"Usage: signapk [-w] \" +\n                           \"[-a <alignment>] \" +\n                           \"[--align-file-size] \" +\n                           \"[-providerClass <className>] \" +\n                           \"[-providerArg <configureArg>] \" +\n                           \"[-loadPrivateKeysFromKeyStore <keyStoreName>]\" +\n                           \"[-keyStorePin <pin>]\" +\n                           \"[--min-sdk-version <n>] \" +\n                           \"[--disable-v2] \" +\n                           \"[--enable-v4] \" +\n                           \"publickey.x509[.pem] privatekey.pk8 \" +\n                           \"[publickey2.x509[.pem] privatekey2.pk8 ...] \" +\n                           \"input.jar output.jar [output-v4-file]\");\n        System.exit(2);\n    }\n\n    public static void main(String[] args) {\n        if (args.length < 4) usage();\n\n        // Install Conscrypt as the highest-priority provider. Its crypto primitives are faster than\n        // the standard or Bouncy Castle ones.\n        Security.insertProviderAt(new OpenSSLProvider(), 1);\n        // Install Bouncy Castle (as the lowest-priority provider) because Conscrypt does not offer\n        // DSA which may still be needed.\n        // TODO: Stop installing Bouncy Castle provider once DSA is no longer needed.\n        Security.addProvider(new BouncyCastleProvider());\n\n        boolean signWholeFile = false;\n        String providerClass = null;\n        String providerArg = null;\n        String keyStoreName = null;\n        String keyStorePin = null;\n        int alignment = 4;\n        boolean alignFileSize = false;\n        Integer minSdkVersionOverride = null;\n        boolean signUsingApkSignatureSchemeV2 = true;\n        boolean signUsingApkSignatureSchemeV4 = false;\n        SigningCertificateLineage certLineage = null;\n        Integer rotationMinSdkVersion = null;\n\n        int argstart = 0;\n        while (argstart < args.length && args[argstart].startsWith(\"-\")) {\n            if (\"-w\".equals(args[argstart])) {\n                signWholeFile = true;\n                ++argstart;\n            } else if (\"-providerClass\".equals(args[argstart])) {\n                if (argstart + 1 >= args.length) {\n                    usage();\n                }\n                providerClass = args[++argstart];\n                ++argstart;\n            } else if(\"-providerArg\".equals(args[argstart])) {\n                if (argstart + 1 >= args.length) {\n                    usage();\n                }\n                providerArg = args[++argstart];\n                ++argstart;\n            } else if (\"-loadPrivateKeysFromKeyStore\".equals(args[argstart])) {\n                if (argstart + 1 >= args.length) {\n                    usage();\n                }\n                keyStoreName = args[++argstart];\n                ++argstart;\n            } else if (\"-keyStorePin\".equals(args[argstart])) {\n                if (argstart + 1 >= args.length) {\n                    usage();\n                }\n                keyStorePin = args[++argstart];\n                ++argstart;\n            } else if (\"-a\".equals(args[argstart])) {\n                alignment = Integer.parseInt(args[++argstart]);\n                ++argstart;\n            } else if (\"--align-file-size\".equals(args[argstart])) {\n                alignFileSize = true;\n                ++argstart;\n            } else if (\"--min-sdk-version\".equals(args[argstart])) {\n                String minSdkVersionString = args[++argstart];\n                try {\n                    minSdkVersionOverride = Integer.parseInt(minSdkVersionString);\n                } catch (NumberFormatException e) {\n                    throw new IllegalArgumentException(\n                            \"--min-sdk-version must be a decimal number: \" + minSdkVersionString);\n                }\n                ++argstart;\n            } else if (\"--disable-v2\".equals(args[argstart])) {\n                signUsingApkSignatureSchemeV2 = false;\n                ++argstart;\n            } else if (\"--enable-v4\".equals(args[argstart])) {\n                signUsingApkSignatureSchemeV4 = true;\n                ++argstart;\n            } else if (\"--lineage\".equals(args[argstart])) {\n                File lineageFile = new File(args[++argstart]);\n                try {\n                    certLineage = SigningCertificateLineage.readFromFile(lineageFile);\n                } catch (Exception e) {\n                    throw new IllegalArgumentException(\n                            \"Error reading lineage file: \" + e.getMessage());\n                }\n                ++argstart;\n            } else if (\"--rotation-min-sdk-version\".equals(args[argstart])) {\n                String rotationMinSdkVersionString = args[++argstart];\n                try {\n                    rotationMinSdkVersion = Integer.parseInt(rotationMinSdkVersionString);\n                } catch (NumberFormatException e) {\n                    throw new IllegalArgumentException(\n                            \"--rotation-min-sdk-version must be a decimal number: \" + rotationMinSdkVersionString);\n                }\n                ++argstart;\n            } else {\n                usage();\n            }\n        }\n\n        int numArgsExcludeV4FilePath;\n        if (signUsingApkSignatureSchemeV4) {\n            numArgsExcludeV4FilePath = args.length - 1;\n        } else {\n            numArgsExcludeV4FilePath = args.length;\n        }\n        if ((numArgsExcludeV4FilePath - argstart) % 2 == 1) usage();\n        int numKeys = ((numArgsExcludeV4FilePath - argstart) / 2) - 1;\n        if (signWholeFile && numKeys > 1) {\n            System.err.println(\"Only one key may be used with -w.\");\n            System.exit(2);\n        }\n\n        loadProviderIfNecessary(providerClass, providerArg);\n\n        String inputFilename = args[numArgsExcludeV4FilePath - 2];\n        String outputFilename = args[numArgsExcludeV4FilePath - 1];\n        String outputV4Filename = \"\";\n        if (signUsingApkSignatureSchemeV4) {\n            outputV4Filename = args[args.length - 1];\n        }\n\n        JarFile inputJar = null;\n        FileOutputStream outputFile = null;\n\n        try {\n            File firstPublicKeyFile = new File(args[argstart+0]);\n\n            X509Certificate[] publicKey = new X509Certificate[numKeys];\n            try {\n                for (int i = 0; i < numKeys; ++i) {\n                    int argNum = argstart + i*2;\n                    publicKey[i] = readPublicKey(new File(args[argNum]));\n                }\n            } catch (IllegalArgumentException e) {\n                System.err.println(e);\n                System.exit(1);\n            }\n\n            // Set all ZIP file timestamps to Jan 1 2009 00:00:00.\n            long timestamp = 1230768000000L;\n            // The Java ZipEntry API we're using converts milliseconds since epoch into MS-DOS\n            // timestamp using the current timezone. We thus adjust the milliseconds since epoch\n            // value to end up with MS-DOS timestamp of Jan 1 2009 00:00:00.\n            timestamp -= TimeZone.getDefault().getOffset(timestamp);\n            KeyStore keyStore = null;\n            if (keyStoreName != null) {\n                keyStore = createKeyStore(keyStoreName, keyStorePin);\n            }\n            PrivateKey[] privateKey = new PrivateKey[numKeys];\n            for (int i = 0; i < numKeys; ++i) {\n                int argNum = argstart + i*2 + 1;\n                if (keyStore == null) {\n                    privateKey[i] = readPrivateKey(new File(args[argNum]));\n                } else {\n                    final String keyAlias = args[argNum];\n                    privateKey[i] = loadPrivateKeyFromKeyStore(keyStore, keyAlias);\n                }\n            }\n            inputJar = new JarFile(new File(inputFilename), false);  // Don't verify.\n\n            outputFile = new FileOutputStream(outputFilename);\n\n            // NOTE: Signing currently recompresses any compressed entries using Deflate (default\n            // compression level for OTA update files and maximum compession level for APKs).\n            if (signWholeFile) {\n                int digestAlgorithm = getDigestAlgorithmForOta(publicKey[0]);\n                signWholeFile(inputJar, firstPublicKeyFile,\n                        publicKey[0], privateKey[0], digestAlgorithm,\n                        timestamp,\n                        outputFile);\n            } else {\n                // Determine the value to use as minSdkVersion of the APK being signed\n                int minSdkVersion;\n                if (minSdkVersionOverride != null) {\n                    minSdkVersion = minSdkVersionOverride;\n                } else {\n                    try {\n                        minSdkVersion = getMinSdkVersion(inputJar);\n                    } catch (MinSdkVersionException e) {\n                        throw new IllegalArgumentException(\n                                \"Cannot detect minSdkVersion. Use --min-sdk-version to override\",\n                                e);\n                    }\n                }\n\n                DefaultApkSignerEngine.Builder builder = new DefaultApkSignerEngine.Builder(\n                    createSignerConfigs(privateKey, publicKey), minSdkVersion)\n                    .setV1SigningEnabled(true)\n                    .setV2SigningEnabled(signUsingApkSignatureSchemeV2)\n                    .setOtherSignersSignaturesPreserved(false)\n                    .setCreatedBy(\"1.0 (Android SignApk)\");\n\n                if (certLineage != null) {\n                   builder = builder.setSigningCertificateLineage(certLineage);\n                }\n\n                if (rotationMinSdkVersion != null) {\n                   builder = builder.setMinSdkVersionForRotation(rotationMinSdkVersion);\n                }\n\n                try (ApkSignerEngine apkSigner = builder.build()) {\n                    // We don't preserve the input APK's APK Signing Block (which contains v2\n                    // signatures)\n                    apkSigner.inputApkSigningBlock(null);\n\n                    CountingOutputStream outputJarCounter =\n                            new CountingOutputStream(outputFile);\n                    JarOutputStream outputJar = new JarOutputStream(outputJarCounter);\n                    // Use maximum compression for compressed entries because the APK lives forever\n                    // on the system partition.\n                    outputJar.setLevel(9);\n                    copyFiles(inputJar, null, apkSigner, outputJar,\n                              outputJarCounter, timestamp, alignment);\n                    ApkSignerEngine.OutputJarSignatureRequest addV1SignatureRequest =\n                            apkSigner.outputJarEntries();\n                    if (addV1SignatureRequest != null) {\n                        addV1Signature(apkSigner, addV1SignatureRequest, outputJar, timestamp);\n                        addV1SignatureRequest.done();\n                    }\n\n                    // close output and switch to input mode\n                    outputJar.close();\n                    outputJar = null;\n                    outputJarCounter = null;\n                    outputFile = null;\n                    RandomAccessFile v1SignedApk = new RandomAccessFile(outputFilename, \"r\");\n\n                    ZipSections zipSections = findMainZipSections(DataSources.asDataSource(\n                            v1SignedApk));\n\n                    ByteBuffer eocd = ByteBuffer.allocate(zipSections.eocd.remaining());\n                    eocd.put(zipSections.eocd);\n                    eocd.flip();\n                    eocd.order(ByteOrder.LITTLE_ENDIAN);\n\n                    ByteBuffer[] outputChunks = new ByteBuffer[] {};\n\n                    // This loop is supposed to be iterated twice at most.\n                    // The second pass is to align the file size after amending EOCD comments\n                    // with assumption that re-generated signing block would be the same size.\n                    while (true) {\n                        ApkSignerEngine.OutputApkSigningBlockRequest2 addV2SignatureRequest =\n                                apkSigner.outputZipSections2(\n                                        zipSections.beforeCentralDir,\n                                        DataSources.asDataSource(zipSections.centralDir),\n                                        DataSources.asDataSource(eocd));\n                        if (addV2SignatureRequest == null) break;\n\n                        // Need to insert the returned APK Signing Block before ZIP Central\n                        // Directory.\n                        int padding = addV2SignatureRequest.getPaddingSizeBeforeApkSigningBlock();\n                        byte[] apkSigningBlock = addV2SignatureRequest.getApkSigningBlock();\n                        // Because the APK Signing Block is inserted before the Central Directory,\n                        // we need to adjust accordingly the offset of Central Directory inside the\n                        // ZIP End of Central Directory (EoCD) record.\n                        ByteBuffer modifiedEocd = ByteBuffer.allocate(eocd.remaining());\n                        modifiedEocd.put(eocd);\n                        modifiedEocd.flip();\n                        modifiedEocd.order(ByteOrder.LITTLE_ENDIAN);\n                        ApkUtils.setZipEocdCentralDirectoryOffset(\n                                modifiedEocd,\n                                zipSections.beforeCentralDir.size() + padding +\n                                apkSigningBlock.length);\n                        outputChunks =\n                                new ByteBuffer[] {\n                                        ByteBuffer.allocate(padding),\n                                        ByteBuffer.wrap(apkSigningBlock),\n                                        zipSections.centralDir,\n                                        modifiedEocd};\n                        addV2SignatureRequest.done();\n\n                        // Exit the loop if we don't need to align the file size\n                        if (!alignFileSize || alignment < 2) {\n                            break;\n                        }\n\n                        // Calculate the file size\n                        eocd = modifiedEocd;\n                        long fileSize = zipSections.beforeCentralDirSize;\n                        for (ByteBuffer buf : outputChunks) {\n                            fileSize += buf.remaining();\n                        }\n                        // Exit the loop because the file size is aligned.\n                        if (fileSize % alignment == 0) {\n                            break;\n                        }\n                        // Pad EOCD comment to align the file size.\n                        int commentLen = alignment - (int)(fileSize % alignment);\n                        modifiedEocd = ByteBuffer.allocate(eocd.remaining() + commentLen);\n                        modifiedEocd.put(eocd);\n                        modifiedEocd.rewind();\n                        modifiedEocd.order(ByteOrder.LITTLE_ENDIAN);\n                        ApkUtils.updateZipEocdCommentLen(modifiedEocd);\n                        // Since V2 signing block should cover modified EOCD,\n                        // re-iterate the loop with modified EOCD.\n                        eocd = modifiedEocd;\n                    }\n\n                    // close input and switch back to output mode\n                    v1SignedApk.close();\n                    v1SignedApk = null;\n                    outputFile = new FileOutputStream(outputFilename, true);\n                    outputFile.getChannel().truncate(zipSections.beforeCentralDirSize);\n\n                    // This assumes outputChunks are array-backed. To avoid this assumption, the\n                    // code could be rewritten to use FileChannel.\n                    for (ByteBuffer outputChunk : outputChunks) {\n                        outputFile.write(\n                                outputChunk.array(),\n                                outputChunk.arrayOffset() + outputChunk.position(),\n                                outputChunk.remaining());\n                        outputChunk.position(outputChunk.limit());\n                    }\n\n                    outputFile.close();\n                    outputFile = null;\n                    apkSigner.outputDone();\n\n                    if (signUsingApkSignatureSchemeV4) {\n                        final DataSource outputApkIn = DataSources.asDataSource(\n                                new RandomAccessFile(new File(outputFilename), \"r\"));\n                        final File outputV4File =  new File(outputV4Filename);\n                        apkSigner.signV4(outputApkIn, outputV4File, false /* ignore failures */);\n                    }\n                }\n\n                return;\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n            System.exit(1);\n        } finally {\n            try {\n                if (inputJar != null) inputJar.close();\n                if (outputFile != null) outputFile.close();\n            } catch (IOException e) {\n                e.printStackTrace();\n                System.exit(1);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tools/signapk/test/run",
    "content": "#!/usr/bin/make -f\n\npackage := NotePad.apk\n\nall: out/signed-$(package)\n\nclean:\n\trm -rf out\n\n.PHONY: FORCE\n\nDSAPARAM := out/dsaparam\n$(DSAPARAM):\n\tmkdir -p $(dir $@)\n\tumask 0077 && openssl dsaparam -out $@ 1024\n\n%.pem: $(DSAPARAM) FORCE\n\tmkdir -p $(dir $@)\n\tumask 0077 && openssl gendsa -out $@.pk~ $(DSAPARAM)\n\tumask 0077 && openssl pkcs8 -topk8 -nocrypt \\\n\t\t-in $@.pk~ -out $@.pk\n\tumask 0077 && openssl req -new -x509 -key $@.pk -out $@ -days 1095 \\\n\t    -subj \"/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com\"\n\ncert := out/key1.pem\nout/signed-$(package): $(package) $(cert)\n\tmkdir -p $(dir $@)\n\tSIGNAPK_DEBUG=1 \\\n\tsignapk -input $< -output $@ \\\n\t\t-key $(cert).pk -cert $(cert) -tempdir out\n"
  },
  {
    "path": "tools/signtos/Android.bp",
    "content": "//\n// Copyright (C) 2014 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n// the signtos tool - signs Trusty images\n// ============================================================\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\njava_library_host {\n    name: \"signtos\",\n    srcs: [\"SignTos.java\"],\n    manifest: \"SignTos.mf\",\n    static_libs: [\n        \"bouncycastle-unbundled\",\n        \"bouncycastle-bcpkix-unbundled\",\n    ],\n}\n"
  },
  {
    "path": "tools/signtos/SignTos.java",
    "content": "/*\n * Copyright 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.signtos;\n\nimport org.bouncycastle.asn1.ASN1InputStream;\nimport org.bouncycastle.asn1.pkcs.PrivateKeyInfo;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.DataInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.lang.reflect.Constructor;\nimport java.security.GeneralSecurityException;\nimport java.security.Key;\nimport java.security.KeyFactory;\nimport java.security.MessageDigest;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.security.Security;\nimport java.security.Signature;\nimport java.security.interfaces.ECKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.util.Arrays;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.EncryptedPrivateKeyInfo;\nimport javax.crypto.SecretKeyFactory;\nimport javax.crypto.spec.PBEKeySpec;\n\n/**\n * Signs Trusty images for use with operating systems that support it.\n */\npublic class SignTos {\n    /** Size of the signature footer in bytes. */\n    private static final int SIGNATURE_BLOCK_SIZE = 256;\n\n    /** Current signature version code we use. */\n    private static final int VERSION_CODE = 1;\n\n    /** Size of the header on the file to skip. */\n    private static final int HEADER_SIZE = 512;\n\n    private static BouncyCastleProvider sBouncyCastleProvider;\n\n    /**\n     * Reads the password from stdin and returns it as a string.\n     *\n     * @param keyFile The file containing the private key.  Used to prompt the user.\n     */\n    private static String readPassword(File keyFile) {\n        // TODO: use Console.readPassword() when it's available.\n        System.out.print(\"Enter password for \" + keyFile + \" (password will not be hidden): \");\n        System.out.flush();\n        BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));\n        try {\n            return stdin.readLine();\n        } catch (IOException ex) {\n            return null;\n        }\n    }\n\n    /**\n     * Decrypt an encrypted PKCS#8 format private key.\n     *\n     * Based on ghstark's post on Aug 6, 2006 at\n     * http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949\n     *\n     * @param encryptedPrivateKey The raw data of the private key\n     * @param keyFile The file containing the private key\n     */\n    private static PKCS8EncodedKeySpec decryptPrivateKey(byte[] encryptedPrivateKey, File keyFile)\n        throws GeneralSecurityException {\n        EncryptedPrivateKeyInfo epkInfo;\n        try {\n            epkInfo = new EncryptedPrivateKeyInfo(encryptedPrivateKey);\n        } catch (IOException ex) {\n            // Probably not an encrypted key.\n            return null;\n        }\n\n        char[] password = readPassword(keyFile).toCharArray();\n\n        SecretKeyFactory skFactory = SecretKeyFactory.getInstance(epkInfo.getAlgName());\n        Key key = skFactory.generateSecret(new PBEKeySpec(password));\n\n        Cipher cipher = Cipher.getInstance(epkInfo.getAlgName());\n        cipher.init(Cipher.DECRYPT_MODE, key, epkInfo.getAlgParameters());\n\n        try {\n            return epkInfo.getKeySpec(cipher);\n        } catch (InvalidKeySpecException ex) {\n            System.err.println(\"signapk: Password for \" + keyFile + \" may be bad.\");\n            throw ex;\n        }\n    }\n\n    /** Read a PKCS#8 format private key. */\n    private static PrivateKey readPrivateKey(File file) throws IOException,\n            GeneralSecurityException {\n        DataInputStream input = new DataInputStream(new FileInputStream(file));\n        try {\n            byte[] bytes = new byte[(int) file.length()];\n            input.read(bytes);\n\n            /* Check to see if this is in an EncryptedPrivateKeyInfo structure. */\n            PKCS8EncodedKeySpec spec = decryptPrivateKey(bytes, file);\n            if (spec == null) {\n                spec = new PKCS8EncodedKeySpec(bytes);\n            }\n\n            /*\n             * Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm\n             * OID and use that to construct a KeyFactory.\n             */\n            ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));\n            PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());\n            String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();\n\n            return KeyFactory.getInstance(algOid).generatePrivate(spec);\n        } finally {\n            input.close();\n        }\n    }\n\n    /**\n     * Tries to load a JSE Provider by class name. This is for custom PrivateKey\n     * types that might be stored in PKCS#11-like storage.\n     */\n    private static void loadProviderIfNecessary(String providerClassName) {\n        if (providerClassName == null) {\n            return;\n        }\n\n        final Class<?> klass;\n        try {\n            final ClassLoader sysLoader = ClassLoader.getSystemClassLoader();\n            if (sysLoader != null) {\n                klass = sysLoader.loadClass(providerClassName);\n            } else {\n                klass = Class.forName(providerClassName);\n            }\n        } catch (ClassNotFoundException e) {\n            e.printStackTrace();\n            System.exit(1);\n            return;\n        }\n\n        Constructor<?> constructor = null;\n        for (Constructor<?> c : klass.getConstructors()) {\n            if (c.getParameterTypes().length == 0) {\n                constructor = c;\n                break;\n            }\n        }\n        if (constructor == null) {\n            System.err.println(\"No zero-arg constructor found for \" + providerClassName);\n            System.exit(1);\n            return;\n        }\n\n        final Object o;\n        try {\n            o = constructor.newInstance();\n        } catch (Exception e) {\n            e.printStackTrace();\n            System.exit(1);\n            return;\n        }\n        if (!(o instanceof Provider)) {\n            System.err.println(\"Not a Provider class: \" + providerClassName);\n            System.exit(1);\n        }\n\n        Security.insertProviderAt((Provider) o, 1);\n    }\n\n    private static String getSignatureAlgorithm(Key key) {\n        if (\"EC\".equals(key.getAlgorithm())) {\n            ECKey ecKey = (ECKey) key;\n            int curveSize = ecKey.getParams().getOrder().bitLength();\n            if (curveSize <= 256) {\n                return \"SHA256withECDSA\";\n            } else if (curveSize <= 384) {\n                return \"SHA384withECDSA\";\n            } else {\n                return \"SHA512withECDSA\";\n            }\n        } else {\n            throw new IllegalArgumentException(\"Unsupported key type \" + key.getAlgorithm());\n        }\n    }\n\n    /**\n     * @param inputFilename\n     * @param outputFilename\n     */\n    private static void signWholeFile(InputStream input, OutputStream output, PrivateKey signingKey)\n            throws Exception {\n        Signature sig = Signature.getInstance(getSignatureAlgorithm(signingKey));\n        sig.initSign(signingKey);\n\n        byte[] buffer = new byte[8192];\n\n        /* Skip the header. */\n        int skippedBytes = 0;\n        while (skippedBytes != HEADER_SIZE) {\n            int bytesRead = input.read(buffer, 0, HEADER_SIZE - skippedBytes);\n            output.write(buffer, 0, bytesRead);\n            skippedBytes += bytesRead;\n        }\n\n        int totalBytes = 0;\n        for (;;) {\n            int bytesRead = input.read(buffer);\n            if (bytesRead == -1) {\n                break;\n            }\n            totalBytes += bytesRead;\n            sig.update(buffer, 0, bytesRead);\n            output.write(buffer, 0, bytesRead);\n        }\n\n        byte[] sigBlock = new byte[SIGNATURE_BLOCK_SIZE];\n        sigBlock[0] = VERSION_CODE;\n        sig.sign(sigBlock, 1, sigBlock.length - 1);\n\n        output.write(sigBlock);\n    }\n\n    private static void usage() {\n        System.err.println(\"Usage: signtos \" +\n                           \"[-providerClass <className>] \" +\n                           \" privatekey.pk8 \" +\n                           \"input.img output.img\");\n        System.exit(2);\n    }\n\n    public static void main(String[] args) throws Exception {\n        if (args.length < 3) {\n            usage();\n        }\n\n        String providerClass = null;\n        String providerArg = null;\n\n        int argstart = 0;\n        while (argstart < args.length && args[argstart].startsWith(\"-\")) {\n            if (\"-providerClass\".equals(args[argstart])) {\n                if (argstart + 1 >= args.length) {\n                    usage();\n                }\n                providerClass = args[++argstart];\n                ++argstart;\n            } else {\n                usage();\n            }\n        }\n\n        /*\n         * Should only be \"<privatekey> <input> <output>\" left.\n         */\n        if (argstart != args.length - 3) {\n            usage();\n        }\n\n        sBouncyCastleProvider = new BouncyCastleProvider();\n        Security.addProvider(sBouncyCastleProvider);\n\n        loadProviderIfNecessary(providerClass);\n\n        String keyFilename = args[args.length - 3];\n        String inputFilename = args[args.length - 2];\n        String outputFilename = args[args.length - 1];\n\n        PrivateKey privateKey = readPrivateKey(new File(keyFilename));\n\n        InputStream input = new BufferedInputStream(new FileInputStream(inputFilename));\n        OutputStream output = new BufferedOutputStream(new FileOutputStream(outputFilename));\n        try {\n            SignTos.signWholeFile(input, output, privateKey);\n        } finally {\n            input.close();\n            output.close();\n        }\n\n        System.out.println(\"Successfully signed: \" + outputFilename);\n    }\n}\n"
  },
  {
    "path": "tools/signtos/SignTos.mf",
    "content": "Main-Class: com.android.signtos.SignTos\n"
  },
  {
    "path": "tools/soong_to_convert.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2016 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Tool to prioritize which modules to convert to Soong.\n\nGenerally, you'd use this through the make integration, which automatically\ngenerates the CSV input file that this tool expects:\n\n  $ m $OUT/soong_to_convert.txt\n  $ less $OUT/soong_to_convert.txt\n\nThe output is a list of modules that are probably ready to convert to Soong:\n\n  # Blocked on Module (potential problems)\n           283 libEGL (srcs_dotarm)\n           246 libicuuc (dotdot_incs dotdot_srcs)\n           221 libspeexresampler\n           215 libcamera_metadata\n               ...\n             0 zram-perf (dotdot_incs)\n\nThe number at the beginning of the line shows how many native modules depend\non that module.\n\nAll of their dependencies have been satisfied, and any potential problems\nthat Make can detect are listed in parenthesis after the module:\n\n  dotdot_srcs: LOCAL_SRC_FILES contains paths outside $(LOCAL_PATH)\n  dotdot_incs: LOCAL_C_INCLUDES contains paths include '..'\n  srcs_dotarm: LOCAL_SRC_FILES contains source files like <...>.c.arm\n  aidl: LOCAL_SRC_FILES contains .aidl sources\n  objc: LOCAL_SRC_FILES contains Objective-C sources\n  proto: LOCAL_SRC_FILES contains .proto sources\n  rs: LOCAL_SRC_FILES contains renderscript sources\n  vts: LOCAL_SRC_FILES contains .vts sources\n\nNot all problems can be discovered, but this is a starting point.\n\n\"\"\"\nimport csv\nimport sys\n\ndef count_deps(depsdb, module, seen):\n    \"\"\"Based on the depsdb, count the number of transitive dependencies.\n\n    You can pass in an reversed dependency graph to conut the number of\n    modules that depend on the module.\"\"\"\n    count = 0\n    seen.append(module)\n    if module in depsdb:\n        for dep in depsdb[module]:\n            if dep in seen:\n                continue\n            count += 1 + count_deps(depsdb, dep, seen)\n    return count\n\ndef process(reader):\n    \"\"\"Read the input file and produce a list of modules ready to move to Soong\n    \"\"\"\n    problems = dict()\n    deps = dict()\n    reverse_deps = dict()\n    module_types = dict()\n\n    for (module, module_type, problem, dependencies, makefiles, installed) in reader:\n        module_types[module] = module_type\n        problems[module] = problem\n        deps[module] = [d for d in dependencies.strip().split(' ') if d != \"\"]\n        for dep in deps[module]:\n            if not dep in reverse_deps:\n                reverse_deps[dep] = []\n            reverse_deps[dep].append(module)\n\n    results = []\n    for module in problems:\n        # Only display actionable conversions, ones without missing dependencies\n        if len(deps[module]) != 0:\n            continue\n\n        extra = \"\"\n        if len(problems[module]) > 0:\n            extra = \" ({})\".format(problems[module])\n        results.append((count_deps(reverse_deps, module, []), module + extra, module_types[module]))\n\n    return sorted(results, key=lambda result: (-result[0], result[1]))\n\ndef filter(results, module_type):\n    return [x for x in results if x[2] == module_type]\n\ndef display(results):\n    \"\"\"Displays the results\"\"\"\n    count_header = \"# Blocked on\"\n    count_width = len(count_header)\n    print(\"{} Module (potential problems)\".format(count_header))\n    for (count, module, module_type) in results:\n        print(\"{:>{}} {}\".format(count, count_width, module))\n\ndef main(filename):\n    \"\"\"Read the CSV file, print the results\"\"\"\n    with open(filename, 'r') as csvfile:\n        results = process(csv.reader(csvfile))\n\n    native_results = filter(results, \"native\")\n    java_results = filter(results, \"java\")\n\n    print(\"native modules ready to convert\")\n    display(native_results)\n\n    print(\"\")\n    print(\"java modules ready to convert\")\n    display(java_results)\n\nif __name__ == \"__main__\":\n    if len(sys.argv) != 2:\n        print(\"usage: soong_conversion.py <file>\", file=sys.stderr)\n        sys.exit(1)\n\n    main(sys.argv[1])\n"
  },
  {
    "path": "tools/stub_diff_analyzer.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom sys import exit\nfrom typing import List\nfrom glob import glob\nfrom pathlib import Path\nfrom collections import defaultdict\nfrom difflib import Differ\nfrom re import split\nfrom tqdm import tqdm\nimport argparse\n\n\nDIFFER_CODE_LEN = 2\n\nclass DifferCodes:\n    COMMON = '  '\n    UNIQUE_FIRST = '- '\n    UNIQUE_SECOND = '+ '\n    DIFF_IDENT = '? '\n\nclass FilesDiffAnalyzer:\n    def __init__(self, args) -> None:\n        self.out_dir = args.out_dir\n        self.show_diff = args.show_diff\n        self.skip_words = args.skip_words\n        self.first_dir = args.first_dir\n        self.second_dir = args.second_dir\n        self.include_common = args.include_common\n\n        self.first_dir_files = self.get_files(self.first_dir)\n        self.second_dir_files = self.get_files(self.second_dir)\n        self.common_file_map = defaultdict(set)\n\n        self.map_common_files(self.first_dir_files, self.first_dir)\n        self.map_common_files(self.second_dir_files, self.second_dir)\n\n    def get_files(self, dir: str) -> List[str]:\n        \"\"\"Get all files directory in the input directory including the files in the subdirectories\n\n        Recursively finds all files in the input directory.\n        Returns a list of file directory strings, which do not include directories but only files.\n        List is sorted in alphabetical order of the file directories.\n\n        Args:\n            dir: Directory to get the files. String.\n\n        Returns:\n            A list of file directory strings within the input directory.\n            Sorted in Alphabetical order.\n\n        Raises:\n            FileNotFoundError: An error occurred accessing the non-existing directory\n        \"\"\"\n\n        if not dir_exists(dir):\n            raise FileNotFoundError(\"Directory does not exist\")\n\n        if dir[:-2] != \"**\":\n            if dir[:-1] != \"/\":\n                dir += \"/\"\n            dir += \"**\"\n\n        return [file for file in sorted(glob(dir, recursive=True)) if Path(file).is_file()]\n\n    def map_common_files(self, files: List[str], dir: str) -> None:\n        for file in files:\n            file_name = file.split(dir, 1)[-1]\n            self.common_file_map[file_name].add(dir)\n        return\n\n    def compare_file_contents(self, first_file: str, second_file: str) -> List[str]:\n        \"\"\"Compare the contents of the files and return different lines\n\n        Given two file directory strings, compare the contents of the two files\n        and return the list of file contents string prepended with unique identifier codes.\n        The identifier codes include:\n        - '  '(two empty space characters): Line common to two files\n        - '- '(minus followed by a space) : Line unique to first file\n        - '+ '(plus followed by a space)  : Line unique to second file\n\n        Args:\n            first_file: First file directory string to compare the content\n            second_file: Second file directory string to compare the content\n\n        Returns:\n            A list of the file content strings. For example:\n\n            [\n                \"  Foo\",\n                \"- Bar\",\n                \"+ Baz\"\n            ]\n        \"\"\"\n\n        d = Differ()\n        first_file_contents = sort_methods(get_file_contents(first_file))\n        second_file_contents = sort_methods(get_file_contents(second_file))\n        diff = list(d.compare(first_file_contents, second_file_contents))\n        ret = [f\"diff {first_file} {second_file}\"]\n\n        idx = 0\n        while idx < len(diff):\n            line = diff[idx]\n            line_code = line[:DIFFER_CODE_LEN]\n\n            match line_code:\n                case DifferCodes.COMMON:\n                    if self.include_common:\n                        ret.append(line)\n\n                case DifferCodes.UNIQUE_FIRST:\n                    # Should compare line\n                    if (idx < len(diff) - 1 and\n                        (next_line_code := diff[idx + 1][:DIFFER_CODE_LEN])\n                        not in (DifferCodes.UNIQUE_FIRST, DifferCodes.COMMON)):\n                        delta = 1 if next_line_code == DifferCodes.UNIQUE_SECOND else 2\n                        line_to_compare = diff[idx + delta]\n                        if self.lines_differ(line, line_to_compare):\n                            ret.extend([line, line_to_compare])\n                        else:\n                            if self.include_common:\n                                ret.append(DifferCodes.COMMON +\n                                           line[DIFFER_CODE_LEN:])\n                        idx += delta\n                    else:\n                        ret.append(line)\n\n                case DifferCodes.UNIQUE_SECOND:\n                    ret.append(line)\n\n                case DifferCodes.DIFF_IDENT:\n                    pass\n            idx += 1\n        return ret\n\n    def lines_differ(self, line1: str, line2: str) -> bool:\n        \"\"\"Check if the input lines are different or not\n\n        Compare the two lines word by word and check if the two lines are different or not.\n        If the different words in the comparing lines are included in skip_words,\n        the lines are not considered different.\n\n        Args:\n            line1:      first line to compare\n            line2:      second line to compare\n\n        Returns:\n            Boolean value indicating if the two lines are different or not\n\n        \"\"\"\n        # Split by '.' or ' '(whitespace)\n        def split_words(line: str) -> List[str]:\n            return split('\\\\s|\\\\.', line[DIFFER_CODE_LEN:])\n\n        line1_words, line2_words = split_words(line1), split_words(line2)\n        if len(line1_words) != len(line2_words):\n            return True\n\n        for word1, word2 in zip(line1_words, line2_words):\n            if word1 != word2:\n                # not check if words are equal to skip word, but\n                # check if words contain skip word as substring\n                if all(sw not in word1 and sw not in word2 for sw in self.skip_words):\n                    return True\n\n        return False\n\n    def analyze(self) -> None:\n        \"\"\"Analyze file contents in both directories and write to output or console.\n        \"\"\"\n        for file in tqdm(sorted(self.common_file_map.keys())):\n            val = self.common_file_map[file]\n\n            # When file exists in both directories\n            lines = list()\n            if val == set([self.first_dir, self.second_dir]):\n                lines = self.compare_file_contents(\n                    self.first_dir + file, self.second_dir + file)\n            else:\n                existing_dir, not_existing_dir = (\n                    (self.first_dir, self.second_dir) if self.first_dir in val\n                    else (self.second_dir, self.first_dir))\n\n                lines = [f\"{not_existing_dir}{file} does not exist.\"]\n\n                if self.show_diff:\n                    lines.append(f\"Content of {existing_dir}{file}: \\n\")\n                    lines.extend(get_file_contents(existing_dir + file))\n\n            self.write(lines)\n\n    def write(self, lines: List[str]) -> None:\n        if self.out_dir == \"\":\n            pprint(lines)\n        else:\n            write_lines(self.out_dir, lines)\n\n###\n# Helper functions\n###\n\ndef sort_methods(lines: List[str]) -> List[str]:\n    \"\"\"Sort class methods in the file contents by alphabetical order\n\n    Given lines of Java file contents, return lines with class methods sorted in alphabetical order.\n    Also omit empty lines or lines with spaces.\n    For example:\n        l = [\n            \"package android.test;\",\n            \"\",\n            \"public static final int ORANGE = 1;\",\n            \"\",\n            \"public class TestClass {\",\n            \"public TestClass() { throw new RuntimeException(\"Stub!\"); }\",\n            \"public void foo() { throw new RuntimeException(\"Stub!\"); }\",\n            \"public void bar() { throw new RuntimeException(\"Stub!\"); }\",\n            \"}\"\n        ]\n        sort_methods(l) returns\n        [\n            \"package android.test;\",\n            \"public static final int ORANGE = 1;\",\n            \"public class TestClass {\",\n            \"public TestClass() { throw new RuntimeException(\"Stub!\"); }\",\n            \"public void bar() { throw new RuntimeException(\"Stub!\"); }\",\n            \"public void foo() { throw new RuntimeException(\"Stub!\"); }\",\n            \"}\"\n        ]\n\n    Args:\n        lines: List of strings consisted of Java file contents.\n\n    Returns:\n        A list of string with sorted class methods.\n\n    \"\"\"\n    def is_not_blank(l: str) -> bool:\n        return bool(l) and not l.isspace()\n\n    ret = list()\n\n    in_class = False\n    buffer = list()\n    for line in lines:\n        if not in_class:\n            if \"class\" in line:\n                in_class = True\n                ret.append(line)\n            else:\n                # Adding static variables, package info, etc.\n                # Skipping empty or space lines.\n                if is_not_blank(line):\n                    ret.append(line)\n        else:\n            # End of class\n            if line and line[0] == \"}\":\n                in_class = False\n                ret.extend(sorted(buffer))\n                buffer = list()\n                ret.append(line)\n            else:\n                if is_not_blank(line):\n                    buffer.append(line)\n\n    return ret\n\ndef get_file_contents(file_path: str) -> List[str]:\n    lines = list()\n    with open(file_path) as f:\n        lines = [line.rstrip('\\n') for line in f]\n        f.close()\n    return lines\n\ndef pprint(l: List[str]) -> None:\n    for line in l:\n        print(line)\n\ndef write_lines(out_dir: str, lines: List[str]) -> None:\n    with open(out_dir, \"a\") as f:\n        f.writelines(line + '\\n' for line in lines)\n        f.write(\"\\n\")\n        f.close()\n\ndef dir_exists(dir: str) -> bool:\n    return Path(dir).exists()\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n    parser.add_argument('first_dir', action='store', type=str,\n                        help=\"first path to compare file directory and contents\")\n    parser.add_argument('second_dir', action='store', type=str,\n                        help=\"second path to compare file directory and contents\")\n    parser.add_argument('--out', dest='out_dir',\n                        action='store', default=\"\", type=str,\n                        help=\"optional directory to write log. If not set, will print to console\")\n    parser.add_argument('--show-diff-file', dest='show_diff',\n                        action=argparse.BooleanOptionalAction,\n                        help=\"optional flag. If passed, will print out the content of the file unique to each directories\")\n    parser.add_argument('--include-common', dest='include_common',\n                        action=argparse.BooleanOptionalAction,\n                        help=\"optional flag. If passed, will print out the contents common to both files as well,\\\n                            instead of printing only diff lines.\")\n    parser.add_argument('--skip-words', nargs='+',\n                        dest='skip_words', default=[], help=\"optional words to skip in comparison\")\n\n    args = parser.parse_args()\n\n    if not args.first_dir or not args.second_dir:\n        parser.print_usage()\n        exit(0)\n\n    analyzer = FilesDiffAnalyzer(args)\n    analyzer.analyze()\n"
  },
  {
    "path": "tools/test_extract_kernel.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (C) 2018 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport unittest\nfrom extract_kernel import dump_version\n\nclass ExtractKernelTest(unittest.TestCase):\n  def test_extract_version(self):\n    self.assertEqual(\"4.9.100\", dump_version(\n        b'Linux version 4.9.100-a123 (a@a) (a) a\\n\\x00'))\n    self.assertEqual(\"4.9.123\", dump_version(\n        b'Linux version 4.9.123 (@) () \\n\\x00'))\n\n  def test_dump_self(self):\n    self.assertEqual(\"4.9.1\", dump_version(\n        b\"trash\\x00Linux version 4.8.8\\x00trash\\x00\"\n        b\"other trash Linux version 4.9.1-g3 (2@s) (2) a\\n\\x00\"))\n"
  },
  {
    "path": "tools/test_post_process_props.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport contextlib\nimport io\nimport unittest\n\nfrom unittest.mock import *\nfrom post_process_props import *\n\nclass PropTestCase(unittest.TestCase):\n  def test_createFromLine(self):\n    p = Prop.from_line(\"# this is comment\")\n    self.assertTrue(p.is_comment())\n    self.assertEqual(\"\", p.name)\n    self.assertEqual(\"\", p.value)\n    self.assertFalse(p.is_optional())\n    self.assertEqual(\"# this is comment\", str(p))\n\n    for line in [\"a=b\", \"a = b\", \"a= b\", \"a =b\", \"  a=b   \"]:\n      p = Prop.from_line(line)\n      self.assertFalse(p.is_comment())\n      self.assertEqual(\"a\", p.name)\n      self.assertEqual(\"b\", p.value)\n      self.assertFalse(p.is_optional())\n      self.assertEqual(\"a=b\", str(p))\n\n    for line in [\"a?=b\", \"a ?= b\", \"a?= b\", \"a ?=b\", \"  a?=b   \"]:\n      p = Prop.from_line(line)\n      self.assertFalse(p.is_comment())\n      self.assertEqual(\"a\", p.name)\n      self.assertEqual(\"b\", p.value)\n      self.assertTrue(p.is_optional())\n      self.assertEqual(\"a?=b\", str(p))\n\n  def test_makeAsComment(self):\n    p = Prop.from_line(\"a=b\")\n    p.comments.append(\"# a comment\")\n    self.assertFalse(p.is_comment())\n\n    p.make_as_comment()\n    self.assertTrue(p.is_comment())\n    self.assertEqual(\"# a comment\\n#a=b\", str(p))\n\nclass PropListTestcase(unittest.TestCase):\n  def setUp(self):\n    content = \"\"\"\n    # comment\n    foo=true\n    bar=false\n    qux?=1\n    # another comment\n    foo?=false\n    \"\"\"\n    self.patcher = patch(\"post_process_props.open\", mock_open(read_data=content))\n    self.mock_open = self.patcher.start()\n    self.props = PropList(\"file\")\n\n  def tearDown(self):\n    self.patcher.stop()\n    self.props = None\n\n  def test_readFromFile(self):\n    self.assertEqual(4, len(self.props.get_all_props()))\n    expected = [\n        (\"foo\", \"true\", False),\n        (\"bar\", \"false\", False),\n        (\"qux\", \"1\", True),\n        (\"foo\", \"false\", True)\n    ]\n    for i,p in enumerate(self.props.get_all_props()):\n      self.assertEqual(expected[i][0], p.name)\n      self.assertEqual(expected[i][1], p.value)\n      self.assertEqual(expected[i][2], p.is_optional())\n      self.assertFalse(p.is_comment())\n\n    self.assertEqual(set([\"foo\", \"bar\", \"qux\"]), self.props.get_all_names())\n\n    self.assertEqual(\"true\", self.props.get_value(\"foo\"))\n    self.assertEqual(\"false\", self.props.get_value(\"bar\"))\n    self.assertEqual(\"1\", self.props.get_value(\"qux\"))\n\n    # there are two assignments for 'foo'\n    self.assertEqual(2, len(self.props.get_props(\"foo\")))\n\n  def test_putNewProp(self):\n    self.props.put(\"new\", \"30\")\n\n    self.assertEqual(5, len(self.props.get_all_props()))\n    last_prop = self.props.get_all_props()[-1]\n    self.assertEqual(\"new\", last_prop.name)\n    self.assertEqual(\"30\", last_prop.value)\n    self.assertFalse(last_prop.is_optional())\n\n  def test_putExistingNonOptionalProp(self):\n    self.props.put(\"foo\", \"NewValue\")\n\n    self.assertEqual(4, len(self.props.get_all_props()))\n    foo_prop = self.props.get_props(\"foo\")[0]\n    self.assertEqual(\"foo\", foo_prop.name)\n    self.assertEqual(\"NewValue\", foo_prop.value)\n    self.assertFalse(foo_prop.is_optional())\n    self.assertEqual(\"# Value overridden by post_process_props.py. \" +\n                     \"Original value: true\\nfoo=NewValue\", str(foo_prop))\n\n  def test_putExistingOptionalProp(self):\n    self.props.put(\"qux\", \"2\")\n\n    self.assertEqual(5, len(self.props.get_all_props()))\n    last_prop = self.props.get_all_props()[-1]\n    self.assertEqual(\"qux\", last_prop.name)\n    self.assertEqual(\"2\", last_prop.value)\n    self.assertFalse(last_prop.is_optional())\n    self.assertEqual(\"# Auto-added by post_process_props.py\\nqux=2\",\n                     str(last_prop))\n\n  def test_deleteNonOptionalProp(self):\n    props_to_delete = self.props.get_props(\"foo\")[0]\n    props_to_delete.delete(reason=\"testing\")\n\n    self.assertEqual(3, len(self.props.get_all_props()))\n    self.assertEqual(\"# Removed by post_process_props.py because testing\\n\" +\n                     \"#foo=true\", str(props_to_delete))\n\n  def test_deleteOptionalProp(self):\n    props_to_delete = self.props.get_props(\"qux\")[0]\n    props_to_delete.delete(reason=\"testing\")\n\n    self.assertEqual(3, len(self.props.get_all_props()))\n    self.assertEqual(\"# Removed by post_process_props.py because testing\\n\" +\n                     \"#qux?=1\", str(props_to_delete))\n\n  def test_overridingNonOptional(self):\n    props_to_be_overridden = self.props.get_props(\"foo\")[1]\n    self.assertTrue(\"true\", props_to_be_overridden.value)\n\n    self.assertTrue(override_optional_props(self.props))\n\n    # size reduced to 3 because foo?=false was overridden by foo=true\n    self.assertEqual(3, len(self.props.get_all_props()))\n\n    self.assertEqual(1, len(self.props.get_props(\"foo\")))\n    self.assertEqual(\"true\", self.props.get_props(\"foo\")[0].value)\n\n    self.assertEqual(\"# Removed by post_process_props.py because \" +\n                     \"overridden by foo=true\\n#foo?=false\",\n                     str(props_to_be_overridden))\n\n  def test_overridingOptional(self):\n    content = \"\"\"\n    # comment\n    qux?=2\n    foo=true\n    bar=false\n    qux?=1\n    # another comment\n    foo?=false\n    \"\"\"\n    with patch('post_process_props.open', mock_open(read_data=content)) as m:\n      props = PropList(\"hello\")\n\n      props_to_be_overridden = props.get_props(\"qux\")[0]\n      self.assertEqual(\"2\", props_to_be_overridden.value)\n\n      self.assertTrue(override_optional_props(props))\n\n      self.assertEqual(1, len(props.get_props(\"qux\")))\n      self.assertEqual(\"1\", props.get_props(\"qux\")[0].value)\n      # the only left optional assignment becomes non-optional\n      self.assertFalse(props.get_props(\"qux\")[0].is_optional())\n\n      self.assertEqual(\"# Removed by post_process_props.py because \" +\n                       \"overridden by qux?=1\\n#qux?=2\",\n                       str(props_to_be_overridden))\n\n  def test_overridingDuplicated(self):\n    content = \"\"\"\n    # comment\n    foo=true\n    bar=false\n    qux?=1\n    foo=false\n    # another comment\n    foo?=false\n    \"\"\"\n    with patch(\"post_process_props.open\", mock_open(read_data=content)) as m:\n      stderr_redirect = io.StringIO()\n      with contextlib.redirect_stderr(stderr_redirect):\n        props = PropList(\"hello\")\n\n        # fails due to duplicated foo=true and foo=false\n        self.assertFalse(override_optional_props(props))\n\n        self.assertEqual(\"error: found duplicate sysprop assignments:\\n\" +\n                         \"foo=true\\nfoo=false\\n\", stderr_redirect.getvalue())\n\n  def test_overridingDuplicatedWithSameValue(self):\n    content = \"\"\"\n    # comment\n    foo=true\n    bar=false\n    qux?=1\n    foo=true\n    # another comment\n    foo?=false\n    \"\"\"\n    with patch(\"post_process_props.open\", mock_open(read_data=content)) as m:\n      stderr_redirect = io.StringIO()\n      with contextlib.redirect_stderr(stderr_redirect):\n        props = PropList(\"hello\")\n        optional_prop = props.get_props(\"foo\")[2] # the last foo?=false one\n\n        # we have duplicated foo=true and foo=true, but that's allowed\n        # since they have the same value\n        self.assertTrue(override_optional_props(props))\n\n        # foo?=false should be commented out\n        self.assertEqual(\"# Removed by post_process_props.py because \" +\n                         \"overridden by foo=true\\n#foo?=false\",\n                         str(optional_prop))\n\n  def test_allowDuplicates(self):\n    content = \"\"\"\n    # comment\n    foo=true\n    bar=false\n    qux?=1\n    foo=false\n    # another comment\n    foo?=false\n    \"\"\"\n    with patch(\"post_process_props.open\", mock_open(read_data=content)) as m:\n      stderr_redirect = io.StringIO()\n      with contextlib.redirect_stderr(stderr_redirect):\n        props = PropList(\"hello\")\n\n        # we have duplicated foo=true and foo=false, but that's allowed\n        # because it's explicitly allowed\n        self.assertTrue(override_optional_props(props, allow_dup=True))\n\n  def test_validateGrfProps(self):\n    stderr_redirect = io.StringIO()\n    with contextlib.redirect_stderr(stderr_redirect):\n      props = PropList(\"hello\")\n      props.put(\"ro.board.first_api_level\",\"202504\")\n      props.put(\"ro.build.version.codename\", \"REL\")\n\n      # manually set ro.board.api_level to an invalid value\n      props.put(\"ro.board.api_level\",\"202404\")\n      self.assertFalse(validate_grf_props(props))\n\n      props.get_all_props()[-1].make_as_comment()\n      # manually set ro.board.api_level to a valid value\n      props.put(\"ro.board.api_level\",\"202504\")\n      self.assertTrue(validate_grf_props(props))\n\nif __name__ == '__main__':\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tools/tool_event_logger/Android.bp",
    "content": "// Copyright 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Set of error prone rules to ensure code quality\n// PackageLocation check requires the androidCompatible=false otherwise it does not do anything.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n    default_team: \"trendy_team_adte\",\n}\n\npython_library_host {\n    name: \"tool_event_proto\",\n    srcs: [\n        \"proto/tool_event.proto\",\n    ],\n    proto: {\n        canonical_path_from_root: false,\n    },\n}\n\npython_binary_host {\n    name: \"tool_event_logger\",\n    pkg_path: \"tool_event_logger\",\n    srcs: [\n        \"tool_event_logger.py\",\n    ],\n    libs: [\n        \"asuite_cc_client\",\n        \"tool_event_proto\",\n    ],\n    main: \"tool_event_logger.py\",\n}\n\npython_test_host {\n    name: \"tool_event_logger_test\",\n    main: \"tool_event_logger_test.py\",\n    pkg_path: \"tool_event_logger\",\n    srcs: [\n        \"tool_event_logger.py\",\n        \"tool_event_logger_test.py\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n    libs: [\n        \"asuite_cc_client\",\n        \"tool_event_proto\",\n    ],\n}\n"
  },
  {
    "path": "tools/tool_event_logger/OWNERS",
    "content": "include platform/tools/asuite:/OWNERS\n\nzhuoyao@google.com\n"
  },
  {
    "path": "tools/tool_event_logger/proto/tool_event.proto",
    "content": "syntax = \"proto3\";\n\npackage tools.asuite.tool_event_logger;\n\nmessage ToolEvent {\n  // Occurs immediately upon execution of the tool.\n  message InvocationStarted {\n    string command_args = 1;\n    string cwd = 2;\n    string os = 3;\n  }\n\n  // Occurs when tool exits for any reason.\n  message InvocationStopped {\n    int32 exit_code = 2;\n    string exit_log = 3;\n  }\n\n  // ------------------------\n  // FIELDS FOR ToolEvent\n  // ------------------------\n  // Random string generated to identify the invocation.\n  string invocation_id = 1;\n  // Internal user name.\n  string user_name = 2;\n  // The root of Android source.\n  string source_root = 3;\n  // Name of the tool used.\n  string tool_tag = 6;\n  // Name of the host workstation.\n  string host_name = 7;\n\n  oneof event {\n    InvocationStarted invocation_started = 4;\n    InvocationStopped invocation_stopped = 5;\n  }\n}\n"
  },
  {
    "path": "tools/tool_event_logger/tool_event_logger.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport argparse\nimport datetime\nimport getpass\nimport logging\nimport os\nimport platform\nimport sys\nimport tempfile\nimport uuid\n\nfrom atest.metrics import clearcut_client\nfrom atest.proto import clientanalytics_pb2\nfrom proto import tool_event_pb2\n\nLOG_SOURCE = 2395\n\n\nclass ToolEventLogger:\n  \"\"\"Logs tool events to Sawmill through Clearcut.\"\"\"\n\n  def __init__(\n      self,\n      tool_tag: str,\n      invocation_id: str,\n      user_name: str,\n      host_name: str,\n      source_root: str,\n      platform_version: str,\n      python_version: str,\n      client: clearcut_client.Clearcut,\n  ):\n    self.tool_tag = tool_tag\n    self.invocation_id = invocation_id\n    self.user_name = user_name\n    self.host_name = host_name\n    self.source_root = source_root\n    self.platform_version = platform_version\n    self.python_version = python_version\n    self._clearcut_client = client\n\n  @classmethod\n  def create(cls, tool_tag: str):\n    return ToolEventLogger(\n        tool_tag=tool_tag,\n        invocation_id=str(uuid.uuid4()),\n        user_name=getpass.getuser(),\n        host_name=platform.node(),\n        source_root=os.environ.get('ANDROID_BUILD_TOP', ''),\n        platform_version=platform.platform(),\n        python_version=platform.python_version(),\n        client=clearcut_client.Clearcut(LOG_SOURCE),\n    )\n\n  def __enter__(self):\n    return self\n\n  def __exit__(self, exc_type, exc_val, exc_tb):\n    self.flush()\n\n  def log_invocation_started(self, event_time: datetime, command_args: str):\n    \"\"\"Creates an event log with invocation started info.\"\"\"\n    event = self._create_tool_event()\n    event.invocation_started.CopyFrom(\n        tool_event_pb2.ToolEvent.InvocationStarted(\n            command_args=command_args,\n            os=f'{self.platform_version}:{self.python_version}',\n        )\n    )\n\n    logging.debug('Log invocation_started: %s', event)\n    self._log_clearcut_event(event, event_time)\n\n  def log_invocation_stopped(\n      self,\n      event_time: datetime,\n      exit_code: int,\n      exit_log: str,\n  ):\n    \"\"\"Creates an event log with invocation stopped info.\"\"\"\n    event = self._create_tool_event()\n    event.invocation_stopped.CopyFrom(\n        tool_event_pb2.ToolEvent.InvocationStopped(\n            exit_code=exit_code,\n            exit_log=exit_log,\n        )\n    )\n\n    logging.debug('Log invocation_stopped: %s', event)\n    self._log_clearcut_event(event, event_time)\n\n  def flush(self):\n    \"\"\"Sends all batched events to Clearcut.\"\"\"\n    logging.debug('Sending events to Clearcut.')\n    self._clearcut_client.flush_events()\n\n  def _create_tool_event(self):\n    return tool_event_pb2.ToolEvent(\n        tool_tag=self.tool_tag,\n        invocation_id=self.invocation_id,\n        user_name=self.user_name,\n        host_name=self.host_name,\n        source_root=self.source_root,\n    )\n\n  def _log_clearcut_event(\n      self, tool_event: tool_event_pb2.ToolEvent, event_time: datetime\n  ):\n    log_event = clientanalytics_pb2.LogEvent(\n        event_time_ms=int(event_time.timestamp() * 1000),\n        source_extension=tool_event.SerializeToString(),\n    )\n    self._clearcut_client.log(log_event)\n\n\nclass ArgumentParserWithLogging(argparse.ArgumentParser):\n\n  def error(self, message):\n    logging.error('Failed to parse args with error: %s', message)\n    super().error(message)\n\n\ndef create_arg_parser():\n  \"\"\"Creates an instance of the default ToolEventLogger arg parser.\"\"\"\n\n  parser = ArgumentParserWithLogging(\n      description='Build and upload logs for Android dev tools',\n      add_help=True,\n      formatter_class=argparse.RawDescriptionHelpFormatter,\n  )\n\n  parser.add_argument(\n      '--tool_tag',\n      type=str,\n      required=True,\n      help='Name of the tool.',\n  )\n\n  parser.add_argument(\n      '--start_timestamp',\n      type=lambda ts: datetime.datetime.fromtimestamp(float(ts)),\n      required=True,\n      help=(\n          'Timestamp when the tool starts. The timestamp should have the format'\n          '%s.%N which represents the seconds elapses since epoch.'\n      ),\n  )\n\n  parser.add_argument(\n      '--end_timestamp',\n      type=lambda ts: datetime.datetime.fromtimestamp(float(ts)),\n      required=True,\n      help=(\n          'Timestamp when the tool exits. The timestamp should have the format'\n          '%s.%N which represents the seconds elapses since epoch.'\n      ),\n  )\n\n  parser.add_argument(\n      '--tool_args',\n      type=str,\n      help='Parameters that are passed to the tool.',\n  )\n\n  parser.add_argument(\n      '--exit_code',\n      type=int,\n      required=True,\n      help='Tool exit code.',\n  )\n\n  parser.add_argument(\n      '--exit_log',\n      type=str,\n      help='Logs when tool exits.',\n  )\n\n  parser.add_argument(\n      '--dry_run',\n      action='store_true',\n      help='Dry run the tool event logger if set.',\n  )\n\n  return parser\n\n\ndef configure_logging():\n  root_logging_dir = tempfile.mkdtemp(prefix='tool_event_logger_')\n\n  log_fmt = '%(asctime)s %(filename)s:%(lineno)s:%(levelname)s: %(message)s'\n  date_fmt = '%Y-%m-%d %H:%M:%S'\n  _, log_path = tempfile.mkstemp(dir=root_logging_dir, suffix='.log')\n\n  logging.basicConfig(\n      filename=log_path, level=logging.DEBUG, format=log_fmt, datefmt=date_fmt\n  )\n\n\ndef main(argv: list[str]):\n  args = create_arg_parser().parse_args(argv[1:])\n\n  if args.dry_run:\n    logging.debug('This is a dry run.')\n    return\n\n  try:\n    with ToolEventLogger.create(args.tool_tag) as logger:\n      logger.log_invocation_started(args.start_timestamp, args.tool_args)\n      logger.log_invocation_stopped(\n          args.end_timestamp, args.exit_code, args.exit_log\n      )\n  except Exception as e:\n    logging.error('Log failed with unexpected error: %s', e)\n    raise\n\n\nif __name__ == '__main__':\n  configure_logging()\n  main(sys.argv)\n"
  },
  {
    "path": "tools/tool_event_logger/tool_event_logger_test.py",
    "content": "# Copyright 2024, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Unittests for ToolEventLogger.\"\"\"\n\nimport datetime\nimport logging\nimport unittest\nfrom unittest import mock\n\nfrom atest.metrics import clearcut_client\nfrom proto import tool_event_pb2\nfrom tool_event_logger import tool_event_logger\n\nTEST_INVOCATION_ID = 'test_invocation_id'\nTEST_USER_NAME = 'test_user'\nTEST_HOST_NAME = 'test_host_name'\nTEST_TOOL_TAG = 'test_tool'\nTEST_SOURCE_ROOT = 'test_source_root'\nTEST_PLATFORM_VERSION = 'test_platform_version'\nTEST_PYTHON_VERSION = 'test_python_version'\nTEST_EVENT_TIMESTAMP = datetime.datetime.now()\n\n\nclass ToolEventLoggerTest(unittest.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    self.clearcut_client = FakeClearcutClient()\n    self.logger = tool_event_logger.ToolEventLogger(\n        TEST_TOOL_TAG,\n        TEST_INVOCATION_ID,\n        TEST_USER_NAME,\n        TEST_HOST_NAME,\n        TEST_SOURCE_ROOT,\n        TEST_PLATFORM_VERSION,\n        TEST_PYTHON_VERSION,\n        client=self.clearcut_client,\n    )\n\n  def test_log_event_timestamp(self):\n    with self.logger:\n      self.logger.log_invocation_started(\n          datetime.datetime.fromtimestamp(100.101), 'test_command'\n      )\n\n    self.assertEqual(\n        self.clearcut_client.get_last_sent_event().event_time_ms, 100101\n    )\n\n  def test_log_event_basic_information(self):\n    with self.logger:\n      self.logger.log_invocation_started(TEST_EVENT_TIMESTAMP, 'test_command')\n\n    sent_event = self.clearcut_client.get_last_sent_event()\n    log_event = tool_event_pb2.ToolEvent.FromString(sent_event.source_extension)\n    self.assertEqual(log_event.invocation_id, TEST_INVOCATION_ID)\n    self.assertEqual(log_event.user_name, TEST_USER_NAME)\n    self.assertEqual(log_event.host_name, TEST_HOST_NAME)\n    self.assertEqual(log_event.tool_tag, TEST_TOOL_TAG)\n    self.assertEqual(log_event.source_root, TEST_SOURCE_ROOT)\n\n  def test_log_invocation_started(self):\n    expected_invocation_started = tool_event_pb2.ToolEvent.InvocationStarted(\n        command_args='test_command',\n        os=TEST_PLATFORM_VERSION + ':' + TEST_PYTHON_VERSION,\n    )\n\n    with self.logger:\n      self.logger.log_invocation_started(TEST_EVENT_TIMESTAMP, 'test_command')\n\n    self.assertEqual(self.clearcut_client.get_number_of_sent_events(), 1)\n    sent_event = self.clearcut_client.get_last_sent_event()\n    self.assertEqual(\n        expected_invocation_started,\n        tool_event_pb2.ToolEvent.FromString(\n            sent_event.source_extension\n        ).invocation_started,\n    )\n\n  def test_log_invocation_stopped(self):\n    expected_invocation_stopped = tool_event_pb2.ToolEvent.InvocationStopped(\n        exit_code=0,\n        exit_log='exit_log',\n    )\n\n    with self.logger:\n      self.logger.log_invocation_stopped(TEST_EVENT_TIMESTAMP, 0, 'exit_log')\n\n    self.assertEqual(self.clearcut_client.get_number_of_sent_events(), 1)\n    sent_event = self.clearcut_client.get_last_sent_event()\n    self.assertEqual(\n        expected_invocation_stopped,\n        tool_event_pb2.ToolEvent.FromString(\n            sent_event.source_extension\n        ).invocation_stopped,\n    )\n\n  def test_log_multiple_events(self):\n    with self.logger:\n      self.logger.log_invocation_started(TEST_EVENT_TIMESTAMP, 'test_command')\n      self.logger.log_invocation_stopped(TEST_EVENT_TIMESTAMP, 0, 'exit_log')\n\n    self.assertEqual(self.clearcut_client.get_number_of_sent_events(), 2)\n\n\nclass MainTest(unittest.TestCase):\n\n  REQUIRED_ARGS = [\n      '',\n      '--tool_tag',\n      'test_tool',\n      '--start_timestamp',\n      '1',\n      '--end_timestamp',\n      '2',\n      '--exit_code',\n      '0',\n  ]\n\n  def test_log_and_exit_with_missing_required_args(self):\n    with self.assertLogs() as logs:\n      with self.assertRaises(SystemExit) as ex:\n        tool_event_logger.main(['', '--tool_tag', 'test_tool'])\n\n    with self.subTest('Verify exception code'):\n      self.assertEqual(ex.exception.code, 2)\n\n    with self.subTest('Verify log messages'):\n      self.assertIn(\n          'the following arguments are required',\n          '\\n'.join(logs.output),\n      )\n\n  def test_log_and_exit_with_invalid_args(self):\n    with self.assertLogs() as logs:\n      with self.assertRaises(SystemExit) as ex:\n        tool_event_logger.main(['', '--start_timestamp', 'test'])\n\n    with self.subTest('Verify exception code'):\n      self.assertEqual(ex.exception.code, 2)\n\n    with self.subTest('Verify log messages'):\n      self.assertIn(\n          '--start_timestamp: invalid',\n          '\\n'.join(logs.output),\n      )\n\n  def test_log_and_exit_with_dry_run(self):\n    with self.assertLogs(level=logging.DEBUG) as logs:\n      tool_event_logger.main(self.REQUIRED_ARGS + ['--dry_run'])\n\n    with self.subTest('Verify log messages'):\n      self.assertIn('dry run', '\\n'.join(logs.output))\n\n  @mock.patch.object(clearcut_client, 'Clearcut')\n  def test_log_and_exit_with_unexpected_exception(self, mock_cc):\n    mock_cc.return_value = FakeClearcutClient(raise_log_exception=True)\n\n    with self.assertLogs() as logs:\n      with self.assertRaises(Exception) as ex:\n        tool_event_logger.main(self.REQUIRED_ARGS)\n\n    with self.subTest('Verify log messages'):\n      self.assertIn('unexpected error', '\\n'.join(logs.output))\n\n  @mock.patch.object(clearcut_client, 'Clearcut')\n  def test_success(self, mock_cc):\n    mock_clear_cut_client = FakeClearcutClient()\n    mock_cc.return_value = mock_clear_cut_client\n\n    tool_event_logger.main(self.REQUIRED_ARGS)\n\n    self.assertEqual(mock_clear_cut_client.get_number_of_sent_events(), 2)\n\n\nclass FakeClearcutClient:\n\n  def __init__(self, raise_log_exception=False):\n    self.pending_log_events = []\n    self.sent_log_events = []\n    self.raise_log_exception = raise_log_exception\n\n  def log(self, log_event):\n    if self.raise_log_exception:\n      raise Exception('unknown exception')\n    self.pending_log_events.append(log_event)\n\n  def flush_events(self):\n    self.sent_log_events.extend(self.pending_log_events)\n    self.pending_log_events.clear()\n\n  def get_number_of_sent_events(self):\n    return len(self.sent_log_events)\n\n  def get_last_sent_event(self):\n    return self.sent_log_events[-1]\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "tools/warn/.pylintrc",
    "content": "[FORMAT]\n\n# Two spaces for each indentation level.\nindent-string='  '\n"
  },
  {
    "path": "tools/warn/OWNERS",
    "content": "per-file * =srhines@google.com\n"
  },
  {
    "path": "tools/warn/__init__.py",
    "content": ""
  },
  {
    "path": "tools/warn/android_project_list.py",
    "content": "# python3\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Define a project list to sort warnings by project directory path.\"\"\"\n\n\ndef create_pattern(name, pattern=None):\n  \"\"\"Return a tuple of name and warn patten.\"\"\"\n  if pattern is not None:\n    return [name, '(^|.*/)' + pattern + '/.*: warning:']\n  return [name, '(^|.*/)' + name + '/.*: warning:']\n\n\n# A list of [project_name, file_path_pattern].\n# project_name should not contain comma, to be used in CSV output.\nproject_list = [\n    create_pattern('art'),\n    create_pattern('bionic'),\n    create_pattern('bootable'),\n    create_pattern('build'),\n    create_pattern('cts'),\n    create_pattern('dalvik'),\n    create_pattern('developers'),\n    create_pattern('development'),\n    create_pattern('device'),\n    create_pattern('doc'),\n    # match external/google* before external/\n    create_pattern('external/google', 'external/google.*'),\n    create_pattern('external/non-google', 'external'),\n    create_pattern('frameworks/av/camera'),\n    create_pattern('frameworks/av/cmds'),\n    create_pattern('frameworks/av/drm'),\n    create_pattern('frameworks/av/include'),\n    create_pattern('frameworks/av/media/img_utils'),\n    create_pattern('frameworks/av/media/libcpustats'),\n    create_pattern('frameworks/av/media/libeffects'),\n    create_pattern('frameworks/av/media/libmediaplayerservice'),\n    create_pattern('frameworks/av/media/libmedia'),\n    create_pattern('frameworks/av/media/libstagefright'),\n    create_pattern('frameworks/av/media/mtp'),\n    create_pattern('frameworks/av/media/ndk'),\n    create_pattern('frameworks/av/media/utils'),\n    create_pattern('frameworks/av/media/Other', 'frameworks/av/media'),\n    create_pattern('frameworks/av/radio'),\n    create_pattern('frameworks/av/services'),\n    create_pattern('frameworks/av/soundtrigger'),\n    create_pattern('frameworks/av/Other', 'frameworks/av'),\n    create_pattern('frameworks/base/cmds'),\n    create_pattern('frameworks/base/core'),\n    create_pattern('frameworks/base/drm'),\n    create_pattern('frameworks/base/media'),\n    create_pattern('frameworks/base/libs'),\n    create_pattern('frameworks/base/native'),\n    create_pattern('frameworks/base/packages'),\n    create_pattern('frameworks/base/rs'),\n    create_pattern('frameworks/base/services'),\n    create_pattern('frameworks/base/tests'),\n    create_pattern('frameworks/base/tools'),\n    create_pattern('frameworks/base/Other', 'frameworks/base'),\n    create_pattern('frameworks/compile/libbcc'),\n    create_pattern('frameworks/compile/mclinker'),\n    create_pattern('frameworks/compile/slang'),\n    create_pattern('frameworks/compile/Other', 'frameworks/compile'),\n    create_pattern('frameworks/minikin'),\n    create_pattern('frameworks/ml'),\n    create_pattern('frameworks/native/cmds'),\n    create_pattern('frameworks/native/include'),\n    create_pattern('frameworks/native/libs'),\n    create_pattern('frameworks/native/opengl'),\n    create_pattern('frameworks/native/services'),\n    create_pattern('frameworks/native/vulkan'),\n    create_pattern('frameworks/native/Other', 'frameworks/native'),\n    create_pattern('frameworks/opt'),\n    create_pattern('frameworks/rs'),\n    create_pattern('frameworks/webview'),\n    create_pattern('frameworks/wilhelm'),\n    create_pattern('frameworks/Other', 'frameworks'),\n    create_pattern('hardware/akm'),\n    create_pattern('hardware/broadcom'),\n    create_pattern('hardware/google'),\n    create_pattern('hardware/intel'),\n    create_pattern('hardware/interfaces'),\n    create_pattern('hardware/libhardware'),\n    create_pattern('hardware/libhardware_legacy'),\n    create_pattern('hardware/qcom'),\n    create_pattern('hardware/ril'),\n    create_pattern('hardware/Other', 'hardware'),\n    create_pattern('kernel'),\n    create_pattern('libcore'),\n    create_pattern('libnativehelper'),\n    create_pattern('ndk'),\n    # match vendor/unbungled_google/packages before other packages\n    create_pattern('unbundled_google'),\n    create_pattern('packages/providers/MediaProvider'),\n    create_pattern('packages'),\n    create_pattern('pdk'),\n    create_pattern('prebuilts'),\n    create_pattern('system/bt'),\n    create_pattern('system/connectivity'),\n    create_pattern('system/core/adb'),\n    create_pattern('system/libbase'),\n    create_pattern('system/core/debuggerd'),\n    create_pattern('system/core/fastboot'),\n    create_pattern('system/core/fingerprintd'),\n    create_pattern('system/core/fs_mgr'),\n    create_pattern('system/core/gatekeeperd'),\n    create_pattern('system/core/healthd'),\n    create_pattern('system/core/include'),\n    create_pattern('system/core/init'),\n    create_pattern('system/unwinding/libbacktrace'),\n    create_pattern('system/logging/liblog'),\n    create_pattern('system/core/libpixelflinger'),\n    create_pattern('system/core/libprocessgroup'),\n    create_pattern('system/core/libsysutils'),\n    create_pattern('system/core/logcat'),\n    create_pattern('system/core/logd'),\n    create_pattern('system/core/run-as'),\n    create_pattern('system/core/sdcard'),\n    create_pattern('system/core/toolbox'),\n    create_pattern('system/core/Other', 'system/core'),\n    create_pattern('system/extras/ANRdaemon'),\n    create_pattern('system/extras/cpustats'),\n    create_pattern('system/extras/crypto-perf'),\n    create_pattern('system/extras/ext4_utils'),\n    create_pattern('system/extras/f2fs_utils'),\n    create_pattern('system/extras/iotop'),\n    create_pattern('system/extras/libfec'),\n    create_pattern('system/extras/memory_replay'),\n    create_pattern('system/extras/mmap-perf'),\n    create_pattern('system/extras/multinetwork'),\n    create_pattern('system/extras/perfprofd'),\n    create_pattern('system/extras/procrank'),\n    create_pattern('system/extras/runconuid'),\n    create_pattern('system/extras/showmap'),\n    create_pattern('system/extras/simpleperf'),\n    create_pattern('system/extras/su'),\n    create_pattern('system/extras/tests'),\n    create_pattern('system/extras/verity'),\n    create_pattern('system/extras/Other', 'system/extras'),\n    create_pattern('system/gatekeeper'),\n    create_pattern('system/keymaster'),\n    create_pattern('system/libhidl'),\n    create_pattern('system/libhwbinder'),\n    create_pattern('system/media'),\n    create_pattern('system/netd'),\n    create_pattern('system/nvram'),\n    create_pattern('system/security'),\n    create_pattern('system/sepolicy'),\n    create_pattern('system/tools'),\n    create_pattern('system/update_engine'),\n    create_pattern('system/vold'),\n    create_pattern('system/Other', 'system'),\n    create_pattern('toolchain'),\n    create_pattern('test'),\n    create_pattern('tools'),\n    # match vendor/google* before vendor/\n    create_pattern('vendor/google', 'vendor/google.*'),\n    create_pattern('vendor/non-google', 'vendor'),\n    # keep out/obj and other patterns at the end.\n    [\n        'out/obj', '.*/(gen|obj[^/]*)/(include|EXECUTABLES|SHARED_LIBRARIES|'\n        'STATIC_LIBRARIES|NATIVE_TESTS)/.*: warning:'\n    ],\n    ['other', '.*']  # all other unrecognized patterns\n]\n"
  },
  {
    "path": "tools/warn/chrome_project_list.py",
    "content": "# python3\n\"\"\"Clang_Tidy_Warn Project List data for Chrome.\n\nThis file stores the Chrome project_list used in warn.py and\nits dependencies. It has been put into this file for easier navigation and\nunification of the Chrome and Android warn.py.\n\"\"\"\n\n\ndef create_pattern(pattern):\n  \"\"\"Return a tuple of name and warn patten.\"\"\"\n  return [pattern, '(^|.*/)' + pattern + '/.*: warning:']\n\n\n# A list of [project_name, file_path_pattern].\nproject_list = [\n    create_pattern('android_webview'),\n    create_pattern('apps'),\n    create_pattern('ash/app_list'),\n    create_pattern('ash/public'),\n    create_pattern('ash/assistant'),\n    create_pattern('ash/display'),\n    create_pattern('ash/resources'),\n    create_pattern('ash/login'),\n    create_pattern('ash/system'),\n    create_pattern('ash/wm'),\n    create_pattern('ash/shelf'),\n    create_pattern('ash'),\n    create_pattern('base/trace_event'),\n    create_pattern('base/debug'),\n    create_pattern('base/third_party'),\n    create_pattern('base/files'),\n    create_pattern('base/test'),\n    create_pattern('base/util'),\n    create_pattern('base/task'),\n    create_pattern('base/metrics'),\n    create_pattern('base/strings'),\n    create_pattern('base/memory'),\n    create_pattern('base'),\n    create_pattern('build'),\n    create_pattern('build_overrides'),\n    create_pattern('buildtools'),\n    create_pattern('cc'),\n    create_pattern('chrome/services'),\n    create_pattern('chrome/app'),\n    create_pattern('chrome/renderer'),\n    create_pattern('chrome/test'),\n    create_pattern('chrome/common/safe_browsing'),\n    create_pattern('chrome/common/importer'),\n    create_pattern('chrome/common/media_router'),\n    create_pattern('chrome/common/extensions'),\n    create_pattern('chrome/common'),\n    create_pattern('chrome/browser/sync_file_system'),\n    create_pattern('chrome/browser/safe_browsing'),\n    create_pattern('chrome/browser/download'),\n    create_pattern('chrome/browser/ui'),\n    create_pattern('chrome/browser/supervised_user'),\n    create_pattern('chrome/browser/search'),\n    create_pattern('chrome/browser/browsing_data'),\n    create_pattern('chrome/browser/predictors'),\n    create_pattern('chrome/browser/net'),\n    create_pattern('chrome/browser/devtools'),\n    create_pattern('chrome/browser/resource_coordinator'),\n    create_pattern('chrome/browser/page_load_metrics'),\n    create_pattern('chrome/browser/extensions'),\n    create_pattern('chrome/browser/ssl'),\n    create_pattern('chrome/browser/printing'),\n    create_pattern('chrome/browser/profiles'),\n    create_pattern('chrome/browser/chromeos'),\n    create_pattern('chrome/browser/performance_manager'),\n    create_pattern('chrome/browser/metrics'),\n    create_pattern('chrome/browser/component_updater'),\n    create_pattern('chrome/browser/media'),\n    create_pattern('chrome/browser/notifications'),\n    create_pattern('chrome/browser/web_applications'),\n    create_pattern('chrome/browser/media_galleries'),\n    create_pattern('chrome/browser'),\n    create_pattern('chrome'),\n    create_pattern('chromecast'),\n    create_pattern('chromeos/services'),\n    create_pattern('chromeos/dbus'),\n    create_pattern('chromeos/assistant'),\n    create_pattern('chromeos/components'),\n    create_pattern('chromeos/settings'),\n    create_pattern('chromeos/constants'),\n    create_pattern('chromeos/network'),\n    create_pattern('chromeos'),\n    create_pattern('cloud_print'),\n    create_pattern('components/crash'),\n    create_pattern('components/subresource_filter'),\n    create_pattern('components/invalidation'),\n    create_pattern('components/autofill'),\n    create_pattern('components/onc'),\n    create_pattern('components/arc'),\n    create_pattern('components/safe_browsing'),\n    create_pattern('components/services'),\n    create_pattern('components/cast_channel'),\n    create_pattern('components/download'),\n    create_pattern('components/feed'),\n    create_pattern('components/offline_pages'),\n    create_pattern('components/bookmarks'),\n    create_pattern('components/cloud_devices'),\n    create_pattern('components/mirroring'),\n    create_pattern('components/spellcheck'),\n    create_pattern('components/viz'),\n    create_pattern('components/gcm_driver'),\n    create_pattern('components/ntp_snippets'),\n    create_pattern('components/translate'),\n    create_pattern('components/search_engines'),\n    create_pattern('components/background_task_scheduler'),\n    create_pattern('components/signin'),\n    create_pattern('components/chromeos_camera'),\n    create_pattern('components/reading_list'),\n    create_pattern('components/assist_ranker'),\n    create_pattern('components/payments'),\n    create_pattern('components/feedback'),\n    create_pattern('components/ui_devtools'),\n    create_pattern('components/password_manager'),\n    create_pattern('components/omnibox'),\n    create_pattern('components/content_settings'),\n    create_pattern('components/dom_distiller'),\n    create_pattern('components/nacl'),\n    create_pattern('components/metrics'),\n    create_pattern('components/policy'),\n    create_pattern('components/optimization_guide'),\n    create_pattern('components/exo'),\n    create_pattern('components/update_client'),\n    create_pattern('components/data_reduction_proxy'),\n    create_pattern('components/sync'),\n    create_pattern('components/drive'),\n    create_pattern('components/variations'),\n    create_pattern('components/history'),\n    create_pattern('components/webcrypto'),\n    create_pattern('components'),\n    create_pattern('content/public'),\n    create_pattern('content/renderer'),\n    create_pattern('content/test'),\n    create_pattern('content/common'),\n    create_pattern('content/browser'),\n    create_pattern('content/zygote'),\n    create_pattern('content'),\n    create_pattern('courgette'),\n    create_pattern('crypto'),\n    create_pattern('dbus'),\n    create_pattern('device/base'),\n    create_pattern('device/vr'),\n    create_pattern('device/gamepad'),\n    create_pattern('device/test'),\n    create_pattern('device/fido'),\n    create_pattern('device/bluetooth'),\n    create_pattern('device'),\n    create_pattern('docs'),\n    create_pattern('extensions/docs'),\n    create_pattern('extensions/components'),\n    create_pattern('extensions/buildflags'),\n    create_pattern('extensions/renderer'),\n    create_pattern('extensions/test'),\n    create_pattern('extensions/common'),\n    create_pattern('extensions/shell'),\n    create_pattern('extensions/browser'),\n    create_pattern('extensions/strings'),\n    create_pattern('extensions'),\n    create_pattern('fuchsia'),\n    create_pattern('gin'),\n    create_pattern('google_apis'),\n    create_pattern('google_update'),\n    create_pattern('gpu/perftests'),\n    create_pattern('gpu/GLES2'),\n    create_pattern('gpu/command_buffer'),\n    create_pattern('gpu/tools'),\n    create_pattern('gpu/gles2_conform_support'),\n    create_pattern('gpu/ipc'),\n    create_pattern('gpu/khronos_glcts_support'),\n    create_pattern('gpu'),\n    create_pattern('headless'),\n    create_pattern('infra'),\n    create_pattern('ipc'),\n    create_pattern('jingle'),\n    create_pattern('media'),\n    create_pattern('mojo'),\n    create_pattern('native_client'),\n    create_pattern('ative_client_sdk'),\n    create_pattern('net'),\n    create_pattern('out'),\n    create_pattern('pdf'),\n    create_pattern('ppapi'),\n    create_pattern('printing'),\n    create_pattern('remoting'),\n    create_pattern('rlz'),\n    create_pattern('sandbox'),\n    create_pattern('services/audio'),\n    create_pattern('services/content'),\n    create_pattern('services/data_decoder'),\n    create_pattern('services/device'),\n    create_pattern('services/file'),\n    create_pattern('services/identity'),\n    create_pattern('services/image_annotation'),\n    create_pattern('services/media_session'),\n    create_pattern('services/metrics'),\n    create_pattern('services/network'),\n    create_pattern('services/preferences'),\n    create_pattern('services/proxy_resolver'),\n    create_pattern('services/resource_coordinator'),\n    create_pattern('services/service_manager'),\n    create_pattern('services/shape_detection'),\n    create_pattern('services/strings'),\n    create_pattern('services/test'),\n    create_pattern('services/tracing'),\n    create_pattern('services/video_capture'),\n    create_pattern('services/viz'),\n    create_pattern('services/ws'),\n    create_pattern('services'),\n    create_pattern('skia/config'),\n    create_pattern('skia/ext'),\n    create_pattern('skia/public'),\n    create_pattern('skia/tools'),\n    create_pattern('skia'),\n    create_pattern('sql'),\n    create_pattern('storage'),\n    create_pattern('styleguide'),\n    create_pattern('testing'),\n    create_pattern('third_party/Python-Markdown'),\n    create_pattern('third_party/SPIRV-Tools'),\n    create_pattern('third_party/abseil-cpp'),\n    create_pattern('third_party/accessibility-audit'),\n    create_pattern('third_party/accessibility_test_framework'),\n    create_pattern('third_party/adobe'),\n    create_pattern('third_party/afl'),\n    create_pattern('third_party/android_build_tools'),\n    create_pattern('third_party/android_crazy_linker'),\n    create_pattern('third_party/android_data_chart'),\n    create_pattern('third_party/android_deps'),\n    create_pattern('third_party/android_media'),\n    create_pattern('third_party/android_ndk'),\n    create_pattern('third_party/android_opengl'),\n    create_pattern('third_party/android_platform'),\n    create_pattern('third_party/android_protobuf'),\n    create_pattern('third_party/android_sdk'),\n    create_pattern('third_party/android_support_test_runner'),\n    create_pattern('third_party/android_swipe_refresh'),\n    create_pattern('third_party/android_system_sdk'),\n    create_pattern('third_party/android_tools'),\n    create_pattern('third_party/angle'),\n    create_pattern('third_party/apache-mac'),\n    create_pattern('third_party/apache-portable-runtime'),\n    create_pattern('third_party/apache-win32'),\n    create_pattern('third_party/apk-patch-size-estimator'),\n    create_pattern('third_party/apple_apsl'),\n    create_pattern('third_party/arcore-android-sdk'),\n    create_pattern('third_party/ashmem'),\n    create_pattern('third_party/auto'),\n    create_pattern('third_party/axe-core'),\n    create_pattern('third_party/bazel'),\n    create_pattern('third_party/binutils'),\n    create_pattern('third_party/bison'),\n    create_pattern('third_party/blanketjs'),\n    create_pattern('third_party/blink/common'),\n    create_pattern('third_party/blink/manual_tests'),\n    create_pattern('third_party/blink/perf_tests'),\n    create_pattern('third_party/blink/public/common'),\n    create_pattern('third_party/blink/public/default_100_percent'),\n    create_pattern('third_party/blink/public/default_200_percent'),\n    create_pattern('third_party/blink/public/platform'),\n    create_pattern('third_party/blink/public/mojom/ad_tagging'),\n    create_pattern('third_party/blink/public/mojom/app_banner'),\n    create_pattern('third_party/blink/public/mojom/appcache'),\n    create_pattern('third_party/blink/public/mojom/array_buffer'),\n    create_pattern('third_party/blink/public/mojom/associated_interfaces'),\n    create_pattern('third_party/blink/public/mojom/autoplay'),\n    create_pattern('third_party/blink/public/mojom/background_fetch'),\n    create_pattern('third_party/blink/public/mojom/background_sync'),\n    create_pattern('third_party/blink/public/mojom/badging'),\n    create_pattern('third_party/blink/public/mojom/blob'),\n    create_pattern('third_party/blink/public/mojom/bluetooth'),\n    create_pattern('third_party/blink/public/mojom/broadcastchannel'),\n    create_pattern('third_party/blink/public/mojom/cache_storage'),\n    create_pattern('third_party/blink/public/mojom/choosers'),\n    create_pattern('third_party/blink/public/mojom/clipboard'),\n    create_pattern('third_party/blink/public/mojom/commit_result'),\n    create_pattern('third_party/blink/public/mojom/contacts'),\n    create_pattern('third_party/blink/public/mojom/cookie_store'),\n    create_pattern('third_party/blink/public/mojom/crash'),\n    create_pattern('third_party/blink/public/mojom/credentialmanager'),\n    create_pattern('third_party/blink/public/mojom/csp'),\n    create_pattern('third_party/blink/public/mojom/devtools'),\n    create_pattern('third_party/blink/public/mojom/document_metadata'),\n    create_pattern('third_party/blink/public/mojom/dom_storage'),\n    create_pattern('third_party/blink/public/mojom/dwrite_font_proxy'),\n    create_pattern('third_party/blink/public/mojom/feature_policy'),\n    create_pattern('third_party/blink/public/mojom/fetch'),\n    create_pattern('third_party/blink/public/mojom/file'),\n    create_pattern('third_party/blink/public/mojom/filesystem'),\n    create_pattern('third_party/blink/public/mojom/font_unique_name_lookup'),\n    create_pattern('third_party/blink/public/mojom/frame'),\n    create_pattern('third_party/blink/public/mojom/frame_sinks'),\n    create_pattern('third_party/blink/public/mojom/geolocation'),\n    create_pattern('third_party/blink/public/mojom/hyphenation'),\n    create_pattern('third_party/blink/public/mojom/idle'),\n    create_pattern('third_party/blink/public/mojom/indexeddb'),\n    create_pattern('third_party/blink/public/mojom/input'),\n    create_pattern('third_party/blink/public/mojom/insecure_input'),\n    create_pattern('third_party/blink/public/mojom/installation'),\n    create_pattern('third_party/blink/public/mojom/installedapp'),\n    create_pattern('third_party/blink/public/mojom/keyboard_lock'),\n    create_pattern('third_party/blink/public/mojom/leak_detector'),\n    create_pattern('third_party/blink/public/mojom/loader'),\n    create_pattern('third_party/blink/public/mojom/locks'),\n    create_pattern('third_party/blink/public/mojom/manifest'),\n    create_pattern('third_party/blink/public/mojom/media_controls'),\n    create_pattern('third_party/blink/public/mojom/mediasession'),\n    create_pattern('third_party/blink/public/mojom/mediastream'),\n    create_pattern('third_party/blink/public/mojom/messaging'),\n    create_pattern('third_party/blink/public/mojom/mime'),\n    create_pattern('third_party/blink/public/mojom/native_file_system'),\n    create_pattern('third_party/blink/public/mojom/net'),\n    create_pattern('third_party/blink/public/mojom/notifications'),\n    create_pattern('third_party/blink/public/mojom/oom_intervention'),\n    create_pattern('third_party/blink/public/mojom/page'),\n    create_pattern('third_party/blink/public/mojom/payments'),\n    create_pattern('third_party/blink/public/mojom/permissions'),\n    create_pattern('third_party/blink/public/mojom/picture_in_picture'),\n    create_pattern('third_party/blink/public/mojom/plugins'),\n    create_pattern('third_party/blink/public/mojom/portal'),\n    create_pattern('third_party/blink/public/mojom/presentation'),\n    create_pattern('third_party/blink/public/mojom/push_messaging'),\n    create_pattern('third_party/blink/public/mojom/quota'),\n    create_pattern('third_party/blink/public/mojom/remote_objects'),\n    create_pattern('third_party/blink/public/mojom/reporting'),\n    create_pattern('third_party/blink/public/mojom/script'),\n    create_pattern('third_party/blink/public/mojom/selection_menu'),\n    create_pattern('third_party/blink/public/mojom/serial'),\n    create_pattern('third_party/blink/public/mojom/service_worker'),\n    create_pattern('third_party/blink/public/mojom/site_engagement'),\n    create_pattern('third_party/blink/public/mojom/sms'),\n    create_pattern('third_party/blink/public/mojom/speech'),\n    create_pattern('third_party/blink/public/mojom/ukm'),\n    create_pattern('third_party/blink/public/mojom/unhandled_tap_notifier'),\n    create_pattern('third_party/blink/public/mojom/usb'),\n    create_pattern('third_party/blink/public/mojom/use_counter'),\n    create_pattern('third_party/blink/public/mojom/user_agent'),\n    create_pattern('third_party/blink/public/mojom/wake_lock'),\n    create_pattern('third_party/blink/public/mojom/web_client_hints'),\n    create_pattern('third_party/blink/public/mojom/web_feature'),\n    create_pattern('third_party/blink/public/mojom/webaudio'),\n    create_pattern('third_party/blink/public/mojom/webauthn'),\n    create_pattern('third_party/blink/public/mojom/webdatabase'),\n    create_pattern('third_party/blink/public/mojom/webshare'),\n    create_pattern('third_party/blink/public/mojom/window_features'),\n    create_pattern('third_party/blink/public/mojom/worker'),\n    create_pattern('third_party/blink/public/web'),\n    create_pattern('third_party/blink/renderer/bindings'),\n    create_pattern('third_party/blink/renderer/build'),\n    create_pattern('third_party/blink/renderer/controller'),\n    create_pattern('third_party/blink/renderer/core/accessibility'),\n    create_pattern('third_party/blink/renderer/core/animation'),\n    create_pattern('third_party/blink/renderer/core/aom'),\n    create_pattern('third_party/blink/renderer/core/clipboard'),\n    create_pattern('third_party/blink/renderer/core/content_capture'),\n    create_pattern('third_party/blink/renderer/core/context_features'),\n    create_pattern('third_party/blink/renderer/core/css'),\n    create_pattern('third_party/blink/renderer/core/display_lock'),\n    create_pattern('third_party/blink/renderer/core/dom'),\n    create_pattern('third_party/blink/renderer/core/editing'),\n    create_pattern('third_party/blink/renderer/core/events'),\n    create_pattern('third_party/blink/renderer/core/execution_context'),\n    create_pattern('third_party/blink/renderer/core/exported'),\n    create_pattern('third_party/blink/renderer/core/feature_policy'),\n    create_pattern('third_party/blink/renderer/core/fetch'),\n    create_pattern('third_party/blink/renderer/core/fileapi'),\n    create_pattern('third_party/blink/renderer/core/frame'),\n    create_pattern('third_party/blink/renderer/core/fullscreen'),\n    create_pattern('third_party/blink/renderer/core/geometry'),\n    create_pattern('third_party/blink/renderer/core/html'),\n    create_pattern('third_party/blink/renderer/core/imagebitmap'),\n    create_pattern('third_party/blink/renderer/core/input'),\n    create_pattern('third_party/blink/renderer/core/inspector'),\n    create_pattern('third_party/blink/renderer/core/intersection_observer'),\n    create_pattern('third_party/blink/renderer/core/invisible_dom'),\n    create_pattern('third_party/blink/renderer/core/layout'),\n    create_pattern('third_party/blink/renderer/core/loader'),\n    create_pattern('third_party/blink/renderer/core/messaging'),\n    create_pattern('third_party/blink/renderer/core/mojo'),\n    create_pattern('third_party/blink/renderer/core/offscreencanvas'),\n    create_pattern('third_party/blink/renderer/core/origin_trials'),\n    create_pattern('third_party/blink/renderer/core/page'),\n    create_pattern('third_party/blink/renderer/core/paint'),\n    create_pattern('third_party/blink/renderer/core/probe'),\n    create_pattern('third_party/blink/renderer/core/resize_observer'),\n    create_pattern('third_party/blink/renderer/core/scheduler'),\n    create_pattern('third_party/blink/renderer/core/script'),\n    create_pattern('third_party/blink/renderer/core/scroll'),\n    create_pattern('third_party/blink/renderer/core/streams'),\n    create_pattern('third_party/blink/renderer/core/style'),\n    create_pattern('third_party/blink/renderer/core/svg'),\n    create_pattern('third_party/blink/renderer/core/testing'),\n    create_pattern('third_party/blink/renderer/core/timezone'),\n    create_pattern('third_party/blink/renderer/core/timing'),\n    create_pattern('third_party/blink/renderer/core/trustedtypes'),\n    create_pattern('third_party/blink/renderer/core/typed_arrays'),\n    create_pattern('third_party/blink/renderer/core/url'),\n    create_pattern('third_party/blink/renderer/core/win'),\n    create_pattern('third_party/blink/renderer/core/workers'),\n    create_pattern('third_party/blink/renderer/core/xml'),\n    create_pattern('third_party/blink/renderer/core/xmlhttprequest'),\n    create_pattern('third_party/blink/renderer/devtools'),\n    create_pattern('third_party/blink/renderer/modules'),\n    create_pattern('third_party/blink/renderer/platform'),\n    create_pattern('third_party/blink/tools'),\n    create_pattern('third_party/blink/web_tests'),\n    create_pattern('third_party/boringssl'),\n    create_pattern('third_party/bouncycastle'),\n    create_pattern('third_party/breakpad'),\n    create_pattern('third_party/brotli'),\n    create_pattern('third_party/bspatch'),\n    create_pattern('third_party/byte_buddy'),\n    create_pattern('third_party/cacheinvalidation'),\n    create_pattern('third_party/catapult'),\n    create_pattern('third_party/cct_dynamic_module'),\n    create_pattern('third_party/ced'),\n    create_pattern('third_party/chaijs'),\n    create_pattern('third_party/checkstyle'),\n    create_pattern('third_party/chromevox'),\n    create_pattern('third_party/chromite'),\n    create_pattern('third_party/cld_3'),\n    create_pattern('third_party/closure_compiler'),\n    create_pattern('third_party/colorama'),\n    create_pattern('third_party/crashpad'),\n    create_pattern('third_party/crc32c'),\n    create_pattern('third_party/cros_system_api'),\n    create_pattern('third_party/custom_tabs_client'),\n    create_pattern('third_party/d3'),\n    create_pattern('third_party/dav1d'),\n    create_pattern('third_party/dawn'),\n    create_pattern('third_party/decklink'),\n    create_pattern('third_party/depot_tools'),\n    create_pattern('third_party/devscripts'),\n    create_pattern('third_party/devtools-node-modules'),\n    create_pattern('third_party/dom_distiller_js'),\n    create_pattern('third_party/elfutils'),\n    create_pattern('third_party/emoji-segmenter'),\n    create_pattern('third_party/errorprone'),\n    create_pattern('third_party/espresso'),\n    create_pattern('third_party/expat'),\n    create_pattern('third_party/feed'),\n    create_pattern('third_party/ffmpeg'),\n    create_pattern('third_party/flac'),\n    create_pattern('third_party/flatbuffers'),\n    create_pattern('third_party/flot'),\n    create_pattern('third_party/fontconfig'),\n    create_pattern('third_party/freetype'),\n    create_pattern('third_party/fuchsia-sdk'),\n    create_pattern('third_party/gestures'),\n    create_pattern('third_party/gif_player'),\n    create_pattern('third_party/glfw'),\n    create_pattern('third_party/glslang'),\n    create_pattern('third_party/gnu_binutils'),\n    create_pattern('third_party/google-truth'),\n    create_pattern('third_party/google_android_play_core'),\n    create_pattern('third_party/google_appengine_cloudstorage'),\n    create_pattern('third_party/google_input_tools'),\n    create_pattern('third_party/google_toolbox_for_mac'),\n    create_pattern('third_party/google_trust_services'),\n    create_pattern('third_party/googletest'),\n    create_pattern('third_party/gperf'),\n    create_pattern('third_party/gradle_wrapper'),\n    create_pattern('third_party/grpc'),\n    create_pattern('third_party/gson'),\n    create_pattern('third_party/guava'),\n    create_pattern('third_party/gvr-android-keyboard'),\n    create_pattern('third_party/gvr-android-sdk'),\n    create_pattern('third_party/hamcrest'),\n    create_pattern('third_party/harfbuzz-ng'),\n    create_pattern('third_party/hunspell'),\n    create_pattern('third_party/hunspell_dictionaries'),\n    create_pattern('third_party/iaccessible2'),\n    create_pattern('third_party/iccjpeg'),\n    create_pattern('third_party/icu/android'),\n    create_pattern('third_party/icu/android_small'),\n    create_pattern('third_party/icu/cast'),\n    create_pattern('third_party/icu/chromeos'),\n    create_pattern('third_party/icu/common'),\n    create_pattern('third_party/icu/filters'),\n    create_pattern('third_party/icu/flutter'),\n    create_pattern('third_party/icu/fuzzers'),\n    create_pattern('third_party/icu/ios'),\n    create_pattern('third_party/icu/patches'),\n    create_pattern('third_party/icu/scripts'),\n    create_pattern('third_party/icu/source'),\n    create_pattern('third_party/icu/tzres'),\n    create_pattern('third_party/icu4j'),\n    create_pattern('third_party/ijar'),\n    create_pattern('third_party/ink'),\n    create_pattern('third_party/inspector_protocol'),\n    create_pattern('third_party/instrumented_libraries'),\n    create_pattern('third_party/intellij'),\n    create_pattern('third_party/isimpledom'),\n    create_pattern('third_party/jacoco'),\n    create_pattern('third_party/jinja2'),\n    create_pattern('third_party/jsoncpp'),\n    create_pattern('third_party/jsr-305'),\n    create_pattern('third_party/jstemplate'),\n    create_pattern('third_party/junit'),\n    create_pattern('third_party/khronos'),\n    create_pattern('third_party/lcov'),\n    create_pattern('third_party/leveldatabase'),\n    create_pattern('third_party/libFuzzer'),\n    create_pattern('third_party/libXNVCtrl'),\n    create_pattern('third_party/libaddressinput'),\n    create_pattern('third_party/libaom'),\n    create_pattern('third_party/libcxx-pretty-printers'),\n    create_pattern('third_party/libdrm'),\n    create_pattern('third_party/libevdev'),\n    create_pattern('third_party/libjingle_xmpp'),\n    create_pattern('third_party/libjpeg'),\n    create_pattern('third_party/libjpeg_turbo'),\n    create_pattern('third_party/liblouis'),\n    create_pattern('third_party/libovr'),\n    create_pattern('third_party/libphonenumber'),\n    create_pattern('third_party/libpng'),\n    create_pattern('third_party/libprotobuf-mutator'),\n    create_pattern('third_party/libsecret'),\n    create_pattern('third_party/libsrtp'),\n    create_pattern('third_party/libsync'),\n    create_pattern('third_party/libudev'),\n    create_pattern('third_party/libusb'),\n    create_pattern('third_party/libvpx'),\n    create_pattern('third_party/libwebm'),\n    create_pattern('third_party/libwebp'),\n    create_pattern('third_party/libxml'),\n    create_pattern('third_party/libxslt'),\n    create_pattern('third_party/libyuv'),\n    create_pattern('third_party/lighttpd'),\n    create_pattern('third_party/logilab'),\n    create_pattern('third_party/lss'),\n    create_pattern('third_party/lzma_sdk'),\n    create_pattern('third_party/mach_override'),\n    create_pattern('third_party/markdown'),\n    create_pattern('third_party/markupsafe'),\n    create_pattern('third_party/material_design_icons'),\n    create_pattern('third_party/mesa_headers'),\n    create_pattern('third_party/metrics_proto'),\n    create_pattern('third_party/microsoft_webauthn'),\n    create_pattern('third_party/mingw-w64'),\n    create_pattern('third_party/minigbm'),\n    create_pattern('third_party/minizip'),\n    create_pattern('third_party/mocha'),\n    create_pattern('third_party/mockito'),\n    create_pattern('third_party/modp_b64'),\n    create_pattern('third_party/motemplate'),\n    create_pattern('third_party/mozilla'),\n    create_pattern('third_party/nacl_sdk_binaries'),\n    create_pattern('third_party/nasm'),\n    create_pattern('third_party/netty-tcnative'),\n    create_pattern('third_party/netty4'),\n    create_pattern('third_party/node'),\n    create_pattern('third_party/nvml'),\n    create_pattern('third_party/objenesis'),\n    create_pattern('third_party/ocmock'),\n    create_pattern('third_party/openh264'),\n    create_pattern('third_party/openscreen'),\n    create_pattern('third_party/openvr'),\n    create_pattern('third_party/opus'),\n    create_pattern('third_party/ots'),\n    create_pattern('third_party/ow2_asm'),\n    create_pattern('third_party/pdfium'),\n    create_pattern('third_party/pefile'),\n    create_pattern('third_party/perfetto'),\n    create_pattern('third_party/perl'),\n    create_pattern('third_party/pexpect'),\n    create_pattern('third_party/pffft'),\n    create_pattern('third_party/ply'),\n    create_pattern('third_party/polymer'),\n    create_pattern('third_party/proguard'),\n    create_pattern('third_party/protobuf'),\n    create_pattern('third_party/protoc_javalite'),\n    create_pattern('third_party/pycoverage'),\n    create_pattern('third_party/pyelftools'),\n    create_pattern('third_party/pyjson5'),\n    create_pattern('third_party/pylint'),\n    create_pattern('third_party/pymock'),\n    create_pattern('third_party/pystache'),\n    create_pattern('third_party/pywebsocket'),\n    create_pattern('third_party/qcms'),\n    create_pattern('third_party/quic_trace'),\n    create_pattern('third_party/qunit'),\n    create_pattern('third_party/r8'),\n    create_pattern('third_party/re2'),\n    create_pattern('third_party/requests'),\n    create_pattern('third_party/rnnoise'),\n    create_pattern('third_party/robolectric'),\n    create_pattern('third_party/s2cellid'),\n    create_pattern('third_party/sfntly'),\n    create_pattern('third_party/shaderc'),\n    create_pattern('third_party/simplejson'),\n    create_pattern('third_party/sinonjs'),\n    create_pattern('third_party/skia'),\n    create_pattern('third_party/smhasher'),\n    create_pattern('third_party/snappy'),\n    create_pattern('third_party/speech-dispatcher'),\n    create_pattern('third_party/spirv-cross'),\n    create_pattern('third_party/spirv-headers'),\n    create_pattern('third_party/sqlite'),\n    create_pattern('third_party/sqlite4java'),\n    create_pattern('third_party/sudden_motion_sensor'),\n    create_pattern('third_party/swiftshader'),\n    create_pattern('third_party/tcmalloc'),\n    create_pattern('third_party/test_fonts'),\n    create_pattern('third_party/tlslite'),\n    create_pattern('third_party/ub-uiautomator'),\n    create_pattern('third_party/unrar'),\n    create_pattern('third_party/usb_ids'),\n    create_pattern('third_party/usrsctp'),\n    create_pattern('third_party/v4l-utils'),\n    create_pattern('third_party/vulkan'),\n    create_pattern('third_party/wayland'),\n    create_pattern('third_party/wayland-protocols'),\n    create_pattern('third_party/wds'),\n    create_pattern('third_party/web-animations-js'),\n    create_pattern('third_party/webdriver'),\n    create_pattern('third_party/webgl'),\n    create_pattern('third_party/webrtc'),\n    create_pattern('third_party/webrtc_overrides'),\n    create_pattern('third_party/webxr_test_pages'),\n    create_pattern('third_party/widevine'),\n    create_pattern('third_party/win_build_output'),\n    create_pattern('third_party/woff2'),\n    create_pattern('third_party/wtl'),\n    create_pattern('third_party/xdg-utils'),\n    create_pattern('third_party/xstream'),\n    create_pattern('third_party/yasm'),\n    create_pattern('third_party/zlib'),\n    create_pattern('tools'),\n    create_pattern('ui/accelerated_widget_mac'),\n    create_pattern('ui/accessibility'),\n    create_pattern('ui/android'),\n    create_pattern('ui/aura'),\n    create_pattern('ui/aura_extra'),\n    create_pattern('ui/base'),\n    create_pattern('ui/chromeos'),\n    create_pattern('ui/compositor'),\n    create_pattern('ui/compositor_extra'),\n    create_pattern('ui/content_accelerators'),\n    create_pattern('ui/display'),\n    create_pattern('ui/events'),\n    create_pattern('ui/file_manager'),\n    create_pattern('ui/gfx'),\n    create_pattern('ui/gl'),\n    create_pattern('ui/latency'),\n    create_pattern('ui/login'),\n    create_pattern('ui/message_center'),\n    create_pattern('ui/native_theme'),\n    create_pattern('ui/ozone'),\n    create_pattern('ui/platform_window'),\n    create_pattern('ui/resources'),\n    create_pattern('ui/shell_dialogs'),\n    create_pattern('ui/snapshot'),\n    create_pattern('ui/strings'),\n    create_pattern('ui/surface'),\n    create_pattern('ui/touch_selection'),\n    create_pattern('ui/views'),\n    create_pattern('ui/views_bridge_mac'),\n    create_pattern('ui/views_content_client'),\n    create_pattern('ui/web_dialogs'),\n    create_pattern('ui/webui'),\n    create_pattern('ui/wm'),\n    create_pattern('url'),\n    create_pattern('v8/benchmarks'),\n    create_pattern('v8/build_overrides'),\n    create_pattern('v8/custom_deps'),\n    create_pattern('v8/docs'),\n    create_pattern('v8/gni'),\n    create_pattern('v8/include'),\n    create_pattern('v8/infra'),\n    create_pattern('v8/samples'),\n    create_pattern('v8/src'),\n    create_pattern('v8/test'),\n    create_pattern('v8/testing'),\n    create_pattern('v8/third_party'),\n    create_pattern('v8/tools'),\n\n    # keep out/obj and other patterns at the end.\n    [\n        'out/obj', '.*/(gen|obj[^/]*)/(include|EXECUTABLES|SHARED_LIBRARIES|'\n        'STATIC_LIBRARIES|NATIVE_TESTS)/.*: warning:'\n    ],\n    ['other', '.*']  # all other unrecognized patterns\n]\n"
  },
  {
    "path": "tools/warn/cpp_warn_patterns.py",
    "content": "# python3\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Warning patterns for C/C++ compiler, but not clang-tidy.\"\"\"\n\n# No need of doc strings for trivial small functions.\n# pylint:disable=missing-function-docstring\n\nimport re\n\n# pylint:disable=relative-beyond-top-level\nfrom .severity import Severity\n\n\ndef cpp_warn(severity, description, pattern_list):\n  return {\n      'category': 'C/C++',\n      'severity': severity,\n      'description': description,\n      'patterns': pattern_list\n  }\n\n\ndef fixmenow(description, pattern_list):\n  return cpp_warn(Severity.FIXMENOW, description, pattern_list)\n\n\ndef high(description, pattern_list):\n  return cpp_warn(Severity.HIGH, description, pattern_list)\n\n\ndef medium(description, pattern_list):\n  return cpp_warn(Severity.MEDIUM, description, pattern_list)\n\n\ndef low(description, pattern_list):\n  return cpp_warn(Severity.LOW, description, pattern_list)\n\n\ndef skip(description, pattern_list):\n  return cpp_warn(Severity.SKIP, description, pattern_list)\n\n\ndef harmless(description, pattern_list):\n  return cpp_warn(Severity.HARMLESS, description, pattern_list)\n\n\nwarn_patterns = [\n    # pylint does not recognize g-inconsistent-quotes\n    # pylint:disable=line-too-long,bad-option-value,g-inconsistent-quotes\n    medium('Implicit function declaration',\n           [r\".*: warning: implicit declaration of function .+\",\n            r\".*: warning: implicitly declaring library function\"]),\n    skip('skip, conflicting types for ...',\n         [r\".*: warning: conflicting types for '.+'\"]),\n    high('Expression always evaluates to true or false',\n         [r\".*: warning: comparison is always .+ due to limited range of data type\",\n          r\".*: warning: comparison of unsigned .*expression .+ is always true\",\n          r\".*: warning: comparison of unsigned .*expression .+ is always false\"]),\n    high('Use transient memory for control value',\n         [r\".*: warning: .+Using such transient memory for the control value is .*dangerous.\"]),\n    high('Return address of stack memory',\n         [r\".*: warning: Address of stack memory .+ returned to caller\",\n          r\".*: warning: Address of stack memory .+ will be a dangling reference\"]),\n    high('Infinite recursion',\n         [r\".*: warning: all paths through this function will call itself\"]),\n    high('Potential buffer overflow',\n         [r\".*: warning: Size argument is greater than .+ the destination buffer\",\n          r\".*: warning: Potential buffer overflow.\",\n          r\".*: warning: String copy function overflows destination buffer\"]),\n    medium('Incompatible pointer types',\n           [r\".*: warning: assignment from incompatible pointer type\",\n            r\".*: warning: return from incompatible pointer type\",\n            r\".*: warning: passing argument [0-9]+ of '.*' from incompatible pointer type\",\n            r\".*: warning: initialization from incompatible pointer type\"]),\n    high('Incompatible declaration of built in function',\n         [r\".*: warning: incompatible implicit declaration of built-in function .+\"]),\n    high('Incompatible redeclaration of library function',\n         [r\".*: warning: incompatible redeclaration of library function .+\"]),\n    high('Null passed as non-null argument',\n         [r\".*: warning: Null passed to a callee that requires a non-null\"]),\n    medium('Unused command line argument',\n           [r\".*: warning: argument unused during compilation: .+\"]),\n    medium('Set but not used',\n           [r\".*: warning: .* set but not used.*Wunused-but-set\"]),\n    medium('Unused parameter',\n           [r\".*: warning: unused parameter '.*'\"]),\n    medium('Unused function, variable, label, comparison, etc.',\n           [r\".*: warning: '.+' defined but not used\",\n            r\".*: warning: unused function '.+'\",\n            r\".*: warning: unused label '.+'\",\n            r\".*: warning: relational comparison result unused\",\n            r\".*: warning: lambda capture .* is not used\",\n            r\".*: warning: private field '.+' is not used\",\n            r\".*: warning: unused variable '.+'\"]),\n    medium('Statement with no effect or result unused',\n           [r\".*: warning: statement with no effect\",\n            r\".*: warning: expression result unused\"]),\n    medium('Ignoreing return value of function',\n           [r\".*: warning: ignoring return value of function .+Wunused-result\"]),\n    medium('Missing initializer',\n           [r\".*: warning: missing initializer\"]),\n    medium('Need virtual destructor',\n           [r\".*: warning: delete called .* has virtual functions but non-virtual destructor\"]),\n    skip('skip, near initialization for ...',\n         [r\".*: warning: \\(near initialization for '.+'\\)\"]),\n    medium('Expansion of data or time macro',\n           [r\".*: warning: expansion of date or time macro is not reproducible\"]),\n    medium('Macro expansion has undefined behavior',\n           [r\".*: warning: macro expansion .* has undefined behavior\"]),\n    medium('Format string does not match arguments',\n           [r\".*: warning: format '.+' expects type '.+', but argument [0-9]+ has type '.+'\",\n            r\".*: warning: more '%' conversions than data arguments\",\n            r\".*: warning: data argument not used by format string\",\n            r\".*: warning: incomplete format specifier\",\n            r\".*: warning: unknown conversion type .* in format\",\n            r\".*: warning: format .+ expects .+ but argument .+Wformat=\",\n            r\".*: warning: field precision should have .+ but argument has .+Wformat\",\n            r\".*: warning: format specifies type .+ but the argument has .*type .+Wformat\"]),\n    medium('Too many arguments for format string',\n           [r\".*: warning: too many arguments for format\"]),\n    medium('Too many arguments in call',\n           [r\".*: warning: too many arguments in call to \"]),\n    medium('Invalid format specifier',\n           [r\".*: warning: invalid .+ specifier '.+'.+format-invalid-specifier\"]),\n    medium('Comparison between signed and unsigned',\n           [r\".*: warning: comparison between signed and unsigned\",\n            r\".*: warning: comparison of promoted \\~unsigned with unsigned\",\n            r\".*: warning: signed and unsigned type in conditional expression\"]),\n    medium('Comparison between enum and non-enum',\n           [r\".*: warning: enumeral and non-enumeral type in conditional expression\"]),\n    medium('libpng: zero area',\n           [r\".*libpng warning: Ignoring attempt to set cHRM RGB triangle with zero area\"]),\n    medium('Missing braces around initializer',\n           [r\".*: warning: missing braces around initializer.*\"]),\n    harmless('No newline at end of file',\n             [r\".*: warning: no newline at end of file\"]),\n    harmless('Missing space after macro name',\n             [r\".*: warning: missing whitespace after the macro name\"]),\n    low('Cast increases required alignment',\n        [r\".*: warning: cast from .* to .* increases required alignment .*\"]),\n    medium('Qualifier discarded',\n           [r\".*: warning: passing argument [0-9]+ of '.+' discards qualifiers from pointer target type\",\n            r\".*: warning: assignment discards qualifiers from pointer target type\",\n            r\".*: warning: passing .+ to parameter of type .+ discards qualifiers\",\n            r\".*: warning: assigning to .+ from .+ discards qualifiers\",\n            r\".*: warning: initializing .+ discards qualifiers .+types-discards-qualifiers\",\n            r\".*: warning: return discards qualifiers from pointer target type\"]),\n    medium('Unknown attribute',\n           [r\".*: warning: unknown attribute '.+'\"]),\n    medium('Attribute ignored',\n           [r\".*: warning: '_*packed_*' attribute ignored\",\n            r\".*: warning: .* not supported .*Wignored-attributes\",\n            r\".*: warning: attribute declaration must precede definition .+ignored-attributes\"]),\n    medium('Visibility problem',\n           [r\".*: warning: declaration of '.+' will not be visible outside of this function\"]),\n    medium('Visibility mismatch',\n           [r\".*: warning: '.+' declared with greater visibility than the type of its field '.+'\"]),\n    medium('Shift count greater than width of type',\n           [r\".*: warning: (left|right) shift count >= width of type\"]),\n    medium('Shift operator precedence',\n           [r\".*: warning: operator .* has lower precedence .+Wshift-op-parentheses.+\"]),\n    medium('extern &lt;foo&gt; is initialized',\n           [r\".*: warning: '.+' initialized and declared 'extern'\",\n            r\".*: warning: 'extern' variable has an initializer\"]),\n    medium('Old style declaration',\n           [r\".*: warning: 'static' is not at beginning of declaration\"]),\n    medium('Missing return value',\n           [r\".*: warning: control reaches end of non-void function\"]),\n    medium('Implicit int type',\n           [r\".*: warning: type specifier missing, defaults to 'int'\",\n            r\".*: warning: type defaults to 'int' in declaration of '.+'\"]),\n    medium('Main function should return int',\n           [r\".*: warning: return type of 'main' is not 'int'\"]),\n    medium('Variable may be used uninitialized',\n           [r\".*: warning: '.+' may be used uninitialized in this function\"]),\n    high('Variable is used uninitialized',\n         [r\".*: warning: '.+' is used uninitialized in this function\",\n          r\".*: warning: variable '.+' is uninitialized when used here\"]),\n    medium('ld: possible enum size mismatch',\n           [r\".*: warning: .* uses variable-size enums yet the output is to use 32-bit enums; use of enum values across objects may fail\"]),\n    medium('Pointer targets differ in signedness',\n           [r\".*: warning: pointer targets in initialization differ in signedness\",\n            r\".*: warning: pointer targets in assignment differ in signedness\",\n            r\".*: warning: pointer targets in return differ in signedness\",\n            r\".*: warning: pointer targets in passing argument [0-9]+ of '.+' differ in signedness\"]),\n    medium('Assuming overflow does not occur',\n           [r\".*: warning: assuming signed overflow does not occur when assuming that .* is always (true|false)\"]),\n    medium('Suggest adding braces around empty body',\n           [r\".*: warning: suggest braces around empty body in an 'if' statement\",\n            r\".*: warning: empty body in an if-statement\",\n            r\".*: warning: suggest braces around empty body in an 'else' statement\",\n            r\".*: warning: empty body in an else-statement\"]),\n    medium('Suggest adding parentheses',\n           [r\".*: warning: suggest explicit braces to avoid ambiguous 'else'\",\n            r\".*: warning: suggest parentheses around arithmetic in operand of '.+'\",\n            r\".*: warning: suggest parentheses around comparison in operand of '.+'\",\n            r\".*: warning: logical not is only applied to the left hand side of this comparison\",\n            r\".*: warning: using the result of an assignment as a condition without parentheses\",\n            r\".*: warning: .+ has lower precedence than .+ be evaluated first .+Wparentheses\",\n            r\".*: warning: suggest parentheses around '.+?' .+ '.+?'\",\n            r\".*: warning: suggest parentheses around assignment used as truth value\"]),\n    medium('Static variable used in non-static inline function',\n           [r\".*: warning: '.+' is static but used in inline function '.+' which is not static\"]),\n    medium('No type or storage class (will default to int)',\n           [r\".*: warning: data definition has no type or storage class\"]),\n    skip('skip, parameter name (without types) in function declaration',\n         [r\".*: warning: parameter names \\(without types\\) in function declaration\"]),\n    medium('Dereferencing &lt;foo&gt; breaks strict aliasing rules',\n           [r\".*: warning: dereferencing .* break strict-aliasing rules\"]),\n    medium('Cast from pointer to integer of different size',\n           [r\".*: warning: cast from pointer to integer of different size\",\n            r\".*: warning: initialization makes pointer from integer without a cast\"]),\n    medium('Cast to pointer from integer of different size',\n           [r\".*: warning: cast to pointer from integer of different size\"]),\n    medium('Macro redefined',\n           [r\".*: warning: '.+' macro redefined\"]),\n    skip('skip, ... location of the previous definition',\n         [r\".*: warning: this is the location of the previous definition\"]),\n    medium('ld: type and size of dynamic symbol are not defined',\n           [r\".*: warning: type and size of dynamic symbol `.+' are not defined\"]),\n    medium('Pointer from integer without cast',\n           [r\".*: warning: assignment makes pointer from integer without a cast\"]),\n    medium('Pointer from integer without cast',\n           [r\".*: warning: passing argument [0-9]+ of '.+' makes pointer from integer without a cast\"]),\n    medium('Integer from pointer without cast',\n           [r\".*: warning: assignment makes integer from pointer without a cast\"]),\n    medium('Integer from pointer without cast',\n           [r\".*: warning: passing argument [0-9]+ of '.+' makes integer from pointer without a cast\"]),\n    medium('Integer from pointer without cast',\n           [r\".*: warning: return makes integer from pointer without a cast\"]),\n    medium('Ignoring pragma',\n           [r\".*: warning: ignoring #pragma .+\"]),\n    medium('Pragma warning messages',\n           [r\".*: warning: .+W#pragma-messages\"]),\n    medium('Pragma once in main file',\n           [r\".*: warning: #pragma once in main file .+Wpragma-once-outside-header.*\"]),\n    medium('Variable might be clobbered by longjmp or vfork',\n           [r\".*: warning: variable '.+' might be clobbered by 'longjmp' or 'vfork'\"]),\n    medium('Argument might be clobbered by longjmp or vfork',\n           [r\".*: warning: argument '.+' might be clobbered by 'longjmp' or 'vfork'\"]),\n    medium('Redundant declaration',\n           [r\".*: warning: redundant redeclaration of '.+'\"]),\n    skip('skip, previous declaration ... was here',\n         [r\".*: warning: previous declaration of '.+' was here\"]),\n    high('Enum value not handled in switch',\n         [r\".*: warning: .*enumeration value.* not handled in switch.+Wswitch\"]),\n    medium('User defined warnings',\n           [r\".*: warning: .* \\[-Wuser-defined-warnings\\]$\"]),\n    medium('Taking address of temporary',\n           [r\".*: warning: taking address of temporary\"]),\n    medium('Taking address of packed member',\n           [r\".*: warning: taking address of packed member\"]),\n    medium('Pack alignment value is modified',\n           [r\".*: warning: .*#pragma pack alignment value is modified.*Wpragma-pack.*\"]),\n    medium('Possible broken line continuation',\n           [r\".*: warning: backslash and newline separated by space\"]),\n    medium('Undefined variable template',\n           [r\".*: warning: instantiation of variable .* no definition is available\"]),\n    medium('Inline function is not defined',\n           [r\".*: warning: inline function '.*' is not defined\"]),\n    medium('Excess elements in initializer',\n           [r\".*: warning: excess elements in .+ initializer\"]),\n    medium('Decimal constant is unsigned only in ISO C90',\n           [r\".*: warning: this decimal constant is unsigned only in ISO C90\"]),\n    medium('main is usually a function',\n           [r\".*: warning: 'main' is usually a function\"]),\n    medium('Typedef ignored',\n           [r\".*: warning: 'typedef' was ignored in this declaration\"]),\n    high('Address always evaluates to true',\n         [r\".*: warning: the address of '.+' will always evaluate as 'true'\"]),\n    fixmenow('Freeing a non-heap object',\n             [r\".*: warning: attempt to free a non-heap object '.+'\"]),\n    medium('Array subscript has type char',\n           [r\".*: warning: array subscript .+ type 'char'.+Wchar-subscripts\"]),\n    medium('Constant too large for type',\n           [r\".*: warning: integer constant is too large for '.+' type\"]),\n    medium('Constant too large for type, truncated',\n           [r\".*: warning: large integer implicitly truncated to unsigned type\"]),\n    medium('Overflow in expression',\n           [r\".*: warning: overflow in expression; .*Winteger-overflow\"]),\n    medium('Overflow in implicit constant conversion',\n           [r\".*: warning: overflow in implicit constant conversion\"]),\n    medium('Declaration does not declare anything',\n           [r\".*: warning: declaration 'class .+' does not declare anything\"]),\n    medium('Initialization order will be different',\n           [r\".*: warning: '.+' will be initialized after\",\n            r\".*: warning: initializer order does not match the declaration order\",\n            r\".*: warning: field .+ will be initialized after .+Wreorder\"]),\n    skip('skip,   ....',\n         [r\".*: warning:   '.+'\"]),\n    skip('skip,   base ...',\n         [r\".*: warning:   base '.+'\"]),\n    skip('skip,   when initialized here',\n         [r\".*: warning:   when initialized here\"]),\n    medium('Parameter type not specified',\n           [r\".*: warning: type of '.+' defaults to 'int'\"]),\n    medium('Missing declarations',\n           [r\".*: warning: declaration does not declare anything\"]),\n    medium('Missing noreturn',\n           [r\".*: warning: function '.*' could be declared with attribute 'noreturn'\"]),\n    medium('User warning',\n           [r\".*: warning: #warning \\\".+\\\"\"]),\n    medium('Vexing parsing problem',\n           [r\".*: warning: empty parentheses interpreted as a function declaration\"]),\n    medium('Dereferencing void*',\n           [r\".*: warning: dereferencing 'void \\*' pointer\"]),\n    medium('Comparison of pointer and integer',\n           [r\".*: warning: ordered comparison of pointer with integer zero\",\n            r\".*: warning: .*comparison between pointer and integer\"]),\n    medium('Use of error-prone unary operator',\n           [r\".*: warning: use of unary operator that may be intended as compound assignment\"]),\n    medium('Conversion of string constant to non-const char*',\n           [r\".*: warning: deprecated conversion from string constant to '.+'\"]),\n    medium('Function declaration isn''t a prototype',\n           [r\".*: warning: function declaration isn't a prototype\"]),\n    medium('Type qualifiers ignored on function return value',\n           [r\".*: warning: type qualifiers ignored on function return type\",\n            r\".*: warning: .+ type qualifier .+ has no effect .+Wignored-qualifiers\"]),\n    medium('&lt;foo&gt; declared inside parameter list, scope limited to this definition',\n           [r\".*: warning: '.+' declared inside parameter list\"]),\n    skip('skip, its scope is only this ...',\n         [r\".*: warning: its scope is only this definition or declaration, which is probably not what you want\"]),\n    low('Line continuation inside comment',\n        [r\".*: warning: multi-line comment\"]),\n    low('Comment inside comment',\n        [r\".*: warning: '.+' within block comment .*-Wcomment\"]),\n    low('Deprecated declarations',\n        [r\".*: warning: .+ is deprecated.+deprecated-declarations\"]),\n    low('Deprecated register',\n        [r\".*: warning: 'register' storage class specifier is deprecated\"]),\n    low('Converts between pointers to integer types with different sign',\n        [r\".*: warning: .+ converts between pointers to integer types .+Wpointer-sign\\]\"]),\n    harmless('Extra tokens after #endif',\n             [r\".*: warning: extra tokens at end of #endif directive\"]),\n    medium('Comparison between different enums',\n           [r\".*: warning: comparison between '.+' and '.+'.+Wenum-compare\",\n            r\".*: warning: comparison of .* enumeration types .*-Wenum-compare.*\"]),\n    medium('Conversion may change value',\n           [r\".*: warning: converting negative value '.+' to '.+'\",\n            r\".*: warning: conversion to '.+' .+ may (alter|change)\"]),\n    medium('Converting to non-pointer type from NULL',\n           [r\".*: warning: converting to non-pointer type '.+' from NULL\"]),\n    medium('Implicit sign conversion',\n           [r\".*: warning: implicit conversion changes signedness\"]),\n    medium('Converting NULL to non-pointer type',\n           [r\".*: warning: implicit conversion of NULL constant to '.+'\"]),\n    medium('Zero used as null pointer',\n           [r\".*: warning: expression .* zero treated as a null pointer constant\"]),\n    medium('Compare pointer to null character',\n           [r\".*: warning: comparing a pointer to a null character constant\"]),\n    medium('Implicit conversion changes value or loses precision',\n           [r\".*: warning: implicit conversion .* changes value from .* to .*-conversion\",\n            r\".*: warning: implicit conversion loses integer precision:\"]),\n    medium('Passing NULL as non-pointer argument',\n           [r\".*: warning: passing NULL to non-pointer argument [0-9]+ of '.+'\"]),\n    medium('Class seems unusable because of private ctor/dtor',\n           [r\".*: warning: all member functions in class '.+' are private\"]),\n    # skip this next one, because it only points out some RefBase-based classes\n    # where having a private destructor is perfectly fine\n    skip('Class seems unusable because of private ctor/dtor',\n         [r\".*: warning: 'class .+' only defines a private destructor and has no friends\"]),\n    medium('Class seems unusable because of private ctor/dtor',\n           [r\".*: warning: 'class .+' only defines private constructors and has no friends\"]),\n    medium('In-class initializer for static const float/double',\n           [r\".*: warning: in-class initializer for static data member of .+const (float|double)\"]),\n    medium('void* used in arithmetic',\n           [r\".*: warning: pointer of type 'void \\*' used in (arithmetic|subtraction)\",\n            r\".*: warning: arithmetic on .+ to void is a GNU extension.*Wpointer-arith\",\n            r\".*: warning: wrong type argument to increment\"]),\n    medium('Overload resolution chose to promote from unsigned or enum to signed type',\n           [r\".*: warning: passing '.+' chooses '.+' over '.+'.*Wsign-promo\"]),\n    skip('skip,   in call to ...',\n         [r\".*: warning:   in call to '.+'\"]),\n    high('Base should be explicitly initialized in copy constructor',\n         [r\".*: warning: base class '.+' should be explicitly initialized in the copy constructor\"]),\n    medium('Return value from void function',\n           [r\".*: warning: 'return' with a value, in function returning void\"]),\n    medium('Multi-character character constant',\n           [r\".*: warning: multi-character character constant\"]),\n    medium('Conversion from string literal to char*',\n           [r\".*: warning: .+ does not allow conversion from string literal to 'char \\*'\"]),\n    low('Extra \\';\\'',\n        [r\".*: warning: extra ';' .+extra-semi\"]),\n    low('Useless specifier',\n        [r\".*: warning: useless storage class specifier in empty declaration\"]),\n    low('Duplicate declaration specifier',\n        [r\".*: warning: duplicate '.+' declaration specifier\"]),\n    low('Comparison of self is always false',\n        [r\".*: self-comparison always evaluates to false\"]),\n    low('Logical op with constant operand',\n        [r\".*: use of logical '.+' with constant operand\"]),\n    low('Needs a space between literal and string macro',\n        [r\".*: warning: invalid suffix on literal.+ requires a space .+Wliteral-suffix\"]),\n    low('Warnings from #warning',\n        [r\".*: warning: .+-W#warnings\"]),\n    low('Using float/int absolute value function with int/float argument',\n        [r\".*: warning: using .+ absolute value function .+ when argument is .+ type .+Wabsolute-value\",\n         r\".*: warning: absolute value function '.+' given .+ which may cause truncation .+Wabsolute-value\"]),\n    low('Using C++11 extensions',\n        [r\".*: warning: 'auto' type specifier is a C\\+\\+11 extension\"]),\n    low('Using C++17 extensions',\n        [r\".*: warning: .* a C\\+\\+17 extension .+Wc\\+\\+17-extensions\"]),\n    low('Refers to implicitly defined namespace',\n        [r\".*: warning: using directive refers to implicitly-defined namespace .+\"]),\n    low('Invalid pp token',\n        [r\".*: warning: missing .+Winvalid-pp-token\"]),\n    low('need glibc to link',\n        [r\".*: warning: .* requires at runtime .* glibc .* for linking\"]),\n    low('Add braces to avoid dangling else',\n        [r\".*: warning: add explicit braces to avoid dangling else\"]),\n    low('Assigning value to self',\n        [r\".*: warning: explicitly assigning value of .+ to itself\"]),\n    low('Comparison of integers of different signs',\n        [r\".*: warning: comparison of integers of different signs.+sign-compare\"]),\n    low('Incompatible pointer types',\n        [r\".*: warning: incompatible .*pointer types .*-Wincompatible-.*pointer-types\"]),\n    low('Missing braces',\n        [r\".*: warning: suggest braces around initialization of\",\n         r\".*: warning: too many braces around scalar initializer .+Wmany-braces-around-scalar-init\",\n         r\".*: warning: braces around scalar initializer\"]),\n    low('Missing field initializers',\n        [r\".*: warning: missing field '.+' initializer\"]),\n    low('Typedef redefinition',\n        [r\".*: warning: redefinition of typedef '.+' is a C11 feature\"]),\n    low('GNU old-style field designator',\n        [r\".*: warning: use of GNU old-style field designator extension\"]),\n    low('Initializer overrides prior initialization',\n        [r\".*: warning: initializer overrides prior initialization of this subobject\"]),\n    low('GNU extension, variable sized type not at end',\n        [r\".*: warning: field '.+' with variable sized type '.+' not at the end of a struct or class\"]),\n    low('Comparison of constant is always false/true',\n        [r\".*: comparison of .+ is always .+Wtautological-constant-out-of-range-compare\"]),\n    low('Hides overloaded virtual function',\n        [r\".*: '.+' hides overloaded virtual function\"]),\n    medium('Operator new returns NULL',\n           [r\".*: warning: 'operator new' must not return NULL unless it is declared 'throw\\(\\)' .+\"]),\n    medium('NULL used in arithmetic',\n           [r\".*: warning: NULL used in arithmetic\",\n            r\".*: warning: .* subtraction with a null pointer\",\n            r\".*: warning: comparison between NULL and non-pointer\"]),\n    medium('Misspelled header guard',\n           [r\".*: warning: '.+' is used as a header guard .+ followed by .+ different macro\"]),\n    medium('Empty loop body',\n           [r\".*: warning: .+ loop has empty body\"]),\n    medium('Implicit conversion from enumeration type',\n           [r\".*: warning: implicit conversion from enumeration type '.+'\"]),\n    medium('case value not in enumerated type',\n           [r\".*: warning: case value not in enumerated type '.+'\"]),\n    medium('Use of deprecated method',\n           [r\".*: warning: '.+' is deprecated .+\"]),\n    medium('Use of garbage or uninitialized value',\n           [r\".*: warning: .+ uninitialized .+\\[-Wsometimes-uninitialized\\]\"]),\n    medium('Sizeof on array argument',\n           [r\".*: warning: sizeof on array function parameter will return\"]),\n    medium('Bad argument size of memory access functions',\n           [r\".*: warning: .+\\[-Wsizeof-pointer-memaccess\\]\"]),\n    medium('Return value not checked',\n           [r\".*: warning: The return value from .+ is not checked\"]),\n    medium('Possible heap pollution',\n           [r\".*: warning: .*Possible heap pollution from .+ type .+\"]),\n    medium('Variable used in loop condition not modified in loop body',\n           [r\".*: warning: variable '.+' used in loop condition.*Wfor-loop-analysis\"]),\n    medium('Closing a previously closed file',\n           [r\".*: warning: Closing a previously closed file\"]),\n    medium('Unnamed template type argument',\n           [r\".*: warning: template argument.+Wunnamed-type-template-args\"]),\n    medium('Unannotated fall-through between switch labels',\n           [r\".*: warning: unannotated fall-through between switch labels.+Wimplicit-fallthrough\"]),\n    medium('Invalid partial specialization',\n           [r\".*: warning: class template partial specialization.+Winvalid-partial-specialization\"]),\n    medium('Overlapping comparisons',\n           [r\".*: warning: overlapping comparisons.+Wtautological-overlap-compare\"]),\n    medium('bitwise comparison',\n           [r\".*: warning: bitwise comparison.+Wtautological-bitwise-compare\"]),\n    medium('int in bool context',\n           [r\".*: warning: converting.+to a boolean.+Wint-in-bool-context\"]),\n    medium('bitwise conditional parentheses',\n           [r\".*: warning: operator.+has lower precedence.+Wbitwise-conditional-parentheses\"]),\n    medium('sizeof array div',\n           [r\".*: warning: .+number of elements in.+array.+Wsizeof-array-div\"]),\n    medium('bool operation',\n           [r\".*: warning: .+boolean.+always.+Wbool-operation\"]),\n    medium('Undefined bool conversion',\n           [r\".*: warning: .+may be.+always.+true.+Wundefined-bool-conversion\"]),\n    medium('Typedef requires a name',\n           [r\".*: warning: typedef requires a name.+Wmissing-declaration\"]),\n    medium('Unknown escape sequence',\n           [r\".*: warning: unknown escape sequence.+Wunknown-escape-sequence\"]),\n    medium('Unicode whitespace',\n           [r\".*: warning: treating Unicode.+as whitespace.+Wunicode-whitespace\"]),\n    medium('Unused local typedef',\n           [r\".*: warning: unused typedef.+Wunused-local-typedef\"]),\n    medium('varargs warnings',\n           [r\".*: warning: .*argument to 'va_start'.+\\[-Wvarargs\\]\"]),\n    harmless('Discarded qualifier from pointer target type',\n             [r\".*: warning: .+ discards '.+' qualifier from pointer target type\"]),\n    harmless('Use snprintf instead of sprintf',\n             [r\".*: warning: .*sprintf is often misused; please use snprintf\"]),\n    harmless('Unsupported optimizaton flag',\n             [r\".*: warning: optimization flag '.+' is not supported\"]),\n    harmless('Extra or missing parentheses',\n             [r\".*: warning: equality comparison with extraneous parentheses\",\n              r\".*: warning: .+ within .+Wlogical-op-parentheses\"]),\n    harmless('Mismatched class vs struct tags',\n             [r\".*: warning: '.+' defined as a .+ here but previously declared as a .+mismatched-tags\",\n              r\".*: warning: .+ was previously declared as a .+mismatched-tags\"]),\n]\n\n\ndef compile_patterns(patterns):\n  \"\"\"Precompiling every pattern speeds up parsing by about 30x.\"\"\"\n  for i in patterns:\n    i['compiled_patterns'] = []\n    for pat in i['patterns']:\n      i['compiled_patterns'].append(re.compile(pat))\n\n\ncompile_patterns(warn_patterns)\n"
  },
  {
    "path": "tools/warn/html_writer.py",
    "content": "# Lint as: python3\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Emit warning messages to html or csv files.\"\"\"\n\n# Many functions in this module have too many arguments to be refactored.\n# pylint:disable=too-many-arguments,missing-function-docstring\n\n# To emit html page of warning messages:\n#   flags: --byproject, --url, --separator\n# Old stuff for static html components:\n#   html_script_style:  static html scripts and styles\n#   htmlbig:\n#   dump_stats, dump_html_prologue, dump_html_epilogue:\n#   emit_buttons:\n#   dump_fixed\n#   sort_warnings:\n#   emit_stats_by_project:\n#   all_patterns,\n#   findproject, classify_warning\n#   dump_html\n#\n# New dynamic HTML page's static JavaScript data:\n#   Some data are copied from Python to JavaScript, to generate HTML elements.\n#   FlagPlatform           flags.platform\n#   FlagURL                flags.url, used by 'android'\n#   FlagSeparator          flags.separator, used by 'android'\n#   SeverityColors:        list of colors for all severity levels\n#   SeverityHeaders:       list of headers for all severity levels\n#   SeverityColumnHeaders: list of column_headers for all severity levels\n#   ProjectNames:          project_names, or project_list[*][0]\n#   WarnPatternsSeverity:     warn_patterns[*]['severity']\n#   WarnPatternsDescription:  warn_patterns[*]['description']\n#   WarningMessages:          warning_messages\n#   Warnings:                 warning_records\n#   StatsHeader:           warning count table header row\n#   StatsRows:             array of warning count table rows\n#\n# New dynamic HTML page's dynamic JavaScript data:\n#\n# New dynamic HTML related function to emit data:\n#   escape_string, strip_escape_string, emit_warning_arrays\n#   emit_js_data():\n\nfrom __future__ import print_function\nimport csv\nimport datetime\nimport html\nimport sys\n\n# pylint:disable=relative-beyond-top-level\nfrom .severity import Severity\n\n\n# Report files with this number of warnings or more.\nLIMIT_WARNINGS_PER_FILE = 100\n# Report files/directories with this percentage of total warnings or more.\nLIMIT_PERCENT_WARNINGS = 1\n\nHTML_HEAD_SCRIPTS = \"\"\"\\\n  <script type=\"text/javascript\">\n  function expand(id) {\n    var e = document.getElementById(id);\n    var f = document.getElementById(id + \"_mark\");\n    if (e.style.display == 'block') {\n       e.style.display = 'none';\n       f.innerHTML = '&#x2295';\n    }\n    else {\n       e.style.display = 'block';\n       f.innerHTML = '&#x2296';\n    }\n  };\n  function expandCollapse(show) {\n    for (var id = 1; ; id++) {\n      var e = document.getElementById(id + \"\");\n      var f = document.getElementById(id + \"_mark\");\n      if (!e || !f) break;\n      e.style.display = (show ? 'block' : 'none');\n      f.innerHTML = (show ? '&#x2296' : '&#x2295');\n    }\n  };\n  </script>\n  <style type=\"text/css\">\n  th,td{border-collapse:collapse; border:1px solid black;}\n  .button{color:blue;font-size:100%;font-weight:bolder;}\n  .bt{color:black;background-color:transparent;border:none;outline:none;\n      font-size:140%;font-weight:bolder;}\n  .c0{background-color:#e0e0e0;}\n  .c1{background-color:#d0d0d0;}\n  .t1{border-collapse:collapse; width:100%; border:1px solid black;}\n  .box{margin:5pt; padding:5pt; border:1px solid;}\n  </style>\n  <script src=\"https://www.gstatic.com/charts/loader.js\"></script>\n\"\"\"\n\n\ndef make_writer(output_stream):\n\n  def writer(text):\n    return output_stream.write(text + '\\n')\n\n  return writer\n\n\ndef html_big(param):\n  return '<font size=\"+2\">' + param + '</font>'\n\n\ndef dump_html_prologue(title, writer, warn_patterns, project_names):\n  writer('<html>\\n<head>')\n  writer('<title>' + title + '</title>')\n  writer(HTML_HEAD_SCRIPTS)\n  emit_stats_by_project(writer, warn_patterns, project_names)\n  writer('</head>\\n<body>')\n  writer(html_big(title))\n  writer('<p>')\n\n\ndef dump_html_epilogue(writer):\n  writer('</body>\\n</head>\\n</html>')\n\n\ndef sort_warnings(warn_patterns):\n  for i in warn_patterns:\n    i['members'] = sorted(set(i['members']))\n\n\ndef create_warnings(warn_patterns, project_names):\n  \"\"\"Creates warnings s.t.\n\n  warnings[p][s] is as specified in above docs.\n\n  Args:\n    warn_patterns: list of warning patterns for specified platform\n    project_names: list of project names\n\n  Returns:\n    2D warnings array where warnings[p][s] is # of warnings in project name p of\n    severity level s\n  \"\"\"\n  warnings = {p: {s.value: 0 for s in Severity.levels} for p in project_names}\n  for pattern in warn_patterns:\n    value = pattern['severity'].value\n    for project in pattern['projects']:\n      warnings[project][value] += pattern['projects'][project]\n  return warnings\n\n\ndef get_total_by_project(warnings, project_names):\n  \"\"\"Returns dict, project as key and # warnings for that project as value.\"\"\"\n  return {\n      p: sum(warnings[p][s.value] for s in Severity.levels)\n      for p in project_names\n  }\n\n\ndef get_total_by_severity(warnings, project_names):\n  \"\"\"Returns dict, severity as key and # warnings of that severity as value.\"\"\"\n  return {\n      s.value: sum(warnings[p][s.value] for p in project_names)\n      for s in Severity.levels\n  }\n\n\ndef emit_table_header(total_by_severity):\n  \"\"\"Returns list of HTML-formatted content for severity stats.\"\"\"\n\n  stats_header = ['Project']\n  for severity in Severity.levels:\n    if total_by_severity[severity.value]:\n      stats_header.append(\n          '<span style=\\'background-color:{}\\'>{}</span>'.format(\n              severity.color, severity.column_header))\n  stats_header.append('TOTAL')\n  return stats_header\n\n\ndef emit_row_counts_per_project(warnings, total_by_project, total_by_severity,\n                                project_names):\n  \"\"\"Returns total project warnings and row of stats for each project.\n\n  Args:\n    warnings: output of create_warnings(warn_patterns, project_names)\n    total_by_project: output of get_total_by_project(project_names)\n    total_by_severity: output of get_total_by_severity(project_names)\n    project_names: list of project names\n\n  Returns:\n    total_all_projects, the total number of warnings over all projects\n    stats_rows, a 2d list where each row is [Project Name, <severity counts>,\n    total # warnings for this project]\n  \"\"\"\n\n  total_all_projects = 0\n  stats_rows = []\n  for p_name in project_names:\n    if total_by_project[p_name]:\n      one_row = [p_name]\n      for severity in Severity.levels:\n        if total_by_severity[severity.value]:\n          one_row.append(warnings[p_name][severity.value])\n      one_row.append(total_by_project[p_name])\n      stats_rows.append(one_row)\n      total_all_projects += total_by_project[p_name]\n  return total_all_projects, stats_rows\n\n\ndef emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows,\n                                 total_all_projects, writer):\n  \"\"\"Emits stats_header and stats_rows as specified above.\n\n  Args:\n    total_by_severity: output of get_total_by_severity()\n    stats_header: output of emit_table_header()\n    stats_rows: output of emit_row_counts_per_project()\n    total_all_projects: output of emit_row_counts_per_project()\n    writer: writer returned by make_writer(output_stream)\n  \"\"\"\n\n  total_all_severities = 0\n  one_row = ['<b>TOTAL</b>']\n  for severity in Severity.levels:\n    if total_by_severity[severity.value]:\n      one_row.append(total_by_severity[severity.value])\n      total_all_severities += total_by_severity[severity.value]\n  one_row.append(total_all_projects)\n  stats_rows.append(one_row)\n  writer('<script>')\n  emit_const_string_array('StatsHeader', stats_header, writer)\n  emit_const_object_array('StatsRows', stats_rows, writer)\n  writer(DRAW_TABLE_JAVASCRIPT)\n  writer('</script>')\n\n\ndef emit_stats_by_project(writer, warn_patterns, project_names):\n  \"\"\"Dump a google chart table of warnings per project and severity.\"\"\"\n\n  warnings = create_warnings(warn_patterns, project_names)\n  total_by_project = get_total_by_project(warnings, project_names)\n  total_by_severity = get_total_by_severity(warnings, project_names)\n  stats_header = emit_table_header(total_by_severity)\n  total_all_projects, stats_rows = emit_row_counts_per_project(\n      warnings, total_by_project, total_by_severity, project_names)\n  emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows,\n                               total_all_projects, writer)\n\n\ndef dump_stats(writer, warn_patterns):\n  \"\"\"Dump some stats about total number of warnings and date.\"\"\"\n\n  known = 0\n  skipped = 0\n  unknown = 0\n  sort_warnings(warn_patterns)\n  for i in warn_patterns:\n    if i['severity'] == Severity.UNMATCHED:\n      unknown += len(i['members'])\n    elif i['severity'] == Severity.SKIP:\n      skipped += len(i['members'])\n    else:\n      known += len(i['members'])\n  writer('Number of classified warnings: <b>' + str(known) + '</b><br>')\n  writer('Number of skipped warnings: <b>' + str(skipped) + '</b><br>')\n  writer('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>')\n  total = unknown + known + skipped\n  extra_msg = ''\n  if total < 1000:\n    extra_msg = ' (low count may indicate incremental build)'\n  writer('Total number of warnings: <b>' + str(total) + '</b>' + extra_msg)\n  date_time_str = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')\n  writer('<p>(generated on ' + date_time_str + ')')\n\n\n# New base table of warnings, [severity, warn_id, project, warning_message]\n# Need buttons to show warnings in different grouping options.\n# (1) Current, group by severity, id for each warning pattern\n#     sort by severity, warn_id, warning_message\n# (2) Current --byproject, group by severity,\n#     id for each warning pattern + project name\n#     sort by severity, warn_id, project, warning_message\n# (3) New, group by project + severity,\n#     id for each warning pattern\n#     sort by project, severity, warn_id, warning_message\ndef emit_buttons(writer):\n  \"\"\"Write the button elements in HTML.\"\"\"\n  writer('<p><button class=\"button\" onclick=\"expandCollapse(1);\">'\n         'Expand all warnings</button>\\n'\n         '<button class=\"button\" onclick=\"expandCollapse(0);\">'\n         'Collapse all warnings</button>\\n'\n         '<p><button class=\"button\" onclick=\"groupBySeverity();\">'\n         'Group warnings by severity</button>\\n'\n         '<button class=\"button\" onclick=\"groupByProject();\">'\n         'Group warnings by project</button>')\n\n\ndef all_patterns(category):\n  patterns = ''\n  for i in category['patterns']:\n    patterns += i\n    patterns += ' / '\n  return patterns\n\n\ndef dump_fixed(writer, warn_patterns):\n  \"\"\"Show which warnings no longer occur.\"\"\"\n  anchor = 'fixed_warnings'\n  mark = anchor + '_mark'\n  writer('\\n<br><p style=\"background-color:lightblue\"><b>'\n         '<button id=\"' + mark + '\" '\n         'class=\"bt\" onclick=\"expand(\\'' + anchor + '\\');\">'\n         '&#x2295</button> Fixed warnings. '\n         'No more occurrences. Please consider turning these into '\n         'errors if possible, before they are reintroduced in to the build'\n         ':</b></p>')\n  writer('<blockquote>')\n  fixed_patterns = []\n  for i in warn_patterns:\n    if not i['members']:\n      fixed_patterns.append(i['description'] + ' (' + all_patterns(i) + ')')\n  fixed_patterns = sorted(fixed_patterns)\n  writer('<div id=\"' + anchor + '\" style=\"display:none;\"><table>')\n  cur_row_class = 0\n  for text in fixed_patterns:\n    cur_row_class = 1 - cur_row_class\n    # remove last '\\n'\n    out_text = text[:-1] if text[-1] == '\\n' else text\n    writer('<tr><td class=\"c' + str(cur_row_class) + '\">'\n           + out_text + '</td></tr>')\n  writer('</table></div>')\n  writer('</blockquote>')\n\n\ndef write_severity(csvwriter, sev, kind, warn_patterns):\n  \"\"\"Count warnings of given severity and write CSV entries to writer.\"\"\"\n  total = 0\n  for pattern in warn_patterns:\n    if pattern['severity'] == sev and pattern['members']:\n      num_members = len(pattern['members'])\n      total += num_members\n      warning = kind + ': ' + (pattern['description'] or '?')\n      csvwriter.writerow([num_members, '', warning])\n      # print number of warnings for each project, ordered by project name\n      projects = sorted(pattern['projects'].keys())\n      for project in projects:\n        csvwriter.writerow([pattern['projects'][project], project, warning])\n  csvwriter.writerow([total, '', kind + ' warnings'])\n  return total\n\n\ndef dump_csv(csvwriter, warn_patterns):\n  \"\"\"Dump number of warnings in CSV format to writer.\"\"\"\n  sort_warnings(warn_patterns)\n  total = 0\n  for severity in Severity.levels:\n    total += write_severity(\n        csvwriter, severity, severity.column_header, warn_patterns)\n  csvwriter.writerow([total, '', 'All warnings'])\n\n\ndef dump_csv_with_description(csvwriter, warning_records, warning_messages,\n                              warn_patterns, project_names):\n  \"\"\"Outputs all the warning messages by project.\"\"\"\n  csv_output = []\n  for record in warning_records:\n    project_name = project_names[record[1]]\n    pattern = warn_patterns[record[0]]\n    severity = pattern['severity'].header\n    category = pattern['category']\n    description = pattern['description']\n    warning = warning_messages[record[2]]\n    csv_output.append([project_name, severity,\n                       category, description,\n                       warning])\n  csv_output = sorted(csv_output)\n  for output in csv_output:\n    csvwriter.writerow(output)\n\n\n# Return line with escaped backslash and quotation characters.\ndef escape_string(line):\n  return line.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"')\n\n\n# Return line without trailing '\\n' and escape the quotation characters.\ndef strip_escape_string(line):\n  if not line:\n    return line\n  line = line[:-1] if line[-1] == '\\n' else line\n  return escape_string(line)\n\n\ndef emit_warning_array(name, writer, warn_patterns):\n  writer('var warning_{} = ['.format(name))\n  for pattern in warn_patterns:\n    if name == 'severity':\n      writer('{},'.format(pattern[name].value))\n    else:\n      writer('{},'.format(pattern[name]))\n  writer('];')\n\n\ndef emit_warning_arrays(writer, warn_patterns):\n  emit_warning_array('severity', writer, warn_patterns)\n  writer('var warning_description = [')\n  for pattern in warn_patterns:\n    if pattern['members']:\n      writer('\"{}\",'.format(escape_string(pattern['description'])))\n    else:\n      writer('\"\",')  # no such warning\n  writer('];')\n\n\nSCRIPTS_FOR_WARNING_GROUPS = \"\"\"\n  function compareMessages(x1, x2) { // of the same warning type\n    return (WarningMessages[x1[2]] <= WarningMessages[x2[2]]) ? -1 : 1;\n  }\n  function byMessageCount(x1, x2) {\n    return x2[2] - x1[2];  // reversed order\n  }\n  function bySeverityMessageCount(x1, x2) {\n    // orer by severity first\n    if (x1[1] != x2[1])\n      return  x1[1] - x2[1];\n    return byMessageCount(x1, x2);\n  }\n  const ParseLinePattern = /^([^ :]+):(\\\\d+):(.+)/;\n  function addURL(line) { // used by Android\n    if (FlagURL == \"\") return line;\n    if (FlagSeparator == \"\") {\n      return line.replace(ParseLinePattern,\n        \"<a target='_blank' href='\" + FlagURL + \"/$1'>$1</a>:$2:$3\");\n    }\n    return line.replace(ParseLinePattern,\n      \"<a target='_blank' href='\" + FlagURL + \"/$1\" + FlagSeparator +\n        \"$2'>$1:$2</a>:$3\");\n  }\n  function addURLToLine(line, link) { // used by Chrome\n      let line_split = line.split(\":\");\n      let path = line_split.slice(0,3).join(\":\");\n      let msg = line_split.slice(3).join(\":\");\n      let html_link = `<a target=\"_blank\" href=\"${link}\">${path}</a>${msg}`;\n      return html_link;\n  }\n  function createArrayOfDictionaries(n) {\n    var result = [];\n    for (var i=0; i<n; i++) result.push({});\n    return result;\n  }\n  function groupWarningsBySeverity() {\n    // groups is an array of dictionaries,\n    // each dictionary maps from warning type to array of warning messages.\n    var groups = createArrayOfDictionaries(SeverityColors.length);\n    for (var i=0; i<Warnings.length; i++) {\n      var w = Warnings[i][0];\n      var s = WarnPatternsSeverity[w];\n      var k = w.toString();\n      if (!(k in groups[s]))\n        groups[s][k] = [];\n      groups[s][k].push(Warnings[i]);\n    }\n    return groups;\n  }\n  function groupWarningsByProject() {\n    var groups = createArrayOfDictionaries(ProjectNames.length);\n    for (var i=0; i<Warnings.length; i++) {\n      var w = Warnings[i][0];\n      var p = Warnings[i][1];\n      var k = w.toString();\n      if (!(k in groups[p]))\n        groups[p][k] = [];\n      groups[p][k].push(Warnings[i]);\n    }\n    return groups;\n  }\n  var GlobalAnchor = 0;\n  function createWarningSection(header, color, group) {\n    var result = \"\";\n    var groupKeys = [];\n    var totalMessages = 0;\n    for (var k in group) {\n       totalMessages += group[k].length;\n       groupKeys.push([k, WarnPatternsSeverity[parseInt(k)], group[k].length]);\n    }\n    groupKeys.sort(bySeverityMessageCount);\n    for (var idx=0; idx<groupKeys.length; idx++) {\n      var k = groupKeys[idx][0];\n      var messages = group[k];\n      var w = parseInt(k);\n      var wcolor = SeverityColors[WarnPatternsSeverity[w]];\n      var description = WarnPatternsDescription[w];\n      if (description.length == 0)\n          description = \"???\";\n      GlobalAnchor += 1;\n      result += \"<table class='t1'><tr bgcolor='\" + wcolor + \"'><td>\" +\n                \"<button class='bt' id='\" + GlobalAnchor + \"_mark\" +\n                \"' onclick='expand(\\\\\"\" + GlobalAnchor + \"\\\\\");'>\" +\n                \"&#x2295</button> \" +\n                description + \" (\" + messages.length + \")</td></tr></table>\";\n      result += \"<div id='\" + GlobalAnchor +\n                \"' style='display:none;'><table class='t1'>\";\n      var c = 0;\n      messages.sort(compareMessages);\n      if (FlagPlatform == \"chrome\") {\n        for (var i=0; i<messages.length; i++) {\n          result += \"<tr><td class='c\" + c + \"'>\" +\n                    addURLToLine(WarningMessages[messages[i][2]], WarningLinks[messages[i][3]]) + \"</td></tr>\";\n          c = 1 - c;\n        }\n      } else {\n        for (var i=0; i<messages.length; i++) {\n          result += \"<tr><td class='c\" + c + \"'>\" +\n                    addURL(WarningMessages[messages[i][2]]) + \"</td></tr>\";\n          c = 1 - c;\n        }\n      }\n      result += \"</table></div>\";\n    }\n    if (result.length > 0) {\n      return \"<br><span style='background-color:\" + color + \"'><b>\" +\n             header + \": \" + totalMessages +\n             \"</b></span><blockquote><table class='t1'>\" +\n             result + \"</table></blockquote>\";\n\n    }\n    return \"\";  // empty section\n  }\n  function generateSectionsBySeverity() {\n    var result = \"\";\n    var groups = groupWarningsBySeverity();\n    for (s=0; s<SeverityColors.length; s++) {\n      result += createWarningSection(SeverityHeaders[s], SeverityColors[s],\n                                     groups[s]);\n    }\n    return result;\n  }\n  function generateSectionsByProject() {\n    var result = \"\";\n    var groups = groupWarningsByProject();\n    for (i=0; i<groups.length; i++) {\n      result += createWarningSection(ProjectNames[i], 'lightgrey', groups[i]);\n    }\n    return result;\n  }\n  function groupWarnings(generator) {\n    GlobalAnchor = 0;\n    var e = document.getElementById(\"warning_groups\");\n    e.innerHTML = generator();\n  }\n  function groupBySeverity() {\n    groupWarnings(generateSectionsBySeverity);\n  }\n  function groupByProject() {\n    groupWarnings(generateSectionsByProject);\n  }\n\"\"\"\n\n\n# Emit a JavaScript const number\ndef emit_const_number(name, value, writer):\n  writer('const ' + name + ' = ' + str(value) + ';')\n\n\n# Emit a JavaScript const string\ndef emit_const_string(name, value, writer):\n  writer('const ' + name + ' = \"' + escape_string(value) + '\";')\n\n\n# Emit a JavaScript const integer array.\ndef emit_const_int_array(name, array, writer):\n  writer('const ' + name + ' = [')\n  for item in array:\n    writer(str(item) + ',')\n  writer('];')\n\n\n# Emit a JavaScript const string array.\ndef emit_const_string_array(name, array, writer):\n  writer('const ' + name + ' = [')\n  for item in array:\n    writer('\"' + strip_escape_string(item) + '\",')\n  writer('];')\n\n\n# Emit a JavaScript const string array for HTML.\ndef emit_const_html_string_array(name, array, writer):\n  writer('const ' + name + ' = [')\n  for item in array:\n    writer('\"' + html.escape(strip_escape_string(item)) + '\",')\n  writer('];')\n\n\n# Emit a JavaScript const object array.\ndef emit_const_object_array(name, array, writer):\n  writer('const ' + name + ' = [')\n  for item in array:\n    writer(str(item) + ',')\n  writer('];')\n\n\ndef emit_js_data(writer, flags, warning_messages, warning_links,\n                 warning_records, warn_patterns, project_names):\n  \"\"\"Dump dynamic HTML page's static JavaScript data.\"\"\"\n  emit_const_string('FlagPlatform', flags.platform, writer)\n  emit_const_string('FlagURL', flags.url, writer)\n  emit_const_string('FlagSeparator', flags.separator, writer)\n  emit_const_number('LimitWarningsPerFile', LIMIT_WARNINGS_PER_FILE, writer)\n  emit_const_number('LimitPercentWarnings', LIMIT_PERCENT_WARNINGS, writer)\n  emit_const_string_array('SeverityColors', [s.color for s in Severity.levels],\n                          writer)\n  emit_const_string_array('SeverityHeaders',\n                          [s.header for s in Severity.levels], writer)\n  emit_const_string_array('SeverityColumnHeaders',\n                          [s.column_header for s in Severity.levels], writer)\n  emit_const_string_array('ProjectNames', project_names, writer)\n  # pytype: disable=attribute-error\n  emit_const_int_array('WarnPatternsSeverity',\n                       [w['severity'].value for w in warn_patterns], writer)\n  # pytype: enable=attribute-error\n  emit_const_html_string_array('WarnPatternsDescription',\n                               [w['description'] for w in warn_patterns],\n                               writer)\n  emit_const_html_string_array('WarningMessages', warning_messages, writer)\n  emit_const_object_array('Warnings', warning_records, writer)\n  if flags.platform == 'chrome':\n    emit_const_html_string_array('WarningLinks', warning_links, writer)\n\n\nDRAW_TABLE_JAVASCRIPT = \"\"\"\ngoogle.charts.load('current', {'packages':['table']});\ngoogle.charts.setOnLoadCallback(genTables);\nfunction genSelectedProjectsTable() {\n  var data = new google.visualization.DataTable();\n  data.addColumn('string', StatsHeader[0]);\n  for (var i=1; i<StatsHeader.length; i++) {\n    data.addColumn('number', StatsHeader[i]);\n  }\n  data.addRows(StatsRows);\n  for (var i=0; i<StatsRows.length; i++) {\n    for (var j=0; j<StatsHeader.length; j++) {\n      data.setProperty(i, j, 'style', 'border:1px solid black;');\n    }\n  }\n  var table = new google.visualization.Table(\n      document.getElementById('selected_projects_section'));\n  table.draw(data, {allowHtml: true, alternatingRowStyle: true});\n}\n// Global TopDirs and TopFiles are computed later by genTables.\nwindow.TopDirs = [];\nwindow.TopFiles = [];\nfunction computeTopDirsFiles() {\n  var numWarnings = WarningMessages.length;\n  var warningsOfFiles = {};\n  var warningsOfDirs = {};\n  var subDirs = {};\n  function addOneWarning(map, key, type, unique) {\n    function increaseCounter(idx) {\n      map[idx] = 1 + ((idx in map) ? map[idx] : 0);\n    }\n    increaseCounter(key)\n    if (type != \"\") {\n      increaseCounter(type + \" \" + key)\n      if (unique) {\n        increaseCounter(type + \" *\")\n      }\n    }\n  }\n  for (var i = 0; i < numWarnings; i++) {\n    var message = WarningMessages[i]\n    var file = message.replace(/:.*/, \"\");\n    var warningType = message.endsWith(\"]\") ? message.replace(/.*\\[/, \"[\") : \"\";\n    addOneWarning(warningsOfFiles, file, warningType, true);\n    var dirs = file.split(\"/\");\n    var dir = dirs[0];\n    addOneWarning(warningsOfDirs, dir, warningType, true);\n    for (var d = 1; d < dirs.length - 1; d++) {\n      var subDir = dir + \"/\" + dirs[d];\n      if (!(dir in subDirs)) {\n        subDirs[dir] = {};\n      }\n      subDirs[dir][subDir] = 1;\n      dir = subDir;\n      addOneWarning(warningsOfDirs, dir, warningType, false);\n    }\n  }\n  var minDirWarnings = numWarnings*(LimitPercentWarnings/100);\n  var minFileWarnings = Math.min(LimitWarningsPerFile, minDirWarnings);\n  // Each row in TopDirs and TopFiles has\n  // [index, {v:<num_of_warnings>, f:<percent>}, file_or_dir_name]\n  function countWarnings(minWarnings, warningsOf, isDir) {\n    var rows = [];\n    for (var name in warningsOf) {\n      if (isDir && name in subDirs && Object.keys(subDirs[name]).length < 2) {\n        continue; // skip a directory if it has only one subdir\n      }\n      var count = warningsOf[name];\n      if (count >= minWarnings) {\n        name = isDir ? (name + \"/...\") : name;\n        var percent = (100*count/numWarnings).toFixed(1);\n        var countFormat = count + ' (' + percent + '%)';\n        rows.push([0, {v:count, f:countFormat}, name]);\n      }\n    }\n    rows.sort((a,b) => b[1].v - a[1].v);\n    for (var i=0; i<rows.length; i++) {\n      rows[i][0] = i;\n    }\n    return rows;\n  }\n  TopDirs = countWarnings(minDirWarnings, warningsOfDirs, true);\n  TopFiles = countWarnings(minFileWarnings, warningsOfFiles, false);\n}\nfunction genTopDirsFilesTables() {\n  computeTopDirsFiles();\n  function addTable(name, divName, rows, clickFunction) {\n    var data = new google.visualization.DataTable();\n    data.addColumn(\"number\", \"index\"); // not shown in view\n    data.addColumn(\"number\", \"# of warnings\");\n    data.addColumn(\"string\", name);\n    data.addRows(rows);\n    var formatter = new google.visualization.PatternFormat(\n      '<p onclick=\"' + clickFunction + '({0})\">{2}</p>');\n    formatter.format(data, [0, 1, 2], 2);\n    var view = new google.visualization.DataView(data);\n    view.setColumns([1,2]); // hide the index column\n    var table = new google.visualization.Table(\n        document.getElementById(divName));\n    table.draw(view, {allowHtml: true, alternatingRowStyle: true});\n  }\n  addTable(\"[Warning Type] Directory\", \"top_dirs_table\", TopDirs, \"selectDir\");\n  addTable(\"[Warning Type] File\", \"top_files_table\", TopFiles, \"selectFile\");\n}\nfunction selectDirFile(idx, rows, dirFile) {\n  if (rows.length <= idx) {\n    return;\n  }\n  var name = rows[idx][2];\n  var type = \"\";\n  if (name.startsWith(\"[\")) {\n    type = \" \" + name.replace(/ .*/, \"\");\n    name = name.replace(/.* /, \"\");\n  }\n  var spanName = \"selected_\" + dirFile + \"_name\";\n  document.getElementById(spanName).innerHTML = name + type;\n  var divName = \"selected_\" + dirFile + \"_warnings\";\n  var numWarnings = rows[idx][1].v;\n  var prefix = name.replace(/\\\\.\\\\.\\\\.$/, \"\");\n  var data = new google.visualization.DataTable();\n  data.addColumn('string', numWarnings + type + ' warnings in ' + name);\n  var getWarningMessage = (FlagPlatform == \"chrome\")\n        ? ((x) => addURLToLine(WarningMessages[Warnings[x][2]],\n                               WarningLinks[Warnings[x][3]]))\n        : ((x) => addURL(WarningMessages[Warnings[x][2]]));\n  for (var i = 0; i < Warnings.length; i++) {\n    if ((prefix.startsWith(\"*\") || WarningMessages[Warnings[i][2]].startsWith(prefix)) &&\n        (type == \"\" || WarningMessages[Warnings[i][2]].endsWith(type))) {\n      data.addRow([getWarningMessage(i)]);\n    }\n  }\n  var table = new google.visualization.Table(\n      document.getElementById(divName));\n  table.draw(data, {allowHtml: true, alternatingRowStyle: true});\n}\nfunction selectDir(idx) {\n  selectDirFile(idx, TopDirs, \"directory\")\n}\nfunction selectFile(idx) {\n  selectDirFile(idx, TopFiles, \"file\");\n}\nfunction genTables() {\n  genSelectedProjectsTable();\n  if (WarningMessages.length > 1) {\n    genTopDirsFilesTables();\n  }\n}\n\"\"\"\n\n\ndef dump_boxed_section(writer, func):\n  writer('<div class=\"box\">')\n  func()\n  writer('</div>')\n\n\ndef dump_section_header(writer, table_name, section_title):\n  writer('<h3><b><button id=\"' + table_name + '_mark\" class=\"bt\"\\n' +\n         ' onclick=\"expand(\\'' + table_name + '\\');\">&#x2295</button></b>\\n' +\n         section_title + '</h3>')\n\n\ndef dump_table_section(writer, table_name, section_title):\n  dump_section_header(writer, table_name, section_title)\n  writer('<div id=\"' + table_name + '\" style=\"display:none;\"></div>')\n\n\ndef dump_dir_file_section(writer, dir_file, table_name, section_title):\n  section_name = 'top_' + dir_file + '_section'\n  dump_section_header(writer, section_name, section_title)\n  writer('<div id=\"' + section_name + '\" style=\"display:none;\">')\n  writer('<div id=\"' + table_name + '\"></div>')\n  def subsection():\n    subsection_name = 'selected_' + dir_file + '_warnings'\n    subsection_title = ('Warnings in <span id=\"selected_' + dir_file +\n                        '_name\">(click a ' + dir_file +\n                        ' in the above table)</span>')\n    dump_section_header(writer, subsection_name, subsection_title)\n    writer('<div id=\"' + subsection_name + '\" style=\"display:none;\"></div>')\n  dump_boxed_section(writer, subsection)\n  writer('</div>')\n\n\n# HTML output has the following major div elements:\n#  selected_projects_section\n#  top_directory_section\n#    top_dirs_table\n#    selected_directory_warnings\n#  top_file_section\n#    top_files_table\n#    selected_file_warnings\n#  all_warnings_section\n#    warning_groups\n#    fixed_warnings\ndef dump_html(flags, output_stream, warning_messages, warning_links,\n              warning_records, header_str, warn_patterns, project_names):\n  \"\"\"Dump the flags output to output_stream.\"\"\"\n  writer = make_writer(output_stream)\n  dump_html_prologue('Warnings for ' + header_str, writer, warn_patterns,\n                     project_names)\n  dump_stats(writer, warn_patterns)\n  writer('<br><br>Press &#x2295 to show section content,'\n         ' and &#x2296 to hide the content.')\n  def section1():\n    dump_table_section(writer, 'selected_projects_section',\n                       'Number of warnings in preselected project directories')\n  def section2():\n    dump_dir_file_section(\n        writer, 'directory', 'top_dirs_table',\n        'Directories/Warnings with at least ' +\n        str(LIMIT_PERCENT_WARNINGS) + '% of all cases')\n  def section3():\n    dump_dir_file_section(\n        writer, 'file', 'top_files_table',\n        'Files/Warnings with at least ' +\n        str(LIMIT_PERCENT_WARNINGS) + '% of all or ' +\n        str(LIMIT_WARNINGS_PER_FILE) + ' cases')\n  def section4():\n    writer('<script>')\n    emit_js_data(writer, flags, warning_messages, warning_links,\n                 warning_records, warn_patterns, project_names)\n    writer(SCRIPTS_FOR_WARNING_GROUPS)\n    writer('</script>')\n    dump_section_header(writer, 'all_warnings_section',\n                        'All warnings grouped by severities or projects')\n    writer('<div id=\"all_warnings_section\" style=\"display:none;\">')\n    emit_buttons(writer)\n    # Warning messages are grouped by severities or project names.\n    writer('<br><div id=\"warning_groups\"></div>')\n    if flags.byproject:\n      writer('<script>groupByProject();</script>')\n    else:\n      writer('<script>groupBySeverity();</script>')\n    dump_fixed(writer, warn_patterns)\n    writer('</div>')\n  dump_boxed_section(writer, section1)\n  dump_boxed_section(writer, section2)\n  dump_boxed_section(writer, section3)\n  dump_boxed_section(writer, section4)\n  dump_html_epilogue(writer)\n\n\ndef write_html(flags, project_names, warn_patterns, html_path, warning_messages,\n               warning_links, warning_records, header_str):\n  \"\"\"Write warnings html file.\"\"\"\n  if html_path:\n    with open(html_path, 'w') as outf:\n      dump_html(flags, outf, warning_messages, warning_links, warning_records,\n                header_str, warn_patterns, project_names)\n\n\ndef write_out_csv(flags, warn_patterns, warning_messages, warning_links,\n                  warning_records, header_str, project_names):\n  \"\"\"Write warnings csv file.\"\"\"\n  if flags.csvpath:\n    with open(flags.csvpath, 'w') as outf:\n      dump_csv(csv.writer(outf, lineterminator='\\n'), warn_patterns)\n\n  if flags.csvwithdescription:\n    with open(flags.csvwithdescription, 'w') as outf:\n      dump_csv_with_description(csv.writer(outf, lineterminator='\\n'),\n                                warning_records, warning_messages,\n                                warn_patterns, project_names)\n\n  if flags.gencsv:\n    dump_csv(csv.writer(sys.stdout, lineterminator='\\n'), warn_patterns)\n  else:\n    dump_html(flags, sys.stdout, warning_messages, warning_links,\n              warning_records, header_str, warn_patterns, project_names)\n"
  },
  {
    "path": "tools/warn/java_warn_patterns.py",
    "content": "# python3\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Warning patterns for Java compiler tools.\"\"\"\n\n# No need of doc strings for trivial small functions.\n# pylint:disable=missing-function-docstring\n\n# pylint:disable=relative-beyond-top-level\nfrom .cpp_warn_patterns import compile_patterns\nfrom .severity import Severity\n\n\ndef java_warn(severity, description, pattern_list):\n  return {\n      'category': 'Java',\n      'severity': severity,\n      'description': 'Java: ' + description,\n      'patterns': pattern_list\n  }\n\n\ndef java_high(description, pattern_list):\n  return java_warn(Severity.HIGH, description, pattern_list)\n\n\ndef java_medium(description, pattern_list):\n  return java_warn(Severity.MEDIUM, description, pattern_list)\n\n\ndef warn_with_name(name, severity, description=None):\n  if description is None:\n    description = name\n  return java_warn(severity, description,\n                   [r'.*\\.java:.*: warning: .+ \\[' + name + r'\\]$',\n                    r'.*\\.java:.*: warning: \\[' + name + r'\\] .+'])\n\n\ndef high(name, description=None):\n  return warn_with_name(name, Severity.HIGH, description)\n\n\ndef medium(name, description=None):\n  return warn_with_name(name, Severity.MEDIUM, description)\n\n\ndef low(name, description=None):\n  return warn_with_name(name, Severity.LOW, description)\n\n\nwarn_patterns = [\n    # pylint does not recognize g-inconsistent-quotes\n    # pylint:disable=line-too-long,bad-option-value,g-inconsistent-quotes\n    # Warnings from Javac\n    java_medium('Use of deprecated',\n                [r'.*: warning: \\[deprecation\\] .+',\n                 r'.*: warning: \\[removal\\] .+ has been deprecated and marked for removal$']),\n    java_medium('Incompatible SDK implementation',\n                [r'.*\\.java:.*: warning: @Implementation .+ has .+ not .+ as in the SDK ']),\n    medium('unchecked', 'Unchecked conversion'),\n    java_medium('No annotation method',\n                [r'.*\\.class\\): warning: Cannot find annotation method .+ in']),\n    java_medium('No class/method in SDK ...',\n                [r'.*\\.java:.*: warning: No such (class|method) .* for SDK']),\n    java_medium('Unknown enum constant',\n                [r'unknown_source_file: warning: unknown enum constant .+']),\n    # Warnings generated by Error Prone\n    java_medium('Non-ascii characters used, but ascii encoding specified',\n                [r\".*: warning: unmappable character for encoding ascii\"]),\n    java_medium('Non-varargs call of varargs method with inexact argument type for last parameter',\n                [r\".*: warning: non-varargs call of varargs method with inexact argument type for last parameter\"]),\n    java_medium('Unchecked method invocation',\n                [r\".*: warning: \\[unchecked\\] unchecked method invocation: .+ in class .+\"]),\n    java_medium('Unchecked conversion',\n                [r\".*: warning: \\[unchecked\\] unchecked conversion\"]),\n    java_medium('_ used as an identifier',\n                [r\".*: warning: '_' used as an identifier\"]),\n    java_medium('hidden superclass',\n                [r\".*: warning: .* stripped of .* superclass .* \\[HiddenSuperclass\\]\"]),\n    java_high('Use of internal proprietary API',\n              [r\".*: warning: .* is internal proprietary API and may be removed\"]),\n    low('BooleanParameter',\n        'Use parameter comments to document ambiguous literals'),\n    low('ClassNamedLikeTypeParameter',\n        'This class\\'s name looks like a Type Parameter.'),\n    low('ConstantField',\n        'Field name is CONSTANT_CASE, but field is not static and final'),\n    low('EmptySetMultibindingContributions',\n        '@Multibinds is a more efficient and declarative mechanism for ensuring that a set multibinding is present in the graph.'),\n    low('ExpectedExceptionRefactoring',\n        'Prefer assertThrows to ExpectedException'),\n    low('FieldCanBeFinal',\n        'This field is only assigned during initialization; consider making it final'),\n    low('FieldMissingNullable',\n        'Fields that can be null should be annotated @Nullable'),\n    low('ImmutableRefactoring',\n        'Refactors uses of the JSR 305 @Immutable to Error Prone\\'s annotation'),\n    low('LambdaFunctionalInterface',\n        u'Use Java\\'s utility functional interfaces instead of Function\\u003cA, B> for primitive types.'),\n    low('MethodCanBeStatic',\n        'A private method that does not reference the enclosing instance can be static'),\n    low('MixedArrayDimensions',\n        'C-style array declarations should not be used'),\n    low('MultiVariableDeclaration',\n        'Variable declarations should declare only one variable'),\n    low('MultipleTopLevelClasses',\n        'Source files should not contain multiple top-level class declarations'),\n    low('MultipleUnaryOperatorsInMethodCall',\n        'Avoid having multiple unary operators acting on the same variable in a method call'),\n    low('OnNameExpected',\n        'OnNameExpected naming style'),\n    low('PackageLocation',\n        'Package names should match the directory they are declared in'),\n    low('ParameterComment',\n        'Non-standard parameter comment; prefer `/* paramName= */ arg`'),\n    low('ParameterNotNullable',\n        'Method parameters that aren\\'t checked for null shouldn\\'t be annotated @Nullable'),\n    low('PrivateConstructorForNoninstantiableModule',\n        'Add a private constructor to modules that will not be instantiated by Dagger.'),\n    low('PrivateConstructorForUtilityClass',\n        'Utility classes (only static members) are not designed to be instantiated and should be made noninstantiable with a default constructor.'),\n    low('RemoveUnusedImports',\n        'Unused imports'),\n    low('ReturnMissingNullable',\n        'Methods that can return null should be annotated @Nullable'),\n    low('ScopeOnModule',\n        'Scopes on modules have no function and will soon be an error.'),\n    low('SwitchDefault',\n        'The default case of a switch should appear at the end of the last statement group'),\n    low('TestExceptionRefactoring',\n        'Prefer assertThrows to @Test(expected=...)'),\n    low('ThrowsUncheckedException',\n        'Unchecked exceptions do not need to be declared in the method signature.'),\n    low('TryFailRefactoring',\n        'Prefer assertThrows to try/fail'),\n    low('TypeParameterNaming',\n        'Type parameters must be a single letter with an optional numeric suffix, or an UpperCamelCase name followed by the letter \\'T\\'.'),\n    low('UngroupedOverloads',\n        'Constructors and methods with the same name should appear sequentially with no other code in between. Please re-order or re-name methods.'),\n    low('UnnecessarySetDefault',\n        'Unnecessary call to NullPointerTester#setDefault'),\n    low('UnnecessaryStaticImport',\n        'Using static imports for types is unnecessary'),\n    low('UseBinds',\n        '@Binds is a more efficient and declarative mechanism for delegating a binding.'),\n    low('WildcardImport',\n        'Wildcard imports, static or otherwise, should not be used'),\n    medium('AcronymName',\n           'AcronymName'),\n    medium('AmbiguousMethodReference',\n           'Method reference is ambiguous'),\n    medium('AnnotateFormatMethod',\n           'This method passes a pair of parameters through to String.format, but the enclosing method wasn\\'t annotated @FormatMethod. Doing so gives compile-time rather than run-time protection against malformed format strings.'),\n    medium('AnnotationPosition',\n           'Annotations should be positioned after Javadocs, but before modifiers..'),\n    medium('ArgumentSelectionDefectChecker',\n           'Arguments are in the wrong order or could be commented for clarity.'),\n    medium('ArrayAsKeyOfSetOrMap',\n           'Arrays do not override equals() or hashCode, so comparisons will be done on reference equality only. If neither deduplication nor lookup are needed, consider using a List instead. Otherwise, use IdentityHashMap/Set, a Map from a library that handles object arrays, or an Iterable/List of pairs.'),\n    medium('AssertEqualsArgumentOrderChecker',\n           'Arguments are swapped in assertEquals-like call'),\n    medium('AssertFalse',\n           'Assertions may be disabled at runtime and do not guarantee that execution will halt here; consider throwing an exception instead'),\n    medium('AssertThrowsMultipleStatements',\n           'The lambda passed to assertThrows should contain exactly one statement'),\n    medium('AssertionFailureIgnored',\n           'This assertion throws an AssertionError if it fails, which will be caught by an enclosing try block.'),\n    medium('AssistedInjectAndInjectOnConstructors',\n           '@AssistedInject and @Inject should not be used on different constructors in the same class.'),\n    medium('AutoValueFinalMethods',\n           'Make toString(), hashCode() and equals() final in AutoValue classes, so it is clear to readers that AutoValue is not overriding them'),\n    medium('BadAnnotationImplementation',\n           'Classes that implement Annotation must override equals and hashCode. Consider using AutoAnnotation instead of implementing Annotation by hand.'),\n    medium('BadComparable',\n           'Possible sign flip from narrowing conversion'),\n    medium('BadImport',\n           'Importing nested classes/static methods/static fields with commonly-used names can make code harder to read, because it may not be clear from the context exactly which type is being referred to. Qualifying the name with that of the containing class can make the code clearer.'),\n    medium('BadInstanceof',\n           'instanceof used in a way that is equivalent to a null check.'),\n    medium('BigDecimalEquals',\n           'BigDecimal#equals has surprising behavior: it also compares scale.'),\n    medium('BigDecimalLiteralDouble',\n           'new BigDecimal(double) loses precision in this case.'),\n    medium('BinderIdentityRestoredDangerously',\n           'A call to Binder.clearCallingIdentity() should be followed by Binder.restoreCallingIdentity() in a finally block. Otherwise the wrong Binder identity may be used by subsequent code.'),\n    medium('BindingToUnqualifiedCommonType',\n           'This code declares a binding for a common value type without a Qualifier annotation.'),\n    medium('BoxedPrimitiveConstructor',\n           'valueOf or autoboxing provides better time and space performance'),\n    medium('ByteBufferBackingArray',\n           'ByteBuffer.array() shouldn\\'t be called unless ByteBuffer.arrayOffset() is used or if the ByteBuffer was initialized using ByteBuffer.wrap() or ByteBuffer.allocate().'),\n    medium('CannotMockFinalClass',\n           'Mockito cannot mock final classes'),\n    medium('CanonicalDuration',\n           'Duration can be expressed more clearly with different units'),\n    medium('CatchAndPrintStackTrace',\n           'Logging or rethrowing exceptions should usually be preferred to catching and calling printStackTrace'),\n    medium('CatchFail',\n           'Ignoring exceptions and calling fail() is unnecessary, and makes test output less useful'),\n    medium('ChangedAbstract',\n           'Method has changed \\'abstract\\' qualifier'),\n    medium('ClassCanBeStatic',\n           'Inner class is non-static but does not reference enclosing class'),\n    medium('ClassNewInstance',\n           'Class.newInstance() bypasses exception checking; prefer getDeclaredConstructor().newInstance()'),\n    medium('CloseableProvides',\n           'Providing Closeable resources makes their lifecycle unclear'),\n    medium('CollectionToArraySafeParameter',\n           'The type of the array parameter of Collection.toArray needs to be compatible with the array type'),\n    medium('CollectorShouldNotUseState',\n           'Collector.of() should not use state'),\n    medium('ComparableAndComparator',\n           'Class should not implement both `Comparable` and `Comparator`'),\n    medium('ConstructorInvokesOverridable',\n           'Constructors should not invoke overridable methods.'),\n    medium('ConstructorLeaksThis',\n           'Constructors should not pass the \\'this\\' reference out in method invocations, since the object may not be fully constructed.'),\n    medium('DateFormatConstant',\n           'DateFormat is not thread-safe, and should not be used as a constant field.'),\n    medium('DefaultCharset',\n           'Implicit use of the platform default charset, which can result in differing behaviour between JVM executions or incorrect behavior if the encoding of the data source doesn\\'t match expectations.'),\n    medium('DeprecatedThreadMethods',\n           'Avoid deprecated Thread methods; read the method\\'s javadoc for details.'),\n    medium('DoubleBraceInitialization',\n           'Prefer collection factory methods or builders to the double-brace initialization pattern.'),\n    medium('DoubleCheckedLocking',\n           'Double-checked locking on non-volatile fields is unsafe'),\n    medium('EmptyTopLevelDeclaration',\n           'Empty top-level type declaration'),\n    medium('EqualsBrokenForNull',\n           'equals() implementation may throw NullPointerException when given null'),\n    medium('EqualsGetClass',\n           'Overriding Object#equals in a non-final class by using getClass rather than instanceof breaks substitutability of subclasses.'),\n    medium('EqualsHashCode',\n           'Classes that override equals should also override hashCode.'),\n    medium('EqualsIncompatibleType',\n           'An equality test between objects with incompatible types always returns false'),\n    medium('EqualsUnsafeCast',\n           'The contract of #equals states that it should return false for incompatible types, while this implementation may throw ClassCastException.'),\n    medium('EqualsUsingHashCode',\n           'Implementing #equals by just comparing hashCodes is fragile. Hashes collide frequently, and this will lead to false positives in #equals.'),\n    medium('ExpectedExceptionChecker',\n           'Calls to ExpectedException#expect should always be followed by exactly one statement.'),\n    medium('ExtendingJUnitAssert',\n           'When only using JUnit Assert\\'s static methods, you should import statically instead of extending.'),\n    medium('FallThrough',\n           'Switch case may fall through'),\n    medium('Finally',\n           'If you return or throw from a finally, then values returned or thrown from the try-catch block will be ignored. Consider using try-with-resources instead.'),\n    medium('FloatCast',\n           'Use parentheses to make the precedence explicit'),\n    medium('FloatingPointAssertionWithinEpsilon',\n           'This fuzzy equality check is using a tolerance less than the gap to the next number. You may want a less restrictive tolerance, or to assert equality.'),\n    medium('FloatingPointLiteralPrecision',\n           'Floating point literal loses precision'),\n    medium('FragmentInjection',\n           'Classes extending PreferenceActivity must implement isValidFragment such that it does not unconditionally return true to prevent vulnerability to fragment injection attacks.'),\n    medium('FragmentNotInstantiable',\n           'Subclasses of Fragment must be instantiable via Class#newInstance(): the class must be public, static and have a public nullary constructor'),\n    medium('FunctionalInterfaceClash',\n           'Overloads will be ambiguous when passing lambda arguments'),\n    medium('FutureReturnValueIgnored',\n           'Return value of methods returning Future must be checked. Ignoring returned Futures suppresses exceptions thrown from the code that completes the Future.'),\n    medium('GetClassOnEnum',\n           'Calling getClass() on an enum may return a subclass of the enum type'),\n    medium('HardCodedSdCardPath',\n           'Hardcoded reference to /sdcard'),\n    medium('HidingField',\n           'Hiding fields of superclasses may cause confusion and errors'),\n    medium('ImmutableAnnotationChecker',\n           'Annotations should always be immutable'),\n    medium('ImmutableEnumChecker',\n           'Enums should always be immutable'),\n    medium('IncompatibleModifiers',\n           'This annotation has incompatible modifiers as specified by its @IncompatibleModifiers annotation'),\n    medium('InconsistentCapitalization',\n           'It is confusing to have a field and a parameter under the same scope that differ only in capitalization.'),\n    medium('InconsistentHashCode',\n           'Including fields in hashCode which are not compared in equals violates the contract of hashCode.'),\n    medium('InconsistentOverloads',\n           'The ordering of parameters in overloaded methods should be as consistent as possible (when viewed from left to right)'),\n    medium('IncrementInForLoopAndHeader',\n           'This for loop increments the same variable in the header and in the body'),\n    medium('InjectOnConstructorOfAbstractClass',\n           'Constructors on abstract classes are never directly @Injected, only the constructors of their subclasses can be @Inject\\'ed.'),\n    medium('InputStreamSlowMultibyteRead',\n           'Please also override int read(byte[], int, int), otherwise multi-byte reads from this input stream are likely to be slow.'),\n    medium('InstanceOfAndCastMatchWrongType',\n           'Casting inside an if block should be plausibly consistent with the instanceof type'),\n    medium('IntLongMath',\n           'Expression of type int may overflow before being assigned to a long'),\n    medium('IntentBuilderName',\n           'IntentBuilderName'),\n    medium('InvalidParam',\n           'This @param tag doesn\\'t refer to a parameter of the method.'),\n    medium('InvalidTag',\n           'This tag is invalid.'),\n    medium('InvalidThrows',\n           'The documented method doesn\\'t actually throw this checked exception.'),\n    medium('IterableAndIterator',\n           'Class should not implement both `Iterable` and `Iterator`'),\n    medium('JUnit3FloatingPointComparisonWithoutDelta',\n           'Floating-point comparison without error tolerance'),\n    medium('JUnit4ClassUsedInJUnit3',\n           'Some JUnit4 construct cannot be used in a JUnit3 context. Convert your class to JUnit4 style to use them.'),\n    medium('JUnitAmbiguousTestClass',\n           'Test class inherits from JUnit 3\\'s TestCase but has JUnit 4 @Test annotations.'),\n    medium('JavaLangClash',\n           'Never reuse class names from java.lang'),\n    medium('JdkObsolete',\n           'Suggests alternatives to obsolete JDK classes.'),\n    medium('LockNotBeforeTry',\n           'Calls to Lock#lock should be immediately followed by a try block which releases the lock.'),\n    medium('LogicalAssignment',\n           'Assignment where a boolean expression was expected; use == if this assignment wasn\\'t expected or add parentheses for clarity.'),\n    medium('MathAbsoluteRandom',\n           'Math.abs does not always give a positive result. Please consider other methods for positive random numbers.'),\n    medium('MissingCasesInEnumSwitch',\n           'Switches on enum types should either handle all values, or have a default case.'),\n    medium('MissingDefault',\n           'The Google Java Style Guide requires that each switch statement includes a default statement group, even if it contains no code. (This requirement is lifted for any switch statement that covers all values of an enum.)'),\n    medium('MissingFail',\n           'Not calling fail() when expecting an exception masks bugs'),\n    medium('MissingOverride',\n           'method overrides method in supertype; expected @Override'),\n    medium('ModifiedButNotUsed',\n           'A collection or proto builder was created, but its values were never accessed.'),\n    medium('ModifyCollectionInEnhancedForLoop',\n           'Modifying a collection while iterating over it in a loop may cause a ConcurrentModificationException to be thrown.'),\n    medium('MultipleParallelOrSequentialCalls',\n           'Multiple calls to either parallel or sequential are unnecessary and cause confusion.'),\n    medium('MutableConstantField',\n           'Constant field declarations should use the immutable type (such as ImmutableList) instead of the general collection interface type (such as List)'),\n    medium('MutableMethodReturnType',\n           'Method return type should use the immutable type (such as ImmutableList) instead of the general collection interface type (such as List)'),\n    medium('NarrowingCompoundAssignment',\n           'Compound assignments may hide dangerous casts'),\n    medium('NestedInstanceOfConditions',\n           'Nested instanceOf conditions of disjoint types create blocks of code that never execute'),\n    medium('NoFunctionalReturnType',\n           'Instead of returning a functional type, return the actual type that the returned function would return and use lambdas at use site.'),\n    medium('NonAtomicVolatileUpdate',\n           'This update of a volatile variable is non-atomic'),\n    medium('NonCanonicalStaticMemberImport',\n           'Static import of member uses non-canonical name'),\n    medium('NonOverridingEquals',\n           'equals method doesn\\'t override Object.equals'),\n    medium('NotCloseable',\n           'Not closeable'),\n    medium('NullableCollection',\n           'Method should not return a nullable collection'),\n    medium('NullableConstructor',\n           'Constructors should not be annotated with @Nullable since they cannot return null'),\n    medium('NullableDereference',\n           'Dereference of possibly-null value'),\n    medium('NullablePrimitive',\n           '@Nullable should not be used for primitive types since they cannot be null'),\n    medium('NullableVoid',\n           'void-returning methods should not be annotated with @Nullable, since they cannot return null'),\n    medium('ObjectToString',\n           'Calling toString on Objects that don\\'t override toString() doesn\\'t provide useful information'),\n    medium('ObjectsHashCodePrimitive',\n           'Objects.hashCode(Object o) should not be passed a primitive value'),\n    medium('OperatorPrecedence',\n           'Use grouping parenthesis to make the operator precedence explicit'),\n    medium('OptionalNotPresent',\n           'One should not call optional.get() inside an if statement that checks !optional.isPresent'),\n    medium('OrphanedFormatString',\n           'String literal contains format specifiers, but is not passed to a format method'),\n    medium('OverrideThrowableToString',\n           'To return a custom message with a Throwable class, one should override getMessage() instead of toString() for Throwable.'),\n    medium('Overrides',\n           'Varargs doesn\\'t agree for overridden method'),\n    medium('OverridesGuiceInjectableMethod',\n           'This method is not annotated with @Inject, but it overrides a method that is annotated with @com.google.inject.Inject. Guice will inject this method, and it is recommended to annotate it explicitly.'),\n    medium('ParameterName',\n           'Detects `/* name= */`-style comments on actual parameters where the name doesn\\'t match the formal parameter'),\n    medium('PreconditionsInvalidPlaceholder',\n           'Preconditions only accepts the %s placeholder in error message strings'),\n    medium('PrimitiveArrayPassedToVarargsMethod',\n           'Passing a primitive array to a varargs method is usually wrong'),\n    medium('ProtoRedundantSet',\n           'A field on a protocol buffer was set twice in the same chained expression.'),\n    medium('ProtosAsKeyOfSetOrMap',\n           'Protos should not be used as a key to a map, in a set, or in a contains method on a descendant of a collection. Protos have non deterministic ordering and proto equality is deep, which is a performance issue.'),\n    medium('ProvidesFix',\n           'BugChecker has incorrect ProvidesFix tag, please update'),\n    medium('QualifierOrScopeOnInjectMethod',\n           'Qualifiers/Scope annotations on @Inject methods don\\'t have any effect. Move the qualifier annotation to the binding location.'),\n    medium('QualifierWithTypeUse',\n           'Injection frameworks currently don\\'t understand Qualifiers in TYPE_PARAMETER or TYPE_USE contexts.'),\n    medium('ReachabilityFenceUsage',\n           'reachabilityFence should always be called inside a finally block'),\n    medium('RedundantThrows',\n           'Thrown exception is a subtype of another'),\n    medium('ReferenceEquality',\n           'Comparison using reference equality instead of value equality'),\n    medium('RequiredModifiers',\n           'This annotation is missing required modifiers as specified by its @RequiredModifiers annotation'),\n    medium('ReturnFromVoid',\n           'Void methods should not have a @return tag.'),\n    medium('SamShouldBeLast',\n           'SAM-compatible parameters should be last'),\n    medium('ShortCircuitBoolean',\n           u'Prefer the short-circuiting boolean operators \\u0026\\u0026 and || to \\u0026 and |.'),\n    medium('StaticGuardedByInstance',\n           'Writes to static fields should not be guarded by instance locks'),\n    medium('StaticQualifiedUsingExpression',\n           'A static variable or method should be qualified with a class name, not expression'),\n    medium('StreamResourceLeak',\n           'Streams that encapsulate a closeable resource should be closed using try-with-resources'),\n    medium('StringEquality',\n           'String comparison using reference equality instead of value equality'),\n    medium('StringSplitter',\n           'String.split(String) has surprising behavior'),\n    medium('SwigMemoryLeak',\n           'SWIG generated code that can\\'t call a C++ destructor will leak memory'),\n    medium('SynchronizeOnNonFinalField',\n           'Synchronizing on non-final fields is not safe: if the field is ever updated, different threads may end up locking on different objects.'),\n    medium('SystemExitOutsideMain',\n           'Code that contains System.exit() is untestable.'),\n    medium('TestExceptionChecker',\n           'Using @Test(expected=...) is discouraged, since the test will pass if *any* statement in the test method throws the expected exception'),\n    medium('ThreadJoinLoop',\n           'Thread.join needs to be surrounded by a loop until it succeeds, as in Uninterruptibles.joinUninterruptibly.'),\n    medium('ThreadLocalUsage',\n           'ThreadLocals should be stored in static fields'),\n    medium('ThreadPriorityCheck',\n           'Relying on the thread scheduler is discouraged; see Effective Java Item 72 (2nd edition) / 84 (3rd edition).'),\n    medium('ThreeLetterTimeZoneID',\n           'Three-letter time zone identifiers are deprecated, may be ambiguous, and might not do what you intend; the full IANA time zone ID should be used instead.'),\n    medium('ToStringReturnsNull',\n           'An implementation of Object.toString() should never return null.'),\n    medium('TruthAssertExpected',\n           'The actual and expected values appear to be swapped, which results in poor assertion failure messages. The actual value should come first.'),\n    medium('TruthConstantAsserts',\n           'Truth Library assert is called on a constant.'),\n    medium('TruthIncompatibleType',\n           'Argument is not compatible with the subject\\'s type.'),\n    medium('TypeNameShadowing',\n           'Type parameter declaration shadows another named type'),\n    medium('TypeParameterShadowing',\n           'Type parameter declaration overrides another type parameter already declared'),\n    medium('TypeParameterUnusedInFormals',\n           'Declaring a type parameter that is only used in the return type is a misuse of generics: operations on the type parameter are unchecked, it hides unsafe casts at invocations of the method, and it interacts badly with method overload resolution.'),\n    medium('URLEqualsHashCode',\n           'Avoid hash-based containers of java.net.URL--the containers rely on equals() and hashCode(), which cause java.net.URL to make blocking internet connections.'),\n    medium('UndefinedEquals',\n           'Collection, Iterable, Multimap, and Queue do not have well-defined equals behavior'),\n    medium('UnnecessaryDefaultInEnumSwitch',\n           'Switch handles all enum values: an explicit default case is unnecessary and defeats error checking for non-exhaustive switches.'),\n    medium('UnnecessaryParentheses',\n           'Unnecessary use of grouping parentheses'),\n    medium('UnsafeFinalization',\n           'Finalizer may run before native code finishes execution'),\n    medium('UnsafeReflectiveConstructionCast',\n           'Prefer `asSubclass` instead of casting the result of `newInstance`, to detect classes of incorrect type before invoking their constructors.This way, if the class is of the incorrect type,it will throw an exception before invoking its constructor.'),\n    medium('UnsynchronizedOverridesSynchronized',\n           'Unsynchronized method overrides a synchronized method.'),\n    medium('Unused',\n           'Unused.'),\n    medium('UnusedException',\n           'This catch block catches an exception and re-throws another, but swallows the caught exception rather than setting it as a cause. This can make debugging harder.'),\n    medium('UseCorrectAssertInTests',\n           'Java assert is used in test. For testing purposes Assert.* matchers should be used.'),\n    medium('UserHandle',\n           'UserHandle'),\n    medium('UserHandleName',\n           'UserHandleName'),\n    medium('Var',\n           'Non-constant variable missing @Var annotation'),\n    medium('VariableNameSameAsType',\n           'variableName and type with the same name would refer to the static field instead of the class'),\n    medium('WaitNotInLoop',\n           'Because of spurious wakeups, Object.wait() and Condition.await() must always be called in a loop'),\n    medium('WakelockReleasedDangerously',\n           'A wakelock acquired with a timeout may be released by the system before calling `release`, even after checking `isHeld()`. If so, it will throw a RuntimeException. Please wrap in a try/catch block.'),\n    java_medium('Found raw type',\n                [r'.*\\.java:.*: warning: \\[rawtypes\\] found raw type']),\n    java_medium('Redundant cast',\n                [r'.*\\.java:.*: warning: \\[cast\\] redundant cast to']),\n    java_medium('Static method should be qualified',\n                [r'.*\\.java:.*: warning: \\[static\\] static method should be qualified']),\n    medium('AbstractInner'),\n    medium('BothPackageInfoAndHtml'),\n    medium('BuilderSetStyle'),\n    medium('CallbackName'),\n    medium('ExecutorRegistration'),\n    medium('HiddenTypeParameter'),\n    medium('JavaApiUsedByMainlineModule'),\n    medium('ListenerLast'),\n    medium('MinMaxConstant'),\n    medium('MissingBuildMethod'),\n    medium('MissingGetterMatchingBuilder'),\n    medium('NoByteOrShort'),\n    medium('OverlappingConstants'),\n    medium('SetterReturnsThis'),\n    medium('StaticFinalBuilder'),\n    medium('StreamFiles'),\n    medium('Typo'),\n    medium('UseIcu'),\n    medium('fallthrough'),\n    medium('overrides'),\n    medium('serial'),\n    medium('try'),\n    high('AndroidInjectionBeforeSuper',\n         'AndroidInjection.inject() should always be invoked before calling super.lifecycleMethod()'),\n    high('AndroidJdkLibsChecker',\n         'Use of class, field, or method that is not compatible with legacy Android devices'),\n    high('ArrayEquals',\n         'Reference equality used to compare arrays'),\n    high('ArrayFillIncompatibleType',\n         'Arrays.fill(Object[], Object) called with incompatible types.'),\n    high('ArrayHashCode',\n         'hashcode method on array does not hash array contents'),\n    high('ArrayReturn',\n         'ArrayReturn'),\n    high('ArrayToString',\n         'Calling toString on an array does not provide useful information'),\n    high('ArraysAsListPrimitiveArray',\n         'Arrays.asList does not autobox primitive arrays, as one might expect.'),\n    high('AssistedInjectAndInjectOnSameConstructor',\n         '@AssistedInject and @Inject cannot be used on the same constructor.'),\n    high('AsyncCallableReturnsNull',\n         'AsyncCallable should not return a null Future, only a Future whose result is null.'),\n    high('AsyncFunctionReturnsNull',\n         'AsyncFunction should not return a null Future, only a Future whose result is null.'),\n    high('AutoFactoryAtInject',\n         '@AutoFactory and @Inject should not be used in the same type.'),\n    high('AutoValueConstructorOrderChecker',\n         'Arguments to AutoValue constructor are in the wrong order'),\n    high('BadShiftAmount',\n         'Shift by an amount that is out of range'),\n    high('BundleDeserializationCast',\n         'Object serialized in Bundle may have been flattened to base type.'),\n    high('ChainingConstructorIgnoresParameter',\n         'The called constructor accepts a parameter with the same name and type as one of its caller\\'s parameters, but its caller doesn\\'t pass that parameter to it.  It\\'s likely that it was intended to.'),\n    high('CheckReturnValue',\n         'Ignored return value of method that is annotated with @CheckReturnValue'),\n    high('ClassName',\n         'The source file name should match the name of the top-level class it contains'),\n    high('CollectionIncompatibleType',\n         'Incompatible type as argument to Object-accepting Java collections method'),\n    high('ComparableType',\n         u'Implementing \\'Comparable\\u003cT>\\' where T is not compatible with the implementing class.'),\n    high('ComparingThisWithNull',\n         'this == null is always false, this != null is always true'),\n    high('ComparisonContractViolated',\n         'This comparison method violates the contract'),\n    high('ComparisonOutOfRange',\n         'Comparison to value that is out of range for the compared type'),\n    high('CompatibleWithAnnotationMisuse',\n         '@CompatibleWith\\'s value is not a type argument.'),\n    high('CompileTimeConstant',\n         'Non-compile-time constant expression passed to parameter with @CompileTimeConstant type annotation.'),\n    high('ComplexBooleanConstant',\n         'Non-trivial compile time constant boolean expressions shouldn\\'t be used.'),\n    high('ConditionalExpressionNumericPromotion',\n         'A conditional expression with numeric operands of differing types will perform binary numeric promotion of the operands; when these operands are of reference types, the expression\\'s result may not be of the expected type.'),\n    high('ConstantOverflow',\n         'Compile-time constant expression overflows'),\n    high('DaggerProvidesNull',\n         'Dagger @Provides methods may not return null unless annotated with @Nullable'),\n    high('DeadException',\n         'Exception created but not thrown'),\n    high('DeadThread',\n         'Thread created but not started'),\n    java_high('Deprecated item is not annotated with @Deprecated',\n              [r\".*\\.java:.*: warning: \\[.*\\] .+ is not annotated with @Deprecated$\"]),\n    high('DivZero',\n         'Division by integer literal zero'),\n    high('DoNotCall',\n         'This method should not be called.'),\n    high('EmptyIf',\n         'Empty statement after if'),\n    high('EqualsNaN',\n         '== NaN always returns false; use the isNaN methods instead'),\n    high('EqualsReference',\n         '== must be used in equals method to check equality to itself or an infinite loop will occur.'),\n    high('EqualsWrongThing',\n         'Comparing different pairs of fields/getters in an equals implementation is probably a mistake.'),\n    high('ForOverride',\n         'Method annotated @ForOverride must be protected or package-private and only invoked from declaring class, or from an override of the method'),\n    high('FormatString',\n         'Invalid printf-style format string'),\n    high('FormatStringAnnotation',\n         'Invalid format string passed to formatting method.'),\n    high('FunctionalInterfaceMethodChanged',\n         'Casting a lambda to this @FunctionalInterface can cause a behavior change from casting to a functional superinterface, which is surprising to users.  Prefer decorator methods to this surprising behavior.'),\n    high('FuturesGetCheckedIllegalExceptionType',\n         'Futures.getChecked requires a checked exception type with a standard constructor.'),\n    high('FuzzyEqualsShouldNotBeUsedInEqualsMethod',\n         'DoubleMath.fuzzyEquals should never be used in an Object.equals() method'),\n    high('GetClassOnAnnotation',\n         'Calling getClass() on an annotation may return a proxy class'),\n    high('GetClassOnClass',\n         'Calling getClass() on an object of type Class returns the Class object for java.lang.Class; you probably meant to operate on the object directly'),\n    high('GuardedBy',\n         'Checks for unguarded accesses to fields and methods with @GuardedBy annotations'),\n    high('GuiceAssistedInjectScoping',\n         'Scope annotation on implementation class of AssistedInject factory is not allowed'),\n    high('GuiceAssistedParameters',\n         'A constructor cannot have two @Assisted parameters of the same type unless they are disambiguated with named @Assisted annotations.'),\n    high('GuiceInjectOnFinalField',\n         'Although Guice allows injecting final fields, doing so is disallowed because the injected value may not be visible to other threads.'),\n    high('HashtableContains',\n         'contains() is a legacy method that is equivalent to containsValue()'),\n    high('IdentityBinaryExpression',\n         'A binary expression where both operands are the same is usually incorrect.'),\n    high('Immutable',\n         'Type declaration annotated with @Immutable is not immutable'),\n    high('ImmutableModification',\n         'Modifying an immutable collection is guaranteed to throw an exception and leave the collection unmodified'),\n    high('IncompatibleArgumentType',\n         'Passing argument to a generic method with an incompatible type.'),\n    high('IndexOfChar',\n         'The first argument to indexOf is a Unicode code point, and the second is the index to start the search from'),\n    high('InexactVarargsConditional',\n         'Conditional expression in varargs call contains array and non-array arguments'),\n    high('InfiniteRecursion',\n         'This method always recurses, and will cause a StackOverflowError'),\n    high('InjectInvalidTargetingOnScopingAnnotation',\n         'A scoping annotation\\'s Target should include TYPE and METHOD.'),\n    high('InjectMoreThanOneQualifier',\n         'Using more than one qualifier annotation on the same element is not allowed.'),\n    high('InjectMoreThanOneScopeAnnotationOnClass',\n         'A class can be annotated with at most one scope annotation.'),\n    high('InjectOnMemberAndConstructor',\n         'Members shouldn\\'t be annotated with @Inject if constructor is already annotated @Inject'),\n    high('InjectScopeAnnotationOnInterfaceOrAbstractClass',\n         'Scope annotation on an interface or abstact class is not allowed'),\n    high('InjectScopeOrQualifierAnnotationRetention',\n         'Scoping and qualifier annotations must have runtime retention.'),\n    high('InjectedConstructorAnnotations',\n         'Injected constructors cannot be optional nor have binding annotations'),\n    high('InsecureCryptoUsage',\n         'A standard cryptographic operation is used in a mode that is prone to vulnerabilities'),\n    high('InvalidPatternSyntax',\n         'Invalid syntax used for a regular expression'),\n    high('InvalidTimeZoneID',\n         'Invalid time zone identifier. TimeZone.getTimeZone(String) will silently return GMT instead of the time zone you intended.'),\n    high('IsInstanceOfClass',\n         'The argument to Class#isInstance(Object) should not be a Class'),\n    high('IsLoggableTagLength',\n         'Log tag too long, cannot exceed 23 characters.'),\n    high('IterablePathParameter',\n         u'Path implements Iterable\\u003cPath>; prefer Collection\\u003cPath> for clarity'),\n    high('JMockTestWithoutRunWithOrRuleAnnotation',\n         'jMock tests must have a @RunWith(JMock.class) annotation, or the Mockery field must have a @Rule JUnit annotation'),\n    high('JUnit3TestNotRun',\n         'Test method will not be run; please correct method signature (Should be public, non-static, and method name should begin with \"test\").'),\n    high('JUnit4ClassAnnotationNonStatic',\n         'This method should be static'),\n    high('JUnit4SetUpNotRun',\n         'setUp() method will not be run; please add JUnit\\'s @Before annotation'),\n    high('JUnit4TearDownNotRun',\n         'tearDown() method will not be run; please add JUnit\\'s @After annotation'),\n    high('JUnit4TestNotRun',\n         'This looks like a test method but is not run; please add @Test and @Ignore, or, if this is a helper method, reduce its visibility.'),\n    high('JUnitAssertSameCheck',\n         'An object is tested for reference equality to itself using JUnit library.'),\n    high('Java7ApiChecker',\n         'Use of class, field, or method that is not compatible with JDK 7'),\n    high('JavaxInjectOnAbstractMethod',\n         'Abstract and default methods are not injectable with javax.inject.Inject'),\n    high('JavaxInjectOnFinalField',\n         '@javax.inject.Inject cannot be put on a final field.'),\n    high('LiteByteStringUtf8',\n         'This pattern will silently corrupt certain byte sequences from the serialized protocol message. Use ByteString or byte[] directly'),\n    high('LockMethodChecker',\n         'This method does not acquire the locks specified by its @LockMethod annotation'),\n    high('LongLiteralLowerCaseSuffix',\n         'Prefer \\'L\\' to \\'l\\' for the suffix to long literals'),\n    high('LoopConditionChecker',\n         'Loop condition is never modified in loop body.'),\n    high('MathRoundIntLong',\n         'Math.round(Integer) results in truncation'),\n    high('MislabeledAndroidString',\n         'Certain resources in `android.R.string` have names that do not match their content'),\n    high('MissingSuperCall',\n         'Overriding method is missing a call to overridden super method'),\n    high('MissingTestCall',\n         'A terminating method call is required for a test helper to have any effect.'),\n    high('MisusedWeekYear',\n         'Use of \"YYYY\" (week year) in a date pattern without \"ww\" (week in year). You probably meant to use \"yyyy\" (year) instead.'),\n    high('MockitoCast',\n         'A bug in Mockito will cause this test to fail at runtime with a ClassCastException'),\n    high('MockitoUsage',\n         'Missing method call for verify(mock) here'),\n    high('ModifyingCollectionWithItself',\n         'Using a collection function with itself as the argument.'),\n    high('MoreThanOneInjectableConstructor',\n         'This class has more than one @Inject-annotated constructor. Please remove the @Inject annotation from all but one of them.'),\n    high('MustBeClosedChecker',\n         'The result of this method must be closed.'),\n    high('NCopiesOfChar',\n         'The first argument to nCopies is the number of copies, and the second is the item to copy'),\n    high('NoAllocation',\n         '@NoAllocation was specified on this method, but something was found that would trigger an allocation'),\n    high('NonCanonicalStaticImport',\n         'Static import of type uses non-canonical name'),\n    high('NonFinalCompileTimeConstant',\n         '@CompileTimeConstant parameters should be final or effectively final'),\n    high('NonRuntimeAnnotation',\n         'Calling getAnnotation on an annotation that is not retained at runtime.'),\n    high('NullTernary',\n         'This conditional expression may evaluate to null, which will result in an NPE when the result is unboxed.'),\n    high('NumericEquality',\n         'Numeric comparison using reference equality instead of value equality'),\n    high('OptionalEquality',\n         'Comparison using reference equality instead of value equality'),\n    high('OverlappingQualifierAndScopeAnnotation',\n         'Annotations cannot be both Scope annotations and Qualifier annotations: this causes confusion when trying to use them.'),\n    high('OverridesJavaxInjectableMethod',\n         'This method is not annotated with @Inject, but it overrides a method that is  annotated with @javax.inject.Inject. The method will not be Injected.'),\n    high('PackageInfo',\n         'Declaring types inside package-info.java files is very bad form'),\n    high('ParameterPackage',\n         'Method parameter has wrong package'),\n    high('ParcelableCreator',\n         'Detects classes which implement Parcelable but don\\'t have CREATOR'),\n    high('PreconditionsCheckNotNull',\n         'Literal passed as first argument to Preconditions.checkNotNull() can never be null'),\n    high('PreconditionsCheckNotNullPrimitive',\n         'First argument to `Preconditions.checkNotNull()` is a primitive rather than an object reference'),\n    high('PredicateIncompatibleType',\n         'Using ::equals or ::isInstance as an incompatible Predicate; the predicate will always return false'),\n    high('PrivateSecurityContractProtoAccess',\n         'Access to a private protocol buffer field is forbidden. This protocol buffer carries a security contract, and can only be created using an approved library. Direct access to the fields is forbidden.'),\n    high('ProtoFieldNullComparison',\n         'Protobuf fields cannot be null.'),\n    high('ProtoStringFieldReferenceEquality',\n         'Comparing protobuf fields of type String using reference equality'),\n    high('ProtocolBufferOrdinal',\n         'To get the tag number of a protocol buffer enum, use getNumber() instead.'),\n    high('ProvidesMethodOutsideOfModule',\n         '@Provides methods need to be declared in a Module to have any effect.'),\n    high('RandomCast',\n         'Casting a random number in the range [0.0, 1.0) to an integer or long always results in 0.'),\n    high('RandomModInteger',\n         'Use Random.nextInt(int).  Random.nextInt() % n can have negative results'),\n    high('RectIntersectReturnValueIgnored',\n         'Return value of android.graphics.Rect.intersect() must be checked'),\n    high('RestrictTo',\n         'Use of method or class annotated with @RestrictTo'),\n    high('RestrictedApiChecker',\n         ' Check for non-whitelisted callers to RestrictedApiChecker.'),\n    high('ReturnValueIgnored',\n         'Return value of this method must be used'),\n    high('SelfAssignment',\n         'Variable assigned to itself'),\n    high('SelfComparison',\n         'An object is compared to itself'),\n    high('SelfEquals',\n         'Testing an object for equality with itself will always be true.'),\n    high('ShouldHaveEvenArgs',\n         'This method must be called with an even number of arguments.'),\n    high('SizeGreaterThanOrEqualsZero',\n         'Comparison of a size >= 0 is always true, did you intend to check for non-emptiness?'),\n    high('StaticOrDefaultInterfaceMethod',\n         'Static and default interface methods are not natively supported on older Android devices. '),\n    high('StreamToString',\n         'Calling toString on a Stream does not provide useful information'),\n    high('StringBuilderInitWithChar',\n         'StringBuilder does not have a char constructor; this invokes the int constructor.'),\n    high('SubstringOfZero',\n         'String.substring(0) returns the original String'),\n    high('SuppressWarningsDeprecated',\n         'Suppressing \"deprecated\" is probably a typo for \"deprecation\"'),\n    high('ThrowIfUncheckedKnownChecked',\n         'throwIfUnchecked(knownCheckedException) is a no-op.'),\n    high('ThrowNull',\n         'Throwing \\'null\\' always results in a NullPointerException being thrown.'),\n    high('TruthSelfEquals',\n         'isEqualTo should not be used to test an object for equality with itself; the assertion will never fail.'),\n    high('TryFailThrowable',\n         'Catching Throwable/Error masks failures from fail() or assert*() in the try block'),\n    high('TypeParameterQualifier',\n         'Type parameter used as type qualifier'),\n    high('UnlockMethod',\n         'This method does not acquire the locks specified by its @UnlockMethod annotation'),\n    high('UnnecessaryTypeArgument',\n         'Non-generic methods should not be invoked with type arguments'),\n    high('UnusedAnonymousClass',\n         'Instance created but never used'),\n    high('UnusedCollectionModifiedInPlace',\n         'Collection is modified in place, but the result is not used'),\n    high('VarTypeName',\n         '`var` should not be used as a type name.'),\n\n    # Other javac tool warnings\n    java_medium('addNdkApiCoverage failed to getPackage',\n                [r\".*: warning: addNdkApiCoverage failed to getPackage\"]),\n    java_medium('bad path element',\n                [r\".*: warning: \\[path\\] bad path element .*\\.jar\"]),\n    java_medium('Supported version from annotation processor',\n                [r\".*: warning: Supported source version .+ from annotation processor\"]),\n    java_medium('Schema export directory is not provided',\n                [r\".*\\.(java|kt):.*: warning: Schema export directory is not provided\"]),\n]\n\ncompile_patterns(warn_patterns)\n"
  },
  {
    "path": "tools/warn/make_warn_patterns.py",
    "content": "# python3\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Warning patterns for build make tools.\"\"\"\n\n# pylint:disable=relative-beyond-top-level\nfrom .cpp_warn_patterns import compile_patterns\nfrom .severity import Severity\n\nwarn_patterns = [\n    # pylint does not recognize g-inconsistent-quotes\n    # pylint:disable=line-too-long,bad-option-value,g-inconsistent-quotes\n    {'category': 'make', 'severity': Severity.MEDIUM,\n     'description': 'make: overriding commands/ignoring old commands',\n     'patterns': [r\".*: warning: overriding commands for target .+\",\n                  r\".*: warning: ignoring old commands for target .+\"]},\n    {'category': 'make', 'severity': Severity.HIGH,\n     'description': 'make: LOCAL_CLANG is false',\n     'patterns': [r\".*: warning: LOCAL_CLANG is set to false\"]},\n    {'category': 'make', 'severity': Severity.HIGH,\n     'description': 'SDK App using platform shared library',\n     'patterns': [r\".*: warning: .+ \\(.*app:sdk.*\\) should not link to .+ \\(native:platform\\)\"]},\n    {'category': 'make', 'severity': Severity.HIGH,\n     'description': 'System module linking to a vendor module',\n     'patterns': [r\".*: warning: .+ \\(.+\\) should not link to .+ \\(partition:.+\\)\"]},\n    {'category': 'make', 'severity': Severity.HIGH,\n     'description': 'make: lstat file does not exist',\n     'patterns': [r\".*: warning: lstat .+: file does not exist\"]},\n    {'category': 'make', 'severity': Severity.MEDIUM,\n     'description': 'Invalid SDK/NDK linking',\n     'patterns': [r\".*: warning: .+ \\(.+\\) should not link to .+ \\(.+\\)\"]},\n    {'category': 'make', 'severity': Severity.MEDIUM,\n     'description': 'Duplicate header copy',\n     'patterns': [r\".*: warning: Duplicate header copy: .+\"]},\n    {'category': 'FindEmulator', 'severity': Severity.HARMLESS,\n     'description': 'FindEmulator: No such file or directory',\n     'patterns': [r\".*: warning: FindEmulator: .* No such file or directory\"]},\n    {'category': 'make', 'severity': Severity.HARMLESS,\n     'description': 'make: unknown installed file',\n     'patterns': [r\".*: warning: .*_tests: Unknown installed file for module\"]},\n    {'category': 'make', 'severity': Severity.HARMLESS,\n     'description': 'unusual tags debug eng',\n     'patterns': [r\".*: warning: .*: unusual tags debug eng\"]},\n    {'category': 'make', 'severity': Severity.MEDIUM,\n     'description': 'make: please convert to soong',\n     'patterns': [r\".*: warning: .* has been deprecated. Please convert to Soong.\"]},\n    {'category': 'make', 'severity': Severity.MEDIUM,\n     'description': 'make: deprecated macros',\n     'patterns': [r\".*\\.mk:.* warning:.* [A-Z_]+ (is|has been) deprecated.\"]},\n    {'category': 'make', 'severity': Severity.MEDIUM,\n     'description': 'make: other Android.mk warnings',\n     'patterns': [r\".*/Android.mk:.*: warning: .+\"]},\n]\n\n\ncompile_patterns(warn_patterns)\n"
  },
  {
    "path": "tools/warn/other_warn_patterns.py",
    "content": "# python3\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Warning patterns from other tools.\"\"\"\n\n# No need of doc strings for trivial small functions.\n# pylint:disable=missing-function-docstring\n\n# pylint:disable=relative-beyond-top-level\nfrom .cpp_warn_patterns import compile_patterns\nfrom .severity import Severity\n\n\ndef warn(name, severity, description, pattern_list):\n  return {\n      'category': name,\n      'severity': severity,\n      'description': name + ': ' + description,\n      'patterns': pattern_list\n  }\n\n\ndef aapt(description, pattern_list):\n  return warn('aapt', Severity.MEDIUM, description, pattern_list)\n\n\ndef misc(description, pattern_list):\n  return warn('logtags', Severity.LOW, description, pattern_list)\n\n\ndef asm(description, pattern_list):\n  return warn('asm', Severity.MEDIUM, description, pattern_list)\n\n\ndef kotlin(description, pattern):\n  return warn('Kotlin', Severity.MEDIUM, description,\n              [r'.*\\.kt:.*: warning: ' + pattern])\n\n\ndef yacc(description, pattern_list):\n  return warn('yacc', Severity.MEDIUM, description, pattern_list)\n\n\ndef rust(severity, description, pattern):\n  return warn('Rust', severity, description,\n              [r'.*\\.rs:.*: warning: ' + pattern])\n\n\nwarn_patterns = [\n    # pylint does not recognize g-inconsistent-quotes\n    # pylint:disable=line-too-long,bad-option-value,g-inconsistent-quotes\n    # aapt warnings\n    aapt('No comment for public symbol',\n         [r\".*: warning: No comment for public symbol .+\"]),\n    aapt('No default translation',\n         [r\".*: warning: string '.+' has no default translation in .*\"]),\n    aapt('Missing default or required localization',\n         [r\".*: warning: \\*\\*\\*\\* string '.+' has no default or required localization for '.+' in .+\"]),\n    aapt('String marked untranslatable, but translation exists',\n         [r\".*: warning: string '.+' in .* marked untranslatable but exists in locale '??_??'\"]),\n    aapt('empty span in string',\n         [r\".*: warning: empty '.+' span found in text '.+\"]),\n    # misc warnings\n    misc('Duplicate logtag',\n         [r\".*: warning: tag \\\".+\\\" \\(.+\\) duplicated in .+\"]),\n    # Assembler warnings\n    asm('ASM value size does not match register size',\n        [r\".*: warning: value size does not match register size specified by the constraint and modifier\"]),\n    asm('IT instruction is deprecated',\n        [r\".*: warning: applying IT instruction .* is deprecated\"]),\n    asm('section flags ignored',\n        [r\".*: warning: section flags ignored on section redeclaration\"]),\n    asm('setjmp/longjmp/vfork changed binding',\n        [r\".*: warning: .*(setjmp|longjmp|vfork) changed binding to .*\"]),\n    # NDK warnings\n    {'category': 'NDK', 'severity': Severity.HIGH,\n     'description': 'NDK: Generate guard with empty availability, obsoleted',\n     'patterns': [r\".*: warning: .* generate guard with empty availability: obsoleted =\"]},\n    # Protoc warnings\n    {'category': 'Protoc', 'severity': Severity.MEDIUM,\n     'description': 'Proto: Enum name collision after strip',\n     'patterns': [r\".*: warning: Enum .* has the same name .* ignore case and strip\"]},\n    {'category': 'Protoc', 'severity': Severity.MEDIUM,\n     'description': 'Proto: Import not used',\n     'patterns': [r\".*: warning: Import .*/.*\\.proto but not used.$\"]},\n    # Kotlin warnings\n    kotlin('never used parameter or variable', '.+ \\'.*\\' is never used'),\n    kotlin('multiple labels', '.+ more than one label .+ in this scope'),\n    kotlin('type mismatch', 'type mismatch: '),\n    kotlin('is always true', '.+ is always \\'true\\''),\n    kotlin('no effect', '.+ annotation has no effect for '),\n    kotlin('no cast needed', 'no cast needed'),\n    kotlin('accessor not generated', 'an accessor will not be generated '),\n    kotlin('initializer is redundant', '.* initializer is redundant$'),\n    kotlin('elvis operator always returns ...',\n           'elvis operator (?:) always returns .+'),\n    kotlin('shadowed name', 'name shadowed: .+'),\n    kotlin('unchecked cast', 'unchecked cast: .* to .*$'),\n    kotlin('unreachable code', 'unreachable code'),\n    kotlin('unnecessary assertion', 'unnecessary .+ assertion .+'),\n    kotlin('unnecessary safe call on a non-null receiver',\n           'unnecessary safe call on a non-null receiver'),\n    kotlin('Deprecated in Java',\n           '\\'.*\\' is deprecated. Deprecated in Java'),\n    kotlin('Replacing Handler for Executor',\n           '.+ Replacing Handler for Executor in '),\n    kotlin('library has Kotlin runtime',\n           '.+ has Kotlin runtime (bundled|library)'),\n    warn('Kotlin', Severity.MEDIUM, 'bundled Kotlin runtime',\n         ['.*warning: .+ (has|have the) Kotlin (runtime|Runtime library) bundled']),\n    kotlin('other warnings', '.+'),  # catch all other Kotlin warnings\n    # Yacc warnings\n    yacc('deprecate directive',\n         [r\".*\\.yy?:.*: warning: deprecated directive: \"]),\n    yacc('reduce/reduce conflicts',\n         [r\".*\\.yy?: warning: .+ reduce/reduce conflicts \"]),\n    yacc('shift/reduce conflicts',\n         [r\".*\\.yy?: warning: .+ shift/reduce conflicts \"]),\n    {'category': 'yacc', 'severity': Severity.SKIP,\n     'description': 'yacc: fix-its can be applied',\n     'patterns': [r\".*\\.yy?: warning: fix-its can be applied.\"]},\n    # Rust warnings\n    rust(Severity.HIGH, 'Does not derive Copy', '.+ does not derive Copy'),\n    rust(Severity.MEDIUM, '... are deprecated',\n         ('(.+ are deprecated$|' +\n          'use of deprecated item .* (use .* instead|is now preferred))')),\n    rust(Severity.MEDIUM, 'never used', '.* is never used:'),\n    rust(Severity.MEDIUM, 'unused import', 'unused import: '),\n    rust(Severity.MEDIUM, 'unnecessary attribute',\n         '.+ no longer requires an attribute'),\n    rust(Severity.MEDIUM, 'unnecessary parentheses',\n         'unnecessary parentheses around'),\n    # Catch all RenderScript warnings\n    {'category': 'RenderScript', 'severity': Severity.LOW,\n     'description': 'RenderScript warnings',\n     'patterns': [r'.*\\.rscript:.*: warning: ']},\n    {'category': 'RenderScript', 'severity': Severity.HIGH,\n     'description': 'RenderScript is deprecated',\n     'patterns': [r'.*: warning: Renderscript is deprecated:.+']},\n    # Broken/partial warning messages will be skipped.\n    {'category': 'Misc', 'severity': Severity.SKIP,\n     'description': 'skip, ,',\n     'patterns': [r\".*: warning: ,?$\"]},\n    {'category': 'C/C++', 'severity': Severity.SKIP,\n     'description': 'skip, In file included from ...',\n     'patterns': [r\".*: warning: In file included from .+,\"]},\n    # catch-all for warnings this script doesn't know about yet\n    {'category': 'C/C++', 'severity': Severity.UNMATCHED,\n     'description': 'Unclassified/unrecognized warnings',\n     'patterns': [r\".*: warning: .+\"]},\n]\n\n\ncompile_patterns(warn_patterns)\n"
  },
  {
    "path": "tools/warn/severity.py",
    "content": "# python3\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Clang_Tidy_Warn Severity class definition.\n\nThis file stores definition for class Severity that is used in warn_patterns.\n\"\"\"\n\n\n# pylint:disable=too-few-public-methods\nclass SeverityInfo:\n  \"\"\"Class of Severity Info, part of a Severity object.\"\"\"\n\n  def __init__(self, value, color, column_header, header):\n    self.value = value\n    self.color = color\n    self.column_header = column_header\n    self.header = header\n\n\n# pylint:disable=too-few-public-methods\nclass Severity:\n  \"\"\"Class of Severity levels where each level is a SeverityInfo.\"\"\"\n\n  # SEVERITY_UNKNOWN should never occur since every warn_pattern listed has\n  # a specified severity. It exists for protobuf, the other values must\n  # map to non-zero values (since 0 is reserved for a default UNKNOWN), but\n  # logic in clang_tidy_warn.py assumes severity level values are consecutive\n  # ints starting with 0.\n  SEVERITY_UNKNOWN = SeverityInfo(0, 'blueviolet', 'Unknown',\n                                  'Unknown-severity warnings)')\n  FIXMENOW = SeverityInfo(1, 'fuschia', 'FixNow',\n                          'Critical warnings, fix me now')\n  HIGH = SeverityInfo(2, 'red', 'High', 'High severity warnings')\n  MEDIUM = SeverityInfo(3, 'orange', 'Medium', 'Medium severity warnings')\n  LOW = SeverityInfo(4, 'yellow', 'Low', 'Low severity warnings')\n  ANALYZER = SeverityInfo(5, 'hotpink', 'Analyzer', 'Clang-Analyzer warnings')\n  TIDY = SeverityInfo(6, 'peachpuff', 'Tidy', 'Clang-Tidy warnings')\n  HARMLESS = SeverityInfo(7, 'limegreen', 'Harmless', 'Harmless warnings')\n  UNMATCHED = SeverityInfo(8, 'lightblue', 'Unmatched', 'Unmatched warnings')\n  SKIP = SeverityInfo(9, 'grey', 'Unhandled', 'Unhandled warnings')\n\n  levels = [\n      SEVERITY_UNKNOWN, FIXMENOW, HIGH, MEDIUM, LOW, ANALYZER, TIDY, HARMLESS,\n      UNMATCHED, SKIP\n  ]\n  # HTML relies on ordering by value. Sort here to ensure that this is proper\n  levels = sorted(levels, key=lambda severity: severity.value)\n"
  },
  {
    "path": "tools/warn/tidy_warn_patterns.py",
    "content": "# python3\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Warning patterns for clang-tidy.\"\"\"\n\n# No need of doc strings for trivial small functions.\n# pylint:disable=missing-function-docstring\n\n# pylint:disable=relative-beyond-top-level\nfrom .cpp_warn_patterns import compile_patterns\nfrom .severity import Severity\n\n\ndef tidy_warn(description, patterns):\n  return {\n      'category': 'C/C++',\n      'severity': Severity.TIDY,\n      'description': 'clang-tidy ' + description,\n      'patterns': patterns,\n  }\n\n\ndef tidy_warn_pattern(description, pattern):\n  return tidy_warn(description, [r'.*: .+\\[' + pattern + r'\\]$'])\n\n\ndef simple_tidy_warn_pattern(description):\n  return tidy_warn_pattern(description, description)\n\n\ndef group_tidy_warn_pattern(description):\n  return tidy_warn_pattern(description, description + r'-.+')\n\n\ndef analyzer_high(description, patterns):\n  return {\n      'category': 'C/C++',\n      'severity': Severity.HIGH,\n      'description': description,\n      'patterns': patterns\n  }\n\n\ndef analyzer_high_check(check):\n  return analyzer_high(check, [r'.*: .+\\[' + check + r'\\]$'])\n\n\ndef analyzer_group_high(check):\n  return analyzer_high(check, [r'.*: .+\\[' + check + r'.+\\]$'])\n\n\ndef analyzer_warn(description, patterns):\n  return {\n      'category': 'C/C++',\n      'severity': Severity.ANALYZER,\n      'description': description,\n      'patterns': patterns\n  }\n\n\ndef analyzer_warn_check(check):\n  return analyzer_warn(check, [r'.*: .+\\[' + check + r'\\]$'])\n\n\ndef analyzer_group_check(check):\n  return analyzer_warn(check, [r'.*: .+\\[' + check + r'.+\\]$'])\n\n\nwarn_patterns = [\n    # pylint does not recognize g-inconsistent-quotes\n    # pylint:disable=line-too-long,bad-option-value,g-inconsistent-quotes\n    group_tidy_warn_pattern('altera'),\n    group_tidy_warn_pattern('android'),\n    simple_tidy_warn_pattern('abseil-string-find-startswith'),\n    simple_tidy_warn_pattern('bugprone-argument-comment'),\n    simple_tidy_warn_pattern('bugprone-branch-clone'),\n    simple_tidy_warn_pattern('bugprone-copy-constructor-init'),\n    simple_tidy_warn_pattern('bugprone-fold-init-type'),\n    simple_tidy_warn_pattern('bugprone-forward-declaration-namespace'),\n    simple_tidy_warn_pattern('bugprone-forwarding-reference-overload'),\n    simple_tidy_warn_pattern('bugprone-inaccurate-erase'),\n    simple_tidy_warn_pattern('bugprone-incorrect-roundings'),\n    simple_tidy_warn_pattern('bugprone-integer-division'),\n    simple_tidy_warn_pattern('bugprone-lambda-function-name'),\n    simple_tidy_warn_pattern('bugprone-macro-parentheses'),\n    simple_tidy_warn_pattern('bugprone-misplaced-widening-cast'),\n    simple_tidy_warn_pattern('bugprone-move-forwarding-reference'),\n    simple_tidy_warn_pattern('bugprone-parent-virtual-call'),\n    simple_tidy_warn_pattern('bugprone-posix-return'),\n    simple_tidy_warn_pattern('bugprone-sizeof-container'),\n    simple_tidy_warn_pattern('bugprone-sizeof-expression'),\n    simple_tidy_warn_pattern('bugprone-string-constructor'),\n    simple_tidy_warn_pattern('bugprone-string-integer-assignment'),\n    simple_tidy_warn_pattern('bugprone-suspicious-enum-usage'),\n    simple_tidy_warn_pattern('bugprone-suspicious-missing-comma'),\n    simple_tidy_warn_pattern('bugprone-suspicious-string-compare'),\n    simple_tidy_warn_pattern('bugprone-suspicious-semicolon'),\n    simple_tidy_warn_pattern('bugprone-terminating-continue'),\n    simple_tidy_warn_pattern('bugprone-too-small-loop-variable'),\n    simple_tidy_warn_pattern('bugprone-undefined-memory-manipulation'),\n    simple_tidy_warn_pattern('bugprone-unhandled-self-assignment'),\n    simple_tidy_warn_pattern('bugprone-unused-raii'),\n    simple_tidy_warn_pattern('bugprone-unused-return-value'),\n    simple_tidy_warn_pattern('bugprone-use-after-move'),\n    group_tidy_warn_pattern('bugprone'),\n    simple_tidy_warn_pattern('cert-dcl16-c'),\n    simple_tidy_warn_pattern('cert-dcl21-cpp'),\n    simple_tidy_warn_pattern('cert-dcl50-cpp'),\n    simple_tidy_warn_pattern('cert-dcl54-cpp'),\n    simple_tidy_warn_pattern('cert-dcl59-cpp'),\n    simple_tidy_warn_pattern('cert-env33-c'),\n    simple_tidy_warn_pattern('cert-err34-c'),\n    simple_tidy_warn_pattern('cert-err52-cpp'),\n    simple_tidy_warn_pattern('cert-msc30-c'),\n    simple_tidy_warn_pattern('cert-msc50-cpp'),\n    simple_tidy_warn_pattern('cert-oop54-cpp'),\n    group_tidy_warn_pattern('cert'),\n    group_tidy_warn_pattern('clang-diagnostic'),\n    group_tidy_warn_pattern('concurrency'),\n    group_tidy_warn_pattern('cppcoreguidelines'),\n    group_tidy_warn_pattern('fuchsia'),\n    simple_tidy_warn_pattern('google-default-arguments'),\n    simple_tidy_warn_pattern('google-runtime-int'),\n    simple_tidy_warn_pattern('google-runtime-operator'),\n    simple_tidy_warn_pattern('google-runtime-references'),\n    group_tidy_warn_pattern('google-build'),\n    group_tidy_warn_pattern('google-explicit'),\n    group_tidy_warn_pattern('google-redability'),\n    group_tidy_warn_pattern('google-global'),\n    group_tidy_warn_pattern('google-redability'),\n    group_tidy_warn_pattern('google-redability'),\n    group_tidy_warn_pattern('google'),\n    simple_tidy_warn_pattern('hicpp-explicit-conversions'),\n    simple_tidy_warn_pattern('hicpp-function-size'),\n    simple_tidy_warn_pattern('hicpp-invalid-access-moved'),\n    simple_tidy_warn_pattern('hicpp-member-init'),\n    simple_tidy_warn_pattern('hicpp-delete-operators'),\n    simple_tidy_warn_pattern('hicpp-special-member-functions'),\n    simple_tidy_warn_pattern('hicpp-use-equals-default'),\n    simple_tidy_warn_pattern('hicpp-use-equals-delete'),\n    simple_tidy_warn_pattern('hicpp-no-assembler'),\n    simple_tidy_warn_pattern('hicpp-noexcept-move'),\n    simple_tidy_warn_pattern('hicpp-use-override'),\n    group_tidy_warn_pattern('hicpp'),\n    group_tidy_warn_pattern('llvm'),\n    group_tidy_warn_pattern('llvmlibc'),\n    group_tidy_warn_pattern('misc'),\n    group_tidy_warn_pattern('modernize'),\n    simple_tidy_warn_pattern('performance-faster-string-find'),\n    simple_tidy_warn_pattern('performance-for-range-copy'),\n    simple_tidy_warn_pattern('performance-implicit-cast-in-loop'),\n    simple_tidy_warn_pattern('performance-inefficient-string-concatenation'),\n    simple_tidy_warn_pattern('performance-type-promotion-in-math-fn'),\n    simple_tidy_warn_pattern('performance-unnecessary-copy-initialization'),\n    simple_tidy_warn_pattern('performance-unnecessary-value-param'),\n    simple_tidy_warn_pattern('portability-simd-intrinsics'),\n    group_tidy_warn_pattern('performance'),\n    group_tidy_warn_pattern('readability'),\n    simple_tidy_warn_pattern('abseil-string-find-startwith'),\n    simple_tidy_warn_pattern('abseil-faster-strsplit-delimiter'),\n    simple_tidy_warn_pattern('abseil-no-namespace'),\n    simple_tidy_warn_pattern('abseil-no-internal-dependencies'),\n    group_tidy_warn_pattern('abseil'),\n    simple_tidy_warn_pattern('portability-simd-intrinsics'),\n    group_tidy_warn_pattern('portability'),\n\n    tidy_warn('TIMEOUT', [r\".*: warning: clang-tidy aborted \"]),\n    tidy_warn('Long Runs', [r\".*: warning: clang-tidy used \"]),\n\n    # warnings from clang-tidy's clang-analyzer checks\n    analyzer_high('clang-analyzer-core, null pointer',\n                  [r\".*: warning: .+ pointer is null .*\\[clang-analyzer-core\"]),\n    analyzer_high('clang-analyzer-core, uninitialized value',\n                  [r\".*: warning: .+ uninitialized (value|data) .*\\[clang-analyzer-core\"]),\n    analyzer_warn('clang-analyzer-optin.performance.Padding',\n                  [r\".*: warning: Excessive padding in '.*'\"]),\n    # analyzer_warn('clang-analyzer Unreachable code',\n    #               [r\".*: warning: This statement is never executed.*UnreachableCode\"]),\n    analyzer_warn('clang-analyzer Size of malloc may overflow',\n                  [r\".*: warning: .* size of .* may overflow .*MallocOverflow\"]),\n    analyzer_warn('clang-analyzer sozeof() on a pointer type',\n                  [r\".*: warning: .*calls sizeof.* on a pointer type.*SizeofPtr\"]),\n    analyzer_warn('clang-analyzer Pointer arithmetic on non-array variables',\n                  [r\".*: warning: Pointer arithmetic on non-array variables .*PointerArithm\"]),\n    analyzer_warn('clang-analyzer Subtraction of pointers of different memory chunks',\n                  [r\".*: warning: Subtraction of two pointers .*PointerSub\"]),\n    analyzer_warn('clang-analyzer Access out-of-bound array element',\n                  [r\".*: warning: Access out-of-bound array element .*ArrayBound\"]),\n    analyzer_warn('clang-analyzer Out of bound memory access',\n                  [r\".*: warning: Out of bound memory access .*ArrayBoundV2\"]),\n    analyzer_warn('clang-analyzer Possible lock order reversal',\n                  [r\".*: warning: .* Possible lock order reversal.*PthreadLock\"]),\n    analyzer_warn('clang-analyzer call path problems',\n                  [r\".*: warning: Call Path : .+\"]),\n    analyzer_warn_check('clang-analyzer-core.CallAndMessage'),\n    analyzer_high_check('clang-analyzer-core.NonNullParamChecker'),\n    analyzer_high_check('clang-analyzer-core.NullDereference'),\n    analyzer_warn_check('clang-analyzer-core.UndefinedBinaryOperatorResult'),\n    analyzer_warn_check('clang-analyzer-core.DivideZero'),\n    analyzer_warn_check('clang-analyzer-core.VLASize'),\n    analyzer_warn_check('clang-analyzer-core.uninitialized.ArraySubscript'),\n    analyzer_warn_check('clang-analyzer-core.uninitialized.Assign'),\n    analyzer_warn_check('clang-analyzer-core.uninitialized.UndefReturn'),\n    analyzer_warn_check('clang-analyzer-cplusplus.Move'),\n    analyzer_warn_check('clang-analyzer-deadcode.DeadStores'),\n    analyzer_warn_check('clang-analyzer-optin.cplusplus.UninitializedObject'),\n    analyzer_warn_check('clang-analyzer-optin.cplusplus.VirtualCall'),\n    analyzer_warn_check('clang-analyzer-portability.UnixAPI'),\n    analyzer_warn_check('clang-analyzer-unix.cstring.NullArg'),\n    analyzer_high_check('clang-analyzer-unix.MallocSizeof'),\n    analyzer_warn_check('clang-analyzer-valist.Uninitialized'),\n    analyzer_warn_check('clang-analyzer-valist.Unterminated'),\n    analyzer_group_check('clang-analyzer-core.uninitialized'),\n    analyzer_group_check('clang-analyzer-deadcode'),\n    analyzer_warn_check('clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling'),\n    analyzer_warn_check('clang-analyzer-security.insecureAPI.bcmp'),\n    analyzer_warn_check('clang-analyzer-security.insecureAPI.bcopy'),\n    analyzer_warn_check('clang-analyzer-security.insecureAPI.bzero'),\n    analyzer_warn_check('clang-analyzer-security.insecureAPI.strcpy'),\n    analyzer_group_high('clang-analyzer-security.insecureAPI'),\n    analyzer_group_high('clang-analyzer-security'),\n    analyzer_high_check('clang-analyzer-unix.Malloc'),\n    analyzer_high_check('clang-analyzer-cplusplus.NewDeleteLeaks'),\n    analyzer_high_check('clang-analyzer-cplusplus.NewDelete'),\n    analyzer_group_check('clang-analyzer-unix'),\n    analyzer_group_check('clang-analyzer'),  # catch all\n]\n\n\ncompile_patterns(warn_patterns)\n"
  },
  {
    "path": "tools/warn/warn.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Simple wrapper to run warn_common with Python standard Pool.\"\"\"\n\nimport multiprocessing\nimport signal\nimport sys\n\n# pylint:disable=relative-beyond-top-level,no-name-in-module\n# suppress false positive of no-name-in-module warnings\nfrom . import warn_common as common\n\n\ndef classify_warnings(args):\n  \"\"\"Classify a list of warning lines.\n\n  Args:\n    args: dictionary {\n        'group': list of (warning, link),\n        'project_patterns': re.compile(project_list[p][1]),\n        'warn_patterns': list of warn_pattern,\n        'num_processes': number of processes being used for multiprocessing }\n  Returns:\n    results: a list of the classified warnings.\n  \"\"\"\n  results = []\n  for line, link in args['group']:\n    common.classify_one_warning(line, link, results, args['project_patterns'],\n                                args['warn_patterns'])\n\n  # After the main work, ignore all other signals to a child process,\n  # to avoid bad warning/error messages from the exit clean-up process.\n  if args['num_processes'] > 1:\n    signal.signal(signal.SIGTERM, lambda *args: sys.exit(-signal.SIGTERM))\n  return results\n\n\ndef create_and_launch_subprocesses(num_cpu, classify_warnings_fn, arg_groups,\n                                   group_results):\n  \"\"\"Fork num_cpu processes to classify warnings.\"\"\"\n  pool = multiprocessing.Pool(num_cpu)\n  for cpu in range(num_cpu):\n    proc_result = pool.map(classify_warnings_fn, arg_groups[cpu])\n    if proc_result is not None:\n      group_results.append(proc_result)\n  return group_results\n\n\ndef main():\n  \"\"\"Old main() calls new common_main.\"\"\"\n  use_google3 = False\n  common.common_main(use_google3, create_and_launch_subprocesses,\n                     classify_warnings)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tools/warn/warn_common.py",
    "content": "# python3\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Grep warnings messages and output HTML tables or warning counts in CSV.\n\nDefault is to output warnings in HTML tables grouped by warning severity.\nUse option --byproject to output tables grouped by source file projects.\nUse option --gencsv to output warning counts in CSV format.\n\nDefault input file is build.log, which can be changed with the --log flag.\n\"\"\"\n\n# List of important data structures and functions in this script.\n#\n# To parse and keep warning message in the input file:\n#   severity:                classification of message severity\n#   warn_patterns:\n#   warn_patterns[w]['category']     tool that issued the warning, not used now\n#   warn_patterns[w]['description']  table heading\n#   warn_patterns[w]['members']      matched warnings from input\n#   warn_patterns[w]['patterns']     regular expressions to match warnings\n#   warn_patterns[w]['projects'][p]  number of warnings of pattern w in p\n#   warn_patterns[w]['severity']     severity tuple\n#   project_list[p][0]               project name\n#   project_list[p][1]               regular expression to match a project path\n#   project_patterns[p]              re.compile(project_list[p][1])\n#   project_names[p]                 project_list[p][0]\n#   warning_messages     array of each warning message, without source url\n#   warning_links        array of each warning code search link; for 'chrome'\n#   warning_records      array of [idx to warn_patterns,\n#                                  idx to project_names,\n#                                  idx to warning_messages,\n#                                  idx to warning_links]\n#   parse_input_file\n#\nimport argparse\nimport io\nimport multiprocessing\nimport os\nimport re\nimport sys\n\n# pylint:disable=relative-beyond-top-level,no-name-in-module\n# suppress false positive of no-name-in-module warnings\nfrom . import android_project_list\nfrom . import chrome_project_list\nfrom . import cpp_warn_patterns as cpp_patterns\nfrom . import html_writer\nfrom . import java_warn_patterns as java_patterns\nfrom . import make_warn_patterns as make_patterns\nfrom . import other_warn_patterns as other_patterns\nfrom . import tidy_warn_patterns as tidy_patterns\n\n\n# Location of this file is used to guess the root of Android source tree.\nTHIS_FILE_PATH = 'build/make/tools/warn/warn_common.py'\n\n\ndef parse_args(use_google3):\n  \"\"\"Define and parse the args. Return the parse_args() result.\"\"\"\n  parser = argparse.ArgumentParser(\n      description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)\n  parser.add_argument('--capacitor_path', default='',\n                      help='Save capacitor warning file to the passed absolute'\n                      ' path')\n  # csvpath has a different naming than the above path because historically the\n  # original Android script used csvpath, so other scripts rely on it\n  parser.add_argument('--csvpath', default='',\n                      help='Save CSV warning file to the passed path')\n  parser.add_argument('--gencsv', action='store_true',\n                      help='Generate CSV file with number of various warnings')\n  parser.add_argument('--csvwithdescription', default='',\n                      help=\"\"\"Save CSV warning file to the passed path this csv\n                            will contain all the warning descriptions\"\"\")\n  parser.add_argument('--byproject', action='store_true',\n                      help='Separate warnings in HTML output by project names')\n  parser.add_argument('--url', default='',\n                      help='Root URL of an Android source code tree prefixed '\n                      'before files in warnings')\n  parser.add_argument('--separator', default='?l=',\n                      help='Separator between the end of a URL and the line '\n                      'number argument. e.g. #')\n  parser.add_argument('--processes', default=multiprocessing.cpu_count(),\n                      type=int,\n                      help='Number of parallel processes to process warnings')\n  # Old Android build scripts call warn.py without --platform,\n  # so the default platform is set to 'android'.\n  parser.add_argument('--platform', default='android',\n                      choices=['chrome', 'android'],\n                      help='Platform of the build log')\n  # Old Android build scripts call warn.py with only a build.log file path.\n  parser.add_argument('--log', help='Path to build log file')\n  parser.add_argument(dest='buildlog', metavar='build.log',\n                      default='build.log', nargs='?',\n                      help='Path to build.log file')\n  flags = parser.parse_args()\n  if not flags.log:\n    flags.log = flags.buildlog\n  if not use_google3 and not os.path.exists(flags.log):\n    sys.exit('Cannot find log file: ' + flags.log)\n  return flags\n\n\ndef get_project_names(project_list):\n  \"\"\"Get project_names from project_list.\"\"\"\n  return [p[0] for p in project_list]\n\n\ndef find_project_index(line, project_patterns):\n  \"\"\"Return the index to the project pattern array.\"\"\"\n  for idx, pattern in enumerate(project_patterns):\n    if pattern.match(line):\n      return idx\n  return -1\n\n\ndef classify_one_warning(warning, link, results, project_patterns,\n                         warn_patterns):\n  \"\"\"Classify one warning line.\"\"\"\n  for idx, pattern in enumerate(warn_patterns):\n    for cpat in pattern['compiled_patterns']:\n      if cpat.match(warning):\n        project_idx = find_project_index(warning, project_patterns)\n        results.append([warning, link, idx, project_idx])\n        return\n  # If we end up here, there was a problem parsing the log\n  # probably caused by 'make -j' mixing the output from\n  # 2 or more concurrent compiles\n\n\ndef remove_prefix(src, sub):\n  \"\"\"Remove everything before last occurrence of substring sub in string src.\"\"\"\n  if sub in src:\n    inc_sub = src.rfind(sub)\n    return src[inc_sub:]\n  return src\n\n\n# TODO(emmavukelj): Don't have any generate_*_cs_link functions call\n# normalize_path a second time (the first time being in parse_input_file)\ndef generate_cs_link(warning_line, flags, android_root=None):\n  \"\"\"Try to add code search HTTP URL prefix.\"\"\"\n  if flags.platform == 'chrome':\n    return generate_chrome_cs_link(warning_line, flags)\n  if flags.platform == 'android':\n    return generate_android_cs_link(warning_line, flags, android_root)\n  return 'https://cs.corp.google.com/'\n\n\ndef generate_android_cs_link(warning_line, flags, android_root):\n  \"\"\"Generate the code search link for a warning line in Android.\"\"\"\n  # max_splits=2 -> only 3 items\n  raw_path, line_number_str, _ = warning_line.split(':', 2)\n  normalized_path = normalize_path(raw_path, flags, android_root)\n  if not flags.url:\n    return normalized_path\n  link_path = flags.url + '/' + normalized_path\n  if line_number_str.isdigit():\n    link_path += flags.separator + line_number_str\n  return link_path\n\n\ndef generate_chrome_cs_link(warning_line, flags):\n  \"\"\"Generate the code search link for a warning line in Chrome.\"\"\"\n  split_line = warning_line.split(':')\n  raw_path = split_line[0]\n  normalized_path = normalize_path(raw_path, flags)\n  link_base = 'https://cs.chromium.org/'\n  link_add = 'chromium'\n  link_path = None\n\n  # Basically just going through a few specific directory cases and specifying\n  # the proper behavior for that case. This list of cases was accumulated\n  # through trial and error manually going through the warnings.\n  #\n  # This code pattern of using case-specific \"if\"s instead of \"elif\"s looks\n  # possibly accidental and mistaken but it is intentional because some paths\n  # fall under several cases (e.g. third_party/lib/nghttp2_frame.c) and for\n  # those we want the most specific case to be applied. If there is reliable\n  # knowledge of exactly where these occur, this could be changed to \"elif\"s\n  # but there is no reliable set of paths falling under multiple cases at the\n  # moment.\n  if '/src/third_party' in raw_path:\n    link_path = remove_prefix(raw_path, '/src/third_party/')\n  if '/chrome_root/src_internal/' in raw_path:\n    link_path = remove_prefix(raw_path, '/chrome_root/src_internal/')\n    link_path = link_path[len('/chrome_root'):]  # remove chrome_root\n  if '/chrome_root/src/' in raw_path:\n    link_path = remove_prefix(raw_path, '/chrome_root/src/')\n    link_path = link_path[len('/chrome_root'):]  # remove chrome_root\n  if '/libassistant/' in raw_path:\n    link_add = 'eureka_internal/chromium/src'\n    link_base = 'https://cs.corp.google.com/'  # internal data\n    link_path = remove_prefix(normalized_path, '/libassistant/')\n  if raw_path.startswith('gen/'):\n    link_path = '/src/out/Debug/gen/' + normalized_path\n  if '/gen/' in raw_path:\n    return '%s?q=file:%s' % (link_base, remove_prefix(normalized_path, '/gen/'))\n\n  if not link_path and (raw_path.startswith('src/') or\n                        raw_path.startswith('src_internal/')):\n    link_path = '/%s' % raw_path\n\n  if not link_path:  # can't find specific link, send a query\n    return '%s?q=file:%s' % (link_base, normalized_path)\n\n  line_number = int(split_line[1])\n  link = '%s%s%s?l=%d' % (link_base, link_add, link_path, line_number)\n  return link\n\n\ndef find_this_file_and_android_root(path):\n  \"\"\"Return android source root path if this file is found.\"\"\"\n  parts = path.split('/')\n  for idx in reversed(range(2, len(parts))):\n    root_path = '/'.join(parts[:idx])\n    # Android root directory should contain this script.\n    if os.path.exists(root_path + '/' + THIS_FILE_PATH):\n      return root_path\n  return ''\n\n\ndef find_android_root_top_dirs(root_dir):\n  \"\"\"Return a list of directories under the root_dir, if it exists.\"\"\"\n  # Root directory should contain at least build/make and build/soong.\n  if (not os.path.isdir(root_dir + '/build/make') or\n      not os.path.isdir(root_dir + '/build/soong')):\n    return None\n  return list(filter(lambda d: os.path.isdir(root_dir + '/' + d),\n                     os.listdir(root_dir)))\n\n\ndef find_android_root(buildlog):\n  \"\"\"Guess android source root from common prefix of file paths.\"\"\"\n  # Use the longest common prefix of the absolute file paths\n  # of the first 10000 warning messages as the android_root.\n  warning_lines = []\n  warning_pattern = re.compile('^/[^ ]*/[^ ]*: warning: .*')\n  count = 0\n  for line in buildlog:\n    # We want to find android_root of a local build machine.\n    # Do not use RBE warning lines, which has '/b/f/w/' path prefix.\n    # Do not use /tmp/ file warnings.\n    if ('/b/f/w' not in line and not line.startswith('/tmp/') and\n        warning_pattern.match(line)):\n      warning_lines.append(line)\n      count += 1\n      if count > 9999:\n        break\n      # Try to find warn.py and use its location to find\n      # the source tree root.\n      if count < 100:\n        path = os.path.normpath(re.sub(':.*$', '', line))\n        android_root = find_this_file_and_android_root(path)\n        if android_root:\n          return android_root, find_android_root_top_dirs(android_root)\n  # Do not use common prefix of a small number of paths.\n  android_root = ''\n  if count > 10:\n    # pytype: disable=wrong-arg-types\n    root_path = os.path.commonprefix(warning_lines)\n    # pytype: enable=wrong-arg-types\n    if len(root_path) > 2 and root_path[len(root_path) - 1] == '/':\n      android_root = root_path[:-1]\n  if android_root and os.path.isdir(android_root):\n    return android_root, find_android_root_top_dirs(android_root)\n  # When the build.log file is moved to a different machine where\n  # android_root is not found, use the location of this script\n  # to find the android source tree sub directories.\n  if __file__.endswith('/' + THIS_FILE_PATH):\n    script_root = __file__.replace('/' + THIS_FILE_PATH, '')\n    return android_root, find_android_root_top_dirs(script_root)\n  return android_root, None\n\n\ndef remove_android_root_prefix(path, android_root):\n  \"\"\"Remove android_root prefix from path if it is found.\"\"\"\n  if path.startswith(android_root):\n    return path[1 + len(android_root):]\n  return path\n\n\ndef normalize_path(path, flags, android_root=None):\n  \"\"\"Normalize file path relative to src/ or src-internal/ directory.\"\"\"\n  path = os.path.normpath(path)\n\n  if flags.platform == 'android':\n    if android_root:\n      return remove_android_root_prefix(path, android_root)\n    return path\n\n  # Remove known prefix of root path and normalize the suffix.\n  idx = path.find('chrome_root/')\n  if idx >= 0:\n    # remove chrome_root/, we want path relative to that\n    return path[idx + len('chrome_root/'):]\n  return path\n\n\ndef normalize_warning_line(line, flags, android_root=None):\n  \"\"\"Normalize file path relative to src directory in a warning line.\"\"\"\n  line = re.sub(u'[\\u2018\\u2019]', '\\'', line)\n  # replace non-ASCII chars to spaces\n  line = re.sub(u'[^\\x00-\\x7f]', ' ', line)\n  line = line.strip()\n  first_column = line.find(':')\n  return normalize_path(line[:first_column], flags,\n                        android_root) + line[first_column:]\n\n\ndef parse_input_file_chrome(infile, flags):\n  \"\"\"Parse Chrome input file, collect parameters and warning lines.\"\"\"\n  platform_version = 'unknown'\n  board_name = 'unknown'\n  architecture = 'unknown'\n\n  # only handle warning lines of format 'file_path:line_no:col_no: warning: ...'\n  # Bug: http://198657613, This might need change to handle RBE output.\n  chrome_warning_pattern = r'^[^ ]*/[^ ]*:[0-9]+:[0-9]+: warning: .*'\n\n  warning_pattern = re.compile(chrome_warning_pattern)\n\n  # Collect all unique warning lines\n  unique_warnings = dict()\n  for line in infile:\n    if warning_pattern.match(line):\n      normalized_line = normalize_warning_line(line, flags)\n      if normalized_line not in unique_warnings:\n        unique_warnings[normalized_line] = generate_cs_link(line, flags)\n    elif (platform_version == 'unknown' or board_name == 'unknown' or\n          architecture == 'unknown'):\n      result = re.match(r'.+Package:.+chromeos-base/chromeos-chrome-', line)\n      if result is not None:\n        platform_version = 'R' + line.split('chrome-')[1].split('_')[0]\n        continue\n      result = re.match(r'.+Source\\sunpacked\\sin\\s(.+)', line)\n      if result is not None:\n        board_name = result.group(1).split('/')[2]\n        continue\n      result = re.match(r'.+USE:\\s*([^\\s]*).*', line)\n      if result is not None:\n        architecture = result.group(1)\n        continue\n\n  header_str = '%s - %s - %s' % (platform_version, board_name, architecture)\n  return unique_warnings, header_str\n\n\ndef add_normalized_line_to_warnings(line, flags, android_root, unique_warnings):\n  \"\"\"Parse/normalize path, updating warning line and add to warnings dict.\"\"\"\n  normalized_line = normalize_warning_line(line, flags, android_root)\n  if normalized_line not in unique_warnings:\n    unique_warnings[normalized_line] = generate_cs_link(line, flags,\n                                                        android_root)\n  return unique_warnings\n\n\ndef parse_input_file_android(infile, flags):\n  \"\"\"Parse Android input file, collect parameters and warning lines.\"\"\"\n  # pylint:disable=too-many-locals,too-many-branches\n  platform_version = 'unknown'\n  target_product = 'unknown'\n  target_variant = 'unknown'\n  build_id = 'unknown'\n  android_root, root_top_dirs = find_android_root(infile)\n  infile.seek(0)\n\n  # rustc warning messages have two lines that should be combined:\n  #     warning: description\n  #        --> file_path:line_number:column_number\n  # Some warning messages have no file name:\n  #     warning: macro replacement list ... [bugprone-macro-parentheses]\n  # Some makefile warning messages have no line number:\n  #     some/path/file.mk: warning: description\n  # C/C++ compiler warning messages have line and column numbers:\n  #     some/path/file.c:line_number:column_number: warning: description\n  warning_pattern = re.compile('(^[^ ]*/[^ ]*: warning: .*)|(^warning: .*)')\n  rustc_file_position = re.compile('^[ ]+--> [^ ]*/[^ ]*:[0-9]+:[0-9]+')\n\n  # If RBE was used, try to reclaim some warning lines (from stdout)\n  # that contain leading characters from stderr.\n  # The leading characters can be any character, including digits and spaces.\n\n  # If a warning line's source file path contains the special RBE prefix\n  # /b/f/w/, we can remove all leading chars up to and including the \"/b/f/w/\".\n  bfw_warning_pattern = re.compile('.*/b/f/w/([^ ]*: warning: .*)')\n\n  # When android_root is known and available, we find its top directories\n  # and remove all leading chars before a top directory name.\n  # We assume that the leading chars from stderr do not contain \"/\".\n  # For example,\n  #   10external/...\n  #   12 warningsexternal/...\n  #   413 warningexternal/...\n  #   5 warnings generatedexternal/...\n  #   Suppressed 1000 warnings (packages/modules/...\n  if root_top_dirs:\n    extra_warning_pattern = re.compile(\n        '^.[^/]*((' + '|'.join(root_top_dirs) +\n        ')/[^ ]*: warning: .*)')\n  else:\n    extra_warning_pattern = re.compile('^[^/]* ([^ /]*/[^ ]*: warning: .*)')\n\n  # Collect all unique warning lines\n  unique_warnings = dict()\n  checked_warning_lines = dict()\n  line_counter = 0\n  prev_warning = ''\n  for line in infile:\n    line_counter += 1\n    if prev_warning:\n      if rustc_file_position.match(line):\n        # must be a rustc warning, combine 2 lines into one warning\n        line = line.strip().replace('--> ', '') + ': ' + prev_warning\n        unique_warnings = add_normalized_line_to_warnings(\n            line, flags, android_root, unique_warnings)\n        prev_warning = ''\n        continue\n      # add prev_warning, and then process the current line\n      prev_warning = 'unknown_source_file: ' + prev_warning\n      unique_warnings = add_normalized_line_to_warnings(\n          prev_warning, flags, android_root, unique_warnings)\n      prev_warning = ''\n\n    # re.match is slow, with several warning line patterns and\n    # long input lines like \"TIMEOUT: ...\".\n    # We save significant time by skipping non-warning lines.\n    # But do not skip the first 100 lines, because we want to\n    # catch build variables.\n    if line_counter > 100 and line.find('warning: ') < 0:\n      continue\n\n    # A large clean build output can contain up to 90% of duplicated\n    # \"warning:\" lines. If we can skip them quickly, we can\n    # speed up this for-loop 3X to 5X.\n    if line in checked_warning_lines:\n      continue\n    checked_warning_lines[line] = True\n\n    # Clean up extra prefix that could be introduced when RBE was used.\n    if '/b/f/w/' in line:\n      result = bfw_warning_pattern.search(line)\n    else:\n      result = extra_warning_pattern.search(line)\n    if result is not None:\n      line = result.group(1)\n\n    if warning_pattern.match(line):\n      if line.startswith('warning: '):\n        # save this line and combine it with the next line\n        prev_warning = line\n      else:\n        unique_warnings = add_normalized_line_to_warnings(\n            line, flags, android_root, unique_warnings)\n      continue\n\n    if line_counter < 100:\n      # save a little bit of time by only doing this for the first few lines\n      result = re.search('(?<=^PLATFORM_VERSION=).*', line)\n      if result is not None:\n        platform_version = result.group(0)\n        continue\n      result = re.search('(?<=^TARGET_PRODUCT=).*', line)\n      if result is not None:\n        target_product = result.group(0)\n        continue\n      result = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)\n      if result is not None:\n        target_variant = result.group(0)\n        continue\n      result = re.search('(?<=^BUILD_ID=).*', line)\n      if result is not None:\n        build_id = result.group(0)\n        continue\n\n  if android_root:\n    new_unique_warnings = dict()\n    for warning_line in unique_warnings:\n      normalized_line = normalize_warning_line(warning_line, flags,\n                                               android_root)\n      new_unique_warnings[normalized_line] = generate_android_cs_link(\n          warning_line, flags, android_root)\n    unique_warnings = new_unique_warnings\n\n  header_str = '%s - %s - %s (%s)' % (\n      platform_version, target_product, target_variant, build_id)\n  return unique_warnings, header_str\n\n\ndef parse_input_file(infile, flags):\n  \"\"\"Parse one input file for chrome or android.\"\"\"\n  if flags.platform == 'chrome':\n    return parse_input_file_chrome(infile, flags)\n  if flags.platform == 'android':\n    return parse_input_file_android(infile, flags)\n  raise RuntimeError('parse_input_file not defined for platform %s' %\n                     flags.platform)\n\n\ndef parse_compiler_output(compiler_output):\n  \"\"\"Parse compiler output for relevant info.\"\"\"\n  split_output = compiler_output.split(':', 3)  # 3 = max splits\n  file_path = split_output[0]\n  line_number = int(split_output[1])\n  col_number = int(split_output[2].split(' ')[0])\n  warning_message = split_output[3]\n  return file_path, line_number, col_number, warning_message\n\n\ndef get_warn_patterns(platform):\n  \"\"\"Get and initialize warn_patterns.\"\"\"\n  warn_patterns = []\n  if platform == 'chrome':\n    warn_patterns = cpp_patterns.warn_patterns\n  elif platform == 'android':\n    warn_patterns = (make_patterns.warn_patterns + cpp_patterns.warn_patterns +\n                     java_patterns.warn_patterns + tidy_patterns.warn_patterns +\n                     other_patterns.warn_patterns)\n  else:\n    raise Exception('platform name %s is not valid' % platform)\n  for pattern in warn_patterns:\n    pattern['members'] = []\n    # Each warning pattern has a 'projects' dictionary, that\n    # maps a project name to number of warnings in that project.\n    pattern['projects'] = {}\n  return warn_patterns\n\n\ndef get_project_list(platform):\n  \"\"\"Return project list for appropriate platform.\"\"\"\n  if platform == 'chrome':\n    return chrome_project_list.project_list\n  if platform == 'android':\n    return android_project_list.project_list\n  raise Exception('platform name %s is not valid' % platform)\n\n\ndef parallel_classify_warnings(warning_data, args, project_names,\n                               project_patterns, warn_patterns,\n                               use_google3, create_launch_subprocs_fn,\n                               classify_warnings_fn):\n  \"\"\"Classify all warning lines with num_cpu parallel processes.\"\"\"\n  # pylint:disable=too-many-arguments,too-many-locals\n  num_cpu = args.processes\n  group_results = []\n\n  if num_cpu > 1:\n    # set up parallel processing for this...\n    warning_groups = [[] for _ in range(num_cpu)]\n    i = 0\n    for warning, link in warning_data.items():\n      warning_groups[i].append((warning, link))\n      i = (i + 1) % num_cpu\n    arg_groups = [[] for _ in range(num_cpu)]\n    for i, group in enumerate(warning_groups):\n      arg_groups[i] = [{\n          'group': group,\n          'project_patterns': project_patterns,\n          'warn_patterns': warn_patterns,\n          'num_processes': num_cpu\n      }]\n\n    group_results = create_launch_subprocs_fn(num_cpu,\n                                              classify_warnings_fn,\n                                              arg_groups,\n                                              group_results)\n  else:\n    group_results = []\n    for warning, link in warning_data.items():\n      classify_one_warning(warning, link, group_results,\n                           project_patterns, warn_patterns)\n    group_results = [group_results]\n\n  warning_messages = []\n  warning_links = []\n  warning_records = []\n  if use_google3:\n    group_results = [group_results]\n  for group_result in group_results:\n    for result in group_result:\n      for line, link, pattern_idx, project_idx in result:\n        pattern = warn_patterns[pattern_idx]\n        pattern['members'].append(line)\n        message_idx = len(warning_messages)\n        warning_messages.append(line)\n        link_idx = len(warning_links)\n        warning_links.append(link)\n        warning_records.append([pattern_idx, project_idx, message_idx,\n                                link_idx])\n        pname = '???' if project_idx < 0 else project_names[project_idx]\n        # Count warnings by project.\n        if pname in pattern['projects']:\n          pattern['projects'][pname] += 1\n        else:\n          pattern['projects'][pname] = 1\n  return warning_messages, warning_links, warning_records\n\n\ndef process_log(logfile, flags, project_names, project_patterns, warn_patterns,\n                html_path, use_google3, create_launch_subprocs_fn,\n                classify_warnings_fn, logfile_object):\n  # pylint does not recognize g-doc-*\n  # pylint: disable=bad-option-value,g-doc-args\n  # pylint: disable=bad-option-value,g-doc-return-or-yield\n  # pylint: disable=too-many-arguments,too-many-locals\n  \"\"\"Function that handles processing of a log.\n\n  This is isolated into its own function (rather than just taking place in main)\n  so that it can be used by both warn.py and the borg job process_gs_logs.py, to\n  avoid duplication of code.\n  Note that if the arguments to this function change, process_gs_logs.py must\n  be updated accordingly.\n  \"\"\"\n  if logfile_object is None:\n    with io.open(logfile, encoding='utf-8') as log:\n      warning_lines_and_links, header_str = parse_input_file(log, flags)\n  else:\n    warning_lines_and_links, header_str = parse_input_file(\n        logfile_object, flags)\n  warning_messages, warning_links, warning_records = parallel_classify_warnings(\n      warning_lines_and_links, flags, project_names, project_patterns,\n      warn_patterns, use_google3, create_launch_subprocs_fn,\n      classify_warnings_fn)\n\n  html_writer.write_html(flags, project_names, warn_patterns, html_path,\n                         warning_messages, warning_links, warning_records,\n                         header_str)\n\n  return warning_messages, warning_links, warning_records, header_str\n\n\ndef common_main(use_google3, create_launch_subprocs_fn, classify_warnings_fn,\n                logfile_object=None):\n  \"\"\"Shared main function for Google3 and non-Google3 versions of warn.py.\"\"\"\n  flags = parse_args(use_google3)\n  warn_patterns = get_warn_patterns(flags.platform)\n  project_list = get_project_list(flags.platform)\n\n  project_names = get_project_names(project_list)\n  project_patterns = [re.compile(p[1]) for p in project_list]\n\n  # html_path=None because we output html below if not outputting CSV\n  warning_messages, warning_links, warning_records, header_str = process_log(\n      logfile=flags.log, flags=flags, project_names=project_names,\n      project_patterns=project_patterns, warn_patterns=warn_patterns,\n      html_path=None, use_google3=use_google3,\n      create_launch_subprocs_fn=create_launch_subprocs_fn,\n      classify_warnings_fn=classify_warnings_fn,\n      logfile_object=logfile_object)\n\n  html_writer.write_out_csv(flags, warn_patterns, warning_messages,\n                            warning_links, warning_records, header_str,\n                            project_names)\n\n  # Return these values, so that caller can use them, if desired.\n  return flags, warning_messages, warning_records, warn_patterns\n"
  },
  {
    "path": "tools/warn.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2019 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Call -m warn.warn to process warning messages.\n\nThis script is used by Android continuous build bots for all branches.\nOld frozen branches will continue to use the old warn.py, and active\nbranches will use this new version to call -m warn.warn.\n\"\"\"\n\nimport os\nimport subprocess\nimport sys\n\n\ndef main():\n  \"\"\"Old main() calls warn.warn.\"\"\"\n  os.environ['PYTHONPATH'] = os.path.dirname(os.path.abspath(__file__))\n  subprocess.check_call(['/usr/bin/python3', '-m', 'warn.warn'] + sys.argv[1:])\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tools/whichgit",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport itertools\nimport os\nimport subprocess\nimport sys\n\ndef get_build_var(var):\n  return subprocess.run([\"build/soong/soong_ui.bash\",\"--dumpvar-mode\", var],\n                        check=True, capture_output=True, text=True).stdout.strip()\n\n\ndef get_all_modules():\n  product_out = subprocess.run([\"build/soong/soong_ui.bash\", \"--dumpvar-mode\", \"--abs\", \"PRODUCT_OUT\"],\n                                check=True, capture_output=True, text=True).stdout.strip()\n  result = subprocess.run([\"cat\", product_out + \"/all_modules.txt\"], check=True, capture_output=True, text=True)\n  return result.stdout.strip().split(\"\\n\")\n\n\ndef batched(iterable, n):\n  # introduced in itertools 3.12, could delete once that's universally available\n  if n < 1:\n    raise ValueError('n must be at least one')\n  it = iter(iterable)\n  while batch := tuple(itertools.islice(it, n)):\n    yield batch\n\n\ndef get_sources(modules):\n  sources = set()\n  for module_group in batched(modules, 40_000):\n    result = subprocess.run([\"./prebuilts/build-tools/linux-x86/bin/ninja\", \"-f\",\n                            \"out/combined-\" + os.environ[\"TARGET_PRODUCT\"] + \".ninja\",\n                            \"-t\", \"inputs\", \"-d\", ] + list(module_group),\n                            stderr=subprocess.STDOUT, stdout=subprocess.PIPE, check=False, text=True)\n    if result.returncode != 0:\n      sys.stderr.write(result.stdout)\n      sys.exit(1)\n    sources.update(set([f for f in result.stdout.split(\"\\n\") if not f.startswith(\"out/\")]))\n  return sources\n\n\ndef m_nothing():\n  result = subprocess.run([\"build/soong/soong_ui.bash\", \"--build-mode\", \"--all-modules\",\n                           \"--dir=\" + os.getcwd(), \"nothing\"],\n                           check=False, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, text=True)\n  if result.returncode != 0:\n    sys.stderr.write(result.stdout)\n    sys.exit(1)\n\n\ndef get_git_dirs():\n  text = subprocess.run([\"repo\",\"list\"], check=True, capture_output=True, text=True).stdout\n  return [line.split(\" : \")[0] + \"/\" for line in text.split(\"\\n\")]\n\n\ndef get_referenced_projects(git_dirs, files):\n  # files must be sorted\n  referenced_dirs = set()\n  prev_dir = None\n  for f in files:\n    # Optimization is ~5x speedup for large sets of files\n    if prev_dir:\n      if f.startswith(prev_dir):\n        referenced_dirs.add(d)\n        continue\n    for d in git_dirs:\n      if f.startswith(d):\n        referenced_dirs.add(d)\n        prev_dir = d\n        break\n  return referenced_dirs\n\n\ndef main(argv):\n  # Argument parsing\n  ap = argparse.ArgumentParser(description=\"List the required git projects for the given modules\")\n  ap.add_argument(\"--products\", nargs=\"*\",\n                  help=\"One or more TARGET_PRODUCT to check, or \\\"*\\\" for all. If not provided\"\n                        + \"just uses whatever has already been built\")\n  ap.add_argument(\"--variants\", nargs=\"*\",\n                  help=\"The TARGET_BUILD_VARIANTS to check. If not provided just uses whatever has\"\n                        + \" already been built, or eng if --products is supplied\")\n  ap.add_argument(\"--modules\", nargs=\"*\",\n                  help=\"The build modules to check, or \\\"*\\\" for all, or droid if not supplied\")\n  ap.add_argument(\"--why\", nargs=\"*\",\n                  help=\"Also print the input files used in these projects, or \\\"*\\\" for all\")\n  ap.add_argument(\"--unused\", help=\"List the unused git projects for the given modules rather than\"\n                        + \"the used ones. Ignores --why\", action=\"store_true\")\n  args = ap.parse_args(argv[1:])\n\n  modules = args.modules if args.modules else [\"droid\"]\n\n  match args.products:\n    case [\"*\"]:\n      products = get_build_var(\"all_named_products\").split(\" \")\n    case _:\n      products = args.products\n\n  # Get the list of sources for all of the requested build combos\n  if not products and not args.variants:\n    m_nothing()\n    if args.modules == [\"*\"]:\n      modules = get_all_modules()\n    sources = get_sources(modules)\n  else:\n    if not products:\n      sys.stderr.write(\"Error: --products must be supplied if --variants is supplied\")\n      sys.exit(1)\n    sources = set()\n    build_num = 1\n    for product in products:\n      os.environ[\"TARGET_PRODUCT\"] = product\n      variants = args.variants if args.variants else [\"user\", \"userdebug\", \"eng\"]\n      for variant in variants:\n        sys.stderr.write(f\"Analyzing build {build_num} of {len(products)*len(variants)}\\r\")\n        os.environ[\"TARGET_BUILD_VARIANT\"] = variant\n        m_nothing()\n        if args.modules == [\"*\"]:\n          modules = get_all_modules()\n        sources.update(get_sources(modules))\n        build_num += 1\n    sys.stderr.write(\"\\n\\n\")\n\n  sources = sorted(sources)\n\n  if args.unused:\n    # Print the list of git directories that don't contain sources\n    used_git_dirs = set(get_git_dirs())\n    for project in sorted(used_git_dirs.difference(set(get_referenced_projects(used_git_dirs, sources)))):\n      print(project[0:-1])\n  else:\n    # Print the list of git directories that has one or more of the sources in it\n    for project in sorted(get_referenced_projects(get_git_dirs(), sources)):\n      print(project[0:-1])\n      if args.why:\n        if \"*\" in args.why or project[0:-1] in args.why:\n          prefix = project\n          for f in sources:\n            if f.startswith(prefix):\n              print(\"  \" + f)\n\n\nif __name__ == \"__main__\":\n  sys.exit(main(sys.argv))\n\n\n# vim: set ts=2 sw=2 sts=2 expandtab nocindent tw=100:\n"
  },
  {
    "path": "tools/zipalign/Android.bp",
    "content": "//\n// Copyright 2008 The Android Open Source Project\n//\n// Zip alignment tool\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"zipalign_defaults\",\n    target: {\n        windows: {\n            host_ldlibs: [\"-lpthread\"],\n            enabled: true,\n        },\n    },\n}\n\ncc_library_host_static {\n    name: \"libzipalign\",\n    srcs: [\n        \"ZipAlign.cpp\",\n        \"ZipEntry.cpp\",\n        \"ZipFile.cpp\",\n    ],\n    export_include_dirs: [\n        \"include\",\n    ],\n    cflags: [\"-Wall\", \"-Werror\"],\n\n    // NOTE: Do not add any shared_libs dependencies because they will break the\n    // static_sdk_tools target.\n    whole_static_libs: [\n        \"libutils\",\n        \"libcutils\",\n        \"liblog\",\n        \"libziparchive\",\n        \"libz\",\n        \"libbase\",\n        \"libzopfli\",\n    ],\n    defaults: [\"zipalign_defaults\"],\n}\n\ncc_binary_host {\n    name: \"zipalign\",\n    srcs: [\n        \"ZipAlignMain.cpp\",\n    ],\n    cflags: [\"-Wall\", \"-Werror\"],\n    static_libs: [\n        \"libzipalign\",\n    ],\n    defaults: [\"zipalign_defaults\"],\n}\n\ncc_test_host {\n    name: \"zipalign_tests\",\n    srcs: [\n        \"tests/src/*_test.cpp\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n    static_libs: [\n        \"libbase\",\n        \"libzipalign\",\n        \"libgmock\",\n    ],\n    data: [\n         \"tests/data/apkWithUncompressedSharedLibs.zip\",\n         \"tests/data/archiveWithOneDirectoryEntry.zip\",\n         \"tests/data/diffOrders.zip\",\n         \"tests/data/holes.zip\",\n         \"tests/data/unaligned.zip\",\n    ],\n    defaults: [\"zipalign_defaults\"],\n    test_suites: [\"general-tests\"],\n}\n"
  },
  {
    "path": "tools/zipalign/OWNERS",
    "content": "include platform/system/core:/janitors/OWNERS\nsanglardf@google.com\n"
  },
  {
    "path": "tools/zipalign/README.txt",
    "content": "zipalign -- zip archive alignment tool\n\nusage: zipalign [-f] [-v] <align> infile.zip outfile.zip\n       zipalign -c [-v] <align> infile.zip\n\n  -c : check alignment only (does not modify file)\n  -f : overwrite existing outfile.zip\n  -p : page align stored shared object files\n  -v : verbose output\n  <align> is in bytes, e.g. \"4\" provides 32-bit alignment\n  infile.zip is an existing Zip archive\n  outfile.zip will be created\n\n\nThe purpose of zipalign is to ensure that all uncompressed data starts\nwith a particular alignment relative to the start of the file.  This\nallows those portions to be accessed directly with mmap() even if they\ncontain binary data with alignment restrictions.\n\nSome data needs to be word-aligned for easy access, others might benefit\nfrom being page-aligned.  The adjustment is made by altering the size of\nthe \"extra\" field in the zip Local File Header sections.  Existing data\nin the \"extra\" fields may be altered by this process.\n\nCompressed data isn't very useful until it's uncompressed, so there's no\nneed to adjust its alignment.\n\nAlterations to the archive, such as renaming or deleting entries, will\npotentially disrupt the alignment of the modified entry and all later\nentries.  Files added to an \"aligned\" archive will not be aligned.\n\nBy default, zipalign will not overwrite an existing output file.  With the\n\"-f\" flag, an existing file will be overwritten.\n\nYou can use the \"-c\" flag to test whether a zip archive is properly aligned.\n\nThe \"-p\" flag aligns any file with a \".so\" extension, and which is stored\nuncompressed in the zip archive, to a 4096-byte page boundary.  This\nfacilitates directly loading shared libraries from inside a zip archive.\n\n"
  },
  {
    "path": "tools/zipalign/ZipAlign.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ZipFile.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nnamespace android {\n\n// An entry is considered a directory if it has a stored size of zero\n// and it ends with '/' or '\\' character.\nstatic bool isDirectory(ZipEntry* entry) {\n   if (entry->getUncompressedLen() != 0) {\n       return false;\n   }\n\n   const char* name = entry->getFileName();\n   size_t nameLength = strlen(name);\n   char lastChar = name[nameLength-1];\n   return lastChar == '/' || lastChar == '\\\\';\n}\n\nstatic int getAlignment(bool pageAlignSharedLibs, int defaultAlignment,\n    ZipEntry* pEntry, int pageSize) {\n    if (!pageAlignSharedLibs) {\n        return defaultAlignment;\n    }\n\n    const char* ext = strrchr(pEntry->getFileName(), '.');\n    if (ext && strcmp(ext, \".so\") == 0) {\n        return pageSize;\n    }\n\n    return defaultAlignment;\n}\n\n/*\n * Copy all entries from \"pZin\" to \"pZout\", aligning as needed.\n */\nstatic int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment, bool zopfli,\n    bool pageAlignSharedLibs, int pageSize)\n{\n    int numEntries = pZin->getNumEntries();\n    ZipEntry* pEntry;\n    status_t status;\n\n    for (int i = 0; i < numEntries; i++) {\n        ZipEntry* pNewEntry;\n        int padding = 0;\n\n        pEntry = pZin->getEntryByIndex(i);\n        if (pEntry == NULL) {\n            fprintf(stderr, \"ERROR: unable to retrieve entry %d\\n\", i);\n            return 1;\n        }\n\n        if (pEntry->isCompressed() || isDirectory(pEntry)) {\n            /* copy the entry without padding */\n            //printf(\"--- %s: orig at %ld len=%ld (compressed)\\n\",\n            //    pEntry->getFileName(), (long) pEntry->getFileOffset(),\n            //    (long) pEntry->getUncompressedLen());\n\n            if (zopfli) {\n                status = pZout->addRecompress(pZin, pEntry, &pNewEntry);\n            } else {\n                status = pZout->add(pZin, pEntry, padding, &pNewEntry);\n            }\n        } else {\n            const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry,\n                                             pageSize);\n\n            //printf(\"--- %s: orig at %ld(+%d) len=%ld, adding pad=%d\\n\",\n            //    pEntry->getFileName(), (long) pEntry->getFileOffset(),\n            //    bias, (long) pEntry->getUncompressedLen(), padding);\n            status = pZout->add(pZin, pEntry, alignTo, &pNewEntry);\n        }\n\n        if (status != OK)\n            return 1;\n        //printf(\" added '%s' at %ld (pad=%d)\\n\",\n        //    pNewEntry->getFileName(), (long) pNewEntry->getFileOffset(),\n        //    padding);\n    }\n\n    return 0;\n}\n\n/*\n * Process a file.  We open the input and output files, failing if the\n * output file exists and \"force\" wasn't specified.\n */\nint process(const char* inFileName, const char* outFileName,\n    int alignment, bool force, bool zopfli, bool pageAlignSharedLibs, int pageSize)\n{\n    ZipFile zin, zout;\n\n    //printf(\"PROCESS: align=%d in='%s' out='%s' force=%d\\n\",\n    //    alignment, inFileName, outFileName, force);\n\n    /* this mode isn't supported -- do a trivial check */\n    if (strcmp(inFileName, outFileName) == 0) {\n        fprintf(stderr, \"Input and output can't be same file\\n\");\n        return 1;\n    }\n\n    /* don't overwrite existing unless given permission */\n    if (!force && access(outFileName, F_OK) == 0) {\n        fprintf(stderr, \"Output file '%s' exists\\n\", outFileName);\n        return 1;\n    }\n\n    if (zin.open(inFileName, ZipFile::kOpenReadOnly) != OK) {\n        fprintf(stderr, \"Unable to open '%s' as zip archive: %s\\n\", inFileName, strerror(errno));\n        return 1;\n    }\n    if (zout.open(outFileName,\n            ZipFile::kOpenReadWrite|ZipFile::kOpenCreate|ZipFile::kOpenTruncate)\n        != OK)\n    {\n        fprintf(stderr, \"Unable to open '%s' as zip archive\\n\", outFileName);\n        return 1;\n    }\n\n    int result = copyAndAlign(&zin, &zout, alignment, zopfli, pageAlignSharedLibs,\n                              pageSize);\n    if (result != 0) {\n        printf(\"zipalign: failed rewriting '%s' to '%s'\\n\",\n            inFileName, outFileName);\n    }\n    return result;\n}\n\n/*\n * Verify the alignment of a zip archive.\n */\nint verify(const char* fileName, int alignment, bool verbose,\n    bool pageAlignSharedLibs, int pageSize)\n{\n    ZipFile zipFile;\n    bool foundBad = false;\n\n    if (verbose)\n        printf(\"Verifying alignment of %s (%d)...\\n\", fileName, alignment);\n\n    if (zipFile.open(fileName, ZipFile::kOpenReadOnly) != OK) {\n        fprintf(stderr, \"Unable to open '%s' for verification\\n\", fileName);\n        return 1;\n    }\n\n    int numEntries = zipFile.getNumEntries();\n    ZipEntry* pEntry;\n\n    for (int i = 0; i < numEntries; i++) {\n        pEntry = zipFile.getEntryByIndex(i);\n        if (pEntry->isCompressed()) {\n            if (verbose) {\n                printf(\"%8jd %s (OK - compressed)\\n\",\n                    (intmax_t) pEntry->getFileOffset(), pEntry->getFileName());\n            }\n        } else if(isDirectory(pEntry)) {\n            // Directory entries do not need to be aligned.\n            if (verbose)\n                printf(\"%8jd %s (OK - directory)\\n\",\n                       (intmax_t) pEntry->getFileOffset(), pEntry->getFileName());\n            continue;\n       } else {\n            off_t offset = pEntry->getFileOffset();\n            const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry,\n                                             pageSize);\n            if ((offset % alignTo) != 0) {\n                if (verbose) {\n                    printf(\"%8jd %s (BAD - %jd)\\n\",\n                        (intmax_t) offset, pEntry->getFileName(),\n                        (intmax_t) (offset % alignTo));\n                }\n                foundBad = true;\n            } else {\n                if (verbose) {\n                    printf(\"%8jd %s (OK)\\n\",\n                        (intmax_t) offset, pEntry->getFileName());\n                }\n            }\n        }\n    }\n\n    if (verbose)\n        printf(\"Verification %s\\n\", foundBad ? \"FAILED\" : \"successful\");\n\n    return foundBad ? 1 : 0;\n}\n\n} // namespace android\n"
  },
  {
    "path": "tools/zipalign/ZipAlignMain.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Zip alignment tool\n */\n\n#include \"ZipAlign.h\"\n\n#include <getopt.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nusing namespace android;\n\n/*\n * Show program usage.\n */\nvoid usage(void)\n{\n    fprintf(stderr, \"Zip alignment utility\\n\");\n    fprintf(stderr, \"Copyright (C) 2009 The Android Open Source Project\\n\\n\");\n    fprintf(stderr,\n        \"Usage: zipalign [-f] [-p] [-P <pagesize_kb>] [-v] [-z] <align> infile.zip outfile.zip\\n\"\n        \"       zipalign -c [-p] [-P <pagesize_kb>] [-v] <align> infile.zip\\n\\n\" );\n    fprintf(stderr,\n        \"  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\\n\");\n    fprintf(stderr, \"  -c: check alignment only (does not modify file)\\n\");\n    fprintf(stderr, \"  -f: overwrite existing outfile.zip\\n\");\n    fprintf(stderr, \"  -p: 4kb page-align uncompressed .so files\\n\");\n    fprintf(stderr, \"  -v: verbose output\\n\");\n    fprintf(stderr, \"  -z: recompress using Zopfli\\n\");\n    fprintf(stderr, \"  -P <pagesize_kb>: Align uncompressed .so files to the specified\\n\");\n    fprintf(stderr, \"                    page size. Valid values for <pagesize_kb> are 4, 16\\n\");\n    fprintf(stderr, \"                    and 64. '-P' cannot be used in combination with '-p'.\\n\");\n}\n\n\n/*\n * Parse args.\n */\nint main(int argc, char* const argv[])\n{\n    bool wantUsage = false;\n    bool check = false;\n    bool force = false;\n    bool verbose = false;\n    bool zopfli = false;\n    bool pageAlignSharedLibs = false;\n    int pageSize = 4096;\n    bool legacyPageAlignmentFlag = false;   // -p\n    bool pageAlignmentFlag = false;         // -P <pagesize_kb>\n    int result = 1;\n    int alignment;\n    char* endp;\n\n    int opt;\n\n    while ((opt = getopt(argc, argv, \"fcpvzP:\")) != -1) {\n        switch (opt) {\n        case 'c':\n            check = true;\n            break;\n        case 'f':\n            force = true;\n            break;\n        case 'v':\n            verbose = true;\n            break;\n        case 'z':\n            zopfli = true;\n            break;\n        case 'p':\n            legacyPageAlignmentFlag = true;\n            pageAlignSharedLibs = true;\n            pageSize = 4096;\n            break;\n        case 'P':\n            pageAlignmentFlag = true;\n            pageAlignSharedLibs = true;\n\n            if (!optarg) {\n                fprintf(stderr, \"ERROR: -P requires an argument\\n\");\n                wantUsage = true;\n                goto bail;\n            }\n\n            pageSize = atoi(optarg);\n            if (pageSize != 4 && pageSize != 16 && pageSize != 64) {\n                fprintf(stderr, \"ERROR: Invalid argument for -P: %s\\n\", optarg);\n                wantUsage = true;\n                goto bail;\n            }\n\n            pageSize *= 1024;  // Convert from kB to bytes.\n\n            break;\n        default:\n            fprintf(stderr, \"ERROR: unknown flag -%c\\n\", opt);\n            wantUsage = true;\n            goto bail;\n        }\n    }\n\n    if (legacyPageAlignmentFlag && pageAlignmentFlag) {\n            fprintf(stderr, \"ERROR: Invalid options: '-P <pagesize_kb>' and '-p'\"\n                            \"cannot be used in combination.\\n\");\n            wantUsage = true;\n            goto bail;\n    }\n\n    if (!((check && (argc - optind) == 2) || (!check && (argc - optind) == 3))) {\n        wantUsage = true;\n        goto bail;\n    }\n\n    alignment = strtol(argv[optind], &endp, 10);\n    if (*endp != '\\0' || alignment <= 0) {\n        fprintf(stderr, \"Invalid value for alignment: %s\\n\", argv[optind]);\n        wantUsage = true;\n        goto bail;\n    }\n\n    if (check) {\n        /* check existing archive for correct alignment */\n        result = verify(argv[optind + 1], alignment, verbose, pageAlignSharedLibs, pageSize);\n    } else {\n        /* create the new archive */\n        result = process(argv[optind + 1], argv[optind + 2], alignment, force, zopfli,\n                         pageAlignSharedLibs, pageSize);\n\n        /* trust, but verify */\n        if (result == 0) {\n            result = verify(argv[optind + 2], alignment, verbose, pageAlignSharedLibs, pageSize);\n        }\n    }\n\nbail:\n    if (wantUsage) {\n        usage();\n        result = 2;\n    }\n\n    return result;\n}\n"
  },
  {
    "path": "tools/zipalign/ZipEntry.cpp",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Access to entries in a Zip archive.\n//\n\n#define _POSIX_THREAD_SAFE_FUNCTIONS  // For mingw localtime_r().\n\n#define LOG_TAG \"zip\"\n\n#include \"ZipEntry.h\"\n#include <utils/Log.h>\n\n#include <assert.h>\n#include <inttypes.h>\n#include <stdio.h>\n#include <string.h>\n#include <time.h>\n\nnamespace android {\n\n/*\n * Initialize a new ZipEntry structure from a FILE* positioned at a\n * CentralDirectoryEntry.\n *\n * On exit, the file pointer will be at the start of the next CDE or\n * at the EOCD.\n */\nstatus_t ZipEntry::initFromCDE(FILE* fp)\n{\n    //ALOGV(\"initFromCDE ---\\n\");\n\n    /* read the CDE */\n    status_t result = mCDE.read(fp);\n    if (result != OK) {\n        ALOGD(\"mCDE.read failed\\n\");\n        return result;\n    }\n\n    //mCDE.dump();\n\n    /* using the info in the CDE, go load up the LFH */\n    off_t posn = ftello(fp);\n    if (fseeko(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {\n        ALOGD(\"local header seek failed (%\" PRIu32 \")\\n\",\n            mCDE.mLocalHeaderRelOffset);\n        return UNKNOWN_ERROR;\n    }\n\n    result = mLFH.read(fp);\n    if (result != OK) {\n        ALOGD(\"mLFH.read failed\\n\");\n        return result;\n    }\n\n    if (fseeko(fp, posn, SEEK_SET) != 0)\n        return UNKNOWN_ERROR;\n\n    //mLFH.dump();\n\n    /*\n     * We *might* need to read the Data Descriptor at this point and\n     * integrate it into the LFH.  If this bit is set, the CRC-32,\n     * compressed size, and uncompressed size will be zero.  In practice\n     * these seem to be rare.\n     */\n    bool hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;\n    if (hasDD) {\n        // do something clever\n        //ALOGD(\"+++ has data descriptor\\n\");\n    }\n\n    /*\n     * Check the LFH.  Note that this will fail if the \"kUsesDataDescr\"\n     * flag is set, because the LFH is incomplete.  (Not a problem, since we\n     * prefer the CDE values.)\n     */\n    if (!hasDD && !compareHeaders()) {\n        ALOGW(\"WARNING: header mismatch\\n\");\n        // keep going?\n    }\n\n    /*\n     * If the mVersionToExtract is greater than 20, we may have an\n     * issue unpacking the record -- could be encrypted, compressed\n     * with something we don't support, or use Zip64 extensions.  We\n     * can defer worrying about that to when we're extracting data.\n     */\n\n    return OK;\n}\n\n/*\n * Initialize a new entry.  Pass in the file name and an optional comment.\n *\n * Initializes the CDE and the LFH.\n */\nvoid ZipEntry::initNew(const char* fileName, const char* comment)\n{\n    assert(fileName != NULL && *fileName != '\\0');  // name required\n\n    /* most fields are properly initialized by constructor */\n    mCDE.mVersionMadeBy = kDefaultMadeBy;\n    mCDE.mVersionToExtract = kDefaultVersion;\n    mCDE.mCompressionMethod = kCompressStored;\n    mCDE.mFileNameLength = strlen(fileName);\n    if (comment != NULL)\n        mCDE.mFileCommentLength = strlen(comment);\n    mCDE.mExternalAttrs = 0x81b60020;   // matches what WinZip does\n\n    if (mCDE.mFileNameLength > 0) {\n        mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];\n        strcpy((char*) mCDE.mFileName, fileName);\n    }\n    if (mCDE.mFileCommentLength > 0) {\n        /* TODO: stop assuming null-terminated ASCII here? */\n        mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];\n        assert(comment != NULL);\n        strcpy((char*) mCDE.mFileComment, comment);\n    }\n\n    copyCDEtoLFH();\n}\n\n/*\n * Initialize a new entry, starting with the ZipEntry from a different\n * archive.\n *\n * Initializes the CDE and the LFH.\n */\nstatus_t ZipEntry::initFromExternal(const ZipEntry* pEntry)\n{\n    /*\n     * Copy everything in the CDE over, then fix up the hairy bits.\n     */\n    memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));\n\n    if (mCDE.mFileNameLength > 0) {\n        mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];\n        if (mCDE.mFileName == NULL)\n            return NO_MEMORY;\n        strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);\n    }\n    if (mCDE.mFileCommentLength > 0) {\n        mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];\n        if (mCDE.mFileComment == NULL)\n            return NO_MEMORY;\n        strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);\n    }\n    if (mCDE.mExtraFieldLength > 0) {\n        /* we null-terminate this, though it may not be a string */\n        mCDE.mExtraField = new uint8_t[mCDE.mExtraFieldLength+1];\n        if (mCDE.mExtraField == NULL)\n            return NO_MEMORY;\n        memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,\n            mCDE.mExtraFieldLength+1);\n    }\n\n    /* construct the LFH from the CDE */\n    copyCDEtoLFH();\n\n    /*\n     * The LFH \"extra\" field is independent of the CDE \"extra\", so we\n     * handle it here.\n     */\n    assert(mLFH.mExtraField == NULL);\n    mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;\n    if (mLFH.mExtraFieldLength > 0) {\n        mLFH.mExtraField = new uint8_t[mLFH.mExtraFieldLength+1];\n        if (mLFH.mExtraField == NULL)\n            return NO_MEMORY;\n        memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,\n            mLFH.mExtraFieldLength+1);\n    }\n\n    return OK;\n}\n\n/*\n * Insert pad bytes in the LFH by tweaking the \"extra\" field.  This will\n * potentially confuse something that put \"extra\" data in here earlier,\n * but I can't find an actual problem.\n */\nstatus_t ZipEntry::addPadding(int padding)\n{\n    if (padding <= 0)\n        return INVALID_OPERATION;\n\n    //ALOGI(\"HEY: adding %d pad bytes to existing %d in %s\\n\",\n    //    padding, mLFH.mExtraFieldLength, mCDE.mFileName);\n\n    if (mLFH.mExtraFieldLength > 0) {\n        /* extend existing field */\n        uint8_t* newExtra;\n\n        newExtra = new uint8_t[mLFH.mExtraFieldLength + padding];\n        if (newExtra == NULL)\n            return NO_MEMORY;\n        memset(newExtra + mLFH.mExtraFieldLength, 0, padding);\n        memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);\n\n        delete[] mLFH.mExtraField;\n        mLFH.mExtraField = newExtra;\n        mLFH.mExtraFieldLength += padding;\n    } else {\n        /* create new field */\n        mLFH.mExtraField = new uint8_t[padding];\n        memset(mLFH.mExtraField, 0, padding);\n        mLFH.mExtraFieldLength = padding;\n    }\n\n    return OK;\n}\n\n/*\n * Set the fields in the LFH equal to the corresponding fields in the CDE.\n *\n * This does not touch the LFH \"extra\" field.\n */\nvoid ZipEntry::copyCDEtoLFH(void)\n{\n    mLFH.mVersionToExtract  = mCDE.mVersionToExtract;\n    mLFH.mGPBitFlag         = mCDE.mGPBitFlag;\n    mLFH.mCompressionMethod = mCDE.mCompressionMethod;\n    mLFH.mLastModFileTime   = mCDE.mLastModFileTime;\n    mLFH.mLastModFileDate   = mCDE.mLastModFileDate;\n    mLFH.mCRC32             = mCDE.mCRC32;\n    mLFH.mCompressedSize    = mCDE.mCompressedSize;\n    mLFH.mUncompressedSize  = mCDE.mUncompressedSize;\n    mLFH.mFileNameLength    = mCDE.mFileNameLength;\n    // the \"extra field\" is independent\n\n    delete[] mLFH.mFileName;\n    if (mLFH.mFileNameLength > 0) {\n        mLFH.mFileName = new uint8_t[mLFH.mFileNameLength+1];\n        strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);\n    } else {\n        mLFH.mFileName = NULL;\n    }\n}\n\n/*\n * Set some information about a file after we add it.\n */\nvoid ZipEntry::setDataInfo(uint32_t uncompLen, uint32_t compLen, uint32_t crc32,\n    uint32_t compressionMethod)\n{\n    mCDE.mCompressionMethod = compressionMethod;\n    mCDE.mCRC32 = crc32;\n    mCDE.mCompressedSize = compLen;\n    mCDE.mUncompressedSize = uncompLen;\n    mCDE.mCompressionMethod = compressionMethod;\n    if (compressionMethod == kCompressDeflated) {\n        mCDE.mGPBitFlag |= 0x0002;      // indicates maximum compression used\n    }\n    copyCDEtoLFH();\n}\n\n/*\n * See if the data in mCDE and mLFH match up.  This is mostly useful for\n * debugging these classes, but it can be used to identify damaged\n * archives.\n *\n * Returns \"false\" if they differ.\n */\nbool ZipEntry::compareHeaders(void) const\n{\n    if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {\n        ALOGV(\"cmp: VersionToExtract\\n\");\n        return false;\n    }\n    if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {\n        ALOGV(\"cmp: GPBitFlag\\n\");\n        return false;\n    }\n    if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {\n        ALOGV(\"cmp: CompressionMethod\\n\");\n        return false;\n    }\n    if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {\n        ALOGV(\"cmp: LastModFileTime\\n\");\n        return false;\n    }\n    if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {\n        ALOGV(\"cmp: LastModFileDate\\n\");\n        return false;\n    }\n    if (mCDE.mCRC32 != mLFH.mCRC32) {\n        ALOGV(\"cmp: CRC32\\n\");\n        return false;\n    }\n    if (mCDE.mCompressedSize != mLFH.mCompressedSize) {\n        ALOGV(\"cmp: CompressedSize\\n\");\n        return false;\n    }\n    if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {\n        ALOGV(\"cmp: UncompressedSize\\n\");\n        return false;\n    }\n    if (mCDE.mFileNameLength != mLFH.mFileNameLength) {\n        ALOGV(\"cmp: FileNameLength\\n\");\n        return false;\n    }\n#if 0       // this seems to be used for padding, not real data\n    if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {\n        ALOGV(\"cmp: ExtraFieldLength\\n\");\n        return false;\n    }\n#endif\n    if (mCDE.mFileName != NULL) {\n        if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {\n            ALOGV(\"cmp: FileName\\n\");\n            return false;\n        }\n    }\n\n    return true;\n}\n\n\n/*\n * Convert the DOS date/time stamp into a UNIX time stamp.\n */\ntime_t ZipEntry::getModWhen(void) const\n{\n    struct tm parts;\n\n    parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;\n    parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;\n    parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;\n    parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);\n    parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;\n    parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;\n    parts.tm_wday = parts.tm_yday = 0;\n    parts.tm_isdst = -1;        // DST info \"not available\"\n\n    return mktime(&parts);\n}\n\n/*\n * Set the CDE/LFH timestamp from UNIX time.\n */\nvoid ZipEntry::setModWhen(time_t when)\n{\n    /* round up to an even number of seconds */\n    time_t even = (when & 1) ? (when + 1) : when;\n\n    /* expand */\n    struct tm tmResult;\n    struct tm* ptm = localtime_r(&even, &tmResult);\n\n    // The earliest valid time for ZIP file entries is 1980-01-01. See:\n    // https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html.\n    // Set any time before 1980 to 1980-01-01.\n    if (ptm->tm_year < 80) {\n        ptm->tm_year = 80;\n        ptm->tm_mon = 0;\n        ptm->tm_mday = 1;\n        ptm->tm_hour = 0;\n        ptm->tm_min = 0;\n        ptm->tm_sec = 0;\n    }\n\n    uint16_t zdate = static_cast<uint16_t>(\n        (ptm->tm_year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday);\n    uint16_t ztime = static_cast<uint16_t>(\n        ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1);\n\n    mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;\n    mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;\n}\n\n\n/*\n * ===========================================================================\n *      ZipEntry::LocalFileHeader\n * ===========================================================================\n */\n\n/*\n * Read a local file header.\n *\n * On entry, \"fp\" points to the signature at the start of the header.\n * On exit, \"fp\" points to the start of data.\n */\nstatus_t ZipEntry::LocalFileHeader::read(FILE* fp)\n{\n    status_t result = OK;\n    uint8_t buf[kLFHLen];\n\n    assert(mFileName == NULL);\n    assert(mExtraField == NULL);\n\n    if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {\n        ALOGD(\"whoops: didn't find expected signature\\n\");\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);\n    mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);\n    mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);\n    mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);\n    mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);\n    mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);\n    mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);\n    mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);\n    mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);\n    mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);\n\n    // TODO: validate sizes\n\n    /* grab filename */\n    if (mFileNameLength != 0) {\n        mFileName = new uint8_t[mFileNameLength+1];\n        if (mFileName == NULL) {\n            result = NO_MEMORY;\n            goto bail;\n        }\n        if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {\n            result = UNKNOWN_ERROR;\n            goto bail;\n        }\n        mFileName[mFileNameLength] = '\\0';\n    }\n\n    /* grab extra field */\n    if (mExtraFieldLength != 0) {\n        mExtraField = new uint8_t[mExtraFieldLength+1];\n        if (mExtraField == NULL) {\n            result = NO_MEMORY;\n            goto bail;\n        }\n        if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {\n            result = UNKNOWN_ERROR;\n            goto bail;\n        }\n        mExtraField[mExtraFieldLength] = '\\0';\n    }\n\nbail:\n    return result;\n}\n\n/*\n * Write a local file header.\n */\nstatus_t ZipEntry::LocalFileHeader::write(FILE* fp)\n{\n    uint8_t buf[kLFHLen];\n\n    ZipEntry::putLongLE(&buf[0x00], kSignature);\n    ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);\n    ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);\n    ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);\n    ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);\n    ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);\n    ZipEntry::putLongLE(&buf[0x0e], mCRC32);\n    ZipEntry::putLongLE(&buf[0x12], mCompressedSize);\n    ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);\n    ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);\n    ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);\n\n    if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)\n        return UNKNOWN_ERROR;\n\n    /* write filename */\n    if (mFileNameLength != 0) {\n        if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)\n            return UNKNOWN_ERROR;\n    }\n\n    /* write \"extra field\" */\n    if (mExtraFieldLength != 0) {\n        if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)\n            return UNKNOWN_ERROR;\n    }\n\n    return OK;\n}\n\n\n/*\n * Dump the contents of a LocalFileHeader object.\n */\nvoid ZipEntry::LocalFileHeader::dump(void) const\n{\n    ALOGD(\" LocalFileHeader contents:\\n\");\n    ALOGD(\"  versToExt=%\" PRIu16 \" gpBits=0x%04\" PRIx16 \" compression=%\" PRIu16 \"\\n\",\n        mVersionToExtract, mGPBitFlag, mCompressionMethod);\n    ALOGD(\"  modTime=0x%04\" PRIx16 \" modDate=0x%04\" PRIx16 \" crc32=0x%08\" PRIx32 \"\\n\",\n        mLastModFileTime, mLastModFileDate, mCRC32);\n    ALOGD(\"  compressedSize=%\" PRIu32 \" uncompressedSize=%\" PRIu32 \"\\n\",\n        mCompressedSize, mUncompressedSize);\n    ALOGD(\"  filenameLen=%\" PRIu16 \" extraLen=%\" PRIu16 \"\\n\",\n        mFileNameLength, mExtraFieldLength);\n    if (mFileName != NULL)\n        ALOGD(\"  filename: '%s'\\n\", mFileName);\n}\n\n\n/*\n * ===========================================================================\n *      ZipEntry::CentralDirEntry\n * ===========================================================================\n */\n\n/*\n * Read the central dir entry that appears next in the file.\n *\n * On entry, \"fp\" should be positioned on the signature bytes for the\n * entry.  On exit, \"fp\" will point at the signature word for the next\n * entry or for the EOCD.\n */\nstatus_t ZipEntry::CentralDirEntry::read(FILE* fp)\n{\n    status_t result = OK;\n    uint8_t buf[kCDELen];\n\n    /* no re-use */\n    assert(mFileName == NULL);\n    assert(mExtraField == NULL);\n    assert(mFileComment == NULL);\n\n    if (fread(buf, 1, kCDELen, fp) != kCDELen) {\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {\n        ALOGD(\"Whoops: didn't find expected signature\\n\");\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);\n    mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);\n    mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);\n    mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);\n    mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);\n    mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);\n    mCRC32 = ZipEntry::getLongLE(&buf[0x10]);\n    mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);\n    mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);\n    mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);\n    mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);\n    mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);\n    mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);\n    mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);\n    mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);\n    mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);\n\n    // TODO: validate sizes and offsets\n\n    /* grab filename */\n    if (mFileNameLength != 0) {\n        mFileName = new uint8_t[mFileNameLength+1];\n        if (mFileName == NULL) {\n            result = NO_MEMORY;\n            goto bail;\n        }\n        if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {\n            result = UNKNOWN_ERROR;\n            goto bail;\n        }\n        mFileName[mFileNameLength] = '\\0';\n    }\n\n    /* read \"extra field\" */\n    if (mExtraFieldLength != 0) {\n        mExtraField = new uint8_t[mExtraFieldLength+1];\n        if (mExtraField == NULL) {\n            result = NO_MEMORY;\n            goto bail;\n        }\n        if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {\n            result = UNKNOWN_ERROR;\n            goto bail;\n        }\n        mExtraField[mExtraFieldLength] = '\\0';\n    }\n\n\n    /* grab comment, if any */\n    if (mFileCommentLength != 0) {\n        mFileComment = new uint8_t[mFileCommentLength+1];\n        if (mFileComment == NULL) {\n            result = NO_MEMORY;\n            goto bail;\n        }\n        if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)\n        {\n            result = UNKNOWN_ERROR;\n            goto bail;\n        }\n        mFileComment[mFileCommentLength] = '\\0';\n    }\n\nbail:\n    return result;\n}\n\n/*\n * Write a central dir entry.\n */\nstatus_t ZipEntry::CentralDirEntry::write(FILE* fp)\n{\n    uint8_t buf[kCDELen];\n\n    ZipEntry::putLongLE(&buf[0x00], kSignature);\n    ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);\n    ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);\n    ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);\n    ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);\n    ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);\n    ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);\n    ZipEntry::putLongLE(&buf[0x10], mCRC32);\n    ZipEntry::putLongLE(&buf[0x14], mCompressedSize);\n    ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);\n    ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);\n    ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);\n    ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);\n    ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);\n    ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);\n    ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);\n    ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);\n\n    if (fwrite(buf, 1, kCDELen, fp) != kCDELen)\n        return UNKNOWN_ERROR;\n\n    /* write filename */\n    if (mFileNameLength != 0) {\n        if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)\n            return UNKNOWN_ERROR;\n    }\n\n    /* write \"extra field\" */\n    if (mExtraFieldLength != 0) {\n        if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)\n            return UNKNOWN_ERROR;\n    }\n\n    /* write comment */\n    if (mFileCommentLength != 0) {\n        if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)\n            return UNKNOWN_ERROR;\n    }\n\n    return OK;\n}\n\n/*\n * Dump the contents of a CentralDirEntry object.\n */\nvoid ZipEntry::CentralDirEntry::dump(void) const\n{\n    ALOGD(\" CentralDirEntry contents:\\n\");\n    ALOGD(\"  versMadeBy=%\" PRIu16 \" versToExt=%\" PRIu16 \" gpBits=0x%04\" PRIx16 \" compression=%\" PRIu16 \"\\n\",\n        mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);\n    ALOGD(\"  modTime=0x%04\" PRIx16 \" modDate=0x%04\" PRIx16 \" crc32=0x%08\" PRIx32 \"\\n\",\n        mLastModFileTime, mLastModFileDate, mCRC32);\n    ALOGD(\"  compressedSize=%\" PRIu32 \" uncompressedSize=%\" PRIu32 \"\\n\",\n        mCompressedSize, mUncompressedSize);\n    ALOGD(\"  filenameLen=%\" PRIu16 \" extraLen=%\" PRIu16 \" commentLen=%\" PRIu16 \"\\n\",\n        mFileNameLength, mExtraFieldLength, mFileCommentLength);\n    ALOGD(\"  diskNumStart=%\" PRIu16 \" intAttr=0x%04\" PRIx16 \" extAttr=0x%08\" PRIx32 \" relOffset=%\" PRIu32 \"\\n\",\n        mDiskNumberStart, mInternalAttrs, mExternalAttrs,\n        mLocalHeaderRelOffset);\n\n    if (mFileName != NULL)\n        ALOGD(\"  filename: '%s'\\n\", mFileName);\n    if (mFileComment != NULL)\n        ALOGD(\"  comment: '%s'\\n\", mFileComment);\n}\n\n} // namespace android\n\n"
  },
  {
    "path": "tools/zipalign/ZipEntry.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Zip archive entries.\n//\n// The ZipEntry class is tightly meshed with the ZipFile class.\n//\n#ifndef __LIBS_ZIPENTRY_H\n#define __LIBS_ZIPENTRY_H\n\n#include <utils/Errors.h>\n\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <time.h>\n\nnamespace android {\n\nclass ZipFile;\n\n/*\n * ZipEntry objects represent a single entry in a Zip archive.\n *\n * You can use one of these to get or set information about an entry, but\n * there are no functions here for accessing the data itself.  (We could\n * tuck a pointer to the ZipFile in here for convenience, but that raises\n * the likelihood of using ZipEntry objects after discarding the ZipFile.)\n *\n * File information is stored in two places: next to the file data (the Local\n * File Header, and possibly a Data Descriptor), and at the end of the file\n * (the Central Directory Entry).  The two must be kept in sync.\n */\nclass ZipEntry {\npublic:\n    friend class ZipFile;\n\n    ZipEntry(void)\n        : mDeleted(false), mMarked(false)\n        {}\n    ~ZipEntry(void) {}\n\n    /*\n     * Returns \"true\" if the data is compressed.\n     */\n    bool isCompressed(void) const {\n        return mCDE.mCompressionMethod != kCompressStored;\n    }\n    int getCompressionMethod(void) const { return mCDE.mCompressionMethod; }\n\n    /*\n     * Return the uncompressed length.\n     */\n    off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; }\n\n    /*\n     * Return the compressed length.  For uncompressed data, this returns\n     * the same thing as getUncompresesdLen().\n     */\n    off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }\n\n    /*\n     * Return the absolute file offset of the start of the compressed or\n     * uncompressed data.\n     */\n    off_t getFileOffset(void) const {\n        return mCDE.mLocalHeaderRelOffset +\n                LocalFileHeader::kLFHLen +\n                mLFH.mFileNameLength +\n                mLFH.mExtraFieldLength;\n    }\n\n    /*\n     * Return the data CRC.\n     */\n    uint32_t getCRC32(void) const { return mCDE.mCRC32; }\n\n    /*\n     * Return file modification time in UNIX seconds-since-epoch.\n     */\n    time_t getModWhen(void) const;\n\n    /*\n     * Return the archived file name.\n     */\n    const char* getFileName(void) const { return (const char*) mCDE.mFileName; }\n\n    /*\n     * Application-defined \"mark\".  Can be useful when synchronizing the\n     * contents of an archive with contents on disk.\n     */\n    bool getMarked(void) const { return mMarked; }\n    void setMarked(bool val) { mMarked = val; }\n\n    /*\n     * Some basic functions for raw data manipulation.  \"LE\" means\n     * Little Endian.\n     */\n    static inline uint16_t getShortLE(const uint8_t* buf) {\n        return buf[0] | (buf[1] << 8);\n    }\n    static inline uint32_t getLongLE(const uint8_t* buf) {\n        return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);\n    }\n    static inline void putShortLE(uint8_t* buf, uint16_t val) {\n        buf[0] = (uint8_t) val;\n        buf[1] = (uint8_t) (val >> 8);\n    }\n    static inline void putLongLE(uint8_t* buf, uint32_t val) {\n        buf[0] = (uint8_t) val;\n        buf[1] = (uint8_t) (val >> 8);\n        buf[2] = (uint8_t) (val >> 16);\n        buf[3] = (uint8_t) (val >> 24);\n    }\n\n    /* defined for Zip archives */\n    enum {\n        kCompressStored     = 0,        // no compression\n        // shrunk           = 1,\n        // reduced 1        = 2,\n        // reduced 2        = 3,\n        // reduced 3        = 4,\n        // reduced 4        = 5,\n        // imploded         = 6,\n        // tokenized        = 7,\n        kCompressDeflated   = 8,        // standard deflate\n        // Deflate64        = 9,\n        // lib imploded     = 10,\n        // reserved         = 11,\n        // bzip2            = 12,\n    };\n\n    /*\n     * Deletion flag.  If set, the entry will be removed on the next\n     * call to \"flush\".\n     */\n    bool getDeleted(void) const { return mDeleted; }\n\nprotected:\n    /*\n     * Initialize the structure from the file, which is pointing at\n     * our Central Directory entry.\n     */\n    status_t initFromCDE(FILE* fp);\n\n    /*\n     * Initialize the structure for a new file.  We need the filename\n     * and comment so that we can properly size the LFH area.  The\n     * filename is mandatory, the comment is optional.\n     */\n    void initNew(const char* fileName, const char* comment);\n\n    /*\n     * Initialize the structure with the contents of a ZipEntry from\n     * another file.\n     */\n    status_t initFromExternal(const ZipEntry* pEntry);\n\n    /*\n     * Add some pad bytes to the LFH.  We do this by adding or resizing\n     * the \"extra\" field.\n     */\n    status_t addPadding(int padding);\n\n    /*\n     * Set information about the data for this entry.\n     */\n    void setDataInfo(uint32_t uncompLen, uint32_t compLen, uint32_t crc32,\n        uint32_t compressionMethod);\n\n    /*\n     * Set the modification date.\n     */\n    void setModWhen(time_t when);\n\n    /*\n     * Return the offset of the local file header.\n     */\n    off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }\n\n    /*\n     * Set the offset of the local file header, relative to the start of\n     * the current file.\n     */\n    void setLFHOffset(off_t offset) {\n        mCDE.mLocalHeaderRelOffset = (uint32_t) offset;\n    }\n\n    /* mark for deletion; used by ZipFile::remove() */\n    void setDeleted(void) { mDeleted = true; }\n\nprivate:\n    /* these are private and not defined */\n    ZipEntry(const ZipEntry& src);\n    ZipEntry& operator=(const ZipEntry& src);\n\n    /* returns \"true\" if the CDE and the LFH agree */\n    bool compareHeaders(void) const;\n    void copyCDEtoLFH(void);\n\n    bool        mDeleted;       // set if entry is pending deletion\n    bool        mMarked;        // app-defined marker\n\n    /*\n     * Every entry in the Zip archive starts off with one of these.\n     */\n    class LocalFileHeader {\n    public:\n        LocalFileHeader(void) :\n            mVersionToExtract(0),\n            mGPBitFlag(0),\n            mCompressionMethod(0),\n            mLastModFileTime(0),\n            mLastModFileDate(0),\n            mCRC32(0),\n            mCompressedSize(0),\n            mUncompressedSize(0),\n            mFileNameLength(0),\n            mExtraFieldLength(0),\n            mFileName(NULL),\n            mExtraField(NULL)\n        {}\n        virtual ~LocalFileHeader(void) {\n            delete[] mFileName;\n            delete[] mExtraField;\n        }\n\n        status_t read(FILE* fp);\n        status_t write(FILE* fp);\n\n        // uint32_t mSignature;\n        uint16_t mVersionToExtract;\n        uint16_t mGPBitFlag;\n        uint16_t mCompressionMethod;\n        uint16_t mLastModFileTime;\n        uint16_t mLastModFileDate;\n        uint32_t mCRC32;\n        uint32_t mCompressedSize;\n        uint32_t mUncompressedSize;\n        uint16_t mFileNameLength;\n        uint16_t mExtraFieldLength;\n        uint8_t* mFileName;\n        uint8_t* mExtraField;\n\n        enum {\n            kSignature      = 0x04034b50,\n            kLFHLen         = 30,       // LocalFileHdr len, excl. var fields\n        };\n\n        void dump(void) const;\n    };\n\n    /*\n     * Every entry in the Zip archive has one of these in the \"central\n     * directory\" at the end of the file.\n     */\n    class CentralDirEntry {\n    public:\n        CentralDirEntry(void) :\n            mVersionMadeBy(0),\n            mVersionToExtract(0),\n            mGPBitFlag(0),\n            mCompressionMethod(0),\n            mLastModFileTime(0),\n            mLastModFileDate(0),\n            mCRC32(0),\n            mCompressedSize(0),\n            mUncompressedSize(0),\n            mFileNameLength(0),\n            mExtraFieldLength(0),\n            mFileCommentLength(0),\n            mDiskNumberStart(0),\n            mInternalAttrs(0),\n            mExternalAttrs(0),\n            mLocalHeaderRelOffset(0),\n            mFileName(NULL),\n            mExtraField(NULL),\n            mFileComment(NULL)\n        {}\n        ~CentralDirEntry(void) {\n            delete[] mFileName;\n            delete[] mExtraField;\n            delete[] mFileComment;\n        }\n\n        status_t read(FILE* fp);\n        status_t write(FILE* fp);\n\n        // uint32_t mSignature;\n        uint16_t mVersionMadeBy;\n        uint16_t mVersionToExtract;\n        uint16_t mGPBitFlag;\n        uint16_t mCompressionMethod;\n        uint16_t mLastModFileTime;\n        uint16_t mLastModFileDate;\n        uint32_t mCRC32;\n        uint32_t mCompressedSize;\n        uint32_t mUncompressedSize;\n        uint16_t mFileNameLength;\n        uint16_t mExtraFieldLength;\n        uint16_t mFileCommentLength;\n        uint16_t mDiskNumberStart;\n        uint16_t mInternalAttrs;\n        uint32_t mExternalAttrs;\n        uint32_t mLocalHeaderRelOffset;\n        uint8_t* mFileName;\n        uint8_t* mExtraField;\n        uint8_t* mFileComment;\n\n        void dump(void) const;\n\n        enum {\n            kSignature      = 0x02014b50,\n            kCDELen         = 46,       // CentralDirEnt len, excl. var fields\n        };\n    };\n\n    enum {\n        //kDataDescriptorSignature  = 0x08074b50,   // currently unused\n        kDataDescriptorLen  = 16,           // four 32-bit fields\n\n        kDefaultVersion     = 20,           // need deflate, nothing much else\n        kDefaultMadeBy      = 0x0317,       // 03=UNIX, 17=spec v2.3\n        kUsesDataDescr      = 0x0008,       // GPBitFlag bit 3\n    };\n\n    LocalFileHeader     mLFH;\n    CentralDirEntry     mCDE;\n};\n\n}; // namespace android\n\n#endif // __LIBS_ZIPENTRY_H\n"
  },
  {
    "path": "tools/zipalign/ZipFile.cpp",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Access to Zip archives.\n//\n\n#define LOG_TAG \"zip\"\n\n#include <utils/Log.h>\n#include <ziparchive/zip_archive.h>\n\n#include \"ZipFile.h\"\n\n#include <zlib.h>\n\n#include \"zopfli/deflate.h\"\n\n#include <memory.h>\n#include <sys/stat.h>\n#include <errno.h>\n#include <assert.h>\n#include <inttypes.h>\n\n_Static_assert(sizeof(off_t) == 8, \"off_t too small\");\n\nnamespace android {\n\n/*\n * Some environments require the \"b\", some choke on it.\n */\n#define FILE_OPEN_RO        \"rb\"\n#define FILE_OPEN_RW        \"r+b\"\n#define FILE_OPEN_RW_CREATE \"w+b\"\n\n/* should live somewhere else? */\nstatic status_t errnoToStatus(int err)\n{\n    if (err == ENOENT)\n        return NAME_NOT_FOUND;\n    else if (err == EACCES)\n        return PERMISSION_DENIED;\n    else\n        return UNKNOWN_ERROR;\n}\n\n/*\n * Open a file and parse its guts.\n */\nstatus_t ZipFile::open(const char* zipFileName, int flags)\n{\n    bool newArchive = false;\n\n    assert(mZipFp == NULL);     // no reopen\n\n    if ((flags & kOpenTruncate))\n        flags |= kOpenCreate;           // trunc implies create\n\n    if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))\n        return INVALID_OPERATION;       // not both\n    if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))\n        return INVALID_OPERATION;       // not neither\n    if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))\n        return INVALID_OPERATION;       // create requires write\n\n    if (flags & kOpenTruncate) {\n        newArchive = true;\n    } else {\n        newArchive = (access(zipFileName, F_OK) != 0);\n        if (!(flags & kOpenCreate) && newArchive) {\n            /* not creating, must already exist */\n            ALOGD(\"File %s does not exist\", zipFileName);\n            return NAME_NOT_FOUND;\n        }\n    }\n\n    /* open the file */\n    const char* openflags;\n    if (flags & kOpenReadWrite) {\n        if (newArchive)\n            openflags = FILE_OPEN_RW_CREATE;\n        else\n            openflags = FILE_OPEN_RW;\n    } else {\n        openflags = FILE_OPEN_RO;\n    }\n    mZipFp = fopen(zipFileName, openflags);\n    if (mZipFp == NULL) {\n        int err = errno;\n        ALOGD(\"fopen failed: %d\\n\", err);\n        return errnoToStatus(err);\n    }\n\n    status_t result;\n    if (!newArchive) {\n        /*\n         * Load the central directory.  If that fails, then this probably\n         * isn't a Zip archive.\n         */\n        result = readCentralDir();\n    } else {\n        /*\n         * Newly-created.  The EndOfCentralDir constructor actually\n         * sets everything to be the way we want it (all zeroes).  We\n         * set mNeedCDRewrite so that we create *something* if the\n         * caller doesn't add any files.  (We could also just unlink\n         * the file if it's brand new and nothing was added, but that's\n         * probably doing more than we really should -- the user might\n         * have a need for empty zip files.)\n         */\n        mNeedCDRewrite = true;\n        result = OK;\n    }\n\n    if (flags & kOpenReadOnly)\n        mReadOnly = true;\n    else\n        assert(!mReadOnly);\n\n    return result;\n}\n\n/*\n * Return the Nth entry in the archive.\n */\nZipEntry* ZipFile::getEntryByIndex(int idx) const\n{\n    if (idx < 0 || idx >= (int) mEntries.size())\n        return NULL;\n\n    return mEntries[idx];\n}\n\n/*\n * Find an entry by name.\n */\nZipEntry* ZipFile::getEntryByName(const char* fileName) const\n{\n    /*\n     * Do a stupid linear string-compare search.\n     *\n     * There are various ways to speed this up, especially since it's rare\n     * to intermingle changes to the archive with \"get by name\" calls.  We\n     * don't want to sort the mEntries vector itself, however, because\n     * it's used to recreate the Central Directory.\n     *\n     * (Hash table works, parallel list of pointers in sorted order is good.)\n     */\n    int idx;\n\n    for (idx = mEntries.size()-1; idx >= 0; idx--) {\n        ZipEntry* pEntry = mEntries[idx];\n        if (!pEntry->getDeleted() &&\n            strcmp(fileName, pEntry->getFileName()) == 0)\n        {\n            return pEntry;\n        }\n    }\n\n    return NULL;\n}\n\n/*\n * Empty the mEntries vector.\n */\nvoid ZipFile::discardEntries(void)\n{\n    int count = mEntries.size();\n\n    while (--count >= 0)\n        delete mEntries[count];\n\n    mEntries.clear();\n}\n\n\n/*\n * Find the central directory and read the contents.\n *\n * The fun thing about ZIP archives is that they may or may not be\n * readable from start to end.  In some cases, notably for archives\n * that were written to stdout, the only length information is in the\n * central directory at the end of the file.\n *\n * Of course, the central directory can be followed by a variable-length\n * comment field, so we have to scan through it backwards.  The comment\n * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff\n * itself, plus apparently sometimes people throw random junk on the end\n * just for the fun of it.\n *\n * This is all a little wobbly.  If the wrong value ends up in the EOCD\n * area, we're hosed.  This appears to be the way that everbody handles\n * it though, so we're in pretty good company if this fails.\n */\nstatus_t ZipFile::readCentralDir(void)\n{\n    fseeko(mZipFp, 0, SEEK_END);\n    off_t fileLength = ftello(mZipFp);\n    rewind(mZipFp);\n\n    /* too small to be a ZIP archive? */\n    if (fileLength < EndOfCentralDir::kEOCDLen) {\n        ALOGD(\"Length is %lld -- too small\\n\", (long long) fileLength);\n        return INVALID_OPERATION;\n    }\n\n    off_t seekStart;\n    size_t readAmount;\n    if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {\n        seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;\n        readAmount = EndOfCentralDir::kMaxEOCDSearch;\n    } else {\n        seekStart = 0;\n        readAmount = fileLength;\n    }\n    if (fseeko(mZipFp, seekStart, SEEK_SET) != 0) {\n        ALOGD(\"Failure seeking to end of zip at %lld\", (long long) seekStart);\n        return UNKNOWN_ERROR;\n    }\n\n    /* read the last part of the file into the buffer */\n    uint8_t buf[EndOfCentralDir::kMaxEOCDSearch];\n    if (fread(buf, 1, readAmount, mZipFp) != readAmount) {\n        if (feof(mZipFp)) {\n            ALOGW(\"fread %zu bytes failed, unexpected EOF\", readAmount);\n        } else {\n            ALOGW(\"fread %zu bytes failed, %s\", readAmount, strerror(errno));\n        }\n        return UNKNOWN_ERROR;\n    }\n\n    /* find the end-of-central-dir magic */\n    int i;\n    for (i = readAmount - 4; i >= 0; i--) {\n        if (buf[i] == 0x50 &&\n            ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)\n        {\n            ALOGV(\"+++ Found EOCD at buf+%d\\n\", i);\n            break;\n        }\n    }\n    if (i < 0) {\n        ALOGD(\"EOCD not found, not Zip\\n\");\n        return INVALID_OPERATION;\n    }\n\n    /* extract eocd values */\n    status_t result = mEOCD.readBuf(buf + i, readAmount - i);\n    if (result != OK) {\n        ALOGD(\"Failure reading %zu bytes of EOCD values\", readAmount - i);\n        return result;\n    }\n    //mEOCD.dump();\n\n    if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||\n        mEOCD.mNumEntries != mEOCD.mTotalNumEntries)\n    {\n        ALOGD(\"Archive spanning not supported\\n\");\n        return INVALID_OPERATION;\n    }\n\n    /*\n     * So far so good.  \"mCentralDirSize\" is the size in bytes of the\n     * central directory, so we can just seek back that far to find it.\n     * We can also seek forward mCentralDirOffset bytes from the\n     * start of the file.\n     *\n     * We're not guaranteed to have the rest of the central dir in the\n     * buffer, nor are we guaranteed that the central dir will have any\n     * sort of convenient size.  We need to skip to the start of it and\n     * read the header, then the other goodies.\n     *\n     * The only thing we really need right now is the file comment, which\n     * we're hoping to preserve.\n     */\n    if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {\n        ALOGD(\"Failure seeking to central dir offset %\" PRIu32 \"\\n\",\n             mEOCD.mCentralDirOffset);\n        return UNKNOWN_ERROR;\n    }\n\n    /*\n     * Loop through and read the central dir entries.\n     */\n    ALOGV(\"Scanning %\" PRIu16 \" entries...\\n\", mEOCD.mTotalNumEntries);\n    int entry;\n    for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {\n        ZipEntry* pEntry = new ZipEntry;\n\n        result = pEntry->initFromCDE(mZipFp);\n        if (result != OK) {\n            ALOGD(\"initFromCDE failed\\n\");\n            delete pEntry;\n            return result;\n        }\n\n        mEntries.add(pEntry);\n    }\n\n\n    /*\n     * If all went well, we should now be back at the EOCD.\n     */\n    {\n        uint8_t checkBuf[4];\n        if (fread(checkBuf, 1, 4, mZipFp) != 4) {\n            if (feof(mZipFp)) {\n                ALOGW(\"fread EOCD failed, unexpected EOF\");\n            } else {\n                ALOGW(\"fread EOCD failed, %s\", strerror(errno));\n            }\n            return INVALID_OPERATION;\n        }\n        if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {\n            ALOGD(\"EOCD read check failed\\n\");\n            return UNKNOWN_ERROR;\n        }\n        ALOGV(\"+++ EOCD read check passed\\n\");\n    }\n\n    return OK;\n}\n\n\n/*\n * Add a new file to the archive.\n *\n * This requires creating and populating a ZipEntry structure, and copying\n * the data into the file at the appropriate position.  The \"appropriate\n * position\" is the current location of the central directory, which we\n * casually overwrite (we can put it back later).\n *\n * If we were concerned about safety, we would want to make all changes\n * in a temp file and then overwrite the original after everything was\n * safely written.  Not really a concern for us.\n */\nstatus_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,\n    const char* storageName, int compressionMethod, ZipEntry** ppEntry)\n{\n    ZipEntry* pEntry = NULL;\n    status_t result = OK;\n    off_t lfhPosn, startPosn, endPosn, uncompressedLen;\n    uint32_t crc = 0;\n    time_t modWhen;\n\n    if (mReadOnly)\n        return INVALID_OPERATION;\n\n    assert(compressionMethod == ZipEntry::kCompressDeflated ||\n           compressionMethod == ZipEntry::kCompressStored);\n\n    /* make sure we're in a reasonable state */\n    assert(mZipFp != NULL);\n    assert(mEntries.size() == mEOCD.mTotalNumEntries);\n\n    /* make sure it doesn't already exist */\n    if (getEntryByName(storageName) != NULL)\n        return ALREADY_EXISTS;\n\n    FILE* inputFp = NULL;\n    if (!data) {\n        inputFp = fopen(fileName, FILE_OPEN_RO);\n        if (inputFp == NULL)\n            return errnoToStatus(errno);\n    }\n\n    if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    pEntry = new ZipEntry;\n    pEntry->initNew(storageName, NULL);\n\n    /*\n     * From here on out, failures are more interesting.\n     */\n    mNeedCDRewrite = true;\n\n    /*\n     * Write the LFH, even though it's still mostly blank.  We need it\n     * as a place-holder.  In theory the LFH isn't necessary, but in\n     * practice some utilities demand it.\n     */\n    lfhPosn = ftello(mZipFp);\n    pEntry->mLFH.write(mZipFp);\n    startPosn = ftello(mZipFp);\n\n    /*\n     * Copy the data in, possibly compressing it as we go.\n     */\n    if (compressionMethod == ZipEntry::kCompressDeflated) {\n        bool failed = false;\n        result = compressFpToFp(mZipFp, inputFp, data, size, &crc);\n        if (result != OK) {\n            ALOGD(\"compression failed, storing\\n\");\n            failed = true;\n        } else {\n            /*\n             * Make sure it has compressed \"enough\".  This probably ought\n             * to be set through an API call, but I don't expect our\n             * criteria to change over time.\n             */\n            off_t src = inputFp ? ftello(inputFp) : size;\n            off_t dst = ftello(mZipFp) - startPosn;\n            if (dst + (dst / 10) > src) {\n                ALOGD(\"insufficient compression (src=%lld dst=%lld), storing\\n\",\n                    (long long) src, (long long) dst);\n                failed = true;\n            }\n        }\n\n        if (failed) {\n            compressionMethod = ZipEntry::kCompressStored;\n            if (inputFp) rewind(inputFp);\n            fseeko(mZipFp, startPosn, SEEK_SET);\n            /* fall through to kCompressStored case */\n        }\n    }\n    /* handle \"no compression\" request, or failed compression from above */\n    if (compressionMethod == ZipEntry::kCompressStored) {\n        if (inputFp) {\n            result = copyFpToFp(mZipFp, inputFp, &crc);\n        } else {\n            result = copyDataToFp(mZipFp, data, size, &crc);\n        }\n        if (result != OK) {\n            // don't need to truncate; happens in CDE rewrite\n            ALOGD(\"failed copying data in\\n\");\n            goto bail;\n        }\n    }\n\n    // currently seeked to end of file\n    uncompressedLen = inputFp ? ftello(inputFp) : size;\n\n    /*\n     * We could write the \"Data Descriptor\", but there doesn't seem to\n     * be any point since we're going to go back and write the LFH.\n     *\n     * Update file offsets.\n     */\n    endPosn = ftello(mZipFp);            // seeked to end of compressed data\n\n    /*\n     * Success!  Fill out new values.\n     */\n    pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,\n        compressionMethod);\n    modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));\n    pEntry->setModWhen(modWhen);\n    pEntry->setLFHOffset(lfhPosn);\n    mEOCD.mNumEntries++;\n    mEOCD.mTotalNumEntries++;\n    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()\n    mEOCD.mCentralDirOffset = endPosn;\n\n    /*\n     * Go back and write the LFH.\n     */\n    if (fseeko(mZipFp, lfhPosn, SEEK_SET) != 0) {\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n    pEntry->mLFH.write(mZipFp);\n\n    /*\n     * Add pEntry to the list.\n     */\n    mEntries.add(pEntry);\n    if (ppEntry != NULL)\n        *ppEntry = pEntry;\n    pEntry = NULL;\n\nbail:\n    if (inputFp != NULL)\n        fclose(inputFp);\n    delete pEntry;\n    return result;\n}\n\n/*\n * Based on the current position in the output zip, assess where the entry\n * payload will end up if written as-is. If alignment is not satisfactory,\n * add some padding in the extra field.\n *\n */\nstatus_t ZipFile::alignEntry(android::ZipEntry* pEntry, uint32_t alignTo){\n    if (alignTo == 0 || alignTo == 1)\n        return OK;\n\n    // Calculate where the entry payload offset will end up if we were to write\n    // it as-is.\n    uint64_t expectedPayloadOffset = ftello(mZipFp) +\n        android::ZipEntry::LocalFileHeader::kLFHLen +\n        pEntry->mLFH.mFileNameLength +\n        pEntry->mLFH.mExtraFieldLength;\n\n    // If the alignment is not what was requested, add some padding in the extra\n    // so the payload ends up where is requested.\n    uint64_t alignDiff = alignTo - (expectedPayloadOffset % alignTo);\n    if (alignDiff == alignTo)\n        return OK;\n\n    return pEntry->addPadding(alignDiff);\n}\n\n/*\n * Add an entry by copying it from another zip file.  If \"padding\" is\n * nonzero, the specified number of bytes will be added to the \"extra\"\n * field in the header.\n *\n * If \"ppEntry\" is non-NULL, a pointer to the new entry will be returned.\n */\nstatus_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,\n    int alignTo, ZipEntry** ppEntry)\n{\n    ZipEntry* pEntry = NULL;\n    status_t result;\n    off_t lfhPosn, endPosn;\n\n    if (mReadOnly)\n        return INVALID_OPERATION;\n\n    /* make sure we're in a reasonable state */\n    assert(mZipFp != NULL);\n    assert(mEntries.size() == mEOCD.mTotalNumEntries);\n\n    if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    pEntry = new ZipEntry;\n    if (pEntry == NULL) {\n        result = NO_MEMORY;\n        goto bail;\n    }\n\n    result = pEntry->initFromExternal(pSourceEntry);\n    if (result != OK)\n        goto bail;\n\n    result = alignEntry(pEntry, alignTo);\n    if (result != OK)\n      goto bail;\n\n    /*\n     * From here on out, failures are more interesting.\n     */\n    mNeedCDRewrite = true;\n\n    /*\n     * Write the LFH.  Since we're not recompressing the data, we already\n     * have all of the fields filled out.\n     */\n    lfhPosn = ftello(mZipFp);\n    pEntry->mLFH.write(mZipFp);\n\n    /*\n     * Copy the data over.\n     *\n     * If the \"has data descriptor\" flag is set, we want to copy the DD\n     * fields as well.  This is a fixed-size area immediately following\n     * the data.\n     */\n    if (fseeko(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) {\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    off_t copyLen;\n    copyLen = pSourceEntry->getCompressedLen();\n    if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)\n        copyLen += ZipEntry::kDataDescriptorLen;\n\n    if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)\n        != OK)\n    {\n        ALOGW(\"copy of '%s' failed\\n\", pEntry->mCDE.mFileName);\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    /*\n     * Update file offsets.\n     */\n    endPosn = ftello(mZipFp);\n\n    /*\n     * Success!  Fill out new values.\n     */\n    pEntry->setLFHOffset(lfhPosn);      // sets mCDE.mLocalHeaderRelOffset\n    mEOCD.mNumEntries++;\n    mEOCD.mTotalNumEntries++;\n    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()\n    mEOCD.mCentralDirOffset = endPosn;\n\n    /*\n     * Add pEntry to the list.\n     */\n    mEntries.add(pEntry);\n    if (ppEntry != NULL)\n        *ppEntry = pEntry;\n    pEntry = NULL;\n\n    result = OK;\n\nbail:\n    delete pEntry;\n    return result;\n}\n\n/*\n * Add an entry by copying it from another zip file, recompressing with\n * Zopfli if already compressed.\n *\n * If \"ppEntry\" is non-NULL, a pointer to the new entry will be returned.\n */\nstatus_t ZipFile::addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,\n    ZipEntry** ppEntry)\n{\n    ZipEntry* pEntry = NULL;\n    status_t result;\n    off_t lfhPosn, uncompressedLen;\n\n    if (mReadOnly)\n        return INVALID_OPERATION;\n\n    /* make sure we're in a reasonable state */\n    assert(mZipFp != NULL);\n    assert(mEntries.size() == mEOCD.mTotalNumEntries);\n\n    if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    pEntry = new ZipEntry;\n    if (pEntry == NULL) {\n        result = NO_MEMORY;\n        goto bail;\n    }\n\n    result = pEntry->initFromExternal(pSourceEntry);\n    if (result != OK)\n        goto bail;\n\n    /*\n     * From here on out, failures are more interesting.\n     */\n    mNeedCDRewrite = true;\n\n    /*\n     * Write the LFH, even though it's still mostly blank.  We need it\n     * as a place-holder.  In theory the LFH isn't necessary, but in\n     * practice some utilities demand it.\n     */\n    lfhPosn = ftello(mZipFp);\n    pEntry->mLFH.write(mZipFp);\n\n    /*\n     * Copy the data over.\n     *\n     * If the \"has data descriptor\" flag is set, we want to copy the DD\n     * fields as well.  This is a fixed-size area immediately following\n     * the data.\n     */\n    if (fseeko(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) {\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    uncompressedLen = pSourceEntry->getUncompressedLen();\n\n    if (pSourceEntry->isCompressed()) {\n        void *buf = pSourceZip->uncompress(pSourceEntry);\n        if (buf == NULL) {\n            result = NO_MEMORY;\n            goto bail;\n        }\n        off_t startPosn = ftello(mZipFp);\n        uint32_t crc;\n        if (compressFpToFp(mZipFp, NULL, buf, uncompressedLen, &crc) != OK) {\n            ALOGW(\"recompress of '%s' failed\\n\", pEntry->mCDE.mFileName);\n            result = UNKNOWN_ERROR;\n            free(buf);\n            goto bail;\n        }\n        off_t endPosn = ftello(mZipFp);\n        pEntry->setDataInfo(uncompressedLen, endPosn - startPosn,\n            pSourceEntry->getCRC32(), ZipEntry::kCompressDeflated);\n        free(buf);\n    } else {\n        off_t copyLen = pSourceEntry->getCompressedLen();\n        if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)\n            copyLen += ZipEntry::kDataDescriptorLen;\n\n        if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)\n            != OK)\n        {\n            ALOGW(\"copy of '%s' failed\\n\", pEntry->mCDE.mFileName);\n            result = UNKNOWN_ERROR;\n            goto bail;\n        }\n    }\n\n    /*\n     * Success!  Fill out new values.\n     */\n    pEntry->setLFHOffset(lfhPosn);\n    mEOCD.mNumEntries++;\n    mEOCD.mTotalNumEntries++;\n    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()\n    mEOCD.mCentralDirOffset = ftello(mZipFp);\n\n    /*\n     * Go back and write the LFH.\n     */\n    if (fseeko(mZipFp, lfhPosn, SEEK_SET) != 0) {\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n    pEntry->mLFH.write(mZipFp);\n\n    /*\n     * Add pEntry to the list.\n     */\n    mEntries.add(pEntry);\n    if (ppEntry != NULL)\n        *ppEntry = pEntry;\n    pEntry = NULL;\n\n    result = OK;\n\nbail:\n    delete pEntry;\n    return result;\n}\n\n/*\n * Copy all of the bytes in \"src\" to \"dst\".\n *\n * On exit, \"srcFp\" will be seeked to the end of the file, and \"dstFp\"\n * will be seeked immediately past the data.\n */\nstatus_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32)\n{\n    uint8_t tmpBuf[32768];\n    size_t count;\n\n    *pCRC32 = crc32(0L, Z_NULL, 0);\n\n    while (1) {\n        count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);\n        if (ferror(srcFp) || ferror(dstFp)) {\n            status_t status = errnoToStatus(errno);\n            ALOGW(\"fread %zu bytes failed, %s\", count, strerror(errno));\n            return status;\n        }\n        if (count == 0)\n            break;\n\n        *pCRC32 = crc32(*pCRC32, tmpBuf, count);\n\n        if (fwrite(tmpBuf, 1, count, dstFp) != count) {\n            ALOGW(\"fwrite %zu bytes failed, %s\", count, strerror(errno));\n            return UNKNOWN_ERROR;\n        }\n    }\n\n    return OK;\n}\n\n/*\n * Copy all of the bytes in \"src\" to \"dst\".\n *\n * On exit, \"dstFp\" will be seeked immediately past the data.\n */\nstatus_t ZipFile::copyDataToFp(FILE* dstFp,\n    const void* data, size_t size, uint32_t* pCRC32)\n{\n    *pCRC32 = crc32(0L, Z_NULL, 0);\n    if (size > 0) {\n        *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);\n        if (fwrite(data, 1, size, dstFp) != size) {\n            ALOGW(\"fwrite %zu bytes failed, %s\", size, strerror(errno));\n            return UNKNOWN_ERROR;\n        }\n    }\n\n    return OK;\n}\n\n/*\n * Copy some of the bytes in \"src\" to \"dst\".\n *\n * If \"pCRC32\" is NULL, the CRC will not be computed.\n *\n * On exit, \"srcFp\" will be seeked to the end of the file, and \"dstFp\"\n * will be seeked immediately past the data just written.\n */\nstatus_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, size_t length,\n    uint32_t* pCRC32)\n{\n    uint8_t tmpBuf[32768];\n    size_t count;\n\n    if (pCRC32 != NULL)\n        *pCRC32 = crc32(0L, Z_NULL, 0);\n\n    while (length) {\n        size_t readSize;\n\n        readSize = sizeof(tmpBuf);\n        if (readSize > length)\n            readSize = length;\n\n        count = fread(tmpBuf, 1, readSize, srcFp);\n        if (count != readSize) {     // error or unexpected EOF\n            if (feof(srcFp)) {\n                ALOGW(\"fread %zu bytes failed, unexpected EOF\", readSize);\n            } else {\n                ALOGW(\"fread %zu bytes failed, %s\", readSize, strerror(errno));\n            }\n            return UNKNOWN_ERROR;\n        }\n\n        if (pCRC32 != NULL)\n            *pCRC32 = crc32(*pCRC32, tmpBuf, count);\n\n        if (fwrite(tmpBuf, 1, count, dstFp) != count) {\n            ALOGW(\"fwrite %zu bytes failed, %s\", count, strerror(errno));\n            return UNKNOWN_ERROR;\n        }\n\n        length -= readSize;\n    }\n\n    return OK;\n}\n\n/*\n * Compress all of the data in \"srcFp\" and write it to \"dstFp\".\n *\n * On exit, \"srcFp\" will be seeked to the end of the file, and \"dstFp\"\n * will be seeked immediately past the compressed data.\n */\nstatus_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,\n    const void* data, size_t size, uint32_t* pCRC32)\n{\n    status_t result = OK;\n    const size_t kBufSize = 1024 * 1024;\n    uint8_t* inBuf = NULL;\n    uint8_t* outBuf = NULL;\n    size_t outSize = 0;\n    bool atEof = false;     // no feof() aviailable yet\n    uint32_t crc;\n    ZopfliOptions options;\n    unsigned char bp = 0;\n\n    ZopfliInitOptions(&options);\n\n    crc = crc32(0L, Z_NULL, 0);\n\n    if (data) {\n        crc = crc32(crc, (const unsigned char*)data, size);\n        ZopfliDeflate(&options, 2, true, (const unsigned char*)data, size, &bp,\n            &outBuf, &outSize);\n    } else {\n        /*\n         * Create an input buffer and an output buffer.\n         */\n        inBuf = new uint8_t[kBufSize];\n        if (inBuf == NULL) {\n            result = NO_MEMORY;\n            goto bail;\n        }\n\n        /*\n         * Loop while we have data.\n         */\n        do {\n            size_t getSize;\n            getSize = fread(inBuf, 1, kBufSize, srcFp);\n            if (ferror(srcFp)) {\n                ALOGD(\"deflate read failed (errno=%d)\\n\", errno);\n                result = UNKNOWN_ERROR;\n                delete[] inBuf;\n                goto bail;\n            }\n            if (getSize < kBufSize) {\n                ALOGV(\"+++  got %zu bytes, EOF reached\\n\", getSize);\n                atEof = true;\n            }\n\n            crc = crc32(crc, inBuf, getSize);\n            ZopfliDeflate(&options, 2, atEof, inBuf, getSize, &bp, &outBuf, &outSize);\n        } while (!atEof);\n        delete[] inBuf;\n    }\n\n    ALOGV(\"+++ writing %zu bytes\\n\", outSize);\n    if (fwrite(outBuf, 1, outSize, dstFp) != outSize) {\n        ALOGW(\"fwrite %zu bytes failed, %s\", outSize, strerror(errno));\n        result = UNKNOWN_ERROR;\n        goto bail;\n    }\n\n    *pCRC32 = crc;\n\nbail:\n    free(outBuf);\n\n    return result;\n}\n\n/*\n * Mark an entry as deleted.\n *\n * We will eventually need to crunch the file down, but if several files\n * are being removed (perhaps as part of an \"update\" process) we can make\n * things considerably faster by deferring the removal to \"flush\" time.\n */\nstatus_t ZipFile::remove(ZipEntry* pEntry)\n{\n    /*\n     * Should verify that pEntry is actually part of this archive, and\n     * not some stray ZipEntry from a different file.\n     */\n\n    /* mark entry as deleted, and mark archive as dirty */\n    pEntry->setDeleted();\n    mNeedCDRewrite = true;\n    return OK;\n}\n\n/*\n * Flush any pending writes.\n *\n * In particular, this will crunch out deleted entries, and write the\n * Central Directory and EOCD if we have stomped on them.\n */\nstatus_t ZipFile::flush(void)\n{\n    status_t result = OK;\n    off_t eocdPosn;\n    int i, count;\n\n    if (mReadOnly)\n        return INVALID_OPERATION;\n    if (!mNeedCDRewrite)\n        return OK;\n\n    assert(mZipFp != NULL);\n\n    result = crunchArchive();\n    if (result != OK)\n        return result;\n\n    if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) return UNKNOWN_ERROR;\n\n    count = mEntries.size();\n    for (i = 0; i < count; i++) {\n        ZipEntry* pEntry = mEntries[i];\n        pEntry->mCDE.write(mZipFp);\n    }\n\n    eocdPosn = ftello(mZipFp);\n    mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;\n\n    mEOCD.write(mZipFp);\n\n    /*\n     * If we had some stuff bloat up during compression and get replaced\n     * with plain files, or if we deleted some entries, there's a lot\n     * of wasted space at the end of the file.  Remove it now.\n     */\n    if (ftruncate(fileno(mZipFp), ftello(mZipFp)) != 0) {\n        ALOGW(\"ftruncate failed %lld: %s\\n\", (long long) ftello(mZipFp), strerror(errno));\n        // not fatal\n    }\n\n    /* should we clear the \"newly added\" flag in all entries now? */\n\n    mNeedCDRewrite = false;\n    return OK;\n}\n\n/*\n * Crunch deleted files out of an archive by shifting the later files down.\n *\n * Because we're not using a temp file, we do the operation inside the\n * current file.\n */\nstatus_t ZipFile::crunchArchive(void)\n{\n    status_t result = OK;\n    int i, count;\n    long delCount, adjust;\n\n#if 0\n    printf(\"CONTENTS:\\n\");\n    for (i = 0; i < (int) mEntries.size(); i++) {\n        printf(\" %d: lfhOff=%ld del=%d\\n\",\n            i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());\n    }\n    printf(\"  END is %ld\\n\", (long) mEOCD.mCentralDirOffset);\n#endif\n\n    /*\n     * Roll through the set of files, shifting them as appropriate.  We\n     * could probably get a slight performance improvement by sliding\n     * multiple files down at once (because we could use larger reads\n     * when operating on batches of small files), but it's not that useful.\n     */\n    count = mEntries.size();\n    delCount = adjust = 0;\n    for (i = 0; i < count; i++) {\n        ZipEntry* pEntry = mEntries[i];\n        long span;\n\n        if (pEntry->getLFHOffset() != 0) {\n            long nextOffset;\n\n            /* Get the length of this entry by finding the offset\n             * of the next entry.  Directory entries don't have\n             * file offsets, so we need to find the next non-directory\n             * entry.\n             */\n            nextOffset = 0;\n            for (int ii = i+1; nextOffset == 0 && ii < count; ii++)\n                nextOffset = mEntries[ii]->getLFHOffset();\n            if (nextOffset == 0)\n                nextOffset = mEOCD.mCentralDirOffset;\n            span = nextOffset - pEntry->getLFHOffset();\n\n            assert(span >= ZipEntry::LocalFileHeader::kLFHLen);\n        } else {\n            /* This is a directory entry.  It doesn't have\n             * any actual file contents, so there's no need to\n             * move anything.\n             */\n            span = 0;\n        }\n\n        //printf(\"+++ %d: off=%ld span=%ld del=%d [count=%d]\\n\",\n        //    i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);\n\n        if (pEntry->getDeleted()) {\n            adjust += span;\n            delCount++;\n\n            delete pEntry;\n            mEntries.removeAt(i);\n\n            /* adjust loop control */\n            count--;\n            i--;\n        } else if (span != 0 && adjust > 0) {\n            /* shuffle this entry back */\n            //printf(\"+++ Shuffling '%s' back %ld\\n\",\n            //    pEntry->getFileName(), adjust);\n            result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,\n                        pEntry->getLFHOffset(), span);\n            if (result != OK) {\n                /* this is why you use a temp file */\n                ALOGE(\"error during crunch - archive is toast\\n\");\n                return result;\n            }\n\n            pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);\n        }\n    }\n\n    /*\n     * Fix EOCD info.  We have to wait until the end to do some of this\n     * because we use mCentralDirOffset to determine \"span\" for the\n     * last entry.\n     */\n    mEOCD.mCentralDirOffset -= adjust;\n    mEOCD.mNumEntries -= delCount;\n    mEOCD.mTotalNumEntries -= delCount;\n    mEOCD.mCentralDirSize = 0;  // mark invalid; set by flush()\n\n    assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);\n    assert(mEOCD.mNumEntries == count);\n\n    return result;\n}\n\n/*\n * Works like memmove(), but on pieces of a file.\n */\nstatus_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)\n{\n    if (dst == src || n <= 0)\n        return OK;\n\n    uint8_t readBuf[32768];\n\n    if (dst < src) {\n        /* shift stuff toward start of file; must read from start */\n        while (n != 0) {\n            size_t getSize = sizeof(readBuf);\n            if (getSize > n)\n                getSize = n;\n\n            if (fseeko(fp, src, SEEK_SET) != 0) {\n                ALOGW(\"filemove src seek %lld failed, %s\",\n                    (long long) src, strerror(errno));\n                return UNKNOWN_ERROR;\n            }\n\n            if (fread(readBuf, 1, getSize, fp) != getSize) {\n                if (feof(fp)) {\n                    ALOGW(\"fread %zu bytes off=%lld failed, unexpected EOF\",\n                        getSize, (long long) src);\n                } else {\n                    ALOGW(\"fread %zu bytes off=%lld failed, %s\",\n                        getSize, (long long) src, strerror(errno));\n                }\n                return UNKNOWN_ERROR;\n            }\n\n            if (fseeko(fp, dst, SEEK_SET) != 0) {\n                ALOGW(\"filemove dst seek %lld failed, %s\",\n                    (long long) dst, strerror(errno));\n                return UNKNOWN_ERROR;\n            }\n\n            if (fwrite(readBuf, 1, getSize, fp) != getSize) {\n                ALOGW(\"filemove write %zu off=%lld failed, %s\",\n                    getSize, (long long) dst, strerror(errno));\n                return UNKNOWN_ERROR;\n            }\n\n            src += getSize;\n            dst += getSize;\n            n -= getSize;\n        }\n    } else {\n        /* shift stuff toward end of file; must read from end */\n        assert(false);      // write this someday, maybe\n        return UNKNOWN_ERROR;\n    }\n\n    return OK;\n}\n\n\n/*\n * Get the modification time from a file descriptor.\n */\ntime_t ZipFile::getModTime(int fd)\n{\n    struct stat sb;\n\n    if (fstat(fd, &sb) < 0) {\n        ALOGD(\"HEY: fstat on fd %d failed\\n\", fd);\n        return (time_t) -1;\n    }\n\n    return sb.st_mtime;\n}\n\n\n#if 0       /* this is a bad idea */\n/*\n * Get a copy of the Zip file descriptor.\n *\n * We don't allow this if the file was opened read-write because we tend\n * to leave the file contents in an uncertain state between calls to\n * flush().  The duplicated file descriptor should only be valid for reads.\n */\nint ZipFile::getZipFd(void) const\n{\n    if (!mReadOnly)\n        return INVALID_OPERATION;\n    assert(mZipFp != NULL);\n\n    int fd;\n    fd = dup(fileno(mZipFp));\n    if (fd < 0) {\n        ALOGD(\"didn't work, errno=%d\\n\", errno);\n    }\n\n    return fd;\n}\n#endif\n\n\n#if 0\n/*\n * Expand data.\n */\nbool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const\n{\n    return false;\n}\n#endif\n\nclass BufferWriter : public zip_archive::Writer {\n  public:\n    BufferWriter(void* buf, size_t size) : Writer(),\n        buf_(reinterpret_cast<uint8_t*>(buf)), size_(size), bytes_written_(0) {}\n\n    bool Append(uint8_t* buf, size_t buf_size) override {\n        if (bytes_written_ + buf_size > size_) {\n            return false;\n        }\n\n        memcpy(buf_ + bytes_written_, buf, buf_size);\n        bytes_written_ += buf_size;\n        return true;\n    }\n\n  private:\n    uint8_t* const buf_;\n    const size_t size_;\n    size_t bytes_written_;\n};\n\nclass FileReader : public zip_archive::Reader {\n  public:\n    FileReader(FILE* fp) : Reader(), fp_(fp), current_offset_(0) {\n    }\n\n    bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {\n        // Data is usually requested sequentially, so this helps avoid pointless\n        // seeks every time we perform a read. There's an impedence mismatch\n        // here because the original API was designed around pread and pwrite.\n        if (offset != current_offset_) {\n            if (fseeko(fp_, offset, SEEK_SET) != 0) {\n                return false;\n            }\n\n            current_offset_ = offset;\n        }\n\n        size_t read = fread(buf, 1, len, fp_);\n        if (read != len) {\n            return false;\n        }\n\n        current_offset_ += read;\n        return true;\n    }\n\n  private:\n    FILE* fp_;\n    mutable off64_t current_offset_;\n};\n\n// free the memory when you're done\nvoid* ZipFile::uncompress(const ZipEntry* entry) const\n{\n    size_t unlen = entry->getUncompressedLen();\n    size_t clen = entry->getCompressedLen();\n\n    void* buf = malloc(unlen);\n    if (buf == NULL) {\n        return NULL;\n    }\n\n    fseeko(mZipFp, 0, SEEK_SET);\n\n    off_t offset = entry->getFileOffset();\n    if (fseeko(mZipFp, offset, SEEK_SET) != 0) {\n        goto bail;\n    }\n\n    switch (entry->getCompressionMethod())\n    {\n        case ZipEntry::kCompressStored: {\n            ssize_t amt = fread(buf, 1, unlen, mZipFp);\n            if (amt != (ssize_t)unlen) {\n                goto bail;\n            }\n#if 0\n            printf(\"data...\\n\");\n            const unsigned char* p = (unsigned char*)buf;\n            const unsigned char* end = p+unlen;\n            for (int i=0; i<32 && p < end; i++) {\n                printf(\"0x%08x \", (int)(offset+(i*0x10)));\n                for (int j=0; j<0x10 && p < end; j++) {\n                    printf(\" %02x\", *p);\n                    p++;\n                }\n                printf(\"\\n\");\n            }\n#endif\n\n            }\n            break;\n        case ZipEntry::kCompressDeflated: {\n            const FileReader reader(mZipFp);\n            BufferWriter writer(buf, unlen);\n            if (zip_archive::Inflate(reader, clen, unlen, &writer, nullptr) != 0) {\n                goto bail;\n            }\n            break;\n        }\n        default:\n            goto bail;\n    }\n    return buf;\n\nbail:\n    free(buf);\n    return NULL;\n}\n\n\n/*\n * ===========================================================================\n *      ZipFile::EndOfCentralDir\n * ===========================================================================\n */\n\n/*\n * Read the end-of-central-dir fields.\n *\n * \"buf\" should be positioned at the EOCD signature, and should contain\n * the entire EOCD area including the comment.\n */\nstatus_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len)\n{\n    /* don't allow re-use */\n    assert(mComment == NULL);\n\n    if (len < kEOCDLen) {\n        /* looks like ZIP file got truncated */\n        ALOGD(\" Zip EOCD: expected >= %d bytes, found %d\\n\",\n            kEOCDLen, len);\n        return INVALID_OPERATION;\n    }\n\n    /* this should probably be an assert() */\n    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)\n        return UNKNOWN_ERROR;\n\n    mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);\n    mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);\n    mNumEntries = ZipEntry::getShortLE(&buf[0x08]);\n    mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);\n    mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);\n    mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);\n    mCommentLen = ZipEntry::getShortLE(&buf[0x14]);\n\n    // TODO: validate mCentralDirOffset\n\n    if (mCommentLen > 0) {\n        if (kEOCDLen + mCommentLen > len) {\n            ALOGD(\"EOCD(%d) + comment(%\" PRIu16 \") exceeds len (%d)\\n\",\n                kEOCDLen, mCommentLen, len);\n            return UNKNOWN_ERROR;\n        }\n        mComment = new uint8_t[mCommentLen];\n        memcpy(mComment, buf + kEOCDLen, mCommentLen);\n    }\n\n    return OK;\n}\n\n/*\n * Write an end-of-central-directory section.\n */\nstatus_t ZipFile::EndOfCentralDir::write(FILE* fp)\n{\n    uint8_t buf[kEOCDLen];\n\n    ZipEntry::putLongLE(&buf[0x00], kSignature);\n    ZipEntry::putShortLE(&buf[0x04], mDiskNumber);\n    ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);\n    ZipEntry::putShortLE(&buf[0x08], mNumEntries);\n    ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);\n    ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);\n    ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);\n    ZipEntry::putShortLE(&buf[0x14], mCommentLen);\n\n    if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) {\n        ALOGW(\"fwrite EOCD failed, %s\", strerror(errno));\n        return UNKNOWN_ERROR;\n    }\n    if (mCommentLen > 0) {\n        assert(mComment != NULL);\n        if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) {\n            ALOGW(\"fwrite %d bytes failed, %s\",\n                (int) mCommentLen, strerror(errno));\n            return UNKNOWN_ERROR;\n        }\n    }\n\n    return OK;\n}\n\n/*\n * Dump the contents of an EndOfCentralDir object.\n */\nvoid ZipFile::EndOfCentralDir::dump(void) const\n{\n    ALOGD(\" EndOfCentralDir contents:\\n\");\n    ALOGD(\"  diskNum=%\" PRIu16 \" diskWCD=%\" PRIu16 \" numEnt=%\" PRIu16 \" totalNumEnt=%\" PRIu16 \"\\n\",\n        mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);\n    ALOGD(\"  centDirSize=%\" PRIu32 \" centDirOff=%\" PRIu32 \" commentLen=%\" PRIu32 \"\\n\",\n        mCentralDirSize, mCentralDirOffset, mCommentLen);\n}\n\n} // namespace android\n"
  },
  {
    "path": "tools/zipalign/ZipFile.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// General-purpose Zip archive access.  This class allows both reading and\n// writing to Zip archives, including deletion of existing entries.\n//\n#ifndef __LIBS_ZIPFILE_H\n#define __LIBS_ZIPFILE_H\n\n#include <utils/Vector.h>\n#include <utils/Errors.h>\n#include <stdio.h>\n\n#include \"ZipEntry.h\"\n\nnamespace android {\n\n/*\n * Manipulate a Zip archive.\n *\n * Some changes will not be visible in the until until \"flush\" is called.\n *\n * The correct way to update a file archive is to make all changes to a\n * copy of the archive in a temporary file, and then unlink/rename over\n * the original after everything completes.  Because we're only interested\n * in using this for packaging, we don't worry about such things.  Crashing\n * after making changes and before flush() completes could leave us with\n * an unusable Zip archive.\n */\nclass ZipFile {\npublic:\n    ZipFile(void)\n      : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)\n      {}\n    ~ZipFile(void) {\n        if (!mReadOnly)\n            flush();\n        if (mZipFp != NULL)\n            fclose(mZipFp);\n        discardEntries();\n    }\n\n    /*\n     * Open a new or existing archive.\n     */\n    enum {\n        kOpenReadOnly   = 0x01,\n        kOpenReadWrite  = 0x02,\n        kOpenCreate     = 0x04,     // create if it doesn't exist\n        kOpenTruncate   = 0x08,     // if it exists, empty it\n    };\n    status_t open(const char* zipFileName, int flags);\n\n    /*\n     * Add a file to the end of the archive.  Specify whether you want the\n     * library to try to store it compressed.\n     *\n     * If \"storageName\" is specified, the archive will use that instead\n     * of \"fileName\".\n     *\n     * If there is already an entry with the same name, the call fails.\n     * Existing entries with the same name must be removed first.\n     *\n     * If \"ppEntry\" is non-NULL, a pointer to the new entry will be returned.\n     */\n    status_t add(const char* fileName, int compressionMethod,\n        ZipEntry** ppEntry)\n    {\n        return add(fileName, fileName, compressionMethod, ppEntry);\n    }\n    status_t add(const char* fileName, const char* storageName,\n        int compressionMethod, ZipEntry** ppEntry)\n    {\n        return addCommon(fileName, NULL, 0, storageName,\n                         compressionMethod, ppEntry);\n    }\n\n    /*\n     * Add a file from an in-memory data buffer.\n     *\n     * If \"ppEntry\" is non-NULL, a pointer to the new entry will be returned.\n     */\n    status_t add(const void* data, size_t size, const char* storageName,\n        int compressionMethod, ZipEntry** ppEntry)\n    {\n        return addCommon(NULL, data, size, storageName,\n                         compressionMethod, ppEntry);\n    }\n\n    /*\n     * Add an entry by copying it from another zip file.  If \"alignment\" is\n     * nonzero, an appropriate number of bytes will be added to the \"extra\"\n     * field in the header so the entry payload is aligned.\n     *\n     * If \"ppEntry\" is non-NULL, a pointer to the new entry will be returned.\n     */\n    status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,\n        int alignment, ZipEntry** ppEntry);\n\n    /*\n     * Add an entry by copying it from another zip file, recompressing with\n     * Zopfli if already compressed.\n     *\n     * If \"ppEntry\" is non-NULL, a pointer to the new entry will be returned.\n     */\n    status_t addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,\n        ZipEntry** ppEntry);\n\n    /*\n     * Mark an entry as having been removed.  It is not actually deleted\n     * from the archive or our internal data structures until flush() is\n     * called.\n     */\n    status_t remove(ZipEntry* pEntry);\n\n    /*\n     * Flush changes.  If mNeedCDRewrite is set, this writes the central dir.\n     */\n    status_t flush(void);\n\n    /*\n     * Expand the data into the buffer provided.  The buffer must hold\n     * at least <uncompressed len> bytes.  Variation expands directly\n     * to a file.\n     *\n     * Returns \"false\" if an error was encountered in the compressed data.\n     */\n    //bool uncompress(const ZipEntry* pEntry, void* buf) const;\n    //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;\n    void* uncompress(const ZipEntry* pEntry) const;\n\n    /*\n     * Get an entry, by name.  Returns NULL if not found.\n     *\n     * Does not return entries pending deletion.\n     */\n    ZipEntry* getEntryByName(const char* fileName) const;\n\n    /*\n     * Get the Nth entry in the archive.\n     *\n     * This will return an entry that is pending deletion.\n     */\n    int getNumEntries(void) const { return mEntries.size(); }\n    ZipEntry* getEntryByIndex(int idx) const;\n\nprivate:\n    /* these are private and not defined */\n    ZipFile(const ZipFile& src);\n    ZipFile& operator=(const ZipFile& src);\n\n    status_t alignEntry(android::ZipEntry* pEntry, uint32_t alignTo);\n\n    class EndOfCentralDir {\n    public:\n        EndOfCentralDir(void) :\n            mDiskNumber(0),\n            mDiskWithCentralDir(0),\n            mNumEntries(0),\n            mTotalNumEntries(0),\n            mCentralDirSize(0),\n            mCentralDirOffset(0),\n            mCommentLen(0),\n            mComment(NULL)\n            {}\n        virtual ~EndOfCentralDir(void) {\n            delete[] mComment;\n        }\n\n        status_t readBuf(const uint8_t* buf, int len);\n        status_t write(FILE* fp);\n\n        //uint32_t mSignature;\n        uint16_t mDiskNumber;\n        uint16_t mDiskWithCentralDir;\n        uint16_t mNumEntries;\n        uint16_t mTotalNumEntries;\n        uint32_t mCentralDirSize;\n        uint32_t mCentralDirOffset;      // offset from first disk\n        uint16_t mCommentLen;\n        uint8_t* mComment;\n\n        enum {\n            kSignature      = 0x06054b50,\n            kEOCDLen        = 22,       // EndOfCentralDir len, excl. comment\n\n            kMaxCommentLen  = 65535,    // longest possible in ushort\n            kMaxEOCDSearch  = kMaxCommentLen + EndOfCentralDir::kEOCDLen,\n\n        };\n\n        void dump(void) const;\n    };\n\n\n    /* read all entries in the central dir */\n    status_t readCentralDir(void);\n\n    /* crunch deleted entries out */\n    status_t crunchArchive(void);\n\n    /* clean up mEntries */\n    void discardEntries(void);\n\n    /* common handler for all \"add\" functions */\n    status_t addCommon(const char* fileName, const void* data, size_t size,\n        const char* storageName, int compressionMethod, ZipEntry** ppEntry);\n\n    /* copy all of \"srcFp\" into \"dstFp\" */\n    status_t copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32);\n    /* copy all of \"data\" into \"dstFp\" */\n    status_t copyDataToFp(FILE* dstFp,\n        const void* data, size_t size, uint32_t* pCRC32);\n    /* copy some of \"srcFp\" into \"dstFp\" */\n    status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, size_t length,\n        uint32_t* pCRC32);\n    /* like memmove(), but on parts of a single file */\n    status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);\n    /* compress all of \"srcFp\" into \"dstFp\", using Deflate */\n    status_t compressFpToFp(FILE* dstFp, FILE* srcFp,\n        const void* data, size_t size, uint32_t* pCRC32);\n\n    /* get modification date from a file descriptor */\n    time_t getModTime(int fd);\n\n    /*\n     * We use stdio FILE*, which gives us buffering but makes dealing\n     * with files >2GB awkward.  Until we support Zip64, we're fine.\n     */\n    FILE*           mZipFp;             // Zip file pointer\n\n    /* one of these per file */\n    EndOfCentralDir mEOCD;\n\n    /* did we open this read-only? */\n    bool            mReadOnly;\n\n    /* set this when we trash the central dir */\n    bool            mNeedCDRewrite;\n\n    /*\n     * One ZipEntry per entry in the zip file.  I'm using pointers instead\n     * of objects because it's easier than making operator= work for the\n     * classes and sub-classes.\n     */\n    Vector<ZipEntry*>   mEntries;\n};\n\n}; // namespace android\n\n#endif // __LIBS_ZIPFILE_H\n"
  },
  {
    "path": "tools/zipalign/include/ZipAlign.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ZIPALIGN_H\n#define ZIPALIGN_H\n\nnamespace android {\n\n/*\n * Generate a new, aligned, zip \"output\" from an \"input\" zip.\n * - alignTo: Alignment (in bytes) for uncompressed entries.\n * - force  : Overwrite output if it exists, fail otherwise.\n * - zopfli : Recompress compressed entries with more efficient algorithm.\n *            Copy compressed entries as-is, and unaligned, otherwise.\n * - pageAlignSharedLibs: Align .so files to @pageSize and other files to\n *   alignTo, or all files to alignTo if false..\n * - pageSize: Specifies the page size of the target device. This is used\n *             to correctly page-align shared libraries.\n *\n * Returns 0 on success.\n */\nint process(const char* input, const char* output, int alignTo, bool force,\n    bool zopfli, bool pageAlignSharedLibs, int pageSize);\n\n/*\n * Verify the alignment of a zip archive.\n * - alignTo: Alignment (in bytes) for uncompressed entries.\n * - pageAlignSharedLibs: Align .so files to @pageSize and other files to\n *   alignTo, or all files to alignTo if false..\n * - pageSize: Specifies the page size of the target device. This is used\n *             to correctly page-align shared libraries.\n *\n * Returns 0 on success.\n */\nint verify(const char* fileName, int alignTo, bool verbose,\n    bool pageAlignSharedLibs, int pageSize);\n\n} // namespace android\n\n#endif // ZIPALIGN_H\n"
  },
  {
    "path": "tools/zipalign/tests/src/align_test.cpp",
    "content": "#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"ZipAlign.h\"\n\n#include <filesystem>\n#include <stdio.h>\n#include <string>\n\n#include <android-base/file.h>\n\nusing namespace android;\nusing namespace base;\n\n// This load the whole file to memory so be careful!\nstatic bool sameContent(const std::string& path1, const std::string& path2) {\n  std::string f1;\n  if (!ReadFileToString(path1, &f1)) {\n    printf(\"Unable to read '%s' content: %m\\n\", path1.c_str());\n    return false;\n  }\n\n  std::string f2;\n  if (!ReadFileToString(path2, &f2)) {\n    printf(\"Unable to read '%s' content %m\\n\", path1.c_str());\n    return false;\n  }\n\n  if (f1.size() != f2.size()) {\n    printf(\"File '%s' and '%s' are not the same\\n\", path1.c_str(), path2.c_str());\n    return false;\n  }\n\n  return f1.compare(f2) == 0;\n}\n\nstatic std::string GetTestPath(const std::string& filename) {\n  static std::string test_data_dir = android::base::GetExecutableDirectory() + \"/tests/data/\";\n  return test_data_dir + filename;\n}\n\nstatic std::string GetTempPath(const std::string& filename) {\n  std::filesystem::path temp_path = std::filesystem::path(testing::TempDir());\n  temp_path += filename;\n  return temp_path.string();\n}\n\nTEST(Align, Unaligned) {\n  const std::string src = GetTestPath(\"unaligned.zip\");\n  const std::string dst = GetTempPath(\"unaligned_out.zip\");\n  int pageSize = 4096;\n\n  int processed = process(src.c_str(), dst.c_str(), 4, true, false, false, pageSize);\n  ASSERT_EQ(0, processed);\n\n  int verified = verify(dst.c_str(), 4, true, false, pageSize);\n  ASSERT_EQ(0, verified);\n}\n\nTEST(Align, DoubleAligment) {\n  const std::string src = GetTestPath(\"unaligned.zip\");\n  const std::string tmp = GetTempPath(\"da_aligned.zip\");\n  const std::string dst = GetTempPath(\"da_d_aligner.zip\");\n  int pageSize = 4096;\n\n  int processed = process(src.c_str(), tmp.c_str(), 4, true, false, false, pageSize);\n  ASSERT_EQ(0, processed);\n\n  int verified = verify(tmp.c_str(), 4, true, false, pageSize);\n  ASSERT_EQ(0, verified);\n\n  // Align the result of the previous run. Essentially double aligning.\n  processed = process(tmp.c_str(), dst.c_str(), 4, true, false, false, pageSize);\n  ASSERT_EQ(0, processed);\n\n  verified = verify(dst.c_str(), 4, true, false, pageSize);\n  ASSERT_EQ(0, verified);\n\n  // Nothing should have changed between tmp and dst.\n  std::string tmp_content;\n  ASSERT_EQ(true, ReadFileToString(tmp, &tmp_content));\n\n  std::string dst_content;\n  ASSERT_EQ(true, ReadFileToString(dst, &dst_content));\n\n  ASSERT_EQ(tmp_content, dst_content);\n}\n\n// Align a zip featuring a hole at the beginning. The\n// hole in the archive is a delete entry in the Central\n// Directory.\nTEST(Align, Holes) {\n  const std::string src = GetTestPath(\"holes.zip\");\n  const std::string dst = GetTempPath(\"holes_out.zip\");\n  int pageSize = 4096;\n\n  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);\n  ASSERT_EQ(0, processed);\n\n  int verified = verify(dst.c_str(), 4, false, true, pageSize);\n  ASSERT_EQ(0, verified);\n}\n\n// Align a zip where LFH order and CD entries differ.\nTEST(Align, DifferenteOrders) {\n  const std::string src = GetTestPath(\"diffOrders.zip\");\n  const std::string dst = GetTempPath(\"diffOrders_out.zip\");\n  int pageSize = 4096;\n\n  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);\n  ASSERT_EQ(0, processed);\n\n  int verified = verify(dst.c_str(), 4, false, true, pageSize);\n  ASSERT_EQ(0, verified);\n}\n\nTEST(Align, DirectoryEntryDoNotRequireAlignment) {\n  const std::string src = GetTestPath(\"archiveWithOneDirectoryEntry.zip\");\n  int pageSize = 4096;\n  int verified = verify(src.c_str(), 4, false, true, pageSize);\n  ASSERT_EQ(0, verified);\n}\n\nTEST(Align, DirectoryEntry) {\n  const std::string src = GetTestPath(\"archiveWithOneDirectoryEntry.zip\");\n  const std::string dst = GetTempPath(\"archiveWithOneDirectoryEntry_out.zip\");\n  int pageSize = 4096;\n\n  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);\n  ASSERT_EQ(0, processed);\n  ASSERT_EQ(true, sameContent(src, dst));\n\n  int verified = verify(dst.c_str(), 4, false, true, pageSize);\n  ASSERT_EQ(0, verified);\n}\n\nclass UncompressedSharedLibsTest : public ::testing::Test {\n  protected:\n    static void SetUpTestSuite() {\n      src = GetTestPath(\"apkWithUncompressedSharedLibs.zip\");\n      dst = GetTempPath(\"apkWithUncompressedSharedLibs_out.zip\");\n    }\n\n    static std::string src;\n    static std::string dst;\n};\n\nstd::string UncompressedSharedLibsTest::src;\nstd::string UncompressedSharedLibsTest::dst;\n\nTEST_F(UncompressedSharedLibsTest, Unaligned) {\n  int pageSize = 4096;\n\n  int processed = process(src.c_str(), dst.c_str(), 4, true, false, false, pageSize);\n  ASSERT_EQ(0, processed);\n\n  int verified = verify(dst.c_str(), 4, true, true, pageSize);\n  ASSERT_NE(0, verified); // .so's not page-aligned\n}\n\nTEST_F(UncompressedSharedLibsTest, AlignedPageSize4kB) {\n  int pageSize = 4096;\n\n  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);\n  ASSERT_EQ(0, processed);\n\n  int verified = verify(dst.c_str(), 4, true, true, pageSize);\n  ASSERT_EQ(0, verified);\n}\n\nTEST_F(UncompressedSharedLibsTest, AlignedPageSize16kB) {\n  int pageSize = 16384;\n\n  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);\n  ASSERT_EQ(0, processed);\n\n  int verified = verify(dst.c_str(), 4, true, true, pageSize);\n  ASSERT_EQ(0, verified);\n}\n\nTEST_F(UncompressedSharedLibsTest, AlignedPageSize64kB) {\n  int pageSize = 65536;\n\n  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);\n  ASSERT_EQ(0, processed);\n\n  int verified = verify(dst.c_str(), 4, true, true, pageSize);\n  ASSERT_EQ(0, verified);\n}\n"
  },
  {
    "path": "tools/ziptime/Android.bp",
    "content": "//\n// Copyright 2015 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n//\n// Zip timestamp removal tool\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary_host {\n\n    srcs: [\n        \"ZipTime.cpp\",\n        \"ZipEntry.cpp\",\n        \"ZipFile.cpp\",\n    ],\n\n    name: \"ziptime\",\n    cflags: [\"-Wall\", \"-Werror\"],\n    target: {\n        windows: {\n            enabled: true,\n        },\n    },\n\n}\n"
  },
  {
    "path": "tools/ziptime/README.txt",
    "content": "ziptime -- zip timestamp tool\n\nusage: ziptime file.zip\n\n  file.zip is an existing Zip archive to rewrite\n\n\nThis tools replaces the timestamps in the zip headers with a static time\n(Jan 1 2008). The extra fields are not changed, so you'll need to use the\n-X option to zip so that it doesn't create the 'universal time' extra.\n"
  },
  {
    "path": "tools/ziptime/ZipEntry.cpp",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Access to entries in a Zip archive.\n//\n\n#include \"ZipEntry.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <inttypes.h>\n\nusing namespace android;\n\n#define LOG(...) fprintf(stderr, __VA_ARGS__)\n\n/* Jan 01 2008 */\n#define STATIC_DATE (28 << 9 | 1 << 5 | 1)\n#define STATIC_TIME 0\n\n/*\n * Initialize a new ZipEntry structure from a FILE* positioned at a\n * CentralDirectoryEntry. Rewrites the headers to remove the dynamic\n * timestamps.\n *\n * On exit, the file pointer will be at the start of the next CDE or\n * at the EOCD.\n */\nstatus_t ZipEntry::initAndRewriteFromCDE(FILE* fp)\n{\n    /* read the CDE */\n    status_t result = mCDE.rewrite(fp);\n    if (result != 0) {\n        LOG(\"mCDE.rewrite failed\\n\");\n        return result;\n    }\n\n    /* using the info in the CDE, go load up the LFH */\n    off_t posn = ftello(fp);\n    if (fseeko(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {\n        LOG(\"local header seek failed (%\" PRIu32 \")\\n\",\n            mCDE.mLocalHeaderRelOffset);\n        return -1;\n    }\n\n    result = mLFH.rewrite(fp);\n    if (result != 0) {\n        LOG(\"mLFH.rewrite failed\\n\");\n        return result;\n    }\n\n    if (fseeko(fp, posn, SEEK_SET) != 0)\n        return -1;\n\n    return 0;\n}\n\n/*\n * ===========================================================================\n *      ZipEntry::LocalFileHeader\n * ===========================================================================\n */\n\n/*\n * Rewrite a local file header.\n *\n * On entry, \"fp\" points to the signature at the start of the header.\n */\nstatus_t ZipEntry::LocalFileHeader::rewrite(FILE* fp)\n{\n    uint8_t buf[kLFHLen];\n\n    if (fread(buf, 1, kLFHLen, fp) != kLFHLen)\n        return -1;\n\n    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {\n        LOG(\"whoops: didn't find expected signature\\n\");\n        return -1;\n    }\n\n    ZipEntry::putShortLE(&buf[0x0a], STATIC_TIME);\n    ZipEntry::putShortLE(&buf[0x0c], STATIC_DATE);\n\n    if (fseek(fp, -kLFHLen, SEEK_CUR) != 0)\n        return -1;\n\n    if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)\n        return -1;\n\n    return 0;\n}\n\n/*\n * ===========================================================================\n *      ZipEntry::CentralDirEntry\n * ===========================================================================\n */\n\n/*\n * Read and rewrite the central dir entry that appears next in the file.\n *\n * On entry, \"fp\" should be positioned on the signature bytes for the\n * entry.  On exit, \"fp\" will point at the signature word for the next\n * entry or for the EOCD.\n */\nstatus_t ZipEntry::CentralDirEntry::rewrite(FILE* fp)\n{\n    uint8_t buf[kCDELen];\n    uint16_t fileNameLength, extraFieldLength, fileCommentLength;\n\n    if (fread(buf, 1, kCDELen, fp) != kCDELen)\n        return -1;\n\n    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {\n        LOG(\"Whoops: didn't find expected signature\\n\");\n        return -1;\n    }\n\n    ZipEntry::putShortLE(&buf[0x0c], STATIC_TIME);\n    ZipEntry::putShortLE(&buf[0x0e], STATIC_DATE);\n\n    fileNameLength = ZipEntry::getShortLE(&buf[0x1c]);\n    extraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);\n    fileCommentLength = ZipEntry::getShortLE(&buf[0x20]);\n    mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);\n\n    if (fseek(fp, -kCDELen, SEEK_CUR) != 0)\n        return -1;\n\n    if (fwrite(buf, 1, kCDELen, fp) != kCDELen)\n        return -1;\n\n    if (fseek(fp, fileNameLength + extraFieldLength + fileCommentLength, SEEK_CUR) != 0)\n        return -1;\n\n    return 0;\n}\n"
  },
  {
    "path": "tools/ziptime/ZipEntry.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Zip archive entries.\n//\n// The ZipEntry class is tightly meshed with the ZipFile class.\n//\n#ifndef __LIBS_ZIPENTRY_H\n#define __LIBS_ZIPENTRY_H\n\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdio.h>\n\ntypedef int status_t;\n\nnamespace android {\n\nclass ZipFile;\n\n/*\n * ZipEntry objects represent a single entry in a Zip archive.\n *\n * File information is stored in two places: next to the file data (the Local\n * File Header, and possibly a Data Descriptor), and at the end of the file\n * (the Central Directory Entry).  The two must be kept in sync.\n */\nclass ZipEntry {\npublic:\n    friend class ZipFile;\n\n    ZipEntry(void) {}\n    ~ZipEntry(void) {}\n\n    /*\n     * Some basic functions for raw data manipulation.  \"LE\" means\n     * Little Endian.\n     */\n    static inline uint16_t getShortLE(const uint8_t* buf) {\n        return buf[0] | (buf[1] << 8);\n    }\n    static inline uint32_t getLongLE(const uint8_t* buf) {\n        return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);\n    }\n    static inline void putShortLE(uint8_t* buf, uint16_t val) {\n        buf[0] = (uint8_t) val;\n        buf[1] = (uint8_t) (val >> 8);\n    }\n\nprotected:\n    /*\n     * Initialize the structure from the file, which is pointing at\n     * our Central Directory entry. And rewrite it.\n     */\n    status_t initAndRewriteFromCDE(FILE* fp);\n\nprivate:\n    /* these are private and not defined */\n    ZipEntry(const ZipEntry& src);\n    ZipEntry& operator=(const ZipEntry& src);\n\n    /*\n     * Every entry in the Zip archive starts off with one of these.\n     */\n    class LocalFileHeader {\n    public:\n        LocalFileHeader(void) {}\n\n        status_t rewrite(FILE* fp);\n\n        enum {\n            kSignature      = 0x04034b50,\n            kLFHLen         = 30,       // LocalFileHdr len, excl. var fields\n        };\n    };\n\n    /*\n     * Every entry in the Zip archive has one of these in the \"central\n     * directory\" at the end of the file.\n     */\n    class CentralDirEntry {\n    public:\n        CentralDirEntry(void) :\n            mLocalHeaderRelOffset(0)\n        {}\n\n        status_t rewrite(FILE* fp);\n\n        uint32_t mLocalHeaderRelOffset;\n\n        enum {\n            kSignature      = 0x02014b50,\n            kCDELen         = 46,       // CentralDirEnt len, excl. var fields\n        };\n    };\n\n    LocalFileHeader     mLFH;\n    CentralDirEntry     mCDE;\n};\n\n}; // namespace android\n\n#endif // __LIBS_ZIPENTRY_H\n"
  },
  {
    "path": "tools/ziptime/ZipFile.cpp",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Access to Zip archives.\n//\n\n#include \"ZipFile.h\"\n\n#include <memory.h>\n#include <sys/stat.h>\n#include <errno.h>\n#include <assert.h>\n#include <inttypes.h>\n\nusing namespace android;\n\n#define LOG(...) fprintf(stderr, __VA_ARGS__)\n\n/*\n * Open a file and rewrite the headers\n */\nstatus_t ZipFile::rewrite(const char* zipFileName)\n{\n    assert(mZipFp == NULL);     // no reopen\n\n    /* open the file */\n    mZipFp = fopen(zipFileName, \"r+b\");\n    if (mZipFp == NULL) {\n        LOG(\"fopen \\\"%s\\\" failed: %s\\n\", zipFileName, strerror(errno));\n        return -1;\n    }\n\n    /*\n     * Load the central directory.  If that fails, then this probably\n     * isn't a Zip archive.\n     */\n    return rewriteCentralDir();\n}\n\n/*\n * Find the central directory, read and rewrite the contents.\n *\n * The fun thing about ZIP archives is that they may or may not be\n * readable from start to end.  In some cases, notably for archives\n * that were written to stdout, the only length information is in the\n * central directory at the end of the file.\n *\n * Of course, the central directory can be followed by a variable-length\n * comment field, so we have to scan through it backwards.  The comment\n * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff\n * itself, plus apparently sometimes people throw random junk on the end\n * just for the fun of it.\n *\n * This is all a little wobbly.  If the wrong value ends up in the EOCD\n * area, we're hosed.  This appears to be the way that everbody handles\n * it though, so we're in pretty good company if this fails.\n */\nstatus_t ZipFile::rewriteCentralDir(void)\n{\n    fseeko(mZipFp, 0, SEEK_END);\n    off_t fileLength = ftello(mZipFp);\n    rewind(mZipFp);\n\n    /* too small to be a ZIP archive? */\n    if (fileLength < EndOfCentralDir::kEOCDLen) {\n        LOG(\"Length is %lld -- too small\\n\", (long long) fileLength);\n        return -1;\n    }\n\n    off_t seekStart;\n    size_t readAmount;\n    if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {\n        seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;\n        readAmount = EndOfCentralDir::kMaxEOCDSearch;\n    } else {\n        seekStart = 0;\n        readAmount = fileLength;\n    }\n    if (fseeko(mZipFp, seekStart, SEEK_SET) != 0) {\n        LOG(\"Failure seeking to end of zip at %lld\", (long long) seekStart);\n        return -1;\n    }\n\n    /* read the last part of the file into the buffer */\n    uint8_t buf[EndOfCentralDir::kMaxEOCDSearch];\n    if (fread(buf, 1, readAmount, mZipFp) != readAmount) {\n        LOG(\"short file? wanted %zu\\n\", readAmount);\n        return -1;\n    }\n\n    /* find the end-of-central-dir magic */\n    int i;\n    for (i = readAmount - 4; i >= 0; i--) {\n        if (buf[i] == 0x50 &&\n            ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)\n        {\n            break;\n        }\n    }\n    if (i < 0) {\n        LOG(\"EOCD not found, not Zip\\n\");\n        return -1;\n    }\n\n    /* extract eocd values */\n    status_t result = mEOCD.readBuf(buf + i, readAmount - i);\n    if (result != 0) {\n        LOG(\"Failure reading %zu bytes of EOCD values\", readAmount - i);\n        return result;\n    }\n\n    /*\n     * So far so good.  \"mCentralDirSize\" is the size in bytes of the\n     * central directory, so we can just seek back that far to find it.\n     * We can also seek forward mCentralDirOffset bytes from the\n     * start of the file.\n     *\n     * We're not guaranteed to have the rest of the central dir in the\n     * buffer, nor are we guaranteed that the central dir will have any\n     * sort of convenient size.  We need to skip to the start of it and\n     * read the header, then the other goodies.\n     *\n     * The only thing we really need right now is the file comment, which\n     * we're hoping to preserve.\n     */\n    if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {\n        LOG(\"Failure seeking to central dir offset %\" PRIu32 \"\\n\",\n             mEOCD.mCentralDirOffset);\n        return -1;\n    }\n\n    /*\n     * Loop through and read the central dir entries.\n     */\n    for (int entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {\n        ZipEntry* pEntry = new ZipEntry;\n        result = pEntry->initAndRewriteFromCDE(mZipFp);\n        delete pEntry;\n        if (result != 0) {\n            LOG(\"initFromCDE failed\\n\");\n            return -1;\n        }\n    }\n\n    /*\n     * If all went well, we should now be back at the EOCD.\n     */\n    uint8_t checkBuf[4];\n    if (fread(checkBuf, 1, 4, mZipFp) != 4) {\n        LOG(\"EOCD check read failed\\n\");\n        return -1;\n    }\n    if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {\n        LOG(\"EOCD read check failed\\n\");\n        return -1;\n    }\n\n    return 0;\n}\n\n/*\n * ===========================================================================\n *      ZipFile::EndOfCentralDir\n * ===========================================================================\n */\n\n/*\n * Read the end-of-central-dir fields.\n *\n * \"buf\" should be positioned at the EOCD signature, and should contain\n * the entire EOCD area including the comment.\n */\nstatus_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len)\n{\n    uint16_t diskNumber, diskWithCentralDir, numEntries;\n\n    if (len < kEOCDLen) {\n        /* looks like ZIP file got truncated */\n        LOG(\" Zip EOCD: expected >= %d bytes, found %d\\n\",\n            kEOCDLen, len);\n        return -1;\n    }\n\n    /* this should probably be an assert() */\n    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)\n        return -1;\n\n    diskNumber = ZipEntry::getShortLE(&buf[0x04]);\n    diskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);\n    numEntries = ZipEntry::getShortLE(&buf[0x08]);\n    mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);\n    mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);\n\n    if (diskNumber != 0 || diskWithCentralDir != 0 ||\n        numEntries != mTotalNumEntries)\n    {\n        LOG(\"Archive spanning not supported\\n\");\n        return -1;\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "tools/ziptime/ZipFile.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Class to rewrite zip file headers to remove dynamic timestamps.\n//\n#ifndef __LIBS_ZIPFILE_H\n#define __LIBS_ZIPFILE_H\n\n#include <stdio.h>\n\n#include \"ZipEntry.h\"\n\nnamespace android {\n\n/*\n * Manipulate a Zip archive.\n */\nclass ZipFile {\npublic:\n    ZipFile(void) : mZipFp(NULL) {}\n    ~ZipFile(void) {\n        if (mZipFp != NULL)\n            fclose(mZipFp);\n    }\n\n    /*\n     * Rewrite an archive's headers to remove dynamic timestamps.\n     */\n    status_t rewrite(const char* zipFileName);\n\nprivate:\n    /* these are private and not defined */\n    ZipFile(const ZipFile& src);\n    ZipFile& operator=(const ZipFile& src);\n\n    class EndOfCentralDir {\n    public:\n        EndOfCentralDir(void) : mTotalNumEntries(0), mCentralDirOffset(0) {}\n\n        status_t readBuf(const uint8_t* buf, int len);\n\n        uint16_t mTotalNumEntries;\n        uint32_t mCentralDirOffset;      // offset from first disk\n\n        enum {\n            kSignature      = 0x06054b50,\n            kEOCDLen        = 22,       // EndOfCentralDir len, excl. comment\n\n            kMaxCommentLen  = 65535,    // longest possible in ushort\n            kMaxEOCDSearch  = kMaxCommentLen + EndOfCentralDir::kEOCDLen,\n\n        };\n    };\n\n    /* read all entries in the central dir */\n    status_t rewriteCentralDir(void);\n\n    /*\n     * We use stdio FILE*, which gives us buffering but makes dealing\n     * with files >2GB awkward.  Until we support Zip64, we're fine.\n     */\n    FILE*           mZipFp;             // Zip file pointer\n\n    /* one of these per file */\n    EndOfCentralDir mEOCD;\n};\n\n}; // namespace android\n\n#endif // __LIBS_ZIPFILE_H\n"
  },
  {
    "path": "tools/ziptime/ZipTime.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Zip tool to remove dynamic timestamps\n */\n#include \"ZipFile.h\"\n\n#include <stdlib.h>\n#include <stdio.h>\n\nusing namespace android;\n\nstatic void usage(void)\n{\n    fprintf(stderr, \"Zip timestamp utility\\n\");\n    fprintf(stderr, \"Copyright (C) 2015 The Android Open Source Project\\n\\n\");\n    fprintf(stderr, \"Usage: ziptime file.zip\\n\");\n}\n\nint main(int argc, char* const argv[])\n{\n    if (argc != 2) {\n        usage();\n        return 2;\n    }\n\n    ZipFile zip;\n    if (zip.rewrite(argv[1]) != 0) {\n        fprintf(stderr, \"Unable to rewrite '%s' as zip archive\\n\", argv[1]);\n        return 1;\n    }\n\n    return 0;\n}\n"
  }
]