1d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan/*
2d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * Copyright (C) 2016 The Android Open Source Project
3d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan *
4d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * Licensed under the Apache License, Version 2.0 (the "License");
5d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * you may not use this file except in compliance with the License.
6d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * You may obtain a copy of the License at
7d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan *
8d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan *      http://www.apache.org/licenses/LICENSE-2.0
9d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan *
10d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * Unless required by applicable law or agreed to in writing, software
11d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * distributed under the License is distributed on an "AS IS" BASIS,
12d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * See the License for the specific language governing permissions and
14d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * limitations under the License.
15d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan */
16d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanpackage android.car.usb.handler;
17d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
18d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport android.content.BroadcastReceiver;
19d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport android.content.Context;
20d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport android.content.Intent;
21d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport android.content.IntentFilter;
22d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport android.hardware.usb.UsbDevice;
23d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport android.hardware.usb.UsbManager;
24d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport android.os.Handler;
25d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport android.os.Looper;
26d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport android.os.Message;
27d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport android.util.Log;
28d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport com.android.internal.annotations.GuardedBy;
29d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport java.util.ArrayList;
30d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanimport java.util.List;
31d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
32d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan/**
33d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * Controller used to handle USB device connections.
34d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan * TODO: Support handling multiple new USB devices at the same time.
35d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan */
36d428549b58b2df5015bff81d79747265ee8be536Kevin Crossanpublic final class UsbHostController
37d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        implements UsbDeviceHandlerResolver.UsbDeviceHandlerResolverCallback {
38d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
39d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    /**
40d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan     * Callbacks for controller
41d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan     */
42d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    public interface UsbHostControllerCallbacks {
43d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        /** Host controller ready for shutdown */
44d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        void shutdown();
45d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        /** Change of processing state */
46d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        void processingStateChanged(boolean processing);
47d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        /** Title of processing changed */
48d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        void titleChanged(String title);
49d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        /** Options for USB device changed */
50d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        void optionsUpdated(List<UsbDeviceSettings> options);
51d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
52d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
53d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private static final String TAG = UsbHostController.class.getSimpleName();
54d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private static final boolean LOCAL_LOGD = true;
55d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private static final boolean LOCAL_LOGV = true;
56d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
57d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
58d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private final List<UsbDeviceSettings> mEmptyList = new ArrayList<>();
59d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private final Context mContext;
60d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private final UsbHostControllerCallbacks mCallback;
61d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private final UsbSettingsStorage mUsbSettingsStorage;
62d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private final UsbManager mUsbManager;
63d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private final UsbDeviceHandlerResolver mUsbResolver;
64d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private final UsbHostControllerHandler mHandler;
65d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
66d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private final BroadcastReceiver mUsbBroadcastReceiver = new BroadcastReceiver() {
67d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        @Override
68d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        public void onReceive(Context context, Intent intent) {
69d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
70d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
714f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan                unsetActiveDeviceIfMatch(device);
72d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
73d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
744f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan                setActiveDeviceIfMatch(device);
75d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            }
76d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
77d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    };
78d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
79d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    @GuardedBy("this")
80d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private UsbDevice mActiveDevice;
81d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
82d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    public UsbHostController(Context context, UsbHostControllerCallbacks callbacks) {
83d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mContext = context;
84d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mCallback = callbacks;
85d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mHandler = new UsbHostControllerHandler(Looper.myLooper());
86d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mUsbSettingsStorage = new UsbSettingsStorage(context);
87d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
88d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mUsbResolver = new UsbDeviceHandlerResolver(mUsbManager, mContext, this);
89d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        IntentFilter filter = new IntentFilter();
90d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
91d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
92d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        context.registerReceiver(mUsbBroadcastReceiver, filter);
93d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
94d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
95d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
964f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan    private synchronized void setActiveDeviceIfMatch(UsbDevice device) {
974f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        if (mActiveDevice != null && device != null
984f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan                && UsbUtil.isDevicesMatching(device, mActiveDevice)) {
99d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            mActiveDevice = device;
100d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
101d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
102d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
1034f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan    private synchronized void unsetActiveDeviceIfMatch(UsbDevice device) {
104d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mHandler.requestDeviceRemoved();
1054f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        if (mActiveDevice != null && device != null
1064f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan                && UsbUtil.isDevicesMatching(device, mActiveDevice)) {
107d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            mActiveDevice = null;
108d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
109d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
110d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
111d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private synchronized boolean startDeviceProcessingIfNull(UsbDevice device) {
112d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        if (mActiveDevice == null) {
113d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            mActiveDevice = device;
114d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            return true;
115d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
116d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        return false;
117d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
118d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
119d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private synchronized void stopDeviceProcessing() {
120d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mActiveDevice = null;
121d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
122d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
123d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private synchronized UsbDevice getActiveDevice() {
124d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        return mActiveDevice;
125d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
126d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
127d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private boolean deviceMatchedActiveDevice(UsbDevice device) {
128d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        UsbDevice activeDevice = getActiveDevice();
1294f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        return activeDevice != null && UsbUtil.isDevicesMatching(activeDevice, device);
1304f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan    }
1314f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan
1324f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan    private String generateTitle() {
1334f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        String manufacturer = mActiveDevice.getManufacturerName();
1344f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        String product = mActiveDevice.getProductName();
1354f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        if (manufacturer == null && product == null) {
1364f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan            return mContext.getString(R.string.usb_unknown_device);
1374f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        }
1384f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        if (manufacturer != null && product != null) {
1394f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan            return manufacturer + " " + product;
1404f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        }
1414f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        if (manufacturer != null) {
1424f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan            return manufacturer;
1434f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        }
1444f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        return product;
145d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
146d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
147d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    /**
148d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan     * Processes device new device.
149d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan     * <p>
150d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan     * It will load existing settings or resolve supported handlers.
151d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan     */
152d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    public void processDevice(UsbDevice device) {
153d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        if (!startDeviceProcessingIfNull(device)) {
154d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            Log.w(TAG, "Currently, other device is being processed");
155d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
156d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mCallback.optionsUpdated(mEmptyList);
157d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mCallback.processingStateChanged(true);
158d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
1594f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        UsbDeviceSettings settings = mUsbSettingsStorage.getSettings(device);
160d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        if (settings != null && mUsbResolver.dispatch(
161d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                    mActiveDevice, settings.getHandler(), settings.getAoap())) {
162d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            if (LOCAL_LOGV) {
163d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                Log.v(TAG, "Usb Device: " + device + " was sent to component: "
164d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                        + settings.getHandler());
165d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            }
166d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            return;
167d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
1684f208d8be0fd9cbf1d9defb0587507f11a092b98Kevin Crossan        mCallback.titleChanged(generateTitle());
169d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mUsbResolver.resolve(device);
170d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
171d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
172d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    /**
173d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan     * Applies device settings.
174d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan     */
175d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    public void applyDeviceSettings(UsbDeviceSettings settings) {
176d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mUsbSettingsStorage.saveSettings(settings);
177d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mUsbResolver.dispatch(getActiveDevice(), settings.getHandler(), settings.getAoap());
178d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
179d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
180d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    /**
181d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan     * Release object.
182d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan     */
183d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    public void release() {
184d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mContext.unregisterReceiver(mUsbBroadcastReceiver);
185d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mUsbResolver.release();
186d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
187d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
188d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    @Override
189d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    public void onHandlersResolveCompleted(
190d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            UsbDevice device, List<UsbDeviceSettings> handlers) {
191d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        if (LOCAL_LOGD) {
192d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            Log.d(TAG, "onHandlersResolveComplete: " + device);
193d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
194d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        if (deviceMatchedActiveDevice(device)) {
195d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            mCallback.processingStateChanged(false);
196d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            if (handlers.isEmpty()) {
197d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                onDeviceDispatched();
198d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            } else if (handlers.size() == 1) {
199d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                applyDeviceSettings(handlers.get(0));
200d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            } else {
201d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                mCallback.optionsUpdated(handlers);
202d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            }
203d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        } else {
204d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            Log.w(TAG, "Handlers ignored as they came for inactive device");
205d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
206d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
207d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
208d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    @Override
209d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    public void onDeviceDispatched() {
210d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        stopDeviceProcessing();
211d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        mCallback.shutdown();
212d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
213d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
214d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    void doHandleDeviceRemoved() {
215d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        if (getActiveDevice() == null) {
216d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            if (LOCAL_LOGD) {
217d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                Log.d(TAG, "USB device detached");
218d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            }
219d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            stopDeviceProcessing();
220d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            mCallback.shutdown();
221d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
222d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
223d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
224d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    private class UsbHostControllerHandler extends Handler {
225d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        private static final int MSG_DEVICE_REMOVED = 1;
226d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
227d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        private static final int DEVICE_REMOVE_TIMEOUT_MS = 500;
228d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
229d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        private UsbHostControllerHandler(Looper looper) {
230d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            super(looper);
231d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
232d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
233d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        private void requestDeviceRemoved() {
234d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            sendEmptyMessageDelayed(MSG_DEVICE_REMOVED, DEVICE_REMOVE_TIMEOUT_MS);
235d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
236d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
237d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        @Override
238d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        public void handleMessage(Message msg) {
239d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            switch (msg.what) {
240d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                case MSG_DEVICE_REMOVED:
241d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                    doHandleDeviceRemoved();
242d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                    break;
243d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                default:
244d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                    Log.w(TAG, "Unhandled message: " + msg);
245d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan                    super.handleMessage(msg);
246d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan            }
247d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan        }
248d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan    }
249d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan
250d428549b58b2df5015bff81d79747265ee8be536Kevin Crossan}
251