diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index df18d3ea92056..946b2339db4b1 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.Display; 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.os.SomeArgs; 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku * 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 profiles = mUserManager.getEnabledProfiles(userId); if (profiles != null) { for (int i = 0; i < profiles.size(); i++) { @@ -435,6 +431,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku * 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - private void onUserStarted(int userId) { + private void onUserUnlocked(int userId) { synchronized (mLock) { ensureGroupStateLoadedLocked(userId); @@ -2510,6 +2512,15 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku 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 b805c103f0113..1d555c6491abf 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 @@ public final class ActivityManagerService extends ActivityManagerNative } } + // 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 62e78a456c98a..e49bc3c6c3c36 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 @@ final class UserController { 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 f5da1032c718d..13f48263b69e8 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 @@ public class UserManagerService extends IUserManager.Stub { 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 @@ public class UserManagerService extends IUserManager.Stub { 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); }