Loading core/java/android/security/advancedprotection/AdvancedProtectionManager.java +2 −2 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ import java.util.concurrent.Executor; @FlaggedApi(Flags.FLAG_AAPM_API) @SystemService(Context.ADVANCED_PROTECTION_SERVICE) public class AdvancedProtectionManager { private static final String TAG = "AdvancedProtectionM"; private static final String TAG = "AdvancedProtectionMgr"; private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback> mCallbackMap = new ConcurrentHashMap<>(); Loading Loading @@ -73,7 +73,7 @@ public class AdvancedProtectionManager { * Registers a {@link Callback} to be notified of changes to the Advanced Protection state. * * <p>The provided callback will be called on the specified executor with the updated * {@link AdvancedProtectionState}. Methods are called when the state changes, as well as once * state. Methods are called when the state changes, as well as once * on initial registration. * * @param executor The executor of where the callback will execute. Loading services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java +40 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.provider.Settings.Secure.ADVANCED_PROTECTION_MODE; import android.Manifest; import android.annotation.EnforcePermission; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.Binder; import android.os.Handler; Loading @@ -35,37 +36,55 @@ import android.provider.Settings; import android.security.advancedprotection.IAdvancedProtectionCallback; import android.security.advancedprotection.IAdvancedProtectionService; import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.UserManagerInternal; import com.android.server.security.advancedprotection.features.AdvancedProtectionHook; import java.io.FileDescriptor; import java.util.ArrayList; /** @hide */ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub { private static final String TAG = "AdvancedProtectionService"; private static final int MODE_CHANGED = 0; private static final int CALLBACK_ADDED = 1; private final Context mContext; private final Handler mHandler; private final AdvancedProtectionStore mStore; // Features owned by the service - their code will be executed when state changes private final ArrayList<AdvancedProtectionHook> mHooks = new ArrayList<>(); // External features - they will be called on state change private final ArrayMap<IBinder, IAdvancedProtectionCallback> mCallbacks = new ArrayMap<>(); private AdvancedProtectionService(@NonNull Context context) { super(PermissionEnforcer.fromContext(context)); mContext = context; mHandler = new AdvancedProtectionHandler(FgThread.get().getLooper()); mStore = new AdvancedProtectionStore(context); } private void initFeatures(boolean enabled) { } // Only for tests @VisibleForTesting AdvancedProtectionService(@NonNull Context context, @NonNull AdvancedProtectionStore store, @NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer) { @NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer, @Nullable AdvancedProtectionHook hook) { super(permissionEnforcer); mContext = context; mStore = store; mHandler = new AdvancedProtectionHandler(looper); if (hook != null) { mHooks.add(hook); } } @Override Loading Loading @@ -100,8 +119,8 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub @Override @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public void unregisterAdvancedProtectionCallback(@NonNull IAdvancedProtectionCallback callback) throws RemoteException { public void unregisterAdvancedProtectionCallback( @NonNull IAdvancedProtectionCallback callback) { unregisterAdvancedProtectionCallback_enforcePermission(); synchronized (mCallbacks) { mCallbacks.remove(callback.asBinder()); Loading @@ -118,6 +137,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub if (enabled != isAdvancedProtectionEnabledInternal()) { mStore.store(enabled); sendModeChanged(enabled); Slog.i(TAG, "Advanced protection is " + (enabled ? "enabled" : "disabled")); } } } finally { Loading Loading @@ -156,6 +176,17 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub public void onStart() { publishBinderService(Context.ADVANCED_PROTECTION_SERVICE, mService); } @Override public void onBootPhase(@BootPhase int phase) { if (phase == PHASE_SYSTEM_SERVICES_READY) { boolean enabled = mService.isAdvancedProtectionEnabledInternal(); if (enabled) { Slog.i(TAG, "Advanced protection is enabled"); } mService.initFeatures(enabled); } } } @VisibleForTesting Loading Loading @@ -205,6 +236,12 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub private void handleAllCallbacks(boolean enabled) { ArrayList<IAdvancedProtectionCallback> deadObjects = new ArrayList<>(); for (int i = 0; i < mHooks.size(); i++) { AdvancedProtectionHook feature = mHooks.get(i); if (feature.isAvailable()) { feature.onAdvancedProtectionChanged(enabled); } } synchronized (mCallbacks) { for (int i = 0; i < mCallbacks.size(); i++) { IAdvancedProtectionCallback callback = mCallbacks.valueAt(i); Loading services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.java 0 → 100644 +31 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.security.advancedprotection.features; import android.annotation.NonNull; import android.content.Context; /** @hide */ public abstract class AdvancedProtectionHook { /** Called on boot phase PHASE_SYSTEM_SERVICES_READY */ public AdvancedProtectionHook(@NonNull Context context, boolean enabled) {} /** Whether this feature is relevant on this device. If false, onAdvancedProtectionChanged will * not be called, and the feature will not be displayed in the onboarding UX. */ public abstract boolean isAvailable(); /** Called whenever advanced protection state changes */ public void onAdvancedProtectionChanged(boolean enabled) {} } services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java +158 −14 Original line number Diff line number Diff line Loading @@ -28,28 +28,34 @@ import android.os.RemoteException; import android.os.test.FakePermissionEnforcer; import android.os.test.TestLooper; import android.provider.Settings; import android.security.advancedprotection.IAdvancedProtectionCallback; import com.android.server.security.advancedprotection.features.AdvancedProtectionHook; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.concurrent.atomic.AtomicBoolean; @SuppressLint("VisibleForTests") @RunWith(JUnit4.class) public class AdvancedProtectionServiceTest { private AdvancedProtectionService mService; private FakePermissionEnforcer mPermissionEnforcer; private Context mContext; private AdvancedProtectionService.AdvancedProtectionStore mStore; private TestLooper mLooper; @Before @SuppressLint("VisibleForTests") public void setup() throws Settings.SettingNotFoundException { mContext = mock(Context.class); mPermissionEnforcer = new FakePermissionEnforcer(); mPermissionEnforcer.grant(Manifest.permission.SET_ADVANCED_PROTECTION_MODE); mPermissionEnforcer.grant(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); AdvancedProtectionService.AdvancedProtectionStore store = new AdvancedProtectionService.AdvancedProtectionStore(mContext) { mStore = new AdvancedProtectionService.AdvancedProtectionStore(mContext) { private boolean mEnabled = false; @Override Loading @@ -63,22 +69,146 @@ public class AdvancedProtectionServiceTest { } }; mService = new AdvancedProtectionService(mContext, store, new TestLooper().getLooper(), mPermissionEnforcer); mLooper = new TestLooper(); mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(), mPermissionEnforcer, null); } @Test public void testEnableProtection() throws RemoteException { public void testToggleProtection() { mService.setAdvancedProtectionEnabled(true); assertTrue(mService.isAdvancedProtectionEnabled()); mService.setAdvancedProtectionEnabled(false); assertFalse(mService.isAdvancedProtectionEnabled()); } @Test public void testDisableProtection() throws RemoteException { mService.setAdvancedProtectionEnabled(false); public void testDisableProtection_byDefault() { assertFalse(mService.isAdvancedProtectionEnabled()); } @Test public void testEnableProtection_withHook() { AtomicBoolean callbackCaptor = new AtomicBoolean(false); AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) { @Override public boolean isAvailable() { return true; } @Override public void onAdvancedProtectionChanged(boolean enabled) { callbackCaptor.set(enabled); } }; mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(), mPermissionEnforcer, hook); mService.setAdvancedProtectionEnabled(true); mLooper.dispatchNext(); assertTrue(callbackCaptor.get()); } @Test public void testEnableProtection_withFeature_notAvailable() { AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false); AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) { @Override public boolean isAvailable() { return false; } @Override public void onAdvancedProtectionChanged(boolean enabled) { callbackCalledCaptor.set(true); } }; mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(), mPermissionEnforcer, hook); mService.setAdvancedProtectionEnabled(true); mLooper.dispatchNext(); assertFalse(callbackCalledCaptor.get()); } @Test public void testEnableProtection_withFeature_notCalledIfModeNotChanged() { AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false); AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) { @Override public boolean isAvailable() { return true; } @Override public void onAdvancedProtectionChanged(boolean enabled) { callbackCalledCaptor.set(true); } }; mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(), mPermissionEnforcer, hook); mService.setAdvancedProtectionEnabled(true); mLooper.dispatchNext(); assertTrue(callbackCalledCaptor.get()); callbackCalledCaptor.set(false); mService.setAdvancedProtectionEnabled(true); mLooper.dispatchAll(); assertFalse(callbackCalledCaptor.get()); } @Test public void testRegisterCallback() throws RemoteException { AtomicBoolean callbackCaptor = new AtomicBoolean(false); IAdvancedProtectionCallback callback = new IAdvancedProtectionCallback.Stub() { @Override public void onAdvancedProtectionChanged(boolean enabled) { callbackCaptor.set(enabled); } }; mService.setAdvancedProtectionEnabled(true); mLooper.dispatchAll(); mService.registerAdvancedProtectionCallback(callback); mLooper.dispatchNext(); assertTrue(callbackCaptor.get()); mService.setAdvancedProtectionEnabled(false); mLooper.dispatchNext(); assertFalse(callbackCaptor.get()); } @Test public void testUnregisterCallback() throws RemoteException { AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false); IAdvancedProtectionCallback callback = new IAdvancedProtectionCallback.Stub() { @Override public void onAdvancedProtectionChanged(boolean enabled) { callbackCalledCaptor.set(true); } }; mService.setAdvancedProtectionEnabled(true); mService.registerAdvancedProtectionCallback(callback); mLooper.dispatchAll(); callbackCalledCaptor.set(false); mService.unregisterAdvancedProtectionCallback(callback); mService.setAdvancedProtectionEnabled(false); mLooper.dispatchNext(); assertFalse(callbackCalledCaptor.get()); } @Test public void testSetProtection_withoutPermission() { mPermissionEnforcer.revoke(Manifest.permission.SET_ADVANCED_PROTECTION_MODE); Loading @@ -90,4 +220,18 @@ public class AdvancedProtectionServiceTest { mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); assertThrows(SecurityException.class, () -> mService.isAdvancedProtectionEnabled()); } @Test public void testRegisterCallback_withoutPermission() { mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); assertThrows(SecurityException.class, () -> mService.registerAdvancedProtectionCallback( new IAdvancedProtectionCallback.Default())); } @Test public void testUnregisterCallback_withoutPermission() { mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); assertThrows(SecurityException.class, () -> mService.unregisterAdvancedProtectionCallback( new IAdvancedProtectionCallback.Default())); } } Loading
core/java/android/security/advancedprotection/AdvancedProtectionManager.java +2 −2 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ import java.util.concurrent.Executor; @FlaggedApi(Flags.FLAG_AAPM_API) @SystemService(Context.ADVANCED_PROTECTION_SERVICE) public class AdvancedProtectionManager { private static final String TAG = "AdvancedProtectionM"; private static final String TAG = "AdvancedProtectionMgr"; private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback> mCallbackMap = new ConcurrentHashMap<>(); Loading Loading @@ -73,7 +73,7 @@ public class AdvancedProtectionManager { * Registers a {@link Callback} to be notified of changes to the Advanced Protection state. * * <p>The provided callback will be called on the specified executor with the updated * {@link AdvancedProtectionState}. Methods are called when the state changes, as well as once * state. Methods are called when the state changes, as well as once * on initial registration. * * @param executor The executor of where the callback will execute. Loading
services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java +40 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.provider.Settings.Secure.ADVANCED_PROTECTION_MODE; import android.Manifest; import android.annotation.EnforcePermission; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.Binder; import android.os.Handler; Loading @@ -35,37 +36,55 @@ import android.provider.Settings; import android.security.advancedprotection.IAdvancedProtectionCallback; import android.security.advancedprotection.IAdvancedProtectionService; import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.UserManagerInternal; import com.android.server.security.advancedprotection.features.AdvancedProtectionHook; import java.io.FileDescriptor; import java.util.ArrayList; /** @hide */ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub { private static final String TAG = "AdvancedProtectionService"; private static final int MODE_CHANGED = 0; private static final int CALLBACK_ADDED = 1; private final Context mContext; private final Handler mHandler; private final AdvancedProtectionStore mStore; // Features owned by the service - their code will be executed when state changes private final ArrayList<AdvancedProtectionHook> mHooks = new ArrayList<>(); // External features - they will be called on state change private final ArrayMap<IBinder, IAdvancedProtectionCallback> mCallbacks = new ArrayMap<>(); private AdvancedProtectionService(@NonNull Context context) { super(PermissionEnforcer.fromContext(context)); mContext = context; mHandler = new AdvancedProtectionHandler(FgThread.get().getLooper()); mStore = new AdvancedProtectionStore(context); } private void initFeatures(boolean enabled) { } // Only for tests @VisibleForTesting AdvancedProtectionService(@NonNull Context context, @NonNull AdvancedProtectionStore store, @NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer) { @NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer, @Nullable AdvancedProtectionHook hook) { super(permissionEnforcer); mContext = context; mStore = store; mHandler = new AdvancedProtectionHandler(looper); if (hook != null) { mHooks.add(hook); } } @Override Loading Loading @@ -100,8 +119,8 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub @Override @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public void unregisterAdvancedProtectionCallback(@NonNull IAdvancedProtectionCallback callback) throws RemoteException { public void unregisterAdvancedProtectionCallback( @NonNull IAdvancedProtectionCallback callback) { unregisterAdvancedProtectionCallback_enforcePermission(); synchronized (mCallbacks) { mCallbacks.remove(callback.asBinder()); Loading @@ -118,6 +137,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub if (enabled != isAdvancedProtectionEnabledInternal()) { mStore.store(enabled); sendModeChanged(enabled); Slog.i(TAG, "Advanced protection is " + (enabled ? "enabled" : "disabled")); } } } finally { Loading Loading @@ -156,6 +176,17 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub public void onStart() { publishBinderService(Context.ADVANCED_PROTECTION_SERVICE, mService); } @Override public void onBootPhase(@BootPhase int phase) { if (phase == PHASE_SYSTEM_SERVICES_READY) { boolean enabled = mService.isAdvancedProtectionEnabledInternal(); if (enabled) { Slog.i(TAG, "Advanced protection is enabled"); } mService.initFeatures(enabled); } } } @VisibleForTesting Loading Loading @@ -205,6 +236,12 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub private void handleAllCallbacks(boolean enabled) { ArrayList<IAdvancedProtectionCallback> deadObjects = new ArrayList<>(); for (int i = 0; i < mHooks.size(); i++) { AdvancedProtectionHook feature = mHooks.get(i); if (feature.isAvailable()) { feature.onAdvancedProtectionChanged(enabled); } } synchronized (mCallbacks) { for (int i = 0; i < mCallbacks.size(); i++) { IAdvancedProtectionCallback callback = mCallbacks.valueAt(i); Loading
services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.java 0 → 100644 +31 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.security.advancedprotection.features; import android.annotation.NonNull; import android.content.Context; /** @hide */ public abstract class AdvancedProtectionHook { /** Called on boot phase PHASE_SYSTEM_SERVICES_READY */ public AdvancedProtectionHook(@NonNull Context context, boolean enabled) {} /** Whether this feature is relevant on this device. If false, onAdvancedProtectionChanged will * not be called, and the feature will not be displayed in the onboarding UX. */ public abstract boolean isAvailable(); /** Called whenever advanced protection state changes */ public void onAdvancedProtectionChanged(boolean enabled) {} }
services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java +158 −14 Original line number Diff line number Diff line Loading @@ -28,28 +28,34 @@ import android.os.RemoteException; import android.os.test.FakePermissionEnforcer; import android.os.test.TestLooper; import android.provider.Settings; import android.security.advancedprotection.IAdvancedProtectionCallback; import com.android.server.security.advancedprotection.features.AdvancedProtectionHook; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.concurrent.atomic.AtomicBoolean; @SuppressLint("VisibleForTests") @RunWith(JUnit4.class) public class AdvancedProtectionServiceTest { private AdvancedProtectionService mService; private FakePermissionEnforcer mPermissionEnforcer; private Context mContext; private AdvancedProtectionService.AdvancedProtectionStore mStore; private TestLooper mLooper; @Before @SuppressLint("VisibleForTests") public void setup() throws Settings.SettingNotFoundException { mContext = mock(Context.class); mPermissionEnforcer = new FakePermissionEnforcer(); mPermissionEnforcer.grant(Manifest.permission.SET_ADVANCED_PROTECTION_MODE); mPermissionEnforcer.grant(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); AdvancedProtectionService.AdvancedProtectionStore store = new AdvancedProtectionService.AdvancedProtectionStore(mContext) { mStore = new AdvancedProtectionService.AdvancedProtectionStore(mContext) { private boolean mEnabled = false; @Override Loading @@ -63,22 +69,146 @@ public class AdvancedProtectionServiceTest { } }; mService = new AdvancedProtectionService(mContext, store, new TestLooper().getLooper(), mPermissionEnforcer); mLooper = new TestLooper(); mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(), mPermissionEnforcer, null); } @Test public void testEnableProtection() throws RemoteException { public void testToggleProtection() { mService.setAdvancedProtectionEnabled(true); assertTrue(mService.isAdvancedProtectionEnabled()); mService.setAdvancedProtectionEnabled(false); assertFalse(mService.isAdvancedProtectionEnabled()); } @Test public void testDisableProtection() throws RemoteException { mService.setAdvancedProtectionEnabled(false); public void testDisableProtection_byDefault() { assertFalse(mService.isAdvancedProtectionEnabled()); } @Test public void testEnableProtection_withHook() { AtomicBoolean callbackCaptor = new AtomicBoolean(false); AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) { @Override public boolean isAvailable() { return true; } @Override public void onAdvancedProtectionChanged(boolean enabled) { callbackCaptor.set(enabled); } }; mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(), mPermissionEnforcer, hook); mService.setAdvancedProtectionEnabled(true); mLooper.dispatchNext(); assertTrue(callbackCaptor.get()); } @Test public void testEnableProtection_withFeature_notAvailable() { AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false); AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) { @Override public boolean isAvailable() { return false; } @Override public void onAdvancedProtectionChanged(boolean enabled) { callbackCalledCaptor.set(true); } }; mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(), mPermissionEnforcer, hook); mService.setAdvancedProtectionEnabled(true); mLooper.dispatchNext(); assertFalse(callbackCalledCaptor.get()); } @Test public void testEnableProtection_withFeature_notCalledIfModeNotChanged() { AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false); AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) { @Override public boolean isAvailable() { return true; } @Override public void onAdvancedProtectionChanged(boolean enabled) { callbackCalledCaptor.set(true); } }; mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(), mPermissionEnforcer, hook); mService.setAdvancedProtectionEnabled(true); mLooper.dispatchNext(); assertTrue(callbackCalledCaptor.get()); callbackCalledCaptor.set(false); mService.setAdvancedProtectionEnabled(true); mLooper.dispatchAll(); assertFalse(callbackCalledCaptor.get()); } @Test public void testRegisterCallback() throws RemoteException { AtomicBoolean callbackCaptor = new AtomicBoolean(false); IAdvancedProtectionCallback callback = new IAdvancedProtectionCallback.Stub() { @Override public void onAdvancedProtectionChanged(boolean enabled) { callbackCaptor.set(enabled); } }; mService.setAdvancedProtectionEnabled(true); mLooper.dispatchAll(); mService.registerAdvancedProtectionCallback(callback); mLooper.dispatchNext(); assertTrue(callbackCaptor.get()); mService.setAdvancedProtectionEnabled(false); mLooper.dispatchNext(); assertFalse(callbackCaptor.get()); } @Test public void testUnregisterCallback() throws RemoteException { AtomicBoolean callbackCalledCaptor = new AtomicBoolean(false); IAdvancedProtectionCallback callback = new IAdvancedProtectionCallback.Stub() { @Override public void onAdvancedProtectionChanged(boolean enabled) { callbackCalledCaptor.set(true); } }; mService.setAdvancedProtectionEnabled(true); mService.registerAdvancedProtectionCallback(callback); mLooper.dispatchAll(); callbackCalledCaptor.set(false); mService.unregisterAdvancedProtectionCallback(callback); mService.setAdvancedProtectionEnabled(false); mLooper.dispatchNext(); assertFalse(callbackCalledCaptor.get()); } @Test public void testSetProtection_withoutPermission() { mPermissionEnforcer.revoke(Manifest.permission.SET_ADVANCED_PROTECTION_MODE); Loading @@ -90,4 +220,18 @@ public class AdvancedProtectionServiceTest { mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); assertThrows(SecurityException.class, () -> mService.isAdvancedProtectionEnabled()); } @Test public void testRegisterCallback_withoutPermission() { mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); assertThrows(SecurityException.class, () -> mService.registerAdvancedProtectionCallback( new IAdvancedProtectionCallback.Default())); } @Test public void testUnregisterCallback_withoutPermission() { mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); assertThrows(SecurityException.class, () -> mService.unregisterAdvancedProtectionCallback( new IAdvancedProtectionCallback.Default())); } }