• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package android.car.usb.handler;
17 
18 import android.content.BroadcastReceiver;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.IntentFilter;
22 import android.hardware.usb.UsbDevice;
23 import android.hardware.usb.UsbDeviceConnection;
24 import android.hardware.usb.UsbManager;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.util.Log;
29 
30 import com.android.internal.annotations.GuardedBy;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 
35 /**
36  * Controller used to handle USB device connections.
37  * TODO: Support handling multiple new USB devices at the same time.
38  */
39 public final class UsbHostController
40         implements UsbDeviceHandlerResolver.UsbDeviceHandlerResolverCallback {
41 
42     /**
43      * Callbacks for controller
44      */
45     public interface UsbHostControllerCallbacks {
46         /** Host controller ready for shutdown */
shutdown()47         void shutdown();
48         /** Change of processing state */
processingStarted()49         void processingStarted();
50         /** Title of processing changed */
titleChanged(String title)51         void titleChanged(String title);
52         /** Options for USB device changed */
optionsUpdated(List<UsbDeviceSettings> options)53         void optionsUpdated(List<UsbDeviceSettings> options);
54     }
55 
56     private static final String TAG = UsbHostController.class.getSimpleName();
57     private static final boolean LOCAL_LOGD = true;
58     private static final boolean LOCAL_LOGV = true;
59 
60     private static final int DISPATCH_RETRY_DELAY_MS = 1000;
61     private static final int DISPATCH_RETRY_ATTEMPTS = 5;
62 
63     private final List<UsbDeviceSettings> mEmptyList = new ArrayList<>();
64     private final Context mContext;
65     private final UsbHostControllerCallbacks mCallback;
66     private final UsbSettingsStorage mUsbSettingsStorage;
67     private final UsbManager mUsbManager;
68     private final UsbDeviceHandlerResolver mUsbResolver;
69     private final UsbHostControllerHandler mHandler;
70 
71     private final BroadcastReceiver mUsbBroadcastReceiver = new BroadcastReceiver() {
72         @Override
73         public void onReceive(Context context, Intent intent) {
74             if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
75                 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
76                 unsetActiveDeviceIfMatch(device);
77             } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
78                 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
79                 setActiveDeviceIfMatch(device);
80             }
81         }
82     };
83 
84     @GuardedBy("this")
85     private UsbDevice mActiveDevice;
86 
UsbHostController(Context context, UsbHostControllerCallbacks callbacks)87     public UsbHostController(Context context, UsbHostControllerCallbacks callbacks) {
88         mContext = context;
89         mCallback = callbacks;
90         mHandler = new UsbHostControllerHandler(Looper.myLooper());
91         mUsbSettingsStorage = new UsbSettingsStorage(context);
92         mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
93         mUsbResolver = new UsbDeviceHandlerResolver(mUsbManager, mContext, this);
94         IntentFilter filter = new IntentFilter();
95         filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
96         filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
97         context.registerReceiver(mUsbBroadcastReceiver, filter);
98     }
99 
setActiveDeviceIfMatch(UsbDevice device)100     private synchronized void setActiveDeviceIfMatch(UsbDevice device) {
101         if (mActiveDevice != null && device != null
102                 && UsbUtil.isDevicesMatching(device, mActiveDevice)) {
103             mActiveDevice = device;
104         }
105     }
106 
unsetActiveDeviceIfMatch(UsbDevice device)107     private synchronized void unsetActiveDeviceIfMatch(UsbDevice device) {
108         mHandler.requestDeviceRemoved();
109         if (mActiveDevice != null && device != null
110                 && UsbUtil.isDevicesMatching(device, mActiveDevice)) {
111             mActiveDevice = null;
112         }
113     }
114 
startDeviceProcessingIfNull(UsbDevice device)115     private synchronized boolean startDeviceProcessingIfNull(UsbDevice device) {
116         if (mActiveDevice == null) {
117             mActiveDevice = device;
118             return true;
119         }
120         return false;
121     }
122 
stopDeviceProcessing()123     private synchronized void stopDeviceProcessing() {
124         mActiveDevice = null;
125     }
126 
getActiveDevice()127     private synchronized UsbDevice getActiveDevice() {
128         return mActiveDevice;
129     }
130 
deviceMatchedActiveDevice(UsbDevice device)131     private boolean deviceMatchedActiveDevice(UsbDevice device) {
132         UsbDevice activeDevice = getActiveDevice();
133         return activeDevice != null && UsbUtil.isDevicesMatching(activeDevice, device);
134     }
135 
generateTitle(Context context, UsbDevice usbDevice)136     private static String generateTitle(Context context, UsbDevice usbDevice) {
137         String manufacturer = usbDevice.getManufacturerName();
138         String product = usbDevice.getProductName();
139         if (manufacturer == null && product == null) {
140             return context.getString(R.string.usb_unknown_device);
141         }
142         if (manufacturer != null && product != null) {
143             return manufacturer + " " + product;
144         }
145         if (manufacturer != null) {
146             return manufacturer;
147         }
148         return product;
149     }
150 
151     /**
152      * Processes device new device.
153      * <p>
154      * It will load existing settings or resolve supported handlers.
155      */
processDevice(UsbDevice device)156     public void processDevice(UsbDevice device) {
157         if (!startDeviceProcessingIfNull(device)) {
158             Log.w(TAG, "Currently, other device is being processed");
159         }
160         mCallback.optionsUpdated(mEmptyList);
161         mCallback.processingStarted();
162 
163         UsbDeviceSettings settings = mUsbSettingsStorage.getSettings(device);
164 
165         if (settings == null) {
166             resolveDevice(device);
167         } else {
168             Object obj =
169                     new UsbHostControllerHandlerDispatchData(
170                             device, settings, DISPATCH_RETRY_ATTEMPTS, true);
171             Message.obtain(mHandler, UsbHostControllerHandler.MSG_DEVICE_DISPATCH, obj)
172                     .sendToTarget();
173         }
174     }
175 
176     /**
177      * Applies device settings.
178      */
applyDeviceSettings(UsbDeviceSettings settings)179     public void applyDeviceSettings(UsbDeviceSettings settings) {
180         mUsbSettingsStorage.saveSettings(settings);
181         Message msg = mHandler.obtainMessage();
182         msg.obj =
183                 new UsbHostControllerHandlerDispatchData(
184                         getActiveDevice(), settings, DISPATCH_RETRY_ATTEMPTS, false);
185         msg.what = UsbHostControllerHandler.MSG_DEVICE_DISPATCH;
186         msg.sendToTarget();
187     }
188 
resolveDevice(UsbDevice device)189     private void resolveDevice(UsbDevice device) {
190         mCallback.titleChanged(generateTitle(mContext, device));
191         mUsbResolver.resolve(device);
192     }
193 
194     /**
195      * Release object.
196      */
release()197     public void release() {
198         mContext.unregisterReceiver(mUsbBroadcastReceiver);
199         mUsbResolver.release();
200     }
201 
isDeviceAoapPossible(UsbDevice device)202     private boolean isDeviceAoapPossible(UsbDevice device) {
203         if (AoapInterface.isDeviceInAoapMode(device)) {
204             return true;
205         }
206 
207         UsbManager usbManager = mContext.getSystemService(UsbManager.class);
208         UsbDeviceConnection connection = UsbUtil.openConnection(usbManager, device);
209         boolean aoapSupported = AoapInterface.isSupported(mContext, device, connection);
210         connection.close();
211 
212         return aoapSupported;
213     }
214 
215     @Override
onHandlersResolveCompleted( UsbDevice device, List<UsbDeviceSettings> handlers)216     public void onHandlersResolveCompleted(
217             UsbDevice device, List<UsbDeviceSettings> handlers) {
218         if (LOCAL_LOGD) {
219             Log.d(TAG, "onHandlersResolveComplete: " + device);
220         }
221         if (deviceMatchedActiveDevice(device)) {
222             if (handlers.isEmpty()) {
223                 onDeviceDispatched();
224             } else if (handlers.size() == 1) {
225                 applyDeviceSettings(handlers.get(0));
226             } else {
227                 if (isDeviceAoapPossible(device)) {
228                     // Device supports AOAP mode, if we have just single AOAP handler then use it
229                     // instead of showing disambiguation dialog to the user.
230                     UsbDeviceSettings aoapHandler = getSingleAoapDeviceHandlerOrNull(handlers);
231                     if (aoapHandler != null) {
232                         applyDeviceSettings(aoapHandler);
233                         return;
234                     }
235                 }
236                 mCallback.optionsUpdated(handlers);
237             }
238         } else {
239             Log.w(TAG, "Handlers ignored as they came for inactive device");
240         }
241     }
242 
getSingleAoapDeviceHandlerOrNull(List<UsbDeviceSettings> handlers)243     private UsbDeviceSettings getSingleAoapDeviceHandlerOrNull(List<UsbDeviceSettings> handlers) {
244         UsbDeviceSettings aoapHandler = null;
245         for (UsbDeviceSettings handler : handlers) {
246             if (handler.getAoap()) {
247                 if (aoapHandler != null) { // Found multiple AOAP handlers.
248                     return null;
249                 }
250                 aoapHandler = handler;
251             }
252         }
253         return aoapHandler;
254     }
255 
256     @Override
onDeviceDispatched()257     public void onDeviceDispatched() {
258         stopDeviceProcessing();
259         mCallback.shutdown();
260     }
261 
doHandleDeviceRemoved()262     void doHandleDeviceRemoved() {
263         if (getActiveDevice() == null) {
264             if (LOCAL_LOGD) {
265                 Log.d(TAG, "USB device detached");
266             }
267             stopDeviceProcessing();
268             mCallback.shutdown();
269         }
270     }
271 
272     private class UsbHostControllerHandlerDispatchData {
273         private final UsbDevice mUsbDevice;
274         private final UsbDeviceSettings mUsbDeviceSettings;
275 
276         public int mRetries = 0;
277         public boolean mCanResolve = true;
278 
UsbHostControllerHandlerDispatchData( UsbDevice usbDevice, UsbDeviceSettings usbDeviceSettings, int retries, boolean canResolve)279         public UsbHostControllerHandlerDispatchData(
280                 UsbDevice usbDevice, UsbDeviceSettings usbDeviceSettings,
281                 int retries, boolean canResolve) {
282             mUsbDevice = usbDevice;
283             mUsbDeviceSettings = usbDeviceSettings;
284             mRetries = retries;
285             mCanResolve = canResolve;
286         }
287 
getUsbDevice()288         public UsbDevice getUsbDevice() {
289             return mUsbDevice;
290         }
291 
getUsbDeviceSettings()292         public UsbDeviceSettings getUsbDeviceSettings() {
293             return mUsbDeviceSettings;
294         }
295     }
296 
297     private class UsbHostControllerHandler extends Handler {
298         private static final int MSG_DEVICE_REMOVED = 1;
299         private static final int MSG_DEVICE_DISPATCH = 2;
300 
301         private static final int DEVICE_REMOVE_TIMEOUT_MS = 500;
302 
UsbHostControllerHandler(Looper looper)303         private UsbHostControllerHandler(Looper looper) {
304             super(looper);
305         }
306 
requestDeviceRemoved()307         private void requestDeviceRemoved() {
308             sendEmptyMessageDelayed(MSG_DEVICE_REMOVED, DEVICE_REMOVE_TIMEOUT_MS);
309         }
310 
311         @Override
handleMessage(Message msg)312         public void handleMessage(Message msg) {
313             switch (msg.what) {
314                 case MSG_DEVICE_REMOVED:
315                     doHandleDeviceRemoved();
316                     break;
317                 case MSG_DEVICE_DISPATCH:
318                     UsbHostControllerHandlerDispatchData data =
319                             (UsbHostControllerHandlerDispatchData) msg.obj;
320                     UsbDevice device = data.getUsbDevice();
321                     UsbDeviceSettings settings = data.getUsbDeviceSettings();
322                     if (!mUsbResolver.dispatch(device, settings.getHandler(), settings.getAoap())) {
323                         if (data.mRetries > 0) {
324                             --data.mRetries;
325                             Message nextMessage = Message.obtain(msg);
326                             mHandler.sendMessageDelayed(nextMessage, DISPATCH_RETRY_DELAY_MS);
327                         } else if (data.mCanResolve) {
328                             resolveDevice(device);
329                         }
330                     } else if (LOCAL_LOGV) {
331                         Log.v(TAG, "Usb Device: " + data.getUsbDevice() + " was sent to component: "
332                                 + settings.getHandler());
333                     }
334                     break;
335                 default:
336                     Log.w(TAG, "Unhandled message: " + msg);
337                     super.handleMessage(msg);
338             }
339         }
340     }
341 
342 }
343