diff --git a/cmds/idmap2/.clang-format b/cmds/idmap2/.clang-format
new file mode 100644
index 0000000000000..c91502a257f3f
--- /dev/null
+++ b/cmds/idmap2/.clang-format
@@ -0,0 +1,7 @@
+BasedOnStyle: Google
+ColumnLimit: 100
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+PointerAlignment: Left
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
new file mode 100644
index 0000000000000..5a6c813fd2025
--- /dev/null
+++ b/cmds/idmap2/Android.bp
@@ -0,0 +1,191 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+ name: "libidmap2",
+ host_supported: true,
+ tidy: true,
+ tidy_flags: [
+ "-system-headers",
+ "-warnings-as-errors=*",
+ ],
+ srcs: [
+ "libidmap2/BinaryStreamVisitor.cpp",
+ "libidmap2/CommandLineOptions.cpp",
+ "libidmap2/FileUtils.cpp",
+ "libidmap2/Idmap.cpp",
+ "libidmap2/PrettyPrintVisitor.cpp",
+ "libidmap2/RawPrintVisitor.cpp",
+ "libidmap2/ResourceUtils.cpp",
+ "libidmap2/Xml.cpp",
+ "libidmap2/ZipFile.cpp",
+ ],
+ export_include_dirs: ["include"],
+ target: {
+ android: {
+ static: {
+ enabled: false,
+ },
+ shared_libs: [
+ "libandroidfw",
+ "libbase",
+ "libutils",
+ "libziparchive",
+ ],
+ },
+ host: {
+ shared: {
+ enabled: false,
+ },
+ static_libs: [
+ "libandroidfw",
+ "libbase",
+ "libutils",
+ "libziparchive",
+ ],
+ },
+ },
+}
+
+cc_test {
+ name: "idmap2_tests",
+ host_supported: true,
+ tidy: true,
+ tidy_flags: [
+ "-system-headers",
+ "-warnings-as-errors=*",
+ ],
+ srcs: [
+ "tests/BinaryStreamVisitorTests.cpp",
+ "tests/CommandLineOptionsTests.cpp",
+ "tests/FileUtilsTests.cpp",
+ "tests/Idmap2BinaryTests.cpp",
+ "tests/IdmapTests.cpp",
+ "tests/Main.cpp",
+ "tests/PrettyPrintVisitorTests.cpp",
+ "tests/RawPrintVisitorTests.cpp",
+ "tests/ResourceUtilsTests.cpp",
+ "tests/XmlTests.cpp",
+ "tests/ZipFileTests.cpp",
+ ],
+ required: [
+ "idmap2",
+ ],
+ static_libs: ["libgmock"],
+ target: {
+ android: {
+ shared_libs: [
+ "libandroidfw",
+ "libbase",
+ "libidmap2",
+ "liblog",
+ "libutils",
+ "libz",
+ "libziparchive",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libandroidfw",
+ "libbase",
+ "libidmap2",
+ "liblog",
+ "libutils",
+ "libziparchive",
+ ],
+ shared_libs: [
+ "libz",
+ ],
+ },
+ },
+ data: ["tests/data/**/*.apk"],
+}
+
+cc_binary {
+ name: "idmap2",
+ host_supported: true,
+ tidy: true,
+ tidy_flags: [
+ "-system-headers",
+ "-warnings-as-errors=*",
+ ],
+ srcs: [
+ "idmap2/Create.cpp",
+ "idmap2/Dump.cpp",
+ "idmap2/Lookup.cpp",
+ "idmap2/Main.cpp",
+ "idmap2/Scan.cpp",
+ "idmap2/Verify.cpp",
+ ],
+ target: {
+ android: {
+ shared_libs: [
+ "libandroidfw",
+ "libbase",
+ "libidmap2",
+ "libutils",
+ "libziparchive",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libandroidfw",
+ "libbase",
+ "libidmap2",
+ "liblog",
+ "libutils",
+ "libziparchive",
+ ],
+ shared_libs: [
+ "libz",
+ ],
+ },
+ },
+}
+
+cc_binary {
+ name: "idmap2d",
+ host_supported: false,
+ tidy: true,
+ tidy_checks: [
+ // remove google-default-arguments or clang-tidy will complain about
+ // the auto-generated file IIdmap2.cpp
+ "-google-default-arguments",
+ ],
+ tidy_flags: [
+ "-system-headers",
+ "-warnings-as-errors=*",
+ ],
+ srcs: [
+ ":idmap2_aidl",
+ "idmap2d/Idmap2Service.cpp",
+ "idmap2d/Main.cpp",
+ ],
+ shared_libs: [
+ "libandroidfw",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libidmap2",
+ "libutils",
+ "libziparchive",
+ ],
+}
+
+filegroup {
+ name: "idmap2_aidl",
+ srcs: [
+ "idmap2d/aidl/android/os/IIdmap2.aidl",
+ ],
+}
diff --git a/cmds/idmap2/AndroidTest.xml b/cmds/idmap2/AndroidTest.xml
new file mode 100644
index 0000000000000..5147f4e6cb4c8
--- /dev/null
+++ b/cmds/idmap2/AndroidTest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cmds/idmap2/CPPLINT.cfg b/cmds/idmap2/CPPLINT.cfg
new file mode 100644
index 0000000000000..9dc6b4a773805
--- /dev/null
+++ b/cmds/idmap2/CPPLINT.cfg
@@ -0,0 +1,18 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set noparent
+linelength=100
+root=..
+filter=+build/include_alpha
diff --git a/cmds/idmap2/OWNERS b/cmds/idmap2/OWNERS
new file mode 100644
index 0000000000000..23ec5ab0d1f37
--- /dev/null
+++ b/cmds/idmap2/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+toddke@google.com
diff --git a/cmds/idmap2/TEST_MAPPING b/cmds/idmap2/TEST_MAPPING
new file mode 100644
index 0000000000000..26ccf038cba21
--- /dev/null
+++ b/cmds/idmap2/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit" : [
+ {
+ "name" : "idmap2_tests"
+ }
+ ]
+}
diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h
new file mode 100644
index 0000000000000..dcc69b30743da
--- /dev/null
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_IDMAP2_COMMANDS_H_
+#define IDMAP2_IDMAP2_COMMANDS_H_
+
+#include
+#include
+
+bool Create(const std::vector& args, std::ostream& out_error);
+bool Dump(const std::vector& args, std::ostream& out_error);
+bool Lookup(const std::vector& args, std::ostream& out_error);
+bool Scan(const std::vector& args, std::ostream& out_error);
+bool Verify(const std::vector& args, std::ostream& out_error);
+
+#endif // IDMAP2_IDMAP2_COMMANDS_H_
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
new file mode 100644
index 0000000000000..291eaeb9c2116
--- /dev/null
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include // umask
+#include // umask
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+
+using android::ApkAssets;
+using android::idmap2::BinaryStreamVisitor;
+using android::idmap2::CommandLineOptions;
+using android::idmap2::Idmap;
+
+bool Create(const std::vector& args, std::ostream& out_error) {
+ std::string target_apk_path, overlay_apk_path, idmap_path;
+
+ const CommandLineOptions opts =
+ CommandLineOptions("idmap2 create")
+ .MandatoryOption("--target-apk-path",
+ "input: path to apk which will have its resources overlaid",
+ &target_apk_path)
+ .MandatoryOption("--overlay-apk-path",
+ "input: path to apk which contains the new resource values",
+ &overlay_apk_path)
+ .MandatoryOption("--idmap-path", "output: path to where to write idmap file",
+ &idmap_path);
+ if (!opts.Parse(args, out_error)) {
+ return false;
+ }
+
+ const std::unique_ptr target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ out_error << "error: failed to load apk " << target_apk_path << std::endl;
+ return false;
+ }
+
+ const std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ out_error << "error: failed to load apk " << overlay_apk_path << std::endl;
+ return false;
+ }
+
+ const std::unique_ptr idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, out_error);
+ if (!idmap) {
+ return false;
+ }
+
+ umask(0133); // u=rw,g=r,o=r
+ std::ofstream fout(idmap_path);
+ if (fout.fail()) {
+ out_error << "failed to open idmap path " << idmap_path << std::endl;
+ return false;
+ }
+ BinaryStreamVisitor visitor(fout);
+ idmap->accept(&visitor);
+ fout.close();
+ if (fout.fail()) {
+ out_error << "failed to write to idmap path " << idmap_path << std::endl;
+ return false;
+ }
+
+ return true;
+}
diff --git a/cmds/idmap2/idmap2/Dump.cpp b/cmds/idmap2/idmap2/Dump.cpp
new file mode 100644
index 0000000000000..c8cdcfa6d3dc7
--- /dev/null
+++ b/cmds/idmap2/idmap2/Dump.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+#include "idmap2/PrettyPrintVisitor.h"
+#include "idmap2/RawPrintVisitor.h"
+
+using android::idmap2::CommandLineOptions;
+using android::idmap2::Idmap;
+using android::idmap2::PrettyPrintVisitor;
+using android::idmap2::RawPrintVisitor;
+
+bool Dump(const std::vector& args, std::ostream& out_error) {
+ std::string idmap_path;
+ bool verbose;
+
+ const CommandLineOptions opts =
+ CommandLineOptions("idmap2 dump")
+ .MandatoryOption("--idmap-path", "input: path to idmap file to pretty-print", &idmap_path)
+ .OptionalFlag("--verbose", "annotate every byte of the idmap", &verbose);
+ if (!opts.Parse(args, out_error)) {
+ return false;
+ }
+ std::ifstream fin(idmap_path);
+ const std::unique_ptr idmap = Idmap::FromBinaryStream(fin, out_error);
+ fin.close();
+ if (!idmap) {
+ return false;
+ }
+
+ if (verbose) {
+ RawPrintVisitor visitor(std::cout);
+ idmap->accept(&visitor);
+ } else {
+ PrettyPrintVisitor visitor(std::cout);
+ idmap->accept(&visitor);
+ }
+
+ return true;
+}
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
new file mode 100644
index 0000000000000..1191e6a27b079
--- /dev/null
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ConfigDescription.h"
+#include "androidfw/ResourceUtils.h"
+#include "androidfw/StringPiece.h"
+#include "androidfw/Util.h"
+#include "utils/String16.h"
+#include "utils/String8.h"
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+#include "idmap2/Xml.h"
+#include "idmap2/ZipFile.h"
+
+using android::ApkAssets;
+using android::ApkAssetsCookie;
+using android::AssetManager2;
+using android::ConfigDescription;
+using android::is_valid_resid;
+using android::kInvalidCookie;
+using android::Res_value;
+using android::ResStringPool;
+using android::ResTable_config;
+using android::String16;
+using android::String8;
+using android::StringPiece16;
+using android::base::StringPrintf;
+using android::idmap2::CommandLineOptions;
+using android::idmap2::IdmapHeader;
+using android::idmap2::ResourceId;
+using android::idmap2::Xml;
+using android::idmap2::ZipFile;
+using android::util::Utf16ToUtf8;
+
+namespace {
+std::pair WARN_UNUSED ParseResReference(const AssetManager2& am,
+ const std::string& res,
+ const std::string& fallback_package) {
+ // first, try to parse as a hex number
+ char* endptr = nullptr;
+ ResourceId resid;
+ resid = strtol(res.c_str(), &endptr, 16);
+ if (*endptr == '\0') {
+ return std::make_pair(true, resid);
+ }
+
+ // next, try to parse as a package:type/name string
+ resid = am.GetResourceId(res, "", fallback_package);
+ if (is_valid_resid(resid)) {
+ return std::make_pair(true, resid);
+ }
+
+ // end of the road: res could not be parsed
+ return std::make_pair(false, 0);
+}
+
+std::pair WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+ Res_value value;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
+ if (cookie == kInvalidCookie) {
+ return std::make_pair(false, "");
+ }
+
+ std::string out;
+
+ // TODO(martenkongstad): use optional parameter GetResource(..., std::string*
+ // stacktrace = NULL) instead
+ out.append(StringPrintf("cookie=%d ", cookie));
+
+ out.append("config='");
+ out.append(config.toString().c_str());
+ out.append("' value=");
+
+ switch (value.dataType) {
+ case Res_value::TYPE_INT_DEC:
+ out.append(StringPrintf("%d", value.data));
+ break;
+ case Res_value::TYPE_INT_HEX:
+ out.append(StringPrintf("0x%08x", value.data));
+ break;
+ case Res_value::TYPE_INT_BOOLEAN:
+ out.append(value.data != 0 ? "true" : "false");
+ break;
+ case Res_value::TYPE_STRING: {
+ const ResStringPool* pool = am.GetStringPoolForCookie(cookie);
+ size_t len;
+ if (pool->isUTF8()) {
+ const char* str = pool->string8At(value.data, &len);
+ out.append(str, len);
+ } else {
+ const char16_t* str16 = pool->stringAt(value.data, &len);
+ out += Utf16ToUtf8(StringPiece16(str16, len));
+ }
+ } break;
+ default:
+ out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
+ break;
+ }
+ return std::make_pair(true, out);
+}
+
+std::pair GetTargetPackageNameFromManifest(const std::string& apk_path) {
+ const auto zip = ZipFile::Open(apk_path);
+ if (!zip) {
+ return std::make_pair(false, "");
+ }
+ const auto entry = zip->Uncompress("AndroidManifest.xml");
+ if (!entry) {
+ return std::make_pair(false, "");
+ }
+ const auto xml = Xml::Create(entry->buf, entry->size);
+ if (!xml) {
+ return std::make_pair(false, "");
+ }
+ const auto tag = xml->FindTag("overlay");
+ if (!tag) {
+ return std::make_pair(false, "");
+ }
+ const auto iter = tag->find("targetPackage");
+ if (iter == tag->end()) {
+ return std::make_pair(false, "");
+ }
+ return std::make_pair(true, iter->second);
+}
+} // namespace
+
+bool Lookup(const std::vector& args, std::ostream& out_error) {
+ std::vector idmap_paths;
+ std::string config_str, resid_str;
+ const CommandLineOptions opts =
+ CommandLineOptions("idmap2 lookup")
+ .MandatoryOption("--idmap-path", "input: path to idmap file to load", &idmap_paths)
+ .MandatoryOption("--config", "configuration to use", &config_str)
+ .MandatoryOption("--resid",
+ "Resource ID (in the target package; '0xpptteeee' or "
+ "'[package:]type/name') to look up",
+ &resid_str);
+
+ if (!opts.Parse(args, out_error)) {
+ return false;
+ }
+
+ ConfigDescription config;
+ if (!ConfigDescription::Parse(config_str, &config)) {
+ out_error << "error: failed to parse config" << std::endl;
+ return false;
+ }
+
+ std::vector> apk_assets;
+ std::string target_path;
+ std::string target_package_name;
+ for (size_t i = 0; i < idmap_paths.size(); i++) {
+ const auto& idmap_path = idmap_paths[i];
+ std::fstream fin(idmap_path);
+ auto idmap_header = IdmapHeader::FromBinaryStream(fin);
+ fin.close();
+ if (!idmap_header) {
+ out_error << "error: failed to read idmap from " << idmap_path << std::endl;
+ return false;
+ }
+
+ if (i == 0) {
+ target_path = idmap_header->GetTargetPath().to_string();
+ auto target_apk = ApkAssets::Load(target_path);
+ if (!target_apk) {
+ out_error << "error: failed to read target apk from " << target_path << std::endl;
+ return false;
+ }
+ apk_assets.push_back(std::move(target_apk));
+
+ bool lookup_ok;
+ std::tie(lookup_ok, target_package_name) =
+ GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
+ if (!lookup_ok) {
+ out_error << "error: failed to parse android:targetPackage from overlay manifest"
+ << std::endl;
+ return false;
+ }
+ } else if (target_path != idmap_header->GetTargetPath()) {
+ out_error << "error: different target APKs (expected target APK " << target_path << " but "
+ << idmap_path << " has target APK " << idmap_header->GetTargetPath() << ")"
+ << std::endl;
+ return false;
+ }
+
+ auto overlay_apk = ApkAssets::LoadOverlay(idmap_path);
+ if (!overlay_apk) {
+ out_error << "error: failed to read overlay apk from " << idmap_header->GetOverlayPath()
+ << std::endl;
+ return false;
+ }
+ apk_assets.push_back(std::move(overlay_apk));
+ }
+
+ // AssetManager2::SetApkAssets requires raw ApkAssets pointers, not unique_ptrs
+ std::vector raw_pointer_apk_assets;
+ std::transform(apk_assets.cbegin(), apk_assets.cend(), std::back_inserter(raw_pointer_apk_assets),
+ [](const auto& p) -> const ApkAssets* { return p.get(); });
+ AssetManager2 am;
+ am.SetApkAssets(raw_pointer_apk_assets);
+ am.SetConfiguration(config);
+
+ ResourceId resid;
+ bool lookup_ok;
+ std::tie(lookup_ok, resid) = ParseResReference(am, resid_str, target_package_name);
+ if (!lookup_ok) {
+ out_error << "error: failed to parse resource ID" << std::endl;
+ return false;
+ }
+
+ std::string value;
+ std::tie(lookup_ok, value) = GetValue(am, resid);
+ if (!lookup_ok) {
+ out_error << StringPrintf("error: resource 0x%08x not found", resid) << std::endl;
+ return false;
+ }
+ std::cout << value << std::endl;
+
+ return true;
+}
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
new file mode 100644
index 0000000000000..5d9ea778915a8
--- /dev/null
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include // EXIT_{FAILURE,SUCCESS}
+#include
+#include
+#include