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

Commit b66d65fa authored by Patrick Baumann's avatar Patrick Baumann
Browse files

Whitelist for package broadcasts

This change adds a whitelist of UIDs that package change broadcasts may
be sent to. This must be pre-calculated and stored in memory before the
package is updated or modified as the visibility may change during that
transaction.

Test: atest PackageManagerTests AppsFilterTest AppEnumerationTests
Fixes: 143534210
Change-Id: I003c54528141d8b03033f8241559dd5895df064a
parent 89f01252
Loading
Loading
Loading
Loading
+16 −0
Original line number Original line Diff line number Diff line
@@ -403,4 +403,20 @@ public abstract class ActivityManagerInternal {
     * Called by DevicePolicyManagerService to set the uid of the device owner.
     * Called by DevicePolicyManagerService to set the uid of the device owner.
     */
     */
    public abstract void setDeviceOwnerUid(int uid);
    public abstract void setDeviceOwnerUid(int uid);

    /**
     * Sends a broadcast, assuming the caller to be the system and allowing the inclusion of an
     * approved whitelist of app Ids >= {@link android.os.Process#FIRST_APPLICATION_UID} that the
     * broadcast my be sent to; any app Ids < {@link android.os.Process#FIRST_APPLICATION_UID} are
     * automatically whitelisted.
     *
     * @see com.android.server.am.ActivityManagerService#broadcastIntentWithFeature(
     *      IApplicationThread, String, Intent, String, IIntentReceiver, int, String, Bundle,
     *      String[], int, Bundle, boolean, boolean, int)
     */
    public abstract int broadcastIntent(Intent intent,
            IIntentReceiver resultTo,
            String[] requiredPermissions, boolean serialized,
            int userId, int[] appIdWhitelist);

}
}
+61 −5
Original line number Original line Diff line number Diff line
@@ -15568,7 +15568,7 @@ public class ActivityManagerService extends IActivityManager.Stub
    }
    }
    private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
    private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
            int callingUid, int[] users) {
            int callingUid, int[] users, int[] broadcastWhitelist) {
        // TODO: come back and remove this assumption to triage all broadcasts
        // TODO: come back and remove this assumption to triage all broadcasts
        int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
        int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
@@ -15644,6 +15644,15 @@ public class ActivityManagerService extends IActivityManager.Stub
        } catch (RemoteException ex) {
        } catch (RemoteException ex) {
            // pm is in same process, this will never happen.
            // pm is in same process, this will never happen.
        }
        }
        if (receivers != null && broadcastWhitelist != null) {
            for (int i = receivers.size() - 1; i >= 0; i--) {
                final int uid = receivers.get(i).activityInfo.applicationInfo.uid;
                if (uid >= Process.FIRST_APPLICATION_UID
                        && Arrays.binarySearch(broadcastWhitelist, UserHandle.getAppId(uid)) < 0) {
                    receivers.remove(i);
                }
            }
        }
        return receivers;
        return receivers;
    }
    }
@@ -15738,7 +15747,8 @@ public class ActivityManagerService extends IActivityManager.Stub
        return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent,
        return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent,
                resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
                resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
                appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
                appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
                realCallingPid, userId, false /* allowBackgroundActivityStarts */);
                realCallingPid, userId, false /* allowBackgroundActivityStarts */,
                null /*broadcastWhitelist*/);
    }
    }
    @GuardedBy("this")
    @GuardedBy("this")
