Home | History | Annotate | Download | only in usb
      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 com.google.android.car.kitchensink.setting.usb;
     17 
     18 import android.content.BroadcastReceiver;
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.content.IntentFilter;
     22 import android.content.pm.PackageManager;
     23 import android.hardware.usb.UsbDevice;
     24 import android.hardware.usb.UsbManager;
     25 import android.os.Handler;
     26 import android.os.Looper;
     27 import android.os.Message;
     28 import android.util.Log;
     29 
     30 import com.android.internal.annotations.GuardedBy;
     31 
     32 import java.util.ArrayList;
     33 import java.util.List;
     34 
     35 /**
     36  * Controller used to handle USB device connections.
     37  */
     38 public 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 != null && device.getSerialNumber() != null
    105                 && device.getSerialNumber().equals(mProcessingDeviceSerial)) {
    106             mActiveDevice = device;
    107         }
    108     }
    109 
    110     private synchronized void unsetActiveDeviceIfSerialMatch(UsbDevice device) {
    111         mHandler.requestDeviceRemoved();
    112         if (mActiveDevice != null && mActiveDevice.getSerialNumber() != null
    113                 && mActiveDevice.getSerialNumber().equals(device.getSerialNumber())) {
    114             mActiveDevice = null;
    115         }
    116     }
    117 
    118     private synchronized boolean startDeviceProcessingIfNull(UsbDevice device) {
    119         if (mActiveDevice == null) {
    120             mActiveDevice = device;
    121             mProcessingDeviceSerial = device.getSerialNumber();
    122             return true;
    123         }
    124         return false;
    125     }
    126 
    127     private synchronized void stopDeviceProcessing() {
    128         mActiveDevice = null;
    129         mProcessingDeviceSerial = null;
    130     }
    131 
    132     private synchronized UsbDevice getActiveDevice() {
    133         return mActiveDevice;
    134     }
    135 
    136     private boolean deviceMatchedActiveDevice(UsbDevice device) {
    137         UsbDevice activeDevice = getActiveDevice();
    138         return activeDevice != null && activeDevice.getSerialNumber() != null
    139                 && activeDevice.getSerialNumber().equals(device.getSerialNumber());
    140     }
    141 
    142     /**
    143      * Processes device new device.
    144      * <p>
    145      * It will load existing settings or resolve supported handlers.
    146      */
    147     public void processDevice(UsbDevice device) {
    148         if (!startDeviceProcessingIfNull(device)) {
    149             Log.w(TAG, "Currently, other device is being processed");
    150         }
    151         mCallback.optionsUpdated(mEmptyList);
    152         mCallback.processingStateChanged(true);
    153 
    154         UsbDeviceSettings settings = mUsbSettingsStorage.getSettings(
    155                 device.getSerialNumber(), device.getVendorId(), device.getProductId());
    156         if (settings != null && mUsbReslover.dispatch(
    157                     mActiveDevice, settings.getHandler(), settings.getAoap())) {
    158             if (LOCAL_LOGV) {
    159                 Log.v(TAG, "Usb Device: " + device + " was sent to component: "
    160                         + settings.getHandler());
    161             }
    162             return;
    163         }
    164         mCallback.titleChanged(device.getManufacturerName() + " " + device.getProductName());
    165         mUsbReslover.resolve(device);
    166     }
    167 
    168     /**
    169      * Applies device settings.
    170      */
    171     public void applyDeviceSettings(UsbDeviceSettings settings) {
    172         mUsbSettingsStorage.saveSettings(settings);
    173         mUsbReslover.dispatch(getActiveDevice(), settings.getHandler(), settings.getAoap());
    174     }
    175 
    176     /**
    177      * Release object.
    178      */
    179     public void release() {
    180         mContext.unregisterReceiver(mUsbBroadcastReceiver);
    181         mUsbReslover.release();
    182     }
    183 
    184     @Override
    185     public void onHandlersResolveCompleted(
    186             UsbDevice device, List<UsbDeviceSettings> handlers) {
    187         if (LOCAL_LOGD) {
    188             Log.d(TAG, "onHandlersResolveComplete: " + device);
    189         }
    190         if (deviceMatchedActiveDevice(device)) {
    191             mCallback.processingStateChanged(false);
    192             if (handlers.isEmpty()) {
    193                 onDeviceDispatched();
    194             } else {
    195                 mCallback.optionsUpdated(handlers);
    196             }
    197         } else {
    198             Log.w(TAG, "Handlers ignored as they came for inactive device");
    199         }
    200     }
    201 
    202     @Override
    203     public void onDeviceDispatched() {
    204         stopDeviceProcessing();
    205         mCallback.shutdown();
    206     }
    207 
    208     void doHandleDeviceRemoved() {
    209         if (getActiveDevice() == null) {
    210             if (LOCAL_LOGD) {
    211                 Log.d(TAG, "USB device detached");
    212             }
    213             stopDeviceProcessing();
    214             mCallback.shutdown();
    215         }
    216     }
    217 
    218     private static Intent createDeviceAttachedIntent(UsbDevice device) {
    219         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    220         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
    221         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    222         return intent;
    223     }
    224 
    225     private class UsbHostControllerHandler extends Handler {
    226         private static final int MSG_DEVICE_REMOVED = 1;
    227 
    228         private static final int DEVICE_REMOVE_TIMEOUT_MS = 500;
    229 
    230         private UsbHostControllerHandler(Looper looper) {
    231             super(looper);
    232         }
    233 
    234         private void requestDeviceRemoved() {
    235             sendEmptyMessageDelayed(MSG_DEVICE_REMOVED, DEVICE_REMOVE_TIMEOUT_MS);
    236         }
    237 
    238         @Override
    239         public void handleMessage(Message msg) {
    240             switch (msg.what) {
    241                 case MSG_DEVICE_REMOVED:
    242                     doHandleDeviceRemoved();
    243                     break;
    244                 default:
    245                     Log.w(TAG, "Unhandled message: " + msg);
    246                     super.handleMessage(msg);
    247             }
    248         }
    249     }
    250 
    251 
    252 }
    253