Merge "Add request object for querying historical ops"

This commit is contained in:
Svetoslav Ganov
2019-02-18 15:48:19 +00:00
committed by Android (Google) Code Review
5 changed files with 173 additions and 76 deletions

View File

@@ -304,7 +304,7 @@ package android.app {
}
public class AppOpsManager {
method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(int, @Nullable String, @Nullable String[], long, long, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method public static String[] getOpStrs();
method @Deprecated @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, String, int[]);
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
@@ -394,6 +394,17 @@ package android.app {
field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR;
}
public static final class AppOpsManager.HistoricalOpsRequest {
}
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 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);
}
public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);

View File

@@ -101,8 +101,8 @@ package android.app {
public class AppOpsManager {
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory();
method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(int, @Nullable String, @Nullable String[], long, long, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOpsFromDiskRaw(int, @Nullable String, @Nullable String[], long, long, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method public static int getNumOps();
method public static String[] getOpStrs();
method public boolean isOperationActive(int, int, String);
@@ -206,6 +206,17 @@ package android.app {
field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR;
}
public static final class AppOpsManager.HistoricalOpsRequest {
}
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 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);
}
public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);

View File

@@ -46,6 +46,8 @@ import android.util.ArrayMap;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.Immutable;
import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsNotedCallback;
@@ -2209,6 +2211,115 @@ public class AppOpsManager {
void visitHistoricalOp(@NonNull HistoricalOp ops);
}
/**
* Request for getting historical app op usage. The request acts
* as a filtering criteria when querying historical op usage.
*
* @hide
*/
@Immutable
@TestApi
@SystemApi
public static final class HistoricalOpsRequest {
private final int mUid;
private final @Nullable String mPackageName;
private final @Nullable List<String> mOpNames;
private final long mBeginTimeMillis;
private final long mEndTimeMillis;
private HistoricalOpsRequest(int uid, @Nullable String packageName,
@Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis) {
mUid = uid;
mPackageName = packageName;
mOpNames = opNames;
mBeginTimeMillis = beginTimeMillis;
mEndTimeMillis = endTimeMillis;
}
/**
* Builder for creating a {@link HistoricalOpsRequest}.
*
* @hide
*/
@TestApi
@SystemApi
public static final class Builder {
private int mUid = Process.INVALID_UID;
private @Nullable String mPackageName;
private @Nullable List<String> mOpNames;
private final long mBeginTimeMillis;
private final long mEndTimeMillis;
/**
* Creates a new builder.
*
* @param beginTimeMillis The beginning of the interval in milliseconds since
* epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be non
* negative.
* @param endTimeMillis The end of the interval in milliseconds since
* epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after
* {@code beginTimeMillis}. Pass {@link Long#MAX_VALUE} to get the most recent
* history including ops that happen while this call is in flight.
*/
public Builder(long beginTimeMillis, long endTimeMillis) {
Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
"beginTimeMillis must be non negative and lesser than endTimeMillis");
mBeginTimeMillis = beginTimeMillis;
mEndTimeMillis = endTimeMillis;
}
/**
* Sets the UID to query for.
*
* @param uid The uid. Pass {@link android.os.Process#INVALID_UID} for any uid.
* @return This builder.
*/
public @NonNull Builder setUid(int uid) {
Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
"uid must be " + Process.INVALID_UID + " or non negative");
mUid = uid;
return this;
}
/**
* Sets the package to query for.
*
* @param packageName The package name. <code>Null</code> for any package.
* @return This builder.
*/
public @NonNull Builder setPackageName(@Nullable String packageName) {
mPackageName = packageName;
return this;
}
/**
* Sets the op names to query for.
*
* @param opNames The op names. <code>Null</code> for any op.
* @return This builder.
*/
public @NonNull Builder setOpNames(@Nullable List<String> opNames) {
if (opNames != null) {
final int opCount = opNames.size();
for (int i = 0; i < opCount; i++) {
Preconditions.checkArgument(AppOpsManager.strOpToOp(
opNames.get(i)) != AppOpsManager.OP_NONE);
}
}
mOpNames = opNames;
return this;
}
/**
* @return a new {@link HistoricalOpsRequest}.
*/
public @NonNull HistoricalOpsRequest build() {
return new HistoricalOpsRequest(mUid, mPackageName, mOpNames,
mBeginTimeMillis, mEndTimeMillis);
}
}
}
/**
* This class represents historical app op state of all UIDs for a given time interval.
*
@@ -3671,26 +3782,7 @@ public class AppOpsManager {
/**
* Retrieve historical app op stats for a period.
*
* <p>Historical data can be obtained
* for a specific package by specifying the <code>packageName</code> argument,
* for a specific UID if specifying the <code>uid</code> argument, for a
* specific package in a UID by specifying the <code>packageName</code>
* and the <code>uid</code> arguments, for all packages by passing
* {@link android.os.Process#INVALID_UID} and <code>null</code> for the
* <code>uid</code> and <code>packageName</code> arguments, respectively.
* Similarly, you can specify the <code>opNames</code> argument to get
* data only for these ops or <code>null</code> for all ops.
*
* @param uid The UID to query for.
* @param packageName The package to query for.
* @param beginTimeMillis The beginning of the interval in milliseconds since
* epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be non
* negative.
* @param endTimeMillis The end of the interval in milliseconds since
* epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after
* {@code beginTimeMillis}. Pass {@link Long#MAX_VALUE} to get the most recent
* history including ops that happen while this call is in flight.
* @param opNames The ops to query for. Pass {@code null} for all ops.
* @param request A request object describing the data being queried for.
* @param executor Executor on which to run the callback. If <code>null</code>
* the callback is executed on the default executor running on the main thread.
* @param callback Callback on which to deliver the result.
@@ -3702,13 +3794,13 @@ public class AppOpsManager {
@TestApi
@SystemApi
@RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
public void getHistoricalOps(int uid, @Nullable String packageName,
@Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
public void getHistoricalOps(@NonNull HistoricalOpsRequest request,
@NonNull Executor executor, @NonNull Consumer<HistoricalOps> callback) {
Preconditions.checkNotNull(executor, "executor cannot be null");
Preconditions.checkNotNull(callback, "callback cannot be null");
try {
mService.getHistoricalOps(uid, packageName, opNames, beginTimeMillis, endTimeMillis,
mService.getHistoricalOps(request.mUid, request.mPackageName, request.mOpNames,
request.mBeginTimeMillis, request.mEndTimeMillis,
new RemoteCallback((result) -> {
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
final long identity = Binder.clearCallingIdentity();
@@ -3725,29 +3817,12 @@ public class AppOpsManager {
/**
* Retrieve historical app op stats for a period.
*
* <p>Historical data can be obtained
* for a specific package by specifying the <code>packageName</code> argument,
* for a specific UID if specifying the <code>uid</code> argument, for a
* specific package in a UID by specifying the <code>packageName</code>
* and the <code>uid</code> arguments, for all packages by passing
* {@link android.os.Process#INVALID_UID} and <code>null</code> for the
* <code>uid</code> and <code>packageName</code> arguments, respectively.
* Similarly, you can specify the <code>opNames</code> argument to get
* data only for these ops or <code>null</code> for all ops.
* <p>
* This method queries only the on disk state and the returned ops are raw,
* which is their times are relative to the history start as opposed to the
* epoch start.
*
* @param uid The UID to query for.
* @param packageName The package to query for.
* @param beginTimeMillis The beginning of the interval in milliseconds since
* history start. History time grows as one goes into the past.
* @param endTimeMillis The end of the interval in milliseconds since
* history start. History time grows as one goes into the past. Must be after
* {@code beginTimeMillis}.
* @param opNames The ops to query for. Pass {@code null} for all ops.
* @param request A request object describing the data being queried for.
* @param executor Executor on which to run the callback. If <code>null</code>
* the callback is executed on the default executor running on the main thread.
* @param callback Callback on which to deliver the result.
@@ -3758,15 +3833,15 @@ public class AppOpsManager {
*/
@TestApi
@RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName,
@Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
public void getHistoricalOpsFromDiskRaw(@NonNull HistoricalOpsRequest request,
@Nullable Executor executor, @NonNull Consumer<HistoricalOps> callback) {
Preconditions.checkNotNull(executor, "executor cannot be null");
Preconditions.checkNotNull(callback, "callback cannot be null");
try {
mService.getHistoricalOpsFromDiskRaw(uid, packageName, opNames, beginTimeMillis,
endTimeMillis, new RemoteCallback((result) -> {
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName,
request.mOpNames, request.mBeginTimeMillis, request.mEndTimeMillis,
new RemoteCallback((result) -> {
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
final long identity = Binder.clearCallingIdentity();
try {
executor.execute(() -> callback.accept(ops));

View File

@@ -44,9 +44,9 @@ interface IAppOpsService {
int checkPackage(int uid, String packageName);
List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
void getHistoricalOps(int uid, String packageName, in String[] ops, long beginTimeMillis,
void getHistoricalOps(int uid, String packageName, in List<String> ops, long beginTimeMillis,
long endTimeMillis, in RemoteCallback callback);
void getHistoricalOpsFromDiskRaw(int uid, String packageName, in String[] ops,
void getHistoricalOpsFromDiskRaw(int uid, String packageName, in List<String> ops,
long beginTimeMillis, long endTimeMillis, in RemoteCallback callback);
void offsetHistory(long duration);
void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep);

View File

@@ -38,6 +38,7 @@ import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManager.HistoricalOps;
import android.app.AppOpsManager.HistoricalOpsRequest;
import android.app.AppOpsManagerInternal;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
import android.content.BroadcastReceiver;
@@ -1024,25 +1025,29 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void getHistoricalOps(int uid, @NonNull String packageName,
@Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
@Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
@NonNull RemoteCallback callback) {
Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
"uid must be " + Process.INVALID_UID + " or non negative");
Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
"beginTimeMillis must be non negative and lesser than endTimeMillis");
// Use the builder to validate arguments.
final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder(
beginTimeMillis, endTimeMillis)
.setUid(uid)
.setPackageName(packageName)
.setOpNames(opNames)
.build();
Preconditions.checkNotNull(callback, "callback cannot be null");
checkValidOpsOrNull(opNames);
mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
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, opNames, beginTimeMillis,
getHistoricalPackagesOpsCompat(uid, packageName, opNamesArray, beginTimeMillis,
endTimeMillis, callback);
} else {
// Must not hold the appops lock
mHistoricalRegistry.getHistoricalOps(uid, packageName, opNames,
mHistoricalRegistry.getHistoricalOps(uid, packageName, opNamesArray,
beginTimeMillis, endTimeMillis, callback);
}
}
@@ -1101,20 +1106,25 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
@Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
@Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
@NonNull RemoteCallback callback) {
Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
"uid must be " + Process.INVALID_UID + " or non negative");
Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
"beginTimeMillis must be non negative and lesser than endTimeMillis");
// Use the builder to validate arguments.
final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder(
beginTimeMillis, endTimeMillis)
.setUid(uid)
.setPackageName(packageName)
.setOpNames(opNames)
.build();
Preconditions.checkNotNull(callback, "callback cannot be null");
checkValidOpsOrNull(opNames);
mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
final String[] opNamesArray = (opNames != null)
? opNames.toArray(new String[opNames.size()]) : null;
// Must not hold the appops lock
mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNames,
mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNamesArray,
beginTimeMillis, endTimeMillis, callback);
}
@@ -4266,16 +4276,6 @@ public class AppOpsService extends IAppOpsService.Stub {
return packageNames;
}
private static void checkValidOpsOrNull(String[] opNames) {
if (opNames != null) {
for (String opName : opNames) {
if (AppOpsManager.strOpToOp(opName) == AppOpsManager.OP_NONE) {
throw new IllegalArgumentException("Unknown op: " + opName);
}
}
}
}
private final class ClientRestrictionState implements DeathRecipient {
private final IBinder token;
SparseArray<boolean[]> perUserRestrictions;