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:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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. */
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user