Merge "Remove data from DiscreteRegistry for uninstalled packages." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e27c8a16c5
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user