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

content / public / test / nested_message_pump_android.cc [blame]

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/public/test/nested_message_pump_android.h"

#include "base/android/jni_android.h"
#include "base/auto_reset.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "content/public/test/android/test_support_content_jni_headers/NestedSystemMessageHandler_jni.h"

namespace content {

NestedMessagePumpAndroid::NestedMessagePumpAndroid() = default;
NestedMessagePumpAndroid::~NestedMessagePumpAndroid() = default;

// We need to override Run() instead of using the implementation from
// base::MessagePumpAndroid because one of the side-effects of
// dispatchOneMessage() is calling Looper::pollOnce(). If that happens while we
// are inside Alooper_pollOnce(), we get a crash because Android Looper isn't
// re-entrant safe. Instead, we keep the entire run loop in Java (in
// MessageQueue.next()).
void NestedMessagePumpAndroid::Run(base::MessagePump::Delegate* delegate) {
  // Preserve delegate and quit state of the current run loop so it can be
  // restored after the loop below has completed.
  auto* old_delegate = SetDelegate(delegate);
  bool old_quit = SetQuit(false);

  ScheduleWork();
  while (!ShouldQuit()) {
    RunJavaSystemMessageHandler();

    // Handle deferred work if necessary.
    // Note: |deferred_work_type_| and |deferred_do_idle_work_| must be reset
    // here because another Run() loop can be triggered when we dispatch work.
    CHECK(!ShouldDeferWork());
    auto work_type = std::exchange(deferred_work_type_, kNone);
    auto do_idle_work = std::exchange(deferred_do_idle_work_, true);
    switch (work_type) {
      case kNone:
        // Do nothing.
        break;
      case kDelayed:
        base::MessagePumpAndroid::DoDelayedLooperWork();
        break;
      case kNonDelayed:
        base::MessagePumpAndroid::DoNonDelayedLooperWork(do_idle_work);
        break;
    }
  }

  // Restore old run loop state.
  SetDelegate(old_delegate);
  SetQuit(old_quit);
}

void NestedMessagePumpAndroid::Quit() {
  QuitJavaSystemMessageHandler();
  SetQuit(true);
}

// Since this pump allows Run() to be called on the UI thread, there's no need
// to also attach the pump on that thread. Making attach a no-op also prevents
// the top level run loop from being considered a nested one.
void NestedMessagePumpAndroid::Attach(Delegate*) {}

void NestedMessagePumpAndroid::DoDelayedLooperWork() {
  if (ShouldDeferWork()) {
    switch (deferred_work_type_) {
      case kNone:
        deferred_work_type_ = kDelayed;
        break;
      case kDelayed:
        // Do nothing. We are already going to process the next delayed work.
        break;
      case kNonDelayed:
        // Do nothing. The immediate work will be processed and then a
        // request to process immediate and idle work will be made. This
        // will unconditionally process the next work item which should be
        // the delayed item.
        break;
    }
    QuitJavaSystemMessageHandler();
    return;
  }

  base::MessagePumpAndroid::DoDelayedLooperWork();
}

void NestedMessagePumpAndroid::DoNonDelayedLooperWork(bool do_idle_work) {
  if (ShouldDeferWork()) {
    deferred_work_type_ = kNonDelayed;

    // Only do idle work if we are consistently asked to do it. If there
    // is a request to defer the idle work, then we will honor that over
    // any previous request to do idle work. Once the non-delayed work is
    // dispatched, another request to do the idle work will be made.
    deferred_do_idle_work_ &= do_idle_work;

    QuitJavaSystemMessageHandler();
    return;
  }

  base::MessagePumpAndroid::DoNonDelayedLooperWork(do_idle_work);
}

void NestedMessagePumpAndroid::RunJavaSystemMessageHandler() {
  auto* env = base::android::AttachCurrentThread();
  CHECK(!quit_message_handler_);
  CHECK(!inside_run_message_handler_);
  base::AutoReset<bool> auto_reset(&inside_run_message_handler_, true);
  // Dispatch the first available Java message and one or more C++ messages as
  // a side effect. If there are no Java messages available, this call will
  // block until one becomes available (while continuing to process C++ work).
  bool ret = Java_NestedSystemMessageHandler_dispatchOneMessage(env);
  CHECK(ret) << "Error running java message loop, tests will likely fail.";
  quit_message_handler_ = false;
}

void NestedMessagePumpAndroid::QuitJavaSystemMessageHandler() {
  if (!inside_run_message_handler_ || quit_message_handler_)
    return;

  quit_message_handler_ = true;

  // Wake up the Java message dispatcher to exit dispatchOneMessage().
  auto* env = base::android::AttachCurrentThread();
  Java_NestedSystemMessageHandler_postQuitMessage(env);
}

}  // namespace content