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

Commit 0f3307a7 authored by Jackal Guo's avatar Jackal Guo
Browse files

Introduce DistractingPackageHelper

Move the implementaion of handling distracting packages out of the
PackageManagerService.

Fix: 225784320
Test: atest CtsSuspendAppsTestCases:DistractingPackageTest
Test: atest CtsSuspendAppsPermissionTestCases:NegativePermissionsTest
Test: atest CtsAppEnumerationTestCases:AppEnumerationTests
Test: atest FrameworksServicesTests:PackageManagerSettingsTests
Test: atest FrameworksServicesTests:PackageUserStateTest
Change-Id: I4e44c1f039318bf02d00ff6a6b8b973244108008
Merged-In: I4e44c1f039318bf02d00ff6a6b8b973244108008
parent 127afbda
Loading
Loading
Loading
Loading
+0 −11
Original line number Diff line number Diff line
@@ -320,17 +320,6 @@ public final class BroadcastHelper {
                broadcastAllowlist, null);
    }

    public void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId,
            int distractionFlags) {
        final Bundle extras = new Bundle(3);
        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
        extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
        sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras,
                Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null, null,
                null);
    }

    public void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
            int[] userIds, int[] instantUserIds) {
        sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
+198 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm;

import static android.content.pm.PackageManager.RESTRICTION_NONE;

import android.annotation.NonNull;
import android.content.Intent;
import android.content.pm.PackageManager.DistractionRestriction;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;

import com.android.internal.util.ArrayUtils;
import com.android.server.pm.pkg.PackageStateInternal;

import java.util.ArrayList;
import java.util.List;

/**
 * Mark, unmark, or remove any {@link DistractionRestriction restrictions} set on given packages.
 */
public final class DistractingPackageHelper {

    // TODO(b/198166813): remove PMS dependency
    private final PackageManagerService mPm;
    private final PackageManagerServiceInjector mInjector;
    private final BroadcastHelper mBroadcastHelper;
    private final SuspendPackageHelper mSuspendPackageHelper;

    /**
     * Constructor for {@link PackageManagerService}.
     */
    DistractingPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector,
            BroadcastHelper broadcastHelper, SuspendPackageHelper suspendPackageHelper) {
        mPm = pm;
        mInjector = injector;
        mBroadcastHelper = broadcastHelper;
        mSuspendPackageHelper = suspendPackageHelper;
    }

    /**
     * Mark or unmark the given packages as distracting to the given user.
     *
     * @param packageNames Packages to mark as distracting.
     * @param restrictionFlags Any combination of restrictions to impose on the given packages.
     *                         {@link DistractionRestriction#RESTRICTION_NONE} can be used to
     *                         clear any existing restrictions.
     * @param userId the user for which changes are taking place.
     * @param callingUid The caller's uid.
     *
     * @return A list of packages that could not have the {@code restrictionFlags} set. The system
     * may prevent restricting critical packages to preserve normal device function.
     */
    String[] setDistractingPackageRestrictionsAsUser(@NonNull Computer snapshot,
            String[] packageNames, int restrictionFlags, int userId, int callingUid) {
        if (ArrayUtils.isEmpty(packageNames)) {
            return packageNames;
        }
        if (restrictionFlags != RESTRICTION_NONE
                && !mSuspendPackageHelper.isSuspendAllowedForUser(snapshot, userId, callingUid)) {
            Slog.w(PackageManagerService.TAG,
                    "Cannot restrict packages due to restrictions on user " + userId);
            return packageNames;
        }

        final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
        final IntArray changedUids = new IntArray(packageNames.length);
        final List<String> unactionedPackages = new ArrayList<>(packageNames.length);

        final ArraySet<String> changesToCommit = new ArraySet<>();
        final boolean[] canRestrict = (restrictionFlags != RESTRICTION_NONE)
                ? mSuspendPackageHelper.canSuspendPackageForUser(snapshot, packageNames, userId,
                callingUid) : null;
        for (int i = 0; i < packageNames.length; i++) {
            final String packageName = packageNames[i];
            final PackageStateInternal packageState =
                    snapshot.getPackageStateInternal(packageName);
            if (packageState == null
                    || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
                Slog.w(PackageManagerService.TAG,
                        "Could not find package setting for package: " + packageName
                                + ". Skipping...");
                unactionedPackages.add(packageName);
                continue;
            }
            if (canRestrict != null && !canRestrict[i]) {
                unactionedPackages.add(packageName);
                continue;
            }
            final int oldDistractionFlags = packageState.getUserStateOrDefault(userId)
                    .getDistractionFlags();
            if (restrictionFlags != oldDistractionFlags) {
                changedPackagesList.add(packageName);
                changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
                changesToCommit.add(packageName);
            }
        }

        mPm.commitPackageStateMutation(null /* initialState */, mutator -> {
            final int size = changesToCommit.size();
            for (int index = 0; index < size; index++) {
                mutator.forPackage(changesToCommit.valueAt(index))
                        .userState(userId)
                        .setDistractionFlags(restrictionFlags);
            }
        });

        if (!changedPackagesList.isEmpty()) {
            final String[] changedPackages = changedPackagesList.toArray(
                    new String[changedPackagesList.size()]);
            sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
                    restrictionFlags);
            mPm.scheduleWritePackageRestrictions(userId);
        }
        return unactionedPackages.toArray(new String[0]);
    }

    /**
     * Removes any {@link DistractionRestriction restrictions} set on given packages.
     *
     * <p> Caller must flush package restrictions if it cares about immediate data consistency.
     *
     * @param packagesToChange The packages on which restrictions are to be removed.
     * @param userId the user for which changes are taking place.
     */
    void removeDistractingPackageRestrictions(@NonNull Computer snapshot,
            String[] packagesToChange, int userId) {
        if (ArrayUtils.isEmpty(packagesToChange)) {
            return;
        }
        final List<String> changedPackages = new ArrayList<>(packagesToChange.length);
        final IntArray changedUids = new IntArray(packagesToChange.length);
        for (int i = 0; i < packagesToChange.length; i++) {
            final String packageName = packagesToChange[i];
            final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName);
            if (ps != null && ps.getUserStateOrDefault(userId).getDistractionFlags()
                    != RESTRICTION_NONE) {
                changedPackages.add(ps.getPackageName());
                changedUids.add(UserHandle.getUid(userId, ps.getAppId()));
            }
        }
        mPm.commitPackageStateMutation(null /* initialState */, mutator -> {
            for (int index = 0; index < changedPackages.size(); index++) {
                mutator.forPackage(changedPackages.get(index))
                        .userState(userId)
                        .setDistractionFlags(RESTRICTION_NONE);
            }
        });

        if (!changedPackages.isEmpty()) {
            final String[] packageArray = changedPackages.toArray(
                    new String[changedPackages.size()]);
            sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId,
                    RESTRICTION_NONE);
            mPm.scheduleWritePackageRestrictions(userId);
        }
    }

    /**
     * Send broadcast intents for packages distracting changes.
     *
     * @param pkgList The names of packages which have suspension changes.
     * @param uidList The uids of packages which have suspension changes.
     * @param userId The user where packages reside.
     */
    void sendDistractingPackagesChanged(@NonNull String[] pkgList,
            int[] uidList, int userId, int distractionFlags) {
        final Bundle extras = new Bundle(3);
        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
        extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);

        final Handler handler = mInjector.getHandler();
        handler.post(() -> mBroadcastHelper.sendPackageBroadcast(
                Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */, extras,
                Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
                null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
                null /* allowList */, null /* bOptions */));
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
    @NonNull protected abstract PackageObserverHelper getPackageObserverHelper();
    @NonNull protected abstract ResolveIntentHelper getResolveIntentHelper();
    @NonNull protected abstract SuspendPackageHelper getSuspendPackageHelper();
    @NonNull protected abstract DistractingPackageHelper getDistractingPackageHelper();
    @NonNull protected abstract ProtectedPackages getProtectedPackages();
    @NonNull protected abstract UserNeedsBadgingCache getUserNeedsBadging();
    @NonNull protected abstract InstantAppRegistry getInstantAppRegistry();
