Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a60bd295 authored by yutingfang's avatar yutingfang
Browse files

Log AppOp access events in DiscreteRegistry

For a selected list of AppOps, log their access events(note, start, finish,
pause, resume) in DiscreteRegistry.

Flag: EXEMPT log only update
Bug: 365584286
Test: Run `statsd_testdrive 931` and operate device to trigger log
event, verify event is properly logged.

Change-Id: I82882742b222709916e5811032c0037dd3bf2a50
parent 5db09741
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -257,3 +257,11 @@ flag {
    description: "This flag is used to enable replacing permission BODY_SENSORS(and BODY_SENSORS_BACKGROUND) with granular health permission READ_HEART_RATE(and READ_HEALTH_DATA_IN_BACKGROUND)"
    bug: "364638912"
}

flag {
    name: "appop_access_tracking_logging_enabled"
    is_fixed_read_only: true
    namespace: "permissions"
    description: "Enables logging of the AppOp access tracking"
    bug: "365584286"
}
+22 −14
Original line number Diff line number Diff line
@@ -110,7 +110,8 @@ final class AttributedOp {

        mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime,
                AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
                AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE,
                DiscreteRegistry.ACCESS_TYPE_NOTE_OP);
    }

    /**
@@ -254,7 +255,7 @@ final class AttributedOp {
        if (isStarted) {
            mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                    parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
                    attributionFlags, attributionChainId);
                    attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP);
        }
    }

@@ -290,11 +291,16 @@ final class AttributedOp {
     * stopping in the HistoricalRegistry, but does not delete it.
     *
     * @param triggeredByUidStateChange If {@code true}, then this method operates as usual, except
     * that {@link AppOpsService#mActiveWatchers} will not be notified. This is currently only
     * used in {@link #onUidStateChanged(int)}, for the purpose of restarting (i.e.,
     * finishing then immediately starting again in the new uid state) the AttributedOp. In this
     * case, the caller is responsible for guaranteeing that either the AttributedOp is started
     * again or all {@link AppOpsService#mActiveWatchers} are notified that the AttributedOp is
     *                                  that {@link AppOpsService#mActiveWatchers} will not be
     *                                  notified. This is currently only
     *                                  used in {@link #onUidStateChanged(int)}, for the purpose of
     *                                  restarting (i.e.,
     *                                  finishing then immediately starting again in the new uid
     *                                  state) the AttributedOp. In this
     *                                  case, the caller is responsible for guaranteeing that either
     *                                  the AttributedOp is started
     *                                  again or all {@link AppOpsService#mActiveWatchers} are
     *                                  notified that the AttributedOp is
     *                                  finished.
     */
    @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
