blob: 10f8d55448e014b8d9717d34197428fbf4bd9200 [file] [log] [blame]
// 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.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.util.Log;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class SystemMessageHandler extends Handler {
private static final String TAG = "SystemMessageHandler";
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;
// The following members are used to detect and trace the presence of sync
// barriers in Android's MessageQueue. Note that this detection is
// experimental, temporary and intended only for diagnostic purposes.
private MessageQueue mMessageQueue;
private Field mMessageQueueMessageField;
private Field mMessageTargetField;
private boolean mQueueHasSyncBarrier;
private long mSyncBarrierTraceId;
private SystemMessageHandler(long messagePumpDelegateNative) {
mMessagePumpDelegateNative = messagePumpDelegateNative;
tryEnableSyncBarrierDetection();
}
@Override
public void handleMessage(Message msg) {
updateWhetherQueueHasBlockingSyncBarrier();
if (msg.what == DELAYED_SCHEDULED_WORK) {
mDelayedScheduledTimeTicks = 0;
}
nativeDoRunLoopOnce(mMessagePumpDelegateNative, mDelayedScheduledTimeTicks);
}
@SuppressWarnings("unused")
@CalledByNative
private void scheduleWork() {
updateWhetherQueueHasBlockingSyncBarrier();
if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:immediateWorkBlocked");
sendEmptyMessage(SCHEDULED_WORK);
}
@SuppressWarnings("unused")
@CalledByNative
private void scheduleDelayedWork(long delayedTimeTicks, long millis) {
if (mDelayedScheduledTimeTicks != 0) {
removeMessages(DELAYED_SCHEDULED_WORK);
}
mDelayedScheduledTimeTicks = delayedTimeTicks;
updateWhetherQueueHasBlockingSyncBarrier();
if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:delayedWorkBlocked");
sendEmptyMessageDelayed(DELAYED_SCHEDULED_WORK, millis);
}
@SuppressWarnings("unused")
@CalledByNative
private void removeAllPendingMessages() {
updateWhetherQueueHasBlockingSyncBarrier();
removeMessages(SCHEDULED_WORK);
removeMessages(DELAYED_SCHEDULED_WORK);
}
private void updateWhetherQueueHasBlockingSyncBarrier() {
if (mMessageQueue == null) return;
// As barrier detection is only used for tracing, early out when tracing
// is disabled to avoid any potential performance penalties.
if (!TraceEvent.enabled()) {
mQueueHasSyncBarrier = false;
return;
}
Message queueHead = (Message) getField(mMessageQueue, mMessageQueueMessageField);
setqueueHasSyncBarrier(isSyncBarrierMessage(queueHead));
}
private boolean isSyncBarrierMessage(Message message) {
if (message == null) return false;
// Sync barrier messages have null targets.
return getField(message, mMessageTargetField) == null;
}
private void tryEnableSyncBarrierDetection() {
assert mMessageQueue == null;
boolean success = false;
try {
Method getQueueMethod = Looper.class.getMethod("getQueue", new Class[]{});
mMessageQueue = (MessageQueue) getQueueMethod.invoke(getLooper());
mMessageQueueMessageField = mMessageQueue.getClass().getDeclaredField("mMessages");
mMessageQueueMessageField.setAccessible(true);
mMessageTargetField = Message.class.getDeclaredField("target");
mMessageTargetField.setAccessible(true);
mSyncBarrierTraceId = hashCode();
success = true;
} catch (NoSuchMethodException e) {
Log.e(TAG, "Failed to load method: " + e);
} catch (NoSuchFieldException e) {
Log.e(TAG, "Failed to load field: " + e);
} catch (InvocationTargetException e) {
Log.e(TAG, "Failed invocation: " + e);
} catch (IllegalAccessException e) {
Log.e(TAG, "Illegal access to reflected invocation: " + e);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument to reflected invocation: " + e);
} catch (RuntimeException e) {
Log.e(TAG, e.toString());
} finally {
if (!success) disableSyncBarrierDetection();
}
}
private void disableSyncBarrierDetection() {
Log.e(TAG, "Unexpected error with sync barrier detection, disabling.");
mMessageQueue = null;
mMessageQueueMessageField = null;
mMessageTargetField = null;
setqueueHasSyncBarrier(false);
}
private void setqueueHasSyncBarrier(boolean queueHasSyncBarrier) {
if (queueHasSyncBarrier == mQueueHasSyncBarrier) return;
mQueueHasSyncBarrier = queueHasSyncBarrier;
if (mQueueHasSyncBarrier) {
TraceEvent.startAsync("SyncBarrier", mSyncBarrierTraceId);
} else {
TraceEvent.finishAsync("SyncBarrier", mSyncBarrierTraceId);
}
}
private Object getField(Object object, Field field) {
try {
return field.get(object);
} catch (IllegalAccessException e) {
Log.e(TAG, "Failed field access: " + e);
disableSyncBarrierDetection();
}
return null;
}
@CalledByNative
private static SystemMessageHandler create(long messagePumpDelegateNative) {
return new SystemMessageHandler(messagePumpDelegateNative);
}
private native void nativeDoRunLoopOnce(
long messagePumpDelegateNative, long delayedScheduledTimeTicks);
}