blob: decbc0179cb4d7cf8860a029c30c76032e921fd5 [file] [log] [blame]
David Brazdil2b9c35b2018-01-12 15:44:43 +00001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <fstream>
18#include <iostream>
David Brazdil0b6de0c2018-06-28 11:56:41 +010019#include <map>
20#include <set>
David Brazdil2b9c35b2018-01-12 15:44:43 +000021
22#include "android-base/stringprintf.h"
23#include "android-base/strings.h"
24
David Brazdil20c765f2018-10-27 21:45:15 +000025#include "base/bit_utils.h"
David Brazdildcfa89b2018-10-31 11:04:10 +000026#include "base/hiddenapi_flags.h"
David Sehr79e26072018-04-06 17:58:50 -070027#include "base/mem_map.h"
David Sehrc431b9d2018-03-02 12:01:51 -080028#include "base/os.h"
David Brazdildcfa89b2018-10-31 11:04:10 +000029#include "base/stl_util.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000030#include "base/unix_file/fd_file.h"
31#include "dex/art_dex_file_loader.h"
Mathieu Chartier396dc082018-08-06 12:29:57 -070032#include "dex/class_accessor-inl.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000033#include "dex/dex_file-inl.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000034
35namespace art {
36
37static int original_argc;
38static char** original_argv;
39
40static std::string CommandLine() {
41 std::vector<std::string> command;
Andreas Gampe2a487eb2018-11-19 11:41:22 -080042 command.reserve(original_argc);
David Brazdil2b9c35b2018-01-12 15:44:43 +000043 for (int i = 0; i < original_argc; ++i) {
44 command.push_back(original_argv[i]);
45 }
46 return android::base::Join(command, ' ');
47}
48
49static void UsageErrorV(const char* fmt, va_list ap) {
50 std::string error;
51 android::base::StringAppendV(&error, fmt, ap);
52 LOG(ERROR) << error;
53}
54
55static void UsageError(const char* fmt, ...) {
56 va_list ap;
57 va_start(ap, fmt);
58 UsageErrorV(fmt, ap);
59 va_end(ap);
60}
61
62NO_RETURN static void Usage(const char* fmt, ...) {
63 va_list ap;
64 va_start(ap, fmt);
65 UsageErrorV(fmt, ap);
66 va_end(ap);
67
68 UsageError("Command: %s", CommandLine().c_str());
David Brazdil003e64b2018-06-27 13:20:52 +010069 UsageError("Usage: hiddenapi [command_name] [options]...");
David Brazdil2b9c35b2018-01-12 15:44:43 +000070 UsageError("");
David Brazdil003e64b2018-06-27 13:20:52 +010071 UsageError(" Command \"encode\": encode API list membership in boot dex files");
David Brazdil20c765f2018-10-27 21:45:15 +000072 UsageError(" --input-dex=<filename>: dex file which belongs to boot class path");
73 UsageError(" --output-dex=<filename>: file to write encoded dex into");
74 UsageError(" input and output dex files are paired in order of appearance");
David Brazdil2b9c35b2018-01-12 15:44:43 +000075 UsageError("");
David Brazdil91690d32018-11-04 18:07:23 +000076 UsageError(" --api-flags=<filename>:");
77 UsageError(" CSV file with signatures of methods/fields and their respective flags");
78 UsageError("");
79 UsageError(" --no-force-assign-all:");
80 UsageError(" Disable check that all dex entries have been assigned a flag");
David Brazdil2b9c35b2018-01-12 15:44:43 +000081 UsageError("");
David Brazdil0b6de0c2018-06-28 11:56:41 +010082 UsageError(" Command \"list\": dump lists of public and private API");
83 UsageError(" --boot-dex=<filename>: dex file which belongs to boot class path");
David Brazdil345c0ed2018-08-03 10:26:44 +010084 UsageError(" --stub-classpath=<filenames>: colon-separated list of dex/apk files");
85 UsageError(" which form API stubs of boot class path. Multiple classpaths can");
86 UsageError(" be specified");
David Brazdil0b6de0c2018-06-28 11:56:41 +010087 UsageError("");
88 UsageError(" --out-public=<filename>: output file for a list of all public APIs");
89 UsageError(" --out-private=<filename>: output file for a list of all private APIs");
90 UsageError("");
David Brazdil2b9c35b2018-01-12 15:44:43 +000091
92 exit(EXIT_FAILURE);
93}
94
David Brazdil0b6de0c2018-06-28 11:56:41 +010095template<typename E>
96static bool Contains(const std::vector<E>& vec, const E& elem) {
97 return std::find(vec.begin(), vec.end(), elem) != vec.end();
98}
99
Mathieu Chartier396dc082018-08-06 12:29:57 -0700100class DexClass : public ClassAccessor {
David Brazdil2b9c35b2018-01-12 15:44:43 +0000101 public:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700102 explicit DexClass(const ClassAccessor& accessor) : ClassAccessor(accessor) {}
David Brazdil2b9c35b2018-01-12 15:44:43 +0000103
Mathieu Chartier396dc082018-08-06 12:29:57 -0700104 const uint8_t* GetData() const { return dex_file_.GetClassData(GetClassDef()); }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000105
Mathieu Chartier396dc082018-08-06 12:29:57 -0700106 const dex::TypeIndex GetSuperclassIndex() const { return GetClassDef().superclass_idx_; }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100107
108 bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); }
109
David Brazdil0b6de0c2018-06-28 11:56:41 +0100110 std::string GetSuperclassDescriptor() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700111 return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : "";
David Brazdil0b6de0c2018-06-28 11:56:41 +0100112 }
113
114 std::set<std::string> GetInterfaceDescriptors() const {
115 std::set<std::string> list;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700116 const DexFile::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef());
David Brazdil0b6de0c2018-06-28 11:56:41 +0100117 for (uint32_t i = 0; ifaces != nullptr && i < ifaces->Size(); ++i) {
118 list.insert(dex_file_.StringByTypeIdx(ifaces->GetTypeItem(i).type_idx_));
119 }
120 return list;
121 }
122
David Brazdil345c0ed2018-08-03 10:26:44 +0100123 inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100124
125 inline bool Equals(const DexClass& other) const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700126 bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100127 if (equals) {
128 // TODO(dbrazdil): Check that methods/fields match as well once b/111116543 is fixed.
129 CHECK_EQ(GetAccessFlags(), other.GetAccessFlags());
130 CHECK_EQ(GetSuperclassDescriptor(), other.GetSuperclassDescriptor());
131 CHECK(GetInterfaceDescriptors() == other.GetInterfaceDescriptors());
132 }
133 return equals;
134 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000135
136 private:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700137 uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100138 bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000139};
140
141class DexMember {
142 public:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700143 DexMember(const DexClass& klass, const ClassAccessor::Field& item)
144 : klass_(klass), item_(item), is_method_(false) {
145 DCHECK_EQ(GetFieldId().class_idx_, klass.GetClassIdx());
146 }
147
148 DexMember(const DexClass& klass, const ClassAccessor::Method& item)
149 : klass_(klass), item_(item), is_method_(true) {
150 DCHECK_EQ(GetMethodId().class_idx_, klass.GetClassIdx());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000151 }
152
David Brazdil0b6de0c2018-06-28 11:56:41 +0100153 inline const DexClass& GetDeclaringClass() const { return klass_; }
154
Mathieu Chartier396dc082018-08-06 12:29:57 -0700155 inline bool IsMethod() const { return is_method_; }
156 inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); }
David Brazdil345c0ed2018-08-03 10:26:44 +0100157 inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100158
David Brazdil345c0ed2018-08-03 10:26:44 +0100159 inline bool IsPublicOrProtected() const {
160 return HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100161 }
162
David Brazdil2b9c35b2018-01-12 15:44:43 +0000163 // Constructs a string with a unique signature of this class member.
164 std::string GetApiEntry() const {
165 std::stringstream ss;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700166 ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":")
167 << GetSignature();
David Brazdil2b9c35b2018-01-12 15:44:43 +0000168 return ss.str();
169 }
170
Mathieu Chartier396dc082018-08-06 12:29:57 -0700171 inline bool operator==(const DexMember& other) const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100172 // These need to match if they should resolve to one another.
173 bool equals = IsMethod() == other.IsMethod() &&
174 GetName() == other.GetName() &&
175 GetSignature() == other.GetSignature();
176
177 // Sanity checks if they do match.
178 if (equals) {
179 CHECK_EQ(IsVirtualMethod(), other.IsVirtualMethod());
180 }
181
182 return equals;
183 }
184
David Brazdil2b9c35b2018-01-12 15:44:43 +0000185 private:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700186 inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100187 inline uint32_t HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
188
189 inline std::string GetName() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700190 return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId())
191 : item_.GetDexFile().GetFieldName(GetFieldId());
David Brazdil0b6de0c2018-06-28 11:56:41 +0100192 }
193
194 inline std::string GetSignature() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700195 return IsMethod() ? item_.GetDexFile().GetMethodSignature(GetMethodId()).ToString()
196 : item_.GetDexFile().GetFieldTypeDescriptor(GetFieldId());
197 }
198
199 inline const ClassAccessor::Method& GetMethod() const {
200 DCHECK(IsMethod());
201 return down_cast<const ClassAccessor::Method&>(item_);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100202 }
203
David Brazdil2b9c35b2018-01-12 15:44:43 +0000204 inline const DexFile::MethodId& GetMethodId() const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100205 DCHECK(IsMethod());
Mathieu Chartier396dc082018-08-06 12:29:57 -0700206 return item_.GetDexFile().GetMethodId(item_.GetIndex());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000207 }
208
209 inline const DexFile::FieldId& GetFieldId() const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100210 DCHECK(!IsMethod());
Mathieu Chartier396dc082018-08-06 12:29:57 -0700211 return item_.GetDexFile().GetFieldId(item_.GetIndex());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000212 }
213
David Brazdil2b9c35b2018-01-12 15:44:43 +0000214 const DexClass& klass_;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700215 const ClassAccessor::BaseItem& item_;
216 const bool is_method_;
David Brazdil2b9c35b2018-01-12 15:44:43 +0000217};
218
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100219class ClassPath final {
David Brazdil2b9c35b2018-01-12 15:44:43 +0000220 public:
David Brazdil0b6de0c2018-06-28 11:56:41 +0100221 ClassPath(const std::vector<std::string>& dex_paths, bool open_writable) {
222 OpenDexFiles(dex_paths, open_writable);
223 }
224
225 template<typename Fn>
226 void ForEachDexClass(Fn fn) {
227 for (auto& dex_file : dex_files_) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700228 for (ClassAccessor accessor : dex_file->GetClasses()) {
229 fn(DexClass(accessor));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100230 }
231 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000232 }
233
David Brazdil003e64b2018-06-27 13:20:52 +0100234 template<typename Fn>
235 void ForEachDexMember(Fn fn) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700236 ForEachDexClass([&fn](const DexClass& klass) {
237 for (const ClassAccessor::Field& field : klass.GetFields()) {
238 fn(DexMember(klass, field));
239 }
240 for (const ClassAccessor::Method& method : klass.GetMethods()) {
241 fn(DexMember(klass, method));
David Brazdil2b9c35b2018-01-12 15:44:43 +0000242 }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100243 });
David Brazdil2b9c35b2018-01-12 15:44:43 +0000244 }
245
David Brazdil20c765f2018-10-27 21:45:15 +0000246 std::vector<const DexFile*> GetDexFiles() const {
247 return MakeNonOwningPointerVector(dex_files_);
248 }
249
David Brazdil2b9c35b2018-01-12 15:44:43 +0000250 void UpdateDexChecksums() {
251 for (auto& dex_file : dex_files_) {
252 // Obtain a writeable pointer to the dex header.
253 DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
254 // Recalculate checksum and overwrite the value in the header.
255 header->checksum_ = dex_file->CalculateChecksum();
256 }
257 }
258
David Brazdil003e64b2018-06-27 13:20:52 +0100259 private:
David Brazdil0b6de0c2018-06-28 11:56:41 +0100260 void OpenDexFiles(const std::vector<std::string>& dex_paths, bool open_writable) {
David Brazdil003e64b2018-06-27 13:20:52 +0100261 ArtDexFileLoader dex_loader;
262 std::string error_msg;
David Brazdil003e64b2018-06-27 13:20:52 +0100263
David Brazdil0b6de0c2018-06-28 11:56:41 +0100264 if (open_writable) {
265 for (const std::string& filename : dex_paths) {
Andreas Gampe9b031f72018-10-04 11:03:34 -0700266 File fd(filename.c_str(), O_RDWR, /* check_usage= */ false);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100267 CHECK_NE(fd.Fd(), -1) << "Unable to open file '" << filename << "': " << strerror(errno);
268
269 // Memory-map the dex file with MAP_SHARED flag so that changes in memory
270 // propagate to the underlying file. We run dex file verification as if
271 // the dex file was not in boot claass path to check basic assumptions,
272 // such as that at most one of public/private/protected flag is set.
273 // We do those checks here and skip them when loading the processed file
274 // into boot class path.
275 std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
Andreas Gampe9b031f72018-10-04 11:03:34 -0700276 /* location= */ filename,
277 /* verify= */ true,
278 /* verify_checksum= */ true,
279 /* mmap_shared= */ true,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100280 &error_msg));
281 CHECK(dex_file.get() != nullptr) << "Open failed for '" << filename << "' " << error_msg;
282 CHECK(dex_file->IsStandardDexFile()) << "Expected a standard dex file '" << filename << "'";
283 CHECK(dex_file->EnableWrite())
284 << "Failed to enable write permission for '" << filename << "'";
285 dex_files_.push_back(std::move(dex_file));
286 }
287 } else {
288 for (const std::string& filename : dex_paths) {
289 bool success = dex_loader.Open(filename.c_str(),
Andreas Gampe9b031f72018-10-04 11:03:34 -0700290 /* location= */ filename,
291 /* verify= */ true,
292 /* verify_checksum= */ true,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100293 &error_msg,
294 &dex_files_);
295 CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
296 }
David Brazdil003e64b2018-06-27 13:20:52 +0100297 }
298 }
299
David Brazdil0b6de0c2018-06-28 11:56:41 +0100300 // Opened dex files. Note that these are opened as `const` but may be written into.
David Brazdil003e64b2018-06-27 13:20:52 +0100301 std::vector<std::unique_ptr<const DexFile>> dex_files_;
302};
303
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100304class HierarchyClass final {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100305 public:
306 HierarchyClass() {}
307
308 void AddDexClass(const DexClass& klass) {
309 CHECK(dex_classes_.empty() || klass.Equals(dex_classes_.front()));
310 dex_classes_.push_back(klass);
311 }
312
313 void AddExtends(HierarchyClass& parent) {
314 CHECK(!Contains(extends_, &parent));
315 CHECK(!Contains(parent.extended_by_, this));
316 extends_.push_back(&parent);
317 parent.extended_by_.push_back(this);
318 }
319
320 const DexClass& GetOneDexClass() const {
321 CHECK(!dex_classes_.empty());
322 return dex_classes_.front();
323 }
324
325 // See comment on Hierarchy::ForEachResolvableMember.
326 template<typename Fn>
327 bool ForEachResolvableMember(const DexMember& other, Fn fn) {
328 return ForEachResolvableMember_Impl(other, fn) != ResolutionResult::kNotFound;
329 }
330
David Brazdil345c0ed2018-08-03 10:26:44 +0100331 // Returns true if this class contains at least one member matching `other`.
332 bool HasMatchingMember(const DexMember& other) {
333 return ForEachMatchingMember(
334 other, [](const DexMember&) { return true; }) != ResolutionResult::kNotFound;
335 }
336
337 // Recursively iterates over all subclasses of this class and invokes `fn`
338 // on each one. If `fn` returns false for a particular subclass, exploring its
339 // subclasses is skipped.
340 template<typename Fn>
341 void ForEachSubClass(Fn fn) {
342 for (HierarchyClass* subclass : extended_by_) {
343 if (fn(subclass)) {
344 subclass->ForEachSubClass(fn);
345 }
346 }
347 }
348
David Brazdil0b6de0c2018-06-28 11:56:41 +0100349 private:
350 // Result of resolution which takes into account whether the member was found
351 // for the first time or not. This is just a performance optimization to prevent
352 // re-visiting previously visited members.
353 // Note that order matters. When accumulating results, we always pick the maximum.
354 enum class ResolutionResult {
355 kNotFound,
356 kFoundOld,
357 kFoundNew,
358 };
359
360 inline ResolutionResult Accumulate(ResolutionResult a, ResolutionResult b) {
361 return static_cast<ResolutionResult>(
362 std::max(static_cast<unsigned>(a), static_cast<unsigned>(b)));
363 }
364
365 template<typename Fn>
366 ResolutionResult ForEachResolvableMember_Impl(const DexMember& other, Fn fn) {
367 // First try to find a member matching `other` in this class.
368 ResolutionResult foundInClass = ForEachMatchingMember(other, fn);
369
370 switch (foundInClass) {
371 case ResolutionResult::kFoundOld:
372 // A matching member was found and previously explored. All subclasses
373 // must have been explored too.
374 break;
375
376 case ResolutionResult::kFoundNew:
377 // A matching member was found and this was the first time it was visited.
378 // If it is a virtual method, visit all methods overriding/implementing it too.
379 if (other.IsVirtualMethod()) {
380 for (HierarchyClass* subclass : extended_by_) {
381 subclass->ForEachOverridingMember(other, fn);
382 }
383 }
384 break;
385
386 case ResolutionResult::kNotFound:
387 // A matching member was not found in this class. Explore the superclasses
388 // and implemented interfaces.
389 for (HierarchyClass* superclass : extends_) {
390 foundInClass = Accumulate(
391 foundInClass, superclass->ForEachResolvableMember_Impl(other, fn));
392 }
393 break;
394 }
395
396 return foundInClass;
397 }
398
399 template<typename Fn>
400 ResolutionResult ForEachMatchingMember(const DexMember& other, Fn fn) {
401 ResolutionResult found = ResolutionResult::kNotFound;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700402 auto compare_member = [&](const DexMember& member) {
403 if (member == other) {
404 found = Accumulate(found, fn(member) ? ResolutionResult::kFoundNew
405 : ResolutionResult::kFoundOld);
406 }
407 };
David Brazdil0b6de0c2018-06-28 11:56:41 +0100408 for (const DexClass& dex_class : dex_classes_) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700409 for (const ClassAccessor::Field& field : dex_class.GetFields()) {
410 compare_member(DexMember(dex_class, field));
411 }
412 for (const ClassAccessor::Method& method : dex_class.GetMethods()) {
413 compare_member(DexMember(dex_class, method));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100414 }
415 }
416 return found;
417 }
418
419 template<typename Fn>
420 void ForEachOverridingMember(const DexMember& other, Fn fn) {
421 CHECK(other.IsVirtualMethod());
422 ResolutionResult found = ForEachMatchingMember(other, fn);
423 if (found == ResolutionResult::kFoundOld) {
424 // No need to explore further.
425 return;
426 } else {
427 for (HierarchyClass* subclass : extended_by_) {
428 subclass->ForEachOverridingMember(other, fn);
429 }
430 }
431 }
432
433 // DexClass entries of this class found across all the provided dex files.
434 std::vector<DexClass> dex_classes_;
435
436 // Classes which this class inherits, or interfaces which it implements.
437 std::vector<HierarchyClass*> extends_;
438
439 // Classes which inherit from this class.
440 std::vector<HierarchyClass*> extended_by_;
441};
442
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100443class Hierarchy final {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100444 public:
David Brazdil345c0ed2018-08-03 10:26:44 +0100445 explicit Hierarchy(ClassPath& classpath) : classpath_(classpath) {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100446 BuildClassHierarchy();
447 }
448
449 // Perform an operation for each member of the hierarchy which could potentially
450 // be the result of method/field resolution of `other`.
451 // The function `fn` should accept a DexMember reference and return true if
452 // the member was changed. This drives a performance optimization which only
453 // visits overriding members the first time the overridden member is visited.
454 // Returns true if at least one resolvable member was found.
455 template<typename Fn>
456 bool ForEachResolvableMember(const DexMember& other, Fn fn) {
457 HierarchyClass* klass = FindClass(other.GetDeclaringClass().GetDescriptor());
458 return (klass != nullptr) && klass->ForEachResolvableMember(other, fn);
459 }
460
David Brazdil345c0ed2018-08-03 10:26:44 +0100461 // Returns true if `member`, which belongs to this classpath, is visible to
462 // code in child class loaders.
463 bool IsMemberVisible(const DexMember& member) {
464 if (!member.IsPublicOrProtected()) {
465 // Member is private or package-private. Cannot be visible.
466 return false;
467 } else if (member.GetDeclaringClass().IsPublic()) {
468 // Member is public or protected, and class is public. It must be visible.
469 return true;
470 } else if (member.IsConstructor()) {
471 // Member is public or protected constructor and class is not public.
472 // Must be hidden because it cannot be implicitly exposed by a subclass.
473 return false;
474 } else {
475 // Member is public or protected method, but class is not public. Check if
476 // it is exposed through a public subclass.
477 // Example code (`foo` exposed by ClassB):
478 // class ClassA { public void foo() { ... } }
479 // public class ClassB extends ClassA {}
480 HierarchyClass* klass = FindClass(member.GetDeclaringClass().GetDescriptor());
481 CHECK(klass != nullptr);
482 bool visible = false;
483 klass->ForEachSubClass([&visible, &member](HierarchyClass* subclass) {
484 if (subclass->HasMatchingMember(member)) {
485 // There is a member which matches `member` in `subclass`, either
486 // a virtual method overriding `member` or a field overshadowing
487 // `member`. In either case, `member` remains hidden.
488 CHECK(member.IsVirtualMethod() || !member.IsMethod());
489 return false; // do not explore deeper
490 } else if (subclass->GetOneDexClass().IsPublic()) {
491 // `subclass` inherits and exposes `member`.
492 visible = true;
493 return false; // do not explore deeper
494 } else {
495 // `subclass` inherits `member` but does not expose it.
496 return true; // explore deeper
497 }
498 });
499 return visible;
500 }
501 }
502
David Brazdil0b6de0c2018-06-28 11:56:41 +0100503 private:
504 HierarchyClass* FindClass(const std::string& descriptor) {
505 auto it = classes_.find(descriptor);
506 if (it == classes_.end()) {
507 return nullptr;
508 } else {
509 return &it->second;
510 }
511 }
512
513 void BuildClassHierarchy() {
514 // Create one HierarchyClass entry in `classes_` per class descriptor
515 // and add all DexClass objects with the same descriptor to that entry.
Mathieu Chartier396dc082018-08-06 12:29:57 -0700516 classpath_.ForEachDexClass([this](const DexClass& klass) {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100517 classes_[klass.GetDescriptor()].AddDexClass(klass);
518 });
519
520 // Connect each HierarchyClass to its successors and predecessors.
521 for (auto& entry : classes_) {
522 HierarchyClass& klass = entry.second;
523 const DexClass& dex_klass = klass.GetOneDexClass();
524
525 if (!dex_klass.HasSuperclass()) {
526 CHECK(dex_klass.GetInterfaceDescriptors().empty())
527 << "java/lang/Object should not implement any interfaces";
528 continue;
529 }
530
531 HierarchyClass* superclass = FindClass(dex_klass.GetSuperclassDescriptor());
532 CHECK(superclass != nullptr);
533 klass.AddExtends(*superclass);
534
535 for (const std::string& iface_desc : dex_klass.GetInterfaceDescriptors()) {
536 HierarchyClass* iface = FindClass(iface_desc);
537 CHECK(iface != nullptr);
538 klass.AddExtends(*iface);
539 }
540 }
541 }
542
David Brazdil345c0ed2018-08-03 10:26:44 +0100543 ClassPath& classpath_;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100544 std::map<std::string, HierarchyClass> classes_;
545};
546
David Brazdil20c765f2018-10-27 21:45:15 +0000547// Builder of dex section containing hiddenapi flags.
548class HiddenapiClassDataBuilder final {
549 public:
550 explicit HiddenapiClassDataBuilder(const DexFile& dex_file)
551 : num_classdefs_(dex_file.NumClassDefs()),
552 next_class_def_idx_(0u),
553 class_def_has_non_zero_flags_(false),
554 dex_file_has_non_zero_flags_(false),
555 data_(sizeof(uint32_t) * (num_classdefs_ + 1), 0u) {
556 *GetSizeField() = GetCurrentDataSize();
557 }
558
559 // Notify the builder that new flags for the next class def
560 // will be written now. The builder records the current offset
561 // into the header.
562 void BeginClassDef(uint32_t idx) {
563 CHECK_EQ(next_class_def_idx_, idx);
564 CHECK_LT(idx, num_classdefs_);
565 GetOffsetArray()[idx] = GetCurrentDataSize();
566 class_def_has_non_zero_flags_ = false;
567 }
568
569 // Notify the builder that all flags for this class def have been
570 // written. The builder updates the total size of the data struct
571 // and may set offset for class def in header to zero if no data
572 // has been written.
573 void EndClassDef(uint32_t idx) {
574 CHECK_EQ(next_class_def_idx_, idx);
575 CHECK_LT(idx, num_classdefs_);
576
577 ++next_class_def_idx_;
578
579 if (!class_def_has_non_zero_flags_) {
580 // No need to store flags for this class. Remove the written flags
581 // and set offset in header to zero.
582 data_.resize(GetOffsetArray()[idx]);
583 GetOffsetArray()[idx] = 0u;
584 }
585
586 dex_file_has_non_zero_flags_ |= class_def_has_non_zero_flags_;
587
588 if (idx == num_classdefs_ - 1) {
589 if (dex_file_has_non_zero_flags_) {
590 // This was the last class def and we have generated non-zero hiddenapi
591 // flags. Update total size in the header.
592 *GetSizeField() = GetCurrentDataSize();
593 } else {
594 // This was the last class def and we have not generated any non-zero
595 // hiddenapi flags. Clear all the data.
596 data_.clear();
597 }
598 }
599 }
600
601 // Append flags at the end of the data struct. This should be called
602 // between BeginClassDef and EndClassDef in the order of appearance of
603 // fields/methods in the class data stream.
David Brazdil47cd2722018-10-23 12:50:02 +0100604 void WriteFlags(hiddenapi::ApiList flags) {
David Brazdildcfa89b2018-10-31 11:04:10 +0000605 uint32_t uint_flags = flags.GetIntValue();
David Brazdil47cd2722018-10-23 12:50:02 +0100606 EncodeUnsignedLeb128(&data_, uint_flags);
607 class_def_has_non_zero_flags_ |= (uint_flags != 0u);
David Brazdil20c765f2018-10-27 21:45:15 +0000608 }
609
610 // Return backing data, assuming that all flags have been written.
611 const std::vector<uint8_t>& GetData() const {
612 CHECK_EQ(next_class_def_idx_, num_classdefs_) << "Incomplete data";
613 return data_;
614 }
615
616 private:
617 // Returns pointer to the size field in the header of this dex section.
618 uint32_t* GetSizeField() {
619 // Assume malloc() aligns allocated memory to at least uint32_t.
620 CHECK(IsAligned<sizeof(uint32_t)>(data_.data()));
621 return reinterpret_cast<uint32_t*>(data_.data());
622 }
623
624 // Returns pointer to array of offsets (indexed by class def indices) in the
625 // header of this dex section.
626 uint32_t* GetOffsetArray() { return &GetSizeField()[1]; }
627 uint32_t GetCurrentDataSize() const { return data_.size(); }
628
629 // Number of class defs in this dex file.
630 const uint32_t num_classdefs_;
631
632 // Next expected class def index.
633 uint32_t next_class_def_idx_;
634
635 // Whether non-zero flags have been encountered for this class def.
636 bool class_def_has_non_zero_flags_;
637
638 // Whether any non-zero flags have been encountered for this dex file.
639 bool dex_file_has_non_zero_flags_;
640
641 // Vector containing the data of the built data structure.
642 std::vector<uint8_t> data_;
643};
644
645// Edits a dex file, inserting a new HiddenapiClassData section.
646class DexFileEditor final {
647 public:
648 DexFileEditor(const DexFile& old_dex, const std::vector<uint8_t>& hiddenapi_class_data)
649 : old_dex_(old_dex),
650 hiddenapi_class_data_(hiddenapi_class_data),
651 loaded_dex_header_(nullptr),
652 loaded_dex_maplist_(nullptr) {}
653
654 // Copies dex file into a backing data vector, appends the given HiddenapiClassData
655 // and updates the MapList.
656 void Encode() {
657 // We do not support non-standard dex encodings, e.g. compact dex.
658 CHECK(old_dex_.IsStandardDexFile());
659
660 // If there are no data to append, copy the old dex file and return.
661 if (hiddenapi_class_data_.empty()) {
662 AllocateMemory(old_dex_.Size());
663 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
664 return;
665 }
666
667 // Find the old MapList, find its size.
668 const DexFile::MapList* old_map = old_dex_.GetMapList();
669 CHECK_LT(old_map->size_, std::numeric_limits<uint32_t>::max());
670
671 // Compute the size of the new dex file. We append the HiddenapiClassData,
672 // one MapItem and possibly some padding to align the new MapList.
673 CHECK(IsAligned<kMapListAlignment>(old_dex_.Size()))
674 << "End of input dex file is not 4-byte aligned, possibly because its MapList is not "
675 << "at the end of the file.";
676 size_t size_delta =
677 RoundUp(hiddenapi_class_data_.size(), kMapListAlignment) + sizeof(DexFile::MapItem);
678 size_t new_size = old_dex_.Size() + size_delta;
679 AllocateMemory(new_size);
680
681 // Copy the old dex file into the backing data vector. Load the copied
682 // dex file to obtain pointers to its header and MapList.
683 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
684 ReloadDex(/* verify= */ false);
685
686 // Truncate the new dex file before the old MapList. This assumes that
687 // the MapList is the last entry in the dex file. This is currently true
688 // for our tooling.
689 // TODO: Implement the general case by zero-ing the old MapList (turning
690 // it into padding.
691 RemoveOldMapList();
692
693 // Append HiddenapiClassData.
694 size_t payload_offset = AppendHiddenapiClassData();
695
696 // Wrute new MapList with an entry for HiddenapiClassData.
697 CreateMapListWithNewItem(payload_offset);
698
699 // Check that the pre-computed size matches the actual size.
700 CHECK_EQ(offset_, new_size);
701
702 // Reload to all data structures.
703 ReloadDex(/* verify= */ false);
704
705 // Update the dex checksum.
706 UpdateChecksum();
707
708 // Run DexFileVerifier on the new dex file as a CHECK.
709 ReloadDex(/* verify= */ true);
710 }
711
712 // Writes the edited dex file into a file.
713 void WriteTo(const std::string& path) {
714 CHECK(!data_.empty());
715 std::ofstream ofs(path.c_str(), std::ofstream::out | std::ofstream::binary);
716 ofs.write(reinterpret_cast<const char*>(data_.data()), data_.size());
717 ofs.flush();
718 CHECK(ofs.good());
719 ofs.close();
720 }
721
722 private:
723 static constexpr size_t kMapListAlignment = 4u;
724 static constexpr size_t kHiddenapiClassDataAlignment = 4u;
725
726 void ReloadDex(bool verify) {
727 std::string error_msg;
728 DexFileLoader loader;
729 loaded_dex_ = loader.Open(
730 data_.data(),
731 data_.size(),
732 "test_location",
733 old_dex_.GetLocationChecksum(),
734 /* oat_dex_file= */ nullptr,
735 /* verify= */ verify,
736 /* verify_checksum= */ verify,
737 &error_msg);
738 if (loaded_dex_.get() == nullptr) {
739 LOG(FATAL) << "Failed to load edited dex file: " << error_msg;
740 UNREACHABLE();
741 }
742
743 // Load the location of header and map list before we start editing the file.
744 loaded_dex_header_ = const_cast<DexFile::Header*>(&loaded_dex_->GetHeader());
745 loaded_dex_maplist_ = const_cast<DexFile::MapList*>(loaded_dex_->GetMapList());
746 }
747
748 DexFile::Header& GetHeader() const {
749 CHECK(loaded_dex_header_ != nullptr);
750 return *loaded_dex_header_;
751 }
752
753 DexFile::MapList& GetMapList() const {
754 CHECK(loaded_dex_maplist_ != nullptr);
755 return *loaded_dex_maplist_;
756 }
757
758 void AllocateMemory(size_t total_size) {
759 data_.clear();
760 data_.resize(total_size);
761 CHECK(IsAligned<kMapListAlignment>(data_.data()));
762 CHECK(IsAligned<kHiddenapiClassDataAlignment>(data_.data()));
763 offset_ = 0;
764 }
765
766 uint8_t* GetCurrentDataPtr() {
767 return data_.data() + offset_;
768 }
769
770 void UpdateDataSize(off_t delta, bool update_header) {
771 offset_ += delta;
772 if (update_header) {
773 DexFile::Header& header = GetHeader();
774 header.file_size_ += delta;
775 header.data_size_ += delta;
776 }
777 }
778
779 template<typename T>
780 T* Append(const T* src, size_t len, bool update_header = true) {
781 CHECK_LE(offset_ + len, data_.size());
782 uint8_t* dst = GetCurrentDataPtr();
783 memcpy(dst, src, len);
784 UpdateDataSize(len, update_header);
785 return reinterpret_cast<T*>(dst);
786 }
787
788 void InsertPadding(size_t alignment) {
789 size_t len = RoundUp(offset_, alignment) - offset_;
790 std::vector<uint8_t> padding(len, 0);
791 Append(padding.data(), padding.size());
792 }
793
794 void RemoveOldMapList() {
795 size_t map_size = GetMapList().Size();
796 uint8_t* map_start = reinterpret_cast<uint8_t*>(&GetMapList());
797 CHECK_EQ(map_start + map_size, GetCurrentDataPtr()) << "MapList not at the end of dex file";
798 UpdateDataSize(-static_cast<off_t>(map_size), /* update_header= */ true);
799 CHECK_EQ(map_start, GetCurrentDataPtr());
800 loaded_dex_maplist_ = nullptr; // do not use this map list any more
801 }
802
803 void CreateMapListWithNewItem(size_t payload_offset) {
804 InsertPadding(/* alignment= */ kMapListAlignment);
805
806 size_t new_map_offset = offset_;
807 DexFile::MapList* map = Append(old_dex_.GetMapList(), old_dex_.GetMapList()->Size());
808
809 // Check last map entry is a pointer to itself.
810 DexFile::MapItem& old_item = map->list_[map->size_ - 1];
811 CHECK(old_item.type_ == DexFile::kDexTypeMapList);
812 CHECK_EQ(old_item.size_, 1u);
813 CHECK_EQ(old_item.offset_, GetHeader().map_off_);
814
815 // Create a new MapItem entry with new MapList details.
816 DexFile::MapItem new_item;
817 new_item.type_ = old_item.type_;
David Brazdil976b01f2018-11-12 10:46:14 +0000818 new_item.unused_ = 0u; // initialize to ensure dex output is deterministic (b/119308882)
David Brazdil20c765f2018-10-27 21:45:15 +0000819 new_item.size_ = old_item.size_;
820 new_item.offset_ = new_map_offset;
821
822 // Update pointer in the header.
823 GetHeader().map_off_ = new_map_offset;
824
825 // Append a new MapItem and return its pointer.
826 map->size_++;
827 Append(&new_item, sizeof(DexFile::MapItem));
828
829 // Change penultimate entry to point to metadata.
830 old_item.type_ = DexFile::kDexTypeHiddenapiClassData;
831 old_item.size_ = 1u; // there is only one section
832 old_item.offset_ = payload_offset;
833 }
834
835 size_t AppendHiddenapiClassData() {
836 size_t payload_offset = offset_;
837 CHECK_EQ(kMapListAlignment, kHiddenapiClassDataAlignment);
838 CHECK(IsAligned<kHiddenapiClassDataAlignment>(payload_offset))
839 << "Should not need to align the section, previous data was already aligned";
840 Append(hiddenapi_class_data_.data(), hiddenapi_class_data_.size());
841 return payload_offset;
842 }
843
844 void UpdateChecksum() {
845 GetHeader().checksum_ = loaded_dex_->CalculateChecksum();
846 }
847
848 const DexFile& old_dex_;
849 const std::vector<uint8_t>& hiddenapi_class_data_;
850
851 std::vector<uint8_t> data_;
852 size_t offset_;
853
854 std::unique_ptr<const DexFile> loaded_dex_;
855 DexFile::Header* loaded_dex_header_;
856 DexFile::MapList* loaded_dex_maplist_;
857};
858
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100859class HiddenApi final {
David Brazdil003e64b2018-06-27 13:20:52 +0100860 public:
David Brazdil91690d32018-11-04 18:07:23 +0000861 HiddenApi() : force_assign_all_(true) {}
David Brazdil003e64b2018-06-27 13:20:52 +0100862
863 void Run(int argc, char** argv) {
864 switch (ParseArgs(argc, argv)) {
865 case Command::kEncode:
866 EncodeAccessFlags();
867 break;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100868 case Command::kList:
869 ListApi();
870 break;
David Brazdil003e64b2018-06-27 13:20:52 +0100871 }
872 }
873
874 private:
875 enum class Command {
David Brazdil003e64b2018-06-27 13:20:52 +0100876 kEncode,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100877 kList,
David Brazdil003e64b2018-06-27 13:20:52 +0100878 };
879
880 Command ParseArgs(int argc, char** argv) {
881 // Skip over the binary's path.
882 argv++;
883 argc--;
884
885 if (argc > 0) {
886 const StringPiece command(argv[0]);
887 if (command == "encode") {
888 for (int i = 1; i < argc; ++i) {
889 const StringPiece option(argv[i]);
David Brazdil20c765f2018-10-27 21:45:15 +0000890 if (option.starts_with("--input-dex=")) {
891 boot_dex_paths_.push_back(option.substr(strlen("--input-dex=")).ToString());
892 } else if (option.starts_with("--output-dex=")) {
893 output_dex_paths_.push_back(option.substr(strlen("--output-dex=")).ToString());
David Brazdil91690d32018-11-04 18:07:23 +0000894 } else if (option.starts_with("--api-flags=")) {
895 api_list_path_ = option.substr(strlen("--api-flags=")).ToString();
896 } else if (option == "--no-force-assign-all") {
897 force_assign_all_ = false;
David Brazdil003e64b2018-06-27 13:20:52 +0100898 } else {
899 Usage("Unknown argument '%s'", option.data());
900 }
901 }
902 return Command::kEncode;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100903 } else if (command == "list") {
904 for (int i = 1; i < argc; ++i) {
905 const StringPiece option(argv[i]);
906 if (option.starts_with("--boot-dex=")) {
907 boot_dex_paths_.push_back(option.substr(strlen("--boot-dex=")).ToString());
David Brazdil345c0ed2018-08-03 10:26:44 +0100908 } else if (option.starts_with("--stub-classpath=")) {
909 stub_classpaths_.push_back(android::base::Split(
910 option.substr(strlen("--stub-classpath=")).ToString(), ":"));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100911 } else if (option.starts_with("--out-public=")) {
912 out_public_path_ = option.substr(strlen("--out-public=")).ToString();
913 } else if (option.starts_with("--out-private=")) {
914 out_private_path_ = option.substr(strlen("--out-private=")).ToString();
915 } else {
916 Usage("Unknown argument '%s'", option.data());
917 }
918 }
919 return Command::kList;
David Brazdil003e64b2018-06-27 13:20:52 +0100920 } else {
921 Usage("Unknown command '%s'", command.data());
922 }
923 } else {
924 Usage("No command specified");
925 }
926 }
927
928 void EncodeAccessFlags() {
929 if (boot_dex_paths_.empty()) {
David Brazdil20c765f2018-10-27 21:45:15 +0000930 Usage("No input DEX files specified");
931 } else if (output_dex_paths_.size() != boot_dex_paths_.size()) {
932 Usage("Number of input DEX files does not match number of output DEX files");
David Brazdil003e64b2018-06-27 13:20:52 +0100933 }
934
935 // Load dex signatures.
David Brazdil91690d32018-11-04 18:07:23 +0000936 std::map<std::string, hiddenapi::ApiList> api_list = OpenApiFile(api_list_path_);
David Brazdil003e64b2018-06-27 13:20:52 +0100937
David Brazdil20c765f2018-10-27 21:45:15 +0000938 // Iterate over input dex files and insert HiddenapiClassData sections.
939 for (size_t i = 0; i < boot_dex_paths_.size(); ++i) {
940 const std::string& input_path = boot_dex_paths_[i];
941 const std::string& output_path = output_dex_paths_[i];
David Brazdil003e64b2018-06-27 13:20:52 +0100942
David Brazdil20c765f2018-10-27 21:45:15 +0000943 ClassPath boot_classpath({ input_path }, /* open_writable= */ false);
944 std::vector<const DexFile*> input_dex_files = boot_classpath.GetDexFiles();
945 CHECK_EQ(input_dex_files.size(), 1u);
946 const DexFile& input_dex = *input_dex_files[0];
David Brazdil003e64b2018-06-27 13:20:52 +0100947
David Brazdil20c765f2018-10-27 21:45:15 +0000948 HiddenapiClassDataBuilder builder(input_dex);
David Brazdil91690d32018-11-04 18:07:23 +0000949 boot_classpath.ForEachDexClass([&](const DexClass& boot_class) {
David Brazdil20c765f2018-10-27 21:45:15 +0000950 builder.BeginClassDef(boot_class.GetClassDefIndex());
951 if (boot_class.GetData() != nullptr) {
952 auto fn_shared = [&](const DexMember& boot_member) {
David Brazdil20c765f2018-10-27 21:45:15 +0000953 auto it = api_list.find(boot_member.GetApiEntry());
David Brazdil91690d32018-11-04 18:07:23 +0000954 bool api_list_found = (it != api_list.end());
955 CHECK(!force_assign_all_ || api_list_found)
956 << "Could not find flags for dex entry: " << boot_member.GetApiEntry();
957 builder.WriteFlags(api_list_found ? it->second : hiddenapi::ApiList::Whitelist());
David Brazdil20c765f2018-10-27 21:45:15 +0000958 };
959 auto fn_field = [&](const ClassAccessor::Field& boot_field) {
960 fn_shared(DexMember(boot_class, boot_field));
961 };
962 auto fn_method = [&](const ClassAccessor::Method& boot_method) {
963 fn_shared(DexMember(boot_class, boot_method));
964 };
965 boot_class.VisitFieldsAndMethods(fn_field, fn_field, fn_method, fn_method);
966 }
967 builder.EndClassDef(boot_class.GetClassDefIndex());
968 });
969
970 DexFileEditor dex_editor(input_dex, builder.GetData());
971 dex_editor.Encode();
972 dex_editor.WriteTo(output_path);
973 }
David Brazdil003e64b2018-06-27 13:20:52 +0100974 }
975
David Brazdil91690d32018-11-04 18:07:23 +0000976 std::map<std::string, hiddenapi::ApiList> OpenApiFile(const std::string& path) {
977 CHECK(!path.empty());
David Brazdil003e64b2018-06-27 13:20:52 +0100978 std::ifstream api_file(path, std::ifstream::in);
979 CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno);
980
David Brazdil91690d32018-11-04 18:07:23 +0000981 std::map<std::string, hiddenapi::ApiList> api_flag_map;
982
David Brazdil003e64b2018-06-27 13:20:52 +0100983 for (std::string line; std::getline(api_file, line);) {
David Brazdil91690d32018-11-04 18:07:23 +0000984 std::vector<std::string> values = android::base::Split(line, ",");
985 CHECK_EQ(values.size(), 2u) << "Currently only signature and one flag are supported";
986
987 const std::string& signature = values[0];
988 CHECK(api_flag_map.find(signature) == api_flag_map.end()) << "Duplicate entry: " << signature;
989
990 const std::string& flag_str = values[1];
991 hiddenapi::ApiList membership = hiddenapi::ApiList::FromName(flag_str);
992 CHECK(membership.IsValid()) << "Unknown ApiList name: " << flag_str;
993
994 api_flag_map.emplace(signature, membership);
David Brazdil003e64b2018-06-27 13:20:52 +0100995 }
David Brazdil91690d32018-11-04 18:07:23 +0000996
David Brazdil003e64b2018-06-27 13:20:52 +0100997 api_file.close();
David Brazdil91690d32018-11-04 18:07:23 +0000998 return api_flag_map;
David Brazdil003e64b2018-06-27 13:20:52 +0100999 }
David Brazdil2b9c35b2018-01-12 15:44:43 +00001000
David Brazdil0b6de0c2018-06-28 11:56:41 +01001001 void ListApi() {
1002 if (boot_dex_paths_.empty()) {
1003 Usage("No boot DEX files specified");
David Brazdil345c0ed2018-08-03 10:26:44 +01001004 } else if (stub_classpaths_.empty()) {
David Brazdil0b6de0c2018-06-28 11:56:41 +01001005 Usage("No stub DEX files specified");
1006 } else if (out_public_path_.empty()) {
1007 Usage("No public API output path specified");
1008 } else if (out_private_path_.empty()) {
1009 Usage("No private API output path specified");
1010 }
1011
1012 // Complete list of boot class path members. The associated boolean states
1013 // whether it is public (true) or private (false).
1014 std::map<std::string, bool> boot_members;
1015
1016 // Deduplicate errors before printing them.
1017 std::set<std::string> unresolved;
1018
1019 // Open all dex files.
Andreas Gampe9b031f72018-10-04 11:03:34 -07001020 ClassPath boot_classpath(boot_dex_paths_, /* open_writable= */ false);
David Brazdil345c0ed2018-08-03 10:26:44 +01001021 Hierarchy boot_hierarchy(boot_classpath);
David Brazdil0b6de0c2018-06-28 11:56:41 +01001022
1023 // Mark all boot dex members private.
Mathieu Chartier396dc082018-08-06 12:29:57 -07001024 boot_classpath.ForEachDexMember([&boot_members](const DexMember& boot_member) {
David Brazdil0b6de0c2018-06-28 11:56:41 +01001025 boot_members[boot_member.GetApiEntry()] = false;
1026 });
1027
1028 // Resolve each SDK dex member against the framework and mark it white.
David Brazdil345c0ed2018-08-03 10:26:44 +01001029 for (const std::vector<std::string>& stub_classpath_dex : stub_classpaths_) {
Andreas Gampe9b031f72018-10-04 11:03:34 -07001030 ClassPath stub_classpath(stub_classpath_dex, /* open_writable= */ false);
David Brazdil345c0ed2018-08-03 10:26:44 +01001031 Hierarchy stub_hierarchy(stub_classpath);
1032 stub_classpath.ForEachDexMember(
Mathieu Chartier396dc082018-08-06 12:29:57 -07001033 [&stub_hierarchy, &boot_hierarchy, &boot_members, &unresolved](
1034 const DexMember& stub_member) {
David Brazdil345c0ed2018-08-03 10:26:44 +01001035 if (!stub_hierarchy.IsMemberVisible(stub_member)) {
1036 // Typically fake constructors and inner-class `this` fields.
1037 return;
1038 }
1039 bool resolved = boot_hierarchy.ForEachResolvableMember(
1040 stub_member,
Mathieu Chartier396dc082018-08-06 12:29:57 -07001041 [&boot_members](const DexMember& boot_member) {
David Brazdil345c0ed2018-08-03 10:26:44 +01001042 std::string entry = boot_member.GetApiEntry();
1043 auto it = boot_members.find(entry);
1044 CHECK(it != boot_members.end());
1045 if (it->second) {
1046 return false; // has been marked before
1047 } else {
1048 it->second = true;
1049 return true; // marked for the first time
1050 }
1051 });
1052 if (!resolved) {
1053 unresolved.insert(stub_member.GetApiEntry());
1054 }
1055 });
1056 }
David Brazdil0b6de0c2018-06-28 11:56:41 +01001057
1058 // Print errors.
1059 for (const std::string& str : unresolved) {
1060 LOG(WARNING) << "unresolved: " << str;
1061 }
1062
1063 // Write into public/private API files.
1064 std::ofstream file_public(out_public_path_.c_str());
1065 std::ofstream file_private(out_private_path_.c_str());
Elliott Hughes9c8957d2018-11-27 22:24:57 -08001066 for (const std::pair<const std::string, bool>& entry : boot_members) {
David Brazdil0b6de0c2018-06-28 11:56:41 +01001067 if (entry.second) {
1068 file_public << entry.first << std::endl;
1069 } else {
1070 file_private << entry.first << std::endl;
1071 }
1072 }
1073 file_public.close();
1074 file_private.close();
1075 }
1076
David Brazdil91690d32018-11-04 18:07:23 +00001077 // Whether to check that all dex entries have been assigned flags.
1078 // Defaults to true.
1079 bool force_assign_all_;
1080
David Brazdil2b9c35b2018-01-12 15:44:43 +00001081 // Paths to DEX files which should be processed.
David Brazdil003e64b2018-06-27 13:20:52 +01001082 std::vector<std::string> boot_dex_paths_;
David Brazdil345c0ed2018-08-03 10:26:44 +01001083
David Brazdil20c765f2018-10-27 21:45:15 +00001084 // Output paths where modified DEX files should be written.
1085 std::vector<std::string> output_dex_paths_;
1086
David Brazdil345c0ed2018-08-03 10:26:44 +01001087 // Set of public API stub classpaths. Each classpath is formed by a list
1088 // of DEX/APK files in the order they appear on the classpath.
1089 std::vector<std::vector<std::string>> stub_classpaths_;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001090
1091 // Paths to text files which contain the lists of API members.
David Brazdil91690d32018-11-04 18:07:23 +00001092 std::string api_list_path_;
David Brazdil0b6de0c2018-06-28 11:56:41 +01001093
1094 // Paths to text files to which we will output list of all API members.
1095 std::string out_public_path_;
1096 std::string out_private_path_;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001097};
1098
1099} // namespace art
1100
1101int main(int argc, char** argv) {
David Brazdil20c765f2018-10-27 21:45:15 +00001102 art::original_argc = argc;
1103 art::original_argv = argv;
David Brazdil003e64b2018-06-27 13:20:52 +01001104 android::base::InitLogging(argv);
1105 art::MemMap::Init();
1106 art::HiddenApi().Run(argc, argv);
1107 return EXIT_SUCCESS;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001108}