• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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