Make sure persistent processes are not replicated for secondary users.

An intent is launched in a singleton process if the process is persistent
and the resolved activity/service/etc is not requested to run in a different
process.

Change-Id: I1463e73a76bc8bde4185f9cf4395edb47515841d
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b6ebbdf..185fcb9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3458,9 +3458,9 @@
             ai.disableCompatibilityMode();
         }
         if (stopped) {
-            p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
+            ai.flags |= ApplicationInfo.FLAG_STOPPED;
         } else {
-            p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
+            ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
         }
         if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
             ai.enabled = true;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e37adc7..77bec41 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1808,16 +1808,18 @@
 
     final ProcessRecord getProcessRecordLocked(
             String processName, int uid) {
-        // Temporary hack to make Settings run per user
-        if (uid == Process.SYSTEM_UID && !processName.equals("com.android.settings")) {
+        if (uid == Process.SYSTEM_UID) {
             // The system gets to run in any process.  If there are multiple
             // processes with the same uid, just pick the first (this
             // should never happen).
             SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(
                     processName);
-            return procs != null ? procs.valueAt(0) : null;
+            if (procs == null) return null;
+            final int N = procs.size();
+            for (int i = 0; i < N; i++) {
+                if (UserId.isSameUser(procs.keyAt(i), uid)) return procs.valueAt(i);
+            }
         }
-        // uid = applyUserId(uid);
         ProcessRecord proc = mProcessNames.get(processName, uid);
         return proc;
     }
@@ -6183,7 +6185,9 @@
                 if (cpi == null) {
                     return null;
                 }
-
+                if (isSingleton(cpi.processName, cpi.applicationInfo)) {
+                    userId = 0;
+                }
                 cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
 
                 String msg;
@@ -10923,6 +10927,9 @@
                     return null;
                 }
                 if (userId > 0) {
+                    if (isSingleton(sInfo.processName, sInfo.applicationInfo)) {
+                        userId = 0;
+                    }
                     sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo, userId);
                 }
                 ComponentName name = new ComponentName(
@@ -11740,7 +11747,22 @@
             }
         }
     }
-    
+
+    boolean isSingleton(String componentProcessName, ApplicationInfo aInfo) {
+        boolean result = false;
+        if (UserId.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) {
+            result = false;
+        } else if (componentProcessName == aInfo.packageName) {
+            result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
+        } else if ("system".equals(componentProcessName)) {
+            result = true;
+        }
+        if (DEBUG_MU) {
+            Slog.v(TAG, "isSingleton(" + componentProcessName + ", " + aInfo + ") = " + result);
+        }
+        return result;
+    }
+
     public int bindService(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType,
             IServiceConnection connection, int flags, int userId) {
@@ -11808,6 +11830,11 @@
             if (res.record == null) {
                 return -1;
             }
+            if (isSingleton(res.record.processName, res.record.appInfo)) {
+                userId = 0;
+                res = retrieveServiceLocked(service, resolvedType, Binder.getCallingPid(),
+                        Binder.getCallingUid(), 0);
+            }
             ServiceRecord s = res.record;
 
             final long origId = Binder.clearCallingIdentity();
@@ -12740,7 +12767,11 @@
                 if (ai != null) {
                     receivers = new ArrayList();
                     ResolveInfo ri = new ResolveInfo();
-                    ri.activityInfo = getActivityInfoForUser(ai, userId);
+                    if (isSingleton(ai.processName, ai.applicationInfo)) {
+                        ri.activityInfo = getActivityInfoForUser(ai, 0);
+                    } else {
+                        ri.activityInfo = getActivityInfoForUser(ai, userId);
+                    }
                     receivers.add(ri);
                 }
             } else {
@@ -14894,16 +14925,14 @@
         if (info == null) return null;
         ApplicationInfo newInfo = new ApplicationInfo(info);
         newInfo.uid = applyUserId(info.uid, userId);
-        if (newInfo.uid >= Process.FIRST_APPLICATION_UID) {
-            newInfo.dataDir = USER_DATA_DIR + userId + "/"
-                    + info.packageName;
-        }
+        newInfo.dataDir = USER_DATA_DIR + userId + "/"
+                + info.packageName;
         return newInfo;
     }
 
     ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
-        if (aInfo == null || aInfo.applicationInfo.uid < Process.FIRST_APPLICATION_UID
-                || userId < 1) {
+        if (aInfo == null
+                || (userId < 1 && aInfo.applicationInfo.uid < UserId.PER_USER_RANGE)) {
             return aInfo;
         }
 
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 6596e1f..2c53186 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2345,8 +2345,9 @@
         }
 
         if (err == ActivityManager.START_SUCCESS) {
+            final int userId = aInfo != null ? UserId.getUserId(aInfo.applicationInfo.uid) : 0;
             Slog.i(TAG, "START {" + intent.toShortString(true, true, true, false)
-                    + "} from pid " + (callerApp != null ? callerApp.pid : callingPid));
+                    + " u=" + userId + "} from pid " + (callerApp != null ? callerApp.pid : callingPid));
         }
 
         ActivityRecord sourceRecord = null;
@@ -2943,6 +2944,9 @@
         // Collect information about the target of the Intent.
         ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
                 profileFile, profileFd, userId);
+        if (mService.isSingleton(aInfo.processName, aInfo.applicationInfo)) {
+            userId = 0;
+        }
         aInfo = mService.getActivityInfoForUser(aInfo, userId);
 
         synchronized (mService) {
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 1b83e0b..47b8c0a 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -719,8 +719,10 @@
                     info.activityInfo.applicationInfo.packageName,
                     info.activityInfo.name);
             if (r.callingUid != Process.SYSTEM_UID) {
-                info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, UserId
-                        .getUserId(r.callingUid));
+                boolean isSingleton = mService.isSingleton(info.activityInfo.processName,
+                        info.activityInfo.applicationInfo);
+                int targetUserId = isSingleton ? 0 : UserId.getUserId(r.callingUid);
+                info.activityInfo = mService.getActivityInfoForUser(info.activityInfo,targetUserId);
             }
             r.curReceiver = info.activityInfo;
             if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {