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