• 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.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                 unsetActiveDeviceIfMatch(device);
72             } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
73                 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
74                 setActiveDeviceIfMatch(device);
75             }
76         }
77     };
78 
79     @GuardedBy("this")
80     private UsbDevice mActiveDevice;
81 
UsbHostController(Context context, UsbHostControllerCallbacks callbacks)82     public UsbHostController(Context context, UsbHostControllerCallbacks callbacks) {
83         mContext = context;
84         mCallback = callbacks;
85         mHandler = new UsbHostControllerHandler(Looper.myLooper());
86         mUsbSettingsStorage = new UsbSettingsStorage(context);
87         mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
88         mUsbResolver = new UsbDeviceHandlerResolver(mUsbManager, mContext, this);
89         IntentFilter filter = new IntentFilter();
90         filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
91         filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
92         context.registerReceiver(mUsbBroadcastReceiver, filter);
93 
94     }
95 
setActiveDeviceIfMatch(UsbDevice device)96     private synchronized void setActiveDeviceIfMatch(UsbDevice device) {
97         if (mActiveDevice != null && device != null
98                 && UsbUtil.isDevicesMatching(device, mActiveDevice)) {
99             mActiveDevice = device;
100         }
101     }
102 
unsetActiveDeviceIfMatch(UsbDevice device)103     private synchronized void unsetActiveDeviceIfMatch(UsbDevice device) {
104         mHandler.requestDeviceRemoved();
105         if (mActiveDevice != null && device != null
106                 && UsbUtil.isDevicesMatching(device, mActiveDevice)) {
107             mActiveDevice = null;
108         }
109     }
110 
startDeviceProcessingIfNull(UsbDevice device)111     private synchronized boolean startDeviceProcessingIfNull(UsbDevice device) {
112         if (mActiveDevice == null) {
113             mActiveDevice = device;
114             return true;
115         }
116         return false;
117     }
118 
stopDeviceProcessing()119     private synchronized void stopDeviceProcessing() {
120         mActiveDevice = null;
121     }
122 
getActiveDevice()123     private synchronized UsbDevice getActiveDevice() {
124         return mActiveDevice;
125     }
126 
deviceMatchedActiveDevice(UsbDevice device)127     private boolean deviceMatchedActiveDevice(UsbDevice device) {
128         UsbDevice activeDevice = getActiveDevice();
129         return activeDevice != null && UsbUtil.isDevicesMatching(activeDevice, device);
130     }
131 
generateTitle()132     private String generateTitle() {
133         String manufacturer = mActiveDevice.getManufacturerName();
134         String product = mActiveDevice.getProductName();
135         if (manufacturer == null && product == null) {
136             return mContext.getString(R.string.usb_unknown_device);
137         }
138         if (manufacturer != null && product != null) {
139             return manufacturer + " " + product;
140         }
141         if (manufacturer != null) {
142             return manufacturer;
143         }
144         return product;
145     }
146 
147     /**
148      * Processes device new device.
149      * <p>
150      * It will load existing settings or resolve supported handlers.
151      */
processDevice(UsbDevice device)152     public void processDevice(UsbDevice device) {
153         if (!startDeviceProcessingIfNull(device)) {
154             Log.w(TAG, "Currently, other device is being processed");
155         }
156         mCallback.optionsUpdated(mEmptyList);
157         mCallback.processingStateChanged(true);
158 
159         UsbDeviceSettings settings = mUsbSettingsStorage.getSettings(device);
160         if (settings != null && mUsbResolver.dispatch(
161                     mActiveDevice, settings.getHandler(), settings.getAoap())) {
162             if (LOCAL_LOGV) {
163                 Log.v(TAG, "Usb Device: " + device + " was sent to component: "
164                         + settings.getHandler());
165             }
166             return;
167         }
168         mCallback.titleChanged(generateTitle());
169         mUsbResolver.resolve(device);
170     }
171 
172     /**
173      * Applies device settings.
174      */
applyDeviceSettings(UsbDeviceSettings settings)175     public void applyDeviceSettings(UsbDeviceSettings settings) {
176         mUsbSettingsStorage.saveSettings(settings);
177         mUsbResolver.dispatch(getActiveDevice(), settings.getHandler(), settings.getAoap());
178     }
179 
180     /**
181      * Release object.
182      */
release()183     public void release() {
184         mContext.unregisterReceiver(mUsbBroadcastReceiver);
185         mUsbResolver.release();
186     }
187 
188     @Override
onHandlersResolveCompleted( UsbDevice device, List<UsbDeviceSettings> handlers)189     public void onHandlersResolveCompleted(
190             UsbDevice device, List<UsbDeviceSettings> handlers) {
191         if (LOCAL_LOGD) {
192             Log.d(TAG, "onHandlersResolveComplete: " + device);
193         }
194         if (deviceMatchedActiveDevice(device)) {
195             mCallback.processingStateChanged(false);
196             if (handlers.isEmpty()) {
197                 onDeviceDispatched();
198             } else if (handlers.size() == 1) {
199                 applyDeviceSettings(handlers.get(0));
200             } else {
201                 mCallback.optionsUpdated(handlers);
202             }
203         } else {
204             Log.w(TAG, "Handlers ignored as they came for inactive device");
205         }
206     }
207 
208     @Override
onDeviceDispatched()209     public void onDeviceDispatched() {
210         stopDeviceProcessing();
211         mCallback.shutdown();
212     }
213 
doHandleDeviceRemoved()214     void doHandleDeviceRemoved() {
215         if (getActiveDevice() == null) {
216             if (LOCAL_LOGD) {
217                 Log.d(TAG, "USB device detached");
218             }
219             stopDeviceProcessing();
220             mCallback.shutdown();
221         }
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 
UsbHostControllerHandler(Looper looper)229         private UsbHostControllerHandler(Looper looper) {
230             super(looper);
231         }
232 
requestDeviceRemoved()233         private void requestDeviceRemoved() {
234             sendEmptyMessageDelayed(MSG_DEVICE_REMOVED, DEVICE_REMOVE_TIMEOUT_MS);
235         }
236 
237         @Override
handleMessage(Message msg)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