diff options
| author | 2012-08-29 18:15:06 -0700 | |
|---|---|---|
| committer | 2012-08-29 18:15:07 -0700 | |
| commit | 12631ec35eafc466e0deef315daa5fdfb5066372 (patch) | |
| tree | 6881c386ff69eca08eae5e90b5199aa4f36a48a5 /services/java/com | |
| parent | ffc45b7d5708e706aeb19454c5172136761d214b (diff) | |
| parent | 80a4af2bbc6af42ae605e454bf89558e564f5244 (diff) | |
Merge "Start implementing concept of "running" users." into jb-mr1-dev
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 368db2661f56..d8c8dfa61a36 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)); + } } } } @@ -6297,6 +6348,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; @@ -9048,6 +9109,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) { @@ -11107,6 +11175,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. @@ -11649,7 +11725,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, @@ -13491,75 +13567,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 |