am 3a2854bc: Merge "Queues, queues, queues and input." into jb-mr2-dev

* commit '3a2854bcee08e3af3ca1b042c1fef6ca68f8c70a':
  Queues, queues, queues and input.
This commit is contained in:
Jeff Brown
2013-04-08 17:03:20 -07:00
committed by Android Git Automerger
14 changed files with 1055 additions and 953 deletions

View File

@ -371,9 +371,9 @@ class ContextImpl extends Context {
return new DisplayManager(ctx.getOuterContext()); return new DisplayManager(ctx.getOuterContext());
}}); }});
registerService(INPUT_METHOD_SERVICE, new ServiceFetcher() { registerService(INPUT_METHOD_SERVICE, new StaticServiceFetcher() {
public Object createService(ContextImpl ctx) { public Object createStaticService() {
return InputMethodManager.getInstance(ctx); return InputMethodManager.getInstance();
}}); }});
registerService(TEXT_SERVICES_MANAGER_SERVICE, new ServiceFetcher() { registerService(TEXT_SERVICES_MANAGER_SERVICE, new ServiceFetcher() {

View File

@ -1,7 +1,20 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app; package android.app;
import com.android.internal.view.IInputMethodSession;
import android.content.Context; import android.content.Context;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -25,7 +38,6 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import java.io.File; import java.io.File;
import java.lang.ref.WeakReference;
/** /**
* Convenience for implementing an activity that will be implemented * Convenience for implementing an activity that will be implemented
@ -65,7 +77,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
private NativeContentView mNativeContentView; private NativeContentView mNativeContentView;
private InputMethodManager mIMM; private InputMethodManager mIMM;
private InputMethodCallback mInputMethodCallback;
private int mNativeHandle; private int mNativeHandle;
@ -118,22 +129,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
} }
} }
static final class InputMethodCallback implements InputMethodManager.FinishedEventCallback {
WeakReference<NativeActivity> mNa;
InputMethodCallback(NativeActivity na) {
mNa = new WeakReference<NativeActivity>(na);
}
@Override
public void finishedEvent(int seq, boolean handled) {
NativeActivity na = mNa.get();
if (na != null) {
na.finishPreDispatchKeyEventNative(na.mNativeHandle, seq, handled);
}
}
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
String libname = "main"; String libname = "main";
@ -141,7 +136,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
ActivityInfo ai; ActivityInfo ai;
mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
mInputMethodCallback = new InputMethodCallback(this);
getWindow().takeSurface(this); getWindow().takeSurface(this);
getWindow().takeInputQueue(this); getWindow().takeInputQueue(this);
@ -353,7 +347,8 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
} }
void preDispatchKeyEvent(KeyEvent event, int seq) { void preDispatchKeyEvent(KeyEvent event, int seq) {
mIMM.dispatchInputEvent(this, seq, event, mInputMethodCallback); // FIXME: Input dispatch should be redirected back through ViewRootImpl again.
finishPreDispatchKeyEventNative(mNativeHandle, seq, false);
} }
void setWindowFlags(int flags, int mask) { void setWindowFlags(int flags, int mask) {

View File

@ -709,7 +709,7 @@ public class WallpaperManager {
public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) { public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
try { try {
//Log.v(TAG, "Sending new wallpaper offsets from app..."); //Log.v(TAG, "Sending new wallpaper offsets from app...");
WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( WindowManagerGlobal.getWindowSession().setWallpaperPosition(
windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep); windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
//Log.v(TAG, "...app returning after sending offsets!"); //Log.v(TAG, "...app returning after sending offsets!");
} catch (RemoteException e) { } catch (RemoteException e) {
@ -747,7 +747,7 @@ public class WallpaperManager {
int x, int y, int z, Bundle extras) { int x, int y, int z, Bundle extras) {
try { try {
//Log.v(TAG, "Sending new wallpaper offsets from app..."); //Log.v(TAG, "Sending new wallpaper offsets from app...");
WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand( WindowManagerGlobal.getWindowSession().sendWallpaperCommand(
windowToken, action, x, y, z, extras, false); windowToken, action, x, y, z, extras, false);
//Log.v(TAG, "...app returning after sending offsets!"); //Log.v(TAG, "...app returning after sending offsets!");
} catch (RemoteException e) { } catch (RemoteException e) {
@ -767,7 +767,7 @@ public class WallpaperManager {
*/ */
public void clearWallpaperOffsets(IBinder windowToken) { public void clearWallpaperOffsets(IBinder windowToken) {
try { try {
WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( WindowManagerGlobal.getWindowSession().setWallpaperPosition(
windowToken, -1, -1, -1, -1); windowToken, -1, -1, -1, -1);
} catch (RemoteException e) { } catch (RemoteException e) {
// Ignore. // Ignore.

View File

@ -256,6 +256,12 @@ public abstract class Context {
* Return the Looper for the main thread of the current process. This is * Return the Looper for the main thread of the current process. This is
* the thread used to dispatch calls to application components (activities, * the thread used to dispatch calls to application components (activities,
* services, etc). * services, etc).
* <p>
* By definition, this method returns the same result as would be obtained
* by calling {@link Looper#getMainLooper() Looper.getMainLooper()}.
* </p>
*
* @return The main looper.
*/ */
public abstract Looper getMainLooper(); public abstract Looper getMainLooper();

View File

@ -1,94 +0,0 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.util.Log;
import java.util.HashMap;
/**
* A class to help with measuring latency in your code.
*
* Suggested usage:
* 1) Instanciate a LatencyTimer as a class field.
* private [static] LatencyTimer mLt = new LatencyTimer(100, 1000);
* 2) At various points in the code call sample with a string and the time delta to some fixed time.
* The string should be unique at each point of the code you are measuring.
* mLt.sample("before processing event", System.nanoTime() - event.getEventTimeNano());
* processEvent(event);
* mLt.sample("after processing event ", System.nanoTime() - event.getEventTimeNano());
*
* @hide
*/
public final class LatencyTimer
{
final String TAG = "LatencyTimer";
final int mSampleSize;
final int mScaleFactor;
volatile HashMap<String, long[]> store = new HashMap<String, long[]>();
/**
* Creates a LatencyTimer object
* @param sampleSize number of samples to collect before printing out the average
* @param scaleFactor divisor used to make each sample smaller to prevent overflow when
* (sampleSize * average sample value)/scaleFactor > Long.MAX_VALUE
*/
public LatencyTimer(int sampleSize, int scaleFactor) {
if (scaleFactor == 0) {
scaleFactor = 1;
}
mScaleFactor = scaleFactor;
mSampleSize = sampleSize;
}
/**
* Add a sample delay for averaging.
* @param tag string used for printing out the result. This should be unique at each point of
* this called.
* @param delta time difference from an unique point of reference for a particular iteration
*/
public void sample(String tag, long delta) {
long[] array = getArray(tag);
// array[mSampleSize] holds the number of used entries
final int index = (int) array[mSampleSize]++;
array[index] = delta;
if (array[mSampleSize] == mSampleSize) {
long totalDelta = 0;
for (long d : array) {
totalDelta += d/mScaleFactor;
}
array[mSampleSize] = 0;
Log.i(TAG, tag + " average = " + totalDelta / mSampleSize);
}
}
private long[] getArray(String tag) {
long[] data = store.get(tag);
if (data == null) {
synchronized(store) {
data = store.get(tag);
if (data == null) {
data = new long[mSampleSize + 1];
store.put(tag, data);
data[mSampleSize] = 0;
}
}
}
return data;
}
}

View File

@ -191,6 +191,14 @@ public final class Looper {
mThread = Thread.currentThread(); mThread = Thread.currentThread();
} }
/**
* Returns true if the current thread is this looper's thread.
* @hide
*/
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
/** /**
* Quits the looper. * Quits the looper.
* *

View File

@ -762,7 +762,7 @@ public abstract class WallpaperService extends Service {
mWindowToken = wrapper.mWindowToken; mWindowToken = wrapper.mWindowToken;
mSurfaceHolder.setSizeFromLayout(); mSurfaceHolder.setSizeFromLayout();
mInitializing = true; mInitializing = true;
mSession = WindowManagerGlobal.getWindowSession(getMainLooper()); mSession = WindowManagerGlobal.getWindowSession();
mWindow.setSession(mSession); mWindow.setSession(mSession);

File diff suppressed because it is too large Load Diff

View File

@ -136,11 +136,11 @@ public final class WindowManagerGlobal {
} }
} }
public static IWindowSession getWindowSession(Looper mainLooper) { public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) { synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) { if (sWindowSession == null) {
try { try {
InputMethodManager imm = InputMethodManager.getInstance(mainLooper); InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService(); IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession( sWindowSession = windowManager.openSession(
imm.getClient(), imm.getInputContext()); imm.getClient(), imm.getInputContext());
@ -351,7 +351,7 @@ public final class WindowManagerGlobal {
View view = root.getView(); View view = root.getView();
if (view != null) { if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance(view.getContext()); InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) { if (imm != null) {
imm.windowDismissed(mViews[index].getWindowToken()); imm.windowDismissed(mViews[index].getWindowToken());
} }

View File

@ -34,9 +34,11 @@ import android.os.Message;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ResultReceiver; import android.os.ResultReceiver;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SystemClock; import android.os.Trace;
import android.text.style.SuggestionSpan; import android.text.style.SuggestionSpan;
import android.util.Log; import android.util.Log;
import android.util.Pools.Pool;
import android.util.Pools.SimplePool;
import android.util.PrintWriterPrinter; import android.util.PrintWriterPrinter;
import android.util.Printer; import android.util.Printer;
import android.view.InputChannel; import android.view.InputChannel;
@ -45,6 +47,7 @@ import android.view.InputEventSender;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.ViewRootImpl; import android.view.ViewRootImpl;
import android.util.SparseArray;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
@ -200,8 +203,9 @@ public final class InputMethodManager {
static final boolean DEBUG = false; static final boolean DEBUG = false;
static final String TAG = "InputMethodManager"; static final String TAG = "InputMethodManager";
static final Object mInstanceSync = new Object(); static final String PENDING_EVENT_COUNTER = "aq:imm";
static InputMethodManager mInstance;
static InputMethodManager sInstance;
/** /**
* @hide Flag for IInputMethodManager.windowGainedFocus: a view in * @hide Flag for IInputMethodManager.windowGainedFocus: a view in
@ -232,7 +236,14 @@ public final class InputMethodManager {
*/ */
static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500; static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500;
private static final int MAX_PENDING_EVENT_POOL_SIZE = 4; /** @hide */
public static final int DISPATCH_IN_PROGRESS = -1;
/** @hide */
public static final int DISPATCH_NOT_HANDLED = 0;
/** @hide */
public static final int DISPATCH_HANDLED = 1;
final IInputMethodManager mService; final IInputMethodManager mService;
final Looper mMainLooper; final Looper mMainLooper;
@ -323,10 +334,8 @@ public final class InputMethodManager {
InputChannel mCurChannel; InputChannel mCurChannel;
ImeInputEventSender mCurSender; ImeInputEventSender mCurSender;
PendingEvent mPendingEventPool; final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
int mPendingEventPoolSize; final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
PendingEvent mPendingEventHead;
PendingEvent mPendingEventTail;
// ----------------------------------------------------------- // -----------------------------------------------------------
@ -334,7 +343,9 @@ public final class InputMethodManager {
static final int MSG_BIND = 2; static final int MSG_BIND = 2;
static final int MSG_UNBIND = 3; static final int MSG_UNBIND = 3;
static final int MSG_SET_ACTIVE = 4; static final int MSG_SET_ACTIVE = 4;
static final int MSG_EVENT_TIMEOUT = 5; static final int MSG_SEND_INPUT_EVENT = 5;
static final int MSG_TIMEOUT_INPUT_EVENT = 6;
static final int MSG_FLUSH_INPUT_EVENT = 7;
class H extends Handler { class H extends Handler {
H(Looper looper) { H(Looper looper) {
@ -453,15 +464,16 @@ public final class InputMethodManager {
} }
return; return;
} }
case MSG_EVENT_TIMEOUT: { case MSG_SEND_INPUT_EVENT: {
// Even though the message contains both the sequence number sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj);
// and the PendingEvent object itself, we only pass the return;
// sequence number to the timeoutEvent function because it's }
// possible for the PendingEvent object to be dequeued and case MSG_TIMEOUT_INPUT_EVENT: {
// recycled concurrently. To avoid a possible race, we make finishedInputEvent(msg.arg1, false, true);
// a point of always looking up the PendingEvent within the return;
// queue given only the sequence number of the event. }
timeoutEvent(msg.arg1); case MSG_FLUSH_INPUT_EVENT: {
finishedInputEvent(msg.arg1, false, false);
return; return;
} }
} }
@ -538,10 +550,6 @@ public final class InputMethodManager {
mH = new H(looper); mH = new H(looper);
mIInputContext = new ControlledInputConnectionWrapper(looper, mIInputContext = new ControlledInputConnectionWrapper(looper,
mDummyInputConnection, this); mDummyInputConnection, this);
if (mInstance == null) {
mInstance = this;
}
} }
/** /**
@ -549,25 +557,15 @@ public final class InputMethodManager {
* doesn't already exist. * doesn't already exist.
* @hide * @hide
*/ */
static public InputMethodManager getInstance(Context context) { public static InputMethodManager getInstance() {
return getInstance(context.getMainLooper()); synchronized (InputMethodManager.class) {
} if (sInstance == null) {
/**
* Internally, the input method manager can't be context-dependent, so
* we have this here for the places that need it.
* @hide
*/
static public InputMethodManager getInstance(Looper mainLooper) {
synchronized (mInstanceSync) {
if (mInstance != null) {
return mInstance;
}
IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE); IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
IInputMethodManager service = IInputMethodManager.Stub.asInterface(b); IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
mInstance = new InputMethodManager(service, mainLooper); sInstance = new InputMethodManager(service, Looper.getMainLooper());
}
return sInstance;
} }
return mInstance;
} }
/** /**
@ -575,8 +573,8 @@ public final class InputMethodManager {
* if it exists. * if it exists.
* @hide * @hide
*/ */
static public InputMethodManager peekInstance() { public static InputMethodManager peekInstance() {
return mInstance; return sInstance;
} }
/** @hide */ /** @hide */
@ -1585,13 +1583,18 @@ public final class InputMethodManager {
} }
/** /**
* Dispatches an input event to the IME.
*
* Returns {@link #DISPATCH_HANDLED} if the event was handled.
* Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
* Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
* callback will be invoked later.
*
* @hide * @hide
*/ */
public int dispatchInputEvent(Context context, int seq, InputEvent event, public int dispatchInputEvent(InputEvent event, Object token,
FinishedEventCallback callback) { FinishedInputEventCallback callback, Handler handler) {
synchronized (mH) { synchronized (mH) {
if (DEBUG) Log.d(TAG, "dispatchInputEvent");
if (mCurMethod != null) { if (mCurMethod != null) {
if (event instanceof KeyEvent) { if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent)event; KeyEvent keyEvent = (KeyEvent)event;
@ -1599,144 +1602,140 @@ public final class InputMethodManager {
&& keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
&& keyEvent.getRepeatCount() == 0) { && keyEvent.getRepeatCount() == 0) {
showInputMethodPickerLocked(); showInputMethodPickerLocked();
return ViewRootImpl.EVENT_HANDLED; return DISPATCH_HANDLED;
} }
} }
if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod); if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
final long startTime = SystemClock.uptimeMillis();
PendingEvent p = obtainPendingEventLocked(
event, token, mCurId, callback, handler);
if (mMainLooper.isCurrentThread()) {
// Already running on the IMM thread so we can send the event immediately.
return sendInputEventOnMainLooperLocked(p);
}
// Post the event to the IMM thread.
Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
msg.setAsynchronous(true);
mH.sendMessage(msg);
return DISPATCH_IN_PROGRESS;
}
}
return DISPATCH_NOT_HANDLED;
}
// Must be called on the main looper
void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
final boolean handled;
synchronized (mH) {
int result = sendInputEventOnMainLooperLocked(p);
if (result == DISPATCH_IN_PROGRESS) {
return;
}
handled = (result == DISPATCH_HANDLED);
}
invokeFinishedInputEventCallback(p, handled);
}
// Must be called on the main looper
int sendInputEventOnMainLooperLocked(PendingEvent p) {
if (mCurChannel != null) { if (mCurChannel != null) {
if (mCurSender == null) { if (mCurSender == null) {
mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper()); mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
} }
final InputEvent event = p.mEvent;
final int seq = event.getSequenceNumber();
if (mCurSender.sendInputEvent(seq, event)) { if (mCurSender.sendInputEvent(seq, event)) {
enqueuePendingEventLocked(startTime, seq, mCurId, callback); mPendingEvents.put(seq, p);
return ViewRootImpl.EVENT_PENDING_IME; Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
} else { mPendingEvents.size());
Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
msg.setAsynchronous(true);
mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
return DISPATCH_IN_PROGRESS;
}
Log.w(TAG, "Unable to send input event to IME: " Log.w(TAG, "Unable to send input event to IME: "
+ mCurId + " dropping: " + event); + mCurId + " dropping: " + event);
} }
} return DISPATCH_NOT_HANDLED;
}
}
return ViewRootImpl.EVENT_POST_IME;
} }
void finishedEvent(int seq, boolean handled) { void finishedInputEvent(int seq, boolean handled, boolean timeout) {
final FinishedEventCallback callback; final PendingEvent p;
synchronized (mH) { synchronized (mH) {
PendingEvent p = dequeuePendingEventLocked(seq); int index = mPendingEvents.indexOfKey(seq);
if (p == null) { if (index < 0) {
return; // spurious, event already finished or timed out return; // spurious, event already finished or timed out
} }
mH.removeMessages(MSG_EVENT_TIMEOUT, p);
callback = p.mCallback;
recyclePendingEventLocked(p);
}
callback.finishedEvent(seq, handled);
}
void timeoutEvent(int seq) { p = mPendingEvents.valueAt(index);
final FinishedEventCallback callback; mPendingEvents.removeAt(index);
synchronized (mH) { Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());
PendingEvent p = dequeuePendingEventLocked(seq);
if (p == null) { if (timeout) {
return; // spurious, event already finished or timed out
}
long delay = SystemClock.uptimeMillis() - p.mStartTime;
Log.w(TAG, "Timeout waiting for IME to handle input event after " Log.w(TAG, "Timeout waiting for IME to handle input event after "
+ delay + "ms: " + p.mInputMethodId); + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId);
callback = p.mCallback;
recyclePendingEventLocked(p);
}
callback.finishedEvent(seq, false);
}
private void enqueuePendingEventLocked(
long startTime, int seq, String inputMethodId, FinishedEventCallback callback) {
PendingEvent p = obtainPendingEventLocked(startTime, seq, inputMethodId, callback);
if (mPendingEventTail != null) {
mPendingEventTail.mNext = p;
mPendingEventTail = p;
} else { } else {
mPendingEventHead = p; mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p);
mPendingEventTail = p; }
} }
Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, seq, 0, p); invokeFinishedInputEventCallback(p, handled);
}
// Assumes the event has already been removed from the queue.
void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
p.mHandled = handled;
if (p.mHandler.getLooper().isCurrentThread()) {
// Already running on the callback handler thread so we can send the
// callback immediately.
p.run();
} else {
// Post the event to the callback handler thread.
// In this case, the callback will be responsible for recycling the event.
Message msg = Message.obtain(p.mHandler, p);
msg.setAsynchronous(true); msg.setAsynchronous(true);
mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT); msg.sendToTarget();
}
private PendingEvent dequeuePendingEventLocked(int seq) {
PendingEvent p = mPendingEventHead;
if (p == null) {
return null;
}
if (p.mSeq == seq) {
mPendingEventHead = p.mNext;
if (mPendingEventHead == null) {
mPendingEventTail = null;
}
} else {
PendingEvent prev;
do {
prev = p;
p = p.mNext;
if (p == null) {
return null;
}
} while (p.mSeq != seq);
prev.mNext = p.mNext;
if (mPendingEventTail == p) {
mPendingEventTail = prev;
}
}
p.mNext = null;
return p;
}
private PendingEvent obtainPendingEventLocked(
long startTime, int seq, String inputMethodId, FinishedEventCallback callback) {
PendingEvent p = mPendingEventPool;
if (p != null) {
mPendingEventPoolSize -= 1;
mPendingEventPool = p.mNext;
p.mNext = null;
} else {
p = new PendingEvent();
}
p.mStartTime = startTime;
p.mSeq = seq;
p.mInputMethodId = inputMethodId;
p.mCallback = callback;
return p;
}
private void recyclePendingEventLocked(PendingEvent p) {
p.mInputMethodId = null;
p.mCallback = null;
if (mPendingEventPoolSize < MAX_PENDING_EVENT_POOL_SIZE) {
mPendingEventPoolSize += 1;
p.mNext = mPendingEventPool;
mPendingEventPool = p;
} }
} }
private void flushPendingEventsLocked() { private void flushPendingEventsLocked() {
mH.removeMessages(MSG_EVENT_TIMEOUT); mH.removeMessages(MSG_FLUSH_INPUT_EVENT);
PendingEvent p = mPendingEventHead; final int count = mPendingEvents.size();
while (p != null) { for (int i = 0; i < count; i++) {
Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, p.mSeq, 0, p); int seq = mPendingEvents.keyAt(i);
Message msg = mH.obtainMessage(MSG_FLUSH_INPUT_EVENT, seq, 0);
msg.setAsynchronous(true); msg.setAsynchronous(true);
mH.sendMessage(msg); msg.sendToTarget();
p = p.mNext;
} }
} }
private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
String inputMethodId, FinishedInputEventCallback callback, Handler handler) {
PendingEvent p = mPendingEventPool.acquire();
if (p == null) {
p = new PendingEvent();
}
p.mEvent = event;
p.mToken = token;
p.mInputMethodId = inputMethodId;
p.mCallback = callback;
p.mHandler = handler;
return p;
}
private void recyclePendingEventLocked(PendingEvent p) {
p.recycle();
mPendingEventPool.release(p);
}
public void showInputMethodPicker() { public void showInputMethodPicker() {
synchronized (mH) { synchronized (mH) {
showInputMethodPickerLocked(); showInputMethodPickerLocked();
@ -1946,8 +1945,8 @@ public final class InputMethodManager {
* the IME has been finished. * the IME has been finished.
* @hide * @hide
*/ */
public interface FinishedEventCallback { public interface FinishedInputEventCallback {
public void finishedEvent(int seq, boolean handled); public void onFinishedInputEvent(Object token, boolean handled);
} }
private final class ImeInputEventSender extends InputEventSender { private final class ImeInputEventSender extends InputEventSender {
@ -1957,16 +1956,34 @@ public final class InputMethodManager {
@Override @Override
public void onInputEventFinished(int seq, boolean handled) { public void onInputEventFinished(int seq, boolean handled) {
finishedEvent(seq, handled); finishedInputEvent(seq, handled, false);
} }
} }
private static final class PendingEvent { private final class PendingEvent implements Runnable {
public PendingEvent mNext; public InputEvent mEvent;
public Object mToken;
public long mStartTime;
public int mSeq;
public String mInputMethodId; public String mInputMethodId;
public FinishedEventCallback mCallback; public FinishedInputEventCallback mCallback;
public Handler mHandler;
public boolean mHandled;
public void recycle() {
mEvent = null;
mToken = null;
mInputMethodId = null;
mCallback = null;
mHandler = null;
mHandled = false;
}
@Override
public void run() {
mCallback.onFinishedInputEvent(mToken, mHandled);
synchronized (mH) {
recyclePendingEventLocked(this);
}
}
} }
} }

View File

@ -97,7 +97,7 @@ public class UniverseBackground extends FrameLayout {
public UniverseBackground(Context context) { public UniverseBackground(Context context) {
super(context); super(context);
setBackgroundColor(0xff000000); setBackgroundColor(0xff000000);
mSession = WindowManagerGlobal.getWindowSession(context.getMainLooper()); mSession = WindowManagerGlobal.getWindowSession();
mContent = View.inflate(context, R.layout.universe, null); mContent = View.inflate(context, R.layout.universe, null);
addView(mContent); addView(mContent);
mContent.findViewById(R.id.close).setOnClickListener(new View.OnClickListener() { mContent.findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {

View File

@ -22,6 +22,6 @@ package android.view.inputmethod;
public class InputMethodManager_Accessor { public class InputMethodManager_Accessor {
public static void resetInstance() { public static void resetInstance() {
InputMethodManager.mInstance = null; InputMethodManager.sInstance = null;
} }
} }

View File

@ -35,28 +35,15 @@ public class InputMethodManager_Delegate {
// ---- Overridden methods ---- // ---- Overridden methods ----
@LayoutlibDelegate @LayoutlibDelegate
/*package*/ static InputMethodManager getInstance(Looper mainLooper) { /*package*/ static InputMethodManager getInstance() {
synchronized (InputMethodManager.mInstanceSync) { synchronized (InputMethodManager.class) {
if (InputMethodManager.mInstance != null) { InputMethodManager imm = InputMethodManager.peekInstance();
return InputMethodManager.mInstance; if (imm == null) {
imm = new InputMethodManager(
new BridgeIInputMethodManager(), Looper.getMainLooper());
InputMethodManager.sInstance = imm;
}
return imm;
} }
InputMethodManager.mInstance = new InputMethodManager(new BridgeIInputMethodManager(),
mainLooper);
}
return InputMethodManager.mInstance;
}
@LayoutlibDelegate
/*package*/ static InputMethodManager getInstance(Context context) {
synchronized (InputMethodManager.mInstanceSync) {
if (InputMethodManager.mInstance != null) {
return InputMethodManager.mInstance;
}
InputMethodManager.mInstance = new InputMethodManager(new BridgeIInputMethodManager(),
Looper.myLooper());
}
return InputMethodManager.mInstance;
} }
} }

View File

@ -232,7 +232,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
sCurrentContext = mContext; sCurrentContext = mContext;
// create an InputMethodManager // create an InputMethodManager
InputMethodManager.getInstance(Looper.myLooper()); InputMethodManager.getInstance();
LayoutLog currentLog = mParams.getLog(); LayoutLog currentLog = mParams.getLog();
Bridge.setLog(currentLog); Bridge.setLog(currentLog);