• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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