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.os.Handler; 8 import android.os.Message; 9 import android.util.Log; 10 11 import java.lang.reflect.InvocationTargetException; 12 import java.lang.reflect.Method; 13 14 class SystemMessageHandler extends Handler { 15 16 private static final String TAG = "SystemMessageHandler"; 17 18 private static final int SCHEDULED_WORK = 1; 19 private static final int DELAYED_SCHEDULED_WORK = 2; 20 21 // Native class pointer set by the constructor of the SharedClient native class. 22 private long mMessagePumpDelegateNative = 0; 23 private long mDelayedScheduledTimeTicks = 0; 24 25 // Reflected API for marking a message as asynchronous. This is a workaround 26 // to provide fair Chromium task dispatch when served by the Android UI 27 // thread's Looper, avoiding stalls when the Looper has a sync barrier. 28 // Note: Use of this API is experimental and likely to evolve in the future. 29 private Method mMessageMethodSetAsynchronous; 30 SystemMessageHandler(long messagePumpDelegateNative)31 private SystemMessageHandler(long messagePumpDelegateNative) { 32 mMessagePumpDelegateNative = messagePumpDelegateNative; 33 34 try { 35 Class<?> messageClass = Class.forName("android.os.Message"); 36 mMessageMethodSetAsynchronous = messageClass.getMethod( 37 "setAsynchronous", new Class[]{boolean.class}); 38 } catch (ClassNotFoundException e) { 39 Log.e(TAG, "Failed to find android.os.Message class:" + e); 40 } catch (NoSuchMethodException e) { 41 Log.e(TAG, "Failed to load Message.setAsynchronous method:" + e); 42 } catch (RuntimeException e) { 43 Log.e(TAG, "Exception while loading Message.setAsynchronous method: " + e); 44 } 45 46 } 47 48 @Override handleMessage(Message msg)49 public void handleMessage(Message msg) { 50 if (msg.what == DELAYED_SCHEDULED_WORK) { 51 mDelayedScheduledTimeTicks = 0; 52 } 53 nativeDoRunLoopOnce(mMessagePumpDelegateNative, mDelayedScheduledTimeTicks); 54 } 55 56 @SuppressWarnings("unused") 57 @CalledByNative scheduleWork()58 private void scheduleWork() { 59 sendMessage(obtainAsyncMessage(SCHEDULED_WORK)); 60 } 61 62 @SuppressWarnings("unused") 63 @CalledByNative scheduleDelayedWork(long delayedTimeTicks, long millis)64 private void scheduleDelayedWork(long delayedTimeTicks, long millis) { 65 if (mDelayedScheduledTimeTicks != 0) { 66 removeMessages(DELAYED_SCHEDULED_WORK); 67 } 68 mDelayedScheduledTimeTicks = delayedTimeTicks; 69 sendMessageDelayed(obtainAsyncMessage(DELAYED_SCHEDULED_WORK), millis); 70 } 71 72 @SuppressWarnings("unused") 73 @CalledByNative removeAllPendingMessages()74 private void removeAllPendingMessages() { 75 removeMessages(SCHEDULED_WORK); 76 removeMessages(DELAYED_SCHEDULED_WORK); 77 } 78 obtainAsyncMessage(int what)79 private Message obtainAsyncMessage(int what) { 80 Message msg = Message.obtain(); 81 msg.what = what; 82 if (mMessageMethodSetAsynchronous != null) { 83 // If invocation fails, assume this is indicative of future 84 // failures, and avoid log spam by nulling the reflected method. 85 try { 86 mMessageMethodSetAsynchronous.invoke(msg, true); 87 } catch (IllegalAccessException e) { 88 Log.e(TAG, "Illegal access to asynchronous message creation, disabling."); 89 mMessageMethodSetAsynchronous = null; 90 } catch (IllegalArgumentException e) { 91 Log.e(TAG, "Illegal argument for asynchronous message creation, disabling."); 92 mMessageMethodSetAsynchronous = null; 93 } catch (InvocationTargetException e) { 94 Log.e(TAG, "Invocation exception during asynchronous message creation, disabling."); 95 mMessageMethodSetAsynchronous = null; 96 } catch (RuntimeException e) { 97 Log.e(TAG, "Runtime exception during asynchronous message creation, disabling."); 98 mMessageMethodSetAsynchronous = null; 99 } 100 } 101 return msg; 102 } 103 104 @CalledByNative create(long messagePumpDelegateNative)105 private static SystemMessageHandler create(long messagePumpDelegateNative) { 106 return new SystemMessageHandler(messagePumpDelegateNative); 107 } 108 nativeDoRunLoopOnce( long messagePumpDelegateNative, long delayedScheduledTimeTicks)109 private native void nativeDoRunLoopOnce( 110 long messagePumpDelegateNative, long delayedScheduledTimeTicks); 111 } 112