@@ -335,7 +341,9 @@ final class AttributedOp {
            mAppOpsService.mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
                    parent.packageName, persistentDeviceId, tag, event.getUidState(),
                    event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
                    event.getAttributionFlags(), event.getAttributionChainId());
                    event.getAttributionFlags(), event.getAttributionChainId(),
                    isPausing ? DiscreteRegistry.ACCESS_TYPE_PAUSE_OP
                            : DiscreteRegistry.ACCESS_TYPE_FINISH_OP);

            if (!isPausing) {
                mAppOpsService.mInProgressStartOpEventPool.release(event);
@@ -443,7 +451,7 @@ final class AttributedOp {
            mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                    parent.packageName, persistentDeviceId, tag, event.getUidState(),
                    event.getFlags(), startTime, event.getAttributionFlags(),
                    event.getAttributionChainId());
                    event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP);
            if (shouldSendActive) {
                mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
                        parent.packageName, tag, event.getVirtualDeviceId(), true,
@@ -867,9 +875,9 @@ final class AttributedOp {
                @Nullable String attributionTag, int virtualDeviceId, @NonNull Runnable onDeath,
                int proxyUid, @Nullable String proxyPackageName,
                @Nullable String proxyAttributionTag, @Nullable String proxyDeviceId,
                @AppOpsManager.UidState int uidState,
                @AppOpsManager.OpFlags int flags, @AppOpsManager.AttributionFlags
                int attributionFlags, int attributionChainId) throws RemoteException {
                @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
                @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId)
                throws RemoteException {

            InProgressStartOpEvent recycled = acquire();

+67 −4
Original line number Diff line number Diff line
@@ -32,13 +32,23 @@ 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_FLAG_TRUSTED_PROXY;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
import static android.app.AppOpsManager.OP_PROCESS_OUTGOING_CALLS;
import static android.app.AppOpsManager.OP_READ_ICC_SMS;
import static android.app.AppOpsManager.OP_READ_SMS;
import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING;
import static android.app.AppOpsManager.OP_SEND_SMS;
import static android.app.AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_WRITE_ICC_SMS;
import static android.app.AppOpsManager.OP_WRITE_SMS;
import static android.app.AppOpsManager.flagsToString;
import static android.app.AppOpsManager.getUidStateName;
import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
@@ -46,6 +56,7 @@ import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_I
import static java.lang.Long.min;
import static java.lang.Math.max;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -62,6 +73,7 @@ import android.util.Xml;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -72,6 +84,8 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
@@ -125,7 +139,6 @@ import java.util.Set;
 * relies on {@link HistoricalRegistry} for controlling that no calls are allowed until then. All
 * outside calls are going through {@link HistoricalRegistry}, where
 * {@link HistoricalRegistry#isPersistenceInitializedMLocked()} check is done.
 *
 */

final class DiscreteRegistry {
@@ -142,11 +155,40 @@ final class DiscreteRegistry {
            + OP_PHONE_CALL_MICROPHONE + "," + OP_PHONE_CALL_CAMERA + ","
            + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + "," + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
            + "," + OP_RESERVED_FOR_TESTING;
    private static final int[] sDiscreteOpsToLog =
            new int[]{OP_FINE_LOCATION, OP_COARSE_LOCATION, OP_EMERGENCY_LOCATION, OP_CAMERA,
                    OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, OP_PHONE_CALL_CAMERA,
                    OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, OP_READ_SMS,
                    OP_WRITE_SMS, OP_SEND_SMS, OP_READ_ICC_SMS, OP_WRITE_ICC_SMS,
                    OP_SMS_FINANCIAL_TRANSACTIONS, OP_SYSTEM_ALERT_WINDOW, OP_MONITOR_LOCATION,
                    OP_MONITOR_HIGH_POWER_LOCATION, OP_PROCESS_OUTGOING_CALLS,
            };
    private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(7).toMillis();
    private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
    private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION =
            Duration.ofMinutes(1).toMillis();

    static final int ACCESS_TYPE_NOTE_OP =
            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__NOTE_OP;
    static final int ACCESS_TYPE_START_OP =
            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__START_OP;
    static final int ACCESS_TYPE_FINISH_OP =
            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__FINISH_OP;
    static final int ACCESS_TYPE_PAUSE_OP =
            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__PAUSE_OP;
    static final int ACCESS_TYPE_RESUME_OP =
            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__RESUME_OP;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"ACCESS_TYPE_"}, value = {
            ACCESS_TYPE_NOTE_OP,
            ACCESS_TYPE_START_OP,
            ACCESS_TYPE_FINISH_OP,
            ACCESS_TYPE_PAUSE_OP,
            ACCESS_TYPE_RESUME_OP
    })
    public @interface AccessType {}

    private static long sDiscreteHistoryCutoff;
    private static long sDiscreteHistoryQuantization;
    private static int[] sDiscreteOps;
@@ -255,7 +297,23 @@ final class DiscreteRegistry {
    void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId, int op,
            @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
            @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
            @AccessType int accessType) {
        if (shouldLogAccess(op)) {
            int firstChar = 0;
            if (attributionTag != null && attributionTag.startsWith(packageName)) {
                firstChar = packageName.length();
                if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar)
                        == '.') {
                    firstChar++;
                }
            }
            FrameworkStatsLog.write(FrameworkStatsLog.APP_OP_ACCESS_TRACKED, uid, op, accessType,
                    uidState, flags, attributionFlags,
                    attributionTag == null ? null : attributionTag.substring(firstChar),
                    attributionChainId);
        }

        if (!isDiscreteOp(op, flags)) {
            return;
        }
@@ -1523,6 +1581,11 @@ final class DiscreteRegistry {
        return true;
    }

    private static boolean shouldLogAccess(int op) {
        return Flags.appopAccessTrackingLoggingEnabled()
                && ArrayUtils.contains(sDiscreteOpsToLog, op);
    }

    private static long discretizeTimeStamp(long timeStamp) {
        return timeStamp / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;

+6 −4
Original line number Diff line number Diff line
@@ -474,7 +474,8 @@ final class HistoricalRegistry {
    void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
            @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
            @OpFlags int flags, long accessTime,
            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
            @DiscreteRegistry.AccessType int accessType) {
        synchronized (mInMemoryLock) {
            if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                if (!isPersistenceInitializedMLocked()) {
@@ -487,7 +488,7 @@ final class HistoricalRegistry {

                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
                        attributionTag, flags, uidState, accessTime, -1, attributionFlags,
                        attributionChainId);
                        attributionChainId, accessType);
            }
        }
    }
@@ -510,7 +511,8 @@ final class HistoricalRegistry {
    void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
            @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
            @OpFlags int flags, long eventStartTime, long increment,
            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
            @DiscreteRegistry.AccessType int accessType) {
        synchronized (mInMemoryLock) {
            if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                if (!isPersistenceInitializedMLocked()) {
@@ -522,7 +524,7 @@ final class HistoricalRegistry {
                        attributionTag, uidState, flags, increment);
                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
                        attributionTag, flags, uidState, eventStartTime, increment,
                        attributionFlags, attributionChainId);
                        attributionFlags, attributionChainId, accessType);
            }
        }
    }
+4 −2
Original line number Diff line number Diff line
@@ -86,7 +86,8 @@ public class DiscreteAppOpPersistenceTest {
        int attributionChainId = AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;

        mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
                uidState, accessTime, duration, attributionFlags, attributionChainId);
                uidState, accessTime, duration, attributionFlags, attributionChainId,
                DiscreteRegistry.ACCESS_TYPE_FINISH_OP);

        // Verify in-memory object is correct
        fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
@@ -117,7 +118,8 @@ public class DiscreteAppOpPersistenceTest {
        int attributionChainId = 10;

        mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
                uidState, accessTime, duration, attributionFlags, attributionChainId);
                uidState, accessTime, duration, attributionFlags, attributionChainId,
                DiscreteRegistry.ACCESS_TYPE_START_OP);

        fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
                duration, uidState, opFlags, attributionFlags, attributionChainId);