From a8a9bd65bf5865d83ef44f54552ca39522bfbcf0 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 9 Oct 2012 15:36:59 -0700 Subject: [PATCH] Fix issue #7311376: Add API to allow apps to know if they are... ...running as the foreground user Add UserManager.isUserRunning() which is the public version of the existing method on ActivityManager. Also add UserManager.isUserRunningOrStopping() since that seems like it will be useful. And fix the internal function that returns the array of currently running users to not include stopped users. Change-Id: I84672fa8748fc027fd402729586b5603f640e498 --- api/17.txt | 2 + api/current.txt | 2 + core/java/android/app/ActivityManager.java | 2 +- .../android/app/ActivityManagerNative.java | 6 ++- core/java/android/app/IActivityManager.java | 2 +- core/java/android/os/UserManager.java | 35 ++++++++++++++ .../server/am/ActivityManagerService.java | 47 +++++++++++++++---- 7 files changed, 83 insertions(+), 13 deletions(-) diff --git a/api/17.txt b/api/17.txt index f8ad4d983a932..bebd566542773 100644 --- a/api/17.txt +++ b/api/17.txt @@ -16620,6 +16620,8 @@ package android.os { method public android.os.UserHandle getUserForSerialNumber(long); method public java.lang.String getUserName(); method public boolean isUserAGoat(); + method public boolean isUserRunning(android.os.UserHandle); + method public boolean isUserRunningOrStopping(android.os.UserHandle); } public abstract class Vibrator { diff --git a/api/current.txt b/api/current.txt index f8ad4d983a932..bebd566542773 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16620,6 +16620,8 @@ package android.os { method public android.os.UserHandle getUserForSerialNumber(long); method public java.lang.String getUserName(); method public boolean isUserAGoat(); + method public boolean isUserRunning(android.os.UserHandle); + method public boolean isUserRunningOrStopping(android.os.UserHandle); } public abstract class Vibrator { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 0eda6b4e090f3..594be68c3591d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1981,7 +1981,7 @@ public class ActivityManager { */ public boolean isUserRunning(int userid) { try { - return ActivityManagerNative.getDefault().isUserRunning(userid); + return ActivityManagerNative.getDefault().isUserRunning(userid, false); } catch (RemoteException e) { return false; } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index bb62c9e5d4cff..7492629b6dab6 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1608,7 +1608,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case IS_USER_RUNNING_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int userid = data.readInt(); - boolean result = isUserRunning(userid); + boolean orStopping = data.readInt() != 0; + boolean result = isUserRunning(userid, orStopping); reply.writeNoException(); reply.writeInt(result ? 1 : 0); return true; @@ -3865,11 +3866,12 @@ class ActivityManagerProxy implements IActivityManager return userInfo; } - public boolean isUserRunning(int userid) throws RemoteException { + public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(userid); + data.writeInt(orStopping ? 1 : 0); mRemote.transact(IS_USER_RUNNING_TRANSACTION, data, reply, 0); reply.readException(); boolean result = reply.readInt() != 0; diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index da844efbaec98..97250e9f71712 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -326,7 +326,7 @@ public interface IActivityManager extends IInterface { public boolean switchUser(int userid) throws RemoteException; public int stopUser(int userid, IStopUserCallback callback) throws RemoteException; public UserInfo getCurrentUser() throws RemoteException; - public boolean isUserRunning(int userid) throws RemoteException; + public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException; public int[] getRunningUserIds() throws RemoteException; public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 83a0c7800c2f9..2739cac369f99 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -16,6 +16,8 @@ package android.os; import com.android.internal.R; + +import android.app.ActivityManagerNative; import android.content.Context; import android.content.pm.UserInfo; import android.graphics.Bitmap; @@ -81,6 +83,39 @@ public class UserManager { return false; } + /** + * Return whether the given user is actively running. This means that + * the user is in the "started" state, not "stopped" -- it is currently + * allowed to run code through scheduled alarms, receiving broadcasts, + * etc. A started user may be either the current foreground user or a + * background user; the result here does not distinguish between the two. + * @param user The user to retrieve the running state for. + */ + public boolean isUserRunning(UserHandle user) { + try { + return ActivityManagerNative.getDefault().isUserRunning( + user.getIdentifier(), false); + } catch (RemoteException e) { + return false; + } + } + + /** + * Return whether the given user is actively running or stopping. + * This is like {@link #isUserRunning(UserHandle)}, but will also return + * true if the user had been running but is in the process of being stopped + * (but is not yet fully stopped, and still running some code). + * @param user The user to retrieve the running state for. + */ + public boolean isUserRunningOrStopping(UserHandle user) { + try { + return ActivityManagerNative.getDefault().isUserRunning( + user.getIdentifier(), true); + } catch (RemoteException e) { + return false; + } + } + /** * Returns the UserInfo object describing a specific user. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index c2aa3a5ecb00e..6b7e29ca776a9 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -3585,7 +3585,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "Failed trying to unstop package " + packageName + ": " + e); } - if (isUserRunningLocked(user)) { + if (isUserRunningLocked(user, false)) { forceStopPackageLocked(packageName, pkgUid); } } @@ -9338,6 +9338,12 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(" User #"); pw.print(uss.mHandle.getIdentifier()); pw.print(": "); uss.dump("", pw); } + pw.print(" mStartedUserArray: ["); + for (int i=0; i 0) pw.print(", "); + pw.print(mStartedUserArray[i]); + } + pw.println("]"); pw.print(" mUserLru: ["); for (int i=0; i 0) pw.print(", "); @@ -14134,10 +14140,12 @@ public final class ActivityManagerService extends ActivityManagerNative // so we can just fairly silently bring the user back from // the almost-dead. uss.mState = UserStartedState.STATE_RUNNING; + updateStartedUserArrayLocked(); } else if (uss.mState == UserStartedState.STATE_SHUTDOWN) { // This means ACTION_SHUTDOWN has been sent, so we will // need to treat this as a new boot of the user. uss.mState = UserStartedState.STATE_BOOTING; + updateStartedUserArrayLocked(); } mHandler.removeMessages(REPORT_USER_SWITCH_MSG); @@ -14318,8 +14326,7 @@ public final class ActivityManagerService extends ActivityManagerNative void finishUserSwitch(UserStartedState uss) { synchronized (this) { - if ((uss.mState == UserStartedState.STATE_BOOTING - || uss.mState == UserStartedState.STATE_SHUTDOWN) + if (uss.mState == UserStartedState.STATE_BOOTING && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) { uss.mState = UserStartedState.STATE_RUNNING; final int userId = uss.mHandle.getIdentifier(); @@ -14410,6 +14417,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (uss.mState != UserStartedState.STATE_STOPPING && uss.mState != UserStartedState.STATE_SHUTDOWN) { uss.mState = UserStartedState.STATE_STOPPING; + updateStartedUserArrayLocked(); long ident = Binder.clearCallingIdentity(); try { @@ -14514,7 +14522,7 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public boolean isUserRunning(int userId) { + public boolean isUserRunning(int userId, boolean orStopped) { if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: isUserRunning() from pid=" @@ -14525,13 +14533,19 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException(msg); } synchronized (this) { - return isUserRunningLocked(userId); + return isUserRunningLocked(userId, orStopped); } } - boolean isUserRunningLocked(int userId) { + boolean isUserRunningLocked(int userId, boolean orStopped) { UserStartedState state = mStartedUsers.get(userId); - return state != null && state.mState != UserStartedState.STATE_STOPPING + if (state == null) { + return false; + } + if (orStopped) { + return true; + } + return state.mState != UserStartedState.STATE_STOPPING && state.mState != UserStartedState.STATE_SHUTDOWN; } @@ -14552,9 +14566,24 @@ public final class ActivityManagerService extends ActivityManagerNative } private void updateStartedUserArrayLocked() { - mStartedUserArray = new int[mStartedUsers.size()]; + int num = 0; for (int i=0; i