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