More multi-user stuff.

- New public APIs to find out when a user goes to the foreground,
  background, and is first initializing.
- New activity manager callback to be involved in the user switch
  process, allowing other services to let it know when it is safe
  to stop freezing the screen.
- Wallpaper service now implements this to handle its user switch,
  telling the activity manager when it is done.  (Currently this is
  only handling the old wallpaper going away, we need a little more
  work to correctly wait for the new wallpaper to get added.)
- Lock screen now implements the callback to do its user switch.  It
  also now locks itself when this happens, instead of relying on
  some other entity making sure it is locked.
- Pre-boot broadcasts now go to all users.
- WallpaperManager now has an API to find out if a named wallpaper is
  in use by any users.

Change-Id: I27877aef1d82126c0a1428c3d1861619ee5f8653
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 @@
             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 @@
         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 @@
     // 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 @@
     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 @@
     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 @@
     }
 
     /**
+     * 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 bca5ade..15dec3e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2287,17 +2287,62 @@
             "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 @@
 
     /**
      * 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 @@
 
     /**
      * 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 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 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 @@
  */
 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 @@
                     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" />