Merge changes from topic "unencrypted-dirs-q" into qt-dev

* changes:
  [RESTRICT AUTOMERGE] Log to EventLog on prepareUserStorage failure
  [RESTRICT AUTOMERGE] Ignore errors preparing user storage for existing users
  [RESTRICT AUTOMERGE] UserDataPreparer: reboot to recovery for system user only
  [RESTRICT AUTOMERGE] UserDataPreparer: reboot to recovery if preparing user storage fails
  [RESTRICT AUTOMERGE] StorageManagerService: don't ignore failures to prepare user storage
  Check user unlocked before write to /data/system_ce/0/snapshots
This commit is contained in:
Eric Biggers
2022-04-25 17:11:39 +00:00
committed by Android (Google) Code Review
6 changed files with 130 additions and 4 deletions

View File

@@ -221,4 +221,12 @@ public abstract class UserManagerInternal {
*/ */
public abstract boolean isSettingRestrictedForUser(String setting, int userId, String value, public abstract boolean isSettingRestrictedForUser(String setting, int userId, String value,
int callingUid); int callingUid);
/**
* Returns {@code true} if the system should ignore errors when preparing
* the storage directories for the user with ID {@code userId}. This will
* return {@code false} for all new users; it will only return {@code true}
* for users that already existed on-disk from an older version of Android.
*/
public abstract boolean shouldIgnorePrepareStorageErrors(int userId);
} }

View File

@@ -117,6 +117,7 @@ import android.text.format.DateUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.AtomicFile; import android.util.AtomicFile;
import android.util.DataUnit; import android.util.DataUnit;
import android.util.EventLog;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.util.Slog; import android.util.Slog;
@@ -2848,7 +2849,21 @@ class StorageManagerService extends IStorageManager.Stub
try { try {
mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags); mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
} catch (Exception e) { } catch (Exception e) {
EventLog.writeEvent(0x534e4554, "224585613", -1, "");
Slog.wtf(TAG, e); Slog.wtf(TAG, e);
// Make sure to re-throw this exception; we must not ignore failure
// to prepare the user storage as it could indicate that encryption
// wasn't successfully set up.
//
// Very unfortunately, these errors need to be ignored for broken
// users that already existed on-disk from older Android versions.
UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
if (umInternal.shouldIgnorePrepareStorageErrors(userId)) {
Slog.wtf(TAG, "ignoring error preparing storage for existing user " + userId
+ "; device may be insecure!");
return;
}
throw new RuntimeException(e);
} }
} }

View File

@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.os.Environment; import android.os.Environment;
import android.os.FileUtils; import android.os.FileUtils;
import android.os.RecoverySystem;
import android.os.storage.StorageManager; import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo; import android.os.storage.VolumeInfo;
import android.os.SystemProperties; import android.os.SystemProperties;
@@ -115,6 +116,16 @@ class UserDataPreparer {
// Try one last time; if we fail again we're really in trouble // Try one last time; if we fail again we're really in trouble
prepareUserDataLI(volumeUuid, userId, userSerial, prepareUserDataLI(volumeUuid, userId, userSerial,
flags | StorageManager.FLAG_STORAGE_DE, false); flags | StorageManager.FLAG_STORAGE_DE, false);
} else {
try {
Log.wtf(TAG, "prepareUserData failed for user " + userId, e);
if (userId == UserHandle.USER_SYSTEM) {
RecoverySystem.rebootPromptAndWipeUserData(mContext,
"prepareUserData failed for system user");
}
} catch (IOException e2) {
throw new RuntimeException("error rebooting into recovery", e2);
}
} }
} }
} }

View File

