1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.security.advancedprotection; 18 19 import static android.provider.Settings.Secure.ADVANCED_PROTECTION_MODE; 20 import static android.provider.Settings.Secure.AAPM_USB_DATA_PROTECTION; 21 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; 22 23 import android.Manifest; 24 import android.annotation.EnforcePermission; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.app.StatsManager; 28 import android.content.Context; 29 import android.content.SharedPreferences; 30 import android.content.pm.UserInfo; 31 import android.os.Binder; 32 import android.os.Environment; 33 import android.os.Handler; 34 import android.os.IBinder; 35 import android.os.Looper; 36 import android.os.Message; 37 import android.os.PermissionEnforcer; 38 import android.os.RemoteException; 39 import android.os.ResultReceiver; 40 import android.os.ShellCallback; 41 import android.os.UserHandle; 42 import android.provider.Settings; 43 import android.security.advancedprotection.AdvancedProtectionFeature; 44 import android.security.advancedprotection.AdvancedProtectionManager; 45 import android.security.advancedprotection.AdvancedProtectionManager.FeatureId; 46 import android.security.advancedprotection.AdvancedProtectionManager.SupportDialogType; 47 import android.security.advancedprotection.IAdvancedProtectionCallback; 48 import android.security.advancedprotection.IAdvancedProtectionService; 49 import android.security.advancedprotection.AdvancedProtectionProtoEnums; 50 import android.util.ArrayMap; 51 import android.util.Slog; 52 import android.util.StatsEvent; 53 54 import com.android.internal.annotations.VisibleForTesting; 55 import com.android.internal.util.DumpUtils; 56 import com.android.internal.util.FrameworkStatsLog; 57 import com.android.server.FgThread; 58 import com.android.server.LocalServices; 59 import com.android.server.SystemService; 60 import com.android.server.pm.UserManagerInternal; 61 import com.android.server.security.advancedprotection.features.AdvancedProtectionHook; 62 import com.android.server.security.advancedprotection.features.AdvancedProtectionProvider; 63 import com.android.server.security.advancedprotection.features.DisallowCellular2GAdvancedProtectionHook; 64 import com.android.server.security.advancedprotection.features.DisallowInstallUnknownSourcesAdvancedProtectionHook; 65 import com.android.server.security.advancedprotection.features.MemoryTaggingExtensionHook; 66 import com.android.server.security.advancedprotection.features.UsbDataAdvancedProtectionHook; 67 import com.android.server.security.advancedprotection.features.DisallowWepAdvancedProtectionProvider; 68 69 import java.io.File; 70 import java.io.FileDescriptor; 71 import java.io.PrintWriter; 72 import java.util.ArrayList; 73 import java.util.List; 74 import java.util.Set; 75 76 /** @hide */ 77 public class AdvancedProtectionService extends IAdvancedProtectionService.Stub { 78 private static final String TAG = "AdvancedProtectionService"; 79 private static final int MODE_CHANGED = 0; 80 private static final int CALLBACK_ADDED = 1; 81 82 // Shared preferences keys 83 private static final String PREFERENCE = "advanced_protection_preference"; 84 private static final String ENABLED_CHANGE_TIME = "enabled_change_time"; 85 private static final String LAST_DIALOG_FEATURE_ID = "last_dialog_feature_id"; 86 private static final String LAST_DIALOG_TYPE = "last_dialog_type"; 87 private static final String LAST_DIALOG_HOURS_SINCE_ENABLED = "last_dialog_hours_since_enabled"; 88 private static final String LAST_DIALOG_LEARN_MORE_CLICKED = "last_dialog_learn_more_clicked"; 89 private static final long MILLIS_PER_HOUR = 60 * 60 * 1000; 90 91 private final Context mContext; 92 private final Handler mHandler; 93 private final AdvancedProtectionStore mStore; 94 private final UserManagerInternal mUserManager; 95 96 // Features living with the service - their code will be executed when state changes 97 private final ArrayList<AdvancedProtectionHook> mHooks = new ArrayList<>(); 98 // External features - they will be called on state change 99 private final ArrayMap<IBinder, IAdvancedProtectionCallback> mCallbacks = new ArrayMap<>(); 100 // For tracking only - not called on state change 101 private final ArrayList<AdvancedProtectionProvider> mProviders = new ArrayList<>(); 102 103 // Used to store logging data 104 private SharedPreferences mSharedPreferences; 105 private boolean mEmitLogs = true; 106 AdvancedProtectionService(@onNull Context context)107 private AdvancedProtectionService(@NonNull Context context) { 108 super(PermissionEnforcer.fromContext(context)); 109 mContext = context; 110 mHandler = new AdvancedProtectionHandler(FgThread.get().getLooper()); 111 mStore = new AdvancedProtectionStore(mContext); 112 mUserManager = LocalServices.getService(UserManagerInternal.class); 113 } 114 initFeatures(boolean enabled)115 private void initFeatures(boolean enabled) { 116 if (android.security.Flags.aapmFeatureDisableInstallUnknownSources()) { 117 try { 118 mHooks.add(new DisallowInstallUnknownSourcesAdvancedProtectionHook(mContext, enabled)); 119 } catch (Exception e) { 120 Slog.e(TAG, "Failed to initialize DisallowInstallUnknownSources", e); 121 } 122 } 123 if (android.security.Flags.aapmFeatureMemoryTaggingExtension()) { 124 try { 125 mHooks.add(new MemoryTaggingExtensionHook(mContext, enabled)); 126 } catch (Exception e) { 127 Slog.e(TAG, "Failed to initialize MemoryTaggingExtension", e); 128 } 129 } 130 if (android.security.Flags.aapmFeatureDisableCellular2g()) { 131 try { 132 mHooks.add(new DisallowCellular2GAdvancedProtectionHook(mContext, enabled)); 133 } catch (Exception e) { 134 Slog.e(TAG, "Failed to initialize DisallowCellular2g", e); 135 } 136 } 137 if (android.security.Flags.aapmFeatureUsbDataProtection() 138 // Usb data protection is enabled by default 139 && mStore.retrieveInt(AAPM_USB_DATA_PROTECTION, AdvancedProtectionStore.ON) 140 == AdvancedProtectionStore.ON) { 141 try { 142 mHooks.add(new UsbDataAdvancedProtectionHook(mContext, enabled)); 143 } catch (Exception e) { 144 Slog.e(TAG, "Failed to initialize UsbDataAdvancedProtection", e); 145 } 146 } 147 148 mProviders.add(new DisallowWepAdvancedProtectionProvider()); 149 } 150 initLogging()151 private void initLogging() { 152 StatsManager statsManager = mContext.getSystemService(StatsManager.class); 153 statsManager.setPullAtomCallback( 154 FrameworkStatsLog.ADVANCED_PROTECTION_STATE_INFO, 155 null, // use default PullAtomMetadata values 156 DIRECT_EXECUTOR, 157 new AdvancedProtectionStatePullAtomCallback()); 158 } 159 160 // Only for tests 161 @VisibleForTesting AdvancedProtectionService( @onNull Context context, @NonNull AdvancedProtectionStore store, @NonNull UserManagerInternal userManager, @NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer, @Nullable AdvancedProtectionHook hook, @Nullable AdvancedProtectionProvider provider)162 AdvancedProtectionService( 163 @NonNull Context context, 164 @NonNull AdvancedProtectionStore store, 165 @NonNull UserManagerInternal userManager, 166 @NonNull Looper looper, 167 @NonNull PermissionEnforcer permissionEnforcer, 168 @Nullable AdvancedProtectionHook hook, 169 @Nullable AdvancedProtectionProvider provider) { 170 super(permissionEnforcer); 171 mContext = context; 172 mStore = store; 173 mUserManager = userManager; 174 mHandler = new AdvancedProtectionHandler(looper); 175 if (hook != null) { 176 mHooks.add(hook); 177 } 178 179 if (provider != null) { 180 mProviders.add(provider); 181 } 182 183 mEmitLogs = false; 184 } 185 186 @Override 187 @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) isAdvancedProtectionEnabled()188 public boolean isAdvancedProtectionEnabled() { 189 isAdvancedProtectionEnabled_enforcePermission(); 190 final long identity = Binder.clearCallingIdentity(); 191 try { 192 return isAdvancedProtectionEnabledInternal(); 193 } finally { 194 Binder.restoreCallingIdentity(identity); 195 } 196 } 197 198 // Without permission check isAdvancedProtectionEnabledInternal()199 private boolean isAdvancedProtectionEnabledInternal() { 200 return mStore.retrieveAdvancedProtectionModeEnabled(); 201 } 202 203 @Override 204 @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) registerAdvancedProtectionCallback(@onNull IAdvancedProtectionCallback callback)205 public void registerAdvancedProtectionCallback(@NonNull IAdvancedProtectionCallback callback) 206 throws RemoteException { 207 registerAdvancedProtectionCallback_enforcePermission(); 208 IBinder b = callback.asBinder(); 209 b.linkToDeath(new DeathRecipient(b), 0); 210 synchronized (mCallbacks) { 211 mCallbacks.put(b, callback); 212 sendCallbackAdded(isAdvancedProtectionEnabledInternal(), callback); 213 } 214 } 215 216 @Override 217 @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) unregisterAdvancedProtectionCallback( @onNull IAdvancedProtectionCallback callback)218 public void unregisterAdvancedProtectionCallback( 219 @NonNull IAdvancedProtectionCallback callback) { 220 unregisterAdvancedProtectionCallback_enforcePermission(); 221 synchronized (mCallbacks) { 222 mCallbacks.remove(callback.asBinder()); 223 } 224 } 225 226 @Override 227 @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) setAdvancedProtectionEnabled(boolean enabled)228 public void setAdvancedProtectionEnabled(boolean enabled) { 229 setAdvancedProtectionEnabled_enforcePermission(); 230 final UserHandle user = Binder.getCallingUserHandle(); 231 final long identity = Binder.clearCallingIdentity(); 232 try { 233 enforceAdminUser(user); 234 synchronized (mCallbacks) { 235 if (enabled != isAdvancedProtectionEnabledInternal()) { 236 mStore.storeAdvancedProtectionModeEnabled(enabled); 237 sendModeChanged(enabled); 238 logAdvancedProtectionEnabled(enabled); 239 } 240 } 241 } finally { 242 Binder.restoreCallingIdentity(identity); 243 } 244 } 245 setUsbDataProtectionEnabled(boolean enabled)246 public void setUsbDataProtectionEnabled(boolean enabled) { 247 int value = enabled ? AdvancedProtectionStore.ON 248 : AdvancedProtectionStore.OFF; 249 setAdvancedProtectionSubSettingInt(AAPM_USB_DATA_PROTECTION, value); 250 } 251 setAdvancedProtectionSubSettingInt(String key, int value)252 private void setAdvancedProtectionSubSettingInt(String key, int value) { 253 final long identity = Binder.clearCallingIdentity(); 254 try { 255 synchronized (mCallbacks) { 256 mStore.storeInt(key, value); 257 Slog.i(TAG, "Advanced protection: subsetting" + key + " is " + value); 258 } 259 } finally { 260 Binder.restoreCallingIdentity(identity); 261 } 262 } 263 isUsbDataProtectionEnabled()264 public boolean isUsbDataProtectionEnabled() { 265 final long identity = Binder.clearCallingIdentity(); 266 try { 267 return mStore.retrieveInt(AAPM_USB_DATA_PROTECTION, AdvancedProtectionStore.ON) 268 == AdvancedProtectionStore.ON; 269 } finally { 270 Binder.restoreCallingIdentity(identity); 271 } 272 } 273 274 @Override 275 @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) logDialogShown(@eatureId int featureId, @SupportDialogType int type, boolean learnMoreClicked)276 public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type, 277 boolean learnMoreClicked) { 278 logDialogShown_enforcePermission(); 279 280 if (!mEmitLogs) { 281 return; 282 } 283 284 int hoursSinceEnabled = hoursSinceLastChange(); 285 FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_SUPPORT_DIALOG_DISPLAYED, 286 /*feature_id*/ featureIdToLogEnum(featureId), 287 /*dialogue_type*/ dialogueTypeToLogEnum(type), 288 /*learn_more_clicked*/ learnMoreClicked, 289 /*hours_since_last_change*/ hoursSinceEnabled); 290 291 getSharedPreferences().edit() 292 .putInt(LAST_DIALOG_FEATURE_ID, featureId) 293 .putInt(LAST_DIALOG_TYPE, type) 294 .putBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, learnMoreClicked) 295 .putInt(LAST_DIALOG_HOURS_SINCE_ENABLED, hoursSinceEnabled) 296 .apply(); 297 } 298 featureIdToLogEnum(@eatureId int featureId)299 private int featureIdToLogEnum(@FeatureId int featureId) { 300 switch (featureId) { 301 case AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G: 302 return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_CELLULAR_2G; 303 case AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES: 304 return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES; 305 case AdvancedProtectionManager.FEATURE_ID_DISALLOW_USB: 306 return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_USB; 307 case AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP: 308 return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_WEP; 309 case AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE: 310 return AdvancedProtectionProtoEnums.FEATURE_ID_ENABLE_MTE; 311 default: 312 return AdvancedProtectionProtoEnums.FEATURE_ID_UNKNOWN; 313 } 314 } 315 dialogueTypeToLogEnum(@upportDialogType int type)316 private int dialogueTypeToLogEnum(@SupportDialogType int type) { 317 switch (type) { 318 case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN: 319 return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN; 320 case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION: 321 return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_BLOCKED_INTERACTION; 322 case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING: 323 return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_DISABLED_SETTING; 324 default: 325 return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN; 326 } 327 } 328 logAdvancedProtectionEnabled(boolean enabled)329 private void logAdvancedProtectionEnabled(boolean enabled) { 330 if (!mEmitLogs) { 331 return; 332 } 333 334 Slog.i(TAG, "Advanced protection has been " + (enabled ? "enabled" : "disabled")); 335 SharedPreferences prefs = getSharedPreferences(); 336 FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_STATE_CHANGED, 337 /*enabled*/ enabled, 338 /*hours_since_enabled*/ hoursSinceLastChange(), 339 /*last_dialog_feature_id*/ featureIdToLogEnum( 340 prefs.getInt(LAST_DIALOG_FEATURE_ID, -1)), 341 /*_type*/ dialogueTypeToLogEnum(prefs.getInt(LAST_DIALOG_TYPE, -1)), 342 /*_learn_more_clicked*/ prefs.getBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, false), 343 /*_hours_since_enabled*/ prefs.getInt(LAST_DIALOG_HOURS_SINCE_ENABLED, -1)); 344 prefs.edit() 345 .putLong(ENABLED_CHANGE_TIME, System.currentTimeMillis()) 346 .apply(); 347 } 348 hoursSinceLastChange()349 private int hoursSinceLastChange() { 350 int hoursSinceEnabled = -1; 351 long lastChangeTimeMillis = getSharedPreferences().getLong(ENABLED_CHANGE_TIME, -1); 352 if (lastChangeTimeMillis != -1) { 353 hoursSinceEnabled = (int) 354 ((System.currentTimeMillis() - lastChangeTimeMillis) / MILLIS_PER_HOUR); 355 } 356 return hoursSinceEnabled; 357 } 358 359 @Override 360 @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) getAdvancedProtectionFeatures()361 public List<AdvancedProtectionFeature> getAdvancedProtectionFeatures() { 362 getAdvancedProtectionFeatures_enforcePermission(); 363 List<AdvancedProtectionFeature> features = new ArrayList<>(); 364 for (int i = 0; i < mProviders.size(); i++) { 365 features.addAll(mProviders.get(i).getFeatures(mContext)); 366 } 367 368 for (int i = 0; i < mHooks.size(); i++) { 369 AdvancedProtectionHook hook = mHooks.get(i); 370 if (hook.isAvailable()) { 371 features.add(hook.getFeature()); 372 } 373 } 374 375 return features; 376 } 377 enforceAdminUser(UserHandle user)378 private void enforceAdminUser(UserHandle user) { 379 UserInfo info = mUserManager.getUserInfo(user.getIdentifier()); 380 if (!info.isAdmin()) { 381 throw new SecurityException("Only an admin user can manage advanced protection mode"); 382 } 383 } 384 385 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, @NonNull String[] args, ShellCallback callback, @NonNull ResultReceiver resultReceiver)386 public void onShellCommand(FileDescriptor in, FileDescriptor out, 387 FileDescriptor err, @NonNull String[] args, ShellCallback callback, 388 @NonNull ResultReceiver resultReceiver) { 389 (new AdvancedProtectionShellCommand(this)) 390 .exec(this, in, out, err, args, callback, resultReceiver); 391 } 392 393 @Override dump(FileDescriptor fd, PrintWriter writer, String[] args)394 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 395 if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return; 396 writer.println("AdvancedProtectionService"); 397 writer.println(" isAdvancedProtectionEnabled: " + isAdvancedProtectionEnabledInternal()); 398 writer.println(" mHooks.size(): " + mHooks.size()); 399 writer.println(" mCallbacks.size(): " + mCallbacks.size()); 400 writer.println(" mProviders.size(): " + mProviders.size()); 401 402 writer.println("Hooks: "); 403 mHooks.stream().forEach(hook -> { 404 writer.println(" " + hook.getClass().getSimpleName() + 405 " available: " + hook.isAvailable()); 406 }); 407 writer.println(" Providers: "); 408 mProviders.stream().forEach(provider -> { 409 writer.println(" " + provider.getClass().getSimpleName()); 410 provider.getFeatures(mContext).stream().forEach(feature -> { 411 writer.println(" " + feature.getClass().getSimpleName()); 412 }); 413 }); 414 writer.println(" mSharedPreferences: " + getSharedPreferences().getAll()); 415 } 416 sendModeChanged(boolean enabled)417 void sendModeChanged(boolean enabled) { 418 Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused */ -1) 419 .sendToTarget(); 420 } 421 sendCallbackAdded(boolean enabled, IAdvancedProtectionCallback callback)422 void sendCallbackAdded(boolean enabled, IAdvancedProtectionCallback callback) { 423 Message.obtain(mHandler, CALLBACK_ADDED, /*enabled*/ enabled ? 1 : 0, /*unused*/ -1, 424 /*callback*/ callback) 425 .sendToTarget(); 426 } 427 getSharedPreferences()428 private SharedPreferences getSharedPreferences() { 429 if (mSharedPreferences == null) { 430 initSharedPreferences(); 431 } 432 return mSharedPreferences; 433 } 434 initSharedPreferences()435 private synchronized void initSharedPreferences() { 436 if (mSharedPreferences == null) { 437 Context deviceContext = mContext.createDeviceProtectedStorageContext(); 438 File sharedPrefs = new File(Environment.getDataSystemDirectory(), PREFERENCE); 439 mSharedPreferences = deviceContext.getSharedPreferences(sharedPrefs, 440 Context.MODE_PRIVATE); 441 } 442 } 443 444 public static final class Lifecycle extends SystemService { 445 private final AdvancedProtectionService mService; 446 Lifecycle(@onNull Context context)447 public Lifecycle(@NonNull Context context) { 448 super(context); 449 mService = new AdvancedProtectionService(context); 450 } 451 452 @Override onStart()453 public void onStart() { 454 publishBinderService(Context.ADVANCED_PROTECTION_SERVICE, mService); 455 } 456 457 @Override onBootPhase(@ootPhase int phase)458 public void onBootPhase(@BootPhase int phase) { 459 if (phase == PHASE_SYSTEM_SERVICES_READY) { 460 boolean enabled = mService.isAdvancedProtectionEnabledInternal(); 461 if (enabled) { 462 Slog.i(TAG, "Advanced protection is enabled"); 463 } 464 mService.initFeatures(enabled); 465 mService.initLogging(); 466 } 467 } 468 } 469 470 @VisibleForTesting 471 static class AdvancedProtectionStore { 472 static final int ON = 1; 473 static final int OFF = 0; 474 private final Context mContext; 475 AdvancedProtectionStore(@onNull Context context)476 AdvancedProtectionStore(@NonNull Context context) { 477 mContext = context; 478 } 479 storeAdvancedProtectionModeEnabled(boolean enabled)480 void storeAdvancedProtectionModeEnabled(boolean enabled) { 481 Settings.Secure.putIntForUser(mContext.getContentResolver(), 482 ADVANCED_PROTECTION_MODE, enabled ? ON : OFF, 483 UserHandle.USER_SYSTEM); 484 } 485 retrieveAdvancedProtectionModeEnabled()486 boolean retrieveAdvancedProtectionModeEnabled() { 487 return Settings.Secure.getIntForUser(mContext.getContentResolver(), 488 ADVANCED_PROTECTION_MODE, OFF, UserHandle.USER_SYSTEM) == ON; 489 } 490 storeInt(String key, int value)491 void storeInt(String key, int value) { 492 Settings.Secure.putIntForUser(mContext.getContentResolver(), 493 key, value, 494 UserHandle.USER_SYSTEM); 495 } 496 retrieveInt(String key, int defaultValue)497 int retrieveInt(String key, int defaultValue) { 498 return Settings.Secure.getIntForUser(mContext.getContentResolver(), 499 key, defaultValue, UserHandle.USER_SYSTEM); 500 } 501 } 502 503 private class AdvancedProtectionHandler extends Handler { AdvancedProtectionHandler(@onNull Looper looper)504 private AdvancedProtectionHandler(@NonNull Looper looper) { 505 super(looper); 506 } 507 508 @Override handleMessage(@onNull Message msg)509 public void handleMessage(@NonNull Message msg) { 510 switch (msg.what) { 511 // arg1 == enabled 512 case MODE_CHANGED: 513 handleAllCallbacks(msg.arg1 == 1); 514 break; 515 // arg1 == enabled 516 // obj == callback 517 case CALLBACK_ADDED: 518 handleSingleCallback(msg.arg1 == 1, (IAdvancedProtectionCallback) msg.obj); 519 break; 520 } 521 } 522 handleAllCallbacks(boolean enabled)523 private void handleAllCallbacks(boolean enabled) { 524 ArrayList<IAdvancedProtectionCallback> deadObjects = new ArrayList<>(); 525 526 for (int i = 0; i < mHooks.size(); i++) { 527 AdvancedProtectionHook feature = mHooks.get(i); 528 try { 529 if (feature.isAvailable()) { 530 feature.onAdvancedProtectionChanged(enabled); 531 } 532 } catch (Exception e) { 533 Slog.e(TAG, "Failed to call hook for feature " 534 + feature.getFeature().getId(), e); 535 } 536 } 537 synchronized (mCallbacks) { 538 for (int i = 0; i < mCallbacks.size(); i++) { 539 IAdvancedProtectionCallback callback = mCallbacks.valueAt(i); 540 try { 541 callback.onAdvancedProtectionChanged(enabled); 542 } catch (RemoteException e) { 543 deadObjects.add(callback); 544 } 545 } 546 547 for (int i = 0; i < deadObjects.size(); i++) { 548 mCallbacks.remove(deadObjects.get(i).asBinder()); 549 } 550 } 551 } 552 handleSingleCallback(boolean enabled, IAdvancedProtectionCallback callback)553 private void handleSingleCallback(boolean enabled, IAdvancedProtectionCallback callback) { 554 try { 555 callback.onAdvancedProtectionChanged(enabled); 556 } catch (RemoteException e) { 557 mCallbacks.remove(callback.asBinder()); 558 } 559 } 560 } 561 562 private final class DeathRecipient implements IBinder.DeathRecipient { 563 private final IBinder mBinder; 564 DeathRecipient(IBinder binder)565 DeathRecipient(IBinder binder) { 566 mBinder = binder; 567 } 568 569 @Override binderDied()570 public void binderDied() { 571 synchronized (mCallbacks) { 572 mCallbacks.remove(mBinder); 573 } 574 } 575 } 576 577 private class AdvancedProtectionStatePullAtomCallback 578 implements StatsManager.StatsPullAtomCallback { 579 580 @Override onPullAtom(int atomTag, List<StatsEvent> data)581 public int onPullAtom(int atomTag, List<StatsEvent> data) { 582 if (atomTag != FrameworkStatsLog.ADVANCED_PROTECTION_STATE_INFO) { 583 return StatsManager.PULL_SKIP; 584 } 585 586 data.add( 587 FrameworkStatsLog.buildStatsEvent( 588 FrameworkStatsLog.ADVANCED_PROTECTION_STATE_INFO, 589 /*enabled*/ isAdvancedProtectionEnabledInternal(), 590 /*hours_since_enabled*/ hoursSinceLastChange())); 591 return StatsManager.PULL_SUCCESS; 592 } 593 } 594 } 595