diff options
18 files changed, 1874 insertions, 904 deletions
diff --git a/api/current.txt b/api/current.txt index 176a03b6c5c5..2156867ceb43 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4277,7 +4277,8 @@ package android.app { method public void stopWatchingMode(android.app.AppOpsManager.OnOpChangedListener); method public int unsafeCheckOp(String, int, String); method public int unsafeCheckOpNoThrow(String, int, String); - method public int unsafeCheckOpRaw(String, int, String); + method public int unsafeCheckOpRaw(@NonNull String, int, String); + method public int unsafeCheckOpRawNoThrow(@NonNull String, int, @NonNull String); field public static final int MODE_ALLOWED = 0; // 0x0 field public static final int MODE_DEFAULT = 3; // 0x3 field public static final int MODE_ERRORED = 2; // 0x2 diff --git a/api/system-current.txt b/api/system-current.txt index bcedee08f305..d104808ee7a4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -362,27 +362,34 @@ package android.app { field public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms"; field public static final String OPSTR_WRITE_SMS = "android:write_sms"; field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper"; - field public static final int UID_STATE_BACKGROUND = 5; // 0x5 - field public static final int UID_STATE_CACHED = 6; // 0x6 - field public static final int UID_STATE_FOREGROUND = 4; // 0x4 - field public static final int UID_STATE_FOREGROUND_SERVICE = 3; // 0x3 - field public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 2; // 0x2 - field public static final int UID_STATE_PERSISTENT = 0; // 0x0 - field public static final int UID_STATE_TOP = 1; // 0x1 + field public static final int OP_FLAGS_ALL = 31; // 0x1f + field public static final int OP_FLAGS_ALL_TRUSTED = 13; // 0xd + field public static final int OP_FLAG_SELF = 1; // 0x1 + field public static final int OP_FLAG_TRUSTED_PROXIED = 8; // 0x8 + field public static final int OP_FLAG_TRUSTED_PROXY = 2; // 0x2 + field public static final int OP_FLAG_UNTRUSTED_PROXIED = 16; // 0x10 + field public static final int OP_FLAG_UNTRUSTED_PROXY = 4; // 0x4 + field public static final int UID_STATE_BACKGROUND = 600; // 0x258 + field public static final int UID_STATE_CACHED = 700; // 0x2bc + field public static final int UID_STATE_FOREGROUND = 500; // 0x1f4 + field public static final int UID_STATE_FOREGROUND_SERVICE = 400; // 0x190 + field public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300; // 0x12c + field public static final int UID_STATE_PERSISTENT = 100; // 0x64 + field public static final int UID_STATE_TOP = 200; // 0xc8 } public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable { method public int describeContents(); - method public long getAccessCount(int); - method public long getAccessDuration(int); - method public long getBackgroundAccessCount(); - method public long getBackgroundAccessDuration(); - method public long getBackgroundRejectCount(); - method public long getForegroundAccessCount(); - method public long getForegroundAccessDuration(); - method public long getForegroundRejectCount(); + method public long getAccessCount(int, int, int); + method public long getAccessDuration(int, int, int); + method public long getBackgroundAccessCount(int); + method public long getBackgroundAccessDuration(int); + method public long getBackgroundRejectCount(int); + method public long getForegroundAccessCount(int); + method public long getForegroundAccessDuration(int); + method public long getForegroundRejectCount(int); method @NonNull public String getOpName(); - method public long getRejectCount(int); + method public long getRejectCount(int, int, int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOp> CREATOR; } @@ -404,6 +411,7 @@ package android.app { public static final class AppOpsManager.HistoricalOpsRequest.Builder { ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build(); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setUid(int); @@ -431,17 +439,24 @@ package android.app { public static final class AppOpsManager.OpEntry implements android.os.Parcelable { method public int describeContents(); - method public int getDuration(); - method public long getLastAccessBackgroundTime(); - method public long getLastAccessForegroundTime(); - method public long getLastAccessTime(); - method public long getLastRejectBackgroundTime(); - method public long getLastRejectForegroundTime(); - method public long getLastRejectTime(); + method public long getDuration(); + method public long getLastAccessBackgroundTime(int); + method public long getLastAccessForegroundTime(int); + method public long getLastAccessTime(int); + method public long getLastAccessTime(int, int, int); + method public long getLastBackgroundDuration(int); + method public long getLastDuration(int, int, int); + method public long getLastForegroundDuration(int); + method public long getLastRejectBackgroundTime(int); + method public long getLastRejectForegroundTime(int); + method public long getLastRejectTime(int); + method public long getLastRejectTime(int, int, int); method public int getMode(); - method public String getOpStr(); - method public String getProxyPackageName(); + method @NonNull public String getOpStr(); + method @Nullable public String getProxyPackageName(); + method @Nullable public String getProxyPackageName(int, int); method public int getProxyUid(); + method public int getProxyUid(int, int); method public boolean isRunning(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR; @@ -450,7 +465,7 @@ package android.app { public static final class AppOpsManager.PackageOps implements android.os.Parcelable { method public int describeContents(); method public java.util.List<android.app.AppOpsManager.OpEntry> getOps(); - method public String getPackageName(); + method @NonNull public String getPackageName(); method public int getUid(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR; diff --git a/api/test-current.txt b/api/test-current.txt index 4f7605bdc26e..a3b61552cc0e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -193,29 +193,36 @@ package android.app { field public static final String OPSTR_WRITE_SMS = "android:write_sms"; field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper"; field public static final int OP_COARSE_LOCATION = 0; // 0x0 + field public static final int OP_FLAGS_ALL = 31; // 0x1f + field public static final int OP_FLAG_SELF = 1; // 0x1 + field public static final int OP_FLAG_TRUSTED_PROXIED = 8; // 0x8 + field public static final int OP_FLAG_TRUSTED_PROXY = 2; // 0x2 + field public static final int OP_FLAG_UNTRUSTED_PROXIED = 16; // 0x10 + field public static final int OP_FLAG_UNTRUSTED_PROXY = 4; // 0x4 field public static final int OP_RECORD_AUDIO = 27; // 0x1b + field public static final int OP_START_FOREGROUND = 76; // 0x4c field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18 - field public static final int UID_STATE_BACKGROUND = 5; // 0x5 - field public static final int UID_STATE_CACHED = 6; // 0x6 - field public static final int UID_STATE_FOREGROUND = 4; // 0x4 - field public static final int UID_STATE_FOREGROUND_SERVICE = 3; // 0x3 - field public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 2; // 0x2 - field public static final int UID_STATE_PERSISTENT = 0; // 0x0 - field public static final int UID_STATE_TOP = 1; // 0x1 + field public static final int UID_STATE_BACKGROUND = 600; // 0x258 + field public static final int UID_STATE_CACHED = 700; // 0x2bc + field public static final int UID_STATE_FOREGROUND = 500; // 0x1f4 + field public static final int UID_STATE_FOREGROUND_SERVICE = 400; // 0x190 + field public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300; // 0x12c + field public static final int UID_STATE_PERSISTENT = 100; // 0x64 + field public static final int UID_STATE_TOP = 200; // 0xc8 } public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable { method public int describeContents(); - method public long getAccessCount(int); - method public long getAccessDuration(int); - method public long getBackgroundAccessCount(); - method public long getBackgroundAccessDuration(); - method public long getBackgroundRejectCount(); - method public long getForegroundAccessCount(); - method public long getForegroundAccessDuration(); - method public long getForegroundRejectCount(); + method public long getAccessCount(int, int, int); + method public long getAccessDuration(int, int, int); + method public long getBackgroundAccessCount(int); + method public long getBackgroundAccessDuration(int); + method public long getBackgroundRejectCount(int); + method public long getForegroundAccessCount(int); + method public long getForegroundAccessDuration(int); + method public long getForegroundRejectCount(int); method @NonNull public String getOpName(); - method public long getRejectCount(int); + method public long getRejectCount(int, int, int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOp> CREATOR; } @@ -228,9 +235,9 @@ package android.app { method public int getUidCount(); method @Nullable public android.app.AppOpsManager.HistoricalUidOps getUidOps(int); method @NonNull public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(int); - method public void increaseAccessCount(int, int, @NonNull String, int, long); - method public void increaseAccessDuration(int, int, @NonNull String, int, long); - method public void increaseRejectCount(int, int, @NonNull String, int, long); + method public void increaseAccessCount(int, int, @NonNull String, int, int, long); + method public void increaseAccessDuration(int, int, @NonNull String, int, int, long); + method public void increaseRejectCount(int, int, @NonNull String, int, int, long); method public void offsetBeginAndEndTime(long); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR; @@ -242,6 +249,7 @@ package android.app { public static final class AppOpsManager.HistoricalOpsRequest.Builder { ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build(); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String); method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setUid(int); @@ -271,6 +279,31 @@ package android.app { method public void onOpActiveChanged(int, int, String, boolean); } + public static final class AppOpsManager.OpEntry implements android.os.Parcelable { + method public int describeContents(); + method public long getDuration(); + method public long getLastAccessBackgroundTime(int); + method public long getLastAccessForegroundTime(int); + method public long getLastAccessTime(int); + method public long getLastAccessTime(int, int, int); + method public long getLastBackgroundDuration(int); + method public long getLastDuration(int, int, int); + method public long getLastForegroundDuration(int); + method public long getLastRejectBackgroundTime(int); + method public long getLastRejectForegroundTime(int); + method public long getLastRejectTime(int); + method public long getLastRejectTime(int, int, int); + method public int getMode(); + method @NonNull public String getOpStr(); + method @Nullable public String getProxyPackageName(); + method @Nullable public String getProxyPackageName(int, int); + method public int getProxyUid(); + method public int getProxyUid(int, int); + method public boolean isRunning(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR; + } + public class DownloadManager { field public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri"; } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 5bf7ef54f8b7..91df05f35295 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -43,6 +43,8 @@ import android.os.SystemProperties; import android.os.UserManager; import android.provider.Settings; import android.util.ArrayMap; +import android.util.LongSparseArray; +import android.util.LongSparseLongArray; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -55,8 +57,10 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; @@ -64,9 +68,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.function.Supplier; /** * API for interacting with "application operation" tracking. @@ -227,7 +233,7 @@ public class AppOpsManager { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = { "UID_STATE_" }, value = { + @IntDef(prefix = { "UID_STATE_" }, value = { UID_STATE_PERSISTENT, UID_STATE_TOP, UID_STATE_FOREGROUND_SERVICE_LOCATION, @@ -239,78 +245,322 @@ public class AppOpsManager { public @interface UidState {} /** - * Invalid UID state. + * Uid state: The UID is a foreground persistent app. * @hide */ - public static final int UID_STATE_INVALID = -1; + @TestApi + @SystemApi + public static final int UID_STATE_PERSISTENT = 100; /** - * Metrics about an op when its uid is persistent. + * Uid state: The UID is top foreground app. * @hide */ @TestApi @SystemApi - public static final int UID_STATE_PERSISTENT = 0; + public static final int UID_STATE_TOP = 200; /** - * Metrics about an op when its uid is at the top. + * Uid state: The UID is running a foreground service of location type. * @hide */ @TestApi @SystemApi - public static final int UID_STATE_TOP = 1; + public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300; /** - * Metrics about an op when its uid is running a foreground service with location type. + * Uid state: The UID is running a foreground service. * @hide */ @TestApi @SystemApi - public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 2; + public static final int UID_STATE_FOREGROUND_SERVICE = 400; /** - * Metrics about an op when its uid is running a foreground service. + * The max, which is min priority, UID state for which any app op + * would be considered as performed in the foreground. + * @hide + */ + public static final int UID_STATE_MAX_LAST_NON_RESTRICTED = UID_STATE_FOREGROUND_SERVICE; + + /** + * Uid state: The UID is a foreground app. * @hide */ @TestApi @SystemApi - public static final int UID_STATE_FOREGROUND_SERVICE = 3; + public static final int UID_STATE_FOREGROUND = 500; /** - * Last UID state in which we don't restrict what an op can do. + * Uid state: The UID is a background app. * @hide */ - public static final int UID_STATE_LAST_NON_RESTRICTED = UID_STATE_FOREGROUND_SERVICE_LOCATION; + @TestApi + @SystemApi + public static final int UID_STATE_BACKGROUND = 600; /** - * Metrics about an op when its uid is in the foreground for any other reasons. + * Uid state: The UID is a cached app. * @hide */ @TestApi @SystemApi - public static final int UID_STATE_FOREGROUND = 4; + public static final int UID_STATE_CACHED = 700; + + /** + * Uid state: The UID state with the highest priority. + * @hide + */ + public static final int MAX_PRIORITY_UID_STATE = UID_STATE_PERSISTENT; + + /** + * Uid state: The UID state with the lowest priority. + * @hide + */ + public static final int MIN_PRIORITY_UID_STATE = UID_STATE_CACHED; + + /** + * Resolves the first unrestricted state given an app op. Location is + * special as we want to allow its access only if a dedicated location + * foreground service is running. For other ops we consider any foreground + * service as a foreground state. + * + * @param op The op to resolve. + * @return The last restricted UID state. + * + * @hide + */ + public static int resolveFirstUnrestrictedUidState(int op) { + switch (op) { + case OP_FINE_LOCATION: + case OP_COARSE_LOCATION: { + return UID_STATE_FOREGROUND_SERVICE_LOCATION; + } + } + return UID_STATE_FOREGROUND_SERVICE; + } /** - * Metrics about an op when its uid is in the background for any reason. + * Resolves the last restricted state given an app op. Location is + * special as we want to allow its access only if a dedicated location + * foreground service is running. For other ops we consider any foreground + * service as a foreground state. + * + * @param op The op to resolve. + * @return The last restricted UID state. + * + * @hide + */ + public static int resolveLastRestrictedUidState(int op) { + switch (op) { + case OP_FINE_LOCATION: + case OP_COARSE_LOCATION: { + return UID_STATE_FOREGROUND_SERVICE; + } + } + return UID_STATE_FOREGROUND; + } + + /** @hide Note: Keep these sorted */ + public static final int[] UID_STATES = { + UID_STATE_PERSISTENT, + UID_STATE_TOP, + UID_STATE_FOREGROUND_SERVICE_LOCATION, + UID_STATE_FOREGROUND_SERVICE, + UID_STATE_FOREGROUND, + UID_STATE_BACKGROUND, + UID_STATE_CACHED + }; + + /** @hide */ + public static String getUidStateName(@UidState int uidState) { + switch (uidState) { + case UID_STATE_PERSISTENT: + return "pers"; + case UID_STATE_TOP: + return "top"; + case UID_STATE_FOREGROUND_SERVICE_LOCATION: + return "fgsvcl"; + case UID_STATE_FOREGROUND_SERVICE: + return "fgsvc"; + case UID_STATE_FOREGROUND: + return "fg"; + case UID_STATE_BACKGROUND: + return "bg"; + case UID_STATE_CACHED: + return "cch"; + default: + return "unknown"; + } + } + + /** + * Flag: non proxy operations. These are operations + * performed on behalf of the app itself and not on behalf of + * another one. + * * @hide */ @TestApi @SystemApi - public static final int UID_STATE_BACKGROUND = 5; + public static final int OP_FLAG_SELF = 0x1; /** - * Metrics about an op when its uid is cached. + * Flag: trusted proxy operations. These are operations + * performed on behalf of another app by a trusted app. + * Which is work a trusted app blames on another app. + * + * @hide + */ + @TestApi + @SystemApi + public static final int OP_FLAG_TRUSTED_PROXY = 0x2; + + /** + * Flag: untrusted proxy operations. These are operations + * performed on behalf of another app by an untrusted app. + * Which is work an untrusted app blames on another app. + * + * @hide + */ + @TestApi + @SystemApi + public static final int OP_FLAG_UNTRUSTED_PROXY = 0x4; + + /** + * Flag: trusted proxied operations. These are operations + * performed by a trusted other app on behalf of an app. + * Which is work an app was blamed for by a trusted app. + * + * @hide + */ + @TestApi + @SystemApi + public static final int OP_FLAG_TRUSTED_PROXIED = 0x8; + + /** + * Flag: untrusted proxied operations. These are operations + * performed by an untrusted other app on behalf of an app. + * Which is work an app was blamed for by an untrusted app. + * + * @hide + */ + @TestApi + @SystemApi + public static final int OP_FLAG_UNTRUSTED_PROXIED = 0x10; + + /** + * Flags: all operations. These include operations matched + * by {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. + * * @hide */ @TestApi @SystemApi - public static final int UID_STATE_CACHED = 6; + public static final int OP_FLAGS_ALL = + OP_FLAG_SELF + | OP_FLAG_TRUSTED_PROXY + | OP_FLAG_UNTRUSTED_PROXY + | OP_FLAG_TRUSTED_PROXIED + | OP_FLAG_UNTRUSTED_PROXIED; /** - * Number of uid states we track. + * Flags: all trusted operations which is ones either the app did {@link #OP_FLAG_SELF}, + * or it was blamed for by a trusted app {@link #OP_FLAG_TRUSTED_PROXIED}, or ones the + * app if untrusted blamed on other apps {@link #OP_FLAG_UNTRUSTED_PROXY}. + * * @hide */ - public static final int _NUM_UID_STATE = 7; + @SystemApi + public static final int OP_FLAGS_ALL_TRUSTED = AppOpsManager.OP_FLAG_SELF + | AppOpsManager.OP_FLAG_UNTRUSTED_PROXY + | AppOpsManager.OP_FLAG_TRUSTED_PROXIED; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "FLAG_" }, value = { + OP_FLAG_SELF, + OP_FLAG_TRUSTED_PROXY, + OP_FLAG_UNTRUSTED_PROXY, + OP_FLAG_TRUSTED_PROXIED, + OP_FLAG_UNTRUSTED_PROXIED + }) + public @interface OpFlags {} + + + /** @hide */ + public static final String getFlagName(@OpFlags int flag) { + switch (flag) { + case OP_FLAG_SELF: + return "s"; + case OP_FLAG_TRUSTED_PROXY: + return "tp"; + case OP_FLAG_UNTRUSTED_PROXY: + return "up"; + case OP_FLAG_TRUSTED_PROXIED: + return "tpd"; + case OP_FLAG_UNTRUSTED_PROXIED: + return "upd"; + default: + return "unknown"; + } + } + + private static final int UID_STATE_OFFSET = 31; + private static final int FLAGS_MASK = 0xFFFFFFFF; + + /** + * Key for a data bucket storing app op state. The bucket + * is composed of the uid state and state flags. This way + * we can query data for given uid state and a set of flags where + * the flags control which type of data to get. For example, + * one can get the ops an app did on behalf of other apps + * while in the background. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) + public @interface DataBucketKey { + } + + /** @hide */ + public static String keyToString(@DataBucketKey long key) { + final int uidState = extractUidStateFromKey(key); + final int flags = extractFlagsFromKey(key); + return "[" + getUidStateName(uidState) + "-" + flagsToString(flags) + "]"; + } + + /** @hide */ + public static @DataBucketKey long makeKey(@UidState int uidState, @OpFlags int flags) { + return ((long) uidState << UID_STATE_OFFSET) | flags; + } + + /** @hide */ + public static int extractUidStateFromKey(@DataBucketKey long key) { + return (int) (key >> UID_STATE_OFFSET); + } + + /** @hide */ + public static int extractFlagsFromKey(@DataBucketKey long key) { + return (int) (key & FLAGS_MASK); + } + + /** @hide */ + public static String flagsToString(@OpFlags int flags) { + final StringBuilder flagsBuilder = new StringBuilder(); + while (flags != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(flags); + flags &= ~flag; + if (flagsBuilder.length() > 0) { + flagsBuilder.append('|'); + } + flagsBuilder.append(getFlagName(flag)); + } + return flagsBuilder.toString(); + } // when adding one of these: // - increment _NUM_OP @@ -551,7 +801,7 @@ public class AppOpsManager { @UnsupportedAppUsage public static final int OP_MANAGE_IPSEC_TUNNELS = 75; /** @hide Any app start foreground service. */ - @UnsupportedAppUsage + @TestApi public static final int OP_START_FOREGROUND = 76; /** @hide */ @UnsupportedAppUsage @@ -1947,14 +2197,23 @@ public class AppOpsManager { mEntries = entries; } - public String getPackageName() { + /** + * @return The name of the package. + */ + public @NonNull String getPackageName() { return mPackageName; } + /** + * @return The uid of the package. + */ public int getUid() { return mUid; } + /** + * @return The ops of the package. + */ public List<OpEntry> getOps() { return mEntries; } @@ -1999,57 +2258,59 @@ public class AppOpsManager { * Class holding the information about one unique operation of an application. * @hide */ + @TestApi + @Immutable @SystemApi public static final class OpEntry implements Parcelable { private final int mOp; - private final @Mode int mMode; - private final long[] mTimes; - private final long[] mRejectTimes; - private final int mDuration; - private final int mProxyUid; private final boolean mRunning; - private final String mProxyPackageName; + private final @Mode int mMode; + private final @Nullable LongSparseLongArray mAccessTimes; + private final @Nullable LongSparseLongArray mRejectTimes; + private final @Nullable LongSparseLongArray mDurations; + private final @Nullable LongSparseLongArray mProxyUids; + private final @Nullable LongSparseArray<String> mProxyPackageNames; /** * @hide */ - public OpEntry(int op, @Mode int mode, long time, long rejectTime, int duration, - int proxyUid, String proxyPackage) { + public OpEntry(int op, boolean running, @Mode int mode, + @Nullable LongSparseLongArray accessTimes, @Nullable LongSparseLongArray rejectTimes, + @Nullable LongSparseLongArray durations, @Nullable LongSparseLongArray proxyUids, + @Nullable LongSparseArray<String> proxyPackageNames) { mOp = op; + mRunning = running; mMode = mode; - mTimes = new long[_NUM_UID_STATE]; - mRejectTimes = new long[_NUM_UID_STATE]; - mTimes[0] = time; - mRejectTimes[0] = rejectTime; - mDuration = duration; - mRunning = duration == -1; - mProxyUid = proxyUid; - mProxyPackageName = proxyPackage; + mAccessTimes = accessTimes; + mRejectTimes = rejectTimes; + mDurations = durations; + mProxyUids = proxyUids; + mProxyPackageNames = proxyPackageNames; } /** * @hide */ - public OpEntry(int op, @Mode int mode, long[] times, long[] rejectTimes, int duration, - boolean running, int proxyUid, String proxyPackage) { + public OpEntry(int op, @Mode int mode) { mOp = op; mMode = mode; - mTimes = new long[_NUM_UID_STATE]; - mRejectTimes = new long[_NUM_UID_STATE]; - System.arraycopy(times, 0, mTimes, 0, _NUM_UID_STATE); - System.arraycopy(rejectTimes, 0, mRejectTimes, 0, _NUM_UID_STATE); - mDuration = duration; - mRunning = running; - mProxyUid = proxyUid; - mProxyPackageName = proxyPackage; + mRunning = false; + mAccessTimes = null; + mRejectTimes = null; + mDurations = null; + mProxyUids = null; + mProxyPackageNames = null; } /** - * @hide - */ - public OpEntry(int op, @Mode int mode, long[] times, long[] rejectTimes, int duration, - int proxyUid, String proxyPackage) { - this(op, mode, times, rejectTimes, duration, duration == -1, proxyUid, proxyPackage); + * Returns all keys for which we have mapped state in any of the data buckets - + * access time, reject time, duration. + * @hide */ + public @Nullable LongSparseArray<Object> collectKeys() { + LongSparseArray<Object> result = AppOpsManager.collectKeys(mAccessTimes, null); + result = AppOpsManager.collectKeys(mRejectTimes, result); + result = AppOpsManager.collectKeys(mDurations, result); + return result; } /** @@ -2061,14 +2322,14 @@ public class AppOpsManager { } /** - * Return this entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}. + * @return This entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}. */ - public String getOpStr() { + public @NonNull String getOpStr() { return sOpToString[mOp]; } /** - * Return this entry's current mode, such as {@link #MODE_ALLOWED}. + * @return this entry's current mode, such as {@link #MODE_ALLOWED}. */ public @Mode int getMode() { return mMode; @@ -2079,89 +2340,331 @@ public class AppOpsManager { */ @UnsupportedAppUsage public long getTime() { - return maxTime(mTimes, 0, _NUM_UID_STATE); + return getLastAccessTime(OP_FLAGS_ALL); } /** - * Return the last wall clock time this op was accessed by the app. + * Return the last wall clock time in milliseconds this op was accessed. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last access time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastAccessForegroundTime(int) + * @see #getLastAccessBackgroundTime(int) + * @see #getLastAccessTime(int, int, int) */ - public long getLastAccessTime() { - return maxTime(mTimes, 0, _NUM_UID_STATE); + public long getLastAccessTime(@OpFlags int flags) { + return maxForFlagsInStates(mAccessTimes, MAX_PRIORITY_UID_STATE, + MIN_PRIORITY_UID_STATE, flags); } /** - * Return the last wall clock time this op was accessed by the app while in the foreground. + * Return the last wall clock time in milliseconds this op was accessed + * by the app while in the foreground. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last foreground access time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastAccessBackgroundTime(int) + * @see #getLastAccessTime(int) + * @see #getLastAccessTime(int, int, int) */ - public long getLastAccessForegroundTime() { - return maxTime(mTimes, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1); + public long getLastAccessForegroundTime(@OpFlags int flags) { + return maxForFlagsInStates(mAccessTimes, MAX_PRIORITY_UID_STATE, + resolveFirstUnrestrictedUidState(mOp), flags); } /** - * Return the last wall clock time this op was accessed by the app while in the background. + * Return the last wall clock time in milliseconds this op was accessed + * by the app while in the background. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last foreground access time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastAccessForegroundTime(int) + * @see #getLastAccessTime(int) + * @see #getLastAccessTime(int, int, int) */ - public long getLastAccessBackgroundTime() { - return maxTime(mTimes, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE); + public long getLastAccessBackgroundTime(@OpFlags int flags) { + return maxForFlagsInStates(mAccessTimes, resolveLastRestrictedUidState(mOp), + MIN_PRIORITY_UID_STATE, flags); } /** - * @hide + * Return the last wall clock time in milliseconds this op was accessed + * by the app for a given range of UID states. + * + * @param fromUidState The UID state for which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param toUidState The UID state for which to query. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * + * @return the last foreground access time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastAccessForegroundTime(int) + * @see #getLastAccessBackgroundTime(int) + * @see #getLastAccessTime(int) */ - public long getLastTimeFor(int uidState) { - return mTimes[uidState]; + public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState, + @OpFlags int flags) { + return maxForFlagsInStates(mAccessTimes, fromUidState, toUidState, flags); } /** * @hide */ public long getRejectTime() { - return maxTime(mRejectTimes, 0, _NUM_UID_STATE); + return getLastRejectTime(OP_FLAGS_ALL); } /** - * Return the last wall clock time the app made an attempt to access this op but - * was rejected. + * Return the last wall clock time in milliseconds the app made an attempt + * to access this op but was rejected. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last reject time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastRejectBackgroundTime(int) + * @see #getLastRejectForegroundTime(int) + * @see #getLastRejectTime(int, int, int) */ - public long getLastRejectTime() { - return maxTime(mRejectTimes, 0, _NUM_UID_STATE); + public long getLastRejectTime(@OpFlags int flags) { + return maxForFlagsInStates(mRejectTimes, MAX_PRIORITY_UID_STATE, + MIN_PRIORITY_UID_STATE, flags); } /** - * Return the last wall clock time the app made an attempt to access this op while in - * the foreground but was rejected. + * Return the last wall clock time in milliseconds the app made an attempt + * to access this op while in the foreground but was rejected. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last foreground reject time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastRejectBackgroundTime(int) + * @see #getLastRejectTime(int, int, int) + * @see #getLastRejectTime(int) */ - public long getLastRejectForegroundTime() { - return maxTime(mRejectTimes, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1); + public long getLastRejectForegroundTime(@OpFlags int flags) { + return maxForFlagsInStates(mRejectTimes, MAX_PRIORITY_UID_STATE, + resolveFirstUnrestrictedUidState(mOp), flags); } /** - * Return the last wall clock time the app made an attempt to access this op while in - * the background but was rejected. + * Return the last wall clock time in milliseconds the app made an attempt + * to access this op while in the background but was rejected. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last background reject time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastRejectForegroundTime(int) + * @see #getLastRejectTime(int, int, int) + * @see #getLastRejectTime(int) */ - public long getLastRejectBackgroundTime() { - return maxTime(mRejectTimes, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE); + public long getLastRejectBackgroundTime(@OpFlags int flags) { + return maxForFlagsInStates(mRejectTimes, resolveLastRestrictedUidState(mOp), + MIN_PRIORITY_UID_STATE, flags); } /** - * @hide + * Return the last wall clock time state in milliseconds the app made an + * attempt to access this op for a given range of UID states. + * + * @param fromUidState The UID state from which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param toUidState The UID state to which to query. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the last foreground access time in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). + * + * @see #getLastRejectForegroundTime(int) + * @see #getLastRejectBackgroundTime(int) + * @see #getLastRejectTime(int) */ - public long getLastRejectTimeFor(int uidState) { - return mRejectTimes[uidState]; + public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState, + @OpFlags int flags) { + return maxForFlagsInStates(mRejectTimes, fromUidState, toUidState, flags); } + /** + * @return Whether the operation is running. + */ public boolean isRunning() { return mRunning; } - public int getDuration() { - return mDuration; + /** + * @return The duration of the operation in milliseconds. + */ + public long getDuration() { + return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL); + } + + /** + * Return the duration in milliseconds the app accessed this op while + * in the foreground. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the foreground access duration in milliseconds. + * + * @see #getLastBackgroundDuration(int) + * @see #getLastDuration(int, int, int) + */ + public long getLastForegroundDuration(@OpFlags int flags) { + return sumForFlagsInStates(mDurations, MAX_PRIORITY_UID_STATE, + resolveFirstUnrestrictedUidState(mOp), flags); + } + + /** + * Return the duration in milliseconds the app accessed this op while + * in the background. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the background access duration in milliseconds. + * + * @see #getLastForegroundDuration(int) + * @see #getLastDuration(int, int, int) + */ + public long getLastBackgroundDuration(@OpFlags int flags) { + return sumForFlagsInStates(mDurations, resolveLastRestrictedUidState(mOp), + MIN_PRIORITY_UID_STATE, flags); + } + + /** + * Return the duration in milliseconds the app accessed this op for + * a given range of UID states. + * + * @param fromUidState The UID state for which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param toUidState The UID state for which to query. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return the access duration in milliseconds. + */ + public long getLastDuration(@UidState int fromUidState, @UidState int toUidState, + @OpFlags int flags) { + return sumForFlagsInStates(mDurations, fromUidState, toUidState, flags); } + /** + * Gets the UID of the app that performed the op on behalf of this app and + * as a result blamed the op on this app or {@link Process#INVALID_UID} if + * there is no proxy. + * + * @return The proxy UID. + */ public int getProxyUid() { - return mProxyUid; + return (int) findFirstNonNegativeForFlagsInStates(mDurations, + MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL); + } + + /** + * Gets the UID of the app that performed the op on behalf of this app and + * as a result blamed the op on this app or {@link Process#INVALID_UID} if + * there is no proxy. + * + * @param uidState The UID state for which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * + * @return The proxy UID. + */ + public int getProxyUid(@UidState int uidState, @OpFlags int flags) { + return (int) findFirstNonNegativeForFlagsInStates(mDurations, + uidState, uidState, flags); + } + + /** + * Gets the package name of the app that performed the op on behalf of this + * app and as a result blamed the op on this app or {@code null} + * if there is no proxy. + * + * @return The proxy package name. + */ + public @Nullable String getProxyPackageName() { + return findFirstNonNullForFlagsInStates(mProxyPackageNames, MAX_PRIORITY_UID_STATE, + MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL); } - public String getProxyPackageName() { - return mProxyPackageName; + /** + * Gets the package name of the app that performed the op on behalf of this + * app and as a result blamed the op on this app for a UID state or + * {@code null} if there is no proxy. + * + * @param uidState The UID state for which to query. Could be one of + * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, + * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, + * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return The proxy package name. + */ + public @Nullable String getProxyPackageName(@UidState int uidState, @OpFlags int flags) { + return findFirstNonNullForFlagsInStates(mProxyPackageNames, uidState, uidState, flags); } @Override @@ -2173,23 +2676,23 @@ public class AppOpsManager { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mOp); dest.writeInt(mMode); - dest.writeLongArray(mTimes); - dest.writeLongArray(mRejectTimes); - dest.writeInt(mDuration); dest.writeBoolean(mRunning); - dest.writeInt(mProxyUid); - dest.writeString(mProxyPackageName); + writeLongSparseLongArrayToParcel(mAccessTimes, dest); + writeLongSparseLongArrayToParcel(mRejectTimes, dest); + writeLongSparseLongArrayToParcel(mDurations, dest); + writeLongSparseLongArrayToParcel(mProxyUids, dest); + writeLongSparseStringArrayToParcel(mProxyPackageNames, dest); } OpEntry(Parcel source) { mOp = source.readInt(); mMode = source.readInt(); - mTimes = source.createLongArray(); - mRejectTimes = source.createLongArray(); - mDuration = source.readInt(); mRunning = source.readBoolean(); - mProxyUid = source.readInt(); - mProxyPackageName = source.readString(); + mAccessTimes = readLongSparseLongArrayFromParcel(source); + mRejectTimes = readLongSparseLongArrayFromParcel(source); + mDurations = readLongSparseLongArrayFromParcel(source); + mProxyUids = readLongSparseLongArrayFromParcel(source); + mProxyPackageNames = readLongSparseStringArrayFromParcel(source); } public static final @android.annotation.NonNull Creator<OpEntry> CREATOR = new Creator<OpEntry>() { @@ -2226,14 +2729,17 @@ public class AppOpsManager { private final @Nullable List<String> mOpNames; private final long mBeginTimeMillis; private final long mEndTimeMillis; + private final @OpFlags int mFlags; private HistoricalOpsRequest(int uid, @Nullable String packageName, - @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis) { + @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis, + @OpFlags int flags) { mUid = uid; mPackageName = packageName; mOpNames = opNames; mBeginTimeMillis = beginTimeMillis; mEndTimeMillis = endTimeMillis; + mFlags = flags; } /** @@ -2249,6 +2755,7 @@ public class AppOpsManager { private @Nullable List<String> mOpNames; private final long mBeginTimeMillis; private final long mEndTimeMillis; + private @OpFlags int mFlags = OP_FLAGS_ALL; /** * Creates a new builder. @@ -2311,11 +2818,28 @@ public class AppOpsManager { } /** + * Sets the op flags to query for. The flags specify the type of + * op data being queried. + * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. + * @return This builder. + */ + public @NonNull Builder setFlags(@OpFlags int flags) { + Preconditions.checkFlagsArgument(flags, OP_FLAGS_ALL); + mFlags = flags; + return this; + } + + /** * @return a new {@link HistoricalOpsRequest}. */ public @NonNull HistoricalOpsRequest build() { return new HistoricalOpsRequest(mUid, mPackageName, mOpNames, - mBeginTimeMillis, mEndTimeMillis); + mBeginTimeMillis, mEndTimeMillis, mFlags); } } } @@ -2521,25 +3045,25 @@ public class AppOpsManager { /** @hide */ @TestApi public void increaseAccessCount(int opCode, int uid, @NonNull String packageName, - @UidState int uidState, long increment) { + @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalUidOps(uid).increaseAccessCount(opCode, - packageName, uidState, increment); + packageName, uidState, flags, increment); } /** @hide */ @TestApi public void increaseRejectCount(int opCode, int uid, @NonNull String packageName, - @UidState int uidState, long increment) { + @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalUidOps(uid).increaseRejectCount(opCode, - packageName, uidState, increment); + packageName, uidState, flags, increment); } /** @hide */ @TestApi public void increaseAccessDuration(int opCode, int uid, @NonNull String packageName, - @UidState int uidState, long increment) { + @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalUidOps(uid).increaseAccessDuration(opCode, - packageName, uidState, increment); + packageName, uidState, flags, increment); } /** @hide */ @@ -2834,21 +3358,21 @@ public class AppOpsManager { } private void increaseAccessCount(int opCode, @NonNull String packageName, - @UidState int uidState, long increment) { + @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalPackageOps(packageName).increaseAccessCount( - opCode, uidState, increment); + opCode, uidState, flags, increment); } private void increaseRejectCount(int opCode, @NonNull String packageName, - @UidState int uidState, long increment) { + @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalPackageOps(packageName).increaseRejectCount( - opCode, uidState, increment); + opCode, uidState, flags, increment); } private void increaseAccessDuration(int opCode, @NonNull String packageName, - @UidState int uidState, long increment) { + @UidState int uidState, @OpFlags int flags, long increment) { getOrCreateHistoricalPackageOps(packageName).increaseAccessDuration( - opCode, uidState, increment); + opCode, uidState, flags, increment); } /** @@ -3070,16 +3594,19 @@ public class AppOpsManager { return true; } - private void increaseAccessCount(int opCode, @UidState int uidState, long increment) { - getOrCreateHistoricalOp(opCode).increaseAccessCount(uidState, increment); + private void increaseAccessCount(int opCode, @UidState int uidState, + @OpFlags int flags, long increment) { + getOrCreateHistoricalOp(opCode).increaseAccessCount(uidState, flags, increment); } - private void increaseRejectCount(int opCode, @UidState int uidState, long increment) { - getOrCreateHistoricalOp(opCode).increaseRejectCount(uidState, increment); + private void increaseRejectCount(int opCode, @UidState int uidState, + @OpFlags int flags, long increment) { + getOrCreateHistoricalOp(opCode).increaseRejectCount(uidState, flags, increment); } - private void increaseAccessDuration(int opCode, @UidState int uidState, long increment) { - getOrCreateHistoricalOp(opCode).increaseAccessDuration(uidState, increment); + private void increaseAccessDuration(int opCode, @UidState int uidState, + @OpFlags int flags, long increment) { + getOrCreateHistoricalOp(opCode).increaseAccessDuration(uidState, flags, increment); } /** @@ -3095,7 +3622,6 @@ public class AppOpsManager { * Gets number historical app ops. * * @return The number historical app ops. - * * @see #getOpAt(int) */ public int getOpCount() { @@ -3109,9 +3635,7 @@ public class AppOpsManager { * Gets the historical op at a given index. * * @param index The index to lookup. - * * @return The op at the given index. - * * @see #getOpCount() */ public @NonNull HistoricalOp getOpAt(int index) { @@ -3125,7 +3649,6 @@ public class AppOpsManager { * Gets the historical entry for a given op name. * * @param opName The op name. - * * @return The historical entry for that op name. */ public @Nullable HistoricalOp getOp(@NonNull String opName) { @@ -3219,39 +3742,33 @@ public class AppOpsManager { @SystemApi public static final class HistoricalOp implements Parcelable { private final int mOp; - private @Nullable long[] mAccessCount; - private @Nullable long[] mRejectCount; - private @Nullable long[] mAccessDuration; + private @Nullable LongSparseLongArray mAccessCount; + private @Nullable LongSparseLongArray mRejectCount; + private @Nullable LongSparseLongArray mAccessDuration; /** @hide */ public HistoricalOp(int op) { mOp = op; - mAccessCount = new long[_NUM_UID_STATE]; - mRejectCount = new long[_NUM_UID_STATE]; - mAccessDuration = new long[_NUM_UID_STATE]; } private HistoricalOp(@NonNull HistoricalOp other) { mOp = other.mOp; if (other.mAccessCount != null) { - System.arraycopy(other.mAccessCount, 0, getOrCreateAccessCount(), - 0, other.mAccessCount.length); + mAccessCount = other.mAccessCount.clone(); } if (other.mRejectCount != null) { - System.arraycopy(other.mRejectCount, 0, getOrCreateRejectCount(), - 0, other.mRejectCount.length); + mRejectCount = other.mRejectCount.clone(); } if (other.mAccessDuration != null) { - System.arraycopy(other.mAccessDuration, 0, getOrCreateAccessDuration(), - 0, other.mAccessDuration.length); + mAccessDuration = other.mAccessDuration.clone(); } } private HistoricalOp(@NonNull Parcel parcel) { mOp = parcel.readInt(); - mAccessCount = parcel.createLongArray(); - mRejectCount = parcel.createLongArray(); - mAccessDuration = parcel.createLongArray(); + mAccessCount = readLongSparseLongArrayFromParcel(parcel); + mRejectCount = readLongSparseLongArrayFromParcel(parcel); + mAccessDuration = readLongSparseLongArrayFromParcel(parcel); } private void filter(double scaleFactor) { @@ -3266,90 +3783,64 @@ public class AppOpsManager { && !hasData(mAccessDuration); } - private boolean hasData(@NonNull long[] array) { - for (long value : array) { - if (value != 0) { - return true; - } - } - return false; + private boolean hasData(@NonNull LongSparseLongArray array) { + return (array != null && array.size() > 0); } private @Nullable HistoricalOp splice(double fractionToRemove) { - HistoricalOp splice = null; - if (mAccessCount != null) { - for (int i = 0; i < _NUM_UID_STATE; i++) { - final long spliceAccessCount = Math.round( - mAccessCount[i] * fractionToRemove); - if (spliceAccessCount > 0) { - if (splice == null) { - splice = new HistoricalOp(mOp); - } - splice.getOrCreateAccessCount()[i] = spliceAccessCount; - mAccessCount[i] -= spliceAccessCount; - } - } - } - - if (mRejectCount != null) { - for (int i = 0; i < _NUM_UID_STATE; i++) { - final long spliceRejectCount = Math.round( - mRejectCount[i] * fractionToRemove); - - if (spliceRejectCount > 0) { - if (splice == null) { - splice = new HistoricalOp(mOp); - } - splice.getOrCreateRejectCount()[i] = spliceRejectCount; - mRejectCount[i] -= spliceRejectCount; - } - } - } + final HistoricalOp splice = new HistoricalOp(mOp); + splice(mAccessCount, splice::getOrCreateAccessCount, fractionToRemove); + splice(mRejectCount, splice::getOrCreateRejectCount, fractionToRemove); + splice(mAccessDuration, splice::getOrCreateAccessDuration, fractionToRemove); + return splice; + } - if (mAccessDuration != null) { - for (int i = 0; i < _NUM_UID_STATE; i++) { - final long spliceAccessDuration = Math.round( - mAccessDuration[i] * fractionToRemove); - if (spliceAccessDuration > 0) { - if (splice == null) { - splice = new HistoricalOp(mOp); - } - splice.getOrCreateAccessDuration()[i] = spliceAccessDuration; - mAccessDuration[i] -= spliceAccessDuration; + private static void splice(@Nullable LongSparseLongArray sourceContainer, + @NonNull Supplier<LongSparseLongArray> destContainerProvider, + double fractionToRemove) { + if (sourceContainer != null) { + final int size = sourceContainer.size(); + for (int i = 0; i < size; i++) { + final long key = sourceContainer.keyAt(i); + final long value = sourceContainer.valueAt(i); + final long removedFraction = Math.round(value * fractionToRemove); + if (removedFraction > 0) { + destContainerProvider.get().put(key, removedFraction); + sourceContainer.put(key, value - removedFraction); } } } - return splice; } private void merge(@NonNull HistoricalOp other) { - if (other.mAccessCount != null) { - for (int i = 0; i < _NUM_UID_STATE; i++) { - getOrCreateAccessCount()[i] += other.mAccessCount[i]; - } - } - if (other.mRejectCount != null) { - for (int i = 0; i < _NUM_UID_STATE; i++) { - getOrCreateRejectCount()[i] += other.mRejectCount[i]; - } - } - if (other.mAccessDuration != null) { - for (int i = 0; i < _NUM_UID_STATE; i++) { - getOrCreateAccessDuration()[i] += other.mAccessDuration[i]; - } - } + merge(this::getOrCreateAccessCount, other.mAccessCount); + merge(this::getOrCreateRejectCount, other.mRejectCount); + merge(this::getOrCreateAccessDuration, other.mAccessDuration); + } + + private void increaseAccessCount(@UidState int uidState, @OpFlags int flags, + long increment) { + increaseCount(getOrCreateAccessCount(), uidState, flags, increment); } - private void increaseAccessCount(@UidState int uidState, long increment) { - getOrCreateAccessCount()[uidState] += increment; + private void increaseRejectCount(@UidState int uidState, @OpFlags int flags, + long increment) { + increaseCount(getOrCreateRejectCount(), uidState, flags, increment); } - private void increaseRejectCount(@UidState int uidState, long increment) { - getOrCreateRejectCount()[uidState] += increment; + private void increaseAccessDuration(@UidState int uidState, @OpFlags int flags, + long increment) { + increaseCount(getOrCreateAccessDuration(), uidState, flags, increment); } - private void increaseAccessDuration(@UidState int uidState, long increment) { - getOrCreateAccessDuration()[uidState] += increment; + private void increaseCount(@NonNull LongSparseLongArray counts, + @UidState int uidState, @OpFlags int flags, long increment) { + while (flags != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(flags); + flags &= ~flag; + final long key = makeKey(uidState, flag); + counts.put(key, counts.get(key) + increment); + } } /** @@ -3369,154 +3860,186 @@ public class AppOpsManager { /** * Gets the number times the op was accessed (performed) in the foreground. * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. * @return The times the op was accessed in the foreground. * - * @see #getBackgroundAccessCount() - * @see #getAccessCount(int) + * @see #getBackgroundAccessCount(int) + * @see #getAccessCount(int, int, int) */ - public long getForegroundAccessCount() { - if (mAccessCount == null) { - return 0; - } - return sum(mAccessCount, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1); + public long getForegroundAccessCount(@OpFlags int flags) { + return sumForFlagsInStates(mAccessCount, MAX_PRIORITY_UID_STATE, + resolveFirstUnrestrictedUidState(mOp), flags); } /** * Gets the number times the op was accessed (performed) in the background. * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. * @return The times the op was accessed in the background. * - * @see #getForegroundAccessCount() - * @see #getAccessCount(int) + * @see #getForegroundAccessCount(int) + * @see #getAccessCount(int, int, int) */ - public long getBackgroundAccessCount() { - if (mAccessCount == null) { - return 0; - } - return sum(mAccessCount, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE); + public long getBackgroundAccessCount(@OpFlags int flags) { + return sumForFlagsInStates(mAccessCount, resolveLastRestrictedUidState(mOp), + MIN_PRIORITY_UID_STATE, flags); } /** - * Gets the number times the op was accessed (performed) for a given uid state. + * Gets the number times the op was accessed (performed) for a + * range of uid states. * - * @param uidState The UID state for which to query. Could be one of + * @param fromUidState The UID state from which to query. Could be one of * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, * {@link #UID_STATE_FOREGROUND_SERVICE_LOCATION}, * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param toUidState The UID state to which to query. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. * * @return The times the op was accessed for the given UID state. * - * @see #getForegroundAccessCount() - * @see #getBackgroundAccessCount() + * @see #getForegroundAccessCount(int) + * @see #getBackgroundAccessCount(int) */ - public long getAccessCount(@UidState int uidState) { - if (mAccessCount == null) { - return 0; - } - return mAccessCount[uidState]; + public long getAccessCount(@UidState int fromUidState, @UidState int toUidState, + @OpFlags int flags) { + return sumForFlagsInStates(mAccessCount, fromUidState, toUidState, flags); } /** * Gets the number times the op was rejected in the foreground. * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. * @return The times the op was rejected in the foreground. * - * @see #getBackgroundRejectCount() - * @see #getRejectCount(int) + * @see #getBackgroundRejectCount(int) + * @see #getRejectCount(int, int, int) */ - public long getForegroundRejectCount() { - if (mRejectCount == null) { - return 0; - } - return sum(mRejectCount, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1); + public long getForegroundRejectCount(@OpFlags int flags) { + return sumForFlagsInStates(mRejectCount, MAX_PRIORITY_UID_STATE, + resolveFirstUnrestrictedUidState(mOp), flags); } /** * Gets the number times the op was rejected in the background. * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. * @return The times the op was rejected in the background. * - * @see #getForegroundRejectCount() - * @see #getRejectCount(int) + * @see #getForegroundRejectCount(int) + * @see #getRejectCount(int, int, int) */ - public long getBackgroundRejectCount() { - if (mRejectCount == null) { - return 0; - } - return sum(mRejectCount, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE); + public long getBackgroundRejectCount(@OpFlags int flags) { + return sumForFlagsInStates(mRejectCount, resolveLastRestrictedUidState(mOp), + MIN_PRIORITY_UID_STATE, flags); } /** - * Gets the number times the op was rejected for a given uid state. + * Gets the number times the op was rejected for a given range of UID states. * - * @param uidState The UID state for which to query. Could be one of + * @param fromUidState The UID state from which to query. Could be one of * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, * {@link #UID_STATE_FOREGROUND_SERVICE_LOCATION}, * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param toUidState The UID state to which to query. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. * * @return The times the op was rejected for the given UID state. * - * @see #getForegroundRejectCount() - * @see #getBackgroundRejectCount() + * @see #getForegroundRejectCount(int) + * @see #getBackgroundRejectCount(int) */ - public long getRejectCount(@UidState int uidState) { - if (mRejectCount == null) { - return 0; - } - return mRejectCount[uidState]; + public long getRejectCount(@UidState int fromUidState, @UidState int toUidState, + @OpFlags int flags) { + return sumForFlagsInStates(mRejectCount, fromUidState, toUidState, flags); } /** * Gets the total duration the app op was accessed (performed) in the foreground. * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. * @return The total duration the app op was accessed in the foreground. * - * @see #getBackgroundAccessDuration() - * @see #getAccessDuration(int) + * @see #getBackgroundAccessDuration(int) + * @see #getAccessDuration(int, int, int) */ - public long getForegroundAccessDuration() { - if (mAccessDuration == null) { - return 0; - } - return sum(mAccessDuration, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1); + public long getForegroundAccessDuration(@OpFlags int flags) { + return sumForFlagsInStates(mAccessDuration, MAX_PRIORITY_UID_STATE, + resolveFirstUnrestrictedUidState(mOp), flags); } /** * Gets the total duration the app op was accessed (performed) in the background. * + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. * @return The total duration the app op was accessed in the background. * - * @see #getForegroundAccessDuration() - * @see #getAccessDuration(int) + * @see #getForegroundAccessDuration(int) + * @see #getAccessDuration(int, int, int) */ - public long getBackgroundAccessDuration() { - if (mAccessDuration == null) { - return 0; - } - return sum(mAccessDuration, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE); + public long getBackgroundAccessDuration(@OpFlags int flags) { + return sumForFlagsInStates(mAccessDuration, resolveLastRestrictedUidState(mOp), + MIN_PRIORITY_UID_STATE, flags); } /** - * Gets the total duration the app op was accessed (performed) for a given UID state. + * Gets the total duration the app op was accessed (performed) for a given + * range of UID states. * - * @param uidState The UID state for which to query. Could be one of + * @param fromUidState The UID state from which to query. Could be one of * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, * {@link #UID_STATE_FOREGROUND_SERVICE_LOCATION}, * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND}, * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}. + * @param toUidState The UID state from which to query. + * @param flags The flags which are any combination of + * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, + * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED}, + * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL} + * for any flag. * * @return The total duration the app op was accessed for the given UID state. * - * @see #getForegroundAccessDuration() - * @see #getBackgroundAccessDuration() + * @see #getForegroundAccessDuration(int) + * @see #getBackgroundAccessDuration(int) */ - public long getAccessDuration(@UidState int uidState) { - if (mAccessDuration == null) { - return 0; - } - return mAccessDuration[uidState]; + public long getAccessDuration(@UidState int fromUidState, @UidState int toUidState, + @OpFlags int flags) { + return sumForFlagsInStates(mAccessDuration, fromUidState, toUidState, flags); } @Override @@ -3527,69 +4050,113 @@ public class AppOpsManager { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mOp); - parcel.writeLongArray(mAccessCount); - parcel.writeLongArray(mRejectCount); - parcel.writeLongArray(mAccessDuration); + writeLongSparseLongArrayToParcel(mAccessCount, parcel); + writeLongSparseLongArrayToParcel(mRejectCount, parcel); + writeLongSparseLongArrayToParcel(mAccessDuration, parcel); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final HistoricalOp other = (HistoricalOp) obj; + if (mOp != other.mOp) { + return false; + } + if (!Objects.equals(mAccessCount, other.mAccessCount)) { + return false; + } + if (!Objects.equals(mRejectCount, other.mRejectCount)) { + return false; + } + return Objects.equals(mAccessDuration, other.mAccessDuration); + } + + @Override + public int hashCode() { + int result = mOp; + result = 31 * result + Objects.hashCode(mAccessCount); + result = 31 * result + Objects.hashCode(mRejectCount); + result = 31 * result + Objects.hashCode(mAccessDuration); + return result; } private void accept(@NonNull HistoricalOpsVisitor visitor) { visitor.visitHistoricalOp(this); } - private @NonNull long[] getOrCreateAccessCount() { + private @NonNull LongSparseLongArray getOrCreateAccessCount() { if (mAccessCount == null) { - mAccessCount = new long[_NUM_UID_STATE]; + mAccessCount = new LongSparseLongArray(); } return mAccessCount; } - private @NonNull long[] getOrCreateRejectCount() { + private @NonNull LongSparseLongArray getOrCreateRejectCount() { if (mRejectCount == null) { - mRejectCount = new long[_NUM_UID_STATE]; + mRejectCount = new LongSparseLongArray(); } return mRejectCount; } - private @NonNull long[] getOrCreateAccessDuration() { + private @NonNull LongSparseLongArray getOrCreateAccessDuration() { if (mAccessDuration == null) { - mAccessDuration = new long[_NUM_UID_STATE]; + mAccessDuration = new LongSparseLongArray(); } return mAccessDuration; } /** + * Multiplies the entries in the array with the passed in scale factor and + * rounds the result at up 0.5 boundary. * - * Computes the sum given the start and end index. - * - * @param counts The data array. - * @param start The start index (inclusive) - * @param end The end index (exclusive) - * @return The sum. + * @param data The data to scale. + * @param scaleFactor The scale factor. */ - private static long sum(@NonNull long[] counts, int start, int end) { - long totalCount = 0; - for (int i = start; i < end; i++) { - totalCount += counts[i]; + private static void scale(@NonNull LongSparseLongArray data, double scaleFactor) { + if (data != null) { + final int size = data.size(); + for (int i = 0; i < size; i++) { + data.put(data.keyAt(i), (long) HistoricalOps.round( + (double) data.valueAt(i) * scaleFactor)); + } } - return totalCount; } /** - * Multiplies the entries in the array with the passed in scale factor and - * rounds the result at up 0.5 boundary. + * Merges two arrays while lazily acquiring the destination. * - * @param data The data to scale. - * @param scaleFactor The scale factor. + * @param thisSupplier The destination supplier. + * @param other The array to merge in. */ - private static void scale(@NonNull long[] data, double scaleFactor) { - if (data != null) { - for (int i = 0; i < _NUM_UID_STATE; i++) { - data[i] = (long) HistoricalOps.round((double) data[i] * scaleFactor); + private static void merge(@NonNull Supplier<LongSparseLongArray> thisSupplier, + @Nullable LongSparseLongArray other) { + if (other != null) { + final int otherSize = other.size(); + for (int i = 0; i < otherSize; i++) { + final LongSparseLongArray that = thisSupplier.get(); + final long otherKey = other.keyAt(i); + final long otherValue = other.valueAt(i); + that.put(otherKey, that.get(otherKey) + otherValue); } } } - public static final @android.annotation.NonNull Creator<HistoricalOp> CREATOR = new Creator<HistoricalOp>() { + /** @hide */ + public @Nullable LongSparseArray<Object> collectKeys() { + LongSparseArray<Object> result = AppOpsManager.collectKeys(mAccessCount, + null /*result*/); + result = AppOpsManager.collectKeys(mRejectCount, result); + result = AppOpsManager.collectKeys(mAccessDuration, result); + return result; + } + + public static final @android.annotation.NonNull Creator<HistoricalOp> CREATOR = + new Creator<HistoricalOp>() { @Override public @NonNull HistoricalOp createFromParcel(@NonNull Parcel source) { return new HistoricalOp(source); @@ -3600,36 +4167,101 @@ public class AppOpsManager { return new HistoricalOp[size]; } }; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final HistoricalOp other = (HistoricalOp) obj; - if (mOp != other.mOp) { - return false; - } - if (!Arrays.equals(mAccessCount, other.mAccessCount)) { - return false; + /** + * Computes the sum of the counts for the given flags in between the begin and + * end UID states. + * + * @param counts The data array. + * @param beginUidState The beginning UID state (exclusive). + * @param endUidState The end UID state. + * @param flags The UID flags. + * @return The sum. + */ + private static long sumForFlagsInStates(@Nullable LongSparseLongArray counts, + @UidState int beginUidState, @UidState int endUidState, @OpFlags int flags) { + if (counts == null) { + return 0; + } + long sum = 0; + while (flags != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(flags); + flags &= ~flag; + for (int uidState : UID_STATES) { + if (uidState < beginUidState || uidState > endUidState) { + continue; + } + final long key = makeKey(uidState, flag); + sum += counts.get(key); } - if (!Arrays.equals(mRejectCount, other.mRejectCount)) { - return false; + } + return sum; + } + + /** + * Finds the first non-negative value for the given flags in between the begin and + * end UID states. + * + * @param counts The data array. + * @param flags The UID flags. + * @param beginUidState The beginning UID state (exclusive). + * @param endUidState The end UID state. + * @return The non-negative value or -1. + */ + private static long findFirstNonNegativeForFlagsInStates(@Nullable LongSparseLongArray counts, + @OpFlags int flags, @UidState int beginUidState, @UidState int endUidState) { + if (counts == null) { + return -1; + } + while (flags != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(flags); + flags &= ~flag; + for (int uidState : UID_STATES) { + if (uidState < beginUidState || uidState > endUidState) { + continue; + } + final long key = makeKey(uidState, flag); + final long value = counts.get(key); + if (value >= 0) { + return value; + } } - return Arrays.equals(mAccessDuration, other.mAccessDuration); } + return -1; + } - @Override - public int hashCode() { - int result = mOp; - result = 31 * result + Arrays.hashCode(mAccessCount); - result = 31 * result + Arrays.hashCode(mRejectCount); - result = 31 * result + Arrays.hashCode(mAccessDuration); - return result; + /** + * Finds the first non-null value for the given flags in between the begin and + * end UID states. + * + * @param counts The data array. + * @param flags The UID flags. + * @param beginUidState The beginning UID state (exclusive). + * @param endUidState The end UID state. + * @return The non-negative value or -1. + */ + private static @Nullable String findFirstNonNullForFlagsInStates( + @Nullable LongSparseArray<String> counts, @OpFlags int flags, + @UidState int beginUidState, @UidState int endUidState) { + if (counts == null) { + return null; + } + while (flags != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(flags); + flags &= ~flag; + for (int uidState : UID_STATES) { + if (uidState < beginUidState || uidState > endUidState) { + continue; + } + final long key = makeKey(uidState, flag); + final String value = counts.get(key); + if (value != null) { + return value; + } + } } + return null; } /** @@ -3800,7 +4432,7 @@ public class AppOpsManager { Preconditions.checkNotNull(callback, "callback cannot be null"); try { mService.getHistoricalOps(request.mUid, request.mPackageName, request.mOpNames, - request.mBeginTimeMillis, request.mEndTimeMillis, + request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> { final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS); final long identity = Binder.clearCallingIdentity(); @@ -3840,7 +4472,7 @@ public class AppOpsManager { try { mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName, request.mOpNames, request.mBeginTimeMillis, request.mEndTimeMillis, - new RemoteCallback((result) -> { + request.mFlags, new RemoteCallback((result) -> { final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS); final long identity = Binder.clearCallingIdentity(); try { @@ -4310,7 +4942,20 @@ public class AppOpsManager { * Like {@link #checkOp} but returns the <em>raw</em> mode associated with the op. * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}. */ - public int unsafeCheckOpRaw(String op, int uid, String packageName) { + public int unsafeCheckOpRaw(@NonNull String op, int uid, String packageName) { + try { + return mService.checkOperationRaw(strOpToOp(op), uid, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Like {@link #unsafeCheckOpNoThrow(String, int, String)} but returns the <em>raw</em> + * mode associated with the op. Does not throw a security exception, does not translate + * {@link #MODE_FOREGROUND}. + */ + public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) { try { return mService.checkOperationRaw(strOpToOp(op), uid, packageName); } catch (RemoteException e) { @@ -4899,16 +5544,110 @@ public class AppOpsManager { } /** - * @hide + * Computes the max for the given flags in between the begin and + * end UID states. + * + * @param counts The data array. + * @param flags The UID flags. + * @param beginUidState The beginning UID state (exclusive). + * @param endUidState The end UID state. + * @return The sum. + */ + private static long maxForFlagsInStates(@Nullable LongSparseLongArray counts, + @UidState int beginUidState, @UidState int endUidState, + @OpFlags int flags) { + if (counts == null) { + return 0; + } + long max = 0; + while (flags != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(flags); + flags &= ~flag; + for (int uidState : UID_STATES) { + if (uidState < beginUidState || uidState > endUidState) { + continue; + } + final long key = makeKey(uidState, flag); + max = Math.max(max, counts.get(key)); + } + } + return max; + } + + + private static void writeLongSparseLongArrayToParcel( + @Nullable LongSparseLongArray array, @NonNull Parcel parcel) { + if (array != null) { + final int size = array.size(); + parcel.writeInt(size); + for (int i = 0; i < size; i++) { + parcel.writeLong(array.keyAt(i)); + parcel.writeLong(array.valueAt(i)); + } + } else { + parcel.writeInt(-1); + } + } + + private static @Nullable LongSparseLongArray readLongSparseLongArrayFromParcel( + @NonNull Parcel parcel) { + final int size = parcel.readInt(); + if (size < 0) { + return null; + } + final LongSparseLongArray array = new LongSparseLongArray(size); + for (int i = 0; i < size; i++) { + array.append(parcel.readLong(), parcel.readLong()); + } + return array; + } + + private static void writeLongSparseStringArrayToParcel( + @Nullable LongSparseArray<String> array, @NonNull Parcel parcel) { + if (array != null) { + final int size = array.size(); + parcel.writeInt(size); + for (int i = 0; i < size; i++) { + parcel.writeLong(array.keyAt(i)); + parcel.writeString(array.valueAt(i)); + } + } else { + parcel.writeInt(-1); + } + } + + private static @Nullable LongSparseArray<String> readLongSparseStringArrayFromParcel( + @NonNull Parcel parcel) { + final int size = parcel.readInt(); + if (size < 0) { + return null; + } + final LongSparseArray<String> array = new LongSparseArray<>(size); + for (int i = 0; i < size; i++) { + array.append(parcel.readLong(), parcel.readString()); + } + return array; + } + + /** + * Collects the keys from an array to the result creating the result if needed. + * + * @param array The array whose keys to collect. + * @param result The optional result store collected keys. + * @return The result collected keys array. */ - public static long maxTime(long[] times, int start, int end) { - long time = 0; - for (int i = start; i < end; i++) { - if (times[i] > time) { - time = times[i]; + private static LongSparseArray<Object> collectKeys(@Nullable LongSparseLongArray array, + @Nullable LongSparseArray<Object> result) { + if (array != null) { + if (result == null) { + result = new LongSparseArray<>(); + } + final int accessSize = array.size(); + for (int i = 0; i < accessSize; i++) { + result.put(array.keyAt(i), null); } } - return time; + return result; } /** @hide */ diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index 65859c76a42a..ad614b1047ed 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -1,18 +1,37 @@ { - "imports": [ - { - "path": "frameworks/base/services/core/java/com/android/server/am" - }, - { - "path": "frameworks/base/services/core/java/com/android/server/wm" - } - ], - "presubmit": [ - { - "name": "CtsFragmentTestCases" - }, - { - "name": "CtsFragmentTestCasesSdk26" - } - ] + "imports": [ + { + "path": "frameworks/base/services/core/java/com/android/server/am" + }, + { + "path": "frameworks/base/services/core/java/com/android/server/wm" + } + ], + "presubmit": [ + { + "name": "CtsFragmentTestCases" + }, + { + "name": "CtsFragmentTestCasesSdk26" + }, + { + "file_patterns": ["(/|^)AppOpsManager.java"], + "name": "CtsAppOpsTestCases" + }, + { + "file_patterns": ["(/|^)AppOpsManager.java"], + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.appop.AppOpsUpgradeTest" + }, + { + "include-filter": "com.android.server.appop.AppOpsServiceTest" + }, + { + "include-filter": "com.android.server.appop.AppOpsActiveWatcherTest" + } + ] + } + ] } diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java index 9ba0f5da135e..cf49803a7225 100644 --- a/core/java/android/util/LongSparseArray.java +++ b/core/java/android/util/LongSparseArray.java @@ -21,6 +21,9 @@ import com.android.internal.util.GrowingArrayUtils; import libcore.util.EmptyArray; +import java.util.Arrays; +import java.util.Objects; + /** * SparseArray mapping longs to Objects. Unlike a normal array of Objects, * there can be gaps in the indices. It is intended to be more memory efficient diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index af163ac8f246..8dcdb4026246 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -22,6 +22,8 @@ import com.android.internal.util.GrowingArrayUtils; import android.annotation.UnsupportedAppUsage; import libcore.util.EmptyArray; +import java.util.Arrays; + /** * Map of {@code long} to {@code long}. Unlike a normal array of longs, there * can be gaps in the indices. It is intended to be more memory efficient than using a diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index c096961c57b8..c4af4c797d37 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -48,9 +48,9 @@ interface IAppOpsService { @UnsupportedAppUsage List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops); void getHistoricalOps(int uid, String packageName, in List<String> ops, long beginTimeMillis, - long endTimeMillis, in RemoteCallback callback); + long endTimeMillis, int flags, in RemoteCallback callback); void getHistoricalOpsFromDiskRaw(int uid, String packageName, in List<String> ops, - long beginTimeMillis, long endTimeMillis, in RemoteCallback callback); + long beginTimeMillis, long endTimeMillis, int flags, in RemoteCallback callback); void offsetHistory(long duration); void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep); void addHistoricalOps(in AppOpsManager.HistoricalOps ops); diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java index 4ac3ce436f82..09107ce6b77b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java +++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java @@ -48,6 +48,11 @@ public class RecentLocationAccesses { // Keep last 24 hours of location app information. private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS; + /** The flags for querying ops that are trusted for showing in the UI. */ + public static final int TRUSTED_STATE_FLAGS = AppOpsManager.OP_FLAG_SELF + | AppOpsManager.OP_FLAG_UNTRUSTED_PROXY + | AppOpsManager.OP_FLAG_TRUSTED_PROXIED; + @VisibleForTesting static final int[] LOCATION_OPS = new int[]{ AppOpsManager.OP_FINE_LOCATION, @@ -136,8 +141,7 @@ public class RecentLocationAccesses { // Earliest time for a location access to end and still be shown in list. long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS; for (AppOpsManager.OpEntry entry : entries) { - locationAccessFinishTime = Math.max(entry.getLastAccessBackgroundTime(), - entry.getLastAccessForegroundTime()); + locationAccessFinishTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS); } // Bail out if the entry is out of date. if (locationAccessFinishTime < recentLocationCutoffTime) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java index d5b89ca719ad..c1c5fa932b0c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java @@ -17,6 +17,7 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.util.LongSparseLongArray; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -152,11 +153,12 @@ public class RecentLocationAccessesTest { } private OpEntry createOpEntryWithTime(int op, long time) { - final long[] times = new long[AppOpsManager._NUM_UID_STATE]; // Slot for background access timestamp. - times[AppOpsManager.UID_STATE_LAST_NON_RESTRICTED + 1] = time; - final long[] rejectTimes = new long[AppOpsManager._NUM_UID_STATE]; - return new OpEntry(op, AppOpsManager.MODE_ALLOWED, times, rejectTimes, 0 /* duration */, - 0 /* proxyUid */, "" /* proxyPackage */); + final LongSparseLongArray accessTimes = new LongSparseLongArray(); + accessTimes.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_BACKGROUND, + AppOpsManager.OP_FLAG_SELF), time); + + return new OpEntry(op, false, AppOpsManager.MODE_ALLOWED, accessTimes, null /*durations*/, + null /*rejectTimes*/, null /* proxyUids */, null /* proxyPackages */); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java index 08d536720029..8bd5fd2163ae 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java @@ -17,6 +17,7 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.util.LongSparseLongArray; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -153,6 +154,13 @@ public class RecentLocationAppsTest { } private OpEntry createOpEntryWithTime(int op, long time, int duration) { - return new OpEntry(op, AppOpsManager.MODE_ALLOWED, time, 0L, duration, 0, ""); + final LongSparseLongArray accessTimes = new LongSparseLongArray(); + accessTimes.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP, + AppOpsManager.OP_FLAG_SELF), time); + final LongSparseLongArray durations = new LongSparseLongArray(); + durations.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP, + AppOpsManager.OP_FLAG_SELF), duration); + return new OpEntry(op, false, AppOpsManager.MODE_ALLOWED, accessTimes, + null /*rejectTimes*/, durations, null /* proxyUids */, null /* proxyPackages */); } } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index c8e8ef862f84..e28d484eebc7 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1727,7 +1727,8 @@ class StorageManagerService extends IStorageManager.Stub final List<AppOpsManager.PackageOps> pkgs = manager.getOpsForPackage(uid, packageName, ops); for (AppOpsManager.PackageOps pkg : CollectionUtils.emptyIfNull(pkgs)) { for (AppOpsManager.OpEntry op : CollectionUtils.emptyIfNull(pkg.getOps())) { - maxTime = Math.max(maxTime, op.getLastAccessTime()); + maxTime = Math.max(maxTime, op.getLastAccessTime( + AppOpsManager.OP_FLAGS_ALL_TRUSTED)); } } return maxTime; diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 70c28a854512..ba7288e6d15f 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -16,6 +16,9 @@ package com.android.server.appop; +import static android.app.AppOpsManager.MAX_PRIORITY_UID_STATE; +import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE; +import static android.app.AppOpsManager.OP_FLAGS_ALL; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.UID_STATE_BACKGROUND; @@ -23,12 +26,12 @@ import static android.app.AppOpsManager.UID_STATE_CACHED; import static android.app.AppOpsManager.UID_STATE_FOREGROUND; import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE; import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION; -import static android.app.AppOpsManager.UID_STATE_LAST_NON_RESTRICTED; +import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED; import static android.app.AppOpsManager.UID_STATE_PERSISTENT; import static android.app.AppOpsManager.UID_STATE_TOP; -import static android.app.AppOpsManager._NUM_UID_STATE; import static android.app.AppOpsManager.modeToName; import static android.app.AppOpsManager.opToName; +import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState; import android.Manifest; import android.annotation.NonNull; @@ -39,6 +42,9 @@ import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AppOpsManager.HistoricalOps; import android.app.AppOpsManager.HistoricalOpsRequest; +import android.app.AppOpsManager.Mode; +import android.app.AppOpsManager.OpEntry; +import android.app.AppOpsManager.OpFlags; import android.app.AppOpsManagerInternal; import android.app.AppOpsManagerInternal.CheckOpsDelegate; import android.content.BroadcastReceiver; @@ -76,6 +82,8 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.KeyValueListParser; +import android.util.LongSparseArray; +import android.util.LongSparseLongArray; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -164,36 +172,6 @@ public class AppOpsService extends IAppOpsService.Stub { UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_NONEXISTENT }; - static final String[] UID_STATE_NAMES = new String[] { - "pers ", // UID_STATE_PERSISTENT - "top ", // UID_STATE_TOP - "fgsvcl", // UID_STATE_FOREGROUND_SERVICE_LOCATION - "fgsvc", // UID_STATE_FOREGROUND_SERVICE - "fg ", // UID_STATE_FOREGROUND - "bg ", // UID_STATE_BACKGROUND - "cch ", // UID_STATE_CACHED - }; - - static final String[] UID_STATE_TIME_ATTRS = new String[] { - "tp", // UID_STATE_PERSISTENT - "tt", // UID_STATE_TOP - "tfsl", // UID_STATE_FOREGROUND_SERVICE_LOCATION - "tfs", // UID_STATE_FOREGROUND_SERVICE - "tf", // UID_STATE_FOREGROUND - "tb", // UID_STATE_BACKGROUND - "tc", // UID_STATE_CACHED - }; - - static final String[] UID_STATE_REJECT_ATTRS = new String[] { - "rp", // UID_STATE_PERSISTENT - "rt", // UID_STATE_TOP - "rfsl", // UID_STATE_FOREGROUND_SERVICE_LOCATION - "rfs", // UID_STATE_FOREGROUND_SERVICE - "rf", // UID_STATE_FOREGROUND - "rb", // UID_STATE_BACKGROUND - "rc", // UID_STATE_CACHED - }; - Context mContext; final AtomicFile mFile; final Handler mHandler; @@ -355,12 +333,14 @@ public class AppOpsService extends IAppOpsService.Stub { public boolean isDefault() { return (pkgOps == null || pkgOps.isEmpty()) - && (opModes == null || opModes.size() <= 0); + && (opModes == null || opModes.size() <= 0) + && (state == UID_STATE_CACHED + && (pendingState == UID_STATE_CACHED)); } - int evalMode(int mode) { + int evalMode(int op, int mode) { if (mode == AppOpsManager.MODE_FOREGROUND) { - return state <= UID_STATE_LAST_NON_RESTRICTED + return state <= AppOpsManager.resolveLastRestrictedUidState(op) ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED; } return mode; @@ -425,41 +405,121 @@ public class AppOpsService extends IAppOpsService.Stub { } final static class Op { + int op; + boolean running; final UidState uidState; - final int uid; - final String packageName; - final int op; - int proxyUid = -1; - String proxyPackageName; - int mode; - int duration; - long time[] = new long[_NUM_UID_STATE]; - long rejectTime[] = new long[_NUM_UID_STATE]; + final @NonNull String packageName; + + private @Mode int mode; + private @Nullable LongSparseLongArray mAccessTimes; + private @Nullable LongSparseLongArray mRejectTimes; + private @Nullable LongSparseLongArray mDurations; + private @Nullable LongSparseLongArray mProxyUids; + private @Nullable LongSparseArray<String> mProxyPackageNames; + int startNesting; long startRealtime; - Op(UidState _uidState, String _packageName, int _op) { - uidState = _uidState; - uid = _uidState.uid; - packageName = _packageName; - op = _op; - mode = AppOpsManager.opToDefaultMode(op); + Op(UidState uidState, String packageName, int op) { + this.op = op; + this.uidState = uidState; + this.packageName = packageName; + this.mode = AppOpsManager.opToDefaultMode(op); } - boolean hasAnyTime() { - for (int i = 0; i < AppOpsManager._NUM_UID_STATE; i++) { - if (time[i] != 0) { - return true; - } - if (rejectTime[i] != 0) { - return true; - } + int getMode() { + return mode; + } + + int evalMode() { + return uidState.evalMode(op, mode); + } + + /** @hide */ + public void accessed(long time, int proxyUid, @Nullable String proxyPackageName, + @AppOpsManager.UidState int uidState, @OpFlags int flags) { + final long key = AppOpsManager.makeKey(uidState, flags); + if (mAccessTimes == null) { + mAccessTimes = new LongSparseLongArray(); + } + mAccessTimes.put(key, time); + updateProxyState(key, proxyUid, proxyPackageName); + if (mDurations != null) { + mDurations.delete(key); } - return false; } - int getMode() { - return uidState.evalMode(mode); + /** @hide */ + public void rejected(long time, int proxyUid, @Nullable String proxyPackageName, + @AppOpsManager.UidState int uidState, @OpFlags int flags) { + final long key = AppOpsManager.makeKey(uidState, flags); + if (mRejectTimes == null) { + mRejectTimes = new LongSparseLongArray(); + } + mRejectTimes.put(key, time); + updateProxyState(key, proxyUid, proxyPackageName); + if (mDurations != null) { + mDurations.delete(key); + } + } + + /** @hide */ + public void started(long time, @AppOpsManager.UidState int uidState, @OpFlags int flags) { + updateAccessTimeAndDuration(time, -1 /*duration*/, uidState, flags); + running = true; + } + + /** @hide */ + public void finished(long time, long duration, @AppOpsManager.UidState int uidState, + @OpFlags int flags) { + updateAccessTimeAndDuration(time, duration, uidState, flags); + running = false; + } + + /** @hide */ + public void running(long time, long duration, @AppOpsManager.UidState int uidState, + @OpFlags int flags) { + updateAccessTimeAndDuration(time, duration, uidState, flags); + } + + /** @hide */ + public void continuing(long duration, @AppOpsManager.UidState int uidState, + @OpFlags int flags) { + final long key = AppOpsManager.makeKey(uidState, flags); + if (mDurations == null) { + mDurations = new LongSparseLongArray(); + } + mDurations.put(key, duration); + } + + private void updateAccessTimeAndDuration(long time, long duration, + @AppOpsManager.UidState int uidState, @OpFlags int flags) { + final long key = AppOpsManager.makeKey(uidState, flags); + if (mAccessTimes == null) { + mAccessTimes = new LongSparseLongArray(); + } + mAccessTimes.put(key, time); + if (mDurations == null) { + mDurations = new LongSparseLongArray(); + } + mDurations.put(key, duration); + } + + private void updateProxyState(long key, int proxyUid, + @Nullable String proxyPackageName) { + if (mProxyUids == null) { + mProxyUids = new LongSparseLongArray(); + } + mProxyUids.put(key, proxyUid); + if (mProxyPackageNames == null) { + mProxyPackageNames = new LongSparseArray<>(); + } + mProxyPackageNames.put(key, proxyPackageName); + } + + boolean hasAnyTime() { + return (mAccessTimes != null && mAccessTimes.size() > 0) + || (mRejectTimes != null && mRejectTimes.size() > 0); } } @@ -812,7 +872,7 @@ public class AppOpsService extends IAppOpsService.Stub { final int opCount = client.mStartedOps.size(); for (int j = opCount - 1; j >= 0; j--) { final Op op = client.mStartedOps.get(j); - if (uid == op.uid && packageName.equals(op.packageName)) { + if (uid == op.uidState.uid && packageName.equals(op.packageName)) { finishOperationLocked(op, /*finishNested*/ true); client.mStartedOps.remove(j); if (op.startNesting <= 0) { @@ -829,9 +889,9 @@ public class AppOpsService extends IAppOpsService.Stub { final int opCount = ops.size(); for (int i = 0; i < opCount; i++) { final Op op = ops.valueAt(i); - if (op.duration == -1) { + if (op.running) { scheduleOpActiveChangedIfNeededLocked( - op.op, op.uid, op.packageName, false); + op.op, op.uidState.uid, op.packageName, false); } } } @@ -854,7 +914,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidState != null && uidState.pendingState != newState) { final int oldPendingState = uidState.pendingState; uidState.pendingState = newState; - if (newState < uidState.state || newState <= UID_STATE_LAST_NON_RESTRICTED) { + if (newState < uidState.state || newState <= UID_STATE_MAX_LAST_NON_RESTRICTED) { // We are moving to a more important state, or the new state is in the // foreground, then always do it immediately. commitUidPendingStateLocked(uidState); @@ -880,8 +940,18 @@ public class AppOpsService extends IAppOpsService.Stub { for (int j = ops.size() - 1; j >= 0; j--) { final Op op = ops.valueAt(j); if (op.startNesting > 0) { - op.time[oldPendingState] = now; - op.time[newState] = now; + final long duration = SystemClock.elapsedRealtime() + - op.startRealtime; + // We don't support proxy long running ops (start/stop) + mHistoricalRegistry.increaseOpAccessDuration(op.op, + op.uidState.uid, op.packageName, oldPendingState, + AppOpsManager.OP_FLAG_SELF, duration); + // Finish the op in the old state + op.finished(now, duration, oldPendingState, + AppOpsManager.OP_FLAG_SELF); + // Start the op in the new state + op.startRealtime = now; + op.started(now, newState, AppOpsManager.OP_FLAG_SELF); } } } @@ -911,13 +981,7 @@ public class AppOpsService extends IAppOpsService.Stub { resOps = new ArrayList<>(); for (int j=0; j<pkgOps.size(); j++) { Op curOp = pkgOps.valueAt(j); - final boolean running = curOp.duration == -1; - long duration = running - ? (elapsedNow - curOp.startRealtime) - : curOp.duration; - resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time, - curOp.rejectTime, (int) duration, running, curOp.proxyUid, - curOp.proxyPackageName)); + resOps.add(getOpEntryForResult(curOp, elapsedNow)); } } else { for (int j=0; j<ops.length; j++) { @@ -926,13 +990,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (resOps == null) { resOps = new ArrayList<>(); } - final boolean running = curOp.duration == -1; - final long duration = running - ? (elapsedNow - curOp.startRealtime) - : curOp.duration; - resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time, - curOp.rejectTime, (int) duration, running, curOp.proxyUid, - curOp.proxyPackageName)); + resOps.add(getOpEntryForResult(curOp, elapsedNow)); } } } @@ -947,8 +1005,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (ops == null) { resOps = new ArrayList<>(); for (int j=0; j<uidOps.size(); j++) { - resOps.add(new AppOpsManager.OpEntry(uidOps.keyAt(j), uidOps.valueAt(j), - 0, 0, 0, -1, null)); + resOps.add(new OpEntry(uidOps.keyAt(j), uidOps.valueAt(j))); } } else { for (int j=0; j<ops.length; j++) { @@ -957,14 +1014,27 @@ public class AppOpsService extends IAppOpsService.Stub { if (resOps == null) { resOps = new ArrayList<>(); } - resOps.add(new AppOpsManager.OpEntry(uidOps.keyAt(index), uidOps.valueAt(index), - 0, 0, 0, -1, null)); + resOps.add(new OpEntry(uidOps.keyAt(j), uidOps.valueAt(j))); } } } return resOps; } + private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op, long elapsedNow) { + if (op.running) { + op.continuing(elapsedNow - op.startRealtime, + op.uidState.state, AppOpsManager.OP_FLAG_SELF); + } + final OpEntry entry = new OpEntry(op.op, op.running, op.mode, + op.mAccessTimes != null ? op.mAccessTimes.clone() : null, + op.mRejectTimes != null ? op.mRejectTimes.clone() : null, + op.mDurations != null ? op.mDurations.clone() : null, + op.mProxyUids != null ? op.mProxyUids.clone() : null, + op.mProxyPackageNames != null ? op.mProxyPackageNames.clone() : null); + return entry; + } + @Override public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) { mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, @@ -1026,13 +1096,14 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void getHistoricalOps(int uid, @NonNull String packageName, @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis, - @NonNull RemoteCallback callback) { + @OpFlags int flags, @NonNull RemoteCallback callback) { // Use the builder to validate arguments. - final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder( + new HistoricalOpsRequest.Builder( beginTimeMillis, endTimeMillis) .setUid(uid) .setPackageName(packageName) .setOpNames(opNames) + .setFlags(flags) .build(); Preconditions.checkNotNull(callback, "callback cannot be null"); @@ -1041,79 +1112,23 @@ public class AppOpsService extends IAppOpsService.Stub { final String[] opNamesArray = (opNames != null) ? opNames.toArray(new String[opNames.size()]) : null; - if (mHistoricalRegistry.getMode() == AppOpsManager.HISTORICAL_MODE_DISABLED) { - // TODO (bug:122218838): Remove once the feature fully enabled. - getHistoricalPackagesOpsCompat(uid, packageName, opNamesArray, beginTimeMillis, - endTimeMillis, callback); - } else { - // Must not hold the appops lock - mHistoricalRegistry.getHistoricalOps(uid, packageName, opNamesArray, - beginTimeMillis, endTimeMillis, callback); - } - } - private void getHistoricalPackagesOpsCompat(int uid, @NonNull String packageName, - @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, - @NonNull RemoteCallback callback) { - synchronized (AppOpsService.this) { - final HistoricalOps ops = new HistoricalOps(beginTimeMillis, endTimeMillis); - if (opNames == null) { - opNames = AppOpsManager.getOpStrs(); - } - final int uidStateCount = mUidStates.size(); - for (int uidIdx = 0; uidIdx < uidStateCount; uidIdx++) { - final UidState uidState = mUidStates.valueAt(uidIdx); - if (uidState.pkgOps == null || uidState.pkgOps.isEmpty() - || (uid != Process.INVALID_UID && uid != uidState.uid)) { - continue; - } - final ArrayMap<String, Ops> packages = uidState.pkgOps; - final int packageCount = packages.size(); - for (int pkgIdx = 0; pkgIdx < packageCount; pkgIdx++) { - final Ops pkgOps = packages.valueAt(pkgIdx); - if (packageName != null && !packageName.equals(pkgOps.packageName)) { - continue; - } - final int opCount = opNames.length; - for (int opIdx = 0; opIdx < opCount; opIdx++) { - final String opName = opNames[opIdx]; - if (!ArrayUtils.contains(opNames, opName)) { - continue; - } - final int opCode = AppOpsManager.strOpToOp(opName); - final Op op = pkgOps.get(opCode); - if (op == null) { - continue; - } - final int stateCount = AppOpsManager._NUM_UID_STATE; - for (int stateIdx = 0; stateIdx < stateCount; stateIdx++) { - if (op.rejectTime[stateIdx] != 0) { - ops.increaseRejectCount(opCode, uidState.uid, - pkgOps.packageName, stateIdx, 1); - } else if (op.time[stateIdx] != 0) { - ops.increaseAccessCount(opCode, uidState.uid, - pkgOps.packageName, stateIdx, 1); - } - } - } - } - } - final Bundle payload = new Bundle(); - payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, ops); - callback.sendResult(payload); - } + // Must not hold the appops lock + mHistoricalRegistry.getHistoricalOps(uid, packageName, opNamesArray, + beginTimeMillis, endTimeMillis, flags, callback); } @Override public void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis, - @NonNull RemoteCallback callback) { + @OpFlags int flags, @NonNull RemoteCallback callback) { // Use the builder to validate arguments. - final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder( + new HistoricalOpsRequest.Builder( beginTimeMillis, endTimeMillis) .setUid(uid) .setPackageName(packageName) .setOpNames(opNames) + .setFlags(flags) .build(); Preconditions.checkNotNull(callback, "callback cannot be null"); @@ -1125,7 +1140,7 @@ public class AppOpsService extends IAppOpsService.Stub { // Must not hold the appops lock mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNamesArray, - beginTimeMillis, endTimeMillis, callback); + beginTimeMillis, endTimeMillis, flags, callback); } @Override @@ -1172,7 +1187,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - void enforceManageAppOpsModes(int callingPid, int callingUid, int targetUid) { + private void enforceManageAppOpsModes(int callingPid, int callingUid, int targetUid) { if (callingPid == Process.myPid()) { return; } @@ -1316,6 +1331,8 @@ public class AppOpsService extends IAppOpsService.Stub { return; } + boolean scheduleWrite = false; + int numPkgs = pkgOps.size(); for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) { Ops ops = pkgOps.valueAt(pkgNum); @@ -1327,14 +1344,14 @@ public class AppOpsService extends IAppOpsService.Stub { int defaultMode = AppOpsManager.opToDefaultMode(code); if (op.mode != defaultMode) { - Slog.w(TAG, "resetting app-op mode for " + AppOpsManager.opToName(code) + " of " - + pkgOps.keyAt(pkgNum)); - op.mode = defaultMode; - - scheduleWriteLocked(); + scheduleWrite = true; } } + + if (scheduleWrite) { + scheduleWriteLocked(); + } } } @@ -1544,9 +1561,10 @@ public class AppOpsService extends IAppOpsService.Stub { curOp.mode = AppOpsManager.opToDefaultMode(curOp.op); changed = true; uidChanged = true; - callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName, + final int uid = curOp.uidState.uid; + callbacks = addCallbacks(callbacks, curOp.op, uid, packageName, mOpModeWatchers.get(curOp.op)); - callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName, + callbacks = addCallbacks(callbacks, curOp.op, uid, packageName, mPackageModeWatchers.get(packageName)); if (!curOp.hasAnyTime()) { pkgOps.removeAt(j); @@ -1727,6 +1745,7 @@ public class AppOpsService extends IAppOpsService.Stub { private int checkOperationUnchecked(int code, int uid, String packageName, boolean raw) { synchronized (this) { + checkPackage(uid, packageName); if (isOpRestrictedLocked(uid, code, packageName)) { return AppOpsManager.MODE_IGNORED; } @@ -1735,13 +1754,13 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidState != null && uidState.opModes != null && uidState.opModes.indexOfKey(code) >= 0) { final int rawMode = uidState.opModes.get(code); - return raw ? rawMode : uidState.evalMode(rawMode); + return raw ? rawMode : uidState.evalMode(code, rawMode); } Op op = getOpLocked(code, uid, packageName, false, true, false); if (op == null) { return AppOpsManager.opToDefaultMode(code); } - return op.mode; + return raw ? op.mode : op.evalMode(); } } @@ -1855,21 +1874,32 @@ public class AppOpsService extends IAppOpsService.Stub { String proxyPackageName, int proxiedUid, String proxiedPackageName) { verifyIncomingUid(proxyUid); verifyIncomingOp(code); + String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName); if (resolveProxyPackageName == null) { return AppOpsManager.MODE_IGNORED; } + + final boolean isProxyTrusted = mContext.checkPermission( + Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid) + == PackageManager.PERMISSION_GRANTED; + + final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY + : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY; final int proxyMode = noteOperationUnchecked(code, proxyUid, - resolveProxyPackageName, -1, null); + resolveProxyPackageName, Process.INVALID_UID, null, proxyFlags); if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) { return proxyMode; } + String resolveProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName); if (resolveProxiedPackageName == null) { return AppOpsManager.MODE_IGNORED; } + final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED + : AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED; return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName, - proxyMode, resolveProxyPackageName); + proxyUid, resolveProxyPackageName, proxiedFlags); } @Override @@ -1892,11 +1922,12 @@ public class AppOpsService extends IAppOpsService.Stub { if (resolvedPackageName == null) { return AppOpsManager.MODE_IGNORED; } - return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null); + return noteOperationUnchecked(code, uid, resolvedPackageName, Process.INVALID_UID, null, + AppOpsManager.OP_FLAG_SELF); } private int noteOperationUnchecked(int code, int uid, String packageName, - int proxyUid, String proxyPackageName) { + int proxyUid, String proxyPackageName, @OpFlags int flags) { synchronized (this) { final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, false /* uidMismatchExpected */); @@ -1914,49 +1945,52 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } final UidState uidState = ops.uidState; - if (op.duration == -1) { + if (op.running) { + final OpEntry entry = new OpEntry(op.op, op.running, op.mode, op.mAccessTimes, + op.mRejectTimes, op.mDurations, op.mProxyUids, op.mProxyPackageNames); Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName - + " code " + code + " time=" + op.time[uidState.state] - + " duration=" + op.duration); + + " code " + code + " time=" + entry.getLastAccessTime(uidState.state, + uidState.state, flags) + " duration=" + entry.getLastDuration( + uidState.state, uidState.state, flags)); } - op.duration = 0; + final int switchCode = AppOpsManager.opToSwitch(code); // If there is a non-default per UID policy (we set UID op mode only if // non-default) it takes over, otherwise use the per package policy. if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) { - final int uidMode = uidState.evalMode(uidState.opModes.get(switchCode)); + final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode)); if (uidMode != AppOpsManager.MODE_ALLOWED) { if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName); - op.rejectTime[uidState.state] = System.currentTimeMillis(); + op.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName, + uidState.state, flags); + mHistoricalRegistry.incrementOpRejected(code, uid, packageName, + uidState.state, flags); scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode); - mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName, - uidState.state); return uidMode; } } else { final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; - final int mode = switchOp.getMode(); - if (mode != AppOpsManager.MODE_ALLOWED) { + final int mode = switchOp.evalMode(); + if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName); - op.rejectTime[uidState.state] = System.currentTimeMillis(); - scheduleOpNotedIfNeededLocked(op.op, uid, packageName, mode); - mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName, - uidState.state); + op.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName, + uidState.state, flags); + mHistoricalRegistry.incrementOpRejected(code, uid, packageName, + uidState.state, flags); + scheduleOpNotedIfNeededLocked(code, uid, packageName, mode); return mode; } } if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid + " package " + packageName); - op.time[uidState.state] = System.currentTimeMillis(); + op.accessed(System.currentTimeMillis(), proxyUid, proxyPackageName, + uidState.state, flags); mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName, - uidState.state); - op.rejectTime[uidState.state] = 0; - op.proxyUid = proxyUid; - op.proxyPackageName = proxyPackageName; + uidState.state, flags); scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED); return AppOpsManager.MODE_ALLOWED; @@ -2080,29 +2114,34 @@ public class AppOpsService extends IAppOpsService.Stub { final UidState uidState = ops.uidState; // If there is a non-default per UID policy (we set UID op mode only if // non-default) it takes over, otherwise use the per package policy. + final int opCode = op.op; if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) { - final int uidMode = uidState.evalMode(uidState.opModes.get(switchCode)); + final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode)); if (uidMode != AppOpsManager.MODE_ALLOWED && (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) { if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + resolvedPackageName); - op.rejectTime[uidState.state] = System.currentTimeMillis(); - mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName, - uidState.state); + // We don't support proxy long running ops (start/stop) + op.rejected(System.currentTimeMillis(), -1 /*proxyUid*/, + null /*proxyPackage*/, uidState.state, AppOpsManager.OP_FLAG_SELF); + mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName, + uidState.state, AppOpsManager.OP_FLAG_SELF); return uidMode; } } else { final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; - final int mode = switchOp.getMode(); + final int mode = switchOp.evalMode(); if (mode != AppOpsManager.MODE_ALLOWED && (!startIfModeDefault || mode != AppOpsManager.MODE_DEFAULT)) { if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + resolvedPackageName); - op.rejectTime[uidState.state] = System.currentTimeMillis(); - mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName, - uidState.state); + // We don't support proxy long running ops (start/stop) + op.rejected(System.currentTimeMillis(), -1 /*proxyUid*/, + null /*proxyPackage*/, uidState.state, AppOpsManager.OP_FLAG_SELF); + mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName, + uidState.state, AppOpsManager.OP_FLAG_SELF); return mode; } } @@ -2110,11 +2149,12 @@ public class AppOpsService extends IAppOpsService.Stub { + " package " + resolvedPackageName); if (op.startNesting == 0) { op.startRealtime = SystemClock.elapsedRealtime(); - op.time[uidState.state] = System.currentTimeMillis(); - mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName, - uidState.state); - op.rejectTime[uidState.state] = 0; - op.duration = -1; + // We don't support proxy long running ops (start/stop) + op.started(System.currentTimeMillis(), uidState.state, + AppOpsManager.OP_FLAG_SELF); + mHistoricalRegistry.incrementOpAccessedCount(opCode, uid, packageName, + uidState.state, AppOpsManager.OP_FLAG_SELF); + scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true); } op.startNesting++; @@ -2161,7 +2201,7 @@ public class AppOpsService extends IAppOpsService.Stub { } finally { Binder.restoreCallingIdentity(identity); } - Slog.wtf(TAG, "Operation not started: uid=" + op.uid + " pkg=" + Slog.wtf(TAG, "Operation not started: uid=" + op.uidState.uid + " pkg=" + op.packageName + " op=" + AppOpsManager.opToName(op.op)); return; } @@ -2272,16 +2312,24 @@ public class AppOpsService extends IAppOpsService.Stub { } void finishOperationLocked(Op op, boolean finishNested) { + final int opCode = op.op; + final int uid = op.uidState.uid; if (op.startNesting <= 1 || finishNested) { if (op.startNesting == 1 || finishNested) { - op.duration = (int)(SystemClock.elapsedRealtime() - op.startRealtime); - mHistoricalRegistry.increaseOpAccessDuration(op.op, op.uid, op.packageName, - op.uidState.state, op.duration); - op.time[op.uidState.state] = System.currentTimeMillis(); + // We don't support proxy long running ops (start/stop) + final long duration = SystemClock.elapsedRealtime() - op.startRealtime; + op.finished(System.currentTimeMillis(), duration, op.uidState.state, + AppOpsManager.OP_FLAG_SELF); + mHistoricalRegistry.increaseOpAccessDuration(opCode, uid, op.packageName, + op.uidState.state, AppOpsManager.OP_FLAG_SELF, duration); } else { - Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg " - + op.packageName + " code " + op.op + " time=" + op.time - + " duration=" + op.duration + " nesting=" + op.startNesting); + final OpEntry entry = new OpEntry(op.op, op.running, op.mode, op.mAccessTimes, + op.mRejectTimes, op.mDurations, op.mProxyUids, op.mProxyPackageNames); + Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + + op.packageName + " code " + opCode + " time=" + + entry.getLastAccessTime(OP_FLAGS_ALL) + + " duration=" + entry.getLastDuration(MAX_PRIORITY_UID_STATE, + MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL) + " nesting=" + op.startNesting); } if (op.startNesting >= 1) { op.uidState.startNesting -= op.startNesting; @@ -2311,7 +2359,7 @@ public class AppOpsService extends IAppOpsService.Stub { throw new IllegalArgumentException("Bad operation #" + op); } - private UidState getUidStateLocked(int uid, boolean edit) { + private @NonNull UidState getUidStateLocked(int uid, boolean edit) { UidState uidState = mUidStates.get(uid); if (uidState == null) { if (!edit) { @@ -2335,8 +2383,8 @@ public class AppOpsService extends IAppOpsService.Stub { } private void commitUidPendingStateLocked(UidState uidState) { - final boolean lastForeground = uidState.state <= UID_STATE_LAST_NON_RESTRICTED; - final boolean nowForeground = uidState.pendingState <= UID_STATE_LAST_NON_RESTRICTED; + final boolean lastForeground = uidState.state <= UID_STATE_MAX_LAST_NON_RESTRICTED; + final boolean nowForeground = uidState.pendingState <= UID_STATE_MAX_LAST_NON_RESTRICTED; uidState.state = uidState.pendingState; uidState.pendingStateCommitTime = 0; if (uidState.hasForegroundWatchers && lastForeground != nowForeground) { @@ -2345,7 +2393,15 @@ public class AppOpsService extends IAppOpsService.Stub { continue; } final int code = uidState.foregroundOps.keyAt(fgi); - + // For location ops we consider fg state only if the fg service + // is of location type, for all other ops any fg service will do. + final long resolvedLastRestrictedUidState = resolveFirstUnrestrictedUidState(code); + final boolean resolvedLastFg = uidState.state <= resolvedLastRestrictedUidState; + final boolean resolvedNowBg = uidState.pendingState + <= resolvedLastRestrictedUidState; + if (resolvedLastFg == resolvedNowBg) { + continue; + } final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code); if (callbacks != null) { for (int cbi = callbacks.size() - 1; cbi >= 0; cbi--) { @@ -2360,8 +2416,10 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidState.pkgOps != null) { for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) { final Op op = uidState.pkgOps.valueAt(pkgi).get(code); - if (doAllPackages || (op != null - && op.mode == AppOpsManager.MODE_FOREGROUND)) { + if (op == null) { + continue; + } + if (doAllPackages || op.mode == AppOpsManager.MODE_FOREGROUND) { mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChanged, this, callback, code, uidState.uid, @@ -2651,7 +2709,7 @@ public class AppOpsService extends IAppOpsService.Stub { final int idx = uidState.opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND); if (idx >= 0) { uidState.opModes.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, - uidState.opModes.valueAt(idx)); + uidState.opModes.valueAt(idx)); } } if (uidState.pkgOps == null) { @@ -2664,7 +2722,7 @@ public class AppOpsService extends IAppOpsService.Stub { final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND); if (op != null && op.mode != AppOpsManager.opToDefaultMode(op.op)) { final Op copy = new Op(op.uidState, op.packageName, - AppOpsManager.OP_RUN_ANY_IN_BACKGROUND); + AppOpsManager.OP_RUN_ANY_IN_BACKGROUND); copy.mode = op.mode; ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy); changed = true; @@ -2692,7 +2750,7 @@ public class AppOpsService extends IAppOpsService.Stub { scheduleFastWriteLocked(); } - void readUidOps(XmlPullParser parser) throws NumberFormatException, + private void readUidOps(XmlPullParser parser) throws NumberFormatException, XmlPullParserException, IOException { final int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); int outerDepth = parser.getDepth(); @@ -2720,8 +2778,8 @@ public class AppOpsService extends IAppOpsService.Stub { } } - void readPackage(XmlPullParser parser) throws NumberFormatException, - XmlPullParserException, IOException { + private void readPackage(XmlPullParser parser) + throws NumberFormatException, XmlPullParserException, IOException { String pkgName = parser.getAttributeValue(null, "n"); int outerDepth = parser.getDepth(); int type; @@ -2742,9 +2800,10 @@ public class AppOpsService extends IAppOpsService.Stub { } } - void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException, - XmlPullParserException, IOException { + private void readUid(XmlPullParser parser, String pkgName) + throws NumberFormatException, XmlPullParserException, IOException { int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); + final UidState uidState = getUidStateLocked(uid, true); String isPrivilegedString = parser.getAttributeValue(null, "p"); boolean isPrivileged = false; if (isPrivilegedString == null) { @@ -2774,113 +2833,73 @@ public class AppOpsService extends IAppOpsService.Stub { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } - String tagName = parser.getName(); if (tagName.equals("op")) { - UidState uidState = getUidStateLocked(uid, true); - if (uidState.pkgOps == null) { - uidState.pkgOps = new ArrayMap<>(); - } + readOp(parser, uidState, pkgName, isPrivileged); + } else { + Slog.w(TAG, "Unknown element under <pkg>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + uidState.evalForegroundOps(mOpModeWatchers); + } - Op op = new Op(uidState, pkgName, - Integer.parseInt(parser.getAttributeValue(null, "n"))); - - for (int i = parser.getAttributeCount()-1; i >= 0; i--) { - final String name = parser.getAttributeName(i); - final String value = parser.getAttributeValue(i); - switch (name) { - case "m": - op.mode = Integer.parseInt(value); - break; - case "d": - op.duration = Integer.parseInt(value); - break; - case "pu": - op.proxyUid = Integer.parseInt(value); - break; - case "pp": - op.proxyPackageName = value; - break; - case "tp": - op.time[AppOpsManager.UID_STATE_PERSISTENT] = Long.parseLong(value); - break; - case "tt": - op.time[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value); - break; - case "tfsl": - op.time[AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION] = - Long.parseLong(value); - break; - case "tfs": - op.time[AppOpsManager.UID_STATE_FOREGROUND_SERVICE] = - Long.parseLong(value); - break; - case "tf": - op.time[AppOpsManager.UID_STATE_FOREGROUND] = Long.parseLong(value); - break; - case "tb": - op.time[AppOpsManager.UID_STATE_BACKGROUND] = Long.parseLong(value); - break; - case "tc": - op.time[AppOpsManager.UID_STATE_CACHED] = Long.parseLong(value); - break; - case "rp": - op.rejectTime[AppOpsManager.UID_STATE_PERSISTENT] = - Long.parseLong(value); - break; - case "rt": - op.rejectTime[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value); - break; - case "rfsl": - op.rejectTime[AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION] = - Long.parseLong(value); - break; - case "rfs": - op.rejectTime[AppOpsManager.UID_STATE_FOREGROUND_SERVICE] = - Long.parseLong(value); - break; - case "rf": - op.rejectTime[AppOpsManager.UID_STATE_FOREGROUND] = - Long.parseLong(value); - break; - case "rb": - op.rejectTime[AppOpsManager.UID_STATE_BACKGROUND] = - Long.parseLong(value); - break; - case "rc": - op.rejectTime[AppOpsManager.UID_STATE_CACHED] = - Long.parseLong(value); - break; - case "t": - // Backwards compat. - op.time[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value); - break; - case "r": - // Backwards compat. - op.rejectTime[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value); - break; - default: - Slog.w(TAG, "Unknown attribute in 'op' tag: " + name); - break; - } - } + private void readOp(XmlPullParser parser, @NonNull UidState uidState, + @NonNull String pkgName, boolean isPrivileged) throws NumberFormatException, + XmlPullParserException, IOException { + Op op = new Op(uidState, pkgName, + Integer.parseInt(parser.getAttributeValue(null, "n"))); + + final int mode = XmlUtils.readIntAttribute(parser, "m", + AppOpsManager.opToDefaultMode(op.op)); + op.mode = mode; + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + String tagName = parser.getName(); + if (tagName.equals("st")) { + final long key = XmlUtils.readLongAttribute(parser, "n"); + + final int flags = AppOpsManager.extractFlagsFromKey(key); + final int state = AppOpsManager.extractUidStateFromKey(key); - Ops ops = uidState.pkgOps.get(pkgName); - if (ops == null) { - ops = new Ops(pkgName, uidState, isPrivileged); - uidState.pkgOps.put(pkgName, ops); + final long accessTime = XmlUtils.readLongAttribute(parser, "t", 0); + final long rejectTime = XmlUtils.readLongAttribute(parser, "r", 0); + final long accessDuration = XmlUtils.readLongAttribute(parser, "d", 0); + final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp"); + final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", 0); + + if (accessTime > 0) { + op.accessed(accessTime, proxyUid, proxyPkg, state, flags); + } + if (rejectTime > 0) { + op.rejected(rejectTime, proxyUid, proxyPkg, state, flags); + } + if (accessDuration > 0) { + op.running(accessTime, accessDuration, state, flags); } - ops.put(op.op, op); } else { - Slog.w(TAG, "Unknown element under <pkg>: " + Slog.w(TAG, "Unknown element under <op>: " + parser.getName()); XmlUtils.skipCurrentTag(parser); } } - UidState uidState = getUidStateLocked(uid, false); - if (uidState != null) { - uidState.evalForegroundOps(mOpModeWatchers); + + if (uidState.pkgOps == null) { + uidState.pkgOps = new ArrayMap<>(); + } + Ops ops = uidState.pkgOps.get(pkgName); + if (ops == null) { + ops = new Ops(pkgName, uidState, isPrivileged); + uidState.pkgOps.put(pkgName, ops); } + ops.put(op.op, op); } void writeState() { @@ -2955,30 +2974,53 @@ public class AppOpsService extends IAppOpsService.Stub { if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) { out.attribute(null, "m", Integer.toString(op.getMode())); } - for (int k = 0; k < _NUM_UID_STATE; k++) { - final long time = op.getLastTimeFor(k); - if (time != 0) { - out.attribute(null, UID_STATE_TIME_ATTRS[k], - Long.toString(time)); + + final LongSparseArray keys = op.collectKeys(); + if (keys == null || keys.size() <= 0) { + continue; + } + + final int keyCount = keys.size(); + for (int k = 0; k < keyCount; k++) { + final long key = keys.keyAt(k); + + final int uidState = AppOpsManager.extractUidStateFromKey(key); + final int flags = AppOpsManager.extractFlagsFromKey(key); + + final long accessTime = op.getLastAccessTime( + uidState, uidState, flags); + final long rejectTime = op.getLastRejectTime( + uidState, uidState, flags); + final long accessDuration = op.getLastDuration( + uidState, uidState, flags); + final String proxyPkg = op.getProxyPackageName(uidState, flags); + final int proxyUid = op.getProxyUid(uidState, flags); + + if (accessTime <= 0 && rejectTime <= 0 && accessDuration <= 0 + && proxyPkg == null && proxyUid < 0) { + continue; } - final long rejectTime = op.getLastRejectTimeFor(k); - if (rejectTime != 0) { - out.attribute(null, UID_STATE_REJECT_ATTRS[k], - Long.toString(rejectTime)); + + out.startTag(null, "st"); + out.attribute(null, "n", Long.toString(key)); + if (accessTime > 0) { + out.attribute(null, "t", Long.toString(accessTime)); } + if (rejectTime > 0) { + out.attribute(null, "r", Long.toString(rejectTime)); + } + if (accessDuration > 0) { + out.attribute(null, "d", Long.toString(accessDuration)); + } + if (proxyPkg != null) { + out.attribute(null, "pp", proxyPkg); + } + if (proxyUid >= 0) { + out.attribute(null, "pu", Integer.toString(proxyUid)); + } + out.endTag(null, "st"); } - int dur = op.getDuration(); - if (dur != 0) { - out.attribute(null, "d", Integer.toString(dur)); - } - int proxyUid = op.getProxyUid(); - if (proxyUid != -1) { - out.attribute(null, "pu", Integer.toString(proxyUid)); - } - String proxyPackageName = op.getProxyPackageName(); - if (proxyPackageName != null) { - out.attribute(null, "pp", proxyPackageName); - } + out.endTag(null, "op"); } out.endTag(null, "uid"); @@ -3462,32 +3504,80 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" Limit output to data associated with the given package name."); pw.println(" --watchers"); pw.println(" Only output the watcher sections."); + pw.println(" --history"); + pw.println(" Output the historical data."); } - private void dumpTimesLocked(PrintWriter pw, String firstPrefix, String prefix, long[] times, - long now, SimpleDateFormat sdf, Date date) { - boolean hasTime = false; - for (int i = 0; i < _NUM_UID_STATE; i++) { - if (times[i] != 0) { - hasTime = true; - break; - } - } - if (!hasTime) { + private void dumpStatesLocked(@NonNull PrintWriter pw, @NonNull Op op, + long now, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) { + + final OpEntry entry = new OpEntry(op.op, op.running, op.mode, op.mAccessTimes, + op.mRejectTimes, op.mDurations, op.mProxyUids, op.mProxyPackageNames); + + final LongSparseArray keys = entry.collectKeys(); + if (keys == null || keys.size() <= 0) { return; } - boolean first = true; - for (int i = 0; i < _NUM_UID_STATE; i++) { - if (times[i] != 0) { - pw.print(first ? firstPrefix : prefix); - first = false; - pw.print(UID_STATE_NAMES[i]); - pw.print(" = "); - date.setTime(times[i]); + + final int keyCount = keys.size(); + for (int k = 0; k < keyCount; k++) { + final long key = keys.keyAt(k); + + final int uidState = AppOpsManager.extractUidStateFromKey(key); + final int flags = AppOpsManager.extractFlagsFromKey(key); + + final long accessTime = entry.getLastAccessTime( + uidState, uidState, flags); + final long rejectTime = entry.getLastRejectTime( + uidState, uidState, flags); + final long accessDuration = entry.getLastDuration( + uidState, uidState, flags); + final String proxyPkg = entry.getProxyPackageName(uidState, flags); + final int proxyUid = entry.getProxyUid(uidState, flags); + + if (accessTime > 0) { + pw.print(prefix); + pw.print("Access: "); + pw.print(AppOpsManager.keyToString(key)); + pw.print(" "); + date.setTime(accessTime); pw.print(sdf.format(date)); pw.print(" ("); - TimeUtils.formatDuration(times[i]-now, pw); - pw.println(")"); + TimeUtils.formatDuration(accessTime - now, pw); + pw.print(")"); + if (accessDuration > 0) { + pw.print(" duration="); + TimeUtils.formatDuration(accessDuration, pw); + } + if (proxyUid >= 0) { + pw.print(" proxy["); + pw.print("uid="); + pw.print(proxyUid); + pw.print(", pkg="); + pw.print(proxyPkg); + pw.print("]"); + } + pw.println(); + } + + if (rejectTime > 0) { + pw.print(prefix); + pw.print("Reject: "); + pw.print(AppOpsManager.keyToString(key)); + date.setTime(rejectTime); + pw.print(sdf.format(date)); + pw.print(" ("); + TimeUtils.formatDuration(rejectTime - now, pw); + pw.print(")"); + if (proxyUid >= 0) { + pw.print(" proxy["); + pw.print("uid="); + pw.print(proxyUid); + pw.print(", pkg="); + pw.print(proxyPkg); + pw.print("]"); + } + pw.println(); } } } @@ -3501,6 +3591,7 @@ public class AppOpsService extends IAppOpsService.Stub { int dumpUid = Process.INVALID_UID; int dumpMode = -1; boolean dumpWatchers = false; + boolean dumpHistory = false; if (args != null) { for (int i=0; i<args.length; i++) { @@ -3550,6 +3641,8 @@ public class AppOpsService extends IAppOpsService.Stub { } } else if ("--watchers".equals(arg)) { dumpWatchers = true; + } else if ("--history".equals(arg)) { + dumpHistory = true; } else if (arg.length() > 0 && arg.charAt(0) == '-'){ pw.println("Unknown option: " + arg); return; @@ -3562,7 +3655,9 @@ public class AppOpsService extends IAppOpsService.Stub { synchronized (this) { pw.println("Current AppOps Service state:"); - mConstants.dump(pw); + if (!dumpHistory && !dumpWatchers) { + mConstants.dump(pw); + } pw.println(); final long now = System.currentTimeMillis(); final long nowElapsed = SystemClock.elapsedRealtime(); @@ -3571,7 +3666,7 @@ public class AppOpsService extends IAppOpsService.Stub { final Date date = new Date(); boolean needSep = false; if (dumpOp < 0 && dumpMode < 0 && dumpPackage == null && mProfileOwners != null - && !dumpWatchers) { + && !dumpWatchers && !dumpHistory) { pw.println(" Profile owners:"); for (int poi = 0; poi < mProfileOwners.size(); poi++) { pw.print(" User #"); @@ -3582,7 +3677,7 @@ public class AppOpsService extends IAppOpsService.Stub { } pw.println(); } - if (mOpModeWatchers.size() > 0) { + if (mOpModeWatchers.size() > 0 && !dumpHistory) { boolean printedHeader = false; for (int i=0; i<mOpModeWatchers.size(); i++) { if (dumpOp >= 0 && dumpOp != mOpModeWatchers.keyAt(i)) { @@ -3612,7 +3707,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } } - if (mPackageModeWatchers.size() > 0 && dumpOp < 0) { + if (mPackageModeWatchers.size() > 0 && dumpOp < 0 && !dumpHistory) { boolean printedHeader = false; for (int i=0; i<mPackageModeWatchers.size(); i++) { if (dumpPackage != null && !dumpPackage.equals(mPackageModeWatchers.keyAt(i))) { @@ -3632,7 +3727,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } } - if (mModeWatchers.size() > 0 && dumpOp < 0) { + if (mModeWatchers.size() > 0 && dumpOp < 0 && !dumpHistory) { boolean printedHeader = false; for (int i=0; i<mModeWatchers.size(); i++) { final ModeCallback cb = mModeWatchers.valueAt(i); @@ -3730,7 +3825,7 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(cb); } } - if (mClients.size() > 0 && dumpMode < 0 && !dumpWatchers) { + if (mClients.size() > 0 && dumpMode < 0 && !dumpWatchers && !dumpHistory) { needSep = true; boolean printedHeader = false; for (int i=0; i<mClients.size(); i++) { @@ -3759,7 +3854,7 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" Started ops:"); printedStarted = true; } - pw.print(" "); pw.print("uid="); pw.print(op.uid); + pw.print(" "); pw.print("uid="); pw.print(op.uidState.uid); pw.print(" pkg="); pw.print(op.packageName); pw.print(" op="); pw.println(AppOpsManager.opToName(op.op)); } @@ -3767,7 +3862,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } if (mAudioRestrictions.size() > 0 && dumpOp < 0 && dumpPackage != null - && dumpMode < 0 && !dumpWatchers) { + && dumpMode < 0 && !dumpWatchers && !dumpWatchers) { boolean printedHeader = false; for (int o=0; o<mAudioRestrictions.size(); o++) { final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o)); @@ -3800,7 +3895,7 @@ public class AppOpsService extends IAppOpsService.Stub { final SparseIntArray opModes = uidState.opModes; final ArrayMap<String, Ops> pkgOps = uidState.pkgOps; - if (dumpWatchers) { + if (dumpWatchers || dumpHistory) { continue; } if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) { @@ -3847,10 +3942,10 @@ public class AppOpsService extends IAppOpsService.Stub { pw.print(" Uid "); UserHandle.formatUid(pw, uidState.uid); pw.println(":"); pw.print(" state="); - pw.println(UID_STATE_NAMES[uidState.state]); + pw.println(AppOpsManager.getUidStateName(uidState.state)); if (uidState.state != uidState.pendingState) { pw.print(" pendingState="); - pw.println(UID_STATE_NAMES[uidState.pendingState]); + pw.println(AppOpsManager.getUidStateName(uidState.pendingState)); } if (uidState.pendingStateCommitTime != 0) { pw.print(" pendingStateCommitTime="); @@ -3906,7 +4001,8 @@ public class AppOpsService extends IAppOpsService.Stub { boolean printedPackage = false; for (int j=0; j<ops.size(); j++) { final Op op = ops.valueAt(j); - if (dumpOp >= 0 && dumpOp != op.op) { + final int opCode = op.op; + if (dumpOp >= 0 && dumpOp != opCode) { continue; } if (dumpMode >= 0 && dumpMode != op.mode) { @@ -3916,32 +4012,23 @@ public class AppOpsService extends IAppOpsService.Stub { pw.print(" Package "); pw.print(ops.packageName); pw.println(":"); printedPackage = true; } - pw.print(" "); pw.print(AppOpsManager.opToName(op.op)); + pw.print(" "); pw.print(AppOpsManager.opToName(opCode)); pw.print(" ("); pw.print(AppOpsManager.modeToName(op.mode)); - final int switchOp = AppOpsManager.opToSwitch(op.op); - if (switchOp != op.op) { + final int switchOp = AppOpsManager.opToSwitch(opCode); + if (switchOp != opCode) { pw.print(" / switch "); pw.print(AppOpsManager.opToName(switchOp)); final Op switchObj = ops.get(switchOp); - int mode = switchObj != null - ? switchObj.mode : AppOpsManager.opToDefaultMode(switchOp); + int mode = switchObj != null ? switchObj.mode + : AppOpsManager.opToDefaultMode(switchOp); pw.print("="); pw.print(AppOpsManager.modeToName(mode)); } pw.println("): "); - dumpTimesLocked(pw, - " Access: ", - " ", op.time, now, sdf, date); - dumpTimesLocked(pw, - " Reject: ", - " ", op.rejectTime, now, sdf, date); - if (op.duration == -1) { + dumpStatesLocked(pw, op, now, sdf, date, " "); + if (op.running) { pw.print(" Running start at: "); TimeUtils.formatDuration(nowElapsed-op.startRealtime, pw); pw.println(); - } else if (op.duration != 0) { - pw.print(" duration="); - TimeUtils.formatDuration(op.duration, pw); - pw.println(); } if (op.startNesting != 0) { pw.print(" startNesting="); @@ -3960,7 +4047,7 @@ public class AppOpsService extends IAppOpsService.Stub { ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); boolean printedTokenHeader = false; - if (dumpMode >= 0 || dumpWatchers) { + if (dumpMode >= 0 || dumpWatchers || dumpHistory) { continue; } @@ -4044,7 +4131,9 @@ public class AppOpsService extends IAppOpsService.Stub { } // Must not hold the appops lock - mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpOp); + if (dumpHistory && !dumpWatchers) { + mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpOp); + } } private static final class Restriction { @@ -4158,7 +4247,7 @@ public class AppOpsService extends IAppOpsService.Stub { final ClientState client = mClients.valueAt(i); for (int j = client.mStartedOps.size() - 1; j >= 0; j--) { final Op op = client.mStartedOps.get(j); - if (op.op == code && op.uid == uid) return true; + if (op.op == code && op.uidState.uid == uid) return true; } } } diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 4485a54b5cdf..280bc026a76e 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -23,6 +23,7 @@ import android.app.AppOpsManager.HistoricalOp; import android.app.AppOpsManager.HistoricalOps; import android.app.AppOpsManager.HistoricalPackageOps; import android.app.AppOpsManager.HistoricalUidOps; +import android.app.AppOpsManager.OpFlags; import android.app.AppOpsManager.UidState; import android.content.ContentResolver; import android.database.ContentObserver; @@ -37,6 +38,7 @@ import android.os.RemoteCallback; import android.os.UserHandle; import android.provider.Settings; import android.util.ArraySet; +import android.util.LongSparseArray; import android.util.Slog; import android.util.TimeUtils; import android.util.Xml; @@ -297,20 +299,20 @@ final class HistoricalRegistry { } } - @Nullable void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, + void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, - @NonNull RemoteCallback callback) { + @OpFlags int flags, @NonNull RemoteCallback callback) { final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis); mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames, - beginTimeMillis, endTimeMillis); + beginTimeMillis, endTimeMillis, flags); final Bundle payload = new Bundle(); payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result); callback.sendResult(payload); } - @Nullable void getHistoricalOps(int uid, @NonNull String packageName, + void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, - @NonNull RemoteCallback callback) { + @OpFlags int flags, @NonNull RemoteCallback callback) { final long currentTimeMillis = System.currentTimeMillis(); if (endTimeMillis == Long.MAX_VALUE) { endTimeMillis = currentTimeMillis; @@ -326,6 +328,8 @@ final class HistoricalRegistry { synchronized (mOnDiskLock) { final List<HistoricalOps> pendingWrites; final HistoricalOps currentOps; + boolean collectOpsFromDisk; + synchronized (mInMemoryLock) { currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis); if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis() @@ -338,10 +342,11 @@ final class HistoricalRegistry { } pendingWrites = new ArrayList<>(mPendingWrites); mPendingWrites.clear(); + collectOpsFromDisk = inMemoryAdjEndTimeMillis > currentOps.getEndTimeMillis(); } // If the query was only for in-memory state - done. - if (inMemoryAdjEndTimeMillis > currentOps.getEndTimeMillis()) { + if (collectOpsFromDisk) { // If there is a write in flight we need to force it now persistPendingHistory(pendingWrites); // Collect persisted state. @@ -352,7 +357,7 @@ final class HistoricalRegistry { final long onDiskAdjEndTimeMillis = Math.max(inMemoryAdjEndTimeMillis - onDiskAndInMemoryOffsetMillis, 0); mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames, - onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis); + onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis, flags); } // Rebase the result time to be since epoch. @@ -366,32 +371,31 @@ final class HistoricalRegistry { } void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, - @UidState int uidState) { + @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) - .increaseAccessCount(op, uid, packageName, uidState, 1); - + .increaseAccessCount(op, uid, packageName, uidState, flags, 1); } } } void incrementOpRejected(int op, int uid, @NonNull String packageName, - @UidState int uidState) { + @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) - .increaseRejectCount(op, uid, packageName, uidState, 1); + .increaseRejectCount(op, uid, packageName, uidState, flags, 1); } } } void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, - @UidState int uidState, long increment) { + @UidState int uidState, @OpFlags int flags, long increment) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) - .increaseAccessDuration(op, uid, packageName, uidState, increment); + .increaseAccessDuration(op, uid, packageName, uidState, flags, increment); } } } @@ -593,20 +597,20 @@ final class HistoricalRegistry { private static final String TAG_HISTORY = "history"; private static final String TAG_OPS = "ops"; private static final String TAG_UID = "uid"; - private static final String TAG_PACKAGE = "package"; + private static final String TAG_PACKAGE = "pkg"; private static final String TAG_OP = "op"; - private static final String TAG_STATE = "state"; + private static final String TAG_STATE = "st"; - private static final String ATTR_VERSION = "version"; - private static final String ATTR_NAME = "name"; - private static final String ATTR_ACCESS_COUNT = "accessCount"; - private static final String ATTR_REJECT_COUNT = "rejectCount"; - private static final String ATTR_ACCESS_DURATION = "accessDuration"; - private static final String ATTR_BEGIN_TIME = "beginTime"; - private static final String ATTR_END_TIME = "endTime"; - private static final String ATTR_OVERFLOW = "overflow"; + private static final String ATTR_VERSION = "ver"; + private static final String ATTR_NAME = "na"; + private static final String ATTR_ACCESS_COUNT = "ac"; + private static final String ATTR_REJECT_COUNT = "rc"; + private static final String ATTR_ACCESS_DURATION = "du"; + private static final String ATTR_BEGIN_TIME = "beg"; + private static final String ATTR_END_TIME = "end"; + private static final String ATTR_OVERFLOW = "ov"; - private static final int CURRENT_VERSION = 1; + private static final int CURRENT_VERSION = 2; private final long mBaseSnapshotInterval; private final long mIntervalCompressionMultiplier; @@ -657,7 +661,8 @@ final class HistoricalRegistry { @Nullable List<HistoricalOps> readHistoryRawDLocked() { return collectHistoricalOpsBaseDLocked(Process.INVALID_UID /*filterUid*/, null /*filterPackageName*/, null /*filterOpNames*/, - 0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/); + 0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/, + AppOpsManager.OP_FLAGS_ALL); } @Nullable List<HistoricalOps> readHistoryDLocked() { @@ -697,9 +702,10 @@ final class HistoricalRegistry { private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps, int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames, - long filterBeingMillis, long filterEndMillis) { + long filterBeingMillis, long filterEndMillis, @OpFlags int filterFlags) { final List<HistoricalOps> readOps = collectHistoricalOpsBaseDLocked(filterUid, - filterPackageName, filterOpNames, filterBeingMillis, filterEndMillis); + filterPackageName, filterOpNames, filterBeingMillis, filterEndMillis, + filterFlags); if (readOps != null) { final int readCount = readOps.size(); for (int i = 0; i < readCount; i++) { @@ -711,7 +717,7 @@ final class HistoricalRegistry { private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked( int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames, - long filterBeginTimeMillis, long filterEndTimeMillis) { + long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) { File baseDir = null; try { baseDir = mHistoricalAppOpsDir.startRead(); @@ -724,8 +730,8 @@ final class HistoricalRegistry { final long[] globalContentOffsetMillis = {0}; final LinkedList<HistoricalOps> ops = collectHistoricalOpsRecursiveDLocked( baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis, - filterEndTimeMillis, globalContentOffsetMillis, null /*outOps*/, - 0 /*depth*/, historyFiles); + filterEndTimeMillis, filterFlags, globalContentOffsetMillis, + null /*outOps*/, 0 /*depth*/, historyFiles); if (DEBUG) { filesInvariant.stopTracking(baseDir); } @@ -741,7 +747,8 @@ final class HistoricalRegistry { private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsRecursiveDLocked( @NonNull File baseDir, int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames, long filterBeginTimeMillis, - long filterEndTimeMillis, @NonNull long[] globalContentOffsetMillis, + long filterEndTimeMillis, @OpFlags int filterFlags, + @NonNull long[] globalContentOffsetMillis, @Nullable LinkedList<HistoricalOps> outOps, int depth, @NonNull Set<String> historyFiles) throws IOException, XmlPullParserException { @@ -757,7 +764,7 @@ final class HistoricalRegistry { final List<HistoricalOps> readOps = readHistoricalOpsLocked(baseDir, previousIntervalEndMillis, currentIntervalEndMillis, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis, filterEndTimeMillis, - globalContentOffsetMillis, depth, historyFiles); + filterFlags, globalContentOffsetMillis, depth, historyFiles); // Empty is a special signal to stop diving if (readOps != null && readOps.isEmpty()) { @@ -767,7 +774,7 @@ final class HistoricalRegistry { // Collect older historical data from subsequent levels outOps = collectHistoricalOpsRecursiveDLocked( baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis, - filterEndTimeMillis, globalContentOffsetMillis, outOps, depth + 1, + filterEndTimeMillis, filterFlags, globalContentOffsetMillis, outOps, depth + 1, historyFiles); // Make older historical data relative to the current historical level @@ -836,22 +843,24 @@ final class HistoricalRegistry { previousIntervalEndMillis, currentIntervalEndMillis, Process.INVALID_UID /*filterUid*/, null /*filterPackageName*/, null /*filterOpNames*/, Long.MIN_VALUE /*filterBeginTimeMillis*/, - Long.MAX_VALUE /*filterEndTimeMillis*/, null, depth, - null /*historyFiles*/); + Long.MAX_VALUE /*filterEndTimeMillis*/, AppOpsManager.OP_FLAGS_ALL, + null, depth, null /*historyFiles*/); if (DEBUG) { enforceOpsWellFormed(existingOps); } // Offset existing ops to account for elapsed time - final int existingOpCount = existingOps.size(); - if (existingOpCount > 0) { - // Compute elapsed time - final long elapsedTimeMillis = passedOps.get(passedOps.size() - 1) + if (existingOps != null) { + final int existingOpCount = existingOps.size(); + if (existingOpCount > 0) { + // Compute elapsed time + final long elapsedTimeMillis = passedOps.get(passedOps.size() - 1) .getEndTimeMillis(); - for (int i = 0; i < existingOpCount; i++) { - final HistoricalOps existingOp = existingOps.get(i); - existingOp.offsetBeginAndEndTime(elapsedTimeMillis); + for (int i = 0; i < existingOpCount; i++) { + final HistoricalOps existingOp = existingOps.get(i); + existingOp.offsetBeginAndEndTime(elapsedTimeMillis); + } } } @@ -864,9 +873,10 @@ final class HistoricalRegistry { // Consolidate passed ops at the current slot duration ensuring each snapshot is // full. To achieve this we put all passed and existing ops in a list and will // merge them to ensure each represents a snapshot at the current granularity. - final List<HistoricalOps> allOps = new LinkedList<>(); - allOps.addAll(passedOps); - allOps.addAll(existingOps); + final List<HistoricalOps> allOps = new LinkedList<>(passedOps); + if (existingOps != null) { + allOps.addAll(existingOps); + } if (DEBUG) { enforceOpsWellFormed(allOps); @@ -944,10 +954,10 @@ final class HistoricalRegistry { overflowedOps, oldFileNames, depth + 1); } - private @NonNull List<HistoricalOps> readHistoricalOpsLocked(File baseDir, - long intervalBeginMillis, long intervalEndMillis, int filterUid, - @Nullable String filterPackageName, @Nullable String[] filterOpNames, - long filterBeginTimeMillis, long filterEndTimeMillis, + private @Nullable List<HistoricalOps> readHistoricalOpsLocked(File baseDir, + long intervalBeginMillis, long intervalEndMillis, + int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames, + long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags, @Nullable long[] cumulativeOverflowMillis, int depth, @NonNull Set<String> historyFiles) throws IOException, XmlPullParserException { @@ -973,12 +983,13 @@ final class HistoricalRegistry { } } return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterOpNames, - filterBeginTimeMillis, filterEndTimeMillis, cumulativeOverflowMillis); + filterBeginTimeMillis, filterEndTimeMillis, filterFlags, + cumulativeOverflowMillis); } private @Nullable List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file, int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames, - long filterBeginTimeMillis, long filterEndTimeMillis, + long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags, @Nullable long[] cumulativeOverflowMillis) throws IOException, XmlPullParserException { if (DEBUG) { @@ -989,13 +1000,22 @@ final class HistoricalRegistry { final XmlPullParser parser = Xml.newPullParser(); parser.setInput(stream, StandardCharsets.UTF_8.name()); XmlUtils.beginDocument(parser, TAG_HISTORY); + + // We haven't released version 1 and have more detailed + // accounting - just nuke the current state + final int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION); + if (CURRENT_VERSION == 2 && version < CURRENT_VERSION) { + throw new IllegalStateException("Dropping unsupported history " + + "version 1 for file:" + file); + } + final long overflowMillis = XmlUtils.readLongAttribute(parser, ATTR_OVERFLOW, 0); final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_OPS.equals(parser.getName())) { final HistoricalOps ops = readeHistoricalOpsDLocked(parser, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis, - filterEndTimeMillis, cumulativeOverflowMillis); + filterEndTimeMillis, filterFlags, cumulativeOverflowMillis); if (ops == null) { continue; } @@ -1029,7 +1049,8 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readeHistoricalOpsDLocked( @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames, long filterBeginTimeMillis, - long filterEndTimeMillis, @Nullable long[] cumulativeOverflowMillis) + long filterEndTimeMillis, @OpFlags int filterFlags, + @Nullable long[] cumulativeOverflowMillis) throws IOException, XmlPullParserException { final long beginTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_BEGIN_TIME, 0) + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0); @@ -1045,6 +1066,10 @@ final class HistoricalRegistry { } final long filteredBeginTimeMillis = Math.max(beginTimeMillis, filterBeginTimeMillis); final long filteredEndTimeMillis = Math.min(endTimeMillis, filterEndTimeMillis); + // // Keep reading as subsequent records may start matching + // if (filteredEndTimeMillis - filterBeginTimeMillis <= 0) { + // return null; + // } final double filterScale = (double) (filteredEndTimeMillis - filteredBeginTimeMillis) / (double) (endTimeMillis - beginTimeMillis); HistoricalOps ops = null; @@ -1052,7 +1077,7 @@ final class HistoricalRegistry { while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_UID.equals(parser.getName())) { final HistoricalOps returnedOps = readHistoricalUidOpsDLocked(ops, parser, - filterUid, filterPackageName, filterOpNames, filterScale); + filterUid, filterPackageName, filterOpNames, filterFlags, filterScale); if (ops == null) { ops = returnedOps; } @@ -1067,7 +1092,8 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readHistoricalUidOpsDLocked( @Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames, - double filterScale) throws IOException, XmlPullParserException { + @OpFlags int filterFlags, double filterScale) + throws IOException, XmlPullParserException { final int uid = XmlUtils.readIntAttribute(parser, ATTR_NAME); if (filterUid != Process.INVALID_UID && filterUid != uid) { XmlUtils.skipCurrentTag(parser); @@ -1077,7 +1103,8 @@ final class HistoricalRegistry { while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_PACKAGE.equals(parser.getName())) { final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops, - uid, parser, filterPackageName, filterOpNames, filterScale); + uid, parser, filterPackageName, filterOpNames, filterFlags, + filterScale); if (ops == null) { ops = returnedOps; } @@ -1089,7 +1116,8 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readHistoricalPackageOpsDLocked( @Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser, @Nullable String filterPackageName, @Nullable String[] filterOpNames, - double filterScale) throws IOException, XmlPullParserException { + @OpFlags int filterFlags, double filterScale) + throws IOException, XmlPullParserException { final String packageName = XmlUtils.readStringAttribute(parser, ATTR_NAME); if (filterPackageName != null && !filterPackageName.equals(packageName)) { XmlUtils.skipCurrentTag(parser); @@ -1099,7 +1127,7 @@ final class HistoricalRegistry { while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_OP.equals(parser.getName())) { final HistoricalOps returnedOps = readHistoricalOpDLocked(ops, uid, - packageName, parser, filterOpNames, filterScale); + packageName, parser, filterOpNames, filterFlags, filterScale); if (ops == null) { ops = returnedOps; } @@ -1110,7 +1138,7 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops, int uid, String packageName, @NonNull XmlPullParser parser, - @Nullable String[] filterOpNames, double filterScale) + @Nullable String[] filterOpNames, @OpFlags int filterFlags, double filterScale) throws IOException, XmlPullParserException { final int op = XmlUtils.readIntAttribute(parser, ATTR_NAME); if (filterOpNames != null && !ArrayUtils.contains(filterOpNames, @@ -1121,8 +1149,8 @@ final class HistoricalRegistry { final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_STATE.equals(parser.getName())) { - final HistoricalOps returnedOps = readUidStateDLocked(ops, uid, - packageName, op, parser, filterScale); + final HistoricalOps returnedOps = readStateDLocked(ops, uid, + packageName, op, parser, filterFlags, filterScale); if (ops == null) { ops = returnedOps; } @@ -1131,10 +1159,15 @@ final class HistoricalRegistry { return ops; } - private @Nullable HistoricalOps readUidStateDLocked(@Nullable HistoricalOps ops, + private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops, int uid, String packageName, int op, @NonNull XmlPullParser parser, - double filterScale) throws IOException { - final int uidState = XmlUtils.readIntAttribute(parser, ATTR_NAME); + @OpFlags int filterFlags, double filterScale) throws IOException { + final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME); + final int flags = AppOpsManager.extractFlagsFromKey(key) & filterFlags; + if (flags == 0) { + return null; + } + final int uidState = AppOpsManager.extractUidStateFromKey(key); long accessCount = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_COUNT, 0); if (accessCount > 0) { if (!Double.isNaN(filterScale)) { @@ -1144,7 +1177,7 @@ final class HistoricalRegistry { if (ops == null) { ops = new HistoricalOps(0, 0); } - ops.increaseAccessCount(op, uid, packageName, uidState, accessCount); + ops.increaseAccessCount(op, uid, packageName, uidState, flags, accessCount); } long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0); if (rejectCount > 0) { @@ -1155,7 +1188,7 @@ final class HistoricalRegistry { if (ops == null) { ops = new HistoricalOps(0, 0); } - ops.increaseRejectCount(op, uid, packageName, uidState, rejectCount); + ops.increaseRejectCount(op, uid, packageName, uidState, flags, rejectCount); } long accessDuration = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0); if (accessDuration > 0) { @@ -1166,7 +1199,7 @@ final class HistoricalRegistry { if (ops == null) { ops = new HistoricalOps(0, 0); } - ops.increaseAccessDuration(op, uid, packageName, uidState, accessDuration); + ops.increaseAccessDuration(op, uid, packageName, uidState, flags, accessDuration); } return ops; } @@ -1241,24 +1274,34 @@ final class HistoricalRegistry { private void writeHistoricalOpDLocked(@NonNull HistoricalOp op, @NonNull XmlSerializer serializer) throws IOException { + final LongSparseArray keys = op.collectKeys(); + if (keys == null || keys.size() <= 0) { + return; + } serializer.startTag(null, TAG_OP); serializer.attribute(null, ATTR_NAME, Integer.toString(op.getOpCode())); - for (int uidState = 0; uidState < AppOpsManager._NUM_UID_STATE; uidState++) { - writeUidStateOnLocked(op, uidState, serializer); + final int keyCount = keys.size(); + for (int i = 0; i < keyCount; i++) { + writeStateOnLocked(op, keys.keyAt(i), serializer); } serializer.endTag(null, TAG_OP); } - private void writeUidStateOnLocked(@NonNull HistoricalOp op, @UidState int uidState, + private void writeStateOnLocked(@NonNull HistoricalOp op, long key, @NonNull XmlSerializer serializer) throws IOException { - final long accessCount = op.getAccessCount(uidState); - final long rejectCount = op.getRejectCount(uidState); - final long accessDuration = op.getAccessDuration(uidState); - if (accessCount == 0 && rejectCount == 0 && accessDuration == 0) { + final int uidState = AppOpsManager.extractUidStateFromKey(key); + final int flags = AppOpsManager.extractFlagsFromKey(key); + + final long accessCount = op.getAccessCount(uidState, uidState, flags); + final long rejectCount = op.getRejectCount(uidState, uidState, flags); + final long accessDuration = op.getAccessDuration(uidState, uidState, flags); + + if (accessCount <= 0 && rejectCount <= 0 && accessDuration <= 0) { return; } + serializer.startTag(null, TAG_STATE); - serializer.attribute(null, ATTR_NAME, Integer.toString(uidState)); + serializer.attribute(null, ATTR_NAME, Long.toString(key)); if (accessCount > 0) { serializer.attribute(null, ATTR_ACCESS_COUNT, Long.toString(accessCount)); } @@ -1532,24 +1575,29 @@ final class HistoricalRegistry { mWriter.print(mEntryPrefix); mWriter.print(AppOpsManager.opToName(ops.getOpCode())); mWriter.println(":"); - for (int uidState = 0; uidState < AppOpsManager._NUM_UID_STATE; uidState++) { + final LongSparseArray keys = ops.collectKeys(); + final int keyCount = keys.size(); + for (int i = 0; i < keyCount; i++) { + final long key = keys.keyAt(i); + final int uidState = AppOpsManager.extractUidStateFromKey(key); + final int flags = AppOpsManager.extractFlagsFromKey(key); boolean printedUidState = false; - final long accessCount = ops.getAccessCount(uidState); + final long accessCount = ops.getAccessCount(uidState, uidState, flags); if (accessCount > 0) { if (!printedUidState) { mWriter.print(mUidStatePrefix); - mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]); + mWriter.print(AppOpsManager.keyToString(key)); mWriter.print(" = "); printedUidState = true; } mWriter.print("access="); mWriter.print(accessCount); } - final long rejectCount = ops.getRejectCount(uidState); + final long rejectCount = ops.getRejectCount(uidState, uidState, flags); if (rejectCount > 0) { if (!printedUidState) { mWriter.print(mUidStatePrefix); - mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]); + mWriter.print(AppOpsManager.keyToString(key)); mWriter.print(" = "); printedUidState = true; } else { @@ -1558,11 +1606,11 @@ final class HistoricalRegistry { mWriter.print("reject="); mWriter.print(rejectCount); } - final long accessDuration = ops.getAccessDuration(uidState); + final long accessDuration = ops.getAccessDuration(uidState, uidState, flags); if (accessDuration > 0) { if (!printedUidState) { mWriter.print(mUidStatePrefix); - mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]); + mWriter.print(AppOpsManager.keyToString(key)); mWriter.print(" = "); printedUidState = true; } else { diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING new file mode 100644 index 000000000000..f2e2782e8eea --- /dev/null +++ b/services/core/java/com/android/server/appop/TEST_MAPPING @@ -0,0 +1,21 @@ +{ + "presubmit": [ + { + "name": "CtsAppOpsTestCases" + }, + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.appop.AppOpsUpgradeTest" + }, + { + "include-filter": "com.android.server.appop.AppOpsServiceTest" + }, + { + "include-filter": "com.android.server.appop.AppOpsActiveWatcherTest" + } + ] + } + ] +} diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java index c8e67820ce46..d0158e0c819f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java @@ -685,10 +685,10 @@ public class AppStateTrackerTest { List<OpEntry> entries = new ArrayList<>(); entries.add(new OpEntry( AppOpsManager.OP_ACCESS_NOTIFICATIONS, - AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null)); + AppOpsManager.MODE_IGNORED)); entries.add(new OpEntry( AppStateTracker.TARGET_OP, - AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null)); + AppOpsManager.MODE_IGNORED)); ops.add(new PackageOps(PACKAGE_1, UID_1, entries)); @@ -696,7 +696,7 @@ public class AppStateTrackerTest { entries = new ArrayList<>(); entries.add(new OpEntry( AppStateTracker.TARGET_OP, - AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null)); + AppOpsManager.MODE_IGNORED)); ops.add(new PackageOps(PACKAGE_2, UID_2, entries)); @@ -704,7 +704,7 @@ public class AppStateTrackerTest { entries = new ArrayList<>(); entries.add(new OpEntry( AppStateTracker.TARGET_OP, - AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null)); + AppOpsManager.MODE_ALLOWED)); ops.add(new PackageOps(PACKAGE_1, UID_10_1, entries)); @@ -712,10 +712,10 @@ public class AppStateTrackerTest { entries = new ArrayList<>(); entries.add(new OpEntry( AppStateTracker.TARGET_OP, - AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null)); + AppOpsManager.MODE_IGNORED)); entries.add(new OpEntry( AppOpsManager.OP_ACCESS_NOTIFICATIONS, - AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null)); + AppOpsManager.MODE_IGNORED)); ops.add(new PackageOps(PACKAGE_3, UID_10_3, entries)); diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java index edd89f9e61d1..96f329b9161e 100644 --- a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java @@ -45,21 +45,6 @@ public class AppOpsNotedWatcherTest { private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000; - public void testWatchNotedOpsRequiresPermission() { - // Create a mock listener - final OnOpNotedListener listener = mock(OnOpNotedListener.class); - - // Try to start watching noted ops - final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); - try { - appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION, - AppOpsManager.OP_RECORD_AUDIO}, listener); - fail("Watching noted ops shoudl require " + Manifest.permission.WATCH_APPOPS); - } catch (SecurityException expected) { - /*ignored*/ - } - } - @Test public void testWatchNotedOps() { // Create a mock listener diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java index eb0c6279ea99..66d2baba2909 100644 --- a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java +++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java @@ -107,8 +107,8 @@ public class AppOpsUpgradeTest { } final AppOpsService.Op _op1 = ops.get(op1); final AppOpsService.Op _op2 = ops.get(op2); - final int mode1 = (_op1 == null) ? defaultModeOp1 : _op1.mode; - final int mode2 = (_op2 == null) ? defaultModeOp2 : _op2.mode; + final int mode1 = (_op1 == null) ? defaultModeOp1 : _op1.getMode(); + final int mode2 = (_op2 == null) ? defaultModeOp2 : _op2.getMode(); assertEquals(mode1, mode2); if (mode1 != defaultModeOp1) { numberOfNonDefaultOps++; |