Improve multi-user broadcasts.

You can now use ALL and CURRENT when sending broadcasts, to specify
where the broadcast goes.

Sticky broadcasts are now correctly separated per user, and registered
receivers are filtered based on the requested target user.

New Context APIs for more kinds of sending broadcasts as users.

Updating a bunch of system code that sends broadcasts to explicitly
specify which user the broadcast goes to.

Made a single version of the code for interpreting the requested
target user ID that all entries to activity manager (start activity,
send broadcast, start service) use.

Change-Id: Ie29f02dd5242ef8c8fa56c54593a315cd2574e1c
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 9b7be02..ac8a514 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -268,7 +268,7 @@
             Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
             intent.putExtra("time-zone", zone.getID());
-            mContext.sendBroadcast(intent);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
         }
     }
     
@@ -661,7 +661,7 @@
                     Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
                     intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                             | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                    mContext.sendBroadcast(intent);
+                    mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
                 }
                 
                 synchronized (mLock) {
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 4192a93..f0989e7 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -33,6 +33,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UEventObserver;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.EventLog;
 import android.util.Slog;
@@ -345,21 +346,21 @@
             statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             if (mPlugType != 0 && mLastPlugType == 0) {
                 statusIntent.setAction(Intent.ACTION_POWER_CONNECTED);
-                mContext.sendBroadcast(statusIntent);
+                mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
             }
             else if (mPlugType == 0 && mLastPlugType != 0) {
                 statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);
-                mContext.sendBroadcast(statusIntent);
+                mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
             }
 
             if (sendBatteryLow) {
                 mSentLowBatteryBroadcast = true;
                 statusIntent.setAction(Intent.ACTION_BATTERY_LOW);
-                mContext.sendBroadcast(statusIntent);
+                mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
             } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {
                 mSentLowBatteryBroadcast = false;
                 statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);
-                mContext.sendBroadcast(statusIntent);
+                mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
             }
 
             // Update the battery LED
@@ -414,7 +415,7 @@
                     " icon:" + icon  + " invalid charger:" + mInvalidCharger);
         }
 
-        ActivityManagerNative.broadcastStickyIntent(intent, null);
+        ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
     }
 
     private final void logBatteryStats() {
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index 4c98ac3..4b43070 100755
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -23,6 +23,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.Binder;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
 import java.util.List;
@@ -650,7 +651,8 @@
                         intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                         if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
-                        mContext.sendBroadcast(intent,BLUETOOTH_PERM);
+                        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                                BLUETOOTH_PERM);
                     }
                     break;
                 }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 8a1ac10..04991bb 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -81,6 +81,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.security.Credentials;
 import android.security.KeyStore;
@@ -1851,7 +1852,8 @@
         Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
         intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
         intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
-        mContext.sendOrderedBroadcast(intent, RECEIVE_DATA_ACTIVITY_CHANGE);
+        mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
+                RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
     }
 
     /**
@@ -1925,7 +1927,7 @@
                 log("sendStickyBroadcast: action=" + intent.getAction());
             }
 
-            mContext.sendStickyBroadcast(intent);
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
         }
     }
 
@@ -1946,7 +1948,7 @@
         synchronized(this) {
             mSystemReady = true;
             if (mInitialBroadcast != null) {
-                mContext.sendStickyBroadcast(mInitialBroadcast);
+                mContext.sendStickyBroadcastAsUser(mInitialBroadcast, UserHandle.ALL);
                 mInitialBroadcast = null;
             }
         }
@@ -2465,7 +2467,7 @@
          * Connectivity events can happen before boot has completed ...
          */
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        mContext.sendBroadcast(intent);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     // Caller must grab mDnsLock.
@@ -3110,7 +3112,7 @@
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
             Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
         intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
-        mContext.sendStickyBroadcast(intent);
+        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private static class SettingsObserver extends ContentObserver {
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index f966a33..77b062c 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -55,6 +55,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
@@ -570,10 +571,10 @@
             intent.putExtra("expiration", admin.passwordExpirationDate);
         }
         if (result != null) {
-            mContext.sendOrderedBroadcast(intent, null, result, mHandler,
-                    Activity.RESULT_OK, null, null);
+            mContext.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
+                    null, result, mHandler, Activity.RESULT_OK, null, null);
         } else {
-            mContext.sendBroadcast(intent);
+            mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
         }
     }
 
@@ -712,7 +713,7 @@
     private void sendChangedNotification() {
         Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
         intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mContext.sendBroadcast(intent);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private void loadSettingsLocked() {
@@ -1734,7 +1735,8 @@
             }
             Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED);
             intent.setComponent(admin.info.getComponent());
