1 /*
2  * Copyright (C) 2017 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.android.settings.connecteddevice.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.hardware.usb.UsbManager;
23 import android.hardware.usb.UsbPortStatus;
24 import android.util.Log;
25 
26 import com.android.settingslib.core.lifecycle.LifecycleObserver;
27 import com.android.settingslib.core.lifecycle.events.OnPause;
28 import com.android.settingslib.core.lifecycle.events.OnResume;
29 
30 /**
31  * Receiver to receive usb update and use {@link UsbConnectionListener} to invoke callback
32  */
33 public class UsbConnectionBroadcastReceiver extends BroadcastReceiver implements LifecycleObserver,
34         OnResume, OnPause {
35     private static final String TAG = "UsbBroadcastReceiver";
36     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
37 
38     private Context mContext;
39     private UsbConnectionListener mUsbConnectionListener;
40     private boolean mListeningToUsbEvents;
41     private UsbBackend mUsbBackend;
42 
43     private boolean mConnected;
44     private long mFunctions;
45     private int mDataRole;
46     private int mPowerRole;
47 
UsbConnectionBroadcastReceiver(Context context, UsbConnectionListener usbConnectionListener, UsbBackend backend)48     public UsbConnectionBroadcastReceiver(Context context,
49             UsbConnectionListener usbConnectionListener, UsbBackend backend) {
50         mContext = context;
51         mUsbConnectionListener = usbConnectionListener;
52         mUsbBackend = backend;
53 
54         mFunctions = UsbManager.FUNCTION_NONE;
55         mDataRole = UsbPortStatus.DATA_ROLE_NONE;
56         mPowerRole = UsbPortStatus.POWER_ROLE_NONE;
57     }
58 
59     @Override
onReceive(Context context, Intent intent)60     public void onReceive(Context context, Intent intent) {
61         if (DEBUG) {
62             Log.d(TAG, "onReceive() action : " + intent.getAction());
63         }
64         boolean isUsbConfigured = intent.getExtras() != null
65                 ? intent.getExtras().getBoolean(UsbManager.USB_CONFIGURED) : false;
66         if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
67             mConnected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED)
68                     || intent.getExtras().getBoolean(UsbManager.USB_HOST_CONNECTED);
69             long functions = UsbManager.FUNCTION_NONE;
70             if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_MTP)
71                     && intent.getExtras().getBoolean(UsbManager.USB_DATA_UNLOCKED, false)) {
72                 functions |= UsbManager.FUNCTION_MTP;
73             }
74             if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_PTP)
75                     && intent.getExtras().getBoolean(UsbManager.USB_DATA_UNLOCKED, false)) {
76                 functions |= UsbManager.FUNCTION_PTP;
77             }
78             if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_MIDI)) {
79                 functions |= UsbManager.FUNCTION_MIDI;
80             }
81             if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_RNDIS)) {
82                 functions |= UsbManager.FUNCTION_RNDIS;
83             }
84             if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_ACCESSORY)) {
85                 functions |= UsbManager.FUNCTION_ACCESSORY;
86             }
87             if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_NCM)) {
88                 functions |= UsbManager.FUNCTION_NCM;
89             }
90             if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_UVC)) {
91                 functions |= UsbManager.FUNCTION_UVC;
92             }
93             mFunctions = functions;
94             mDataRole = mUsbBackend.getDataRole();
95             mPowerRole = mUsbBackend.getPowerRole();
96         } else if (UsbManager.ACTION_USB_PORT_CHANGED.equals(intent.getAction())) {
97             UsbPortStatus portStatus = intent.getExtras()
98                     .getParcelable(UsbManager.EXTRA_PORT_STATUS);
99             if (portStatus != null) {
100                 mDataRole = portStatus.getCurrentDataRole();
101                 mPowerRole = portStatus.getCurrentPowerRole();
102             }
103         }
104         if (mUsbConnectionListener != null) {
105             mUsbConnectionListener.onUsbConnectionChanged(mConnected, mFunctions, mPowerRole,
106                     mDataRole, isUsbConfigured);
107         }
108     }
109 
register()110     public void register() {
111         if (!mListeningToUsbEvents) {
112             mConnected = false;
113             final IntentFilter intentFilter = new IntentFilter();
114             intentFilter.addAction(UsbManager.ACTION_USB_STATE);
115             intentFilter.addAction(UsbManager.ACTION_USB_PORT_CHANGED);
116             final Intent intent = mContext.registerReceiver(this, intentFilter);
117             // TODO b/77240599 use an api instead of sticky intent
118             if (intent != null) {
119                 onReceive(mContext, intent);
120             }
121             mListeningToUsbEvents = true;
122         }
123     }
124 
unregister()125     public void unregister() {
126         if (mListeningToUsbEvents) {
127             mContext.unregisterReceiver(this);
128             mListeningToUsbEvents = false;
129         }
130     }
131 
isConnected()132     public boolean isConnected() {
133         return mConnected;
134     }
135 
136     @Override
onResume()137     public void onResume() {
138         register();
139     }
140 
141     @Override
onPause()142     public void onPause() {
143         unregister();
144     }
145 
146     /**
147      * Interface definition for a callback to be invoked when usb connection is changed.
148      */
149     interface UsbConnectionListener {
onUsbConnectionChanged(boolean connected, long functions, int powerRole, int dataRole, boolean isUsbConfigured)150         void onUsbConnectionChanged(boolean connected, long functions, int powerRole, int dataRole,
151                 boolean isUsbConfigured);
152     }
153 }
154