Make AppWidgets encryption-aware.

Only parse and load AppWidget configuration details after a user has
been unlocked.  Yell loudly if someone accidentally tries loading
data for a locked user.

Tidy up protected broadcast logic a bit more to handle persistent
processes.  Add backwards compatible behavior for APPWIDGET_UPDATE
broadcast simliar to APPWIDGET_CONFIGURE, since some apps are sending
it to themselves.

Add hidden USER_HANDLE extra to a handful of broadcasts to make
logic more consistent.

Bug: 26247049, 26219971
Change-Id: I54e4f2e343488571f9baa1a316962f41186c1a2c
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index df18d3e..946b233 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.server.appwidget;
 
+import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -76,6 +77,7 @@
 import android.view.WindowManager;
 import android.widget.RemoteViews;
 
+import com.android.internal.R;
 import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.os.BackgroundThread;
@@ -83,7 +85,6 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.widget.IRemoteViewsAdapterConnection;
 import com.android.internal.widget.IRemoteViewsFactory;
-import com.android.internal.R;
 import com.android.server.LocalServices;
 import com.android.server.WidgetBackupProvider;
 
@@ -139,8 +140,10 @@
     private static final int CURRENT_VERSION = 1;
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
         public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
+            final String action = intent.getAction();
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
 
             if (DEBUG) {
                 Slog.i(TAG, "Received broadcast: " + action);
@@ -148,23 +151,16 @@
 
             if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
                 onConfigurationChanged();
-            } else if (Intent.ACTION_USER_STARTED.equals(action)) {
-                onUserStarted(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                        UserHandle.USER_NULL));
+            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+                onUserUnlocked(userId);
             } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
-                onUserStopped(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                        UserHandle.USER_NULL));
+                onUserStopped(userId);
             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                refreshProfileWidgetsMaskedState(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                        UserHandle.USER_NULL));
+                refreshProfileWidgetsMaskedState(userId);
             } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED.equals(action)) {
-                UserHandle profile = (UserHandle)intent.getParcelableExtra(Intent.EXTRA_USER);
-                if (profile != null) {
-                    refreshWidgetMaskedState(profile.getIdentifier());
-                }
+                refreshWidgetMaskedState(userId);
             } else {
-                onPackageBroadcastReceived(intent, intent.getIntExtra(
-                        Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
+                onPackageBroadcastReceived(intent, userId);
             }
         }
     };
@@ -263,7 +259,7 @@
                 sdFilter, null, null);
 
         IntentFilter userFilter = new IntentFilter();
