1 /*
2  * Copyright 2022, 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 <jni.h>
18 #include <core_jni_helpers.h>
19 #include <utils/misc.h>
20 #include <androidfw/ResourceTimer.h>
21 
22 namespace android {
23 
24 // ----------------------------------------------------------------------------
25 
26 static struct {
27   jfieldID maxTimer;
28   jfieldID maxBuckets;
29   jfieldID maxLargest;
30   jfieldID timers;
31 } gConfigOffsets;
32 
33 static struct {
34   jfieldID count;
35   jfieldID total;
36   jfieldID mintime;
37   jfieldID maxtime;
38   jfieldID largest;
39   jfieldID percentile;
40 } gTimerOffsets;
41 
42 // ----------------------------------------------------------------------------
43 
44 
NativeGetTimers(JNIEnv * env,jobject,jobjectArray timer,jboolean reset)45 static int NativeGetTimers(JNIEnv* env, jobject /*clazz*/, jobjectArray timer, jboolean reset) {
46   size_t size = ResourceTimer::counterSize;
47   if (jsize st = env->GetArrayLength(timer); st < size) {
48     // Shrink the size to the minimum of the available counters and the available space.
49     size = st;
50   }
51   for (size_t i = 0; i < size; i++) {
52     ResourceTimer::Timer src;
53     ResourceTimer::copy(i, src, reset);
54     jobject dst = env->GetObjectArrayElement(timer, i);
55     env->SetIntField(dst, gTimerOffsets.count, src.count);
56     if (src.count == 0) {
57       continue;
58     }
59 
60     src.compute();
61     env->SetIntField(dst, gTimerOffsets.count, src.count);
62     env->SetLongField(dst, gTimerOffsets.total, src.total);
63     env->SetIntField(dst, gTimerOffsets.mintime, src.mintime);
64     env->SetIntField(dst, gTimerOffsets.maxtime, src.maxtime);
65     jintArray percentile =
66         reinterpret_cast<jintArray>(env->GetObjectField(dst, gTimerOffsets.percentile));
67     env->SetIntArrayRegion(percentile, 0, 1, &src.pvalues.p50.nominal);
68     env->SetIntArrayRegion(percentile, 1, 1, &src.pvalues.p90.nominal);
69     env->SetIntArrayRegion(percentile, 2, 1, &src.pvalues.p95.nominal);
70     env->SetIntArrayRegion(percentile, 3, 1, &src.pvalues.p99.nominal);
71     jintArray largest =
72         reinterpret_cast<jintArray>(env->GetObjectField(dst, gTimerOffsets.largest));
73     env->SetIntArrayRegion(largest, 0, ResourceTimer::Timer::MaxLargest, src.largest);
74   }
75   return size;
76 }
77 
counterName(JNIEnv * env,int counter)78 static jstring counterName(JNIEnv *env, int counter) {
79   char const *s = ResourceTimer::toString(static_cast<ResourceTimer::Counter>(counter));
80   return env->NewStringUTF(s);
81 }
82 
NativeEnableTimers(JNIEnv * env,jobject,jobject config)83 static int NativeEnableTimers(JNIEnv* env, jobject /*clazz*/, jobject config) {
84   ResourceTimer::enable();
85 
86   env->SetIntField(config, gConfigOffsets.maxTimer, ResourceTimer::counterSize);
87   env->SetIntField(config, gConfigOffsets.maxBuckets, 4);       // Number of ints in PValues
88   env->SetIntField(config, gConfigOffsets.maxLargest, ResourceTimer::Timer::MaxLargest);
89 
90   jclass str = env->FindClass("java/lang/String");
91   jstring empty = counterName(env, 0);
92   jobjectArray timers = env->NewObjectArray(ResourceTimer::counterSize, str, empty);
93   for (int i = 0; i < ResourceTimer::counterSize; i++) {
94     env->SetObjectArrayElement(timers, i, counterName(env, i));
95   }
96   env->SetObjectField(config, gConfigOffsets.timers, timers);
97   return 0;
98 }
99 
100 // ----------------------------------------------------------------------------
101 
102 // JNI registration.
103 static const JNINativeMethod gResourceTimerMethods[] = {
104   {"nativeEnableTimers", "(Landroid/content/res/ResourceTimer$Config;)I",
105    (void *) NativeEnableTimers},
106   {"nativeGetTimers", "([Landroid/content/res/ResourceTimer$Timer;Z)I",
107    (void *) NativeGetTimers},
108 };
109 
register_android_content_res_ResourceTimer(JNIEnv * env)110 int register_android_content_res_ResourceTimer(JNIEnv* env) {
111   jclass config = FindClassOrDie(env, "android/content/res/ResourceTimer$Config");
112   gConfigOffsets.maxTimer = GetFieldIDOrDie(env, config, "maxTimer", "I");
113   gConfigOffsets.maxBuckets = GetFieldIDOrDie(env, config, "maxBuckets", "I");
114   gConfigOffsets.maxLargest = GetFieldIDOrDie(env, config, "maxLargest", "I");
115   gConfigOffsets.timers = GetFieldIDOrDie(env, config, "timers", "[Ljava/lang/String;");
116 
117   jclass timers = FindClassOrDie(env, "android/content/res/ResourceTimer$Timer");
118   gTimerOffsets.count = GetFieldIDOrDie(env, timers, "count", "I");
119   gTimerOffsets.total = GetFieldIDOrDie(env, timers, "total", "J");
120   gTimerOffsets.mintime = GetFieldIDOrDie(env, timers, "mintime", "I");
121   gTimerOffsets.maxtime = GetFieldIDOrDie(env, timers, "maxtime", "I");
122   gTimerOffsets.largest = GetFieldIDOrDie(env, timers, "largest", "[I");
123   gTimerOffsets.percentile = GetFieldIDOrDie(env, timers, "percentile", "[I");
124 
125   return RegisterMethodsOrDie(env, "android/content/res/ResourceTimer", gResourceTimerMethods,
126                               NELEM(gResourceTimerMethods));
127 }
128 
129 }; // namespace android
130