UsbHostController.java revision d428549b58b2df5015bff81d79747265ee8be536
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 android.car.usb.handler;
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.UsbManager;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
27import android.util.Log;
28import com.android.internal.annotations.GuardedBy;
29import java.util.ArrayList;
30import 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 */
36public final class UsbHostController
37        implements UsbDeviceHandlerResolver.UsbDeviceHandlerResolverCallback {
38
39    /**
40     * Callbacks for controller
41     */
42    public interface UsbHostControllerCallbacks {
43        /** Host controller ready for shutdown */
44        void shutdown();
45        /** Change of processing state */
46        void processingStateChanged(boolean processing);
47        /** Title of processing changed */
48        void titleChanged(String title);
49        /** Options for USB device changed */
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
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
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
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
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
123    private synchronized void stopDeviceProcessing() {
124        mActiveDevice = null;
125        mProcessingDeviceSerial = null;
126    }
127
128    private synchronized UsbDevice getActiveDevice() {
129        return mActiveDevice;
130    }
131
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     */
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     */
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     */
174    public void release() {
175        mContext.unregisterReceiver(mUsbBroadcastReceiver);
176        mUsbResolver.release();
177    }
178
179    @Override
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
200    public void onDeviceDispatched() {
201        stopDeviceProcessing();
202        mCallback.shutdown();
203    }
204
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
220        private UsbHostControllerHandler(Looper looper) {
221            super(looper);
222        }
223
224        private void requestDeviceRemoved() {
225            sendEmptyMessageDelayed(MSG_DEVICE_REMOVED, DEVICE_REMOVE_TIMEOUT_MS);
226        }
227
228        @Override
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