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 android.car.usb.handler;
17 
18 import static android.content.Intent.ACTION_USER_UNLOCKED;
19 
20 import android.annotation.Nullable;
21 import android.app.Activity;
22 import android.content.BroadcastReceiver;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.PackageManager.NameNotFoundException;
30 import android.hardware.usb.UsbDevice;
31 import android.hardware.usb.UsbManager;
32 import android.os.Bundle;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.util.Log;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.view.Window;
39 import android.widget.AdapterView;
40 import android.widget.ArrayAdapter;
41 import android.widget.ImageView;
42 import android.widget.LinearLayout;
43 import android.widget.ListView;
44 import android.widget.TextView;
45 
46 import java.util.List;
47 
48 /**
49  * Activity to handle USB device attached.
50  * <p>
51  * When user plugs in USB device: a) Device was used before and user selected handler for it. In
52  * this case handler will be launched. b) Device has not handler assigned. In this case supported
53  * handlers will be captured, and user will be presented with choice to assign default handler.
54  * After that handler will be launched.
55  *
56  * <p>Note: Activity launched by the system and by {@link BootUsbService}.
57  */
58 public class UsbHostManagementActivity extends Activity {
59     private static final String TAG = UsbHostManagementActivity.class.getSimpleName();
60 
61     private HandlersAdapter mListAdapter;
62     private ListView mHandlersList;
63     private TextView mHandlerTitle;
64     private LinearLayout mUsbHandlersDialog;
65     private UsbHostController mController;
66     private PackageManager mPackageManager;
67 
68     private final ResolveBroadcastReceiver mResolveBroadcastReceiver
69             = new ResolveBroadcastReceiver();
70     private boolean mReceiverRegistered = false;
71 
unregisterResolveBroadcastReceiver()72     private void unregisterResolveBroadcastReceiver() {
73         if (mReceiverRegistered) {
74             Log.d(TAG, "Unregistering USER_UNLOCKED broadcast");
75             unregisterReceiver(mResolveBroadcastReceiver);
76             mReceiverRegistered = false;
77         }
78     }
79 
processDevice()80     private void processDevice() {
81         UsbDevice connectedDevice = getDevice();
82 
83         if (connectedDevice != null) {
84             Log.d(TAG, "Processing device: " + connectedDevice.getProductName());
85             mController.processDevice(connectedDevice);
86         } else {
87             Log.d(TAG, "Device not found.");
88             finish();
89         }
90     }
91 
92     private class ResolveBroadcastReceiver extends BroadcastReceiver {
onReceive(Context context, Intent intent)93         public void onReceive(Context context, Intent intent) {
94             // We could have been unregistered after receiving the intent but before processing it,
95             // so make sure we are still registered.
96             if (mReceiverRegistered) {
97                 unregisterResolveBroadcastReceiver();
98                 processDevice();
99             }
100         }
101     }
102 
103     private final AdapterView.OnItemClickListener mHandlerClickListener =
104             new AdapterView.OnItemClickListener() {
105         @Override
106         public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
107             UsbDeviceSettings settings = (UsbDeviceSettings) parent.getItemAtPosition(position);
108             settings.setDefaultHandler(true);
109             mController.applyDeviceSettings(settings);
110         }
111     };
112 
113     @Override
onCreate(Bundle savedInstanceState)114     public void onCreate(Bundle savedInstanceState) {
115         super.onCreate(savedInstanceState);
116         requestWindowFeature(Window.FEATURE_NO_TITLE);
117         setContentView(R.layout.usb_host);
118         mUsbHandlersDialog = findViewById(R.id.usb_handlers_dialog);
119         mHandlersList = findViewById(R.id.usb_handlers_list);
120         mHandlerTitle = findViewById(R.id.usb_handler_heading);
121         mListAdapter = new HandlersAdapter(this);
122         mHandlersList.setAdapter(mListAdapter);
123         mHandlersList.setOnItemClickListener(mHandlerClickListener);
124         mController = new UsbHostController(this, new UsbCallbacks());
125         mPackageManager = getPackageManager();
126         hideDialog();
127     }
128 
129     @Override
onDestroy()130     public void onDestroy() {
131         super.onDestroy();
132         mController.release();
133     }
134 
135     @Override
onPause()136     public void onPause() {
137         super.onPause();
138         unregisterResolveBroadcastReceiver();
139     }
140 
141     @Override
onResume()142     public void onResume() {
143         super.onResume();
144 
145         UserManager userManager = getSystemService(UserManager.class);
146         // Checks should pass if activity is started from BootUsbService, but necessary for system
147         // calls.
148         if (userManager.isUserUnlocked() || getUserId() == UserHandle.USER_SYSTEM) {
149             processDevice();
150         } else {
151             Log.d(TAG, "Waiting for user unlocked to process device.");
152             mReceiverRegistered = true;
153             registerReceiver(mResolveBroadcastReceiver, new IntentFilter(ACTION_USER_UNLOCKED),
154                     Context.RECEIVER_NOT_EXPORTED);
155             // in case the car was unlocked while the receiver was being registered
156             if (userManager.isUserUnlocked()) {
157                 mResolveBroadcastReceiver.onReceive(this, new Intent(ACTION_USER_UNLOCKED));
158             }
159         }
160     }
161 
hideDialog()162     private void hideDialog() {
163         setTheme(android.R.style.Theme_Translucent);
164         if (mUsbHandlersDialog != null) {
165             mUsbHandlersDialog.setVisibility(View.GONE);
166         }
167     }
168 
showDialog()169     private void showDialog() {
170         setTranslucent(false);
171         setTheme(android.R.style.Theme_DeviceDefault_Dialog);
172         if (mUsbHandlersDialog != null) {
173             mUsbHandlersDialog.setVisibility(View.VISIBLE);
174         }
175     }
176 
177     class UsbCallbacks implements UsbHostController.UsbHostControllerCallbacks {
178         private boolean mProcessing = false;
179 
180         @Override
shutdown()181         public void shutdown() {
182             runOnUiThread(new Runnable() {
183                 @Override
184                 public void run() {
185                     finish();
186                 }
187             });
188         }
189 
190         @Override
processingStarted()191         public void processingStarted() {
192             mProcessing = true;
193             runOnUiThread(new Runnable() {
194                 @Override
195                 public void run() {
196                     if (mProcessing && !mListAdapter.isEmpty()) {
197                         showDialog();
198                     }
199                 }
200             });
201         }
202 
203         @Override
titleChanged(final String title)204         public void titleChanged(final String title) {
205             runOnUiThread(() -> mHandlerTitle.setText(title));
206         }
207 
208         @Override
optionsUpdated(final List<UsbDeviceSettings> options)209         public void optionsUpdated(final List<UsbDeviceSettings> options) {
210             if (options != null && !options.isEmpty()) {
211                 runOnUiThread(new Runnable() {
212                     @Override
213                     public void run() {
214                         if (mProcessing) {
215                             showDialog();
216                         }
217                         mListAdapter.clear();
218                         mListAdapter.addAll(options);
219                     }
220                 });
221             }
222         }
223     }
224 
225     @Override
onNewIntent(Intent intent)226     public void onNewIntent(Intent intent) {
227         super.onNewIntent(intent);
228         setIntent(intent);
229     }
230 
231     @Nullable
getDevice()232     private UsbDevice getDevice() {
233         if (!UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {
234             return null;
235         }
236         return (UsbDevice) getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
237     }
238 
239     private class HandlersAdapter extends ArrayAdapter<UsbDeviceSettings> {
240         class HandlerHolder {
241             public TextView mAppName;
242             public ImageView mAppIcon;
243         }
244 
HandlersAdapter(Context context)245         HandlersAdapter(Context context) {
246             super(context, R.layout.usb_handler_row);
247         }
248 
249         @Override
getView(int position, View convertView, ViewGroup parent)250         public View getView(int position, View convertView, ViewGroup parent) {
251             View rowView = convertView;
252             if (rowView == null) {
253                 rowView = getLayoutInflater().inflate(R.layout.usb_handler_row, null);
254                 HandlerHolder holder = new HandlerHolder();
255                 holder.mAppName = (TextView) rowView.findViewById(R.id.usb_handler_title);
256                 holder.mAppIcon = (ImageView) rowView.findViewById(R.id.usb_handler_icon);
257                 rowView.setTag(holder);
258             }
259 
260             HandlerHolder holder = (HandlerHolder) rowView.getTag();
261             ComponentName handler = getItem(position).getHandler();
262 
263             try {
264                 ApplicationInfo appInfo =
265                         mPackageManager.getApplicationInfo(handler.getPackageName(), 0);
266                 holder.mAppName.setText(appInfo.loadLabel(mPackageManager));
267                 holder.mAppIcon.setImageDrawable(appInfo.loadIcon(mPackageManager));
268             } catch (NameNotFoundException e) {
269                 Log.e(TAG, "Handling package not found: " + handler.getPackageName());
270                 holder.mAppName.setText(handler.flattenToShortString());
271                 holder.mAppIcon.setImageResource(android.R.color.transparent);
272             }
273             return rowView;
274         }
275     }
276 }
277