summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hani Kazmi <hanikazmi@google.com> 2025-03-11 04:28:26 -0700
committer Hani Kazmi <hanikazmi@google.com> 2025-03-12 03:48:56 -0700
commitca4ed8b90c43adaf7c0f37bc332ac7966c129813 (patch)
treee50ad51cf5be7ffe6c20127c86762a290030cac5
parent4f7b6c2494edbfcad7a75beb488e8ac92ac4f7a4 (diff)
[AAPM] Add framework stats atoms for Advanced Protection
Emit logs whenever advanced protection state changes, or the user is impacted (inferred by a dialogue being shown) Bug: 391635274 Change-Id: I2407c5be8fefe55a9c3b352cb48cbe2d3f407841 Test: AdvancedProtectionManagerTest AdvancedProtectionServiceTest Flag: android.security.aapm_api
-rw-r--r--core/java/android/security/advancedprotection/AdvancedProtectionManager.java37
-rw-r--r--core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl2
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java153
3 files changed, 189 insertions, 3 deletions
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 0b2239aa42b2..62b2bcf32442 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -124,6 +124,18 @@ public final class AdvancedProtectionManager {
@Retention(RetentionPolicy.SOURCE)
public @interface FeatureId {}
+ /** @hide */
+ public static String featureIdToString(@FeatureId int featureId) {
+ return switch(featureId) {
+ case FEATURE_ID_DISALLOW_CELLULAR_2G -> "DISALLOW_CELLULAR_2G";
+ case FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES -> "DISALLOW_INSTALL_UNKNOWN_SOURCES";
+ case FEATURE_ID_DISALLOW_USB -> "DISALLOW_USB";
+ case FEATURE_ID_DISALLOW_WEP -> "DISALLOW_WEP";
+ case FEATURE_ID_ENABLE_MTE -> "ENABLE_MTE";
+ default -> "UNKNOWN";
+ };
+ }
+
private static final Set<Integer> ALL_FEATURE_IDS = Set.of(
FEATURE_ID_DISALLOW_CELLULAR_2G,
FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
@@ -147,7 +159,7 @@ public final class AdvancedProtectionManager {
"android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
/**
- * A string extra used with {@link #createSupportIntent} to identify the feature that needs to
+ * An int extra used with {@link #createSupportIntent} to identify the feature that needs to
* show a support dialog explaining it was disabled by advanced protection.
*
* @hide */
@@ -156,7 +168,7 @@ public final class AdvancedProtectionManager {
"android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
/**
- * A string extra used with {@link #createSupportIntent} to identify the type of the action that
+ * An int extra used with {@link #createSupportIntent} to identify the type of the action that
* needs to be explained in the support dialog.
*
* @hide */
@@ -194,6 +206,16 @@ public final class AdvancedProtectionManager {
@Retention(RetentionPolicy.SOURCE)
public @interface SupportDialogType {}
+ /** @hide */
+ public static String supportDialogTypeToString(@SupportDialogType int type) {
+ return switch(type) {
+ case SUPPORT_DIALOG_TYPE_UNKNOWN -> "UNKNOWN";
+ case SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION -> "BLOCKED_INTERACTION";
+ case SUPPORT_DIALOG_TYPE_DISABLED_SETTING -> "DISABLED_SETTING";
+ default -> "UNKNOWN";
+ };
+ }
+
private static final Set<Integer> ALL_SUPPORT_DIALOG_TYPES = Set.of(
SUPPORT_DIALOG_TYPE_UNKNOWN,
SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
@@ -372,6 +394,17 @@ public final class AdvancedProtectionManager {
return createSupportIntent(featureId, type);
}
+ /** @hide */
+ @RequiresPermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
+ public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
+ boolean learnMoreClicked) {
+ try {
+ mService.logDialogShown(featureId, type, learnMoreClicked);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* A callback class for monitoring changes to Advanced Protection state
*
diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
index 1939f829c700..0fc0fbea2989 100644
--- a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
+++ b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
@@ -35,4 +35,6 @@ interface IAdvancedProtectionService {
void setAdvancedProtectionEnabled(boolean enabled);
@EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE")
List<AdvancedProtectionFeature> getAdvancedProtectionFeatures();
+ @EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE")
+ void logDialogShown(int featureId, int type, boolean learnMoreClicked);
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index 93fd2768d13e..6872ca9e46ee 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -23,7 +23,9 @@ import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.SharedPreferences;
import android.os.Binder;
+import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -32,14 +34,21 @@ import android.os.PermissionEnforcer;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.UserHandle;
import android.provider.Settings;
import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.security.advancedprotection.AdvancedProtectionManager;
+import android.security.advancedprotection.AdvancedProtectionManager.FeatureId;
+import android.security.advancedprotection.AdvancedProtectionManager.SupportDialogType;
import android.security.advancedprotection.IAdvancedProtectionCallback;
import android.security.advancedprotection.IAdvancedProtectionService;
+import android.security.advancedprotection.AdvancedProtectionProtoEnums;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -51,7 +60,9 @@ import com.android.server.security.advancedprotection.features.DisallowInstallUn
import com.android.server.security.advancedprotection.features.MemoryTaggingExtensionHook;
import com.android.server.security.advancedprotection.features.UsbDataAdvancedProtectionHook;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -61,6 +72,15 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
private static final int MODE_CHANGED = 0;
private static final int CALLBACK_ADDED = 1;
+ // Shared preferences keys
+ private static final String PREFERENCE = "advanced_protection_preference";
+ private static final String ENABLED_CHANGE_TIME = "enabled_change_time";
+ private static final String LAST_DIALOG_FEATURE_ID = "last_dialog_feature_id";
+ private static final String LAST_DIALOG_TYPE = "last_dialog_type";
+ private static final String LAST_DIALOG_HOURS_SINCE_ENABLED = "last_dialog_hours_since_enabled";
+ private static final String LAST_DIALOG_LEARN_MORE_CLICKED = "last_dialog_learn_more_clicked";
+ private static final long MILLIS_PER_HOUR = 60 * 60 * 1000;
+
private final Context mContext;
private final Handler mHandler;
private final AdvancedProtectionStore mStore;
@@ -72,6 +92,10 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
// For tracking only - not called on state change
private final ArrayList<AdvancedProtectionProvider> mProviders = new ArrayList<>();
+ // Used to store logging data
+ private SharedPreferences mSharedPreferences;
+ private boolean mEmitLogs = true;
+
private AdvancedProtectionService(@NonNull Context context) {
super(PermissionEnforcer.fromContext(context));
mContext = context;
@@ -126,6 +150,8 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
if (provider != null) {
mProviders.add(provider);
}
+
+ mEmitLogs = false;
}
@Override
@@ -178,7 +204,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
if (enabled != isAdvancedProtectionEnabledInternal()) {
mStore.store(enabled);
sendModeChanged(enabled);
- Slog.i(TAG, "Advanced protection is " + (enabled ? "enabled" : "disabled"));
+ logAdvancedProtectionEnabled(enabled);
}
}
} finally {
@@ -188,6 +214,91 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
@Override
@EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
+ public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
+ boolean learnMoreClicked) {
+ logDialogShown_enforcePermission();
+
+ if (!mEmitLogs) {
+ return;
+ }
+
+ int hoursSinceEnabled = hoursSinceLastChange();
+ FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_SUPPORT_DIALOG_DISPLAYED,
+ /*feature_id*/ featureIdToLogEnum(featureId),
+ /*dialogue_type*/ dialogueTypeToLogEnum(type),
+ /*learn_more_clicked*/ learnMoreClicked,
+ /*hours_since_last_change*/ hoursSinceEnabled);
+
+ getSharedPreferences().edit()
+ .putInt(LAST_DIALOG_FEATURE_ID, featureId)
+ .putInt(LAST_DIALOG_TYPE, type)
+ .putBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, learnMoreClicked)
+ .putInt(LAST_DIALOG_HOURS_SINCE_ENABLED, hoursSinceEnabled)
+ .apply();
+ }
+
+ private int featureIdToLogEnum(@FeatureId int featureId) {
+ switch (featureId) {
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_CELLULAR_2G;
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_USB:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_USB;
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_WEP;
+ case AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_ENABLE_MTE;
+ default:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_UNKNOWN;
+ }
+ }
+
+ private int dialogueTypeToLogEnum(@SupportDialogType int type) {
+ switch (type) {
+ case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN;
+ case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_BLOCKED_INTERACTION;
+ case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_DISABLED_SETTING;
+ default:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN;
+ }
+ }
+
+ private void logAdvancedProtectionEnabled(boolean enabled) {
+ if (!mEmitLogs) {
+ return;
+ }
+
+ Slog.i(TAG, "Advanced protection has been " + (enabled ? "enabled" : "disabled"));
+ SharedPreferences prefs = getSharedPreferences();
+ FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_STATE_CHANGED,
+ /*enabled*/ enabled,
+ /*hours_since_enabled*/ hoursSinceLastChange(),
+ /*last_dialog_feature_id*/ featureIdToLogEnum(
+ prefs.getInt(LAST_DIALOG_FEATURE_ID, -1)),
+ /*_type*/ dialogueTypeToLogEnum(prefs.getInt(LAST_DIALOG_TYPE, -1)),
+ /*_learn_more_clicked*/ prefs.getBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, false),
+ /*_hours_since_enabled*/ prefs.getInt(LAST_DIALOG_HOURS_SINCE_ENABLED, -1));
+ prefs.edit()
+ .putLong(ENABLED_CHANGE_TIME, System.currentTimeMillis())
+ .apply();
+ }
+
+ private int hoursSinceLastChange() {
+ int hoursSinceEnabled = -1;
+ long lastChangeTimeMillis = getSharedPreferences().getLong(ENABLED_CHANGE_TIME, -1);
+ if (lastChangeTimeMillis != -1) {
+ hoursSinceEnabled = (int)
+ ((System.currentTimeMillis() - lastChangeTimeMillis) / MILLIS_PER_HOUR);
+ }
+ return hoursSinceEnabled;
+ }
+
+ @Override
+ @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
public List<AdvancedProtectionFeature> getAdvancedProtectionFeatures() {
getAdvancedProtectionFeatures_enforcePermission();
List<AdvancedProtectionFeature> features = new ArrayList<>();
@@ -213,6 +324,30 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
.exec(this, in, out, err, args, callback, resultReceiver);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+ writer.println("AdvancedProtectionService");
+ writer.println(" isAdvancedProtectionEnabled: " + isAdvancedProtectionEnabledInternal());
+ writer.println(" mHooks.size(): " + mHooks.size());
+ writer.println(" mCallbacks.size(): " + mCallbacks.size());
+ writer.println(" mProviders.size(): " + mProviders.size());
+
+ writer.println("Hooks: ");
+ mHooks.stream().forEach(hook -> {
+ writer.println(" " + hook.getClass().getSimpleName() +
+ " available: " + hook.isAvailable());
+ });
+ writer.println(" Providers: ");
+ mProviders.stream().forEach(provider -> {
+ writer.println(" " + provider.getClass().getSimpleName());
+ provider.getFeatures().stream().forEach(feature -> {
+ writer.println(" " + feature.getClass().getSimpleName());
+ });
+ });
+ writer.println(" mSharedPreferences: " + getSharedPreferences().getAll());
+ }
+
void sendModeChanged(boolean enabled) {
Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused */ -1)
.sendToTarget();
@@ -224,6 +359,22 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
.sendToTarget();
}
+ private SharedPreferences getSharedPreferences() {
+ if (mSharedPreferences == null) {
+ initSharedPreferences();
+ }
+ return mSharedPreferences;
+ }
+
+ private synchronized void initSharedPreferences() {
+ if (mSharedPreferences == null) {
+ Context deviceContext = mContext.createDeviceProtectedStorageContext();
+ File sharedPrefs = new File(Environment.getDataSystemDirectory(), PREFERENCE);
+ mSharedPreferences = deviceContext.getSharedPreferences(sharedPrefs,
+ Context.MODE_PRIVATE);
+ }
+ }
+
public static final class Lifecycle extends SystemService {
private final AdvancedProtectionService mService;