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

Commit 2170ee43 authored by Fyodor Kupolov's avatar Fyodor Kupolov Committed by Android (Google) Code Review
Browse files

Merge "Limit the number of apps running as system user"

parents bd1d652f 7db5af12
Loading
Loading
Loading
Loading
+153 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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 android.content.pm;

import android.Manifest;
import android.app.AppGlobals;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;

import com.android.internal.annotations.VisibleForTesting;

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

/**
 * Helper class for querying installed applications using multiple criteria.
 *
 * @hide
 */
public class AppsQueryHelper {

    /**
     * Return apps without launcher icon
     */
    public static int GET_NON_LAUNCHABLE_APPS = 1;

    /**
     * Return apps with {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission
     */
    public static int GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM = 1 << 1;

    private final Context mContext;
    private List<ApplicationInfo> mAllApps;

    public AppsQueryHelper(Context context) {
        mContext = context;
    }

    /**
     * Return a List of all packages that satisfy a specified criteria.
     * @param flags search flags. Use any combination of {@link #GET_NON_LAUNCHABLE_APPS},
     * {@link #GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM}
     * @param systemAppsOnly if true, only system apps will be returned
     * @param user user, whose apps are queried
     */
    public List<String> queryApps(int flags, boolean systemAppsOnly, UserHandle user) {
        boolean nonLaunchableApps = (flags & GET_NON_LAUNCHABLE_APPS) > 0;
        boolean interactAcrossUsers = (flags & GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM) > 0;
        if (mAllApps == null) {
            mAllApps = getAllApps(user.getIdentifier());
        }

        List<String> result = new ArrayList<>();
        if (flags == 0) {
            final int allAppsSize = mAllApps.size();
            for (int i = 0; i < allAppsSize; i++) {
                final ApplicationInfo appInfo = mAllApps.get(i);
                if (systemAppsOnly && !appInfo.isSystemApp()) {
                    continue;
                }
                result.add(appInfo.packageName);
            }
            return result;
        }

        if (nonLaunchableApps) {
            Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
            final List<ResolveInfo> resolveInfos = queryIntentActivitiesAsUser(intent,
                    user.getIdentifier());

            ArraySet<String> appsWithLaunchers = new ArraySet<>();
            final int resolveInfosSize = resolveInfos.size();
            for (int i = 0; i < resolveInfosSize; i++) {
                appsWithLaunchers.add(resolveInfos.get(i).activityInfo.packageName);
            }
            final int allAppsSize = mAllApps.size();
            for (int i = 0; i < allAppsSize; i++) {
                final ApplicationInfo appInfo = mAllApps.get(i);
                if (systemAppsOnly && !appInfo.isSystemApp()) {
                    continue;
                }
                final String packageName = appInfo.packageName;
                if (!appsWithLaunchers.contains(packageName)) {
                    result.add(packageName);
                }
            }
        }

        if (interactAcrossUsers) {
            final List<PackageInfo> packagesHoldingPermissions = getPackagesHoldingPermission(
                    Manifest.permission.INTERACT_ACROSS_USERS, user.getIdentifier());
            final int packagesHoldingPermissionsSize = packagesHoldingPermissions.size();
            for (int i = 0; i < packagesHoldingPermissionsSize; i++) {
                PackageInfo packageInfo = packagesHoldingPermissions.get(i);
                if (systemAppsOnly && !packageInfo.applicationInfo.isSystemApp()) {
                    continue;
                }
                if (!result.contains(packageInfo.packageName)) {
                    result.add(packageInfo.packageName);
                }
            }
        }

        return result;
    }

    @VisibleForTesting
    @SuppressWarnings("unchecked")
    protected List<ApplicationInfo> getAllApps(int userId) {
        try {
            return AppGlobals.getPackageManager().getInstalledApplications(
                    PackageManager.GET_UNINSTALLED_PACKAGES
                            | PackageManager.GET_DISABLED_COMPONENTS, userId).getList();
        } catch (RemoteException e) {
            throw new IllegalStateException("Package manager has died", e);
        }
    }

    @VisibleForTesting
    protected List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int userId) {
        return mContext.getPackageManager()
                .queryIntentActivitiesAsUser(intent, PackageManager.GET_DISABLED_COMPONENTS
                        | PackageManager.GET_UNINSTALLED_PACKAGES, userId);
    }

    @VisibleForTesting
    @SuppressWarnings("unchecked")
    protected List<PackageInfo> getPackagesHoldingPermission(String perm, int userId) {
        try {
            return AppGlobals.getPackageManager().getPackagesHoldingPermissions(new String[]{perm},
                    0, userId).getList();
        } catch (RemoteException e) {
            throw new IllegalStateException("Package manager has died", e);
        }
    }
}
+9 −0
Original line number Diff line number Diff line
@@ -128,6 +128,15 @@ public class UserInfo implements Parcelable {
        return (flags & FLAG_DISABLED) != FLAG_DISABLED;
    }

    /**
     * Returns true if the user is a split system user.
     * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
     * the method always returns false.
     */
    public boolean isSystemOnly() {
        return id == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser();
    }

    /**
     * @return true if this user can be switched to.
     **/
+131 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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 android.content.pm;

import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.test.AndroidTestCase;

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

public class AppsQueryHelperTests extends AndroidTestCase {

