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

Commit 552aa465 authored by Nate Myren's avatar Nate Myren Committed by Android (Google) Code Review
Browse files

Merge "Support AttributionSource chains in PermissionUsageHelper" into sc-dev

parents 6c9c6cb6 5cd62ee5
Loading
Loading
Loading
Loading
+2 −4
Original line number Original line Diff line number Diff line
@@ -158,6 +158,8 @@ public final class PermissionManager {
        mPermissionManager = IPermissionManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
        mPermissionManager = IPermissionManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
                "permissionmgr"));
                "permissionmgr"));
        mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class);
        mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class);
        //TODO ntmyren: there should be a way to only enable the watcher when requested
        mUsageHelper = new PermissionUsageHelper(context);
    }
    }


    /**
    /**
@@ -878,10 +880,6 @@ public final class PermissionManager {
    @NonNull
    @NonNull
    @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
    @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
    public List<PermGroupUsage> getIndicatorAppOpUsageData() {
    public List<PermGroupUsage> getIndicatorAppOpUsageData() {
        // Lazily initialize the usage helper
        if (mUsageHelper == null) {
            mUsageHelper = new PermissionUsageHelper(mContext);
        }
        return mUsageHelper.getOpUsageData(new AudioManager().isMicrophoneMute());
        return mUsageHelper.getOpUsageData(new AudioManager().isMicrophoneMute());
    }
    }


+116 −4
Original line number Original line Diff line number Diff line
@@ -19,6 +19,10 @@ package android.permission;
import static android.Manifest.permission_group.CAMERA;
import static android.Manifest.permission_group.CAMERA;
import static android.Manifest.permission_group.LOCATION;
import static android.Manifest.permission_group.LOCATION;
import static android.Manifest.permission_group.MICROPHONE;
import static android.Manifest.permission_group.MICROPHONE;
import static android.app.AppOpsManager.ATTRIBUTION_FLAGS_NONE;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
import static android.app.AppOpsManager.AttributionFlags;
import static android.app.AppOpsManager.OPSTR_CAMERA;
import static android.app.AppOpsManager.OPSTR_CAMERA;
import static android.app.AppOpsManager.OPSTR_COARSE_LOCATION;
import static android.app.AppOpsManager.OPSTR_COARSE_LOCATION;
import static android.app.AppOpsManager.OPSTR_FINE_LOCATION;
import static android.app.AppOpsManager.OPSTR_FINE_LOCATION;
@@ -30,6 +34,7 @@ import static android.media.AudioSystem.MODE_IN_COMMUNICATION;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
@@ -56,7 +61,7 @@ import java.util.Objects;
 *
 *
 * @hide
 * @hide
 */
 */
