// Copyright 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.

package org.chromium.base;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class SystemMessageHandler extends Handler {

    private static final String TAG = "SystemMessageHandler";

    private static final int SCHEDULED_WORK = 1;
    private static final int DELAYED_SCHEDULED_WORK = 2;

    // Reflected API for marking a message as asynchronous. This is a workaround
    // to provide *fair* Chromium task dispatch when served by the Android UI
    // thread's Looper, avoiding stalls when the Looper has a sync barrier.
    // Note: Use of this API is experimental and demonstrative, and is likely
    // to be removed in the near future.
    private Method mMessageMethodSetAsynchonous;

    // Native class pointer set by the constructor of the SharedClient native class.
    private long mMessagePumpDelegateNative = 0;
    private long mDelayedScheduledTimeTicks = 0;

    private SystemMessageHandler(long messagePumpDelegateNative) {
        mMessagePumpDelegateNative = messagePumpDelegateNative;

        try {
            Class<?> messageClass = Class.forName("android.os.Message");
            mMessageMethodSetAsynchonous = messageClass.getMethod(
                    "setAsynchronous", new Class[]{boolean.class});
        } catch (ClassNotFoundException e) {
            Log.e(TAG, "Failed to find android.os.Message class:" + e);
        } catch (NoSuchMethodException e) {
            Log.e(TAG, "Failed to load Message.setAsynchronous method:" + e);
        } catch (RuntimeException e) {
            Log.e(TAG, "Exception while loading Message.setAsynchronous method: " + e);
        }
    }

    @Override
    public void handleMessage(Message msg) {
        if (msg.what == DELAYED_SCHEDULED_WORK) {
            mDelayedScheduledTimeTicks = 0;
        }
        nativeDoRunLoopOnce(mMessagePumpDelegateNative, mDelayedScheduledTimeTicks);
    }

    @SuppressWarnings("unused")
    @CalledByNative
    private void scheduleWork() {
        sendMessage(obtainAsyncMessage(SCHEDULED_WORK));
    }

    @SuppressWarnings("unused")
    @CalledByNative
    private void scheduleDelayedWork(long delayedTimeTicks, long millis) {
        if (mDelayedScheduledTimeTicks != 0) {
            removeMessages(DELAYED_SCHEDULED_WORK);
        }
        mDelayedScheduledTimeTicks = delayedTimeTicks;
        sendMessageDelayed(obtainAsyncMessage(DELAYED_SCHEDULED_WORK), millis);
    }

    @SuppressWarnings("unused")
    @CalledByNative
    private void removeAllPendingMessages() {
        removeMessages(SCHEDULED_WORK);
        removeMessages(DELAYED_SCHEDULED_WORK);
    }

    private Message obtainAsyncMessage(int what) {
        Message msg = Message.obtain();
        msg.what = what;
        if (mMessageMethodSetAsynchonous != null) {
            // If invocation fails, assume this is indicative of future
            // failures, and avoid log spam by nulling the reflected method.
            try {
                mMessageMethodSetAsynchonous.invoke(msg, true);
            } catch (IllegalAccessException e) {
                Log.e(TAG, "Illegal access to asynchronous message creation, disabling.");
                mMessageMethodSetAsynchonous = null;
            } catch (IllegalArgumentException e) {
                Log.e(TAG, "Illegal argument for asynchronous message creation, disabling.");
                mMessageMethodSetAsynchonous = null;
            } catch (InvocationTargetException e) {
                Log.e(TAG, "Invocation exception during asynchronous message creation, disabling.");
                mMessageMethodSetAsynchonous = null;
            }
        }
        return msg;
    }

    @CalledByNative
    private static SystemMessageHandler create(long messagePumpDelegateNative) {
        return new SystemMessageHandler(messagePumpDelegateNative);
    }

    private native void nativeDoRunLoopOnce(
            long messagePumpDelegateNative, long delayedScheduledTimeTicks);
}

@@ -244,119 +244,8 @@
 age;
