Merge "Atom for when fgs accesses appop" into rvc-dev am: 10398d29f3
Change-Id: I05bd0d910df7d69a6a58cf32a4ebfdab43612e1a
This commit is contained in:
@@ -393,6 +393,8 @@ message Atom {
|
||||
WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"];
|
||||
AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"];
|
||||
SnapshotMergeReported snapshot_merge_reported = 255;
|
||||
ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended =
|
||||
256 [(module) = "framework"];
|
||||
SdkExtensionStatus sdk_extension_status = 354;
|
||||
}
|
||||
|
||||
@@ -3285,12 +3287,12 @@ message OverlayStateChanged {
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Logs foreground service starts and stops.
|
||||
* Note that this is not when a service starts or stops, but when it is
|
||||
* considered foreground.
|
||||
* Logged from
|
||||
* //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
|
||||
* frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
|
||||
*/
|
||||
message ForegroundServiceStateChanged {
|
||||
optional int32 uid = 1 [(is_uid) = true];
|
||||
@@ -3302,6 +3304,49 @@ message ForegroundServiceStateChanged {
|
||||
EXIT = 2;
|
||||
}
|
||||
optional State state = 3;
|
||||
|
||||
// Whether the fgs is allowed while-in-use permissions, i.e. is considered 'in-use' to the user.
|
||||
// (If the fgs was started while the app wasn't TOP it usually will be denied these permissions)
|
||||
optional bool allow_while_in_use_permission = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the number of times a uid accesses a sensitive AppOp during a foreground service session.
|
||||
* A foreground service session is any continuous period during which the uid holds at least one
|
||||
* foreground service; the atom will be pushed when the uid no longer holds any foreground services.
|
||||
* Accesses initiated while the uid is in the TOP state are ignored.
|
||||
* Sessions with no attempted accesses are not logged.
|
||||
* Logged from
|
||||
* frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
|
||||
*/
|
||||
message ForegroundServiceAppOpSessionEnded {
|
||||
optional int32 uid = 1 [(is_uid) = true];
|
||||
|
||||
// The operation's name.
|
||||
// To the extent possible, preserve the mapping from AppOpsManager.OP_ constants.
|
||||
// Only these named ops are actually logged.
|
||||
enum AppOpName {
|
||||
OP_NONE = -1; // Also represents UNKNOWN.
|
||||
OP_COARSE_LOCATION = 0;
|
||||
OP_FINE_LOCATION = 1;
|
||||
OP_CAMERA = 26;
|
||||
OP_RECORD_AUDIO = 27;
|
||||
}
|
||||
optional AppOpName app_op_name = 2 [default = OP_NONE];
|
||||
|
||||
// The uid's permission mode for accessing the AppOp during this fgs session.
|
||||
enum Mode {
|
||||
MODE_UNKNOWN = 0;
|
||||
MODE_ALLOWED = 1; // Always allowed
|
||||
MODE_IGNORED = 2; // Denied
|
||||
MODE_FOREGROUND = 3; // Allow-while-in-use (or allowed-one-time)
|
||||
}
|
||||
optional Mode app_op_mode = 3;
|
||||
|
||||
// Number of times this AppOp was requested and allowed.
|
||||
optional int32 count_ops_accepted = 4;
|
||||
// Number of times this AppOp was requested but denied.
|
||||
optional int32 count_ops_rejected = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6910,11 +6910,7 @@ public class AppOpsManager {
|
||||
* Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
|
||||
*/
|
||||
public int unsafeCheckOpRaw(@NonNull String op, int uid, @NonNull String packageName) {
|
||||
try {
|
||||
return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return unsafeCheckOpRawNoThrow(op, uid, packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6923,8 +6919,17 @@ public class AppOpsManager {
|
||||
* {@link #MODE_FOREGROUND}.
|
||||
*/
|
||||
public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
|
||||
return unsafeCheckOpRawNoThrow(strOpToOp(op), uid, packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <em>raw</em> mode associated with the op.
|
||||
* Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
|
||||
* @hide
|
||||
*/
|
||||
public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
|
||||
try {
|
||||
return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
|
||||
return mService.checkOperationRaw(op, uid, packageName);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_E
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
@@ -88,11 +89,13 @@ import android.util.EventLog;
|
||||
import android.util.PrintWriterPrinter;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.TimeUtils;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.webkit.WebViewZygote;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.app.procstats.ServiceState;
|
||||
import com.android.internal.messages.nano.SystemMessageProto;
|
||||
import com.android.internal.notification.SystemNotificationChannels;
|
||||
@@ -177,6 +180,10 @@ public final class ActiveServices {
|
||||
/** Temporary list for holding the results of calls to {@link #collectPackageServicesLocked} */
|
||||
private ArrayList<ServiceRecord> mTmpCollectionResults = null;
|
||||
|
||||
/** Mapping from uid to their foreground service AppOpCallbacks (if they have one). */
|
||||
@GuardedBy("mAm")
|
||||
private final SparseArray<AppOpCallback> mFgsAppOpCallbacks = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* For keeping ActiveForegroundApps retaining state while the screen is off.
|
||||
*/
|
||||
@@ -1455,7 +1462,9 @@ public final class ActiveServices {
|
||||
null, true, false, "");
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
|
||||
r.appInfo.uid, r.shortInstanceName,
|
||||
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
|
||||
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
|
||||
r.mAllowWhileInUsePermissionInFgs);
|
||||
registerAppOpCallbackLocked(r);
|
||||
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
|
||||
}
|
||||
r.postNotification();
|
||||
@@ -1504,9 +1513,11 @@ public final class ActiveServices {
|
||||
mAm.mAppOpsService.finishOperation(
|
||||
AppOpsManager.getToken(mAm.mAppOpsService),
|
||||
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
|
||||
unregisterAppOpCallbackLocked(r);
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
|
||||
r.appInfo.uid, r.shortInstanceName,
|
||||
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
|
||||
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
|
||||
r.mAllowWhileInUsePermissionInFgs);
|
||||
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
|
||||
if (r.app != null) {
|
||||
mAm.updateLruProcessLocked(r.app, false, null);
|
||||
@@ -1527,6 +1538,207 @@ public final class ActiveServices {
|
||||
}
|
||||
}
|
||||
|
||||
/** Registers an AppOpCallback for monitoring special AppOps for this foreground service. */
|
||||
private void registerAppOpCallbackLocked(@NonNull ServiceRecord r) {
|
||||
if (r.app == null) {
|
||||
return;
|
||||
}
|
||||
final int uid = r.appInfo.uid;
|
||||
AppOpCallback callback = mFgsAppOpCallbacks.get(uid);
|
||||
if (callback == null) {
|
||||
callback = new AppOpCallback(r.app, mAm.getAppOpsManager());
|
||||
mFgsAppOpCallbacks.put(uid, callback);
|
||||
}
|
||||
callback.registerLocked();
|
||||
}
|
||||
|
||||
/** Unregisters a foreground service's AppOpCallback. */
|
||||
private void unregisterAppOpCallbackLocked(@NonNull ServiceRecord r) {
|
||||
final int uid = r.appInfo.uid;
|
||||
final AppOpCallback callback = mFgsAppOpCallbacks.get(uid);
|
||||
if (callback != null) {
|
||||
callback.unregisterLocked();
|
||||
if (callback.isObsoleteLocked()) {
|
||||
mFgsAppOpCallbacks.remove(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For monitoring when {@link #LOGGED_AP_OPS} AppOps occur by an app while it is holding
|
||||
* at least one foreground service and is not also in the TOP state.
|
||||
* Once the uid no longer holds any foreground services, this callback becomes stale
|
||||
* (marked by {@link #isObsoleteLocked()}) and must no longer be used.
|
||||
*
|
||||
* Methods that end in Locked should only be called while the mAm lock is held.
|
||||
*/
|
||||
private static final class AppOpCallback {
|
||||
/** AppOps that should be logged if they occur during a foreground service. */
|
||||
private static final int[] LOGGED_AP_OPS = new int[] {
|
||||
AppOpsManager.OP_COARSE_LOCATION,
|
||||
AppOpsManager.OP_FINE_LOCATION,
|
||||
AppOpsManager.OP_RECORD_AUDIO,
|
||||
AppOpsManager.OP_CAMERA
|
||||
};
|
||||
|
||||
private final ProcessRecord mProcessRecord;
|
||||
|
||||
/** Count of acceptances per appop (for LOGGED_AP_OPS) during this fgs session. */
|
||||
@GuardedBy("mCounterLock")
|
||||
private final SparseIntArray mAcceptedOps = new SparseIntArray();
|
||||
/** Count of rejections per appop (for LOGGED_AP_OPS) during this fgs session. */
|
||||
@GuardedBy("mCounterLock")
|
||||
private final SparseIntArray mRejectedOps = new SparseIntArray();
|
||||
|
||||
/** Lock for the purposes of mAcceptedOps and mRejectedOps. */
|
||||
private final Object mCounterLock = new Object();
|
||||
|
||||
/**
|
||||
* AppOp Mode (e.g. {@link AppOpsManager#MODE_ALLOWED} per op.
|
||||
* This currently cannot change without the process being killed, so they are constants.
|
||||
*/
|
||||
private final SparseIntArray mAppOpModes = new SparseIntArray();
|
||||
|
||||
/**
|
||||
* Number of foreground services currently associated with this AppOpCallback (i.e.
|
||||
* currently held for this uid).
|
||||
*/
|
||||
@GuardedBy("mAm")
|
||||
private int mNumFgs = 0;
|
||||
|
||||
/**
|
||||
* Indicates that this Object is stale and must not be used.
|
||||
* Specifically, when mNumFgs decreases down to 0, the callbacks will be unregistered and
|
||||
* this AppOpCallback is unusable.
|
||||
*/
|
||||
@GuardedBy("mAm")
|
||||
private boolean mDestroyed = false;
|
||||
|
||||
private final AppOpsManager mAppOpsManager;
|
||||
|
||||
AppOpCallback(@NonNull ProcessRecord r, @NonNull AppOpsManager appOpsManager) {
|
||||
mProcessRecord = r;
|
||||
mAppOpsManager = appOpsManager;
|
||||
for (int op : LOGGED_AP_OPS) {
|
||||
int mode = appOpsManager.unsafeCheckOpRawNoThrow(op, r.uid, r.info.packageName);
|
||||
mAppOpModes.put(op, mode);
|
||||
}
|
||||
}
|
||||
|
||||
private final AppOpsManager.OnOpNotedListener mOpNotedCallback =
|
||||
new AppOpsManager.OnOpNotedListener() {
|
||||
@Override
|
||||
public void onOpNoted(int op, int uid, String pkgName, int result) {
|
||||
if (uid == mProcessRecord.uid && isNotTop()) {
|
||||
incrementOpCount(op, result == AppOpsManager.MODE_ALLOWED);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final AppOpsManager.OnOpActiveChangedInternalListener mOpActiveCallback =
|
||||
new AppOpsManager.OnOpActiveChangedInternalListener() {
|
||||
@Override
|
||||
public void onOpActiveChanged(int op, int uid, String pkgName, boolean active) {
|
||||
if (uid == mProcessRecord.uid && active && isNotTop()) {
|
||||
incrementOpCount(op, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private boolean isNotTop() {
|
||||
return mProcessRecord.getCurProcState() != ActivityManager.PROCESS_STATE_TOP;
|
||||
}
|
||||
|
||||
private void incrementOpCount(int op, boolean allowed) {
|
||||
synchronized (mCounterLock) {
|
||||
final SparseIntArray counter = allowed ? mAcceptedOps : mRejectedOps;
|
||||
final int index = counter.indexOfKey(op);
|
||||
if (index < 0) {
|
||||
counter.put(op, 1);
|
||||
} else {
|
||||
counter.setValueAt(index, counter.valueAt(index) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void registerLocked() {
|
||||
if (isObsoleteLocked()) {
|
||||
Slog.wtf(TAG, "Trying to register on a stale AppOpCallback.");
|
||||
return;
|
||||
}
|
||||
mNumFgs++;
|
||||
if (mNumFgs == 1) {
|
||||
mAppOpsManager.startWatchingNoted(LOGGED_AP_OPS, mOpNotedCallback);
|
||||
mAppOpsManager.startWatchingActive(LOGGED_AP_OPS, mOpActiveCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void unregisterLocked() {
|
||||
mNumFgs--;
|
||||
if (mNumFgs <= 0) {
|
||||
mDestroyed = true;
|
||||
logFinalValues();
|
||||
mAppOpsManager.stopWatchingNoted(mOpNotedCallback);
|
||||
mAppOpsManager.stopWatchingActive(mOpActiveCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that all foreground services for this uid are now over and the callback is
|
||||
* stale and must never be used again.
|
||||
*/
|
||||
boolean isObsoleteLocked() {
|
||||
return mDestroyed;
|
||||
}
|
||||
|
||||
private void logFinalValues() {
|
||||
synchronized (mCounterLock) {
|
||||
for (int op : LOGGED_AP_OPS) {
|
||||
final int acceptances = mAcceptedOps.get(op);
|
||||
final int rejections = mRejectedOps.get(op);
|
||||
if (acceptances > 0 || rejections > 0) {
|
||||
FrameworkStatsLog.write(
|
||||
FrameworkStatsLog.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED,
|
||||
mProcessRecord.uid, opToEnum(op),
|
||||
modeToEnum(mAppOpModes.get(op)),
|
||||
acceptances, rejections
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Maps AppOp mode to atoms.proto enum. */
|
||||
private static int modeToEnum(int mode) {
|
||||
switch (mode) {
|
||||
case AppOpsManager.MODE_ALLOWED: return FrameworkStatsLog
|
||||
.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_ALLOWED;
|
||||
case AppOpsManager.MODE_IGNORED: return FrameworkStatsLog
|
||||
.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_IGNORED;
|
||||
case AppOpsManager.MODE_FOREGROUND: return FrameworkStatsLog
|
||||
.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_FOREGROUND;
|
||||
default: return FrameworkStatsLog
|
||||
.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Maps AppOp op value to atoms.proto enum. */
|
||||
private static int opToEnum(int op) {
|
||||
switch (op) {
|
||||
case AppOpsManager.OP_COARSE_LOCATION: return FrameworkStatsLog
|
||||
.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_COARSE_LOCATION;
|
||||
case AppOpsManager.OP_FINE_LOCATION: return FrameworkStatsLog
|
||||
.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_FINE_LOCATION;
|
||||
case AppOpsManager.OP_CAMERA: return FrameworkStatsLog
|
||||
.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_CAMERA;
|
||||
case AppOpsManager.OP_RECORD_AUDIO: return FrameworkStatsLog
|
||||
.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_RECORD_AUDIO;
|
||||
default: return FrameworkStatsLog
|
||||
.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelForegroundNotificationLocked(ServiceRecord r) {
|
||||
if (r.foregroundId != 0) {
|
||||
// First check to see if this app has any other active foreground services
|
||||
@@ -3136,9 +3348,11 @@ public final class ActiveServices {
|
||||
mAm.mAppOpsService.finishOperation(
|
||||
AppOpsManager.getToken(mAm.mAppOpsService),
|
||||
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
|
||||
unregisterAppOpCallbackLocked(r);
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
|
||||
r.appInfo.uid, r.shortInstanceName,
|
||||
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
|
||||
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
|
||||
r.mAllowWhileInUsePermissionInFgs);
|
||||
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user