1 /* 2 * Copyright (C) 2020 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.userswitchmonitor; 17 18 import android.app.Notification; 19 import android.app.NotificationChannel; 20 import android.app.NotificationManager; 21 import android.app.Service; 22 import android.car.Car; 23 import android.car.user.CarUserManager; 24 import android.car.user.CarUserManager.UserLifecycleEvent; 25 import android.content.Intent; 26 import android.os.IBinder; 27 import android.util.Log; 28 29 import java.io.FileDescriptor; 30 import java.io.PrintWriter; 31 import java.util.ArrayList; 32 import java.util.List; 33 34 /** 35 * Service that users {@link CarUserManager.UserLifecycleEvent UserLifecycleEvents} to monitor 36 * user switches. 37 * 38 */ 39 public final class UserSwitchMonitorService extends Service { 40 41 static final String TAG = "UserSwitchMonitor"; 42 43 private static final String CMD_HELP = "help"; 44 private static final String CMD_REGISTER = "register"; 45 private static final String CMD_UNREGISTER = "unregister"; 46 47 private final Object mLock = new Object(); 48 49 private final int mUserId = android.os.Process.myUserHandle().getIdentifier(); 50 51 private final List<UserLifecycleEvent> mEvents = new ArrayList<>(); 52 53 private final CarUserManager.UserLifecycleListener mListener = (e) -> { 54 Log.d(TAG, "onEvent(" + mUserId + "): " + e); 55 synchronized (mLock) { 56 mEvents.add(e); 57 } 58 }; 59 60 private Car mCar; 61 private CarUserManager mCarUserManager; 62 private NotificationManager mNotificationManager; 63 64 @Override onCreate()65 public void onCreate() { 66 mCar = Car.createCar(this); 67 mCarUserManager = (CarUserManager) mCar.getCarManager(Car.CAR_USER_SERVICE); 68 registerListener(); 69 70 mNotificationManager = getSystemService(NotificationManager.class); 71 } 72 registerListener()73 private void registerListener() { 74 Log.d(TAG, "registerListener(): " + mListener); 75 mCarUserManager.addListener((r)-> r.run(), mListener); 76 } 77 78 @Override onStartCommand(Intent intent, int flags, int startId)79 public int onStartCommand(Intent intent, int flags, int startId) { 80 Log.d(TAG, "onStartCommand(" + mUserId + "): " + intent); 81 82 String channelId = "4815162342"; 83 String name = "UserSwitchMonitor"; 84 NotificationChannel channel = new NotificationChannel(channelId, name, 85 NotificationManager.IMPORTANCE_MIN); 86 mNotificationManager.createNotificationChannel(channel); 87 88 // Cannot use R.drawable because package name is different on app2 89 int iconResId = getApplicationInfo().icon; 90 startForeground(startId, 91 new Notification.Builder(this, channelId) 92 .setContentText(name) 93 .setContentTitle(name) 94 .setSmallIcon(iconResId) 95 .build()); 96 97 return super.onStartCommand(intent, flags, startId); 98 } 99 100 @Override onDestroy()101 public void onDestroy() { 102 Log.d(TAG, "onDestroy(" + mUserId + ")"); 103 104 unregisterListener(); 105 if (mCar != null && mCar.isConnected()) { 106 mCar.disconnect(); 107 } 108 super.onDestroy(); 109 } 110 unregisterListener()111 private void unregisterListener() { 112 Log.d(TAG, "unregisterListener(): " + mListener); 113 if (mCarUserManager != null) { 114 mCarUserManager.removeListener(mListener); 115 } else { 116 Log.w(TAG, "Cannot remove listener because manager is null"); 117 } 118 } 119 120 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)121 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 122 if (args != null && args.length > 0) { 123 executeCommand(pw, args); 124 return; 125 } 126 super.dump(fd, pw, args); 127 128 pw.printf("User id: %d\n", mUserId); 129 synchronized (mLock) { 130 if (mEvents.isEmpty()) { 131 pw.println("Did not receive any event yet"); 132 return; 133 } 134 int size = mEvents.size(); 135 String indent = " "; 136 pw.printf("Received %d events:\n", size); 137 for (int i = 0; i < size; i++) { 138 pw.printf("%s%d: %s\n", indent, (i + 1), mEvents.get(i)); 139 } 140 } 141 } 142 143 @Override onBind(Intent intent)144 public IBinder onBind(Intent intent) { 145 Log.d(TAG, "onBind(): " + intent); 146 return null; 147 } 148 executeCommand(PrintWriter pw, String[] args)149 private void executeCommand(PrintWriter pw, String[] args) { 150 String cmd = args[0]; 151 switch (cmd) { 152 case CMD_HELP: 153 cmdHelp(pw); 154 break; 155 case CMD_REGISTER: 156 cmdRegister(pw); 157 break; 158 case CMD_UNREGISTER: 159 cmdUnregister(pw); 160 break; 161 default: 162 pw.printf("invalid command: %s\n\n", cmd); 163 cmdHelp(pw); 164 } 165 } 166 cmdHelp(PrintWriter pw)167 private void cmdHelp(PrintWriter pw) { 168 pw.printf("Options:\n"); 169 pw.printf(" help: show this help\n"); 170 pw.printf(" register: register the service to receive events\n"); 171 pw.printf(" unregister: unregister the service from receiving events\n"); 172 } 173 cmdRegister(PrintWriter pw)174 private void cmdRegister(PrintWriter pw) { 175 pw.printf("registering listener %s\n", mListener); 176 runCmd(pw, () -> registerListener()); 177 } 178 cmdUnregister(PrintWriter pw)179 private void cmdUnregister(PrintWriter pw) { 180 pw.printf("unregistering listener %s\n", mListener); 181 runCmd(pw, () -> unregisterListener()); 182 } 183 runCmd(PrintWriter pw, Runnable r)184 private void runCmd(PrintWriter pw, Runnable r) { 185 try { 186 r.run(); 187 } catch (Exception e) { 188 Log.e(TAG, "error running command", e); 189 pw.printf("failed: %s\n", e); 190 } 191 } 192 } 193