blob: 141dd220ad6fe8fc44e96207d50f4788e3c6eb4a [file] [log] [blame]
David Brazdil2b9c35b2018-01-12 15:44:43 +00001/*
2 * Copyright (C) 2017 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 <fstream>
18#include <iostream>
David Brazdil0b6de0c2018-06-28 11:56:41 +010019#include <map>
20#include <set>
Vladimir Markoe5125562019-02-06 17:38:26 +000021#include <string>
22#include <string_view>
David Brazdil2b9c35b2018-01-12 15:44:43 +000023
24#include "android-base/stringprintf.h"
25#include "android-base/strings.h"
26
David Brazdil20c765f2018-10-27 21:45:15 +000027#include "base/bit_utils.h"
David Brazdildcfa89b2018-10-31 11:04:10 +000028#include "base/hiddenapi_flags.h"
David Sehr79e26072018-04-06 17:58:50 -070029#include "base/mem_map.h"
David Sehrc431b9d2018-03-02 12:01:51 -080030#include "base/os.h"
David Brazdildcfa89b2018-10-31 11:04:10 +000031#include "base/stl_util.h"
Vladimir Markoe5125562019-02-06 17:38:26 +000032#include "base/string_view_cpp20.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000033#include "base/unix_file/fd_file.h"
34#include "dex/art_dex_file_loader.h"
Mathieu Chartier396dc082018-08-06 12:29:57 -070035#include "dex/class_accessor-inl.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000036#include "dex/dex_file-inl.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000037
38namespace art {
David Brazdil62a4bcf2018-12-13 17:00:06 +000039namespace hiddenapi {
David Brazdil2b9c35b2018-01-12 15:44:43 +000040
Mathew Inwoodb62f6f12019-01-07 14:02:52 +000041const char kErrorHelp[] = "\nSee go/hiddenapi-error for help.";
42
David Brazdil2b9c35b2018-01-12 15:44:43 +000043static int original_argc;
44static char** original_argv;
45
46static std::string CommandLine() {
47 std::vector<std::string> command;
Andreas Gampe2a487eb2018-11-19 11:41:22 -080048 command.reserve(original_argc);
David Brazdil2b9c35b2018-01-12 15:44:43 +000049 for (int i = 0; i < original_argc; ++i) {
50 command.push_back(original_argv[i]);
51 }
52 return android::base::Join(command, ' ');
53}
54
55static void UsageErrorV(const char* fmt, va_list ap) {
56 std::string error;
57 android::base::StringAppendV(&error, fmt, ap);
58 LOG(ERROR) << error;
59}
60
61static void UsageError(const char* fmt, ...) {
62 va_list ap;
63 va_start(ap, fmt);
64 UsageErrorV(fmt, ap);
65 va_end(ap);
66}
67
68NO_RETURN static void Usage(const char* fmt, ...) {
69 va_list ap;
70 va_start(ap, fmt);
71 UsageErrorV(fmt, ap);
72 va_end(ap);
73
74 UsageError("Command: %s", CommandLine().c_str());
David Brazdil003e64b2018-06-27 13:20:52 +010075 UsageError("Usage: hiddenapi [command_name] [options]...");
David Brazdil2b9c35b2018-01-12 15:44:43 +000076 UsageError("");
David Brazdil003e64b2018-06-27 13:20:52 +010077 UsageError(" Command \"encode\": encode API list membership in boot dex files");
David Brazdil20c765f2018-10-27 21:45:15 +000078 UsageError(" --input-dex=<filename>: dex file which belongs to boot class path");
79 UsageError(" --output-dex=<filename>: file to write encoded dex into");
80 UsageError(" input and output dex files are paired in order of appearance");
David Brazdil2b9c35b2018-01-12 15:44:43 +000081 UsageError("");
David Brazdil91690d32018-11-04 18:07:23 +000082 UsageError(" --api-flags=<filename>:");
83 UsageError(" CSV file with signatures of methods/fields and their respective flags");
84 UsageError("");
85 UsageError(" --no-force-assign-all:");
86 UsageError(" Disable check that all dex entries have been assigned a flag");
David Brazdil2b9c35b2018-01-12 15:44:43 +000087 UsageError("");
David Brazdil0b6de0c2018-06-28 11:56:41 +010088 UsageError(" Command \"list\": dump lists of public and private API");
89 UsageError(" --boot-dex=<filename>: dex file which belongs to boot class path");
David Brazdil62a4bcf2018-12-13 17:00:06 +000090 UsageError(" --public-stub-classpath=<filenames>:");
David Brazdil90faceb2018-12-14 14:36:15 +000091 UsageError(" --core-platform-stub-classpath=<filenames>:");
David Brazdil62a4bcf2018-12-13 17:00:06 +000092 UsageError(" colon-separated list of dex/apk files which form API stubs of boot");
93 UsageError(" classpath. Multiple classpaths can be specified");
David Brazdil0b6de0c2018-06-28 11:56:41 +010094 UsageError("");
David Brazdil62a4bcf2018-12-13 17:00:06 +000095 UsageError(" --out-api-flags=<filename>: output file for a CSV file with API flags");
David Brazdil0b6de0c2018-06-28 11:56:41 +010096 UsageError("");
David Brazdil2b9c35b2018-01-12 15:44:43 +000097
98 exit(EXIT_FAILURE);
99}
100
David Brazdil0b6de0c2018-06-28 11:56:41 +0100101template<typename E>
102static bool Contains(const std::vector<E>& vec, const E& elem) {
103 return std::find(vec.begin(), vec.end(), elem) != vec.end();
104}
105
Mathieu Chartier396dc082018-08-06 12:29:57 -0700106class DexClass : public ClassAccessor {
David Brazdil2b9c35b2018-01-12 15:44:43 +0000107 public:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700108 explicit DexClass(const ClassAccessor& accessor) : ClassAccessor(accessor) {}
David Brazdil2b9c35b2018-01-12 15:44:43 +0000109
Mathieu Chartier396dc082018-08-06 12:29:57 -0700110 const uint8_t* GetData() const { return dex_file_.GetClassData(GetClassDef()); }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000111
Mathieu Chartier396dc082018-08-06 12:29:57 -0700112 const dex::TypeIndex GetSuperclassIndex() const { return GetClassDef().superclass_idx_; }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100113
114 bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); }
115
David Brazdil0b6de0c2018-06-28 11:56:41 +0100116 std::string GetSuperclassDescriptor() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700117 return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : "";
David Brazdil0b6de0c2018-06-28 11:56:41 +0100118 }
119
120 std::set<std::string> GetInterfaceDescriptors() const {
121 std::set<std::string> list;
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800122 const dex::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef());
David Brazdil0b6de0c2018-06-28 11:56:41 +0100123 for (uint32_t i = 0; ifaces != nullptr && i < ifaces->Size(); ++i) {
124 list.insert(dex_file_.StringByTypeIdx(ifaces->GetTypeItem(i).type_idx_));
125 }
126 return list;
127 }
128
David Brazdil345c0ed2018-08-03 10:26:44 +0100129 inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
David Brazdil2da3cbb2019-01-30 16:17:50 +0000130 inline bool IsInterface() const { return HasAccessFlags(kAccInterface); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100131
132 inline bool Equals(const DexClass& other) const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700133 bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100134 if (equals) {
135 // TODO(dbrazdil): Check that methods/fields match as well once b/111116543 is fixed.
David Brazdil1ff5a652019-01-18 11:44:44 +0000136 CHECK_EQ(GetAccessFlags(), other.GetAccessFlags())
137 << "Inconsistent access flags of class " << GetDescriptor() << ": "
138 << "0x" << std::hex << GetAccessFlags() << std::dec << " (" << dex_file_.GetLocation()
139 << ") and 0x" << std::hex << other.GetAccessFlags() << std::dec << " ("
140 << other.dex_file_.GetLocation() << ")";
141 CHECK_EQ(GetSuperclassDescriptor(), other.GetSuperclassDescriptor())
142 << "Inconsistent superclass of class " << GetDescriptor() << ": "
143 << GetSuperclassDescriptor() << " (" << dex_file_.GetLocation()
144 << ") and " << other.GetSuperclassDescriptor() << " (" << other.dex_file_.GetLocation()
145 << ")";
146 CHECK(GetInterfaceDescriptors() == other.GetInterfaceDescriptors())
147 << "Inconsistent set of interfaces of class " << GetDescriptor() << ": "
148 << JoinStringSet(GetInterfaceDescriptors()) << " (" << dex_file_.GetLocation()
149 << ") and " << JoinStringSet(other.GetInterfaceDescriptors()) << " ("
150 << other.dex_file_.GetLocation() << ")";
David Brazdil0b6de0c2018-06-28 11:56:41 +0100151 }
152 return equals;
153 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000154
155 private:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700156 uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100157 bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
David Brazdil1ff5a652019-01-18 11:44:44 +0000158
159 static std::string JoinStringSet(const std::set<std::string>& s) {
160 return "{" + ::android::base::Join(std::vector<std::string>(s.begin(), s.end()), ",") + "}";
161 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000162};
163
164class DexMember {
165 public:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700166 DexMember(const DexClass& klass, const ClassAccessor::Field& item)
167 : klass_(klass), item_(item), is_method_(false) {
168 DCHECK_EQ(GetFieldId().class_idx_, klass.GetClassIdx());
169 }
170
171 DexMember(const DexClass& klass, const ClassAccessor::Method& item)
172 : klass_(klass), item_(item), is_method_(true) {
173 DCHECK_EQ(GetMethodId().class_idx_, klass.GetClassIdx());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000174 }
175
David Brazdil0b6de0c2018-06-28 11:56:41 +0100176 inline const DexClass& GetDeclaringClass() const { return klass_; }
177
Mathieu Chartier396dc082018-08-06 12:29:57 -0700178 inline bool IsMethod() const { return is_method_; }
179 inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); }
David Brazdil345c0ed2018-08-03 10:26:44 +0100180 inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100181
David Brazdil345c0ed2018-08-03 10:26:44 +0100182 inline bool IsPublicOrProtected() const {
183 return HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100184 }
185
David Brazdil2b9c35b2018-01-12 15:44:43 +0000186 // Constructs a string with a unique signature of this class member.
187 std::string GetApiEntry() const {
188 std::stringstream ss;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700189 ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":")
190 << GetSignature();
David Brazdil2b9c35b2018-01-12 15:44:43 +0000191 return ss.str();
192 }
193
Mathieu Chartier396dc082018-08-06 12:29:57 -0700194 inline bool operator==(const DexMember& other) const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100195 // These need to match if they should resolve to one another.
196 bool equals = IsMethod() == other.IsMethod() &&
197 GetName() == other.GetName() &&
198 GetSignature() == other.GetSignature();
199
200 // Sanity checks if they do match.
201 if (equals) {
202 CHECK_EQ(IsVirtualMethod(), other.IsVirtualMethod());
203 }
204
205 return equals;
206 }
207
David Brazdil2b9c35b2018-01-12 15:44:43 +0000208 private:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700209 inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100210 inline uint32_t HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
211
212 inline std::string GetName() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700213 return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId())
214 : item_.GetDexFile().GetFieldName(GetFieldId());
David Brazdil0b6de0c2018-06-28 11:56:41 +0100215 }
216
217 inline std::string GetSignature() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700218 return IsMethod() ? item_.GetDexFile().GetMethodSignature(GetMethodId()).ToString()
219 : item_.GetDexFile().GetFieldTypeDescriptor(GetFieldId());
220 }
221
222 inline const ClassAccessor::Method& GetMethod() const {
223 DCHECK(IsMethod());
224 return down_cast<const ClassAccessor::Method&>(item_);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100225 }
226
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800227 inline const dex::MethodId& GetMethodId() const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100228 DCHECK(IsMethod());
Mathieu Chartier396dc082018-08-06 12:29:57 -0700229 return item_.GetDexFile().GetMethodId(item_.GetIndex());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000230 }
231
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800232 inline const dex::FieldId& GetFieldId() const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100233 DCHECK(!IsMethod());
Mathieu Chartier396dc082018-08-06 12:29:57 -0700234 return item_.GetDexFile().GetFieldId(item_.GetIndex());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000235 }
236
David Brazdil2b9c35b2018-01-12 15:44:43 +0000237 const DexClass& klass_;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700238 const ClassAccessor::BaseItem& item_;
239 const bool is_method_;
David Brazdil2b9c35b2018-01-12 15:44:43 +0000240};
241
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100242class ClassPath final {
David Brazdil2b9c35b2018-01-12 15:44:43 +0000243 public:
David Brazdil0b6de0c2018-06-28 11:56:41 +0100244 ClassPath(const std::vector<std::string>& dex_paths, bool open_writable) {
245 OpenDexFiles(dex_paths, open_writable);
246 }
247
248 template<typename Fn>
249 void ForEachDexClass(Fn fn) {
250 for (auto& dex_file : dex_files_) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700251 for (ClassAccessor accessor : dex_file->GetClasses()) {
252 fn(DexClass(accessor));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100253 }
254 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000255 }
256
David Brazdil003e64b2018-06-27 13:20:52 +0100257 template<typename Fn>
258 void ForEachDexMember(Fn fn) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700259 ForEachDexClass([&fn](const DexClass& klass) {
260 for (const ClassAccessor::Field& field : klass.GetFields()) {
261 fn(DexMember(klass, field));
262 }
263 for (const ClassAccessor::Method& method : klass.GetMethods()) {
264 fn(DexMember(klass, method));
David Brazdil2b9c35b2018-01-12 15:44:43 +0000265 }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100266 });
David Brazdil2b9c35b2018-01-12 15:44:43 +0000267 }
268
David Brazdil20c765f2018-10-27 21:45:15 +0000269 std::vector<const DexFile*> GetDexFiles() const {
270 return MakeNonOwningPointerVector(dex_files_);
271 }
272
David Brazdil2b9c35b2018-01-12 15:44:43 +0000273 void UpdateDexChecksums() {
274 for (auto& dex_file : dex_files_) {
275 // Obtain a writeable pointer to the dex header.
276 DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
277 // Recalculate checksum and overwrite the value in the header.
278 header->checksum_ = dex_file->CalculateChecksum();
279 }
280 }
281
David Brazdil003e64b2018-06-27 13:20:52 +0100282 private:
David Brazdil0b6de0c2018-06-28 11:56:41 +0100283 void OpenDexFiles(const std::vector<std::string>& dex_paths, bool open_writable) {
David Brazdil003e64b2018-06-27 13:20:52 +0100284 ArtDexFileLoader dex_loader;
285 std::string error_msg;
David Brazdil003e64b2018-06-27 13:20:52 +0100286
David Brazdil0b6de0c2018-06-28 11:56:41 +0100287 if (open_writable) {
288 for (const std::string& filename : dex_paths) {
Andreas Gampe9b031f72018-10-04 11:03:34 -0700289 File fd(filename.c_str(), O_RDWR, /* check_usage= */ false);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100290 CHECK_NE(fd.Fd(), -1) << "Unable to open file '" << filename << "': " << strerror(errno);
291
292 // Memory-map the dex file with MAP_SHARED flag so that changes in memory
293 // propagate to the underlying file. We run dex file verification as if
294 // the dex file was not in boot claass path to check basic assumptions,
295 // such as that at most one of public/private/protected flag is set.
296 // We do those checks here and skip them when loading the processed file
297 // into boot class path.
298 std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
Andreas Gampe9b031f72018-10-04 11:03:34 -0700299 /* location= */ filename,
300 /* verify= */ true,
301 /* verify_checksum= */ true,
302 /* mmap_shared= */ true,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100303 &error_msg));
304 CHECK(dex_file.get() != nullptr) << "Open failed for '" << filename << "' " << error_msg;
305 CHECK(dex_file->IsStandardDexFile()) << "Expected a standard dex file '" << filename << "'";
306 CHECK(dex_file->EnableWrite())
307 << "Failed to enable write permission for '" << filename << "'";
308 dex_files_.push_back(std::move(dex_file));
309 }
310 } else {
311 for (const std::string& filename : dex_paths) {
312 bool success = dex_loader.Open(filename.c_str(),
Andreas Gampe9b031f72018-10-04 11:03:34 -0700313 /* location= */ filename,
314 /* verify= */ true,
315 /* verify_checksum= */ true,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100316 &error_msg,
317 &dex_files_);
318 CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
319 }
David Brazdil003e64b2018-06-27 13:20:52 +0100320 }
321 }
322
David Brazdil0b6de0c2018-06-28 11:56:41 +0100323 // Opened dex files. Note that these are opened as `const` but may be written into.
David Brazdil003e64b2018-06-27 13:20:52 +0100324 std::vector<std::unique_ptr<const DexFile>> dex_files_;
325};
326
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100327class HierarchyClass final {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100328 public:
329 HierarchyClass() {}
330
331 void AddDexClass(const DexClass& klass) {
332 CHECK(dex_classes_.empty() || klass.Equals(dex_classes_.front()));
333 dex_classes_.push_back(klass);
334 }
335
336 void AddExtends(HierarchyClass& parent) {
337 CHECK(!Contains(extends_, &parent));
338 CHECK(!Contains(parent.extended_by_, this));
339 extends_.push_back(&parent);
340 parent.extended_by_.push_back(this);
341 }
342
343 const DexClass& GetOneDexClass() const {
344 CHECK(!dex_classes_.empty());
345 return dex_classes_.front();
346 }
347
348 // See comment on Hierarchy::ForEachResolvableMember.
349 template<typename Fn>
350 bool ForEachResolvableMember(const DexMember& other, Fn fn) {
David Brazdil2da3cbb2019-01-30 16:17:50 +0000351 std::vector<HierarchyClass*> visited;
352 return ForEachResolvableMember_Impl(other, fn, true, true, visited);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100353 }
354
David Brazdil345c0ed2018-08-03 10:26:44 +0100355 // Returns true if this class contains at least one member matching `other`.
356 bool HasMatchingMember(const DexMember& other) {
David Brazdil2da3cbb2019-01-30 16:17:50 +0000357 return ForEachMatchingMember(other, [](const DexMember&) { return true; });
David Brazdil345c0ed2018-08-03 10:26:44 +0100358 }
359
360 // Recursively iterates over all subclasses of this class and invokes `fn`
361 // on each one. If `fn` returns false for a particular subclass, exploring its
362 // subclasses is skipped.
363 template<typename Fn>
364 void ForEachSubClass(Fn fn) {
365 for (HierarchyClass* subclass : extended_by_) {
366 if (fn(subclass)) {
367 subclass->ForEachSubClass(fn);
368 }
369 }
370 }
371
David Brazdil0b6de0c2018-06-28 11:56:41 +0100372 private:
David Brazdil0b6de0c2018-06-28 11:56:41 +0100373 template<typename Fn>
David Brazdil2da3cbb2019-01-30 16:17:50 +0000374 bool ForEachResolvableMember_Impl(const DexMember& other,
375 Fn fn,
376 bool allow_explore_up,
377 bool allow_explore_down,
378 std::vector<HierarchyClass*> visited) {
379 if (std::find(visited.begin(), visited.end(), this) == visited.end()) {
380 visited.push_back(this);
381 } else {
382 return false;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100383 }
384
David Brazdil2da3cbb2019-01-30 16:17:50 +0000385 // First try to find a member matching `other` in this class.
386 bool found = ForEachMatchingMember(other, fn);
387
388 // If not found, see if it is inherited from parents. Note that this will not
389 // revisit parents already in `visited`.
390 if (!found && allow_explore_up) {
391 for (HierarchyClass* superclass : extends_) {
392 found |= superclass->ForEachResolvableMember_Impl(
393 other,
394 fn,
395 /* allow_explore_up */ true,
396 /* allow_explore_down */ false,
397 visited);
398 }
399 }
400
401 // If this is a virtual method, continue exploring into subclasses so as to visit
402 // all overriding methods. Allow subclasses to explore their superclasses if this
403 // is an interface. This is needed to find implementations of this interface's
404 // methods inherited from superclasses (b/122551864).
405 if (allow_explore_down && other.IsVirtualMethod()) {
406 for (HierarchyClass* subclass : extended_by_) {
407 subclass->ForEachResolvableMember_Impl(
408 other,
409 fn,
410 /* allow_explore_up */ GetOneDexClass().IsInterface(),
411 /* allow_explore_down */ true,
412 visited);
413 }
414 }
415
416 return found;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100417 }
418
419 template<typename Fn>
David Brazdil2da3cbb2019-01-30 16:17:50 +0000420 bool ForEachMatchingMember(const DexMember& other, Fn fn) {
421 bool found = false;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700422 auto compare_member = [&](const DexMember& member) {
David Brazdil2da3cbb2019-01-30 16:17:50 +0000423 // TODO(dbrazdil): Check whether class of `other` can access `member`.
Mathieu Chartier396dc082018-08-06 12:29:57 -0700424 if (member == other) {
David Brazdil2da3cbb2019-01-30 16:17:50 +0000425 found = true;
426 fn(member);
Mathieu Chartier396dc082018-08-06 12:29:57 -0700427 }
428 };
David Brazdil0b6de0c2018-06-28 11:56:41 +0100429 for (const DexClass& dex_class : dex_classes_) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700430 for (const ClassAccessor::Field& field : dex_class.GetFields()) {
431 compare_member(DexMember(dex_class, field));
432 }
433 for (const ClassAccessor::Method& method : dex_class.GetMethods()) {
434 compare_member(DexMember(dex_class, method));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100435 }
436 }
437 return found;
438 }
439
David Brazdil0b6de0c2018-06-28 11:56:41 +0100440 // DexClass entries of this class found across all the provided dex files.
441 std::vector<DexClass> dex_classes_;
442
443 // Classes which this class inherits, or interfaces which it implements.
444 std::vector<HierarchyClass*> extends_;
445
446 // Classes which inherit from this class.
447 std::vector<HierarchyClass*> extended_by_;
448};
449
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100450class Hierarchy final {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100451 public:
David Brazdil345c0ed2018-08-03 10:26:44 +0100452 explicit Hierarchy(ClassPath& classpath) : classpath_(classpath) {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100453 BuildClassHierarchy();
454 }
455
456 // Perform an operation for each member of the hierarchy which could potentially
457 // be the result of method/field resolution of `other`.
458 // The function `fn` should accept a DexMember reference and return true if
459 // the member was changed. This drives a performance optimization which only
460 // visits overriding members the first time the overridden member is visited.
461 // Returns true if at least one resolvable member was found.
462 template<typename Fn>
463 bool ForEachResolvableMember(const DexMember& other, Fn fn) {
464 HierarchyClass* klass = FindClass(other.GetDeclaringClass().GetDescriptor());
465 return (klass != nullptr) && klass->ForEachResolvableMember(other, fn);
466 }
467
David Brazdil345c0ed2018-08-03 10:26:44 +0100468 // Returns true if `member`, which belongs to this classpath, is visible to
469 // code in child class loaders.
470 bool IsMemberVisible(const DexMember& member) {
471 if (!member.IsPublicOrProtected()) {
472 // Member is private or package-private. Cannot be visible.
473 return false;
474 } else if (member.GetDeclaringClass().IsPublic()) {
475 // Member is public or protected, and class is public. It must be visible.
476 return true;
477 } else if (member.IsConstructor()) {
478 // Member is public or protected constructor and class is not public.
479 // Must be hidden because it cannot be implicitly exposed by a subclass.
480 return false;
481 } else {
482 // Member is public or protected method, but class is not public. Check if
483 // it is exposed through a public subclass.
484 // Example code (`foo` exposed by ClassB):
485 // class ClassA { public void foo() { ... } }
486 // public class ClassB extends ClassA {}
487 HierarchyClass* klass = FindClass(member.GetDeclaringClass().GetDescriptor());
488 CHECK(klass != nullptr);
489 bool visible = false;
490 klass->ForEachSubClass([&visible, &member](HierarchyClass* subclass) {
491 if (subclass->HasMatchingMember(member)) {
492 // There is a member which matches `member` in `subclass`, either
493 // a virtual method overriding `member` or a field overshadowing
494 // `member`. In either case, `member` remains hidden.
495 CHECK(member.IsVirtualMethod() || !member.IsMethod());
496 return false; // do not explore deeper
497 } else if (subclass->GetOneDexClass().IsPublic()) {
498 // `subclass` inherits and exposes `member`.
499 visible = true;
500 return false; // do not explore deeper
501 } else {
502 // `subclass` inherits `member` but does not expose it.
503 return true; // explore deeper
504 }
505 });
506 return visible;
507 }
508 }
509
David Brazdil0b6de0c2018-06-28 11:56:41 +0100510 private:
511 HierarchyClass* FindClass(const std::string& descriptor) {
512 auto it = classes_.find(descriptor);
513 if (it == classes_.end()) {
514 return nullptr;
515 } else {
516 return &it->second;
517 }
518 }
519
520 void BuildClassHierarchy() {
521 // Create one HierarchyClass entry in `classes_` per class descriptor
522 // and add all DexClass objects with the same descriptor to that entry.
Mathieu Chartier396dc082018-08-06 12:29:57 -0700523 classpath_.ForEachDexClass([this](const DexClass& klass) {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100524 classes_[klass.GetDescriptor()].AddDexClass(klass);
525 });
526
527 // Connect each HierarchyClass to its successors and predecessors.
528 for (auto& entry : classes_) {
529 HierarchyClass& klass = entry.second;
530 const DexClass& dex_klass = klass.GetOneDexClass();
531
532 if (!dex_klass.HasSuperclass()) {
533 CHECK(dex_klass.GetInterfaceDescriptors().empty())
534 << "java/lang/Object should not implement any interfaces";
535 continue;
536 }
537
538 HierarchyClass* superclass = FindClass(dex_klass.GetSuperclassDescriptor());
539 CHECK(superclass != nullptr);
540 klass.AddExtends(*superclass);
541
542 for (const std::string& iface_desc : dex_klass.GetInterfaceDescriptors()) {
543 HierarchyClass* iface = FindClass(iface_desc);
544 CHECK(iface != nullptr);
545 klass.AddExtends(*iface);
546 }
547 }
548 }
549
David Brazdil345c0ed2018-08-03 10:26:44 +0100550 ClassPath& classpath_;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100551 std::map<std::string, HierarchyClass> classes_;
552};
553
David Brazdil20c765f2018-10-27 21:45:15 +0000554// Builder of dex section containing hiddenapi flags.
555class HiddenapiClassDataBuilder final {
556 public:
557 explicit HiddenapiClassDataBuilder(const DexFile& dex_file)
558 : num_classdefs_(dex_file.NumClassDefs()),
559 next_class_def_idx_(0u),
560 class_def_has_non_zero_flags_(false),
561 dex_file_has_non_zero_flags_(false),
562 data_(sizeof(uint32_t) * (num_classdefs_ + 1), 0u) {
563 *GetSizeField() = GetCurrentDataSize();
564 }
565
566 // Notify the builder that new flags for the next class def
567 // will be written now. The builder records the current offset
568 // into the header.
569 void BeginClassDef(uint32_t idx) {
570 CHECK_EQ(next_class_def_idx_, idx);
571 CHECK_LT(idx, num_classdefs_);
572 GetOffsetArray()[idx] = GetCurrentDataSize();
573 class_def_has_non_zero_flags_ = false;
574 }
575
576 // Notify the builder that all flags for this class def have been
577 // written. The builder updates the total size of the data struct
578 // and may set offset for class def in header to zero if no data
579 // has been written.
580 void EndClassDef(uint32_t idx) {
581 CHECK_EQ(next_class_def_idx_, idx);
582 CHECK_LT(idx, num_classdefs_);
583
584 ++next_class_def_idx_;
585
586 if (!class_def_has_non_zero_flags_) {
587 // No need to store flags for this class. Remove the written flags
588 // and set offset in header to zero.
589 data_.resize(GetOffsetArray()[idx]);
590 GetOffsetArray()[idx] = 0u;
591 }
592
593 dex_file_has_non_zero_flags_ |= class_def_has_non_zero_flags_;
594
595 if (idx == num_classdefs_ - 1) {
596 if (dex_file_has_non_zero_flags_) {
597 // This was the last class def and we have generated non-zero hiddenapi
598 // flags. Update total size in the header.
599 *GetSizeField() = GetCurrentDataSize();
600 } else {
601 // This was the last class def and we have not generated any non-zero
602 // hiddenapi flags. Clear all the data.
603 data_.clear();
604 }
605 }
606 }
607
608 // Append flags at the end of the data struct. This should be called
609 // between BeginClassDef and EndClassDef in the order of appearance of
610 // fields/methods in the class data stream.
David Brazdil90faceb2018-12-14 14:36:15 +0000611 void WriteFlags(const ApiList& flags) {
612 uint32_t dex_flags = flags.GetDexFlags();
613 EncodeUnsignedLeb128(&data_, dex_flags);
614 class_def_has_non_zero_flags_ |= (dex_flags != 0u);
David Brazdil20c765f2018-10-27 21:45:15 +0000615 }
616
617 // Return backing data, assuming that all flags have been written.
618 const std::vector<uint8_t>& GetData() const {
619 CHECK_EQ(next_class_def_idx_, num_classdefs_) << "Incomplete data";
620 return data_;
621 }
622
623 private:
624 // Returns pointer to the size field in the header of this dex section.
625 uint32_t* GetSizeField() {
626 // Assume malloc() aligns allocated memory to at least uint32_t.
627 CHECK(IsAligned<sizeof(uint32_t)>(data_.data()));
628 return reinterpret_cast<uint32_t*>(data_.data());
629 }
630
631 // Returns pointer to array of offsets (indexed by class def indices) in the
632 // header of this dex section.
633 uint32_t* GetOffsetArray() { return &GetSizeField()[1]; }
634 uint32_t GetCurrentDataSize() const { return data_.size(); }
635
636 // Number of class defs in this dex file.
637 const uint32_t num_classdefs_;
638
639 // Next expected class def index.
640 uint32_t next_class_def_idx_;
641
642 // Whether non-zero flags have been encountered for this class def.
643 bool class_def_has_non_zero_flags_;
644
645 // Whether any non-zero flags have been encountered for this dex file.
646 bool dex_file_has_non_zero_flags_;
647
648 // Vector containing the data of the built data structure.
649 std::vector<uint8_t> data_;
650};
651
652// Edits a dex file, inserting a new HiddenapiClassData section.
653class DexFileEditor final {
654 public:
655 DexFileEditor(const DexFile& old_dex, const std::vector<uint8_t>& hiddenapi_class_data)
656 : old_dex_(old_dex),
657 hiddenapi_class_data_(hiddenapi_class_data),
658 loaded_dex_header_(nullptr),
659 loaded_dex_maplist_(nullptr) {}
660
661 // Copies dex file into a backing data vector, appends the given HiddenapiClassData
662 // and updates the MapList.
663 void Encode() {
664 // We do not support non-standard dex encodings, e.g. compact dex.
665 CHECK(old_dex_.IsStandardDexFile());
666
667 // If there are no data to append, copy the old dex file and return.
668 if (hiddenapi_class_data_.empty()) {
669 AllocateMemory(old_dex_.Size());
670 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
671 return;
672 }
673
674 // Find the old MapList, find its size.
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800675 const dex::MapList* old_map = old_dex_.GetMapList();
David Brazdil20c765f2018-10-27 21:45:15 +0000676 CHECK_LT(old_map->size_, std::numeric_limits<uint32_t>::max());
677
678 // Compute the size of the new dex file. We append the HiddenapiClassData,
679 // one MapItem and possibly some padding to align the new MapList.
680 CHECK(IsAligned<kMapListAlignment>(old_dex_.Size()))
681 << "End of input dex file is not 4-byte aligned, possibly because its MapList is not "
682 << "at the end of the file.";
683 size_t size_delta =
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800684 RoundUp(hiddenapi_class_data_.size(), kMapListAlignment) + sizeof(dex::MapItem);
David Brazdil20c765f2018-10-27 21:45:15 +0000685 size_t new_size = old_dex_.Size() + size_delta;
686 AllocateMemory(new_size);
687
688 // Copy the old dex file into the backing data vector. Load the copied
689 // dex file to obtain pointers to its header and MapList.
690 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
691 ReloadDex(/* verify= */ false);
692
693 // Truncate the new dex file before the old MapList. This assumes that
694 // the MapList is the last entry in the dex file. This is currently true
695 // for our tooling.
696 // TODO: Implement the general case by zero-ing the old MapList (turning
697 // it into padding.
698 RemoveOldMapList();
699
700 // Append HiddenapiClassData.
701 size_t payload_offset = AppendHiddenapiClassData();
702
703 // Wrute new MapList with an entry for HiddenapiClassData.
704 CreateMapListWithNewItem(payload_offset);
705
706 // Check that the pre-computed size matches the actual size.
707 CHECK_EQ(offset_, new_size);
708
709 // Reload to all data structures.
710 ReloadDex(/* verify= */ false);
711
712 // Update the dex checksum.
713 UpdateChecksum();
714
715 // Run DexFileVerifier on the new dex file as a CHECK.
716 ReloadDex(/* verify= */ true);
717 }
718
719 // Writes the edited dex file into a file.
720 void WriteTo(const std::string& path) {
721 CHECK(!data_.empty());
722 std::ofstream ofs(path.c_str(), std::ofstream::out | std::ofstream::binary);
723 ofs.write(reinterpret_cast<const char*>(data_.data()), data_.size());
724 ofs.flush();
725 CHECK(ofs.good());
726 ofs.close();
727 }
728
729 private:
730 static constexpr size_t kMapListAlignment = 4u;
731 static constexpr size_t kHiddenapiClassDataAlignment = 4u;
732
733 void ReloadDex(bool verify) {
734 std::string error_msg;
735 DexFileLoader loader;
736 loaded_dex_ = loader.Open(
737 data_.data(),
738 data_.size(),
739 "test_location",
740 old_dex_.GetLocationChecksum(),
741 /* oat_dex_file= */ nullptr,
742 /* verify= */ verify,
743 /* verify_checksum= */ verify,
744 &error_msg);
745 if (loaded_dex_.get() == nullptr) {
746 LOG(FATAL) << "Failed to load edited dex file: " << error_msg;
747 UNREACHABLE();
748 }
749
750 // Load the location of header and map list before we start editing the file.
751 loaded_dex_header_ = const_cast<DexFile::Header*>(&loaded_dex_->GetHeader());
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800752 loaded_dex_maplist_ = const_cast<dex::MapList*>(loaded_dex_->GetMapList());
David Brazdil20c765f2018-10-27 21:45:15 +0000753 }
754
755 DexFile::Header& GetHeader() const {
756 CHECK(loaded_dex_header_ != nullptr);
757 return *loaded_dex_header_;
758 }
759
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800760 dex::MapList& GetMapList() const {
David Brazdil20c765f2018-10-27 21:45:15 +0000761 CHECK(loaded_dex_maplist_ != nullptr);
762 return *loaded_dex_maplist_;
763 }
764
765 void AllocateMemory(size_t total_size) {
766 data_.clear();
767 data_.resize(total_size);
768 CHECK(IsAligned<kMapListAlignment>(data_.data()));
769 CHECK(IsAligned<kHiddenapiClassDataAlignment>(data_.data()));
770 offset_ = 0;
771 }
772
773 uint8_t* GetCurrentDataPtr() {
774 return data_.data() + offset_;
775 }
776
777 void UpdateDataSize(off_t delta, bool update_header) {
778 offset_ += delta;
779 if (update_header) {
780 DexFile::Header& header = GetHeader();
781 header.file_size_ += delta;
782 header.data_size_ += delta;
783 }
784 }
785
786 template<typename T>
787 T* Append(const T* src, size_t len, bool update_header = true) {
788 CHECK_LE(offset_ + len, data_.size());
789 uint8_t* dst = GetCurrentDataPtr();
790 memcpy(dst, src, len);
791 UpdateDataSize(len, update_header);
792 return reinterpret_cast<T*>(dst);
793 }
794
795 void InsertPadding(size_t alignment) {
796 size_t len = RoundUp(offset_, alignment) - offset_;
797 std::vector<uint8_t> padding(len, 0);
798 Append(padding.data(), padding.size());
799 }
800
801 void RemoveOldMapList() {
802 size_t map_size = GetMapList().Size();
803 uint8_t* map_start = reinterpret_cast<uint8_t*>(&GetMapList());
804 CHECK_EQ(map_start + map_size, GetCurrentDataPtr()) << "MapList not at the end of dex file";
805 UpdateDataSize(-static_cast<off_t>(map_size), /* update_header= */ true);
806 CHECK_EQ(map_start, GetCurrentDataPtr());
807 loaded_dex_maplist_ = nullptr; // do not use this map list any more
808 }
809
810 void CreateMapListWithNewItem(size_t payload_offset) {
811 InsertPadding(/* alignment= */ kMapListAlignment);
812
813 size_t new_map_offset = offset_;
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800814 dex::MapList* map = Append(old_dex_.GetMapList(), old_dex_.GetMapList()->Size());
David Brazdil20c765f2018-10-27 21:45:15 +0000815
816 // Check last map entry is a pointer to itself.
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800817 dex::MapItem& old_item = map->list_[map->size_ - 1];
David Brazdil20c765f2018-10-27 21:45:15 +0000818 CHECK(old_item.type_ == DexFile::kDexTypeMapList);
819 CHECK_EQ(old_item.size_, 1u);
820 CHECK_EQ(old_item.offset_, GetHeader().map_off_);
821
822 // Create a new MapItem entry with new MapList details.
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800823 dex::MapItem new_item;
David Brazdil20c765f2018-10-27 21:45:15 +0000824 new_item.type_ = old_item.type_;
David Brazdil976b01f2018-11-12 10:46:14 +0000825 new_item.unused_ = 0u; // initialize to ensure dex output is deterministic (b/119308882)
David Brazdil20c765f2018-10-27 21:45:15 +0000826 new_item.size_ = old_item.size_;
827 new_item.offset_ = new_map_offset;
828
829 // Update pointer in the header.
830 GetHeader().map_off_ = new_map_offset;
831
832 // Append a new MapItem and return its pointer.
833 map->size_++;
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800834 Append(&new_item, sizeof(dex::MapItem));
David Brazdil20c765f2018-10-27 21:45:15 +0000835
836 // Change penultimate entry to point to metadata.
837 old_item.type_ = DexFile::kDexTypeHiddenapiClassData;
838 old_item.size_ = 1u; // there is only one section
839 old_item.offset_ = payload_offset;
840 }
841
842 size_t AppendHiddenapiClassData() {
843 size_t payload_offset = offset_;
844 CHECK_EQ(kMapListAlignment, kHiddenapiClassDataAlignment);
845 CHECK(IsAligned<kHiddenapiClassDataAlignment>(payload_offset))
846 << "Should not need to align the section, previous data was already aligned";
847 Append(hiddenapi_class_data_.data(), hiddenapi_class_data_.size());
848 return payload_offset;
849 }
850
851 void UpdateChecksum() {
852 GetHeader().checksum_ = loaded_dex_->CalculateChecksum();
853 }
854
855 const DexFile& old_dex_;
856 const std::vector<uint8_t>& hiddenapi_class_data_;
857
858 std::vector<uint8_t> data_;
859 size_t offset_;
860
861 std::unique_ptr<const DexFile> loaded_dex_;
862 DexFile::Header* loaded_dex_header_;
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800863 dex::MapList* loaded_dex_maplist_;
David Brazdil20c765f2018-10-27 21:45:15 +0000864};
865
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100866class HiddenApi final {
David Brazdil003e64b2018-06-27 13:20:52 +0100867 public:
David Brazdil91690d32018-11-04 18:07:23 +0000868 HiddenApi() : force_assign_all_(true) {}
David Brazdil003e64b2018-06-27 13:20:52 +0100869
870 void Run(int argc, char** argv) {
871 switch (ParseArgs(argc, argv)) {
872 case Command::kEncode:
873 EncodeAccessFlags();
874 break;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100875 case Command::kList:
876 ListApi();
877 break;
David Brazdil003e64b2018-06-27 13:20:52 +0100878 }
879 }
880
881 private:
882 enum class Command {
David Brazdil003e64b2018-06-27 13:20:52 +0100883 kEncode,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100884 kList,
David Brazdil003e64b2018-06-27 13:20:52 +0100885 };
886
887 Command ParseArgs(int argc, char** argv) {
888 // Skip over the binary's path.
889 argv++;
890 argc--;
891
892 if (argc > 0) {
Vladimir Markoe5125562019-02-06 17:38:26 +0000893 const char* raw_command = argv[0];
894 const std::string_view command(raw_command);
David Brazdil003e64b2018-06-27 13:20:52 +0100895 if (command == "encode") {
896 for (int i = 1; i < argc; ++i) {
Vladimir Markoe5125562019-02-06 17:38:26 +0000897 const char* raw_option = argv[i];
898 const std::string_view option(raw_option);
899 if (StartsWith(option, "--input-dex=")) {
900 boot_dex_paths_.push_back(std::string(option.substr(strlen("--input-dex="))));
901 } else if (StartsWith(option, "--output-dex=")) {
902 output_dex_paths_.push_back(std::string(option.substr(strlen("--output-dex="))));
903 } else if (StartsWith(option, "--api-flags=")) {
904 api_flags_path_ = std::string(option.substr(strlen("--api-flags=")));
David Brazdil91690d32018-11-04 18:07:23 +0000905 } else if (option == "--no-force-assign-all") {
906 force_assign_all_ = false;
David Brazdil003e64b2018-06-27 13:20:52 +0100907 } else {
Vladimir Markoe5125562019-02-06 17:38:26 +0000908 Usage("Unknown argument '%s'", raw_option);
David Brazdil003e64b2018-06-27 13:20:52 +0100909 }
910 }
911 return Command::kEncode;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100912 } else if (command == "list") {
913 for (int i = 1; i < argc; ++i) {
Vladimir Markoe5125562019-02-06 17:38:26 +0000914 const char* raw_option = argv[i];
915 const std::string_view option(raw_option);
916 if (StartsWith(option, "--boot-dex=")) {
917 boot_dex_paths_.push_back(std::string(option.substr(strlen("--boot-dex="))));
918 } else if (StartsWith(option, "--public-stub-classpath=")) {
David Brazdil62a4bcf2018-12-13 17:00:06 +0000919 stub_classpaths_.push_back(std::make_pair(
Vladimir Markoe5125562019-02-06 17:38:26 +0000920 std::string(option.substr(strlen("--public-stub-classpath="))),
David Brazdil62a4bcf2018-12-13 17:00:06 +0000921 ApiList::Whitelist()));
Vladimir Markoe5125562019-02-06 17:38:26 +0000922 } else if (StartsWith(option, "--core-platform-stub-classpath=")) {
David Brazdil90faceb2018-12-14 14:36:15 +0000923 stub_classpaths_.push_back(std::make_pair(
Vladimir Markoe5125562019-02-06 17:38:26 +0000924 std::string(option.substr(strlen("--core-platform-stub-classpath="))),
David Brazdil90faceb2018-12-14 14:36:15 +0000925 ApiList::CorePlatformApi()));
Vladimir Markoe5125562019-02-06 17:38:26 +0000926 } else if (StartsWith(option, "--out-api-flags=")) {
927 api_flags_path_ = std::string(option.substr(strlen("--out-api-flags=")));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100928 } else {
Vladimir Markoe5125562019-02-06 17:38:26 +0000929 Usage("Unknown argument '%s'", raw_option);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100930 }
931 }
932 return Command::kList;
David Brazdil003e64b2018-06-27 13:20:52 +0100933 } else {
Vladimir Markoe5125562019-02-06 17:38:26 +0000934 Usage("Unknown command '%s'", raw_command);
David Brazdil003e64b2018-06-27 13:20:52 +0100935 }
936 } else {
937 Usage("No command specified");
938 }
939 }
940
941 void EncodeAccessFlags() {
942 if (boot_dex_paths_.empty()) {
David Brazdil20c765f2018-10-27 21:45:15 +0000943 Usage("No input DEX files specified");
944 } else if (output_dex_paths_.size() != boot_dex_paths_.size()) {
945 Usage("Number of input DEX files does not match number of output DEX files");
David Brazdil003e64b2018-06-27 13:20:52 +0100946 }
947
948 // Load dex signatures.
David Brazdil62a4bcf2018-12-13 17:00:06 +0000949 std::map<std::string, ApiList> api_list = OpenApiFile(api_flags_path_);
David Brazdil003e64b2018-06-27 13:20:52 +0100950
David Brazdil20c765f2018-10-27 21:45:15 +0000951 // Iterate over input dex files and insert HiddenapiClassData sections.
952 for (size_t i = 0; i < boot_dex_paths_.size(); ++i) {
953 const std::string& input_path = boot_dex_paths_[i];
954 const std::string& output_path = output_dex_paths_[i];
David Brazdil003e64b2018-06-27 13:20:52 +0100955
David Brazdil20c765f2018-10-27 21:45:15 +0000956 ClassPath boot_classpath({ input_path }, /* open_writable= */ false);
957 std::vector<const DexFile*> input_dex_files = boot_classpath.GetDexFiles();
958 CHECK_EQ(input_dex_files.size(), 1u);
959 const DexFile& input_dex = *input_dex_files[0];
David Brazdil003e64b2018-06-27 13:20:52 +0100960
David Brazdil20c765f2018-10-27 21:45:15 +0000961 HiddenapiClassDataBuilder builder(input_dex);
David Brazdil91690d32018-11-04 18:07:23 +0000962 boot_classpath.ForEachDexClass([&](const DexClass& boot_class) {
David Brazdil20c765f2018-10-27 21:45:15 +0000963 builder.BeginClassDef(boot_class.GetClassDefIndex());
964 if (boot_class.GetData() != nullptr) {
965 auto fn_shared = [&](const DexMember& boot_member) {
David Brazdil20c765f2018-10-27 21:45:15 +0000966 auto it = api_list.find(boot_member.GetApiEntry());
David Brazdil91690d32018-11-04 18:07:23 +0000967 bool api_list_found = (it != api_list.end());
David Brazdil3482caa2019-01-23 18:24:06 +0000968 CHECK(!force_assign_all_ || api_list_found)
969 << "Could not find hiddenapi flags for dex entry: " << boot_member.GetApiEntry();
David Brazdil62a4bcf2018-12-13 17:00:06 +0000970 builder.WriteFlags(api_list_found ? it->second : ApiList::Whitelist());
David Brazdil20c765f2018-10-27 21:45:15 +0000971 };
972 auto fn_field = [&](const ClassAccessor::Field& boot_field) {
973 fn_shared(DexMember(boot_class, boot_field));
974 };
975 auto fn_method = [&](const ClassAccessor::Method& boot_method) {
976 fn_shared(DexMember(boot_class, boot_method));
977 };
978 boot_class.VisitFieldsAndMethods(fn_field, fn_field, fn_method, fn_method);
979 }
980 builder.EndClassDef(boot_class.GetClassDefIndex());
981 });
982
983 DexFileEditor dex_editor(input_dex, builder.GetData());
984 dex_editor.Encode();
985 dex_editor.WriteTo(output_path);
986 }
David Brazdil003e64b2018-06-27 13:20:52 +0100987 }
988
David Brazdil62a4bcf2018-12-13 17:00:06 +0000989 std::map<std::string, ApiList> OpenApiFile(const std::string& path) {
David Brazdil91690d32018-11-04 18:07:23 +0000990 CHECK(!path.empty());
David Brazdil003e64b2018-06-27 13:20:52 +0100991 std::ifstream api_file(path, std::ifstream::in);
992 CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno);
993
David Brazdil62a4bcf2018-12-13 17:00:06 +0000994 std::map<std::string, ApiList> api_flag_map;
David Brazdil91690d32018-11-04 18:07:23 +0000995
David Brazdil90faceb2018-12-14 14:36:15 +0000996 size_t line_number = 1;
Mathew Inwoodb62f6f12019-01-07 14:02:52 +0000997 for (std::string line; std::getline(api_file, line); line_number++) {
David Brazdil91690d32018-11-04 18:07:23 +0000998 std::vector<std::string> values = android::base::Split(line, ",");
David Brazdil90faceb2018-12-14 14:36:15 +0000999 CHECK_GT(values.size(), 1u) << path << ":" << line_number
1000 << ": No flags found: " << line << kErrorHelp;
1001
David Brazdil91690d32018-11-04 18:07:23 +00001002 const std::string& signature = values[0];
David Brazdil90faceb2018-12-14 14:36:15 +00001003 CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number
1004 << ": Duplicate entry: " << signature << kErrorHelp;
Mathew Inwoodb62f6f12019-01-07 14:02:52 +00001005
David Brazdil90faceb2018-12-14 14:36:15 +00001006 ApiList membership;
1007 bool success = ApiList::FromNames(values.begin() + 1, values.end(), &membership);
1008 CHECK(success) << path << ":" << line_number
1009 << ": Some flags were not recognized: " << line << kErrorHelp;
1010 CHECK(membership.IsValid()) << path << ":" << line_number
1011 << ": Invalid combination of flags: " << line << kErrorHelp;
David Brazdil91690d32018-11-04 18:07:23 +00001012
1013 api_flag_map.emplace(signature, membership);
David Brazdil003e64b2018-06-27 13:20:52 +01001014 }
David Brazdil91690d32018-11-04 18:07:23 +00001015
David Brazdil003e64b2018-06-27 13:20:52 +01001016 api_file.close();
David Brazdil91690d32018-11-04 18:07:23 +00001017 return api_flag_map;
David Brazdil003e64b2018-06-27 13:20:52 +01001018 }
David Brazdil2b9c35b2018-01-12 15:44:43 +00001019
David Brazdil0b6de0c2018-06-28 11:56:41 +01001020 void ListApi() {
1021 if (boot_dex_paths_.empty()) {
1022 Usage("No boot DEX files specified");
David Brazdil345c0ed2018-08-03 10:26:44 +01001023 } else if (stub_classpaths_.empty()) {
David Brazdil0b6de0c2018-06-28 11:56:41 +01001024 Usage("No stub DEX files specified");
David Brazdil62a4bcf2018-12-13 17:00:06 +00001025 } else if (api_flags_path_.empty()) {
1026 Usage("No output path specified");
David Brazdil0b6de0c2018-06-28 11:56:41 +01001027 }
1028
1029 // Complete list of boot class path members. The associated boolean states
1030 // whether it is public (true) or private (false).
David Brazdil62a4bcf2018-12-13 17:00:06 +00001031 std::map<std::string, ApiList> boot_members;
David Brazdil0b6de0c2018-06-28 11:56:41 +01001032
1033 // Deduplicate errors before printing them.
1034 std::set<std::string> unresolved;
1035
1036 // Open all dex files.
Andreas Gampe9b031f72018-10-04 11:03:34 -07001037 ClassPath boot_classpath(boot_dex_paths_, /* open_writable= */ false);
David Brazdil345c0ed2018-08-03 10:26:44 +01001038 Hierarchy boot_hierarchy(boot_classpath);
David Brazdil0b6de0c2018-06-28 11:56:41 +01001039
1040 // Mark all boot dex members private.
David Brazdil62a4bcf2018-12-13 17:00:06 +00001041 boot_classpath.ForEachDexMember([&](const DexMember& boot_member) {
David Brazdil90faceb2018-12-14 14:36:15 +00001042 boot_members[boot_member.GetApiEntry()] = ApiList();
David Brazdil0b6de0c2018-06-28 11:56:41 +01001043 });
1044
1045 // Resolve each SDK dex member against the framework and mark it white.
David Brazdil62a4bcf2018-12-13 17:00:06 +00001046 for (const auto& cp_entry : stub_classpaths_) {
1047 ClassPath stub_classpath(android::base::Split(cp_entry.first, ":"),
1048 /* open_writable= */ false);
David Brazdil345c0ed2018-08-03 10:26:44 +01001049 Hierarchy stub_hierarchy(stub_classpath);
David Brazdil62a4bcf2018-12-13 17:00:06 +00001050 const ApiList stub_api_list = cp_entry.second;
1051
David Brazdil345c0ed2018-08-03 10:26:44 +01001052 stub_classpath.ForEachDexMember(
David Brazdil62a4bcf2018-12-13 17:00:06 +00001053 [&](const DexMember& stub_member) {
David Brazdil345c0ed2018-08-03 10:26:44 +01001054 if (!stub_hierarchy.IsMemberVisible(stub_member)) {
1055 // Typically fake constructors and inner-class `this` fields.
1056 return;
1057 }
1058 bool resolved = boot_hierarchy.ForEachResolvableMember(
1059 stub_member,
David Brazdil62a4bcf2018-12-13 17:00:06 +00001060 [&](const DexMember& boot_member) {
David Brazdil345c0ed2018-08-03 10:26:44 +01001061 std::string entry = boot_member.GetApiEntry();
1062 auto it = boot_members.find(entry);
1063 CHECK(it != boot_members.end());
David Brazdil2da3cbb2019-01-30 16:17:50 +00001064 it->second |= stub_api_list;
David Brazdil345c0ed2018-08-03 10:26:44 +01001065 });
1066 if (!resolved) {
1067 unresolved.insert(stub_member.GetApiEntry());
1068 }
1069 });
1070 }
David Brazdil0b6de0c2018-06-28 11:56:41 +01001071
1072 // Print errors.
1073 for (const std::string& str : unresolved) {
1074 LOG(WARNING) << "unresolved: " << str;
1075 }
1076
1077 // Write into public/private API files.
David Brazdil62a4bcf2018-12-13 17:00:06 +00001078 std::ofstream file_flags(api_flags_path_.c_str());
1079 for (const auto& entry : boot_members) {
David Brazdil90faceb2018-12-14 14:36:15 +00001080 if (entry.second.IsEmpty()) {
David Brazdil62a4bcf2018-12-13 17:00:06 +00001081 file_flags << entry.first << std::endl;
David Brazdil90faceb2018-12-14 14:36:15 +00001082 } else {
1083 file_flags << entry.first << "," << entry.second << std::endl;
David Brazdil0b6de0c2018-06-28 11:56:41 +01001084 }
1085 }
David Brazdil62a4bcf2018-12-13 17:00:06 +00001086 file_flags.close();
David Brazdil0b6de0c2018-06-28 11:56:41 +01001087 }
1088
David Brazdil91690d32018-11-04 18:07:23 +00001089 // Whether to check that all dex entries have been assigned flags.
1090 // Defaults to true.
1091 bool force_assign_all_;
1092
David Brazdil2b9c35b2018-01-12 15:44:43 +00001093 // Paths to DEX files which should be processed.
David Brazdil003e64b2018-06-27 13:20:52 +01001094 std::vector<std::string> boot_dex_paths_;
David Brazdil345c0ed2018-08-03 10:26:44 +01001095
David Brazdil20c765f2018-10-27 21:45:15 +00001096 // Output paths where modified DEX files should be written.
1097 std::vector<std::string> output_dex_paths_;
1098
David Brazdil345c0ed2018-08-03 10:26:44 +01001099 // Set of public API stub classpaths. Each classpath is formed by a list
1100 // of DEX/APK files in the order they appear on the classpath.
David Brazdil62a4bcf2018-12-13 17:00:06 +00001101 std::vector<std::pair<std::string, ApiList>> stub_classpaths_;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001102
David Brazdil62a4bcf2018-12-13 17:00:06 +00001103 // Path to CSV file containing the list of API members and their flags.
1104 // This could be both an input and output path.
1105 std::string api_flags_path_;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001106};
1107
David Brazdil62a4bcf2018-12-13 17:00:06 +00001108} // namespace hiddenapi
David Brazdil2b9c35b2018-01-12 15:44:43 +00001109} // namespace art
1110
1111int main(int argc, char** argv) {
David Brazdil62a4bcf2018-12-13 17:00:06 +00001112 art::hiddenapi::original_argc = argc;
1113 art::hiddenapi::original_argv = argv;
David Brazdil003e64b2018-06-27 13:20:52 +01001114 android::base::InitLogging(argv);
1115 art::MemMap::Init();
David Brazdil62a4bcf2018-12-13 17:00:06 +00001116 art::hiddenapi::HiddenApi().Run(argc, argv);
David Brazdil003e64b2018-06-27 13:20:52 +01001117 return EXIT_SUCCESS;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001118}