blob: febcb55bd0cdd02ba1de593e409eba76ea71fe2a [file] [log] [blame]
Fedor Kudasovfa4e4602019-07-01 14:03:58 +01001/*
2 * Copyright (C) 2019 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 "android_view_FrameMetricsObserver.h"
18
19namespace android {
20
21struct {
22 jfieldID frameMetrics;
23 jfieldID timingDataBuffer;
24 jfieldID messageQueue;
25 jmethodID callback;
26} gFrameMetricsObserverClassInfo;
27
28static JNIEnv* getenv(JavaVM* vm) {
29 JNIEnv* env;
30 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
31 LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
32 }
33 return env;
34}
35
36static jlongArray get_metrics_buffer(JNIEnv* env, jobject observer) {
37 jobject frameMetrics = env->GetObjectField(
38 observer, gFrameMetricsObserverClassInfo.frameMetrics);
39 LOG_ALWAYS_FATAL_IF(frameMetrics == nullptr, "unable to retrieve data sink object");
40 jobject buffer = env->GetObjectField(
41 frameMetrics, gFrameMetricsObserverClassInfo.timingDataBuffer);
42 LOG_ALWAYS_FATAL_IF(buffer == nullptr, "unable to retrieve data sink buffer");
43 return reinterpret_cast<jlongArray>(buffer);
44}
45
46class NotifyHandler : public MessageHandler {
47public:
48 NotifyHandler(JavaVM* vm, FrameMetricsObserverProxy* observer) : mVm(vm), mObserver(observer) {}
49
50 virtual void handleMessage(const Message& message);
51
52private:
53 JavaVM* const mVm;
54 FrameMetricsObserverProxy* const mObserver;
55};
56
57void NotifyHandler::handleMessage(const Message& message) {
58 JNIEnv* env = getenv(mVm);
59
60 jobject target = env->NewLocalRef(mObserver->getObserverReference());
61
62 if (target != nullptr) {
63 jlongArray javaBuffer = get_metrics_buffer(env, target);
64 int dropCount = 0;
65 while (mObserver->getNextBuffer(env, javaBuffer, &dropCount)) {
66 env->CallVoidMethod(target, gFrameMetricsObserverClassInfo.callback, dropCount);
67 }
68 env->DeleteLocalRef(target);
69 }
70
71 mObserver->decStrong(nullptr);
72}
73
74FrameMetricsObserverProxy::FrameMetricsObserverProxy(JavaVM *vm, jobject observer) : mVm(vm) {
75 JNIEnv* env = getenv(mVm);
76
77 mObserverWeak = env->NewWeakGlobalRef(observer);
78 LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
79 "unable to create frame stats observer reference");
80
81 jlongArray buffer = get_metrics_buffer(env, observer);
82 jsize bufferSize = env->GetArrayLength(reinterpret_cast<jarray>(buffer));
83 LOG_ALWAYS_FATAL_IF(bufferSize != kBufferSize,
84 "Mismatched Java/Native FrameMetrics data format.");
85
86 jobject messageQueueLocal = env->GetObjectField(
87 observer, gFrameMetricsObserverClassInfo.messageQueue);
88 mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal);
89 LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available");
90
91 mMessageHandler = new NotifyHandler(mVm, this);
92 LOG_ALWAYS_FATAL_IF(mMessageHandler == nullptr,
93 "OOM: unable to allocate NotifyHandler");
94}
95
96FrameMetricsObserverProxy::~FrameMetricsObserverProxy() {
97 JNIEnv* env = getenv(mVm);
98 env->DeleteWeakGlobalRef(mObserverWeak);
99}
100
101bool FrameMetricsObserverProxy::getNextBuffer(JNIEnv* env, jlongArray sink, int* dropCount) {
102 FrameMetricsNotification& elem = mRingBuffer[mNextInQueue];
103
104 if (elem.hasData.load()) {
105 env->SetLongArrayRegion(sink, 0, kBufferSize, elem.buffer);
106 *dropCount = elem.dropCount;
107 mNextInQueue = (mNextInQueue + 1) % kRingSize;
108 elem.hasData = false;
109 return true;
110 }
111
112 return false;
113}
114
115void FrameMetricsObserverProxy::notify(const int64_t* stats) {
116 FrameMetricsNotification& elem = mRingBuffer[mNextFree];
117
118 if (!elem.hasData.load()) {
119 memcpy(elem.buffer, stats, kBufferSize * sizeof(stats[0]));
120
121 elem.dropCount = mDroppedReports;
122 mDroppedReports = 0;
123
124 incStrong(nullptr);
125 mNextFree = (mNextFree + 1) % kRingSize;
126 elem.hasData = true;
127
128 mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage);
129 } else {
130 mDroppedReports++;
131 }
132}
133
134int register_android_view_FrameMetricsObserver(JNIEnv* env) {
135 jclass observerClass = FindClassOrDie(env, "android/view/FrameMetricsObserver");
136 gFrameMetricsObserverClassInfo.frameMetrics = GetFieldIDOrDie(
137 env, observerClass, "mFrameMetrics", "Landroid/view/FrameMetrics;");
138 gFrameMetricsObserverClassInfo.messageQueue = GetFieldIDOrDie(
139 env, observerClass, "mMessageQueue", "Landroid/os/MessageQueue;");
140 gFrameMetricsObserverClassInfo.callback = GetMethodIDOrDie(
141 env, observerClass, "notifyDataAvailable", "(I)V");
142
143 jclass metricsClass = FindClassOrDie(env, "android/view/FrameMetrics");
144 gFrameMetricsObserverClassInfo.timingDataBuffer = GetFieldIDOrDie(
145 env, metricsClass, "mTimingData", "[J");
146 return JNI_OK;
147}
148
149} // namespace android