diff options
| author | 2022-11-18 07:01:11 +0000 | |
|---|---|---|
| committer | 2022-12-16 09:55:30 +0000 | |
| commit | 7ffd4c59e97cb7d6c8a568dec2bcba019c0b75f5 (patch) | |
| tree | 73a96c2505458ef08f1a98e4a06af53a1d9f0cbf | |
| parent | 383978a4eed3738c8c64806262851ad840832256 (diff) | |
Extract snapshot persist thread from TaskSnapshotPersister(1/N)
Extract the IO thread and queue handler to SnapshotPersistQueue from
TaskSnapshotPersister, which will handle the snapshot IO tasks for both
Task and Activity snapshot.
The Persister and Loader class just need to create a different
PersistInfoProvider if something need to specify with different config.
Bug: 207481538
Test: atest TaskSnapshotPersisterLoaderTest TaskSnapshotCacheTest
TaskSnapshotLowResDisabledTest
Change-Id: Id739aca9eaeff7935ebf4b5f9a8ebc7bcd2779c7
11 files changed, 544 insertions, 437 deletions
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java new file mode 100644 index 000000000000..54ba370b188b --- /dev/null +++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2022 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.wm; + +import static android.graphics.Bitmap.CompressFormat.JPEG; + +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.graphics.Bitmap; +import android.os.Process; +import android.os.SystemClock; +import android.util.AtomicFile; +import android.util.Slog; +import android.window.TaskSnapshot; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; +import com.android.server.wm.TaskSnapshotPersister.PersistInfoProvider; +import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayDeque; + +/** + * Singleton worker thread to queue up persist or delete tasks of {@link TaskSnapshot}s to disk. + */ +class SnapshotPersistQueue { + private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM; + private static final long DELAY_MS = 100; + private static final int MAX_STORE_QUEUE_DEPTH = 2; + private static final int COMPRESS_QUALITY = 95; + + @GuardedBy("mLock") + private final ArrayDeque<WriteQueueItem> mWriteQueue = new ArrayDeque<>(); + @GuardedBy("mLock") + private final ArrayDeque<StoreWriteQueueItem> mStoreQueueItems = new ArrayDeque<>(); + @GuardedBy("mLock") + private boolean mQueueIdling; + @GuardedBy("mLock") + private boolean mPaused; + private boolean mStarted; + private final Object mLock = new Object(); + private final UserManagerInternal mUserManagerInternal; + + SnapshotPersistQueue() { + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); + } + + Object getLock() { + return mLock; + } + + void systemReady() { + start(); + } + + /** + * Starts persisting. + */ + void start() { + if (!mStarted) { + mStarted = true; + mPersister.start(); + } + } + + /** + * Temporarily pauses/unpauses persisting of task snapshots. + * + * @param paused Whether task snapshot persisting should be paused. + */ + void setPaused(boolean paused) { + synchronized (mLock) { + mPaused = paused; + if (!paused) { + mLock.notifyAll(); + } + } + } + + @TestApi + void waitForQueueEmpty() { + while (true) { + synchronized (mLock) { + if (mWriteQueue.isEmpty() && mQueueIdling) { + return; + } + } + SystemClock.sleep(DELAY_MS); + } + } + + @GuardedBy("mLock") + void sendToQueueLocked(WriteQueueItem item) { + mWriteQueue.offer(item); + item.onQueuedLocked(); + ensureStoreQueueDepthLocked(); + if (!mPaused) { + mLock.notifyAll(); + } + } + + @GuardedBy("mLock") + private void ensureStoreQueueDepthLocked() { + while (mStoreQueueItems.size() > MAX_STORE_QUEUE_DEPTH) { + final StoreWriteQueueItem item = mStoreQueueItems.poll(); + mWriteQueue.remove(item); + Slog.i(TAG, "Queue is too deep! Purged item with index=" + item.mId); + } + } + + private void deleteSnapshot(int index, int userId, PersistInfoProvider provider) { + final File protoFile = provider.getProtoFile(index, userId); + final File bitmapLowResFile = provider.getLowResolutionBitmapFile(index, userId); + protoFile.delete(); + if (bitmapLowResFile.exists()) { + bitmapLowResFile.delete(); + } + final File bitmapFile = provider.getHighResolutionBitmapFile(index, userId); + if (bitmapFile.exists()) { + bitmapFile.delete(); + } + } + + private final Thread mPersister = new Thread("TaskSnapshotPersister") { + public void run() { + android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + while (true) { + WriteQueueItem next; + boolean isReadyToWrite = false; + synchronized (mLock) { + if (mPaused) { + next = null; + } else { + next = mWriteQueue.poll(); + if (next != null) { + if (next.isReady()) { + isReadyToWrite = true; + next.onDequeuedLocked(); + } else { + mWriteQueue.addLast(next); + } + } + } + } + if (next != null) { + if (isReadyToWrite) { + next.write(); + } + SystemClock.sleep(DELAY_MS); + } + synchronized (mLock) { + final boolean writeQueueEmpty = mWriteQueue.isEmpty(); + if (!writeQueueEmpty && !mPaused) { + continue; + } + try { + mQueueIdling = writeQueueEmpty; + mLock.wait(); + mQueueIdling = false; + } catch (InterruptedException e) { + } + } + } + } + }; + + abstract static class WriteQueueItem { + protected final PersistInfoProvider mPersistInfoProvider; + WriteQueueItem(@NonNull PersistInfoProvider persistInfoProvider) { + mPersistInfoProvider = persistInfoProvider; + } + /** + * @return {@code true} if item is ready to have {@link WriteQueueItem#write} called + */ + boolean isReady() { + return true; + } + + abstract void write(); + + /** + * Called when this queue item has been put into the queue. + */ + void onQueuedLocked() { + } + + /** + * Called when this queue item has been taken out of the queue. + */ + void onDequeuedLocked() { + } + } + + StoreWriteQueueItem createStoreWriteQueueItem(int id, int userId, TaskSnapshot snapshot, + PersistInfoProvider provider) { + return new StoreWriteQueueItem(id, userId, snapshot, provider); + } + + class StoreWriteQueueItem extends WriteQueueItem { + private final int mId; + private final int mUserId; + private final TaskSnapshot mSnapshot; + + StoreWriteQueueItem(int id, int userId, TaskSnapshot snapshot, + PersistInfoProvider provider) { + super(provider); + mId = id; + mUserId = userId; + mSnapshot = snapshot; + } + + @GuardedBy("mLock") + @Override + void onQueuedLocked() { + mStoreQueueItems.offer(this); + } + + @GuardedBy("mLock") + @Override + void onDequeuedLocked() { + mStoreQueueItems.remove(this); + } + + @Override + boolean isReady() { + return mUserManagerInternal.isUserUnlocked(mUserId); + } + + @Override + void write() { + if (!mPersistInfoProvider.createDirectory(mUserId)) { + Slog.e(TAG, "Unable to create snapshot directory for user dir=" + + mPersistInfoProvider.getDirectory(mUserId)); + } + boolean failed = false; + if (!writeProto()) { + failed = true; + } + if (!writeBuffer()) { + failed = true; + } + if (failed) { + deleteSnapshot(mId, mUserId, mPersistInfoProvider); + } + } + + boolean writeProto() { + final TaskSnapshotProto proto = new TaskSnapshotProto(); + proto.orientation = mSnapshot.getOrientation(); + proto.rotation = mSnapshot.getRotation(); + proto.taskWidth = mSnapshot.getTaskSize().x; + proto.taskHeight = mSnapshot.getTaskSize().y; + proto.insetLeft = mSnapshot.getContentInsets().left; + proto.insetTop = mSnapshot.getContentInsets().top; + proto.insetRight = mSnapshot.getContentInsets().right; + proto.insetBottom = mSnapshot.getContentInsets().bottom; + proto.letterboxInsetLeft = mSnapshot.getLetterboxInsets().left; + proto.letterboxInsetTop = mSnapshot.getLetterboxInsets().top; + proto.letterboxInsetRight = mSnapshot.getLetterboxInsets().right; + proto.letterboxInsetBottom = mSnapshot.getLetterboxInsets().bottom; + proto.isRealSnapshot = mSnapshot.isRealSnapshot(); + proto.windowingMode = mSnapshot.getWindowingMode(); + proto.appearance = mSnapshot.getAppearance(); + proto.isTranslucent = mSnapshot.isTranslucent(); + proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString(); + proto.id = mSnapshot.getId(); + final byte[] bytes = TaskSnapshotProto.toByteArray(proto); + final File file = mPersistInfoProvider.getProtoFile(mId, mUserId); + final AtomicFile atomicFile = new AtomicFile(file); + FileOutputStream fos = null; + try { + fos = atomicFile.startWrite(); + fos.write(bytes); + atomicFile.finishWrite(fos); + } catch (IOException e) { + atomicFile.failWrite(fos); + Slog.e(TAG, "Unable to open " + file + " for persisting. " + e); + return false; + } + return true; + } + + boolean writeBuffer() { + if (TaskSnapshotController.isInvalidHardwareBuffer(mSnapshot.getHardwareBuffer())) { + Slog.e(TAG, "Invalid task snapshot hw buffer, taskId=" + mId); + return false; + } + final Bitmap bitmap = Bitmap.wrapHardwareBuffer( + mSnapshot.getHardwareBuffer(), mSnapshot.getColorSpace()); + if (bitmap == null) { + Slog.e(TAG, "Invalid task snapshot hw bitmap"); + return false; + } + + final Bitmap swBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false /* isMutable */); + + final File file = mPersistInfoProvider.getHighResolutionBitmapFile(mId, mUserId); + try { + FileOutputStream fos = new FileOutputStream(file); + swBitmap.compress(JPEG, COMPRESS_QUALITY, fos); + fos.close(); + } catch (IOException e) { + Slog.e(TAG, "Unable to open " + file + " for persisting.", e); + return false; + } + + if (!mPersistInfoProvider.enableLowResSnapshots()) { + swBitmap.recycle(); + return true; + } + + final Bitmap lowResBitmap = Bitmap.createScaledBitmap(swBitmap, + (int) (bitmap.getWidth() * mPersistInfoProvider.lowResScaleFactor()), + (int) (bitmap.getHeight() * mPersistInfoProvider.lowResScaleFactor()), + true /* filter */); + swBitmap.recycle(); + + final File lowResFile = mPersistInfoProvider.getLowResolutionBitmapFile(mId, mUserId); + try { + FileOutputStream lowResFos = new FileOutputStream(lowResFile); + lowResBitmap.compress(JPEG, COMPRESS_QUALITY, lowResFos); + lowResFos.close(); + } catch (IOException e) { + Slog.e(TAG, "Unable to open " + lowResFile + " for persisting.", e); + return false; + } + lowResBitmap.recycle(); + + return true; + } + } + + DeleteWriteQueueItem createDeleteWriteQueueItem(int id, int userId, + PersistInfoProvider provider) { + return new DeleteWriteQueueItem(id, userId, provider); + } + + private class DeleteWriteQueueItem extends WriteQueueItem { + private final int mId; + private final int mUserId; + + DeleteWriteQueueItem(int id, int userId, PersistInfoProvider provider) { + super(provider); + mId = id; + mUserId = userId; + } + + @Override + void write() { + deleteSnapshot(mId, mUserId, mPersistInfoProvider); + } + } +} diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index c1b9e662df9d..7ea4ba8d5c11 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -50,6 +50,7 @@ import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.server.policy.WindowManagerPolicy.ScreenOffListener; +import com.android.server.wm.TaskSnapshotPersister.PersistInfoProvider; import com.android.server.wm.utils.InsetUtils; import com.google.android.collect.Sets; @@ -93,6 +94,8 @@ class TaskSnapshotController { @VisibleForTesting static final int SNAPSHOT_MODE_NONE = 2; + static final String SNAPSHOTS_DIRNAME = "snapshots"; + private final WindowManagerService mService; private final TaskSnapshotCache mCache; @@ -119,11 +122,14 @@ class TaskSnapshotController { * Flag indicating if task snapshot is enabled on this device. */ private boolean mTaskSnapshotEnabled; + private final PersistInfoProvider mPersistInfoProvider; - TaskSnapshotController(WindowManagerService service) { + TaskSnapshotController(WindowManagerService service, SnapshotPersistQueue persistQueue) { mService = service; - mPersister = new TaskSnapshotPersister(mService, Environment::getDataSystemCeDirectory); - mLoader = new TaskSnapshotLoader(mPersister); + mPersistInfoProvider = createPersistInfoProvider(service, + Environment::getDataSystemCeDirectory); + mPersister = new TaskSnapshotPersister(persistQueue, mPersistInfoProvider); + mLoader = new TaskSnapshotLoader(mPersistInfoProvider); mCache = new TaskSnapshotCache(mService, mLoader); mIsRunningOnTv = mService.mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); @@ -137,8 +143,36 @@ class TaskSnapshotController { .getBoolean(com.android.internal.R.bool.config_disableTaskSnapshots); } - void systemReady() { - mPersister.start(); + static PersistInfoProvider createPersistInfoProvider(WindowManagerService service, + TaskSnapshotPersister.DirectoryResolver resolver) { + final float highResTaskSnapshotScale = service.mContext.getResources().getFloat( + com.android.internal.R.dimen.config_highResTaskSnapshotScale); + final float lowResTaskSnapshotScale = service.mContext.getResources().getFloat( + com.android.internal.R.dimen.config_lowResTaskSnapshotScale); + + if (lowResTaskSnapshotScale < 0 || 1 <= lowResTaskSnapshotScale) { + throw new RuntimeException("Low-res scale must be between 0 and 1"); + } + if (highResTaskSnapshotScale <= 0 || 1 < highResTaskSnapshotScale) { + throw new RuntimeException("High-res scale must be between 0 and 1"); + } + if (highResTaskSnapshotScale <= lowResTaskSnapshotScale) { + throw new RuntimeException("High-res scale must be greater than low-res scale"); + } + + final float lowResScaleFactor; + final boolean enableLowResSnapshots; + if (lowResTaskSnapshotScale > 0) { + lowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale; + enableLowResSnapshots = true; + } else { + lowResScaleFactor = 0; + enableLowResSnapshots = false; + } + final boolean use16BitFormat = service.mContext.getResources().getBoolean( + com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat); + return new PersistInfoProvider(resolver, SNAPSHOTS_DIRNAME, + enableLowResSnapshots, lowResScaleFactor, use16BitFormat); } void onTransitionStarting(DisplayContent displayContent) { @@ -247,7 +281,7 @@ class TaskSnapshotController { TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk, boolean isLowResolution) { return mCache.getSnapshot(taskId, userId, restoreFromDisk, isLowResolution - && mPersister.enableLowResSnapshots()); + && mPersistInfoProvider.enableLowResSnapshots()); } /** @@ -310,7 +344,7 @@ class TaskSnapshotController { final boolean isShowWallpaper = mainWindow.hasWallpaper(); if (pixelFormat == PixelFormat.UNKNOWN) { - pixelFormat = mPersister.use16BitFormat() && activity.fillsParent() + pixelFormat = mPersistInfoProvider.use16BitFormat() && activity.fillsParent() && !(isWindowTranslucent && isShowWallpaper) ? PixelFormat.RGB_565 : PixelFormat.RGBA_8888; @@ -416,7 +450,7 @@ class TaskSnapshotController { if (checkIfReadyToSnapshot(task) == null) { return null; } - final int pixelFormat = mPersister.use16BitFormat() + final int pixelFormat = mPersistInfoProvider.use16BitFormat() ? PixelFormat.RGB_565 : PixelFormat.RGBA_8888; return createImeSnapshot(task, pixelFormat); @@ -637,15 +671,6 @@ class TaskSnapshotController { } /** - * Temporarily pauses/unpauses persisting of task snapshots. - * - * @param paused Whether task snapshot persisting should be paused. - */ - void setPersisterPaused(boolean paused) { - mPersister.setPaused(paused); - } - - /** * Called when screen is being turned off. */ void screenTurningOff(int displayId, ScreenOffListener listener) { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java index 9189e51f860c..903613a391cd 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java @@ -31,6 +31,7 @@ import android.hardware.HardwareBuffer; import android.util.Slog; import android.window.TaskSnapshot; +import com.android.server.wm.TaskSnapshotPersister.PersistInfoProvider; import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto; import java.io.File; @@ -48,10 +49,10 @@ class TaskSnapshotLoader { private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotLoader" : TAG_WM; - private final TaskSnapshotPersister mPersister; + private final PersistInfoProvider mPersistInfoProvider; - TaskSnapshotLoader(TaskSnapshotPersister persister) { - mPersister = persister; + TaskSnapshotLoader(PersistInfoProvider persistInfoProvider) { + mPersistInfoProvider = persistInfoProvider; } static class PreRLegacySnapshotConfig { @@ -137,14 +138,15 @@ class TaskSnapshotLoader { * @return The loaded {@link TaskSnapshot} or {@code null} if it couldn't be loaded. */ TaskSnapshot loadTask(int taskId, int userId, boolean loadLowResolutionBitmap) { - final File protoFile = mPersister.getProtoFile(taskId, userId); + final File protoFile = mPersistInfoProvider.getProtoFile(taskId, userId); if (!protoFile.exists()) { return null; } try { final byte[] bytes = Files.readAllBytes(protoFile.toPath()); final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes); - final File highResBitmap = mPersister.getHighResolutionBitmapFile(taskId, userId); + final File highResBitmap = mPersistInfoProvider + .getHighResolutionBitmapFile(taskId, userId); PreRLegacySnapshotConfig legacyConfig = getLegacySnapshotConfig(proto.taskWidth, proto.legacyScale, highResBitmap.exists(), loadLowResolutionBitmap); @@ -152,16 +154,16 @@ class TaskSnapshotLoader { boolean forceLoadReducedJpeg = legacyConfig != null && legacyConfig.mForceLoadReducedJpeg; File bitmapFile = (loadLowResolutionBitmap || forceLoadReducedJpeg) - ? mPersister.getLowResolutionBitmapFile(taskId, userId) : highResBitmap; + ? mPersistInfoProvider.getLowResolutionBitmapFile(taskId, userId) + : highResBitmap; if (!bitmapFile.exists()) { return null; } final Options options = new Options(); - options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent - ? Config.RGB_565 - : Config.ARGB_8888; + options.inPreferredConfig = mPersistInfoProvider.use16BitFormat() + && !proto.isTranslucent ? Config.RGB_565 : Config.ARGB_8888; final Bitmap bitmap = BitmapFactory.decodeFile(bitmapFile.getPath(), options); if (bitmap == null) { Slog.w(TAG, "Failed to load bitmap: " + bitmapFile.getPath()); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index 03098e3eaca9..7e159eb3d8cf 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java @@ -16,32 +16,14 @@ package com.android.server.wm; -import static android.graphics.Bitmap.CompressFormat.JPEG; - -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - import android.annotation.NonNull; -import android.annotation.TestApi; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.os.Process; -import android.os.SystemClock; import android.util.ArraySet; -import android.util.AtomicFile; -import android.util.Slog; import android.window.TaskSnapshot; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.LocalServices; -import com.android.server.pm.UserManagerInternal; -import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayDeque; import java.util.Arrays; /** @@ -51,30 +33,12 @@ import java.util.Arrays; */ class TaskSnapshotPersister { - private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM; - private static final String SNAPSHOTS_DIRNAME = "snapshots"; private static final String LOW_RES_FILE_POSTFIX = "_reduced"; - private static final long DELAY_MS = 100; - private static final int QUALITY = 95; private static final String PROTO_EXTENSION = ".proto"; private static final String BITMAP_EXTENSION = ".jpg"; - private static final int MAX_STORE_QUEUE_DEPTH = 2; - @GuardedBy("mLock") - private final ArrayDeque<WriteQueueItem> mWriteQueue = new ArrayDeque<>(); - @GuardedBy("mLock") - private final ArrayDeque<StoreWriteQueueItem> mStoreQueueItems = new ArrayDeque<>(); - @GuardedBy("mLock") - private boolean mQueueIdling; - @GuardedBy("mLock") - private boolean mPaused; - private boolean mStarted; - private final Object mLock = new Object(); - private final DirectoryResolver mDirectoryResolver; - private final float mLowResScaleFactor; - private boolean mEnableLowResSnapshots; - private final boolean mUse16BitFormat; - private final UserManagerInternal mUserManagerInternal; + // Shared with SnapshotPersistQueue + private final Object mLock; /** * The list of ids of the tasks that have been persisted since {@link #removeObsoleteFiles} was @@ -83,44 +47,75 @@ class TaskSnapshotPersister { @GuardedBy("mLock") private final ArraySet<Integer> mPersistedTaskIdsSinceLastRemoveObsolete = new ArraySet<>(); - TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) { - mDirectoryResolver = resolver; - mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); + private final SnapshotPersistQueue mSnapshotPersistQueue; + private final PersistInfoProvider mPersistInfoProvider; + TaskSnapshotPersister(SnapshotPersistQueue persistQueue, + PersistInfoProvider persistInfoProvider) { + mSnapshotPersistQueue = persistQueue; + mPersistInfoProvider = persistInfoProvider; + mLock = persistQueue.getLock(); + } - final float highResTaskSnapshotScale = service.mContext.getResources().getFloat( - com.android.internal.R.dimen.config_highResTaskSnapshotScale); - final float lowResTaskSnapshotScale = service.mContext.getResources().getFloat( - com.android.internal.R.dimen.config_lowResTaskSnapshotScale); + interface DirectoryResolver { + File getSystemDirectoryForUser(int userId); + } + /** + * Persist information provider, the snapshot persister and loader can know where the file is, + * and the scale of a snapshot, etc. + */ + static class PersistInfoProvider { + protected final DirectoryResolver mDirectoryResolver; + private final String mDirName; + private final boolean mEnableLowResSnapshots; + private final float mLowResScaleFactor; + private final boolean mUse16BitFormat; - if (lowResTaskSnapshotScale < 0 || 1 <= lowResTaskSnapshotScale) { - throw new RuntimeException("Low-res scale must be between 0 and 1"); + PersistInfoProvider(DirectoryResolver directoryResolver, String dirName, + boolean enableLowResSnapshots, float lowResScaleFactor, boolean use16BitFormat) { + mDirectoryResolver = directoryResolver; + mDirName = dirName; + mEnableLowResSnapshots = enableLowResSnapshots; + mLowResScaleFactor = lowResScaleFactor; + mUse16BitFormat = use16BitFormat; } - if (highResTaskSnapshotScale <= 0 || 1 < highResTaskSnapshotScale) { - throw new RuntimeException("High-res scale must be between 0 and 1"); + + @NonNull + File getDirectory(int userId) { + return new File(mDirectoryResolver.getSystemDirectoryForUser(userId), mDirName); } - if (highResTaskSnapshotScale <= lowResTaskSnapshotScale) { - throw new RuntimeException("High-res scale must be greater than low-res scale"); + + /** + * Return if task snapshots are stored in 16 bit pixel format. + * + * @return true if task snapshots are stored in 16 bit pixel format. + */ + boolean use16BitFormat() { + return mUse16BitFormat; } - if (lowResTaskSnapshotScale > 0) { - mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale; - mEnableLowResSnapshots = true; - } else { - mLowResScaleFactor = 0; - mEnableLowResSnapshots = false; + boolean createDirectory(int userId) { + final File dir = getDirectory(userId); + return dir.exists() || dir.mkdir(); } - mUse16BitFormat = service.mContext.getResources().getBoolean( - com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat); - } + File getProtoFile(int index, int userId) { + return new File(getDirectory(userId), index + PROTO_EXTENSION); + } - /** - * Starts persisting. - */ - void start() { - if (!mStarted) { - mStarted = true; - mPersister.start(); + File getLowResolutionBitmapFile(int index, int userId) { + return new File(getDirectory(userId), index + LOW_RES_FILE_POSTFIX + BITMAP_EXTENSION); + } + + File getHighResolutionBitmapFile(int index, int userId) { + return new File(getDirectory(userId), index + BITMAP_EXTENSION); + } + + boolean enableLowResSnapshots() { + return mEnableLowResSnapshots; + } + + float lowResScaleFactor() { + return mLowResScaleFactor; } } @@ -134,7 +129,8 @@ class TaskSnapshotPersister { void persistSnapshot(int taskId, int userId, TaskSnapshot snapshot) { synchronized (mLock) { mPersistedTaskIdsSinceLastRemoveObsolete.add(taskId); - sendToQueueLocked(new StoreWriteQueueItem(taskId, userId, snapshot)); + mSnapshotPersistQueue.sendToQueueLocked(mSnapshotPersistQueue + .createStoreWriteQueueItem(taskId, userId, snapshot, mPersistInfoProvider)); } } @@ -147,7 +143,8 @@ class TaskSnapshotPersister { void onTaskRemovedFromRecents(int taskId, int userId) { synchronized (mLock) { mPersistedTaskIdsSinceLastRemoveObsolete.remove(taskId); - sendToQueueLocked(new DeleteWriteQueueItem(taskId, userId)); + mSnapshotPersistQueue.sendToQueueLocked(mSnapshotPersistQueue + .createDeleteWriteQueueItem(taskId, userId, mPersistInfoProvider)); } } @@ -162,322 +159,20 @@ class TaskSnapshotPersister { void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) { synchronized (mLock) { mPersistedTaskIdsSinceLastRemoveObsolete.clear(); - sendToQueueLocked(new RemoveObsoleteFilesQueueItem(persistentTaskIds, runningUserIds)); - } - } - - void setPaused(boolean paused) { - synchronized (mLock) { - mPaused = paused; - if (!paused) { - mLock.notifyAll(); - } - } - } - - boolean enableLowResSnapshots() { - return mEnableLowResSnapshots; - } - - /** - * Return if task snapshots are stored in 16 bit pixel format. - * - * @return true if task snapshots are stored in 16 bit pixel format. - */ - boolean use16BitFormat() { - return mUse16BitFormat; - } - - @TestApi - void waitForQueueEmpty() { - while (true) { - synchronized (mLock) { - if (mWriteQueue.isEmpty() && mQueueIdling) { - return; - } - } - SystemClock.sleep(DELAY_MS); - } - } - - @GuardedBy("mLock") - private void sendToQueueLocked(WriteQueueItem item) { - mWriteQueue.offer(item); - item.onQueuedLocked(); - ensureStoreQueueDepthLocked(); - if (!mPaused) { - mLock.notifyAll(); - } - } - - @GuardedBy("mLock") - private void ensureStoreQueueDepthLocked() { - while (mStoreQueueItems.size() > MAX_STORE_QUEUE_DEPTH) { - final StoreWriteQueueItem item = mStoreQueueItems.poll(); - mWriteQueue.remove(item); - Slog.i(TAG, "Queue is too deep! Purged item with taskid=" + item.mTaskId); - } - } - - private File getDirectory(int userId) { - return new File(mDirectoryResolver.getSystemDirectoryForUser(userId), SNAPSHOTS_DIRNAME); - } - - File getProtoFile(int taskId, int userId) { - return new File(getDirectory(userId), taskId + PROTO_EXTENSION); - } - - File getHighResolutionBitmapFile(int taskId, int userId) { - return new File(getDirectory(userId), taskId + BITMAP_EXTENSION); - } - - @NonNull - File getLowResolutionBitmapFile(int taskId, int userId) { - return new File(getDirectory(userId), taskId + LOW_RES_FILE_POSTFIX + BITMAP_EXTENSION); - } - - private boolean createDirectory(int userId) { - final File dir = getDirectory(userId); - return dir.exists() || dir.mkdir(); - } - - private void deleteSnapshot(int taskId, int userId) { - final File protoFile = getProtoFile(taskId, userId); - final File bitmapLowResFile = getLowResolutionBitmapFile(taskId, userId); - protoFile.delete(); - if (bitmapLowResFile.exists()) { - bitmapLowResFile.delete(); - } - final File bitmapFile = getHighResolutionBitmapFile(taskId, userId); - if (bitmapFile.exists()) { - bitmapFile.delete(); - } - } - - interface DirectoryResolver { - File getSystemDirectoryForUser(int userId); - } - - private Thread mPersister = new Thread("TaskSnapshotPersister") { - public void run() { - android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - while (true) { - WriteQueueItem next; - boolean isReadyToWrite = false; - synchronized (mLock) { - if (mPaused) { - next = null; - } else { - next = mWriteQueue.poll(); - if (next != null) { - if (next.isReady()) { - isReadyToWrite = true; - next.onDequeuedLocked(); - } else { - mWriteQueue.addLast(next); - } - } - } - } - if (next != null) { - if (isReadyToWrite) { - next.write(); - } - SystemClock.sleep(DELAY_MS); - } - synchronized (mLock) { - final boolean writeQueueEmpty = mWriteQueue.isEmpty(); - if (!writeQueueEmpty && !mPaused) { - continue; - } - try { - mQueueIdling = writeQueueEmpty; - mLock.wait(); - mQueueIdling = false; - } catch (InterruptedException e) { - } - } - } - } - }; - - private abstract class WriteQueueItem { - /** - * @return {@code true} if item is ready to have {@link WriteQueueItem#write} called - */ - boolean isReady() { - return true; - } - - abstract void write(); - - /** - * Called when this queue item has been put into the queue. - */ - void onQueuedLocked() { - } - - /** - * Called when this queue item has been taken out of the queue. - */ - void onDequeuedLocked() { - } - } - - private class StoreWriteQueueItem extends WriteQueueItem { - private final int mTaskId; - private final int mUserId; - private final TaskSnapshot mSnapshot; - - StoreWriteQueueItem(int taskId, int userId, TaskSnapshot snapshot) { - mTaskId = taskId; - mUserId = userId; - mSnapshot = snapshot; - } - - @GuardedBy("mLock") - @Override - void onQueuedLocked() { - mStoreQueueItems.offer(this); - } - - @GuardedBy("mLock") - @Override - void onDequeuedLocked() { - mStoreQueueItems.remove(this); - } - - @Override - boolean isReady() { - return mUserManagerInternal.isUserUnlocked(mUserId); - } - - @Override - void write() { - if (!createDirectory(mUserId)) { - Slog.e(TAG, "Unable to create snapshot directory for user dir=" - + getDirectory(mUserId)); - } - boolean failed = false; - if (!writeProto()) { - failed = true; - } - if (!writeBuffer()) { - failed = true; - } - if (failed) { - deleteSnapshot(mTaskId, mUserId); - } - } - - boolean writeProto() { - final TaskSnapshotProto proto = new TaskSnapshotProto(); - proto.orientation = mSnapshot.getOrientation(); - proto.rotation = mSnapshot.getRotation(); - proto.taskWidth = mSnapshot.getTaskSize().x; - proto.taskHeight = mSnapshot.getTaskSize().y; - proto.insetLeft = mSnapshot.getContentInsets().left; - proto.insetTop = mSnapshot.getContentInsets().top; - proto.insetRight = mSnapshot.getContentInsets().right; - proto.insetBottom = mSnapshot.getContentInsets().bottom; - proto.letterboxInsetLeft = mSnapshot.getLetterboxInsets().left; - proto.letterboxInsetTop = mSnapshot.getLetterboxInsets().top; - proto.letterboxInsetRight = mSnapshot.getLetterboxInsets().right; - proto.letterboxInsetBottom = mSnapshot.getLetterboxInsets().bottom; - proto.isRealSnapshot = mSnapshot.isRealSnapshot(); - proto.windowingMode = mSnapshot.getWindowingMode(); - proto.appearance = mSnapshot.getAppearance(); - proto.isTranslucent = mSnapshot.isTranslucent(); - proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString(); - proto.id = mSnapshot.getId(); - final byte[] bytes = TaskSnapshotProto.toByteArray(proto); - final File file = getProtoFile(mTaskId, mUserId); - final AtomicFile atomicFile = new AtomicFile(file); - FileOutputStream fos = null; - try { - fos = atomicFile.startWrite(); - fos.write(bytes); - atomicFile.finishWrite(fos); - } catch (IOException e) { - atomicFile.failWrite(fos); - Slog.e(TAG, "Unable to open " + file + " for persisting. " + e); - return false; - } - return true; - } - - boolean writeBuffer() { - if (TaskSnapshotController.isInvalidHardwareBuffer(mSnapshot.getHardwareBuffer())) { - Slog.e(TAG, "Invalid task snapshot hw buffer, taskId=" + mTaskId); - return false; - } - final Bitmap bitmap = Bitmap.wrapHardwareBuffer( - mSnapshot.getHardwareBuffer(), mSnapshot.getColorSpace()); - if (bitmap == null) { - Slog.e(TAG, "Invalid task snapshot hw bitmap"); - return false; - } - - final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */); - - final File file = getHighResolutionBitmapFile(mTaskId, mUserId); - try { - FileOutputStream fos = new FileOutputStream(file); - swBitmap.compress(JPEG, QUALITY, fos); - fos.close(); - } catch (IOException e) { - Slog.e(TAG, "Unable to open " + file + " for persisting.", e); - return false; - } - - if (!mEnableLowResSnapshots) { - swBitmap.recycle(); - return true; - } - - final Bitmap lowResBitmap = Bitmap.createScaledBitmap(swBitmap, - (int) (bitmap.getWidth() * mLowResScaleFactor), - (int) (bitmap.getHeight() * mLowResScaleFactor), true /* filter */); - swBitmap.recycle(); - - final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId); - try { - FileOutputStream lowResFos = new FileOutputStream(lowResFile); - lowResBitmap.compress(JPEG, QUALITY, lowResFos); - lowResFos.close(); - } catch (IOException e) { - Slog.e(TAG, "Unable to open " + lowResFile + " for persisting.", e); - return false; - } - lowResBitmap.recycle(); - - return true; - } - } - - private class DeleteWriteQueueItem extends WriteQueueItem { - private final int mTaskId; - private final int mUserId; - - DeleteWriteQueueItem(int taskId, int userId) { - mTaskId = taskId; - mUserId = userId; - } - - @Override - void write() { - deleteSnapshot(mTaskId, mUserId); + mSnapshotPersistQueue.sendToQueueLocked(new RemoveObsoleteFilesQueueItem( + persistentTaskIds, runningUserIds, mPersistInfoProvider)); } } @VisibleForTesting - class RemoveObsoleteFilesQueueItem extends WriteQueueItem { + class RemoveObsoleteFilesQueueItem extends SnapshotPersistQueue.WriteQueueItem { private final ArraySet<Integer> mPersistentTaskIds; private final int[] mRunningUserIds; @VisibleForTesting RemoveObsoleteFilesQueueItem(ArraySet<Integer> persistentTaskIds, - int[] runningUserIds) { + int[] runningUserIds, PersistInfoProvider provider) { + super(provider); mPersistentTaskIds = new ArraySet<>(persistentTaskIds); mRunningUserIds = Arrays.copyOf(runningUserIds, runningUserIds.length); } @@ -489,7 +184,7 @@ class TaskSnapshotPersister { newPersistedTaskIds = new ArraySet<>(mPersistedTaskIdsSinceLastRemoveObsolete); } for (int userId : mRunningUserIds) { - final File dir = getDirectory(userId); + final File dir = mPersistInfoProvider.getDirectory(userId); final String[] files = dir.list(); if (files == null) { continue; diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index cf541fcde459..b6d1381427bd 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -626,12 +626,12 @@ class TransitionController { mWakeT.apply(); // Usually transitions put quite a load onto the system already (with all the things // happening in app), so pause task snapshot persisting to not increase the load. - mAtm.mWindowManager.mTaskSnapshotController.setPersisterPaused(true); + mAtm.mWindowManager.mSnapshotPersistQueue.setPaused(true); mTransitionPlayerProc.setRunningRemoteAnimation(true); } else if (mPlayingTransitions.isEmpty()) { mWakeT.setEarlyWakeupEnd(); mWakeT.apply(); - mAtm.mWindowManager.mTaskSnapshotController.setPersisterPaused(false); + mAtm.mWindowManager.mSnapshotPersistQueue.setPaused(false); mTransitionPlayerProc.setRunningRemoteAnimation(false); mRemotePlayer.clear(); return; diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 4a43f4f73eda..c11391e1236e 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -205,10 +205,10 @@ public class WindowAnimator { if (runningExpensiveAnimations && !mRunningExpensiveAnimations) { // Usually app transitions put quite a load onto the system already (with all the things // happening in app), so pause task snapshot persisting to not increase the load. - mService.mTaskSnapshotController.setPersisterPaused(true); + mService.mSnapshotPersistQueue.setPaused(true); mTransaction.setEarlyWakeupStart(); } else if (!runningExpensiveAnimations && mRunningExpensiveAnimations) { - mService.mTaskSnapshotController.setPersisterPaused(false); + mService.mSnapshotPersistQueue.setPaused(false); mTransaction.setEarlyWakeupEnd(); } mRunningExpensiveAnimations = runningExpensiveAnimations; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 23bce36fc5d4..dbb62a0ad9db 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -694,6 +694,7 @@ public class WindowManagerService extends IWindowManager.Stub // changes the orientation. private final PowerManager.WakeLock mScreenFrozenLock; + final SnapshotPersistQueue mSnapshotPersistQueue; final TaskSnapshotController mTaskSnapshotController; final BlurController mBlurController; @@ -1200,7 +1201,8 @@ public class WindowManagerService extends IWindowManager.Stub mSyncEngine = new BLASTSyncEngine(this); mWindowPlacerLocked = new WindowSurfacePlacer(this); - mTaskSnapshotController = new TaskSnapshotController(this); + mSnapshotPersistQueue = new SnapshotPersistQueue(); + mTaskSnapshotController = new TaskSnapshotController(this, mSnapshotPersistQueue); mWindowTracing = WindowTracing.createDefaultAndStartLooper(this, Choreographer.getInstance()); @@ -5158,7 +5160,7 @@ public class WindowManagerService extends IWindowManager.Stub mSystemReady = true; mPolicy.systemReady(); mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady); - mTaskSnapshotController.systemReady(); + mSnapshotPersistQueue.systemReady(); mHasWideColorGamutSupport = queryWideColorGamutSupport(); mHasHdrSupport = queryHdrSupport(); UiThread.getHandler().post(mSettingsObserver::loadSettings); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java index 98dee38a3f38..b7ba7a857843 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java @@ -23,8 +23,8 @@ import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import android.window.TaskSnapshot; import android.platform.test.annotations.Presubmit; +import android.window.TaskSnapshot; import androidx.test.filters.SmallTest; @@ -98,7 +98,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { public void testReduced_notCached() { final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, false /* restoreFromDisk */, false /* isLowResolution */)); @@ -115,7 +115,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { public void testRestoreFromDisk() { final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, false /* restoreFromDisk */, false /* isLowResolution */)); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java index ec0ca01d14d9..255d1479c6bd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java @@ -22,11 +22,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import android.window.TaskSnapshot; import android.content.res.Configuration; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.util.ArraySet; +import android.window.TaskSnapshot; import androidx.test.filters.MediumTest; @@ -67,7 +67,7 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas @Test public void testPersistAndLoadSnapshot() { mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final File[] files = new File[]{ new File(FILES_DIR.getPath() + "/snapshots/1.proto"), new File(FILES_DIR.getPath() + "/snapshots/1.jpg")}; @@ -91,7 +91,7 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas final ArraySet<Integer> taskIds = new ArraySet<>(); taskIds.add(1); mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId}); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final File[] existsFiles = new File[]{ new File(FILES_DIR.getPath() + "/snapshots/1.proto"), new File(FILES_DIR.getPath() + "/snapshots/1.jpg")}; @@ -111,7 +111,7 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas taskIds.add(1); mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId}); mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final File[] existsFiles = new File[]{ new File(FILES_DIR.getPath() + "/snapshots/1.proto"), new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), @@ -128,7 +128,7 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas public void testReduced_notCached() { final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, false /* restoreFromDisk */, false /* isLowResolution */)); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java index 7409d62e175e..f057d8262ff9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.ActivityManager; @@ -42,6 +43,7 @@ import android.window.TaskSnapshot; import androidx.test.filters.MediumTest; import com.android.server.wm.TaskSnapshotLoader.PreRLegacySnapshotConfig; +import com.android.server.wm.TaskSnapshotPersister.PersistInfoProvider; import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem; import org.junit.Test; @@ -72,7 +74,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa @Test public void testPersistAndLoadSnapshot() { mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"), new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; @@ -86,7 +88,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa snapshot.getHardwareBuffer().close(); mPersister.persistSnapshot(1, mTestUserId, snapshot); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); assertTrueForFiles(files, file -> !file.exists(), " snapshot files must be removed by invalid buffer"); } @@ -95,7 +97,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa public void testTaskRemovedFromRecents() { mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); mPersister.onTaskRemovedFromRecents(1, mTestUserId); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.proto").exists()); assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.jpg").exists()); assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg").exists()); @@ -113,7 +115,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa mPersister.removeObsoleteFiles(new ArraySet<>(), new int[]{mTestUserId}); mPersister.removeObsoleteFiles(new ArraySet<>(), new int[]{mTestUserId}); mPersister.removeObsoleteFiles(new ArraySet<>(), new int[]{mTestUserId}); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); assertTrue(SystemClock.elapsedRealtime() - ms > 500); } @@ -123,15 +125,15 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa @Test public void testPurging() { mPersister.persistSnapshot(100, mTestUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); - mPersister.setPaused(true); + mSnapshotPersistQueue.waitForQueueEmpty(); + mSnapshotPersistQueue.setPaused(true); mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); mPersister.removeObsoleteFiles(new ArraySet<>(), new int[]{mTestUserId}); mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); mPersister.persistSnapshot(3, mTestUserId, createSnapshot()); mPersister.persistSnapshot(4, mTestUserId, createSnapshot()); - mPersister.setPaused(false); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.setPaused(false); + mSnapshotPersistQueue.waitForQueueEmpty(); // Make sure 1,2 were purged but removeObsoleteFiles wasn't. final File[] existsFiles = new File[]{ @@ -147,8 +149,10 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa @Test public void testGetTaskId() { + PersistInfoProvider persistInfoProvider = mock(PersistInfoProvider.class); RemoveObsoleteFilesQueueItem removeObsoleteFilesQueueItem = - mPersister.new RemoveObsoleteFilesQueueItem(new ArraySet<>(), new int[]{}); + mPersister.new RemoveObsoleteFilesQueueItem( + new ArraySet<>(), new int[]{}, persistInfoProvider); assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("blablablulp")); assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("nothing.err")); assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("/invalid/")); @@ -311,7 +315,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa assertFalse(b.isRealSnapshot()); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.persistSnapshot(2, mTestUserId, b); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, @@ -334,7 +338,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa assertEquals(WINDOWING_MODE_PINNED, b.getWindowingMode()); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.persistSnapshot(2, mTestUserId, b); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, @@ -357,7 +361,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa assertFalse(b.isTranslucent()); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.persistSnapshot(2, mTestUserId, b); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, @@ -381,7 +385,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa assertEquals(lightBarFlags, b.getAppearance()); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.persistSnapshot(2, mTestUserId, b); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, @@ -402,7 +406,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa .build(); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.persistSnapshot(2, mTestUserId, b); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, @@ -418,7 +422,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa final ArraySet<Integer> taskIds = new ArraySet<>(); taskIds.add(1); mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId}); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final File[] existsFiles = new File[]{ new File(FILES_DIR.getPath() + "/snapshots/1.proto"), new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), @@ -438,7 +442,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa taskIds.add(1); mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId}); mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final File[] existsFiles = new File[]{ new File(FILES_DIR.getPath() + "/snapshots/1.proto"), new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), @@ -456,7 +460,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa .build(); mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); mPersister.persistSnapshot(2, mTestUserId, a); - mPersister.waitForQueueEmpty(); + mSnapshotPersistQueue.waitForQueueEmpty(); final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java index 677359f5695c..243c9e865cbd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java @@ -45,6 +45,7 @@ import android.window.TaskSnapshot; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; +import com.android.server.wm.TaskSnapshotPersister.PersistInfoProvider; import org.junit.After; import org.junit.AfterClass; @@ -66,6 +67,7 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { private ContextWrapper mContextSpy; private Resources mResourcesSpy; + SnapshotPersistQueue mSnapshotPersistQueue; TaskSnapshotPersister mPersister; TaskSnapshotLoader mLoader; int mTestUserId; @@ -108,9 +110,12 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { com.android.internal.R.dimen.config_lowResTaskSnapshotScale)) .thenReturn(mLowResScale); - mPersister = new TaskSnapshotPersister(mWm, userId -> FILES_DIR); - mLoader = new TaskSnapshotLoader(mPersister); - mPersister.start(); + mSnapshotPersistQueue = new SnapshotPersistQueue(); + PersistInfoProvider provider = + TaskSnapshotController.createPersistInfoProvider(mWm, userId -> FILES_DIR); + mPersister = new TaskSnapshotPersister(mSnapshotPersistQueue, provider); + mLoader = new TaskSnapshotLoader(provider); + mSnapshotPersistQueue.start(); } @After |