Fedor Kudasov | fa4e460 | 2019-07-01 14:03:58 +0100 | [diff] [blame] | 1 | /* |
| 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 | |
| 19 | namespace android { |
| 20 | |
| 21 | struct { |
| 22 | jfieldID frameMetrics; |
| 23 | jfieldID timingDataBuffer; |
| 24 | jfieldID messageQueue; |
| 25 | jmethodID callback; |
| 26 | } gFrameMetricsObserverClassInfo; |
| 27 | |
| 28 | static 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 | |
| 36 | static 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 | |
| 46 | class NotifyHandler : public MessageHandler { |
| 47 | public: |
| 48 | NotifyHandler(JavaVM* vm, FrameMetricsObserverProxy* observer) : mVm(vm), mObserver(observer) {} |
| 49 | |
| 50 | virtual void handleMessage(const Message& message); |
| 51 | |
| 52 | private: |
| 53 | JavaVM* const mVm; |
| 54 | FrameMetricsObserverProxy* const mObserver; |
| 55 | }; |
| 56 | |
| 57 | void 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 | |
| 74 | FrameMetricsObserverProxy::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 | |
| 96 | FrameMetricsObserverProxy::~FrameMetricsObserverProxy() { |
| 97 | JNIEnv* env = getenv(mVm); |
| 98 | env->DeleteWeakGlobalRef(mObserverWeak); |
| 99 | } |
| 100 | |
| 101 | bool 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 | |
| 115 | void 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 | |
| 134 | int 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 |