@@ -248,8 +249,8 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
    @Override
    @Deprecated
    public final void removeDistractingPackageRestrictions(String packageName, int userId) {
        mService.removeDistractingPackageRestrictions(snapshot(), new String[]{packageName},
                userId);
        getDistractingPackageHelper().removeDistractingPackageRestrictions(snapshot(),
                new String[]{packageName}, userId);
    }

    @Override
+25 −100
Original line number Diff line number Diff line
@@ -159,7 +159,6 @@ import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.ExceptionUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -939,6 +938,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
    private final ResolveIntentHelper mResolveIntentHelper;
    private final DexOptHelper mDexOptHelper;
    private final SuspendPackageHelper mSuspendPackageHelper;
    private final DistractingPackageHelper mDistractingPackageHelper;
    private final IntentResolverInterceptor mIntentResolverInterceptor;
    private final StorageEventHelper mStorageEventHelper;

@@ -1683,6 +1683,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
        mResolveIntentHelper = testParams.resolveIntentHelper;
        mDexOptHelper = testParams.dexOptHelper;
        mSuspendPackageHelper = testParams.suspendPackageHelper;
        mDistractingPackageHelper = testParams.distractingPackageHelper;

        mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);

@@ -1842,6 +1843,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
                mProtectedPackages);
        mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper,
                mRemovePackageHelper);
        mDistractingPackageHelper = new DistractingPackageHelper(this, mInjector, mBroadcastHelper,
                mSuspendPackageHelper);

        synchronized (mLock) {
            // Create the computer as soon as the state objects have been installed.  The
@@ -3065,43 +3068,19 @@ public class PackageManagerService implements PackageSender, TestUtilityService

    void removeAllDistractingPackageRestrictions(@NonNull Computer snapshot, int userId) {
        final String[] allPackages = snapshot.getAllAvailablePackageNames();
        removeDistractingPackageRestrictions(snapshot, allPackages, userId);
        mDistractingPackageHelper.removeDistractingPackageRestrictions(snapshot, allPackages,
                userId);
    }

    /**
     * Removes any {@link android.content.pm.PackageManager.DistractionRestriction restrictions}
     * set on given packages.
     *
     * <p> Caller must flush package restrictions if it cares about immediate data consistency.
     *
     * @param packagesToChange The packages on which restrictions are to be removed.
     * @param userId the user for which changes are taking place.
     */
    void removeDistractingPackageRestrictions(@NonNull Computer snapshot,
            String[] packagesToChange, int userId) {
        final List<String> changedPackages = new ArrayList<>();
        final IntArray changedUids = new IntArray();
        for (String packageName : packagesToChange) {
            final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName);
            if (ps != null && ps.getUserStateOrDefault(userId).getDistractionFlags() != 0) {
                changedPackages.add(ps.getPackageName());
                changedUids.add(UserHandle.getUid(userId, ps.getAppId()));
            }
        }
        commitPackageStateMutation(null, mutator -> {
            for (int index = 0; index < changedPackages.size(); index++) {
                mutator.forPackage(changedPackages.get(index))
                        .userState(userId)
                        .setDistractionFlags(0);
            }
        });
    private void enforceCanSetDistractingPackageRestrictionsAsUser(@NonNull Computer snapshot,
            int callingUid, int userId, String callingMethod) {
        mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
                callingMethod);

        if (!changedPackages.isEmpty()) {
            final String[] packageArray = changedPackages.toArray(
                    new String[changedPackages.size()]);
            mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
                    packageArray, changedUids.toArray(), userId, 0));
            scheduleWritePackageRestrictions(userId);
        if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
                && UserHandle.getUserId(callingUid) != userId) {
            throw new SecurityException("Calling uid " + callingUid + " cannot call for user "
                    + userId);
        }
    }