-            mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+            mContext.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
+                    null, new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
                     try {
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 9231674..c919595 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -39,6 +39,7 @@
 import android.os.StatFs;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.format.Formatter;
 import android.util.EventLog;
@@ -416,8 +417,8 @@
         //cancel notification since memory has been freed
         mNotificationMgr.cancel(LOW_MEMORY_NOTIFICATION_ID);
 
-        mContext.removeStickyBroadcast(mStorageLowIntent);
-        mContext.sendBroadcast(mStorageOkIntent);
+        mContext.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
+        mContext.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL);
     }
 
     /**
@@ -433,8 +434,8 @@
      */
     private final void cancelFullNotification() {
         if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
-        mContext.removeStickyBroadcast(mStorageFullIntent);
-        mContext.sendBroadcast(mStorageNotFullIntent);
+        mContext.removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
+        mContext.sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
     }
 
     public void updateMemory() {
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index f1ff27f..65c39f2 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -34,6 +34,7 @@
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.UEventObserver;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.dreams.IDreamManager;
 import android.util.Log;
@@ -226,7 +227,7 @@
                 }
             } else {
                 // dreams feature not enabled, send legacy intent
-                mContext.sendStickyBroadcast(intent);
+                mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
             }
         }
     }
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 932cba1..0b12410 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -31,6 +31,7 @@
 import android.os.Message;
 import android.os.StatFs;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.format.Time;
 import android.util.Slog;
@@ -157,7 +158,8 @@
             @Override
             public void handleMessage(Message msg) {
                 if (msg.what == MSG_SEND_BROADCAST) {
-                    mContext.sendBroadcast((Intent)msg.obj, android.Manifest.permission.READ_LOGS);
+                    mContext.sendBroadcastAsUser((Intent)msg.obj, UserHandle.OWNER,
+                            android.Manifest.permission.READ_LOGS);
                 }
             }
         };
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 3a45720..5993f32 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -51,6 +51,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.provider.Settings.NameValueTable;
@@ -719,7 +720,8 @@
             }
         }
         if (changesMade) {
-            mContext.sendBroadcast(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION));
+            mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
+                    UserHandle.ALL);
         }
     }
 
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index bb5d552..1eccbbe 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1047,12 +1047,13 @@
         // add StorageVolume extra
         intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolumeMap.get(path));
         Slog.d(TAG, "sendStorageIntent " + intent);
-        mContext.sendBroadcast(intent);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private void sendUmsIntent(boolean c) {
-        mContext.sendBroadcast(
-                new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
+        mContext.sendBroadcastAsUser(
+                new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
+                UserHandle.ALL);
     }
 
     private void validatePermission(String perm) {
diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java
index 87843d9..2a7a2eb 100644
--- a/services/java/com/android/server/NsdService.java
+++ b/services/java/com/android/server/NsdService.java
@@ -31,6 +31,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -448,7 +449,7 @@
         } else {
             intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED);
         }
-        mContext.sendStickyBroadcast(intent);
+        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private boolean isNsdEnabled() {
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 087e8db..8361477 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.telephony.CellLocation;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
@@ -588,7 +589,7 @@
         Bundle data = new Bundle();
         state.fillInNotifierBundle(data);
         intent.putExtras(data);
-        mContext.sendStickyBroadcast(intent);
+        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private void broadcastSignalStrengthChanged(SignalStrength signalStrength) {
@@ -606,7 +607,7 @@
         Bundle data = new Bundle();
         signalStrength.fillInNotifierBundle(data);
         intent.putExtras(data);
-        mContext.sendStickyBroadcast(intent);
+        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private void broadcastCallStateChanged(int state, String incomingNumber) {
@@ -629,7 +630,8 @@
         if (!TextUtils.isEmpty(incomingNumber)) {
             intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
         }
-        mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                android.Manifest.permission.READ_PHONE_STATE);
     }
 
     private void broadcastDataConnectionStateChanged(int state,
@@ -662,14 +664,14 @@
 
         intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
         intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
-        mContext.sendStickyBroadcast(intent);
+        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private void broadcastDataConnectionFailed(String reason, String apnType) {
         Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
         intent.putExtra(PhoneConstants.FAILURE_REASON_KEY, reason);
         intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
-        mContext.sendStickyBroadcast(intent);
+        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private boolean checkNotifyPermission(String method) {
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index 98e6dc0..49f39fe 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -44,6 +44,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -368,7 +369,7 @@
         }
 
         if (mPollStickyBroadcast != null) {
-            mContext.removeStickyBroadcast(mPollStickyBroadcast);
+            mContext.removeStickyBroadcastAsUser(mPollStickyBroadcast, UserHandle.ALL);
         }
     }
 
@@ -494,7 +495,7 @@
             onPollAlarm();
 
             Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
-            mContext.sendBroadcast(broadcast);
+            mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
         }
 
         private void onPollAlarm() {
@@ -563,7 +564,7 @@
             broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
             broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
             broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
-            mContext.sendStickyBroadcast(broadcast);
+            mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
             mPollStickyBroadcast = broadcast;
 
             mAlarmManager.cancel(mPendingPollIntent);
@@ -621,7 +622,7 @@
                     Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
                     broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
                             mPolicyThrottleValue.get());
-                    mContext.sendStickyBroadcast(broadcast);
+                    mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
 
                 } // else already up!
             } else {
@@ -699,7 +700,7 @@
                 }
                 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
                 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