@@ -15747,7 +15757,8 @@ public class ActivityManagerService extends IActivityManager.Stub
            IIntentReceiver resultTo, int resultCode, String resultData,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
            int realCallingPid, int userId, boolean allowBackgroundActivityStarts) {
            int realCallingPid, int userId, boolean allowBackgroundActivityStarts,
            @Nullable int[] broadcastWhitelist) {
        intent = new Intent(intent);
        intent = new Intent(intent);
        final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
        final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
@@ -15756,6 +15767,12 @@ public class ActivityManagerService extends IActivityManager.Stub
            intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
            intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
        }
        }
        if (userId == UserHandle.USER_ALL && broadcastWhitelist != null) {
                Slog.e(TAG, "broadcastWhitelist only applies when sending to individual users. "
                        + "Assuming restrictive whitelist.");
                broadcastWhitelist = new int[]{};
        }
        // By default broadcasts do not go to stopped apps.
        // By default broadcasts do not go to stopped apps.
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
@@ -16242,7 +16259,8 @@ public class ActivityManagerService extends IActivityManager.Stub
        // Need to resolve the intent to interested receivers...
        // Need to resolve the intent to interested receivers...
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
                 == 0) {
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
            receivers = collectReceiverComponents(
                    intent, resolvedType, callingUid, users, broadcastWhitelist);
        }
        }
        if (intent.getComponent() == null) {
        if (intent.getComponent() == null) {
            if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
            if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
@@ -16272,6 +16290,17 @@ public class ActivityManagerService extends IActivityManager.Stub
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
                + " replacePending=" + replacePending);
                + " replacePending=" + replacePending);
        if (registeredReceivers != null && broadcastWhitelist != null) {
            // if a uid whitelist was provided, remove anything in the application space that wasn't
            // in it.
            for (int i = registeredReceivers.size() - 1; i >= 0; i--) {
                final int uid = registeredReceivers.get(i).owningUid;
                if (uid >= Process.FIRST_APPLICATION_UID
                        && Arrays.binarySearch(broadcastWhitelist, UserHandle.getAppId(uid)) < 0) {
                    registeredReceivers.remove(i);
                }
            }
        }
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        if (!ordered && NR > 0) {
        if (!ordered && NR > 0) {
@@ -16555,7 +16584,8 @@ public class ActivityManagerService extends IActivityManager.Stub
                return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
                return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
                        resultTo, resultCode, resultData, resultExtras, requiredPermissions,
                        resultTo, resultCode, resultData, resultExtras, requiredPermissions,
                        OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
                        OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
                        realCallingPid, userId, allowBackgroundActivityStarts);
                        realCallingPid, userId, allowBackgroundActivityStarts,
                        null /*broadcastWhitelist*/);
            } finally {
            } finally {
                Binder.restoreCallingIdentity(origId);
                Binder.restoreCallingIdentity(origId);
            }
            }
