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