Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af6ec296 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

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
parent 36a832dd
Loading
Loading
Loading
Loading
+31 −20
Original line number Diff line number Diff line
@@ -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<UserInfo> 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) {
+33 −19
Original line number Diff line number Diff line
@@ -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.
            // 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
        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;
        }
        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,
+1 −0
Original line number Diff line number Diff line
@@ -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,
+2 −0
Original line number Diff line number Diff line
@@ -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);
    }