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 
17 package com.android.server.location.injector;
18 
19 import static com.android.server.location.LocationManagerService.D;
20 import static com.android.server.location.LocationManagerService.TAG;
21 import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
22 import static com.android.server.location.injector.UserInfoHelper.UserListener.CURRENT_USER_CHANGED;
23 import static com.android.server.location.injector.UserInfoHelper.UserListener.USER_STARTED;
24 import static com.android.server.location.injector.UserInfoHelper.UserListener.USER_STOPPED;
25 import static com.android.server.location.injector.UserInfoHelper.UserListener.USER_VISIBILITY_CHANGED;
26 
27 import android.annotation.IntDef;
28 import android.annotation.UserIdInt;
29 import android.util.IndentingPrintWriter;
30 import android.util.Log;
31 
32 import java.io.FileDescriptor;
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.Arrays;
36 import java.util.concurrent.CopyOnWriteArrayList;
37 
38 /**
39  * Provides accessors and listeners for all user info.
40  */
41 public abstract class UserInfoHelper {
42 
43     /**
44      * Listener for current user changes.
45      */
46     public interface UserListener {
47 
48         int CURRENT_USER_CHANGED = 1;
49         int USER_STARTED = 2;
50         int USER_STOPPED = 3;
51         int USER_VISIBILITY_CHANGED = 4;
52 
53         @IntDef({CURRENT_USER_CHANGED, USER_STARTED, USER_STOPPED, USER_VISIBILITY_CHANGED})
54         @Retention(RetentionPolicy.SOURCE)
55         @interface UserChange {}
56 
57         /**
58          * Called when something has changed about the given user.
59          */
onUserChanged(@serIdInt int userId, @UserChange int change)60         void onUserChanged(@UserIdInt int userId, @UserChange int change);
61     }
62 
63     private final CopyOnWriteArrayList<UserListener> mListeners;
64 
UserInfoHelper()65     public UserInfoHelper() {
66         mListeners = new CopyOnWriteArrayList<>();
67     }
68 
69     /**
70      * Adds a listener for user changed events. Callbacks occur on an unspecified thread.
71      */
addListener(UserListener listener)72     public final void addListener(UserListener listener) {
73         mListeners.add(listener);
74     }
75 
76     /**
77      * Removes a listener for user changed events.
78      */
removeListener(UserListener listener)79     public final void removeListener(UserListener listener) {
80         mListeners.remove(listener);
81     }
82 
dispatchOnUserStarted(@serIdInt int userId)83     protected final void dispatchOnUserStarted(@UserIdInt int userId) {
84         if (D) {
85             Log.d(TAG, "u" + userId + " started");
86         }
87 
88         for (UserListener listener : mListeners) {
89             listener.onUserChanged(userId, USER_STARTED);
90         }
91     }
92 
dispatchOnUserStopped(@serIdInt int userId)93     protected final void dispatchOnUserStopped(@UserIdInt int userId) {
94         if (D) {
95             Log.d(TAG, "u" + userId + " stopped");
96         }
97 
98         for (UserListener listener : mListeners) {
99             listener.onUserChanged(userId, USER_STOPPED);
100         }
101     }
102 
dispatchOnCurrentUserChanged(@serIdInt int fromUserId, @UserIdInt int toUserId)103     protected final void dispatchOnCurrentUserChanged(@UserIdInt int fromUserId,
104             @UserIdInt int toUserId) {
105         int[] fromUserIds = getProfileIds(fromUserId);
106         int[] toUserIds = getProfileIds(toUserId);
107         if (D) {
108             Log.d(TAG, "current user changed from u" + Arrays.toString(fromUserIds) + " to u"
109                     + Arrays.toString(toUserIds));
110         }
111         EVENT_LOG.logUserSwitched(fromUserId, toUserId);
112 
113         for (UserListener listener : mListeners) {
114             for (int userId : fromUserIds) {
115                 listener.onUserChanged(userId, CURRENT_USER_CHANGED);
116             }
117         }
118 
119         for (UserListener listener : mListeners) {
120             for (int userId : toUserIds) {
121                 listener.onUserChanged(userId, CURRENT_USER_CHANGED);
122             }
123         }
124     }
125 
dispatchOnVisibleUserChanged(@serIdInt int userId, boolean visible)126     protected final void dispatchOnVisibleUserChanged(@UserIdInt int userId, boolean visible) {
127         if (D) {
128             Log.d(TAG, "visibility of u" + userId + " changed to "
129                     + (visible ? "visible" : "invisible"));
130         }
131         EVENT_LOG.logUserVisibilityChanged(userId, visible);
132 
133         for (UserListener listener : mListeners) {
134             listener.onUserChanged(userId, USER_VISIBILITY_CHANGED);
135         }
136     }
137 
138     /**
139      * Returns an array of running user ids. This will include all running users, and will also
140      * include any profiles of the running users. The caller must never mutate the returned
141      * array.
142      */
getRunningUserIds()143     public abstract int[] getRunningUserIds();
144 
145     /**
146      * Returns {@code true} if the given user id is either the current user or a profile of the
147      * current user.
148      */
isCurrentUserId(@serIdInt int userId)149     public abstract boolean isCurrentUserId(@UserIdInt int userId);
150 
151     /**
152      * Returns the current user id. Where possible, prefer to use {@link #isCurrentUserId(int)}
153      * instead, as that method has more flexibility.
154      */
getCurrentUserId()155     public abstract @UserIdInt int getCurrentUserId();
156 
157     /**
158      * Returns {@code true} if the user is visible.
159      *
160      * <p>The visibility of a user is defined by {@link android.os.UserManager#isUserVisible()}.
161      */
isVisibleUserId(@serIdInt int userId)162     public abstract boolean isVisibleUserId(@UserIdInt int userId);
163 
getProfileIds(@serIdInt int userId)164     protected abstract int[] getProfileIds(@UserIdInt int userId);
165 
166     /**
167      * Dump info for debugging.
168      */
dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args)169     public abstract void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args);
170 }
171