1 /*
2  * Copyright (C) 2019 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.app.Notification;
21 import android.app.NotificationChannel;
22 import android.app.NotificationManager;
23 import android.app.Service;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.hardware.usb.UsbDevice;
29 import android.hardware.usb.UsbManager;
30 import android.os.Binder;
31 import android.os.UserHandle;
32 import android.os.UserManager;
33 import android.util.Log;
34 
35 import java.util.ArrayList;
36 
37 /**
38  * Starts the {@link UsbHostManagementActivity} for each connected usb device when {@link
39  * #onStartCommand(Intent, int, int)} is called. This is meant to allow this activity to start for
40  * connected devices on start up, without holding up processing of the LOCKED_BOOT_COMPLETED intent.
41  */
42 public class BootUsbService extends Service {
43     private static final String TAG = BootUsbService.class.getSimpleName();
44     private static final int NOTIFICATION_ID = 1;
45 
46     static final String USB_DEVICE_LIST_KEY = "usb_device_list";
47 
48     private ArrayList<UsbDevice> mDeviceList;
49 
50     private final UserUnlockedBroadcastReceiver mUserUnlockedBroadcastReceiver =
51             new UserUnlockedBroadcastReceiver();
52     private boolean mReceiverRegistered = false;
53 
54     private class UserUnlockedBroadcastReceiver extends BroadcastReceiver {
55         private int mStartId;
56 
setStartId(int startId)57         public void setStartId(int startId) {
58             mStartId = startId;
59         }
60 
onReceive(Context context, Intent intent)61         public void onReceive(Context context, Intent intent) {
62             // We could have been unregistered after receiving the intent but before processing it,
63             // so make sure we are still registered.
64             if (mReceiverRegistered) {
65                 unregisterUserUnlockedReceiver();
66                 processDevices(mStartId);
67             }
68         }
69     }
70 
71     @Override
onBind(Intent intent)72     public Binder onBind(Intent intent) {
73         return null;
74     }
75 
76     @Override
onCreate()77     public void onCreate() {
78         String notificationIdString =
79                 getResources().getString(R.string.usb_boot_service_notification);
80         NotificationManager notificationManager = getSystemService(NotificationManager.class);
81         NotificationChannel notificationChannel =
82                 new NotificationChannel(
83                         notificationIdString,
84                         "Car Usb Handler enumeration",
85                         NotificationManager.IMPORTANCE_NONE);
86         notificationManager.createNotificationChannel(notificationChannel);
87         Notification notification =
88                 new Notification.Builder(
89                         this, notificationIdString).setSmallIcon(R.drawable.ic_launcher).build();
90         // CarUsbHandler runs in the background, so startForeground must be explicitly called.
91         startForeground(NOTIFICATION_ID, notification);
92     }
93 
94     @Override
onStartCommand(Intent intent, int flags, int startId)95     public int onStartCommand(Intent intent, int flags, int startId) {
96         mDeviceList = intent.getParcelableArrayListExtra(USB_DEVICE_LIST_KEY);
97         UserManager userManager = getSystemService(UserManager.class);
98         if (!userManager.isUserUnlocked() && getUserId() != UserHandle.USER_SYSTEM) {
99             Log.i(TAG, "Waiting for user unlocked to process connected devices.");
100             registerUserUnlockedReceiver(startId, userManager);
101             return START_REDELIVER_INTENT;
102         }
103         processDevices(startId);
104         return START_NOT_STICKY;
105     }
106 
107     @Override
onDestroy()108     public void onDestroy() {
109         super.onDestroy();
110         unregisterUserUnlockedReceiver();
111     }
112 
registerUserUnlockedReceiver(int startId, UserManager userManager)113     private void registerUserUnlockedReceiver(int startId, UserManager userManager) {
114         mReceiverRegistered = true;
115         mUserUnlockedBroadcastReceiver.setStartId(startId);
116         registerReceiver(mUserUnlockedBroadcastReceiver, new IntentFilter(ACTION_USER_UNLOCKED),
117                 Context.RECEIVER_NOT_EXPORTED);
118         // in case the car was unlocked while the receiver was being registered
119         if (userManager.isUserUnlocked()) {
120             mUserUnlockedBroadcastReceiver.onReceive(this, new Intent(ACTION_USER_UNLOCKED));
121         }
122     }
123 
unregisterUserUnlockedReceiver()124     private void unregisterUserUnlockedReceiver() {
125         if (mReceiverRegistered) {
126             Log.d(TAG, "Unregistering USER_UNLOCKED broadcast");
127             unregisterReceiver(mUserUnlockedBroadcastReceiver);
128             mReceiverRegistered = false;
129         }
130     }
131 
processDevices(int startId)132     private void processDevices(int startId) {
133         Log.i(TAG, "Processing devices");
134         for (UsbDevice device : mDeviceList) {
135             Log.d(TAG, "Processing device: " + device.getProductName());
136             handle(this, device);
137         }
138         stopSelf(startId);
139     }
140 
handle(Context context, UsbDevice device)141     private void handle(Context context, UsbDevice device) {
142         Intent manageDevice = new Intent(context, UsbHostManagementActivity.class);
143         manageDevice.setAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
144         manageDevice.putExtra(UsbManager.EXTRA_DEVICE, device);
145         manageDevice.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
146         context.startActivityAsUser(manageDevice, UserHandle.CURRENT);
147     }
148 }
149