1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/message_loop/message_pump_android.h" 6 7 #include <jni.h> 8 9 #include "base/android/jni_android.h" 10 #include "base/android/scoped_java_ref.h" 11 #include "base/lazy_instance.h" 12 #include "base/logging.h" 13 #include "base/run_loop.h" 14 #include "base/time/time.h" 15 #include "jni/SystemMessageHandler_jni.h" 16 17 using base::android::ScopedJavaLocalRef; 18 19 // ---------------------------------------------------------------------------- 20 // Native JNI methods called by Java. 21 // ---------------------------------------------------------------------------- 22 // This method can not move to anonymous namespace as it has been declared as 23 // 'static' in system_message_handler_jni.h. 24 static void DoRunLoopOnce(JNIEnv* env, 25 const JavaParamRef<jobject>& obj, 26 jlong native_delegate, 27 jlong delayed_scheduled_time_ticks) { 28 base::MessagePump::Delegate* delegate = 29 reinterpret_cast<base::MessagePump::Delegate*>(native_delegate); 30 DCHECK(delegate); 31 // This is based on MessagePumpForUI::DoRunLoop() from desktop. 32 // Note however that our system queue is handled in the java side. 33 // In desktop we inspect and process a single system message and then 34 // we call DoWork() / DoDelayedWork(). 35 // On Android, the java message queue may contain messages for other handlers 36 // that will be processed before calling here again. 37 bool did_work = delegate->DoWork(); 38 39 // In the java side, |SystemMessageHandler| keeps a single "delayed" message. 40 // It's an expensive operation to |removeMessage| there, so this is optimized 41 // to avoid those calls. 42 // 43 // At this stage, |next_delayed_work_time| can be: 44 // 1) The same as previously scheduled: nothing to be done, move along. This 45 // is the typical case, since this method is called for every single message. 46 // 47 // 2) Not previously scheduled: just post a new message in java. 48 // 49 // 3) Shorter than previously scheduled: far less common. In this case, 50 // |removeMessage| and post a new one. 51 // 52 // 4) Longer than previously scheduled (or null): nothing to be done, move 53 // along. 54 // 55 // Side note: base::TimeTicks is a C++ representation and can't be 56 // compared in java. When calling |scheduleDelayedWork|, pass the 57 // |InternalValue()| to java and then back to C++ so the comparisons can be 58 // done here. 59 // This roundtrip allows comparing TimeTicks directly (cheap) and 60 // avoid comparisons with TimeDelta / Now() (expensive). 61 base::TimeTicks next_delayed_work_time; 62 did_work |= delegate->DoDelayedWork(&next_delayed_work_time); 63 64 if (!next_delayed_work_time.is_null()) { 65 // Schedule a new message if there's nothing already scheduled or there's a 66 // shorter delay than previously scheduled (see (2) and (3) above). 67 if (delayed_scheduled_time_ticks == 0 || 68 next_delayed_work_time < base::TimeTicks::FromInternalValue( 69 delayed_scheduled_time_ticks)) { 70 Java_SystemMessageHandler_scheduleDelayedWork(env, obj, 71 next_delayed_work_time.ToInternalValue(), 72 (next_delayed_work_time - 73 base::TimeTicks::Now()).InMillisecondsRoundedUp()); 74 } 75 } 76 77 // This is a major difference between android and other platforms: since we 78 // can't inspect it and process just one single message, instead we'll yeld 79 // the callstack. 80 if (did_work) 81 return; 82 83 delegate->DoIdleWork(); 84 } 85 86 namespace base { 87 88 MessagePumpForUI::MessagePumpForUI() 89 : run_loop_(NULL) { 90 } 91 92 MessagePumpForUI::~MessagePumpForUI() { 93 } 94 95 void MessagePumpForUI::Run(Delegate* delegate) { 96 NOTREACHED() << "UnitTests should rely on MessagePumpForUIStub in" 97 " test_stub_android.h"; 98 } 99 100 void MessagePumpForUI::Start(Delegate* delegate) { 101 run_loop_ = new RunLoop(); 102 // Since the RunLoop was just created above, BeforeRun should be guaranteed to 103 // return true (it only returns false if the RunLoop has been Quit already). 104 if (!run_loop_->BeforeRun()) 105 NOTREACHED(); 106 107 DCHECK(system_message_handler_obj_.is_null()); 108 109 JNIEnv* env = base::android::AttachCurrentThread(); 110 DCHECK(env); 111 112 system_message_handler_obj_.Reset( 113 Java_SystemMessageHandler_create( 114 env, reinterpret_cast<intptr_t>(delegate))); 115 } 116 117 void MessagePumpForUI::Quit() { 118 if (!system_message_handler_obj_.is_null()) { 119 JNIEnv* env = base::android::AttachCurrentThread(); 120 DCHECK(env); 121 122 Java_SystemMessageHandler_removeAllPendingMessages(env, 123 system_message_handler_obj_.obj()); 124 system_message_handler_obj_.Reset(); 125 } 126 127 if (run_loop_) { 128 run_loop_->AfterRun(); 129 delete run_loop_; 130 run_loop_ = NULL; 131 } 132 } 133 134 void MessagePumpForUI::ScheduleWork() { 135 DCHECK(!system_message_handler_obj_.is_null()); 136 137 JNIEnv* env = base::android::AttachCurrentThread(); 138 DCHECK(env); 139 140 Java_SystemMessageHandler_scheduleWork(env, 141 system_message_handler_obj_.obj()); 142 } 143 144 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { 145 DCHECK(!system_message_handler_obj_.is_null()); 146 147 JNIEnv* env = base::android::AttachCurrentThread(); 148 DCHECK(env); 149 150 jlong millis = 151 (delayed_work_time - TimeTicks::Now()).InMillisecondsRoundedUp(); 152 // Note that we're truncating to milliseconds as required by the java side, 153 // even though delayed_work_time is microseconds resolution. 154 Java_SystemMessageHandler_scheduleDelayedWork(env, 155 system_message_handler_obj_.obj(), 156 delayed_work_time.ToInternalValue(), millis); 157 } 158 159 // static 160 bool MessagePumpForUI::RegisterBindings(JNIEnv* env) { 161 return RegisterNativesImpl(env); 162 } 163 164 } // namespace base 165