Start implementing concept of "running" users.

The activity manager now keeps track of which users are running.

Initially, only user 0 is running.

When you switch to another user, that user is started so it is
running.  It is only at this point that BOOT_COMPLETED is sent
for that user and it is allowed to execute anything.

You can stop any user except user 0, which brings it back to the
same state as when you first boot the device.  This is also used
to be able to more cleaning delete a user, by first stopping it
before removing its data.

There is a new broadcast ACTION_USER_STOPPED sent when a user is
stopped; system services need to handle this like they currently
handle ACTION_PACKAGE_RESTARTED when individual packages are
restarted.

Change-Id: I89adbd7cbaf4a0bb72ea201385f93477f40a4119
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e644db4..3d88b464 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -210,6 +210,15 @@
      */
     public static final int INTENT_SENDER_SERVICE = 4;
 
+    /** @hide User operation call: success! */
+    public static final int USER_OP_SUCCESS = 0;
+
+    /** @hide User operation call: given user id is not known. */
+    public static final int USER_OP_UNKNOWN_USER = -1;
+
+    /** @hide User operation call: given user id is the current user, can't be stopped. */
+    public static final int USER_OP_IS_CURRENT = -2;
+
     /*package*/ ActivityManager(Context context, Handler handler) {
         mContext = context;
         mHandler = handler;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index adc9434..05c009f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1570,6 +1570,17 @@
             return true;
         }
 
+        case STOP_USER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int userid = data.readInt();
+            IStopUserCallback callback = IStopUserCallback.Stub.asInterface(
+                    data.readStrongBinder());
+            int result = stopUser(userid, callback);
+            reply.writeNoException();
+            reply.writeInt(result);
+            return true;
+        }
+
         case GET_CURRENT_USER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             UserInfo userInfo = getCurrentUser();
@@ -3756,11 +3767,25 @@
         return result;
     }
 
+    public int stopUser(int userid, IStopUserCallback callback) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(userid);
+        data.writeStrongInterface(callback);
+        mRemote.transact(STOP_USER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int result = reply.readInt();
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
+
     public UserInfo getCurrentUser() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
+        mRemote.transact(GET_CURRENT_USER_TRANSACTION, data, reply, 0);
         reply.readException();
         UserInfo userInfo = UserInfo.CREATOR.createFromParcel(reply);
         reply.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c3e911e..70d8445 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -331,6 +331,7 @@
 
     // Multi-user APIs
     public boolean switchUser(int userid) throws RemoteException;
+    public int stopUser(int userid, IStopUserCallback callback) throws RemoteException;
     public UserInfo getCurrentUser() throws RemoteException;
 
     public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
@@ -611,4 +612,5 @@
     int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+150;
     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;
 }
diff --git a/core/java/android/app/IStopUserCallback.aidl b/core/java/android/app/IStopUserCallback.aidl
new file mode 100644
index 0000000..19ac1d5
--- /dev/null
+++ b/core/java/android/app/IStopUserCallback.aidl
@@ -0,0 +1,27 @@
+/*
+** Copyright 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;
+
+/**
+ * Callback to find out when we have finished stopping a user.
+ * {@hide}
+ */
+interface IStopUserCallback
+{
+    void userStopped(int userId);
+    void userStopAborted(int userId);
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 06edf32..53e0a75 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -27,7 +27,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
-import android.media.RemoteControlClient;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -2287,6 +2286,15 @@
             "android.intent.action.USER_ADDED";
 
     /**
+     * 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.
+     * @hide
+     */
+    public static final String ACTION_USER_STOPPED =
+            "android.intent.action.USER_STOPPED";
+
+    /**
      * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
      * the userHandle of the user.
      * @hide
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 650681a..3477a90 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -49,6 +49,7 @@
         sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
         sPackageFilt.addDataScheme("package");
         sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
+        sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
         sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
         sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
     }
@@ -136,6 +137,9 @@
     public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
         return false;
     }
+
+    public void onHandleUserStop(Intent intent, int userHandle) {
+    }
     
     public void onUidRemoved(int uid) {
     }
@@ -307,6 +311,10 @@
                     intent.getIntExtra(Intent.EXTRA_UID, 0), true);
         } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
             onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
+        } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+            if (intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
+                onHandleUserStop(intent, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+            }
         } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
             String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             mAppearingPackages = pkgList;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5829787..96eb54a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -62,6 +62,7 @@
     <protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
     <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_SWITCHED" />
 
     <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />