UsbHostController.java revision b6af5c511a7f78e6bec870a5a0f660dabda45665
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.google.android.car.kitchensink.setting.usb; 17 18import android.content.BroadcastReceiver; 19import android.content.Context; 20import android.content.Intent; 21import android.content.IntentFilter; 22import android.content.pm.PackageManager; 23import android.hardware.usb.UsbDevice; 24import android.hardware.usb.UsbManager; 25import android.os.Handler; 26import android.os.Looper; 27import android.os.Message; 28import android.util.Log; 29 30import com.android.internal.annotations.GuardedBy; 31 32import java.util.ArrayList; 33import java.util.List; 34 35/** 36 * Controller used to handle USB device connections. 37 */ 38public final class UsbHostController 39 implements UsbDeviceHandlerResolver.UsbDeviceHandlerResolverCallback { 40 41 /** 42 * Callbacks for controller 43 */ 44 public interface UsbHostControllerCallbacks { 45 /** Host controller ready for shutdown*/ 46 void shutdown(); 47 /** Change of processing state*/ 48 void processingStateChanged(boolean processing); 49 /** Title of processing changed */ 50 void titleChanged(String title); 51 /** Options for USB device changed */ 52 void optionsUpdated(List<UsbDeviceSettings> options); 53 } 54 55 private static final String TAG = UsbHostController.class.getSimpleName(); 56 private static final boolean LOCAL_LOGD = true; 57 private static final boolean LOCAL_LOGV = true; 58 59 60 private final List<UsbDeviceSettings> mEmptyList = new ArrayList<>(); 61 private final Context mContext; 62 private final UsbHostControllerCallbacks mCallback; 63 private final UsbSettingsStorage mUsbSettingsStorage; 64 private final UsbManager mUsbManager; 65 private final PackageManager mPackageManager; 66 private final UsbDeviceHandlerResolver mUsbReslover; 67 private final UsbHostControllerHandler mHandler; 68 69 private final BroadcastReceiver mUsbBroadcastReceiver = new BroadcastReceiver() { 70 @Override 71 public void onReceive(Context context, Intent intent) { 72 if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) { 73 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE); 74 unsetActiveDeviceIfSerialMatch(device); 75 } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { 76 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE); 77 setActiveDeviceIfSerialMatch(device); 78 } 79 } 80 }; 81 82 @GuardedBy("this") 83 private UsbDevice mActiveDevice; 84 85 @GuardedBy("this") 86 private String mProcessingDeviceSerial; 87 88 public UsbHostController(Context context, UsbHostControllerCallbacks callbacks) { 89 mContext = context; 90 mCallback = callbacks; 91 mHandler = new UsbHostControllerHandler(Looper.myLooper()); 92 mUsbSettingsStorage = new UsbSettingsStorage(context); 93 mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); 94 mPackageManager = context.getPackageManager(); 95 mUsbReslover = new UsbDeviceHandlerResolver(mUsbManager, mContext, this); 96 IntentFilter filter = new IntentFilter(); 97 filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); 98 filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); 99 context.registerReceiver(mUsbBroadcastReceiver, filter); 100 101 } 102 103 private synchronized void setActiveDeviceIfSerialMatch(UsbDevice device) { 104 if (device.getSerialNumber().equals(mProcessingDeviceSerial)) { 105 mActiveDevice = device; 106 } 107 } 108 109 private synchronized void unsetActiveDeviceIfSerialMatch(UsbDevice device) { 110 mHandler.requestDeviceRemoved(); 111 if (mActiveDevice != null 112 && mActiveDevice.getSerialNumber().equals(device.getSerialNumber())) { 113 mActiveDevice = null; 114 } 115 } 116 117 private synchronized boolean startDeviceProcessingIfNull(UsbDevice device) { 118 if (mActiveDevice == null) { 119 mActiveDevice = device; 120 mProcessingDeviceSerial = device.getSerialNumber(); 121 return true; 122 } 123 return false; 124 } 125 126 private synchronized void stopDeviceProcessing() { 127 mActiveDevice = null; 128 mProcessingDeviceSerial = null; 129 } 130 131 private synchronized UsbDevice getActiveDevice() { 132 return mActiveDevice; 133 } 134 135 private boolean deviceMatchedActiveDevice(UsbDevice device) { 136 UsbDevice activeDevice = getActiveDevice(); 137 return activeDevice != null 138 && activeDevice.getSerialNumber().equals(device.getSerialNumber()); 139 } 140 141 /** 142 * Processes device new device. 143 * <p> 144 * It will load existing settings or resolve supported handlers. 145 */ 146 public void processDevice(UsbDevice device) { 147 if (!startDeviceProcessingIfNull(device)) { 148 Log.w(TAG, "Currently, other device is being processed"); 149 } 150 mCallback.optionsUpdated(mEmptyList); 151 mCallback.processingStateChanged(true); 152 153 UsbDeviceSettings settings = mUsbSettingsStorage.getSettings( 154 device.getSerialNumber(), device.getVendorId(), device.getProductId()); 155 if (settings != null && mUsbReslover.dispatch( 156 mActiveDevice, settings.getHandler(), settings.getAoap())) { 157 if (LOCAL_LOGV) { 158 Log.v(TAG, "Usb Device: " + device + " was sent to component: " 159 + settings.getHandler()); 160 } 161 return; 162 } 163 mCallback.titleChanged(device.getManufacturerName() + " " + device.getProductName()); 164 mUsbReslover.resolve(device); 165 } 166 167 /** 168 * Applies device settings. 169 */ 170 public void applyDeviceSettings(UsbDeviceSettings settings) { 171 mUsbSettingsStorage.saveSettings(settings); 172 mUsbReslover.dispatch(getActiveDevice(), settings.getHandler(), settings.getAoap()); 173 } 174 175 /** 176 * Release object. 177 */ 178 public void release() { 179 mContext.unregisterReceiver(mUsbBroadcastReceiver); 180 mUsbReslover.release(); 181 } 182 183 @Override 184 public void onHandlersResolveCompleted( 185 UsbDevice device, List<UsbDeviceSettings> handlers) { 186 if (LOCAL_LOGD) { 187 Log.d(TAG, "onHandlersResolveComplete: " + device); 188 } 189 if (deviceMatchedActiveDevice(device)) { 190 mCallback.processingStateChanged(false); 191 if (handlers.isEmpty()) { 192 onDeviceDispatched(); 193 } else { 194 mCallback.optionsUpdated(handlers); 195 } 196 } else { 197 Log.w(TAG, "Handlers ignored as they came for inactive device"); 198 } 199 } 200 201 @Override 202 public void onDeviceDispatched() { 203 stopDeviceProcessing(); 204 mCallback.shutdown(); 205 } 206 207 void doHandleDeviceRemoved() { 208 if (getActiveDevice() == null) { 209 if (LOCAL_LOGD) { 210 Log.d(TAG, "USB device detached"); 211 } 212 stopDeviceProcessing(); 213 mCallback.shutdown(); 214 } 215 } 216 217 private static Intent createDeviceAttachedIntent(UsbDevice device) { 218 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); 219 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 220 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 221 return intent; 222 } 223 224 private class UsbHostControllerHandler extends Handler { 225 private static final int MSG_DEVICE_REMOVED = 1; 226 227 private static final int DEVICE_REMOVE_TIMEOUT_MS = 500; 228 229 private UsbHostControllerHandler(Looper looper) { 230 super(looper); 231 } 232 233 private void requestDeviceRemoved() { 234 sendEmptyMessageDelayed(MSG_DEVICE_REMOVED, DEVICE_REMOVE_TIMEOUT_MS); 235 } 236 237 @Override 238 public void handleMessage(Message msg) { 239 switch (msg.what) { 240 case MSG_DEVICE_REMOVED: 241 doHandleDeviceRemoved(); 242 break; 243 default: 244 Log.w(TAG, "Unhandled message: " + msg); 245 super.handleMessage(msg); 246 } 247 } 248 } 249 250 251} 252