David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 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 | |
David Brazdil | 2bb2fbd | 2018-11-13 18:24:26 +0000 | [diff] [blame] | 17 | #include "base/sdk_version.h" |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 18 | #include "class_linker.h" |
| 19 | #include "dex/art_dex_file_loader.h" |
Mathew Inwood | 597d7f6 | 2018-03-22 11:36:47 +0000 | [diff] [blame] | 20 | #include "hidden_api.h" |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 21 | #include "jni.h" |
| 22 | #include "runtime.h" |
| 23 | #include "scoped_thread_state_change-inl.h" |
| 24 | #include "thread.h" |
| 25 | #include "ti-agent/scoped_utf_chars.h" |
| 26 | |
| 27 | namespace art { |
| 28 | namespace Test674HiddenApi { |
| 29 | |
Andrei Onea | 7992467 | 2020-07-30 14:23:00 +0100 | [diff] [blame] | 30 | // Should be the same as dalvik.system.VMRuntime.PREVENT_META_REFLECTION_BLOCKLIST_ACCESS |
| 31 | static constexpr uint64_t kPreventMetaReflectionBlocklistAccess = 142365358; |
Andrei Onea | 6442eaf | 2020-01-08 16:55:56 +0000 | [diff] [blame] | 32 | |
David Brazdil | e768182 | 2018-12-14 16:25:33 +0000 | [diff] [blame] | 33 | std::vector<std::vector<std::unique_ptr<const DexFile>>> opened_dex_files; |
| 34 | |
David Brazdil | 5a61bb7 | 2018-01-19 16:59:46 +0000 | [diff] [blame] | 35 | extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) { |
David Brazdil | 9226522 | 2018-02-02 11:21:40 +0000 | [diff] [blame] | 36 | Runtime* runtime = Runtime::Current(); |
David Brazdil | f50ac10 | 2018-10-17 18:00:06 +0100 | [diff] [blame] | 37 | runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); |
David Brazdil | e768182 | 2018-12-14 16:25:33 +0000 | [diff] [blame] | 38 | runtime->SetCorePlatformApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); |
David Brazdil | dcfa89b | 2018-10-31 11:04:10 +0000 | [diff] [blame] | 39 | runtime->SetTargetSdkVersion( |
Andrei Onea | fc12a6c | 2020-07-29 19:52:34 +0100 | [diff] [blame] | 40 | static_cast<uint32_t>(hiddenapi::ApiList::MaxTargetO().GetMaxAllowedSdkVersion())); |
David Brazdil | 9226522 | 2018-02-02 11:21:40 +0000 | [diff] [blame] | 41 | runtime->SetDedupeHiddenApiWarnings(false); |
David Brazdil | 5a61bb7 | 2018-01-19 16:59:46 +0000 | [diff] [blame] | 42 | } |
| 43 | |
David Brazdil | e768182 | 2018-12-14 16:25:33 +0000 | [diff] [blame] | 44 | extern "C" JNIEXPORT void JNICALL Java_Main_setDexDomain( |
| 45 | JNIEnv*, jclass, jint int_index, jboolean is_core_platform) { |
| 46 | size_t index = static_cast<size_t>(int_index); |
| 47 | CHECK_LT(index, opened_dex_files.size()); |
| 48 | for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) { |
| 49 | const_cast<DexFile*>(dex_file.get())->SetHiddenapiDomain( |
| 50 | (is_core_platform == JNI_FALSE) ? hiddenapi::Domain::kPlatform |
| 51 | : hiddenapi::Domain::kCorePlatform); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | extern "C" JNIEXPORT jint JNICALL Java_Main_appendToBootClassLoader( |
| 56 | JNIEnv* env, jclass klass, jstring jpath, jboolean is_core_platform) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 57 | ScopedUtfChars utf(env, jpath); |
| 58 | const char* path = utf.c_str(); |
David Brazdil | e768182 | 2018-12-14 16:25:33 +0000 | [diff] [blame] | 59 | CHECK(path != nullptr); |
| 60 | |
| 61 | const size_t index = opened_dex_files.size(); |
| 62 | const jint int_index = static_cast<jint>(index); |
| 63 | opened_dex_files.push_back(std::vector<std::unique_ptr<const DexFile>>()); |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 64 | |
| 65 | ArtDexFileLoader dex_loader; |
| 66 | std::string error_msg; |
David Brazdil | e768182 | 2018-12-14 16:25:33 +0000 | [diff] [blame] | 67 | |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 68 | if (!dex_loader.Open(path, |
| 69 | path, |
| 70 | /* verify */ false, |
| 71 | /* verify_checksum */ true, |
| 72 | &error_msg, |
David Brazdil | e768182 | 2018-12-14 16:25:33 +0000 | [diff] [blame] | 73 | &opened_dex_files[index])) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 74 | LOG(FATAL) << "Could not open " << path << " for boot classpath extension: " << error_msg; |
| 75 | UNREACHABLE(); |
| 76 | } |
| 77 | |
David Brazdil | e768182 | 2018-12-14 16:25:33 +0000 | [diff] [blame] | 78 | Java_Main_setDexDomain(env, klass, int_index, is_core_platform); |
| 79 | |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 80 | ScopedObjectAccess soa(Thread::Current()); |
David Brazdil | e768182 | 2018-12-14 16:25:33 +0000 | [diff] [blame] | 81 | for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) { |
Mathieu Chartier | 0a19e21 | 2019-11-27 14:35:24 -0800 | [diff] [blame] | 82 | Runtime::Current()->GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file.get()); |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 83 | } |
David Brazdil | e768182 | 2018-12-14 16:25:33 +0000 | [diff] [blame] | 84 | |
| 85 | return int_index; |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 86 | } |
| 87 | |
Andrei Onea | fc12a6c | 2020-07-29 19:52:34 +0100 | [diff] [blame] | 88 | extern "C" JNIEXPORT void JNICALL Java_Main_setSdkAll(JNIEnv*, jclass, jboolean value) { |
David Brazdil | 74d4884 | 2019-01-22 16:06:25 +0000 | [diff] [blame] | 89 | std::vector<std::string> exemptions; |
| 90 | if (value != JNI_FALSE) { |
| 91 | exemptions.push_back("L"); |
| 92 | } |
| 93 | Runtime::Current()->SetHiddenApiExemptions(exemptions); |
| 94 | } |
| 95 | |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 96 | static jobject NewInstance(JNIEnv* env, jclass klass) { |
| 97 | jmethodID constructor = env->GetMethodID(klass, "<init>", "()V"); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 98 | if (constructor == nullptr) { |
| 99 | return nullptr; |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 100 | } |
| 101 | return env->NewObject(klass, constructor); |
| 102 | } |
| 103 | |
| 104 | extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverField( |
| 105 | JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) { |
| 106 | ScopedUtfChars utf_name(env, name); |
| 107 | jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I") |
| 108 | : env->GetFieldID(klass, utf_name.c_str(), "I"); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 109 | if (field == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 110 | env->ExceptionClear(); |
| 111 | return JNI_FALSE; |
| 112 | } |
| 113 | |
| 114 | return JNI_TRUE; |
| 115 | } |
| 116 | |
| 117 | extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canGetField( |
| 118 | JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) { |
| 119 | ScopedUtfChars utf_name(env, name); |
| 120 | jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I") |
| 121 | : env->GetFieldID(klass, utf_name.c_str(), "I"); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 122 | if (field == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 123 | env->ExceptionClear(); |
| 124 | return JNI_FALSE; |
| 125 | } |
| 126 | if (is_static) { |
| 127 | env->GetStaticIntField(klass, field); |
| 128 | } else { |
| 129 | jobject obj = NewInstance(env, klass); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 130 | if (obj == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 131 | env->ExceptionDescribe(); |
| 132 | env->ExceptionClear(); |
| 133 | return JNI_FALSE; |
| 134 | } |
| 135 | env->GetIntField(obj, field); |
| 136 | } |
| 137 | |
| 138 | if (env->ExceptionOccurred()) { |
| 139 | env->ExceptionDescribe(); |
| 140 | env->ExceptionClear(); |
| 141 | return JNI_FALSE; |
| 142 | } |
| 143 | |
| 144 | return JNI_TRUE; |
| 145 | } |
| 146 | |
| 147 | extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canSetField( |
| 148 | JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) { |
| 149 | ScopedUtfChars utf_name(env, name); |
| 150 | jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I") |
| 151 | : env->GetFieldID(klass, utf_name.c_str(), "I"); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 152 | if (field == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 153 | env->ExceptionClear(); |
| 154 | return JNI_FALSE; |
| 155 | } |
| 156 | if (is_static) { |
| 157 | env->SetStaticIntField(klass, field, 42); |
| 158 | } else { |
| 159 | jobject obj = NewInstance(env, klass); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 160 | if (obj == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 161 | env->ExceptionDescribe(); |
| 162 | env->ExceptionClear(); |
| 163 | return JNI_FALSE; |
| 164 | } |
| 165 | env->SetIntField(obj, field, 42); |
| 166 | } |
| 167 | |
| 168 | if (env->ExceptionOccurred()) { |
| 169 | env->ExceptionDescribe(); |
| 170 | env->ExceptionClear(); |
| 171 | return JNI_FALSE; |
| 172 | } |
| 173 | |
| 174 | return JNI_TRUE; |
| 175 | } |
| 176 | |
| 177 | extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverMethod( |
| 178 | JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) { |
| 179 | ScopedUtfChars utf_name(env, name); |
| 180 | jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I") |
| 181 | : env->GetMethodID(klass, utf_name.c_str(), "()I"); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 182 | if (method == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 183 | env->ExceptionClear(); |
| 184 | return JNI_FALSE; |
| 185 | } |
| 186 | |
| 187 | return JNI_TRUE; |
| 188 | } |
| 189 | |
| 190 | extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeMethodA( |
| 191 | JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) { |
| 192 | ScopedUtfChars utf_name(env, name); |
| 193 | jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I") |
| 194 | : env->GetMethodID(klass, utf_name.c_str(), "()I"); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 195 | if (method == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 196 | env->ExceptionClear(); |
| 197 | return JNI_FALSE; |
| 198 | } |
| 199 | |
| 200 | if (is_static) { |
| 201 | env->CallStaticIntMethodA(klass, method, nullptr); |
| 202 | } else { |
| 203 | jobject obj = NewInstance(env, klass); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 204 | if (obj == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 205 | env->ExceptionDescribe(); |
| 206 | env->ExceptionClear(); |
| 207 | return JNI_FALSE; |
| 208 | } |
| 209 | env->CallIntMethodA(obj, method, nullptr); |
| 210 | } |
| 211 | |
| 212 | if (env->ExceptionOccurred()) { |
| 213 | env->ExceptionDescribe(); |
| 214 | env->ExceptionClear(); |
| 215 | return JNI_FALSE; |
| 216 | } |
| 217 | |
| 218 | return JNI_TRUE; |
| 219 | } |
| 220 | |
| 221 | extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeMethodV( |
| 222 | JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) { |
| 223 | ScopedUtfChars utf_name(env, name); |
| 224 | jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I") |
| 225 | : env->GetMethodID(klass, utf_name.c_str(), "()I"); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 226 | if (method == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 227 | env->ExceptionClear(); |
| 228 | return JNI_FALSE; |
| 229 | } |
| 230 | |
| 231 | if (is_static) { |
| 232 | env->CallStaticIntMethod(klass, method); |
| 233 | } else { |
| 234 | jobject obj = NewInstance(env, klass); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 235 | if (obj == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 236 | env->ExceptionDescribe(); |
| 237 | env->ExceptionClear(); |
| 238 | return JNI_FALSE; |
| 239 | } |
| 240 | env->CallIntMethod(obj, method); |
| 241 | } |
| 242 | |
| 243 | if (env->ExceptionOccurred()) { |
| 244 | env->ExceptionDescribe(); |
| 245 | env->ExceptionClear(); |
| 246 | return JNI_FALSE; |
| 247 | } |
| 248 | |
| 249 | return JNI_TRUE; |
| 250 | } |
| 251 | |
| 252 | static constexpr size_t kConstructorSignatureLength = 5; // e.g. (IZ)V |
| 253 | static constexpr size_t kNumConstructorArgs = kConstructorSignatureLength - 3; |
| 254 | |
| 255 | extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverConstructor( |
| 256 | JNIEnv* env, jclass, jclass klass, jstring args) { |
| 257 | ScopedUtfChars utf_args(env, args); |
| 258 | jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str()); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 259 | if (constructor == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 260 | env->ExceptionClear(); |
| 261 | return JNI_FALSE; |
| 262 | } |
| 263 | |
| 264 | return JNI_TRUE; |
| 265 | } |
| 266 | |
| 267 | extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorA( |
| 268 | JNIEnv* env, jclass, jclass klass, jstring args) { |
| 269 | ScopedUtfChars utf_args(env, args); |
| 270 | jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str()); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 271 | if (constructor == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 272 | env->ExceptionClear(); |
| 273 | return JNI_FALSE; |
| 274 | } |
| 275 | |
| 276 | // CheckJNI won't allow out-of-range values, so just zero everything. |
| 277 | CHECK_EQ(strlen(utf_args.c_str()), kConstructorSignatureLength); |
| 278 | size_t initargs_size = sizeof(jvalue) * kNumConstructorArgs; |
| 279 | jvalue *initargs = reinterpret_cast<jvalue*>(alloca(initargs_size)); |
| 280 | memset(initargs, 0, initargs_size); |
| 281 | |
| 282 | env->NewObjectA(klass, constructor, initargs); |
| 283 | if (env->ExceptionOccurred()) { |
| 284 | env->ExceptionDescribe(); |
| 285 | env->ExceptionClear(); |
| 286 | return JNI_FALSE; |
| 287 | } |
| 288 | |
| 289 | return JNI_TRUE; |
| 290 | } |
| 291 | |
| 292 | extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorV( |
| 293 | JNIEnv* env, jclass, jclass klass, jstring args) { |
| 294 | ScopedUtfChars utf_args(env, args); |
| 295 | jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str()); |
Yi Kong | 4b22b34 | 2018-08-02 14:43:21 -0700 | [diff] [blame] | 296 | if (constructor == nullptr) { |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 297 | env->ExceptionClear(); |
| 298 | return JNI_FALSE; |
| 299 | } |
| 300 | |
| 301 | // CheckJNI won't allow out-of-range values, so just zero everything. |
| 302 | CHECK_EQ(strlen(utf_args.c_str()), kConstructorSignatureLength); |
| 303 | size_t initargs_size = sizeof(jvalue) * kNumConstructorArgs; |
| 304 | jvalue *initargs = reinterpret_cast<jvalue*>(alloca(initargs_size)); |
| 305 | memset(initargs, 0, initargs_size); |
| 306 | |
| 307 | static_assert(kNumConstructorArgs == 2, "Change the varargs below if you change the constant"); |
| 308 | env->NewObject(klass, constructor, initargs[0], initargs[1]); |
| 309 | if (env->ExceptionOccurred()) { |
| 310 | env->ExceptionDescribe(); |
| 311 | env->ExceptionClear(); |
| 312 | return JNI_FALSE; |
| 313 | } |
| 314 | |
| 315 | return JNI_TRUE; |
| 316 | } |
| 317 | |
| 318 | extern "C" JNIEXPORT jint JNICALL Java_Reflection_getHiddenApiAccessFlags(JNIEnv*, jclass) { |
David Brazdil | 8586569 | 2018-10-30 17:26:20 +0000 | [diff] [blame] | 319 | return static_cast<jint>(kAccHiddenapiBits); |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 320 | } |
| 321 | |
Andrei Onea | 6442eaf | 2020-01-08 16:55:56 +0000 | [diff] [blame] | 322 | extern "C" JNIEXPORT void JNICALL Java_Reflection_setHiddenApiCheckHardening(JNIEnv*, jclass, |
| 323 | jboolean value) { |
Andrei Onea | 037d282 | 2020-11-19 00:20:04 +0000 | [diff] [blame] | 324 | CompatFramework& compat_framework = Runtime::Current()->GetCompatFramework(); |
| 325 | std::set<uint64_t> disabled_changes = compat_framework.GetDisabledCompatChanges(); |
Andrei Onea | 6442eaf | 2020-01-08 16:55:56 +0000 | [diff] [blame] | 326 | if (value == JNI_TRUE) { |
| 327 | // If hidden api check hardening is enabled, remove it from the set of disabled changes. |
Andrei Onea | 7992467 | 2020-07-30 14:23:00 +0100 | [diff] [blame] | 328 | disabled_changes.erase(kPreventMetaReflectionBlocklistAccess); |
Andrei Onea | 6442eaf | 2020-01-08 16:55:56 +0000 | [diff] [blame] | 329 | } else { |
| 330 | // If hidden api check hardening is disabled, add it to the set of disabled changes. |
Andrei Onea | 7992467 | 2020-07-30 14:23:00 +0100 | [diff] [blame] | 331 | disabled_changes.insert(kPreventMetaReflectionBlocklistAccess); |
Andrei Onea | 6442eaf | 2020-01-08 16:55:56 +0000 | [diff] [blame] | 332 | } |
Andrei Onea | 037d282 | 2020-11-19 00:20:04 +0000 | [diff] [blame] | 333 | compat_framework.SetDisabledCompatChanges(disabled_changes); |
Andrei Onea | 6442eaf | 2020-01-08 16:55:56 +0000 | [diff] [blame] | 334 | } |
| 335 | |
David Brazdil | 11b67b2 | 2018-01-18 16:41:40 +0000 | [diff] [blame] | 336 | } // namespace Test674HiddenApi |
| 337 | } // namespace art |