Merge "Stack Trace sampling and reporting."
This commit is contained in:
committed by
Android (Google) Code Review
commit
4b7fd29ff1
@@ -6426,9 +6426,13 @@ package android.app {
|
||||
public class StatusBarManager {
|
||||
}
|
||||
|
||||
public final class SyncNotedAppOp {
|
||||
public final class SyncNotedAppOp implements android.os.Parcelable {
|
||||
ctor public SyncNotedAppOp(@IntRange(from=0L) int, @Nullable String);
|
||||
method public int describeContents();
|
||||
method @Nullable public String getFeatureId();
|
||||
method @NonNull public String getOp();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.app.SyncNotedAppOp> CREATOR;
|
||||
}
|
||||
|
||||
@Deprecated public class TabActivity extends android.app.ActivityGroup {
|
||||
|
||||
@@ -365,6 +365,7 @@ package android.app {
|
||||
}
|
||||
|
||||
public class AppOpsManager {
|
||||
method @Nullable @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
|
||||
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 @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...);
|
||||
@@ -681,6 +682,19 @@ package android.app {
|
||||
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
|
||||
}
|
||||
|
||||
public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
|
||||
ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
|
||||
method public int describeContents();
|
||||
method @Nullable public String getFeatureId();
|
||||
method @NonNull public String getMessage();
|
||||
method @NonNull public String getOp();
|
||||
method @NonNull public String getPackageName();
|
||||
method public int getSamplingStrategy();
|
||||
method @IntRange(from=0L) public int getUid();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.app.RuntimeAppOpAccessMessage> CREATOR;
|
||||
}
|
||||
|
||||
public class SearchManager implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
|
||||
method public void launchAssist(@Nullable android.os.Bundle);
|
||||
}
|
||||
|
||||
@@ -164,6 +164,7 @@ 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 @Nullable @RequiresPermission("android.permission.GET_APP_OPS_STATS") public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
|
||||
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.MANAGE_APPOPS") 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();
|
||||
@@ -447,6 +448,19 @@ package android.app {
|
||||
method public android.graphics.Rect getSourceRectHint();
|
||||
}
|
||||
|
||||
public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
|
||||
ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
|
||||
method public int describeContents();
|
||||
method @Nullable public String getFeatureId();
|
||||
method @NonNull public String getMessage();
|
||||
method @NonNull public String getOp();
|
||||
method @NonNull public String getPackageName();
|
||||
method public int getSamplingStrategy();
|
||||
method @IntRange(from=0L) public int getUid();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.app.RuntimeAppOpAccessMessage> CREATOR;
|
||||
}
|
||||
|
||||
public class StatusBarManager {
|
||||
method public void collapsePanels();
|
||||
method public void expandNotificationsPanel();
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package android.app;
|
||||
|
||||
import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__DEFAULT;
|
||||
import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
|
||||
import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.IntDef;
|
||||
@@ -48,6 +52,8 @@ import android.os.Parcelable;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
@@ -63,6 +69,7 @@ import com.android.internal.app.IAppOpsAsyncNotedCallback;
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsNotedCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.internal.app.MessageSamplingConfig;
|
||||
import com.android.internal.os.RuntimeInit;
|
||||
import com.android.internal.os.ZygoteInit;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
@@ -141,6 +148,13 @@ public class AppOpsManager {
|
||||
@UnsupportedAppUsage
|
||||
final IAppOpsService mService;
|
||||
|
||||
/**
|
||||
* Service for the application context, to be used by static methods via
|
||||
* {@link #getService()}
|
||||
*/
|
||||
@GuardedBy("sLock")
|
||||
static IAppOpsService sService;
|
||||
|
||||
@GuardedBy("mModeWatchers")
|
||||
private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers =
|
||||
new ArrayMap<>();
|
||||
@@ -159,6 +173,50 @@ public class AppOpsManager {
|
||||
@GuardedBy("sLock")
|
||||
private static @Nullable AppOpsCollector sNotedAppOpsCollector;
|
||||
|
||||
/**
|
||||
* Additional collector that collect accesses and forwards a few of them them via
|
||||
* {@link IAppOpsService#reportRuntimeAppOpAccessMessageAndGetConfig}.
|
||||
*/
|
||||
private static AppOpsCollector sMessageCollector =
|
||||
new AppOpsCollector() {
|
||||
@Override
|
||||
public void onNoted(@NonNull SyncNotedAppOp op) {
|
||||
reportStackTraceIfNeeded(op);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncOp) {
|
||||
// collected directly in AppOpsService
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelfNoted(@NonNull SyncNotedAppOp op) {
|
||||
reportStackTraceIfNeeded(op);
|
||||
}
|
||||
|
||||
private void reportStackTraceIfNeeded(@NonNull SyncNotedAppOp op) {
|
||||
if (sConfig.getSampledOpCode() == OP_NONE
|
||||
&& sConfig.getExpirationTimeSinceBootMillis()
|
||||
>= SystemClock.elapsedRealtime()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageSamplingConfig config = sConfig;
|
||||
if (leftCircularDistance(strOpToOp(op.getOp()), config.getSampledOpCode(),
|
||||
_NUM_OP) <= config.getAcceptableLeftDistance()
|
||||
|| config.getExpirationTimeSinceBootMillis()
|
||||
< SystemClock.elapsedRealtime()) {
|
||||
String stackTrace = getFormattedStackTrace();
|
||||
try {
|
||||
sConfig = getService().reportRuntimeAppOpAccessMessageAndGetConfig(
|
||||
ActivityThread.currentOpPackageName(), op, stackTrace);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static IBinder sClientId;
|
||||
|
||||
/**
|
||||
@@ -550,7 +608,6 @@ public class AppOpsManager {
|
||||
})
|
||||
public @interface OpFlags {}
|
||||
|
||||
|
||||
/** @hide */
|
||||
public static final String getFlagName(@OpFlags int flag) {
|
||||
switch (flag) {
|
||||
@@ -569,6 +626,18 @@ public class AppOpsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategies used for message sampling
|
||||
* @hide
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(prefix = {"RUNTIME_APP_OPS_ACCESS__SAMPLING_STRATEGY__"}, value = {
|
||||
RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__DEFAULT,
|
||||
RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM,
|
||||
RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED
|
||||
})
|
||||
public @interface SamplingStrategy {}
|
||||
|
||||
private static final int UID_STATE_OFFSET = 31;
|
||||
private static final int FLAGS_MASK = 0xFFFFFFFF;
|
||||
|
||||
@@ -2225,6 +2294,10 @@ public class AppOpsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Config used to control app ops access messages sampling */
|
||||
private static MessageSamplingConfig sConfig =
|
||||
new MessageSamplingConfig(OP_NONE, 0, 0);
|
||||
|
||||
/** @hide */
|
||||
public static final String KEY_HISTORICAL_OPS = "historical_ops";
|
||||
|
||||
@@ -7268,6 +7341,17 @@ public class AppOpsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
private static IAppOpsService getService() {
|
||||
synchronized (sLock) {
|
||||
if (sService == null) {
|
||||
sService = IAppOpsService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
||||
}
|
||||
return sService;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #startOp(String, int, String, String, String)} instead
|
||||
*/
|
||||
@@ -7614,6 +7698,7 @@ public class AppOpsManager {
|
||||
sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
|
||||
}
|
||||
}
|
||||
sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7764,6 +7849,10 @@ public class AppOpsManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int code = notedAppOps.nextSetBit(0); code != -1;
|
||||
code = notedAppOps.nextSetBit(code + 1)) {
|
||||
sMessageCollector.onNoted(new SyncNotedAppOp(code, featureId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7958,10 +8047,13 @@ public class AppOpsManager {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = firstInteresting; i <= lastInteresting; i++) {
|
||||
sb.append(trace[i]);
|
||||
if (i != lastInteresting) {
|
||||
if (i != firstInteresting) {
|
||||
sb.append('\n');
|
||||
}
|
||||
if (sb.length() + trace[i].toString().length() > 600) {
|
||||
break;
|
||||
}
|
||||
sb.append(trace[i]);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
@@ -8088,6 +8180,22 @@ public class AppOpsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls current AppOps access report and picks package and op to watch for next access report
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@TestApi
|
||||
@RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
|
||||
public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() {
|
||||
try {
|
||||
return mService.collectRuntimeAppOpAccessMessage();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all supported operation names.
|
||||
* @hide
|
||||
@@ -8297,4 +8405,12 @@ public class AppOpsManager {
|
||||
|
||||
return AppOpsManager.MODE_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate left circular distance for two numbers modulo size.
|
||||
* @hide
|
||||
*/
|
||||
public static int leftCircularDistance(int from, int to, int size) {
|
||||
return (to + size - from) % size;
|
||||
}
|
||||
}
|
||||
|
||||
19
core/java/android/app/RuntimeAppOpAccessMessage.aidl
Normal file
19
core/java/android/app/RuntimeAppOpAccessMessage.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app;
|
||||
|
||||
parcelable RuntimeAppOpAccessMessage;
|
||||
245
core/java/android/app/RuntimeAppOpAccessMessage.java
Normal file
245
core/java/android/app/RuntimeAppOpAccessMessage.java
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.TestApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.annotations.Immutable;
|
||||
import com.android.internal.util.DataClass;
|
||||
|
||||
/**
|
||||
* Message for noted runtime permission access.
|
||||
* @hide
|
||||
*/
|
||||
@Immutable
|
||||
@TestApi
|
||||
@SystemApi
|
||||
/*@DataClass(genConstructor = false)
|
||||
@DataClass.Suppress("getOpCode")*/
|
||||
public final class RuntimeAppOpAccessMessage implements Parcelable {
|
||||
/** Uid of package for which runtime app op access message was collected */
|
||||
private final @IntRange(from = 0L) int mUid;
|
||||
/** Op code of operation access which was collected */
|
||||
private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode;
|
||||
/** Name of package for which runtime app op access message was collected */
|
||||
private final @NonNull String mPackageName;
|
||||
/** Feature of package for which runtime app op access message was collected */
|
||||
private final @Nullable String mFeatureId;
|
||||
/** Message collected (including stacktrace for synchronous ops) */
|
||||
private final @NonNull String mMessage;
|
||||
/** Sampling strategy used to collect this message. */
|
||||
private final @AppOpsManager.SamplingStrategy int mSamplingStrategy;
|
||||
|
||||
public @NonNull String getOp() {
|
||||
return AppOpsManager.opToPublicName(mOpCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new RuntimeAppOpAccessMessage.
|
||||
*
|
||||
* @param uid
|
||||
* Uid of package for which runtime app op access message was collected
|
||||
* @param opCode
|
||||
* Op code of operation access which was collected
|
||||
* @param packageName
|
||||
* Name of package for which runtime app op access message was collected
|
||||
* @param featureId
|
||||
* Feature of package for which runtime app op access message was collected
|
||||
* @param message
|
||||
* Message collected (including stacktrace for synchronous ops)
|
||||
* @param samplingStrategy
|
||||
* Sampling strategy used to collect this message.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public RuntimeAppOpAccessMessage(
|
||||
@IntRange(from = 0L) int uid,
|
||||
@IntRange(from = 0L) int opCode,
|
||||
@NonNull String packageName,
|
||||
@Nullable String featureId,
|
||||
@NonNull String message,
|
||||
@AppOpsManager.SamplingStrategy int samplingStrategy) {
|
||||
this.mUid = uid;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mUid,
|
||||
"from", 0L);
|
||||
this.mOpCode = opCode;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mOpCode,
|
||||
"from", 0L,
|
||||
"to", AppOpsManager._NUM_OP - 1);
|
||||
this.mPackageName = packageName;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mPackageName);
|
||||
this.mFeatureId = featureId;
|
||||
this.mMessage = message;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mMessage);
|
||||
this.mSamplingStrategy = samplingStrategy;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
AppOpsManager.SamplingStrategy.class, null, mSamplingStrategy);
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Code below generated by codegen v1.0.14.
|
||||
//
|
||||
// DO NOT MODIFY!
|
||||
// CHECKSTYLE:OFF Generated code
|
||||
//
|
||||
// To regenerate run:
|
||||
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/RuntimeAppOpAccessMessage.java
|
||||
//
|
||||
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
||||
// Settings > Editor > Code Style > Formatter Control
|
||||
//@formatter:off
|
||||
|
||||
|
||||
/**
|
||||
* Uid of package for which runtime app op access message was collected
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @IntRange(from = 0L) int getUid() {
|
||||
return mUid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of package for which runtime app op access message was collected
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature of package for which runtime app op access message was collected
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @Nullable String getFeatureId() {
|
||||
return mFeatureId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message collected (including stacktrace for synchronous ops)
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull String getMessage() {
|
||||
return mMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sampling strategy used to collect this message.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @AppOpsManager.SamplingStrategy int getSamplingStrategy() {
|
||||
return mSamplingStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
// You can override field parcelling by defining methods like:
|
||||
// void parcelFieldName(Parcel dest, int flags) { ... }
|
||||
|
||||
byte flg = 0;
|
||||
if (mFeatureId != null) flg |= 0x8;
|
||||
dest.writeByte(flg);
|
||||
dest.writeInt(mUid);
|
||||
dest.writeInt(mOpCode);
|
||||
dest.writeString(mPackageName);
|
||||
if (mFeatureId != null) dest.writeString(mFeatureId);
|
||||
dest.writeString(mMessage);
|
||||
dest.writeInt(mSamplingStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public int describeContents() { return 0; }
|
||||
|
||||
/** @hide */
|
||||
@SuppressWarnings({"unchecked", "RedundantCast"})
|
||||
@DataClass.Generated.Member
|
||||
/* package-private */ RuntimeAppOpAccessMessage(@NonNull Parcel in) {
|
||||
// You can override field unparcelling by defining methods like:
|
||||
// static FieldType unparcelFieldName(Parcel in) { ... }
|
||||
|
||||
byte flg = in.readByte();
|
||||
int uid = in.readInt();
|
||||
int opCode = in.readInt();
|
||||
String packageName = in.readString();
|
||||
String featureId = (flg & 0x8) == 0 ? null : in.readString();
|
||||
String message = in.readString();
|
||||
int samplingStrategy = in.readInt();
|
||||
|
||||
this.mUid = uid;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mUid,
|
||||
"from", 0L);
|
||||
this.mOpCode = opCode;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mOpCode,
|
||||
"from", 0L,
|
||||
"to", AppOpsManager._NUM_OP - 1);
|
||||
this.mPackageName = packageName;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mPackageName);
|
||||
this.mFeatureId = featureId;
|
||||
this.mMessage = message;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mMessage);
|
||||
this.mSamplingStrategy = samplingStrategy;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
AppOpsManager.SamplingStrategy.class, null, mSamplingStrategy);
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
public static final @NonNull Parcelable.Creator<RuntimeAppOpAccessMessage> CREATOR
|
||||
= new Parcelable.Creator<RuntimeAppOpAccessMessage>() {
|
||||
@Override
|
||||
public RuntimeAppOpAccessMessage[] newArray(int size) {
|
||||
return new RuntimeAppOpAccessMessage[size];
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeAppOpAccessMessage createFromParcel(@NonNull Parcel in) {
|
||||
return new RuntimeAppOpAccessMessage(in);
|
||||
}
|
||||
};
|
||||
|
||||
/*@DataClass.Generated(
|
||||
time = 1581517099127L,
|
||||
codegenVersion = "1.0.14",
|
||||
sourceFile = "frameworks/base/core/java/android/app/RuntimeAppOpAccessMessage.java",
|
||||
inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.app.AppOpsManager.SamplingStrategy int mSamplingStrategy\npublic @android.annotation.NonNull java.lang.String getOp()\nclass RuntimeAppOpAccessMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")*/
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
//@formatter:on
|
||||
// End of generated code
|
||||
|
||||
}
|
||||
19
core/java/android/app/SyncNotedAppOp.aidl
Normal file
19
core/java/android/app/SyncNotedAppOp.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app;
|
||||
|
||||
parcelable SyncNotedAppOp;
|
||||
@@ -19,8 +19,10 @@ package android.app;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.annotations.Immutable;
|
||||
import com.android.internal.util.DataClass;
|
||||
|
||||
/**
|
||||
* Description of an app-op that was noted for the current process.
|
||||
@@ -32,10 +34,35 @@ import com.android.internal.annotations.Immutable;
|
||||
* itself}.
|
||||
*/
|
||||
@Immutable
|
||||
public final class SyncNotedAppOp {
|
||||
private final int mOpCode;
|
||||
/*@DataClass(
|
||||
genEqualsHashCode = true,
|
||||
genConstructor = false
|
||||
)
|
||||
@DataClass.Suppress("getOpCode")*/
|
||||
public final class SyncNotedAppOp implements Parcelable {
|
||||
|
||||
/** op code of synchronous appop noted */
|
||||
private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode;
|
||||
/** featureId of synchronous appop noted */
|
||||
private final @Nullable String mFeatureId;
|
||||
|
||||
/**
|
||||
* Creates a new SyncNotedAppOp.
|
||||
*
|
||||
* @param opCode
|
||||
* op code of synchronous appop noted
|
||||
* @param featureId
|
||||
* featureId of synchronous appop noted
|
||||
*/
|
||||
public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String featureId) {
|
||||
this.mOpCode = opCode;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mOpCode,
|
||||
"from", 0,
|
||||
"to", AppOpsManager._NUM_OP - 1);
|
||||
this.mFeatureId = featureId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The op that was noted.
|
||||
*/
|
||||
@@ -43,37 +70,118 @@ public final class SyncNotedAppOp {
|
||||
return AppOpsManager.opToPublicName(mOpCode);
|
||||
}
|
||||
|
||||
// Code below generated by codegen v1.0.14.
|
||||
//
|
||||
// DO NOT MODIFY!
|
||||
// CHECKSTYLE:OFF Generated code
|
||||
//
|
||||
// To regenerate run:
|
||||
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/SyncNotedAppOp.java
|
||||
//
|
||||
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
||||
// Settings > Editor > Code Style > Formatter Control
|
||||
//@formatter:off
|
||||
|
||||
|
||||
/**
|
||||
* @return The {@link android.content.Context#createFeatureContext Feature} in the app
|
||||
* featureId of synchronous appop noted
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @Nullable String getFeatureId() {
|
||||
return mFeatureId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new sync op description
|
||||
*
|
||||
* @param opCode The op that was noted
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public SyncNotedAppOp(@IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode,
|
||||
@Nullable String featureId) {
|
||||
mOpCode = opCode;
|
||||
mFeatureId = featureId;
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public boolean equals(@Nullable Object o) {
|
||||
// You can override field equality logic by defining either of the methods like:
|
||||
// boolean fieldNameEquals(SyncNotedAppOp other) { ... }
|
||||
// boolean fieldNameEquals(FieldType otherValue) { ... }
|
||||
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
@SuppressWarnings("unchecked")
|
||||
SyncNotedAppOp that = (SyncNotedAppOp) o;
|
||||
//noinspection PointlessBooleanExpression
|
||||
return true
|
||||
&& mOpCode == that.mOpCode
|
||||
&& java.util.Objects.equals(mFeatureId, that.mFeatureId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof SyncNotedAppOp)) {
|
||||
return false;
|
||||
@DataClass.Generated.Member
|
||||
public int hashCode() {
|
||||
// You can override field hashCode logic by defining methods like:
|
||||
// int fieldNameHashCode() { ... }
|
||||
|
||||
int _hash = 1;
|
||||
_hash = 31 * _hash + mOpCode;
|
||||
_hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId);
|
||||
return _hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
|
||||
// You can override field parcelling by defining methods like:
|
||||
// void parcelFieldName(Parcel dest, int flags) { ... }
|
||||
|
||||
byte flg = 0;
|
||||
if (mFeatureId != null) flg |= 0x2;
|
||||
dest.writeByte(flg);
|
||||
dest.writeInt(mOpCode);
|
||||
if (mFeatureId != null) dest.writeString(mFeatureId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public int describeContents() { return 0; }
|
||||
|
||||
/** @hide */
|
||||
@SuppressWarnings({"unchecked", "RedundantCast"})
|
||||
@DataClass.Generated.Member
|
||||
/* package-private */ SyncNotedAppOp(@NonNull android.os.Parcel in) {
|
||||
// You can override field unparcelling by defining methods like:
|
||||
// static FieldType unparcelFieldName(Parcel in) { ... }
|
||||
|
||||
byte flg = in.readByte();
|
||||
int opCode = in.readInt();
|
||||
String featureId = (flg & 0x2) == 0 ? null : in.readString();
|
||||
|
||||
this.mOpCode = opCode;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mOpCode,
|
||||
"from", 0,
|
||||
"to", AppOpsManager._NUM_OP - 1);
|
||||
this.mFeatureId = featureId;
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
public static final @NonNull Parcelable.Creator<SyncNotedAppOp> CREATOR
|
||||
= new Parcelable.Creator<SyncNotedAppOp>() {
|
||||
@Override
|
||||
public SyncNotedAppOp[] newArray(int size) {
|
||||
return new SyncNotedAppOp[size];
|
||||
}
|
||||
|
||||
return mOpCode == ((SyncNotedAppOp) other).mOpCode;
|
||||
}
|
||||
@Override
|
||||
public SyncNotedAppOp createFromParcel(@NonNull android.os.Parcel in) {
|
||||
return new SyncNotedAppOp(in);
|
||||
}
|
||||
};
|
||||
|
||||
/*@DataClass.Generated(
|
||||
time = 1579188889960L,
|
||||
codegenVersion = "1.0.14",
|
||||
sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java",
|
||||
inputSignatures = "private final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\npublic @android.annotation.NonNull java.lang.String getOp()\npublic @android.annotation.SystemApi int getOpCode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genConstructor=false)")*/
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
//@formatter:on
|
||||
// End of generated code
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mOpCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.internal.app;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.AsyncNotedAppOp;
|
||||
import android.app.SyncNotedAppOp;
|
||||
import android.app.RuntimeAppOpAccessMessage;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteCallback;
|
||||
@@ -25,6 +27,7 @@ import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsActiveCallback;
|
||||
import com.android.internal.app.IAppOpsAsyncNotedCallback;
|
||||
import com.android.internal.app.IAppOpsNotedCallback;
|
||||
import com.android.internal.app.MessageSamplingConfig;
|
||||
|
||||
interface IAppOpsService {
|
||||
// These methods are also called by native code, so must
|
||||
@@ -54,6 +57,9 @@ interface IAppOpsService {
|
||||
|
||||
// Remaining methods are only used in Java.
|
||||
int checkPackage(int uid, String packageName);
|
||||
RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
|
||||
MessageSamplingConfig reportRuntimeAppOpAccessMessageAndGetConfig(String packageName,
|
||||
in SyncNotedAppOp appOp, String message);
|
||||
@UnsupportedAppUsage
|
||||
List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
|
||||
@UnsupportedAppUsage
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.app;
|
||||
|
||||
parcelable MessageSamplingConfig;
|
||||
187
core/java/com/android/internal/app/MessageSamplingConfig.java
Normal file
187
core/java/com/android/internal/app/MessageSamplingConfig.java
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.app;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.AppOpsManager;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.annotations.Immutable;
|
||||
import com.android.internal.util.DataClass;
|
||||
|
||||
/**
|
||||
* Configuration for AppOps access messages sampling.
|
||||
*/
|
||||
@Immutable
|
||||
/*@DataClass*/
|
||||
public final class MessageSamplingConfig implements Parcelable {
|
||||
/** Op code targeted during current sampling session */
|
||||
private final @IntRange(from = -1L, to = AppOpsManager._NUM_OP - 1) int mSampledOpCode;
|
||||
/** Range of ops which should be reported during current sampling session */
|
||||
private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mAcceptableLeftDistance;
|
||||
/** Expiration time for this sampling config */
|
||||
private final @IntRange(from = 0L) long mExpirationTimeSinceBootMillis;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Code below generated by codegen v1.0.14.
|
||||
//
|
||||
// DO NOT MODIFY!
|
||||
// CHECKSTYLE:OFF Generated code
|
||||
//
|
||||
// To regenerate run:
|
||||
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/app/MessageSamplingConfig.java
|
||||
//
|
||||
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
||||
// Settings > Editor > Code Style > Formatter Control
|
||||
//@formatter:off
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new MessageSamplingConfig.
|
||||
*
|
||||
* @param sampledOpCode
|
||||
* Op code targeted during current sampling session
|
||||
* @param acceptableLeftDistance
|
||||
* Range of ops which should be reported during current sampling session
|
||||
* @param expirationTimeSinceBootMillis
|
||||
* Expiration time for this sampling config
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public MessageSamplingConfig(
|
||||
@IntRange(from = -1L, to = AppOpsManager._NUM_OP - 1) int sampledOpCode,
|
||||
@IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int acceptableLeftDistance,
|
||||
@IntRange(from = 0L) long expirationTimeSinceBootMillis) {
|
||||
this.mSampledOpCode = sampledOpCode;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mSampledOpCode,
|
||||
"from", -1L,
|
||||
"to", AppOpsManager._NUM_OP - 1);
|
||||
this.mAcceptableLeftDistance = acceptableLeftDistance;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mAcceptableLeftDistance,
|
||||
"from", 0L,
|
||||
"to", AppOpsManager._NUM_OP - 1);
|
||||
this.mExpirationTimeSinceBootMillis = expirationTimeSinceBootMillis;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mExpirationTimeSinceBootMillis,
|
||||
"from", 0L);
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Op code targeted during current sampling session
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @IntRange(from = -1L, to = AppOpsManager._NUM_OP - 1) int getSampledOpCode() {
|
||||
return mSampledOpCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Range of ops which should be reported during current sampling session
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int getAcceptableLeftDistance() {
|
||||
return mAcceptableLeftDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expiration time for this sampling config
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @IntRange(from = 0L) long getExpirationTimeSinceBootMillis() {
|
||||
return mExpirationTimeSinceBootMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
// You can override field parcelling by defining methods like:
|
||||
// void parcelFieldName(Parcel dest, int flags) { ... }
|
||||
|
||||
dest.writeInt(mSampledOpCode);
|
||||
dest.writeInt(mAcceptableLeftDistance);
|
||||
dest.writeLong(mExpirationTimeSinceBootMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public int describeContents() { return 0; }
|
||||
|
||||
/** @hide */
|
||||
@SuppressWarnings({"unchecked", "RedundantCast"})
|
||||
@DataClass.Generated.Member
|
||||
/* package-private */ MessageSamplingConfig(@NonNull Parcel in) {
|
||||
// You can override field unparcelling by defining methods like:
|
||||
// static FieldType unparcelFieldName(Parcel in) { ... }
|
||||
|
||||
int sampledOpCode = in.readInt();
|
||||
int acceptableLeftDistance = in.readInt();
|
||||
long expirationTimeSinceBootMillis = in.readLong();
|
||||
|
||||
this.mSampledOpCode = sampledOpCode;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mSampledOpCode,
|
||||
"from", -1L,
|
||||
"to", AppOpsManager._NUM_OP - 1);
|
||||
this.mAcceptableLeftDistance = acceptableLeftDistance;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mAcceptableLeftDistance,
|
||||
"from", 0L,
|
||||
"to", AppOpsManager._NUM_OP - 1);
|
||||
this.mExpirationTimeSinceBootMillis = expirationTimeSinceBootMillis;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mExpirationTimeSinceBootMillis,
|
||||
"from", 0L);
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
public static final @NonNull Parcelable.Creator<MessageSamplingConfig> CREATOR
|
||||
= new Parcelable.Creator<MessageSamplingConfig>() {
|
||||
@Override
|
||||
public MessageSamplingConfig[] newArray(int size) {
|
||||
return new MessageSamplingConfig[size];
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageSamplingConfig createFromParcel(@NonNull Parcel in) {
|
||||
return new MessageSamplingConfig(in);
|
||||
}
|
||||
};
|
||||
|
||||
/*@DataClass.Generated(
|
||||
time = 1580691255495L,
|
||||
codegenVersion = "1.0.14",
|
||||
sourceFile = "frameworks/base/core/java/com/android/internal/app/MessageSamplingConfig.java",
|
||||
inputSignatures = "private final @android.annotation.IntRange(from=-1L, to=AppOpsManager._NUM_OP - 1) int mSampledOpCode\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mAcceptableLeftDistance\nprivate final @android.annotation.IntRange(from=0L) long mExpirationTimeSinceBootMillis\nclass MessageSamplingConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")*/
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
//@formatter:on
|
||||
// End of generated code
|
||||
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import static android.app.AppOpsManager.OP_CAMERA;
|
||||
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_FLAGS_ALL;
|
||||
import static android.app.AppOpsManager.OP_FLAG_SELF;
|
||||
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
|
||||
import static android.app.AppOpsManager.OP_NONE;
|
||||
import static android.app.AppOpsManager.OP_PLAY_AUDIO;
|
||||
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
|
||||
@@ -52,11 +53,14 @@ import static android.app.AppOpsManager.extractUidStateFromKey;
|
||||
import static android.app.AppOpsManager.makeKey;
|
||||
import static android.app.AppOpsManager.modeToName;
|
||||
import static android.app.AppOpsManager.opToName;
|
||||
import static android.app.AppOpsManager.opToPublicName;
|
||||
import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
|
||||
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
|
||||
import static android.content.Intent.EXTRA_REPLACING;
|
||||
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
|
||||
import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
|
||||
import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
|
||||
import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;
|
||||
|
||||
import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
|
||||
|
||||
@@ -79,6 +83,8 @@ import android.app.AppOpsManager.OpFlags;
|
||||
import android.app.AppOpsManagerInternal;
|
||||
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
|
||||
import android.app.AsyncNotedAppOp;
|
||||
import android.app.RuntimeAppOpAccessMessage;
|
||||
import android.app.SyncNotedAppOp;
|
||||
import android.compat.Compatibility;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
@@ -87,6 +93,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PermissionInfo;
|
||||
@@ -137,6 +144,7 @@ import com.android.internal.app.IAppOpsAsyncNotedCallback;
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsNotedCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.internal.app.MessageSamplingConfig;
|
||||
import com.android.internal.os.Zygote;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
@@ -147,6 +155,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.LockGuard;
|
||||
import com.android.server.SystemServerInitThreadPool;
|
||||
import com.android.server.pm.PackageList;
|
||||
|
||||
import libcore.util.EmptyArray;
|
||||
|
||||
@@ -163,6 +172,8 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -172,6 +183,8 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class AppOpsService extends IAppOpsService.Stub {
|
||||
static final String TAG = "AppOps";
|
||||
@@ -308,6 +321,34 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
|
||||
private ActivityManagerInternal mActivityManagerInternal;
|
||||
|
||||
/** Package sampled for message collection in the current session */
|
||||
@GuardedBy("this")
|
||||
private String mSampledPackage = null;
|
||||
|
||||
/** Appop sampled for message collection in the current session */
|
||||
@GuardedBy("this")
|
||||
private int mSampledAppOpCode = OP_NONE;
|
||||
|
||||
/** Maximum distance for appop to be considered for message collection in the current session */
|
||||
@GuardedBy("this")
|
||||
private int mAcceptableLeftDistance = 0;
|
||||
|
||||
/** Number of messages collected for sampled package and appop in the current session */
|
||||
@GuardedBy("this")
|
||||
private float mMessagesCollectedCount;
|
||||
|
||||
/** List of rarely used packages priorities for message collection */
|
||||
@GuardedBy("this")
|
||||
private ArraySet<String> mRarelyUsedPackages = new ArraySet<>();
|
||||
|
||||
/** Sampling strategy used for current session */
|
||||
@GuardedBy("this")
|
||||
@AppOpsManager.SamplingStrategy
|
||||
private int mSamplingStrategy;
|
||||
|
||||
/** Last runtime permission access message collected and ready for reporting */
|
||||
@GuardedBy("this")
|
||||
private RuntimeAppOpAccessMessage mCollectedRuntimePermissionMessage;
|
||||
/**
|
||||
* An unsynchronized pool of {@link OpEventProxyInfo} objects.
|
||||
*/
|
||||
@@ -1542,6 +1583,38 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
}
|
||||
}, packageSuspendFilter);
|
||||
|
||||
final IntentFilter packageAddedFilter = new IntentFilter();
|
||||
packageAddedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
packageAddedFilter.addDataScheme("package");
|
||||
mContext.registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final Uri data = intent.getData();
|
||||
|
||||
final String packageName = data.getSchemeSpecificPart();
|
||||
PackageInfo pi = LocalServices.getService(
|
||||
PackageManagerInternal.class).getPackageInfo(packageName,
|
||||
PackageManager.GET_PERMISSIONS, Process.myUid(), mContext.getUserId());
|
||||
if (isSamplingTarget(pi)) {
|
||||
synchronized (this) {
|
||||
mRarelyUsedPackages.add(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, packageAddedFilter);
|
||||
|
||||
List<String> packageNames = getPackageNamesForSampling();
|
||||
synchronized (this) {
|
||||
resamplePackageAndAppOpLocked(packageNames);
|
||||
}
|
||||
|
||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
initializeRarelyUsedPackagesList(new ArraySet<>(packageNames));
|
||||
}
|
||||
});
|
||||
|
||||
PackageManagerInternal packageManagerInternal = LocalServices.getService(
|
||||
PackageManagerInternal.class);
|
||||
packageManagerInternal.setExternalSourcesPolicy(
|
||||
@@ -3037,6 +3110,9 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
featureId, message, System.currentTimeMillis());
|
||||
final boolean[] wasNoteForwarded = {false};
|
||||
|
||||
reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode, featureId,
|
||||
message);
|
||||
|
||||
if (callbacks != null) {
|
||||
callbacks.broadcast((cb) -> {
|
||||
try {
|
||||
@@ -5501,6 +5577,227 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
mHistoricalRegistry.clearHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Report runtime access to AppOp together with message (including stack trace)
|
||||
*
|
||||
* @param packageName The package which reported the op
|
||||
* @param notedAppOp contains code of op and featureId provided by developer
|
||||
* @param message Message describing AppOp access (can be stack trace)
|
||||
*
|
||||
* @return Config for future sampling to reduce amount of reporting
|
||||
*/
|
||||
@Override
|
||||
public MessageSamplingConfig reportRuntimeAppOpAccessMessageAndGetConfig(
|
||||
String packageName, SyncNotedAppOp notedAppOp, String message) {
|
||||
int uid = Binder.getCallingUid();
|
||||
Objects.requireNonNull(packageName);
|
||||
synchronized (this) {
|
||||
switchPackageIfRarelyUsedLocked(packageName);
|
||||
if (!packageName.equals(mSampledPackage)) {
|
||||
return new MessageSamplingConfig(OP_NONE, 0,
|
||||
Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
|
||||
}
|
||||
|
||||
Objects.requireNonNull(notedAppOp);
|
||||
Objects.requireNonNull(message);
|
||||
|
||||
reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName,
|
||||
AppOpsManager.strOpToOp(notedAppOp.getOp()),
|
||||
notedAppOp.getFeatureId(), message);
|
||||
|
||||
return new MessageSamplingConfig(mSampledAppOpCode, mAcceptableLeftDistance,
|
||||
Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report runtime access to AppOp together with message (entry point for reporting
|
||||
* asynchronous access)
|
||||
* @param uid Uid of the package which reported the op
|
||||
* @param packageName The package which reported the op
|
||||
* @param opCode Code of AppOp
|
||||
* @param featureId FeautreId of AppOp reported
|
||||
* @param message Message describing AppOp access (can be stack trace)
|
||||
*/
|
||||
private void reportRuntimeAppOpAccessMessageAsyncLocked(int uid,
|
||||
@NonNull String packageName, int opCode, @Nullable String featureId,
|
||||
@NonNull String message) {
|
||||
switchPackageIfRarelyUsedLocked(packageName);
|
||||
if (!Objects.equals(mSampledPackage, packageName)) {
|
||||
return;
|
||||
}
|
||||
reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName, opCode, featureId, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether reported message is within the range of watched AppOps and picks it for
|
||||
* reporting uniformly at random across all received messages.
|
||||
*/
|
||||
private void reportRuntimeAppOpAccessMessageInternalLocked(int uid,
|
||||
@NonNull String packageName, int opCode, @Nullable String featureId,
|
||||
@NonNull String message) {
|
||||
int newLeftDistance = AppOpsManager.leftCircularDistance(opCode,
|
||||
mSampledAppOpCode, _NUM_OP);
|
||||
|
||||
if (mAcceptableLeftDistance < newLeftDistance) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAcceptableLeftDistance > newLeftDistance) {
|
||||
mAcceptableLeftDistance = newLeftDistance;
|
||||
mMessagesCollectedCount = 0.0f;
|
||||
}
|
||||
|
||||
mMessagesCollectedCount += 1.0f;
|
||||
if (ThreadLocalRandom.current().nextFloat() <= 1.0f / mMessagesCollectedCount) {
|
||||
mCollectedRuntimePermissionMessage = new RuntimeAppOpAccessMessage(uid, opCode,
|
||||
packageName, featureId, message, mSamplingStrategy);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/** Pulls current AppOps access report and resamples package and app op to watch */
|
||||
@Override
|
||||
public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() {
|
||||
mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
|
||||
Binder.getCallingPid(), Binder.getCallingUid(), null);
|
||||
RuntimeAppOpAccessMessage result;
|
||||
List<String> packageNames = getPackageNamesForSampling();
|
||||
synchronized (this) {
|
||||
result = mCollectedRuntimePermissionMessage;
|
||||
resamplePackageAndAppOpLocked(packageNames);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if package is in the list of rarely used package and starts watching the new package
|
||||
* to collect incoming message.
|
||||
* @param packageName
|
||||
*/
|
||||
private void switchPackageIfRarelyUsedLocked(@NonNull String packageName) {
|
||||
if (mRarelyUsedPackages.contains(packageName)) {
|
||||
mRarelyUsedPackages.remove(packageName);
|
||||
if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
|
||||
mSamplingStrategy = RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
|
||||
resampleAppOpForPackageLocked(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Resamples package and appop to watch from the list provided. */
|
||||
private void resamplePackageAndAppOpLocked(@NonNull List<String> packageNames) {
|
||||
if (!packageNames.isEmpty()) {
|
||||
mSamplingStrategy = RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;
|
||||
resampleAppOpForPackageLocked(packageNames.get(
|
||||
ThreadLocalRandom.current().nextInt(packageNames.size())));
|
||||
}
|
||||
}
|
||||
|
||||
/** Resamples appop for the chosen package and initializes sampling state */
|
||||
private void resampleAppOpForPackageLocked(@NonNull String packageName) {
|
||||
mMessagesCollectedCount = 0.0f;
|
||||
mSampledAppOpCode = ThreadLocalRandom.current().nextInt(_NUM_OP);
|
||||
mAcceptableLeftDistance = _NUM_OP;
|
||||
mSampledPackage = packageName;
|
||||
mCollectedRuntimePermissionMessage = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates list of rarely used packages - packages which were not used over last week or
|
||||
* which declared but did not use permissions over last week.
|
||||
* */
|
||||
private void initializeRarelyUsedPackagesList(@NonNull ArraySet<String> candidates) {
|
||||
AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
|
||||
List<String> runtimeAppOpsList = getRuntimeAppOpsList();
|
||||
AppOpsManager.HistoricalOpsRequest histOpsRequest =
|
||||
new AppOpsManager.HistoricalOpsRequest.Builder(
|
||||
Instant.now().minus(7, ChronoUnit.DAYS).toEpochMilli(),
|
||||
Long.MAX_VALUE).setOpNames(runtimeAppOpsList).setFlags(
|
||||
OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED).build();
|
||||
appOps.getHistoricalOps(histOpsRequest, AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
new Consumer<HistoricalOps>() {
|
||||
@Override
|
||||
public void accept(HistoricalOps histOps) {
|
||||
int uidCount = histOps.getUidCount();
|
||||
for (int uidIdx = 0; uidIdx < uidCount; uidIdx++) {
|
||||
final AppOpsManager.HistoricalUidOps uidOps = histOps.getUidOpsAt(
|
||||
uidIdx);
|
||||
int pkgCount = uidOps.getPackageCount();
|
||||
for (int pkgIdx = 0; pkgIdx < pkgCount; pkgIdx++) {
|
||||
String packageName = uidOps.getPackageOpsAt(
|
||||
pkgIdx).getPackageName();
|
||||
if (!candidates.contains(packageName)) {
|
||||
continue;
|
||||
}
|
||||
AppOpsManager.HistoricalPackageOps packageOps =
|
||||
uidOps.getPackageOpsAt(pkgIdx);
|
||||
if (packageOps.getOpCount() != 0) {
|
||||
candidates.remove(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized (this) {
|
||||
mRarelyUsedPackages = candidates;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** List of app ops related to runtime permissions */
|
||||
private List<String> getRuntimeAppOpsList() {
|
||||
ArrayList<String> result = new ArrayList();
|
||||
for (int i = 0; i < _NUM_OP; i++) {
|
||||
if (shouldCollectNotes(i)) {
|
||||
result.add(opToPublicName(i));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns list of packages to be used for package sampling */
|
||||
private @NonNull List<String> getPackageNamesForSampling() {
|
||||
List<String> packageNames = new ArrayList<>();
|
||||
PackageManagerInternal packageManagerInternal = LocalServices.getService(
|
||||
PackageManagerInternal.class);
|
||||
PackageList packages = packageManagerInternal.getPackageList();
|
||||
for (String packageName : packages.getPackageNames()) {
|
||||
PackageInfo pkg = packageManagerInternal.getPackageInfo(packageName,
|
||||
PackageManager.GET_PERMISSIONS, Process.myUid(), mContext.getUserId());
|
||||
if (isSamplingTarget(pkg)) {
|
||||
packageNames.add(pkg.packageName);
|
||||
}
|
||||
}
|
||||
return packageNames;
|
||||
}
|
||||
|
||||
/** Checks whether package should be included in sampling pool */
|
||||
private boolean isSamplingTarget(@Nullable PackageInfo pkg) {
|
||||
if (pkg == null) {
|
||||
return false;
|
||||
}
|
||||
if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.Q) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] requestedPermissions = pkg.requestedPermissions;
|
||||
if (requestedPermissions == null) {
|
||||
return false;
|
||||
}
|
||||
for (String permission : requestedPermissions) {
|
||||
PermissionInfo permissionInfo;
|
||||
try {
|
||||
permissionInfo = mContext.getPackageManager().getPermissionInfo(permission, 0);
|
||||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
continue;
|
||||
}
|
||||
if (permissionInfo.getProtection() == PROTECTION_DANGEROUS) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void removeUidsForUserLocked(int userHandle) {
|
||||
for (int i = mUidStates.size() - 1; i >= 0; --i) {
|
||||
final int uid = mUidStates.keyAt(i);
|
||||
|
||||
Reference in New Issue
Block a user