summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--api/current.txt4
-rw-r--r--core/java/android/app/ActivityManagerNative.java38
-rw-r--r--core/java/android/app/IActivityManager.java5
-rw-r--r--core/java/android/app/IUserSwitchObserver.aidl25
-rw-r--r--core/java/android/app/IWallpaperManager.aidl5
-rw-r--r--core/java/android/app/WallpaperManager.java19
-rw-r--r--core/java/android/content/Intent.java58
-rw-r--r--core/java/android/content/pm/UserInfo.java11
-rw-r--r--core/java/android/service/wallpaper/IWallpaperConnection.aidl1
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java6
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--policy/src/com/android/internal/policy/impl/GlobalActions.java1
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java33
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java7
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java168
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java329
-rwxr-xr-xservices/java/com/android/server/am/ActivityStack.java59
-rw-r--r--services/java/com/android/server/pm/UserManagerService.java84
-rwxr-xr-xservices/java/com/android/server/wm/WindowManagerService.java1
20 files changed, 664 insertions, 193 deletions
diff --git a/Android.mk b/Android.mk
index cb1b90b..be98487 100644
--- a/Android.mk
+++ b/Android.mk
@@ -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;
}
}