1/* 2 * Copyright (C) 2010 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#define LOG_TAG "ThreadedRenderer" 18 19#include <algorithm> 20#include <atomic> 21 22#include "jni.h" 23#include <nativehelper/JNIHelp.h> 24#include "core_jni_helpers.h" 25#include <GraphicsJNI.h> 26#include <nativehelper/ScopedPrimitiveArray.h> 27 28#include <gui/BufferItemConsumer.h> 29#include <gui/BufferQueue.h> 30#include <gui/Surface.h> 31 32#include <EGL/egl.h> 33#include <EGL/eglext.h> 34#include <private/EGL/cache.h> 35 36#include <utils/Looper.h> 37#include <utils/RefBase.h> 38#include <utils/StrongPointer.h> 39#include <utils/Timers.h> 40#include <android_runtime/android_view_Surface.h> 41#include <system/window.h> 42 43#include "android_os_MessageQueue.h" 44 45#include <Animator.h> 46#include <AnimationContext.h> 47#include <FrameInfo.h> 48#include <FrameMetricsObserver.h> 49#include <IContextFactory.h> 50#include <PropertyValuesAnimatorSet.h> 51#include <RenderNode.h> 52#include <renderthread/CanvasContext.h> 53#include <renderthread/RenderProxy.h> 54#include <renderthread/RenderTask.h> 55#include <renderthread/RenderThread.h> 56 57namespace android { 58 59using namespace android::uirenderer; 60using namespace android::uirenderer::renderthread; 61 62struct { 63 jfieldID frameMetrics; 64 jfieldID timingDataBuffer; 65 jfieldID messageQueue; 66 jmethodID callback; 67} gFrameMetricsObserverClassInfo; 68 69static JNIEnv* getenv(JavaVM* vm) { 70 JNIEnv* env; 71 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { 72 LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); 73 } 74 return env; 75} 76 77class OnFinishedEvent { 78public: 79 OnFinishedEvent(BaseRenderNodeAnimator* animator, AnimationListener* listener) 80 : animator(animator), listener(listener) {} 81 sp<BaseRenderNodeAnimator> animator; 82 sp<AnimationListener> listener; 83}; 84 85class InvokeAnimationListeners : public MessageHandler { 86public: 87 explicit InvokeAnimationListeners(std::vector<OnFinishedEvent>& events) { 88 mOnFinishedEvents.swap(events); 89 } 90 91 static void callOnFinished(OnFinishedEvent& event) { 92 event.listener->onAnimationFinished(event.animator.get()); 93 } 94 95 virtual void handleMessage(const Message& message) { 96 std::for_each(mOnFinishedEvents.begin(), mOnFinishedEvents.end(), callOnFinished); 97 mOnFinishedEvents.clear(); 98 } 99 100private: 101 std::vector<OnFinishedEvent> mOnFinishedEvents; 102}; 103 104class FinishAndInvokeListener : public MessageHandler { 105public: 106 explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim) 107 : mAnimator(anim) { 108 mListener = anim->getOneShotListener(); 109 mRequestId = anim->getRequestId(); 110 } 111 112 virtual void handleMessage(const Message& message) { 113 if (mAnimator->getRequestId() == mRequestId) { 114 // Request Id has not changed, meaning there's no animation lifecyle change since the 115 // message is posted, so go ahead and call finish to make sure the PlayState is properly 116 // updated. This is needed because before the next frame comes in from UI thread to 117 // trigger an animation update, there could be reverse/cancel etc. So we need to update 118 // the playstate in time to ensure all the subsequent events get chained properly. 119 mAnimator->end(); 120 } 121 mListener->onAnimationFinished(nullptr); 122 } 123private: 124 sp<PropertyValuesAnimatorSet> mAnimator; 125 sp<AnimationListener> mListener; 126 uint32_t mRequestId; 127}; 128 129class RenderingException : public MessageHandler { 130public: 131 RenderingException(JavaVM* vm, const std::string& message) 132 : mVm(vm) 133 , mMessage(message) { 134 } 135 136 virtual void handleMessage(const Message&) { 137 throwException(mVm, mMessage); 138 } 139 140 static void throwException(JavaVM* vm, const std::string& message) { 141 JNIEnv* env = getenv(vm); 142 jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); 143 } 144 145private: 146 JavaVM* mVm; 147 std::string mMessage; 148}; 149 150class RootRenderNode : public RenderNode, ErrorHandler { 151public: 152 explicit RootRenderNode(JNIEnv* env) : RenderNode() { 153 mLooper = Looper::getForThread(); 154 LOG_ALWAYS_FATAL_IF(!mLooper.get(), 155 "Must create RootRenderNode on a thread with a looper!"); 156 env->GetJavaVM(&mVm); 157 } 158 159 virtual ~RootRenderNode() {} 160 161 virtual void onError(const std::string& message) override { 162 mLooper->sendMessage(new RenderingException(mVm, message), 0); 163 } 164 165 virtual void prepareTree(TreeInfo& info) override { 166 info.errorHandler = this; 167 168 for (auto& anim : mRunningVDAnimators) { 169 // Assume that the property change in VD from the animators will not be consumed. Mark 170 // otherwise if the VDs are found in the display list tree. For VDs that are not in 171 // the display list tree, we stop providing animation pulses by 1) removing them from 172 // the animation list, 2) post a delayed message to end them at end time so their 173 // listeners can receive the corresponding callbacks. 174 anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); 175 // Mark the VD dirty so it will damage itself during prepareTree. 176 anim->getVectorDrawable()->markDirty(); 177 } 178 if (info.mode == TreeInfo::MODE_FULL) { 179 for (auto &anim : mPausedVDAnimators) { 180 anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); 181 anim->getVectorDrawable()->markDirty(); 182 } 183 } 184 // TODO: This is hacky 185 info.updateWindowPositions = true; 186 RenderNode::prepareTree(info); 187 info.updateWindowPositions = false; 188 info.errorHandler = nullptr; 189 } 190 191 void sendMessage(const sp<MessageHandler>& handler) { 192 mLooper->sendMessage(handler, 0); 193 } 194 195 void sendMessageDelayed(const sp<MessageHandler>& handler, nsecs_t delayInMs) { 196 mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0); 197 } 198 199 void attachAnimatingNode(RenderNode* animatingNode) { 200 mPendingAnimatingRenderNodes.push_back(animatingNode); 201 } 202 203 void attachPendingVectorDrawableAnimators() { 204 mRunningVDAnimators.insert(mPendingVectorDrawableAnimators.begin(), 205 mPendingVectorDrawableAnimators.end()); 206 mPendingVectorDrawableAnimators.clear(); 207 } 208 209 void detachAnimators() { 210 // Remove animators from the list and post a delayed message in future to end the animator 211 // For infinite animators, remove the listener so we no longer hold a global ref to the AVD 212 // java object, and therefore the AVD objects in both native and Java can be properly 213 // released. 214 for (auto& anim : mRunningVDAnimators) { 215 detachVectorDrawableAnimator(anim.get()); 216 anim->clearOneShotListener(); 217 } 218 for (auto& anim : mPausedVDAnimators) { 219 anim->clearOneShotListener(); 220 } 221 mRunningVDAnimators.clear(); 222 mPausedVDAnimators.clear(); 223 } 224 225 // Move all the animators to the paused list, and send a delayed message to notify the finished 226 // listener. 227 void pauseAnimators() { 228 mPausedVDAnimators.insert(mRunningVDAnimators.begin(), mRunningVDAnimators.end()); 229 for (auto& anim : mRunningVDAnimators) { 230 detachVectorDrawableAnimator(anim.get()); 231 } 232 mRunningVDAnimators.clear(); 233 } 234 235 void doAttachAnimatingNodes(AnimationContext* context) { 236 for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) { 237 RenderNode* node = mPendingAnimatingRenderNodes[i].get(); 238 context->addAnimatingRenderNode(*node); 239 } 240 mPendingAnimatingRenderNodes.clear(); 241 } 242 243 // Run VectorDrawable animators after prepareTree. 244 void runVectorDrawableAnimators(AnimationContext* context, TreeInfo& info) { 245 // Push staging. 246 if (info.mode == TreeInfo::MODE_FULL) { 247 pushStagingVectorDrawableAnimators(context); 248 } 249 250 // Run the animators in the running list. 251 for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) { 252 if ((*it)->animate(*context)) { 253 it = mRunningVDAnimators.erase(it); 254 } else { 255 it++; 256 } 257 } 258 259 // Run the animators in paused list during full sync. 260 if (info.mode == TreeInfo::MODE_FULL) { 261 // During full sync we also need to pulse paused animators, in case their targets 262 // have been added back to the display list. All the animators that passed the 263 // scheduled finish time will be removed from the paused list. 264 for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { 265 if ((*it)->animate(*context)) { 266 // Animator has finished, remove from the list. 267 it = mPausedVDAnimators.erase(it); 268 } else { 269 it++; 270 } 271 } 272 } 273 274 // Move the animators with a target not in DisplayList to paused list. 275 for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) { 276 if (!(*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { 277 // Vector Drawable is not in the display list, we should remove this animator from 278 // the list, put it in the paused list, and post a delayed message to end the 279 // animator. 280 detachVectorDrawableAnimator(it->get()); 281 mPausedVDAnimators.insert(*it); 282 it = mRunningVDAnimators.erase(it); 283 } else { 284 it++; 285 } 286 } 287 288 // Move the animators with a target in DisplayList from paused list to running list, and 289 // trim paused list. 290 if (info.mode == TreeInfo::MODE_FULL) { 291 // Check whether any paused animator's target is back in Display List. If so, put the 292 // animator back in the running list. 293 for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { 294 if ((*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { 295 mRunningVDAnimators.insert(*it); 296 it = mPausedVDAnimators.erase(it); 297 } else { 298 it++; 299 } 300 } 301 // Trim paused VD animators at full sync, so that when Java loses reference to an 302 // animator, we know we won't be requested to animate it any more, then we remove such 303 // animators from the paused list so they can be properly freed. We also remove the 304 // animators from paused list when the time elapsed since start has exceeded duration. 305 trimPausedVDAnimators(context); 306 } 307 308 info.out.hasAnimations |= !mRunningVDAnimators.empty(); 309 } 310 311 void trimPausedVDAnimators(AnimationContext* context) { 312 // Trim paused vector drawable animator list. 313 for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { 314 // Remove paused VD animator if no one else is referencing it. Note that animators that 315 // have passed scheduled finish time are removed from list when they are being pulsed 316 // before prepare tree. 317 // TODO: this is a bit hacky, need to figure out a better way to track when the paused 318 // animators should be freed. 319 if ((*it)->getStrongCount() == 1) { 320 it = mPausedVDAnimators.erase(it); 321 } else { 322 it++; 323 } 324 } 325 } 326 327 void pushStagingVectorDrawableAnimators(AnimationContext* context) { 328 for (auto& anim : mRunningVDAnimators) { 329 anim->pushStaging(*context); 330 } 331 } 332 333 void destroy() { 334 for (auto& renderNode : mPendingAnimatingRenderNodes) { 335 renderNode->animators().endAllStagingAnimators(); 336 } 337 mPendingAnimatingRenderNodes.clear(); 338 mPendingVectorDrawableAnimators.clear(); 339 } 340 341 void addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { 342 mPendingVectorDrawableAnimators.insert(anim); 343 } 344 345private: 346 sp<Looper> mLooper; 347 JavaVM* mVm; 348 std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes; 349 std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators; 350 std::set< sp<PropertyValuesAnimatorSet> > mRunningVDAnimators; 351 // mPausedVDAnimators stores a list of animators that have not yet passed the finish time, but 352 // their VectorDrawable targets are no longer in the DisplayList. We skip these animators when 353 // render thread runs animators independent of UI thread (i.e. RT_ONLY mode). These animators 354 // need to be re-activated once their VD target is added back into DisplayList. Since that could 355 // only happen when we do a full sync, we need to make sure to pulse these paused animators at 356 // full sync. If any animator's VD target is found in DisplayList during a full sync, we move 357 // the animator back to the running list. 358 std::set< sp<PropertyValuesAnimatorSet> > mPausedVDAnimators; 359 void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { 360 if (anim->isInfinite() || !anim->isRunning()) { 361 // Do not need to post anything if the animation is infinite (i.e. no meaningful 362 // end listener action), or if the animation has already ended. 363 return; 364 } 365 nsecs_t remainingTimeInMs = anim->getRemainingPlayTime(); 366 // Post a delayed onFinished event that is scheduled to be handled when the animator ends. 367 if (anim->getOneShotListener()) { 368 // VectorDrawable's oneshot listener is updated when there are user triggered animation 369 // lifecycle changes, such as start(), end(), etc. By using checking and clearing 370 // one shot listener, we ensure the same end listener event gets posted only once. 371 // Therefore no duplicates. Another benefit of using one shot listener is that no 372 // removal is necessary: the end time of animation will not change unless triggered by 373 // user events, in which case the already posted listener's id will become stale, and 374 // the onFinished callback will then be ignored. 375 sp<FinishAndInvokeListener> message 376 = new FinishAndInvokeListener(anim); 377 sendMessageDelayed(message, remainingTimeInMs); 378 anim->clearOneShotListener(); 379 } 380 } 381}; 382 383class AnimationContextBridge : public AnimationContext { 384public: 385 AnimationContextBridge(renderthread::TimeLord& clock, RootRenderNode* rootNode) 386 : AnimationContext(clock), mRootNode(rootNode) { 387 } 388 389 virtual ~AnimationContextBridge() {} 390 391 // Marks the start of a frame, which will update the frame time and move all 392 // next frame animations into the current frame 393 virtual void startFrame(TreeInfo::TraversalMode mode) { 394 if (mode == TreeInfo::MODE_FULL) { 395 mRootNode->doAttachAnimatingNodes(this); 396 mRootNode->attachPendingVectorDrawableAnimators(); 397 } 398 AnimationContext::startFrame(mode); 399 } 400 401 // Runs any animations still left in mCurrentFrameAnimations 402 virtual void runRemainingAnimations(TreeInfo& info) { 403 AnimationContext::runRemainingAnimations(info); 404 mRootNode->runVectorDrawableAnimators(this, info); 405 postOnFinishedEvents(); 406 } 407 408 virtual void pauseAnimators() override { 409 mRootNode->pauseAnimators(); 410 } 411 412 virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { 413 OnFinishedEvent event(animator, listener); 414 mOnFinishedEvents.push_back(event); 415 } 416 417 virtual void destroy() { 418 AnimationContext::destroy(); 419 mRootNode->detachAnimators(); 420 postOnFinishedEvents(); 421 } 422 423private: 424 sp<RootRenderNode> mRootNode; 425 std::vector<OnFinishedEvent> mOnFinishedEvents; 426 427 void postOnFinishedEvents() { 428 if (mOnFinishedEvents.size()) { 429 sp<InvokeAnimationListeners> message 430 = new InvokeAnimationListeners(mOnFinishedEvents); 431 mRootNode->sendMessage(message); 432 } 433 } 434}; 435 436class ContextFactoryImpl : public IContextFactory { 437public: 438 explicit ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {} 439 440 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { 441 return new AnimationContextBridge(clock, mRootNode); 442 } 443 444private: 445 RootRenderNode* mRootNode; 446}; 447 448class ObserverProxy; 449 450class NotifyHandler : public MessageHandler { 451public: 452 NotifyHandler(JavaVM* vm, ObserverProxy* observer) : mVm(vm), mObserver(observer) {} 453 454 virtual void handleMessage(const Message& message); 455 456private: 457 JavaVM* const mVm; 458 ObserverProxy* const mObserver; 459}; 460 461static jlongArray get_metrics_buffer(JNIEnv* env, jobject observer) { 462 jobject frameMetrics = env->GetObjectField( 463 observer, gFrameMetricsObserverClassInfo.frameMetrics); 464 LOG_ALWAYS_FATAL_IF(frameMetrics == nullptr, "unable to retrieve data sink object"); 465 jobject buffer = env->GetObjectField( 466 frameMetrics, gFrameMetricsObserverClassInfo.timingDataBuffer); 467 LOG_ALWAYS_FATAL_IF(buffer == nullptr, "unable to retrieve data sink buffer"); 468 return reinterpret_cast<jlongArray>(buffer); 469} 470 471/* 472 * Implements JNI layer for hwui frame metrics reporting. 473 */ 474class ObserverProxy : public FrameMetricsObserver { 475public: 476 ObserverProxy(JavaVM *vm, jobject observer) : mVm(vm) { 477 JNIEnv* env = getenv(mVm); 478 479 mObserverWeak = env->NewWeakGlobalRef(observer); 480 LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr, 481 "unable to create frame stats observer reference"); 482 483 jlongArray buffer = get_metrics_buffer(env, observer); 484 jsize bufferSize = env->GetArrayLength(reinterpret_cast<jarray>(buffer)); 485 LOG_ALWAYS_FATAL_IF(bufferSize != kBufferSize, 486 "Mismatched Java/Native FrameMetrics data format."); 487 488 jobject messageQueueLocal = env->GetObjectField( 489 observer, gFrameMetricsObserverClassInfo.messageQueue); 490 mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal); 491 LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available"); 492 493 mMessageHandler = new NotifyHandler(mVm, this); 494 LOG_ALWAYS_FATAL_IF(mMessageHandler == nullptr, 495 "OOM: unable to allocate NotifyHandler"); 496 } 497 498 ~ObserverProxy() { 499 JNIEnv* env = getenv(mVm); 500 env->DeleteWeakGlobalRef(mObserverWeak); 501 } 502 503 jweak getObserverReference() { 504 return mObserverWeak; 505 } 506 507 bool getNextBuffer(JNIEnv* env, jlongArray sink, int* dropCount) { 508 FrameMetricsNotification& elem = mRingBuffer[mNextInQueue]; 509 510 if (elem.hasData.load()) { 511 env->SetLongArrayRegion(sink, 0, kBufferSize, elem.buffer); 512 *dropCount = elem.dropCount; 513 mNextInQueue = (mNextInQueue + 1) % kRingSize; 514 elem.hasData = false; 515 return true; 516 } 517 518 return false; 519 } 520 521 virtual void notify(const int64_t* stats) { 522 FrameMetricsNotification& elem = mRingBuffer[mNextFree]; 523 524 if (!elem.hasData.load()) { 525 memcpy(elem.buffer, stats, kBufferSize * sizeof(stats[0])); 526 527 elem.dropCount = mDroppedReports; 528 mDroppedReports = 0; 529 530 incStrong(nullptr); 531 mNextFree = (mNextFree + 1) % kRingSize; 532 elem.hasData = true; 533 534 mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage); 535 } else { 536 mDroppedReports++; 537 } 538 } 539 540private: 541 static const int kBufferSize = static_cast<int>(FrameInfoIndex::NumIndexes); 542 static constexpr int kRingSize = 3; 543 544 class FrameMetricsNotification { 545 public: 546 FrameMetricsNotification() : hasData(false) {} 547 548 std::atomic_bool hasData; 549 int64_t buffer[kBufferSize]; 550 int dropCount = 0; 551 }; 552 553 JavaVM* const mVm; 554 jweak mObserverWeak; 555 556 sp<MessageQueue> mMessageQueue; 557 sp<NotifyHandler> mMessageHandler; 558 Message mMessage; 559 560 int mNextFree = 0; 561 int mNextInQueue = 0; 562 FrameMetricsNotification mRingBuffer[kRingSize]; 563 564 int mDroppedReports = 0; 565}; 566 567void NotifyHandler::handleMessage(const Message& message) { 568 JNIEnv* env = getenv(mVm); 569 570 jobject target = env->NewLocalRef(mObserver->getObserverReference()); 571 572 if (target != nullptr) { 573 jlongArray javaBuffer = get_metrics_buffer(env, target); 574 int dropCount = 0; 575 while (mObserver->getNextBuffer(env, javaBuffer, &dropCount)) { 576 env->CallVoidMethod(target, gFrameMetricsObserverClassInfo.callback, dropCount); 577 } 578 env->DeleteLocalRef(target); 579 } 580 581 mObserver->decStrong(nullptr); 582} 583 584static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) { 585 RenderProxy::rotateProcessStatsBuffer(); 586} 587 588static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz, 589 jint fd) { 590 RenderProxy::setProcessStatsBuffer(fd); 591} 592 593static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz, 594 jlong proxyPtr) { 595 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 596 return proxy->getRenderThreadTid(); 597} 598 599static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) { 600 RootRenderNode* node = new RootRenderNode(env); 601 node->incStrong(0); 602 node->setName("RootRenderNode"); 603 return reinterpret_cast<jlong>(node); 604} 605 606static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, 607 jboolean translucent, jlong rootRenderNodePtr) { 608 RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr); 609 ContextFactoryImpl factory(rootRenderNode); 610 return (jlong) new RenderProxy(translucent, rootRenderNode, &factory); 611} 612 613static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz, 614 jlong proxyPtr) { 615 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 616 delete proxy; 617} 618 619static jboolean android_view_ThreadedRenderer_loadSystemProperties(JNIEnv* env, jobject clazz, 620 jlong proxyPtr) { 621 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 622 return proxy->loadSystemProperties(); 623} 624 625static void android_view_ThreadedRenderer_setName(JNIEnv* env, jobject clazz, 626 jlong proxyPtr, jstring jname) { 627 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 628 const char* name = env->GetStringUTFChars(jname, NULL); 629 proxy->setName(name); 630 env->ReleaseStringUTFChars(jname, name); 631} 632 633static void android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz, 634 jlong proxyPtr, jobject jsurface) { 635 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 636 sp<Surface> surface = android_view_Surface_getSurface(env, jsurface); 637 proxy->initialize(surface); 638} 639 640static void android_view_ThreadedRenderer_updateSurface(JNIEnv* env, jobject clazz, 641 jlong proxyPtr, jobject jsurface) { 642 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 643 sp<Surface> surface; 644 if (jsurface) { 645 surface = android_view_Surface_getSurface(env, jsurface); 646 } 647 proxy->updateSurface(surface); 648} 649 650static jboolean android_view_ThreadedRenderer_pauseSurface(JNIEnv* env, jobject clazz, 651 jlong proxyPtr, jobject jsurface) { 652 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 653 sp<Surface> surface; 654 if (jsurface) { 655 surface = android_view_Surface_getSurface(env, jsurface); 656 } 657 return proxy->pauseSurface(surface); 658} 659 660static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz, 661 jlong proxyPtr, jboolean stopped) { 662 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 663 proxy->setStopped(stopped); 664} 665 666static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr, 667 jfloat lightRadius, jint ambientShadowAlpha, jint spotShadowAlpha) { 668 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 669 proxy->setup(lightRadius, ambientShadowAlpha, spotShadowAlpha); 670} 671 672static void android_view_ThreadedRenderer_setLightCenter(JNIEnv* env, jobject clazz, 673 jlong proxyPtr, jfloat lightX, jfloat lightY, jfloat lightZ) { 674 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 675 proxy->setLightCenter((Vector3){lightX, lightY, lightZ}); 676} 677 678static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, 679 jlong proxyPtr, jboolean opaque) { 680 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 681 proxy->setOpaque(opaque); 682} 683 684static void android_view_ThreadedRenderer_setWideGamut(JNIEnv* env, jobject clazz, 685 jlong proxyPtr, jboolean wideGamut) { 686 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 687 proxy->setWideGamut(wideGamut); 688} 689 690static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, 691 jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) { 692 LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE, 693 "Mismatched size expectations, given %d expected %d", 694 frameInfoSize, UI_THREAD_FRAME_INFO_SIZE); 695 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 696 env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo()); 697 return proxy->syncAndDrawFrame(); 698} 699 700static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz, 701 jlong proxyPtr, jlong rootNodePtr) { 702 RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr); 703 rootRenderNode->destroy(); 704 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 705 proxy->destroy(); 706} 707 708static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz, 709 jlong rootNodePtr, jlong animatingNodePtr) { 710 RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr); 711 RenderNode* animatingNode = reinterpret_cast<RenderNode*>(animatingNodePtr); 712 rootRenderNode->attachAnimatingNode(animatingNode); 713} 714 715static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* env, jobject clazz, 716 jlong rootNodePtr, jlong animatorPtr) { 717 RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr); 718 PropertyValuesAnimatorSet* animator = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr); 719 rootRenderNode->addVectorDrawableAnimator(animator); 720} 721 722static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz, 723 jlong functorPtr, jboolean waitForCompletion) { 724 Functor* functor = reinterpret_cast<Functor*>(functorPtr); 725 RenderProxy::invokeFunctor(functor, waitForCompletion); 726} 727 728static jlong android_view_ThreadedRenderer_createTextureLayer(JNIEnv* env, jobject clazz, 729 jlong proxyPtr) { 730 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 731 DeferredLayerUpdater* layer = proxy->createTextureLayer(); 732 return reinterpret_cast<jlong>(layer); 733} 734 735static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz, 736 jlong proxyPtr, jlong nodePtr) { 737 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 738 RenderNode* node = reinterpret_cast<RenderNode*>(nodePtr); 739 proxy->buildLayer(node); 740} 741 742static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz, 743 jlong proxyPtr, jlong layerPtr, jobject jbitmap) { 744 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 745 DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); 746 SkBitmap bitmap; 747 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); 748 return proxy->copyLayerInto(layer, bitmap); 749} 750 751static void android_view_ThreadedRenderer_pushLayerUpdate(JNIEnv* env, jobject clazz, 752 jlong proxyPtr, jlong layerPtr) { 753 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 754 DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); 755 proxy->pushLayerUpdate(layer); 756} 757 758static void android_view_ThreadedRenderer_cancelLayerUpdate(JNIEnv* env, jobject clazz, 759 jlong proxyPtr, jlong layerPtr) { 760 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 761 DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); 762 proxy->cancelLayerUpdate(layer); 763} 764 765static void android_view_ThreadedRenderer_detachSurfaceTexture(JNIEnv* env, jobject clazz, 766 jlong proxyPtr, jlong layerPtr) { 767 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 768 DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); 769 proxy->detachSurfaceTexture(layer); 770} 771 772static void android_view_ThreadedRenderer_destroyHardwareResources(JNIEnv* env, jobject clazz, 773 jlong proxyPtr) { 774 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 775 proxy->destroyHardwareResources(); 776} 777 778static void android_view_ThreadedRenderer_trimMemory(JNIEnv* env, jobject clazz, 779 jint level) { 780 RenderProxy::trimMemory(level); 781} 782 783static void android_view_ThreadedRenderer_overrideProperty(JNIEnv* env, jobject clazz, 784 jstring name, jstring value) { 785 const char* nameCharArray = env->GetStringUTFChars(name, NULL); 786 const char* valueCharArray = env->GetStringUTFChars(value, NULL); 787 RenderProxy::overrideProperty(nameCharArray, valueCharArray); 788 env->ReleaseStringUTFChars(name, nameCharArray); 789 env->ReleaseStringUTFChars(name, valueCharArray); 790} 791 792static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz, 793 jlong proxyPtr) { 794 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 795 proxy->fence(); 796} 797 798static void android_view_ThreadedRenderer_stopDrawing(JNIEnv* env, jobject clazz, 799 jlong proxyPtr) { 800 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 801 proxy->stopDrawing(); 802} 803 804static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobject clazz, 805 jlong proxyPtr) { 806 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 807 proxy->notifyFramePending(); 808} 809 810static void android_view_ThreadedRenderer_serializeDisplayListTree(JNIEnv* env, jobject clazz, 811 jlong proxyPtr) { 812 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 813 proxy->serializeDisplayListTree(); 814} 815 816static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz, 817 jlong proxyPtr, jobject javaFileDescriptor, jint dumpFlags) { 818 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 819 int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor); 820 proxy->dumpProfileInfo(fd, dumpFlags); 821} 822 823static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz, 824 jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) { 825 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 826 RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); 827 proxy->addRenderNode(renderNode, placeFront); 828} 829 830static void android_view_ThreadedRenderer_removeRenderNode(JNIEnv* env, jobject clazz, 831 jlong proxyPtr, jlong renderNodePtr) { 832 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 833 RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); 834 proxy->removeRenderNode(renderNode); 835} 836 837static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject clazz, 838 jlong proxyPtr, jlong renderNodePtr) { 839 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 840 RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); 841 proxy->drawRenderNode(renderNode); 842} 843 844static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env, 845 jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) { 846 RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); 847 proxy->setContentDrawBounds(left, top, right, bottom); 848} 849 850static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, 851 jobject clazz, jobject jsurface, jint left, jint top, 852 jint right, jint bottom, jobject jbitmap) { 853 SkBitmap bitmap; 854 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); 855 sp<Surface> surface = android_view_Surface_getSurface(env, jsurface); 856 return RenderProxy::copySurfaceInto(surface, left, top, right, bottom, &bitmap); 857} 858 859class ContextFactory : public IContextFactory { 860public: 861 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { 862 return new AnimationContext(clock); 863 } 864}; 865 866static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(JNIEnv* env, 867 jobject clazz, jlong renderNodePtr, jint jwidth, jint jheight) { 868 RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); 869 if (jwidth <= 0 || jheight <= 0) { 870 ALOGW("Invalid width %d or height %d", jwidth, jheight); 871 return nullptr; 872 } 873 874 uint32_t width = jwidth; 875 uint32_t height = jheight; 876 877 // Create a Surface wired up to a BufferItemConsumer 878 sp<IGraphicBufferProducer> producer; 879 sp<IGraphicBufferConsumer> rawConsumer; 880 BufferQueue::createBufferQueue(&producer, &rawConsumer); 881 // We only need 1 buffer but some drivers have bugs so workaround it by setting max count to 2 882 rawConsumer->setMaxBufferCount(2); 883 sp<BufferItemConsumer> consumer = new BufferItemConsumer(rawConsumer, 884 GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER); 885 consumer->setDefaultBufferSize(width, height); 886 sp<Surface> surface = new Surface(producer); 887 888 // Render into the surface 889 { 890 ContextFactory factory; 891 RenderProxy proxy{true, renderNode, &factory}; 892 proxy.loadSystemProperties(); 893 proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer); 894 proxy.initialize(surface); 895 // Shadows can't be used via this interface, so just set the light source 896 // to all 0s. 897 proxy.setup(0, 0, 0); 898 proxy.setLightCenter((Vector3){0, 0, 0}); 899 nsecs_t vsync = systemTime(CLOCK_MONOTONIC); 900 UiFrameInfoBuilder(proxy.frameInfo()) 901 .setVsync(vsync, vsync) 902 .addFlag(FrameInfoFlags::SurfaceCanvas); 903 proxy.syncAndDrawFrame(); 904 } 905 906 // Yank out the GraphicBuffer 907 BufferItem bufferItem; 908 status_t err; 909 if ((err = consumer->acquireBuffer(&bufferItem, 0, true)) != OK) { 910 ALOGW("Failed to acquireBuffer, error %d (%s)", err, strerror(-err)); 911 return nullptr; 912 } 913 sp<GraphicBuffer> buffer = bufferItem.mGraphicBuffer; 914 // We don't really care if this fails or not since we're just going to destroy this anyway 915 consumer->releaseBuffer(bufferItem); 916 if (!buffer.get()) { 917 ALOGW("GraphicBuffer is null?"); 918 return nullptr; 919 } 920 if (buffer->getWidth() != width || buffer->getHeight() != height) { 921 ALOGW("GraphicBuffer size mismatch, got %dx%d expected %dx%d", 922 buffer->getWidth(), buffer->getHeight(), width, height); 923 // Continue I guess? 924 } 925 sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer); 926 return createBitmap(env, bitmap.release(), android::bitmap::kBitmapCreateFlag_Mutable); 927} 928 929static void android_view_ThreadedRenderer_disableVsync(JNIEnv*, jclass) { 930 RenderProxy::disableVsync(); 931} 932 933// ---------------------------------------------------------------------------- 934// FrameMetricsObserver 935// ---------------------------------------------------------------------------- 936 937static jlong android_view_ThreadedRenderer_addFrameMetricsObserver(JNIEnv* env, 938 jclass clazz, jlong proxyPtr, jobject fso) { 939 JavaVM* vm = nullptr; 940 if (env->GetJavaVM(&vm) != JNI_OK) { 941 LOG_ALWAYS_FATAL("Unable to get Java VM"); 942 return 0; 943 } 944 945 renderthread::RenderProxy* renderProxy = 946 reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); 947 948 FrameMetricsObserver* observer = new ObserverProxy(vm, fso); 949 renderProxy->addFrameMetricsObserver(observer); 950 return reinterpret_cast<jlong>(observer); 951} 952 953static void android_view_ThreadedRenderer_removeFrameMetricsObserver(JNIEnv* env, jclass clazz, 954 jlong proxyPtr, jlong observerPtr) { 955 FrameMetricsObserver* observer = reinterpret_cast<FrameMetricsObserver*>(observerPtr); 956 renderthread::RenderProxy* renderProxy = 957 reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); 958 959 renderProxy->removeFrameMetricsObserver(observer); 960} 961 962// ---------------------------------------------------------------------------- 963// Shaders 964// ---------------------------------------------------------------------------- 965 966static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz, 967 jstring diskCachePath) { 968 const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL); 969 android::egl_set_cache_filename(cacheArray); 970 env->ReleaseStringUTFChars(diskCachePath, cacheArray); 971} 972 973// ---------------------------------------------------------------------------- 974// JNI Glue 975// ---------------------------------------------------------------------------- 976 977const char* const kClassPathName = "android/view/ThreadedRenderer"; 978 979static const JNINativeMethod gMethods[] = { 980 { "nRotateProcessStatsBuffer", "()V", (void*) android_view_ThreadedRenderer_rotateProcessStatsBuffer }, 981 { "nSetProcessStatsBuffer", "(I)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer }, 982 { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid }, 983 { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode }, 984 { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy }, 985 { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy }, 986 { "nLoadSystemProperties", "(J)Z", (void*) android_view_ThreadedRenderer_loadSystemProperties }, 987 { "nSetName", "(JLjava/lang/String;)V", (void*) android_view_ThreadedRenderer_setName }, 988 { "nInitialize", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_initialize }, 989 { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface }, 990 { "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface }, 991 { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped }, 992 { "nSetup", "(JFII)V", (void*) android_view_ThreadedRenderer_setup }, 993 { "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter }, 994 { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, 995 { "nSetWideGamut", "(JZ)V", (void*) android_view_ThreadedRenderer_setWideGamut }, 996 { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, 997 { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy }, 998 { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode }, 999 { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator }, 1000 { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, 1001 { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer }, 1002 { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer }, 1003 { "nCopyLayerInto", "(JJLandroid/graphics/Bitmap;)Z", (void*) android_view_ThreadedRenderer_copyLayerInto }, 1004 { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate }, 1005 { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate }, 1006 { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture }, 1007 { "nDestroyHardwareResources", "(J)V", (void*) android_view_ThreadedRenderer_destroyHardwareResources }, 1008 { "nTrimMemory", "(I)V", (void*) android_view_ThreadedRenderer_trimMemory }, 1009 { "nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) android_view_ThreadedRenderer_overrideProperty }, 1010 { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence }, 1011 { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing }, 1012 { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending }, 1013 { "nSerializeDisplayListTree", "(J)V", (void*) android_view_ThreadedRenderer_serializeDisplayListTree }, 1014 { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo }, 1015 { "setupShadersDiskCache", "(Ljava/lang/String;)V", 1016 (void*) android_view_ThreadedRenderer_setupShadersDiskCache }, 1017 { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode}, 1018 { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode}, 1019 { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode}, 1020 { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds}, 1021 { "nAddFrameMetricsObserver", 1022 "(JLandroid/view/FrameMetricsObserver;)J", 1023 (void*)android_view_ThreadedRenderer_addFrameMetricsObserver }, 1024 { "nRemoveFrameMetricsObserver", 1025 "(JJ)V", 1026 (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver }, 1027 { "nCopySurfaceInto", "(Landroid/view/Surface;IIIILandroid/graphics/Bitmap;)I", 1028 (void*)android_view_ThreadedRenderer_copySurfaceInto }, 1029 { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", 1030 (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode }, 1031 { "disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync }, 1032}; 1033 1034int register_android_view_ThreadedRenderer(JNIEnv* env) { 1035 jclass observerClass = FindClassOrDie(env, "android/view/FrameMetricsObserver"); 1036 gFrameMetricsObserverClassInfo.frameMetrics = GetFieldIDOrDie( 1037 env, observerClass, "mFrameMetrics", "Landroid/view/FrameMetrics;"); 1038 gFrameMetricsObserverClassInfo.messageQueue = GetFieldIDOrDie( 1039 env, observerClass, "mMessageQueue", "Landroid/os/MessageQueue;"); 1040 gFrameMetricsObserverClassInfo.callback = GetMethodIDOrDie( 1041 env, observerClass, "notifyDataAvailable", "(I)V"); 1042 1043 jclass metricsClass = FindClassOrDie(env, "android/view/FrameMetrics"); 1044 gFrameMetricsObserverClassInfo.timingDataBuffer = GetFieldIDOrDie( 1045 env, metricsClass, "mTimingData", "[J"); 1046 1047 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); 1048} 1049 1050}; // namespace android 1051