blob: 2f016e9d8a299724a8783c2711f3b482ab963315 [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>
David Brazdil2b9c35b2018-01-12 15:44:43 +000021
22#include "android-base/stringprintf.h"
23#include "android-base/strings.h"
24
David Brazdil20c765f2018-10-27 21:45:15 +000025#include "base/bit_utils.h"
26#include "base/stl_util.h"
David Sehr79e26072018-04-06 17:58:50 -070027#include "base/mem_map.h"
David Sehrc431b9d2018-03-02 12:01:51 -080028#include "base/os.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000029#include "base/unix_file/fd_file.h"
30#include "dex/art_dex_file_loader.h"
Mathieu Chartier396dc082018-08-06 12:29:57 -070031#include "dex/class_accessor-inl.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000032#include "dex/dex_file-inl.h"
David Sehr67bf42e2018-02-26 16:43:04 -080033#include "dex/hidden_api_access_flags.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000034
35namespace art {
36
37static int original_argc;
38static char** original_argv;
39
40static std::string CommandLine() {
41 std::vector<std::string> command;
Andreas Gampe2a487eb2018-11-19 11:41:22 -080042 command.reserve(original_argc);
David Brazdil2b9c35b2018-01-12 15:44:43 +000043 for (int i = 0; i < original_argc; ++i) {
44 command.push_back(original_argv[i]);
45 }
46 return android::base::Join(command, ' ');
47}
48
49static void UsageErrorV(const char* fmt, va_list ap) {
50 std::string error;
51 android::base::StringAppendV(&error, fmt, ap);
52 LOG(ERROR) << error;
53}
54
55static void UsageError(const char* fmt, ...) {
56 va_list ap;
57 va_start(ap, fmt);
58 UsageErrorV(fmt, ap);
59 va_end(ap);
60}
61
62NO_RETURN static void Usage(const char* fmt, ...) {
63 va_list ap;
64 va_start(ap, fmt);
65 UsageErrorV(fmt, ap);
66 va_end(ap);
67
68 UsageError("Command: %s", CommandLine().c_str());
David Brazdil003e64b2018-06-27 13:20:52 +010069 UsageError("Usage: hiddenapi [command_name] [options]...");
David Brazdil2b9c35b2018-01-12 15:44:43 +000070 UsageError("");
David Brazdil003e64b2018-06-27 13:20:52 +010071 UsageError(" Command \"encode\": encode API list membership in boot dex files");
David Brazdil20c765f2018-10-27 21:45:15 +000072 UsageError(" --input-dex=<filename>: dex file which belongs to boot class path");
73 UsageError(" --output-dex=<filename>: file to write encoded dex into");
74 UsageError(" input and output dex files are paired in order of appearance");
David Brazdil2b9c35b2018-01-12 15:44:43 +000075 UsageError("");
David Brazdil003e64b2018-06-27 13:20:52 +010076 UsageError(" --light-greylist=<filename>:");
77 UsageError(" --dark-greylist=<filename>:");
78 UsageError(" --blacklist=<filename>:");
79 UsageError(" text files with signatures of methods/fields to be annotated");
David Brazdil2b9c35b2018-01-12 15:44:43 +000080 UsageError("");
David Brazdil0b6de0c2018-06-28 11:56:41 +010081 UsageError(" Command \"list\": dump lists of public and private API");
82 UsageError(" --boot-dex=<filename>: dex file which belongs to boot class path");
David Brazdil345c0ed2018-08-03 10:26:44 +010083 UsageError(" --stub-classpath=<filenames>: colon-separated list of dex/apk files");
84 UsageError(" which form API stubs of boot class path. Multiple classpaths can");
85 UsageError(" be specified");
David Brazdil0b6de0c2018-06-28 11:56:41 +010086 UsageError("");
87 UsageError(" --out-public=<filename>: output file for a list of all public APIs");
88 UsageError(" --out-private=<filename>: output file for a list of all private APIs");
89 UsageError("");
David Brazdil2b9c35b2018-01-12 15:44:43 +000090
91 exit(EXIT_FAILURE);
92}
93
David Brazdil0b6de0c2018-06-28 11:56:41 +010094template<typename E>
95static bool Contains(const std::vector<E>& vec, const E& elem) {
96 return std::find(vec.begin(), vec.end(), elem) != vec.end();
97}
98
Mathieu Chartier396dc082018-08-06 12:29:57 -070099class DexClass : public ClassAccessor {
David Brazdil2b9c35b2018-01-12 15:44:43 +0000100 public:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700101 explicit DexClass(const ClassAccessor& accessor) : ClassAccessor(accessor) {}
David Brazdil2b9c35b2018-01-12 15:44:43 +0000102
Mathieu Chartier396dc082018-08-06 12:29:57 -0700103 const uint8_t* GetData() const { return dex_file_.GetClassData(GetClassDef()); }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000104
Mathieu Chartier396dc082018-08-06 12:29:57 -0700105 const dex::TypeIndex GetSuperclassIndex() const { return GetClassDef().superclass_idx_; }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100106
107 bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); }
108
David Brazdil0b6de0c2018-06-28 11:56:41 +0100109 std::string GetSuperclassDescriptor() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700110 return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : "";
David Brazdil0b6de0c2018-06-28 11:56:41 +0100111 }
112
113 std::set<std::string> GetInterfaceDescriptors() const {
114 std::set<std::string> list;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700115 const DexFile::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef());
David Brazdil0b6de0c2018-06-28 11:56:41 +0100116 for (uint32_t i = 0; ifaces != nullptr && i < ifaces->Size(); ++i) {
117 list.insert(dex_file_.StringByTypeIdx(ifaces->GetTypeItem(i).type_idx_));
118 }
119 return list;
120 }
121
David Brazdil345c0ed2018-08-03 10:26:44 +0100122 inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100123
124 inline bool Equals(const DexClass& other) const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700125 bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100126 if (equals) {
127 // TODO(dbrazdil): Check that methods/fields match as well once b/111116543 is fixed.
128 CHECK_EQ(GetAccessFlags(), other.GetAccessFlags());
129 CHECK_EQ(GetSuperclassDescriptor(), other.GetSuperclassDescriptor());
130 CHECK(GetInterfaceDescriptors() == other.GetInterfaceDescriptors());
131 }
132 return equals;
133 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000134
135 private:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700136 uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100137 bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000138};
139
140class DexMember {
141 public:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700142 DexMember(const DexClass& klass, const ClassAccessor::Field& item)
143 : klass_(klass), item_(item), is_method_(false) {
144 DCHECK_EQ(GetFieldId().class_idx_, klass.GetClassIdx());
145 }
146
147 DexMember(const DexClass& klass, const ClassAccessor::Method& item)
148 : klass_(klass), item_(item), is_method_(true) {
149 DCHECK_EQ(GetMethodId().class_idx_, klass.GetClassIdx());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000150 }
151
David Brazdil0b6de0c2018-06-28 11:56:41 +0100152 inline const DexClass& GetDeclaringClass() const { return klass_; }
153
Mathieu Chartier396dc082018-08-06 12:29:57 -0700154 inline bool IsMethod() const { return is_method_; }
155 inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); }
David Brazdil345c0ed2018-08-03 10:26:44 +0100156 inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100157
David Brazdil345c0ed2018-08-03 10:26:44 +0100158 inline bool IsPublicOrProtected() const {
159 return HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100160 }
161
David Brazdil2b9c35b2018-01-12 15:44:43 +0000162 // Constructs a string with a unique signature of this class member.
163 std::string GetApiEntry() const {
164 std::stringstream ss;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700165 ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":")
166 << GetSignature();
David Brazdil2b9c35b2018-01-12 15:44:43 +0000167 return ss.str();
168 }
169
Mathieu Chartier396dc082018-08-06 12:29:57 -0700170 inline bool operator==(const DexMember& other) const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100171 // These need to match if they should resolve to one another.
172 bool equals = IsMethod() == other.IsMethod() &&
173 GetName() == other.GetName() &&
174 GetSignature() == other.GetSignature();
175
176 // Sanity checks if they do match.
177 if (equals) {
178 CHECK_EQ(IsVirtualMethod(), other.IsVirtualMethod());
179 }
180
181 return equals;
182 }
183
David Brazdil2b9c35b2018-01-12 15:44:43 +0000184 private:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700185 inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100186 inline uint32_t HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
187
188 inline std::string GetName() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700189 return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId())
190 : item_.GetDexFile().GetFieldName(GetFieldId());
David Brazdil0b6de0c2018-06-28 11:56:41 +0100191 }
192
193 inline std::string GetSignature() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700194 return IsMethod() ? item_.GetDexFile().GetMethodSignature(GetMethodId()).ToString()
195 : item_.GetDexFile().GetFieldTypeDescriptor(GetFieldId());
196 }
197
198 inline const ClassAccessor::Method& GetMethod() const {
199 DCHECK(IsMethod());
200 return down_cast<const ClassAccessor::Method&>(item_);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100201 }
202
David Brazdil2b9c35b2018-01-12 15:44:43 +0000203 inline const DexFile::MethodId& GetMethodId() const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100204 DCHECK(IsMethod());
Mathieu Chartier396dc082018-08-06 12:29:57 -0700205 return item_.GetDexFile().GetMethodId(item_.GetIndex());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000206 }
207
208 inline const DexFile::FieldId& GetFieldId() const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100209 DCHECK(!IsMethod());
Mathieu Chartier396dc082018-08-06 12:29:57 -0700210 return item_.GetDexFile().GetFieldId(item_.GetIndex());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000211 }
212
David Brazdil2b9c35b2018-01-12 15:44:43 +0000213 const DexClass& klass_;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700214 const ClassAccessor::BaseItem& item_;
215 const bool is_method_;
David Brazdil2b9c35b2018-01-12 15:44:43 +0000216};
217
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100218class ClassPath final {
David Brazdil2b9c35b2018-01-12 15:44:43 +0000219 public:
David Brazdil0b6de0c2018-06-28 11:56:41 +0100220 ClassPath(const std::vector<std::string>& dex_paths, bool open_writable) {
221 OpenDexFiles(dex_paths, open_writable);
222 }
223
224 template<typename Fn>
225 void ForEachDexClass(Fn fn) {
226 for (auto& dex_file : dex_files_) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700227 for (ClassAccessor accessor : dex_file->GetClasses()) {
228 fn(DexClass(accessor));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100229 }
230 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000231 }
232
David Brazdil003e64b2018-06-27 13:20:52 +0100233 template<typename Fn>
234 void ForEachDexMember(Fn fn) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700235 ForEachDexClass([&fn](const DexClass& klass) {
236 for (const ClassAccessor::Field& field : klass.GetFields()) {
237 fn(DexMember(klass, field));
238 }
239 for (const ClassAccessor::Method& method : klass.GetMethods()) {
240 fn(DexMember(klass, method));
David Brazdil2b9c35b2018-01-12 15:44:43 +0000241 }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100242 });
David Brazdil2b9c35b2018-01-12 15:44:43 +0000243 }
244
David Brazdil20c765f2018-10-27 21:45:15 +0000245 std::vector<const DexFile*> GetDexFiles() const {
246 return MakeNonOwningPointerVector(dex_files_);
247 }
248
David Brazdil2b9c35b2018-01-12 15:44:43 +0000249 void UpdateDexChecksums() {
250 for (auto& dex_file : dex_files_) {
251 // Obtain a writeable pointer to the dex header.
252 DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
253 // Recalculate checksum and overwrite the value in the header.
254 header->checksum_ = dex_file->CalculateChecksum();
255 }
256 }
257
David Brazdil003e64b2018-06-27 13:20:52 +0100258 private:
David Brazdil0b6de0c2018-06-28 11:56:41 +0100259 void OpenDexFiles(const std::vector<std::string>& dex_paths, bool open_writable) {
David Brazdil003e64b2018-06-27 13:20:52 +0100260 ArtDexFileLoader dex_loader;
261 std::string error_msg;
David Brazdil003e64b2018-06-27 13:20:52 +0100262
David Brazdil0b6de0c2018-06-28 11:56:41 +0100263 if (open_writable) {
264 for (const std::string& filename : dex_paths) {
Andreas Gampe9b031f72018-10-04 11:03:34 -0700265 File fd(filename.c_str(), O_RDWR, /* check_usage= */ false);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100266 CHECK_NE(fd.Fd(), -1) << "Unable to open file '" << filename << "': " << strerror(errno);
267
268 // Memory-map the dex file with MAP_SHARED flag so that changes in memory
269 // propagate to the underlying file. We run dex file verification as if
270 // the dex file was not in boot claass path to check basic assumptions,
271 // such as that at most one of public/private/protected flag is set.
272 // We do those checks here and skip them when loading the processed file
273 // into boot class path.
274 std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
Andreas Gampe9b031f72018-10-04 11:03:34 -0700275 /* location= */ filename,
276 /* verify= */ true,
277 /* verify_checksum= */ true,
278 /* mmap_shared= */ true,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100279 &error_msg));
280 CHECK(dex_file.get() != nullptr) << "Open failed for '" << filename << "' " << error_msg;
281 CHECK(dex_file->IsStandardDexFile()) << "Expected a standard dex file '" << filename << "'";
282 CHECK(dex_file->EnableWrite())
283 << "Failed to enable write permission for '" << filename << "'";
284 dex_files_.push_back(std::move(dex_file));
285 }
286 } else {
287 for (const std::string& filename : dex_paths) {
288 bool success = dex_loader.Open(filename.c_str(),
Andreas Gampe9b031f72018-10-04 11:03:34 -0700289 /* location= */ filename,
290 /* verify= */ true,
291 /* verify_checksum= */ true,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100292 &error_msg,
293 &dex_files_);
294 CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
295 }
David Brazdil003e64b2018-06-27 13:20:52 +0100296 }
297 }
298
David Brazdil0b6de0c2018-06-28 11:56:41 +0100299 // Opened dex files. Note that these are opened as `const` but may be written into.
David Brazdil003e64b2018-06-27 13:20:52 +0100300 std::vector<std::unique_ptr<const DexFile>> dex_files_;
301};
302
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100303class HierarchyClass final {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100304 public:
305 HierarchyClass() {}
306
307 void AddDexClass(const DexClass& klass) {
308 CHECK(dex_classes_.empty() || klass.Equals(dex_classes_.front()));
309 dex_classes_.push_back(klass);
310 }
311
312 void AddExtends(HierarchyClass& parent) {
313 CHECK(!Contains(extends_, &parent));
314 CHECK(!Contains(parent.extended_by_, this));
315 extends_.push_back(&parent);
316 parent.extended_by_.push_back(this);
317 }
318
319 const DexClass& GetOneDexClass() const {
320 CHECK(!dex_classes_.empty());
321 return dex_classes_.front();
322 }
323
324 // See comment on Hierarchy::ForEachResolvableMember.
325 template<typename Fn>
326 bool ForEachResolvableMember(const DexMember& other, Fn fn) {
327 return ForEachResolvableMember_Impl(other, fn) != ResolutionResult::kNotFound;
328 }
329
David Brazdil345c0ed2018-08-03 10:26:44 +0100330 // Returns true if this class contains at least one member matching `other`.
331 bool HasMatchingMember(const DexMember& other) {
332 return ForEachMatchingMember(
333 other, [](const DexMember&) { return true; }) != ResolutionResult::kNotFound;
334 }
335
336 // Recursively iterates over all subclasses of this class and invokes `fn`
337 // on each one. If `fn` returns false for a particular subclass, exploring its
338 // subclasses is skipped.
339 template<typename Fn>
340 void ForEachSubClass(Fn fn) {
341 for (HierarchyClass* subclass : extended_by_) {
342 if (fn(subclass)) {
343 subclass->ForEachSubClass(fn);
344 }
345 }
346 }
347
David Brazdil0b6de0c2018-06-28 11:56:41 +0100348 private:
349 // Result of resolution which takes into account whether the member was found
350 // for the first time or not. This is just a performance optimization to prevent
351 // re-visiting previously visited members.
352 // Note that order matters. When accumulating results, we always pick the maximum.
353 enum class ResolutionResult {
354 kNotFound,
355 kFoundOld,
356 kFoundNew,
357 };
358
359 inline ResolutionResult Accumulate(ResolutionResult a, ResolutionResult b) {
360 return static_cast<ResolutionResult>(
361 std::max(static_cast<unsigned>(a), static_cast<unsigned>(b)));
362 }
363
364 template<typename Fn>
365 ResolutionResult ForEachResolvableMember_Impl(const DexMember& other, Fn fn) {
366 // First try to find a member matching `other` in this class.
367 ResolutionResult foundInClass = ForEachMatchingMember(other, fn);
368
369 switch (foundInClass) {
370 case ResolutionResult::kFoundOld:
371 // A matching member was found and previously explored. All subclasses
372 // must have been explored too.
373 break;
374
375 case ResolutionResult::kFoundNew:
376 // A matching member was found and this was the first time it was visited.
377 // If it is a virtual method, visit all methods overriding/implementing it too.
378 if (other.IsVirtualMethod()) {
379 for (HierarchyClass* subclass : extended_by_) {
380 subclass->ForEachOverridingMember(other, fn);
381 }
382 }
383 break;
384
385 case ResolutionResult::kNotFound:
386 // A matching member was not found in this class. Explore the superclasses
387 // and implemented interfaces.
388 for (HierarchyClass* superclass : extends_) {
389 foundInClass = Accumulate(
390 foundInClass, superclass->ForEachResolvableMember_Impl(other, fn));
391 }
392 break;
393 }
394
395 return foundInClass;
396 }
397
398 template<typename Fn>
399 ResolutionResult ForEachMatchingMember(const DexMember& other, Fn fn) {
400 ResolutionResult found = ResolutionResult::kNotFound;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700401 auto compare_member = [&](const DexMember& member) {
402 if (member == other) {
403 found = Accumulate(found, fn(member) ? ResolutionResult::kFoundNew
404 : ResolutionResult::kFoundOld);
405 }
406 };
David Brazdil0b6de0c2018-06-28 11:56:41 +0100407 for (const DexClass& dex_class : dex_classes_) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700408 for (const ClassAccessor::Field& field : dex_class.GetFields()) {
409 compare_member(DexMember(dex_class, field));
410 }
411 for (const ClassAccessor::Method& method : dex_class.GetMethods()) {
412 compare_member(DexMember(dex_class, method));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100413 }
414 }
415 return found;
416 }
417
418 template<typename Fn>
419 void ForEachOverridingMember(const DexMember& other, Fn fn) {
420 CHECK(other.IsVirtualMethod());
421 ResolutionResult found = ForEachMatchingMember(other, fn);
422 if (found == ResolutionResult::kFoundOld) {
423 // No need to explore further.
424 return;
425 } else {
426 for (HierarchyClass* subclass : extended_by_) {
427 subclass->ForEachOverridingMember(other, fn);
428 }
429 }
430 }
431
432 // DexClass entries of this class found across all the provided dex files.
433 std::vector<DexClass> dex_classes_;
434
435 // Classes which this class inherits, or interfaces which it implements.
436 std::vector<HierarchyClass*> extends_;
437
438 // Classes which inherit from this class.
439 std::vector<HierarchyClass*> extended_by_;
440};
441
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100442class Hierarchy final {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100443 public:
David Brazdil345c0ed2018-08-03 10:26:44 +0100444 explicit Hierarchy(ClassPath& classpath) : classpath_(classpath) {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100445 BuildClassHierarchy();
446 }
447
448 // Perform an operation for each member of the hierarchy which could potentially
449 // be the result of method/field resolution of `other`.
450 // The function `fn` should accept a DexMember reference and return true if
451 // the member was changed. This drives a performance optimization which only
452 // visits overriding members the first time the overridden member is visited.
453 // Returns true if at least one resolvable member was found.
454 template<typename Fn>
455 bool ForEachResolvableMember(const DexMember& other, Fn fn) {
456 HierarchyClass* klass = FindClass(other.GetDeclaringClass().GetDescriptor());
457 return (klass != nullptr) && klass->ForEachResolvableMember(other, fn);
458 }
459
David Brazdil345c0ed2018-08-03 10:26:44 +0100460 // Returns true if `member`, which belongs to this classpath, is visible to
461 // code in child class loaders.
462 bool IsMemberVisible(const DexMember& member) {
463 if (!member.IsPublicOrProtected()) {
464 // Member is private or package-private. Cannot be visible.
465 return false;
466 } else if (member.GetDeclaringClass().IsPublic()) {
467 // Member is public or protected, and class is public. It must be visible.
468 return true;
469 } else if (member.IsConstructor()) {
470 // Member is public or protected constructor and class is not public.
471 // Must be hidden because it cannot be implicitly exposed by a subclass.
472 return false;
473 } else {
474 // Member is public or protected method, but class is not public. Check if
475 // it is exposed through a public subclass.
476 // Example code (`foo` exposed by ClassB):
477 // class ClassA { public void foo() { ... } }
478 // public class ClassB extends ClassA {}
479 HierarchyClass* klass = FindClass(member.GetDeclaringClass().GetDescriptor());
480 CHECK(klass != nullptr);
481 bool visible = false;
482 klass->ForEachSubClass([&visible, &member](HierarchyClass* subclass) {
483 if (subclass->HasMatchingMember(member)) {
484 // There is a member which matches `member` in `subclass`, either
485 // a virtual method overriding `member` or a field overshadowing
486 // `member`. In either case, `member` remains hidden.
487 CHECK(member.IsVirtualMethod() || !member.IsMethod());
488 return false; // do not explore deeper
489 } else if (subclass->GetOneDexClass().IsPublic()) {
490 // `subclass` inherits and exposes `member`.
491 visible = true;
492 return false; // do not explore deeper
493 } else {
494 // `subclass` inherits `member` but does not expose it.
495 return true; // explore deeper
496 }
497 });
498 return visible;
499 }
500 }
501
David Brazdil0b6de0c2018-06-28 11:56:41 +0100502 private:
503 HierarchyClass* FindClass(const std::string& descriptor) {
504 auto it = classes_.find(descriptor);
505 if (it == classes_.end()) {
506 return nullptr;
507 } else {
508 return &it->second;
509 }
510 }
511
512 void BuildClassHierarchy() {
513 // Create one HierarchyClass entry in `classes_` per class descriptor
514 // and add all DexClass objects with the same descriptor to that entry.
Mathieu Chartier396dc082018-08-06 12:29:57 -0700515 classpath_.ForEachDexClass([this](const DexClass& klass) {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100516 classes_[klass.GetDescriptor()].AddDexClass(klass);
517 });
518
519 // Connect each HierarchyClass to its successors and predecessors.
520 for (auto& entry : classes_) {
521 HierarchyClass& klass = entry.second;
522 const DexClass& dex_klass = klass.GetOneDexClass();
523
524 if (!dex_klass.HasSuperclass()) {
525 CHECK(dex_klass.GetInterfaceDescriptors().empty())
526 << "java/lang/Object should not implement any interfaces";
527 continue;
528 }
529
530 HierarchyClass* superclass = FindClass(dex_klass.GetSuperclassDescriptor());
531 CHECK(superclass != nullptr);
532 klass.AddExtends(*superclass);
533
534 for (const std::string& iface_desc : dex_klass.GetInterfaceDescriptors()) {
535 HierarchyClass* iface = FindClass(iface_desc);
536 CHECK(iface != nullptr);
537 klass.AddExtends(*iface);
538 }
539 }
540 }
541
David Brazdil345c0ed2018-08-03 10:26:44 +0100542 ClassPath& classpath_;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100543 std::map<std::string, HierarchyClass> classes_;
544};
545
David Brazdil20c765f2018-10-27 21:45:15 +0000546// Builder of dex section containing hiddenapi flags.
547class HiddenapiClassDataBuilder final {
548 public:
549 explicit HiddenapiClassDataBuilder(const DexFile& dex_file)
550 : num_classdefs_(dex_file.NumClassDefs()),
551 next_class_def_idx_(0u),
552 class_def_has_non_zero_flags_(false),
553 dex_file_has_non_zero_flags_(false),
554 data_(sizeof(uint32_t) * (num_classdefs_ + 1), 0u) {
555 *GetSizeField() = GetCurrentDataSize();
556 }
557
558 // Notify the builder that new flags for the next class def
559 // will be written now. The builder records the current offset
560 // into the header.
561 void BeginClassDef(uint32_t idx) {
562 CHECK_EQ(next_class_def_idx_, idx);
563 CHECK_LT(idx, num_classdefs_);
564 GetOffsetArray()[idx] = GetCurrentDataSize();
565 class_def_has_non_zero_flags_ = false;
566 }
567
568 // Notify the builder that all flags for this class def have been
569 // written. The builder updates the total size of the data struct
570 // and may set offset for class def in header to zero if no data
571 // has been written.
572 void EndClassDef(uint32_t idx) {
573 CHECK_EQ(next_class_def_idx_, idx);
574 CHECK_LT(idx, num_classdefs_);
575
576 ++next_class_def_idx_;
577
578 if (!class_def_has_non_zero_flags_) {
579 // No need to store flags for this class. Remove the written flags
580 // and set offset in header to zero.
581 data_.resize(GetOffsetArray()[idx]);
582 GetOffsetArray()[idx] = 0u;
583 }
584
585 dex_file_has_non_zero_flags_ |= class_def_has_non_zero_flags_;
586
587 if (idx == num_classdefs_ - 1) {
588 if (dex_file_has_non_zero_flags_) {
589 // This was the last class def and we have generated non-zero hiddenapi
590 // flags. Update total size in the header.
591 *GetSizeField() = GetCurrentDataSize();
592 } else {
593 // This was the last class def and we have not generated any non-zero
594 // hiddenapi flags. Clear all the data.
595 data_.clear();
596 }
597 }
598 }
599
600 // Append flags at the end of the data struct. This should be called
601 // between BeginClassDef and EndClassDef in the order of appearance of
602 // fields/methods in the class data stream.
David Brazdil47cd2722018-10-23 12:50:02 +0100603 void WriteFlags(hiddenapi::ApiList flags) {
604 uint32_t uint_flags = static_cast<uint32_t>(flags);
605 EncodeUnsignedLeb128(&data_, uint_flags);
606 class_def_has_non_zero_flags_ |= (uint_flags != 0u);
David Brazdil20c765f2018-10-27 21:45:15 +0000607 }
608
609 // Return backing data, assuming that all flags have been written.
610 const std::vector<uint8_t>& GetData() const {
611 CHECK_EQ(next_class_def_idx_, num_classdefs_) << "Incomplete data";
612 return data_;
613 }
614
615 private:
616 // Returns pointer to the size field in the header of this dex section.
617 uint32_t* GetSizeField() {
618 // Assume malloc() aligns allocated memory to at least uint32_t.
619 CHECK(IsAligned<sizeof(uint32_t)>(data_.data()));
620 return reinterpret_cast<uint32_t*>(data_.data());
621 }
622
623 // Returns pointer to array of offsets (indexed by class def indices) in the
624 // header of this dex section.
625 uint32_t* GetOffsetArray() { return &GetSizeField()[1]; }
626 uint32_t GetCurrentDataSize() const { return data_.size(); }
627
628 // Number of class defs in this dex file.
629 const uint32_t num_classdefs_;
630
631 // Next expected class def index.
632 uint32_t next_class_def_idx_;
633
634 // Whether non-zero flags have been encountered for this class def.
635 bool class_def_has_non_zero_flags_;
636
637 // Whether any non-zero flags have been encountered for this dex file.
638 bool dex_file_has_non_zero_flags_;
639
640 // Vector containing the data of the built data structure.
641 std::vector<uint8_t> data_;
642};
643
644// Edits a dex file, inserting a new HiddenapiClassData section.
645class DexFileEditor final {
646 public:
647 DexFileEditor(const DexFile& old_dex, const std::vector<uint8_t>& hiddenapi_class_data)
648 : old_dex_(old_dex),
649 hiddenapi_class_data_(hiddenapi_class_data),
650 loaded_dex_header_(nullptr),
651 loaded_dex_maplist_(nullptr) {}
652
653 // Copies dex file into a backing data vector, appends the given HiddenapiClassData
654 // and updates the MapList.
655 void Encode() {
656 // We do not support non-standard dex encodings, e.g. compact dex.
657 CHECK(old_dex_.IsStandardDexFile());
658
659 // If there are no data to append, copy the old dex file and return.
660 if (hiddenapi_class_data_.empty()) {
661 AllocateMemory(old_dex_.Size());
662 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
663 return;
664 }
665
666 // Find the old MapList, find its size.
667 const DexFile::MapList* old_map = old_dex_.GetMapList();
668 CHECK_LT(old_map->size_, std::numeric_limits<uint32_t>::max());
669
670 // Compute the size of the new dex file. We append the HiddenapiClassData,
671 // one MapItem and possibly some padding to align the new MapList.
672 CHECK(IsAligned<kMapListAlignment>(old_dex_.Size()))
673 << "End of input dex file is not 4-byte aligned, possibly because its MapList is not "
674 << "at the end of the file.";
675 size_t size_delta =
676 RoundUp(hiddenapi_class_data_.size(), kMapListAlignment) + sizeof(DexFile::MapItem);
677 size_t new_size = old_dex_.Size() + size_delta;
678 AllocateMemory(new_size);
679
680 // Copy the old dex file into the backing data vector. Load the copied
681 // dex file to obtain pointers to its header and MapList.
682 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
683 ReloadDex(/* verify= */ false);
684
685 // Truncate the new dex file before the old MapList. This assumes that
686 // the MapList is the last entry in the dex file. This is currently true
687 // for our tooling.
688 // TODO: Implement the general case by zero-ing the old MapList (turning
689 // it into padding.
690 RemoveOldMapList();
691
692 // Append HiddenapiClassData.
693 size_t payload_offset = AppendHiddenapiClassData();
694
695 // Wrute new MapList with an entry for HiddenapiClassData.
696 CreateMapListWithNewItem(payload_offset);
697
698 // Check that the pre-computed size matches the actual size.
699 CHECK_EQ(offset_, new_size);
700
701 // Reload to all data structures.
702 ReloadDex(/* verify= */ false);
703
704 // Update the dex checksum.
705 UpdateChecksum();
706
707 // Run DexFileVerifier on the new dex file as a CHECK.
708 ReloadDex(/* verify= */ true);
709 }
710
711 // Writes the edited dex file into a file.
712 void WriteTo(const std::string& path) {
713 CHECK(!data_.empty());
714 std::ofstream ofs(path.c_str(), std::ofstream::out | std::ofstream::binary);
715 ofs.write(reinterpret_cast<const char*>(data_.data()), data_.size());
716 ofs.flush();
717 CHECK(ofs.good());
718 ofs.close();
719 }
720
721 private:
722 static constexpr size_t kMapListAlignment = 4u;
723 static constexpr size_t kHiddenapiClassDataAlignment = 4u;
724
725 void ReloadDex(bool verify) {
726 std::string error_msg;
727 DexFileLoader loader;
728 loaded_dex_ = loader.Open(
729 data_.data(),
730 data_.size(),
731 "test_location",
732 old_dex_.GetLocationChecksum(),
733 /* oat_dex_file= */ nullptr,
734 /* verify= */ verify,
735 /* verify_checksum= */ verify,
736 &error_msg);
737 if (loaded_dex_.get() == nullptr) {
738 LOG(FATAL) << "Failed to load edited dex file: " << error_msg;
739 UNREACHABLE();
740 }
741
742 // Load the location of header and map list before we start editing the file.
743 loaded_dex_header_ = const_cast<DexFile::Header*>(&loaded_dex_->GetHeader());
744 loaded_dex_maplist_ = const_cast<DexFile::MapList*>(loaded_dex_->GetMapList());
745 }
746
747 DexFile::Header& GetHeader() const {
748 CHECK(loaded_dex_header_ != nullptr);
749 return *loaded_dex_header_;
750 }
751
752 DexFile::MapList& GetMapList() const {
753 CHECK(loaded_dex_maplist_ != nullptr);
754 return *loaded_dex_maplist_;
755 }
756
757 void AllocateMemory(size_t total_size) {
758 data_.clear();
759 data_.resize(total_size);
760 CHECK(IsAligned<kMapListAlignment>(data_.data()));
761 CHECK(IsAligned<kHiddenapiClassDataAlignment>(data_.data()));
762 offset_ = 0;
763 }
764
765 uint8_t* GetCurrentDataPtr() {
766 return data_.data() + offset_;
767 }
768
769 void UpdateDataSize(off_t delta, bool update_header) {
770 offset_ += delta;
771 if (update_header) {
772 DexFile::Header& header = GetHeader();
773 header.file_size_ += delta;
774 header.data_size_ += delta;
775 }
776 }
777
778 template<typename T>
779 T* Append(const T* src, size_t len, bool update_header = true) {
780 CHECK_LE(offset_ + len, data_.size());
781 uint8_t* dst = GetCurrentDataPtr();
782 memcpy(dst, src, len);
783 UpdateDataSize(len, update_header);
784 return reinterpret_cast<T*>(dst);
785 }
786
787 void InsertPadding(size_t alignment) {
788 size_t len = RoundUp(offset_, alignment) - offset_;
789 std::vector<uint8_t> padding(len, 0);
790 Append(padding.data(), padding.size());
791 }
792
793 void RemoveOldMapList() {
794 size_t map_size = GetMapList().Size();
795 uint8_t* map_start = reinterpret_cast<uint8_t*>(&GetMapList());
796 CHECK_EQ(map_start + map_size, GetCurrentDataPtr()) << "MapList not at the end of dex file";
797 UpdateDataSize(-static_cast<off_t>(map_size), /* update_header= */ true);
798 CHECK_EQ(map_start, GetCurrentDataPtr());
799 loaded_dex_maplist_ = nullptr; // do not use this map list any more
800 }
801
802 void CreateMapListWithNewItem(size_t payload_offset) {
803 InsertPadding(/* alignment= */ kMapListAlignment);
804
805 size_t new_map_offset = offset_;
806 DexFile::MapList* map = Append(old_dex_.GetMapList(), old_dex_.GetMapList()->Size());
807
808 // Check last map entry is a pointer to itself.
809 DexFile::MapItem& old_item = map->list_[map->size_ - 1];
810 CHECK(old_item.type_ == DexFile::kDexTypeMapList);
811 CHECK_EQ(old_item.size_, 1u);
812 CHECK_EQ(old_item.offset_, GetHeader().map_off_);
813
814 // Create a new MapItem entry with new MapList details.
815 DexFile::MapItem new_item;
816 new_item.type_ = old_item.type_;
David Brazdil976b01f2018-11-12 10:46:14 +0000817 new_item.unused_ = 0u; // initialize to ensure dex output is deterministic (b/119308882)
David Brazdil20c765f2018-10-27 21:45:15 +0000818 new_item.size_ = old_item.size_;
819 new_item.offset_ = new_map_offset;
820
821 // Update pointer in the header.
822 GetHeader().map_off_ = new_map_offset;
823
824 // Append a new MapItem and return its pointer.
825 map->size_++;
826 Append(&new_item, sizeof(DexFile::MapItem));
827
828 // Change penultimate entry to point to metadata.
829 old_item.type_ = DexFile::kDexTypeHiddenapiClassData;
830 old_item.size_ = 1u; // there is only one section
831 old_item.offset_ = payload_offset;
832 }
833
834 size_t AppendHiddenapiClassData() {
835 size_t payload_offset = offset_;
836 CHECK_EQ(kMapListAlignment, kHiddenapiClassDataAlignment);
837 CHECK(IsAligned<kHiddenapiClassDataAlignment>(payload_offset))
838 << "Should not need to align the section, previous data was already aligned";
839 Append(hiddenapi_class_data_.data(), hiddenapi_class_data_.size());
840 return payload_offset;
841 }
842
843 void UpdateChecksum() {
844 GetHeader().checksum_ = loaded_dex_->CalculateChecksum();
845 }
846
847 const DexFile& old_dex_;
848 const std::vector<uint8_t>& hiddenapi_class_data_;
849
850 std::vector<uint8_t> data_;
851 size_t offset_;
852
853 std::unique_ptr<const DexFile> loaded_dex_;
854 DexFile::Header* loaded_dex_header_;
855 DexFile::MapList* loaded_dex_maplist_;
856};
857
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100858class HiddenApi final {
David Brazdil003e64b2018-06-27 13:20:52 +0100859 public:
860 HiddenApi() {}
861
862 void Run(int argc, char** argv) {
863 switch (ParseArgs(argc, argv)) {
864 case Command::kEncode:
865 EncodeAccessFlags();
866 break;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100867 case Command::kList:
868 ListApi();
869 break;
David Brazdil003e64b2018-06-27 13:20:52 +0100870 }
871 }
872
873 private:
874 enum class Command {
David Brazdil003e64b2018-06-27 13:20:52 +0100875 kEncode,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100876 kList,
David Brazdil003e64b2018-06-27 13:20:52 +0100877 };
878
879 Command ParseArgs(int argc, char** argv) {
880 // Skip over the binary's path.
881 argv++;
882 argc--;
883
884 if (argc > 0) {
885 const StringPiece command(argv[0]);
886 if (command == "encode") {
887 for (int i = 1; i < argc; ++i) {
888 const StringPiece option(argv[i]);
David Brazdil20c765f2018-10-27 21:45:15 +0000889 if (option.starts_with("--input-dex=")) {
890 boot_dex_paths_.push_back(option.substr(strlen("--input-dex=")).ToString());
891 } else if (option.starts_with("--output-dex=")) {
892 output_dex_paths_.push_back(option.substr(strlen("--output-dex=")).ToString());
David Brazdil003e64b2018-06-27 13:20:52 +0100893 } else if (option.starts_with("--light-greylist=")) {
894 light_greylist_path_ = option.substr(strlen("--light-greylist=")).ToString();
895 } else if (option.starts_with("--dark-greylist=")) {
896 dark_greylist_path_ = option.substr(strlen("--dark-greylist=")).ToString();
897 } else if (option.starts_with("--blacklist=")) {
898 blacklist_path_ = option.substr(strlen("--blacklist=")).ToString();
899 } else {
900 Usage("Unknown argument '%s'", option.data());
901 }
902 }
903 return Command::kEncode;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100904 } else if (command == "list") {
905 for (int i = 1; i < argc; ++i) {
906 const StringPiece option(argv[i]);
907 if (option.starts_with("--boot-dex=")) {
908 boot_dex_paths_.push_back(option.substr(strlen("--boot-dex=")).ToString());
David Brazdil345c0ed2018-08-03 10:26:44 +0100909 } else if (option.starts_with("--stub-classpath=")) {
910 stub_classpaths_.push_back(android::base::Split(
911 option.substr(strlen("--stub-classpath=")).ToString(), ":"));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100912 } else if (option.starts_with("--out-public=")) {
913 out_public_path_ = option.substr(strlen("--out-public=")).ToString();
914 } else if (option.starts_with("--out-private=")) {
915 out_private_path_ = option.substr(strlen("--out-private=")).ToString();
916 } else {
917 Usage("Unknown argument '%s'", option.data());
918 }
919 }
920 return Command::kList;
David Brazdil003e64b2018-06-27 13:20:52 +0100921 } else {
922 Usage("Unknown command '%s'", command.data());
923 }
924 } else {
925 Usage("No command specified");
926 }
927 }
928
929 void EncodeAccessFlags() {
930 if (boot_dex_paths_.empty()) {
David Brazdil20c765f2018-10-27 21:45:15 +0000931 Usage("No input DEX files specified");
932 } else if (output_dex_paths_.size() != boot_dex_paths_.size()) {
933 Usage("Number of input DEX files does not match number of output DEX files");
David Brazdil003e64b2018-06-27 13:20:52 +0100934 }
935
936 // Load dex signatures.
David Brazdil47cd2722018-10-23 12:50:02 +0100937 std::map<std::string, hiddenapi::ApiList> api_list;
938 OpenApiFile(light_greylist_path_, api_list, hiddenapi::ApiList::kLightGreylist);
939 OpenApiFile(dark_greylist_path_, api_list, hiddenapi::ApiList::kDarkGreylist);
940 OpenApiFile(blacklist_path_, api_list, hiddenapi::ApiList::kBlacklist);
David Brazdil003e64b2018-06-27 13:20:52 +0100941
David Brazdil20c765f2018-10-27 21:45:15 +0000942 // Iterate over input dex files and insert HiddenapiClassData sections.
943 for (size_t i = 0; i < boot_dex_paths_.size(); ++i) {
944 const std::string& input_path = boot_dex_paths_[i];
945 const std::string& output_path = output_dex_paths_[i];
David Brazdil003e64b2018-06-27 13:20:52 +0100946
David Brazdil20c765f2018-10-27 21:45:15 +0000947 ClassPath boot_classpath({ input_path }, /* open_writable= */ false);
948 std::vector<const DexFile*> input_dex_files = boot_classpath.GetDexFiles();
949 CHECK_EQ(input_dex_files.size(), 1u);
950 const DexFile& input_dex = *input_dex_files[0];
David Brazdil003e64b2018-06-27 13:20:52 +0100951
David Brazdil20c765f2018-10-27 21:45:15 +0000952 HiddenapiClassDataBuilder builder(input_dex);
953 boot_classpath.ForEachDexClass([&api_list, &builder](const DexClass& boot_class) {
954 builder.BeginClassDef(boot_class.GetClassDefIndex());
955 if (boot_class.GetData() != nullptr) {
956 auto fn_shared = [&](const DexMember& boot_member) {
957 // TODO: Load whitelist and CHECK that entry was found.
958 auto it = api_list.find(boot_member.GetApiEntry());
959 builder.WriteFlags(
David Brazdil47cd2722018-10-23 12:50:02 +0100960 (it == api_list.end()) ? hiddenapi::ApiList::kWhitelist : it->second);
David Brazdil20c765f2018-10-27 21:45:15 +0000961 };
962 auto fn_field = [&](const ClassAccessor::Field& boot_field) {
963 fn_shared(DexMember(boot_class, boot_field));
964 };
965 auto fn_method = [&](const ClassAccessor::Method& boot_method) {
966 fn_shared(DexMember(boot_class, boot_method));
967 };
968 boot_class.VisitFieldsAndMethods(fn_field, fn_field, fn_method, fn_method);
969 }
970 builder.EndClassDef(boot_class.GetClassDefIndex());
971 });
972
973 DexFileEditor dex_editor(input_dex, builder.GetData());
974 dex_editor.Encode();
975 dex_editor.WriteTo(output_path);
976 }
David Brazdil003e64b2018-06-27 13:20:52 +0100977 }
978
979 void OpenApiFile(const std::string& path,
David Brazdil47cd2722018-10-23 12:50:02 +0100980 std::map<std::string, hiddenapi::ApiList>& api_list,
981 hiddenapi::ApiList membership) {
David Brazdil003e64b2018-06-27 13:20:52 +0100982 if (path.empty()) {
983 return;
984 }
985
986 std::ifstream api_file(path, std::ifstream::in);
987 CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno);
988
989 for (std::string line; std::getline(api_file, line);) {
990 CHECK(api_list.find(line) == api_list.end())
991 << "Duplicate entry: " << line << " (" << api_list[line] << " and " << membership << ")";
992 api_list.emplace(line, membership);
993 }
994 api_file.close();
995 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000996
David Brazdil0b6de0c2018-06-28 11:56:41 +0100997 void ListApi() {
998 if (boot_dex_paths_.empty()) {
999 Usage("No boot DEX files specified");
David Brazdil345c0ed2018-08-03 10:26:44 +01001000 } else if (stub_classpaths_.empty()) {
David Brazdil0b6de0c2018-06-28 11:56:41 +01001001 Usage("No stub DEX files specified");
1002 } else if (out_public_path_.empty()) {
1003 Usage("No public API output path specified");
1004 } else if (out_private_path_.empty()) {
1005 Usage("No private API output path specified");
1006 }
1007
1008 // Complete list of boot class path members. The associated boolean states
1009 // whether it is public (true) or private (false).
1010 std::map<std::string, bool> boot_members;
1011
1012 // Deduplicate errors before printing them.
1013 std::set<std::string> unresolved;
1014
1015 // Open all dex files.
Andreas Gampe9b031f72018-10-04 11:03:34 -07001016 ClassPath boot_classpath(boot_dex_paths_, /* open_writable= */ false);
David Brazdil345c0ed2018-08-03 10:26:44 +01001017 Hierarchy boot_hierarchy(boot_classpath);
David Brazdil0b6de0c2018-06-28 11:56:41 +01001018
1019 // Mark all boot dex members private.
Mathieu Chartier396dc082018-08-06 12:29:57 -07001020 boot_classpath.ForEachDexMember([&boot_members](const DexMember& boot_member) {
David Brazdil0b6de0c2018-06-28 11:56:41 +01001021 boot_members[boot_member.GetApiEntry()] = false;
1022 });
1023
1024 // Resolve each SDK dex member against the framework and mark it white.
David Brazdil345c0ed2018-08-03 10:26:44 +01001025 for (const std::vector<std::string>& stub_classpath_dex : stub_classpaths_) {
Andreas Gampe9b031f72018-10-04 11:03:34 -07001026 ClassPath stub_classpath(stub_classpath_dex, /* open_writable= */ false);
David Brazdil345c0ed2018-08-03 10:26:44 +01001027 Hierarchy stub_hierarchy(stub_classpath);
1028 stub_classpath.ForEachDexMember(
Mathieu Chartier396dc082018-08-06 12:29:57 -07001029 [&stub_hierarchy, &boot_hierarchy, &boot_members, &unresolved](
1030 const DexMember& stub_member) {
David Brazdil345c0ed2018-08-03 10:26:44 +01001031 if (!stub_hierarchy.IsMemberVisible(stub_member)) {
1032 // Typically fake constructors and inner-class `this` fields.
1033 return;
1034 }
1035 bool resolved = boot_hierarchy.ForEachResolvableMember(
1036 stub_member,
Mathieu Chartier396dc082018-08-06 12:29:57 -07001037 [&boot_members](const DexMember& boot_member) {
David Brazdil345c0ed2018-08-03 10:26:44 +01001038 std::string entry = boot_member.GetApiEntry();
1039 auto it = boot_members.find(entry);
1040 CHECK(it != boot_members.end());
1041 if (it->second) {
1042 return false; // has been marked before
1043 } else {
1044 it->second = true;
1045 return true; // marked for the first time
1046 }
1047 });
1048 if (!resolved) {
1049 unresolved.insert(stub_member.GetApiEntry());
1050 }
1051 });
1052 }
David Brazdil0b6de0c2018-06-28 11:56:41 +01001053
1054 // Print errors.
1055 for (const std::string& str : unresolved) {
1056 LOG(WARNING) << "unresolved: " << str;
1057 }
1058
1059 // Write into public/private API files.
1060 std::ofstream file_public(out_public_path_.c_str());
1061 std::ofstream file_private(out_private_path_.c_str());
1062 for (const std::pair<std::string, bool> entry : boot_members) {
1063 if (entry.second) {
1064 file_public << entry.first << std::endl;
1065 } else {
1066 file_private << entry.first << std::endl;
1067 }
1068 }
1069 file_public.close();
1070 file_private.close();
1071 }
1072
David Brazdil2b9c35b2018-01-12 15:44:43 +00001073 // Paths to DEX files which should be processed.
David Brazdil003e64b2018-06-27 13:20:52 +01001074 std::vector<std::string> boot_dex_paths_;
David Brazdil345c0ed2018-08-03 10:26:44 +01001075
David Brazdil20c765f2018-10-27 21:45:15 +00001076 // Output paths where modified DEX files should be written.
1077 std::vector<std::string> output_dex_paths_;
1078
David Brazdil345c0ed2018-08-03 10:26:44 +01001079 // Set of public API stub classpaths. Each classpath is formed by a list
1080 // of DEX/APK files in the order they appear on the classpath.
1081 std::vector<std::vector<std::string>> stub_classpaths_;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001082
1083 // Paths to text files which contain the lists of API members.
1084 std::string light_greylist_path_;
1085 std::string dark_greylist_path_;
1086 std::string blacklist_path_;
David Brazdil0b6de0c2018-06-28 11:56:41 +01001087
1088 // Paths to text files to which we will output list of all API members.
1089 std::string out_public_path_;
1090 std::string out_private_path_;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001091};
1092
1093} // namespace art
1094
1095int main(int argc, char** argv) {
David Brazdil20c765f2018-10-27 21:45:15 +00001096 art::original_argc = argc;
1097 art::original_argv = argv;
David Brazdil003e64b2018-06-27 13:20:52 +01001098 android::base::InitLogging(argv);
1099 art::MemMap::Init();
1100 art::HiddenApi().Run(argc, argv);
1101 return EXIT_SUCCESS;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001102}