Loading services/core/java/com/android/server/appop/DiscreteRegistry.java +133 −84 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"; Loading Loading @@ -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) { Loading @@ -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, Loading @@ -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; } } Loading @@ -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); } } } } Loading @@ -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, Loading @@ -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(); } } if (uid < Process.FIRST_APPLICATION_UID) { return false; private DiscreteOps getAllDiscreteOps() { DiscreteOps discreteOps = new DiscreteOps(); synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { discreteOps.merge(mDiscreteOps); } } if ((flags & (sDiscreteFlags)) == 0) { if (mCachedOps == null) { return false; mCachedOps = new DiscreteOps(); readDiscreteOpsFromDisk(mCachedOps); } discreteOps.merge(mCachedOps); return discreteOps; } } return true; } } private final class DiscreteOps { private final class DiscreteOps { Loading Loading @@ -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++) { Loading @@ -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); Loading @@ -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, Loading Loading @@ -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()) { Loading Loading @@ -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) { Loading Loading @@ -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; } } } services/core/java/com/android/server/appop/HistoricalRegistry.java +5 −4 Original line number Original line Diff line number Diff line Loading @@ -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); } } Loading Loading @@ -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) { Loading Loading @@ -645,6 +645,7 @@ final class HistoricalRegistry { mPersistence.clearHistoryDLocked(uid, packageName); mPersistence.clearHistoryDLocked(uid, packageName); } } } } mDiscreteRegistry.clearHistory(uid, packageName); } } void writeAndClearDiscreteHistory() { void writeAndClearDiscreteHistory() { Loading Loading
services/core/java/com/android/server/appop/DiscreteRegistry.java +133 −84 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"; Loading Loading @@ -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) { Loading @@ -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, Loading @@ -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; } } Loading @@ -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); } } } } Loading @@ -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, Loading @@ -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(); } } if (uid < Process.FIRST_APPLICATION_UID) { return false; private DiscreteOps getAllDiscreteOps() { DiscreteOps discreteOps = new DiscreteOps(); synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { discreteOps.merge(mDiscreteOps); } } if ((flags & (sDiscreteFlags)) == 0) { if (mCachedOps == null) { return false; mCachedOps = new DiscreteOps(); readDiscreteOpsFromDisk(mCachedOps); } discreteOps.merge(mCachedOps); return discreteOps; } } return true; } } private final class DiscreteOps { private final class DiscreteOps { Loading Loading @@ -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++) { Loading @@ -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); Loading @@ -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, Loading Loading @@ -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()) { Loading Loading @@ -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) { Loading Loading @@ -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; } } }
services/core/java/com/android/server/appop/HistoricalRegistry.java +5 −4 Original line number Original line Diff line number Diff line Loading @@ -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); } } Loading Loading @@ -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) { Loading Loading @@ -645,6 +645,7 @@ final class HistoricalRegistry { mPersistence.clearHistoryDLocked(uid, packageName); mPersistence.clearHistoryDLocked(uid, packageName); } } } } mDiscreteRegistry.clearHistory(uid, packageName); } } void writeAndClearDiscreteHistory() { void writeAndClearDiscreteHistory() { Loading