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