1 /*
2 * Copyright 2018 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 "agq.h"
18 #include <jni.h>
19 #include <android/log.h>
20 #include <time.h>
21
22 #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "AndroidGameQualification", __VA_ARGS__))
23
24 static JavaVM* sJavaVm = nullptr;
25 static JNIEnv* sJniEnv = nullptr;
26
27 // Initialize sJavaVm if the app uses JNI.
JNI_OnLoad(JavaVM * vm,void *)28 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
29 sJavaVm = vm;
30 sJavaVm->GetEnv(reinterpret_cast<void**>(&sJniEnv), JNI_VERSION_1_6);
31 return sJniEnv->GetVersion();
32 }
33
34 namespace android {
35
36 static const char* const INTENT_START = "com.android.game.qualification.START";
37
getJniEnv()38 static JNIEnv* getJniEnv() {
39 if (sJniEnv == nullptr) {
40 sJavaVm->AttachCurrentThread(&sJniEnv, nullptr);
41 }
42 return sJniEnv;
43 }
44
45 // Convert timespec to milliseconds.
timespecToMs(timespec spec)46 jlong timespecToMs(timespec spec) {
47 return jlong(spec.tv_sec) * 1000 + spec.tv_nsec / 1000000;
48 }
49
50 // Create an Intent jobject in Java.
createIntent()51 static jobject createIntent() {
52 struct timespec spec;
53 clock_gettime(CLOCK_MONOTONIC, &spec);
54 JNIEnv* env = getJniEnv();
55 jclass intentClass = env->FindClass("android/content/Intent");
56 jmethodID constructor = env->GetMethodID(intentClass, "<init>", "(Ljava/lang/String;)V");
57 jobject intent = env->NewObject(intentClass, constructor, env->NewStringUTF(INTENT_START));
58 jmethodID set_type =
59 env->GetMethodID(
60 intentClass,
61 "setType",
62 "(Ljava/lang/String;)Landroid/content/Intent;");
63 env->CallObjectMethod(intent, set_type, env->NewStringUTF("text/plain"));
64 jmethodID put_extra =
65 env->GetMethodID(
66 intentClass,
67 "putExtra",
68 "(Ljava/lang/String;J)Landroid/content/Intent;");
69
70 jlong timestamp = timespecToMs(spec);
71 env->CallObjectMethod(intent, put_extra, env->NewStringUTF("timestamp"), timestamp);
72
73 LOGD("Created intent %s at %lld", INTENT_START, (long long)timestamp);
74 return intent;
75 }
76
77 // Implementation of android::GameQualification::Impl.
78
79 class GameQualification::Impl {
80 public:
81 void startLoop(jobject context);
82 void startLoop(ANativeActivity* activity);
83 };
84
85
startLoop(jobject context)86 void GameQualification::Impl::startLoop(jobject context) {
87 JNIEnv* env = getJniEnv();
88 jclass contextClass = env->FindClass("android/content/Context");
89 jmethodID method =
90 env->GetMethodID(
91 contextClass,
92 "sendBroadcast",
93 "(Landroid/content/Intent;)V");
94 env->CallVoidMethod(context, method, createIntent());
95 }
96
startLoop(ANativeActivity * activity)97 void GameQualification::Impl::startLoop(ANativeActivity* activity) {
98 sJavaVm = activity->vm;
99 startLoop(activity->clazz);
100 }
101
102 /* Implementation of GameQualification */
103
GameQualification()104 GameQualification::GameQualification() {
105 mImpl = new GameQualification::Impl();
106 }
107
~GameQualification()108 GameQualification::~GameQualification() {
109 delete mImpl;
110 }
111
startLoop(jobject context)112 void GameQualification::startLoop(jobject context) {
113 mImpl->startLoop(context);
114 }
115
startLoop(ANativeActivity * activity)116 void GameQualification::startLoop(ANativeActivity* activity) {
117 mImpl->startLoop(activity);
118 }
119
120 } // end of namespace android
121