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

Commit efcadc7b authored by Pavel Grafov's avatar Pavel Grafov Committed by Automerger Merge Worker
Browse files

Merge "Suspend newly installed apps when personal apps are suspended" into...

Merge "Suspend newly installed apps when personal apps are suspended" into rvc-dev am: 70b1c45a am: 82625f9d am: 6c474bf7

Change-Id: I4402032a8fcc8ea5b6f07ce7a2b7ae21d77e2830
parents 2a8a2a14 6c474bf7
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -2429,7 +2429,7 @@ public class DevicePolicyManager {
            PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT
            PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT
    })
    })
    @Retention(RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface PersonalAppSuspensionReason {}
    public @interface PersonalAppsSuspensionReason {}
    /**
    /**
     * Return true if the given administrator component is currently active (enabled) in the system.
     * Return true if the given administrator component is currently active (enabled) in the system.
@@ -11961,7 +11961,7 @@ public class DevicePolicyManager {
     *     {@link #PERSONAL_APPS_NOT_SUSPENDED} if apps are not suspended.
     *     {@link #PERSONAL_APPS_NOT_SUSPENDED} if apps are not suspended.
     * @see #setPersonalAppsSuspended
     * @see #setPersonalAppsSuspended
     */
     */
    public @PersonalAppSuspensionReason int getPersonalAppsSuspendedReasons(
    public @PersonalAppsSuspensionReason int getPersonalAppsSuspendedReasons(
            @NonNull ComponentName admin) {
            @NonNull ComponentName admin) {
        throwIfParentInstance("getPersonalAppsSuspendedReasons");
        throwIfParentInstance("getPersonalAppsSuspendedReasons");
        if (mService != null) {
        if (mService != null) {
+2 −2
Original line number Original line Diff line number Diff line
@@ -7088,7 +7088,7 @@ public abstract class PackageManager {
     * Returns any packages in a given set of packages that cannot be suspended via a call to {@link
     * Returns any packages in a given set of packages that cannot be suspended via a call to {@link
     * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
     * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
     * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
     * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
     * packages to keep the device in a functioning state, e.g. the default dialer.
     * packages to keep the device in a functioning state, e.g. the default dialer and launcher.
     * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this API.
     * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this API.
     *
     *
     * <p>
     * <p>
@@ -7106,7 +7106,7 @@ public abstract class PackageManager {
    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
    @NonNull
    @NonNull
    public String[] getUnsuspendablePackages(@NonNull String[] packageNames) {
    public String[] getUnsuspendablePackages(@NonNull String[] packageNames) {
        throw new UnsupportedOperationException("canSuspendPackages not implemented");
        throw new UnsupportedOperationException("getUnsuspendablePackages not implemented");
    }
    }


    /**
    /**
+39 −9
Original line number Original line Diff line number Diff line
@@ -136,7 +136,7 @@ import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManager.PersonalAppSuspensionReason;
import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DeviceStateCache;
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.FactoryResetProtectionPolicy;
@@ -935,10 +935,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                }
                }
            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                handlePackagesChanged(null /* check all admins */, userHandle);
                handlePackagesChanged(null /* check all admins */, userHandle);
            } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
            } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
                    || (Intent.ACTION_PACKAGE_ADDED.equals(action)
                    && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))) {
                handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
                handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                    handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
                } else {
                    handleNewPackageInstalled(intent.getData().getSchemeSpecificPart(), userHandle);
                }
            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
                    && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                    && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
                handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
@@ -2028,6 +2032,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        return false;
        return false;
    }
    }
    private void handleNewPackageInstalled(String packageName, int userHandle) {
        // If personal apps were suspended by the admin, suspend the newly installed one.
        if (!getUserData(userHandle).mAppsSuspended) {
            return;
        }
        final String[] packagesToSuspend = { packageName };
        // Check if package is considered not suspendable?
        if (mInjector.getPackageManager(userHandle)
                .getUnsuspendablePackages(packagesToSuspend).length != 0) {
            Slog.i(LOG_TAG, "Newly installed package is unsuspendable: " + packageName);
            return;
        }
        try {
            mIPackageManager.setPackagesSuspendedAsUser(packagesToSuspend, true /*suspend*/,
                    null, null, null, PLATFORM_PACKAGE_NAME, userHandle);
        } catch (RemoteException ignored) {
            // shouldn't happen.
        }
    }
    /**
    /**
     * Unit test will subclass it to inject mocks.
     * Unit test will subclass it to inject mocks.
     */
     */
@@ -2110,6 +2134,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            return mContext.getPackageManager();
            return mContext.getPackageManager();
        }
        }
        PackageManager getPackageManager(int userId) {
            return mContext
                    .createContextAsUser(UserHandle.of(userId), 0 /* flags */).getPackageManager();
        }
        PowerManagerInternal getPowerManagerInternal() {
        PowerManagerInternal getPowerManagerInternal() {
            return LocalServices.getService(PowerManagerInternal.class);
            return LocalServices.getService(PowerManagerInternal.class);
        }
        }