@@ -19185,6 +19215,32 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
            }
        }
        }
        @Override
        public int broadcastIntent(Intent intent,
                IIntentReceiver resultTo,
                String[] requiredPermissions,
                boolean serialized, int userId, int[] appIdWhitelist) {
            synchronized (ActivityManagerService.this) {
                intent = verifyBroadcastLocked(intent);
                final int callingPid = Binder.getCallingPid();
                final int callingUid = Binder.getCallingUid();
                final long origId = Binder.clearCallingIdentity();
                try {
                    return ActivityManagerService.this.broadcastIntentLocked(null /*callerApp*/,
                            null /*callerPackage*/, null /*callingFeatureId*/, intent,
                            null /*resolvedType*/, resultTo, 0 /*resultCode*/, null /*resultData*/,
                            null /*resultExtras*/, requiredPermissions, AppOpsManager.OP_NONE,
                            null /*options*/, serialized, false /*sticky*/, callingPid, callingUid,
                            callingUid, callingPid, userId, false /*allowBackgroundStarts*/,
                            appIdWhitelist);
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
            }
        }
        @Override
        @Override
        public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
        public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
                boolean fgRequired, String callingPackage, @Nullable String callingFeatureId,
                boolean fgRequired, String callingPackage, @Nullable String callingFeatureId,
+60 −1
Original line number Original line Diff line number Diff line
@@ -55,6 +55,7 @@ import com.android.server.om.OverlayReferenceMapper;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackage;


import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
import java.util.List;
import java.util.Objects;
import java.util.Objects;
import java.util.Set;
import java.util.Set;
@@ -510,6 +511,64 @@ public class AppsFilter {
                && pkgSetting.signatures.mSigningDetails.signaturesMatchExactly(sysSigningDetails);
                && pkgSetting.signatures.mSigningDetails.signaturesMatchExactly(sysSigningDetails);
    }
    }


    private static void sort(int[] uids, int nextUidIndex) {
        Arrays.sort(uids, 0, nextUidIndex);
    }

    /**
     * Fetches all app Ids that a given setting is currently visible to, per provided user. This
     * only includes UIDs >= {@link Process#FIRST_APPLICATION_UID} as all other UIDs can already see
     * all applications.
     *
     * If the setting is visible to all UIDs, null is returned. If an app is not visible to any
     * applications, the int array will be empty.
     *
     * @param users the set of users that should be evaluated for this calculation
     * @param existingSettings the set of all package settings that currently exist on device
     * @return a SparseArray mapping userIds to a sorted int array of appIds that may view the
     *         provided setting or null if the app is visible to all and no whitelist should be
     *         applied.
     */
    @Nullable
    public SparseArray<int[]> getVisibilityWhitelist(PackageSetting setting, int[] users,
            ArrayMap<String, PackageSetting> existingSettings) {
        if (mForceQueryable.contains(setting.appId)) {
            return null;
        }
        // let's reserve max memory to limit the number of allocations
        SparseArray<int[]> result = new SparseArray<>(users.length);
        for (int u = 0; u < users.length; u++) {
            final int userId = users[u];
            int[] appIds = new int[existingSettings.size()];
            int[] buffer = null;
            int whitelistSize = 0;
            for (int i = existingSettings.size() - 1; i >= 0; i--) {
                final PackageSetting existingSetting = existingSettings.valueAt(i);
                final int existingAppId = existingSetting.appId;
                if (existingAppId < Process.FIRST_APPLICATION_UID) {
                    continue;
                }
                final int loc = Arrays.binarySearch(appIds, 0, whitelistSize, existingAppId);
                if (loc >= 0) {
                    continue;
                }
                final int existingUid = UserHandle.getUid(userId, existingAppId);
                if (!shouldFilterApplication(existingUid, existingSetting, setting, userId)) {
                    if (buffer == null) {
                        buffer = new int[appIds.length];
                    }
                    final int insert = ~loc;
                    System.arraycopy(appIds, insert, buffer, 0, whitelistSize - insert);
                    appIds[insert] = existingUid;
                    System.arraycopy(buffer, 0, appIds, insert + 1, whitelistSize - insert);
                    whitelistSize++;
                }
            }
            result.put(userId, Arrays.copyOf(appIds, whitelistSize));
        }
        return result;
    }

    /**
    /**
     * Removes a package for consideration when filtering visibility between apps.
     * Removes a package for consideration when filtering visibility between apps.
     *
     *
+93 −64

File changed.

Preview size limit exceeded, changes collapsed.

+55 −0
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.pm;
package com.android.server.pm;




import static org.hamcrest.Matchers.arrayContaining;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.any;
@@ -47,6 +48,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.pkg.ParsedPackage;


import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
@@ -56,7 +58,9 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoAnnotations;


import java.security.cert.CertificateException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Set;
import java.util.Set;


@@ -635,6 +639,57 @@ public class AppsFilterTest {
                appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, target, instrumentation, 0));
                appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, target, instrumentation, 0));
    }
    }


    @Test
    public void testWhoCanSee() throws Exception {
        final AppsFilter appsFilter =
                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
        appsFilter.onSystemReady();

        final int systemAppId = Process.FIRST_APPLICATION_UID - 1;
        final int seesNothingAppId = Process.FIRST_APPLICATION_UID;
        final int hasProviderAppId = Process.FIRST_APPLICATION_UID + 1;
        final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2;
        PackageSetting system = simulateAddPackage(appsFilter, pkg("some.system.pkg"), systemAppId);
        PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("some.system.pkg"),
                seesNothingAppId);
        PackageSetting hasProvider = simulateAddPackage(appsFilter,
                pkgWithProvider("com.some.package", "com.some.authority"), hasProviderAppId);
        PackageSetting queriesProvider = simulateAddPackage(appsFilter,
                pkgQueriesProvider("com.some.other.package", "com.some.authority"),
                queriesProviderAppId);

        final int[] systemFilter =
                appsFilter.getVisibilityWhitelist(system, new int[]{0}, mExisting).get(0);
        assertThat(Arrays.asList(systemFilter), arrayContaining(systemAppId));

        final int[] seesNothingFilter =
                appsFilter.getVisibilityWhitelist(seesNothing, new int[]{0}, mExisting).get(0);
        assertThat(Arrays.asList(seesNothingFilter),
                arrayContaining(systemAppId, seesNothingAppId));

        final int[] hasProviderFilter =
                appsFilter.getVisibilityWhitelist(hasProvider, new int[]{0}, mExisting).get(0);
        assertThat(Arrays.asList(hasProviderFilter),
                arrayContaining(systemAppId, hasProviderAppId, queriesProviderAppId));

        int[] queriesProviderFilter =
                appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0);
        assertThat(Arrays.asList(queriesProviderFilter),
                arrayContaining(systemAppId, queriesProviderAppId));

        // provider read
        appsFilter.grantImplicitAccess(hasProviderAppId, queriesProviderAppId);

        // ensure implicit access is included in the filter
        queriesProviderFilter =
                appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0);
        assertThat(Arrays.asList(queriesProviderFilter),
                arrayContaining(systemAppId, hasProviderAppId, queriesProviderAppId));
    }

    private void assertThat(List<int[]> asList, Matcher<Integer[]> arrayContainingInAnyOrder) {
    }

    private interface WithSettingBuilder {
    private interface WithSettingBuilder {
        PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
        PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
    }
    }
Loading