diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index bef876f92a016..9c137879c2629 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -219,6 +219,14 @@ public final class UserHandle implements Parcelable { } } + /** + * Whether a UID belongs to a shared app gid. + * @hide + */ + public static boolean isSharedAppGid(int uid) { + return getAppIdFromSharedAppGid(uid) != -1; + } + /** * Returns the user for a given uid. * @param uid A uid for an application running in a particular user. diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index e09a45f9654bf..8b3aaf7be8716 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -31,6 +31,7 @@ import static android.net.NetworkTemplate.buildTemplateMobileWithRatType; import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.NetworkTemplate.getAllCollapsedRatTypes; import static android.os.Debug.getIonHeapsSizeKb; +import static android.os.Process.LAST_SHARED_APPLICATION_GID; import static android.os.Process.getUidForPid; import static android.os.storage.VolumeInfo.TYPE_PRIVATE; import static android.os.storage.VolumeInfo.TYPE_PUBLIC; @@ -1561,20 +1562,59 @@ public class StatsPullAtomService extends SystemService { } int pullCpuTimePerUidFreqLocked(int atomTag, List pulledData) { + // Aggregate times for the same uids. + SparseArray aggregated = new SparseArray<>(); mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> { - for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { - if (cpuFreqTimeMs[freqIndex] >= MIN_CPU_TIME_PER_UID_FREQ) { + // For uids known to be aggregated from many entries allow mutating in place to avoid + // many copies. Otherwise, copy before aggregating. + boolean mutateInPlace = false; + if (UserHandle.isIsolated(uid)) { + // Skip individual isolated uids because they are recycled and quickly removed from + // the underlying data source. + return; + } else if (UserHandle.isSharedAppGid(uid)) { + // All shared app gids are accounted together. + uid = LAST_SHARED_APPLICATION_GID; + mutateInPlace = true; + } else if (UserHandle.isApp(uid)) { + // Apps are accounted under their app id. + uid = UserHandle.getAppId(uid); + } + + long[] aggCpuFreqTimeMs = aggregated.get(uid); + if (aggCpuFreqTimeMs != null) { + if (!mutateInPlace) { + aggCpuFreqTimeMs = Arrays.copyOf(aggCpuFreqTimeMs, cpuFreqTimeMs.length); + aggregated.put(uid, aggCpuFreqTimeMs); + } + for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { + aggCpuFreqTimeMs[freqIndex] += cpuFreqTimeMs[freqIndex]; + } + } else { + if (mutateInPlace) { + cpuFreqTimeMs = Arrays.copyOf(cpuFreqTimeMs, cpuFreqTimeMs.length); + } + aggregated.put(uid, cpuFreqTimeMs); + } + }); + + int size = aggregated.size(); + for (int i = 0; i < size; ++i) { + int uid = aggregated.keyAt(i); + long[] aggCpuFreqTimeMs = aggregated.valueAt(i); + for (int freqIndex = 0; freqIndex < aggCpuFreqTimeMs.length; ++freqIndex) { + if (aggCpuFreqTimeMs[freqIndex] >= MIN_CPU_TIME_PER_UID_FREQ) { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(uid) .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeInt(freqIndex) - .writeLong(cpuFreqTimeMs[freqIndex]) + .writeLong(aggCpuFreqTimeMs[freqIndex]) .build(); pulledData.add(e); } } - }); + } return StatsManager.PULL_SUCCESS; }