    private AppsQueryHelper mAppsQueryHelper;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        mAppsQueryHelper = new AppsQueryHelperTestable(getContext());
    }

    public void testQueryAppsSystemAppsOnly() {
        List<String> apps = mAppsQueryHelper.queryApps(0, true, UserHandle.SYSTEM);
        assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app2", "sys_app3"), apps);

        apps = mAppsQueryHelper.queryApps(0, false, UserHandle.SYSTEM);
        assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app2", "sys_app3", "app4"), apps);
    }

    public void testQueryAppsNonLaunchable() {
        List<String> apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS,
                true, UserHandle.SYSTEM);
        assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);

        apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS,
                false, UserHandle.SYSTEM);
        assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);
    }

    public void testQueryAppsInteractAcrossUser() {
        List<String> apps = mAppsQueryHelper.queryApps(
                AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM, true, UserHandle.SYSTEM);
        assertEqualsIgnoreOrder(Arrays.asList("sys_app1"), apps);

        apps = mAppsQueryHelper.queryApps(
                AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM, false, UserHandle.SYSTEM);
        assertEqualsIgnoreOrder(Arrays.asList("sys_app1"), apps);
    }

    public void testQueryApps() {
        List<String> apps = mAppsQueryHelper.queryApps(
                AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
                        |AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM,
                true, UserHandle.SYSTEM);
        assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);

        apps = mAppsQueryHelper.queryApps(
                AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
                        |AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM,
                false, UserHandle.SYSTEM);
        assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);
    }

    private class AppsQueryHelperTestable extends AppsQueryHelper {

        public AppsQueryHelperTestable(Context context) {
            super(context);
        }

        @Override
        protected List<ApplicationInfo> getAllApps(int userId) {
            final ApplicationInfo ai1 = new ApplicationInfo();
            ai1.flags |= ApplicationInfo.FLAG_SYSTEM;
            ai1.packageName = "sys_app1";
            final ApplicationInfo ai2 = new ApplicationInfo();
            ai2.flags |= ApplicationInfo.FLAG_SYSTEM;
            ai2.packageName = "sys_app2";
            ai2.flags |= ApplicationInfo.FLAG_SYSTEM;
            final ApplicationInfo ai3 = new ApplicationInfo();
            ai3.packageName = "sys_app3";
            ai3.flags |= ApplicationInfo.FLAG_SYSTEM;
            final ApplicationInfo ai4 = new ApplicationInfo();
            ai4.packageName = "app4";
            return Arrays.asList(ai1, ai2, ai3, ai4);
        }

        @Override
        protected List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int userId) {
            assertEquals(Intent.CATEGORY_LAUNCHER, intent.getCategories().iterator().next());
            final ResolveInfo r2 = new ResolveInfo();
            r2.activityInfo = new ActivityInfo();
            r2.activityInfo.packageName = "sys_app2";
            r2.activityInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
            final ResolveInfo r4 = new ResolveInfo();
            r4.activityInfo = new ActivityInfo();
            r4.activityInfo.packageName = "app4";
            return Arrays.asList(r2, r4);
        }

        @Override
        protected List<PackageInfo> getPackagesHoldingPermission(String perm, int userId) {
            final PackageInfo p1 = new PackageInfo();
            p1.packageName = "sys_app1";
            p1.applicationInfo = new ApplicationInfo();
            p1.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
            return Arrays.asList(p1);
        }
    }

    private static void assertEqualsIgnoreOrder(List<String> expected, List<String> actual) {
        assertTrue("Lists not equal. Expected " + expected + " but was " + actual,
                (expected.size() == actual.size())
                        && (new HashSet<>(expected).equals(new HashSet<>(actual))));
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -143,4 +143,7 @@
         access while in power save mode, even if they aren't in the foreground. -->
    <allow-in-power-save-except-idle package="com.android.providers.downloads" />

    <!-- These are the packages that shouldn't run as system user -->
    <system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
    <system-user-blacklisted-app package="com.android.settings" />
</permissions>
+32 −1
Original line number Diff line number Diff line
@@ -99,6 +99,12 @@ public class SystemConfig {
    // URL-handling state upon factory reset.
    final ArraySet<String> mLinkedApps = new ArraySet<>();

    // These are the packages that are whitelisted to be able to run as system user
    final ArraySet<String> mSystemUserWhitelistedApps = new ArraySet<>();

    // These are the packages that should not run under system user
    final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();

    public static SystemConfig getInstance() {
        synchronized (SystemConfig.class) {
            if (sInstance == null) {
@@ -144,6 +150,14 @@ public class SystemConfig {
        return mLinkedApps;
    }

    public ArraySet<String> getSystemUserWhitelistedApps() {
        return mSystemUserWhitelistedApps;
    }

    public ArraySet<String> getSystemUserBlacklistedApps() {
        return mSystemUserBlacklistedApps;
    }

    SystemConfig() {
        // Read configuration from system
        readPermissions(Environment.buildPath(
@@ -380,7 +394,24 @@ public class SystemConfig {
                        mLinkedApps.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);

                } else if ("system-user-whitelisted-app".equals(name)) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, "<system-user-whitelisted-app> without package in " + permFile
                                + " at " + parser.getPositionDescription());
                    } else {
                        mSystemUserWhitelistedApps.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                } else if ("system-user-blacklisted-app".equals(name)) {
                    String pkgname = parser.getAttributeValue(null, "package");
                    if (pkgname == null) {
                        Slog.w(TAG, "<system-user-blacklisted-app without package in " + permFile
                                + " at " + parser.getPositionDescription());
                    } else {
                        mSystemUserBlacklistedApps.add(pkgname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                } else {
                    XmlUtils.skipCurrentTag(parser);
                    continue;
Loading