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