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

Commit 0357db71 authored by mrulhania's avatar mrulhania
Browse files

Capture all runtime ops in discrete op database

discrete ops database have been capturing only a
few app ops (mic, camera and loc). With sqlite
change, this database will capture all runtime
app ops with few more additional ops.

Bug: 400502129
Test: atest android.app.appops.cts.DiscreteAppopsTest --iterations 10
Flag: EXEMPT sqlite is already flagged
Change-Id: I92bb355478c8bf335583d11920cffed665dc509e
parent 973c2d1a
Loading
Loading
Loading
Loading
+10 −1
Original line number Original line Diff line number Diff line
@@ -18,7 +18,6 @@ package android.app;




import static android.location.flags.Flags.FLAG_LOCATION_BYPASS;
import static android.location.flags.Flags.FLAG_LOCATION_BYPASS;
import static android.media.audio.Flags.roForegroundAudioControl;
import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER;
import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER;
import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
@@ -3480,6 +3479,16 @@ public class AppOpsManager {
        return opToPermission(strOpToOp(op));
        return opToPermission(strOpToOp(op));
    }
    }


    /**
     * Whether an app op is backed by a runtime permission or not.
     * @hide
     */
    public static boolean opIsRuntimePermission(int op) {
        if (op == OP_NONE) return false;

        return ArrayUtils.contains(RUNTIME_PERMISSION_OPS, op);
    }

    /**
    /**
     * Retrieve the user restriction associated with an operation, or null if there is not one.
     * Retrieve the user restriction associated with an operation, or null if there is not one.
     * @hide
     * @hide
+9 −0
Original line number Original line Diff line number Diff line
@@ -384,6 +384,15 @@ flag {
    bug: "377584611"
    bug: "377584611"
}
}


flag {
    name: "record_all_runtime_appops_sqlite"
    is_fixed_read_only: true
    is_exported: true
    namespace: "permissions"
    description: "Enables recording of all runtime app ops into SQlite"
    bug: "377584611"
}

flag {
flag {
    name: "ranging_permission_enabled"
    name: "ranging_permission_enabled"
    is_fixed_read_only: true
    is_fixed_read_only: true
+10 −1
Original line number Original line Diff line number Diff line
@@ -40,7 +40,16 @@ public class DiscreteOpsMigrationHelper {
    static void migrateDiscreteOpsToXml(DiscreteOpsSqlRegistry sqlRegistry,
    static void migrateDiscreteOpsToXml(DiscreteOpsSqlRegistry sqlRegistry,
            DiscreteOpsXmlRegistry xmlRegistry) {
            DiscreteOpsXmlRegistry xmlRegistry) {
        List<DiscreteOpsSqlRegistry.DiscreteOp> sqlOps = sqlRegistry.getAllDiscreteOps();
        List<DiscreteOpsSqlRegistry.DiscreteOp> sqlOps = sqlRegistry.getAllDiscreteOps();
        DiscreteOpsXmlRegistry.DiscreteOps xmlOps = getXmlDiscreteOps(sqlOps);

        // Only migrate configured discrete ops. Sqlite may contain all runtime ops, and more.
        List<DiscreteOpsSqlRegistry.DiscreteOp> filteredList = new ArrayList<>();
        for (DiscreteOpsSqlRegistry.DiscreteOp opEvent: sqlOps) {
            if (DiscreteOpsRegistry.isDiscreteOp(opEvent.getOpCode(), opEvent.getOpFlags())) {
                filteredList.add(opEvent);
            }
        }

        DiscreteOpsXmlRegistry.DiscreteOps xmlOps = getXmlDiscreteOps(filteredList);
        xmlRegistry.migrateSqliteData(xmlOps);
        xmlRegistry.migrateSqliteData(xmlOps);
        sqlRegistry.deleteDatabase();
        sqlRegistry.deleteDatabase();
    }
    }
+74 −11
Original line number Original line Diff line number Diff line
@@ -16,6 +16,9 @@


package com.android.server.appop;
package com.android.server.appop;


import static android.app.AppOpsManager.OP_ACCESS_ACCESSIBILITY;
import static android.app.AppOpsManager.OP_ACCESS_NOTIFICATIONS;
import static android.app.AppOpsManager.OP_BIND_ACCESSIBILITY_SERVICE;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
import static android.app.AppOpsManager.OP_EMERGENCY_LOCATION;
import static android.app.AppOpsManager.OP_EMERGENCY_LOCATION;
@@ -23,11 +26,13 @@ import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_FLAG_SELF;
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_PROXIED;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
import static android.app.AppOpsManager.OP_GPS;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
import static android.app.AppOpsManager.OP_PROCESS_OUTGOING_CALLS;
import static android.app.AppOpsManager.OP_PROCESS_OUTGOING_CALLS;
import static android.app.AppOpsManager.OP_READ_DEVICE_IDENTIFIERS;
import static android.app.AppOpsManager.OP_READ_HEART_RATE;
import static android.app.AppOpsManager.OP_READ_HEART_RATE;
import static android.app.AppOpsManager.OP_READ_ICC_SMS;
import static android.app.AppOpsManager.OP_READ_ICC_SMS;
import static android.app.AppOpsManager.OP_READ_OXYGEN_SATURATION;
import static android.app.AppOpsManager.OP_READ_OXYGEN_SATURATION;
@@ -37,6 +42,7 @@ 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_RECEIVE_SANDBOX_TRIGGER_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING;
import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING;
import static android.app.AppOpsManager.OP_RUN_IN_BACKGROUND;
import static android.app.AppOpsManager.OP_SEND_SMS;
import static android.app.AppOpsManager.OP_SEND_SMS;
import static android.app.AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS;
import static android.app.AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
@@ -51,14 +57,15 @@ import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.AppOpsManager;
import android.os.AsyncTask;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Build;
import android.permission.flags.Flags;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig;
import android.util.IntArray;
import android.util.Slog;
import android.util.Slog;


import com.android.internal.util.ArrayUtils;

import java.io.PrintWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Duration;
import java.util.Arrays;
import java.util.Date;
import java.util.Date;
import java.util.Set;
import java.util.Set;


@@ -95,13 +102,38 @@ abstract class DiscreteOpsRegistry {
    static final String PROPERTY_DISCRETE_HISTORY_QUANTIZATION =
    static final String PROPERTY_DISCRETE_HISTORY_QUANTIZATION =
            "discrete_history_quantization_millis";
            "discrete_history_quantization_millis";
    static final String PROPERTY_DISCRETE_FLAGS = "discrete_history_op_flags";
    static final String PROPERTY_DISCRETE_FLAGS = "discrete_history_op_flags";
    // Comma separated app ops list config for testing i.e. "1,2,3,4"
    static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist";
    static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist";
    static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
    // These ops are deemed important for detecting a malicious app, and are recorded.
    static final int[] IMPORTANT_OPS_FOR_SECURITY = new int[] {
            OP_GPS,
            OP_ACCESS_NOTIFICATIONS,
            OP_RUN_IN_BACKGROUND,
            OP_BIND_ACCESSIBILITY_SERVICE,
            OP_ACCESS_ACCESSIBILITY,
            OP_READ_DEVICE_IDENTIFIERS,
            OP_MONITOR_HIGH_POWER_LOCATION,
            OP_MONITOR_LOCATION
    };

    // These are additional ops, which are not backed by runtime permissions, but are recorded.
    static final int[] ADDITIONAL_DISCRETE_OPS = new int[] {
            OP_PHONE_CALL_MICROPHONE,
            OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
            OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
            OP_PHONE_CALL_CAMERA,
            OP_EMERGENCY_LOCATION,
            OP_RESERVED_FOR_TESTING
    };

    // Legacy ops captured in discrete database.
    private static final String LEGACY_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
            + "," + OP_EMERGENCY_LOCATION + "," + OP_CAMERA + "," + OP_RECORD_AUDIO + ","
            + "," + OP_EMERGENCY_LOCATION + "," + OP_CAMERA + "," + OP_RECORD_AUDIO + ","
            + OP_PHONE_CALL_MICROPHONE + "," + OP_PHONE_CALL_CAMERA + ","
            + OP_PHONE_CALL_MICROPHONE + "," + OP_PHONE_CALL_CAMERA + ","
            + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + "," + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
            + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + "," + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
            + "," + OP_READ_HEART_RATE + "," + OP_READ_OXYGEN_SATURATION + ","
            + "," + OP_READ_HEART_RATE + "," + OP_READ_OXYGEN_SATURATION + ","
            + OP_READ_SKIN_TEMPERATURE + "," + OP_RESERVED_FOR_TESTING;
            + OP_READ_SKIN_TEMPERATURE + "," + OP_RESERVED_FOR_TESTING;

    static final int[] sDiscreteOpsToLog =
    static final int[] sDiscreteOpsToLog =
            new int[]{OP_FINE_LOCATION, OP_COARSE_LOCATION, OP_EMERGENCY_LOCATION, OP_CAMERA,
            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_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, OP_PHONE_CALL_CAMERA,
@@ -121,7 +153,7 @@ abstract class DiscreteOpsRegistry {
    // in case of duplicate op events.
    // in case of duplicate op events.
    static long sDiscreteHistoryQuantization;
    static long sDiscreteHistoryQuantization;


    static int[] sDiscreteOps;
    static int[] sDiscreteOps = new int[0];
    static int sDiscreteFlags;
    static int sDiscreteFlags;


    static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED
    static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED
@@ -191,7 +223,7 @@ abstract class DiscreteOpsRegistry {
    }
    }


    static boolean isDiscreteOp(int op, @AppOpsManager.OpFlags int flags) {
    static boolean isDiscreteOp(int op, @AppOpsManager.OpFlags int flags) {
        if (!ArrayUtils.contains(sDiscreteOps, op)) {
        if (Arrays.binarySearch(sDiscreteOps, op) < 0) {
            return false;
            return false;
        }
        }
        if ((flags & (sDiscreteFlags)) == 0) {
        if ((flags & (sDiscreteFlags)) == 0) {
@@ -221,11 +253,42 @@ abstract class DiscreteOpsRegistry {
        } else {
        } else {
            sDiscreteHistoryQuantization = DEFAULT_DISCRETE_HISTORY_QUANTIZATION;
            sDiscreteHistoryQuantization = DEFAULT_DISCRETE_HISTORY_QUANTIZATION;
        }
        }
        sDiscreteFlags = p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS) ? sDiscreteFlags =
        sDiscreteFlags = p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS)
                p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE) : OP_FLAGS_DISCRETE;
                ? p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE) : OP_FLAGS_DISCRETE;
        sDiscreteOps = p.getKeyset().contains(PROPERTY_DISCRETE_OPS_LIST) ? parseOpsList(
        String opsListConfig = p.getString(PROPERTY_DISCRETE_OPS_LIST, null);
                p.getString(PROPERTY_DISCRETE_OPS_LIST, DEFAULT_DISCRETE_OPS)) : parseOpsList(
        sDiscreteOps = opsListConfig == null ? getDefaultOpsList() : parseOpsList(opsListConfig);
                DEFAULT_DISCRETE_OPS);

        Arrays.sort(sDiscreteOps);
    }

    // App ops backed by runtime/dangerous permissions.
    private static IntArray getRuntimePermissionOps() {
        IntArray runtimeOps = new IntArray();
        for (int op = 0; op < AppOpsManager._NUM_OP; op++) {
            if (AppOpsManager.opIsRuntimePermission(op)) {
                runtimeOps.add(op);
            }
        }
        return runtimeOps;
    }

    /**
     * @return an array of app ops captured into discrete database.
     */
    private static int[] getDefaultOpsList() {
        if (!(Flags.recordAllRuntimeAppopsSqlite() && Flags.enableSqliteAppopsAccesses())) {
            return getDefaultLegacyOps();
        }

        IntArray discreteOpsArray = getRuntimePermissionOps();
        discreteOpsArray.addAll(IMPORTANT_OPS_FOR_SECURITY);
        discreteOpsArray.addAll(ADDITIONAL_DISCRETE_OPS);

        return discreteOpsArray.toArray();
    }

    private static int[] getDefaultLegacyOps() {
        return parseOpsList(LEGACY_OPS);
    }
    }


    private static int[] parseOpsList(String opsList) {
    private static int[] parseOpsList(String opsList) {
@@ -243,7 +306,7 @@ abstract class DiscreteOpsRegistry {
            }
            }
        } catch (NumberFormatException e) {
        } catch (NumberFormatException e) {
            Slog.e(TAG, "Failed to parse Discrete ops list: " + e.getMessage());
            Slog.e(TAG, "Failed to parse Discrete ops list: " + e.getMessage());
            return parseOpsList(DEFAULT_DISCRETE_OPS);
            return getDefaultOpsList();
        }
        }
        return result;
        return result;
    }
    }