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; 18 19 import static android.os.UserManager.DISALLOW_SHARE_LOCATION; 20 21 import static com.android.server.location.LocationManagerService.D; 22 import static com.android.server.location.LocationManagerService.TAG; 23 import static com.android.server.location.UserInfoHelper.UserListener.CURRENT_USER_CHANGED; 24 import static com.android.server.location.UserInfoHelper.UserListener.USER_STARTED; 25 import static com.android.server.location.UserInfoHelper.UserListener.USER_STOPPED; 26 27 import android.annotation.CallSuper; 28 import android.annotation.IntDef; 29 import android.annotation.Nullable; 30 import android.annotation.UserIdInt; 31 import android.app.ActivityManagerInternal; 32 import android.content.Context; 33 import android.os.Binder; 34 import android.os.UserHandle; 35 import android.os.UserManager; 36 import android.util.Log; 37 38 import com.android.internal.annotations.GuardedBy; 39 import com.android.internal.util.Preconditions; 40 import com.android.server.LocalServices; 41 42 import java.io.FileDescriptor; 43 import java.io.PrintWriter; 44 import java.lang.annotation.Retention; 45 import java.lang.annotation.RetentionPolicy; 46 import java.util.Arrays; 47 import java.util.Objects; 48 import java.util.concurrent.CopyOnWriteArrayList; 49 50 /** 51 * Provides accessors and listeners for all user info. 52 */ 53 public abstract class UserInfoHelper { 54 55 /** 56 * Listener for current user changes. 57 */ 58 public interface UserListener { 59 60 int CURRENT_USER_CHANGED = 1; 61 int USER_STARTED = 2; 62 int USER_STOPPED = 3; 63 64 @IntDef({CURRENT_USER_CHANGED, USER_STARTED, USER_STOPPED}) 65 @Retention(RetentionPolicy.SOURCE) 66 @interface UserChange {} 67 68 /** 69 * Called when something has changed about the given user. 70 */ onUserChanged(@serIdInt int userId, @UserChange int change)71 void onUserChanged(@UserIdInt int userId, @UserChange int change); 72 } 73 74 private final Context mContext; 75 private final CopyOnWriteArrayList<UserListener> mListeners; 76 77 @GuardedBy("this") 78 @Nullable private ActivityManagerInternal mActivityManagerInternal; 79 @GuardedBy("this") 80 @Nullable private UserManager mUserManager; 81 UserInfoHelper(Context context)82 public UserInfoHelper(Context context) { 83 mContext = context; 84 mListeners = new CopyOnWriteArrayList<>(); 85 } 86 87 /** Called when system is ready. */ 88 @CallSuper onSystemReady()89 public synchronized void onSystemReady() { 90 if (mActivityManagerInternal != null) { 91 return; 92 } 93 94 mActivityManagerInternal = Objects.requireNonNull( 95 LocalServices.getService(ActivityManagerInternal.class)); 96 mUserManager = mContext.getSystemService(UserManager.class); 97 } 98 99 /** 100 * Adds a listener for user changed events. Callbacks occur on an unspecified thread. 101 */ addListener(UserListener listener)102 public final void addListener(UserListener listener) { 103 mListeners.add(listener); 104 } 105 106 /** 107 * Removes a listener for user changed events. 108 */ removeListener(UserListener listener)109 public final void removeListener(UserListener listener) { 110 mListeners.remove(listener); 111 } 112 dispatchOnUserStarted(@serIdInt int userId)113 protected void dispatchOnUserStarted(@UserIdInt int userId) { 114 if (D) { 115 Log.d(TAG, "u" + userId + " started"); 116 } 117 118 for (UserListener listener : mListeners) { 119 listener.onUserChanged(userId, USER_STARTED); 120 } 121 } 122 dispatchOnUserStopped(@serIdInt int userId)123 protected void dispatchOnUserStopped(@UserIdInt int userId) { 124 if (D) { 125 Log.d(TAG, "u" + userId + " stopped"); 126 } 127 128 for (UserListener listener : mListeners) { 129 listener.onUserChanged(userId, USER_STOPPED); 130 } 131 } 132 dispatchOnCurrentUserChanged(@serIdInt int fromUserId, @UserIdInt int toUserId)133 protected void dispatchOnCurrentUserChanged(@UserIdInt int fromUserId, 134 @UserIdInt int toUserId) { 135 int[] fromUserIds = getProfileIds(fromUserId); 136 int[] toUserIds = getProfileIds(toUserId); 137 if (D) { 138 Log.d(TAG, "current user changed from u" + Arrays.toString(fromUserIds) + " to u" 139 + Arrays.toString(toUserIds)); 140 } 141 142 for (UserListener listener : mListeners) { 143 for (int userId : fromUserIds) { 144 listener.onUserChanged(userId, CURRENT_USER_CHANGED); 145 } 146 } 147 148 for (UserListener listener : mListeners) { 149 for (int userId : toUserIds) { 150 listener.onUserChanged(userId, CURRENT_USER_CHANGED); 151 } 152 } 153 } 154 155 /** 156 * Returns an array of current user ids. This will always include the current user, and will 157 * also include any profiles of the current user. The caller must never mutate the returned 158 * array. 159 */ getCurrentUserIds()160 public int[] getCurrentUserIds() { 161 synchronized (this) { 162 if (mActivityManagerInternal == null) { 163 return new int[] {}; 164 } 165 } 166 167 long identity = Binder.clearCallingIdentity(); 168 try { 169 return mActivityManagerInternal.getCurrentProfileIds(); 170 } finally { 171 Binder.restoreCallingIdentity(identity); 172 } 173 } 174 175 /** 176 * Returns true if the given user id is either the current user or a profile of the current 177 * user. 178 */ isCurrentUserId(@serIdInt int userId)179 public boolean isCurrentUserId(@UserIdInt int userId) { 180 synchronized (this) { 181 if (mActivityManagerInternal == null) { 182 return false; 183 } 184 } 185 186 long identity = Binder.clearCallingIdentity(); 187 try { 188 return mActivityManagerInternal.isCurrentProfile(userId); 189 } finally { 190 Binder.restoreCallingIdentity(identity); 191 } 192 } 193 getProfileIds(@serIdInt int userId)194 private int[] getProfileIds(@UserIdInt int userId) { 195 synchronized (this) { 196 Preconditions.checkState(mUserManager != null); 197 } 198 199 long identity = Binder.clearCallingIdentity(); 200 try { 201 return mUserManager.getEnabledProfileIds(userId); 202 } finally { 203 Binder.restoreCallingIdentity(identity); 204 } 205 } 206 207 /** 208 * Dump info for debugging. 209 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)210 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 211 int[] currentUserProfiles = getCurrentUserIds(); 212 pw.println("current users: " + Arrays.toString(currentUserProfiles)); 213 UserManager userManager = mContext.getSystemService(UserManager.class); 214 if (userManager != null) { 215 for (int userId : currentUserProfiles) { 216 if (userManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION, 217 UserHandle.of(userId))) { 218 pw.println(" u" + userId + " restricted"); 219 } 220 } 221 } 222 } 223 } 224