summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/idmap2/.clang-format7
-rw-r--r--cmds/idmap2/Android.bp191
-rw-r--r--cmds/idmap2/AndroidTest.xml26
-rw-r--r--cmds/idmap2/CPPLINT.cfg18
-rw-r--r--cmds/idmap2/OWNERS2
-rw-r--r--cmds/idmap2/TEST_MAPPING7
-rw-r--r--cmds/idmap2/idmap2/Commands.h29
-rw-r--r--cmds/idmap2/idmap2/Create.cpp86
-rw-r--r--cmds/idmap2/idmap2/Dump.cpp60
-rw-r--r--cmds/idmap2/idmap2/Lookup.cpp249
-rw-r--r--cmds/idmap2/idmap2/Main.cpp67
-rw-r--r--cmds/idmap2/idmap2/Scan.cpp159
-rw-r--r--cmds/idmap2/idmap2/Verify.cpp46
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp138
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.h49
-rw-r--r--cmds/idmap2/idmap2d/Main.cpp50
-rw-r--r--cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl27
-rw-r--r--cmds/idmap2/include/idmap2/BinaryStreamVisitor.h49
-rw-r--r--cmds/idmap2/include/idmap2/CommandLineOptions.h71
-rw-r--r--cmds/idmap2/include/idmap2/FileUtils.h41
-rw-r--r--cmds/idmap2/include/idmap2/Idmap.h277
-rw-r--r--cmds/idmap2/include/idmap2/PrettyPrintVisitor.h53
-rw-r--r--cmds/idmap2/include/idmap2/RawPrintVisitor.h59
-rw-r--r--cmds/idmap2/include/idmap2/ResourceUtils.h39
-rw-r--r--cmds/idmap2/include/idmap2/Xml.h51
-rw-r--r--cmds/idmap2/include/idmap2/ZipFile.h62
-rw-r--r--cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp81
-rw-r--r--cmds/idmap2/libidmap2/CommandLineOptions.cpp163
-rw-r--r--cmds/idmap2/libidmap2/FileUtils.cpp82
-rw-r--r--cmds/idmap2/libidmap2/Idmap.cpp443
-rw-r--r--cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp78
-rw-r--r--cmds/idmap2/libidmap2/RawPrintVisitor.cpp131
-rw-r--r--cmds/idmap2/libidmap2/ResourceUtils.cpp55
-rw-r--r--cmds/idmap2/libidmap2/Xml.cpp82
-rw-r--r--cmds/idmap2/libidmap2/ZipFile.cpp67
-rwxr-xr-xcmds/idmap2/static-checks.sh121
-rw-r--r--cmds/idmap2/tests/BinaryStreamVisitorTests.cpp129
-rw-r--r--cmds/idmap2/tests/CommandLineOptionsTests.cpp192
-rw-r--r--cmds/idmap2/tests/FileUtilsTests.cpp76
-rw-r--r--cmds/idmap2/tests/Idmap2BinaryTests.cpp313
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp395
-rw-r--r--cmds/idmap2/tests/Main.cpp38
-rw-r--r--cmds/idmap2/tests/PrettyPrintVisitorTests.cpp83
-rw-r--r--cmds/idmap2/tests/RawPrintVisitorTests.cpp84
-rw-r--r--cmds/idmap2/tests/ResourceUtilsTests.cpp69
-rw-r--r--cmds/idmap2/tests/TestHelpers.h168
-rw-r--r--cmds/idmap2/tests/XmlTests.cpp72
-rw-r--r--cmds/idmap2/tests/ZipFileTests.cpp72
-rw-r--r--cmds/idmap2/tests/data/overlay/AndroidManifest.xml21
-rw-r--r--cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml23
-rw-r--r--cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml23
-rw-r--r--cmds/idmap2/tests/data/overlay/build40
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-static-1.apkbin0 -> 1599 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-static-2.apkbin0 -> 1599 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay.apkbin0 -> 1559 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/res/values-sv/values.xml19
-rw-r--r--cmds/idmap2/tests/data/overlay/res/values/values.xml21
-rw-r--r--cmds/idmap2/tests/data/target/AndroidManifest.xml19
-rw-r--r--cmds/idmap2/tests/data/target/assets/lorem-ipsum.txt1
-rw-r--r--cmds/idmap2/tests/data/target/build17
-rw-r--r--cmds/idmap2/tests/data/target/res/values/values.xml28
-rw-r--r--cmds/idmap2/tests/data/target/res/xml/test.xml25
-rw-r--r--cmds/idmap2/tests/data/target/target.apkbin0 -> 2173 bytes
63 files changed, 5144 insertions, 0 deletions
diff --git a/cmds/idmap2/.clang-format b/cmds/idmap2/.clang-format
new file mode 100644
index 000000000000..c91502a257f3
--- /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 000000000000..5a6c813fd202
--- /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 000000000000..5147f4e6cb4c
--- /dev/null
+++ b/cmds/idmap2/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for idmap2_tests">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="idmap2_tests->/data/local/tmp/idmap2_tests" />
+ </target_preparer>
+ <option name="test-suite-tag" value="idmap2_tests" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="idmap2_tests" />
+ </test>
+</configuration>
diff --git a/cmds/idmap2/CPPLINT.cfg b/cmds/idmap2/CPPLINT.cfg
new file mode 100644
index 000000000000..9dc6b4a77380
--- /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 000000000000..23ec5ab0d1f3
--- /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 000000000000..26ccf038cba2
--- /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 000000000000..dcc69b30743d
--- /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 <string>
+#include <vector>
+
+bool Create(const std::vector<std::string>& args, std::ostream& out_error);
+bool Dump(const std::vector<std::string>& args, std::ostream& out_error);
+bool Lookup(const std::vector<std::string>& args, std::ostream& out_error);
+bool Scan(const std::vector<std::string>& args, std::ostream& out_error);
+bool Verify(const std::vector<std::string>& 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 000000000000..291eaeb9c211
--- /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 <sys/stat.h> // umask
+#include <sys/types.h> // umask
+#include <fstream>
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#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<std::string>& 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<const ApkAssets> 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<const ApkAssets> 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<const Idmap> 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 000000000000..c8cdcfa6d3dc
--- /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 <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#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<std::string>& 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<const Idmap> 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 000000000000..1191e6a27b07
--- /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 <algorithm>
+#include <fstream>
+#include <iterator>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#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<bool, ResourceId> 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<bool, std::string> 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<bool, std::string> 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<std::string>& args, std::ostream& out_error) {
+ std::vector<std::string> 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<std::unique_ptr<const ApkAssets>> 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<const ApkAssets*> 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 000000000000..5d9ea778915a
--- /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 <cstdlib> // EXIT_{FAILURE,SUCCESS}
+#include <functional>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "idmap2/CommandLineOptions.h"
+
+#include "Commands.h"
+
+using android::idmap2::CommandLineOptions;
+
+typedef std::map<std::string, std::function<int(const std::vector<std::string>&, std::ostream&)>>
+ NameToFunctionMap;
+
+static void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) {
+ out << "usage: idmap2 [";
+ for (auto iter = commands.cbegin(); iter != commands.cend(); iter++) {
+ if (iter != commands.cbegin()) {
+ out << "|";
+ }
+ out << iter->first;
+ }
+ out << "]" << std::endl;
+}
+
+int main(int argc, char** argv) {
+ const NameToFunctionMap commands = {
+ {"create", Create}, {"dump", Dump}, {"lookup", Lookup}, {"scan", Scan}, {"verify", Verify},
+ };
+ if (argc <= 1) {
+ PrintUsage(commands, std::cerr);
+ return EXIT_FAILURE;
+ }
+ const std::unique_ptr<std::vector<std::string>> args =
+ CommandLineOptions::ConvertArgvToVector(argc - 1, const_cast<const char**>(argv + 1));
+ if (!args) {
+ std::cerr << "error: failed to parse command line options" << std::endl;
+ return EXIT_FAILURE;
+ }
+ const auto iter = commands.find(argv[1]);
+ if (iter == commands.end()) {
+ std::cerr << argv[1] << ": command not found" << std::endl;
+ PrintUsage(commands, std::cerr);
+ return EXIT_FAILURE;
+ }
+ return iter->second(*args, std::cerr) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
new file mode 100644
index 000000000000..33c274e7fd35
--- /dev/null
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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 <dirent.h>
+#include <fstream>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+#include "idmap2/Xml.h"
+#include "idmap2/ZipFile.h"
+
+#include "Commands.h"
+
+using android::idmap2::CommandLineOptions;
+using android::idmap2::Idmap;
+using android::idmap2::MemoryChunk;
+using android::idmap2::Xml;
+using android::idmap2::ZipFile;
+using android::idmap2::utils::FindFiles;
+
+namespace {
+std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::string>& dirs,
+ bool recursive, std::ostream& out_error) {
+ const auto predicate = [](unsigned char type, const std::string& path) -> bool {
+ static constexpr size_t kExtLen = 4; // strlen(".apk")
+ return type == DT_REG && path.size() > kExtLen &&
+ !path.compare(path.size() - kExtLen, kExtLen, ".apk");
+ };
+ // pass apk paths through a set to filter out duplicates
+ std::set<std::string> paths;
+ for (const auto& dir : dirs) {
+ const auto apk_paths = FindFiles(dir, recursive, predicate);
+ if (!apk_paths) {
+ out_error << "error: failed to open directory " << dir << std::endl;
+ return nullptr;
+ }
+ paths.insert(apk_paths->cbegin(), apk_paths->cend());
+ }
+ return std::unique_ptr<std::vector<std::string>>(
+ new std::vector<std::string>(paths.cbegin(), paths.cend()));
+}
+} // namespace
+
+bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
+ std::vector<std::string> input_directories;
+ std::string target_package_name, target_apk_path, output_directory;
+ bool recursive = false;
+
+ const CommandLineOptions opts =
+ CommandLineOptions("idmap2 scan")
+ .MandatoryOption("--input-directory", "directory containing overlay apks to scan",
+ &input_directories)
+ .OptionalFlag("--recursive", "also scan subfolders of overlay-directory", &recursive)
+ .MandatoryOption("--target-package-name", "package name of target package",
+ &target_package_name)
+ .MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path)
+ .MandatoryOption("--output-directory",
+ "directory in which to write artifacts (idmap files and overlays.list)",
+ &output_directory);
+ if (!opts.Parse(args, out_error)) {
+ return false;
+ }
+
+ const auto apk_paths = FindApkFiles(input_directories, recursive, out_error);
+ if (!apk_paths) {
+ return false;
+ }
+
+ std::vector<std::string> interesting_apks;
+ for (const std::string& path : *apk_paths) {
+ std::unique_ptr<const ZipFile> zip = ZipFile::Open(path);
+ if (!zip) {
+ out_error << "error: failed to open " << path << " as a zip file" << std::endl;
+ return false;
+ }
+
+ std::unique_ptr<const MemoryChunk> entry = zip->Uncompress("AndroidManifest.xml");
+ if (!entry) {
+ out_error << "error: failed to uncompress AndroidManifest.xml from " << path << std::endl;
+ return false;
+ }
+
+ std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size);
+ if (!xml) {
+ out_error << "error: failed to parse AndroidManifest.xml from " << path << std::endl;
+ continue;
+ }
+
+ const auto tag = xml->FindTag("overlay");
+ if (!tag) {
+ continue;
+ }
+
+ auto iter = tag->find("isStatic");
+ if (iter == tag->end() || std::stoul(iter->second) == 0u) {
+ continue;
+ }
+
+ iter = tag->find("targetPackage");
+ if (iter == tag->end() || iter->second != target_package_name) {
+ continue;
+ }
+
+ iter = tag->find("priority");
+ if (iter == tag->end()) {
+ continue;
+ }
+
+ const int priority = std::stoi(iter->second);
+ if (priority < 0) {
+ continue;
+ }
+
+ interesting_apks.insert(
+ std::lower_bound(interesting_apks.begin(), interesting_apks.end(), path), path);
+ }
+
+ std::stringstream stream;
+ for (auto iter = interesting_apks.cbegin(); iter != interesting_apks.cend(); ++iter) {
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, *iter);
+ if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}), out_error) &&
+ !Create(std::vector<std::string>({
+ "--target-apk-path",
+ target_apk_path,
+ "--overlay-apk-path",
+ *iter,
+ "--idmap-path",
+ idmap_path,
+ }),
+ out_error)) {
+ return false;
+ }
+ stream << idmap_path << std::endl;
+ }
+
+ std::cout << stream.str();
+
+ return true;
+}
diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/Verify.cpp
new file mode 100644
index 000000000000..b5fa438b5b9f
--- /dev/null
+++ b/cmds/idmap2/idmap2/Verify.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 <fstream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+
+using android::idmap2::CommandLineOptions;
+using android::idmap2::IdmapHeader;
+
+bool Verify(const std::vector<std::string>& args, std::ostream& out_error) {
+ std::string idmap_path;
+ const CommandLineOptions opts =
+ CommandLineOptions("idmap2 verify")
+ .MandatoryOption("--idmap-path", "input: path to idmap file to verify", &idmap_path);
+ if (!opts.Parse(args, out_error)) {
+ return false;
+ }
+
+ std::ifstream fin(idmap_path);
+ const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
+ fin.close();
+ if (!header) {
+ out_error << "error: failed to parse idmap header" << std::endl;
+ return false;
+ }
+
+ return header->IsUpToDate(out_error);
+}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
new file mode 100644
index 000000000000..cf72cb94da2c
--- /dev/null
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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 <sys/stat.h> // umask
+#include <sys/types.h> // umask
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstring>
+#include <fstream>
+#include <memory>
+#include <ostream>
+#include <string>
+
+#include "android-base/macros.h"
+#include "utils/String8.h"
+#include "utils/Trace.h"
+
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+
+#include "idmap2d/Idmap2Service.h"
+
+using android::binder::Status;
+using android::idmap2::BinaryStreamVisitor;
+using android::idmap2::Idmap;
+using android::idmap2::IdmapHeader;
+
+namespace {
+
+static constexpr const char* kIdmapCacheDir = "/data/resource-cache";
+
+Status ok() {
+ return Status::ok();
+}
+
+Status error(const std::string& msg) {
+ LOG(ERROR) << msg;
+ return Status::fromExceptionCode(Status::EX_NONE, msg.c_str());
+}
+
+} // namespace
+
+namespace android {
+namespace os {
+
+Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path,
+ int32_t user_id ATTRIBUTE_UNUSED, std::string* _aidl_return) {
+ assert(_aidl_return);
+ *_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ return ok();
+}
+
+Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
+ int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
+ assert(_aidl_return);
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ if (unlink(idmap_path.c_str()) == 0) {
+ *_aidl_return = true;
+ return ok();
+ } else {
+ *_aidl_return = false;
+ return error("failed to unlink " + idmap_path + ": " + strerror(errno));
+ }
+}
+
+Status Idmap2Service::createIdmap(const std::string& target_apk_path,
+ const std::string& overlay_apk_path, int32_t user_id,
+ std::unique_ptr<std::string>* _aidl_return) {
+ assert(_aidl_return);
+ std::stringstream trace;
+ trace << __FUNCTION__ << " " << target_apk_path << " " << overlay_apk_path << " "
+ << std::to_string(user_id);
+ ATRACE_NAME(trace.str().c_str());
+ std::cout << trace.str() << std::endl;
+
+ _aidl_return->reset(nullptr);
+
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ std::ifstream fin(idmap_path);
+ const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
+ fin.close();
+ // do not reuse error stream from IsUpToDate below, or error messages will be
+ // polluted with irrelevant data
+ std::stringstream dev_null;
+ if (header && header->IsUpToDate(dev_null)) {
+ return ok();
+ }
+
+ const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return error("failed to load apk " + target_apk_path);
+ }
+
+ const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ return error("failed to load apk " + overlay_apk_path);
+ }
+
+ std::stringstream err;
+ const std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, err);
+ if (!idmap) {
+ return error(err.str());
+ }
+
+ umask(0133); // u=rw,g=r,o=r
+ std::ofstream fout(idmap_path);
+ if (fout.fail()) {
+ return error("failed to open idmap path " + idmap_path);
+ }
+ BinaryStreamVisitor visitor(fout);
+ idmap->accept(&visitor);
+ fout.close();
+ if (fout.fail()) {
+ return error("failed to write to idmap path " + idmap_path);
+ }
+
+ _aidl_return->reset(new std::string(idmap_path));
+ return ok();
+}
+
+} // namespace os
+} // namespace android
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
new file mode 100644
index 000000000000..2b32042d6aa3
--- /dev/null
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -0,0 +1,49 @@
+/*
+ * 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_IDMAP2D_IDMAP2SERVICE_H_
+#define IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
+
+#include <android-base/unique_fd.h>
+#include <binder/BinderService.h>
+
+#include <memory>
+#include <string>
+
+#include "android/os/BnIdmap2.h"
+
+namespace android {
+namespace os {
+class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
+ public:
+ static char const* getServiceName() {
+ return "idmap";
+ }
+
+ binder::Status getIdmapPath(const std::string& overlay_apk_path, int32_t user_id,
+ std::string* _aidl_return);
+
+ binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
+ bool* _aidl_return);
+
+ binder::Status createIdmap(const std::string& target_apk_path,
+ const std::string& overlay_apk_path, int32_t user_id,
+ std::unique_ptr<std::string>* _aidl_return);
+};
+} // namespace os
+} // namespace android
+
+#endif // IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
diff --git a/cmds/idmap2/idmap2d/Main.cpp b/cmds/idmap2/idmap2d/Main.cpp
new file mode 100644
index 000000000000..d64a87b8ee15
--- /dev/null
+++ b/cmds/idmap2/idmap2d/Main.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include <binder/BinderService.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+
+#include <cstdlib> // EXIT_{FAILURE,SUCCESS}
+
+#include <iostream>
+#include <sstream>
+
+#include "android-base/macros.h"
+
+#include "Idmap2Service.h"
+
+using android::BinderService;
+using android::IPCThreadState;
+using android::ProcessState;
+using android::sp;
+using android::status_t;
+using android::os::Idmap2Service;
+
+int main(int argc ATTRIBUTE_UNUSED, char** argv ATTRIBUTE_UNUSED) {
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+ status_t ret = BinderService<Idmap2Service>::publish();
+ if (ret != android::OK) {
+ return EXIT_FAILURE;
+ }
+ sp<ProcessState> ps(ProcessState::self());
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+ IPCThreadState::self()->joinThreadPool();
+ return EXIT_SUCCESS;
+}
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
new file mode 100644
index 000000000000..5d196101a7a6
--- /dev/null
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * @hide
+ */
+interface IIdmap2 {
+ @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
+ boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
+ @nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
+ @utf8InCpp String overlayApkPath, int userId);
+}
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
new file mode 100644
index 000000000000..2368aeadbc9f
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -0,0 +1,49 @@
+/*
+ * 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_INCLUDE_IDMAP2_BINARYSTREAMVISITOR_H_
+#define IDMAP2_INCLUDE_IDMAP2_BINARYSTREAMVISITOR_H_
+
+#include <cstdint>
+#include <iostream>
+#include <string>
+
+#include "idmap2/Idmap.h"
+
+namespace android {
+namespace idmap2 {
+
+class BinaryStreamVisitor : public Visitor {
+ public:
+ explicit BinaryStreamVisitor(std::ostream& stream) : stream_(stream) {
+ }
+ virtual void visit(const Idmap& idmap);
+ virtual void visit(const IdmapHeader& header);
+ virtual void visit(const IdmapData& data);
+ virtual void visit(const IdmapData::Header& header);
+ virtual void visit(const IdmapData::TypeEntry& type_entry);
+
+ private:
+ void Write16(uint16_t value);
+ void Write32(uint32_t value);
+ void WriteString(const StringPiece& value);
+ std::ostream& stream_;
+};
+
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_INCLUDE_IDMAP2_BINARYSTREAMVISITOR_H_
diff --git a/cmds/idmap2/include/idmap2/CommandLineOptions.h b/cmds/idmap2/include/idmap2/CommandLineOptions.h
new file mode 100644
index 000000000000..f3aa68b8d1a2
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/CommandLineOptions.h
@@ -0,0 +1,71 @@
+/*
+ * 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_INCLUDE_IDMAP2_COMMANDLINEOPTIONS_H_
+#define IDMAP2_INCLUDE_IDMAP2_COMMANDLINEOPTIONS_H_
+
+#include <functional>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace idmap2 {
+
+/*
+ * Utility class to convert a command line, including options (--path foo.txt),
+ * into data structures (options.path = "foo.txt").
+ */
+class CommandLineOptions {
+ public:
+ static std::unique_ptr<std::vector<std::string>> ConvertArgvToVector(int argc, const char** argv);
+
+ explicit CommandLineOptions(const std::string& name) : name_(name) {
+ }
+
+ CommandLineOptions& OptionalFlag(const std::string& name, const std::string& description,
+ bool* value);
+ CommandLineOptions& MandatoryOption(const std::string& name, const std::string& description,
+ std::string* value);
+ CommandLineOptions& MandatoryOption(const std::string& name, const std::string& description,
+ std::vector<std::string>* value);
+ CommandLineOptions& OptionalOption(const std::string& name, const std::string& description,
+ std::string* value);
+ bool Parse(const std::vector<std::string>& argv, std::ostream& outError) const;
+ void Usage(std::ostream& out) const;
+
+ private:
+ struct Option {
+ std::string name;
+ std::string description;
+ std::function<void(const std::string& value)> action;
+ enum {
+ COUNT_OPTIONAL,
+ COUNT_EXACTLY_ONCE,
+ COUNT_ONCE_OR_MORE,
+ } count;
+ bool argument;
+ };
+
+ mutable std::vector<Option> options_;
+ std::string name_;
+};
+
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_INCLUDE_IDMAP2_COMMANDLINEOPTIONS_H_
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
new file mode 100644
index 000000000000..05c6d31d395d
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -0,0 +1,41 @@
+/*
+ * 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_INCLUDE_IDMAP2_FILEUTILS_H_
+#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)>
+ FindFilesPredicate;
+std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
+ const FindFilesPredicate& predicate);
+
+std::unique_ptr<std::string> ReadFile(int fd);
+
+std::unique_ptr<std::string> ReadFile(const std::string& path);
+
+} // namespace utils
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
new file mode 100644
index 000000000000..837b7c5264d5
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ */
+
+/*
+ * # idmap file format (current version)
+ *
+ * idmap := header data*
+ * header := magic version target_crc overlay_crc target_path overlay_path
+ * data := data_header data_block*
+ * data_header := target_package_id types_count
+ * data_block := target_type overlay_type entry_count entry_offset entry*
+ * overlay_path := string
+ * target_path := string
+ * entry := <uint32_t>
+ * entry_count := <uint16_t>
+ * entry_offset := <uint16_t>
+ * magic := <uint32_t>
+ * overlay_crc := <uint32_t>
+ * overlay_type := <uint16_t>
+ * string := <uint8_t>[256]
+ * target_crc := <uint32_t>
+ * target_package_id := <uint16_t>
+ * target_type := <uint16_t>
+ * types_count := <uint16_t>
+ * version := <uint32_t>
+ *
+ *
+ * # idmap file format changelog
+ * ## v1
+ * - Identical to idmap v1.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
+#define IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
+
+namespace android {
+namespace idmap2 {
+
+class Idmap;
+class Visitor;
+
+// use typedefs to let the compiler warn us about implicit casts
+typedef uint32_t ResourceId; // 0xpptteeee
+typedef uint8_t PackageId; // pp in 0xpptteeee
+typedef uint8_t TypeId; // tt in 0xpptteeee
+typedef uint16_t EntryId; // eeee in 0xpptteeee
+
+static constexpr const ResourceId kPadding = 0xffffffffu;
+
+static constexpr const EntryId kNoEntry = 0xffffu;
+
+// magic number: all idmap files start with this
+static constexpr const uint32_t kIdmapMagic = android::kIdmapMagic;
+
+// current version of the idmap binary format; must be incremented when the format is changed
+static constexpr const uint32_t kIdmapCurrentVersion = android::kIdmapCurrentVersion;
+
+// strings in the idmap are encoded char arrays of length 'kIdmapStringLength' (including mandatory
+// terminating null)
+static constexpr const size_t kIdmapStringLength = 256;
+
+class IdmapHeader {
+ public:
+ static std::unique_ptr<const IdmapHeader> FromBinaryStream(std::istream& stream);
+
+ inline uint32_t GetMagic() const {
+ return magic_;
+ }
+
+ inline uint32_t GetVersion() const {
+ return version_;
+ }
+
+ inline uint32_t GetTargetCrc() const {
+ return target_crc_;
+ }
+
+ inline uint32_t GetOverlayCrc() const {
+ return overlay_crc_;
+ }
+
+ inline StringPiece GetTargetPath() const {
+ return StringPiece(target_path_);
+ }
+
+ inline StringPiece GetOverlayPath() const {
+ return StringPiece(overlay_path_);
+ }
+
+ // Invariant: anytime the idmap data encoding is changed, the idmap version
+ // field *must* be incremented. Because of this, we know that if the idmap
+ // header is up-to-date the entire file is up-to-date.
+ bool IsUpToDate(std::ostream& out_error) const;
+
+ void accept(Visitor* v) const;
+
+ private:
+ IdmapHeader() {
+ }
+
+ uint32_t magic_;
+ uint32_t version_;
+ uint32_t target_crc_;
+ uint32_t overlay_crc_;
+ char target_path_[kIdmapStringLength];
+ char overlay_path_[kIdmapStringLength];
+
+ friend Idmap;
+ DISALLOW_COPY_AND_ASSIGN(IdmapHeader);
+};
+
+class IdmapData {
+ public:
+ class Header {
+ public:
+ static std::unique_ptr<const Header> FromBinaryStream(std::istream& stream);
+
+ inline PackageId GetTargetPackageId() const {
+ return target_package_id_;
+ }
+
+ inline uint16_t GetTypeCount() const {
+ return type_count_;
+ }
+
+ void accept(Visitor* v) const;
+
+ private:
+ Header() {
+ }
+
+ PackageId target_package_id_;
+ uint16_t type_count_;
+
+ friend Idmap;
+ DISALLOW_COPY_AND_ASSIGN(Header);
+ };
+
+ class TypeEntry {
+ public:
+ static std::unique_ptr<const TypeEntry> FromBinaryStream(std::istream& stream);
+
+ inline TypeId GetTargetTypeId() const {
+ return target_type_id_;
+ }
+
+ inline TypeId GetOverlayTypeId() const {
+ return overlay_type_id_;
+ }
+
+ inline uint16_t GetEntryCount() const {
+ return entries_.size();
+ }
+
+ inline uint16_t GetEntryOffset() const {
+ return entry_offset_;
+ }
+
+ inline EntryId GetEntry(size_t i) const {
+ return i < entries_.size() ? entries_[i] : 0xffffu;
+ }
+
+ void accept(Visitor* v) const;
+
+ private:
+ TypeEntry() {
+ }
+
+ TypeId target_type_id_;
+ TypeId overlay_type_id_;
+ uint16_t entry_offset_;
+ std::vector<EntryId> entries_;
+
+ friend Idmap;
+ DISALLOW_COPY_AND_ASSIGN(TypeEntry);
+ };
+
+ static std::unique_ptr<const IdmapData> FromBinaryStream(std::istream& stream);
+
+ inline const std::unique_ptr<const Header>& GetHeader() const {
+ return header_;
+ }
+
+ inline const std::vector<std::unique_ptr<const TypeEntry>>& GetTypeEntries() const {
+ return type_entries_;
+ }
+
+ void accept(Visitor* v) const;
+
+ private:
+ IdmapData() {
+ }
+
+ std::unique_ptr<const Header> header_;
+ std::vector<std::unique_ptr<const TypeEntry>> type_entries_;
+
+ friend Idmap;
+ DISALLOW_COPY_AND_ASSIGN(IdmapData);
+};
+
+class Idmap {
+ public:
+ static std::string CanonicalIdmapPathFor(const std::string& absolute_dir,
+ const std::string& absolute_apk_path);
+
+ static std::unique_ptr<const Idmap> FromBinaryStream(std::istream& stream,
+ std::ostream& out_error);
+
+ // In the current version of idmap, the first package in each resources.arsc
+ // file is used; change this in the next version of idmap to use a named
+ // package instead; also update FromApkAssets to take additional parameters:
+ // the target and overlay package names
+ static std::unique_ptr<const Idmap> FromApkAssets(const std::string& target_apk_path,
+ const ApkAssets& target_apk_assets,
+ const std::string& overlay_apk_path,
+ const ApkAssets& overlay_apk_assets,
+ std::ostream& out_error);
+
+ inline const std::unique_ptr<const IdmapHeader>& GetHeader() const {
+ return header_;
+ }
+
+ inline const std::vector<std::unique_ptr<const IdmapData>>& GetData() const {
+ return data_;
+ }
+
+ void accept(Visitor* v) const;
+
+ private:
+ Idmap() {
+ }
+
+ std::unique_ptr<const IdmapHeader> header_;
+ std::vector<std::unique_ptr<const IdmapData>> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(Idmap);
+};
+
+class Visitor {
+ public:
+ virtual ~Visitor() {
+ }
+ virtual void visit(const Idmap& idmap) = 0;
+ virtual void visit(const IdmapHeader& header) = 0;
+ virtual void visit(const IdmapData& data) = 0;
+ virtual void visit(const IdmapData::Header& header) = 0;
+ virtual void visit(const IdmapData::TypeEntry& type_entry) = 0;
+};
+
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
new file mode 100644
index 000000000000..c388f4b94251
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
@@ -0,0 +1,53 @@
+/*
+ * 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_INCLUDE_IDMAP2_PRETTYPRINTVISITOR_H_
+#define IDMAP2_INCLUDE_IDMAP2_PRETTYPRINTVISITOR_H_
+
+#include <iostream>
+#include <memory>
+
+#include "androidfw/AssetManager2.h"
+
+#include "idmap2/Idmap.h"
+
+namespace android {
+
+class ApkAssets;
+
+namespace idmap2 {
+
+class PrettyPrintVisitor : public Visitor {
+ public:
+ explicit PrettyPrintVisitor(std::ostream& stream) : stream_(stream) {
+ }
+ virtual void visit(const Idmap& idmap);
+ virtual void visit(const IdmapHeader& header);
+ virtual void visit(const IdmapData& data);
+ virtual void visit(const IdmapData::Header& header);
+ virtual void visit(const IdmapData::TypeEntry& type_entry);
+
+ private:
+ std::ostream& stream_;
+ std::unique_ptr<const ApkAssets> target_apk_;
+ AssetManager2 target_am_;
+ PackageId last_seen_package_id_;
+};
+
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_INCLUDE_IDMAP2_PRETTYPRINTVISITOR_H_
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
new file mode 100644
index 000000000000..7e33b3b06fc3
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -0,0 +1,59 @@
+/*
+ * 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_INCLUDE_IDMAP2_RAWPRINTVISITOR_H_
+#define IDMAP2_INCLUDE_IDMAP2_RAWPRINTVISITOR_H_
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+#include "androidfw/AssetManager2.h"
+
+#include "idmap2/Idmap.h"
+
+namespace android {
+
+class ApkAssets;
+
+namespace idmap2 {
+
+class RawPrintVisitor : public Visitor {
+ public:
+ explicit RawPrintVisitor(std::ostream& stream) : stream_(stream), offset_(0) {
+ }
+ virtual void visit(const Idmap& idmap);
+ virtual void visit(const IdmapHeader& header);
+ virtual void visit(const IdmapData& data);
+ virtual void visit(const IdmapData::Header& header);
+ virtual void visit(const IdmapData::TypeEntry& type_entry);
+
+ private:
+ void print(uint16_t value, const char* fmt, ...);
+ void print(uint32_t value, const char* fmt, ...);
+ void print(const std::string& value, const char* fmt, ...);
+
+ std::ostream& stream_;
+ std::unique_ptr<const ApkAssets> target_apk_;
+ AssetManager2 target_am_;
+ size_t offset_;
+ PackageId last_seen_package_id_;
+};
+
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_INCLUDE_IDMAP2_RAWPRINTVISITOR_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
new file mode 100644
index 000000000000..88a835b6439c
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -0,0 +1,39 @@
+/*
+ * 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_INCLUDE_IDMAP2_RESOURCEUTILS_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
+
+#include <string>
+#include <utility>
+
+#include "android-base/macros.h"
+#include "androidfw/AssetManager2.h"
+
+#include "idmap2/Idmap.h"
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+
+std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
+ ResourceId resid);
+
+} // namespace utils
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h
new file mode 100644
index 000000000000..9ab5ec454750
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/Xml.h
@@ -0,0 +1,51 @@
+/*
+ * 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_INCLUDE_IDMAP2_XML_H_
+#define IDMAP2_INCLUDE_IDMAP2_XML_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/String16.h"
+
+namespace android {
+namespace idmap2 {
+
+class Xml {
+ public:
+ static std::unique_ptr<const Xml> Create(const uint8_t* data, size_t size, bool copyData = false);
+
+ std::unique_ptr<std::map<std::string, std::string>> FindTag(const std::string& name) const;
+
+ ~Xml();
+
+ private:
+ Xml() {
+ }
+
+ mutable ResXMLTree xml_;
+
+ DISALLOW_COPY_AND_ASSIGN(Xml);
+};
+
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_INCLUDE_IDMAP2_XML_H_
diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h
new file mode 100644
index 000000000000..328bd367adfc
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ZipFile.h
@@ -0,0 +1,62 @@
+/*
+ * 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_INCLUDE_IDMAP2_ZIPFILE_H_
+#define IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "android-base/macros.h"
+#include "ziparchive/zip_archive.h"
+
+namespace android {
+namespace idmap2 {
+
+struct MemoryChunk {
+ size_t size;
+ uint8_t buf[0];
+
+ static std::unique_ptr<MemoryChunk> Allocate(size_t size);
+
+ private:
+ MemoryChunk() {
+ }
+};
+
+class ZipFile {
+ public:
+ static std::unique_ptr<const ZipFile> Open(const std::string& path);
+
+ std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const;
+ std::pair<bool, uint32_t> Crc(const std::string& entryPath) const;
+
+ ~ZipFile();
+
+ private:
+ explicit ZipFile(const ::ZipArchiveHandle handle) : handle_(handle) {
+ }
+
+ const ::ZipArchiveHandle handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(ZipFile);
+};
+
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
new file mode 100644
index 000000000000..29969a23250b
--- /dev/null
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 <algorithm>
+#include <cstring>
+#include <string>
+
+#include "android-base/macros.h"
+
+#include "idmap2/BinaryStreamVisitor.h"
+
+namespace android {
+namespace idmap2 {
+
+void BinaryStreamVisitor::Write16(uint16_t value) {
+ uint16_t x = htodl(value);
+ stream_.write(reinterpret_cast<char*>(&x), sizeof(uint16_t));
+}
+
+void BinaryStreamVisitor::Write32(uint32_t value) {
+ uint32_t x = htodl(value);
+ stream_.write(reinterpret_cast<char*>(&x), sizeof(uint32_t));
+}
+
+void BinaryStreamVisitor::WriteString(const StringPiece& value) {
+ char buf[kIdmapStringLength];
+ memset(buf, 0, sizeof(buf));
+ memcpy(buf, value.data(), std::min(value.size(), sizeof(buf)));
+ stream_.write(buf, sizeof(buf));
+}
+
+void BinaryStreamVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+ // nothing to do
+}
+
+void BinaryStreamVisitor::visit(const IdmapHeader& header) {
+ Write32(header.GetMagic());
+ Write32(header.GetVersion());
+ Write32(header.GetTargetCrc());
+ Write32(header.GetOverlayCrc());
+ WriteString(header.GetTargetPath());
+ WriteString(header.GetOverlayPath());
+}
+
+void BinaryStreamVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
+ // nothing to do
+}
+
+void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
+ Write16(header.GetTargetPackageId());
+ Write16(header.GetTypeCount());
+}
+
+void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& te) {
+ const uint16_t entryCount = te.GetEntryCount();
+
+ Write16(te.GetTargetTypeId());
+ Write16(te.GetOverlayTypeId());
+ Write16(entryCount);
+ Write16(te.GetEntryOffset());
+ for (uint16_t i = 0; i < entryCount; i++) {
+ EntryId entry_id = te.GetEntry(i);
+ Write32(entry_id != kNoEntry ? static_cast<uint32_t>(entry_id) : kPadding);
+ }
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
new file mode 100644
index 000000000000..28c3797226e1
--- /dev/null
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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 <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "idmap2/CommandLineOptions.h"
+
+namespace android {
+namespace idmap2 {
+
+std::unique_ptr<std::vector<std::string>> CommandLineOptions::ConvertArgvToVector(
+ int argc, const char** argv) {
+ return std::unique_ptr<std::vector<std::string>>(
+ new std::vector<std::string>(argv + 1, argv + argc));
+}
+
+CommandLineOptions& CommandLineOptions::OptionalFlag(const std::string& name,
+ const std::string& description, bool* value) {
+ assert(value != nullptr);
+ auto func = [value](const std::string& arg ATTRIBUTE_UNUSED) -> void { *value = true; };
+ options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, false});
+ return *this;
+}
+
+CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
+ const std::string& description,
+ std::string* value) {
+ assert(value != nullptr);
+ auto func = [value](const std::string& arg) -> void { *value = arg; };
+ options_.push_back(Option{name, description, func, Option::COUNT_EXACTLY_ONCE, true});
+ return *this;
+}
+
+CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
+ const std::string& description,
+ std::vector<std::string>* value) {
+ assert(value != nullptr);
+ auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
+ options_.push_back(Option{name, description, func, Option::COUNT_ONCE_OR_MORE, true});
+ return *this;
+}
+
+CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
+ const std::string& description,
+ std::string* value) {
+ assert(value != nullptr);
+ auto func = [value](const std::string& arg) -> void { *value = arg; };
+ options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, true});
+ return *this;
+}
+
+bool CommandLineOptions::Parse(const std::vector<std::string>& argv, std::ostream& outError) const {
+ const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
+ return opt.count != Option::COUNT_OPTIONAL;
+ });
+ std::set<std::string> mandatory_opts;
+ std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()),
+ [](const Option& opt) -> std::string { return opt.name; });
+
+ const size_t argv_size = argv.size();
+ for (size_t i = 0; i < argv_size; i++) {
+ const std::string arg = argv[i];
+ if ("--help" == arg || "-h" == arg) {
+ Usage(outError);
+ return false;
+ }
+ bool match = false;
+ for (const Option& opt : options_) {
+ if (opt.name == arg) {
+ match = true;
+
+ if (opt.argument) {
+ i++;
+ if (i >= argv_size) {
+ outError << "error: " << opt.name << ": missing argument" << std::endl;
+ Usage(outError);
+ return false;
+ }
+ }
+ opt.action(argv[i]);
+ mandatory_opts.erase(opt.name);
+ break;
+ }
+ }
+ if (!match) {
+ outError << "error: " << arg << ": unknown option" << std::endl;
+ Usage(outError);
+ return false;
+ }
+ }
+
+ if (!mandatory_opts.empty()) {
+ for (auto iter = mandatory_opts.cbegin(); iter != mandatory_opts.cend(); ++iter) {
+ outError << "error: " << *iter << ": missing mandatory option" << std::endl;
+ }
+ Usage(outError);
+ return false;
+ }
+ return true;
+}
+
+void CommandLineOptions::Usage(std::ostream& out) const {
+ size_t maxLength = 0;
+ out << "usage: " << name_;
+ for (const Option& opt : options_) {
+ const bool mandatory = opt.count != Option::COUNT_OPTIONAL;
+ out << " ";
+ if (!mandatory) {
+ out << "[";
+ }
+ if (opt.argument) {
+ out << opt.name << " arg";
+ maxLength = std::max(maxLength, opt.name.size() + 4);
+ } else {
+ out << opt.name;
+ maxLength = std::max(maxLength, opt.name.size());
+ }
+ if (!mandatory) {
+ out << "]";
+ }
+ if (opt.count == Option::COUNT_ONCE_OR_MORE) {
+ out << " [" << opt.name << " arg [..]]";
+ }
+ }
+ out << std::endl << std::endl;
+ for (const Option& opt : options_) {
+ out << std::left << std::setw(maxLength);
+ if (opt.argument) {
+ out << (opt.name + " arg");
+ } else {
+ out << opt.name;
+ }
+ out << " " << opt.description;
+ if (opt.count == Option::COUNT_ONCE_OR_MORE) {
+ out << " (can be provided multiple times)";
+ }
+ out << std::endl;
+ }
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
new file mode 100644
index 000000000000..4ac4c04d0bfc
--- /dev/null
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "idmap2/FileUtils.h"
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+
+std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
+ const FindFilesPredicate& predicate) {
+ DIR* dir = opendir(root.c_str());
+ if (!dir) {
+ return nullptr;
+ }
+ std::unique_ptr<std::vector<std::string>> vector(new std::vector<std::string>());
+ struct dirent* dirent;
+ while ((dirent = readdir(dir))) {
+ const std::string path = root + "/" + dirent->d_name;
+ if (predicate(dirent->d_type, path)) {
+ vector->push_back(path);
+ }
+ if (recurse && dirent->d_type == DT_DIR && strcmp(dirent->d_name, ".") != 0 &&
+ strcmp(dirent->d_name, "..") != 0) {
+ auto sub_vector = FindFiles(path, recurse, predicate);
+ if (!sub_vector) {
+ closedir(dir);
+ return nullptr;
+ }
+ vector->insert(vector->end(), sub_vector->begin(), sub_vector->end());
+ }
+ }
+ closedir(dir);
+
+ return vector;
+}
+
+std::unique_ptr<std::string> ReadFile(const std::string& path) {
+ std::unique_ptr<std::string> str(new std::string());
+ std::ifstream fin(path);
+ str->append({std::istreambuf_iterator<char>(fin), std::istreambuf_iterator<char>()});
+ fin.close();
+ return str;
+}
+
+std::unique_ptr<std::string> ReadFile(int fd) {
+ std::unique_ptr<std::string> str(new std::string());
+ char buf[1024];
+ ssize_t r;
+ while ((r = read(fd, buf, sizeof(buf))) > 0) {
+ str->append(buf, r);
+ }
+ return r == 0 ? std::move(str) : nullptr;
+}
+
+} // namespace utils
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
new file mode 100644
index 000000000000..5a47e301b66c
--- /dev/null
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -0,0 +1,443 @@
+/*
+ * 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 <algorithm>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/AssetManager2.h"
+#include "utils/String16.h"
+#include "utils/String8.h"
+
+#include "idmap2/Idmap.h"
+#include "idmap2/ResourceUtils.h"
+#include "idmap2/ZipFile.h"
+
+namespace android {
+namespace idmap2 {
+
+#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
+
+#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
+
+struct MatchingResources {
+ void Add(ResourceId target_resid, ResourceId overlay_resid) {
+ TypeId target_typeid = EXTRACT_TYPE(target_resid);
+ if (map.find(target_typeid) == map.end()) {
+ map.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>());
+ }
+ map[target_typeid].insert(std::make_pair(target_resid, overlay_resid));
+ }
+
+ // target type id -> set { pair { overlay entry id, overlay entry id } }
+ std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map;
+};
+
+static bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
+ uint16_t value;
+ if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
+ *out = dtohl(value);
+ return true;
+ }
+ return false;
+}
+
+static bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
+ uint32_t value;
+ if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
+ *out = dtohl(value);
+ return true;
+ }
+ return false;
+}
+
+// a string is encoded as a kIdmapStringLength char array; the array is always null-terminated
+static bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) {
+ char buf[kIdmapStringLength];
+ memset(buf, 0, sizeof(buf));
+ if (!stream.read(buf, sizeof(buf))) {
+ return false;
+ }
+ if (buf[sizeof(buf) - 1] != '\0') {
+ return false;
+ }
+ memcpy(out, buf, sizeof(buf));
+ return true;
+}
+
+static ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
+ return am.GetResourceId(name);
+}
+
+// TODO(martenkongstad): scan for package name instead of assuming package at index 0
+//
+// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
+// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
+// this assumption tends to work out. That said, the correct thing to do is to scan
+// resources.arsc for a package with a given name as read from the package manifest instead of
+// relying on a hard-coded index. This however requires storing the package name in the idmap
+// header, which in turn requires incrementing the idmap version. Because the initial version of
+// idmap2 is compatible with idmap, this will have to wait for now.
+static const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
+ const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
+ if (packages.empty()) {
+ return nullptr;
+ }
+ int id = packages[0]->GetPackageId();
+ return loaded_arsc.GetPackageById(id);
+}
+
+std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
+ std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
+
+ if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) ||
+ !Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) ||
+ !ReadString(stream, idmap_header->target_path_) ||
+ !ReadString(stream, idmap_header->overlay_path_)) {
+ return nullptr;
+ }
+
+ return std::move(idmap_header);
+}
+
+bool IdmapHeader::IsUpToDate(std::ostream& out_error) const {
+ if (magic_ != kIdmapMagic) {
+ out_error << base::StringPrintf("error: bad magic: actual 0x%08x, expected 0x%08x", magic_,
+ kIdmapMagic)
+ << std::endl;
+ return false;
+ }
+
+ if (version_ != kIdmapCurrentVersion) {
+ out_error << base::StringPrintf("error: bad version: actual 0x%08x, expected 0x%08x", version_,
+ kIdmapCurrentVersion)
+ << std::endl;
+ return false;
+ }
+
+ const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path_);
+ if (!target_zip) {
+ out_error << "error: failed to open target " << target_path_ << std::endl;
+ return false;
+ }
+
+ bool status;
+ uint32_t target_crc;
+ std::tie(status, target_crc) = target_zip->Crc("resources.arsc");
+ if (!status) {
+ out_error << "error: failed to get target crc" << std::endl;
+ return false;
+ }
+
+ if (target_crc_ != target_crc) {
+ out_error << base::StringPrintf(
+ "error: bad target crc: idmap version 0x%08x, file system version 0x%08x",
+ target_crc_, target_crc)
+ << std::endl;
+ return false;
+ }
+
+ const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path_);
+ if (!overlay_zip) {
+ out_error << "error: failed to open overlay " << overlay_path_ << std::endl;
+ return false;
+ }
+
+ uint32_t overlay_crc;
+ std::tie(status, overlay_crc) = overlay_zip->Crc("resources.arsc");
+ if (!status) {
+ out_error << "error: failed to get overlay crc" << std::endl;
+ return false;
+ }
+
+ if (overlay_crc_ != overlay_crc) {
+ out_error << base::StringPrintf(
+ "error: bad overlay crc: idmap version 0x%08x, file system version 0x%08x",
+ overlay_crc_, overlay_crc)
+ << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
+ std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
+
+ uint16_t target_package_id16;
+ if (!Read16(stream, &target_package_id16) || !Read16(stream, &idmap_data_header->type_count_)) {
+ return nullptr;
+ }
+ idmap_data_header->target_package_id_ = target_package_id16;
+
+ return std::move(idmap_data_header);
+}
+
+std::unique_ptr<const IdmapData::TypeEntry> IdmapData::TypeEntry::FromBinaryStream(
+ std::istream& stream) {
+ std::unique_ptr<IdmapData::TypeEntry> data(new IdmapData::TypeEntry());
+
+ uint16_t target_type16, overlay_type16, entry_count;
+ if (!Read16(stream, &target_type16) || !Read16(stream, &overlay_type16) ||
+ !Read16(stream, &entry_count) || !Read16(stream, &data->entry_offset_)) {
+ return nullptr;
+ }
+ data->target_type_id_ = target_type16;
+ data->overlay_type_id_ = overlay_type16;
+ for (uint16_t i = 0; i < entry_count; i++) {
+ ResourceId resid;
+ if (!Read32(stream, &resid)) {
+ return nullptr;
+ }
+ data->entries_.push_back(resid);
+ }
+
+ return std::move(data);
+}
+
+std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& stream) {
+ std::unique_ptr<IdmapData> data(new IdmapData());
+ data->header_ = IdmapData::Header::FromBinaryStream(stream);
+ if (!data->header_) {
+ return nullptr;
+ }
+ for (size_t type_count = 0; type_count < data->header_->GetTypeCount(); type_count++) {
+ std::unique_ptr<const TypeEntry> type = IdmapData::TypeEntry::FromBinaryStream(stream);
+ if (!type) {
+ return nullptr;
+ }
+ data->type_entries_.push_back(std::move(type));
+ }
+ return std::move(data);
+}
+
+std::string Idmap::CanonicalIdmapPathFor(const std::string& absolute_dir,
+ const std::string& absolute_apk_path) {
+ assert(absolute_dir.size() > 0 && absolute_dir[0] == "/");
+ assert(absolute_apk_path.size() > 0 && absolute_apk_path[0] == "/");
+ std::string copy(++absolute_apk_path.cbegin(), absolute_apk_path.cend());
+ replace(copy.begin(), copy.end(), '/', '@');
+ return absolute_dir + "/" + copy + "@idmap";
+}
+
+std::unique_ptr<const Idmap> Idmap::FromBinaryStream(std::istream& stream,
+ std::ostream& out_error) {
+ std::unique_ptr<Idmap> idmap(new Idmap());
+
+ idmap->header_ = IdmapHeader::FromBinaryStream(stream);
+ if (!idmap->header_) {
+ out_error << "error: failed to parse idmap header" << std::endl;
+ return nullptr;
+ }
+
+ // idmap version 0x01 does not specify the number of data blocks that follow
+ // the idmap header; assume exactly one data block
+ for (int i = 0; i < 1; i++) {
+ std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
+ if (!data) {
+ out_error << "error: failed to parse data block " << i << std::endl;
+ return nullptr;
+ }
+ idmap->data_.push_back(std::move(data));
+ }
+
+ return std::move(idmap);
+}
+
+std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_path,
+ const ApkAssets& target_apk_assets,
+ const std::string& overlay_apk_path,
+ const ApkAssets& overlay_apk_assets,
+ std::ostream& out_error) {
+ AssetManager2 target_asset_manager;
+ if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
+ out_error << "error: failed to create target asset manager" << std::endl;
+ return nullptr;
+ }
+
+ AssetManager2 overlay_asset_manager;
+ if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true, false)) {
+ out_error << "error: failed to create overlay asset manager" << std::endl;
+ return nullptr;
+ }
+
+ const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
+ if (!target_arsc) {
+ out_error << "error: failed to load target resources.arsc" << std::endl;
+ return nullptr;
+ }
+
+ const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
+ if (!overlay_arsc) {
+ out_error << "error: failed to load overlay resources.arsc" << std::endl;
+ return nullptr;
+ }
+
+ const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
+ if (!target_pkg) {
+ out_error << "error: failed to load target package from resources.arsc" << std::endl;
+ return nullptr;
+ }
+
+ const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
+ if (!overlay_pkg) {
+ out_error << "error: failed to load overlay package from resources.arsc" << std::endl;
+ return nullptr;
+ }
+
+ const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
+ if (!target_zip) {
+ out_error << "error: failed to open target as zip" << std::endl;
+ return nullptr;
+ }
+
+ const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_apk_path);
+ if (!overlay_zip) {
+ out_error << "error: failed to open overlay as zip" << std::endl;
+ return nullptr;
+ }
+
+ std::unique_ptr<IdmapHeader> header(new IdmapHeader());
+ header->magic_ = kIdmapMagic;
+ header->version_ = kIdmapCurrentVersion;
+ bool crc_status;
+ std::tie(crc_status, header->target_crc_) = target_zip->Crc("resources.arsc");
+ if (!crc_status) {
+ out_error << "error: failed to get zip crc for target" << std::endl;
+ return nullptr;
+ }
+ std::tie(crc_status, header->overlay_crc_) = overlay_zip->Crc("resources.arsc");
+ if (!crc_status) {
+ out_error << "error: failed to get zip crc for overlay" << std::endl;
+ return nullptr;
+ }
+
+ if (target_apk_path.size() > sizeof(header->target_path_)) {
+ out_error << "error: target apk path \"" << target_apk_path << "\" longer that maximum size "
+ << sizeof(header->target_path_) << std::endl;
+ return nullptr;
+ }
+ memset(header->target_path_, 0, sizeof(header->target_path_));
+ memcpy(header->target_path_, target_apk_path.data(), target_apk_path.size());
+
+ if (overlay_apk_path.size() > sizeof(header->overlay_path_)) {
+ out_error << "error: overlay apk path \"" << overlay_apk_path << "\" longer that maximum size "
+ << sizeof(header->overlay_path_) << std::endl;
+ return nullptr;
+ }
+ memset(header->overlay_path_, 0, sizeof(header->overlay_path_));
+ memcpy(header->overlay_path_, overlay_apk_path.data(), overlay_apk_path.size());
+
+ std::unique_ptr<Idmap> idmap(new Idmap());
+ idmap->header_ = std::move(header);
+
+ // find the resources that exist in both packages
+ MatchingResources matching_resources;
+ const auto end = overlay_pkg->end();
+ for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
+ const ResourceId overlay_resid = *iter;
+ bool lookup_ok;
+ std::string name;
+ std::tie(lookup_ok, name) = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
+ if (!lookup_ok) {
+ continue;
+ }
+ // prepend "<package>:" to turn name into "<package>:<type>/<name>"
+ name = base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name.c_str());
+ const ResourceId target_resid = NameToResid(target_asset_manager, name);
+ if (target_resid == 0) {
+ continue;
+ }
+ matching_resources.Add(target_resid, overlay_resid);
+ }
+
+ // encode idmap data
+ std::unique_ptr<IdmapData> data(new IdmapData());
+ const auto types_end = matching_resources.map.cend();
+ for (auto ti = matching_resources.map.cbegin(); ti != types_end; ++ti) {
+ auto ei = ti->second.cbegin();
+ std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
+ type->target_type_id_ = EXTRACT_TYPE(ei->first);
+ type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
+ type->entry_offset_ = EXTRACT_ENTRY(ei->first);
+ EntryId last_target_entry = kNoEntry;
+ for (; ei != ti->second.cend(); ++ei) {
+ if (last_target_entry != kNoEntry) {
+ int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
+ type->entries_.insert(type->entries_.end(), count, kNoEntry);
+ }
+ type->entries_.push_back(EXTRACT_ENTRY(ei->second));
+ last_target_entry = EXTRACT_ENTRY(ei->first);
+ }
+ data->type_entries_.push_back(std::move(type));
+ }
+
+ std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
+ data_header->target_package_id_ = target_pkg->GetPackageId();
+ data_header->type_count_ = data->type_entries_.size();
+ data->header_ = std::move(data_header);
+
+ idmap->data_.push_back(std::move(data));
+
+ return std::move(idmap);
+}
+
+void IdmapHeader::accept(Visitor* v) const {
+ assert(v != nullptr);
+ v->visit(*this);
+}
+
+void IdmapData::Header::accept(Visitor* v) const {
+ assert(v != nullptr);
+ v->visit(*this);
+}
+
+void IdmapData::TypeEntry::accept(Visitor* v) const {
+ assert(v != nullptr);
+ v->visit(*this);
+}
+
+void IdmapData::accept(Visitor* v) const {
+ assert(v != nullptr);
+ v->visit(*this);
+ header_->accept(v);
+ auto end = type_entries_.cend();
+ for (auto iter = type_entries_.cbegin(); iter != end; ++iter) {
+ (*iter)->accept(v);
+ }
+}
+
+void Idmap::accept(Visitor* v) const {
+ assert(v != nullptr);
+ v->visit(*this);
+ header_->accept(v);
+ auto end = data_.cend();
+ for (auto iter = data_.cbegin(); iter != end; ++iter) {
+ (*iter)->accept(v);
+ }
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
new file mode 100644
index 000000000000..492e6f049d68
--- /dev/null
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 <string>
+#include <utility>
+
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ApkAssets.h"
+
+#include "idmap2/PrettyPrintVisitor.h"
+#include "idmap2/ResourceUtils.h"
+
+namespace android {
+namespace idmap2 {
+
+#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
+
+void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+}
+
+void PrettyPrintVisitor::visit(const IdmapHeader& header) {
+ stream_ << "target apk path : " << header.GetTargetPath() << std::endl
+ << "overlay apk path : " << header.GetOverlayPath() << std::endl;
+
+ target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
+ if (target_apk_) {
+ target_am_.SetApkAssets({target_apk_.get()});
+ }
+}
+
+void PrettyPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
+}
+
+void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED) {
+ last_seen_package_id_ = header.GetTargetPackageId();
+}
+
+void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& te) {
+ const bool target_package_loaded = !target_am_.GetApkAssets().empty();
+ for (uint16_t i = 0; i < te.GetEntryCount(); i++) {
+ const EntryId entry = te.GetEntry(i);
+ if (entry == kNoEntry) {
+ continue;
+ }
+
+ const ResourceId target_resid =
+ RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i);
+ const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry);
+
+ stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
+ if (target_package_loaded) {
+ bool lookup_ok;
+ std::string name;
+ std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
+ if (lookup_ok) {
+ stream_ << " " << name;
+ }
+ }
+ stream_ << std::endl;
+ }
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
new file mode 100644
index 000000000000..57cfc8ef85b4
--- /dev/null
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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 <cstdarg>
+#include <string>
+#include <utility>
+
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ApkAssets.h"
+
+#include "idmap2/RawPrintVisitor.h"
+#include "idmap2/ResourceUtils.h"
+
+using android::ApkAssets;
+
+namespace android {
+namespace idmap2 {
+
+// verbatim copy fomr PrettyPrintVisitor.cpp, move to common utils
+#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
+
+void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+}
+
+void RawPrintVisitor::visit(const IdmapHeader& header) {
+ print(header.GetMagic(), "magic");
+ print(header.GetVersion(), "version");
+ print(header.GetTargetCrc(), "target crc");
+ print(header.GetOverlayCrc(), "overlay crc");
+ print(header.GetTargetPath().to_string(), "target path");
+ print(header.GetOverlayPath().to_string(), "overlay path");
+
+ target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
+ if (target_apk_) {
+ target_am_.SetApkAssets({target_apk_.get()});
+ }
+}
+
+void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
+}
+
+void RawPrintVisitor::visit(const IdmapData::Header& header) {
+ print(static_cast<uint16_t>(header.GetTargetPackageId()), "target package id");
+ print(header.GetTypeCount(), "type count");
+ last_seen_package_id_ = header.GetTargetPackageId();
+}
+
+void RawPrintVisitor::visit(const IdmapData::TypeEntry& te) {
+ const bool target_package_loaded = !target_am_.GetApkAssets().empty();
+
+ print(static_cast<uint16_t>(te.GetTargetTypeId()), "target type");
+ print(static_cast<uint16_t>(te.GetOverlayTypeId()), "overlay type");
+ print(static_cast<uint16_t>(te.GetEntryCount()), "entry count");
+ print(static_cast<uint16_t>(te.GetEntryOffset()), "entry offset");
+
+ for (uint16_t i = 0; i < te.GetEntryCount(); i++) {
+ const EntryId entry = te.GetEntry(i);
+ if (entry == kNoEntry) {
+ print(kPadding, "no entry");
+ } else {
+ const ResourceId target_resid =
+ RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i);
+ const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry);
+ bool lookup_ok = false;
+ std::string name;
+ if (target_package_loaded) {
+ std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
+ }
+ if (lookup_ok) {
+ print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid,
+ name.c_str());
+ } else {
+ print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid);
+ }
+ }
+ }
+}
+
+void RawPrintVisitor::print(uint16_t value, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string comment;
+ base::StringAppendV(&comment, fmt, ap);
+ va_end(ap);
+
+ stream_ << base::StringPrintf("%08zx: %04x", offset_, value) << " " << comment << std::endl;
+
+ offset_ += sizeof(uint16_t);
+}
+
+void RawPrintVisitor::print(uint32_t value, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string comment;
+ base::StringAppendV(&comment, fmt, ap);
+ va_end(ap);
+
+ stream_ << base::StringPrintf("%08zx: %08x", offset_, value) << " " << comment << std::endl;
+
+ offset_ += sizeof(uint32_t);
+}
+
+void RawPrintVisitor::print(const std::string& value, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string comment;
+ base::StringAppendV(&comment, fmt, ap);
+ va_end(ap);
+
+ stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << ": " << value
+ << std::endl;
+
+ offset_ += kIdmapStringLength;
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
new file mode 100644
index 000000000000..e98f843931c8
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 <string>
+#include <utility>
+
+#include "androidfw/StringPiece.h"
+#include "androidfw/Util.h"
+
+#include "idmap2/ResourceUtils.h"
+
+using android::StringPiece16;
+using android::util::Utf16ToUtf8;
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+
+std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
+ ResourceId resid) {
+ AssetManager2::ResourceName name;
+ if (!am.GetResourceName(resid, &name)) {
+ return std::make_pair(false, "");
+ }
+ std::string out;
+ if (name.type != nullptr) {
+ out.append(name.type, name.type_len);
+ } else {
+ out += Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
+ }
+ out.append("/");
+ if (name.entry != nullptr) {
+ out.append(name.entry, name.entry_len);
+ } else {
+ out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
+ }
+ return std::make_pair(true, out);
+}
+
+} // namespace utils
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp
new file mode 100644
index 000000000000..5543722ce4fe
--- /dev/null
+++ b/cmds/idmap2/libidmap2/Xml.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "idmap2/Xml.h"
+
+namespace android {
+namespace idmap2 {
+
+std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) {
+ std::unique_ptr<Xml> xml(new Xml());
+ if (xml->xml_.setTo(data, size, copyData) != NO_ERROR) {
+ return nullptr;
+ }
+ return xml;
+}
+
+std::unique_ptr<std::map<std::string, std::string>> Xml::FindTag(const std::string& name) const {
+ const String16 tag_to_find(name.c_str(), name.size());
+ xml_.restart();
+ ResXMLParser::event_code_t type;
+ do {
+ type = xml_.next();
+ if (type == ResXMLParser::START_TAG) {
+ size_t len;
+ const String16 tag(xml_.getElementName(&len));
+ if (tag == tag_to_find) {
+ std::unique_ptr<std::map<std::string, std::string>> map(
+ new std::map<std::string, std::string>());
+ for (size_t i = 0; i < xml_.getAttributeCount(); i++) {
+ const String16 key16(xml_.getAttributeName(i, &len));
+ std::string key = String8(key16).c_str();
+
+ std::string value;
+ switch (xml_.getAttributeDataType(i)) {
+ case Res_value::TYPE_STRING: {
+ const String16 value16(xml_.getAttributeStringValue(i, &len));
+ value = String8(value16).c_str();
+ } break;
+ case Res_value::TYPE_INT_DEC:
+ case Res_value::TYPE_INT_HEX:
+ case Res_value::TYPE_INT_BOOLEAN: {
+ Res_value resValue;
+ xml_.getAttributeValue(i, &resValue);
+ value = std::to_string(resValue.data);
+ } break;
+ default:
+ return nullptr;
+ }
+
+ map->emplace(std::make_pair(key, value));
+ }
+ return map;
+ }
+ }
+ } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
+ return nullptr;
+}
+
+Xml::~Xml() {
+ xml_.uninit();
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
new file mode 100644
index 000000000000..3f2079a380d6
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ZipFile.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 <memory>
+#include <string>
+#include <utility>
+
+#include "idmap2/ZipFile.h"
+
+namespace android {
+namespace idmap2 {
+
+std::unique_ptr<MemoryChunk> MemoryChunk::Allocate(size_t size) {
+ void* ptr = ::operator new(sizeof(MemoryChunk) + size);
+ std::unique_ptr<MemoryChunk> chunk(reinterpret_cast<MemoryChunk*>(ptr));
+ chunk->size = size;
+ return chunk;
+}
+
+std::unique_ptr<const ZipFile> ZipFile::Open(const std::string& path) {
+ ::ZipArchiveHandle handle;
+ int32_t status = ::OpenArchive(path.c_str(), &handle);
+ if (status != 0) {
+ return nullptr;
+ }
+ return std::unique_ptr<ZipFile>(new ZipFile(handle));
+}
+
+ZipFile::~ZipFile() {
+ ::CloseArchive(handle_);
+}
+
+std::unique_ptr<const MemoryChunk> ZipFile::Uncompress(const std::string& entryPath) const {
+ ::ZipEntry entry;
+ int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry);
+ if (status != 0) {
+ return nullptr;
+ }
+ std::unique_ptr<MemoryChunk> chunk = MemoryChunk::Allocate(entry.uncompressed_length);
+ status = ::ExtractToMemory(handle_, &entry, chunk->buf, chunk->size);
+ if (status != 0) {
+ return nullptr;
+ }
+ return chunk;
+}
+
+std::pair<bool, uint32_t> ZipFile::Crc(const std::string& entryPath) const {
+ ::ZipEntry entry;
+ int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry);
+ return std::make_pair(status == 0, entry.crc32);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/static-checks.sh b/cmds/idmap2/static-checks.sh
new file mode 100755
index 000000000000..560ccb692bb1
--- /dev/null
+++ b/cmds/idmap2/static-checks.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+#
+# 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.
+#
+
+function _log()
+{
+ echo -e "$*" >&2
+}
+
+function _eval()
+{
+ local label="$1"
+ local cmd="$2"
+ local red="\e[31m"
+ local green="\e[32m"
+ local reset="\e[0m"
+
+ _log "${green}[ RUN ]${reset} ${label}"
+ local output="$(eval "$cmd")"
+ if [[ -z "${output}" ]]; then
+ _log "${green}[ OK ]${reset} ${label}"
+ return 0
+ else
+ echo "${output}"
+ _log "${red}[ FAILED ]${reset} ${label}"
+ errors=$((errors + 1))
+ return 1
+ fi
+}
+
+function _clang_format()
+{
+ local path
+ local errors=0
+
+ for path in $cpp_files; do
+ local output="$(clang-format -style=file "$path" | diff $path -)"
+ if [[ "$output" ]]; then
+ echo "$path"
+ echo "$output"
+ errors=1
+ fi
+ done
+ return $errors
+}
+
+function _bpfmt()
+{
+ local output="$(bpfmt -s -d $bp_files)"
+ if [[ "$output" ]]; then
+ echo "$output"
+ return 1
+ fi
+ return 0
+}
+
+function _cpplint()
+{
+ local cpplint="${ANDROID_BUILD_TOP}/tools/repohooks/tools/cpplint.py"
+ $cpplint --quiet $cpp_files
+}
+
+function _parse_args()
+{
+ local opts
+
+ opts="$(getopt -o cfh --long check,fix,help -- "$@")"
+ if [[ $? -ne 0 ]]; then
+ exit 1
+ fi
+ eval set -- "$opts"
+ while true; do
+ case "$1" in
+ -c|--check) opt_mode="check"; shift ;;
+ -f|--fix) opt_mode="fix"; shift ;;
+ -h|--help) opt_mode="help"; shift ;;
+ *) break ;;
+ esac
+ done
+}
+
+errors=0
+script="$(readlink -f "$BASH_SOURCE")"
+prefix="$(dirname "$script")"
+cpp_files="$(find "$prefix" -name '*.cpp' -or -name '*.h')"
+bp_files="$(find "$prefix" -name 'Android.bp')"
+opt_mode="check"
+
+_parse_args "$@"
+if [[ $opt_mode == "check" ]]; then
+ _eval "clang-format" "_clang_format"
+ _eval "bpfmt" "_bpfmt"
+ _eval "cpplint" "_cpplint"
+ exit $errors
+elif [[ $opt_mode == "fix" ]]; then
+ clang-format -style=file -i $cpp_files
+ bpfmt -s -w $bp_files
+ exit 0
+elif [[ $opt_mode == "help" ]]; then
+ echo "Run static analysis tools such as clang-format and cpplint on the idmap2"
+ echo "module. Optionally fix some of the issues found (--fix). Intended to be run"
+ echo "before merging any changes."
+ echo
+ echo "usage: $(basename $script) [--check|--fix|--help]"
+ exit 0
+else
+ exit 1
+fi
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
new file mode 100644
index 000000000000..8b552dcc1265
--- /dev/null
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 <memory>
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Idmap.h"
+
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream raw_stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap1 = Idmap::FromBinaryStream(raw_stream, error);
+ ASSERT_THAT(idmap1, NotNull());
+
+ std::stringstream stream;
+ BinaryStreamVisitor visitor(stream);
+ idmap1->accept(&visitor);
+
+ std::unique_ptr<const Idmap> idmap2 = Idmap::FromBinaryStream(stream, error);
+ ASSERT_THAT(idmap2, NotNull());
+
+ ASSERT_EQ(idmap1->GetHeader()->GetTargetCrc(), idmap2->GetHeader()->GetTargetCrc());
+ ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath());
+ ASSERT_EQ(idmap1->GetData().size(), 1u);
+ ASSERT_EQ(idmap1->GetData().size(), idmap2->GetData().size());
+
+ const auto& data1 = idmap1->GetData()[0];
+ const auto& data2 = idmap2->GetData()[0];
+
+ ASSERT_EQ(data1->GetHeader()->GetTargetPackageId(), data2->GetHeader()->GetTargetPackageId());
+ ASSERT_EQ(data1->GetTypeEntries().size(), 2u);
+ ASSERT_EQ(data1->GetTypeEntries().size(), data2->GetTypeEntries().size());
+ ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(0), data2->GetTypeEntries()[0]->GetEntry(0));
+ ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(1), data2->GetTypeEntries()[0]->GetEntry(1));
+ ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(2), data2->GetTypeEntries()[0]->GetEntry(2));
+ ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(0), data2->GetTypeEntries()[1]->GetEntry(0));
+ ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(1), data2->GetTypeEntries()[1]->GetEntry(1));
+ ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(2), data2->GetTypeEntries()[1]->GetEntry(2));
+}
+
+TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
+ const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ BinaryStreamVisitor visitor(stream);
+ idmap->accept(&visitor);
+ const std::string str = stream.str();
+ const StringPiece data(str);
+ std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(data);
+ ASSERT_THAT(loaded_idmap, NotNull());
+ ASSERT_EQ(loaded_idmap->TargetPackageId(), 0x7f);
+
+ const IdmapEntry_header* header = loaded_idmap->GetEntryMapForType(0x01);
+ ASSERT_THAT(header, NotNull());
+
+ EntryId entry;
+ bool success = LoadedIdmap::Lookup(header, 0x0000, &entry);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(entry, 0x0000);
+
+ header = loaded_idmap->GetEntryMapForType(0x02);
+ ASSERT_THAT(header, NotNull());
+
+ success = LoadedIdmap::Lookup(header, 0x0002, &entry);
+ ASSERT_FALSE(success);
+
+ success = LoadedIdmap::Lookup(header, 0x0003, &entry);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(entry, 0x0000);
+
+ success = LoadedIdmap::Lookup(header, 0x0004, &entry);
+ ASSERT_FALSE(success);
+
+ success = LoadedIdmap::Lookup(header, 0x0005, &entry);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(entry, 0x0001);
+
+ success = LoadedIdmap::Lookup(header, 0x0006, &entry);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(entry, 0x0002);
+
+ success = LoadedIdmap::Lookup(header, 0x0007, &entry);
+ ASSERT_FALSE(success);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
new file mode 100644
index 000000000000..b04b25660ee4
--- /dev/null
+++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "android-base/file.h"
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Idmap.h"
+#include "androidfw/LoadedArsc.h"
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(CommandLineOptionsTests, Flag) {
+ bool foo = true, bar = false;
+
+ CommandLineOptions opts =
+ CommandLineOptions("test").OptionalFlag("--foo", "", &foo).OptionalFlag("--bar", "", &bar);
+
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--foo", "--bar"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_TRUE(foo);
+ ASSERT_TRUE(bar);
+
+ foo = bar = false;
+ success = opts.Parse({"--foo"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_TRUE(foo);
+ ASSERT_FALSE(bar);
+}
+
+TEST(CommandLineOptionsTests, MandatoryOption) {
+ std::string foo, bar;
+ CommandLineOptions opts = CommandLineOptions("test")
+ .MandatoryOption("--foo", "", &foo)
+ .MandatoryOption("--bar", "", &bar);
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(foo, "FOO");
+ ASSERT_EQ(bar, "BAR");
+
+ success = opts.Parse({"--foo"}, fakeStdErr);
+ ASSERT_FALSE(success);
+}
+
+TEST(CommandLineOptionsTests, MandatoryOptionMultipleArgsButExpectedOnce) {
+ std::string foo;
+ CommandLineOptions opts = CommandLineOptions("test").MandatoryOption("--foo", "", &foo);
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--foo", "FIRST", "--foo", "SECOND"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(foo, "SECOND");
+}
+
+TEST(CommandLineOptionsTests, MandatoryOptionMultipleArgsAndExpectedOnceOrMore) {
+ std::vector<std::string> args;
+ CommandLineOptions opts = CommandLineOptions("test").MandatoryOption("--foo", "", &args);
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--foo", "FOO", "--foo", "BAR"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(args.size(), 2u);
+ ASSERT_EQ(args[0], "FOO");
+ ASSERT_EQ(args[1], "BAR");
+}
+
+TEST(CommandLineOptionsTests, OptionalOption) {
+ std::string foo, bar;
+ CommandLineOptions opts = CommandLineOptions("test")
+ .OptionalOption("--foo", "", &foo)
+ .OptionalOption("--bar", "", &bar);
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(foo, "FOO");
+ ASSERT_EQ(bar, "BAR");
+
+ success = opts.Parse({"--foo", "BAZ"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(foo, "BAZ");
+
+ success = opts.Parse({"--foo"}, fakeStdErr);
+ ASSERT_FALSE(success);
+
+ success = opts.Parse({"--foo", "--bar", "BAR"}, fakeStdErr);
+ ASSERT_FALSE(success);
+
+ success = opts.Parse({"--foo", "FOO", "--bar"}, fakeStdErr);
+ ASSERT_FALSE(success);
+}
+
+TEST(CommandLineOptionsTests, CornerCases) {
+ std::string foo, bar;
+ bool baz = false;
+ CommandLineOptions opts = CommandLineOptions("test")
+ .MandatoryOption("--foo", "", &foo)
+ .OptionalFlag("--baz", "", &baz)
+ .OptionalOption("--bar", "", &bar);
+ std::ostream fakeStdErr(nullptr);
+ bool success = opts.Parse({"--unexpected"}, fakeStdErr);
+ ASSERT_FALSE(success);
+
+ success = opts.Parse({"--bar", "BAR"}, fakeStdErr);
+ ASSERT_FALSE(success);
+
+ success = opts.Parse({"--baz", "--foo", "FOO"}, fakeStdErr);
+ ASSERT_TRUE(success);
+ ASSERT_TRUE(baz);
+ ASSERT_EQ(foo, "FOO");
+}
+
+TEST(CommandLineOptionsTests, ConvertArgvToVector) {
+ const char* argv[] = {
+ "program-name",
+ "--foo",
+ "FOO",
+ nullptr,
+ };
+ std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(3, argv);
+ ASSERT_EQ(v->size(), 2ul);
+ ASSERT_EQ((*v)[0], "--foo");
+ ASSERT_EQ((*v)[1], "FOO");
+}
+
+TEST(CommandLineOptionsTests, ConvertArgvToVectorNoArgs) {
+ const char* argv[] = {
+ "program-name",
+ nullptr,
+ };
+ std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(1, argv);
+ ASSERT_EQ(v->size(), 0ul);
+}
+
+TEST(CommandLineOptionsTests, Usage) {
+ std::string arg1, arg2, arg3, arg4;
+ bool arg5 = false, arg6 = false;
+ std::vector<std::string> arg7;
+ CommandLineOptions opts = CommandLineOptions("test")
+ .MandatoryOption("--aa", "description-aa", &arg1)
+ .OptionalFlag("--bb", "description-bb", &arg5)
+ .OptionalOption("--cc", "description-cc", &arg2)
+ .OptionalOption("--dd", "description-dd", &arg3)
+ .MandatoryOption("--ee", "description-ee", &arg4)
+ .OptionalFlag("--ff", "description-ff", &arg6)
+ .MandatoryOption("--gg", "description-gg", &arg7);
+ std::stringstream stream;
+ opts.Usage(stream);
+ const std::string s = stream.str();
+ ASSERT_NE(s.find("usage: test --aa arg [--bb] [--cc arg] [--dd arg] --ee arg [--ff] --gg arg "
+ "[--gg arg [..]]"),
+ std::string::npos);
+ ASSERT_NE(s.find("--aa arg description-aa"), std::string::npos);
+ ASSERT_NE(s.find("--ff description-ff"), std::string::npos);
+ ASSERT_NE(s.find("--gg arg description-gg (can be provided multiple times)"),
+ std::string::npos);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
new file mode 100644
index 000000000000..0c6439ab8c0c
--- /dev/null
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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 <dirent.h>
+#include <set>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "android-base/macros.h"
+
+#include "idmap2/FileUtils.h"
+
+#include "TestHelpers.h"
+
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+
+TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
+ const auto& root = GetTestDataPath();
+ auto v = utils::FindFiles(root, false,
+ [](unsigned char type ATTRIBUTE_UNUSED,
+ const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
+ ASSERT_THAT(v, NotNull());
+ ASSERT_EQ(v->size(), 4u);
+ ASSERT_EQ(
+ std::set<std::string>(v->begin(), v->end()),
+ std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target"}));
+}
+
+TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
+ const auto& root = GetTestDataPath();
+ auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool {
+ return type == DT_REG && path.size() > 4 && !path.compare(path.size() - 4, 4, ".apk");
+ });
+ ASSERT_THAT(v, NotNull());
+ ASSERT_EQ(v->size(), 4u);
+ ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
+ std::set<std::string>({root + "/target/target.apk", root + "/overlay/overlay.apk",
+ root + "/overlay/overlay-static-1.apk",
+ root + "/overlay/overlay-static-2.apk"}));
+}
+
+TEST(FileUtilsTests, ReadFile) {
+ int pipefd[2];
+ ASSERT_EQ(pipe(pipefd), 0);
+
+ ASSERT_EQ(write(pipefd[1], "foobar", 6), 6);
+ close(pipefd[1]);
+
+ auto data = ReadFile(pipefd[0]);
+ ASSERT_THAT(data, NotNull());
+ ASSERT_EQ(*data, "foobar");
+ close(pipefd[0]);
+}
+
+} // namespace utils
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
new file mode 100644
index 000000000000..5c4e8576985b
--- /dev/null
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+/*
+ * The tests in this file operate on a higher level than the tests in the other
+ * files. Here, all tests execute the idmap2 binary and only depend on
+ * libidmap2 to verify the output of idmap2.
+ */
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring> // strerror
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/PosixUtils.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::android::util::ExecuteBinary;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+class Idmap2BinaryTests : public Idmap2Tests {};
+
+static void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
+ const std::string& overlay_apk_path) {
+ // check that the idmap file looks reasonable (IdmapTests is responsible for
+ // more in-depth verification)
+ ASSERT_EQ(idmap.GetHeader()->GetMagic(), kIdmapMagic);
+ ASSERT_EQ(idmap.GetHeader()->GetVersion(), kIdmapCurrentVersion);
+ ASSERT_EQ(idmap.GetHeader()->GetTargetPath(), target_apk_path);
+ ASSERT_EQ(idmap.GetHeader()->GetOverlayPath(), overlay_apk_path);
+ ASSERT_EQ(idmap.GetData().size(), 1u);
+}
+
+#define ASSERT_IDMAP(idmap_ref, target_apk_path, overlay_apk_path) \
+ do { \
+ ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \
+ } while (0)
+
+TEST_F(Idmap2BinaryTests, Create) {
+ // clang-format off
+ auto result = ExecuteBinary({"idmap2",
+ "create",
+ "--target-apk-path", GetTargetApkPath(),
+ "--overlay-apk-path", GetOverlayApkPath(),
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+
+ struct stat st;
+ ASSERT_EQ(stat(GetIdmapPath().c_str(), &st), 0);
+
+ std::stringstream error;
+ std::ifstream fin(GetIdmapPath());
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(fin, error);
+ fin.close();
+
+ ASSERT_THAT(idmap, NotNull());
+ ASSERT_IDMAP(*idmap, GetTargetApkPath(), GetOverlayApkPath());
+
+ unlink(GetIdmapPath().c_str());
+}
+
+TEST_F(Idmap2BinaryTests, Dump) {
+ // clang-format off
+ auto result = ExecuteBinary({"idmap2",
+ "create",
+ "--target-apk-path", GetTargetApkPath(),
+ "--overlay-apk-path", GetOverlayApkPath(),
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "dump",
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
+ ASSERT_NE(result->stdout.find("0x7f020003 -> 0x7f020000 string/str1"), std::string::npos);
+ ASSERT_NE(result->stdout.find("0x7f020005 -> 0x7f020001 string/str3"), std::string::npos);
+ ASSERT_EQ(result->stdout.find("00000210: 007f target package id"), std::string::npos);
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "dump",
+ "--verbose",
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_NE(result->stdout.find("00000000: 504d4449 magic"), std::string::npos);
+ ASSERT_NE(result->stdout.find("00000210: 007f target package id"), std::string::npos);
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "dump",
+ "--verbose",
+ "--idmap-path", GetTestDataPath() + "/DOES-NOT-EXIST"});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_NE(result->status, EXIT_SUCCESS);
+
+ unlink(GetIdmapPath().c_str());
+}
+
+TEST_F(Idmap2BinaryTests, Scan) {
+ const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
+ const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
+ const std::string idmap_static_1_path =
+ Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_1_apk_path);
+ const std::string idmap_static_2_path =
+ Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_2_apk_path);
+
+ // single input directory, recursive
+ // clang-format off
+ auto result = ExecuteBinary({"idmap2",
+ "scan",
+ "--input-directory", GetTestDataPath(),
+ "--recursive",
+ "--target-package-name", "test.target",
+ "--target-apk-path", GetTargetApkPath(),
+ "--output-directory", GetTempDirPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ std::stringstream expected;
+ expected << idmap_static_1_path << std::endl;
+ expected << idmap_static_2_path << std::endl;
+ ASSERT_EQ(result->stdout, expected.str());
+
+ std::stringstream error;
+ auto idmap_static_1_raw_string = utils::ReadFile(idmap_static_1_path);
+ auto idmap_static_1_raw_stream = std::istringstream(*idmap_static_1_raw_string);
+ auto idmap_static_1 = Idmap::FromBinaryStream(idmap_static_1_raw_stream, error);
+ ASSERT_THAT(idmap_static_1, NotNull());
+ ASSERT_IDMAP(*idmap_static_1, GetTargetApkPath(), overlay_static_1_apk_path);
+
+ auto idmap_static_2_raw_string = utils::ReadFile(idmap_static_2_path);
+ auto idmap_static_2_raw_stream = std::istringstream(*idmap_static_2_raw_string);
+ auto idmap_static_2 = Idmap::FromBinaryStream(idmap_static_2_raw_stream, error);
+ ASSERT_THAT(idmap_static_2, NotNull());
+ ASSERT_IDMAP(*idmap_static_2, GetTargetApkPath(), overlay_static_2_apk_path);
+
+ unlink(idmap_static_2_path.c_str());
+ unlink(idmap_static_1_path.c_str());
+
+ // multiple input directories, non-recursive
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "scan",
+ "--input-directory", GetTestDataPath() + "/target",
+ "--input-directory", GetTestDataPath() + "/overlay",
+ "--target-package-name", "test.target",
+ "--target-apk-path", GetTargetApkPath(),
+ "--output-directory", GetTempDirPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_EQ(result->stdout, expected.str());
+ unlink(idmap_static_2_path.c_str());
+ unlink(idmap_static_1_path.c_str());
+
+ // the same input directory given twice, but no duplicate entries
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "scan",
+ "--input-directory", GetTestDataPath(),
+ "--input-directory", GetTestDataPath(),
+ "--recursive",
+ "--target-package-name", "test.target",
+ "--target-apk-path", GetTargetApkPath(),
+ "--output-directory", GetTempDirPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_EQ(result->stdout, expected.str());
+ unlink(idmap_static_2_path.c_str());
+ unlink(idmap_static_1_path.c_str());
+
+ // no APKs in input-directory: ok, but no output
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "scan",
+ "--input-directory", GetTempDirPath(),
+ "--target-package-name", "test.target",
+ "--target-apk-path", GetTargetApkPath(),
+ "--output-directory", GetTempDirPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_EQ(result->stdout, "");
+}
+
+TEST_F(Idmap2BinaryTests, Lookup) {
+ // clang-format off
+ auto result = ExecuteBinary({"idmap2",
+ "create",
+ "--target-apk-path", GetTargetApkPath(),
+ "--overlay-apk-path", GetOverlayApkPath(),
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "lookup",
+ "--idmap-path", GetIdmapPath(),
+ "--config", "",
+ "--resid", "0x7f020003"}); // string/str1
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
+ ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "lookup",
+ "--idmap-path", GetIdmapPath(),
+ "--config", "",
+ "--resid", "test.target:string/str1"});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
+ ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
+
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "lookup",
+ "--idmap-path", GetIdmapPath(),
+ "--config", "sv",
+ "--resid", "test.target:string/str1"});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_NE(result->stdout.find("overlay-1-sv"), std::string::npos);
+
+ unlink(GetIdmapPath().c_str());
+}
+
+TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) {
+ const std::string invalid_target_apk_path = GetTestDataPath() + "/DOES-NOT-EXIST";
+
+ // missing mandatory options
+ // clang-format off
+ auto result = ExecuteBinary({"idmap2",
+ "create"});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_NE(result->status, EXIT_SUCCESS);
+
+ // missing argument to option
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "create",
+ "--target-apk-path", GetTargetApkPath(),
+ "--overlay-apk-path", GetOverlayApkPath(),
+ "--idmap-path"});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_NE(result->status, EXIT_SUCCESS);
+
+ // invalid target apk path
+ // clang-format off
+ result = ExecuteBinary({"idmap2",
+ "create",
+ "--target-apk-path", invalid_target_apk_path,
+ "--overlay-apk-path", GetOverlayApkPath(),
+ "--idmap-path", GetIdmapPath()});
+ // clang-format on
+ ASSERT_THAT(result, NotNull());
+ ASSERT_NE(result->status, EXIT_SUCCESS);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
new file mode 100644
index 000000000000..0379aa491682
--- /dev/null
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -0,0 +1,395 @@
+/*
+ * 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 <cstdio> // fclose
+
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "android-base/macros.h"
+#include "androidfw/ApkAssets.h"
+
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(IdmapTests, TestCanonicalIdmapPathFor) {
+ ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
+ "/foo/vendor@overlay@bar.apk@idmap");
+}
+
+TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream stream(raw);
+ std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
+ ASSERT_THAT(header, NotNull());
+ ASSERT_EQ(header->GetMagic(), 0x504d4449u);
+ ASSERT_EQ(header->GetVersion(), 0x01u);
+ ASSERT_EQ(header->GetTargetCrc(), 0x1234u);
+ ASSERT_EQ(header->GetOverlayCrc(), 0x5678u);
+ ASSERT_EQ(header->GetTargetPath().to_string(), "target.apk");
+ ASSERT_EQ(header->GetOverlayPath().to_string(), "overlay.apk");
+}
+
+TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ // overwrite the target path string, including the terminating null, with '.'
+ for (size_t i = 0x10; i < 0x110; i++) {
+ raw[i] = '.';
+ }
+ std::istringstream stream(raw);
+ std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
+ ASSERT_THAT(header, IsNull());
+}
+
+TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
+ const size_t offset = 0x210;
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
+ idmap_raw_data_len - offset);
+ std::istringstream stream(raw);
+
+ std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
+ ASSERT_THAT(header, NotNull());
+ ASSERT_EQ(header->GetTargetPackageId(), 0x7fu);
+ ASSERT_EQ(header->GetTypeCount(), 2u);
+}
+
+TEST(IdmapTests, CreateIdmapDataResourceTypeFromBinaryStream) {
+ const size_t offset = 0x214;
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
+ idmap_raw_data_len - offset);
+ std::istringstream stream(raw);
+
+ std::unique_ptr<const IdmapData::TypeEntry> data = IdmapData::TypeEntry::FromBinaryStream(stream);
+ ASSERT_THAT(data, NotNull());
+ ASSERT_EQ(data->GetTargetTypeId(), 0x02u);
+ ASSERT_EQ(data->GetOverlayTypeId(), 0x02u);
+ ASSERT_EQ(data->GetEntryCount(), 1u);
+ ASSERT_EQ(data->GetEntryOffset(), 0u);
+ ASSERT_EQ(data->GetEntry(0), 0u);
+}
+
+TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
+ const size_t offset = 0x210;
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
+ idmap_raw_data_len - offset);
+ std::istringstream stream(raw);
+
+ std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
+ ASSERT_THAT(data, NotNull());
+ ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
+ ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+ const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+ ASSERT_EQ(types.size(), 2u);
+
+ ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u);
+ ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u);
+ ASSERT_EQ(types[0]->GetEntryCount(), 1u);
+ ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
+ ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+
+ ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u);
+ ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u);
+ ASSERT_EQ(types[1]->GetEntryCount(), 3u);
+ ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
+ ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
+ ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+}
+
+TEST(IdmapTests, CreateIdmapFromBinaryStream) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(stream, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ ASSERT_THAT(idmap->GetHeader(), NotNull());
+ ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234u);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678u);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "target.apk");
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlay.apk");
+
+ const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+ ASSERT_EQ(dataBlocks.size(), 1u);
+
+ const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
+ ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+ const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+ ASSERT_EQ(types.size(), 2u);
+
+ ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u);
+ ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u);
+ ASSERT_EQ(types[0]->GetEntryCount(), 1u);
+ ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
+ ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+
+ ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u);
+ ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u);
+ ASSERT_EQ(types[1]->GetEntryCount(), 3u);
+ ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
+ ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
+ ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+}
+
+TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data),
+ 10); // data too small
+ std::istringstream stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(stream, error);
+ ASSERT_THAT(idmap, IsNull());
+}
+
+TEST(IdmapTests, CreateIdmapFromApkAssets) {
+ const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ ASSERT_THAT(idmap->GetHeader(), NotNull());
+ ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xf5ad1d1d);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
+
+ const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+ ASSERT_EQ(dataBlocks.size(), 1u);
+
+ const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+
+ ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
+ ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+
+ const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+ ASSERT_EQ(types.size(), 2u);
+
+ ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01u);
+ ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01u);
+ ASSERT_EQ(types[0]->GetEntryCount(), 1u);
+ ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
+ ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+
+ ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02u);
+ ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02u);
+ ASSERT_EQ(types[1]->GetEntryCount(), 4u);
+ ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
+ ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
+ ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+ ASSERT_EQ(types[1]->GetEntry(3), 0x0002u);
+}
+
+TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
+ std::string target_apk_path(GetTestDataPath());
+ for (int i = 0; i < 32; i++) {
+ target_apk_path += "/target/../";
+ }
+ target_apk_path += "/target/target.apk";
+ ASSERT_GT(target_apk_path.size(), kIdmapStringLength);
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, IsNull());
+}
+
+TEST(IdmapTests, IdmapHeaderIsUpToDate) {
+ fclose(stderr); // silence expected warnings from libandroidfw
+
+ const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ BinaryStreamVisitor visitor(stream);
+ idmap->accept(&visitor);
+
+ std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
+ ASSERT_THAT(header, NotNull());
+ ASSERT_TRUE(header->IsUpToDate(error)) << error.str();
+
+ // magic: bytes (0x0, 0x03)
+ std::string bad_magic_string(stream.str());
+ bad_magic_string[0x0] = '.';
+ bad_magic_string[0x1] = '.';
+ bad_magic_string[0x2] = '.';
+ bad_magic_string[0x3] = '.';
+ std::stringstream bad_magic_stream(bad_magic_string);
+ std::unique_ptr<const IdmapHeader> bad_magic_header =
+ IdmapHeader::FromBinaryStream(bad_magic_stream);
+ ASSERT_THAT(bad_magic_header, NotNull());
+ ASSERT_NE(header->GetMagic(), bad_magic_header->GetMagic());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(error));
+
+ // version: bytes (0x4, 0x07)
+ std::string bad_version_string(stream.str());
+ bad_version_string[0x4] = '.';
+ bad_version_string[0x5] = '.';
+ bad_version_string[0x6] = '.';
+ bad_version_string[0x7] = '.';
+ std::stringstream bad_version_stream(bad_version_string);
+ std::unique_ptr<const IdmapHeader> bad_version_header =
+ IdmapHeader::FromBinaryStream(bad_version_stream);
+ ASSERT_THAT(bad_version_header, NotNull());
+ ASSERT_NE(header->GetVersion(), bad_version_header->GetVersion());
+ ASSERT_FALSE(bad_version_header->IsUpToDate(error));
+
+ // target crc: bytes (0x8, 0xb)
+ std::string bad_target_crc_string(stream.str());
+ bad_target_crc_string[0x8] = '.';
+ bad_target_crc_string[0x9] = '.';
+ bad_target_crc_string[0xa] = '.';
+ bad_target_crc_string[0xb] = '.';
+ std::stringstream bad_target_crc_stream(bad_target_crc_string);
+ std::unique_ptr<const IdmapHeader> bad_target_crc_header =
+ IdmapHeader::FromBinaryStream(bad_target_crc_stream);
+ ASSERT_THAT(bad_target_crc_header, NotNull());
+ ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc());
+ ASSERT_FALSE(bad_target_crc_header->IsUpToDate(error));
+
+ // overlay crc: bytes (0xc, 0xf)
+ std::string bad_overlay_crc_string(stream.str());
+ bad_overlay_crc_string[0xc] = '.';
+ bad_overlay_crc_string[0xd] = '.';
+ bad_overlay_crc_string[0xe] = '.';
+ bad_overlay_crc_string[0xf] = '.';
+ std::stringstream bad_overlay_crc_stream(bad_overlay_crc_string);
+ std::unique_ptr<const IdmapHeader> bad_overlay_crc_header =
+ IdmapHeader::FromBinaryStream(bad_overlay_crc_stream);
+ ASSERT_THAT(bad_overlay_crc_header, NotNull());
+ ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc());
+ ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate(error));
+
+ // target path: bytes (0x10, 0x10f)
+ std::string bad_target_path_string(stream.str());
+ bad_target_path_string[0x10] = '\0';
+ std::stringstream bad_target_path_stream(bad_target_path_string);
+ std::unique_ptr<const IdmapHeader> bad_target_path_header =
+ IdmapHeader::FromBinaryStream(bad_target_path_stream);
+ ASSERT_THAT(bad_target_path_header, NotNull());
+ ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath());
+ ASSERT_FALSE(bad_target_path_header->IsUpToDate(error));
+
+ // overlay path: bytes (0x110, 0x20f)
+ std::string bad_overlay_path_string(stream.str());
+ bad_overlay_path_string[0x110] = '\0';
+ std::stringstream bad_overlay_path_stream(bad_overlay_path_string);
+ std::unique_ptr<const IdmapHeader> bad_overlay_path_header =
+ IdmapHeader::FromBinaryStream(bad_overlay_path_stream);
+ ASSERT_THAT(bad_overlay_path_header, NotNull());
+ ASSERT_NE(header->GetOverlayPath(), bad_overlay_path_header->GetOverlayPath());
+ ASSERT_FALSE(bad_overlay_path_header->IsUpToDate(error));
+}
+
+class TestVisitor : public Visitor {
+ public:
+ explicit TestVisitor(std::ostream& stream) : stream_(stream) {
+ }
+
+ void visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+ stream_ << "TestVisitor::visit(Idmap)" << std::endl;
+ }
+
+ void visit(const IdmapHeader& idmap ATTRIBUTE_UNUSED) {
+ stream_ << "TestVisitor::visit(IdmapHeader)" << std::endl;
+ }
+
+ void visit(const IdmapData& idmap ATTRIBUTE_UNUSED) {
+ stream_ << "TestVisitor::visit(IdmapData)" << std::endl;
+ }
+
+ void visit(const IdmapData::Header& idmap ATTRIBUTE_UNUSED) {
+ stream_ << "TestVisitor::visit(IdmapData::Header)" << std::endl;
+ }
+
+ void visit(const IdmapData::TypeEntry& idmap ATTRIBUTE_UNUSED) {
+ stream_ << "TestVisitor::visit(IdmapData::TypeEntry)" << std::endl;
+ }
+
+ private:
+ std::ostream& stream_;
+};
+
+TEST(IdmapTests, TestVisitor) {
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(stream, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream test_stream;
+ TestVisitor visitor(test_stream);
+ idmap->accept(&visitor);
+
+ ASSERT_EQ(test_stream.str(),
+ "TestVisitor::visit(Idmap)\n"
+ "TestVisitor::visit(IdmapHeader)\n"
+ "TestVisitor::visit(IdmapData)\n"
+ "TestVisitor::visit(IdmapData::Header)\n"
+ "TestVisitor::visit(IdmapData::TypeEntry)\n"
+ "TestVisitor::visit(IdmapData::TypeEntry)\n");
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/Main.cpp b/cmds/idmap2/tests/Main.cpp
new file mode 100644
index 000000000000..f2469eaf57cc
--- /dev/null
+++ b/cmds/idmap2/tests/Main.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 <string>
+
+#include "android-base/file.h"
+
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+
+namespace android {
+namespace idmap2 {
+
+const std::string GetTestDataPath() {
+ return base::GetExecutableDirectory() + "/tests/data";
+}
+
+} // namespace idmap2
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
new file mode 100644
index 000000000000..da9779211f81
--- /dev/null
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 <memory>
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Idmap.h"
+
+#include "idmap2/Idmap.h"
+#include "idmap2/PrettyPrintVisitor.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+using android::ApkAssets;
+
+namespace android {
+namespace idmap2 {
+
+TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
+ const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ PrettyPrintVisitor visitor(stream);
+ idmap->accept(&visitor);
+
+ ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos);
+}
+
+TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
+ fclose(stderr); // silence expected warnings from libandroidfw
+
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream raw_stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(raw_stream, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ PrettyPrintVisitor visitor(stream);
+ idmap->accept(&visitor);
+
+ ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000\n"), std::string::npos);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
new file mode 100644
index 000000000000..c28ce2e02ea9
--- /dev/null
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 <cstdio> // fclose
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "idmap2/Idmap.h"
+#include "idmap2/RawPrintVisitor.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
+ const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap =
+ Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ RawPrintVisitor visitor(stream);
+ idmap->accept(&visitor);
+
+ ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000008: f5ad1d1d target crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000000c: d470336b overlay crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"),
+ std::string::npos);
+}
+
+TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
+ fclose(stderr); // silence expected warnings from libandroidfw
+
+ std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+ std::istringstream raw_stream(raw);
+
+ std::stringstream error;
+ std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(raw_stream, error);
+ ASSERT_THAT(idmap, NotNull());
+
+ std::stringstream stream;
+ RawPrintVisitor visitor(stream);
+ idmap->accept(&visitor);
+
+ ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000008: 00001234 target crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000000c: 00005678 overlay crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f020000 -> 0x7f020000\n"), std::string::npos);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
new file mode 100644
index 000000000000..0547fa00de3d
--- /dev/null
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/ApkAssets.h"
+#include "idmap2/ResourceUtils.h"
+
+#include "TestHelpers.h"
+
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+class ResourceUtilsTests : public Idmap2Tests {
+ protected:
+ void SetUp() override {
+ Idmap2Tests::SetUp();
+
+ apk_assets_ = ApkAssets::Load(GetTargetApkPath());
+ ASSERT_THAT(apk_assets_, NotNull());
+
+ am_.SetApkAssets({apk_assets_.get()});
+ }
+
+ const AssetManager2& GetAssetManager() {
+ return am_;
+ }
+
+ private:
+ AssetManager2 am_;
+ std::unique_ptr<const ApkAssets> apk_assets_;
+};
+
+TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
+ bool lookup_ok;
+ std::string name;
+ std::tie(lookup_ok, name) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
+ ASSERT_TRUE(lookup_ok);
+ ASSERT_EQ(name, "integer/int1");
+}
+
+TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
+ bool lookup_ok;
+ std::tie(lookup_ok, std::ignore) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
+ ASSERT_FALSE(lookup_ok);
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
new file mode 100644
index 000000000000..18dc541021c1
--- /dev/null
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -0,0 +1,168 @@
+/*
+ * 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_TESTS_TESTHELPERS_H_
+#define IDMAP2_TESTS_TESTHELPERS_H_
+
+#include <string>
+
+namespace android {
+namespace idmap2 {
+
+const unsigned char idmap_raw_data[] = {
+ // IDMAP HEADER
+ // 0x0: magic
+ 0x49, 0x44, 0x4d, 0x50,
+
+ // 0x4: version
+ 0x01, 0x00, 0x00, 0x00,
+
+ // 0x8: target crc
+ 0x34, 0x12, 0x00, 0x00,
+
+ // 0xc: overlay crc
+ 0x78, 0x56, 0x00, 0x00,
+
+ // 0x10: target path "target.apk"
+ 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ // 0x110: overlay path "overlay.apk"
+ 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ // DATA HEADER
+ // 0x210: target package id
+ 0x7f, 0x00,
+
+ // 0x212: types count
+ 0x02, 0x00,
+
+ // DATA BLOCK
+ // 0x214: target type
+ 0x02, 0x00,
+
+ // 0x216: overlay type
+ 0x02, 0x00,
+
+ // 0x218: entry count
+ 0x01, 0x00,
+
+ // 0x21a: entry offset
+ 0x00, 0x00,
+
+ // 0x21c: entries
+ 0x00, 0x00, 0x00, 0x00,
+
+ // DATA BLOCK
+ // 0x220: target type
+ 0x03, 0x00,
+
+ // 0x222: overlay type
+ 0x03, 0x00,
+
+ // 0x224: entry count
+ 0x03, 0x00,
+
+ // 0x226: entry offset
+ 0x03, 0x00,
+
+ // 0x228, 0x22c, 0x230: entries
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0xff, 0xff, 0xff, 0xff,
+
+ 0x01, 0x00, 0x00, 0x00};
+
+const unsigned int idmap_raw_data_len = 565;
+
+const std::string GetTestDataPath();
+
+class Idmap2Tests : public testing::Test {
+ protected:
+ virtual void SetUp() {
+#ifdef __ANDROID__
+ tmp_dir_path_ = "/data/local/tmp/idmap2-tests-XXXXXX";
+#else
+ tmp_dir_path_ = "/tmp/idmap2-tests-XXXXXX";
+#endif
+ EXPECT_NE(mkdtemp(const_cast<char*>(tmp_dir_path_.c_str())), nullptr)
+ << "Failed to create temporary directory: " << strerror(errno);
+ target_apk_path_ = GetTestDataPath() + "/target/target.apk";
+ overlay_apk_path_ = GetTestDataPath() + "/overlay/overlay.apk";
+ idmap_path_ = tmp_dir_path_ + "/a.idmap";
+ }
+
+ virtual void TearDown() {
+ EXPECT_EQ(rmdir(tmp_dir_path_.c_str()), 0)
+ << "Failed to remove temporary directory " << tmp_dir_path_ << ": " << strerror(errno);
+ }
+
+ const std::string& GetTempDirPath() {
+ return tmp_dir_path_;
+ }
+
+ const std::string& GetTargetApkPath() {
+ return target_apk_path_;
+ }
+
+ const std::string& GetOverlayApkPath() {
+ return overlay_apk_path_;
+ }
+
+ const std::string& GetIdmapPath() {
+ return idmap_path_;
+ }
+
+ private:
+ std::string tmp_dir_path_;
+ std::string target_apk_path_;
+ std::string overlay_apk_path_;
+ std::string idmap_path_;
+};
+
+} // namespace idmap2
+} // namespace android
+
+#endif // IDMAP2_TESTS_TESTHELPERS_H_
diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp
new file mode 100644
index 000000000000..97ff03e0f9e3
--- /dev/null
+++ b/cmds/idmap2/tests/XmlTests.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <cstdio> // fclose
+
+#include "idmap2/Xml.h"
+#include "idmap2/ZipFile.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(XmlTests, Create) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ ASSERT_THAT(zip, NotNull());
+
+ auto data = zip->Uncompress("AndroidManifest.xml");
+ ASSERT_THAT(data, NotNull());
+
+ auto xml = Xml::Create(data->buf, data->size);
+ ASSERT_THAT(xml, NotNull());
+
+ fclose(stderr); // silence expected warnings from libandroidfw
+ const char* not_xml = "foo";
+ auto fail = Xml::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
+ ASSERT_THAT(fail, IsNull());
+}
+
+TEST(XmlTests, FindTag) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ ASSERT_THAT(zip, NotNull());
+
+ auto data = zip->Uncompress("res/xml/test.xml");
+ ASSERT_THAT(data, NotNull());
+
+ auto xml = Xml::Create(data->buf, data->size);
+ ASSERT_THAT(xml, NotNull());
+
+ auto attrs = xml->FindTag("c");
+ ASSERT_THAT(attrs, NotNull());
+ ASSERT_EQ(attrs->size(), 4u);
+ ASSERT_EQ(attrs->at("type_string"), "fortytwo");
+ ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42);
+ ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42);
+ ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0u);
+
+ auto fail = xml->FindTag("does-not-exist");
+ ASSERT_THAT(fail, IsNull());
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp
new file mode 100644
index 000000000000..a504d3126c05
--- /dev/null
+++ b/cmds/idmap2/tests/ZipFileTests.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <cstdio> // fclose
+#include <string>
+#include <utility>
+
+#include "idmap2/ZipFile.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(ZipFileTests, BasicOpen) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ ASSERT_THAT(zip, NotNull());
+
+ fclose(stderr); // silence expected warnings from libziparchive
+ auto fail = ZipFile::Open(GetTestDataPath() + "/does-not-exist");
+ ASSERT_THAT(fail, IsNull());
+}
+
+TEST(ZipFileTests, Crc) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ ASSERT_THAT(zip, NotNull());
+
+ bool status;
+ uint32_t crc;
+ std::tie(status, crc) = zip->Crc("AndroidManifest.xml");
+ ASSERT_TRUE(status);
+ ASSERT_EQ(crc, 0x762f3d24);
+
+ std::tie(status, std::ignore) = zip->Crc("does-not-exist");
+ ASSERT_FALSE(status);
+}
+
+TEST(ZipFileTests, Uncompress) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ ASSERT_THAT(zip, NotNull());
+
+ auto data = zip->Uncompress("assets/lorem-ipsum.txt");
+ ASSERT_THAT(data, NotNull());
+ const std::string lorem_ipsum("Lorem ipsum dolor sit amet.\n");
+ ASSERT_THAT(data->size, lorem_ipsum.size());
+ ASSERT_THAT(std::string(reinterpret_cast<const char*>(data->buf), data->size), lorem_ipsum);
+
+ auto fail = zip->Uncompress("does-not-exist");
+ ASSERT_THAT(fail, IsNull());
+}
+
+} // namespace idmap2
+} // namespace android
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
new file mode 100644
index 000000000000..9f89d3121a82
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.overlay">
+ <overlay
+ android:targetPackage="test.target" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml
new file mode 100644
index 000000000000..39336cc7e76b
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.overlay.static1">
+ <overlay
+ android:targetPackage="test.target"
+ android:isStatic="true"
+ android:priority="1" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml
new file mode 100644
index 000000000000..e1cc1758d8cc
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.overlay.static2">
+ <overlay
+ android:targetPackage="test.target"
+ android:isStatic="true"
+ android:priority="2" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
new file mode 100644
index 000000000000..cba108674005
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -0,0 +1,40 @@
+# 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.
+
+FRAMEWORK_RES_APK="$(gettop)/out/target/common/obj/APPS/framework-res_intermediates/package-export.apk"
+
+aapt2 compile --dir res -o compiled.flata
+
+aapt2 link \
+ --no-resource-removal \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifest.xml \
+ -o overlay.apk \
+ compiled.flata
+
+aapt2 link \
+ --no-resource-removal \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifestStatic1.xml \
+ -o overlay-static-1.apk \
+ compiled.flata
+
+aapt2 link \
+ --no-resource-removal \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifestStatic2.xml \
+ -o overlay-static-2.apk \
+ compiled.flata
+
+rm compiled.flata
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
new file mode 100644
index 000000000000..9a0f487522c8
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
new file mode 100644
index 000000000000..3fc31c7d11b0
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk
new file mode 100644
index 000000000000..b4cd7cfc3248
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/res/values-sv/values.xml b/cmds/idmap2/tests/data/overlay/res/values-sv/values.xml
new file mode 100644
index 000000000000..eed0b3dac1ab
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/values-sv/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <string name="str1">overlay-1-sv</string>
+ <string name="str4">overlay-4-sv</string>
+</resources>
diff --git a/cmds/idmap2/tests/data/overlay/res/values/values.xml b/cmds/idmap2/tests/data/overlay/res/values/values.xml
new file mode 100644
index 000000000000..815d1a88fa7b
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/values/values.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <string name="str1">overlay-1</string>
+ <string name="str3">overlay-3</string>
+ <integer name="int1">-1</integer>
+ <integer name="not_in_target">-1</integer>
+</resources>
diff --git a/cmds/idmap2/tests/data/target/AndroidManifest.xml b/cmds/idmap2/tests/data/target/AndroidManifest.xml
new file mode 100644
index 000000000000..3a861b4800fa
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.target">
+</manifest>
diff --git a/cmds/idmap2/tests/data/target/assets/lorem-ipsum.txt b/cmds/idmap2/tests/data/target/assets/lorem-ipsum.txt
new file mode 100644
index 000000000000..d2cf010d36ff
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/assets/lorem-ipsum.txt
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet.
diff --git a/cmds/idmap2/tests/data/target/build b/cmds/idmap2/tests/data/target/build
new file mode 100644
index 000000000000..8569c4ff0a6b
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/build
@@ -0,0 +1,17 @@
+# 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.
+
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -A assets -o target.apk compiled.flata
+rm compiled.flata
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
new file mode 100644
index 000000000000..56bf0d60021a
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <string name="a">a</string>
+ <string name="b">b</string>
+ <string name="c">c</string>
+ <string name="str1">target-1</string>
+ <string name="str2">target-2</string>
+ <string name="str3">target-3</string>
+ <string name="str4">target-4</string>
+ <string name="x">x</string>
+ <string name="y">y</string>
+ <string name="z">z</string>
+ <integer name="int1">1</integer>
+</resources>
diff --git a/cmds/idmap2/tests/data/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml
new file mode 100644
index 000000000000..0fe21c6b6d0a
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/res/xml/test.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<a>
+ <b>
+ <c
+ type_string="fortytwo"
+ type_int_dec="42"
+ type_int_hex="0x2a"
+ type_int_boolean="true"
+ />
+ </b>
+</a>
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
new file mode 100644
index 000000000000..18ecc276caae
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