/* * Copyright (C) 2016 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.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT; 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.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.util.Slog; import android.view.Display; import android.view.IWindowSession; import android.view.Surface; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy.StartingSurface; import com.android.internal.view.BaseIWindow; /** * This class represents a starting window that shows a snapshot. *

* DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING METHODS OF THIS CLASS! */ class TaskSnapshotSurface implements StartingSurface { private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM; private static final int MSG_REPORT_DRAW = 0; private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; private final Window mWindow; private final Surface mSurface; private final IWindowSession mSession; private final WindowManagerService mService; private boolean mHasDrawn; private boolean mReportNextDraw; static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token, GraphicBuffer snapshot) { final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); final Window window = new Window(); final IWindowSession session = WindowManagerGlobal.getWindowSession(); window.setSession(session); final Surface surface = new Surface(); final Rect tmpRect = new Rect(); final Rect tmpFrame = new Rect(); final Configuration tmpConfiguration = new Configuration(); synchronized (service.mWindowMap) { layoutParams.type = TYPE_APPLICATION_STARTING; layoutParams.format = snapshot.getFormat(); layoutParams.flags = FLAG_LAYOUT_INSET_DECOR | FLAG_LAYOUT_IN_SCREEN | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT; layoutParams.token = token.token; layoutParams.width = LayoutParams.MATCH_PARENT; layoutParams.height = LayoutParams.MATCH_PARENT; // TODO: Inherit behavior whether to draw behind status bar/nav bar. layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; layoutParams.setTitle(String.format(TITLE_FORMAT, token.mTask.mTaskId)); } try { final int res = session.addToDisplay(window, window.mSeq, layoutParams, View.VISIBLE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect, tmpRect, null); if (res < 0) { Slog.w(TAG, "Failed to add snapshot starting window res=" + res); return null; } } catch (RemoteException e) { // Local call. } final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, surface); window.setOuter(snapshotSurface); try { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpConfiguration, surface); } catch (RemoteException e) { // Local call. } snapshotSurface.drawSnapshot(snapshot); return snapshotSurface; } private TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface) { mService = service; mSession = WindowManagerGlobal.getWindowSession(); mWindow = window; mSurface = surface; } @Override public void remove() { try { mSession.remove(mWindow); } catch (RemoteException e) { // Local call. } } private void drawSnapshot(GraphicBuffer snapshot) { // TODO: Just wrap the buffer here without any copying. final Canvas c = mSurface.lockHardwareCanvas(); c.drawBitmap(Bitmap.createHardwareBitmap(snapshot), 0, 0, null); mSurface.unlockCanvasAndPost(c); final boolean reportNextDraw; synchronized (mService.mWindowMap) { mHasDrawn = true; reportNextDraw = mReportNextDraw; } if (reportNextDraw) { reportDrawn(); } } private void reportDrawn() { synchronized (mService.mWindowMap) { mReportNextDraw = false; } try { mSession.finishDrawing(mWindow); } catch (RemoteException e) { // Local call. } } private static Handler sHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_REPORT_DRAW: final boolean hasDrawn; final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj; synchronized (surface.mService.mWindowMap) { hasDrawn = surface.mHasDrawn; if (!hasDrawn) { surface.mReportNextDraw = true; } } if (hasDrawn) { surface.reportDrawn(); } break; } } }; private static class Window extends BaseIWindow { private TaskSnapshotSurface mOuter; public void setOuter(TaskSnapshotSurface outer) { mOuter = outer; } @Override public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig, Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar) { if (reportDraw) { sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget(); } } } }