public class PermissionUsageHelper {
public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener {


    /** Whether to show the mic and camera icons.  */
    /** Whether to show the mic and camera icons.  */
    private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled";
    private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled";
@@ -140,6 +145,7 @@ public class PermissionUsageHelper {
    private ArrayMap<UserHandle, Context> mUserContexts;
    private ArrayMap<UserHandle, Context> mUserContexts;
    private PackageManager mPkgManager;
    private PackageManager mPkgManager;
    private AppOpsManager mAppOpsManager;
    private AppOpsManager mAppOpsManager;
    private ArrayMap<Integer, ArrayList<AccessChainLink>> mAttributionChains = new ArrayMap<>();


    /**
    /**
     * Constructor for PermissionUsageHelper
     * Constructor for PermissionUsageHelper
@@ -151,6 +157,10 @@ public class PermissionUsageHelper {
        mAppOpsManager = context.getSystemService(AppOpsManager.class);
        mAppOpsManager = context.getSystemService(AppOpsManager.class);
        mUserContexts = new ArrayMap<>();
        mUserContexts = new ArrayMap<>();
        mUserContexts.put(Process.myUserHandle(), mContext);
        mUserContexts.put(Process.myUserHandle(), mContext);
        // TODO ntmyren: make this listen for flag enable/disable changes
        String[] ops = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO };
        mContext.getSystemService(AppOpsManager.class).startWatchingActive(ops,
                context.getMainExecutor(), this);
    }
    }


    private Context getUserContext(UserHandle user) {
    private Context getUserContext(UserHandle user) {
@@ -160,6 +170,45 @@ public class PermissionUsageHelper {
        return mUserContexts.get(user);
        return mUserContexts.get(user);
    }
    }


    @Override
    public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
            boolean active) {
        // not part of an attribution chain. Do nothing
    }

    @Override
    public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
            @Nullable String attributionTag, boolean active, @AttributionFlags int attributionFlags,
            int attributionChainId) {
        if ((attributionFlags & ATTRIBUTION_FLAGS_NONE) != 0) {
            return;
        }

        if (!active) {
            // if any link in the chain is finished, remove the chain.
            // TODO ntmyren: be smarter about this
            mAttributionChains.remove(attributionChainId);
            return;
        }

        ArrayList<AccessChainLink> currentChain = mAttributionChains.computeIfAbsent(
                attributionChainId, k -> new ArrayList<>());
        AccessChainLink link = new AccessChainLink(op, packageName, attributionTag, uid,
                attributionFlags);

        int currSize = currentChain.size();
        if (currSize == 0 || link.isEnd() || !currentChain.get(currSize - 1).isEnd()) {
            // if the list is empty, this link is the end, or the last link in the current chain
            // isn't the end, add it to the end
            currentChain.add(link);
        } else if (link.isStart()) {
            currentChain.add(0, link);
        } else if (currentChain.get(currentChain.size() - 1).isEnd()) {
            // we already have the end, and this is a mid node, so insert before the end
            currentChain.add(currSize - 1, link);
        }
    }

    /**
    /**
     * @see PermissionManager.getIndicatorAppOpUsageData
     * @see PermissionManager.getIndicatorAppOpUsageData
     */
     */
@@ -331,7 +380,7 @@ public class PermissionUsageHelper {
    private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(List<OpUsage> usages) {
    private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(List<OpUsage> usages) {
        ArrayMap<OpUsage, CharSequence> usagesAndLabels = new ArrayMap<>();
        ArrayMap<OpUsage, CharSequence> usagesAndLabels = new ArrayMap<>();


        if (usages == null) {
        if (usages == null || usages.isEmpty()) {
            return usagesAndLabels;
            return usagesAndLabels;
        }
        }


@@ -430,9 +479,52 @@ public class PermissionUsageHelper {
                }
                }
                iterNum++;
                iterNum++;
            }
            }

            // TODO ntmyren: remove this proxy logic once camera is converted to AttributionSource
            // For now: don't add mic proxy usages
            if (!start.op.equals(OPSTR_RECORD_AUDIO)) {
                usagesAndLabels.put(start,
                usagesAndLabels.put(start,
                        proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
                        proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
            }
            }
        }

        for (int i = 0; i < mAttributionChains.size(); i++) {
            List<AccessChainLink> usageList = mAttributionChains.valueAt(i);
            int lastVisible = usageList.size() - 1;
            // TODO ntmyren: remove this mic code once camera is converted to AttributionSource
            // if the list is empty or incomplete, do not show it.
            if (usageList.isEmpty() || !usageList.get(lastVisible).isEnd()
                    || !usageList.get(0).isStart()
                    || !usageList.get(lastVisible).usage.op.equals(OPSTR_RECORD_AUDIO)) {
                continue;
            }

            //TODO ntmyren: remove once camera etc. etc.
            for (AccessChainLink link: usageList) {
                proxyPackages.add(link.usage.getPackageIdHash());
            }

            AccessChainLink start = usageList.get(0);
            AccessChainLink lastVisibleLink = usageList.get(lastVisible);
            while (lastVisible > 0 && !shouldShowPackage(lastVisibleLink.usage.packageName)) {
                lastVisible--;
                lastVisibleLink = usageList.get(lastVisible);
            }
            String proxyLabel = null;
            if (!lastVisibleLink.usage.packageName.equals(start.usage.packageName)) {
                try {
                    PackageManager userPkgManager =
                            getUserContext(lastVisibleLink.usage.getUser()).getPackageManager();
                    ApplicationInfo appInfo = userPkgManager.getApplicationInfo(
                            lastVisibleLink.usage.packageName, 0);
                    proxyLabel = appInfo.loadLabel(userPkgManager).toString();
                } catch (PackageManager.NameNotFoundException e) {
                    // do nothing
                }

            }
            usagesAndLabels.put(start.usage, proxyLabel);
        }


        for (int packageHash : mostRecentUsages.keySet()) {
        for (int packageHash : mostRecentUsages.keySet()) {
            if (!proxyPackages.contains(packageHash)) {
            if (!proxyPackages.contains(packageHash)) {
@@ -495,4 +587,24 @@ public class PermissionUsageHelper {
                    && lastAccessTime == other.lastAccessTime && isRunning == other.isRunning;
                    && lastAccessTime == other.lastAccessTime && isRunning == other.isRunning;
        }
        }
    }
    }

    private static class AccessChainLink {
        public final OpUsage usage;
        public final @AttributionFlags int flags;

        AccessChainLink(String op, String packageName, String attributionTag, int uid,
                int flags) {
            this.usage = new OpUsage(packageName, attributionTag, op, uid,
                    System.currentTimeMillis(), true, null);
            this.flags = flags;
        }

        public boolean isEnd() {
            return (flags & ATTRIBUTION_FLAG_ACCESSOR) != 0;
        }

        public boolean isStart() {
            return (flags & ATTRIBUTION_FLAG_RECEIVER) != 0;
        }
    }
}
}
+2 −10
Original line number Original line Diff line number Diff line
@@ -84,8 +84,8 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManager;
import android.app.AppOpsManager.AttributionFlags;
import android.app.AppOpsManager.AttributedOpEntry;
import android.app.AppOpsManager.AttributedOpEntry;
import android.app.AppOpsManager.AttributionFlags;
import android.app.AppOpsManager.HistoricalOps;
import android.app.AppOpsManager.HistoricalOps;
import android.app.AppOpsManager.Mode;
import android.app.AppOpsManager.Mode;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.OpEntry;
@@ -3255,13 +3255,6 @@ public class AppOpsService extends IAppOpsService.Stub {
                shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
                shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
    }
    }


    // TODO b/184963112: remove once full blaming is implemented
    private boolean isRecognitionServiceTemp(int code, String packageName) {
        return code == OP_RECORD_AUDIO
                && (packageName.equals("com.google.android.googlequicksearchbox")
                || packageName.equals("com.google.android.tts"));
    }

    private SyncNotedAppOp noteProxyOperationImpl(int code, AttributionSource attributionSource,
    private SyncNotedAppOp noteProxyOperationImpl(int code, AttributionSource attributionSource,
            boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
            boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
            boolean skipProxyOperation) {
            boolean skipProxyOperation) {
@@ -3289,8 +3282,7 @@ public class AppOpsService extends IAppOpsService.Stub {
        final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
        final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
        final boolean isProxyTrusted = mContext.checkPermission(
        final boolean isProxyTrusted = mContext.checkPermission(
                Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
                Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
                == PackageManager.PERMISSION_GRANTED || isSelfBlame
                == PackageManager.PERMISSION_GRANTED || isSelfBlame;
                || isRecognitionServiceTemp(code, proxyPackageName);


        if (!skipProxyOperation) {
        if (!skipProxyOperation) {
            final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
            final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY