| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/android/window_android.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_array.h" |
| #include "base/android/jni_string.h" |
| #include "base/android/jni_weak_ref.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/observer_list.h" |
| #include "base/stl_util.h" |
| #include "components/viz/common/frame_sinks/begin_frame_args.h" |
| #include "components/viz/common/frame_sinks/begin_frame_source.h" |
| #include "jni/WindowAndroid_jni.h" |
| #include "ui/android/window_android_compositor.h" |
| #include "ui/android/window_android_observer.h" |
| |
| namespace ui { |
| |
| using base::android::AttachCurrentThread; |
| using base::android::JavaParamRef; |
| using base::android::JavaRef; |
| using base::android::ScopedJavaLocalRef; |
| |
| const float kDefaultMouseWheelTickMultiplier = 64; |
| |
| class WindowAndroid::WindowBeginFrameSource : public viz::BeginFrameSource { |
| public: |
| explicit WindowBeginFrameSource(WindowAndroid* window) |
| : BeginFrameSource(kNotRestartableId), |
| window_(window), |
| observers_(base::ObserverListPolicy::EXISTING_ONLY), |
| observer_count_(0), |
| next_sequence_number_(viz::BeginFrameArgs::kStartingFrameNumber), |
| paused_(false) {} |
| ~WindowBeginFrameSource() override {} |
| |
| // viz::BeginFrameSource implementation. |
| void AddObserver(viz::BeginFrameObserver* obs) override; |
| void RemoveObserver(viz::BeginFrameObserver* obs) override; |
| void DidFinishFrame(viz::BeginFrameObserver* obs) override {} |
| bool IsThrottled() const override { return true; } |
| void OnGpuNoLongerBusy() override; |
| |
| void OnVSync(base::TimeTicks frame_time, base::TimeDelta vsync_period); |
| void OnPauseChanged(bool paused); |
| void AddBeginFrameCompletionCallback(base::OnceClosure callback); |
| |
| private: |
| friend class WindowAndroid::ScopedOnBeginFrame; |
| |
| WindowAndroid* const window_; |
| base::ObserverList<viz::BeginFrameObserver>::Unchecked observers_; |
| int observer_count_; |
| viz::BeginFrameArgs last_begin_frame_args_; |
| uint64_t next_sequence_number_; |
| bool paused_; |
| |
| // Set by ScopedOnBeginFrame. |
| std::vector<base::OnceClosure>* vsync_complete_callbacks_ptr_ = nullptr; |
| }; |
| |
| class WindowAndroid::ScopedOnBeginFrame { |
| public: |
| explicit ScopedOnBeginFrame(WindowAndroid::WindowBeginFrameSource* bfs, |
| bool allow_reentrancy); |
| ~ScopedOnBeginFrame(); |
| |
| private: |
| WindowAndroid::WindowBeginFrameSource* const begin_frame_source_; |
| const bool reentrant_; |
| std::vector<base::OnceClosure> vsync_complete_callbacks_; |
| }; |
| |
| void WindowAndroid::WindowBeginFrameSource::AddObserver( |
| viz::BeginFrameObserver* obs) { |
| DCHECK(obs); |
| DCHECK(!observers_.HasObserver(obs)); |
| |
| observers_.AddObserver(obs); |
| observer_count_++; |
| obs->OnBeginFrameSourcePausedChanged(paused_); |
| window_->SetNeedsBeginFrames(true); |
| |
| // Send a MISSED BeginFrame if possible and necessary. |
| if (last_begin_frame_args_.IsValid()) { |
| viz::BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs(); |
| if (!last_args.IsValid() || |
| last_args.frame_time < last_begin_frame_args_.frame_time) { |
| DCHECK(last_args.sequence_number < |
| last_begin_frame_args_.sequence_number || |
| last_args.source_id != last_begin_frame_args_.source_id); |
| last_begin_frame_args_.type = viz::BeginFrameArgs::MISSED; |
| // TODO(crbug.com/602485): A deadline doesn't make too much sense |
| // for a missed BeginFrame (the intention rather is 'immediately'), |
| // but currently the retro frame logic is very strict in discarding |
| // BeginFrames. |
| last_begin_frame_args_.deadline = |
| base::TimeTicks::Now() + last_begin_frame_args_.interval; |
| ScopedOnBeginFrame scope(this, true /* allow_reentrancy */); |
| obs->OnBeginFrame(last_begin_frame_args_); |
| } |
| } |
| } |
| |
| void WindowAndroid::WindowBeginFrameSource::RemoveObserver( |
| viz::BeginFrameObserver* obs) { |
| DCHECK(obs); |
| DCHECK(observers_.HasObserver(obs)); |
| |
| observers_.RemoveObserver(obs); |
| observer_count_--; |
| if (observer_count_ <= 0) |
| window_->SetNeedsBeginFrames(false); |
| } |
| |
| void WindowAndroid::WindowBeginFrameSource::OnGpuNoLongerBusy() { |
| ScopedOnBeginFrame scope(this, false /* allow_reentrancy */); |
| for (auto& obs : observers_) |
| obs.OnBeginFrame(last_begin_frame_args_); |
| } |
| |
| void WindowAndroid::WindowBeginFrameSource::OnVSync( |
| base::TimeTicks frame_time, |
| base::TimeDelta vsync_period) { |
| // frame time is in the past, so give the next vsync period as the deadline. |
| base::TimeTicks deadline = frame_time + vsync_period; |
| last_begin_frame_args_ = viz::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, source_id(), next_sequence_number_, frame_time, |
| deadline, vsync_period, viz::BeginFrameArgs::NORMAL); |
| DCHECK(last_begin_frame_args_.IsValid()); |
| next_sequence_number_++; |
| if (RequestCallbackOnGpuAvailable()) |
| return; |
| OnGpuNoLongerBusy(); |
| } |
| |
| void WindowAndroid::WindowBeginFrameSource::OnPauseChanged(bool paused) { |
| paused_ = paused; |
| for (auto& obs : observers_) |
| obs.OnBeginFrameSourcePausedChanged(paused_); |
| } |
| |
| void WindowAndroid::WindowBeginFrameSource::AddBeginFrameCompletionCallback( |
| base::OnceClosure callback) { |
| CHECK(vsync_complete_callbacks_ptr_); |
| vsync_complete_callbacks_ptr_->emplace_back(std::move(callback)); |
| } |
| |
| WindowAndroid::ScopedOnBeginFrame::ScopedOnBeginFrame( |
| WindowAndroid::WindowBeginFrameSource* bfs, |
| bool allow_reentrancy) |
| : begin_frame_source_(bfs), |
| reentrant_(allow_reentrancy && |
| begin_frame_source_->vsync_complete_callbacks_ptr_) { |
| if (reentrant_) { |
| DCHECK(begin_frame_source_->vsync_complete_callbacks_ptr_); |
| return; |
| } |
| DCHECK(!begin_frame_source_->vsync_complete_callbacks_ptr_); |
| begin_frame_source_->vsync_complete_callbacks_ptr_ = |
| &vsync_complete_callbacks_; |
| } |
| |
| WindowAndroid::ScopedOnBeginFrame::~ScopedOnBeginFrame() { |
| if (reentrant_) { |
| DCHECK_NE(&vsync_complete_callbacks_, |
| begin_frame_source_->vsync_complete_callbacks_ptr_); |
| return; |
| } |
| DCHECK_EQ(&vsync_complete_callbacks_, |
| begin_frame_source_->vsync_complete_callbacks_ptr_); |
| begin_frame_source_->vsync_complete_callbacks_ptr_ = nullptr; |
| for (base::OnceClosure& callback : vsync_complete_callbacks_) |
| std::move(callback).Run(); |
| } |
| |
| // static |
| WindowAndroid* WindowAndroid::FromJavaWindowAndroid( |
| const JavaParamRef<jobject>& jwindow_android) { |
| if (jwindow_android.is_null()) |
| return nullptr; |
| |
| return reinterpret_cast<WindowAndroid*>(Java_WindowAndroid_getNativePointer( |
| AttachCurrentThread(), jwindow_android)); |
| } |
| |
| WindowAndroid::WindowAndroid(JNIEnv* env, |
| jobject obj, |
| int display_id, |
| float scroll_factor) |
| : display_id_(display_id), |
| compositor_(NULL), |
| begin_frame_source_(new WindowBeginFrameSource(this)), |
| needs_begin_frames_(false) { |
| java_window_.Reset(env, obj); |
| mouse_wheel_scroll_factor_ = |
| scroll_factor > 0 ? scroll_factor |
| : kDefaultMouseWheelTickMultiplier * GetDipScale(); |
| } |
| |
| void WindowAndroid::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) { |
| delete this; |
| } |
| |
| ScopedJavaLocalRef<jobject> WindowAndroid::GetJavaObject() { |
| return base::android::ScopedJavaLocalRef<jobject>(java_window_); |
| } |
| |
| WindowAndroid::~WindowAndroid() { |
| DCHECK(parent_ == nullptr) << "WindowAndroid must be a root view."; |
| DCHECK(!compositor_); |
| RemoveAllChildren(true); |
| Java_WindowAndroid_clearNativePointer(AttachCurrentThread(), GetJavaObject()); |
| } |
| |
| WindowAndroid* WindowAndroid::CreateForTesting() { |
| JNIEnv* env = AttachCurrentThread(); |
| long native_pointer = Java_WindowAndroid_createForTesting(env); |
| return reinterpret_cast<WindowAndroid*>(native_pointer); |
| } |
| |
| void WindowAndroid::OnCompositingDidCommit() { |
| for (WindowAndroidObserver& observer : observer_list_) |
| observer.OnCompositingDidCommit(); |
| } |
| |
| void WindowAndroid::AddObserver(WindowAndroidObserver* observer) { |
| if (!observer_list_.HasObserver(observer)) |
| observer_list_.AddObserver(observer); |
| } |
| |
| void WindowAndroid::AddBeginFrameCompletionCallback( |
| base::OnceClosure callback) { |
| begin_frame_source_->AddBeginFrameCompletionCallback(std::move(callback)); |
| } |
| |
| void WindowAndroid::RemoveObserver(WindowAndroidObserver* observer) { |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| viz::BeginFrameSource* WindowAndroid::GetBeginFrameSource() { |
| return begin_frame_source_.get(); |
| } |
| |
| void WindowAndroid::AttachCompositor(WindowAndroidCompositor* compositor) { |
| if (compositor_ && compositor != compositor_) |
| DetachCompositor(); |
| |
| compositor_ = compositor; |
| for (WindowAndroidObserver& observer : observer_list_) |
| observer.OnAttachCompositor(); |
| |
| compositor_->SetVSyncPaused(vsync_paused_); |
| } |
| |
| void WindowAndroid::DetachCompositor() { |
| compositor_ = NULL; |
| for (WindowAndroidObserver& observer : observer_list_) |
| observer.OnDetachCompositor(); |
| observer_list_.Clear(); |
| } |
| |
| void WindowAndroid::RequestVSyncUpdate() { |
| JNIEnv* env = AttachCurrentThread(); |
| Java_WindowAndroid_requestVSyncUpdate(env, GetJavaObject()); |
| } |
| |
| void WindowAndroid::SetNeedsBeginFrames(bool needs_begin_frames) { |
| if (needs_begin_frames_ == needs_begin_frames) |
| return; |
| |
| needs_begin_frames_ = needs_begin_frames; |
| if (needs_begin_frames_) |
| RequestVSyncUpdate(); |
| } |
| |
| void WindowAndroid::SetNeedsAnimate() { |
| if (compositor_) |
| compositor_->SetNeedsAnimate(); |
| } |
| |
| void WindowAndroid::Animate(base::TimeTicks begin_frame_time) { |
| for (WindowAndroidObserver& observer : observer_list_) |
| observer.OnAnimate(begin_frame_time); |
| } |
| |
| void WindowAndroid::OnVSync(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong time_micros, |
| jlong period_micros) { |
| // Warning: It is generally unsafe to manufacture TimeTicks values. The |
| // following assumption is being made, AND COULD EASILY BREAK AT ANY TIME: |
| // Upstream, Java code is providing "System.nanos() / 1000," and this is the |
| // same timestamp that would be provided by the CLOCK_MONOTONIC POSIX clock. |
| DCHECK_EQ(base::TimeTicks::GetClock(), |
| base::TimeTicks::Clock::LINUX_CLOCK_MONOTONIC); |
| base::TimeTicks frame_time = |
| base::TimeTicks() + base::TimeDelta::FromMicroseconds(time_micros); |
| base::TimeDelta vsync_period( |
| base::TimeDelta::FromMicroseconds(period_micros)); |
| |
| begin_frame_source_->OnVSync(frame_time, vsync_period); |
| |
| if (needs_begin_frames_) |
| RequestVSyncUpdate(); |
| } |
| |
| void WindowAndroid::OnVisibilityChanged(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| bool visible) { |
| for (WindowAndroidObserver& observer : observer_list_) |
| observer.OnRootWindowVisibilityChanged(visible); |
| } |
| |
| void WindowAndroid::OnActivityStopped(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| for (WindowAndroidObserver& observer : observer_list_) |
| observer.OnActivityStopped(); |
| } |
| |
| void WindowAndroid::OnActivityStarted(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| for (WindowAndroidObserver& observer : observer_list_) |
| observer.OnActivityStarted(); |
| } |
| |
| void WindowAndroid::SetVSyncPaused(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| bool paused) { |
| vsync_paused_ = paused; |
| |
| if (compositor_) |
| compositor_->SetVSyncPaused(paused); |
| |
| begin_frame_source_->OnPauseChanged(paused); |
| } |
| |
| bool WindowAndroid::HasPermission(const std::string& permission) { |
| JNIEnv* env = AttachCurrentThread(); |
| return Java_WindowAndroid_hasPermission( |
| env, GetJavaObject(), |
| base::android::ConvertUTF8ToJavaString(env, permission)); |
| } |
| |
| bool WindowAndroid::CanRequestPermission(const std::string& permission) { |
| JNIEnv* env = AttachCurrentThread(); |
| return Java_WindowAndroid_canRequestPermission( |
| env, GetJavaObject(), |
| base::android::ConvertUTF8ToJavaString(env, permission)); |
| } |
| |
| WindowAndroid* WindowAndroid::GetWindowAndroid() const { |
| DCHECK(parent_ == nullptr); |
| return const_cast<WindowAndroid*>(this); |
| } |
| |
| ScopedJavaLocalRef<jobject> WindowAndroid::GetWindowToken() { |
| JNIEnv* env = AttachCurrentThread(); |
| return Java_WindowAndroid_getWindowToken(env, GetJavaObject()); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Native JNI methods |
| // ---------------------------------------------------------------------------- |
| |
| jlong JNI_WindowAndroid_Init(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| int sdk_display_id, |
| float scroll_factor) { |
| WindowAndroid* window = |
| new WindowAndroid(env, obj, sdk_display_id, scroll_factor); |
| return reinterpret_cast<intptr_t>(window); |
| } |
| |
| } // namespace ui |