-%0Aimport android.util.Log;%0A%0Aimport java.lang.reflect.InvocationTargetException;%0Aimport java.lang.reflect.Method;
 %0A%0Acl
@@ -292,71 +292,8 @@
  %7B%0A%0A
-    private static final String TAG = %22SystemMessageHandler%22;%0A%0A
     
@@ -406,1318 +406,315 @@
  // 
-Reflected API for marking a message as asynchronous. This is a workaround%0A    // to provide *fair* Chromium task dispatch when served by the Android UI%0A    // thread's Looper, avoiding stalls when the Looper has a sync barrier.%0A    // Note: Use of this API is experimental and demonstrative, and is likely%0A    // to be removed in the near future.%0A    private Method mMessageMethodSetAsynchonous;%0A%0A    // Native class pointer set by the constructor of the SharedClient native class.%0A    private long mMessagePumpDelegateNative = 0;%0A    private long mDelayedScheduledTimeTicks = 0;%0A%0A    private SystemMessageHandler(long messagePumpDelegateNative) %7B%0A        mMessagePumpDelegateNative = messagePumpDelegateNative;%0A%0A        try %7B%0A            Class%3C?%3E messageClass = Class.forName(%22android.os.Message%22);%0A            mMessageMethodSetAsynchonous = messageClass.getMethod(%0A                    %22setAsynchronous%22, new Class%5B%5D%7Bboolean.class%7D);%0A        %7D catch (ClassNotFoundException e) %7B%0A            Log.e(TAG, %22Failed to find android.os.Message class:%22 + e);%0A        %7D catch (NoSuchMethodException e) %7B%0A            Log.e(TAG, %22Failed to load Message.setAsynchronous method:%22 + e);%0A        %7D catch (RuntimeException e) %7B%0A            Log.e(TAG, %22Exception while loading Message.setAsynchronous method: %22 + e);%0A        %7D
+Native class pointer set by the constructor of the SharedClient native class.%0A    private long mMessagePumpDelegateNative = 0;%0A    private long mDelayedScheduledTimeTicks = 0;%0A%0A    private SystemMessageHandler(long messagePumpDelegateNative) %7B%0A        mMessagePumpDelegateNative = messagePumpDelegateNative;
 %0A   
@@ -1074,27 +1074,13 @@
 send
-Message(obtainAsync
+Empty
 Mess
@@ -1098,17 +1098,16 @@
 ED_WORK)
-)
 ;%0A    %7D%0A
@@ -1410,16 +1410,21 @@
     send
+Empty
 MessageD
@@ -1430,35 +1430,16 @@
 Delayed(
-obtainAsyncMessage(
 DELAYED_
@@ -1452,17 +1452,16 @@
 LED_WORK
-)
 , millis
@@ -1667,1055 +1667,8 @@
  %7D%0A%0A
-    private Message obtainAsyncMessage(int what) %7B%0A        Message msg = Message.obtain();%0A        msg.what = what;%0A        if (mMessageMethodSetAsynchonous != null) %7B%0A            // If invocation fails, assume this is indicative of future%0A            // failures, and avoid log spam by nulling the reflected method.%0A            try %7B%0A                mMessageMethodSetAsynchonous.invoke(msg, true);%0A            %7D catch (IllegalAccessException e) %7B%0A                Log.e(TAG, %22Illegal access to asynchronous message creation, disabling.%22);%0A                mMessageMethodSetAsynchonous = null;%0A            %7D catch (IllegalArgumentException e) %7B%0A                Log.e(TAG, %22Illegal argument for asynchronous message creation, disabling.%22);%0A                mMessageMethodSetAsynchonous = null;%0A            %7D catch (InvocationTargetException e) %7B%0A                Log.e(TAG, %22Invocation exception during asynchronous message creation, disabling.%22);%0A                mMessageMethodSetAsynchonous = null;%0A            %7D%0A        %7D%0A        return msg;%0A    %7D%0A%0A
     

Revert of [Android] Mark posted UI thread tasks as asynchronous (patchset #4 id:60001 of https://codereview.chromium.org/512333002/)