-                mContext.sendStickyBroadcast(broadcast);
+                mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
                 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
                 mWarningNotificationSent = false;
             }
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 617b29d..4900e00 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -37,6 +37,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 
@@ -397,7 +398,7 @@
                     adjustStatusBarCarModeLocked();
 
                     if (oldAction != null) {
-                        mContext.sendBroadcast(new Intent(oldAction));
+                        mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL);
                     }
                     mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
                     action = UiModeManager.ACTION_ENTER_CAR_MODE;
@@ -405,7 +406,7 @@
             } else if (isDeskDockState(mDockState)) {
                 if (!isDeskDockState(mLastBroadcastState)) {
                     if (oldAction != null) {
-                        mContext.sendBroadcast(new Intent(oldAction));
+                        mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL);
                     }
                     mLastBroadcastState = mDockState;
                     action = UiModeManager.ACTION_ENTER_DESK_MODE;
@@ -431,7 +432,7 @@
                 Intent intent = new Intent(action);
                 intent.putExtra("enableFlags", enableFlags);
                 intent.putExtra("disableFlags", disableFlags);
-                mContext.sendOrderedBroadcast(intent, null,
+                mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
                         mResultReceiver, null, Activity.RESULT_OK, null, null);
                 // Attempting to make this transition a little more clean, we are going
                 // to hold off on doing a configuration change until we have finished
diff --git a/services/java/com/android/server/UpdateLockService.java b/services/java/com/android/server/UpdateLockService.java
index 1ffd196..0f778cd 100644
--- a/services/java/com/android/server/UpdateLockService.java
+++ b/services/java/com/android/server/UpdateLockService.java
@@ -27,6 +27,7 @@
 import android.os.SystemClock;
 import android.os.TokenWatcher;
 import android.os.UpdateLock;
+import android.os.UserHandle;
 import android.util.Slog;
 
 import java.io.FileDescriptor;
@@ -78,7 +79,7 @@
                     .putExtra(UpdateLock.NOW_IS_CONVENIENT, state)
                     .putExtra(UpdateLock.TIMESTAMP, System.currentTimeMillis())
                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            mContext.sendStickyBroadcast(intent);
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
         } finally {
             Binder.restoreCallingIdentity(oldIdent);
         }
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 7ed698b..643e937 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -869,7 +869,7 @@
         }
         wallpaper.callbacks.finishBroadcast();
         final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
-        mContext.sendBroadcast(intent);
+        mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
     }
 
     private void checkPermission(String permission) {
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index b0dfa80..e836e77 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -713,6 +713,9 @@
         if (DEBUG_SERVICE) Slog.v(TAG, "retrieveServiceLocked: " + service
                 + " type=" + resolvedType + " callingUid=" + callingUid);
 
+        userId = mAm.handleIncomingUserLocked(callingPid, callingUid, userId,
+                false, true, "service", null);
+
         if (service.getComponent() != null) {
             r = mServiceMap.getServiceByName(service.getComponent(), userId);
         }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a061d58..eee818b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -494,6 +494,14 @@
         }
 
         @Override
+        protected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {
+            if (userId == UserHandle.USER_ALL || userId == filter.owningUserId) {
+                return super.newResult(filter, match, userId);
+            }
+            return null;
+        }
+
+        @Override
         protected BroadcastFilter[] newArray(int size) {
             return new BroadcastFilter[size];
         }
@@ -505,12 +513,14 @@
     };
 
     /**
-     * State of all active sticky broadcasts.  Keys are the action of the
+     * State of all active sticky broadcasts per user.  Keys are the action of the
      * sticky Intent, values are an ArrayList of all broadcasted intents with
-     * that action (which should usually be one).
+     * that action (which should usually be one).  The SparseArray is keyed
+     * by the user ID the sticky is for, and can include UserHandle.USER_ALL
+     * for stickies that are sent to all users.
      */
-    final HashMap<String, ArrayList<Intent>> mStickyBroadcasts =
-            new HashMap<String, ArrayList<Intent>>();
+    final SparseArray<HashMap<String, ArrayList<Intent>>> mStickyBroadcasts =
+            new SparseArray<HashMap<String, ArrayList<Intent>>>();
 
     final ActiveServices mServices;
 
@@ -2353,24 +2363,8 @@
             String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
         enforceNotIsolatedCaller("startActivity");
         if (userId != UserHandle.getCallingUserId()) {
-            // Requesting a different user, make sure that they have the permission
-            if (checkComponentPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
-                    == PackageManager.PERMISSION_GRANTED) {
-                // Translate to the current user id, if caller wasn't aware
-                if (userId == UserHandle.USER_CURRENT) {
-                    userId = mCurrentUserId;
-                }
-            } else {
-                String msg = "Permission Denial: "
-                        + "Request to startActivity as user " + userId
-                        + " but is calling from user " + UserHandle.getCallingUserId()
-                        + "; this requires "
-                        + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            }
+            userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+                    false, true, "startActivity", null);
         } else {
             if (intent.getCategories() != null
                     && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
@@ -3605,7 +3599,7 @@
             intent.putExtra("reason", reason);
         }
         mWindowManager.closeSystemDialogs(reason);
-        
+
         for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
             ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
             if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
@@ -3613,10 +3607,15 @@
                         Activity.RESULT_CANCELED, null, "close-sys");
             }
         }
