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 */ 16package com.android.hardware.usb.externalmanagementtest; 17 18import android.content.BroadcastReceiver; 19import android.content.Context; 20import android.content.Intent; 21import android.content.IntentFilter; 22import android.hardware.usb.UsbDevice; 23import android.hardware.usb.UsbDeviceConnection; 24import android.hardware.usb.UsbManager; 25import android.os.Handler; 26import android.os.HandlerThread; 27import android.os.Looper; 28import android.os.Message; 29import android.util.Log; 30 31import java.util.LinkedList; 32 33import dalvik.system.CloseGuard; 34 35public class UsbDeviceStateController { 36 37 public interface UsbDeviceStateListener { 38 void onDeviceResetComplete(UsbDevice device); 39 void onAoapStartComplete(UsbDevice devie); 40 } 41 42 private static final String TAG = UsbDeviceStateController.class.getSimpleName(); 43 44 private static final int MAX_USB_STATE_CHANGE_WAIT = 5; 45 private static final long USB_STATE_CHANGE_WAIT_TIMEOUT_MS = 500; 46 47 private final Context mContext; 48 private final UsbDeviceStateListener mListener; 49 private final UsbManager mUsbManager; 50 private final HandlerThread mHandlerThread; 51 private final UsbStateHandler mHandler; 52 private final UsbDeviceBroadcastReceiver mUsbStateBroadcastReceiver; 53 private final CloseGuard mCloseGuard = CloseGuard.get(); 54 55 private final Object mUsbConnectionChangeWait = new Object(); 56 private final LinkedList<UsbDevice> mDevicesRemoved = new LinkedList<>(); 57 private final LinkedList<UsbDevice> mDevicesAdded = new LinkedList<>(); 58 private boolean mShouldQuit = false; 59 60 public UsbDeviceStateController(Context context, UsbDeviceStateListener listener, 61 UsbManager usbManager) { 62 mContext = context; 63 mListener = listener; 64 mUsbManager = usbManager; 65 mHandlerThread = new HandlerThread(TAG); 66 mHandlerThread.start(); 67 mCloseGuard.open("release"); 68 mHandler = new UsbStateHandler(mHandlerThread.getLooper()); 69 mUsbStateBroadcastReceiver = new UsbDeviceBroadcastReceiver(); 70 } 71 72 public void init() { 73 IntentFilter filter = new IntentFilter(); 74 filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); 75 filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); 76 mContext.registerReceiver(mUsbStateBroadcastReceiver, filter); 77 } 78 79 public void release() { 80 mCloseGuard.close(); 81 mContext.unregisterReceiver(mUsbStateBroadcastReceiver); 82 synchronized (mUsbConnectionChangeWait) { 83 mShouldQuit = true; 84 mUsbConnectionChangeWait.notifyAll(); 85 } 86 mHandlerThread.quit(); 87 } 88 89 @Override 90 protected void finalize() throws Throwable { 91 try { 92 mCloseGuard.warnIfOpen(); 93 release(); 94 } finally { 95 super.finalize(); 96 } 97 } 98 99 public void startDeviceReset(UsbDevice device) { 100 mHandler.requestDeviceReset(device); 101 } 102 103 public void startAoap(AoapSwitchRequest request) { 104 mHandler.requestAoap(request); 105 } 106 107 private void doHandleDeviceReset(UsbDevice device) { 108 boolean isInAoap = AoapInterface.isDeviceInAoapMode(device); 109 UsbDevice completedDevice = null; 110 if (isInAoap) { 111 completedDevice = resetUsbDeviceAndConfirmModeChange(device); 112 } else { 113 UsbDeviceConnection conn = openConnection(device); 114 if (conn == null) { 115 throw new RuntimeException("cannot open conneciton for device: " + device); 116 } else { 117 try { 118 if (!conn.resetDevice()) { 119 throw new RuntimeException("resetDevice failed for devie: " + device); 120 } else { 121 completedDevice = device; 122 } 123 } finally { 124 conn.close(); 125 } 126 } 127 } 128 mListener.onDeviceResetComplete(completedDevice); 129 } 130 131 private void doHandleAoapStart(AoapSwitchRequest request) { 132 UsbDevice device = request.device; 133 boolean isInAoap = AoapInterface.isDeviceInAoapMode(device); 134 if (isInAoap) { 135 device = resetUsbDeviceAndConfirmModeChange(device); 136 if (device == null) { 137 mListener.onAoapStartComplete(null); 138 return; 139 } 140 } 141 UsbDeviceConnection connection = openConnection(device); 142 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MANUFACTURER, 143 request.manufacturer); 144 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MODEL, 145 request.model); 146 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_DESCRIPTION, 147 request.description); 148 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_VERSION, 149 request.version); 150 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_URI, request.uri); 151 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_SERIAL, request.serial); 152 AoapInterface.sendAoapStart(connection); 153 device = resetUsbDeviceAndConfirmModeChange(device); 154 if (device == null) { 155 mListener.onAoapStartComplete(null); 156 } 157 if (!AoapInterface.isDeviceInAoapMode(device)) { 158 Log.w(TAG, "Device not in AOAP mode after switching: " + device); 159 mListener.onAoapStartComplete(device); 160 } 161 mListener.onAoapStartComplete(device); 162 } 163 164 private UsbDevice resetUsbDeviceAndConfirmModeChange(UsbDevice device) { 165 int retry = 0; 166 boolean removalDetected = false; 167 while (retry < MAX_USB_STATE_CHANGE_WAIT) { 168 UsbDeviceConnection connNow = openConnection(device); 169 if (connNow == null) { 170 removalDetected = true; 171 break; 172 } 173 connNow.resetDevice(); 174 connNow.close(); 175 synchronized (mUsbConnectionChangeWait) { 176 try { 177 mUsbConnectionChangeWait.wait(USB_STATE_CHANGE_WAIT_TIMEOUT_MS); 178 } catch (InterruptedException e) { 179 break; 180 } 181 if (mShouldQuit) { 182 return null; 183 } 184 if (isDeviceRemovedLocked(device)) { 185 removalDetected = true; 186 break; 187 } 188 } 189 retry++; 190 } 191 if (!removalDetected) { 192 Log.w(TAG, "resetDevice failed for device, device still in the same mode: " + device); 193 return null; 194 } 195 retry = 0; 196 UsbDevice newlyAttached = null; 197 while (retry < MAX_USB_STATE_CHANGE_WAIT) { 198 synchronized (mUsbConnectionChangeWait) { 199 try { 200 mUsbConnectionChangeWait.wait(USB_STATE_CHANGE_WAIT_TIMEOUT_MS); 201 } catch (InterruptedException e) { 202 break; 203 } 204 if (mShouldQuit) { 205 return null; 206 } 207 newlyAttached = checkDeviceAttachedLocked(device); 208 } 209 if (newlyAttached != null) { 210 break; 211 } 212 retry++; 213 } 214 if (newlyAttached == null) { 215 Log.w(TAG, "resetDevice failed for device, device disconnected: " + device); 216 return null; 217 } 218 return newlyAttached; 219 } 220 221 private boolean isDeviceRemovedLocked(UsbDevice device) { 222 for (UsbDevice removed : mDevicesRemoved) { 223 if (UsbUtil.isDevicesMatching(device, removed)) { 224 mDevicesRemoved.clear(); 225 return true; 226 } 227 } 228 mDevicesRemoved.clear(); 229 return false; 230 } 231 232 private UsbDevice checkDeviceAttachedLocked(UsbDevice device) { 233 for (UsbDevice attached : mDevicesAdded) { 234 if (UsbUtil.isTheSameDevice(device, attached)) { 235 mDevicesAdded.clear(); 236 return attached; 237 } 238 } 239 mDevicesAdded.clear(); 240 return null; 241 } 242 243 public UsbDeviceConnection openConnection(UsbDevice device) { 244 mUsbManager.grantPermission(device); 245 return mUsbManager.openDevice(device); 246 } 247 248 private void handleUsbDeviceAttached(UsbDevice device) { 249 synchronized (mUsbConnectionChangeWait) { 250 mDevicesAdded.add(device); 251 mUsbConnectionChangeWait.notifyAll(); 252 } 253 } 254 255 private void handleUsbDeviceDetached(UsbDevice device) { 256 synchronized (mUsbConnectionChangeWait) { 257 mDevicesRemoved.add(device); 258 mUsbConnectionChangeWait.notifyAll(); 259 } 260 } 261 262 private class UsbStateHandler extends Handler { 263 private final int MSG_RESET_DEVICE = 1; 264 private final int MSG_AOAP = 2; 265 266 private UsbStateHandler(Looper looper) { 267 super(looper); 268 } 269 270 private void requestDeviceReset(UsbDevice device) { 271 Message msg = obtainMessage(MSG_RESET_DEVICE, device); 272 sendMessage(msg); 273 } 274 275 private void requestAoap(AoapSwitchRequest request) { 276 Message msg = obtainMessage(MSG_AOAP, request); 277 sendMessage(msg); 278 } 279 280 @Override 281 public void handleMessage(Message msg) { 282 switch (msg.what) { 283 case MSG_RESET_DEVICE: 284 doHandleDeviceReset((UsbDevice) msg.obj); 285 break; 286 case MSG_AOAP: 287 doHandleAoapStart((AoapSwitchRequest) msg.obj); 288 break; 289 } 290 } 291 } 292 293 private class UsbDeviceBroadcastReceiver extends BroadcastReceiver { 294 @Override 295 public void onReceive(Context context, Intent intent) { 296 if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) { 297 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE); 298 handleUsbDeviceDetached(device); 299 } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { 300 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE); 301 handleUsbDeviceAttached(device); 302 } 303 } 304 } 305 306 public static class AoapSwitchRequest { 307 public final UsbDevice device; 308 public final String manufacturer; 309 public final String model; 310 public final String description; 311 public final String version; 312 public final String uri; 313 public final String serial; 314 315 public AoapSwitchRequest(UsbDevice device, String manufacturer, String model, 316 String description, String version, String uri, String serial) { 317 this.device = device; 318 this.manufacturer = manufacturer; 319 this.model = model; 320 this.description = description; 321 this.version = version; 322 this.uri = uri; 323 this.serial = serial; 324 } 325 } 326} 327