1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "apkmanifest.hpp"
18 
19 #include <android-base/logging.h>
20 #include <android-base/result.h>
21 #include <androidfw/AssetsProvider.h>
22 #include <androidfw/ResourceTypes.h>
23 #include <androidfw/StringPiece.h>
24 #include <androidfw/Util.h>
25 #include <stddef.h>
26 #include <stdint.h>
27 #include <utils/Errors.h>
28 
29 #include <cstdlib>
30 #include <limits>
31 #include <string>
32 #include <string_view>
33 
34 using android::Asset;
35 using android::AssetsProvider;
36 using android::OK;
37 using android::Res_value;
38 using android::ResXMLParser;
39 using android::ResXMLTree;
40 using android::statusToString;
41 using android::StringPiece16;
42 using android::base::Error;
43 using android::base::Result;
44 using android::util::Utf16ToUtf8;
45 using std::u16string_view;
46 using std::unique_ptr;
47 
48 struct ApkManifestInfo {
49     std::string package;
50     uint32_t version_code;
51     uint32_t version_code_major;
52 };
53 
54 namespace {
55 // See https://developer.android.com/guide/topics/manifest/manifest-element
56 constexpr u16string_view MANIFEST_TAG_NAME{u"manifest"};
57 constexpr u16string_view ANDROID_NAMESPACE_URL{u"http://schemas.android.com/apk/res/android"};
58 constexpr u16string_view PACKAGE_ATTRIBUTE_NAME{u"package"};
59 constexpr u16string_view VERSION_CODE_ATTRIBUTE_NAME{u"versionCode"};
60 constexpr u16string_view VERSION_CODE_MAJOR_ATTRIBUTE_NAME{u"versionCodeMajor"};
61 
62 // Read through the XML parse tree up to the <manifest> element.
findManifestElement(ResXMLTree & tree)63 Result<void> findManifestElement(ResXMLTree& tree) {
64     for (;;) {
65         ResXMLParser::event_code_t event = tree.next();
66         switch (event) {
67             case ResXMLParser::END_DOCUMENT:
68             case ResXMLParser::END_TAG:
69             case ResXMLParser::TEXT:
70             default:
71                 return Error() << "Unexpected XML parsing event: " << event;
72             case ResXMLParser::BAD_DOCUMENT:
73                 return Error() << "Failed to parse XML: " << statusToString(tree.getError());
74             case ResXMLParser::START_NAMESPACE:
75             case ResXMLParser::END_NAMESPACE:
76                 // Not of interest, keep going.
77                 break;
78             case ResXMLParser::START_TAG:
79                 // The first tag in an AndroidManifest.xml should be <manifest> (no namespace).
80                 // And that's actually the only tag we care about.
81                 if (tree.getElementNamespaceID() >= 0) {
82                     return Error() << "Root element has unexpected namespace.";
83                 }
84                 size_t nameLength = 0;
85                 const char16_t* nameChars = tree.getElementName(&nameLength);
86                 if (!nameChars) {
87                     return Error() << "Missing tag name";
88                 }
89                 if (u16string_view(nameChars, nameLength) != MANIFEST_TAG_NAME) {
90                     return Error() << "Expected <manifest> as root element";
91                 }
92                 return {};
93         }
94     }
95 }
96 
97 // Return an attribute encoded as a string, converted to UTF-8. Note that all
98 // attributes are strings in the original XML, but the binary format encodes
99 // some as binary numbers etc. This function does not handle converting those
100 // encodings back to strings, so should only be used when it is known that a
101 // numeric value is not allowed.
getStringOnlyAttribute(const ResXMLTree & tree,size_t index)102 Result<std::string> getStringOnlyAttribute(const ResXMLTree& tree, size_t index) {
103     size_t len;
104     const char16_t* value = tree.getAttributeStringValue(index, &len);
105     if (!value) {
106         return Error() << "Expected attribute to have string value";
107     }
108     return Utf16ToUtf8(StringPiece16(value, len));
109 }
110 
111 // Return the u32 value of an attribute.
getU32Attribute(const ResXMLTree & tree,size_t index)112 Result<uint32_t> getU32Attribute(const ResXMLTree& tree, size_t index) {
113     auto type = tree.getAttributeDataType(index);
114     switch (type) {
115         case Res_value::TYPE_INT_DEC:
116         case Res_value::TYPE_INT_HEX:
117             // This is how we'd expect the version to be encoded - and we don't
118             // care what base it was originally in.
119             return tree.getAttributeData(index);
120         case Res_value::TYPE_STRING: {
121             // If the original string is encoded, then we need to convert it.
122             auto str = OR_RETURN(getStringOnlyAttribute(tree, index));
123             char* str_end = nullptr;
124             // Note that by specifying base 0 we allow for octal, hex, or
125             // decimal representations here.
126             unsigned long value = std::strtoul(str.c_str(), &str_end, 0);
127             if (str_end != str.c_str() + str.size() ||
128                 value > std::numeric_limits<uint32_t>::max()) {
129                 return Error() << "Invalid numeric value";
130             }
131             return static_cast<uint32_t>(value);
132         }
133         default:
134             return Error() << "Expected numeric value, got type " << type;
135     }
136 }
137 
138 // Parse the binary manifest and extract the information we care about.
139 // Everything we're interested in should be an attribute on the <manifest> tag.
140 // We don't care what order they come in, absent attributes will be treated as
141 // the default value, and any unknown attributes (including ones not in the
142 // expected namespace) will be ignored.
parseManifest(const void * manifest,size_t size)143 Result<unique_ptr<ApkManifestInfo>> parseManifest(const void* manifest, size_t size) {
144     ResXMLTree tree;
145     auto status = tree.setTo(manifest, size);
146     if (status != OK) {
147         return Error() << "Failed to create XML Tree: " << statusToString(status);
148     }
149 
150     OR_RETURN(findManifestElement(tree));
151 
152     unique_ptr<ApkManifestInfo> info{new ApkManifestInfo{}};
153 
154     size_t count = tree.getAttributeCount();
155     for (size_t i = 0; i < count; ++i) {
156         size_t len;
157         const char16_t* chars;
158 
159         chars = tree.getAttributeNamespace(i, &len);
160         auto namespaceUrl = chars ? u16string_view(chars, len) : u16string_view();
161 
162         chars = tree.getAttributeName(i, &len);
163         auto attributeName = chars ? u16string_view(chars, len) : u16string_view();
164 
165         if (namespaceUrl.empty()) {
166             if (attributeName == PACKAGE_ATTRIBUTE_NAME) {
167                 auto result = getStringOnlyAttribute(tree, i);
168                 if (!result.ok()) return Error() << "Package name: " << result.error();
169                 info->package = *result;
170             }
171         } else if (namespaceUrl == ANDROID_NAMESPACE_URL) {
172             if (attributeName == VERSION_CODE_ATTRIBUTE_NAME) {
173                 auto result = getU32Attribute(tree, i);
174                 if (!result.ok()) return Error() << "Version code: " << result.error();
175                 info->version_code = *result;
176             } else if (attributeName == VERSION_CODE_MAJOR_ATTRIBUTE_NAME) {
177                 auto result = getU32Attribute(tree, i);
178                 if (!result.ok()) return Error() << "Version code major: " << result.error();
179                 info->version_code_major = *result;
180             }
181         }
182     }
183 
184     return info;
185 }
186 } // namespace
187 
extractManifestInfo(const void * manifest,size_t size)188 const ApkManifestInfo* extractManifestInfo(const void* manifest, size_t size) {
189     auto result = parseManifest(manifest, size);
190     if (!result.ok()) {
191         LOG(ERROR) << "Failed to parse APK manifest:" << result.error().message();
192         return nullptr;
193     }
194     return result->release();
195 }
196 
freeManifestInfo(const ApkManifestInfo * info)197 void freeManifestInfo(const ApkManifestInfo* info) {
198     delete info;
199 }
200 
getPackageName(const ApkManifestInfo * info)201 const char* getPackageName(const ApkManifestInfo* info) {
202     return info->package.c_str();
203 }
204 
getVersionCode(const ApkManifestInfo * info)205 uint64_t getVersionCode(const ApkManifestInfo* info) {
206     return info->version_code | (static_cast<uint64_t>(info->version_code_major) << 32);
207 }
208