diff options
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); } |