Home | History | Annotate | Download | only in walt
      1 /*
      2  * Copyright (C) 2016 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 org.chromium.latency.walt;
     18 
     19 import android.app.PendingIntent;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.hardware.usb.UsbDevice;
     25 import android.hardware.usb.UsbDeviceConnection;
     26 import android.hardware.usb.UsbManager;
     27 import android.support.v4.content.LocalBroadcastManager;
     28 
     29 import java.util.HashMap;
     30 import java.util.Locale;
     31 
     32 public abstract class BaseUsbConnection {
     33     private static final String USB_PERMISSION_RESPONSE_INTENT = "usb-permission-response";
     34     private static final String CONNECT_INTENT = "org.chromium.latency.walt.CONNECT";
     35 
     36     protected SimpleLogger logger;
     37     protected Context context;
     38     private LocalBroadcastManager broadcastManager;
     39     private BroadcastReceiver currentConnectReceiver;
     40     private WaltConnection.ConnectionStateListener connectionStateListener;
     41 
     42     private UsbManager usbManager;
     43     protected UsbDevice usbDevice = null;
     44     protected UsbDeviceConnection usbConnection;
     45 
     46     public BaseUsbConnection(Context context) {
     47         this.context = context;
     48         usbManager = (UsbManager) this.context.getSystemService(Context.USB_SERVICE);
     49         logger = SimpleLogger.getInstance(context);
     50         broadcastManager = LocalBroadcastManager.getInstance(context);
     51     }
     52 
     53     public abstract int getVid();
     54     public abstract int getPid();
     55 
     56     // Used to distinguish between bootloader and normal mode that differ by PID
     57     // TODO: change intent strings to reduce dependence on PID
     58     protected abstract boolean isCompatibleUsbDevice(UsbDevice usbDevice);
     59 
     60     public void onDisconnect() {
     61         if (connectionStateListener != null) {
     62             connectionStateListener.onDisconnect();
     63         }
     64     }
     65 
     66     public void onConnect() {
     67         if (connectionStateListener != null) {
     68             connectionStateListener.onConnect();
     69         }
     70     }
     71 
     72 
     73     private String getConnectIntent() {
     74         return CONNECT_INTENT + getVid() + ":" + getPid();
     75     }
     76 
     77     private String getUsbPermissionResponseIntent() {
     78         return USB_PERMISSION_RESPONSE_INTENT + getVid() + ":" + getPid();
     79     }
     80 
     81     public boolean isConnected() {
     82         return usbConnection != null;
     83     }
     84 
     85     public void registerConnectCallback(final Runnable r) {
     86         if (currentConnectReceiver != null) {
     87             broadcastManager.unregisterReceiver(currentConnectReceiver);
     88             currentConnectReceiver = null;
     89         }
     90 
     91         if (isConnected()) {
     92             r.run();
     93             return;
     94         }
     95 
     96         currentConnectReceiver = new BroadcastReceiver() {
     97             @Override
     98             public void onReceive(Context context, Intent intent) {
     99                 broadcastManager.unregisterReceiver(this);
    100                 r.run();
    101             }
    102         };
    103         broadcastManager.registerReceiver(currentConnectReceiver,
    104                 new IntentFilter(getConnectIntent()));
    105     }
    106 
    107     public void connect() {
    108         UsbDevice usbDevice = findUsbDevice();
    109         connect(usbDevice);
    110     }
    111 
    112     public void connect(UsbDevice usbDevice) {
    113         if (usbDevice == null) {
    114             logger.log("Device not found.");
    115             return;
    116         }
    117 
    118         if (!isCompatibleUsbDevice(usbDevice)) {
    119             logger.log("Not a valid device");
    120             return;
    121         }
    122 
    123         this.usbDevice = usbDevice;
    124 
    125         // Request permission
    126         // This displays a dialog asking user for permission to use the device.
    127         // No dialog is displayed if the permission was already given before or the app started as a
    128         // result of intent filter when the device was plugged in.
    129 
    130         PendingIntent permissionIntent = PendingIntent.getBroadcast(context, 0,
    131                 new Intent(getUsbPermissionResponseIntent()), 0);
    132         context.registerReceiver(respondToUsbPermission,
    133                 new IntentFilter(getUsbPermissionResponseIntent()));
    134         logger.log("Requesting permission for USB device.");
    135         usbManager.requestPermission(this.usbDevice, permissionIntent);
    136     }
    137 
    138     public void disconnect() {
    139         onDisconnect();
    140 
    141         usbConnection.close();
    142         usbConnection = null;
    143         usbDevice = null;
    144 
    145         context.unregisterReceiver(disconnectReceiver);
    146     }
    147 
    148     private BroadcastReceiver disconnectReceiver = new BroadcastReceiver() {
    149         @Override
    150         public void onReceive(Context context, Intent intent) {
    151             UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
    152             if (isConnected() && BaseUsbConnection.this.usbDevice.equals(usbDevice)) {
    153                 logger.log("WALT was detached");
    154                 disconnect();
    155             }
    156         }
    157     };
    158 
    159     private BroadcastReceiver respondToUsbPermission = new BroadcastReceiver() {
    160         @Override
    161         public void onReceive(Context context, Intent intent) {
    162 
    163             if (usbDevice == null) {
    164                 logger.log("USB device was not properly opened");
    165                 return;
    166             }
    167 
    168             if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) &&
    169                     usbDevice.equals(intent.getParcelableExtra(UsbManager.EXTRA_DEVICE))){
    170                 usbConnection = usbManager.openDevice(usbDevice);
    171 
    172                 BaseUsbConnection.this.context.registerReceiver(disconnectReceiver,
    173                         new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED));
    174 
    175                 onConnect();
    176 
    177                 broadcastManager.sendBroadcast(new Intent(getConnectIntent()));
    178             } else {
    179                 logger.log("Could not get permission to open the USB device");
    180             }
    181             BaseUsbConnection.this.context.unregisterReceiver(respondToUsbPermission);
    182         }
    183     };
    184 
    185     public UsbDevice findUsbDevice() {
    186 
    187         logger.log(String.format("Looking for TeensyUSB VID=0x%x PID=0x%x", getVid(), getPid()));
    188 
    189         HashMap<String, UsbDevice> deviceHash = usbManager.getDeviceList();
    190         if (deviceHash.size() == 0) {
    191             logger.log("No connected USB devices found");
    192             return null;
    193         }
    194 
    195         logger.log("Found " + deviceHash.size() + " connected USB devices:");
    196 
    197         UsbDevice usbDevice = null;
    198 
    199         for (String key : deviceHash.keySet()) {
    200 
    201             UsbDevice dev = deviceHash.get(key);
    202 
    203             String msg = String.format(Locale.US,
    204                     "USB Device: %s, VID:PID - %x:%x, %d interfaces",
    205                     key, dev.getVendorId(), dev.getProductId(), dev.getInterfaceCount()
    206             );
    207 
    208             if (isCompatibleUsbDevice(dev)) {
    209                 usbDevice = dev;
    210                 msg = "Using " + msg;
    211             } else {
    212                 msg = "Skipping " + msg;
    213             }
    214 
    215             logger.log(msg);
    216         }
    217         return usbDevice;
    218     }
    219 
    220     public void setConnectionStateListener(WaltConnection.ConnectionStateListener connectionStateListener) {
    221         this.connectionStateListener = connectionStateListener;
    222     }
    223 }
    224