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

Commit d0902057 authored by Rubin Xu's avatar Rubin Xu
Browse files

Mask widgets for suspended packages.

Bug: 22776761
Change-Id: I271257e765a2297c780ac2bd37426d8fb451e5d9
parent 4b72e453
Loading
Loading
Loading
Loading
+179 −49
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -86,6 +85,7 @@ import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;
import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
import com.android.server.policy.IconUtilities;

import libcore.io.IoUtils;

@@ -104,6 +104,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -155,9 +156,17 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
                onUserStopped(userId);
            } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                refreshProfileWidgetsMaskedState(userId);
                reloadWidgetsMaskedStateForUser(userId);
            } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED.equals(action)) {
                refreshWidgetMaskedState(userId);
                synchronized (mLock) {
                    reloadWidgetQuietModeMaskedStateLocked(userId);
                }
            } else if (Intent.ACTION_PACKAGES_SUSPENDED.equals(action)) {
                String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                updateWidgetPackageSuspensionMaskedState(packages, true, getSendingUserId());
            } else if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(action)) {
                String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                updateWidgetPackageSuspensionMaskedState(packages, false, getSendingUserId());
            } else {
                onPackageBroadcastReceived(intent, userId);
            }
@@ -206,6 +215,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    private boolean mSafeMode;
    private int mMaxWidgetBitmapMemory;

    private final IconUtilities mIconUtilities;

    AppWidgetServiceImpl(Context context) {
        mContext = context;
        mPackageManager = AppGlobals.getPackageManager();
@@ -216,6 +227,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
        mBackupRestoreController = new BackupRestoreController();
        mSecurityPolicy = new SecurityPolicy();
        mIconUtilities = new IconUtilities(context);

        computeMaximumWidgetBitmapMemory();
        registerBroadcastReceiver();
@@ -268,6 +280,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
                offModeFilter, null, null);

        IntentFilter suspendPackageFilter = new IntentFilter();
        suspendPackageFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
        suspendPackageFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
                suspendPackageFilter, null, null);
    }

    private void registerOnCrossProfileProvidersChangedListener() {
@@ -413,15 +431,20 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    }

    /**
     * Refresh the masked state for all profiles under the given user.
     * Reload all widgets' masked state for the given user and its associated profiles, including
     * due to quiet mode and package suspension.
     */
    private void refreshProfileWidgetsMaskedState(int userId) {
    private void reloadWidgetsMaskedStateForUser(int userId) {
        if (!mUserManager.isUserUnlocked(userId)) return;
        synchronized (mLock) {
            reloadWidgetPackageSuspensionMaskedStateLocked(userId);
            List<UserInfo> profiles = mUserManager.getEnabledProfiles(userId);
            if (profiles != null) {
                for (int i = 0; i < profiles.size(); i++) {
                    UserInfo user  = profiles.get(i);
                refreshWidgetMaskedState(user.id);
                    reloadWidgetQuietModeMaskedStateLocked(user.id);
                    reloadWidgetPackageSuspensionMaskedStateLocked(user.id);
                }
            }
        }
    }
@@ -429,7 +452,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) {
    private void reloadWidgetQuietModeMaskedStateLocked(int profileId) {
        if (!mUserManager.isUserUnlocked(profileId)) return;
        final long identity = Binder.clearCallingIdentity();
        try {
@@ -438,30 +461,120 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                return;
            }
            boolean shouldMask = user.isQuietModeEnabled();
            final int iconSize = (int) mContext.getResources().getDimension(
                    android.R.dimen.app_icon_size);
            final int N = mProviders.size();
            for (int i = 0; i < N; i++) {
                Provider provider = mProviders.get(i);
                int providerUserId = provider.getUserId();
                if (providerUserId != profileId) {
                    continue;
                }
                if (provider.setMaskedByQuietProfileLocked(shouldMask)) {
                    if (provider.isMaskedLocked()) {
                        maskWidgetsViewsLocked(provider);
                    } else {
                        unmaskWidgetsViewsLocked(provider);
                    }
                }
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    /**
     * Reload widget's masked state due to package suspension state.
     */
    private void reloadWidgetPackageSuspensionMaskedStateLocked(int profileId) {
        final int N = mProviders.size();
        for (int i = 0; i < N; i++) {
            Provider provider = mProviders.get(i);
            int providerUserId = provider.getUserId();
            if (providerUserId != profileId) {
                continue;
            }
            try {
                ApplicationInfo ai = mPackageManager.getApplicationInfo(
                        provider.info.provider.getPackageName(), 0, provider.getUserId());
                boolean suspended = (ai.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
                if (provider.setMaskedBySuspendedPackageLocked(suspended)) {
                    if (provider.isMaskedLocked()) {
                        maskWidgetsViewsLocked(provider);
                    } else {
                        unmaskWidgetsViewsLocked(provider);
                    }
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to query application info", e);
            }
        }
    }

    /**
     * Incrementally update the masked state due to package suspension state.
     */
    private void updateWidgetPackageSuspensionMaskedState(String[] packagesArray, boolean suspended,
            int profileId) {
        if (packagesArray == null) {
            return;
        }
        Set<String> packages = new ArraySet<String>(Arrays.asList(packagesArray));
        synchronized (mLock) {
            final int N = mProviders.size();
            for (int i = 0; i < N; i++) {
                Provider provider = mProviders.get(i);
                int providerUserId = provider.getUserId();
                    if (providerUserId == profileId) {
                if (providerUserId != profileId
                        || !packages.contains(provider.info.provider.getPackageName())) {
                    continue;
                }
                if (provider.setMaskedBySuspendedPackageLocked(suspended)) {
                    if (provider.isMaskedLocked()) {
                        maskWidgetsViewsLocked(provider);
                    } else {
                        unmaskWidgetsViewsLocked(provider);
                    }
                }
            }
        }
    }

    private void maskWidgetsViewsLocked(Provider provider) {
        Bitmap iconBitmap = null;
        try {
            // Load the unbadged application icon and pass it to the widget to appear on
            // the masked view.
            final String providerPackage = provider.info.provider.getPackageName();
            Context userContext = mContext.createPackageContextAsUser(providerPackage, 0,
                    UserHandle.of(provider.getUserId()));
            PackageManager pm = userContext.getPackageManager();
            Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm);
            // Create a bitmap of the icon which is what the widget's remoteview requires.
            iconBitmap = mIconUtilities.createIconBitmap(icon);
        } catch (NameNotFoundException e) {
            Slog.e(TAG, "Fail to get application icon", e);
            // Provider package removed, no need to mask its views as its state will be
            // purged very soon.
            return;
        }

        final int widgetCount = provider.widgets.size();
        for (int j = 0; j < widgetCount; j++) {
            Widget widget = provider.widgets.get(j);
                            if (shouldMask) {
                                widget.replaceWithMaskedViewsLocked(mContext, iconSize);
                            } else {
                                widget.clearMaskedViewsLocked();
                            }
            if (widget.replaceWithMaskedViewsLocked(mContext, iconBitmap)) {
                scheduleNotifyUpdateAppWidgetLocked(widget,
                        widget.getEffectiveViewsLocked());
            }
        }
    }

    private void unmaskWidgetsViewsLocked(Provider provider) {
        final int widgetCount = provider.widgets.size();
        for (int j = 0; j < widgetCount; j++) {
            Widget widget = provider.widgets.get(j);
            if (widget.clearMaskedViewsLocked()) {
                scheduleNotifyUpdateAppWidgetLocked(widget,
                        widget.getEffectiveViewsLocked());
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

@@ -3442,6 +3555,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        PendingIntent broadcast;
        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it

        boolean maskedByQuietProfile;
        boolean maskedBySuspendedPackage;

        int tag = TAG_UNDEFINED; // for use while saving state (the index)

        public int getUserId() {
@@ -3470,6 +3586,24 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        public String toString() {
            return "Provider{" + id + (zombie ? " Z" : "") + '}';
        }

        // returns true if the provider's masked state is changed as a result
        public boolean setMaskedByQuietProfileLocked(boolean masked) {
            boolean oldMaskedState = isMaskedLocked();
            maskedByQuietProfile = masked;
            return isMaskedLocked() != oldMaskedState;
        }

        // returns true if the provider's masked state is changed as a result
        public boolean setMaskedBySuspendedPackageLocked(boolean masked) {
            boolean oldMaskedState = isMaskedLocked();
            maskedBySuspendedPackage = masked;
            return isMaskedLocked() != oldMaskedState;
        }

        public boolean isMaskedLocked() {
            return maskedByQuietProfile || maskedBySuspendedPackage;
        }
    }

    private static final class ProviderId {
@@ -3625,28 +3759,24 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}';
        }

        public void replaceWithMaskedViewsLocked(Context context, int iconSize) {
        private boolean replaceWithMaskedViewsLocked(Context context, Bitmap icon) {
            if (maskedViews != null) {
                return;
                return false;
            }
            maskedViews = new RemoteViews(context.getPackageName(), R.layout.work_widget_mask_view);
            try {
                Drawable icon = context.getPackageManager().getApplicationIcon(
                        provider.info.provider.getPackageName());
                final int width = iconSize;
                final int height = iconSize;
                Bitmap iconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(iconBitmap);
                icon.setBounds(0, 0, width, height);
                icon.draw(canvas);
                maskedViews.setImageViewBitmap(R.id.work_widget_app_icon, iconBitmap);
            } catch (NameNotFoundException e) {
                Slog.e(TAG, "Fail to get application icon", e);
            if (icon != null) {
                maskedViews.setImageViewBitmap(R.id.work_widget_app_icon, icon);
            }
            return true;
        }

        public void clearMaskedViewsLocked() {
        private boolean clearMaskedViewsLocked() {
            if (maskedViews != null) {
                maskedViews = null;
                return true;
            } else {
                return false;
            }
        }

        public RemoteViews getEffectiveViewsLocked() {
+2 −2
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ import android.content.Context;
/**
 * Various utilities shared amongst the Launcher's classes.
 */
final class IconUtilities {
public final class IconUtilities {
    private static final String TAG = "IconUtilities";

    private static final int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff };
@@ -102,7 +102,7 @@ final class IconUtilities {
     * Returns a bitmap suitable for the all apps view.  The bitmap will be a power
     * of two sized ARGB_8888 bitmap that can be used as a gl texture.
     */
    private Bitmap createIconBitmap(Drawable icon) {
    public Bitmap createIconBitmap(Drawable icon) {
        int width = mIconWidth;
        int height = mIconHeight;