diff options
author | Dianne Hackborn <hackbod@google.com> | 2012-09-16 00:05:45 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-09-16 00:05:46 -0700 |
commit | d65afc65ea5b2bf83889dd88a9c94f895da8aece (patch) | |
tree | 128e635a32ae4ca7b2ecbe72f616d6c01702ad77 /services | |
parent | 398bad04a0ced9dacda185db8af7526002631f62 (diff) | |
parent | 5dc5a00e7ebadc085ded7e29feacd17e53698486 (diff) | |
download | frameworks_base-d65afc65ea5b2bf83889dd88a9c94f895da8aece.zip frameworks_base-d65afc65ea5b2bf83889dd88a9c94f895da8aece.tar.gz frameworks_base-d65afc65ea5b2bf83889dd88a9c94f895da8aece.tar.bz2 |
Merge "More multi-user stuff." into jb-mr1-dev
Diffstat (limited to 'services')
5 files changed, 459 insertions, 182 deletions
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index a807f4c..b027c1f 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -16,10 +16,11 @@ package com.android.server; -import static android.os.FileObserver.*; import static android.os.ParcelFileDescriptor.*; +import android.app.ActivityManagerNative; import android.app.AppGlobals; +import android.app.IUserSwitchObserver; import android.app.IWallpaperManager; import android.app.IWallpaperManagerCallback; import android.app.PendingIntent; @@ -43,6 +44,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.RemoteException; import android.os.FileObserver; import android.os.ParcelFileDescriptor; @@ -79,7 +81,6 @@ import org.xmlpull.v1.XmlSerializer; import com.android.internal.content.PackageMonitor; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; -import com.android.server.am.ActivityManagerService; class WallpaperManagerService extends IWallpaperManager.Stub { static final String TAG = "WallpaperService"; @@ -136,7 +137,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { mWallpaper.imageWallpaperPending = false; } bindWallpaperComponentLocked(mWallpaper.imageWallpaperComponent, true, - false, mWallpaper); + false, mWallpaper, null); saveSettingsLocked(mWallpaper); } } @@ -214,12 +215,14 @@ class WallpaperManagerService extends IWallpaperManager.Stub { IWallpaperService mService; IWallpaperEngine mEngine; WallpaperData mWallpaper; + IRemoteCallback mReply; public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) { mInfo = info; mWallpaper = wallpaper; } - + + @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { if (mWallpaper.connection == this) { @@ -235,6 +238,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } } + @Override public void onServiceDisconnected(ComponentName name) { synchronized (mLock) { mService = null; @@ -246,16 +250,35 @@ class WallpaperManagerService extends IWallpaperManager.Stub { > SystemClock.uptimeMillis() && mWallpaper.userId == mCurrentUserId) { Slog.w(TAG, "Reverting to built-in wallpaper!"); - clearWallpaperLocked(true, mWallpaper.userId); + clearWallpaperLocked(true, mWallpaper.userId, null); } } } } + @Override public void attachEngine(IWallpaperEngine engine) { - mEngine = engine; + synchronized (mLock) { + mEngine = engine; + } + } + + @Override + public void engineShown(IWallpaperEngine engine) { + synchronized (mLock) { + if (mReply != null) { + long ident = Binder.clearCallingIdentity(); + try { + mReply.sendResult(null); + } catch (RemoteException e) { + Binder.restoreCallingIdentity(ident); + } + mReply = null; + } + } } + @Override public ParcelFileDescriptor setWallpaper(String name) { synchronized (mLock) { if (mWallpaper.connection == this) { @@ -279,9 +302,10 @@ class WallpaperManagerService extends IWallpaperManager.Stub { clearWallpaperComponentLocked(wallpaper); // Do this only for the current user's wallpaper if (wallpaper.userId == mCurrentUserId - && !bindWallpaperComponentLocked(comp, false, false, wallpaper)) { + && !bindWallpaperComponentLocked(comp, false, false, + wallpaper, null)) { Slog.w(TAG, "Wallpaper no longer available; reverting to default"); - clearWallpaperLocked(false, wallpaper.userId); + clearWallpaperLocked(false, wallpaper.userId, null); } } } @@ -349,7 +373,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (doit) { Slog.w(TAG, "Wallpaper uninstalled, removing: " + wallpaper.wallpaperComponent); - clearWallpaperLocked(false, wallpaper.userId); + clearWallpaperLocked(false, wallpaper.userId, null); } } } @@ -369,7 +393,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } catch (NameNotFoundException e) { Slog.w(TAG, "Wallpaper component gone, removing: " + wallpaper.wallpaperComponent); - clearWallpaperLocked(false, wallpaper.userId); + clearWallpaperLocked(false, wallpaper.userId, null); } } if (wallpaper.nextWallpaperComponent != null @@ -413,28 +437,43 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public void systemReady() { if (DEBUG) Slog.v(TAG, "systemReady"); WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER); - switchWallpaper(wallpaper); + switchWallpaper(wallpaper, null); wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper); wallpaper.wallpaperObserver.startWatching(); IntentFilter userFilter = new IntentFilter(); - userFilter.addAction(Intent.ACTION_USER_SWITCHED); userFilter.addAction(Intent.ACTION_USER_REMOVED); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (Intent.ACTION_USER_SWITCHED.equals(action)) { - switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); - } else if (Intent.ACTION_USER_REMOVED.equals(action)) { + if (Intent.ACTION_USER_REMOVED.equals(action)) { removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); } } }, userFilter); + try { + ActivityManagerNative.getDefault().registerUserSwitchObserver( + new IUserSwitchObserver.Stub() { + @Override + public void onUserSwitching(int newUserId, IRemoteCallback reply) { + switchUser(newUserId, reply); + } + + @Override + public void onUserSwitchComplete(int newUserId) throws RemoteException { + } + }); + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } String getName() { - return mWallpaperMap.get(0).name; + synchronized (mLock) { + return mWallpaperMap.get(0).name; + } } void removeUser(int userId) { @@ -451,7 +490,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } } - void switchUser(int userId) { + void switchUser(int userId, IRemoteCallback reply) { synchronized (mLock) { mCurrentUserId = userId; WallpaperData wallpaper = mWallpaperMap.get(userId); @@ -462,35 +501,35 @@ class WallpaperManagerService extends IWallpaperManager.Stub { wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper); wallpaper.wallpaperObserver.startWatching(); } - switchWallpaper(wallpaper); + switchWallpaper(wallpaper, reply); } } - void switchWallpaper(WallpaperData wallpaper) { + void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) { synchronized (mLock) { RuntimeException e = null; try { ComponentName cname = wallpaper.wallpaperComponent != null ? wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent; - if (bindWallpaperComponentLocked(cname, true, false, wallpaper)) { + if (bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) { return; } } catch (RuntimeException e1) { e = e1; } Slog.w(TAG, "Failure starting previous wallpaper", e); - clearWallpaperLocked(false, wallpaper.userId); + clearWallpaperLocked(false, wallpaper.userId, reply); } } public void clearWallpaper() { if (DEBUG) Slog.v(TAG, "clearWallpaper"); synchronized (mLock) { - clearWallpaperLocked(false, UserHandle.getCallingUserId()); + clearWallpaperLocked(false, UserHandle.getCallingUserId(), null); } } - void clearWallpaperLocked(boolean defaultFailed, int userId) { + void clearWallpaperLocked(boolean defaultFailed, int userId, IRemoteCallback reply) { WallpaperData wallpaper = mWallpaperMap.get(userId); File f = new File(getWallpaperDir(userId), WALLPAPER); if (f.exists()) { @@ -503,7 +542,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (userId != mCurrentUserId) return; if (bindWallpaperComponentLocked(defaultFailed ? wallpaper.imageWallpaperComponent - : null, true, false, wallpaper)) { + : null, true, false, wallpaper, reply)) { return; } } catch (IllegalArgumentException e1) { @@ -518,21 +557,38 @@ class WallpaperManagerService extends IWallpaperManager.Stub { // wallpaper. Slog.e(TAG, "Default wallpaper component not found!", e); clearWallpaperComponentLocked(wallpaper); + if (reply != null) { + try { + reply.sendResult(null); + } catch (RemoteException e1) { + } + } } - public void setDimensionHints(int width, int height) throws RemoteException { - checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); - - int userId = UserHandle.getCallingUserId(); - WallpaperData wallpaper = mWallpaperMap.get(userId); - if (wallpaper == null) { - throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); - } - if (width <= 0 || height <= 0) { - throw new IllegalArgumentException("width and height must be > 0"); + public boolean hasNamedWallpaper(String name) { + synchronized (mLock) { + for (int i=0; i<mWallpaperMap.size(); i++) { + WallpaperData wd = mWallpaperMap.valueAt(i); + if (name.equals(wd.name)) { + return true; + } + } } + return false; + } + public void setDimensionHints(int width, int height) throws RemoteException { + checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); synchronized (mLock) { + int userId = UserHandle.getCallingUserId(); + WallpaperData wallpaper = mWallpaperMap.get(userId); + if (wallpaper == null) { + throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); + } + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("width and height must be > 0"); + } + if (width != wallpaper.width || height != wallpaper.height) { wallpaper.width = width; wallpaper.height = height; @@ -610,14 +666,14 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } public ParcelFileDescriptor setWallpaper(String name) { - if (DEBUG) Slog.v(TAG, "setWallpaper"); - int userId = UserHandle.getCallingUserId(); - WallpaperData wallpaper = mWallpaperMap.get(userId); - if (wallpaper == null) { - throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); - } checkPermission(android.Manifest.permission.SET_WALLPAPER); synchronized (mLock) { + if (DEBUG) Slog.v(TAG, "setWallpaper"); + int userId = UserHandle.getCallingUserId(); + WallpaperData wallpaper = mWallpaperMap.get(userId); + if (wallpaper == null) { + throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); + } final long ident = Binder.clearCallingIdentity(); try { ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper); @@ -657,18 +713,18 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } public void setWallpaperComponent(ComponentName name) { - if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name); - int userId = UserHandle.getCallingUserId(); - WallpaperData wallpaper = mWallpaperMap.get(userId); - if (wallpaper == null) { - throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); - } checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); synchronized (mLock) { + if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name); + int userId = UserHandle.getCallingUserId(); + WallpaperData wallpaper = mWallpaperMap.get(userId); + if (wallpaper == null) { + throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); + } final long ident = Binder.clearCallingIdentity(); try { wallpaper.imageWallpaperPending = false; - bindWallpaperComponentLocked(name, false, true, wallpaper); + bindWallpaperComponentLocked(name, false, true, wallpaper, null); } finally { Binder.restoreCallingIdentity(ident); } @@ -676,7 +732,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, - boolean fromUser, WallpaperData wallpaper) { + boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) { if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName); // Has the component changed? if (!force) { @@ -794,6 +850,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { wallpaper.wallpaperComponent = componentName; wallpaper.connection = newConn; wallpaper.lastDiedTime = SystemClock.uptimeMillis(); + newConn.mReply = reply; try { if (wallpaper.userId == mCurrentUserId) { if (DEBUG) @@ -817,6 +874,13 @@ class WallpaperManagerService extends IWallpaperManager.Stub { void detachWallpaperLocked(WallpaperData wallpaper) { if (wallpaper.connection != null) { + if (wallpaper.connection.mReply != null) { + try { + wallpaper.connection.mReply.sendResult(null); + } catch (RemoteException e) { + } + wallpaper.connection.mReply = null; + } if (wallpaper.connection.mEngine != null) { try { wallpaper.connection.mEngine.destroy(); @@ -849,7 +913,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } catch (RemoteException e) { Slog.w(TAG, "Failed attaching wallpaper; clearing", e); if (!wallpaper.wallpaperUpdating) { - bindWallpaperComponentLocked(null, false, false, wallpaper); + bindWallpaperComponentLocked(null, false, false, wallpaper, null); } } } @@ -1032,11 +1096,11 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (wallpaper.nextWallpaperComponent != null && !wallpaper.nextWallpaperComponent.equals(wallpaper.imageWallpaperComponent)) { if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false, - wallpaper)) { + wallpaper, null)) { // No such live wallpaper or other failure; fall back to the default // live wallpaper (since the profile being restored indicated that the // user had selected a live rather than static one). - bindWallpaperComponentLocked(null, false, false, wallpaper); + bindWallpaperComponentLocked(null, false, false, wallpaper, null); } success = true; } else { @@ -1052,7 +1116,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success); if (success) { bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false, - wallpaper); + wallpaper, null); } } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 1c5a8a5..b8072b3 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -49,6 +49,7 @@ import android.app.IProcessObserver; import android.app.IServiceConnection; import android.app.IStopUserCallback; import android.app.IThumbnailReceiver; +import android.app.IUserSwitchObserver; import android.app.Instrumentation; import android.app.Notification; import android.app.NotificationManager; @@ -100,6 +101,7 @@ import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.IPermissionController; +import android.os.IRemoteCallback; import android.os.IUserManager; import android.os.Looper; import android.os.Message; @@ -247,6 +249,10 @@ public final class ActivityManagerService extends ActivityManagerNative // How long we wait until we timeout on key dispatching during instrumentation. static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000; + // Amount of time we wait for observers to handle a user switch before + // giving up on them and unfreezing the screen. + static final int USER_SWITCH_TIMEOUT = 2*1000; + static final int MY_PID = Process.myPid(); static final String[] EMPTY_STRING_ARRAY = new String[0]; @@ -438,6 +444,17 @@ public final class ActivityManagerService extends ActivityManagerNative final ArrayList<Integer> mUserLru = new ArrayList<Integer>(); /** + * Registered observers of the user switching mechanics. + */ + final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers + = new RemoteCallbackList<IUserSwitchObserver>(); + + /** + * Currently active user switch. + */ + Object mCurUserSwitchCallback; + + /** * Packages that the user has asked to have run in screen size * compatibility mode instead of filling the screen. */ @@ -863,6 +880,9 @@ public final class ActivityManagerService extends ActivityManagerNative static final int DISPATCH_PROCESSES_CHANGED = 31; static final int DISPATCH_PROCESS_DIED = 32; static final int REPORT_MEM_USAGE = 33; + static final int REPORT_USER_SWITCH_MSG = 34; + static final int CONTINUE_USER_SWITCH_MSG = 35; + static final int USER_SWITCH_TIMEOUT_MSG = 36; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1293,6 +1313,18 @@ public final class ActivityManagerService extends ActivityManagerNative thread.start(); break; } + case REPORT_USER_SWITCH_MSG: { + dispatchUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2); + break; + } + case CONTINUE_USER_SWITCH_MSG: { + continueUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2); + break; + } + case USER_SWITCH_TIMEOUT_MSG: { + timeoutUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2); + break; + } } } }; @@ -2142,7 +2174,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - boolean startHomeActivityLocked(int userId, UserStartedState startingUser) { + boolean startHomeActivityLocked(int userId) { if (mHeadless) { // Added because none of the other calls to ensureBootCompleted seem to fire // when running headless. @@ -2181,9 +2213,6 @@ public final class ActivityManagerService extends ActivityManagerNative null, null, 0, 0, 0, 0, null, false, null); } } - if (startingUser != null) { - mMainStack.addStartingUserLocked(startingUser); - } return true; } @@ -3731,7 +3760,7 @@ public final class ActivityManagerService extends ActivityManagerNative broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, false, false, - MY_PID, Process.SYSTEM_UID, userId); + MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } private final boolean killPackageProcessesLocked(String packageName, int appId, @@ -7660,42 +7689,45 @@ public final class ActivityManagerService extends ActivityManagerNative } } + final int[] users = getUsersLocked(); for (int i=0; i<ris.size(); i++) { ActivityInfo ai = ris.get(i).activityInfo; ComponentName comp = new ComponentName(ai.packageName, ai.name); doneReceivers.add(comp); intent.setComponent(comp); - IIntentReceiver finisher = null; - if (i == ris.size()-1) { - finisher = new IIntentReceiver.Stub() { - public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered, - boolean sticky, int sendingUser) { - // The raw IIntentReceiver interface is called - // with the AM lock held, so redispatch to - // execute our code without the lock. - mHandler.post(new Runnable() { - public void run() { - synchronized (ActivityManagerService.this) { - mDidUpdate = true; + for (int j=0; j<users.length; j++) { + IIntentReceiver finisher = null; + if (i == ris.size()-1 && j == users.length-1) { + finisher = new IIntentReceiver.Stub() { + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered, + boolean sticky, int sendingUser) { + // The raw IIntentReceiver interface is called + // with the AM lock held, so redispatch to + // execute our code without the lock. + mHandler.post(new Runnable() { + public void run() { + synchronized (ActivityManagerService.this) { + mDidUpdate = true; + } + writeLastDonePreBootReceivers(doneReceivers); + showBootMessage(mContext.getText( + R.string.android_upgrading_complete), + false); + systemReady(goingCallback); } - writeLastDonePreBootReceivers(doneReceivers); - showBootMessage(mContext.getText( - R.string.android_upgrading_complete), - false); - systemReady(goingCallback); - } - }); - } - }; - } - Slog.i(TAG, "Sending system update to: " + intent.getComponent()); - // XXX also need to send this to stopped users(!!!) - broadcastIntentLocked(null, null, intent, null, finisher, - 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID, - UserHandle.USER_ALL); - if (finisher != null) { - mWaitingUpdate = true; + }); + } + }; + } + Slog.i(TAG, "Sending system update to " + intent.getComponent() + + " for user " + users[j]); + broadcastIntentLocked(null, null, intent, null, finisher, + 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID, + users[j]); + if (finisher != null) { + mWaitingUpdate = true; + } } } } @@ -7817,7 +7849,19 @@ public final class ActivityManagerService extends ActivityManagerNative } catch (RemoteException e) { } + long ident = Binder.clearCallingIdentity(); + try { + Intent intent = new Intent(Intent.ACTION_USER_STARTED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, + false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId); + } finally { + Binder.restoreCallingIdentity(ident); + } mMainStack.resumeTopActivityLocked(null); + sendUserSwitchBroadcastsLocked(-1, mCurrentUserId); } } @@ -11435,9 +11479,12 @@ 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 (userId != UserHandle.USER_ALL && mStartedUsers.get(userId) == null) { - Slog.w(TAG, "Skipping broadcast of " + intent - + ": user " + userId + " is stopped"); - return ActivityManager.BROADCAST_SUCCESS; + if (callingUid != Process.SYSTEM_UID || (intent.getFlags() + & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) { + Slog.w(TAG, "Skipping broadcast of " + intent + + ": user " + userId + " is stopped"); + return ActivityManager.BROADCAST_SUCCESS; + } } /* @@ -13884,44 +13931,177 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, msg); throw new SecurityException(msg); } - synchronized (this) { - if (mCurrentUserId == userId) { - return true; - } - mWindowManager.startFreezingScreen(R.anim.screen_user_exit, - R.anim.screen_user_enter); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (this) { + final int oldUserId = mCurrentUserId; + if (oldUserId == 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)); - } + final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId); + if (userInfo == null) { + Slog.w(TAG, "No user info for user #" + userId); + return false; + } - mCurrentUserId = userId; - Integer userIdInt = Integer.valueOf(userId); - mUserLru.remove(userIdInt); - mUserLru.add(userIdInt); - boolean haveActivities = mMainStack.switchUser(userId); - if (!haveActivities) { - startHomeActivityLocked(userId, mStartedUsers.get(userId)); - } else { - mMainStack.addStartingUserLocked(mStartedUsers.get(userId)); + mWindowManager.startFreezingScreen(R.anim.screen_user_exit, + R.anim.screen_user_enter); + + // 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; + final Integer userIdInt = Integer.valueOf(userId); + mUserLru.remove(userIdInt); + mUserLru.add(userIdInt); + + final UserStartedState uss = mStartedUsers.get(userId); + + mHandler.removeMessages(REPORT_USER_SWITCH_MSG); + mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); + mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG, + oldUserId, userId, uss)); + mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG, + oldUserId, userId, uss), USER_SWITCH_TIMEOUT); + Intent intent = new Intent(Intent.ACTION_USER_STARTED); + 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); + + if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) { + if (userId != 0) { + intent = new Intent(Intent.ACTION_USER_INITIALIZE); + broadcastIntentLocked(null, null, intent, null, + new IIntentReceiver.Stub() { + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered, + boolean sticky, int sendingUser) { + synchronized (ActivityManagerService.this) { + getUserManagerLocked().makeInitialized(userInfo.id); + } + } + }, 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID, + userId); + } else { + getUserManagerLocked().makeInitialized(userInfo.id); + } + } + + boolean haveActivities = mMainStack.switchUserLocked(userId, uss); + if (!haveActivities) { + startHomeActivityLocked(userId); + } + + sendUserSwitchBroadcastsLocked(oldUserId, userId); } + } finally { + Binder.restoreCallingIdentity(ident); } + return true; + } + + void sendUserSwitchBroadcastsLocked(int oldUserId, int newUserId) { 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.sendBroadcastAsUser(addedIntent, UserHandle.ALL, - android.Manifest.permission.MANAGE_USERS); + Intent intent; + if (oldUserId >= 0) { + intent = new Intent(Intent.ACTION_USER_BACKGROUND); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, oldUserId); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, + false, false, MY_PID, Process.SYSTEM_UID, oldUserId); + } + if (newUserId >= 0) { + intent = new Intent(Intent.ACTION_USER_FOREGROUND); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, + false, false, MY_PID, Process.SYSTEM_UID, newUserId); + intent = new Intent(Intent.ACTION_USER_SWITCHED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, + android.Manifest.permission.MANAGE_USERS, + false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); + } } finally { Binder.restoreCallingIdentity(ident); } + } - return true; + void dispatchUserSwitch(final UserStartedState uss, final int oldUserId, + final int newUserId) { + final int N = mUserSwitchObservers.beginBroadcast(); + if (N > 0) { + final IRemoteCallback callback = new IRemoteCallback.Stub() { + int mCount = 0; + @Override + public void sendResult(Bundle data) throws RemoteException { + synchronized (ActivityManagerService.this) { + if (mCurUserSwitchCallback == this) { + mCount++; + if (mCount == N) { + sendContinueUserSwitchLocked(uss, oldUserId, newUserId); + } + } + } + } + }; + synchronized (this) { + mCurUserSwitchCallback = callback; + } + for (int i=0; i<N; i++) { + try { + mUserSwitchObservers.getBroadcastItem(i).onUserSwitching( + newUserId, callback); + } catch (RemoteException e) { + } + } + } else { + synchronized (this) { + sendContinueUserSwitchLocked(uss, oldUserId, newUserId); + } + } + mUserSwitchObservers.finishBroadcast(); + } + + void timeoutUserSwitch(UserStartedState uss, int oldUserId, int newUserId) { + synchronized (this) { + Slog.w(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId); + sendContinueUserSwitchLocked(uss, oldUserId, newUserId); + } + } + + void sendContinueUserSwitchLocked(UserStartedState uss, int oldUserId, int newUserId) { + mCurUserSwitchCallback = null; + mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); + mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG, + oldUserId, newUserId, uss)); + } + + void continueUserSwitch(UserStartedState uss, int oldUserId, int newUserId) { + final int N = mUserSwitchObservers.beginBroadcast(); + for (int i=0; i<N; i++) { + try { + mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(newUserId); + } catch (RemoteException e) { + } + } + mUserSwitchObservers.finishBroadcast(); + synchronized (this) { + mWindowManager.stopFreezingScreen(); + } } void finishUserSwitch(UserStartedState uss) { @@ -13937,7 +14117,6 @@ public final class ActivityManagerService extends ActivityManagerNative android.Manifest.permission.RECEIVE_BOOT_COMPLETED, false, false, MY_PID, Process.SYSTEM_UID, userId); } - mWindowManager.stopFreezingScreen(); } } @@ -14074,6 +14253,26 @@ public final class ActivityManagerService extends ActivityManagerNative return state != null && state.mState != UserStartedState.STATE_STOPPING; } + @Override + public void registerUserSwitchObserver(IUserSwitchObserver observer) { + if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: registerUserSwitchObserver() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + + mUserSwitchObservers.register(observer); + } + + @Override + public void unregisterUserSwitchObserver(IUserSwitchObserver observer) { + mUserSwitchObservers.unregister(observer); + } + private boolean userExists(int userId) { if (userId == 0) { return true; diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index bc835b6..f72d318 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -575,37 +575,36 @@ final class ActivityStack { * Move the activities around in the stack to bring a user to the foreground. * @return whether there are any activities for the specified user. */ - final boolean switchUser(int userId) { - synchronized (mService) { - mCurrentUser = userId; + final boolean switchUserLocked(int userId, UserStartedState uss) { + mCurrentUser = userId; + mStartingUsers.add(uss); - // Only one activity? Nothing to do... - if (mHistory.size() < 2) - return false; + // Only one activity? Nothing to do... + if (mHistory.size() < 2) + return false; - boolean haveActivities = false; - // Check if the top activity is from the new user. - ActivityRecord top = mHistory.get(mHistory.size() - 1); - if (top.userId == userId) return true; - // Otherwise, move the user's activities to the top. - int N = mHistory.size(); - int i = 0; - while (i < N) { - ActivityRecord r = mHistory.get(i); - if (r.userId == userId) { - ActivityRecord moveToTop = mHistory.remove(i); - mHistory.add(moveToTop); - // No need to check the top one now - N--; - haveActivities = true; - } else { - i++; - } + boolean haveActivities = false; + // Check if the top activity is from the new user. + ActivityRecord top = mHistory.get(mHistory.size() - 1); + if (top.userId == userId) return true; + // Otherwise, move the user's activities to the top. + int N = mHistory.size(); + int i = 0; + while (i < N) { + ActivityRecord r = mHistory.get(i); + if (r.userId == userId) { + ActivityRecord moveToTop = mHistory.remove(i); + mHistory.add(moveToTop); + // No need to check the top one now + N--; + haveActivities = true; + } else { + i++; } - // Transition from the old top to the new top - resumeTopActivityLocked(top); - return haveActivities; } + // Transition from the old top to the new top + resumeTopActivityLocked(top); + return haveActivities; } final boolean realStartActivityLocked(ActivityRecord r, @@ -1398,7 +1397,7 @@ final class ActivityStack { // Launcher... if (mMainStack) { ActivityOptions.abort(options); - return mService.startHomeActivityLocked(mCurrentUser, null); + return mService.startHomeActivityLocked(mCurrentUser); } } @@ -3577,10 +3576,6 @@ 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/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index fc01f60..a58c4ea 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -83,6 +83,8 @@ public class UserManagerService extends IUserManager.Stub { private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); + private final int mUserLimit; + private int[] mUserIds; private boolean mGuestEnabled; private int mNextSerialNumber; @@ -125,6 +127,8 @@ public class UserManagerService extends IUserManager.Stub { mPm = pm; mInstallLock = installLock; mPackagesLock = packagesLock; + mUserLimit = mContext.getResources().getInteger( + com.android.internal.R.integer.config_multiuserMaximumUsers); mUsersDir = new File(dataDir, USER_INFO_DIR); mUsersDir.mkdirs(); // Make zeroth user directory, for services to migrate their files to that location @@ -237,16 +241,23 @@ public class UserManagerService extends IUserManager.Stub { // TODO: } + public void makeInitialized(int userId) { + checkManageUsersPermission("makeInitialized"); + synchronized (mPackagesLock) { + UserInfo info = mUsers.get(userId); + if (info != null && (info.flags&UserInfo.FLAG_INITIALIZED) == 0) { + info.flags |= UserInfo.FLAG_INITIALIZED; + writeUserLocked(info); + } + } + } + /** * Check if we've hit the limit of how many users can be created. */ - private boolean isUserLimitReached() { - synchronized (mInstallLock) { - int nUsers = mUsers.size(); - int userLimit = mContext.getResources().getInteger( - com.android.internal.R.integer.config_multiuserMaximumUsers); - return nUsers >= userLimit; - } + private boolean isUserLimitReachedLocked() { + int nUsers = mUsers.size(); + return nUsers >= mUserLimit; } /** @@ -535,28 +546,31 @@ public class UserManagerService extends IUserManager.Stub { public UserInfo createUser(String name, int flags) { checkManageUsersPermission("Only the system can create users"); - if (isUserLimitReached()) return null; - - int userId = getNextAvailableId(); - UserInfo userInfo = new UserInfo(userId, name, null, flags); - File userPath = new File(mBaseUserPath, Integer.toString(userId)); - synchronized (mInstallLock) { - synchronized (mPackagesLock) { - userInfo.serialNumber = mNextSerialNumber++; - mUsers.put(userId, userInfo); - writeUserListLocked(); - writeUserLocked(userInfo); - updateUserIdsLocked(); - mPm.createNewUserLILPw(userId, userPath); + final long ident = Binder.clearCallingIdentity(); + final UserInfo userInfo; + try { + synchronized (mInstallLock) { + synchronized (mPackagesLock) { + if (isUserLimitReachedLocked()) return null; + int userId = getNextAvailableIdLocked(); + userInfo = new UserInfo(userId, name, null, flags); + File userPath = new File(mBaseUserPath, Integer.toString(userId)); + userInfo.serialNumber = mNextSerialNumber++; + mUsers.put(userId, userInfo); + writeUserListLocked(); + writeUserLocked(userInfo); + updateUserIdsLocked(); + mPm.createNewUserLILPw(userId, userPath); + } } - } - if (userInfo != null) { - Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id); - mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_BOOT_COMPLETED), - new UserHandle(userInfo.id)); - mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, - android.Manifest.permission.MANAGE_USERS); + if (userInfo != null) { + Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id); + mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, + android.Manifest.permission.MANAGE_USERS); + } + } finally { + Binder.restoreCallingIdentity(ident); } return userInfo; } @@ -614,9 +628,15 @@ public class UserManagerService extends IUserManager.Stub { } // Let other services shutdown any activity - Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); - mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS); + long ident = Binder.clearCallingIdentity(); + try { + Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); + mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, + android.Manifest.permission.MANAGE_USERS); + } finally { + Binder.restoreCallingIdentity(ident); + } } private void removeDirectoryRecursive(File parent) { @@ -666,7 +686,7 @@ public class UserManagerService extends IUserManager.Stub { * for data and battery stats collection, or unexpected cross-talk. * @return */ - private int getNextAvailableId() { + private int getNextAvailableIdLocked() { synchronized (mPackagesLock) { int i = 0; while (i < Integer.MAX_VALUE) { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 1a101ad..556613e 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -294,7 +294,6 @@ public class WindowManagerService extends IWindowManager.Stub KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED); } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { final int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - Slog.v(TAG, "Switching user from " + mCurrentUserId + " to " + newUserId); mCurrentUserId = newUserId; } } |