summaryrefslogtreecommitdiffstats
path: root/base/message_loop/message_pump_android.cc
blob: babd17b577ec0d45886bf88b81166837ddc46d0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright (c) 2012 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 "base/message_loop/message_pump_android.h"

#include <jni.h>

#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "jni/SystemMessageHandler_jni.h"

using base::android::ScopedJavaLocalRef;

// ----------------------------------------------------------------------------
// Native JNI methods called by Java.
// ----------------------------------------------------------------------------
// This method can not move to anonymous namespace as it has been declared as
// 'static' in system_message_handler_jni.h.
static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate,
    jlong delayed_scheduled_time_ticks) {
  base::MessagePump::Delegate* delegate =
      reinterpret_cast<base::MessagePump::Delegate*>(native_delegate);
  DCHECK(delegate);
  // This is based on MessagePumpForUI::DoRunLoop() from desktop.
  // Note however that our system queue is handled in the java side.
  // In desktop we inspect and process a single system message and then
  // we call DoWork() / DoDelayedWork().
  // On Android, the java message queue may contain messages for other handlers
  // that will be processed before calling here again.
  bool did_work = delegate->DoWork();

  // In the java side, |SystemMessageHandler| keeps a single "delayed" message.
  // It's an expensive operation to |removeMessage| there, so this is optimized
  // to avoid those calls.
  //
  // At this stage, |next_delayed_work_time| can be:
  // 1) The same as previously scheduled: nothing to be done, move along. This
  // is the typical case, since this method is called for every single message.
  //
  // 2) Not previously scheduled: just post a new message in java.
  //
  // 3) Shorter than previously scheduled: far less common. In this case,
  // |removeMessage| and post a new one.
  //
  // 4) Longer than previously scheduled (or null): nothing to be done, move
  // along.
  //
  // Side note: base::TimeTicks is a C++ representation and can't be
  // compared in java. When calling |scheduleDelayedWork|, pass the
  // |InternalValue()| to java and then back to C++ so the comparisons can be
  // done here.
  // This roundtrip allows comparing TimeTicks directly (cheap) and
  // avoid comparisons with TimeDelta / Now() (expensive).
  base::TimeTicks next_delayed_work_time;
  did_work |= delegate->DoDelayedWork(&next_delayed_work_time);

  if (!next_delayed_work_time.is_null()) {
    // Schedule a new message if there's nothing already scheduled or there's a
    // shorter delay than previously scheduled (see (2) and (3) above).
    if (delayed_scheduled_time_ticks == 0 ||
        next_delayed_work_time < base::TimeTicks::FromInternalValue(
            delayed_scheduled_time_ticks)) {
      Java_SystemMessageHandler_scheduleDelayedWork(env, obj,
          next_delayed_work_time.ToInternalValue(),
          (next_delayed_work_time -
           base::TimeTicks::Now()).InMillisecondsRoundedUp());
    }
  }

  // This is a major difference between android and other platforms: since we
  // can't inspect it and process just one single message, instead we'll yeld
  // the callstack.
  if (did_work)
    return;

  delegate->DoIdleWork();
}

namespace base {

MessagePumpForUI::MessagePumpForUI()
    : run_loop_(NULL) {
}

MessagePumpForUI::~MessagePumpForUI() {
}

void MessagePumpForUI::Run(Delegate* delegate) {
  NOTREACHED() << "UnitTests should rely on MessagePumpForUIStub in"
      " test_stub_android.h";
}

void MessagePumpForUI::Start(Delegate* delegate) {
  run_loop_ = new RunLoop();
  // Since the RunLoop was just created above, BeforeRun should be guaranteed to
  // return true (it only returns false if the RunLoop has been Quit already).
  if (!run_loop_->BeforeRun())
    NOTREACHED();

  DCHECK(system_message_handler_obj_.is_null());

  JNIEnv* env = base::android::AttachCurrentThread();
  DCHECK(env);

  system_message_handler_obj_.Reset(
      Java_SystemMessageHandler_create(
          env, reinterpret_cast<intptr_t>(delegate)));
}

void MessagePumpForUI::Quit() {
  if (!system_message_handler_obj_.is_null()) {
    JNIEnv* env = base::android::AttachCurrentThread();
    DCHECK(env);

    Java_SystemMessageHandler_removeAllPendingMessages(env,
        system_message_handler_obj_.obj());
    system_message_handler_obj_.Reset();
  }

  if (run_loop_) {
    run_loop_->AfterRun();
    delete run_loop_;
    run_loop_ = NULL;
  }
}

void MessagePumpForUI::ScheduleWork() {
  DCHECK(!system_message_handler_obj_.is_null());

  JNIEnv* env = base::android::AttachCurrentThread();
  DCHECK(env);

  Java_SystemMessageHandler_scheduleWork(env,
      system_message_handler_obj_.obj());
}

void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
  DCHECK(!system_message_handler_obj_.is_null());

  JNIEnv* env = base::android::AttachCurrentThread();
  DCHECK(env);

  jlong millis =
      (delayed_work_time - TimeTicks::Now()).InMillisecondsRoundedUp();
  // Note that we're truncating to milliseconds as required by the java side,
  // even though delayed_work_time is microseconds resolution.
  Java_SystemMessageHandler_scheduleDelayedWork(env,
      system_message_handler_obj_.obj(),
      delayed_work_time.ToInternalValue(), millis);
}

// static
bool MessagePumpForUI::RegisterBindings(JNIEnv* env) {
  return RegisterNativesImpl(env);
}

}  // namespace base