-        userFilter.addAction(Intent.ACTION_USER_STARTED);
+        userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         userFilter.addAction(Intent.ACTION_USER_STOPPED);
         userFilter.addAction(Intent.ACTION_USER_SWITCHED);
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
@@ -340,6 +336,8 @@
     }
 
     private void onPackageBroadcastReceived(Intent intent, int userId) {
+        if (!isUserRunningAndUnlocked(userId)) return;
+
         final String action = intent.getAction();
         boolean added = false;
         boolean changed = false;
@@ -419,9 +417,7 @@
      * Refresh the masked state for all profiles under the given user.
      */
     private void refreshProfileWidgetsMaskedState(int userId) {
-        if (userId == UserHandle.USER_NULL) {
-            return;
-        }
+        if (!isUserRunningAndUnlocked(userId)) return;
         List<UserInfo> profiles = mUserManager.getEnabledProfiles(userId);
         if (profiles != null) {
             for (int i = 0; i < profiles.size(); i++) {
@@ -435,6 +431,7 @@
      * Mask/unmask widgets in the given profile, depending on the quiet state of the profile.
      */
     private void refreshWidgetMaskedState(int profileId) {
+        if (!isUserRunningAndUnlocked(profileId)) return;
         final long identity = Binder.clearCallingIdentity();
         try {
             UserInfo user  = mUserManager.getUserInfo(profileId);
@@ -484,6 +481,11 @@
     }
 
     private void ensureGroupStateLoadedLocked(int userId) {
+        if (!isUserRunningAndUnlocked(userId)) {
+            throw new IllegalStateException(
+                    "User " + userId + " must be unlocked for widgets to be available");
+        }
+
         final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
 
         // Careful lad, we may have already loaded the state for some
@@ -2321,7 +2323,7 @@
         }
     }
 
-    private void onUserStarted(int userId) {
+    private void onUserUnlocked(int userId) {
         synchronized (mLock) {
             ensureGroupStateLoadedLocked(userId);
 
@@ -2510,6 +2512,15 @@
         mWidgetPackages.clear();
     }
 
+    private boolean isUserRunningAndUnlocked(int userId) {
+        if (userId == UserHandle.USER_NULL) {
+            return false;
+        } else {
+            return mContext.getSystemService(ActivityManager.class)
+                    .isUserRunningAndUnlocked(userId);
+        }
+    }
+
     @Override
     public boolean isBoundWidgetPackage(String packageName, int userId) {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b805c10..1d555c6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17061,6 +17061,8 @@
             }
         }
 
+        // Verify that protected broadcasts are only being sent by system code,
+        // and that system code is only sending protected broadcasts.
         final String action = intent.getAction();
         final boolean isProtectedBroadcast;
         try {
@@ -17070,35 +17072,47 @@
             return ActivityManager.BROADCAST_SUCCESS;
         }
 
-        /*
-         * Prevent non-system code (defined here to be non-persistent
-         * processes) from sending protected broadcasts.
-         */
-        int callingAppId = UserHandle.getAppId(callingUid);
-        if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
-            || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
-            || callingAppId == Process.NFC_UID || callingUid == 0) {
-            // Always okay.
+        final boolean isCallerSystem;
+        switch (UserHandle.getAppId(callingUid)) {
+            case Process.ROOT_UID:
+            case Process.SYSTEM_UID:
+            case Process.PHONE_UID:
+            case Process.SHELL_UID:
+            case Process.BLUETOOTH_UID:
+            case Process.NFC_UID:
+                isCallerSystem = true;
+                break;
+            default:
+                isCallerSystem = (callerApp != null) && callerApp.persistent;
+                break;
+        }
 
-            // Yell if the system is trying to send a non-protected broadcast.
-            // The vast majority of broadcasts sent from system callers should
-            // be protected to avoid security holes, so exceptions here should
-            // be incredibly rare.
-            if (!isProtectedBroadcast
-                    && !Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
-                // TODO: eventually switch over to hard throw
+        if (isCallerSystem) {
+            if (isProtectedBroadcast
+                    || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+                    || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
+                    || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
+                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
+                // Broadcast is either protected, or it's a public action that
+                // we've relaxed, so it's fine for system internals to send.
+            } else {
+                // The vast majority of broadcasts sent from system internals
+                // should be protected to avoid security holes, so yell loudly
+                // to ensure we examine these cases.
                 Log.wtf(TAG, "Sending non-protected broadcast " + action
                         + " from system", new Throwable());
             }
 
-        } else if (callerApp == null || !callerApp.persistent) {
+        } else {
             if (isProtectedBroadcast) {
                 String msg = "Permission Denial: not allowed to send broadcast "
                         + action + " from pid="
                         + callingPid + ", uid=" + callingUid;
                 Slog.w(TAG, msg);
                 throw new SecurityException(msg);
-            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)) {
+
+            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
+                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
                 // Special case for compatibility: we don't want apps to send this,
                 // but historically it has not been protected and apps may be using it
                 // to poke their own app widget.  So, instead of making it protected,
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 62e78a4..e49bc3c 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -245,6 +245,7 @@
                 mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0));
 
                 final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
+                unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 unlockedIntent.addFlags(
                         Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
                 mService.broadcastIntentLocked(null, null, unlockedIntent, null, null, 0, null,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f5da103..13f4826 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -526,6 +526,7 @@
         if (parentHandle != null) {
             intent = new Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
             intent.putExtra(Intent.EXTRA_USER, profileHandle);
+            intent.putExtra(Intent.EXTRA_USER_HANDLE, profileHandle.getIdentifier());
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             intent.putExtras(extras);
             mContext.sendBroadcastAsUser(intent, parentHandle);
@@ -2073,6 +2074,7 @@
         managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
                 Intent.FLAG_RECEIVER_FOREGROUND);
         managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(removedUserId));
+        managedProfileIntent.putExtra(Intent.EXTRA_USER_HANDLE, removedUserId);
         mContext.sendBroadcastAsUser(managedProfileIntent, new UserHandle(parentUserId), null);
     }