diff options
20 files changed, 664 insertions, 193 deletions
@@ -79,6 +79,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IThumbnailRetriever.aidl \ core/java/android/app/ITransientNotification.aidl \ core/java/android/app/IUiModeManager.aidl \ + core/java/android/app/IUserSwitchObserver.aidl \ core/java/android/app/IWallpaperManager.aidl \ core/java/android/app/IWallpaperManagerCallback.aidl \ core/java/android/app/admin/IDevicePolicyManager.aidl \ diff --git a/api/current.txt b/api/current.txt index a5c53e7..bd03fac 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4169,6 +4169,7 @@ package android.app { method public android.graphics.drawable.Drawable getFastDrawable(); method public static android.app.WallpaperManager getInstance(android.content.Context); method public android.app.WallpaperInfo getWallpaperInfo(); + method public boolean hasResourceWallpaper(int); method public android.graphics.drawable.Drawable peekDrawable(); method public android.graphics.drawable.Drawable peekFastDrawable(); method public void sendWallpaperCommand(android.os.IBinder, java.lang.String, int, int, int, android.os.Bundle); @@ -5843,6 +5844,9 @@ package android.content { field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED"; field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED"; field public static final java.lang.String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE"; + field public static final java.lang.String ACTION_USER_BACKGROUND = "android.intent.action.USER_BACKGROUND"; + field public static final java.lang.String ACTION_USER_FOREGROUND = "android.intent.action.USER_FOREGROUND"; + field public static final java.lang.String ACTION_USER_INITIALIZE = "android.intent.action.USER_INITIALIZE"; field public static final java.lang.String ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT"; field public static final java.lang.String ACTION_VIEW = "android.intent.action.VIEW"; field public static final java.lang.String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND"; diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index bf77f6e..eae3b1f 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1734,6 +1734,22 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case REGISTER_USER_SWITCH_OBSERVER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IUserSwitchObserver observer = IUserSwitchObserver.Stub.asInterface( + data.readStrongBinder()); + registerUserSwitchObserver(observer); + return true; + } + + case UNREGISTER_USER_SWITCH_OBSERVER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IUserSwitchObserver observer = IUserSwitchObserver.Stub.asInterface( + data.readStrongBinder()); + unregisterUserSwitchObserver(observer); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -3955,5 +3971,27 @@ class ActivityManagerProxy implements IActivityManager return result; } + public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(observer != null ? observer.asBinder() : null); + mRemote.transact(REGISTER_USER_SWITCH_OBSERVER_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + public void unregisterUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(observer != null ? observer.asBinder() : null); + mRemote.transact(UNREGISTER_USER_SWITCH_OBSERVER_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 4c0e2a7..9ef375a 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -360,6 +360,9 @@ public interface IActivityManager extends IInterface { // manage your activity to make sure it is always the uid you expect. public int getLaunchedFromUid(IBinder activityToken) throws RemoteException; + public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException; + public void unregisterUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -609,4 +612,6 @@ public interface IActivityManager extends IInterface { int IS_INTENT_SENDER_AN_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+151; int START_ACTIVITY_AS_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+152; int STOP_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+153; + int REGISTER_USER_SWITCH_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+154; + int UNREGISTER_USER_SWITCH_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+155; } diff --git a/core/java/android/app/IUserSwitchObserver.aidl b/core/java/android/app/IUserSwitchObserver.aidl new file mode 100644 index 0000000..845897b --- /dev/null +++ b/core/java/android/app/IUserSwitchObserver.aidl @@ -0,0 +1,25 @@ +/* + * 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 android.app; + +import android.os.IRemoteCallback; + +/** {@hide} */ +oneway interface IUserSwitchObserver { + void onUserSwitching(int newUserId, IRemoteCallback reply); + void onUserSwitchComplete(int newUserId); +} diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 69f64a1..3efd3c0 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -52,6 +52,11 @@ interface IWallpaperManager { void clearWallpaper(); /** + * Return whether there is a wallpaper set with the given name. + */ + boolean hasNamedWallpaper(String name); + + /** * Sets the dimension hint for the wallpaper. These hints indicate the desired * minimum width and height for the wallpaper. */ diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 1ad2e6d..9c0064e 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -590,6 +590,25 @@ public class WallpaperManager { } /** + * Return whether any users are currently set to use the wallpaper + * with the given resource ID. That is, their wallpaper has been + * set through {@link #setResource(int)} with the same resource id. + */ + public boolean hasResourceWallpaper(int resid) { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return false; + } + try { + Resources resources = mContext.getResources(); + String name = "res:" + resources.getResourceName(resid); + return sGlobals.mService.hasNamedWallpaper(name); + } catch (RemoteException e) { + return false; + } + } + + /** * Returns the desired minimum width for the wallpaper. Callers of * {@link #setBitmap(android.graphics.Bitmap)} or * {@link #setStream(java.io.InputStream)} should check this value diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index b86ac98..fad3e10 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2287,17 +2287,62 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.PRE_BOOT_COMPLETED"; /** + * Sent the first time a user is starting, to allow system apps to + * perform one time initialization. (This will not be seen by third + * party applications because a newly initialized user does not have any + * third party applications installed for it.) This is sent early in + * starting the user, around the time the home app is started, before + * {@link #ACTION_BOOT_COMPLETED} is sent. + */ + public static final String ACTION_USER_INITIALIZE = + "android.intent.action.USER_INITIALIZE"; + + /** + * Sent when a user switch is happening, causing the process's user to be + * brought to the foreground. This is only sent to receivers registered + * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) + * Context.registerReceiver}. It is sent to the user that is going to the + * foreground. + */ + public static final String ACTION_USER_FOREGROUND = + "android.intent.action.USER_FOREGROUND"; + + /** + * Sent when a user switch is happening, causing the process's user to be + * sent to the background. This is only sent to receivers registered + * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) + * Context.registerReceiver}. It is sent to the user that is going to the + * background. + */ + public static final String ACTION_USER_BACKGROUND = + "android.intent.action.USER_BACKGROUND"; + + /** * Broadcast sent to the system when a user is added. Carries an extra EXTRA_USER_HANDLE that has the - * userHandle of the new user. + * userHandle of the new user. It is sent to all running users. You must hold + * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast. * @hide */ public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED"; /** + * Broadcast sent to the system when a user is started. Carries an extra EXTRA_USER_HANDLE that has + * the userHandle of the user. This is only sent to + * registered receivers, not manifest receivers. It is sent to the user + * that has been started. + * @hide + */ + public static final String ACTION_USER_STARTED = + "android.intent.action.USER_STARTED"; + + /** * Broadcast sent to the system when a user is stopped. Carries an extra EXTRA_USER_HANDLE that has * the userHandle of the user. This is similar to {@link #ACTION_PACKAGE_RESTARTED}, - * but for an entire user instead of a specific package. + * but for an entire user instead of a specific package. This is only sent to + * registered receivers, not manifest receivers. It is sent to all running + * users <em>except</em> the one that has just been stopped (which is no + * longer running). * @hide */ public static final String ACTION_USER_STOPPED = @@ -2305,7 +2350,9 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has - * the userHandle of the user. + * the userHandle of the user. It is sent to all running users except the + * one that has been removed. You must hold + * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast. * @hide */ public static final String ACTION_USER_REMOVED = @@ -2313,7 +2360,10 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USER_HANDLE that has - * the userHandle of the user to become the current one. + * the userHandle of the user to become the current one. This is only sent to + * registered receivers, not manifest receivers. It is sent to all running users. + * You must hold + * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast. * @hide */ public static final String ACTION_USER_SWITCHED = diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index 6bc9a1f..a06aba9 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -30,6 +30,12 @@ public class UserInfo implements Parcelable { public static final int FLAG_MASK_USER_TYPE = 0x0000003F; /** + * *************************** NOTE *************************** + * These flag values CAN NOT CHANGE because they are written + * directly to storage. + */ + + /** * Primary user. Only one user can have this flag set. Meaning of this * flag TBD. */ @@ -52,6 +58,11 @@ public class UserInfo implements Parcelable { */ public static final int FLAG_RESTRICTED = 0x00000008; + /** + * Indicates that this user has gone through its first-time initialization. + */ + public static final int FLAG_INITIALIZED = 0x00000010; + public int id; public int serialNumber; public String name; diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl index b09ccab..f9c5aaa 100644 --- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl +++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl @@ -24,5 +24,6 @@ import android.service.wallpaper.IWallpaperEngine; */ interface IWallpaperConnection { void attachEngine(IWallpaperEngine engine); + void engineShown(IWallpaperEngine engine); ParcelFileDescriptor setWallpaper(String name); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index efa8911..86bbc55 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -1020,6 +1020,12 @@ public abstract class WallpaperService extends Service { mEngine = engine; mActiveEngines.add(engine); engine.attach(this); + try { + mConnection.engineShown(this); + } catch (RemoteException e) { + Log.w(TAG, "Wallpaper host disappeared", e); + return; + } return; } case DO_DETACH: { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7305d8b..0a409ad 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -64,6 +64,8 @@ <protected-broadcast android:name="android.intent.action.USER_ADDED" /> <protected-broadcast android:name="android.intent.action.USER_REMOVED" /> <protected-broadcast android:name="android.intent.action.USER_STOPPED" /> + <protected-broadcast android:name="android.intent.action.USER_BACKGROUND" /> + <protected-broadcast android:name="android.intent.action.USER_FOREGROUND" /> <protected-broadcast android:name="android.intent.action.USER_SWITCHED" /> <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" /> diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index 753b864..d8e361f 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -300,7 +300,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac public void onPress() { try { ActivityManagerNative.getDefault().switchUser(user.id); - WindowManagerGlobal.getWindowManagerService().lockNow(); } catch (RemoteException re) { Log.e(TAG, "Couldn't switch user " + re); } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java index c48e2d7..4524c94 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java @@ -16,6 +16,8 @@ package com.android.internal.policy.impl.keyguard; +import android.app.ActivityManagerNative; +import android.app.IUserSwitchObserver; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -32,7 +34,9 @@ import static android.os.BatteryManager.EXTRA_HEALTH; import android.media.AudioManager; import android.os.BatteryManager; import android.os.Handler; +import android.os.IRemoteCallback; import android.os.Message; +import android.os.RemoteException; import android.provider.Settings; import com.android.internal.telephony.IccCardConstants; @@ -136,7 +140,7 @@ public class KeyguardUpdateMonitor { handleDevicePolicyManagerStateChanged(); break; case MSG_USER_SWITCHED: - handleUserSwitched(msg.arg1); + handleUserSwitched(msg.arg1, (IRemoteCallback)msg.obj); break; case MSG_USER_REMOVED: handleUserRemoved(msg.arg1); @@ -183,9 +187,6 @@ public class KeyguardUpdateMonitor { } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED .equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED)); - } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, - intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); @@ -325,9 +326,25 @@ public class KeyguardUpdateMonitor { filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); - filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_REMOVED); context.registerReceiver(mBroadcastReceiver, filter); + + try { + ActivityManagerNative.getDefault().registerUserSwitchObserver( + new IUserSwitchObserver.Stub() { + @Override + public void onUserSwitching(int newUserId, IRemoteCallback reply) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, + newUserId, 0, reply)); + } + @Override + public void onUserSwitchComplete(int newUserId) throws RemoteException { + } + }); + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } private void watchForDeviceProvisioning() { @@ -375,13 +392,17 @@ public class KeyguardUpdateMonitor { /** * Handle {@link #MSG_USER_SWITCHED} */ - protected void handleUserSwitched(int userId) { + protected void handleUserSwitched(int userId, IRemoteCallback reply) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onUserSwitched(userId); } } + try { + reply.sendResult(null); + } catch (RemoteException e) { + } } /** diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java index 1f0f5ef..372b0fc 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java @@ -48,6 +48,7 @@ import android.util.EventLog; import android.util.Log; import android.view.KeyEvent; import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy; @@ -300,6 +301,12 @@ public class KeyguardViewMediator { synchronized (KeyguardViewMediator.this) { resetStateLocked(); } + // We should always go back to the locked state when a user + // switch happens. Is there a more direct way to do this? + try { + WindowManagerGlobal.getWindowManagerService().lockNow(); + } catch (RemoteException e) { + } } @Override 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; } } |