diff options
| author | 2017-01-23 16:20:53 +0100 | |
|---|---|---|
| committer | 2017-01-24 16:19:31 +0100 | |
| commit | 829b9cd100ddea44fadb9931c0ff11b11aaba059 (patch) | |
| tree | 37cdd755658fe6856e8f79f076808db1de8ddc8a | |
| parent | 8b702edea1bb211316e1deb3cbe291f2d48d7f77 (diff) | |
Fill task snapshot with background color
Make sure to fill the portions that are not covered by the
snapshot are filled with the task background color.
Also fix an issue where the starting window was removed across
configuration changes.
Test: runtest frameworks-services -c
com.android.server.wm.TaskSnapshotSurfaceTest
Bug: 31339431
Change-Id: I2451be87aff79b337015ab4bba72cfa03c0d3582
8 files changed, 185 insertions, 17 deletions
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 2745fe509268..7b4d289ac82a 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -416,7 +416,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta final Configuration overrideConfig = getOverrideConfiguration(); mWindowContainerController = new TaskWindowContainerController(taskId, this, getStackId(), userId, bounds, overrideConfig, mResizeMode, isHomeTask(), isOnTopLauncher(), onTop, - showForAllUsers); + showForAllUsers, lastTaskDescription); } void removeWindowContainer() { @@ -1402,6 +1402,9 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta } lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary, colorBackground); + if (mWindowContainerController != null) { + mWindowContainerController.setTaskDescription(lastTaskDescription); + } // Update the task affiliation color if we are the parent of the group if (taskId == mAffiliatedTaskId) { mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor(); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 29c685d5fe97..079dc8f2b0ad 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -675,7 +675,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // well there is no point now. if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingData"); startingData = null; - } else if (mChildren.size() == 1 && startingSurface != null) { + } else if (mChildren.size() == 1 && startingSurface != null && !isRelaunching()) { // If this is the last window except for a starting transition window, // we need to get rid of the starting transition. if (getController() != null) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 680d0f2881a6..3a3ec71ff86e 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -30,6 +30,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK; import android.app.ActivityManager.StackId; +import android.app.ActivityManager.TaskDescription; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; @@ -90,9 +91,11 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU // Whether this task is an on-top launcher task, which is determined by the root activity. private boolean mIsOnTopLauncher; + private TaskDescription mTaskDescription; + Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, Configuration overrideConfig, boolean isOnTopLauncher, int resizeMode, boolean homeTask, - TaskWindowContainerController controller) { + TaskDescription taskDescription, TaskWindowContainerController controller) { mTaskId = taskId; mStack = stack; mUserId = userId; @@ -102,6 +105,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU mHomeTask = homeTask; setController(controller); setBounds(bounds, overrideConfig); + mTaskDescription = taskDescription; } DisplayContent getDisplayContent() { @@ -647,6 +651,14 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU } } + void setTaskDescription(TaskDescription taskDescription) { + mTaskDescription = taskDescription; + } + + TaskDescription getTaskDescription() { + return mTaskDescription; + } + @Override boolean fillsParent() { return mFillsParent || !StackId.isTaskResizeAllowed(mStack.mStackId); @@ -688,6 +700,5 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU pw.println(triplePrefix + "Activity #" + i + " " + wtoken); wtoken.dump(pw, triplePrefix); } - } } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 4a094237b037..cfcbbd0358b5 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -26,12 +26,16 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.app.ActivityManager.TaskDescription; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.GraphicBuffer; +import android.graphics.Paint; import android.graphics.Rect; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Slog; @@ -43,6 +47,7 @@ import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy.StartingSurface; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.view.BaseIWindow; /** @@ -61,6 +66,7 @@ class TaskSnapshotSurface implements StartingSurface { private final WindowManagerService mService; private boolean mHasDrawn; private boolean mReportNextDraw; + private Paint mFillBackgroundPaint = new Paint(); static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token, GraphicBuffer snapshot) { @@ -73,6 +79,7 @@ class TaskSnapshotSurface implements StartingSurface { final Rect tmpRect = new Rect(); final Rect tmpFrame = new Rect(); final Configuration tmpConfiguration = new Configuration(); + int fillBackgroundColor = Color.WHITE; synchronized (service.mWindowMap) { layoutParams.type = TYPE_APPLICATION_STARTING; layoutParams.format = snapshot.getFormat(); @@ -90,6 +97,12 @@ class TaskSnapshotSurface implements StartingSurface { layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; layoutParams.setTitle(String.format(TITLE_FORMAT, token.mTask.mTaskId)); + if (token.mTask != null) { + final TaskDescription taskDescription = token.mTask.getTaskDescription(); + if (taskDescription != null) { + fillBackgroundColor = taskDescription.getBackgroundColor(); + } + } } try { final int res = session.addToDisplay(window, window.mSeq, layoutParams, @@ -103,7 +116,7 @@ class TaskSnapshotSurface implements StartingSurface { // Local call. } final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, - surface); + surface, fillBackgroundColor); window.setOuter(snapshotSurface); try { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame, @@ -116,11 +129,14 @@ class TaskSnapshotSurface implements StartingSurface { return snapshotSurface; } - private TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface) { + @VisibleForTesting + TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface, + int fillBackgroundColor) { mService = service; mSession = WindowManagerGlobal.getWindowSession(); mWindow = window; mSurface = surface; + mFillBackgroundPaint.setColor(fillBackgroundColor); } @Override @@ -136,7 +152,9 @@ class TaskSnapshotSurface implements StartingSurface { // TODO: Just wrap the buffer here without any copying. final Canvas c = mSurface.lockHardwareCanvas(); - c.drawBitmap(Bitmap.createHardwareBitmap(snapshot), 0, 0, null); + final Bitmap b = Bitmap.createHardwareBitmap(snapshot); + fillEmptyBackground(c, b); + c.drawBitmap(b, 0, 0, null); mSurface.unlockCanvasAndPost(c); final boolean reportNextDraw; synchronized (mService.mWindowMap) { @@ -149,6 +167,21 @@ class TaskSnapshotSurface implements StartingSurface { mSurface.release(); } + @VisibleForTesting + void fillEmptyBackground(Canvas c, Bitmap b) { + final boolean fillHorizontally = c.getWidth() > b.getWidth(); + final boolean fillVertically = c.getHeight() > b.getHeight(); + if (fillHorizontally) { + c.drawRect(b.getWidth(), 0, c.getWidth(), fillVertically + ? b.getHeight() + : c.getHeight(), + mFillBackgroundPaint); + } + if (fillVertically) { + c.drawRect(0, b.getHeight(), c.getWidth(), c.getHeight(), mFillBackgroundPaint); + } + } + private void reportDrawn() { synchronized (mService.mWindowMap) { mReportNextDraw = false; @@ -160,7 +193,7 @@ class TaskSnapshotSurface implements StartingSurface { } } - private static Handler sHandler = new Handler() { + private static Handler sHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java index 3c438ca3195f..61a2cd96f6c5 100644 --- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java +++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import android.app.ActivityManager.TaskDescription; import android.app.ActivityManager.TaskSnapshot; import android.content.res.Configuration; import android.graphics.Rect; @@ -62,7 +63,8 @@ public class TaskWindowContainerController public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener, int stackId, int userId, Rect bounds, Configuration overrideConfig, int resizeMode, - boolean homeTask, boolean isOnTopLauncher, boolean toTop, boolean showForAllUsers) { + boolean homeTask, boolean isOnTopLauncher, boolean toTop, boolean showForAllUsers, + TaskDescription taskDescription) { super(listener, WindowManagerService.getInstance()); mTaskId = taskId; @@ -79,7 +81,7 @@ public class TaskWindowContainerController } EventLog.writeEvent(WM_TASK_CREATED, taskId, stackId); final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode, - homeTask, isOnTopLauncher); + homeTask, isOnTopLauncher, taskDescription); final int position = toTop ? POSITION_TOP : POSITION_BOTTOM; stack.addTask(task, position, showForAllUsers, true /* moveParents */); } @@ -88,9 +90,9 @@ public class TaskWindowContainerController @VisibleForTesting Task createTask(int taskId, TaskStack stack, int userId, Rect bounds, Configuration overrideConfig, int resizeMode, boolean homeTask, - boolean isOnTopLauncher) { + boolean isOnTopLauncher, TaskDescription taskDescription) { return new Task(taskId, stack, userId, mService, bounds, overrideConfig, isOnTopLauncher, - resizeMode, homeTask, this); + resizeMode, homeTask, taskDescription, this); } @Override @@ -263,6 +265,16 @@ public class TaskWindowContainerController } } + public void setTaskDescription(TaskDescription taskDescription) { + synchronized (mWindowMap) { + if (mContainer == null) { + Slog.w(TAG_WM, "setTaskDescription: taskId " + mTaskId + " not found."); + return; + } + mContainer.setTaskDescription(taskDescription); + } + } + void reportSnapshotChanged(TaskSnapshot snapshot) { mHandler.obtainMessage(REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget(); } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java new file mode 100644 index 000000000000..aab75ee1699b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2017 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 org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Color; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test class for {@link TaskSnapshotSurface}. + * + * runtest frameworks-services -c com.android.server.wm.TaskSnapshotSurfaceTest + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class TaskSnapshotSurfaceTest extends WindowTestsBase { + + private TaskSnapshotSurface mSurface; + + @Before + public void setUp() { + mSurface = new TaskSnapshotSurface(null, null, null, Color.WHITE); + } + + @Test + public void fillEmptyBackground_fillHorizontally() throws Exception { + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(200); + when(mockCanvas.getHeight()).thenReturn(100); + final Bitmap b = Bitmap.createBitmap(100, 200, Config.ARGB_8888); + mSurface.fillEmptyBackground(mockCanvas, b); + verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); + } + + @Test + public void fillEmptyBackground_fillVertically() throws Exception { + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(200); + final Bitmap b = Bitmap.createBitmap(200, 100, Config.ARGB_8888); + mSurface.fillEmptyBackground(mockCanvas, b); + verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any()); + } + + @Test + public void fillEmptyBackground_fillBoth() throws Exception { + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(200); + when(mockCanvas.getHeight()).thenReturn(200); + final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888); + mSurface.fillEmptyBackground(mockCanvas, b); + verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); + verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any()); + } + + @Test + public void fillEmptyBackground_dontFill_sameSize() throws Exception { + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888); + mSurface.fillEmptyBackground(mockCanvas, b); + verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); + } + + @Test + public void fillEmptyBackground_dontFill_bitmapLarger() throws Exception { + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + final Bitmap b = Bitmap.createBitmap(200, 200, Config.ARGB_8888); + mSurface.fillEmptyBackground(mockCanvas, b); + verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java index 466da942edbc..085cfd8b1ef0 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java @@ -20,6 +20,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import android.app.ActivityManager.TaskDescription; import android.content.Context; import android.graphics.Rect; import android.os.Binder; @@ -76,7 +77,7 @@ public class WindowFrameTests { final Rect mInsetBounds = new Rect(); boolean mFullscreenForTest = true; TaskWithBounds(Rect bounds) { - super(0, mStubStack, 0, sWm, null, null, false, 0, false, null); + super(0, mStubStack, 0, sWm, null, null, false, 0, false, new TaskDescription(), null); mBounds = bounds; } @Override diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index 7c25e43c11aa..ae344ddb38f3 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import android.app.ActivityManager.TaskDescription; import android.app.ActivityManagerInternal; import android.content.res.Configuration; import android.graphics.Rect; @@ -180,7 +181,7 @@ class WindowTestsBase { /**Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ static Task createTaskInStack(TaskStack stack, int userId) { final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, false, 0, - false, null); + false, new TaskDescription(), null); stack.addTask(newTask, POSITION_TOP); return newTask; } @@ -239,7 +240,7 @@ class WindowTestsBase { Configuration overrideConfig, boolean isOnTopLauncher, int resizeMode, boolean homeTask, TaskWindowContainerController controller) { super(taskId, stack, userId, service, bounds, overrideConfig, isOnTopLauncher, - resizeMode, homeTask, controller); + resizeMode, homeTask, new TaskDescription(), controller); } boolean shouldDeferRemoval() { @@ -270,13 +271,14 @@ class WindowTestsBase { TestTaskWindowContainerController(int stackId) { super(sNextTaskId++, snapshot -> {}, stackId, 0 /* userId */, null /* bounds */, EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, false /* homeTask*/, - false /* isOnTopLauncher */, true /* toTop*/, true /* showForAllUsers */); + false /* isOnTopLauncher */, true /* toTop*/, true /* showForAllUsers */, + new TaskDescription()); } @Override TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, Configuration overrideConfig, int resizeMode, boolean homeTask, - boolean isOnTopLauncher) { + boolean isOnTopLauncher, TaskDescription taskDescription) { return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, isOnTopLauncher, resizeMode, homeTask, this); } |