1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.commands.hid; 18 19 import android.os.Handler; 20 import android.os.HandlerThread; 21 import android.os.Looper; 22 import android.os.Message; 23 import android.os.MessageQueue; 24 import android.os.SystemClock; 25 import android.util.Log; 26 27 import com.android.internal.os.SomeArgs; 28 29 public class Device { 30 private static final String TAG = "HidDevice"; 31 32 // Minimum amount of time to wait before sending input events to a device. Even though we're 33 // guaranteed that the device has been created and opened by the input system, there's still a 34 // window in which the system hasn't started reading events out of it. If a stream of events 35 // begins in during this window (like a button down event) and *then* we start reading, we're 36 // liable to ignore the whole stream. 37 private static final int MIN_WAIT_FOR_FIRST_EVENT = 150; 38 39 private static final int MSG_OPEN_DEVICE = 1; 40 private static final int MSG_SEND_REPORT = 2; 41 private static final int MSG_CLOSE_DEVICE = 3; 42 43 44 private final int mId; 45 private final HandlerThread mThread; 46 private final DeviceHandler mHandler; 47 private long mEventTime; 48 49 private final Object mCond = new Object(); 50 51 static { 52 System.loadLibrary("hidcommand_jni"); 53 } 54 nativeOpenDevice(String name, int id, int vid, int pid, byte[] descriptor, MessageQueue queue, DeviceCallback callback)55 private static native long nativeOpenDevice(String name, int id, int vid, int pid, 56 byte[] descriptor, MessageQueue queue, DeviceCallback callback); nativeSendReport(long ptr, byte[] data)57 private static native void nativeSendReport(long ptr, byte[] data); nativeCloseDevice(long ptr)58 private static native void nativeCloseDevice(long ptr); 59 Device(int id, String name, int vid, int pid, byte[] descriptor, byte[] report)60 public Device(int id, String name, int vid, int pid, byte[] descriptor, byte[] report) { 61 mId = id; 62 mThread = new HandlerThread("HidDeviceHandler"); 63 mThread.start(); 64 mHandler = new DeviceHandler(mThread.getLooper()); 65 SomeArgs args = SomeArgs.obtain(); 66 args.argi1 = id; 67 args.argi2 = vid; 68 args.argi3 = pid; 69 if (name != null) { 70 args.arg1 = name; 71 } else { 72 args.arg1 = id + ":" + vid + ":" + pid; 73 } 74 args.arg2 = descriptor; 75 args.arg3 = report; 76 mHandler.obtainMessage(MSG_OPEN_DEVICE, args).sendToTarget(); 77 mEventTime = SystemClock.uptimeMillis() + MIN_WAIT_FOR_FIRST_EVENT; 78 } 79 sendReport(byte[] report)80 public void sendReport(byte[] report) { 81 Message msg = mHandler.obtainMessage(MSG_SEND_REPORT, report); 82 mHandler.sendMessageAtTime(msg, mEventTime); 83 } 84 addDelay(int delay)85 public void addDelay(int delay) { 86 mEventTime += delay; 87 } 88 close()89 public void close() { 90 Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE); 91 msg.setAsynchronous(true); 92 mHandler.sendMessageAtTime(msg, mEventTime + 1); 93 try { 94 synchronized (mCond) { 95 mCond.wait(); 96 } 97 } catch (InterruptedException ignore) {} 98 } 99 100 private class DeviceHandler extends Handler { 101 private long mPtr; 102 private int mBarrierToken; 103 DeviceHandler(Looper looper)104 public DeviceHandler(Looper looper) { 105 super(looper); 106 } 107 108 @Override handleMessage(Message msg)109 public void handleMessage(Message msg) { 110 switch (msg.what) { 111 case MSG_OPEN_DEVICE: 112 SomeArgs args = (SomeArgs) msg.obj; 113 mPtr = nativeOpenDevice((String) args.arg1, args.argi1, args.argi2, args.argi3, 114 (byte[]) args.arg2, getLooper().myQueue(), new DeviceCallback()); 115 nativeSendReport(mPtr, (byte[]) args.arg3); 116 pauseEvents(); 117 break; 118 case MSG_SEND_REPORT: 119 if (mPtr != 0) { 120 nativeSendReport(mPtr, (byte[]) msg.obj); 121 } else { 122 Log.e(TAG, "Tried to send report to closed device."); 123 } 124 break; 125 case MSG_CLOSE_DEVICE: 126 if (mPtr != 0) { 127 nativeCloseDevice(mPtr); 128 getLooper().quitSafely(); 129 mPtr = 0; 130 } else { 131 Log.e(TAG, "Tried to close already closed device."); 132 } 133 synchronized (mCond) { 134 mCond.notify(); 135 } 136 break; 137 default: 138 throw new IllegalArgumentException("Unknown device message"); 139 } 140 } 141 pauseEvents()142 public void pauseEvents() { 143 mBarrierToken = getLooper().myQueue().postSyncBarrier(); 144 } 145 resumeEvents()146 public void resumeEvents() { 147 getLooper().myQueue().removeSyncBarrier(mBarrierToken); 148 mBarrierToken = 0; 149 } 150 } 151 152 private class DeviceCallback { onDeviceOpen()153 public void onDeviceOpen() { 154 mHandler.resumeEvents(); 155 } 156 onDeviceError()157 public void onDeviceError() { 158 Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE); 159 msg.setAsynchronous(true); 160 msg.sendToTarget(); 161 } 162 } 163 } 164