-        
-        broadcastIntentLocked(null, null, intent, null,
-                null, 0, null, null, null, false, false, -1,
-                callingUid, 0 /* TODO: Verify */);
+
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            broadcastIntentLocked(null, null, intent, null,
+                    null, 0, null, null, null, false, false, -1,
+                    callingUid, UserHandle.USER_ALL);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
     }
 
     public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
@@ -3836,6 +3835,11 @@
             didSomething = true;
         }
 
+        if (name == null) {
+            // Remove all sticky broadcasts from this user.
+            mStickyBroadcasts.remove(userId);
+        }
+
         ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
         for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(userId).values()) {
             if ((name == null || provider.info.packageName.equals(name))
@@ -7573,10 +7577,10 @@
                             };
                         }
                         Slog.i(TAG, "Sending system update to: " + intent.getComponent());
-                        /* TODO: Send this to all users */
+                        // 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,
-                                0 /* UserId zero */);
+                                UserHandle.USER_ALL);
                         if (finisher != null) {
                             mWaitingUpdate = true;
                         }
@@ -9521,35 +9525,37 @@
         needSep = true;
         
         if (!onlyHistory && mStickyBroadcasts != null && dumpPackage == null) {
-            if (needSep) {
-                pw.println();
-            }
-            needSep = true;
-            pw.println("  Sticky broadcasts:");
-            StringBuilder sb = new StringBuilder(128);
-            for (Map.Entry<String, ArrayList<Intent>> ent
-                    : mStickyBroadcasts.entrySet()) {
-                pw.print("  * Sticky action "); pw.print(ent.getKey());
-                if (dumpAll) {
-                    pw.println(":");
-                    ArrayList<Intent> intents = ent.getValue();
-                    final int N = intents.size();
-                    for (int i=0; i<N; i++) {
-                        sb.setLength(0);
-                        sb.append("    Intent: ");
-                        intents.get(i).toShortString(sb, false, true, false, false);
-                        pw.println(sb.toString());
-                        Bundle bundle = intents.get(i).getExtras();
-                        if (bundle != null) {
-                            pw.print("      ");
-                            pw.println(bundle.toString());
+            for (int user=0; user<mStickyBroadcasts.size(); user++) {
+                if (needSep) {
+                    pw.println();
+                }
+                needSep = true;
+                pw.print("  Sticky broadcasts for user ");
+                        pw.print(mStickyBroadcasts.keyAt(user)); pw.println(":");
+                StringBuilder sb = new StringBuilder(128);
+                for (Map.Entry<String, ArrayList<Intent>> ent
+                        : mStickyBroadcasts.valueAt(user).entrySet()) {
+                    pw.print("  * Sticky action "); pw.print(ent.getKey());
+                    if (dumpAll) {
+                        pw.println(":");
+                        ArrayList<Intent> intents = ent.getValue();
+                        final int N = intents.size();
+                        for (int i=0; i<N; i++) {
+                            sb.setLength(0);
+                            sb.append("    Intent: ");
+                            intents.get(i).toShortString(sb, false, true, false, false);
+                            pw.println(sb.toString());
+                            Bundle bundle = intents.get(i).getExtras();
+                            if (bundle != null) {
+                                pw.print("      ");
+                                pw.println(bundle.toString());
+                            }
                         }
+                    } else {
+                        pw.println("");
                     }
-                } else {
-                    pw.println("");
                 }
             }
-            needSep = true;
         }
         
         if (!onlyHistory && dumpAll) {
@@ -10710,6 +10716,47 @@
         }
     }
 
+    int handleIncomingUserLocked(int callingPid, int callingUid, int userId, boolean allowAll,
+            boolean requireFull, String name, String callerPackage) {
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        if (callingUserId != userId) {
+            if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
+                if ((requireFull || checkComponentPermission(
+                        android.Manifest.permission.INTERACT_ACROSS_USERS,
+                        callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED)
+                        && checkComponentPermission(
+                                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                                callingPid, callingUid, -1, true)
+                                != PackageManager.PERMISSION_GRANTED) {
+                    if (userId == UserHandle.USER_CURRENT_OR_SELF) {
+                        // In this case, they would like to just execute as their
+                        // owner user instead of failing.
+                        userId = callingUserId;
+                    } else {
+                        String msg = "Permission Denial: " + name + " from " + callerPackage
+                                + " asks to run as user " + userId
+                                + " but is calling from user " + UserHandle.getUserId(callingUid)
+                                + "; this requires "
+                                + (requireFull
+                                        ? android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
+                                        : android.Manifest.permission.INTERACT_ACROSS_USERS);
+                        Slog.w(TAG, msg);
+                        throw new SecurityException(msg);
+                    }
+                }
+            }
+            if (userId == UserHandle.USER_CURRENT
+                    || userId == UserHandle.USER_CURRENT_OR_SELF) {
+                userId = mCurrentUserId;
+            }
+            if (!allowAll && userId < 0) {
+                throw new IllegalArgumentException(
+                        "Call does not support special user #" + userId);
+            }
+        }
+        return userId;
+    }
+
     boolean isSingleton(String componentProcessName, ApplicationInfo aInfo,
             String className, int flags) {
         boolean result = false;
@@ -10748,26 +10795,6 @@
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
-        if (userId != UserHandle.getCallingUserId()) {
-            // Requesting a different user, make sure that they have permission
-            if (checkComponentPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
-                    == PackageManager.PERMISSION_GRANTED) {
-                // Translate to the current user id, if caller wasn't aware
-                if (userId == UserHandle.USER_CURRENT) {
-                    userId = mCurrentUserId;
-                }
-            } else {
-                String msg = "Permission Denial: Request to bindService as user " + userId
-                        + " but is calling from user " + UserHandle.getCallingUserId()
-                        + "; this requires "
-                        + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            }
-        }
-
         synchronized(this) {
             return mServices.bindServiceLocked(caller, token, service, resolvedType,
                     connection, flags, userId);
@@ -10954,9 +10981,13 @@
     // =========================================================
 
     private final List getStickiesLocked(String action, IntentFilter filter,
-            List cur) {
+            List cur, int userId) {
         final ContentResolver resolver = mContext.getContentResolver();
-        final ArrayList<Intent> list = mStickyBroadcasts.get(action);
+        HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+        if (stickies == null) {
+            return cur;
+        }
+        final ArrayList<Intent> list = stickies.get(action);
         if (list == null) {
             return cur;
         }
@@ -11026,10 +11057,16 @@
             if (actions != null) {
                 while (actions.hasNext()) {
                     String action = (String)actions.next();
-                    allSticky = getStickiesLocked(action, filter, allSticky);
+                    allSticky = getStickiesLocked(action, filter, allSticky,
+                            UserHandle.USER_ALL);
+                    allSticky = getStickiesLocked(action, filter, allSticky,
+                            UserHandle.getUserId(callingUid));
                 }
             } else {
-                allSticky = getStickiesLocked(null, filter, allSticky);
+                allSticky = getStickiesLocked(null, filter, allSticky,
+                        UserHandle.USER_ALL);
+                allSticky = getStickiesLocked(null, filter, allSticky,
+                        UserHandle.getUserId(callingUid));
             }
 
             // The first sticky in the list is returned directly back to
@@ -11144,10 +11181,10 @@
         }
     }
     
-    private final void sendPackageBroadcastLocked(int cmd, String[] packages) {
+    private final void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
         for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
             ProcessRecord r = mLruProcesses.get(i);
-            if (r.thread != null) {
+            if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
                 try {
                     r.thread.dispatchPackageBroadcast(cmd, packages);
                 } catch (RemoteException ex) {
@@ -11155,7 +11192,67 @@
             }
         }
     }
-    
+
+    private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
+            int[] users) {
+        List<ResolveInfo> receivers = null;
+        try {
+            HashSet<ComponentName> singleUserReceivers = null;
+            boolean scannedFirstReceivers = false;
+            for (int user : users) {
+                List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
+                        .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
+                if (newReceivers != null && newReceivers.size() == 0) {
+                    newReceivers = null;
+                }
+                if (receivers == null) {
+                    receivers = newReceivers;
+                } else if (newReceivers != null) {
+                    // We need to concatenate the additional receivers
+                    // found with what we have do far.  This would be easy,
+                    // but we also need to de-dup any receivers that are
+                    // singleUser.
+                    if (!scannedFirstReceivers) {
+                        // Collect any single user receivers we had already retrieved.
+                        scannedFirstReceivers = true;
+                        for (int i=0; i<receivers.size(); i++) {
+                            ResolveInfo ri = receivers.get(i);
+                            if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+                                ComponentName cn = new ComponentName(
+                                        ri.activityInfo.packageName, ri.activityInfo.name);
+                                if (singleUserReceivers == null) {
+                                    singleUserReceivers = new HashSet<ComponentName>();
+                                }
+                                singleUserReceivers.add(cn);
+                            }
+                        }
+                    }
+                    // Add the new results to the existing results, tracking
+                    // and de-dupping single user receivers.
+                    for (int i=0; i<newReceivers.size(); i++) {
+                        ResolveInfo ri = receivers.get(i);
+                        if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+                            ComponentName cn = new ComponentName(
+                                    ri.activityInfo.packageName, ri.activityInfo.name);
+                            if (singleUserReceivers == null) {
+                                singleUserReceivers = new HashSet<ComponentName>();
+                            }
+                            if (!singleUserReceivers.contains(cn)) {
+                                singleUserReceivers.add(cn);
+                                receivers.add(ri);
+                            }
+                        } else {
+                            receivers.add(ri);
+                        }
+                    }
+                }
+            }
+        } catch (RemoteException ex) {
+            // pm is in same process, this will never happen.
+        }
+        return receivers;
+    }
+
     private final int broadcastIntentLocked(ProcessRecord callerApp,
             String callerPackage, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
@@ -11174,39 +11271,41 @@
             Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
         }
 
-        // If the caller is trying to send this broadcast to a different
-        // user, verify that is allowed.
-        if (UserHandle.getUserId(callingUid) != userId) {
-            if (checkComponentPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS,
-                    callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED
-                    && checkComponentPermission(
-                            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                            callingPid, callingUid, -1, true)
-                            != PackageManager.PERMISSION_GRANTED) {
-                String msg = "Permission Denial: " + intent.getAction()
-                        + " broadcast from " + callerPackage
-                        + " asks to send as user " + userId
-                        + " but is calling from user " + UserHandle.getUserId(callingUid)
-                        + "; this requires "
-                        + android.Manifest.permission.INTERACT_ACROSS_USERS;
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            } else {
-                if (userId == UserHandle.USER_CURRENT) {
-                    userId = mCurrentUserId;
-                }
-            }
-        }
+        userId = handleIncomingUserLocked(callingPid, callingUid, userId,
+                true, false, "broadcast", callerPackage);
 
         // Make sure that the user who is receiving this broadcast is started
         // If not, we will just skip it.
-        if (mStartedUsers.get(userId) == null) {
+        if (userId != UserHandle.USER_ALL && mStartedUsers.get(userId) == null) {
             Slog.w(TAG, "Skipping broadcast of " + intent
                     + ": user " + userId + " is stopped");
             return ActivityManager.BROADCAST_SUCCESS;
         }
 
+        /*
+         * Prevent non-system code (defined here to be non-persistent
+         * processes) from sending protected broadcasts.
+         */
+        if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
+            || callingUid == Process.SHELL_UID || callingUid == Process.BLUETOOTH_UID ||
+            callingUid == 0) {
+            // Always okay.
+        } else if (callerApp == null || !callerApp.persistent) {
+            try {
+                if (AppGlobals.getPackageManager().isProtectedBroadcast(
+                        intent.getAction())) {
+                    String msg = "Permission Denial: not allowed to send broadcast "
+                            + intent.getAction() + " from pid="
+                            + callingPid + ", uid=" + callingUid;
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Remote exception", e);
+                return ActivityManager.BROADCAST_SUCCESS;
+            }
+        }
+
         // Handle special intents: if this broadcast is from the package
         // manager about a package being removed, we need to remove all of
         // its activities from the history stack.
@@ -11231,7 +11330,7 @@
                         }
                     }
                 } else {
-                    // If resources are unvailble just force stop all
+                    // If resources are unavailable just force stop all
                     // those packages and flush the attribute cache as well.
                     if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
                         String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
@@ -11240,7 +11339,7 @@
                                 forceStopPackageLocked(pkg, -1, false, true, true, false, userId);
                             }
                             sendPackageBroadcastLocked(
-                                    IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list);
+                                    IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list, userId);
                         }
                     } else {
                         Uri data = intent.getData();
@@ -11253,7 +11352,7 @@
                             }
                             if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
                                 sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
