Home | History | Annotate | Download | only in hid
      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     private static final int MSG_OPEN_DEVICE = 1;
     33     private static final int MSG_SEND_REPORT = 2;
     34     private static final int MSG_CLOSE_DEVICE = 3;
     35 
     36     private final int mId;
     37     private final HandlerThread mThread;
     38     private final DeviceHandler mHandler;
     39     private long mTimeToSend;
     40 
     41     private final Object mCond = new Object();
     42 
     43     static {
     44         System.loadLibrary("hidcommand_jni");
     45     }
     46 
     47     private static native long nativeOpenDevice(String name, int id, int vid, int pid,
     48             byte[] descriptor, DeviceCallback callback);
     49     private static native void nativeSendReport(long ptr, byte[] data);
     50     private static native void nativeCloseDevice(long ptr);
     51 
     52     public Device(int id, String name, int vid, int pid, byte[] descriptor, byte[] report) {
     53         mId = id;
     54         mThread = new HandlerThread("HidDeviceHandler");
     55         mThread.start();
     56         mHandler = new DeviceHandler(mThread.getLooper());
     57         SomeArgs args = SomeArgs.obtain();
     58         args.argi1 = id;
     59         args.argi2 = vid;
     60         args.argi3 = pid;
     61         if (name != null) {
     62             args.arg1 = name;
     63         } else {
     64             args.arg1 = id + ":" + vid + ":" + pid;
     65         }
     66         args.arg2 = descriptor;
     67         args.arg3 = report;
     68         mHandler.obtainMessage(MSG_OPEN_DEVICE, args).sendToTarget();
     69         mTimeToSend = SystemClock.uptimeMillis();
     70     }
     71 
     72     public void sendReport(byte[] report) {
     73         Message msg = mHandler.obtainMessage(MSG_SEND_REPORT, report);
     74         // if two messages are sent at identical time, they will be processed in order received
     75         mHandler.sendMessageAtTime(msg, mTimeToSend);
     76     }
     77 
     78     public void addDelay(int delay) {
     79         mTimeToSend = Math.max(SystemClock.uptimeMillis(), mTimeToSend) + delay;
     80     }
     81 
     82     public void close() {
     83         Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE);
     84         mHandler.sendMessageAtTime(msg, Math.max(SystemClock.uptimeMillis(), mTimeToSend) + 1);
     85         try {
     86             synchronized (mCond) {
     87                 mCond.wait();
     88             }
     89         } catch (InterruptedException ignore) {}
     90     }
     91 
     92     private class DeviceHandler extends Handler {
     93         private long mPtr;
     94         private int mBarrierToken;
     95 
     96         public DeviceHandler(Looper looper) {
     97             super(looper);
     98         }
     99 
    100         @Override
    101         public void handleMessage(Message msg) {
    102             switch (msg.what) {
    103                 case MSG_OPEN_DEVICE:
    104                     SomeArgs args = (SomeArgs) msg.obj;
    105                     mPtr = nativeOpenDevice((String) args.arg1, args.argi1, args.argi2, args.argi3,
    106                             (byte[]) args.arg2, new DeviceCallback());
    107                     pauseEvents();
    108                     break;
    109                 case MSG_SEND_REPORT:
    110                     if (mPtr != 0) {
    111                         nativeSendReport(mPtr, (byte[]) msg.obj);
    112                     } else {
    113                         Log.e(TAG, "Tried to send report to closed device.");
    114                     }
    115                     break;
    116                 case MSG_CLOSE_DEVICE:
    117                     if (mPtr != 0) {
    118                         nativeCloseDevice(mPtr);
    119                         getLooper().quitSafely();
    120                         mPtr = 0;
    121                     } else {
    122                         Log.e(TAG, "Tried to close already closed device.");
    123                     }
    124                     synchronized (mCond) {
    125                         mCond.notify();
    126                     }
    127                     break;
    128                 default:
    129                     throw new IllegalArgumentException("Unknown device message");
    130             }
    131         }
    132 
    133         public void pauseEvents() {
    134             mBarrierToken = getLooper().myQueue().postSyncBarrier();
    135         }
    136 
    137         public void resumeEvents() {
    138             getLooper().myQueue().removeSyncBarrier(mBarrierToken);
    139             mBarrierToken = 0;
    140         }
    141     }
    142 
    143     private class DeviceCallback {
    144         public void onDeviceOpen() {
    145             mHandler.resumeEvents();
    146         }
    147 
    148         public void onDeviceError() {
    149             Log.e(TAG, "Device error occurred, closing /dev/uhid");
    150             Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE);
    151             msg.setAsynchronous(true);
    152             msg.sendToTarget();
    153         }
    154     }
    155 }
    156