Home | History | Annotate | Download | only in base
      1 // Copyright 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 package org.chromium.base;
      6 
      7 import android.annotation.SuppressLint;
      8 import android.os.Build;
      9 import android.os.Handler;
     10 import android.os.Message;
     11 
     12 import org.chromium.base.annotations.CalledByNative;
     13 import org.chromium.base.annotations.MainDex;
     14 
     15 import java.lang.reflect.InvocationTargetException;
     16 import java.lang.reflect.Method;
     17 
     18 @MainDex
     19 class SystemMessageHandler extends Handler {
     20 
     21     private static final String TAG = "cr.SysMessageHandler";
     22 
     23     private static final int SCHEDULED_WORK = 1;
     24     private static final int DELAYED_SCHEDULED_WORK = 2;
     25 
     26     // Native class pointer set by the constructor of the SharedClient native class.
     27     private long mMessagePumpDelegateNative = 0;
     28     private long mMessagePumpNative = 0;
     29     private long mDelayedScheduledTimeTicks = 0;
     30 
     31     protected SystemMessageHandler(long messagePumpDelegateNative, long messagePumpNative) {
     32         mMessagePumpDelegateNative = messagePumpDelegateNative;
     33         mMessagePumpNative = messagePumpNative;
     34     }
     35 
     36     @Override
     37     public void handleMessage(Message msg) {
     38         if (msg.what == DELAYED_SCHEDULED_WORK) {
     39             mDelayedScheduledTimeTicks = 0;
     40         }
     41         nativeDoRunLoopOnce(
     42                 mMessagePumpDelegateNative, mMessagePumpNative, mDelayedScheduledTimeTicks);
     43     }
     44 
     45     @SuppressWarnings("unused")
     46     @CalledByNative
     47     private void scheduleWork() {
     48         sendMessage(obtainAsyncMessage(SCHEDULED_WORK));
     49     }
     50 
     51     @SuppressWarnings("unused")
     52     @CalledByNative
     53     private void scheduleDelayedWork(long delayedTimeTicks, long millis) {
     54         if (mDelayedScheduledTimeTicks != 0) {
     55             removeMessages(DELAYED_SCHEDULED_WORK);
     56         }
     57         mDelayedScheduledTimeTicks = delayedTimeTicks;
     58         sendMessageDelayed(obtainAsyncMessage(DELAYED_SCHEDULED_WORK), millis);
     59     }
     60 
     61     @SuppressWarnings("unused")
     62     @CalledByNative
     63     private void removeAllPendingMessages() {
     64         removeMessages(SCHEDULED_WORK);
     65         removeMessages(DELAYED_SCHEDULED_WORK);
     66     }
     67 
     68     private Message obtainAsyncMessage(int what) {
     69         // Marking the message async provides fair Chromium task dispatch when
     70         // served by the Android UI thread's Looper, avoiding stalls when the
     71         // Looper has a sync barrier.
     72         Message msg = Message.obtain();
     73         msg.what = what;
     74         MessageCompat.setAsynchronous(msg, true);
     75         return msg;
     76     }
     77 
     78     /**
     79      * Abstraction utility class for marking a Message as asynchronous. Prior
     80      * to L MR1 the async Message API was hidden, and for such cases we fall
     81      * back to using reflection to obtain the necessary method.
     82      */
     83     private static class MessageCompat {
     84         /**
     85          * @See android.os.Message#setAsynchronous(boolean)
     86          */
     87         public static void setAsynchronous(Message message, boolean async) {
     88             IMPL.setAsynchronous(message, async);
     89         }
     90 
     91         interface MessageWrapperImpl {
     92             /**
     93              * @See android.os.Message#setAsynchronous(boolean)
     94              */
     95             public void setAsynchronous(Message message, boolean async);
     96         }
     97 
     98         static final MessageWrapperImpl IMPL;
     99         static {
    100             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
    101                 IMPL = new LollipopMr1MessageWrapperImpl();
    102             } else {
    103                 IMPL = new LegacyMessageWrapperImpl();
    104             }
    105         }
    106 
    107         static class LollipopMr1MessageWrapperImpl implements MessageWrapperImpl {
    108             @SuppressLint("NewApi")
    109             @Override
    110             public void setAsynchronous(Message msg, boolean async) {
    111                 msg.setAsynchronous(async);
    112             }
    113         }
    114 
    115         static class LegacyMessageWrapperImpl implements MessageWrapperImpl {
    116             // Reflected API for marking a message as asynchronous.
    117             // Note: Use of this API is experimental and likely to evolve in the future.
    118             private Method mMessageMethodSetAsynchronous;
    119 
    120             LegacyMessageWrapperImpl() {
    121                 try {
    122                     Class<?> messageClass = Class.forName("android.os.Message");
    123                     mMessageMethodSetAsynchronous =
    124                             messageClass.getMethod("setAsynchronous", new Class[] {boolean.class});
    125                 } catch (ClassNotFoundException e) {
    126                     Log.e(TAG, "Failed to find android.os.Message class", e);
    127                 } catch (NoSuchMethodException e) {
    128                     Log.e(TAG, "Failed to load Message.setAsynchronous method", e);
    129                 } catch (RuntimeException e) {
    130                     Log.e(TAG, "Exception while loading Message.setAsynchronous method", e);
    131                 }
    132             }
    133 
    134             @Override
    135             public void setAsynchronous(Message msg, boolean async) {
    136                 if (mMessageMethodSetAsynchronous == null) return;
    137                 // If invocation fails, assume this is indicative of future
    138                 // failures, and avoid log spam by nulling the reflected method.
    139                 try {
    140                     mMessageMethodSetAsynchronous.invoke(msg, async);
    141                 } catch (IllegalAccessException e) {
    142                     Log.e(TAG, "Illegal access to async message creation, disabling.");
    143                     mMessageMethodSetAsynchronous = null;
    144                 } catch (IllegalArgumentException e) {
    145                     Log.e(TAG, "Illegal argument for async message creation, disabling.");
    146                     mMessageMethodSetAsynchronous = null;
    147                 } catch (InvocationTargetException e) {
    148                     Log.e(TAG, "Invocation exception during async message creation, disabling.");
    149                     mMessageMethodSetAsynchronous = null;
    150                 } catch (RuntimeException e) {
    151                     Log.e(TAG, "Runtime exception during async message creation, disabling.");
    152                     mMessageMethodSetAsynchronous = null;
    153                 }
    154             }
    155         }
    156     }
    157 
    158     @CalledByNative
    159     private static SystemMessageHandler create(
    160             long messagePumpDelegateNative, long messagePumpNative) {
    161         return new SystemMessageHandler(messagePumpDelegateNative, messagePumpNative);
    162     }
    163 
    164     private native void nativeDoRunLoopOnce(
    165             long messagePumpDelegateNative, long messagePumpNative, long delayedScheduledTimeTicks);
    166 }
    167