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

Commit 3b4eb357 authored by Yuting Fang's avatar Yuting Fang
Browse files

Add new AppOp APIs to facilitate Privacy Indicators

Bug: 308720988
Test: presubmit
Change-Id: I240263f03a014b790089436cabe4f348a445636c
parent 63718879
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -625,6 +625,8 @@ package android.app {
    method public static String[] getOpStrs();
    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[]);
    method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[], @NonNull String);
    method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermissionGroupUsage> getPermissionGroupUsageForPrivacyIndicator(boolean);
    method public static int opToDefaultMode(@NonNull String);
    method @Nullable public static String opToPermission(@NonNull String);
    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int);
@@ -11374,6 +11376,7 @@ package android.permission {
    method public long getLastAccessTimeMillis();
    method @NonNull public String getPackageName();
    method @NonNull public String getPermissionGroupName();
    method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull public String getPersistentDeviceId();
    method @Nullable public CharSequence getProxyLabel();
    method public int getUid();
    method public boolean isActive();
+67 −1
Original line number Diff line number Diff line
@@ -72,6 +72,8 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.permission.PermissionGroupUsage;
import android.permission.PermissionUsageHelper;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -226,6 +228,7 @@ public class AppOpsManager {
    private static Boolean sFullLog = null;

    final Context mContext;
    private PermissionUsageHelper mUsageHelper;

    @UnsupportedAppUsage
    final IAppOpsService mService;
@@ -7764,6 +7767,44 @@ public class AppOpsManager {
        return (result != null) ? result : Collections.emptyList();
    }

    /**
     * Retrieve current operation state for all applications for a device.
     *
     * The mode of the ops returned are set for the package but may not reflect their effective
     * state due to UID policy or because it's controlled by a different global op.
     *
     * Use {@link #unsafeCheckOp(String, int, String)}} or
     * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
     *
     * @param ops The set of operations you are interested in, or null if you want all of them.
     * @param persistentDeviceId The device that the ops are attributed to.
     *
     * @hide
     */
    @SystemApi
    @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
    @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
    public @NonNull List<AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[] ops,
            @NonNull String persistentDeviceId) {
        final int[] opCodes;
        if (ops != null) {
            final int opCount = ops.length;
            opCodes = new int[opCount];
            for (int i = 0; i < opCount; i++) {
                opCodes[i] = sOpStrToOp.get(ops[i]);
            }
        } else {
            opCodes = null;
        }
        final List<AppOpsManager.PackageOps> result;
        try {
            result =  mService.getPackagesForOpsForDevice(opCodes, persistentDeviceId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        return (result != null) ? result : Collections.emptyList();
    }

    /**
     * Retrieve current operation state for all applications.
     *
@@ -7780,7 +7821,8 @@ public class AppOpsManager {
    @UnsupportedAppUsage
    public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
        try {
            return mService.getPackagesForOps(ops);
            return mService.getPackagesForOpsForDevice(ops,
                    VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -9946,6 +9988,30 @@ public class AppOpsManager {
        appOpsNotedForAttribution.set(op);
    }

    /**
     * Get recent op usage data for CAMERA, MICROPHONE and LOCATION from all connected devices
     * to power privacy indicator.
     *
     * @param includeMicrophoneUsage whether to retrieve microphone usage
     * @return A list of permission groups currently or recently used by all apps by all users in
     * the current profile group.
     *
     * @hide
     */
    @SystemApi
    @NonNull
    @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
    @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
    public List<PermissionGroupUsage> getPermissionGroupUsageForPrivacyIndicator(
            boolean includeMicrophoneUsage) {
        // Lazily initialize the usage helper
        if (mUsageHelper == null) {
            mUsageHelper = new PermissionUsageHelper(mContext);
        }

        return mUsageHelper.getOpUsageDataForAllDevices(includeMicrophoneUsage);
    }

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(value = {
+27 −7
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.permission;

import android.annotation.CurrentTimeMillisLong;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -26,8 +27,8 @@ import com.android.internal.util.DataClass;

/**
 * Represents the usage of a permission group by an app. Supports package name, user, permission
 * group, whether or not the access is running or recent, whether the access is tied to a phone
 * call, and an optional special attribution tag, label and proxy label.
 * group, persistent device Id, whether or not the access is running or recent, whether the access
 * is tied to a phone call, and an optional special attribution tag, label and proxy label.
 *
 * @hide
 */
@@ -48,6 +49,7 @@ public final class PermissionGroupUsage implements Parcelable {
    private final @Nullable CharSequence mAttributionTag;
    private final @Nullable CharSequence mAttributionLabel;
    private final @Nullable CharSequence mProxyLabel;
    private final @NonNull String mPersistentDeviceId;



@@ -79,7 +81,8 @@ public final class PermissionGroupUsage implements Parcelable {
            boolean phoneCall,
            @Nullable CharSequence attributionTag,
            @Nullable CharSequence attributionLabel,
            @Nullable CharSequence proxyLabel) {
            @Nullable CharSequence proxyLabel,
            @NonNull String persistentDeviceId) {
        this.mPackageName = packageName;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mPackageName);
@@ -93,6 +96,9 @@ public final class PermissionGroupUsage implements Parcelable {
        this.mAttributionTag = attributionTag;
        this.mAttributionLabel = attributionLabel;
        this.mProxyLabel = proxyLabel;
        this.mPersistentDeviceId = persistentDeviceId;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mPersistentDeviceId);

        // onConstructed(); // You can define this method to get a callback
    }
@@ -170,6 +176,12 @@ public final class PermissionGroupUsage implements Parcelable {
        return mProxyLabel;
    }

    @DataClass.Generated.Member
    @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
    public @NonNull String getPersistentDeviceId() {
        return mPersistentDeviceId;
    }

    @Override
    @DataClass.Generated.Member
    public String toString() {
@@ -185,7 +197,8 @@ public final class PermissionGroupUsage implements Parcelable {
                "phoneCall = " + mPhoneCall + ", " +
                "attributionTag = " + mAttributionTag + ", " +
                "attributionLabel = " + mAttributionLabel + ", " +
                "proxyLabel = " + mProxyLabel +
                "proxyLabel = " + mProxyLabel + ", " +
                "persistentDeviceId = " + mPersistentDeviceId +
        " }";
    }

@@ -210,7 +223,8 @@ public final class PermissionGroupUsage implements Parcelable {
                && mPhoneCall == that.mPhoneCall
                && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
                && java.util.Objects.equals(mAttributionLabel, that.mAttributionLabel)
                && java.util.Objects.equals(mProxyLabel, that.mProxyLabel);
                && java.util.Objects.equals(mProxyLabel, that.mProxyLabel)
                && java.util.Objects.equals(mPersistentDeviceId, that.mPersistentDeviceId);
    }

    @Override
@@ -229,6 +243,7 @@ public final class PermissionGroupUsage implements Parcelable {
        _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
        _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionLabel);
        _hash = 31 * _hash + java.util.Objects.hashCode(mProxyLabel);
        _hash = 31 * _hash + java.util.Objects.hashCode(mPersistentDeviceId);
        return _hash;
    }

@@ -252,6 +267,7 @@ public final class PermissionGroupUsage implements Parcelable {
        if (mAttributionTag != null) dest.writeCharSequence(mAttributionTag);
        if (mAttributionLabel != null) dest.writeCharSequence(mAttributionLabel);
        if (mProxyLabel != null) dest.writeCharSequence(mProxyLabel);
        dest.writeString(mPersistentDeviceId);
    }

    @Override
@@ -275,6 +291,7 @@ public final class PermissionGroupUsage implements Parcelable {
        CharSequence attributionTag = (flg & 0x40) == 0 ? null : (CharSequence) in.readCharSequence();
        CharSequence attributionLabel = (flg & 0x80) == 0 ? null : (CharSequence) in.readCharSequence();
        CharSequence proxyLabel = (flg & 0x100) == 0 ? null : (CharSequence) in.readCharSequence();
        String persistentDeviceId = in.readString();

        this.mPackageName = packageName;
        com.android.internal.util.AnnotationValidations.validate(
@@ -289,6 +306,9 @@ public final class PermissionGroupUsage implements Parcelable {
        this.mAttributionTag = attributionTag;
        this.mAttributionLabel = attributionLabel;
        this.mProxyLabel = proxyLabel;
        this.mPersistentDeviceId = persistentDeviceId;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mPersistentDeviceId);

        // onConstructed(); // You can define this method to get a callback
    }
@@ -308,10 +328,10 @@ public final class PermissionGroupUsage implements Parcelable {
    };

    @DataClass.Generated(
            time = 1645067417023L,
            time = 1706285211875L,
            codegenVersion = "1.0.23",
            sourceFile = "frameworks/base/core/java/android/permission/PermissionGroupUsage.java",
            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final  int mUid\nprivate final  long mLastAccessTimeMillis\nprivate final @android.annotation.NonNull java.lang.String mPermissionGroupName\nprivate final  boolean mActive\nprivate final  boolean mPhoneCall\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionTag\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionLabel\nprivate final @android.annotation.Nullable java.lang.CharSequence mProxyLabel\nclass PermissionGroupUsage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genToString=true)")
            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final  int mUid\nprivate final  long mLastAccessTimeMillis\nprivate final @android.annotation.NonNull java.lang.String mPermissionGroupName\nprivate final  boolean mActive\nprivate final  boolean mPhoneCall\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionTag\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionLabel\nprivate final @android.annotation.Nullable java.lang.CharSequence mProxyLabel\nprivate final @android.annotation.NonNull java.lang.String mPersistentDeviceId\nclass PermissionGroupUsage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genToString=true)")
    @Deprecated
    private void __metadata() {}

+3 −1
Original line number Diff line number Diff line
@@ -1329,7 +1329,9 @@ public final class PermissionManager {
    public List<PermissionGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
        // Lazily initialize the usage helper
        initializeUsageHelper();
        return mUsageHelper.getOpUsageData(micMuted);
        boolean includeMicrophoneUsage = !micMuted;
        return mUsageHelper.getOpUsageDataByDevice(includeMicrophoneUsage,
                VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
    }

    /**
+56 −7
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@ import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_AC
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.Attribution;
@@ -52,10 +54,12 @@ import android.location.LocationManager;
import android.media.AudioManager;
import android.os.Process;
import android.os.UserHandle;
import android.permission.flags.Flags;
import android.provider.DeviceConfig;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;

@@ -75,6 +79,8 @@ import java.util.Objects;
public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener,
        AppOpsManager.OnOpStartedListener {

    private static final String LOG_TAG = PermissionUsageHelper.class.getName();

    /**
     * Whether to show the mic and camera icons.
     */
@@ -159,6 +165,7 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
    private ArrayMap<UserHandle, Context> mUserContexts;
    private PackageManager mPkgManager;
    private AppOpsManager mAppOpsManager;
    private VirtualDeviceManager mVirtualDeviceManager;
    @GuardedBy("mAttributionChains")
    private final ArrayMap<Integer, ArrayList<AccessChainLink>> mAttributionChains =
            new ArrayMap<>();
@@ -172,6 +179,7 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
        mContext = context;
        mPkgManager = context.getPackageManager();
        mAppOpsManager = context.getSystemService(AppOpsManager.class);
        mVirtualDeviceManager = context.getSystemService(VirtualDeviceManager.class);
        mUserContexts = new ArrayMap<>();
        mUserContexts.put(Process.myUserHandle(), mContext);
        // TODO ntmyren: make this listen for flag enable/disable changes
@@ -280,9 +288,11 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
    }

    /**
     * @see PermissionManager.getIndicatorAppOpUsageData
     * Return Op usage for CAMERA, LOCATION AND MICROPHONE for all packages for a device.
     * The returned data is to power privacy indicator.
     */
    public @NonNull List<PermissionGroupUsage> getOpUsageData(boolean isMicMuted) {
    public @NonNull List<PermissionGroupUsage> getOpUsageDataByDevice(
            boolean includeMicrophoneUsage, String deviceId) {
        List<PermissionGroupUsage> usages = new ArrayList<>();

        if (!shouldShowIndicators()) {
@@ -293,11 +303,11 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
        if (shouldShowLocationIndicator()) {
            ops.addAll(LOCATION_OPS);
        }
        if (!isMicMuted) {
        if (includeMicrophoneUsage) {
            ops.addAll(MIC_OPS);
        }

        Map<String, List<OpUsage>> rawUsages = getOpUsages(ops);
        Map<String, List<OpUsage>> rawUsages = getOpUsagesByDevice(ops, deviceId);

        ArrayList<String> usedPermGroups = new ArrayList<>(rawUsages.keySet());

@@ -349,13 +359,40 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
                        new PermissionGroupUsage(usage.packageName, usage.uid, usage.lastAccessTime,
                                permGroup,
                                usage.isRunning, isPhone, usage.attributionTag, attributionLabel,
                                usagesWithLabels.valueAt(usageNum)));
                                usagesWithLabels.valueAt(usageNum),
                                VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT));
            }
        }

        return usages;
    }

    /**
     * Return Op usage for CAMERA, LOCATION AND MICROPHONE for all packages and all connected
     * devices.
     * The returned data is to power privacy indicator.
     */
    public @NonNull List<PermissionGroupUsage> getOpUsageDataForAllDevices(
            boolean includeMicrophoneUsage) {
        List<PermissionGroupUsage> allUsages = new ArrayList<>();
        List<VirtualDevice> virtualDevices = mVirtualDeviceManager.getVirtualDevices();
        ArraySet<String> persistentDeviceIds = new ArraySet<>();

        for (int num = 0; num < virtualDevices.size(); num++) {
            persistentDeviceIds.add(virtualDevices.get(num).getPersistentDeviceId());
        }
        persistentDeviceIds.add(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);

        for (int index = 0; index < persistentDeviceIds.size(); index++) {
            allUsages.addAll(
                    getOpUsageDataByDevice(includeMicrophoneUsage,
                            persistentDeviceIds.valueAt(index)));
        }

        return allUsages;
    }


    private void updateSubattributionLabelsMap(List<OpUsage> usages,
            ArrayMap<String, Map<String, String>> subAttributionLabelsMap) {
        if (usages == null || usages.isEmpty()) {
@@ -443,12 +480,24 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
     * running/recent info, if the usage is a phone call, per permission group.
     *
     * @param opNames a list of op names to get usage for
     * @param deviceId which device to get op usage for
     * @return A map of permission group -> list of usages that are recent or running
     */
    private Map<String, List<OpUsage>> getOpUsages(List<String> opNames) {
    private Map<String, List<OpUsage>> getOpUsagesByDevice(List<String> opNames, String deviceId) {
        List<AppOpsManager.PackageOps> ops;
        try {
            if (Flags.deviceAwarePermissionApisEnabled()) {
                ops = mAppOpsManager.getPackagesForOps(opNames.toArray(new String[opNames.size()]),
                        deviceId);
            } else if (!Objects.equals(deviceId,
                    VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) {
                Slog.w(LOG_TAG,
                        "device_aware_permission_apis_enabled flag not enabled when deviceId is "
                                + "not default");
                return Collections.emptyMap();
            } else {
                ops = mAppOpsManager.getPackagesForOps(opNames.toArray(new String[opNames.size()]));
            }
        } catch (NullPointerException e) {
            // older builds might not support all the app-ops requested
            return Collections.emptyMap();
Loading