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

Commit 8668a000 authored by Yuting Fang's avatar Yuting Fang Committed by Android (Google) Code Review
Browse files

Merge "Add new AppOp APIs to facilitate Privacy Indicators" into main

parents bb39ee14 3b4eb357
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -626,6 +626,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);
@@ -11382,6 +11384,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