Loading core/java/android/permission/PermissionUsageHelper.java +88 −50 Original line number Diff line number Diff line Loading @@ -183,8 +183,9 @@ public class PermissionUsageHelper { } Map<String, List<OpUsage>> rawUsages = getOpUsages(ops); Set<List<PackageAttribution>> proxyChains = getProxyChains(rawUsages.get(MICROPHONE)); Map<PackageAttribution, CharSequence> packagesWithAttributionLabels = getTrustedAttributions(rawUsages.get(MICROPHONE)); getTrustedAttributions(rawUsages.get(MICROPHONE), proxyChains); List<String> usedPermGroups = new ArrayList<>(rawUsages.keySet()); for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) { Loading @@ -192,10 +193,14 @@ public class PermissionUsageHelper { String permGroup = usedPermGroups.get(permGroupNum); Map<PackageAttribution, CharSequence> pkgAttrLabels = packagesWithAttributionLabels; Set<List<PackageAttribution>> proxies = proxyChains; if (!MICROPHONE.equals(permGroup)) { pkgAttrLabels = new ArrayMap<>(); proxies = new ArraySet<>(); } removeDuplicates(rawUsages.get(permGroup), pkgAttrLabels.keySet()); List<OpUsage> permUsages = removeDuplicatesAndProxies(rawUsages.get(permGroup), pkgAttrLabels.keySet(), proxies); if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) { isPhone = true; Loading @@ -205,9 +210,8 @@ public class PermissionUsageHelper { permGroup = CAMERA; } int numUsages = rawUsages.get(permGroup).size(); for (int usageNum = 0; usageNum < numUsages; usageNum++) { OpUsage usage = rawUsages.get(permGroup).get(usageNum); for (int usageNum = 0; usageNum < permUsages.size(); usageNum++) { OpUsage usage = permUsages.get(usageNum); usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup, usage.lastAccessTime, usage.isRunning, isPhone, packagesWithAttributionLabels.get(usage.toPackageAttr()))); Loading Loading @@ -278,7 +282,7 @@ public class PermissionUsageHelper { AppOpsManager.OpEventProxyInfo proxy = attrOpEntry.getLastProxyInfo(opFlags); if (proxy != null && proxy.getPackageName() != null) { proxyUsage = new OpUsage(proxy.getPackageName(), proxy.getAttributionTag(), uid, lastAccessTime, isRunning, null); proxy.getUid(), lastAccessTime, isRunning, null); } String permGroupName = getGroupForOp(op); Loading Loading @@ -323,17 +327,15 @@ public class PermissionUsageHelper { * trusted attribution label, if there is one */ private ArrayMap<PackageAttribution, CharSequence> getTrustedAttributions( List<OpUsage> usages) { List<OpUsage> usages, Set<List<PackageAttribution>> proxyChains) { ArrayMap<PackageAttribution, CharSequence> attributions = new ArrayMap<>(); if (usages == null) { return attributions; } Set<List<PackageAttribution>> proxyChains = getProxyChains(usages); Map<PackageAttribution, CharSequence> trustedLabels = getTrustedAttributionLabels(usages); for (List<PackageAttribution> chain : proxyChains) { // If this chain is empty, or has only one link, then do not show any special labels if (chain.size() <= 1) { Loading Loading @@ -413,37 +415,24 @@ public class PermissionUsageHelper { * the chain has the previous one listed as a proxy usage. */ private Set<List<PackageAttribution>> getProxyChains(List<OpUsage> usages) { ArrayMap<PackageAttribution, List<PackageAttribution>> proxyChains = new ArrayMap<>(); if (usages == null) { return new ArraySet<>(); } ArrayMap<PackageAttribution, ArrayList<PackageAttribution>> proxyChains = new ArrayMap<>(); // map of usages that still need to be removed, or added to a chain ArrayMap<PackageAttribution, OpUsage> remainingUsages = new ArrayMap<>(); // map of usage.proxy -> usage, telling us if a usage is a proxy ArrayMap<PackageAttribution, PackageAttribution> proxies = new ArrayMap<>(); for (int i = 0; i < remainingUsages.size(); i++) { for (int i = 0; i < usages.size(); i++) { OpUsage usage = usages.get(i); remainingUsages.put(usage.toPackageAttr(), usage); if (usage.proxy != null) { proxies.put(usage.proxy.toPackageAttr(), usage.toPackageAttr()); } } // find and remove all one-link chains (that is, all proxied apps whose proxy is not // included in the usage list), and apps that are neither proxy nor proxied. for (int usageNum = 0; usageNum < usages.size(); usageNum++) { OpUsage usage = usages.get(usageNum); PackageAttribution usageAttr = usage.toPackageAttr(); if (usage.proxy == null) { if (!proxies.containsKey(usageAttr)) { remainingUsages.remove(usageAttr); } continue; } PackageAttribution proxyAttr = usage.proxy.toPackageAttr(); if (!remainingUsages.containsKey(proxyAttr)) { remainingUsages.remove(usageAttr); } } // find all possible starting points for chains // find all possible end points for chains List<PackageAttribution> keys = new ArrayList<>(remainingUsages.keySet()); for (int usageNum = 0; usageNum < remainingUsages.size(); usageNum++) { OpUsage usage = remainingUsages.get(keys.get(usageNum)); Loading @@ -451,16 +440,21 @@ public class PermissionUsageHelper { continue; } PackageAttribution usageAttr = usage.toPackageAttr(); // If this usage has a proxy, but is not a proxy, it is the start of a chain. // If this usage has a proxy, but is not a proxy, it is the end of a chain. // If it has no proxy, and isn't a proxy, remove it. if (!proxies.containsKey(usageAttr) && usage.proxy != null) { proxyChains.put(usageAttr, List.of(usageAttr)); ArrayList<PackageAttribution> proxyList = new ArrayList<>(); proxyList.add(usageAttr); proxyChains.put(usageAttr, proxyList); } else if (!proxies.containsKey(usageAttr) && usage.proxy == null) { remainingUsages.remove(keys.get(usageNum)); } } // assemble the chains // assemble the chains in reverse order, then invert them for (int numStart = 0; numStart < proxyChains.size(); numStart++) { PackageAttribution currPackageAttr = proxyChains.keyAt(numStart); List<PackageAttribution> proxyChain = proxyChains.get(currPackageAttr); ArrayList<PackageAttribution> proxyChain = proxyChains.get(currPackageAttr); OpUsage currentUsage = remainingUsages.get(currPackageAttr); if (currentUsage == null || proxyChain == null) { continue; Loading @@ -484,6 +478,8 @@ public class PermissionUsageHelper { proxyChain.add(currPackageAttr); } // invert the lists, so the element without a proxy is first on the list Collections.reverse(proxyChain); } return new ArraySet<>(proxyChains.values()); Loading Loading @@ -590,7 +586,7 @@ public class PermissionUsageHelper { CharSequence label = getAttributionLabel(usage); if (trustedMap.get(usage.packageName).equals(label)) { toSetMap.put(usage.toPackageAttr(), label); toSetMap.put(opUsage.toPackageAttr(), label); } } Loading Loading @@ -619,36 +615,78 @@ public class PermissionUsageHelper { } } private void removeDuplicates(List<OpUsage> rawUsages, Set<PackageAttribution> specialAttributions) { List<OpUsage> toRemove = new ArrayList<>(); /** * If we have multiple usages of a * @param rawUsages The list of all usages that we wish to * @param specialAttributions A set of all usages that have a special label * @param proxies A list of proxy chains- all links but the last on the chain should be removed, * if the last link has a special label * @return A list of usages without duplicates or proxy usages. */ private List<OpUsage> removeDuplicatesAndProxies(List<OpUsage> rawUsages, Set<PackageAttribution> specialAttributions, Set<List<PackageAttribution>> proxies) { List<OpUsage> deDuped = new ArrayList<>(); if (rawUsages == null) { return; return deDuped; } List<PackageAttribution> toRemoveProxies = new ArrayList<>(); for (List<PackageAttribution> proxyList: proxies) { PackageAttribution lastLink = proxyList.get(proxyList.size() - 1); if (!specialAttributions.contains(lastLink)) { continue; } for (int proxyNum = 0; proxyNum < proxyList.size(); proxyNum++) { if (!proxyList.get(proxyNum).equals(lastLink)) { toRemoveProxies.add(proxyList.get(proxyNum)); } } } for (int usageNum = 0; usageNum < rawUsages.size(); usageNum++) { PackageAttribution usageAttr = rawUsages.get(usageNum).toPackageAttr(); OpUsage usage = rawUsages.get(usageNum); // If this attribution has a special attribution, do not remove it if (specialAttributions.contains(usageAttr)) { if (specialAttributions.contains(usage.toPackageAttr())) { deDuped.add(usage); } // If this attribution is a proxy, remove it if (toRemoveProxies.contains(usage.toPackageAttr())) { continue; } // Search the rest of the list for apps with the same uid. If there is one, mark this // usage for removal. for (int otherUsageNum = usageNum + 1; otherUsageNum < rawUsages.size(); otherUsageNum++) { if (rawUsages.get(otherUsageNum).uid == usageAttr.uid) { toRemove.add(rawUsages.get(usageNum)); // Search the rest of the list for usages with the same UID. If this is the most recent // usage for that uid, keep it. Otherwise, remove it boolean isMostRecentForUid = true; for (int otherUsageNum = 0; otherUsageNum < rawUsages.size(); otherUsageNum++) { OpUsage otherUsage = rawUsages.get(otherUsageNum); if (otherUsage.uid == usage.uid) { if (otherUsage.isRunning && !usage.isRunning) { isMostRecentForUid = false; } else if (usage.isRunning && otherUsage.lastAccessTime >= usage.lastAccessTime) { isMostRecentForUid = false; } else if (otherUsage.lastAccessTime >= usage.lastAccessTime) { isMostRecentForUid = false; } if (!isMostRecentForUid) { break; } } } for (int i = 0; i < toRemove.size(); i++) { rawUsages.remove(toRemove.get(i)); if (isMostRecentForUid) { deDuped.add(usage); } } return deDuped; } private boolean isUserSensitive(String packageName, UserHandle user, String op) { if (op.equals(OPSTR_PHONE_CALL_CAMERA) || op.equals(OPSTR_PHONE_CALL_MICROPHONE)) { return true; Loading Loading
core/java/android/permission/PermissionUsageHelper.java +88 −50 Original line number Diff line number Diff line Loading @@ -183,8 +183,9 @@ public class PermissionUsageHelper { } Map<String, List<OpUsage>> rawUsages = getOpUsages(ops); Set<List<PackageAttribution>> proxyChains = getProxyChains(rawUsages.get(MICROPHONE)); Map<PackageAttribution, CharSequence> packagesWithAttributionLabels = getTrustedAttributions(rawUsages.get(MICROPHONE)); getTrustedAttributions(rawUsages.get(MICROPHONE), proxyChains); List<String> usedPermGroups = new ArrayList<>(rawUsages.keySet()); for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) { Loading @@ -192,10 +193,14 @@ public class PermissionUsageHelper { String permGroup = usedPermGroups.get(permGroupNum); Map<PackageAttribution, CharSequence> pkgAttrLabels = packagesWithAttributionLabels; Set<List<PackageAttribution>> proxies = proxyChains; if (!MICROPHONE.equals(permGroup)) { pkgAttrLabels = new ArrayMap<>(); proxies = new ArraySet<>(); } removeDuplicates(rawUsages.get(permGroup), pkgAttrLabels.keySet()); List<OpUsage> permUsages = removeDuplicatesAndProxies(rawUsages.get(permGroup), pkgAttrLabels.keySet(), proxies); if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) { isPhone = true; Loading @@ -205,9 +210,8 @@ public class PermissionUsageHelper { permGroup = CAMERA; } int numUsages = rawUsages.get(permGroup).size(); for (int usageNum = 0; usageNum < numUsages; usageNum++) { OpUsage usage = rawUsages.get(permGroup).get(usageNum); for (int usageNum = 0; usageNum < permUsages.size(); usageNum++) { OpUsage usage = permUsages.get(usageNum); usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup, usage.lastAccessTime, usage.isRunning, isPhone, packagesWithAttributionLabels.get(usage.toPackageAttr()))); Loading Loading @@ -278,7 +282,7 @@ public class PermissionUsageHelper { AppOpsManager.OpEventProxyInfo proxy = attrOpEntry.getLastProxyInfo(opFlags); if (proxy != null && proxy.getPackageName() != null) { proxyUsage = new OpUsage(proxy.getPackageName(), proxy.getAttributionTag(), uid, lastAccessTime, isRunning, null); proxy.getUid(), lastAccessTime, isRunning, null); } String permGroupName = getGroupForOp(op); Loading Loading @@ -323,17 +327,15 @@ public class PermissionUsageHelper { * trusted attribution label, if there is one */ private ArrayMap<PackageAttribution, CharSequence> getTrustedAttributions( List<OpUsage> usages) { List<OpUsage> usages, Set<List<PackageAttribution>> proxyChains) { ArrayMap<PackageAttribution, CharSequence> attributions = new ArrayMap<>(); if (usages == null) { return attributions; } Set<List<PackageAttribution>> proxyChains = getProxyChains(usages); Map<PackageAttribution, CharSequence> trustedLabels = getTrustedAttributionLabels(usages); for (List<PackageAttribution> chain : proxyChains) { // If this chain is empty, or has only one link, then do not show any special labels if (chain.size() <= 1) { Loading Loading @@ -413,37 +415,24 @@ public class PermissionUsageHelper { * the chain has the previous one listed as a proxy usage. */ private Set<List<PackageAttribution>> getProxyChains(List<OpUsage> usages) { ArrayMap<PackageAttribution, List<PackageAttribution>> proxyChains = new ArrayMap<>(); if (usages == null) { return new ArraySet<>(); } ArrayMap<PackageAttribution, ArrayList<PackageAttribution>> proxyChains = new ArrayMap<>(); // map of usages that still need to be removed, or added to a chain ArrayMap<PackageAttribution, OpUsage> remainingUsages = new ArrayMap<>(); // map of usage.proxy -> usage, telling us if a usage is a proxy ArrayMap<PackageAttribution, PackageAttribution> proxies = new ArrayMap<>(); for (int i = 0; i < remainingUsages.size(); i++) { for (int i = 0; i < usages.size(); i++) { OpUsage usage = usages.get(i); remainingUsages.put(usage.toPackageAttr(), usage); if (usage.proxy != null) { proxies.put(usage.proxy.toPackageAttr(), usage.toPackageAttr()); } } // find and remove all one-link chains (that is, all proxied apps whose proxy is not // included in the usage list), and apps that are neither proxy nor proxied. for (int usageNum = 0; usageNum < usages.size(); usageNum++) { OpUsage usage = usages.get(usageNum); PackageAttribution usageAttr = usage.toPackageAttr(); if (usage.proxy == null) { if (!proxies.containsKey(usageAttr)) { remainingUsages.remove(usageAttr); } continue; } PackageAttribution proxyAttr = usage.proxy.toPackageAttr(); if (!remainingUsages.containsKey(proxyAttr)) { remainingUsages.remove(usageAttr); } } // find all possible starting points for chains // find all possible end points for chains List<PackageAttribution> keys = new ArrayList<>(remainingUsages.keySet()); for (int usageNum = 0; usageNum < remainingUsages.size(); usageNum++) { OpUsage usage = remainingUsages.get(keys.get(usageNum)); Loading @@ -451,16 +440,21 @@ public class PermissionUsageHelper { continue; } PackageAttribution usageAttr = usage.toPackageAttr(); // If this usage has a proxy, but is not a proxy, it is the start of a chain. // If this usage has a proxy, but is not a proxy, it is the end of a chain. // If it has no proxy, and isn't a proxy, remove it. if (!proxies.containsKey(usageAttr) && usage.proxy != null) { proxyChains.put(usageAttr, List.of(usageAttr)); ArrayList<PackageAttribution> proxyList = new ArrayList<>(); proxyList.add(usageAttr); proxyChains.put(usageAttr, proxyList); } else if (!proxies.containsKey(usageAttr) && usage.proxy == null) { remainingUsages.remove(keys.get(usageNum)); } } // assemble the chains // assemble the chains in reverse order, then invert them for (int numStart = 0; numStart < proxyChains.size(); numStart++) { PackageAttribution currPackageAttr = proxyChains.keyAt(numStart); List<PackageAttribution> proxyChain = proxyChains.get(currPackageAttr); ArrayList<PackageAttribution> proxyChain = proxyChains.get(currPackageAttr); OpUsage currentUsage = remainingUsages.get(currPackageAttr); if (currentUsage == null || proxyChain == null) { continue; Loading @@ -484,6 +478,8 @@ public class PermissionUsageHelper { proxyChain.add(currPackageAttr); } // invert the lists, so the element without a proxy is first on the list Collections.reverse(proxyChain); } return new ArraySet<>(proxyChains.values()); Loading Loading @@ -590,7 +586,7 @@ public class PermissionUsageHelper { CharSequence label = getAttributionLabel(usage); if (trustedMap.get(usage.packageName).equals(label)) { toSetMap.put(usage.toPackageAttr(), label); toSetMap.put(opUsage.toPackageAttr(), label); } } Loading Loading @@ -619,36 +615,78 @@ public class PermissionUsageHelper { } } private void removeDuplicates(List<OpUsage> rawUsages, Set<PackageAttribution> specialAttributions) { List<OpUsage> toRemove = new ArrayList<>(); /** * If we have multiple usages of a * @param rawUsages The list of all usages that we wish to * @param specialAttributions A set of all usages that have a special label * @param proxies A list of proxy chains- all links but the last on the chain should be removed, * if the last link has a special label * @return A list of usages without duplicates or proxy usages. */ private List<OpUsage> removeDuplicatesAndProxies(List<OpUsage> rawUsages, Set<PackageAttribution> specialAttributions, Set<List<PackageAttribution>> proxies) { List<OpUsage> deDuped = new ArrayList<>(); if (rawUsages == null) { return; return deDuped; } List<PackageAttribution> toRemoveProxies = new ArrayList<>(); for (List<PackageAttribution> proxyList: proxies) { PackageAttribution lastLink = proxyList.get(proxyList.size() - 1); if (!specialAttributions.contains(lastLink)) { continue; } for (int proxyNum = 0; proxyNum < proxyList.size(); proxyNum++) { if (!proxyList.get(proxyNum).equals(lastLink)) { toRemoveProxies.add(proxyList.get(proxyNum)); } } } for (int usageNum = 0; usageNum < rawUsages.size(); usageNum++) { PackageAttribution usageAttr = rawUsages.get(usageNum).toPackageAttr(); OpUsage usage = rawUsages.get(usageNum); // If this attribution has a special attribution, do not remove it if (specialAttributions.contains(usageAttr)) { if (specialAttributions.contains(usage.toPackageAttr())) { deDuped.add(usage); } // If this attribution is a proxy, remove it if (toRemoveProxies.contains(usage.toPackageAttr())) { continue; } // Search the rest of the list for apps with the same uid. If there is one, mark this // usage for removal. for (int otherUsageNum = usageNum + 1; otherUsageNum < rawUsages.size(); otherUsageNum++) { if (rawUsages.get(otherUsageNum).uid == usageAttr.uid) { toRemove.add(rawUsages.get(usageNum)); // Search the rest of the list for usages with the same UID. If this is the most recent // usage for that uid, keep it. Otherwise, remove it boolean isMostRecentForUid = true; for (int otherUsageNum = 0; otherUsageNum < rawUsages.size(); otherUsageNum++) { OpUsage otherUsage = rawUsages.get(otherUsageNum); if (otherUsage.uid == usage.uid) { if (otherUsage.isRunning && !usage.isRunning) { isMostRecentForUid = false; } else if (usage.isRunning && otherUsage.lastAccessTime >= usage.lastAccessTime) { isMostRecentForUid = false; } else if (otherUsage.lastAccessTime >= usage.lastAccessTime) { isMostRecentForUid = false; } if (!isMostRecentForUid) { break; } } } for (int i = 0; i < toRemove.size(); i++) { rawUsages.remove(toRemove.get(i)); if (isMostRecentForUid) { deDuped.add(usage); } } return deDuped; } private boolean isUserSensitive(String packageName, UserHandle user, String op) { if (op.equals(OPSTR_PHONE_CALL_CAMERA) || op.equals(OPSTR_PHONE_CALL_MICROPHONE)) { return true; Loading