@@ -180,6 +180,8 @@ public class UserManagerService extends IUserManager.Stub {
private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions"; private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions";
private static final String TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL = private static final String TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL =
"lastRequestQuietModeEnabledCall"; "lastRequestQuietModeEnabledCall";
private static final String TAG_IGNORE_PREPARE_STORAGE_ERRORS =
"ignorePrepareStorageErrors";
private static final String ATTR_KEY = "key"; private static final String ATTR_KEY = "key";
private static final String ATTR_VALUE_TYPE = "type"; private static final String ATTR_VALUE_TYPE = "type";
private static final String ATTR_MULTIPLE = "m"; private static final String ATTR_MULTIPLE = "m";
@@ -278,6 +280,14 @@ public class UserManagerService extends IUserManager.Stub {
private long mLastRequestQuietModeEnabledMillis; private long mLastRequestQuietModeEnabledMillis;
/**
* {@code true} if the system should ignore errors when preparing the
* storage directories for this user. This is {@code false} for all new
* users; it will only be {@code true} for users that already existed
* on-disk from an older version of Android.
*/
private boolean mIgnorePrepareStorageErrors;
void setLastRequestQuietModeEnabledMillis(long millis) { void setLastRequestQuietModeEnabledMillis(long millis) {
mLastRequestQuietModeEnabledMillis = millis; mLastRequestQuietModeEnabledMillis = millis;
} }
@@ -286,6 +296,14 @@ public class UserManagerService extends IUserManager.Stub {
return mLastRequestQuietModeEnabledMillis; return mLastRequestQuietModeEnabledMillis;
} }
boolean getIgnorePrepareStorageErrors() {
return mIgnorePrepareStorageErrors;
}
void setIgnorePrepareStorageErrors() {
mIgnorePrepareStorageErrors = true;
}
void clearSeedAccountData() { void clearSeedAccountData() {
seedAccountName = null; seedAccountName = null;
seedAccountType = null; seedAccountType = null;
@@ -2380,6 +2398,10 @@ public class UserManagerService extends IUserManager.Stub {
serializer.endTag(/* namespace */ null, TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL); serializer.endTag(/* namespace */ null, TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL);
} }
serializer.startTag(/* namespace */ null, TAG_IGNORE_PREPARE_STORAGE_ERRORS);
serializer.text(String.valueOf(userData.getIgnorePrepareStorageErrors()));
serializer.endTag(/* namespace */ null, TAG_IGNORE_PREPARE_STORAGE_ERRORS);
serializer.endTag(null, TAG_USER); serializer.endTag(null, TAG_USER);
serializer.endDocument(); serializer.endDocument();
@@ -2488,6 +2510,7 @@ public class UserManagerService extends IUserManager.Stub {
Bundle baseRestrictions = null; Bundle baseRestrictions = null;
Bundle localRestrictions = null; Bundle localRestrictions = null;
Bundle globalRestrictions = null; Bundle globalRestrictions = null;
boolean ignorePrepareStorageErrors = true; // default is true for old users
XmlPullParser parser = Xml.newPullParser(); XmlPullParser parser = Xml.newPullParser();
parser.setInput(is, StandardCharsets.UTF_8.name()); parser.setInput(is, StandardCharsets.UTF_8.name());
@@ -2566,6 +2589,11 @@ public class UserManagerService extends IUserManager.Stub {
if (type == XmlPullParser.TEXT) { if (type == XmlPullParser.TEXT) {
lastRequestQuietModeEnabledTimestamp = Long.parseLong(parser.getText()); lastRequestQuietModeEnabledTimestamp = Long.parseLong(parser.getText());
} }
} else if (TAG_IGNORE_PREPARE_STORAGE_ERRORS.equals(tag)) {
type = parser.next();
if (type == XmlPullParser.TEXT) {
ignorePrepareStorageErrors = Boolean.parseBoolean(parser.getText());
}
} }
} }
} }
@@ -2591,6 +2619,9 @@ public class UserManagerService extends IUserManager.Stub {
userData.persistSeedData = persistSeedData; userData.persistSeedData = persistSeedData;
userData.seedAccountOptions = seedAccountOptions; userData.seedAccountOptions = seedAccountOptions;
userData.setLastRequestQuietModeEnabledMillis(lastRequestQuietModeEnabledTimestamp); userData.setLastRequestQuietModeEnabledMillis(lastRequestQuietModeEnabledTimestamp);
if (ignorePrepareStorageErrors) {
userData.setIgnorePrepareStorageErrors();
}
synchronized (mRestrictionsLock) { synchronized (mRestrictionsLock) {
if (baseRestrictions != null) { if (baseRestrictions != null) {
@@ -3767,6 +3798,9 @@ public class UserManagerService extends IUserManager.Stub {
pw.println(); pw.println();
} }
} }
pw.println(" Ignore errors preparing storage: "
+ userData.getIgnorePrepareStorageErrors());
} }
} }
pw.println(); pw.println();
@@ -4111,6 +4145,14 @@ public class UserManagerService extends IUserManager.Stub {
return UserRestrictionsUtils.isSettingRestrictedForUser(mContext, setting, userId, return UserRestrictionsUtils.isSettingRestrictedForUser(mContext, setting, userId,
value, callingUid); value, callingUid);
} }
@Override
public boolean shouldIgnorePrepareStorageErrors(int userId) {
synchronized (mUsersLock) {
UserData userData = mUsers.get(userId);
return userData != null && userData.getIgnorePrepareStorageErrors();
}
}
} }
/* Remove all the users except of the system one. */ /* Remove all the users except of the system one. */

View File

