1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
|
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "base/mem_map.h"
#include "base/os.h"
#include "base/unix_file/fd_file.h"
#include "dex/art_dex_file_loader.h"
#include "dex/class_accessor-inl.h"
#include "dex/dex_file-inl.h"
#include "dex/hidden_api_access_flags.h"
namespace art {
static int original_argc;
static char** original_argv;
static std::string CommandLine() {
std::vector<std::string> command;
for (int i = 0; i < original_argc; ++i) {
command.push_back(original_argv[i]);
}
return android::base::Join(command, ' ');
}
static void UsageErrorV(const char* fmt, va_list ap) {
std::string error;
android::base::StringAppendV(&error, fmt, ap);
LOG(ERROR) << error;
}
static void UsageError(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
UsageErrorV(fmt, ap);
va_end(ap);
}
NO_RETURN static void Usage(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
UsageErrorV(fmt, ap);
va_end(ap);
UsageError("Command: %s", CommandLine().c_str());
UsageError("Usage: hiddenapi [command_name] [options]...");
UsageError("");
UsageError(" Command \"encode\": encode API list membership in boot dex files");
UsageError(" --dex=<filename>: dex file which belongs to boot class path,");
UsageError(" the file will be overwritten");
UsageError("");
UsageError(" --light-greylist=<filename>:");
UsageError(" --dark-greylist=<filename>:");
UsageError(" --blacklist=<filename>:");
UsageError(" text files with signatures of methods/fields to be annotated");
UsageError("");
UsageError(" Command \"list\": dump lists of public and private API");
UsageError(" --boot-dex=<filename>: dex file which belongs to boot class path");
UsageError(" --stub-classpath=<filenames>: colon-separated list of dex/apk files");
UsageError(" which form API stubs of boot class path. Multiple classpaths can");
UsageError(" be specified");
UsageError("");
UsageError(" --out-public=<filename>: output file for a list of all public APIs");
UsageError(" --out-private=<filename>: output file for a list of all private APIs");
UsageError("");
exit(EXIT_FAILURE);
}
template<typename E>
static bool Contains(const std::vector<E>& vec, const E& elem) {
return std::find(vec.begin(), vec.end(), elem) != vec.end();
}
class DexClass : public ClassAccessor {
public:
explicit DexClass(const ClassAccessor& accessor) : ClassAccessor(accessor) {}
const uint8_t* GetData() const { return dex_file_.GetClassData(GetClassDef()); }
const dex::TypeIndex GetSuperclassIndex() const { return GetClassDef().superclass_idx_; }
bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); }
std::string GetSuperclassDescriptor() const {
return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : "";
}
std::set<std::string> GetInterfaceDescriptors() const {
std::set<std::string> list;
const DexFile::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef());
for (uint32_t i = 0; ifaces != nullptr && i < ifaces->Size(); ++i) {
list.insert(dex_file_.StringByTypeIdx(ifaces->GetTypeItem(i).type_idx_));
}
return list;
}
inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
inline bool Equals(const DexClass& other) const {
bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
if (equals) {
// TODO(dbrazdil): Check that methods/fields match as well once b/111116543 is fixed.
CHECK_EQ(GetAccessFlags(), other.GetAccessFlags());
CHECK_EQ(GetSuperclassDescriptor(), other.GetSuperclassDescriptor());
CHECK(GetInterfaceDescriptors() == other.GetInterfaceDescriptors());
}
return equals;
}
private:
uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; }
bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
};
class DexMember {
public:
DexMember(const DexClass& klass, const ClassAccessor::Field& item)
: klass_(klass), item_(item), is_method_(false) {
DCHECK_EQ(GetFieldId().class_idx_, klass.GetClassIdx());
}
DexMember(const DexClass& klass, const ClassAccessor::Method& item)
: klass_(klass), item_(item), is_method_(true) {
DCHECK_EQ(GetMethodId().class_idx_, klass.GetClassIdx());
}
inline const DexClass& GetDeclaringClass() const { return klass_; }
// Sets hidden bits in access flags and writes them back into the DEX in memory.
// Note that this will not update the cached data of the class accessor
// until it iterates over this item again and therefore will fail a CHECK if
// it is called multiple times on the same DexMember.
void SetHidden(HiddenApiAccessFlags::ApiList value) const {
const uint32_t old_flags = item_.GetRawAccessFlags();
const uint32_t new_flags = HiddenApiAccessFlags::EncodeForDex(old_flags, value);
CHECK_EQ(UnsignedLeb128Size(new_flags), UnsignedLeb128Size(old_flags));
// Locate the LEB128-encoded access flags in class data.
// `ptr` initially points to the next ClassData item. We iterate backwards
// until we hit the terminating byte of the previous Leb128 value.
const uint8_t* ptr = item_.GetDataPointer();
if (IsMethod()) {
ptr = ReverseSearchUnsignedLeb128(ptr);
DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), GetMethod().GetCodeItemOffset());
}
ptr = ReverseSearchUnsignedLeb128(ptr);
DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), old_flags);
// Overwrite the access flags.
UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags);
}
inline bool IsMethod() const { return is_method_; }
inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); }
inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); }
inline bool IsPublicOrProtected() const {
return HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected);
}
// Constructs a string with a unique signature of this class member.
std::string GetApiEntry() const {
std::stringstream ss;
ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":")
<< GetSignature();
return ss.str();
}
inline bool operator==(const DexMember& other) const {
// These need to match if they should resolve to one another.
bool equals = IsMethod() == other.IsMethod() &&
GetName() == other.GetName() &&
GetSignature() == other.GetSignature();
// Sanity checks if they do match.
if (equals) {
CHECK_EQ(IsVirtualMethod(), other.IsVirtualMethod());
}
return equals;
}
private:
inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); }
inline uint32_t HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
inline std::string GetName() const {
return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId())
: item_.GetDexFile().GetFieldName(GetFieldId());
}
inline std::string GetSignature() const {
return IsMethod() ? item_.GetDexFile().GetMethodSignature(GetMethodId()).ToString()
: item_.GetDexFile().GetFieldTypeDescriptor(GetFieldId());
}
inline const ClassAccessor::Method& GetMethod() const {
DCHECK(IsMethod());
return down_cast<const ClassAccessor::Method&>(item_);
}
inline const DexFile::MethodId& GetMethodId() const {
DCHECK(IsMethod());
return item_.GetDexFile().GetMethodId(item_.GetIndex());
}
inline const DexFile::FieldId& GetFieldId() const {
DCHECK(!IsMethod());
return item_.GetDexFile().GetFieldId(item_.GetIndex());
}
const DexClass& klass_;
const ClassAccessor::BaseItem& item_;
const bool is_method_;
};
class ClassPath FINAL {
public:
ClassPath(const std::vector<std::string>& dex_paths, bool open_writable) {
OpenDexFiles(dex_paths, open_writable);
}
template<typename Fn>
void ForEachDexClass(Fn fn) {
for (auto& dex_file : dex_files_) {
for (ClassAccessor accessor : dex_file->GetClasses()) {
fn(DexClass(accessor));
}
}
}
template<typename Fn>
void ForEachDexMember(Fn fn) {
ForEachDexClass([&fn](const DexClass& klass) {
for (const ClassAccessor::Field& field : klass.GetFields()) {
fn(DexMember(klass, field));
}
for (const ClassAccessor::Method& method : klass.GetMethods()) {
fn(DexMember(klass, method));
}
});
}
void UpdateDexChecksums() {
for (auto& dex_file : dex_files_) {
// Obtain a writeable pointer to the dex header.
DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
// Recalculate checksum and overwrite the value in the header.
header->checksum_ = dex_file->CalculateChecksum();
}
}
private:
void OpenDexFiles(const std::vector<std::string>& dex_paths, bool open_writable) {
ArtDexFileLoader dex_loader;
std::string error_msg;
if (open_writable) {
for (const std::string& filename : dex_paths) {
File fd(filename.c_str(), O_RDWR, /* check_usage */ false);
CHECK_NE(fd.Fd(), -1) << "Unable to open file '" << filename << "': " << strerror(errno);
// Memory-map the dex file with MAP_SHARED flag so that changes in memory
// propagate to the underlying file. We run dex file verification as if
// the dex file was not in boot claass path to check basic assumptions,
// such as that at most one of public/private/protected flag is set.
// We do those checks here and skip them when loading the processed file
// into boot class path.
std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
/* location */ filename,
/* verify */ true,
/* verify_checksum */ true,
/* mmap_shared */ true,
&error_msg));
CHECK(dex_file.get() != nullptr) << "Open failed for '" << filename << "' " << error_msg;
CHECK(dex_file->IsStandardDexFile()) << "Expected a standard dex file '" << filename << "'";
CHECK(dex_file->EnableWrite())
<< "Failed to enable write permission for '" << filename << "'";
dex_files_.push_back(std::move(dex_file));
}
} else {
for (const std::string& filename : dex_paths) {
bool success = dex_loader.Open(filename.c_str(),
/* location */ filename,
/* verify */ true,
/* verify_checksum */ true,
&error_msg,
&dex_files_);
CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
}
}
}
// Opened dex files. Note that these are opened as `const` but may be written into.
std::vector<std::unique_ptr<const DexFile>> dex_files_;
};
class HierarchyClass FINAL {
public:
HierarchyClass() {}
void AddDexClass(const DexClass& klass) {
CHECK(dex_classes_.empty() || klass.Equals(dex_classes_.front()));
dex_classes_.push_back(klass);
}
void AddExtends(HierarchyClass& parent) {
CHECK(!Contains(extends_, &parent));
CHECK(!Contains(parent.extended_by_, this));
extends_.push_back(&parent);
parent.extended_by_.push_back(this);
}
const DexClass& GetOneDexClass() const {
CHECK(!dex_classes_.empty());
return dex_classes_.front();
}
// See comment on Hierarchy::ForEachResolvableMember.
template<typename Fn>
bool ForEachResolvableMember(const DexMember& other, Fn fn) {
return ForEachResolvableMember_Impl(other, fn) != ResolutionResult::kNotFound;
}
// Returns true if this class contains at least one member matching `other`.
bool HasMatchingMember(const DexMember& other) {
return ForEachMatchingMember(
other, [](const DexMember&) { return true; }) != ResolutionResult::kNotFound;
}
// Recursively iterates over all subclasses of this class and invokes `fn`
// on each one. If `fn` returns false for a particular subclass, exploring its
// subclasses is skipped.
template<typename Fn>
void ForEachSubClass(Fn fn) {
for (HierarchyClass* subclass : extended_by_) {
if (fn(subclass)) {
subclass->ForEachSubClass(fn);
}
}
}
private:
// Result of resolution which takes into account whether the member was found
// for the first time or not. This is just a performance optimization to prevent
// re-visiting previously visited members.
// Note that order matters. When accumulating results, we always pick the maximum.
enum class ResolutionResult {
kNotFound,
kFoundOld,
kFoundNew,
};
inline ResolutionResult Accumulate(ResolutionResult a, ResolutionResult b) {
return static_cast<ResolutionResult>(
std::max(static_cast<unsigned>(a), static_cast<unsigned>(b)));
}
template<typename Fn>
ResolutionResult ForEachResolvableMember_Impl(const DexMember& other, Fn fn) {
// First try to find a member matching `other` in this class.
ResolutionResult foundInClass = ForEachMatchingMember(other, fn);
switch (foundInClass) {
case ResolutionResult::kFoundOld:
// A matching member was found and previously explored. All subclasses
// must have been explored too.
break;
case ResolutionResult::kFoundNew:
// A matching member was found and this was the first time it was visited.
// If it is a virtual method, visit all methods overriding/implementing it too.
if (other.IsVirtualMethod()) {
for (HierarchyClass* subclass : extended_by_) {
subclass->ForEachOverridingMember(other, fn);
}
}
break;
case ResolutionResult::kNotFound:
// A matching member was not found in this class. Explore the superclasses
// and implemented interfaces.
for (HierarchyClass* superclass : extends_) {
foundInClass = Accumulate(
foundInClass, superclass->ForEachResolvableMember_Impl(other, fn));
}
break;
}
return foundInClass;
}
template<typename Fn>
ResolutionResult ForEachMatchingMember(const DexMember& other, Fn fn) {
ResolutionResult found = ResolutionResult::kNotFound;
auto compare_member = [&](const DexMember& member) {
if (member == other) {
found = Accumulate(found, fn(member) ? ResolutionResult::kFoundNew
: ResolutionResult::kFoundOld);
}
};
for (const DexClass& dex_class : dex_classes_) {
for (const ClassAccessor::Field& field : dex_class.GetFields()) {
compare_member(DexMember(dex_class, field));
}
for (const ClassAccessor::Method& method : dex_class.GetMethods()) {
compare_member(DexMember(dex_class, method));
}
}
return found;
}
template<typename Fn>
void ForEachOverridingMember(const DexMember& other, Fn fn) {
CHECK(other.IsVirtualMethod());
ResolutionResult found = ForEachMatchingMember(other, fn);
if (found == ResolutionResult::kFoundOld) {
// No need to explore further.
return;
} else {
for (HierarchyClass* subclass : extended_by_) {
subclass->ForEachOverridingMember(other, fn);
}
}
}
// DexClass entries of this class found across all the provided dex files.
std::vector<DexClass> dex_classes_;
// Classes which this class inherits, or interfaces which it implements.
std::vector<HierarchyClass*> extends_;
// Classes which inherit from this class.
std::vector<HierarchyClass*> extended_by_;
};
class Hierarchy FINAL {
public:
explicit Hierarchy(ClassPath& classpath) : classpath_(classpath) {
BuildClassHierarchy();
}
// Perform an operation for each member of the hierarchy which could potentially
// be the result of method/field resolution of `other`.
// The function `fn` should accept a DexMember reference and return true if
// the member was changed. This drives a performance optimization which only
// visits overriding members the first time the overridden member is visited.
// Returns true if at least one resolvable member was found.
template<typename Fn>
bool ForEachResolvableMember(const DexMember& other, Fn fn) {
HierarchyClass* klass = FindClass(other.GetDeclaringClass().GetDescriptor());
return (klass != nullptr) && klass->ForEachResolvableMember(other, fn);
}
// Returns true if `member`, which belongs to this classpath, is visible to
// code in child class loaders.
bool IsMemberVisible(const DexMember& member) {
if (!member.IsPublicOrProtected()) {
// Member is private or package-private. Cannot be visible.
return false;
} else if (member.GetDeclaringClass().IsPublic()) {
// Member is public or protected, and class is public. It must be visible.
return true;
} else if (member.IsConstructor()) {
// Member is public or protected constructor and class is not public.
// Must be hidden because it cannot be implicitly exposed by a subclass.
return false;
} else {
// Member is public or protected method, but class is not public. Check if
// it is exposed through a public subclass.
// Example code (`foo` exposed by ClassB):
// class ClassA { public void foo() { ... } }
// public class ClassB extends ClassA {}
HierarchyClass* klass = FindClass(member.GetDeclaringClass().GetDescriptor());
CHECK(klass != nullptr);
bool visible = false;
klass->ForEachSubClass([&visible, &member](HierarchyClass* subclass) {
if (subclass->HasMatchingMember(member)) {
// There is a member which matches `member` in `subclass`, either
// a virtual method overriding `member` or a field overshadowing
// `member`. In either case, `member` remains hidden.
CHECK(member.IsVirtualMethod() || !member.IsMethod());
return false; // do not explore deeper
} else if (subclass->GetOneDexClass().IsPublic()) {
// `subclass` inherits and exposes `member`.
visible = true;
return false; // do not explore deeper
} else {
// `subclass` inherits `member` but does not expose it.
return true; // explore deeper
}
});
return visible;
}
}
private:
HierarchyClass* FindClass(const std::string& descriptor) {
auto it = classes_.find(descriptor);
if (it == classes_.end()) {
return nullptr;
} else {
return &it->second;
}
}
void BuildClassHierarchy() {
// Create one HierarchyClass entry in `classes_` per class descriptor
// and add all DexClass objects with the same descriptor to that entry.
classpath_.ForEachDexClass([this](const DexClass& klass) {
classes_[klass.GetDescriptor()].AddDexClass(klass);
});
// Connect each HierarchyClass to its successors and predecessors.
for (auto& entry : classes_) {
HierarchyClass& klass = entry.second;
const DexClass& dex_klass = klass.GetOneDexClass();
if (!dex_klass.HasSuperclass()) {
CHECK(dex_klass.GetInterfaceDescriptors().empty())
<< "java/lang/Object should not implement any interfaces";
continue;
}
HierarchyClass* superclass = FindClass(dex_klass.GetSuperclassDescriptor());
CHECK(superclass != nullptr);
klass.AddExtends(*superclass);
for (const std::string& iface_desc : dex_klass.GetInterfaceDescriptors()) {
HierarchyClass* iface = FindClass(iface_desc);
CHECK(iface != nullptr);
klass.AddExtends(*iface);
}
}
}
ClassPath& classpath_;
std::map<std::string, HierarchyClass> classes_;
};
class HiddenApi FINAL {
public:
HiddenApi() {}
void Run(int argc, char** argv) {
switch (ParseArgs(argc, argv)) {
case Command::kEncode:
EncodeAccessFlags();
break;
case Command::kList:
ListApi();
break;
}
}
private:
enum class Command {
kEncode,
kList,
};
Command ParseArgs(int argc, char** argv) {
// Skip over the binary's path.
argv++;
argc--;
if (argc > 0) {
const StringPiece command(argv[0]);
if (command == "encode") {
for (int i = 1; i < argc; ++i) {
const StringPiece option(argv[i]);
if (option.starts_with("--dex=")) {
boot_dex_paths_.push_back(option.substr(strlen("--dex=")).ToString());
} else if (option.starts_with("--light-greylist=")) {
light_greylist_path_ = option.substr(strlen("--light-greylist=")).ToString();
} else if (option.starts_with("--dark-greylist=")) {
dark_greylist_path_ = option.substr(strlen("--dark-greylist=")).ToString();
} else if (option.starts_with("--blacklist=")) {
blacklist_path_ = option.substr(strlen("--blacklist=")).ToString();
} else {
Usage("Unknown argument '%s'", option.data());
}
}
return Command::kEncode;
} else if (command == "list") {
for (int i = 1; i < argc; ++i) {
const StringPiece option(argv[i]);
if (option.starts_with("--boot-dex=")) {
boot_dex_paths_.push_back(option.substr(strlen("--boot-dex=")).ToString());
} else if (option.starts_with("--stub-classpath=")) {
stub_classpaths_.push_back(android::base::Split(
option.substr(strlen("--stub-classpath=")).ToString(), ":"));
} else if (option.starts_with("--out-public=")) {
out_public_path_ = option.substr(strlen("--out-public=")).ToString();
} else if (option.starts_with("--out-private=")) {
out_private_path_ = option.substr(strlen("--out-private=")).ToString();
} else {
Usage("Unknown argument '%s'", option.data());
}
}
return Command::kList;
} else {
Usage("Unknown command '%s'", command.data());
}
} else {
Usage("No command specified");
}
}
void EncodeAccessFlags() {
if (boot_dex_paths_.empty()) {
Usage("No boot DEX files specified");
}
// Load dex signatures.
std::map<std::string, HiddenApiAccessFlags::ApiList> api_list;
OpenApiFile(light_greylist_path_, api_list, HiddenApiAccessFlags::kLightGreylist);
OpenApiFile(dark_greylist_path_, api_list, HiddenApiAccessFlags::kDarkGreylist);
OpenApiFile(blacklist_path_, api_list, HiddenApiAccessFlags::kBlacklist);
// Open all dex files.
ClassPath boot_classpath(boot_dex_paths_, /* open_writable */ true);
// Set access flags of all members.
boot_classpath.ForEachDexMember([&api_list](const DexMember& boot_member) {
auto it = api_list.find(boot_member.GetApiEntry());
boot_member.SetHidden(it == api_list.end() ? HiddenApiAccessFlags::kWhitelist : it->second);
});
boot_classpath.UpdateDexChecksums();
}
void OpenApiFile(const std::string& path,
std::map<std::string, HiddenApiAccessFlags::ApiList>& api_list,
HiddenApiAccessFlags::ApiList membership) {
if (path.empty()) {
return;
}
std::ifstream api_file(path, std::ifstream::in);
CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno);
for (std::string line; std::getline(api_file, line);) {
CHECK(api_list.find(line) == api_list.end())
<< "Duplicate entry: " << line << " (" << api_list[line] << " and " << membership << ")";
api_list.emplace(line, membership);
}
api_file.close();
}
void ListApi() {
if (boot_dex_paths_.empty()) {
Usage("No boot DEX files specified");
} else if (stub_classpaths_.empty()) {
Usage("No stub DEX files specified");
} else if (out_public_path_.empty()) {
Usage("No public API output path specified");
} else if (out_private_path_.empty()) {
Usage("No private API output path specified");
}
// Complete list of boot class path members. The associated boolean states
// whether it is public (true) or private (false).
std::map<std::string, bool> boot_members;
// Deduplicate errors before printing them.
std::set<std::string> unresolved;
// Open all dex files.
ClassPath boot_classpath(boot_dex_paths_, /* open_writable */ false);
Hierarchy boot_hierarchy(boot_classpath);
// Mark all boot dex members private.
boot_classpath.ForEachDexMember([&boot_members](const DexMember& boot_member) {
boot_members[boot_member.GetApiEntry()] = false;
});
// Resolve each SDK dex member against the framework and mark it white.
for (const std::vector<std::string>& stub_classpath_dex : stub_classpaths_) {
ClassPath stub_classpath(stub_classpath_dex, /* open_writable */ false);
Hierarchy stub_hierarchy(stub_classpath);
stub_classpath.ForEachDexMember(
[&stub_hierarchy, &boot_hierarchy, &boot_members, &unresolved](
const DexMember& stub_member) {
if (!stub_hierarchy.IsMemberVisible(stub_member)) {
// Typically fake constructors and inner-class `this` fields.
return;
}
bool resolved = boot_hierarchy.ForEachResolvableMember(
stub_member,
[&boot_members](const DexMember& boot_member) {
std::string entry = boot_member.GetApiEntry();
auto it = boot_members.find(entry);
CHECK(it != boot_members.end());
if (it->second) {
return false; // has been marked before
} else {
it->second = true;
return true; // marked for the first time
}
});
if (!resolved) {
unresolved.insert(stub_member.GetApiEntry());
}
});
}
// Print errors.
for (const std::string& str : unresolved) {
LOG(WARNING) << "unresolved: " << str;
}
// Write into public/private API files.
std::ofstream file_public(out_public_path_.c_str());
std::ofstream file_private(out_private_path_.c_str());
for (const std::pair<std::string, bool> entry : boot_members) {
if (entry.second) {
file_public << entry.first << std::endl;
} else {
file_private << entry.first << std::endl;
}
}
file_public.close();
file_private.close();
}
// Paths to DEX files which should be processed.
std::vector<std::string> boot_dex_paths_;
// Set of public API stub classpaths. Each classpath is formed by a list
// of DEX/APK files in the order they appear on the classpath.
std::vector<std::vector<std::string>> stub_classpaths_;
// Paths to text files which contain the lists of API members.
std::string light_greylist_path_;
std::string dark_greylist_path_;
std::string blacklist_path_;
// Paths to text files to which we will output list of all API members.
std::string out_public_path_;
std::string out_private_path_;
};
} // namespace art
int main(int argc, char** argv) {
android::base::InitLogging(argv);
art::MemMap::Init();
art::HiddenApi().Run(argc, argv);
return EXIT_SUCCESS;
}
|