Merge "Atom for when fgs accesses appop" into rvc-dev am: 10398d29f3

Change-Id: Idc59418b66e29913ec225efd119606c827699a15
This commit is contained in:
Automerger Merge Worker
2020-02-25 20:11:40 +00:00
3 changed files with 275 additions and 11 deletions

View File

@@ -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;
}
/**

View File

@@ -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();
}

View File

@@ -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);
}