@@ -15650,7 +15679,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
    }
    }
    @Override
    @Override
    public @PersonalAppSuspensionReason int getPersonalAppsSuspendedReasons(ComponentName who) {
    public @PersonalAppsSuspensionReason int getPersonalAppsSuspendedReasons(ComponentName who) {
        synchronized (getLockObject()) {
        synchronized (getLockObject()) {
            final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
            final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
                    DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER,
                    DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER,
@@ -15669,7 +15698,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        }
        }
    }
    }
    private @PersonalAppSuspensionReason int makeSuspensionReasons(
    private @PersonalAppsSuspensionReason int makeSuspensionReasons(
            boolean explicit, boolean timeout) {
            boolean explicit, boolean timeout) {
        int result = PERSONAL_APPS_NOT_SUSPENDED;
        int result = PERSONAL_APPS_NOT_SUSPENDED;
        if (explicit) {
        if (explicit) {
@@ -15793,7 +15822,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
    }
    }
    private void applyPersonalAppsSuspension(
    private void applyPersonalAppsSuspension(
            int profileUserId, @PersonalAppSuspensionReason int suspensionState) {
            int profileUserId, @PersonalAppsSuspensionReason int suspensionState) {
        final boolean suspended = getUserData(UserHandle.USER_SYSTEM).mAppsSuspended;
        final boolean suspended = getUserData(UserHandle.USER_SYSTEM).mAppsSuspended;
        final boolean shouldSuspend = suspensionState != PERSONAL_APPS_NOT_SUSPENDED;
        final boolean shouldSuspend = suspensionState != PERSONAL_APPS_NOT_SUSPENDED;
        if (suspended != shouldSuspend) {
        if (suspended != shouldSuspend) {
@@ -15813,8 +15842,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        mInjector.binderWithCleanCallingIdentity(() -> {
        mInjector.binderWithCleanCallingIdentity(() -> {
            try {
            try {
                final String[] appsToSuspend =
                final String[] appsToSuspend =
                        new PersonalAppsSuspensionHelper(mContext, mInjector.getPackageManager())
                        new PersonalAppsSuspensionHelper(
                                .getPersonalAppsForSuspension(userId);
                                mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */))
                                .getPersonalAppsForSuspension();
                final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser(
                final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser(
                        appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId);
                        appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId);
                if (!ArrayUtils.isEmpty(failedPackages)) {
                if (!ArrayUtils.isEmpty(failedPackages)) {
+30 −54
Original line number Original line Diff line number Diff line
@@ -20,7 +20,6 @@ import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL


import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
@@ -32,7 +31,7 @@ import android.os.IBinder;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.provider.Settings;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.Log;
import android.util.ArraySet;
import android.util.Slog;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -43,7 +42,6 @@ import com.android.server.inputmethod.InputMethodManagerInternal;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.List;
import java.util.Set;
import java.util.Set;


@@ -56,18 +54,21 @@ public class PersonalAppsSuspensionHelper {
    private final Context mContext;
    private final Context mContext;
    private final PackageManager mPackageManager;
    private final PackageManager mPackageManager;


    public PersonalAppsSuspensionHelper(Context context, PackageManager packageManager) {
    /**
     * @param context Context for the user whose apps should to be suspended.
     */
    public PersonalAppsSuspensionHelper(Context context) {
        mContext = context;
        mContext = context;
        mPackageManager = packageManager;
        mPackageManager = context.getPackageManager();
    }
    }


    /**
    /**
     * @return List of packages that should be suspended to limit personal use.
     * @return List of packages that should be suspended to limit personal use.
     */
     */
    String[] getPersonalAppsForSuspension(@UserIdInt int userId) {
    String[] getPersonalAppsForSuspension() {
        final List<PackageInfo> installedPackageInfos =
        final List<PackageInfo> installedPackageInfos =
                mPackageManager.getInstalledPackagesAsUser(0 /* flags */, userId);
                mPackageManager.getInstalledPackages(0 /* flags */);
        final Set<String> result = new HashSet<>();
        final Set<String> result = new ArraySet<>();
        for (final PackageInfo packageInfo : installedPackageInfos) {
        for (final PackageInfo packageInfo : installedPackageInfos) {
            final ApplicationInfo info = packageInfo.applicationInfo;
            final ApplicationInfo info = packageInfo.applicationInfo;
            if ((!info.isSystemApp() && !info.isUpdatedSystemApp())
            if ((!info.isSystemApp() && !info.isUpdatedSystemApp())
@@ -77,11 +78,15 @@ public class PersonalAppsSuspensionHelper {
        }
        }
        result.removeAll(getCriticalPackages());
        result.removeAll(getCriticalPackages());
        result.removeAll(getSystemLauncherPackages());
        result.removeAll(getSystemLauncherPackages());
        result.removeAll(getAccessibilityServices(userId));
        result.removeAll(getAccessibilityServices());
        result.removeAll(getInputMethodPackages(userId));
        result.removeAll(getInputMethodPackages());
        result.remove(getActiveLauncherPackages(userId));
        result.remove(getSettingsPackageName());
        result.remove(getDialerPackage(userId));

        result.remove(getSettingsPackageName(userId));
        final String[] unsuspendablePackages =
                mPackageManager.getUnsuspendablePackages(result.toArray(new String[0]));
        for (final String pkg : unsuspendablePackages) {
            result.remove(pkg);
        }


        Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result));
        Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result));
        return result.toArray(new String[0]);
        return result.toArray(new String[0]);
@@ -104,7 +109,6 @@ public class PersonalAppsSuspensionHelper {
                final ApplicationInfo applicationInfo =
                final ApplicationInfo applicationInfo =
                        mPackageManager.getApplicationInfo(packageName, 0);
                        mPackageManager.getApplicationInfo(packageName, 0);
                if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) {
                if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) {
                    Log.d(LOG_TAG, "Not suspending system launcher package: " + packageName);
                    result.add(packageName);
                    result.add(packageName);
                }
                }
            } catch (PackageManager.NameNotFoundException e) {
            } catch (PackageManager.NameNotFoundException e) {
@@ -114,81 +118,53 @@ public class PersonalAppsSuspensionHelper {
        return result;
        return result;
    }
    }


    private List<String> getAccessibilityServices(int userId) {
    private List<String> getAccessibilityServices() {
        final List<AccessibilityServiceInfo> accessibilityServiceInfos =
        final List<AccessibilityServiceInfo> accessibilityServiceInfos =
                getAccessibilityManagerForUser(userId)
                getAccessibilityManagerForUser(mContext.getUserId())
                        .getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
                        .getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
        final List<String> result = new ArrayList<>();
        final List<String> result = new ArrayList<>();
        for (final AccessibilityServiceInfo serviceInfo : accessibilityServiceInfos) {
        for (final AccessibilityServiceInfo serviceInfo : accessibilityServiceInfos) {
            final ComponentName componentName =
            final ComponentName componentName =
                    ComponentName.unflattenFromString(serviceInfo.getId());
                    ComponentName.unflattenFromString(serviceInfo.getId());
            if (componentName != null) {
            if (componentName != null) {
                final String packageName = componentName.getPackageName();
                result.add(componentName.getPackageName());
                Slog.d(LOG_TAG, "Not suspending a11y service: " + packageName);
                result.add(packageName);
            }
            }
        }
        }
        return result;
        return result;
    }
    }


    private List<String> getInputMethodPackages(int userId) {
    private List<String> getInputMethodPackages() {
        final List<InputMethodInfo> enabledImes =
        final List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get()
                InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId);
                .getEnabledInputMethodListAsUser(mContext.getUserId());
        final List<String> result = new ArrayList<>();
        final List<String> result = new ArrayList<>();
        for (final InputMethodInfo info : enabledImes) {
        for (final InputMethodInfo info : enabledImes) {
            Slog.d(LOG_TAG, "Not suspending IME: " + info.getPackageName());
            result.add(info.getPackageName());
            result.add(info.getPackageName());
        }
        }
        return result;
        return result;
    }
    }


    @Nullable
    @Nullable
    private String getActiveLauncherPackages(int userId) {
    private String getSettingsPackageName() {
        final Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        return getPackageNameForIntent("active launcher", intent, userId);
    }

    @Nullable
    private String getSettingsPackageName(int userId) {
        final Intent intent = new Intent(Settings.ACTION_SETTINGS);
        final Intent intent = new Intent(Settings.ACTION_SETTINGS);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        return getPackageNameForIntent("settings", intent, userId);
        final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, /* flags= */ 0);
    }

    @Nullable
    private String getDialerPackage(int userId) {
        final Intent intent = new Intent(Intent.ACTION_DIAL);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        return getPackageNameForIntent("dialer", intent, userId);
    }

    @Nullable
    private String getPackageNameForIntent(String name, Intent intent, int userId) {
        final ResolveInfo resolveInfo =
                mPackageManager.resolveActivityAsUser(intent, /* flags= */ 0, userId);
        if (resolveInfo != null) {
        if (resolveInfo != null) {
            final String packageName = resolveInfo.activityInfo.packageName;
            return resolveInfo.activityInfo.packageName;
            Slog.d(LOG_TAG, "Not suspending " + name + " package: " + packageName);
            return packageName;
        }
        }
        return null;
        return null;
    }
    }


    private List<String> getCriticalPackages() {
    private List<String> getCriticalPackages() {
        final List<String> result = Arrays.asList(mContext.getResources()
        return Arrays.asList(mContext.getResources()
                .getStringArray(R.array.config_packagesExemptFromSuspension));
                .getStringArray(R.array.config_packagesExemptFromSuspension));
        Slog.d(LOG_TAG, "Not suspending critical packages: " + String.join(",", result));
        return result;
    }
    }


    private boolean hasLauncherIntent(String packageName) {
    private boolean hasLauncherIntent(String packageName) {
        final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
        final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
        intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
        intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
        intentToResolve.setPackage(packageName);
        intentToResolve.setPackage(packageName);
        final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(
        final List<ResolveInfo> resolveInfos =
                intentToResolve, PackageManager.GET_UNINSTALLED_PACKAGES);
                mPackageManager.queryIntentActivities(intentToResolve, /* flags= */ 0);
        return resolveInfos != null && !resolveInfos.isEmpty();
        return resolveInfos != null && !resolveInfos.isEmpty();
    }
    }