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 
19 #include "android-base/strings.h"
20 
21 #include "base/unix_file/fd_file.h"
22 #include "base/zip_archive.h"
23 #include "common_runtime_test.h"
24 #include "dex/art_dex_file_loader.h"
25 #include "dex/class_accessor-inl.h"
26 #include "dex/dex_file-inl.h"
27 #include "exec_utils.h"
28 
29 namespace art {
30 
31 class HiddenApiTest : public CommonRuntimeTest {
32  protected:
GetHiddenApiCmd()33   std::string GetHiddenApiCmd() {
34     std::string file_path = GetArtBinDir() + "/hiddenapi";
35     if (kIsDebugBuild) {
36       file_path += 'd';
37     }
38     if (!OS::FileExists(file_path.c_str())) {
39       LOG(FATAL) << "Could not find binary " << file_path;
40       UNREACHABLE();
41     }
42     return file_path;
43   }
44 
RunHiddenapiEncode(const ScratchFile & flags_csv,const std::vector<std::string> & extra_args,const ScratchFile & out_dex)45   std::unique_ptr<const DexFile> RunHiddenapiEncode(const ScratchFile& flags_csv,
46                                                     const std::vector<std::string>& extra_args,
47                                                     const ScratchFile& out_dex) {
48     std::string error;
49     ScratchFile in_dex;
50     std::unique_ptr<ZipArchive> jar(
51         ZipArchive::Open(GetTestDexFileName("HiddenApi").c_str(), &error));
52     if (jar == nullptr) {
53       LOG(FATAL) << "Could not open test file " << GetTestDexFileName("HiddenApi") << ": " << error;
54       UNREACHABLE();
55     }
56     std::unique_ptr<ZipEntry> jar_classes_dex(jar->Find("classes.dex", &error));
57     if (jar_classes_dex == nullptr) {
58       LOG(FATAL) << "Could not find classes.dex in test file " << GetTestDexFileName("HiddenApi")
59                  << ": " << error;
60       UNREACHABLE();
61     } else if (!jar_classes_dex->ExtractToFile(*in_dex.GetFile(), &error)) {
62       LOG(FATAL) << "Could not extract classes.dex from test file "
63                  << GetTestDexFileName("HiddenApi") << ": " << error;
64       UNREACHABLE();
65     }
66 
67     std::vector<std::string> argv_str;
68     argv_str.push_back(GetHiddenApiCmd());
69     argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
70     argv_str.push_back("encode");
71     argv_str.push_back("--input-dex=" + in_dex.GetFilename());
72     argv_str.push_back("--output-dex=" + out_dex.GetFilename());
73     argv_str.push_back("--api-flags=" + flags_csv.GetFilename());
74     argv_str.push_back("--no-force-assign-all");
75     int return_code = ExecAndReturnCode(argv_str, &error);
76     if (return_code == 0) {
77       return OpenDex(out_dex);
78     } else {
79       LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code;
80       return nullptr;
81     }
82   }
83 
RunHiddenapiList(const ScratchFile & out_flags_csv)84   bool RunHiddenapiList(const ScratchFile& out_flags_csv) {
85     std::string error;
86     std::string boot_jar = GetTestDexFileName("HiddenApi");
87     std::string stub_jar = GetTestDexFileName("HiddenApiStubs");
88     std::string boot_cp = android::base::Join(GetLibCoreDexFileNames(), ":");
89 
90     std::vector<std::string> argv_str;
91     argv_str.push_back(GetHiddenApiCmd());
92     argv_str.push_back("list");
93     for (const std::string& core_jar : GetLibCoreDexFileNames()) {
94       argv_str.push_back("--boot-dex=" + core_jar);
95     }
96     argv_str.push_back("--boot-dex=" + boot_jar);
97     argv_str.push_back("--public-stub-classpath=" + boot_cp + ":" + stub_jar);
98     argv_str.push_back("--out-api-flags=" + out_flags_csv.GetFilename());
99     int return_code = ExecAndReturnCode(argv_str, &error);
100     if (return_code == 0) {
101       return true;
102     } else {
103       LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code;
104       return false;
105     }
106   }
107 
OpenDex(const ScratchFile & file)108   std::unique_ptr<const DexFile> OpenDex(const ScratchFile& file) {
109     ArtDexFileLoader dex_loader;
110     std::string error_msg;
111 
112     File fd(file.GetFilename(), O_RDONLY, /* check_usage= */ false);
113     if (fd.Fd() == -1) {
114       PLOG(FATAL) << "Unable to open file '" << file.GetFilename() << "'";
115       UNREACHABLE();
116     }
117 
118     std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(
119         fd.Release(), /* location= */ file.GetFilename(), /* verify= */ true,
120         /* verify_checksum= */ true, /* mmap_shared= */ false, &error_msg));
121     if (dex_file.get() == nullptr) {
122       LOG(FATAL) << "Open failed for '" << file.GetFilename() << "' " << error_msg;
123       UNREACHABLE();
124     } else if (!dex_file->IsStandardDexFile()) {
125       LOG(FATAL) << "Expected a standard dex file '" << file.GetFilename() << "'";
126       UNREACHABLE();
127     }
128 
129     return dex_file;
130   }
131 
OpenStream(const ScratchFile & file)132   std::ofstream OpenStream(const ScratchFile& file) {
133     std::ofstream ofs(file.GetFilename(), std::ofstream::out);
134     if (ofs.fail()) {
135       PLOG(FATAL) << "Open failed for '" << file.GetFilename() << "'";
136       UNREACHABLE();
137     }
138     return ofs;
139   }
140 
ReadFlagsCsvFile(const ScratchFile & file)141   std::map<std::string, std::string> ReadFlagsCsvFile(const ScratchFile& file) {
142     std::ifstream ifs(file.GetFilename());
143     std::map<std::string, std::string> flags;
144 
145     for (std::string line; std::getline(ifs, line);) {
146       std::size_t comma = line.find(",");
147       if (comma == std::string::npos) {
148         flags.emplace(line, "");
149       } else {
150         flags.emplace(line.substr(0, comma), line.substr(comma + 1));
151       }
152     }
153 
154     return flags;
155   }
156 
SafeMapGet(const std::string & key,const std::map<std::string,std::string> & map)157   std::string SafeMapGet(const std::string& key, const std::map<std::string, std::string>& map) {
158     auto it = map.find(key);
159     if (it == map.end()) {
160       LOG(FATAL) << "Key not found: " << key;
161       UNREACHABLE();
162     }
163     return it->second;
164   }
165 
FindClass(const char * desc,const DexFile & dex_file)166   const dex::ClassDef& FindClass(const char* desc, const DexFile& dex_file) {
167     const dex::TypeId* type_id = dex_file.FindTypeId(desc);
168     CHECK(type_id != nullptr) << "Could not find class " << desc;
169     const dex::ClassDef* found = dex_file.FindClassDef(dex_file.GetIndexForTypeId(*type_id));
170     CHECK(found != nullptr) << "Could not find class " << desc;
171     return *found;
172   }
173 
GetFieldHiddenFlags(const char * name,uint32_t expected_visibility,const dex::ClassDef & class_def,const DexFile & dex_file)174   hiddenapi::ApiList GetFieldHiddenFlags(const char* name,
175                                          uint32_t expected_visibility,
176                                          const dex::ClassDef& class_def,
177                                          const DexFile& dex_file) {
178     ClassAccessor accessor(dex_file, class_def, /* parse hiddenapi flags */ true);
179     CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data";
180 
181     if (!accessor.HasHiddenapiClassData()) {
182       return hiddenapi::ApiList::Whitelist();
183     }
184 
185     for (const ClassAccessor::Field& field : accessor.GetFields()) {
186       const dex::FieldId& fid = dex_file.GetFieldId(field.GetIndex());
187       if (strcmp(name, dex_file.GetFieldName(fid)) == 0) {
188         const uint32_t actual_visibility = field.GetAccessFlags() & kAccVisibilityFlags;
189         CHECK_EQ(actual_visibility, expected_visibility)
190             << "Field " << name << " in class " << accessor.GetDescriptor();
191         return hiddenapi::ApiList(field.GetHiddenapiFlags());
192       }
193     }
194 
195     LOG(FATAL) << "Could not find field " << name << " in class "
196                << dex_file.GetClassDescriptor(class_def);
197     UNREACHABLE();
198   }
199 
GetMethodHiddenFlags(const char * name,uint32_t expected_visibility,bool expected_native,const dex::ClassDef & class_def,const DexFile & dex_file)200   hiddenapi::ApiList GetMethodHiddenFlags(const char* name,
201                                           uint32_t expected_visibility,
202                                           bool expected_native,
203                                           const dex::ClassDef& class_def,
204                                           const DexFile& dex_file) {
205     ClassAccessor accessor(dex_file, class_def, /* parse hiddenapi flags */ true);
206     CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data";
207 
208     if (!accessor.HasHiddenapiClassData()) {
209       return hiddenapi::ApiList::Whitelist();
210     }
211 
212     for (const ClassAccessor::Method& method : accessor.GetMethods()) {
213       const dex::MethodId& mid = dex_file.GetMethodId(method.GetIndex());
214       if (strcmp(name, dex_file.GetMethodName(mid)) == 0) {
215         CHECK_EQ(expected_native, method.MemberIsNative())
216             << "Method " << name << " in class " << accessor.GetDescriptor();
217         const uint32_t actual_visibility = method.GetAccessFlags() & kAccVisibilityFlags;
218         CHECK_EQ(actual_visibility, expected_visibility)
219             << "Method " << name << " in class " << accessor.GetDescriptor();
220         return hiddenapi::ApiList(method.GetHiddenapiFlags());
221       }
222     }
223 
224     LOG(FATAL) << "Could not find method " << name << " in class "
225                << dex_file.GetClassDescriptor(class_def);
226     UNREACHABLE();
227   }
228 
GetIFieldHiddenFlags(const DexFile & dex_file)229   hiddenapi::ApiList GetIFieldHiddenFlags(const DexFile& dex_file) {
230     return GetFieldHiddenFlags("ifield", kAccPublic, FindClass("LMain;", dex_file), dex_file);
231   }
232 
GetSFieldHiddenFlags(const DexFile & dex_file)233   hiddenapi::ApiList GetSFieldHiddenFlags(const DexFile& dex_file) {
234     return GetFieldHiddenFlags("sfield", kAccPrivate, FindClass("LMain;", dex_file), dex_file);
235   }
236 
GetIMethodHiddenFlags(const DexFile & dex_file)237   hiddenapi::ApiList GetIMethodHiddenFlags(const DexFile& dex_file) {
238     return GetMethodHiddenFlags(
239         "imethod", 0, /* expected_native= */ false, FindClass("LMain;", dex_file), dex_file);
240   }
241 
GetSMethodHiddenFlags(const DexFile & dex_file)242   hiddenapi::ApiList GetSMethodHiddenFlags(const DexFile& dex_file) {
243     return GetMethodHiddenFlags("smethod",
244                                 kAccPublic,
245                                 /* expected_native= */ false,
246                                 FindClass("LMain;", dex_file),
247                                 dex_file);
248   }
249 
GetINMethodHiddenFlags(const DexFile & dex_file)250   hiddenapi::ApiList GetINMethodHiddenFlags(const DexFile& dex_file) {
251     return GetMethodHiddenFlags("inmethod",
252                                 kAccPublic,
253                                 /* expected_native= */ true,
254                                 FindClass("LMain;", dex_file),
255                                 dex_file);
256   }
257 
GetSNMethodHiddenFlags(const DexFile & dex_file)258   hiddenapi::ApiList GetSNMethodHiddenFlags(const DexFile& dex_file) {
259     return GetMethodHiddenFlags("snmethod",
260                                 kAccProtected,
261                                 /* expected_native= */ true,
262                                 FindClass("LMain;", dex_file),
263                                 dex_file);
264   }
265 };
266 
TEST_F(HiddenApiTest,InstanceFieldNoMatch)267 TEST_F(HiddenApiTest, InstanceFieldNoMatch) {
268   ScratchFile dex, flags_csv;
269   OpenStream(flags_csv)
270       << "LMain;->ifield:LBadType1;,greylist" << std::endl
271       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
272       << "LMain;->ifield:LBadType3;,blacklist" << std::endl;
273   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
274   ASSERT_NE(dex_file.get(), nullptr);
275   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIFieldHiddenFlags(*dex_file));
276 }
277 
TEST_F(HiddenApiTest,InstanceFieldLightGreylistMatch)278 TEST_F(HiddenApiTest, InstanceFieldLightGreylistMatch) {
279   ScratchFile dex, flags_csv;
280   OpenStream(flags_csv)
281       << "LMain;->ifield:I,greylist" << std::endl
282       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
283       << "LMain;->ifield:LBadType3;,blacklist" << std::endl;
284   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
285   ASSERT_NE(dex_file.get(), nullptr);
286   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file));
287 }
288 
TEST_F(HiddenApiTest,InstanceFieldDarkGreylistMatch)289 TEST_F(HiddenApiTest, InstanceFieldDarkGreylistMatch) {
290   ScratchFile dex, flags_csv;
291   OpenStream(flags_csv)
292       << "LMain;->ifield:LBadType1;,greylist" << std::endl
293       << "LMain;->ifield:I,greylist-max-o" << std::endl
294       << "LMain;->ifield:LBadType3;,blacklist" << std::endl;
295   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
296   ASSERT_NE(dex_file.get(), nullptr);
297   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIFieldHiddenFlags(*dex_file));
298 }
299 
TEST_F(HiddenApiTest,InstanceFieldBlacklistMatch)300 TEST_F(HiddenApiTest, InstanceFieldBlacklistMatch) {
301   ScratchFile dex, flags_csv;
302   OpenStream(flags_csv)
303       << "LMain;->ifield:LBadType1;,greylist" << std::endl
304       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
305       << "LMain;->ifield:I,blacklist" << std::endl;
306   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
307   ASSERT_NE(dex_file.get(), nullptr);
308   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIFieldHiddenFlags(*dex_file));
309 }
310 
TEST_F(HiddenApiTest,InstanceFieldTwoListsMatch1)311 TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch1) {
312   ScratchFile dex, flags_csv;
313   OpenStream(flags_csv)
314       << "LMain;->ifield:LBadType1;,greylist" << std::endl
315       << "LMain;->ifield:I,blacklist,greylist-max-o" << std::endl;
316   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
317   ASSERT_EQ(dex_file.get(), nullptr);
318 }
319 
TEST_F(HiddenApiTest,InstanceFieldTwoListsMatch2)320 TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch2) {
321   ScratchFile dex, flags_csv;
322   OpenStream(flags_csv)
323       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
324       << "LMain;->ifield:I,blacklist,greylist" << std::endl;
325   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
326   ASSERT_EQ(dex_file.get(), nullptr);
327 }
328 
TEST_F(HiddenApiTest,InstanceFieldTwoListsMatch3)329 TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch3) {
330   ScratchFile dex, flags_csv;
331   OpenStream(flags_csv)
332       << "LMain;->ifield:I,greylist,greylist-max-o" << std::endl
333       << "LMain;->ifield:LBadType3;,blacklist" << std::endl;
334   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
335   ASSERT_EQ(dex_file.get(), nullptr);
336 }
337 
TEST_F(HiddenApiTest,StaticFieldNoMatch)338 TEST_F(HiddenApiTest, StaticFieldNoMatch) {
339   ScratchFile dex, flags_csv;
340   OpenStream(flags_csv)
341       << "LMain;->sfield:LBadType1;,greylist" << std::endl
342       << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
343       << "LMain;->sfield:LBadType3;,blacklist" << std::endl;
344   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
345   ASSERT_NE(dex_file.get(), nullptr);
346   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSFieldHiddenFlags(*dex_file));
347 }
348 
TEST_F(HiddenApiTest,StaticFieldLightGreylistMatch)349 TEST_F(HiddenApiTest, StaticFieldLightGreylistMatch) {
350   ScratchFile dex, flags_csv;
351   OpenStream(flags_csv)
352       << "LMain;->sfield:Ljava/lang/Object;,greylist" << std::endl
353       << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
354       << "LMain;->sfield:LBadType3;,blacklist" << std::endl;
355   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
356   ASSERT_NE(dex_file.get(), nullptr);
357   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSFieldHiddenFlags(*dex_file));
358 }
359 
TEST_F(HiddenApiTest,StaticFieldDarkGreylistMatch)360 TEST_F(HiddenApiTest, StaticFieldDarkGreylistMatch) {
361   ScratchFile dex, flags_csv;
362   OpenStream(flags_csv)
363       << "LMain;->sfield:LBadType1;,greylist" << std::endl
364       << "LMain;->sfield:Ljava/lang/Object;,greylist-max-o" << std::endl
365       << "LMain;->sfield:LBadType3;,blacklist" << std::endl;
366   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
367   ASSERT_NE(dex_file.get(), nullptr);
368   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSFieldHiddenFlags(*dex_file));
369 }
370 
TEST_F(HiddenApiTest,StaticFieldBlacklistMatch)371 TEST_F(HiddenApiTest, StaticFieldBlacklistMatch) {
372   ScratchFile dex, flags_csv;
373   OpenStream(flags_csv)
374       << "LMain;->sfield:LBadType1;,greylist" << std::endl
375       << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
376       << "LMain;->sfield:Ljava/lang/Object;,blacklist" << std::endl;
377   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
378   ASSERT_NE(dex_file.get(), nullptr);
379   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSFieldHiddenFlags(*dex_file));
380 }
381 
TEST_F(HiddenApiTest,StaticFieldTwoListsMatch1)382 TEST_F(HiddenApiTest, StaticFieldTwoListsMatch1) {
383   ScratchFile dex, flags_csv;
384   OpenStream(flags_csv)
385       << "LMain;->sfield:LBadType1;,greylist" << std::endl
386       << "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist-max-o" << std::endl;
387   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
388   ASSERT_EQ(dex_file.get(), nullptr);
389 }
390 
TEST_F(HiddenApiTest,StaticFieldTwoListsMatch2)391 TEST_F(HiddenApiTest, StaticFieldTwoListsMatch2) {
392   ScratchFile dex, flags_csv;
393   OpenStream(flags_csv)
394       << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
395       << "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist" << std::endl;
396   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
397   ASSERT_EQ(dex_file.get(), nullptr);
398 }
399 
TEST_F(HiddenApiTest,StaticFieldTwoListsMatch3)400 TEST_F(HiddenApiTest, StaticFieldTwoListsMatch3) {
401   ScratchFile dex, flags_csv;
402   OpenStream(flags_csv)
403       << "LMain;->sfield:Ljava/lang/Object;,greylist,greylist-max-o" << std::endl
404       << "LMain;->sfield:LBadType3;,blacklist" << std::endl;
405   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
406   ASSERT_EQ(dex_file.get(), nullptr);
407 }
408 
TEST_F(HiddenApiTest,InstanceMethodNoMatch)409 TEST_F(HiddenApiTest, InstanceMethodNoMatch) {
410   ScratchFile dex, flags_csv;
411   OpenStream(flags_csv)
412       << "LMain;->imethod(LBadType1;)V,greylist" << std::endl
413       << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
414       << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
415   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
416   ASSERT_NE(dex_file.get(), nullptr);
417   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIMethodHiddenFlags(*dex_file));
418 }
419 
TEST_F(HiddenApiTest,InstanceMethodLightGreylistMatch)420 TEST_F(HiddenApiTest, InstanceMethodLightGreylistMatch) {
421   ScratchFile dex, flags_csv;
422   OpenStream(flags_csv)
423       << "LMain;->imethod(J)V,greylist" << std::endl
424       << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
425       << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
426   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
427   ASSERT_NE(dex_file.get(), nullptr);
428   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIMethodHiddenFlags(*dex_file));
429 }
430 
TEST_F(HiddenApiTest,InstanceMethodDarkGreylistMatch)431 TEST_F(HiddenApiTest, InstanceMethodDarkGreylistMatch) {
432   ScratchFile dex, flags_csv;
433   OpenStream(flags_csv)
434       << "LMain;->imethod(LBadType1;)V,greylist" << std::endl
435       << "LMain;->imethod(J)V,greylist-max-o" << std::endl
436       << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
437   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
438   ASSERT_NE(dex_file.get(), nullptr);
439   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIMethodHiddenFlags(*dex_file));
440 }
441 
TEST_F(HiddenApiTest,InstanceMethodBlacklistMatch)442 TEST_F(HiddenApiTest, InstanceMethodBlacklistMatch) {
443   ScratchFile dex, flags_csv;
444   OpenStream(flags_csv)
445       << "LMain;->imethod(LBadType1;)V,greylist" << std::endl
446       << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
447       << "LMain;->imethod(J)V,blacklist" << std::endl;
448   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
449   ASSERT_NE(dex_file.get(), nullptr);
450   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIMethodHiddenFlags(*dex_file));
451 }
452 
TEST_F(HiddenApiTest,InstanceMethodTwoListsMatch1)453 TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch1) {
454   ScratchFile dex, flags_csv;
455   OpenStream(flags_csv)
456       << "LMain;->imethod(LBadType1;)V,greylist" << std::endl
457       << "LMain;->imethod(J)V,blacklist,greylist-max-o" << std::endl;
458   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
459   ASSERT_EQ(dex_file.get(), nullptr);
460 }
461 
TEST_F(HiddenApiTest,InstanceMethodTwoListsMatch2)462 TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch2) {
463   ScratchFile dex, flags_csv;
464   OpenStream(flags_csv)
465       << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
466       << "LMain;->imethod(J)V,blacklist,greylist" << std::endl;
467   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
468   ASSERT_EQ(dex_file.get(), nullptr);
469 }
470 
TEST_F(HiddenApiTest,InstanceMethodTwoListsMatch3)471 TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch3) {
472   ScratchFile dex, flags_csv;
473   OpenStream(flags_csv)
474       << "LMain;->imethod(J)V,greylist,greylist-max-o" << std::endl
475       << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
476   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
477   ASSERT_EQ(dex_file.get(), nullptr);
478 }
479 
TEST_F(HiddenApiTest,StaticMethodNoMatch)480 TEST_F(HiddenApiTest, StaticMethodNoMatch) {
481   ScratchFile dex, flags_csv;
482   OpenStream(flags_csv)
483       << "LMain;->smethod(LBadType1;)V,greylist" << std::endl
484       << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
485       << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
486   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
487   ASSERT_NE(dex_file.get(), nullptr);
488   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSMethodHiddenFlags(*dex_file));
489 }
490 
TEST_F(HiddenApiTest,StaticMethodLightGreylistMatch)491 TEST_F(HiddenApiTest, StaticMethodLightGreylistMatch) {
492   ScratchFile dex, flags_csv;
493   OpenStream(flags_csv)
494       << "LMain;->smethod(Ljava/lang/Object;)V,greylist" << std::endl
495       << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
496       << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
497   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
498   ASSERT_NE(dex_file.get(), nullptr);
499   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSMethodHiddenFlags(*dex_file));
500 }
501 
TEST_F(HiddenApiTest,StaticMethodDarkGreylistMatch)502 TEST_F(HiddenApiTest, StaticMethodDarkGreylistMatch) {
503   ScratchFile dex, flags_csv;
504   OpenStream(flags_csv)
505       << "LMain;->smethod(LBadType1;)V,greylist" << std::endl
506       << "LMain;->smethod(Ljava/lang/Object;)V,greylist-max-o" << std::endl
507       << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
508   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
509   ASSERT_NE(dex_file.get(), nullptr);
510   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSMethodHiddenFlags(*dex_file));
511 }
512 
TEST_F(HiddenApiTest,StaticMethodBlacklistMatch)513 TEST_F(HiddenApiTest, StaticMethodBlacklistMatch) {
514   ScratchFile dex, flags_csv;
515   OpenStream(flags_csv)
516       << "LMain;->smethod(LBadType1;)V,greylist" << std::endl
517       << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
518       << "LMain;->smethod(Ljava/lang/Object;)V,blacklist" << std::endl;
519   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
520   ASSERT_NE(dex_file.get(), nullptr);
521   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSMethodHiddenFlags(*dex_file));
522 }
523 
TEST_F(HiddenApiTest,StaticMethodTwoListsMatch1)524 TEST_F(HiddenApiTest, StaticMethodTwoListsMatch1) {
525   ScratchFile dex, flags_csv;
526   OpenStream(flags_csv)
527       << "LMain;->smethod(LBadType1;)V,greylist" << std::endl
528       << "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist-max-o" << std::endl;
529   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
530   ASSERT_EQ(dex_file.get(), nullptr);
531 }
532 
TEST_F(HiddenApiTest,StaticMethodTwoListsMatch2)533 TEST_F(HiddenApiTest, StaticMethodTwoListsMatch2) {
534   ScratchFile dex, flags_csv;
535   OpenStream(flags_csv)
536       << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
537       << "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist" << std::endl;
538   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
539   ASSERT_EQ(dex_file.get(), nullptr);
540 }
541 
TEST_F(HiddenApiTest,StaticMethodTwoListsMatch3)542 TEST_F(HiddenApiTest, StaticMethodTwoListsMatch3) {
543   ScratchFile dex, flags_csv;
544   OpenStream(flags_csv)
545       << "LMain;->smethod(Ljava/lang/Object;)V,greylist,greylist-max-o" << std::endl
546       << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
547   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
548   ASSERT_EQ(dex_file.get(), nullptr);
549 }
550 
TEST_F(HiddenApiTest,InstanceNativeMethodNoMatch)551 TEST_F(HiddenApiTest, InstanceNativeMethodNoMatch) {
552   ScratchFile dex, flags_csv;
553   OpenStream(flags_csv)
554       << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
555       << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
556       << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
557   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
558   ASSERT_NE(dex_file.get(), nullptr);
559   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetINMethodHiddenFlags(*dex_file));
560 }
561 
TEST_F(HiddenApiTest,InstanceNativeMethodLightGreylistMatch)562 TEST_F(HiddenApiTest, InstanceNativeMethodLightGreylistMatch) {
563   ScratchFile dex, flags_csv;
564   OpenStream(flags_csv)
565       << "LMain;->inmethod(C)V,greylist" << std::endl
566       << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
567       << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
568   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
569   ASSERT_NE(dex_file.get(), nullptr);
570   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetINMethodHiddenFlags(*dex_file));
571 }
572 
TEST_F(HiddenApiTest,InstanceNativeMethodDarkGreylistMatch)573 TEST_F(HiddenApiTest, InstanceNativeMethodDarkGreylistMatch) {
574   ScratchFile dex, flags_csv;
575   OpenStream(flags_csv)
576       << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
577       << "LMain;->inmethod(C)V,greylist-max-o" << std::endl
578       << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
579   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
580   ASSERT_NE(dex_file.get(), nullptr);
581   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetINMethodHiddenFlags(*dex_file));
582 }
583 
TEST_F(HiddenApiTest,InstanceNativeMethodBlacklistMatch)584 TEST_F(HiddenApiTest, InstanceNativeMethodBlacklistMatch) {
585   ScratchFile dex, flags_csv;
586   OpenStream(flags_csv)
587       << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
588       << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
589       << "LMain;->inmethod(C)V,blacklist" << std::endl;
590   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
591   ASSERT_NE(dex_file.get(), nullptr);
592   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetINMethodHiddenFlags(*dex_file));
593 }
594 
TEST_F(HiddenApiTest,InstanceNativeMethodTwoListsMatch1)595 TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch1) {
596   ScratchFile dex, flags_csv;
597   OpenStream(flags_csv)
598       << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
599       << "LMain;->inmethod(C)V,blacklist,greylist-max-o" << std::endl;
600   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
601   ASSERT_EQ(dex_file.get(), nullptr);
602 }
603 
TEST_F(HiddenApiTest,InstanceNativeMethodTwoListsMatch2)604 TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch2) {
605   ScratchFile dex, flags_csv;
606   OpenStream(flags_csv)
607       << "LMain;->inmethod(C)V,blacklist,greylist" << std::endl
608       << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl;
609   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
610   ASSERT_EQ(dex_file.get(), nullptr);
611 }
612 
TEST_F(HiddenApiTest,InstanceNativeMethodTwoListsMatch3)613 TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch3) {
614   ScratchFile dex, flags_csv;
615   OpenStream(flags_csv)
616       << "LMain;->inmethod(C)V,greylist,greylist-max-o" << std::endl
617       << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
618   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
619   ASSERT_EQ(dex_file.get(), nullptr);
620 }
621 
TEST_F(HiddenApiTest,StaticNativeMethodNoMatch)622 TEST_F(HiddenApiTest, StaticNativeMethodNoMatch) {
623   ScratchFile dex, flags_csv;
624   OpenStream(flags_csv)
625       << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
626       << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
627       << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
628   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
629   ASSERT_NE(dex_file.get(), nullptr);
630   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSNMethodHiddenFlags(*dex_file));
631 }
632 
TEST_F(HiddenApiTest,StaticNativeMethodLightGreylistMatch)633 TEST_F(HiddenApiTest, StaticNativeMethodLightGreylistMatch) {
634   ScratchFile dex, flags_csv;
635   OpenStream(flags_csv)
636       << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist" << std::endl
637       << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
638       << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
639   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
640   ASSERT_NE(dex_file.get(), nullptr);
641   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSNMethodHiddenFlags(*dex_file));
642 }
643 
TEST_F(HiddenApiTest,StaticNativeMethodDarkGreylistMatch)644 TEST_F(HiddenApiTest, StaticNativeMethodDarkGreylistMatch) {
645   ScratchFile dex, flags_csv;
646   OpenStream(flags_csv)
647       << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
648       << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist-max-o" << std::endl
649       << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
650   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
651   ASSERT_NE(dex_file.get(), nullptr);
652   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSNMethodHiddenFlags(*dex_file));
653 }
654 
TEST_F(HiddenApiTest,StaticNativeMethodBlacklistMatch)655 TEST_F(HiddenApiTest, StaticNativeMethodBlacklistMatch) {
656   ScratchFile dex, flags_csv;
657   OpenStream(flags_csv)
658       << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
659       << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
660       << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist" << std::endl;
661   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
662   ASSERT_NE(dex_file.get(), nullptr);
663   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSNMethodHiddenFlags(*dex_file));
664 }
665 
TEST_F(HiddenApiTest,StaticNativeMethodTwoListsMatch1)666 TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch1) {
667   ScratchFile dex, flags_csv;
668   OpenStream(flags_csv)
669       << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
670       << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist-max-o" << std::endl;
671   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
672   ASSERT_EQ(dex_file.get(), nullptr);
673 }
674 
TEST_F(HiddenApiTest,StaticNativeMethodTwoListsMatch2)675 TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch2) {
676   ScratchFile dex, flags_csv;
677   OpenStream(flags_csv)
678       << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist" << std::endl
679       << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl;
680   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
681   ASSERT_EQ(dex_file.get(), nullptr);
682 }
683 
TEST_F(HiddenApiTest,StaticNativeMethodTwoListsMatch3)684 TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch3) {
685   ScratchFile dex, flags_csv;
686   OpenStream(flags_csv)
687       << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist,greylist-max-o" << std::endl
688       << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
689   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
690   ASSERT_EQ(dex_file.get(), nullptr);
691 }
692 
TEST_F(HiddenApiTest,InstanceFieldCorePlatformApiMatch)693 TEST_F(HiddenApiTest, InstanceFieldCorePlatformApiMatch) {
694   ScratchFile dex, flags_csv;
695   OpenStream(flags_csv)
696       << "LMain;->ifield:LBadType1;,greylist" << std::endl
697       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
698       << "LMain;->ifield:I,greylist,core-platform-api" << std::endl;
699   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
700   ASSERT_NE(dex_file.get(), nullptr);
701   ASSERT_EQ(hiddenapi::ApiList::CorePlatformApi() |
702   hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file));
703 }
704 
TEST_F(HiddenApiTest,InstanceFieldTestApiMatch)705 TEST_F(HiddenApiTest, InstanceFieldTestApiMatch) {
706   ScratchFile dex, flags_csv;
707   OpenStream(flags_csv)
708       << "LMain;->ifield:LBadType1;,greylist" << std::endl
709       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
710       << "LMain;->ifield:I,greylist,test-api" << std::endl;
711   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
712   ASSERT_NE(dex_file.get(), nullptr);
713   ASSERT_EQ(hiddenapi::ApiList::TestApi()
714   | hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file));
715 }
716 
TEST_F(HiddenApiTest,InstanceFieldUnknownFlagMatch)717 TEST_F(HiddenApiTest, InstanceFieldUnknownFlagMatch) {
718   ScratchFile dex, flags_csv;
719   OpenStream(flags_csv)
720       << "LMain;->ifield:LBadType1;,greylist" << std::endl
721       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
722       << "LMain;->ifield:I,greylist,unknown-flag" << std::endl;
723   auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
724   ASSERT_EQ(dex_file.get(), nullptr);
725 }
726 
727 // The following tests use this class hierarchy:
728 //
729 //    AbstractPackageClass  PublicInterface
730 //           |                     |
731 //           |    ┌----------------┘
732 //           |    |
733 //        PackageClass
734 //
735 // Only PublicInterface is in stubs.
736 
737 // Test a method declared in PublicInterface and defined in PackageClass.
TEST_F(HiddenApiTest,InterfaceMethodImplemented)738 TEST_F(HiddenApiTest, InterfaceMethodImplemented) {
739   ScratchFile flags_csv;
740   ASSERT_TRUE(RunHiddenapiList(flags_csv));
741   auto flags = ReadFlagsCsvFile(flags_csv);
742   ASSERT_EQ(SafeMapGet("LPackageClass;->publicMethod1()V", flags), "public-api");
743 }
744 
745 // Test a method declared in PublicInterface, defined in AbstractPackageClass and
746 // inherited by PackageClass.
TEST_F(HiddenApiTest,InterfaceMethodImplementedInParent)747 TEST_F(HiddenApiTest, InterfaceMethodImplementedInParent) {
748   ScratchFile flags_csv;
749   ASSERT_TRUE(RunHiddenapiList(flags_csv));
750   auto flags = ReadFlagsCsvFile(flags_csv);
751   ASSERT_EQ(SafeMapGet("LAbstractPackageClass;->publicMethod2()V", flags), "public-api");
752 }
753 
754 }  // namespace art
755