Merge "Remove data from DiscreteRegistry for uninstalled packages." into sc-dev

This commit is contained in:
Stanislav Zholnin
2021-04-06 15:48:48 +00:00
committed by Android (Google) Code Review
2 changed files with 138 additions and 88 deletions

View File

@@ -44,6 +44,7 @@ import android.os.FileUtils;
import android.os.Process; import android.os.Process;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog; import android.util.Slog;
import android.util.TypedXmlPullParser; import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer; import android.util.TypedXmlSerializer;
@@ -83,7 +84,7 @@ import java.util.List;
*/ */
final class DiscreteRegistry { final class DiscreteRegistry {
static final String TIMELINE_FILE_SUFFIX = "tl"; static final String DISCRETE_HISTORY_FILE_SUFFIX = "tl";
private static final String TAG = DiscreteRegistry.class.getSimpleName(); private static final String TAG = DiscreteRegistry.class.getSimpleName();
private static final String PROPERTY_DISCRETE_HISTORY_CUTOFF = "discrete_history_cutoff_millis"; private static final String PROPERTY_DISCRETE_HISTORY_CUTOFF = "discrete_history_cutoff_millis";
@@ -194,17 +195,6 @@ final class DiscreteRegistry {
} }
} }
private void createDiscreteAccessDir() {
if (!mDiscreteAccessDir.exists()) {
if (!mDiscreteAccessDir.mkdirs()) {
Slog.e(TAG, "Failed to create DiscreteRegistry directory");
}
FileUtils.setPermissions(mDiscreteAccessDir.getPath(),
FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1);
}
}
/* can be called only after HistoricalRegistry.isPersistenceInitialized() check */
void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag, void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime, @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime,
long accessDuration) { long accessDuration) {
@@ -220,80 +210,34 @@ final class DiscreteRegistry {
void writeAndClearAccessHistory() { void writeAndClearAccessHistory() {
synchronized (mOnDiskLock) { synchronized (mOnDiskLock) {
if (mDiscreteAccessDir == null) { if (mDiscreteAccessDir == null) {
Slog.e(TAG, "State not saved - persistence not initialized."); Slog.d(TAG, "State not saved - persistence not initialized.");
return; return;
} }
final File[] files = mDiscreteAccessDir.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
final String fileName = f.getName();
if (!fileName.endsWith(TIMELINE_FILE_SUFFIX)) {
continue;
}
try {
long timestamp = Long.valueOf(fileName.substring(0,
fileName.length() - TIMELINE_FILE_SUFFIX.length()));
if (Instant.now().minus(sDiscreteHistoryCutoff,
ChronoUnit.MILLIS).toEpochMilli() > timestamp) {
f.delete();
Slog.e(TAG, "Deleting file " + fileName);
}
} catch (Throwable t) {
Slog.e(TAG, "Error while cleaning timeline files: " + t.getMessage() + " "
+ t.getStackTrace());
}
}
}
DiscreteOps discreteOps; DiscreteOps discreteOps;
synchronized (mInMemoryLock) { synchronized (mInMemoryLock) {
discreteOps = mDiscreteOps; discreteOps = mDiscreteOps;
mDiscreteOps = new DiscreteOps(); mDiscreteOps = new DiscreteOps();
mCachedOps = null; mCachedOps = null;
} }
if (discreteOps.isEmpty()) { deleteOldDiscreteHistoryFilesLocked();
return; if (!discreteOps.isEmpty()) {
} persistDiscreteOpsLocked(discreteOps);
long currentTimeStamp = Instant.now().toEpochMilli();
try {
final File file = new File(mDiscreteAccessDir,
currentTimeStamp + TIMELINE_FILE_SUFFIX);
discreteOps.writeToFile(file);
} catch (Throwable t) {
Slog.e(TAG,
"Error writing timeline state: " + t.getMessage() + " "
+ Arrays.toString(t.getStackTrace()));
} }
} }
} }
void getHistoricalDiscreteOps(AppOpsManager.HistoricalOps result, long beginTimeMillis, void addFilteredDiscreteOpsToHistoricalOps(AppOpsManager.HistoricalOps result,
long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter, long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter, @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
@Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) { @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
DiscreteOps discreteOps = getAndCacheDiscreteOps(); DiscreteOps discreteOps = getAllDiscreteOps();
discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter, discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
opNamesFilter, attributionTagFilter, flagsFilter); opNamesFilter, attributionTagFilter, flagsFilter);
discreteOps.applyToHistoricalOps(result); discreteOps.applyToHistoricalOps(result);
return; return;
} }
private DiscreteOps getAndCacheDiscreteOps() {
DiscreteOps discreteOps = new DiscreteOps();
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
discreteOps.merge(mDiscreteOps);
}
if (mCachedOps == null) {
mCachedOps = new DiscreteOps();
readDiscreteOpsFromDisk(mCachedOps);
}
discreteOps.merge(mCachedOps);
}
return discreteOps;
}
private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) { private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
synchronized (mOnDiskLock) { synchronized (mOnDiskLock) {
long beginTimeMillis = Instant.now().minus(sDiscreteHistoryCutoff, long beginTimeMillis = Instant.now().minus(sDiscreteHistoryCutoff,
@@ -303,11 +247,11 @@ final class DiscreteRegistry {
if (files != null && files.length > 0) { if (files != null && files.length > 0) {
for (File f : files) { for (File f : files) {
final String fileName = f.getName(); final String fileName = f.getName();
if (!fileName.endsWith(TIMELINE_FILE_SUFFIX)) { if (!fileName.endsWith(DISCRETE_HISTORY_FILE_SUFFIX)) {
continue; continue;
} }
long timestamp = Long.valueOf(fileName.substring(0, long timestamp = Long.valueOf(fileName.substring(0,
fileName.length() - TIMELINE_FILE_SUFFIX.length())); fileName.length() - DISCRETE_HISTORY_FILE_SUFFIX.length()));
if (timestamp < beginTimeMillis) { if (timestamp < beginTimeMillis) {
continue; continue;
} }
@@ -322,8 +266,19 @@ final class DiscreteRegistry {
synchronized (mInMemoryLock) { synchronized (mInMemoryLock) {
mDiscreteOps = new DiscreteOps(); mDiscreteOps = new DiscreteOps();
} }
FileUtils.deleteContentsAndDir(mDiscreteAccessDir); clearOnDiskHistoryLocked();
createDiscreteAccessDir(); }
}
void clearHistory(int uid, String packageName) {
synchronized (mOnDiskLock) {
DiscreteOps discreteOps;
synchronized (mInMemoryLock) {
discreteOps = getAllDiscreteOps();
clearHistory();
}
discreteOps.clearHistory(uid, packageName);
persistDiscreteOpsLocked(discreteOps);
} }
} }
@@ -332,7 +287,7 @@ final class DiscreteRegistry {
@AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp, @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
@NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
int nDiscreteOps) { int nDiscreteOps) {
DiscreteOps discreteOps = getAndCacheDiscreteOps(); DiscreteOps discreteOps = getAllDiscreteOps();
String[] opNamesFilter = dumpOp == OP_NONE ? null String[] opNamesFilter = dumpOp == OP_NONE ? null
: new String[]{AppOpsManager.opToPublicName(dumpOp)}; : new String[]{AppOpsManager.opToPublicName(dumpOp)};
discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter, discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
@@ -340,17 +295,26 @@ final class DiscreteRegistry {
discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps); discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
} }
public static boolean isDiscreteOp(int op, int uid, @AppOpsManager.OpFlags int flags) { private void clearOnDiskHistoryLocked() {
if (!ArrayUtils.contains(sDiscreteOps, op)) { mCachedOps = null;
return false; FileUtils.deleteContentsAndDir(mDiscreteAccessDir);
createDiscreteAccessDir();
}
private DiscreteOps getAllDiscreteOps() {
DiscreteOps discreteOps = new DiscreteOps();
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
discreteOps.merge(mDiscreteOps);
}
if (mCachedOps == null) {
mCachedOps = new DiscreteOps();
readDiscreteOpsFromDisk(mCachedOps);
}
discreteOps.merge(mCachedOps);
return discreteOps;
} }
if (uid < Process.FIRST_APPLICATION_UID) {
return false;
}
if ((flags & (sDiscreteFlags)) == 0) {
return false;
}
return true;
} }
private final class DiscreteOps { private final class DiscreteOps {
@@ -399,6 +363,15 @@ final class DiscreteRegistry {
} }
} }
private void clearHistory(int uid, String packageName) {
if (mUids.containsKey(uid)) {
mUids.get(uid).clearPackage(packageName);
if (mUids.get(uid).isEmpty()) {
mUids.remove(uid);
}
}
}
private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) { private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
int nUids = mUids.size(); int nUids = mUids.size();
for (int i = 0; i < nUids; i++) { for (int i = 0; i < nUids; i++) {
@@ -406,8 +379,7 @@ final class DiscreteRegistry {
} }
} }
private void writeToFile(File f) throws Exception { private void writeToStream(FileOutputStream stream) throws Exception {
FileOutputStream stream = new FileOutputStream(f);
TypedXmlSerializer out = Xml.resolveSerializer(stream); TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true); out.startDocument(null, true);
@@ -423,7 +395,6 @@ final class DiscreteRegistry {
} }
out.endTag(null, TAG_HISTORY); out.endTag(null, TAG_HISTORY);
out.endDocument(); out.endDocument();
stream.close();
} }
private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf, private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
@@ -475,6 +446,60 @@ final class DiscreteRegistry {
} }
} }
private void createDiscreteAccessDir() {
if (!mDiscreteAccessDir.exists()) {
if (!mDiscreteAccessDir.mkdirs()) {
Slog.e(TAG, "Failed to create DiscreteRegistry directory");
}
FileUtils.setPermissions(mDiscreteAccessDir.getPath(),
FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1);
}
}
private void persistDiscreteOpsLocked(DiscreteOps discreteOps) {
long currentTimeStamp = Instant.now().toEpochMilli();
final AtomicFile file = new AtomicFile(new File(mDiscreteAccessDir,
currentTimeStamp + DISCRETE_HISTORY_FILE_SUFFIX));
FileOutputStream stream = null;
try {
stream = file.startWrite();
discreteOps.writeToStream(stream);
file.finishWrite(stream);
} catch (Throwable t) {
Slog.e(TAG,
"Error writing timeline state: " + t.getMessage() + " "
+ Arrays.toString(t.getStackTrace()));
if (stream != null) {
file.failWrite(stream);
}
}
}
private void deleteOldDiscreteHistoryFilesLocked() {
final File[] files = mDiscreteAccessDir.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
final String fileName = f.getName();
if (!fileName.endsWith(DISCRETE_HISTORY_FILE_SUFFIX)) {
continue;
}
try {
long timestamp = Long.valueOf(fileName.substring(0,
fileName.length() - DISCRETE_HISTORY_FILE_SUFFIX.length()));
if (Instant.now().minus(sDiscreteHistoryCutoff,
ChronoUnit.MILLIS).toEpochMilli() > timestamp) {
f.delete();
Slog.e(TAG, "Deleting file " + fileName);
}
} catch (Throwable t) {
Slog.e(TAG, "Error while cleaning timeline files: " + t.getMessage() + " "
+ t.getStackTrace());
}
}
}
}
private void createDiscreteAccessDirLocked() { private void createDiscreteAccessDirLocked() {
if (!mDiscreteAccessDir.exists()) { if (!mDiscreteAccessDir.exists()) {
if (!mDiscreteAccessDir.mkdirs()) { if (!mDiscreteAccessDir.mkdirs()) {
@@ -524,6 +549,10 @@ final class DiscreteRegistry {
} }
} }
private void clearPackage(String packageName) {
mPackages.remove(packageName);
}
void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag, void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
long accessTime, long accessDuration) { long accessTime, long accessDuration) {
@@ -934,5 +963,25 @@ final class DiscreteRegistry {
} }
return result; return result;
} }
private static boolean isDiscreteOp(int op, int uid, @AppOpsManager.OpFlags int flags) {
if (!ArrayUtils.contains(sDiscreteOps, op)) {
return false;
}
if (!isDiscreteUid(uid)) {
return false;
}
if ((flags & (sDiscreteFlags)) == 0) {
return false;
}
return true;
}
private static boolean isDiscreteUid(int uid) {
if (uid < Process.FIRST_APPLICATION_UID) {
return false;
}
return true;
}
} }

View File

@@ -394,8 +394,8 @@ final class HistoricalRegistry {
} }
if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) { if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
mDiscreteRegistry.getHistoricalDiscreteOps(result, beginTimeMillis, endTimeMillis, mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
filter, uid, packageName, opNames, attributionTag, endTimeMillis, filter, uid, packageName, opNames, attributionTag,
flags); flags);
} }
@@ -428,8 +428,8 @@ final class HistoricalRegistry {
inMemoryAdjEndTimeMillis); inMemoryAdjEndTimeMillis);
if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) { if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
mDiscreteRegistry.getHistoricalDiscreteOps(result, beginTimeMillis, endTimeMillis, mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
filter, uid, packageName, opNames, attributionTag, flags); endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags);
} }
if ((historyFlags & HISTORY_FLAG_AGGREGATE) != 0) { if ((historyFlags & HISTORY_FLAG_AGGREGATE) != 0) {
@@ -645,6 +645,7 @@ final class HistoricalRegistry {
mPersistence.clearHistoryDLocked(uid, packageName); mPersistence.clearHistoryDLocked(uid, packageName);
} }
} }
mDiscreteRegistry.clearHistory(uid, packageName);
} }
void writeAndClearDiscreteHistory() { void writeAndClearDiscreteHistory() {