1 /*
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>
19 #include <map>
20 #include <set>
21 #include <string>
22 #include <string_view>
23 
24 #include "android-base/stringprintf.h"
25 #include "android-base/strings.h"
26 
27 #include "base/bit_utils.h"
28 #include "base/hiddenapi_flags.h"
29 #include "base/mem_map.h"
30 #include "base/os.h"
31 #include "base/stl_util.h"
32 #include "base/string_view_cpp20.h"
33 #include "base/unix_file/fd_file.h"
34 #include "dex/art_dex_file_loader.h"
35 #include "dex/class_accessor-inl.h"
36 #include "dex/dex_file-inl.h"
37 
38 namespace art {
39 namespace hiddenapi {
40 
41 const char kErrorHelp[] = "\nSee go/hiddenapi-error for help.";
42 
43 static int original_argc;
44 static char** original_argv;
45 
CommandLine()46 static std::string CommandLine() {
47   std::vector<std::string> command;
48   command.reserve(original_argc);
49   for (int i = 0; i < original_argc; ++i) {
50     command.push_back(original_argv[i]);
51   }
52   return android::base::Join(command, ' ');
53 }
54 
UsageErrorV(const char * fmt,va_list ap)55 static 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 
UsageError(const char * fmt,...)61 static void UsageError(const char* fmt, ...) {
62   va_list ap;
63   va_start(ap, fmt);
64   UsageErrorV(fmt, ap);
65   va_end(ap);
66 }
67 
Usage(const char * fmt,...)68 NO_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());
75   UsageError("Usage: hiddenapi [command_name] [options]...");
76   UsageError("");
77   UsageError("  Command \"encode\": encode API list membership in boot dex files");
78   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");
81   UsageError("");
82   UsageError("    --api-flags=<filename>:");
83   UsageError("        CSV file with signatures of methods/fields and their respective flags");
84   UsageError("");
85   UsageError("    --max-hiddenapi-level=<max-target-*>:");
86   UsageError("        the maximum hidden api level for APIs. If an API was originally restricted");
87   UsageError("        to a newer sdk, turn it into a regular unsupported API instead.");
88   UsageError("        instead. The full list of valid values is in hiddenapi_flags.h");
89   UsageError("");
90   UsageError("    --no-force-assign-all:");
91   UsageError("        Disable check that all dex entries have been assigned a flag");
92   UsageError("");
93   UsageError("  Command \"list\": dump lists of public and private API");
94   UsageError("    --dependency-stub-dex=<filename>: dex file containing API stubs provided");
95   UsageError("      by other parts of the bootclasspath. These are used to resolve");
96   UsageError("      dependencies in dex files specified in --boot-dex but do not appear in");
97   UsageError("      the output");
98   UsageError("    --boot-dex=<filename>: dex file which belongs to boot class path");
99   UsageError("    --public-stub-classpath=<filenames>:");
100   UsageError("    --system-stub-classpath=<filenames>:");
101   UsageError("    --test-stub-classpath=<filenames>:");
102   UsageError("    --core-platform-stub-classpath=<filenames>:");
103   UsageError("        colon-separated list of dex/apk files which form API stubs of boot");
104   UsageError("        classpath. Multiple classpaths can be specified");
105   UsageError("");
106   UsageError("    --out-api-flags=<filename>: output file for a CSV file with API flags");
107   UsageError("    --fragment: the input is only a fragment of the whole bootclasspath and may");
108   UsageError("      not include a complete set of classes. That requires the tool to ignore");
109   UsageError("      missing classes and members. Specify --verbose to see the warnings.");
110   UsageError("    --verbose: output all warnings, even when --fragment is specified.");
111   UsageError("");
112 
113   exit(EXIT_FAILURE);
114 }
115 
116 template<typename E>
Contains(const std::vector<E> & vec,const E & elem)117 static bool Contains(const std::vector<E>& vec, const E& elem) {
118   return std::find(vec.begin(), vec.end(), elem) != vec.end();
119 }
120 
121 class DexClass : public ClassAccessor {
122  public:
DexClass(const ClassAccessor & accessor)123   explicit DexClass(const ClassAccessor& accessor) : ClassAccessor(accessor) {}
124 
GetData() const125   const uint8_t* GetData() const { return dex_file_.GetClassData(GetClassDef()); }
126 
GetSuperclassIndex() const127   const dex::TypeIndex GetSuperclassIndex() const { return GetClassDef().superclass_idx_; }
128 
HasSuperclass() const129   bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); }
130 
GetSuperclassDescriptor() const131   std::string_view GetSuperclassDescriptor() const {
132     return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : "";
133   }
134 
GetInterfaceDescriptors() const135   std::set<std::string_view> GetInterfaceDescriptors() const {
136     std::set<std::string_view> list;
137     const dex::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef());
138     for (uint32_t i = 0; ifaces != nullptr && i < ifaces->Size(); ++i) {
139       list.insert(dex_file_.StringByTypeIdx(ifaces->GetTypeItem(i).type_idx_));
140     }
141     return list;
142   }
143 
IsPublic() const144   inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
IsInterface() const145   inline bool IsInterface() const { return HasAccessFlags(kAccInterface); }
146 
Equals(const DexClass & other) const147   inline bool Equals(const DexClass& other) const {
148     bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
149 
150     if (equals) {
151       LOG(FATAL) << "Class duplication: " << GetDescriptor() << " in " << dex_file_.GetLocation()
152           << " and " << other.dex_file_.GetLocation();
153     }
154 
155     return equals;
156   }
157 
158  private:
GetAccessFlags() const159   uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; }
HasAccessFlags(uint32_t mask) const160   bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
161 
JoinStringSet(const std::set<std::string_view> & s)162   static std::string JoinStringSet(const std::set<std::string_view>& s) {
163     return "{" + ::android::base::Join(std::vector<std::string>(s.begin(), s.end()), ",") + "}";
164   }
165 };
166 
167 class DexMember {
168  public:
DexMember(const DexClass & klass,const ClassAccessor::Field & item)169   DexMember(const DexClass& klass, const ClassAccessor::Field& item)
170       : klass_(klass), item_(item), is_method_(false) {
171     DCHECK_EQ(GetFieldId().class_idx_, klass.GetClassIdx());
172   }
173 
DexMember(const DexClass & klass,const ClassAccessor::Method & item)174   DexMember(const DexClass& klass, const ClassAccessor::Method& item)
175       : klass_(klass), item_(item), is_method_(true) {
176     DCHECK_EQ(GetMethodId().class_idx_, klass.GetClassIdx());
177   }
178 
GetDeclaringClass() const179   inline const DexClass& GetDeclaringClass() const { return klass_; }
180 
IsMethod() const181   inline bool IsMethod() const { return is_method_; }
IsVirtualMethod() const182   inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); }
IsConstructor() const183   inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); }
184 
IsPublicOrProtected() const185   inline bool IsPublicOrProtected() const {
186     return HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected);
187   }
188 
189   // Constructs a string with a unique signature of this class member.
GetApiEntry() const190   std::string GetApiEntry() const {
191     std::stringstream ss;
192     ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":")
193        << GetSignature();
194     return ss.str();
195   }
196 
operator ==(const DexMember & other) const197   inline bool operator==(const DexMember& other) const {
198     // These need to match if they should resolve to one another.
199     bool equals = IsMethod() == other.IsMethod() &&
200                   GetName() == other.GetName() &&
201                   GetSignature() == other.GetSignature();
202 
203     // Soundness check that they do match.
204     if (equals) {
205       CHECK_EQ(IsVirtualMethod(), other.IsVirtualMethod());
206     }
207 
208     return equals;
209   }
210 
211  private:
GetAccessFlags() const212   inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); }
HasAccessFlags(uint32_t mask) const213   inline bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
214 
GetName() const215   inline std::string_view GetName() const {
216     return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId())
217                       : item_.GetDexFile().GetFieldName(GetFieldId());
218   }
219 
GetSignature() const220   inline std::string GetSignature() const {
221     return IsMethod() ? item_.GetDexFile().GetMethodSignature(GetMethodId()).ToString()
222                       : item_.GetDexFile().GetFieldTypeDescriptor(GetFieldId());
223   }
224 
GetMethod() const225   inline const ClassAccessor::Method& GetMethod() const {
226     DCHECK(IsMethod());
227     return down_cast<const ClassAccessor::Method&>(item_);
228   }
229 
GetMethodId() const230   inline const dex::MethodId& GetMethodId() const {
231     DCHECK(IsMethod());
232     return item_.GetDexFile().GetMethodId(item_.GetIndex());
233   }
234 
GetFieldId() const235   inline const dex::FieldId& GetFieldId() const {
236     DCHECK(!IsMethod());
237     return item_.GetDexFile().GetFieldId(item_.GetIndex());
238   }
239 
240   const DexClass& klass_;
241   const ClassAccessor::BaseItem& item_;
242   const bool is_method_;
243 };
244 
245 class ClassPath final {
246  public:
ClassPath(const std::vector<std::string> & dex_paths,bool open_writable,bool ignore_empty)247   ClassPath(const std::vector<std::string>& dex_paths, bool open_writable, bool ignore_empty) {
248     OpenDexFiles(dex_paths, open_writable, ignore_empty);
249   }
250 
251   template<typename Fn>
ForEachDexClass(Fn fn)252   void ForEachDexClass(Fn fn) {
253     for (auto& dex_file : dex_files_) {
254       for (ClassAccessor accessor : dex_file->GetClasses()) {
255         fn(DexClass(accessor));
256       }
257     }
258   }
259 
260   template<typename Fn>
ForEachDexMember(Fn fn)261   void ForEachDexMember(Fn fn) {
262     ForEachDexClass([&fn](const DexClass& klass) {
263       for (const ClassAccessor::Field& field : klass.GetFields()) {
264         fn(DexMember(klass, field));
265       }
266       for (const ClassAccessor::Method& method : klass.GetMethods()) {
267         fn(DexMember(klass, method));
268       }
269     });
270   }
271 
GetDexFiles() const272   std::vector<const DexFile*> GetDexFiles() const {
273     return MakeNonOwningPointerVector(dex_files_);
274   }
275 
UpdateDexChecksums()276   void UpdateDexChecksums() {
277     for (auto& dex_file : dex_files_) {
278       // Obtain a writeable pointer to the dex header.
279       DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
280       // Recalculate checksum and overwrite the value in the header.
281       header->checksum_ = dex_file->CalculateChecksum();
282     }
283   }
284 
285  private:
OpenDexFiles(const std::vector<std::string> & dex_paths,bool open_writable,bool ignore_empty)286   void OpenDexFiles(const std::vector<std::string>& dex_paths,
287                     bool open_writable,
288                     bool ignore_empty) {
289     ArtDexFileLoader dex_loader;
290     std::string error_msg;
291 
292     if (open_writable) {
293       for (const std::string& filename : dex_paths) {
294         File fd(filename.c_str(), O_RDWR, /* check_usage= */ false);
295         CHECK_NE(fd.Fd(), -1) << "Unable to open file '" << filename << "': " << strerror(errno);
296 
297         // Memory-map the dex file with MAP_SHARED flag so that changes in memory
298         // propagate to the underlying file. We run dex file verification as if
299         // the dex file was not in boot claass path to check basic assumptions,
300         // such as that at most one of public/private/protected flag is set.
301         // We do those checks here and skip them when loading the processed file
302         // into boot class path.
303         std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
304                                                                    /* location= */ filename,
305                                                                    /* verify= */ true,
306                                                                    /* verify_checksum= */ true,
307                                                                    /* mmap_shared= */ true,
308                                                                    &error_msg));
309         CHECK(dex_file.get() != nullptr) << "Open failed for '" << filename << "' " << error_msg;
310         CHECK(dex_file->IsStandardDexFile()) << "Expected a standard dex file '" << filename << "'";
311         CHECK(dex_file->EnableWrite())
312             << "Failed to enable write permission for '" << filename << "'";
313         dex_files_.push_back(std::move(dex_file));
314       }
315     } else {
316       for (const std::string& filename : dex_paths) {
317         bool success = dex_loader.Open(filename.c_str(),
318                                        /* location= */ filename,
319                                        /* verify= */ true,
320                                        /* verify_checksum= */ true,
321                                        &error_msg,
322                                        &dex_files_);
323         // If requested ignore a jar with no classes.dex files.
324         if (!success && ignore_empty && error_msg != "Entry not found") {
325           CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
326         }
327       }
328     }
329   }
330 
331   // Opened dex files. Note that these are opened as `const` but may be written into.
332   std::vector<std::unique_ptr<const DexFile>> dex_files_;
333 };
334 
335 class HierarchyClass final {
336  public:
HierarchyClass()337   HierarchyClass() {}
338 
AddDexClass(const DexClass & klass)339   void AddDexClass(const DexClass& klass) {
340     CHECK(dex_classes_.empty() || klass.Equals(dex_classes_.front()));
341     dex_classes_.push_back(klass);
342   }
343 
AddExtends(HierarchyClass & parent)344   void AddExtends(HierarchyClass& parent) {
345     CHECK(!Contains(extends_, &parent));
346     CHECK(!Contains(parent.extended_by_, this));
347     extends_.push_back(&parent);
348     parent.extended_by_.push_back(this);
349   }
350 
GetOneDexClass() const351   const DexClass& GetOneDexClass() const {
352     CHECK(!dex_classes_.empty());
353     return dex_classes_.front();
354   }
355 
356   // See comment on Hierarchy::ForEachResolvableMember.
357   template<typename Fn>
ForEachResolvableMember(const DexMember & other,Fn fn)358   bool ForEachResolvableMember(const DexMember& other, Fn fn) {
359     std::vector<HierarchyClass*> visited;
360     return ForEachResolvableMember_Impl(other, fn, true, true, visited);
361   }
362 
363   // Returns true if this class contains at least one member matching `other`.
HasMatchingMember(const DexMember & other)364   bool HasMatchingMember(const DexMember& other) {
365     return ForEachMatchingMember(other, [](const DexMember&) { return true; });
366   }
367 
368   // Recursively iterates over all subclasses of this class and invokes `fn`
369   // on each one. If `fn` returns false for a particular subclass, exploring its
370   // subclasses is skipped.
371   template<typename Fn>
ForEachSubClass(Fn fn)372   void ForEachSubClass(Fn fn) {
373     for (HierarchyClass* subclass : extended_by_) {
374       if (fn(subclass)) {
375         subclass->ForEachSubClass(fn);
376       }
377     }
378   }
379 
380  private:
381   template<typename Fn>
ForEachResolvableMember_Impl(const DexMember & other,Fn fn,bool allow_explore_up,bool allow_explore_down,std::vector<HierarchyClass * > visited)382   bool ForEachResolvableMember_Impl(const DexMember& other,
383                                     Fn fn,
384                                     bool allow_explore_up,
385                                     bool allow_explore_down,
386                                     std::vector<HierarchyClass*> visited) {
387     if (std::find(visited.begin(), visited.end(), this) == visited.end()) {
388       visited.push_back(this);
389     } else {
390       return false;
391     }
392 
393     // First try to find a member matching `other` in this class.
394     bool found = ForEachMatchingMember(other, fn);
395 
396     // If not found, see if it is inherited from parents. Note that this will not
397     // revisit parents already in `visited`.
398     if (!found && allow_explore_up) {
399       for (HierarchyClass* superclass : extends_) {
400         found |= superclass->ForEachResolvableMember_Impl(
401             other,
402             fn,
403             /* allow_explore_up */ true,
404             /* allow_explore_down */ false,
405             visited);
406       }
407     }
408 
409     // If this is a virtual method, continue exploring into subclasses so as to visit
410     // all overriding methods. Allow subclasses to explore their superclasses if this
411     // is an interface. This is needed to find implementations of this interface's
412     // methods inherited from superclasses (b/122551864).
413     if (allow_explore_down && other.IsVirtualMethod()) {
414       for (HierarchyClass* subclass : extended_by_) {
415         subclass->ForEachResolvableMember_Impl(
416             other,
417             fn,
418             /* allow_explore_up */ GetOneDexClass().IsInterface(),
419             /* allow_explore_down */ true,
420             visited);
421       }
422     }
423 
424     return found;
425   }
426 
427   template<typename Fn>
ForEachMatchingMember(const DexMember & other,Fn fn)428   bool ForEachMatchingMember(const DexMember& other, Fn fn) {
429     bool found = false;
430     auto compare_member = [&](const DexMember& member) {
431       // TODO(dbrazdil): Check whether class of `other` can access `member`.
432       if (member == other) {
433         found = true;
434         fn(member);
435       }
436     };
437     for (const DexClass& dex_class : dex_classes_) {
438       for (const ClassAccessor::Field& field : dex_class.GetFields()) {
439         compare_member(DexMember(dex_class, field));
440       }
441       for (const ClassAccessor::Method& method : dex_class.GetMethods()) {
442         compare_member(DexMember(dex_class, method));
443       }
444     }
445     return found;
446   }
447 
448   // DexClass entries of this class found across all the provided dex files.
449   std::vector<DexClass> dex_classes_;
450 
451   // Classes which this class inherits, or interfaces which it implements.
452   std::vector<HierarchyClass*> extends_;
453 
454   // Classes which inherit from this class.
455   std::vector<HierarchyClass*> extended_by_;
456 };
457 
458 class Hierarchy final {
459  public:
Hierarchy(ClassPath & classpath,bool fragment,bool verbose)460   Hierarchy(ClassPath& classpath, bool fragment, bool verbose) : classpath_(classpath) {
461     BuildClassHierarchy(fragment, verbose);
462   }
463 
464   // Perform an operation for each member of the hierarchy which could potentially
465   // be the result of method/field resolution of `other`.
466   // The function `fn` should accept a DexMember reference and return true if
467   // the member was changed. This drives a performance optimization which only
468   // visits overriding members the first time the overridden member is visited.
469   // Returns true if at least one resolvable member was found.
470   template<typename Fn>
ForEachResolvableMember(const DexMember & other,Fn fn)471   bool ForEachResolvableMember(const DexMember& other, Fn fn) {
472     HierarchyClass* klass = FindClass(other.GetDeclaringClass().GetDescriptor());
473     return (klass != nullptr) && klass->ForEachResolvableMember(other, fn);
474   }
475 
476   // Returns true if `member`, which belongs to this classpath, is visible to
477   // code in child class loaders.
IsMemberVisible(const DexMember & member)478   bool IsMemberVisible(const DexMember& member) {
479     if (!member.IsPublicOrProtected()) {
480       // Member is private or package-private. Cannot be visible.
481       return false;
482     } else if (member.GetDeclaringClass().IsPublic()) {
483       // Member is public or protected, and class is public. It must be visible.
484       return true;
485     } else if (member.IsConstructor()) {
486       // Member is public or protected constructor and class is not public.
487       // Must be hidden because it cannot be implicitly exposed by a subclass.
488       return false;
489     } else {
490       // Member is public or protected method, but class is not public. Check if
491       // it is exposed through a public subclass.
492       // Example code (`foo` exposed by ClassB):
493       //   class ClassA { public void foo() { ... } }
494       //   public class ClassB extends ClassA {}
495       HierarchyClass* klass = FindClass(member.GetDeclaringClass().GetDescriptor());
496       CHECK(klass != nullptr);
497       bool visible = false;
498       klass->ForEachSubClass([&visible, &member](HierarchyClass* subclass) {
499         if (subclass->HasMatchingMember(member)) {
500           // There is a member which matches `member` in `subclass`, either
501           // a virtual method overriding `member` or a field overshadowing
502           // `member`. In either case, `member` remains hidden.
503           CHECK(member.IsVirtualMethod() || !member.IsMethod());
504           return false;  // do not explore deeper
505         } else if (subclass->GetOneDexClass().IsPublic()) {
506           // `subclass` inherits and exposes `member`.
507           visible = true;
508           return false;  // do not explore deeper
509         } else {
510           // `subclass` inherits `member` but does not expose it.
511           return true;   // explore deeper
512         }
513       });
514       return visible;
515     }
516   }
517 
518  private:
FindClass(const std::string_view & descriptor)519   HierarchyClass* FindClass(const std::string_view& descriptor) {
520     auto it = classes_.find(descriptor);
521     if (it == classes_.end()) {
522       return nullptr;
523     } else {
524       return &it->second;
525     }
526   }
527 
BuildClassHierarchy(bool fragment,bool verbose)528   void BuildClassHierarchy(bool fragment, bool verbose) {
529     // Create one HierarchyClass entry in `classes_` per class descriptor
530     // and add all DexClass objects with the same descriptor to that entry.
531     classpath_.ForEachDexClass([this](const DexClass& klass) {
532       classes_[klass.GetDescriptor()].AddDexClass(klass);
533     });
534 
535     // Connect each HierarchyClass to its successors and predecessors.
536     for (auto& entry : classes_) {
537       HierarchyClass& klass = entry.second;
538       const DexClass& dex_klass = klass.GetOneDexClass();
539 
540       if (!dex_klass.HasSuperclass()) {
541         CHECK(dex_klass.GetInterfaceDescriptors().empty())
542             << "java/lang/Object should not implement any interfaces";
543         continue;
544       }
545 
546       auto add_extends = [&](const std::string_view& extends_desc) {
547         HierarchyClass* extends = FindClass(extends_desc);
548         if (extends != nullptr) {
549           klass.AddExtends(*extends);
550         } else if (!fragment || verbose) {
551           auto severity = verbose ? ::android::base::WARNING : ::android::base::FATAL;
552           LOG(severity)
553               << "Superclass/interface " << extends_desc
554               << " of class " << dex_klass.GetDescriptor() << " from dex file \""
555               << dex_klass.GetDexFile().GetLocation() << "\" was not found. "
556               << "Either it is missing or it appears later in the classpath spec.";
557         }
558       };
559 
560       add_extends(dex_klass.GetSuperclassDescriptor());
561       for (const std::string_view& iface_desc : dex_klass.GetInterfaceDescriptors()) {
562         add_extends(iface_desc);
563       }
564     }
565   }
566 
567   ClassPath& classpath_;
568   std::map<std::string_view, HierarchyClass> classes_;
569 };
570 
571 // Builder of dex section containing hiddenapi flags.
572 class HiddenapiClassDataBuilder final {
573  public:
HiddenapiClassDataBuilder(const DexFile & dex_file)574   explicit HiddenapiClassDataBuilder(const DexFile& dex_file)
575       : num_classdefs_(dex_file.NumClassDefs()),
576         next_class_def_idx_(0u),
577         class_def_has_non_zero_flags_(false),
578         dex_file_has_non_zero_flags_(false),
579         data_(sizeof(uint32_t) * (num_classdefs_ + 1), 0u) {
580     *GetSizeField() = GetCurrentDataSize();
581   }
582 
583   // Notify the builder that new flags for the next class def
584   // will be written now. The builder records the current offset
585   // into the header.
BeginClassDef(uint32_t idx)586   void BeginClassDef(uint32_t idx) {
587     CHECK_EQ(next_class_def_idx_, idx);
588     CHECK_LT(idx, num_classdefs_);
589     GetOffsetArray()[idx] = GetCurrentDataSize();
590     class_def_has_non_zero_flags_ = false;
591   }
592 
593   // Notify the builder that all flags for this class def have been
594   // written. The builder updates the total size of the data struct
595   // and may set offset for class def in header to zero if no data
596   // has been written.
EndClassDef(uint32_t idx)597   void EndClassDef(uint32_t idx) {
598     CHECK_EQ(next_class_def_idx_, idx);
599     CHECK_LT(idx, num_classdefs_);
600 
601     ++next_class_def_idx_;
602 
603     if (!class_def_has_non_zero_flags_) {
604       // No need to store flags for this class. Remove the written flags
605       // and set offset in header to zero.
606       data_.resize(GetOffsetArray()[idx]);
607       GetOffsetArray()[idx] = 0u;
608     }
609 
610     dex_file_has_non_zero_flags_ |= class_def_has_non_zero_flags_;
611 
612     if (idx == num_classdefs_ - 1) {
613       if (dex_file_has_non_zero_flags_) {
614         // This was the last class def and we have generated non-zero hiddenapi
615         // flags. Update total size in the header.
616         *GetSizeField() = GetCurrentDataSize();
617       } else {
618         // This was the last class def and we have not generated any non-zero
619         // hiddenapi flags. Clear all the data.
620         data_.clear();
621       }
622     }
623   }
624 
625   // Append flags at the end of the data struct. This should be called
626   // between BeginClassDef and EndClassDef in the order of appearance of
627   // fields/methods in the class data stream.
WriteFlags(const ApiList & flags)628   void WriteFlags(const ApiList& flags) {
629     uint32_t dex_flags = flags.GetDexFlags();
630     EncodeUnsignedLeb128(&data_, dex_flags);
631     class_def_has_non_zero_flags_ |= (dex_flags != 0u);
632   }
633 
634   // Return backing data, assuming that all flags have been written.
GetData() const635   const std::vector<uint8_t>& GetData() const {
636     CHECK_EQ(next_class_def_idx_, num_classdefs_) << "Incomplete data";
637     return data_;
638   }
639 
640  private:
641   // Returns pointer to the size field in the header of this dex section.
GetSizeField()642   uint32_t* GetSizeField() {
643     // Assume malloc() aligns allocated memory to at least uint32_t.
644     CHECK(IsAligned<sizeof(uint32_t)>(data_.data()));
645     return reinterpret_cast<uint32_t*>(data_.data());
646   }
647 
648   // Returns pointer to array of offsets (indexed by class def indices) in the
649   // header of this dex section.
GetOffsetArray()650   uint32_t* GetOffsetArray() { return &GetSizeField()[1]; }
GetCurrentDataSize() const651   uint32_t GetCurrentDataSize() const { return data_.size(); }
652 
653   // Number of class defs in this dex file.
654   const uint32_t num_classdefs_;
655 
656   // Next expected class def index.
657   uint32_t next_class_def_idx_;
658 
659   // Whether non-zero flags have been encountered for this class def.
660   bool class_def_has_non_zero_flags_;
661 
662   // Whether any non-zero flags have been encountered for this dex file.
663   bool dex_file_has_non_zero_flags_;
664 
665   // Vector containing the data of the built data structure.
666   std::vector<uint8_t> data_;
667 };
668 
669 // Edits a dex file, inserting a new HiddenapiClassData section.
670 class DexFileEditor final {
671  public:
DexFileEditor(const DexFile & old_dex,const std::vector<uint8_t> & hiddenapi_class_data)672   DexFileEditor(const DexFile& old_dex, const std::vector<uint8_t>& hiddenapi_class_data)
673       : old_dex_(old_dex),
674         hiddenapi_class_data_(hiddenapi_class_data),
675         loaded_dex_header_(nullptr),
676         loaded_dex_maplist_(nullptr) {}
677 
678   // Copies dex file into a backing data vector, appends the given HiddenapiClassData
679   // and updates the MapList.
Encode()680   void Encode() {
681     // We do not support non-standard dex encodings, e.g. compact dex.
682     CHECK(old_dex_.IsStandardDexFile());
683 
684     // If there are no data to append, copy the old dex file and return.
685     if (hiddenapi_class_data_.empty()) {
686       AllocateMemory(old_dex_.Size());
687       Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
688       return;
689     }
690 
691     // Find the old MapList, find its size.
692     const dex::MapList* old_map = old_dex_.GetMapList();
693     CHECK_LT(old_map->size_, std::numeric_limits<uint32_t>::max());
694 
695     // Compute the size of the new dex file. We append the HiddenapiClassData,
696     // one MapItem and possibly some padding to align the new MapList.
697     CHECK(IsAligned<kMapListAlignment>(old_dex_.Size()))
698         << "End of input dex file is not 4-byte aligned, possibly because its MapList is not "
699         << "at the end of the file.";
700     size_t size_delta =
701         RoundUp(hiddenapi_class_data_.size(), kMapListAlignment) + sizeof(dex::MapItem);
702     size_t new_size = old_dex_.Size() + size_delta;
703     AllocateMemory(new_size);
704 
705     // Copy the old dex file into the backing data vector. Load the copied
706     // dex file to obtain pointers to its header and MapList.
707     Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
708     ReloadDex(/* verify= */ false);
709 
710     // Truncate the new dex file before the old MapList. This assumes that
711     // the MapList is the last entry in the dex file. This is currently true
712     // for our tooling.
713     // TODO: Implement the general case by zero-ing the old MapList (turning
714     // it into padding.
715     RemoveOldMapList();
716 
717     // Append HiddenapiClassData.
718     size_t payload_offset = AppendHiddenapiClassData();
719 
720     // Wrute new MapList with an entry for HiddenapiClassData.
721     CreateMapListWithNewItem(payload_offset);
722 
723     // Check that the pre-computed size matches the actual size.
724     CHECK_EQ(offset_, new_size);
725 
726     // Reload to all data structures.
727     ReloadDex(/* verify= */ false);
728 
729     // Update the dex checksum.
730     UpdateChecksum();
731 
732     // Run DexFileVerifier on the new dex file as a CHECK.
733     ReloadDex(/* verify= */ true);
734   }
735 
736   // Writes the edited dex file into a file.
WriteTo(const std::string & path)737   void WriteTo(const std::string& path) {
738     CHECK(!data_.empty());
739     std::ofstream ofs(path.c_str(), std::ofstream::out | std::ofstream::binary);
740     ofs.write(reinterpret_cast<const char*>(data_.data()), data_.size());
741     ofs.flush();
742     CHECK(ofs.good());
743     ofs.close();
744   }
745 
746  private:
747   static constexpr size_t kMapListAlignment = 4u;
748   static constexpr size_t kHiddenapiClassDataAlignment = 4u;
749 
ReloadDex(bool verify)750   void ReloadDex(bool verify) {
751     std::string error_msg;
752     DexFileLoader loader;
753     loaded_dex_ = loader.Open(
754         data_.data(),
755         data_.size(),
756         "test_location",
757         old_dex_.GetLocationChecksum(),
758         /* oat_dex_file= */ nullptr,
759         /* verify= */ verify,
760         /* verify_checksum= */ verify,
761         &error_msg);
762     if (loaded_dex_.get() == nullptr) {
763       LOG(FATAL) << "Failed to load edited dex file: " << error_msg;
764       UNREACHABLE();
765     }
766 
767     // Load the location of header and map list before we start editing the file.
768     loaded_dex_header_ = const_cast<DexFile::Header*>(&loaded_dex_->GetHeader());
769     loaded_dex_maplist_ = const_cast<dex::MapList*>(loaded_dex_->GetMapList());
770   }
771 
GetHeader() const772   DexFile::Header& GetHeader() const {
773     CHECK(loaded_dex_header_ != nullptr);
774     return *loaded_dex_header_;
775   }
776 
GetMapList() const777   dex::MapList& GetMapList() const {
778     CHECK(loaded_dex_maplist_ != nullptr);
779     return *loaded_dex_maplist_;
780   }
781 
AllocateMemory(size_t total_size)782   void AllocateMemory(size_t total_size) {
783     data_.clear();
784     data_.resize(total_size);
785     CHECK(IsAligned<kMapListAlignment>(data_.data()));
786     CHECK(IsAligned<kHiddenapiClassDataAlignment>(data_.data()));
787     offset_ = 0;
788   }
789 
GetCurrentDataPtr()790   uint8_t* GetCurrentDataPtr() {
791     return data_.data() + offset_;
792   }
793 
UpdateDataSize(off_t delta,bool update_header)794   void UpdateDataSize(off_t delta, bool update_header) {
795     offset_ += delta;
796     if (update_header) {
797       DexFile::Header& header = GetHeader();
798       header.file_size_ += delta;
799       header.data_size_ += delta;
800     }
801   }
802 
803   template<typename T>
Append(const T * src,size_t len,bool update_header=true)804   T* Append(const T* src, size_t len, bool update_header = true) {
805     CHECK_LE(offset_ + len, data_.size());
806     uint8_t* dst = GetCurrentDataPtr();
807     memcpy(dst, src, len);
808     UpdateDataSize(len, update_header);
809     return reinterpret_cast<T*>(dst);
810   }
811 
InsertPadding(size_t alignment)812   void InsertPadding(size_t alignment) {
813     size_t len = RoundUp(offset_, alignment) - offset_;
814     std::vector<uint8_t> padding(len, 0);
815     Append(padding.data(), padding.size());
816   }
817 
RemoveOldMapList()818   void RemoveOldMapList() {
819     size_t map_size = GetMapList().Size();
820     uint8_t* map_start = reinterpret_cast<uint8_t*>(&GetMapList());
821     CHECK_EQ(map_start + map_size, GetCurrentDataPtr()) << "MapList not at the end of dex file";
822     UpdateDataSize(-static_cast<off_t>(map_size), /* update_header= */ true);
823     CHECK_EQ(map_start, GetCurrentDataPtr());
824     loaded_dex_maplist_ = nullptr;  // do not use this map list any more
825   }
826 
CreateMapListWithNewItem(size_t payload_offset)827   void CreateMapListWithNewItem(size_t payload_offset) {
828     InsertPadding(/* alignment= */ kMapListAlignment);
829 
830     size_t new_map_offset = offset_;
831     dex::MapList* map = Append(old_dex_.GetMapList(), old_dex_.GetMapList()->Size());
832 
833     // Check last map entry is a pointer to itself.
834     dex::MapItem& old_item = map->list_[map->size_ - 1];
835     CHECK(old_item.type_ == DexFile::kDexTypeMapList);
836     CHECK_EQ(old_item.size_, 1u);
837     CHECK_EQ(old_item.offset_, GetHeader().map_off_);
838 
839     // Create a new MapItem entry with new MapList details.
840     dex::MapItem new_item;
841     new_item.type_ = old_item.type_;
842     new_item.unused_ = 0u;  // initialize to ensure dex output is deterministic (b/119308882)
843     new_item.size_ = old_item.size_;
844     new_item.offset_ = new_map_offset;
845 
846     // Update pointer in the header.
847     GetHeader().map_off_ = new_map_offset;
848 
849     // Append a new MapItem and return its pointer.
850     map->size_++;
851     Append(&new_item, sizeof(dex::MapItem));
852 
853     // Change penultimate entry to point to metadata.
854     old_item.type_ = DexFile::kDexTypeHiddenapiClassData;
855     old_item.size_ = 1u;  // there is only one section
856     old_item.offset_ = payload_offset;
857   }
858 
AppendHiddenapiClassData()859   size_t AppendHiddenapiClassData() {
860     size_t payload_offset = offset_;
861     CHECK_EQ(kMapListAlignment, kHiddenapiClassDataAlignment);
862     CHECK(IsAligned<kHiddenapiClassDataAlignment>(payload_offset))
863         << "Should not need to align the section, previous data was already aligned";
864     Append(hiddenapi_class_data_.data(), hiddenapi_class_data_.size());
865     return payload_offset;
866   }
867 
UpdateChecksum()868   void UpdateChecksum() {
869     GetHeader().checksum_ = loaded_dex_->CalculateChecksum();
870   }
871 
872   const DexFile& old_dex_;
873   const std::vector<uint8_t>& hiddenapi_class_data_;
874 
875   std::vector<uint8_t> data_;
876   size_t offset_;
877 
878   std::unique_ptr<const DexFile> loaded_dex_;
879   DexFile::Header* loaded_dex_header_;
880   dex::MapList* loaded_dex_maplist_;
881 };
882 
883 class HiddenApi final {
884  public:
HiddenApi()885   HiddenApi() : force_assign_all_(true) {}
886 
Run(int argc,char ** argv)887   void Run(int argc, char** argv) {
888     switch (ParseArgs(argc, argv)) {
889     case Command::kEncode:
890       EncodeAccessFlags();
891       break;
892     case Command::kList:
893       ListApi();
894       break;
895     }
896   }
897 
898  private:
899   enum class Command {
900     kEncode,
901     kList,
902   };
903 
ParseArgs(int argc,char ** argv)904   Command ParseArgs(int argc, char** argv) {
905     // Skip over the binary's path.
906     argv++;
907     argc--;
908 
909     if (argc > 0) {
910       const char* raw_command = argv[0];
911       const std::string_view command(raw_command);
912       if (command == "encode") {
913         for (int i = 1; i < argc; ++i) {
914           const char* raw_option = argv[i];
915           const std::string_view option(raw_option);
916           if (StartsWith(option, "--input-dex=")) {
917             boot_dex_paths_.push_back(std::string(option.substr(strlen("--input-dex="))));
918           } else if (StartsWith(option, "--output-dex=")) {
919             output_dex_paths_.push_back(std::string(option.substr(strlen("--output-dex="))));
920           } else if (StartsWith(option, "--api-flags=")) {
921             api_flags_path_ = std::string(option.substr(strlen("--api-flags=")));
922           } else if (option == "--no-force-assign-all") {
923             force_assign_all_ = false;
924           } else if (StartsWith(option, "--max-hiddenapi-level=")) {
925             max_hiddenapi_level_ = std::string(option.substr(strlen("--max-hiddenapi-level=")));
926           } else {
927             Usage("Unknown argument '%s'", raw_option);
928           }
929         }
930         return Command::kEncode;
931       } else if (command == "list") {
932         for (int i = 1; i < argc; ++i) {
933           const char* raw_option = argv[i];
934           const std::string_view option(raw_option);
935           if (StartsWith(option, "--dependency-stub-dex=")) {
936             const std::string path(std::string(option.substr(strlen("--dependency-stub-dex="))));
937             dependency_stub_dex_paths_.push_back(path);
938             // Add path to the boot dex path to resolve dependencies.
939             boot_dex_paths_.push_back(path);
940           } else if (StartsWith(option, "--boot-dex=")) {
941             boot_dex_paths_.push_back(std::string(option.substr(strlen("--boot-dex="))));
942           } else if (StartsWith(option, "--public-stub-classpath=")) {
943             stub_classpaths_.push_back(std::make_pair(
944                 std::string(option.substr(strlen("--public-stub-classpath="))),
945                 ApiStubs::Kind::kPublicApi));
946           } else if (StartsWith(option, "--system-stub-classpath=")) {
947             stub_classpaths_.push_back(std::make_pair(
948                 std::string(option.substr(strlen("--system-stub-classpath="))),
949                 ApiStubs::Kind::kSystemApi));
950           } else if (StartsWith(option, "--test-stub-classpath=")) {
951             stub_classpaths_.push_back(std::make_pair(
952                 std::string(option.substr(strlen("--test-stub-classpath="))),
953                 ApiStubs::Kind::kTestApi));
954           } else if (StartsWith(option, "--core-platform-stub-classpath=")) {
955             stub_classpaths_.push_back(std::make_pair(
956                 std::string(option.substr(strlen("--core-platform-stub-classpath="))),
957                 ApiStubs::Kind::kCorePlatformApi));
958           } else if (StartsWith(option, "--out-api-flags=")) {
959             api_flags_path_ = std::string(option.substr(strlen("--out-api-flags=")));
960           } else if (option == "--fragment") {
961             fragment_ = true;
962           } else if (option == "--verbose") {
963             verbose_ = true;
964           } else {
965             Usage("Unknown argument '%s'", raw_option);
966           }
967         }
968         return Command::kList;
969       } else {
970         Usage("Unknown command '%s'", raw_command);
971       }
972     } else {
973       Usage("No command specified");
974     }
975   }
976 
EncodeAccessFlags()977   void EncodeAccessFlags() {
978     if (boot_dex_paths_.empty()) {
979       Usage("No input DEX files specified");
980     } else if (output_dex_paths_.size() != boot_dex_paths_.size()) {
981       Usage("Number of input DEX files does not match number of output DEX files");
982     }
983 
984     // Load dex signatures.
985     std::map<std::string, ApiList> api_list = OpenApiFile(api_flags_path_);
986 
987     // Iterate over input dex files and insert HiddenapiClassData sections.
988     for (size_t i = 0; i < boot_dex_paths_.size(); ++i) {
989       const std::string& input_path = boot_dex_paths_[i];
990       const std::string& output_path = output_dex_paths_[i];
991 
992       ClassPath boot_classpath({ input_path },
993                                /* open_writable= */ false,
994                                /* ignore_empty= */ false);
995       std::vector<const DexFile*> input_dex_files = boot_classpath.GetDexFiles();
996       CHECK_EQ(input_dex_files.size(), 1u);
997       const DexFile& input_dex = *input_dex_files[0];
998 
999       HiddenapiClassDataBuilder builder(input_dex);
1000       boot_classpath.ForEachDexClass([&](const DexClass& boot_class) {
1001         builder.BeginClassDef(boot_class.GetClassDefIndex());
1002         if (boot_class.GetData() != nullptr) {
1003           auto fn_shared = [&](const DexMember& boot_member) {
1004             auto it = api_list.find(boot_member.GetApiEntry());
1005             bool api_list_found = (it != api_list.end());
1006             CHECK(!force_assign_all_ || api_list_found)
1007                 << "Could not find hiddenapi flags for dex entry: " << boot_member.GetApiEntry();
1008             builder.WriteFlags(api_list_found ? it->second : ApiList::Sdk());
1009           };
1010           auto fn_field = [&](const ClassAccessor::Field& boot_field) {
1011             fn_shared(DexMember(boot_class, boot_field));
1012           };
1013           auto fn_method = [&](const ClassAccessor::Method& boot_method) {
1014             fn_shared(DexMember(boot_class, boot_method));
1015           };
1016           boot_class.VisitFieldsAndMethods(fn_field, fn_field, fn_method, fn_method);
1017         }
1018         builder.EndClassDef(boot_class.GetClassDefIndex());
1019       });
1020 
1021       DexFileEditor dex_editor(input_dex, builder.GetData());
1022       dex_editor.Encode();
1023       dex_editor.WriteTo(output_path);
1024     }
1025   }
1026 
OpenApiFile(const std::string & path)1027   std::map<std::string, ApiList> OpenApiFile(const std::string& path) {
1028     CHECK(!path.empty());
1029     std::ifstream api_file(path, std::ifstream::in);
1030     CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno);
1031 
1032     std::map<std::string, ApiList> api_flag_map;
1033 
1034     size_t line_number = 1;
1035     for (std::string line; std::getline(api_file, line); line_number++) {
1036       // Every line contains a comma separated list with the signature as the
1037       // first element and the api flags as the rest
1038       std::vector<std::string> values = android::base::Split(line, ",");
1039       CHECK_GT(values.size(), 1u) << path << ":" << line_number
1040           << ": No flags found: " << line << kErrorHelp;
1041 
1042       const std::string& signature = values[0];
1043 
1044       // Skip signature
1045       std::vector<std::string>::iterator apiListBegin = values.begin() + 1;
1046       std::vector<std::string>::iterator apiListEnd = values.end();
1047       if (!max_hiddenapi_level_.empty()) {
1048           auto clamp_fn = [this](const std::string& apiListName) {
1049               return ApiList::CoerceAtMost(apiListName,
1050                                            max_hiddenapi_level_);
1051           };
1052           std::transform(apiListBegin, apiListEnd, apiListBegin, clamp_fn);
1053       }
1054 
1055       CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number
1056           << ": Duplicate entry: " << signature << kErrorHelp;
1057 
1058       ApiList membership;
1059 
1060       bool success = ApiList::FromNames(apiListBegin, apiListEnd, &membership);
1061       CHECK(success) << path << ":" << line_number
1062           << ": Some flags were not recognized: " << line << kErrorHelp;
1063       CHECK(membership.IsValid()) << path << ":" << line_number
1064           << ": Invalid combination of flags: " << line << kErrorHelp;
1065 
1066       api_flag_map.emplace(signature, membership);
1067     }
1068 
1069     api_file.close();
1070     return api_flag_map;
1071   }
1072 
1073   // A special flag added to the set of flags in boot_members to indicate that
1074   // it should be excluded from the output.
1075   static constexpr std::string_view kExcludeFromOutput{"exclude-from-output"};
1076 
ListApi()1077   void ListApi() {
1078     if (boot_dex_paths_.empty()) {
1079       Usage("No boot DEX files specified");
1080     } else if (stub_classpaths_.empty()) {
1081       Usage("No stub DEX files specified");
1082     } else if (api_flags_path_.empty()) {
1083       Usage("No output path specified");
1084     }
1085 
1086     // Complete list of boot class path members. The associated boolean states
1087     // whether it is public (true) or private (false).
1088     std::map<std::string, std::set<std::string_view>> boot_members;
1089 
1090     // Deduplicate errors before printing them.
1091     std::set<std::string> unresolved;
1092 
1093     // Open all dex files.
1094     ClassPath boot_classpath(boot_dex_paths_,
1095                              /* open_writable= */ false,
1096                              /* ignore_empty= */ false);
1097     Hierarchy boot_hierarchy(boot_classpath, fragment_, verbose_);
1098 
1099     // Mark all boot dex members private.
1100     boot_classpath.ForEachDexMember([&](const DexMember& boot_member) {
1101       boot_members[boot_member.GetApiEntry()] = {};
1102     });
1103 
1104     // Open all dependency API stub dex files.
1105     ClassPath dependency_classpath(dependency_stub_dex_paths_,
1106                                    /* open_writable= */ false,
1107                                    /* ignore_empty= */ false);
1108 
1109     // Mark all dependency API stub dex members as coming from the dependency.
1110     dependency_classpath.ForEachDexMember([&](const DexMember& boot_member) {
1111       boot_members[boot_member.GetApiEntry()] = {kExcludeFromOutput};
1112     });
1113 
1114     // Resolve each SDK dex member against the framework and mark it white.
1115     for (const auto& cp_entry : stub_classpaths_) {
1116       // Ignore any empty stub jars as it just means that they provide no APIs
1117       // for the current kind, e.g. framework-sdkextensions does not provide
1118       // any public APIs.
1119       ClassPath stub_classpath(android::base::Split(cp_entry.first, ":"),
1120                                /* open_writable= */ false,
1121                                /* ignore_empty= */ true);
1122       Hierarchy stub_hierarchy(stub_classpath, fragment_, verbose_);
1123       const ApiStubs::Kind stub_api = cp_entry.second;
1124 
1125       stub_classpath.ForEachDexMember(
1126           [&](const DexMember& stub_member) {
1127             if (!stub_hierarchy.IsMemberVisible(stub_member)) {
1128               // Typically fake constructors and inner-class `this` fields.
1129               return;
1130             }
1131             bool resolved = boot_hierarchy.ForEachResolvableMember(
1132                 stub_member,
1133                 [&](const DexMember& boot_member) {
1134                   std::string entry = boot_member.GetApiEntry();
1135                   auto it = boot_members.find(entry);
1136                   CHECK(it != boot_members.end());
1137                   it->second.insert(ApiStubs::ToString(stub_api));
1138                 });
1139             if (!resolved) {
1140               unresolved.insert(stub_member.GetApiEntry());
1141             }
1142           });
1143     }
1144 
1145     // Print errors.
1146     if (!fragment_ || verbose_) {
1147       for (const std::string& str : unresolved) {
1148         LOG(WARNING) << "unresolved: " << str;
1149       }
1150     }
1151 
1152     // Write into public/private API files.
1153     std::ofstream file_flags(api_flags_path_.c_str());
1154     for (const auto& entry : boot_members) {
1155       std::set<std::string_view> flags = entry.second;
1156       if (flags.empty()) {
1157         // There are no flags so it cannot be from the dependency stub API dex
1158         // files so just output the signature.
1159         file_flags << entry.first << std::endl;
1160       } else if (flags.find(kExcludeFromOutput) == flags.end()) {
1161         // The entry has flags and is not from the dependency stub API dex so
1162         // output it.
1163         file_flags << entry.first << ",";
1164         file_flags << android::base::Join(entry.second, ",") << std::endl;
1165       }
1166     }
1167     file_flags.close();
1168   }
1169 
1170   // Whether to check that all dex entries have been assigned flags.
1171   // Defaults to true.
1172   bool force_assign_all_;
1173 
1174   // Paths to DEX files which should be processed.
1175   std::vector<std::string> boot_dex_paths_;
1176 
1177   // Paths to DEX files containing API stubs provided by other parts of the
1178   // boot class path which the DEX files in boot_dex_paths depend.
1179   std::vector<std::string> dependency_stub_dex_paths_;
1180 
1181   // Output paths where modified DEX files should be written.
1182   std::vector<std::string> output_dex_paths_;
1183 
1184   // Set of public API stub classpaths. Each classpath is formed by a list
1185   // of DEX/APK files in the order they appear on the classpath.
1186   std::vector<std::pair<std::string, ApiStubs::Kind>> stub_classpaths_;
1187 
1188   // Path to CSV file containing the list of API members and their flags.
1189   // This could be both an input and output path.
1190   std::string api_flags_path_;
1191 
1192   // Override limit for sdk-max-* hidden APIs.
1193   std::string max_hiddenapi_level_;
1194 
1195   // Whether the input is only a fragment of the whole bootclasspath and may
1196   // not include a complete set of classes. That requires the tool to ignore missing
1197   // classes and members.
1198   bool fragment_ = false;
1199 
1200   // Whether to output all warnings, even when `fragment_` is set.
1201   bool verbose_ = false;
1202 };
1203 
1204 }  // namespace hiddenapi
1205 }  // namespace art
1206 
main(int argc,char ** argv)1207 int main(int argc, char** argv) {
1208   art::hiddenapi::original_argc = argc;
1209   art::hiddenapi::original_argv = argv;
1210   android::base::InitLogging(argv);
1211   art::MemMap::Init();
1212   art::hiddenapi::HiddenApi().Run(argc, argv);
1213   return EXIT_SUCCESS;
1214 }
1215