@@ -28,12 +28,14 @@ import android.graphics.Bitmap;
import android.graphics.Bitmap.Config; import android.graphics.Bitmap.Config;
import android.os.Process; import android.os.Process;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserManagerInternal;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.AtomicFile; import com.android.internal.os.AtomicFile;
import com.android.server.LocalServices;
import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto; import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto;
import java.io.File; import java.io.File;
@@ -74,6 +76,7 @@ class TaskSnapshotPersister {
private final Object mLock = new Object(); private final Object mLock = new Object();
private final DirectoryResolver mDirectoryResolver; private final DirectoryResolver mDirectoryResolver;
private final float mReducedScale; private final float mReducedScale;
private final UserManagerInternal mUserManagerInternal;
/** /**
* The list of ids of the tasks that have been persisted since {@link #removeObsoleteFiles} was * The list of ids of the tasks that have been persisted since {@link #removeObsoleteFiles} was
@@ -84,6 +87,9 @@ class TaskSnapshotPersister {
TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) { TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
mDirectoryResolver = resolver; mDirectoryResolver = resolver;
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
if (service.mLowRamTaskSnapshotsAndRecents) { if (service.mLowRamTaskSnapshotsAndRecents) {
// Use very low res snapshots if we are using Go version of recents. // Use very low res snapshots if we are using Go version of recents.
mReducedScale = LOW_RAM_RECENTS_REDUCED_SCALE; mReducedScale = LOW_RAM_RECENTS_REDUCED_SCALE;
@@ -172,7 +178,7 @@ class TaskSnapshotPersister {
return; return;
} }
} }
SystemClock.sleep(100); SystemClock.sleep(DELAY_MS);
} }
} }
@@ -218,7 +224,7 @@ class TaskSnapshotPersister {
private boolean createDirectory(int userId) { private boolean createDirectory(int userId) {
final File dir = getDirectory(userId); final File dir = getDirectory(userId);
return dir.exists() || dir.mkdirs(); return dir.exists() || dir.mkdir();
} }
private void deleteSnapshot(int taskId, int userId) { private void deleteSnapshot(int taskId, int userId) {
@@ -243,18 +249,26 @@ class TaskSnapshotPersister {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) { while (true) {
WriteQueueItem next; WriteQueueItem next;
boolean isReadyToWrite = false;
synchronized (mLock) { synchronized (mLock) {
if (mPaused) { if (mPaused) {
next = null; next = null;
} else { } else {
next = mWriteQueue.poll(); next = mWriteQueue.poll();
if (next != null) { if (next != null) {
next.onDequeuedLocked(); if (next.isReady()) {
isReadyToWrite = true;
next.onDequeuedLocked();
} else {
mWriteQueue.addLast(next);
}
} }
} }
} }
if (next != null) { if (next != null) {
next.write(); if (isReadyToWrite) {
next.write();
}
SystemClock.sleep(DELAY_MS); SystemClock.sleep(DELAY_MS);
} }
synchronized (mLock) { synchronized (mLock) {
@@ -274,6 +288,13 @@ class TaskSnapshotPersister {
}; };
private abstract class WriteQueueItem { private abstract class WriteQueueItem {
/**
* @return {@code true} if item is ready to have {@link WriteQueueItem#write} called
*/
boolean isReady() {
return true;
}
abstract void write(); abstract void write();
/** /**
@@ -312,6 +333,11 @@ class TaskSnapshotPersister {
mStoreQueueItems.remove(this); mStoreQueueItems.remove(this);
} }
@Override
boolean isReady() {
return mUserManagerInternal.isUserUnlocked(mUserId);
}
@Override @Override
void write() { void write() {
if (!createDirectory(mUserId)) { if (!createDirectory(mUserId)) {

View File

@@ -23,6 +23,9 @@ import static android.graphics.GraphicBuffer.USAGE_SW_READ_RARELY;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityManager.TaskSnapshot;
import android.content.ComponentName; import android.content.ComponentName;
import android.graphics.Canvas; import android.graphics.Canvas;
@@ -32,9 +35,14 @@ import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.UserManager; import android.os.UserManager;
import android.os.UserManagerInternal;
import com.android.server.LocalServices;
import org.junit.After; import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import java.io.File; import java.io.File;
@@ -50,10 +58,26 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
TaskSnapshotLoader mLoader; TaskSnapshotLoader mLoader;
int mTestUserId; int mTestUserId;
@BeforeClass
public static void setUpOnce() {
final UserManagerInternal userManager = mock(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, userManager);
}
@AfterClass
public static void tearDownOnce() {
LocalServices.removeServiceForTest(UserManagerInternal.class);
}
@Before @Before
public void setUp() { public void setUp() {
final UserManager um = UserManager.get(getInstrumentation().getTargetContext()); final UserManager um = UserManager.get(getInstrumentation().getTargetContext());
mTestUserId = um.getUserHandle(); mTestUserId = um.getUserHandle();
final UserManagerInternal userManagerInternal =
LocalServices.getService(UserManagerInternal.class);
when(userManagerInternal.isUserUnlocked(mTestUserId)).thenReturn(true);
mPersister = new TaskSnapshotPersister(mWm, userId -> FILES_DIR); mPersister = new TaskSnapshotPersister(mWm, userId -> FILES_DIR);
mLoader = new TaskSnapshotLoader(mPersister); mLoader = new TaskSnapshotLoader(mPersister);
mPersister.start(); mPersister.start();