Loading core/java/com/android/internal/compat/ChangeReporter.java +60 −42 Original line number Diff line number Diff line Loading @@ -18,22 +18,24 @@ package com.android.internal.compat; import static android.text.TextUtils.formatSimple; import static java.util.Collections.EMPTY_SET; import android.annotation.IntDef; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.compat.flags.Flags; import com.android.internal.util.FrameworkStatsLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.HashMap; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; /** * A helper class to report changes to stats log. Loading @@ -42,6 +44,8 @@ import java.util.Set; */ public final class ChangeReporter { private static final String TAG = "CompatChangeReporter"; private static final Function<Integer, Set<ChangeReport>> NEW_CHANGE_REPORT_SET = uid -> Collections.synchronizedSet(new HashSet<>()); private int mSource; private static final class ChangeReport { Loading Loading @@ -69,15 +73,14 @@ public final class ChangeReporter { } // Maps uid to a set of ChangeReports (that were reported for that uid). @GuardedBy("mReportedChanges") private final Map<Integer, Set<ChangeReport>> mReportedChanges; private final ConcurrentHashMap<Integer, Set<ChangeReport>> mReportedChanges; // When true will of every time to debug (logcat). private boolean mDebugLogAll; public ChangeReporter(@Source int source) { mSource = source; mReportedChanges = new HashMap<>(); mReportedChanges = new ConcurrentHashMap<>(); mDebugLogAll = false; } Loading @@ -93,14 +96,15 @@ public final class ChangeReporter { * actually log. If the sdk version does not matter, should be true. */ public void reportChange(int uid, long changeId, int state, boolean isLoggableBySdk) { if (shouldWriteToStatsLog(uid, changeId, state)) { boolean isAlreadyReported = checkAndSetIsAlreadyReported(uid, new ChangeReport(changeId, state)); if (!isAlreadyReported) { FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId, state, mSource); } if (shouldWriteToDebug(uid, changeId, state, isLoggableBySdk)) { if (shouldWriteToDebug(isAlreadyReported, state, isLoggableBySdk)) { debugLog(uid, changeId, state); } markAsReported(uid, new ChangeReport(changeId, state)); } /** Loading Loading @@ -129,7 +133,6 @@ public final class ChangeReporter { mDebugLogAll = false; } /** * Returns whether the next report should be logged to FrameworkStatsLog. * Loading @@ -139,28 +142,26 @@ public final class ChangeReporter { * @return true if the report should be logged */ @VisibleForTesting public boolean shouldWriteToStatsLog(int uid, long changeId, int state) { boolean shouldWriteToStatsLog(int uid, long changeId, int state) { return !isAlreadyReported(uid, new ChangeReport(changeId, state)); } /** * Returns whether the next report should be logged to logcat. * * @param uid affected by the change * @param changeId the reported change id * @param isAlreadyReported is the change already reported * @param state of the reported change - enabled/disabled/only logged * @param isLoggableBySdk whether debug logging is allowed for this change based on target * SDK version. This is combined with other logic to determine whether to * @param isLoggableBySdk whether debug logging is allowed for this change based on target SDK * version. This is combined with other logic to determine whether to * actually log. If the sdk version does not matter, should be true. * @return true if the report should be logged */ @VisibleForTesting public boolean shouldWriteToDebug( int uid, long changeId, int state, boolean isLoggableBySdk) { private boolean shouldWriteToDebug( boolean isAlreadyReported, int state, boolean isLoggableBySdk) { // If log all bit is on, always return true. if (mDebugLogAll) return true; // If the change has already been reported, do not write. if (isAlreadyReported(uid, new ChangeReport(changeId, state))) return false; if (isAlreadyReported) return false; // If the flag is turned off or the TAG's logging is forced to debug level with // `adb setprop log.tag.CompatChangeReporter=DEBUG`, write to debug since the above checks Loading @@ -178,33 +179,53 @@ public final class ChangeReporter { * @param uid affected by the change * @param changeId the reported change id * @param state of the reported change - enabled/disabled/only logged * * @return true if the report should be logged */ @VisibleForTesting public boolean shouldWriteToDebug(int uid, long changeId, int state) { boolean shouldWriteToDebug(int uid, long changeId, int state) { return shouldWriteToDebug(uid, changeId, state, true); } private boolean isAlreadyReported(int uid, ChangeReport report) { synchronized (mReportedChanges) { Set<ChangeReport> reportedChangesForUid = mReportedChanges.get(uid); if (reportedChangesForUid == null) { return false; } else { return reportedChangesForUid.contains(report); /** * Returns whether the next report should be logged to logcat. * * @param uid affected by the change * @param changeId the reported change id * @param state of the reported change - enabled/disabled/only logged * @param isLoggableBySdk whether debug logging is allowed for this change based on target SDK * version. This is combined with other logic to determine whether to * actually log. If the sdk version does not matter, should be true. * @return true if the report should be logged */ @VisibleForTesting boolean shouldWriteToDebug(int uid, long changeId, int state, boolean isLoggableBySdk) { return shouldWriteToDebug( isAlreadyReported(uid, new ChangeReport(changeId, state)), state, isLoggableBySdk); } /** * Return if change has been reported. Also mark change as reported if not. * * @param uid affected by the change * @param changeReport change reported to be checked and marked as reported. * * @return true if change has been reported, and vice versa. */ private boolean checkAndSetIsAlreadyReported(int uid, ChangeReport changeReport) { boolean isAlreadyReported = isAlreadyReported(uid, changeReport); if (!isAlreadyReported) { markAsReported(uid, changeReport); } return isAlreadyReported; } private boolean isAlreadyReported(int uid, ChangeReport report) { return mReportedChanges.getOrDefault(uid, EMPTY_SET).contains(report); } private void markAsReported(int uid, ChangeReport report) { synchronized (mReportedChanges) { Set<ChangeReport> reportedChangesForUid = mReportedChanges.get(uid); if (reportedChangesForUid == null) { mReportedChanges.put(uid, new HashSet<ChangeReport>()); reportedChangesForUid = mReportedChanges.get(uid); } reportedChangesForUid.add(report); } mReportedChanges.computeIfAbsent(uid, NEW_CHANGE_REPORT_SET).add(report); } /** Loading @@ -216,10 +237,8 @@ public final class ChangeReporter { * @param uid to reset */ public void resetReportedChanges(int uid) { synchronized (mReportedChanges) { mReportedChanges.remove(uid); } } private void debugLog(int uid, long changeId, int state) { String message = formatSimple("Compat change id reported: %d; UID %d; state: %s", changeId, Loading @@ -229,7 +248,6 @@ public final class ChangeReporter { } else { Log.d(TAG, message); } } /** Loading Loading
core/java/com/android/internal/compat/ChangeReporter.java +60 −42 Original line number Diff line number Diff line Loading @@ -18,22 +18,24 @@ package com.android.internal.compat; import static android.text.TextUtils.formatSimple; import static java.util.Collections.EMPTY_SET; import android.annotation.IntDef; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.compat.flags.Flags; import com.android.internal.util.FrameworkStatsLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.HashMap; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; /** * A helper class to report changes to stats log. Loading @@ -42,6 +44,8 @@ import java.util.Set; */ public final class ChangeReporter { private static final String TAG = "CompatChangeReporter"; private static final Function<Integer, Set<ChangeReport>> NEW_CHANGE_REPORT_SET = uid -> Collections.synchronizedSet(new HashSet<>()); private int mSource; private static final class ChangeReport { Loading Loading @@ -69,15 +73,14 @@ public final class ChangeReporter { } // Maps uid to a set of ChangeReports (that were reported for that uid). @GuardedBy("mReportedChanges") private final Map<Integer, Set<ChangeReport>> mReportedChanges; private final ConcurrentHashMap<Integer, Set<ChangeReport>> mReportedChanges; // When true will of every time to debug (logcat). private boolean mDebugLogAll; public ChangeReporter(@Source int source) { mSource = source; mReportedChanges = new HashMap<>(); mReportedChanges = new ConcurrentHashMap<>(); mDebugLogAll = false; } Loading @@ -93,14 +96,15 @@ public final class ChangeReporter { * actually log. If the sdk version does not matter, should be true. */ public void reportChange(int uid, long changeId, int state, boolean isLoggableBySdk) { if (shouldWriteToStatsLog(uid, changeId, state)) { boolean isAlreadyReported = checkAndSetIsAlreadyReported(uid, new ChangeReport(changeId, state)); if (!isAlreadyReported) { FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId, state, mSource); } if (shouldWriteToDebug(uid, changeId, state, isLoggableBySdk)) { if (shouldWriteToDebug(isAlreadyReported, state, isLoggableBySdk)) { debugLog(uid, changeId, state); } markAsReported(uid, new ChangeReport(changeId, state)); } /** Loading Loading @@ -129,7 +133,6 @@ public final class ChangeReporter { mDebugLogAll = false; } /** * Returns whether the next report should be logged to FrameworkStatsLog. * Loading @@ -139,28 +142,26 @@ public final class ChangeReporter { * @return true if the report should be logged */ @VisibleForTesting public boolean shouldWriteToStatsLog(int uid, long changeId, int state) { boolean shouldWriteToStatsLog(int uid, long changeId, int state) { return !isAlreadyReported(uid, new ChangeReport(changeId, state)); } /** * Returns whether the next report should be logged to logcat. * * @param uid affected by the change * @param changeId the reported change id * @param isAlreadyReported is the change already reported * @param state of the reported change - enabled/disabled/only logged * @param isLoggableBySdk whether debug logging is allowed for this change based on target * SDK version. This is combined with other logic to determine whether to * @param isLoggableBySdk whether debug logging is allowed for this change based on target SDK * version. This is combined with other logic to determine whether to * actually log. If the sdk version does not matter, should be true. * @return true if the report should be logged */ @VisibleForTesting public boolean shouldWriteToDebug( int uid, long changeId, int state, boolean isLoggableBySdk) { private boolean shouldWriteToDebug( boolean isAlreadyReported, int state, boolean isLoggableBySdk) { // If log all bit is on, always return true. if (mDebugLogAll) return true; // If the change has already been reported, do not write. if (isAlreadyReported(uid, new ChangeReport(changeId, state))) return false; if (isAlreadyReported) return false; // If the flag is turned off or the TAG's logging is forced to debug level with // `adb setprop log.tag.CompatChangeReporter=DEBUG`, write to debug since the above checks Loading @@ -178,33 +179,53 @@ public final class ChangeReporter { * @param uid affected by the change * @param changeId the reported change id * @param state of the reported change - enabled/disabled/only logged * * @return true if the report should be logged */ @VisibleForTesting public boolean shouldWriteToDebug(int uid, long changeId, int state) { boolean shouldWriteToDebug(int uid, long changeId, int state) { return shouldWriteToDebug(uid, changeId, state, true); } private boolean isAlreadyReported(int uid, ChangeReport report) { synchronized (mReportedChanges) { Set<ChangeReport> reportedChangesForUid = mReportedChanges.get(uid); if (reportedChangesForUid == null) { return false; } else { return reportedChangesForUid.contains(report); /** * Returns whether the next report should be logged to logcat. * * @param uid affected by the change * @param changeId the reported change id * @param state of the reported change - enabled/disabled/only logged * @param isLoggableBySdk whether debug logging is allowed for this change based on target SDK * version. This is combined with other logic to determine whether to * actually log. If the sdk version does not matter, should be true. * @return true if the report should be logged */ @VisibleForTesting boolean shouldWriteToDebug(int uid, long changeId, int state, boolean isLoggableBySdk) { return shouldWriteToDebug( isAlreadyReported(uid, new ChangeReport(changeId, state)), state, isLoggableBySdk); } /** * Return if change has been reported. Also mark change as reported if not. * * @param uid affected by the change * @param changeReport change reported to be checked and marked as reported. * * @return true if change has been reported, and vice versa. */ private boolean checkAndSetIsAlreadyReported(int uid, ChangeReport changeReport) { boolean isAlreadyReported = isAlreadyReported(uid, changeReport); if (!isAlreadyReported) { markAsReported(uid, changeReport); } return isAlreadyReported; } private boolean isAlreadyReported(int uid, ChangeReport report) { return mReportedChanges.getOrDefault(uid, EMPTY_SET).contains(report); } private void markAsReported(int uid, ChangeReport report) { synchronized (mReportedChanges) { Set<ChangeReport> reportedChangesForUid = mReportedChanges.get(uid); if (reportedChangesForUid == null) { mReportedChanges.put(uid, new HashSet<ChangeReport>()); reportedChangesForUid = mReportedChanges.get(uid); } reportedChangesForUid.add(report); } mReportedChanges.computeIfAbsent(uid, NEW_CHANGE_REPORT_SET).add(report); } /** Loading @@ -216,10 +237,8 @@ public final class ChangeReporter { * @param uid to reset */ public void resetReportedChanges(int uid) { synchronized (mReportedChanges) { mReportedChanges.remove(uid); } } private void debugLog(int uid, long changeId, int state) { String message = formatSimple("Compat change id reported: %d; UID %d; state: %s", changeId, Loading @@ -229,7 +248,6 @@ public final class ChangeReporter { } else { Log.d(TAG, message); } } /** Loading