@Override public boolean sendKeyEvent(KeyEvent event) { // BaseInputConnection.sendKeyEvent() dispatches the key event to the main thread. // In order to ensure events are processed in the proper order, we must block the // IC thread until the main thread finishes processing the key event super.sendKeyEvent(event); final View v = getView(); if (v == null) { return false; } final Handler icHandler = mEditableClient.getInputConnectionHandler(); final Handler mainHandler = v.getRootView().getHandler(); if (icHandler.getLooper() != mainHandler.getLooper()) { // We are on separate IC thread but the event is queued on the main thread; // wait on IC thread until the main thread processes our posted Runnable. At // that point the key event has already been processed. mainHandler.post( new Runnable() { @Override public void run() { InputThreadUtils.sInstance.endWaitForUiThread(); } }); InputThreadUtils.sInstance.waitForUiThread(icHandler); } return false; // seems to always return false }
public void runOnIcThread( final Handler uiHandler, final GoannaEditableClient client, final Runnable runnable) { final Handler icHandler = client.getInputConnectionHandler(); if (icHandler.getLooper() == uiHandler.getLooper()) { // IC thread is UI thread; safe to run directly runnable.run(); return; } runOnIcThread(icHandler, runnable); }
/** * Sends this message using one three methods: * * <ul> * <li>If the current thread is the same thread as the message Handler's thread, it is * dispatched immediately to the Handler * <li>If this TiMessenger is currently blocking, it is pushed into the internal message queue * to be processed by the next call to {@link #dispatchMessage()} * <li>If this TiMessenger is <b>NOT</b> current blocking, it is queued to it's Handler normally * by using msg.sendToTarget() * </ul> * * @param message The message to send */ public void sendMessage(Message message) { Handler target = message.getTarget(); long currentThreadId = Thread.currentThread().getId(); long targetThreadId = -1; if (target != null) { targetThreadId = target.getLooper().getThread().getId(); } if (target != null && currentThreadId == targetThreadId) { target.dispatchMessage(message); } else { if (isBlocking()) { try { messageQueue.put(message); } catch (InterruptedException e) { Log.w(TAG, "Interrupted trying to put new message, sending to handler", e); message.sendToTarget(); } } else { message.sendToTarget(); } } }
/** * Runs runnable on handler and waits for it to finish. If the current thread is the handler * thread, just runs it. */ public static void runOnHandlerSynchronously(Handler h, final Runnable runnable) { final boolean done[] = new boolean[1]; if (h.getLooper().getThread() == Thread.currentThread()) { runnable.run(); } else { h.postAtFrontOfQueue( new Runnable() { @Override public void run() { runnable.run(); synchronized (done) { done[0] = true; done.notify(); } } }); synchronized (done) { while (!done[0]) { try { done.wait(); } catch (InterruptedException e) { throw new IllegalStateException(e); } } } } }
@Test public void shouldRemoveTaggedCallback() throws Exception { ShadowLooper.pauseMainLooper(); Handler handler = new Handler(); final int[] count = new int[1]; Runnable r = new Runnable() { @Override public void run() { count[0]++; } }; String tag1 = "tag1", tag2 = "tag2"; handler.postAtTime(r, tag1, 100); handler.postAtTime(r, tag2, 105); handler.removeCallbacks(r, tag2); ShadowLooper.unPauseMainLooper(); assertThat(count[0]).as("run count").isEqualTo(1); // This assertion proves that it was the first runnable that ran, // which proves that the correctly tagged runnable was removed. assertThat(shadowOf(handler.getLooper()).getScheduler().getCurrentTime()) .as("currentTime") .isEqualTo(100); }
/** * Make a best-effort determination of the current position, given the time constraint. I want to * use a Looper, so call me from an AsyncTask background thread or something. * * @param ctx a Context providing access to a LocationManager * @param timeout milliseconds * @return The last known location (or null) after acquiring at least four satellites or using the * alloted time searching for them. */ public static Location get(final Context ctx, final long timeout) { Looper.prepare(); final Handler handler = new Handler(); LocationManager lm = WRService.getSystemService(ctx, Context.LOCATION_SERVICE); Criteria c = new Criteria(); c.setAccuracy(Criteria.ACCURACY_FINE); String p = lm.getBestProvider(c, true); if (p == null) return null; final LocationGetter getter = new LocationGetter(handler.getLooper()); try { lm.requestLocationUpdates(p, 0, 0, getter); handler.postDelayed( new Runnable() { public void run() { handler.getLooper().quit(); } }, timeout); Looper.loop(); } finally { lm.removeUpdates(getter); } return lm.getLastKnownLocation(p); }
/** close dialog */ public void closeDialog() { if (Looper.myLooper() == mHandler.getLooper()) { close(); } else { mHandler.post(mCloseAction); } }
/** dismiss dialog */ public void dismissDialog() { if (Looper.myLooper() == mHandler.getLooper()) { dismiss(); } else { mHandler.post(mDismissAction); } }
/** Stops Face Unlock and unbinds from the service. Called on the UI thread. */ public boolean stop() { if (DEBUG) Log.d(TAG, "stop()"); if (mHandler.getLooper() != Looper.myLooper()) { Log.e(TAG, "stop() called off of the UI thread"); } boolean mWasRunning = mIsRunning; stopUi(); if (mBoundToService) { if (mService != null) { try { mService.unregisterCallback(mFaceUnlockCallback); } catch (RemoteException e) { // Not much we can do } } Log.d(TAG, "Unbinding from Face Unlock service"); mContext.unbindService(mConnection); mBoundToService = false; } else { // This is usually not an error when this happens. Sometimes we will tell it to // unbind multiple times because it's called from both onWindowFocusChanged and // onDetachedFromWindow. if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound"); } mIsRunning = false; return mWasRunning; }
/** * Binds to the Face Unlock service. Face Unlock will be started when the bind completes. The Face * Unlock view is displayed to hide the backup lock while the service is starting up. Called on * the UI thread. */ public boolean start() { if (DEBUG) Log.d(TAG, "start()"); if (mHandler.getLooper() != Looper.myLooper()) { Log.e(TAG, "start() called off of the UI thread"); } if (mIsRunning) { Log.w(TAG, "start() called when already running"); } // Show Face Unlock view, but only for a little bit so lockpattern will become visible if // Face Unlock fails to start or crashes // This must show before bind to guarantee that Face Unlock has a place to display show(SERVICE_STARTUP_VIEW_TIMEOUT); if (!mBoundToService) { Log.d(TAG, "Binding to Face Unlock service"); mContext.bindService( new Intent(IFaceLockInterface.class.getName()), mConnection, Context.BIND_AUTO_CREATE, mLockPatternUtils.getCurrentUser()); mBoundToService = true; } else { Log.w(TAG, "Attempt to bind to Face Unlock when already bound"); } mIsRunning = true; return true; }
public void waitForUiThread(Handler icHandler) { if (DEBUG) { ThreadUtils.assertOnThread(icHandler.getLooper().getThread(), AssertBehavior.THROW); Log.d( LOGTAG, "waitForUiThread() blocking on thread " + icHandler.getLooper().getThread().getName()); } try { Runnable runnable = null; do { runnable = mIcRunnableSync.take(); runnable.run(); } while (runnable != mIcSignalRunnable); } catch (InterruptedException e) { } }
private void runOnIcThread(Handler icHandler, final Runnable runnable) { if (DEBUG) { ThreadUtils.assertOnUiThread(); Log.d(LOGTAG, "runOnIcThread() on thread " + icHandler.getLooper().getThread().getName()); } Runnable runner = new Runnable() { @Override public void run() { try { Runnable queuedRunnable = mIcRunnableSync.take(); if (DEBUG && queuedRunnable != runnable) { throw new IllegalThreadStateException("sync error"); } queuedRunnable.run(); } catch (InterruptedException e) { } } }; try { // if we are not inside waitForUiThread(), runner will call the runnable icHandler.post(runner); // runnable will be called by either runner from above or waitForUiThread() mIcRunnableSync.put(runnable); } catch (InterruptedException e) { } finally { // if waitForUiThread() already called runnable, runner should not call it again icHandler.removeCallbacks(runner); } }
@VisibleForTesting public static void setUiThread(Looper looper) { synchronized (sLock) { if (sUiThreadHandler != null && sUiThreadHandler.getLooper() != looper) { throw new RuntimeException( "UI thread looper is already set to " + sUiThreadHandler.getLooper() + " (Main thread looper is " + Looper.getMainLooper() + "), cannot set to new looper " + looper); } else { sUiThreadHandler = new Handler(looper); } } }
protected void checkThread() { if (!ALLOW_SIM_OP_IN_UI_THREAD) { // Make sure this isn't the UI thread, since it will block if (mBaseHandler.getLooper().equals(Looper.myLooper())) { loge("query() called on the main UI thread!"); throw new IllegalStateException( "You cannot call query on this provder from the main UI thread."); } } }
/** * Processes pending events on the Gecko thread before returning. Must be called on the input * connection thread during a test. */ protected void processGeckoEvents(final InputConnection ic) { fAssertSame( "Should be called on input connection thread", Looper.myLooper(), inputConnectionHandler.getLooper()); fAssertTrue( "Should be able to process Gecko events", ic.performPrivateCommand("process-gecko-events", null)); }
@Override public void postDelayed(Object event, long delayMillis) { if (event == null) { throw new NullPointerException("Event must not be null"); } Handler handler = getMainHandlerNotNull(); if (handler.getLooper().getThread().isAlive()) { mImpl.postDelayed(event, delayMillis, handler); } // otherwise the bus is already stopped }
public DisplayManagerService(Context context, Handler mainHandler, Handler uiHandler) { mContext = context; mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1"); mHandler = new DisplayManagerHandler(mainHandler.getLooper()); mUiHandler = uiHandler; mDisplayAdapterListener = new DisplayAdapterListener(); mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER); }
/** Begin monitoring connectivity */ public void startMonitoring(Context context, Handler target) { if (DBG) Log.d(TAG, "startMonitoring: target: " + target); mContext = context; mCsHandler = target; if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null) { adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); } mBtdtHandler = new BtdtHandler(target.getLooper(), this); }
public IntentFirewall(AMSInterface ams, Handler handler) { mAms = ams; mHandler = new FirewallHandler(handler.getLooper()); File rulesDir = getRulesDir(); rulesDir.mkdirs(); readRulesDir(rulesDir); mObserver = new RuleObserver(rulesDir); mObserver.startWatching(); }
@Test public void testRemoveCallbacks() throws Exception { Handler handler = new Handler(); ShadowLooper shadowLooper = shadowOf(handler.getLooper()); shadowLooper.pause(); handler.post(scratchRunnable); handler.removeCallbacks(scratchRunnable); shadowLooper.unPause(); assertThat(scratchRunnable.wasRun).isFalse(); }
/** * Sets the Face Unlock view to visible, hiding it after the specified amount of time. If * timeoutMillis is 0, no hide is performed. Called on the UI thread. */ public void show(long timeoutMillis) { if (DEBUG) Log.d(TAG, "show()"); if (mHandler.getLooper() != Looper.myLooper()) { Log.e(TAG, "show() called off of the UI thread"); } removeDisplayMessages(); if (mFaceUnlockView != null) { mFaceUnlockView.setVisibility(View.VISIBLE); } if (timeoutMillis > 0) { mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACE_UNLOCK_VIEW, timeoutMillis); } }
/** * Begin monitoring data connectivity. * * @param context is the current Android context * @param target is the Hander to which to return the events. */ public void startMonitoring(Context context, Handler target) { mTarget = target; mContext = context; mHandler = new MdstHandler(target.getLooper(), this); IntentFilter filter = new IntentFilter(); filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); filter.addAction(DataConnectionTracker.ACTION_DATA_CONNECTION_TRACKER_MESSENGER); mContext.registerReceiver(new MobileDataStateReceiver(), filter); mMobileDataState = Phone.DataState.DISCONNECTED; }
public void destroy() { if (mHandler != null) { mHandler.getLooper().quit(); } // 避免因为同步机制导致等待过久做成UI线程假死,因此使用异步线程来清除数据 Thread thread = new Thread( new Runnable() { @Override public void run() { cleanup(); } }); thread.start(); }
/** * Sets the listener the AudioRecord notifies when a previously set marker is reached or for each * periodic record head position update. Use this method to receive AudioRecord events in the * Handler associated with another thread than the one in which you created the AudioTrack * instance. * * @param listener * @param handler the Handler that will receive the event notification messages. */ public void setRecordPositionUpdateListener( OnRecordPositionUpdateListener listener, Handler handler) { synchronized (mPositionListenerLock) { mPositionListener = listener; if (listener != null) { if (handler != null) { mEventHandler = new NativeEventHandler(this, handler.getLooper()); } else { // no given handler, use the looper the AudioRecord was created in mEventHandler = new NativeEventHandler(this, mInitializationLooper); } } else { mEventHandler = null; } } }
/** * Begin monitoring data connectivity. * * @param context is the current Android context * @param target is the Hander to which to return the events. */ public void startMonitoring(Context context, Handler target) { mTarget = target; mContext = context; mHandler = new MdstHandler(target.getLooper(), this); IntentFilter filter = new IntentFilter(); filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN); filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); mContext.registerReceiver(new MobileDataStateReceiver(), filter); mMobileDataState = PhoneConstants.DataState.DISCONNECTED; TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); }
public static ParcelFileDescriptor open( File file, int mode, Handler handler, OnCloseListener listener) throws IOException { if (handler == null) { throw new IllegalArgumentException("Handler must not be null"); } else if (listener == null) { throw new IllegalArgumentException("Listener must not be null"); } else { FileDescriptor fd = openInternal(file, mode); if (fd == null) { return null; } FileDescriptor[] comm = createCommSocketPair(); ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd, comm[0]); IoUtils.setBlocking(comm[MODE_WORLD_READABLE], true); new ListenerBridge(comm[MODE_WORLD_READABLE], handler.getLooper(), listener).start(); return pfd; } }
/** * Processes pending events on the input connection thread before returning. Must be called on * the input connection thread during a test. */ protected void processInputConnectionEvents() { fAssertSame( "Should be called on input connection thread", Looper.myLooper(), inputConnectionHandler.getLooper()); // Adapted from GeckoThread.pumpMessageLoop. MessageQueue queue = Looper.myQueue(); queue.addIdleHandler( new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { final Message msg = Message.obtain(inputConnectionHandler); msg.obj = inputConnectionHandler; inputConnectionHandler.sendMessageAtFrontOfQueue(msg); return false; // Remove this idle handler. } }); final Method getNextMessage; try { getNextMessage = queue.getClass().getDeclaredMethod("next"); } catch (final NoSuchMethodException e) { throw new UnsupportedOperationException(e); } getNextMessage.setAccessible(true); while (true) { final Message msg; try { msg = (Message) getNextMessage.invoke(queue); } catch (final IllegalAccessException | InvocationTargetException e) { throw new UnsupportedOperationException(e); } if (msg.obj == inputConnectionHandler && msg.getTarget() == inputConnectionHandler) { // Our idle signal break; } else if (msg.getTarget() == null) { Looper.myLooper().quit(); break; } msg.getTarget().dispatchMessage(msg); } }
ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) { mSensorEventListener = listener; Looper looper = (handler != null) ? handler.getLooper() : mMainLooper; // currently we create one Handler instance per listener, but we could // have one per looper (we'd need to pass the ListenerDelegate // instance to handleMessage and keep track of them separately). mHandler = new Handler(looper) { @Override public void handleMessage(Message msg) { final SensorEvent t = (SensorEvent) msg.obj; final int handle = t.sensor.getHandle(); switch (t.sensor.getType()) { // Only report accuracy for sensors that support it. case Sensor.TYPE_MAGNETIC_FIELD: case Sensor.TYPE_ORIENTATION: // call onAccuracyChanged() only if the value changes final int accuracy = mSensorAccuracies.get(handle); if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { mSensorAccuracies.put(handle, t.accuracy); mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy); } break; default: // For other sensors, just report the accuracy once if (mFirstEvent.get(handle) == false) { mFirstEvent.put(handle, true); mSensorEventListener.onAccuracyChanged(t.sensor, SENSOR_STATUS_ACCURACY_HIGH); } break; } mSensorEventListener.onSensorChanged(t); sPool.returnToPool(t); } }; addSensor(sensor); }
private void stop() { synchronized (this) { while (mHandler == null) { try { wait(1000); if (mHandler == null) { // We timed out; just give up. The process is probably // quitting anyways, so we let the OS do the clean up Log.w(LOGTAG, "timed out waiting for handler"); return; } } catch (InterruptedException e) { } } } Looper looper = mHandler.getLooper(); looper.quit(); try { looper.getThread().join(); } catch (InterruptedException e) { } }
@Override public void onTerminate(final EGLContext eglContext) { // Make sure we do this on the correct thread. if (mHandler.getLooper() != Looper.myLooper()) { mHandler.post( new Runnable() { @Override public void run() { onTerminate(eglContext); } }); return; } synchronized (sEglLock) { if (sEgl == null) return; if (EGLImpl.getInitCount(sEglDisplay) == 1) { usePbufferSurface(eglContext); GLES20Canvas.terminateCaches(); sEgl.eglDestroyContext(sEglDisplay, eglContext); sEglContextStorage.set(null); sEglContextStorage.remove(); sEgl.eglDestroySurface(sEglDisplay, sPbuffer); sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); sEgl.eglReleaseThread(); sEgl.eglTerminate(sEglDisplay); sEgl = null; sEglDisplay = null; sEglConfig = null; sPbuffer = null; } } }