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

Commit e8d1c495 authored by Atneya Nair's avatar Atneya Nair
Browse files

Push additional package data to audioserver

Maintain a set of the AIDL package state objects in the permission
provider, which collects targetSdk and audio capture flag values,
which audioserver requires.

Pull initial package generation logic into AudioService. Still key the
package set by the package name, to ensure package updates don't create
separate package state objects.

Bug: 391967879
Bug: 380150343
Flag: EXEMPT refactor
Test: atest CtsMediaAudioPermissionTestCases
Change-Id: I6a6a69642fde361b9fbb05b46ea612de5ae87ba1
parent 1a81a01d
Loading
Loading
Loading
Loading
+38 −47
Original line number Diff line number Diff line
@@ -38,7 +38,6 @@ import android.os.RemoteException;
import android.os.Trace;
import android.os.Process;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Log;

@@ -46,7 +45,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.media.permission.INativePermissionController;
import com.android.media.permission.PermissionEnum;
import com.android.media.permission.UidPackageState;
import com.android.server.pm.pkg.PackageState;
import com.android.media.permission.UidPackageState.PackageState;

import java.util.Arrays;
import java.util.Collection;
@@ -55,11 +54,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/** Responsible for synchronizing system server permission state to the native audioserver. */
public class AudioServerPermissionProvider {
@@ -108,7 +104,7 @@ public class AudioServerPermissionProvider {
    private INativePermissionController mDest;

    @GuardedBy("mLock")
    private final Map<Integer, Set<String>> mPackageMap;
    private final Map<Integer, Map<String, PackageState>> mPackageMap;

    // Values are sorted
    @GuardedBy("mLock")
@@ -121,10 +117,13 @@ public class AudioServerPermissionProvider {
    private int mHdsUid = -1;

    /**
     * @param appInfos - PackageState for all apps on the device, used to populate init state
     * @param packageMap - Map from app-ids to Map of packageNames to PackageState (also containing
     * name)
     * @param permissionPredicate - Check if a UID holds an android permission (string)
     * @param userIdSupplier - Return all users (not uids) on the device, which apps can run in
     */
    public AudioServerPermissionProvider(
            Collection<PackageState> appInfos,
            Map<Integer, Map<String, PackageState>> packageMap,
            BiPredicate<Integer, String> permissionPredicate,
            Supplier<int[]> userIdSupplier) {
        for (int i = 0; i < PermissionEnum.ENUM_SIZE; i++) {
@@ -133,7 +132,7 @@ public class AudioServerPermissionProvider {
        mUserIdSupplier = userIdSupplier;
        mPermissionPredicate = permissionPredicate;
        // Initialize the package state
        mPackageMap = generatePackageMappings(appInfos);
        mPackageMap = packageMap;
    }

    /**
@@ -162,35 +161,47 @@ public class AudioServerPermissionProvider {
    }

    /**
     * Called when a package is added or removed
     * Called when a package is added, modified, or removed
     *
     * @param uid - uid of modified package (only app-id matters)
     * @param packageName - the (new) packageName
     * @param packageState - the (new) packageState
     * @param isRemove - true if the package is being removed, false if it is being added
     */
    public void onModifyPackageState(int uid, String packageName, boolean isRemove) {
    public void onModifyPackageState(int uid, PackageState packageState, boolean isRemove) {
        // No point in maintaining package mappings for uids of different users
        uid = UserHandle.getAppId(uid);
        synchronized (mLock) {
            // Update state
            Set<String> packages;
            Map<String, PackageState> packages;
            if (!isRemove) {
                packages = mPackageMap.computeIfAbsent(uid, unused -> new ArraySet(1));
                packages.add(packageName);
                packages = mPackageMap.computeIfAbsent(uid, unused -> new HashMap<>());
                if (packageState.equals(packages.put(packageState.packageName, packageState))) {
                    // no change
                    return;
                }
            } else {
                packages = mPackageMap.get(uid);
                if (packages != null) {
                    packages.remove(packageName);
                    if (packages.isEmpty()) mPackageMap.remove(uid);
                    if (packages.remove(packageState.packageName) == null) {
                        // no change
                        return;
                    }
                    if (packages.isEmpty()) {
                        mPackageMap.remove(uid);
                    }
                } else {
                    // no change
                    return;
                }
            }
            // Push state to destination
            if (mDest == null) {
                // Will re-sync when service is back online
                return;
            }
            var state = new UidPackageState();
            state.uid = uid;
            state.packageNames = packages != null ? List.copyOf(packages) : Collections.emptyList();
            state.packageStates = List.copyOf(packages.values());
            try {
                mDest.updatePackagesForUid(state);
            } catch (RemoteException e) {
@@ -230,10 +241,9 @@ public class AudioServerPermissionProvider {
    public void setIsolatedServiceUid(int uid, int owningUid) {
        synchronized (mLock) {
            if (mHdsUid == uid) return;
            var packageNameSet = mPackageMap.get(UserHandle.getAppId(owningUid));
            if (packageNameSet != null) {
                var packageName = packageNameSet.iterator().next();
                onModifyPackageState(uid, packageName, /* isRemove= */ false);
            var packages = mPackageMap.get(UserHandle.getAppId(owningUid));
            if (packages != null) { var packageState = packages.values().iterator().next();
                onModifyPackageState(uid, packageState, /* isRemove= */ false);
            } else {
                Log.wtf(TAG, "setIsolatedService owning uid not found");
            }
@@ -263,16 +273,16 @@ public class AudioServerPermissionProvider {

    public void clearIsolatedServiceUid(int uid) {
        synchronized (mLock) {
            var packageNameSet = mPackageMap.get(UserHandle.getAppId(uid));
            var packages = mPackageMap.get(UserHandle.getAppId(uid));
            if (mHdsUid != uid) {
                Log.wtf(TAG,
                        "Unexpected isolated service uid cleared: " + uid + packageNameSet
                        "Unexpected isolated service uid cleared: " + uid + packages
                                + ", expected " + mHdsUid);
                return;
            }
            if (packageNameSet != null) {
                var packageName = packageNameSet.iterator().next();
                onModifyPackageState(uid, packageName, /* isRemove= */ true);
            if (packages != null) {
                var packageState = packages.values().iterator().next();
                onModifyPackageState(uid, packageState, /* isRemove= */ true);
            } else {
                Log.wtf(TAG, "clearIsolatedService uid not found");
            }
@@ -319,7 +329,7 @@ public class AudioServerPermissionProvider {
                                entry -> {
                                    UidPackageState state = new UidPackageState();
                                    state.uid = entry.getKey();
                                    state.packageNames = List.copyOf(entry.getValue());
                                    state.packageStates = List.copyOf(entry.getValue().values());
                                    return state;
                                })
                        .toList();
@@ -361,23 +371,4 @@ public class AudioServerPermissionProvider {
        Arrays.sort(unwrapped);
        return unwrapped;
    }

    /**
     * Aggregation operation on all package states list: groups by states by app-id and merges the
     * packageName for each state into an ArraySet.
     */
    private static Map<Integer, Set<String>> generatePackageMappings(
            Collection<PackageState> appInfos) {
        Collector<PackageState, Object, Set<String>> reducer =
                Collectors.mapping(
                        (PackageState p) -> p.getPackageName(),
                        Collectors.toCollection(() -> new ArraySet(1)));

        return appInfos.stream()
                .collect(
                        Collectors.groupingBy(
                                /* predicate */ (PackageState p) -> p.getAppId(),
                                /* factory */ HashMap::new,
                                /* downstream collector */ reducer));
    }
}
+49 −5
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import static android.Manifest.permission.QUERY_AUDIO_STATE;
import static android.Manifest.permission.WRITE_SETTINGS;
import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_REPLACED;
import static android.content.Intent.EXTRA_ARCHIVAL;
import static android.content.Intent.EXTRA_REPLACING;
import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
@@ -123,6 +124,7 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -266,6 +268,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.media.permission.UidPackageState;
import com.android.modules.expresslog.Counter;
import com.android.server.EventLogTags;
import com.android.server.LocalManagerRegistry;
@@ -314,6 +317,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BooleanSupplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
/**
@@ -13619,16 +13623,51 @@ public class AudioService extends IAudioService.Stub
    private static final String mMetricsId = MediaMetrics.Name.AUDIO_SERVICE
            + MediaMetrics.SEPARATOR;
    /*
     * Create AIDL defined package state for audioserver
     */
    @VisibleForTesting
    static UidPackageState.PackageState makePackageState(PackageState p) {
        final var ret = new UidPackageState.PackageState();
        ret.packageName = p.getPackageName();
        ret.targetSdk = p.getTargetSdkVersion();
        ret.isPlaybackCaptureAllowed = p.isAudioPlaybackCaptureAllowed();
        return ret;
    }
    /**
     * Aggregation operation on all package states list: groups by states by app-id and merges the
     * packages per app-id into a Map keyed by the packageName.
     */
    @VisibleForTesting
    static Map<Integer, Map<String, UidPackageState.PackageState>> generatePackageMap(
            Collection<PackageState> appInfos) {
        Collector<PackageState, Object, Map<String, UidPackageState.PackageState>> reducer =
                Collectors.toMap(p -> p.getPackageName(), /* key */
                                 AudioService::makePackageState, /* val */
                                 (x, y) -> x, /* mergeFunc, x,y should be identical */
                                 () -> new ArrayMap(1) /* factory */);
        return appInfos.stream()
                .collect(
                        Collectors.groupingBy(
                                /* predicate */ (PackageState p) -> p.getAppId(),
                                /* factory */ HashMap::new,
                                /* downstream collector */ reducer));
    }
    private static AudioServerPermissionProvider initializeAudioServerPermissionProvider(
            Context context, AudioPolicyFacade audioPolicy, Executor audioserverExecutor) {
        Collection<PackageState> packageStates = null;
        try (PackageManagerLocal.UnfilteredSnapshot snapshot =
        Map<Integer, Map<String, UidPackageState.PackageState>> packageStates = null;
        try (var snapshot =
                    LocalManagerRegistry.getManager(PackageManagerLocal.class)
                        .withUnfilteredSnapshot()) {
            packageStates = snapshot.getPackageStates().values();
            packageStates = generatePackageMap(snapshot.getPackageStates().values());
        }
        var umi = LocalServices.getService(UserManagerInternal.class);
        var pmsi = LocalServices.getService(PermissionManagerServiceInternal.class);
        var pmi = LocalServices.getService(PackageManagerInternal.class);
        var provider = new AudioServerPermissionProvider(packageStates,
                (Integer uid, String perm) -> ActivityManager.checkComponentPermission(perm, uid,
                        /* owningUid = */ -1, /* exported */true)
@@ -13643,6 +13682,7 @@ public class AudioService extends IAudioService.Stub
        IntentFilter packageUpdateFilter = new IntentFilter();
        packageUpdateFilter.addAction(ACTION_PACKAGE_ADDED);
        packageUpdateFilter.addAction(ACTION_PACKAGE_REPLACED);
        packageUpdateFilter.addDataScheme("package");
        context.registerReceiverForAllUsers(new BroadcastReceiver() {
@@ -13655,9 +13695,13 @@ public class AudioService extends IAudioService.Stub
                    intent.getBooleanExtra(EXTRA_REPLACING, false) + " archival: " +
                    intent.getBooleanExtra(EXTRA_ARCHIVAL, false) + " for package " +
                    pkgName + " with uid " + uid);
                if (ACTION_PACKAGE_ADDED.equals(action)) {
                if (ACTION_PACKAGE_ADDED.equals(action)
                        || ACTION_PACKAGE_REPLACED.equals(action)) {
                    audioserverExecutor.execute(() ->
                            provider.onModifyPackageState(uid, pkgName, false /* isRemoved */));
                            provider.onModifyPackageState(
                                uid,
                                makePackageState(pmi.getPackageStateInternal(pkgName)),
                                false /* isRemoved */));
                }
            }
        }, packageUpdateFilter, null, null); // main thread is fine, since dispatch on executor
+213 −130

File changed.

Preview size limit exceeded, changes collapsed.