-                                        new String[] {ssp});
+                                        new String[] {ssp}, userId);
                             }
                         }
                     }
@@ -11297,30 +11396,6 @@
             mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY, proxy));
         }
 
-        /*
-         * Prevent non-system code (defined here to be non-persistent
-         * processes) from sending protected broadcasts.
-         */
-        if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
-            || callingUid == Process.SHELL_UID || callingUid == Process.BLUETOOTH_UID ||
-            callingUid == 0) {
-            // Always okay.
-        } else if (callerApp == null || !callerApp.persistent) {
-            try {
-                if (AppGlobals.getPackageManager().isProtectedBroadcast(
-                        intent.getAction())) {
-                    String msg = "Permission Denial: not allowed to send broadcast "
-                            + intent.getAction() + " from pid="
-                            + callingPid + ", uid=" + callingUid;
-                    Slog.w(TAG, msg);
-                    throw new SecurityException(msg);
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Remote exception", e);
-                return ActivityManager.BROADCAST_SUCCESS;
-            }
-        }
-        
         // Add to the sticky list if requested.
         if (sticky) {
             if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
@@ -11341,10 +11416,38 @@
                 throw new SecurityException(
                         "Sticky broadcasts can't target a specific component");
             }
-            ArrayList<Intent> list = mStickyBroadcasts.get(intent.getAction());
+            // We use userId directly here, since the "all" target is maintained
+            // as a separate set of sticky broadcasts.
+            if (userId != UserHandle.USER_ALL) {
+                // But first, if this is not a broadcast to all users, then
+                // make sure it doesn't conflict with an existing broadcast to
+                // all users.
+                HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
+                        UserHandle.USER_ALL);
+                if (stickies != null) {
+                    ArrayList<Intent> list = stickies.get(intent.getAction());
+                    if (list != null) {
+                        int N = list.size();
+                        int i;
+                        for (i=0; i<N; i++) {
+                            if (intent.filterEquals(list.get(i))) {
+                                throw new IllegalArgumentException(
+                                        "Sticky broadcast " + intent + " for user "
+                                        + userId + " conflicts with existing global broadcast");
+                            }
+                        }
+                    }
+                }
+            }
+            HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+            if (stickies == null) {
+                stickies = new HashMap<String, ArrayList<Intent>>();
+                mStickyBroadcasts.put(userId, stickies);
+            }
+            ArrayList<Intent> list = stickies.get(intent.getAction());
             if (list == null) {
                 list = new ArrayList<Intent>();
-                mStickyBroadcasts.put(intent.getAction(), list);
+                stickies.put(intent.getAction(), list);
             }
             int N = list.size();
             int i;
