package jp.co.cyberagent.stf; import android.net.LocalServerSocket; import android.net.LocalSocket; import android.os.SystemClock; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.Surface; import java.io.IOException; import java.net.UnknownHostException; import jp.co.cyberagent.stf.compat.InputManagerWrapper; import jp.co.cyberagent.stf.compat.PowerManagerWrapper; import jp.co.cyberagent.stf.compat.WindowManagerWrapper; import jp.co.cyberagent.stf.proto.Wire; import jp.co.cyberagent.stf.util.InternalApi; import jp.co.cyberagent.stf.util.ProcUtil; public class Agent { public static final String PROCESS_NAME = "stf.agent"; public static final String SOCKET = "stfagent"; private InputManagerWrapper inputManager; private PowerManagerWrapper powerManager; private WindowManagerWrapper windowManager; private LocalServerSocket serverSocket; private int deviceId = -1; // KeyCharacterMap.VIRTUAL_KEYBOARD private KeyCharacterMap keyCharacterMap; public static void main(String[] args) { ProcUtil.setArgV0(PROCESS_NAME); for (String arg : args) { if (arg.equals("--version")) { System.out.println(BuildConfig.VERSION_NAME); return; } else if (arg.equals("--debug-info")) { printServiceDebugInfo(); return; } else { System.err.println("Error: unknown argument " + arg); System.exit(1); } } new Agent().run(); } private static void printServiceDebugInfo() { String[] services = { "accessibility", "account", "activity", "alarm", "audio", "bluetooth", "clipboard", "connectivity", "device_policy", "display", "download", "input_method", "input", "keyguard", "layout_inflater", "location", "media_router", "notification", "servicediscovery", "power", "search", "sensor", "storage", "phone", "textservices", "uimode", "user", "vibrator", "wallpaper", "wifip2p", "wifi", "window", }; for (String service : services) { if (InternalApi.hasService(service)) { System.out.printf("FAIL: %s\n", service); } else { System.out.printf("OK: %s\n", service); } } } private void run() { powerManager = new PowerManagerWrapper(); inputManager = new InputManagerWrapper(); windowManager = new WindowManagerWrapper(); selectDevice(); loadKeyCharacterMap(); startServer(); waitForClients(); } private void selectDevice() { try { deviceId = KeyCharacterMap.class.getDeclaredField("VIRTUAL_KEYBOARD") .getInt(KeyCharacterMap.class); } catch (NoSuchFieldException e) { System.err.println("Falling back to KeyCharacterMap.BUILT_IN_KEYBOARD"); deviceId = 0; } catch (IllegalAccessException e) { e.printStackTrace(); System.exit(1); } } private void loadKeyCharacterMap() { keyCharacterMap = KeyCharacterMap.load(deviceId); } private void startServer() { try { serverSocket = new LocalServerSocket(SOCKET); System.err.printf("Listening on @%s\n", SOCKET); } catch (UnknownHostException e) { e.printStackTrace(); System.exit(1); } catch (IOException e) { stopServer(); e.printStackTrace(); System.exit(1); } } private void stopServer() { if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } private void waitForClients() { while (true) { try { LocalSocket clientSocket = serverSocket.accept(); InputClient client = new InputClient(clientSocket); client.start(); } catch (IOException e) { e.printStackTrace(); break; } } } private class InputClient extends Thread { private LocalSocket clientSocket; public InputClient(LocalSocket clientSocket) { this.clientSocket = clientSocket; } @Override public void interrupt() { try { clientSocket.close(); } catch (IOException e){ e.printStackTrace(); } } @Override public void run() { System.err.println("InputClient started"); try { while (!isInterrupted()) { Wire.Envelope envelope = Wire.Envelope.parseDelimitedFrom(clientSocket.getInputStream()); if (envelope == null) { break; } switch (envelope.getType()) { case DO_KEYEVENT: handleKeyEventRequest(envelope); break; case DO_TYPE: handleTypeRequest(envelope); break; case DO_WAKE: handleWakeRequest(envelope); break; case SET_ROTATION: handleSetRotationRequest(envelope); break; default: System.err.printf("Unknown request type %d; maybe it's a Service call?\n", envelope.getType()); } } } catch (IOException e) { e.printStackTrace(); } System.err.println("InputClient closing"); } private void handleKeyEventRequest(Wire.Envelope envelope) throws IOException { Wire.KeyEventRequest request = Wire.KeyEventRequest.parseFrom(envelope.getMessage()); int meta = 0; if (request.getShiftKey()) { meta |= KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON; } if (request.getCtrlKey()) { meta |= KeyEvent.META_CTRL_LEFT_ON | KeyEvent.META_CTRL_RIGHT_ON | KeyEvent.META_CTRL_ON; } if (request.getAltKey()) { meta |= KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON; } if (request.getMetaKey()) { meta |= meta |= KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON; } if (request.getSymKey()) { meta |= KeyEvent.META_SYM_ON; } if (request.getFunctionKey()) { meta |= KeyEvent.META_FUNCTION_ON; } if (request.getCapsLockKey()) { meta |= KeyEvent.META_CAPS_LOCK_ON; } if (request.getNumLockKey()) { meta |= KeyEvent.META_NUM_LOCK_ON; } if (request.getScrollLockKey()) { meta |= KeyEvent.META_SCROLL_LOCK_ON; } switch (request.getEvent()) { case DOWN: keyDown(request.getKeyCode(), meta); break; case UP: keyUp(request.getKeyCode(), meta); break; case PRESS: keyPress(request.getKeyCode(), meta); break; } } private void handleWakeRequest(Wire.Envelope envelope) throws IOException { Wire.DoWakeRequest request = Wire.DoWakeRequest.parseFrom(envelope.getMessage()); wake(); } private void handleTypeRequest(Wire.Envelope envelope) throws IOException { Wire.DoTypeRequest request = Wire.DoTypeRequest.parseFrom(envelope.getMessage()); type(request.getText()); } private void handleSetRotationRequest(Wire.Envelope envelope) throws IOException { Wire.SetRotationRequest request = Wire.SetRotationRequest.parseFrom(envelope.getMessage()); switch (request.getRotation()) { case 0: freezeRotation(Surface.ROTATION_0); break; case 180: freezeRotation(Surface.ROTATION_180); break; case 270: freezeRotation(Surface.ROTATION_270); break; case 90: freezeRotation(Surface.ROTATION_90); break; } if (!request.getLock()) { thawRotation(); } } private void keyDown(int keyCode, int metaState) { long time = SystemClock.uptimeMillis(); inputManager.injectKeyEvent(new KeyEvent( time, time, KeyEvent.ACTION_DOWN, keyCode, 0, metaState, deviceId, 0, KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD )); } private void keyUp(int keyCode, int metaState) { long time = SystemClock.uptimeMillis(); inputManager.injectKeyEvent(new KeyEvent( time, time, KeyEvent.ACTION_UP, keyCode, 0, metaState, deviceId, 0, KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD )); } private void keyPress(int keyCode, int metaState) { keyDown(keyCode, metaState); keyUp(keyCode, metaState); } private void type(String text) { KeyEvent[] events = keyCharacterMap.getEvents(text.toCharArray()); if (events != null) { for (KeyEvent event : events) { inputManager.injectKeyEvent(event); } } } private void wake() { powerManager.wakeUp(); } private void freezeRotation(int rotation) { windowManager.freezeRotation(rotation); } private void thawRotation() { windowManager.thawRotation(); } } }