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