@@ -11360,22 +11463,29 @@
             }
         }
 
+        int[] users;
+        if (userId == UserHandle.USER_ALL) {
+            // Caller wants broadcast to go to all started users.
+            users = new int[mStartedUsers.size()];
+            for (int i=0; i<mStartedUsers.size(); i++) {
+                users[i] = mStartedUsers.keyAt(i);
+            }
+        } else {
+            // Caller wants broadcast to go to one specific user.
+            users = new int[] {userId};
+        }
+
         // Figure out who all will receive this broadcast.
         List receivers = null;
         List<BroadcastFilter> registeredReceivers = null;
-        try {
-            // Need to resolve the intent to interested receivers...
-            if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
-                     == 0) {
-                receivers = AppGlobals.getPackageManager().queryIntentReceivers(
-                        intent, resolvedType, STOCK_PM_FLAGS, userId);
-            }
-            if (intent.getComponent() == null) {
-                registeredReceivers = mReceiverResolver.queryIntent(intent,
-                        resolvedType, false, userId);
-            }
-        } catch (RemoteException ex) {
-            // pm is in same process, this will never happen.
+        // Need to resolve the intent to interested receivers...
+        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                 == 0) {
+            receivers = collectReceiverComponents(intent, resolvedType, users);
+        }
+        if (intent.getComponent() == null) {
+            registeredReceivers = mReceiverResolver.queryIntent(intent,
+                    resolvedType, false, userId);
         }
 
         final boolean replacePending =
