/* * 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 #include "android-base/strings.h" #include "base/unix_file/fd_file.h" #include "base/zip_archive.h" #include "common_runtime_test.h" #include "dex/art_dex_file_loader.h" #include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "exec_utils.h" namespace art { class HiddenApiTest : public CommonRuntimeTest { protected: std::string GetHiddenApiCmd() { std::string file_path = GetArtBinDir() + "/hiddenapi"; if (kIsDebugBuild) { file_path += 'd'; } if (!OS::FileExists(file_path.c_str())) { LOG(FATAL) << "Could not find binary " << file_path; UNREACHABLE(); } return file_path; } std::unique_ptr RunHiddenapiEncode(const ScratchFile& flags_csv, const std::vector& extra_args, const ScratchFile& out_dex) { std::string error; ScratchFile in_dex; std::unique_ptr jar( ZipArchive::Open(GetTestDexFileName("HiddenApi").c_str(), &error)); if (jar == nullptr) { LOG(FATAL) << "Could not open test file " << GetTestDexFileName("HiddenApi") << ": " << error; UNREACHABLE(); } std::unique_ptr jar_classes_dex(jar->Find("classes.dex", &error)); if (jar_classes_dex == nullptr) { LOG(FATAL) << "Could not find classes.dex in test file " << GetTestDexFileName("HiddenApi") << ": " << error; UNREACHABLE(); } else if (!jar_classes_dex->ExtractToFile(*in_dex.GetFile(), &error)) { LOG(FATAL) << "Could not extract classes.dex from test file " << GetTestDexFileName("HiddenApi") << ": " << error; UNREACHABLE(); } std::vector argv_str; argv_str.push_back(GetHiddenApiCmd()); argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end()); argv_str.push_back("encode"); argv_str.push_back("--input-dex=" + in_dex.GetFilename()); argv_str.push_back("--output-dex=" + out_dex.GetFilename()); argv_str.push_back("--api-flags=" + flags_csv.GetFilename()); argv_str.push_back("--no-force-assign-all"); int return_code = ExecAndReturnCode(argv_str, &error); if (return_code == 0) { return OpenDex(out_dex); } else { LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code; return nullptr; } } bool RunHiddenapiList(const ScratchFile& out_flags_csv) { std::string error; std::string boot_jar = GetTestDexFileName("HiddenApi"); std::string stub_jar = GetTestDexFileName("HiddenApiStubs"); std::string boot_cp = android::base::Join(GetLibCoreDexFileNames(), ":"); std::vector argv_str; argv_str.push_back(GetHiddenApiCmd()); argv_str.push_back("list"); for (const std::string& core_jar : GetLibCoreDexFileNames()) { argv_str.push_back("--boot-dex=" + core_jar); } argv_str.push_back("--boot-dex=" + boot_jar); argv_str.push_back("--public-stub-classpath=" + boot_cp + ":" + stub_jar); argv_str.push_back("--out-api-flags=" + out_flags_csv.GetFilename()); int return_code = ExecAndReturnCode(argv_str, &error); if (return_code == 0) { return true; } else { LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code; return false; } } std::unique_ptr OpenDex(const ScratchFile& file) { ArtDexFileLoader dex_loader; std::string error_msg; File fd(file.GetFilename(), O_RDONLY, /* check_usage= */ false); if (fd.Fd() == -1) { PLOG(FATAL) << "Unable to open file '" << file.GetFilename() << "'"; UNREACHABLE(); } std::unique_ptr dex_file(dex_loader.OpenDex( fd.Release(), /* location= */ file.GetFilename(), /* verify= */ true, /* verify_checksum= */ true, /* mmap_shared= */ false, &error_msg)); if (dex_file.get() == nullptr) { LOG(FATAL) << "Open failed for '" << file.GetFilename() << "' " << error_msg; UNREACHABLE(); } else if (!dex_file->IsStandardDexFile()) { LOG(FATAL) << "Expected a standard dex file '" << file.GetFilename() << "'"; UNREACHABLE(); } return dex_file; } std::ofstream OpenStream(const ScratchFile& file) { std::ofstream ofs(file.GetFilename(), std::ofstream::out); if (ofs.fail()) { PLOG(FATAL) << "Open failed for '" << file.GetFilename() << "'"; UNREACHABLE(); } return ofs; } std::map ReadFlagsCsvFile(const ScratchFile& file) { std::ifstream ifs(file.GetFilename()); std::map flags; for (std::string line; std::getline(ifs, line);) { std::size_t comma = line.find(","); if (comma == std::string::npos) { flags.emplace(line, ""); } else { flags.emplace(line.substr(0, comma), line.substr(comma + 1)); } } return flags; } std::string SafeMapGet(const std::string& key, const std::map& map) { auto it = map.find(key); if (it == map.end()) { LOG(FATAL) << "Key not found: " << key; UNREACHABLE(); } return it->second; } const dex::ClassDef& FindClass(const char* desc, const DexFile& dex_file) { const dex::TypeId* type_id = dex_file.FindTypeId(desc); CHECK(type_id != nullptr) << "Could not find class " << desc; const dex::ClassDef* found = dex_file.FindClassDef(dex_file.GetIndexForTypeId(*type_id)); CHECK(found != nullptr) << "Could not find class " << desc; return *found; } hiddenapi::ApiList GetFieldHiddenFlags(const char* name, uint32_t expected_visibility, const dex::ClassDef& class_def, const DexFile& dex_file) { ClassAccessor accessor(dex_file, class_def, /* parse hiddenapi flags */ true); CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data"; if (!accessor.HasHiddenapiClassData()) { return hiddenapi::ApiList::Whitelist(); } for (const ClassAccessor::Field& field : accessor.GetFields()) { const dex::FieldId& fid = dex_file.GetFieldId(field.GetIndex()); if (strcmp(name, dex_file.GetFieldName(fid)) == 0) { const uint32_t actual_visibility = field.GetAccessFlags() & kAccVisibilityFlags; CHECK_EQ(actual_visibility, expected_visibility) << "Field " << name << " in class " << accessor.GetDescriptor(); return hiddenapi::ApiList(field.GetHiddenapiFlags()); } } LOG(FATAL) << "Could not find field " << name << " in class " << dex_file.GetClassDescriptor(class_def); UNREACHABLE(); } hiddenapi::ApiList GetMethodHiddenFlags(const char* name, uint32_t expected_visibility, bool expected_native, const dex::ClassDef& class_def, const DexFile& dex_file) { ClassAccessor accessor(dex_file, class_def, /* parse hiddenapi flags */ true); CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data"; if (!accessor.HasHiddenapiClassData()) { return hiddenapi::ApiList::Whitelist(); } for (const ClassAccessor::Method& method : accessor.GetMethods()) { const dex::MethodId& mid = dex_file.GetMethodId(method.GetIndex()); if (strcmp(name, dex_file.GetMethodName(mid)) == 0) { CHECK_EQ(expected_native, method.MemberIsNative()) << "Method " << name << " in class " << accessor.GetDescriptor(); const uint32_t actual_visibility = method.GetAccessFlags() & kAccVisibilityFlags; CHECK_EQ(actual_visibility, expected_visibility) << "Method " << name << " in class " << accessor.GetDescriptor(); return hiddenapi::ApiList(method.GetHiddenapiFlags()); } } LOG(FATAL) << "Could not find method " << name << " in class " << dex_file.GetClassDescriptor(class_def); UNREACHABLE(); } hiddenapi::ApiList GetIFieldHiddenFlags(const DexFile& dex_file) { return GetFieldHiddenFlags("ifield", kAccPublic, FindClass("LMain;", dex_file), dex_file); } hiddenapi::ApiList GetSFieldHiddenFlags(const DexFile& dex_file) { return GetFieldHiddenFlags("sfield", kAccPrivate, FindClass("LMain;", dex_file), dex_file); } hiddenapi::ApiList GetIMethodHiddenFlags(const DexFile& dex_file) { return GetMethodHiddenFlags( "imethod", 0, /* expected_native= */ false, FindClass("LMain;", dex_file), dex_file); } hiddenapi::ApiList GetSMethodHiddenFlags(const DexFile& dex_file) { return GetMethodHiddenFlags("smethod", kAccPublic, /* expected_native= */ false, FindClass("LMain;", dex_file), dex_file); } hiddenapi::ApiList GetINMethodHiddenFlags(const DexFile& dex_file) { return GetMethodHiddenFlags("inmethod", kAccPublic, /* expected_native= */ true, FindClass("LMain;", dex_file), dex_file); } hiddenapi::ApiList GetSNMethodHiddenFlags(const DexFile& dex_file) { return GetMethodHiddenFlags("snmethod", kAccProtected, /* expected_native= */ true, FindClass("LMain;", dex_file), dex_file); } }; TEST_F(HiddenApiTest, InstanceFieldNoMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:LBadType3;,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldLightGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->ifield:I,greylist" << std::endl << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:LBadType3;,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldDarkGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:I,greylist-max-o" << std::endl << "LMain;->ifield:LBadType3;,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldBlacklistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:I,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch1) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:I,blacklist,greylist-max-o" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch2) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:I,blacklist,greylist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch3) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->ifield:I,greylist,greylist-max-o" << std::endl << "LMain;->ifield:LBadType3;,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, StaticFieldNoMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->sfield:LBadType1;,greylist" << std::endl << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl << "LMain;->sfield:LBadType3;,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticFieldLightGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->sfield:Ljava/lang/Object;,greylist" << std::endl << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl << "LMain;->sfield:LBadType3;,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticFieldDarkGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->sfield:LBadType1;,greylist" << std::endl << "LMain;->sfield:Ljava/lang/Object;,greylist-max-o" << std::endl << "LMain;->sfield:LBadType3;,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticFieldBlacklistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->sfield:LBadType1;,greylist" << std::endl << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl << "LMain;->sfield:Ljava/lang/Object;,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticFieldTwoListsMatch1) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->sfield:LBadType1;,greylist" << std::endl << "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist-max-o" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, StaticFieldTwoListsMatch2) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl << "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, StaticFieldTwoListsMatch3) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->sfield:Ljava/lang/Object;,greylist,greylist-max-o" << std::endl << "LMain;->sfield:LBadType3;,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, InstanceMethodNoMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->imethod(LBadType1;)V,greylist" << std::endl << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceMethodLightGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->imethod(J)V,greylist" << std::endl << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceMethodDarkGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->imethod(LBadType1;)V,greylist" << std::endl << "LMain;->imethod(J)V,greylist-max-o" << std::endl << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceMethodBlacklistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->imethod(LBadType1;)V,greylist" << std::endl << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->imethod(J)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch1) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->imethod(LBadType1;)V,greylist" << std::endl << "LMain;->imethod(J)V,blacklist,greylist-max-o" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch2) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->imethod(J)V,blacklist,greylist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch3) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->imethod(J)V,greylist,greylist-max-o" << std::endl << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, StaticMethodNoMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->smethod(LBadType1;)V,greylist" << std::endl << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticMethodLightGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->smethod(Ljava/lang/Object;)V,greylist" << std::endl << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticMethodDarkGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->smethod(LBadType1;)V,greylist" << std::endl << "LMain;->smethod(Ljava/lang/Object;)V,greylist-max-o" << std::endl << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticMethodBlacklistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->smethod(LBadType1;)V,greylist" << std::endl << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->smethod(Ljava/lang/Object;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticMethodTwoListsMatch1) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->smethod(LBadType1;)V,greylist" << std::endl << "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist-max-o" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, StaticMethodTwoListsMatch2) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, StaticMethodTwoListsMatch3) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->smethod(Ljava/lang/Object;)V,greylist,greylist-max-o" << std::endl << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, InstanceNativeMethodNoMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetINMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceNativeMethodLightGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->inmethod(C)V,greylist" << std::endl << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetINMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceNativeMethodDarkGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl << "LMain;->inmethod(C)V,greylist-max-o" << std::endl << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetINMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceNativeMethodBlacklistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->inmethod(C)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetINMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch1) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl << "LMain;->inmethod(C)V,blacklist,greylist-max-o" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch2) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->inmethod(C)V,blacklist,greylist" << std::endl << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch3) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->inmethod(C)V,greylist,greylist-max-o" << std::endl << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, StaticNativeMethodNoMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSNMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticNativeMethodLightGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist" << std::endl << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSNMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticNativeMethodDarkGreylistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist-max-o" << std::endl << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSNMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticNativeMethodBlacklistMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSNMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch1) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist-max-o" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch2) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist" << std::endl << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch3) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist,greylist-max-o" << std::endl << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } TEST_F(HiddenApiTest, InstanceFieldCorePlatformApiMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:I,greylist,core-platform-api" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::CorePlatformApi() | hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldTestApiMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:I,greylist,test-api" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); ASSERT_EQ(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldUnknownFlagMatch) { ScratchFile dex, flags_csv; OpenStream(flags_csv) << "LMain;->ifield:LBadType1;,greylist" << std::endl << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl << "LMain;->ifield:I,greylist,unknown-flag" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_EQ(dex_file.get(), nullptr); } // The following tests use this class hierarchy: // // AbstractPackageClass PublicInterface // | | // | ┌----------------┘ // | | // PackageClass // // Only PublicInterface is in stubs. // Test a method declared in PublicInterface and defined in PackageClass. TEST_F(HiddenApiTest, InterfaceMethodImplemented) { ScratchFile flags_csv; ASSERT_TRUE(RunHiddenapiList(flags_csv)); auto flags = ReadFlagsCsvFile(flags_csv); ASSERT_EQ(SafeMapGet("LPackageClass;->publicMethod1()V", flags), "public-api"); } // Test a method declared in PublicInterface, defined in AbstractPackageClass and // inherited by PackageClass. TEST_F(HiddenApiTest, InterfaceMethodImplementedInParent) { ScratchFile flags_csv; ASSERT_TRUE(RunHiddenapiList(flags_csv)); auto flags = ReadFlagsCsvFile(flags_csv); ASSERT_EQ(SafeMapGet("LAbstractPackageClass;->publicMethod2()V", flags), "public-api"); } } // namespace art