summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/storage/ICeStorageLockEventListener.java30
-rw-r--r--core/java/android/os/storage/StorageManagerInternal.java16
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java41
-rw-r--r--services/core/java/com/android/server/TEST_MAPPING9
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java35
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/OWNERS2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/StorageManagerServiceTest.java139
7 files changed, 272 insertions, 0 deletions
diff --git a/core/java/android/os/storage/ICeStorageLockEventListener.java b/core/java/android/os/storage/ICeStorageLockEventListener.java
new file mode 100644
index 000000000000..f16a7feb0f3a
--- /dev/null
+++ b/core/java/android/os/storage/ICeStorageLockEventListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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 android.os.storage;
+
+
+/**
+ * Callback class for receiving CE storage lock events from StorageManagerService.
+ * @hide
+ */
+public interface ICeStorageLockEventListener {
+
+ /**
+ * Called when the CE storage corresponding to the userId is locked
+ */
+ void onStorageLocked(int userId);
+}
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 6995ea87ebe3..8ba2fa495540 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -16,10 +16,12 @@
package android.os.storage;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.UserInfo;
+import android.multiuser.Flags;
import android.os.IInstalld;
import android.os.IVold;
import android.os.ParcelFileDescriptor;
@@ -201,4 +203,18 @@ public abstract class StorageManagerInternal {
*/
public abstract int enableFsverity(IInstalld.IFsveritySetupAuthToken authToken, String filePath,
String packageName) throws IOException;
+
+ /**
+ * Registers a {@link ICeStorageLockEventListener} for receiving CE storage lock events.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE)
+ public abstract void registerStorageLockEventListener(
+ @NonNull ICeStorageLockEventListener listener);
+
+ /**
+ * Unregisters the {@link ICeStorageLockEventListener} which was registered previously
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE)
+ public abstract void unregisterStorageLockEventListener(
+ @NonNull ICeStorageLockEventListener listener);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e7fae2483d16..67e18ca32e21 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -107,6 +107,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.DiskInfo;
+import android.os.storage.ICeStorageLockEventListener;
import android.os.storage.IObbActionListener;
import android.os.storage.IStorageEventListener;
import android.os.storage.IStorageManager;
@@ -139,6 +140,7 @@ import android.util.TimeUtils;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.AppFuseMount;
@@ -185,6 +187,7 @@ import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -602,6 +605,9 @@ class StorageManagerService extends IStorageManager.Stub
// Not guarded by lock, always used on the ActivityManager thread
private final SparseArray<PackageMonitor> mPackageMonitorsForUser = new SparseArray<>();
+ /** List of listeners registered for ce storage callbacks */
+ private final CopyOnWriteArrayList<ICeStorageLockEventListener>
+ mCeStorageEventCallbacks = new CopyOnWriteArrayList<>();
class ObbState implements IBinder.DeathRecipient {
public ObbState(String rawPath, String canonicalPath, int callingUid,
@@ -3315,6 +3321,11 @@ class StorageManagerService extends IStorageManager.Stub
synchronized (mLock) {
mCeUnlockedUsers.remove(userId);
}
+ if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
+ dispatchCeStorageLockedEvent(userId);
+ }
}
@Override
@@ -4580,6 +4591,18 @@ class StorageManagerService extends IStorageManager.Stub
return StorageManager.MOUNT_MODE_EXTERNAL_NONE;
}
+ @VisibleForTesting
+ CopyOnWriteArrayList<ICeStorageLockEventListener> getCeStorageEventCallbacks() {
+ return mCeStorageEventCallbacks;
+ }
+
+ @VisibleForTesting
+ void dispatchCeStorageLockedEvent(int userId) {
+ for (ICeStorageLockEventListener listener: mCeStorageEventCallbacks) {
+ listener.onStorageLocked(userId);
+ }
+ }
+
private static class Callbacks extends Handler {
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
@@ -5066,5 +5089,23 @@ class StorageManagerService extends IStorageManager.Stub
throw new IOException(e);
}
}
+
+ @Override
+ public void registerStorageLockEventListener(
+ @NonNull ICeStorageLockEventListener listener) {
+ boolean registered = mCeStorageEventCallbacks.add(listener);
+ if (!registered) {
+ Slog.w(TAG, "Failed to register listener: " + listener);
+ }
+ }
+
+ @Override
+ public void unregisterStorageLockEventListener(
+ @NonNull ICeStorageLockEventListener listener) {
+ boolean unregistered = mCeStorageEventCallbacks.remove(listener);
+ if (!unregistered) {
+ Slog.w(TAG, "Unregistering " + listener + " that was not registered");
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index baae40b88e89..5933639f2317 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -74,6 +74,15 @@
"file_patterns": ["SensitiveContentProtectionManagerService\\.java"]
},
{
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.StorageManagerServiceTest"
+ }
+ ],
+ "file_patterns": ["StorageManagerService\\.java"]
+ },
+ {
"name": "FrameworksServicesTests",
"options": [
{
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 593174420d94..19562ef79fbb 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -102,8 +102,10 @@ import android.os.ShellCallback;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.ICeStorageLockEventListener;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
+import android.os.storage.StorageManagerInternal;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.security.AndroidKeyStoreMaintenance;
@@ -338,6 +340,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private final CopyOnWriteArrayList<LockSettingsStateListener> mLockSettingsStateListeners =
new CopyOnWriteArrayList<>();
+ private final StorageManagerInternal mStorageManagerInternal;
+
// This class manages life cycle events for encrypted users on File Based Encryption (FBE)
// devices. The most basic of these is to show/hide notifications about missing features until
// the user unlocks the account and credential-encrypted storage is available.
@@ -577,6 +581,10 @@ public class LockSettingsService extends ILockSettings.Stub {
return null;
}
+ public StorageManagerInternal getStorageManagerInternal() {
+ return LocalServices.getService(StorageManagerInternal.class);
+ }
+
public SyntheticPasswordManager getSyntheticPasswordManager(LockSettingsStorage storage) {
return new SyntheticPasswordManager(getContext(), storage, getUserManager(),
new PasswordSlotManager());
@@ -672,6 +680,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mNotificationManager = injector.getNotificationManager();
mUserManager = injector.getUserManager();
mStorageManager = injector.getStorageManager();
+ mStorageManagerInternal = injector.getStorageManagerInternal();
mStrongAuthTracker = injector.getStrongAuthTracker();
mStrongAuthTracker.register(mStrongAuth);
mGatekeeperPasswords = new LongSparseArray<>();
@@ -925,8 +934,34 @@ public class LockSettingsService extends ILockSettings.Stub {
mStorage.prefetchUser(UserHandle.USER_SYSTEM);
mBiometricDeferredQueue.systemReady(mInjector.getFingerprintManager(),
mInjector.getFaceManager(), mInjector.getBiometricManager());
+ if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
+ mStorageManagerInternal.registerStorageLockEventListener(mCeStorageLockEventListener);
+ }
}
+ private final ICeStorageLockEventListener mCeStorageLockEventListener =
+ new ICeStorageLockEventListener() {
+ @Override
+ public void onStorageLocked(int userId) {
+ Slog.i(TAG, "Storage lock event received for " + userId);
+ if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
+ mHandler.post(() -> {
+ UserProperties userProperties =
+ mUserManager.getUserProperties(UserHandle.of(userId));
+ if (userProperties != null
+ && userProperties.getAllowStoppingUserWithDelayedLocking()) {
+ int strongAuthRequired = LockPatternUtils.StrongAuthTracker
+ .getDefaultFlags(mContext);
+ requireStrongAuth(strongAuthRequired, userId);
+ }
+ });
+ }
+ }};
+
private void loadEscrowData() {
mRebootEscrowManager.loadRebootEscrowDataIfAvailable(mHandler);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/OWNERS b/services/tests/mockingservicestests/src/com/android/server/OWNERS
index 0eb8639bd005..dc5cb8d6bdf5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/OWNERS
@@ -3,3 +3,5 @@ per-file *AppStateTracker* = file:/apex/jobscheduler/OWNERS
per-file *DeviceIdleController* = file:/apex/jobscheduler/OWNERS
per-file SensitiveContentProtectionManagerService* = file:/core/java/android/permission/OWNERS
per-file RescuePartyTest.java = file:/packages/CrashRecovery/OWNERS
+per-file *Storage* = file:/core/java/android/os/storage/OWNERS
+
diff --git a/services/tests/mockingservicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/StorageManagerServiceTest.java
new file mode 100644
index 000000000000..2e4b97ef7dd2
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/StorageManagerServiceTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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;
+
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.multiuser.Flags;
+import android.os.UserManager;
+import android.os.storage.ICeStorageLockEventListener;
+import android.os.storage.StorageManagerInternal;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class StorageManagerServiceTest {
+
+ private final Context mRealContext = androidx.test.platform.app.InstrumentationRegistry
+ .getInstrumentation().getTargetContext();
+ private StorageManagerService mStorageManagerService;
+ private StorageManagerInternal mStorageManagerInternal;
+
+ private static final int TEST_USER_ID = 1001;
+
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+ .spyStatic(UserManager.class)
+ .build();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private static class TestStorageEventListener implements ICeStorageLockEventListener {
+
+ private int mExpectedUserId;
+
+ private TestStorageEventListener(int userId) {
+ mExpectedUserId = userId;
+ }
+
+ @Override
+ public void onStorageLocked(int userId) {
+ assertThat(userId).isEqualTo(mExpectedUserId);
+ }
+ }
+
+
+ @Before
+ public void setFixtures() {
+ // Called when WatchedUserStates is constructed
+ doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache());
+
+ mStorageManagerService = new StorageManagerService(mRealContext);
+ mStorageManagerInternal = LocalServices.getService(StorageManagerInternal.class);
+ assertWithMessage("LocalServices.getService(StorageManagerInternal.class)")
+ .that(mStorageManagerInternal).isNotNull();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(StorageManagerInternal.class);
+ }
+
+ @Test
+ public void testRegisterLockEventListener() {
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
+ Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+ CopyOnWriteArrayList<ICeStorageLockEventListener> storageLockEventListeners =
+ mStorageManagerService.getCeStorageEventCallbacks();
+ assertThat(storageLockEventListeners).isNotNull();
+ int registeredCallbackCount = storageLockEventListeners.size();
+ TestStorageEventListener testStorageEventListener =
+ new TestStorageEventListener(TEST_USER_ID);
+ mStorageManagerInternal.registerStorageLockEventListener(testStorageEventListener);
+ assertNumberOfStorageCallbackReceivers(registeredCallbackCount + 1);
+
+ mStorageManagerInternal.unregisterStorageLockEventListener(testStorageEventListener);
+ assertNumberOfStorageCallbackReceivers(registeredCallbackCount);
+ }
+
+ @Test
+ public void testDispatchCeStorageLockEvent() {
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
+ Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
+
+ assertThat(mStorageManagerService.getCeStorageEventCallbacks()).isNotNull();
+ int callbackReceiverSize = mStorageManagerService.getCeStorageEventCallbacks().size();
+ TestStorageEventListener testStorageEventListener =
+ spy(new TestStorageEventListener(TEST_USER_ID));
+
+ // Add testStorageEventListener to the list of storage callback listeners
+ mStorageManagerService.getCeStorageEventCallbacks().add(testStorageEventListener);
+ assertNumberOfStorageCallbackReceivers(callbackReceiverSize + 1);
+
+ mStorageManagerService.dispatchCeStorageLockedEvent(TEST_USER_ID);
+ verify(testStorageEventListener).onStorageLocked(eq(TEST_USER_ID));
+
+ // Remove testStorageEventListener from the list of storage callback listeners
+ mStorageManagerService.getCeStorageEventCallbacks().remove(testStorageEventListener);
+ assertNumberOfStorageCallbackReceivers(callbackReceiverSize);
+ }
+
+ private void assertNumberOfStorageCallbackReceivers(int callbackReceiverSize) {
+ assertThat(mStorageManagerService.getCeStorageEventCallbacks()).isNotNull();
+ assertThat(mStorageManagerService.getCeStorageEventCallbacks().size())
+ .isEqualTo(callbackReceiverSize);
+ }
+}