@@ -11568,13 +11678,15 @@
         }
     }
 
-    // TODO: Use the userId; maybe mStickyBroadcasts need to be tied to the user.
     public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
+        userId = handleIncomingUserLocked(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, true, false, "removeStickyBroadcast", null);
+
         synchronized(this) {
             if (checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -11585,15 +11697,24 @@
                 Slog.w(TAG, msg);
                 throw new SecurityException(msg);
             }
-            ArrayList<Intent> list = mStickyBroadcasts.get(intent.getAction());
-            if (list != null) {
-                int N = list.size();
-                int i;
-                for (i=0; i<N; i++) {
-                    if (intent.filterEquals(list.get(i))) {
-                        list.remove(i);
-                        break;
+            HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+            if (stickies != null) {
+                ArrayList<Intent> list = stickies.get(intent.getAction());
+                if (list != null) {
+                    int N = list.size();
+                    int i;
+                    for (i=0; i<N; i++) {
+                        if (intent.filterEquals(list.get(i))) {
+                            list.remove(i);
+                            break;
+                        }
                     }
+                    if (list.size() <= 0) {
+                        stickies.remove(intent.getAction());
+                    }
+                }
+                if (stickies.size() <= 0) {
+                    mStickyBroadcasts.remove(userId);
                 }
             }
         }
@@ -11924,12 +12045,12 @@
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                         | Intent.FLAG_RECEIVER_REPLACE_PENDING);
                 broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
-                        null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
+                        null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
                 if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
                     broadcastIntentLocked(null, null,
                             new Intent(Intent.ACTION_LOCALE_CHANGED),
                             null, null, 0, null, null,
-                            null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
+                            null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
                 }
             }
         }
@@ -13625,7 +13746,8 @@
             // Inform of user switch
             Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
             addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-            mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
+            mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
+                    android.Manifest.permission.MANAGE_USERS);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index 4e6d0fa..b0ccd8f 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import android.content.IntentFilter;
+import android.os.UserHandle;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 
@@ -28,6 +29,7 @@
     final String packageName;
     final String requiredPermission;
     final int owningUid;
+    final int owningUserId;
 
     BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
             String _packageName, String _requiredPermission, int _owningUid) {
@@ -36,6 +38,7 @@
         packageName = _packageName;
         requiredPermission = _requiredPermission;
         owningUid = _owningUid;
+        owningUserId = UserHandle.getUserId(owningUid);
     }
     
     public void dump(PrintWriter pw, String prefix) {
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index e4f1f7a..79fb458 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -43,6 +43,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
 
@@ -417,7 +418,7 @@
         broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, activeList);
         broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
                 erroredList);
