diff options
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; |