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