diff options
| author | 2012-08-27 19:18:31 -0700 | |
|---|---|---|
| committer | 2012-08-28 16:30:55 -0700 | |
| commit | 80a4af2bbc6af42ae605e454bf89558e564f5244 (patch) | |
| tree | 3cd054c0e9576c9fda33fc36670ef10458f71ee8 /services/java/com | |
| parent | def8b0f011b5b2b02235063d5021ddfd58aa0baf (diff) | |
Start implementing concept of "running" users.
The activity manager now keeps track of which users are running.
Initially, only user 0 is running.
When you switch to another user, that user is started so it is
running. It is only at this point that BOOT_COMPLETED is sent
for that user and it is allowed to execute anything.
You can stop any user except user 0, which brings it back to the
same state as when you first boot the device. This is also used
to be able to more cleaning delete a user, by first stopping it
before removing its data.
There is a new broadcast ACTION_USER_STOPPED sent when a user is
stopped; system services need to handle this like they currently
handle ACTION_PACKAGE_RESTARTED when individual packages are
restarted.
Change-Id: I89adbd7cbaf4a0bb72ea201385f93477f40a4119
Diffstat (limited to 'services/java/com')
7 files changed, 427 insertions, 89 deletions
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 32ac8e1506cd..9b7be028cfe1 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -34,6 +34,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserHandle; import android.os.WorkSource; import android.text.TextUtils; import android.text.format.Time; @@ -303,7 +304,7 @@ class AlarmManagerService extends IAlarmManager.Stub { } } } - + public void removeLocked(String packageName) { removeLocked(mRtcWakeupAlarms, packageName); removeLocked(mRtcAlarms, packageName); @@ -327,6 +328,29 @@ class AlarmManagerService extends IAlarmManager.Stub { } } } + + public void removeUserLocked(int userHandle) { + removeUserLocked(mRtcWakeupAlarms, userHandle); + removeUserLocked(mRtcAlarms, userHandle); + removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle); + removeUserLocked(mElapsedRealtimeAlarms, userHandle); + } + + private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) { + if (alarmList.size() <= 0) { + return; + } + + // iterator over the list removing any it where the intent match + Iterator<Alarm> it = alarmList.iterator(); + + while (it.hasNext()) { + Alarm alarm = it.next(); + if (UserHandle.getUserId(alarm.operation.getTargetUid()) == userHandle) { + it.remove(); + } + } + } public boolean lookForPackageLocked(String packageName) { return lookForPackageLocked(mRtcWakeupAlarms, packageName) @@ -822,6 +846,7 @@ class AlarmManagerService extends IAlarmManager.Stub { // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + sdFilter.addAction(Intent.ACTION_USER_STOPPED); mContext.registerReceiver(this, sdFilter); } @@ -841,6 +866,11 @@ class AlarmManagerService extends IAlarmManager.Stub { return; } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + } else if (Intent.ACTION_USER_STOPPED.equals(action)) { + int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + if (userHandle >= 0) { + removeUserLocked(userHandle); + } } else { if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index d6fed39afad3..9b61ec480bd7 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -544,6 +544,11 @@ public class NotificationManagerService extends INotificationManager.Stub mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals( TelephonyManager.EXTRA_STATE_OFFHOOK)); updateNotificationPulse(); + } else if (action.equals(Intent.ACTION_USER_STOPPED)) { + int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + if (userHandle >= 0) { + cancelAllNotificationsUser(userHandle); + } } else if (action.equals(Intent.ACTION_USER_PRESENT)) { // turn off LED when user passes through lock screen mNotificationLight.turnOff(); @@ -619,6 +624,7 @@ public class NotificationManagerService extends INotificationManager.Stub filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); filter.addAction(Intent.ACTION_USER_PRESENT); + filter.addAction(Intent.ACTION_USER_STOPPED); mContext.registerReceiver(mIntentReceiver, filter); IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -1249,6 +1255,29 @@ public class NotificationManagerService extends INotificationManager.Stub } } + /** + * Cancels all notifications from a given user. + */ + boolean cancelAllNotificationsUser(int userHandle) { + synchronized (mNotificationList) { + final int N = mNotificationList.size(); + boolean canceledSomething = false; + for (int i = N-1; i >= 0; --i) { + NotificationRecord r = mNotificationList.get(i); + if (UserHandle.getUserId(r.uid) != userHandle) { + continue; + } + canceledSomething = true; + mNotificationList.remove(i); + cancelNotificationLocked(r, false); + } + if (canceledSomething) { + updateLightsLocked(); + } + return canceledSomething; + } + } + @Deprecated public void cancelNotification(String pkg, int id) { cancelNotificationWithTag(pkg, null /* tag */, id); diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index ca7faa2de622..b0dfa800a45e 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -989,6 +989,17 @@ public class ActiveServices { // restarting state. mRestartingServices.remove(r); + // Make sure that the user who owns this service is started. If not, + // we don't want to allow it to run. + if (mAm.mStartedUsers.get(r.userId) == null) { + Slog.w(TAG, "Unable to launch app " + + r.appInfo.packageName + "/" + + r.appInfo.uid + " for service " + + r.intent.getIntent() + ": user " + r.userId + " is stopped"); + bringDownServiceLocked(r, true); + return false; + } + // Service is now being launched, its package can't be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( @@ -1509,7 +1520,7 @@ public class ActiveServices { boolean didSomething = false; ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); for (ServiceRecord service : mServiceMap.getAllServices(userId)) { - if (service.packageName.equals(name) + if ((name == null || service.packageName.equals(name)) && (service.app == null || evenPersistent || !service.app.persistent)) { if (!doit) { return true; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 6e4759d31ab7..6467b7ac8e98 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -46,6 +46,7 @@ import android.app.IInstrumentationWatcher; import android.app.INotificationManager; import android.app.IProcessObserver; import android.app.IServiceConnection; +import android.app.IStopUserCallback; import android.app.IThumbnailReceiver; import android.app.Instrumentation; import android.app.Notification; @@ -428,6 +429,11 @@ public final class ActivityManagerService extends ActivityManagerNative long mPreviousProcessVisibleTime; /** + * Which uses have been started, so are allowed to run code. + */ + final SparseArray<UserStartedState> mStartedUsers = new SparseArray<UserStartedState>(); + + /** * Packages that the user has asked to have run in screen size * compatibility mode instead of filling the screen. */ @@ -791,7 +797,6 @@ public final class ActivityManagerService extends ActivityManagerNative static ActivityThread mSystemThread; private int mCurrentUserId; - private SparseIntArray mLoggedInUsers = new SparseIntArray(5); private UserManager mUserManager; private final class AppDeathRecipient implements IBinder.DeathRecipient { @@ -1506,6 +1511,9 @@ public final class ActivityManagerService extends ActivityManagerNative systemDir, "usagestats").toString()); mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0")); + // User 0 is the first and only user that runs at boot. + mStartedUsers.put(0, new UserStartedState(new UserHandle(0), true)); + GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", ConfigurationInfo.GL_ES_VERSION_UNDEFINED); @@ -2095,7 +2103,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - boolean startHomeActivityLocked(int userId) { + boolean startHomeActivityLocked(int userId, UserStartedState startingUser) { if (mHeadless) { // Added because none of the other calls to ensureBootCompleted seem to fire // when running headless. @@ -2135,6 +2143,9 @@ public final class ActivityManagerService extends ActivityManagerNative null, null, 0, 0, 0, 0, null, false, null); } } + if (startingUser != null) { + mMainStack.addStartingUserLocked(startingUser); + } return true; } @@ -3454,7 +3465,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "Invalid packageName: " + packageName); return; } - killPackageProcessesLocked(packageName, pkgUid, + killPackageProcessesLocked(packageName, pkgUid, -1, ProcessList.SERVICE_ADJ, false, true, true, false, "kill background"); } } finally { @@ -3650,7 +3661,8 @@ public final class ActivityManagerService extends ActivityManagerNative } private void forceStopPackageLocked(final String packageName, int uid) { - forceStopPackageLocked(packageName, uid, false, false, true, false, UserHandle.getUserId(uid)); + forceStopPackageLocked(packageName, uid, false, false, true, false, + UserHandle.getUserId(uid)); Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, Uri.fromParts("package", packageName, null)); if (!mProcessesReady) { @@ -3662,16 +3674,27 @@ public final class ActivityManagerService extends ActivityManagerNative false, false, MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid)); } - + + private void forceStopUserLocked(int userId) { + forceStopPackageLocked(null, -1, false, false, true, false, userId); + Intent intent = new Intent(Intent.ACTION_USER_STOPPED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, + false, false, + MY_PID, Process.SYSTEM_UID, userId); + } + private final boolean killPackageProcessesLocked(String packageName, int uid, - int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit, - boolean evenPersistent, String reason) { + int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart, + boolean doit, boolean evenPersistent, String reason) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); // Remove all processes this package may have touched: all with the // same UID (except for the system or root user), and all whose name // matches the package name. - final String procNamePrefix = packageName + ":"; + final String procNamePrefix = packageName != null ? (packageName + ":") : null; for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { final int NA = apps.size(); for (int ia=0; ia<NA; ia++) { @@ -3684,6 +3707,18 @@ public final class ActivityManagerService extends ActivityManagerNative if (doit) { procs.add(app); } + // If no package is specified, we call all processes under the + // give user id. + } else if (packageName == null) { + if (app.userId == userId) { + if (app.setAdj >= minOomAdj) { + if (!doit) { + return true; + } + app.removed = true; + procs.add(app); + } + } // If uid is specified and the uid and process name match // Or, the uid is not specified and the process name matches } else if (((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid) @@ -3714,7 +3749,7 @@ public final class ActivityManagerService extends ActivityManagerNative int i; int N; - if (uid < 0) { + if (uid < 0 && name != null) { try { uid = AppGlobals.getPackageManager().getPackageUid(name, userId); } catch (RemoteException e) { @@ -3722,24 +3757,45 @@ public final class ActivityManagerService extends ActivityManagerNative } if (doit) { - Slog.i(TAG, "Force stopping package " + name + " uid=" + uid); + if (name != null) { + Slog.i(TAG, "Force stopping package " + name + " uid=" + uid); + } else { + Slog.i(TAG, "Force stopping user " + userId); + } Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator(); while (badApps.hasNext()) { SparseArray<Long> ba = badApps.next(); - if (ba.get(uid) != null) { + for (i=ba.size()-1; i>=0; i--) { + boolean remove = false; + final int entUid = ba.keyAt(i); + if (name != null) { + if (entUid == uid) { + remove = true; + } + } else if (UserHandle.getUserId(entUid) == userId) { + remove = true; + } + if (remove) { + ba.removeAt(i); + } + } + if (ba.size() == 0) { badApps.remove(); } } } - - boolean didSomething = killPackageProcessesLocked(name, uid, -100, - callerWillRestart, false, doit, evenPersistent, "force stop"); + + boolean didSomething = killPackageProcessesLocked(name, uid, + name == null ? userId : -1 , -100, callerWillRestart, false, + doit, evenPersistent, + name == null ? ("force stop user " + userId) : ("force stop " + name)); TaskRecord lastTask = null; for (i=0; i<mMainStack.mHistory.size(); i++) { ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); - final boolean samePackage = r.packageName.equals(name); + final boolean samePackage = r.packageName.equals(name) + || (name == null && r.userId == userId); if (r.userId == userId && (samePackage || r.task == lastTask) && (r.app == null || evenPersistent || !r.app.persistent)) { @@ -3776,7 +3832,7 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>(); for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(userId).values()) { - if (provider.info.packageName.equals(name) + if ((name == null || provider.info.packageName.equals(name)) && (provider.proc == null || evenPersistent || !provider.proc.persistent)) { if (!doit) { return true; @@ -3792,7 +3848,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (doit) { - if (purgeCache) { + if (purgeCache && name != null) { AttributeCache ac = AttributeCache.instance(); if (ac != null) { ac.removePackage(name); @@ -4197,15 +4253,6 @@ public final class ActivityManagerService extends ActivityManagerNative } }, pkgFilter); - IntentFilter userFilter = new IntentFilter(); - userFilter.addAction(Intent.ACTION_USER_REMOVED); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - onUserRemoved(intent); - } - }, userFilter); - synchronized (this) { // Ensure that any processes we had put on hold are now started // up. @@ -4227,13 +4274,17 @@ public final class ActivityManagerService extends ActivityManagerNative // Tell anyone interested that we are done booting! SystemProperties.set("sys.boot_completed", "1"); SystemProperties.set("dev.bootcomplete", "1"); - List<UserInfo> users = getUserManager().getUsers(); - for (UserInfo user : users) { - broadcastIntentLocked(null, null, - new Intent(Intent.ACTION_BOOT_COMPLETED, null), - null, null, 0, null, null, - android.Manifest.permission.RECEIVE_BOOT_COMPLETED, - false, false, MY_PID, Process.SYSTEM_UID, user.id); + for (int i=0; i<mStartedUsers.size(); i++) { + UserStartedState uss = mStartedUsers.valueAt(i); + if (uss.mState == UserStartedState.STATE_BOOTING) { + uss.mState = UserStartedState.STATE_RUNNING; + broadcastIntentLocked(null, null, + new Intent(Intent.ACTION_BOOT_COMPLETED, null), + null, null, 0, null, null, + android.Manifest.permission.RECEIVE_BOOT_COMPLETED, + false, false, MY_PID, Process.SYSTEM_UID, + mStartedUsers.keyAt(i)); + } } } } @@ -6296,6 +6347,16 @@ public final class ActivityManagerService extends ActivityManagerNative "Attempt to launch content provider before system ready"); } + // Make sure that the user who owns this provider is started. If not, + // we don't want to allow it to run. + if (mStartedUsers.get(userId) == null) { + Slog.w(TAG, "Unable to launch app " + + cpi.applicationInfo.packageName + "/" + + cpi.applicationInfo.uid + " for provider " + + name + ": user " + userId + " is stopped"); + return null; + } + ComponentName comp = new ComponentName(cpi.packageName, cpi.name); cpr = mProviderMap.getProviderByClass(comp, userId); final boolean firstClass = cpr == null; @@ -9047,6 +9108,13 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(); + pw.println(" mStartedUsers:"); + for (int i=0; i<mStartedUsers.size(); i++) { + UserStartedState uss = mStartedUsers.valueAt(i); + pw.print(" User #"); pw.print(uss.mHandle.getIdentifier()); + pw.println(":"); + uss.dump(" ", pw); + } pw.println(" mHomeProcess: " + mHomeProcess); pw.println(" mPreviousProcess: " + mPreviousProcess); if (dumpAll) { @@ -11106,6 +11174,14 @@ public final class ActivityManagerService extends ActivityManagerNative } } + // Make sure that the user who is receiving this broadcast is started + // If not, we will just skip it. + if (mStartedUsers.get(userId) == null) { + Slog.w(TAG, "Skipping broadcast of " + intent + + ": user " + userId + " is stopped"); + return ActivityManager.BROADCAST_SUCCESS; + } + // Handle special intents: if this broadcast is from the package // manager about a package being removed, we need to remove all of // its activities from the history stack. @@ -11648,7 +11724,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.instrumentationProfileFile = null; app.instrumentationArguments = null; - forceStopPackageLocked(app.processName, -1, false, false, true, true, app.userId); + forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, app.userId); } public void finishInstrumentation(IApplicationThread target, @@ -13490,75 +13566,174 @@ public final class ActivityManagerService extends ActivityManagerNative // Multi-user methods + @Override public boolean switchUser(int userId) { - final int callingUid = Binder.getCallingUid(); - if (callingUid != 0 && callingUid != Process.myUid()) { - Slog.e(TAG, "Trying to switch user from unauthorized app"); - return false; + if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: switchUser() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); } - if (mCurrentUserId == userId) - return true; - synchronized (this) { - // Check if user is already logged in, otherwise check if user exists first before - // adding to the list of logged in users. - if (mLoggedInUsers.indexOfKey(userId) < 0) { - if (!userExists(userId)) { - return false; - } - mLoggedInUsers.append(userId, userId); + if (mCurrentUserId == userId) { + return true; + } + + // If the user we are switching to is not currently started, then + // we need to start it now. + if (mStartedUsers.get(userId) == null) { + mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false)); } mCurrentUserId = userId; boolean haveActivities = mMainStack.switchUser(userId); if (!haveActivities) { - startHomeActivityLocked(userId); + startHomeActivityLocked(userId, mStartedUsers.get(userId)); } - } - // Inform of user switch - Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS); + long ident = Binder.clearCallingIdentity(); + try { + // Inform of user switch + Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS); + } finally { + Binder.restoreCallingIdentity(ident); + } return true; } - @Override - public UserInfo getCurrentUser() throws RemoteException { - final int callingUid = Binder.getCallingUid(); - if (callingUid != 0 && callingUid != Process.myUid()) { - Slog.e(TAG, "Trying to get user from unauthorized app"); - return null; + void finishUserSwitch(UserStartedState uss) { + synchronized (this) { + if (uss.mState == UserStartedState.STATE_BOOTING + && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) { + uss.mState = UserStartedState.STATE_RUNNING; + broadcastIntentLocked(null, null, + new Intent(Intent.ACTION_BOOT_COMPLETED, null), + null, null, 0, null, null, + android.Manifest.permission.RECEIVE_BOOT_COMPLETED, + false, false, MY_PID, Process.SYSTEM_UID, uss.mHandle.getIdentifier()); + } } - return getUserManager().getUserInfo(mCurrentUserId); } - private void onUserRemoved(Intent intent) { - int extraUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (extraUserId < 1) return; - - // Kill all the processes for the user - ArrayList<Pair<String, Integer>> pkgAndUids = new ArrayList<Pair<String,Integer>>(); + @Override + public int stopUser(final int userId, final IStopUserCallback callback) { + if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: switchUser() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + if (userId <= 0) { + throw new IllegalArgumentException("Can't stop primary user " + userId); + } synchronized (this) { - HashMap<String,SparseArray<ProcessRecord>> map = mProcessNames.getMap(); - for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) { - SparseArray<ProcessRecord> uids = uidMap.getValue(); - for (int i = 0; i < uids.size(); i++) { - if (UserHandle.getUserId(uids.keyAt(i)) == extraUserId) { - pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i))); - } + if (mCurrentUserId == userId) { + return ActivityManager.USER_OP_IS_CURRENT; + } + + final UserStartedState uss = mStartedUsers.get(userId); + if (uss == null) { + // User is not started, nothing to do... but we do need to + // callback if requested. + if (callback != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + try { + callback.userStopped(userId); + } catch (RemoteException e) { + } + } + }); + } + return ActivityManager.USER_OP_SUCCESS; + } + + if (callback != null) { + uss.mStopCallbacks.add(callback); + } + + if (uss.mState != UserStartedState.STATE_STOPPING) { + uss.mState = UserStartedState.STATE_STOPPING; + + long ident = Binder.clearCallingIdentity(); + try { + // Inform of user switch + Intent intent = new Intent(Intent.ACTION_SHUTDOWN); + final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, String data, + Bundle extras, boolean ordered, boolean sticky) { + finishUserStop(uss); + } + }; + broadcastIntentLocked(null, null, intent, + null, resultReceiver, 0, null, null, null, + true, false, MY_PID, Process.SYSTEM_UID, userId); + } finally { + Binder.restoreCallingIdentity(ident); } } + } + + return ActivityManager.USER_OP_SUCCESS; + } + + void finishUserStop(UserStartedState uss) { + final int userId = uss.mHandle.getIdentifier(); + boolean stopped; + ArrayList<IStopUserCallback> callbacks; + synchronized (this) { + callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks); + if (uss.mState != UserStartedState.STATE_STOPPING + || mStartedUsers.get(userId) != uss) { + stopped = false; + } else { + stopped = true; + // User can no longer run. + mStartedUsers.remove(userId); + + // Clean up all state and processes associated with the user. + // Kill all the processes for the user. + forceStopUserLocked(userId); + } + } - for (Pair<String,Integer> pkgAndUid : pkgAndUids) { - forceStopPackageLocked(pkgAndUid.first, pkgAndUid.second, - false, false, true, true, extraUserId); + for (int i=0; i<callbacks.size(); i++) { + try { + if (stopped) callbacks.get(i).userStopped(userId); + else callbacks.get(i).userStopAborted(userId); + } catch (RemoteException e) { } } } + @Override + public UserInfo getCurrentUser() { + if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: getCurrentUser() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + synchronized (this) { + return getUserManager().getUserInfo(mCurrentUserId); + } + } + private boolean userExists(int userId) { UserInfo user = getUserManager().getUserInfo(userId); return user != null; diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index ccea41aacc25..a389edc25f59 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -211,7 +211,10 @@ final class ActivityStack { */ final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible = new ArrayList<IActivityManager.WaitResult>(); - + + final ArrayList<UserStartedState> mStartingUsers + = new ArrayList<UserStartedState>(); + /** * Set when the system is going to sleep, until we have * successfully paused the current activity and released our wake lock. @@ -1397,7 +1400,7 @@ final class ActivityStack { // Launcher... if (mMainStack) { ActivityOptions.abort(options); - return mService.startHomeActivityLocked(0); + return mService.startHomeActivityLocked(0, null); } } @@ -1427,7 +1430,16 @@ final class ActivityStack { ActivityOptions.abort(options); return false; } - + + // Make sure that the user who owns this activity is started. If not, + // we will just leave it as is because someone should be bringing + // another user's activities to the top of the stack. + if (mService.mStartedUsers.get(next.userId) == null) { + Slog.w(TAG, "Skipping resume of top activity " + next + + ": user " + next.userId + " is stopped"); + return false; + } + // The activity may be waiting for stop, but that is no longer // appropriate for it. mStoppingActivities.remove(next); @@ -1494,7 +1506,7 @@ final class ActivityStack { Slog.d(TAG, "no-history finish of " + last + " on new resume"); } requestFinishActivityLocked(last.appToken, Activity.RESULT_CANCELED, null, - "no-history"); + "no-history"); } } @@ -3414,6 +3426,7 @@ final class ActivityStack { ArrayList<ActivityRecord> stops = null; ArrayList<ActivityRecord> finishes = null; ArrayList<ActivityRecord> thumbnails = null; + ArrayList<UserStartedState> startingUsers = null; int NS = 0; int NF = 0; int NT = 0; @@ -3495,6 +3508,10 @@ final class ActivityStack { booting = mService.mBooting; mService.mBooting = false; } + if (mStartingUsers.size() > 0) { + startingUsers = new ArrayList<UserStartedState>(mStartingUsers); + mStartingUsers.clear(); + } } int i; @@ -3539,6 +3556,10 @@ final class ActivityStack { if (booting) { mService.finishBooting(); + } else if (startingUsers != null) { + for (i=0; i<startingUsers.size(); i++) { + mService.finishUserSwitch(startingUsers.get(i)); + } } mService.trimApplications(); @@ -3556,6 +3577,10 @@ final class ActivityStack { return res; } + final void addStartingUserLocked(UserStartedState uss) { + mStartingUsers.add(uss); + } + /** * @return Returns true if the activity is being finished, false if for * some reason it is being left as-is. diff --git a/services/java/com/android/server/am/UserStartedState.java b/services/java/com/android/server/am/UserStartedState.java new file mode 100644 index 000000000000..3f3ed8505c72 --- /dev/null +++ b/services/java/com/android/server/am/UserStartedState.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import java.io.PrintWriter; +import java.util.ArrayList; + +import android.app.IStopUserCallback; +import android.os.UserHandle; + +public class UserStartedState { + public final static int STATE_BOOTING = 0; + public final static int STATE_RUNNING = 1; + public final static int STATE_STOPPING = 2; + + public final UserHandle mHandle; + public final ArrayList<IStopUserCallback> mStopCallbacks + = new ArrayList<IStopUserCallback>(); + + public int mState = STATE_BOOTING; + + public UserStartedState(UserHandle handle, boolean initial) { + mHandle = handle; + } + + void dump(String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("mState="); pw.println(mState); + } +} diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index 750aa720e953..fb04d0f1c941 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -23,6 +23,8 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.IStopUserCallback; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -33,6 +35,7 @@ import android.os.FileUtils; import android.os.IUserManager; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.os.RemoteException; import android.os.UserHandle; import android.util.AtomicFile; import android.util.Slog; @@ -549,13 +552,36 @@ public class UserManagerService extends IUserManager.Stub { */ public boolean removeUser(int userHandle) { checkManageUsersPermission("Only the system can remove users"); + final UserInfo user; + synchronized (mPackagesLock) { + user = mUsers.get(userHandle); + if (userHandle == 0 || user == null) { + return false; + } + } + + int res; + try { + res = ActivityManagerNative.getDefault().stopUser(userHandle, + new IStopUserCallback.Stub() { + @Override + public void userStopped(int userId) { + finishRemoveUser(userId); + } + @Override + public void userStopAborted(int userId) { + } + }); + } catch (RemoteException e) { + return false; + } + + return res == ActivityManager.USER_OP_SUCCESS; + } + + void finishRemoveUser(int userHandle) { synchronized (mInstallLock) { synchronized (mPackagesLock) { - final UserInfo user = mUsers.get(userHandle); - if (userHandle == 0 || user == null) { - return false; - } - // Cleanup package manager settings mPm.cleanUpUserLILPw(userHandle); @@ -574,7 +600,6 @@ public class UserManagerService extends IUserManager.Stub { Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS); - return true; } @Override |