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 17package org.chromium.latency.walt; 18 19import android.app.PendingIntent; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.hardware.usb.UsbDevice; 25import android.hardware.usb.UsbDeviceConnection; 26import android.hardware.usb.UsbManager; 27import android.support.v4.content.LocalBroadcastManager; 28 29import java.util.HashMap; 30import java.util.Locale; 31 32public 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