// 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.Build;
import android.os.Handler;
import android.os.Message;

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

class SystemMessageHandler extends Handler {

    private static final String TAG = "cr.SysMessageHandler";

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

    // 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;
    }

    @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) {
        // Marking the message async provides fair Chromium task dispatch when
        // served by the Android UI thread's Looper, avoiding stalls when the
        // Looper has a sync barrier.
        Message msg = Message.obtain();
        msg.what = what;
        MessageCompat.setAsynchronous(msg, true);
        return msg;
    }

    /**
     * Abstraction utility class for marking a Message as asynchronous. Prior
     * to L MR1 the async Message API was hidden, and for such cases we fall
     * back to using reflection to obtain the necessary method.
     */
    private static class MessageCompat {
        /**
         * @See android.os.Message#setAsynchronous(boolean)
         */
        public static void setAsynchronous(Message message, boolean async) {
            IMPL.setAsynchronous(message, async);
        }

        interface MessageWrapperImpl {
            /**
             * @See android.os.Message#setAsynchronous(boolean)
             */
            public void setAsynchronous(Message message, boolean async);
        }

        static final MessageWrapperImpl IMPL;
        static {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
                IMPL = new LollipopMr1MessageWrapperImpl();
            } else {
                IMPL = new LegacyMessageWrapperImpl();
            }
        }

        static class LollipopMr1MessageWrapperImpl implements MessageWrapperImpl {
            @Override
            public void setAsynchronous(Message msg, boolean async) {
                msg.setAsynchronous(async);
            }
        }

        static class LegacyMessageWrapperImpl implements MessageWrapperImpl {
            // Reflected API for marking a message as asynchronous.
            // Note: Use of this API is experimental and likely to evolve in the future.
            private Method mMessageMethodSetAsynchronous;

            LegacyMessageWrapperImpl() {
                try {
                    Class<?> messageClass = Class.forName("android.os.Message");
                    mMessageMethodSetAsynchronous =
                            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 setAsynchronous(Message msg, boolean async) {
                if (mMessageMethodSetAsynchronous == null) return;
                // If invocation fails, assume this is indicative of future
                // failures, and avoid log spam by nulling the reflected method.
                try {
                    mMessageMethodSetAsynchronous.invoke(msg, async);
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "Illegal access to async message creation, disabling.");
                    mMessageMethodSetAsynchronous = null;
                } catch (IllegalArgumentException e) {
                    Log.e(TAG, "Illegal argument for async message creation, disabling.");
                    mMessageMethodSetAsynchronous = null;
                } catch (InvocationTargetException e) {
                    Log.e(TAG, "Invocation exception during async message creation, disabling.");
                    mMessageMethodSetAsynchronous = null;
                } catch (RuntimeException e) {
                    Log.e(TAG, "Runtime exception during async message creation, disabling.");
                    mMessageMethodSetAsynchronous = null;
                }
            }
        }
    }

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

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

@@ -183,24 +183,64 @@
 mium.base;%0A%0A
+import android.annotation.SuppressLint;%0A
 import andro
@@ -3388,32 +3388,68 @@
 geWrapperImpl %7B%0A
+            @SuppressLint(%22NewApi%22)%0A
             @Ove

Fix lint new api error in SystemMessageHandler.