@@ -5591,73 +5570,13 @@ public class PackageManagerService implements PackageSender, TestUtilityService
        @Override
        public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
                int restrictionFlags, int userId) {
            mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
                    "setDistractingPackageRestrictionsAsUser");

            final int callingUid = Binder.getCallingUid();
            if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
                    && UserHandle.getUserId(callingUid) != userId) {
                throw new SecurityException("Calling uid " + callingUid + " cannot call for user "
                        + userId);
            }
            Objects.requireNonNull(packageNames, "packageNames cannot be null");
            final Computer snapshot = snapshotComputer();
            if (restrictionFlags != 0
                    && !mSuspendPackageHelper.isSuspendAllowedForUser(snapshot, userId,
                    callingUid)) {
                Slog.w(PackageManagerService.TAG, "Cannot restrict packages due to restrictions on user " + userId);
                return packageNames;
            }

            final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
            final IntArray changedUids = new IntArray(packageNames.length);
            final List<String> unactionedPackages = new ArrayList<>(packageNames.length);

            ArraySet<String> changesToCommit = new ArraySet<>();
            final boolean[] canRestrict = (restrictionFlags != 0)
                    ? mSuspendPackageHelper.canSuspendPackageForUser(snapshot, packageNames, userId,
                    callingUid) : null;
            for (int i = 0; i < packageNames.length; i++) {
                final String packageName = packageNames[i];
                final PackageStateInternal packageState =
                        snapshot.getPackageStateInternal(packageName);
                if (packageState == null
                        || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
                    Slog.w(PackageManagerService.TAG, "Could not find package setting for package: " + packageName
                            + ". Skipping...");
                    unactionedPackages.add(packageName);
                    continue;
                }
                if (canRestrict != null && !canRestrict[i]) {
                    unactionedPackages.add(packageName);
                    continue;
                }
                final int oldDistractionFlags = packageState.getUserStateOrDefault(userId)
                        .getDistractionFlags();
                if (restrictionFlags != oldDistractionFlags) {
                    changedPackagesList.add(packageName);
                    changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
                    changesToCommit.add(packageName);
                }
            }

            commitPackageStateMutation(null, mutator -> {
                final int size = changesToCommit.size();
                for (int index = 0; index < size; index++) {
                    mutator.forPackage(changesToCommit.valueAt(index))
                            .userState(userId)
                            .setDistractionFlags(restrictionFlags);
                }
            });

            if (!changedPackagesList.isEmpty()) {
                final String[] changedPackages = changedPackagesList.toArray(
                        new String[changedPackagesList.size()]);
                mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
                        changedPackages, changedUids.toArray(), userId, restrictionFlags));
                scheduleWritePackageRestrictions(userId);
            }
            return unactionedPackages.toArray(new String[0]);
            enforceCanSetDistractingPackageRestrictionsAsUser(snapshot, callingUid, userId,
                    "setDistractingPackageRestrictionsAsUser");
            Objects.requireNonNull(packageNames, "packageNames cannot be null");
            return mDistractingPackageHelper.setDistractingPackageRestrictionsAsUser(snapshot,
                    packageNames, restrictionFlags, userId, callingUid);
        }

        @Override
@@ -6119,6 +6038,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService
            return mSuspendPackageHelper;
        }

        @NonNull
        @Override
        protected DistractingPackageHelper getDistractingPackageHelper() {
            return mDistractingPackageHelper;
        }

        @NonNull
        @Override
        protected ProtectedPackages getProtectedPackages() {
+1 −0
Original line number Diff line number Diff line
@@ -116,4 +116,5 @@ public final class PackageManagerServiceTestParams {
    public DexOptHelper dexOptHelper;
    public SuspendPackageHelper suspendPackageHelper;
    public StorageEventHelper storageEventHelper;
    public DistractingPackageHelper distractingPackageHelper;
}