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);
   }
 }
 public void endWaitForUiThread() {
   if (DEBUG) {
     ThreadUtils.assertOnUiThread();
     Log.d(LOGTAG, "endWaitForUiThread()");
   }
   try {
     mIcRunnableSync.put(mIcSignalRunnable);
   } catch (InterruptedException e) {
   }
 }
  private boolean processKey(int keyCode, KeyEvent event, boolean down) {
    if (GamepadUtils.isSonyXperiaGamepadKeyEvent(event)) {
      event = GamepadUtils.translateSonyXperiaGamepadKeys(keyCode, event);
      keyCode = event.getKeyCode();
    }

    if (keyCode > KeyEvent.getMaxKeyCode() || !shouldProcessKey(keyCode, event)) {
      return false;
    }
    event = translateKey(keyCode, event);
    keyCode = event.getKeyCode();

    View view = getView();
    if (view == null) {
      InputThreadUtils.sInstance.sendEventFromUiThread(
          ThreadUtils.getUiHandler(), mEditableClient, GoannaEvent.createKeyEvent(event, 0));
      return true;
    }

    // KeyListener returns true if it handled the event for us. KeyListener is only
    // safe to use on the UI thread; therefore we need to pass a proxy Editable to it
    KeyListener keyListener = TextKeyListener.getInstance();
    Handler uiHandler = view.getRootView().getHandler();
    Editable uiEditable =
        InputThreadUtils.sInstance.getEditableForUiThread(uiHandler, mEditableClient);
    boolean skip = shouldSkipKeyListener(keyCode, event);
    if (down) {
      mEditableClient.setSuppressKeyUp(true);
    }
    if (skip
        || (down && !keyListener.onKeyDown(view, uiEditable, keyCode, event))
        || (!down && !keyListener.onKeyUp(view, uiEditable, keyCode, event))) {
      InputThreadUtils.sInstance.sendEventFromUiThread(
          uiHandler,
          mEditableClient,
          GoannaEvent.createKeyEvent(event, TextKeyListener.getMetaState(uiEditable)));
      if (skip && down) {
        // Usually, the down key listener call above adjusts meta states for us.
        // However, if we skip that call above, we have to manually adjust meta
        // states so the meta states remain consistent
        TextKeyListener.adjustMetaAfterKeypress(uiEditable);
      }
    }
    if (down) {
      mEditableClient.setSuppressKeyUp(false);
    }
    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) {
   }
 }
 /**
  * This is a hack.
  *
  * <p>Startup results in us writing prefs -- we fetch the Distribution, which caches its state.
  * Our tests try to wipe those prefs, but apparently sometimes race with startup, which leads to
  * us not getting one of our expected messages. The test fails.
  *
  * <p>This hack waits for any existing background tasks -- such as the one that writes prefs -- to
  * finish before we begin the test.
  */
 private void waitForBackgroundHappiness() {
   final Object signal = new Object();
   final Runnable done =
       new Runnable() {
         @Override
         public void run() {
           synchronized (signal) {
             signal.notify();
           }
         }
       };
   synchronized (signal) {
     ThreadUtils.postToBackgroundThread(done);
     try {
       signal.wait();
     } catch (InterruptedException e) {
       mAsserter.ok(false, "InterruptedException waiting on background thread.", e.toString());
     }
   }
   mAsserter.dumpLog("Background task completed. Proceeding.");
 }
 public Editable getEditableForUiThread(
     final Handler uiHandler, final GoannaEditableClient client) {
   if (DEBUG) {
     ThreadUtils.assertOnThread(uiHandler.getLooper().getThread(), AssertBehavior.THROW);
   }
   final Handler icHandler = client.getInputConnectionHandler();
   if (icHandler.getLooper() == uiHandler.getLooper()) {
     // IC thread is UI thread; safe to use Editable directly
     return client.getEditable();
   }
   // IC thread is not UI thread; we need to return a proxy Editable in order
   // to safely use the Editable from the UI thread
   if (mUiEditable != null) {
     return mUiEditable;
   }
   final InvocationHandler invokeEditable =
       new InvocationHandler() {
         @Override
         public Object invoke(final Object proxy, final Method method, final Object[] args)
             throws Throwable {
           if (DEBUG) {
             ThreadUtils.assertOnThread(uiHandler.getLooper().getThread(), AssertBehavior.THROW);
             Log.d(LOGTAG, "UiEditable." + method.getName() + "() blocking");
           }
           synchronized (icHandler) {
             // Now we are on UI thread
             mUiEditableReturn = null;
             mUiEditableException = null;
             // Post a Runnable that calls the real Editable and saves any
             // result/exception. Then wait on the Runnable to finish
             runOnIcThread(
                 icHandler,
                 new Runnable() {
                   @Override
                   public void run() {
                     synchronized (icHandler) {
                       try {
                         mUiEditableReturn = method.invoke(client.getEditable(), args);
                       } catch (Exception e) {
                         mUiEditableException = e;
                       }
                       if (DEBUG) {
                         Log.d(LOGTAG, "UiEditable." + method.getName() + "() returning");
                       }
                       icHandler.notify();
                     }
                   }
                 });
             // let InterruptedException propagate
             icHandler.wait();
             if (mUiEditableException != null) {
               throw mUiEditableException;
             }
             return mUiEditableReturn;
           }
         }
       };
   mUiEditable =
       (Editable)
           Proxy.newProxyInstance(
               Editable.class.getClassLoader(), new Class<?>[] {Editable.class}, invokeEditable);
   return mUiEditable;
 }