-        mContext.sendStickyBroadcast(broadcast);
+        mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
         if (DBG) {
             Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
                     activeList.size() + ", " + erroredList.size());
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index c2c0a71..bb11fe7 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -47,6 +47,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.provider.Telephony.Carriers;
@@ -1092,7 +1093,7 @@
             // send an intent to notify that the GPS is receiving fixes.
             Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
             intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
-            mContext.sendBroadcast(intent);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
             updateStatus(LocationProvider.AVAILABLE, mSvCount);
         }
 
@@ -1150,7 +1151,7 @@
                 // send an intent to notify that the GPS has been enabled or disabled.
                 Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
                 intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
-                mContext.sendBroadcast(intent);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
             }
         }
     }
@@ -1202,7 +1203,7 @@
             // send an intent to notify that the GPS is no longer receiving fixes.
             Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
             intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
-            mContext.sendBroadcast(intent);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
             updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
         }
     }
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index a7cba5a..c6e66cf 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1231,7 +1231,7 @@
         if (mRestrictBackground) {
             final Intent broadcast = new Intent(
                     ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
-            mContext.sendBroadcast(broadcast);
+            mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
         }
     }
 
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index ffe2a3d..f2d2fb7 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -99,6 +99,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.telephony.PhoneStateListener;
@@ -989,7 +990,8 @@
         // finally, dispatch updated event to any listeners
         final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
         updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mContext.sendBroadcast(updatedIntent, READ_NETWORK_USAGE_HISTORY);
+        mContext.sendBroadcastAsUser(updatedIntent, UserHandle.ALL,
+                READ_NETWORK_USAGE_HISTORY);
     }
 
     /**
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index fb04d0f..492158d 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -538,9 +538,10 @@
         if (userInfo != null) {
             Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
             addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
-            mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
             mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_BOOT_COMPLETED),
                     new UserHandle(userInfo.id));
+            mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
+                    android.Manifest.permission.MANAGE_USERS);
         }
         return userInfo;
     }
diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java
index 75f8445..ce1e147 100644
--- a/services/java/com/android/server/power/Notifier.java
+++ b/services/java/com/android/server/power/Notifier.java
@@ -30,6 +30,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.WorkSource;
 import android.util.EventLog;
 import android.util.Slog;
@@ -361,7 +362,7 @@
         }
 
         if (ActivityManagerNative.isSystemReady()) {
-            mContext.sendOrderedBroadcast(mScreenOnIntent, null,
+            mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
                     mWakeUpBroadcastDone, mHandler, 0, null, null);
         } else {
             EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
@@ -403,7 +404,7 @@
         }
 
         if (ActivityManagerNative.isSystemReady()) {
-            mContext.sendOrderedBroadcast(mScreenOffIntent, null,
+            mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, null,
                     mGoToSleepBroadcastDone, mHandler, 0, null, null);
         } else {
             EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
diff --git a/services/java/com/android/server/power/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java
index a3770d7..c7f7390 100644
--- a/services/java/com/android/server/power/ShutdownThread.java
+++ b/services/java/com/android/server/power/ShutdownThread.java
@@ -37,6 +37,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.os.Vibrator;
 import android.os.SystemVibrator;
 import android.os.storage.IMountService;
@@ -296,8 +297,8 @@
         
         // First send the high-level shut down broadcast.
         mActionDone = false;
-        mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
-                br, mHandler, 0, null, null);
+        mContext.sendOrderedBroadcastAsUser(new Intent(Intent.ACTION_SHUTDOWN),
+                UserHandle.ALL, null, br, mHandler, 0, null, null);
         
         final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
         synchronized (mActionDoneSync) {
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index ddecf14..607ff39 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -41,6 +41,7 @@
 import android.os.Parcelable;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
 import android.os.SystemClock;
@@ -540,7 +541,7 @@
                 }
             }
 
-            mContext.sendStickyBroadcast(intent);
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
         }
 
         private void updateAudioSourceFunction() {
@@ -563,7 +564,7 @@
                         Slog.e(TAG, "could not open audio source PCM file", e);
                     }
                 }
-                mContext.sendStickyBroadcast(intent);
+                mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
                 mAudioSourceEnabled = enabled;
             }
         }
diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index 9b3459b..a8453d3 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -35,6 +35,7 @@
 import android.os.Binder;
 import android.os.FileUtils;
 import android.os.Process;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.Xml;
@@ -546,7 +547,7 @@
         }
 
         // Send broadcast to running activity with registered intent
-        mContext.sendBroadcast(intent);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
 
         // Start activity with registered intent
         resolveActivity(intent, matches, defaultPackage, device, null);
@@ -559,7 +560,7 @@
         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
         if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
-        mContext.sendBroadcast(intent);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     public void accessoryAttached(UsbAccessory accessory) {
@@ -586,7 +587,7 @@
         Intent intent = new Intent(
                 UsbManager.ACTION_USB_ACCESSORY_DETACHED);
         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        mContext.sendBroadcast(intent);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,