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 "class_linker.h"
18#include "dex/art_dex_file_loader.h"
19#include "hidden_api.h"
20#include "jni.h"
21#include "runtime.h"
22#include "scoped_thread_state_change-inl.h"
23#include "thread.h"
24#include "ti-agent/scoped_utf_chars.h"
25
26namespace art {
27namespace Test674HiddenApi {
28
29extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
30  Runtime* runtime = Runtime::Current();
31  runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
32  runtime->SetDedupeHiddenApiWarnings(false);
33  runtime->AlwaysSetHiddenApiWarningFlag();
34}
35
36extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(
37    JNIEnv* env, jclass, jstring jpath) {
38  ScopedUtfChars utf(env, jpath);
39  const char* path = utf.c_str();
40  if (path == nullptr) {
41    return;
42  }
43
44  ArtDexFileLoader dex_loader;
45  std::string error_msg;
46  std::vector<std::unique_ptr<const DexFile>> dex_files;
47  if (!dex_loader.Open(path,
48                       path,
49                       /* verify */ false,
50                       /* verify_checksum */ true,
51                       &error_msg,
52                       &dex_files)) {
53    LOG(FATAL) << "Could not open " << path << " for boot classpath extension: " << error_msg;
54    UNREACHABLE();
55  }
56
57  ScopedObjectAccess soa(Thread::Current());
58  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
59    Runtime::Current()->GetClassLinker()->AppendToBootClassPath(
60        Thread::Current(), *dex_file.release());
61  }
62}
63
64static jobject NewInstance(JNIEnv* env, jclass klass) {
65  jmethodID constructor = env->GetMethodID(klass, "<init>", "()V");
66  if (constructor == NULL) {
67    return NULL;
68  }
69  return env->NewObject(klass, constructor);
70}
71
72extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverField(
73    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
74  ScopedUtfChars utf_name(env, name);
75  jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
76                             : env->GetFieldID(klass, utf_name.c_str(), "I");
77  if (field == NULL) {
78    env->ExceptionClear();
79    return JNI_FALSE;
80  }
81
82  return JNI_TRUE;
83}
84
85extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canGetField(
86    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
87  ScopedUtfChars utf_name(env, name);
88  jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
89                             : env->GetFieldID(klass, utf_name.c_str(), "I");
90  if (field == NULL) {
91    env->ExceptionClear();
92    return JNI_FALSE;
93  }
94  if (is_static) {
95    env->GetStaticIntField(klass, field);
96  } else {
97    jobject obj = NewInstance(env, klass);
98    if (obj == NULL) {
99      env->ExceptionDescribe();
100      env->ExceptionClear();
101      return JNI_FALSE;
102    }
103    env->GetIntField(obj, field);
104  }
105
106  if (env->ExceptionOccurred()) {
107    env->ExceptionDescribe();
108    env->ExceptionClear();
109    return JNI_FALSE;
110  }
111
112  return JNI_TRUE;
113}
114
115extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canSetField(
116    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
117  ScopedUtfChars utf_name(env, name);
118  jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
119                             : env->GetFieldID(klass, utf_name.c_str(), "I");
120  if (field == NULL) {
121    env->ExceptionClear();
122    return JNI_FALSE;
123  }
124  if (is_static) {
125    env->SetStaticIntField(klass, field, 42);
126  } else {
127    jobject obj = NewInstance(env, klass);
128    if (obj == NULL) {
129      env->ExceptionDescribe();
130      env->ExceptionClear();
131      return JNI_FALSE;
132    }
133    env->SetIntField(obj, field, 42);
134  }
135
136  if (env->ExceptionOccurred()) {
137    env->ExceptionDescribe();
138    env->ExceptionClear();
139    return JNI_FALSE;
140  }
141
142  return JNI_TRUE;
143}
144
145extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverMethod(
146    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
147  ScopedUtfChars utf_name(env, name);
148  jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
149                               : env->GetMethodID(klass, utf_name.c_str(), "()I");
150  if (method == NULL) {
151    env->ExceptionClear();
152    return JNI_FALSE;
153  }
154
155  return JNI_TRUE;
156}
157
158extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeMethodA(
159    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
160  ScopedUtfChars utf_name(env, name);
161  jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
162                               : env->GetMethodID(klass, utf_name.c_str(), "()I");
163  if (method == NULL) {
164    env->ExceptionClear();
165    return JNI_FALSE;
166  }
167
168  if (is_static) {
169    env->CallStaticIntMethodA(klass, method, nullptr);
170  } else {
171    jobject obj = NewInstance(env, klass);
172    if (obj == NULL) {
173      env->ExceptionDescribe();
174      env->ExceptionClear();
175      return JNI_FALSE;
176    }
177    env->CallIntMethodA(obj, method, nullptr);
178  }
179
180  if (env->ExceptionOccurred()) {
181    env->ExceptionDescribe();
182    env->ExceptionClear();
183    return JNI_FALSE;
184  }
185
186  return JNI_TRUE;
187}
188
189extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeMethodV(
190    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
191  ScopedUtfChars utf_name(env, name);
192  jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
193                               : env->GetMethodID(klass, utf_name.c_str(), "()I");
194  if (method == NULL) {
195    env->ExceptionClear();
196    return JNI_FALSE;
197  }
198
199  if (is_static) {
200    env->CallStaticIntMethod(klass, method);
201  } else {
202    jobject obj = NewInstance(env, klass);
203    if (obj == NULL) {
204      env->ExceptionDescribe();
205      env->ExceptionClear();
206      return JNI_FALSE;
207    }
208    env->CallIntMethod(obj, method);
209  }
210
211  if (env->ExceptionOccurred()) {
212    env->ExceptionDescribe();
213    env->ExceptionClear();
214    return JNI_FALSE;
215  }
216
217  return JNI_TRUE;
218}
219
220static constexpr size_t kConstructorSignatureLength = 5;  // e.g. (IZ)V
221static constexpr size_t kNumConstructorArgs = kConstructorSignatureLength - 3;
222
223extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverConstructor(
224    JNIEnv* env, jclass, jclass klass, jstring args) {
225  ScopedUtfChars utf_args(env, args);
226  jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
227  if (constructor == NULL) {
228    env->ExceptionClear();
229    return JNI_FALSE;
230  }
231
232  return JNI_TRUE;
233}
234
235extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorA(
236    JNIEnv* env, jclass, jclass klass, jstring args) {
237  ScopedUtfChars utf_args(env, args);
238  jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
239  if (constructor == NULL) {
240    env->ExceptionClear();
241    return JNI_FALSE;
242  }
243
244  // CheckJNI won't allow out-of-range values, so just zero everything.
245  CHECK_EQ(strlen(utf_args.c_str()), kConstructorSignatureLength);
246  size_t initargs_size = sizeof(jvalue) * kNumConstructorArgs;
247  jvalue *initargs = reinterpret_cast<jvalue*>(alloca(initargs_size));
248  memset(initargs, 0, initargs_size);
249
250  env->NewObjectA(klass, constructor, initargs);
251  if (env->ExceptionOccurred()) {
252    env->ExceptionDescribe();
253    env->ExceptionClear();
254    return JNI_FALSE;
255  }
256
257  return JNI_TRUE;
258}
259
260extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorV(
261    JNIEnv* env, jclass, jclass klass, jstring args) {
262  ScopedUtfChars utf_args(env, args);
263  jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
264  if (constructor == NULL) {
265    env->ExceptionClear();
266    return JNI_FALSE;
267  }
268
269  // CheckJNI won't allow out-of-range values, so just zero everything.
270  CHECK_EQ(strlen(utf_args.c_str()), kConstructorSignatureLength);
271  size_t initargs_size = sizeof(jvalue) * kNumConstructorArgs;
272  jvalue *initargs = reinterpret_cast<jvalue*>(alloca(initargs_size));
273  memset(initargs, 0, initargs_size);
274
275  static_assert(kNumConstructorArgs == 2, "Change the varargs below if you change the constant");
276  env->NewObject(klass, constructor, initargs[0], initargs[1]);
277  if (env->ExceptionOccurred()) {
278    env->ExceptionDescribe();
279    env->ExceptionClear();
280    return JNI_FALSE;
281  }
282
283  return JNI_TRUE;
284}
285
286extern "C" JNIEXPORT jint JNICALL Java_Reflection_getHiddenApiAccessFlags(JNIEnv*, jclass) {
287  return static_cast<jint>(kAccHiddenApiBits);
288}
289
290extern "C" JNIEXPORT jboolean JNICALL Java_ChildClass_hasPendingWarning(JNIEnv*, jclass) {
291  return Runtime::Current()->HasPendingHiddenApiWarning();
292}
293
294extern "C" JNIEXPORT void JNICALL Java_ChildClass_clearWarning(JNIEnv*, jclass) {
295  Runtime::Current()->SetPendingHiddenApiWarning(false);
296}
297
298}  // namespace Test674HiddenApi
299}  // namespace art
300