/*
|
* Copyright (C) 2011 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.Manifest.permission.DEVICE_POWER;
|
import static android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
|
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
|
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
|
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
|
import android.content.ClipData;
|
import android.graphics.Rect;
|
import android.graphics.Region;
|
import android.os.Binder;
|
import android.os.Bundle;
|
import android.os.IBinder;
|
import android.os.Parcel;
|
import android.os.Process;
|
import android.os.RemoteException;
|
import android.os.Trace;
|
import android.os.UserHandle;
|
import android.util.MergedConfiguration;
|
import android.util.Slog;
|
import android.view.DisplayCutout;
|
import android.view.IWindow;
|
import android.view.IWindowId;
|
import android.view.IWindowSession;
|
import android.view.IWindowSessionCallback;
|
import android.view.InputChannel;
|
import android.view.InsetsState;
|
import android.view.SurfaceControl;
|
import android.view.SurfaceSession;
|
import android.view.WindowManager;
|
|
import com.android.internal.os.logging.MetricsLoggerWrapper;
|
import com.android.server.wm.WindowManagerService.H;
|
|
import java.io.PrintWriter;
|
import java.util.HashSet;
|
import java.util.List;
|
import java.util.Set;
|
import java.util.function.BiConsumer;
|
|
/**
|
* This class represents an active client session. There is generally one
|
* Session object per process that is interacting with the window manager.
|
*/
|
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
|
final WindowManagerService mService;
|
final IWindowSessionCallback mCallback;
|
final int mUid;
|
final int mPid;
|
private final String mStringName;
|
SurfaceSession mSurfaceSession;
|
private int mNumWindow = 0;
|
// Set of visible application overlay window surfaces connected to this session.
|
private final Set<WindowSurfaceController> mAppOverlaySurfaces = new HashSet<>();
|
// Set of visible alert window surfaces connected to this session.
|
private final Set<WindowSurfaceController> mAlertWindowSurfaces = new HashSet<>();
|
private final DragDropController mDragDropController;
|
final boolean mCanAddInternalSystemWindow;
|
final boolean mCanHideNonSystemOverlayWindows;
|
final boolean mCanAcquireSleepToken;
|
private AlertWindowNotification mAlertWindowNotification;
|
private boolean mShowingAlertWindowNotificationAllowed;
|
private boolean mClientDead = false;
|
private float mLastReportedAnimatorScale;
|
private String mPackageName;
|
private String mRelayoutTag;
|
|
public Session(WindowManagerService service, IWindowSessionCallback callback) {
|
mService = service;
|
mCallback = callback;
|
mUid = Binder.getCallingUid();
|
mPid = Binder.getCallingPid();
|
mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
|
mCanAddInternalSystemWindow = service.mContext.checkCallingOrSelfPermission(
|
INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED;
|
mCanHideNonSystemOverlayWindows = service.mContext.checkCallingOrSelfPermission(
|
HIDE_NON_SYSTEM_OVERLAY_WINDOWS) == PERMISSION_GRANTED;
|
mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
|
== PERMISSION_GRANTED;
|
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
|
mDragDropController = mService.mDragDropController;
|
StringBuilder sb = new StringBuilder();
|
sb.append("Session{");
|
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
sb.append(" ");
|
sb.append(mPid);
|
if (mUid < Process.FIRST_APPLICATION_UID) {
|
sb.append(":");
|
sb.append(mUid);
|
} else {
|
sb.append(":u");
|
sb.append(UserHandle.getUserId(mUid));
|
sb.append('a');
|
sb.append(UserHandle.getAppId(mUid));
|
}
|
sb.append("}");
|
mStringName = sb.toString();
|
|
try {
|
mCallback.asBinder().linkToDeath(this, 0);
|
} catch (RemoteException e) {
|
// The caller has died, so we can just forget about this.
|
// Hmmm, should we call killSessionLocked()??
|
}
|
}
|
|
@Override
|
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
|
throws RemoteException {
|
try {
|
return super.onTransact(code, data, reply, flags);
|
} catch (RuntimeException e) {
|
// Log all 'real' exceptions thrown to the caller
|
if (!(e instanceof SecurityException)) {
|
Slog.wtf(TAG_WM, "Window Session Crash", e);
|
}
|
throw e;
|
}
|
}
|
|
@Override
|
public void binderDied() {
|
synchronized (mService.mGlobalLock) {
|
mCallback.asBinder().unlinkToDeath(this, 0);
|
mClientDead = true;
|
killSessionLocked();
|
}
|
}
|
|
@Override
|
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
|
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
|
Rect outStableInsets, Rect outOutsets,
|
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
|
InsetsState outInsetsState) {
|
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
|
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
|
outInsetsState);
|
}
|
|
@Override
|
public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
|
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
|
InsetsState outInsetsState) {
|
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
|
new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */,
|
new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
|
outInsetsState);
|
}
|
|
@Override
|
public void remove(IWindow window) {
|
mService.removeWindow(this, window);
|
}
|
|
@Override
|
public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) {
|
mService.setWillReplaceWindows(appToken, childrenOnly);
|
}
|
|
@Override
|
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
|
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
|
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
|
Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
|
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
|
SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
|
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
|
+ Binder.getCallingPid());
|
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
|
int res = mService.relayoutWindow(this, window, seq, attrs,
|
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
|
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
|
outStableInsets, outsets, outBackdropFrame, cutout,
|
mergedConfiguration, outSurfaceControl, outInsetsState);
|
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
|
+ Binder.getCallingPid());
|
return res;
|
}
|
|
@Override
|
public boolean outOfMemory(IWindow window) {
|
return mService.outOfMemoryWindow(this, window);
|
}
|
|
@Override
|
public void setTransparentRegion(IWindow window, Region region) {
|
mService.setTransparentRegionWindow(this, window, region);
|
}
|
|
@Override
|
public void setInsets(IWindow window, int touchableInsets,
|
Rect contentInsets, Rect visibleInsets, Region touchableArea) {
|
mService.setInsetsWindow(this, window, touchableInsets, contentInsets,
|
visibleInsets, touchableArea);
|
}
|
|
@Override
|
public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
|
mService.getWindowDisplayFrame(this, window, outDisplayFrame);
|
}
|
|
@Override
|
public void finishDrawing(IWindow window) {
|
if (WindowManagerService.localLOGV) Slog.v(
|
TAG_WM, "IWindow finishDrawing called for " + window);
|
mService.finishDrawingWindow(this, window);
|
}
|
|
@Override
|
public void setInTouchMode(boolean mode) {
|
synchronized (mService.mGlobalLock) {
|
mService.mInTouchMode = mode;
|
}
|
}
|
|
@Override
|
public boolean getInTouchMode() {
|
synchronized (mService.mGlobalLock) {
|
return mService.mInTouchMode;
|
}
|
}
|
|
@Override
|
public boolean performHapticFeedback(int effectId, boolean always) {
|
long ident = Binder.clearCallingIdentity();
|
try {
|
return mService.mPolicy.performHapticFeedback(mUid, mPackageName,
|
effectId, always, null);
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
|
/* Drag/drop */
|
|
@Override
|
public IBinder performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource,
|
float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {
|
final int callerPid = Binder.getCallingPid();
|
final int callerUid = Binder.getCallingUid();
|
final long ident = Binder.clearCallingIdentity();
|
try {
|
return mDragDropController.performDrag(mSurfaceSession, callerPid, callerUid, window,
|
flags, surface, touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data);
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
|
@Override
|
public void reportDropResult(IWindow window, boolean consumed) {
|
final long ident = Binder.clearCallingIdentity();
|
try {
|
mDragDropController.reportDropResult(window, consumed);
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
|
@Override
|
public void cancelDragAndDrop(IBinder dragToken, boolean skipAnimation) {
|
final long ident = Binder.clearCallingIdentity();
|
try {
|
mDragDropController.cancelDragAndDrop(dragToken, skipAnimation);
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
|
@Override
|
public void dragRecipientEntered(IWindow window) {
|
mDragDropController.dragRecipientEntered(window);
|
}
|
|
@Override
|
public void dragRecipientExited(IWindow window) {
|
mDragDropController.dragRecipientExited(window);
|
}
|
|
@Override
|
public boolean startMovingTask(IWindow window, float startX, float startY) {
|
if (DEBUG_TASK_POSITIONING) Slog.d(
|
TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");
|
|
long ident = Binder.clearCallingIdentity();
|
try {
|
return mService.mTaskPositioningController.startMovingTask(window, startX, startY);
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
|
@Override
|
public void finishMovingTask(IWindow window) {
|
if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishMovingTask");
|
|
long ident = Binder.clearCallingIdentity();
|
try {
|
mService.mTaskPositioningController.finishTaskPositioning(window);
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
|
@Override
|
public void reportSystemGestureExclusionChanged(IWindow window, List<Rect> exclusionRects) {
|
long ident = Binder.clearCallingIdentity();
|
try {
|
mService.reportSystemGestureExclusionChanged(this, window, exclusionRects);
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
|
private void actionOnWallpaper(IBinder window,
|
BiConsumer<WallpaperController, WindowState> action) {
|
final WindowState windowState = mService.windowForClientLocked(this, window, true);
|
action.accept(windowState.getDisplayContent().mWallpaperController, windowState);
|
}
|
|
@Override
|
public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
|
synchronized (mService.mGlobalLock) {
|
long ident = Binder.clearCallingIdentity();
|
try {
|
actionOnWallpaper(window, (wpController, windowState) ->
|
wpController.setWindowWallpaperPosition(windowState, x, y, xStep, yStep));
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
}
|
|
@Override
|
public void wallpaperOffsetsComplete(IBinder window) {
|
synchronized (mService.mGlobalLock) {
|
actionOnWallpaper(window, (wpController, windowState) ->
|
wpController.wallpaperOffsetsComplete(window));
|
}
|
}
|
|
@Override
|
public void setWallpaperDisplayOffset(IBinder window, int x, int y) {
|
synchronized (mService.mGlobalLock) {
|
long ident = Binder.clearCallingIdentity();
|
try {
|
actionOnWallpaper(window, (wpController, windowState) ->
|
wpController.setWindowWallpaperDisplayOffset(windowState, x, y));
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
}
|
|
@Override
|
public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
|
int z, Bundle extras, boolean sync) {
|
synchronized (mService.mGlobalLock) {
|
long ident = Binder.clearCallingIdentity();
|
try {
|
final WindowState windowState = mService.windowForClientLocked(this, window, true);
|
return windowState.getDisplayContent().mWallpaperController
|
.sendWindowWallpaperCommand(windowState, action, x, y, z, extras, sync);
|
} finally {
|
Binder.restoreCallingIdentity(ident);
|
}
|
}
|
}
|
|
@Override
|
public void wallpaperCommandComplete(IBinder window, Bundle result) {
|
synchronized (mService.mGlobalLock) {
|
actionOnWallpaper(window, (wpController, windowState) ->
|
wpController.wallpaperCommandComplete(window));
|
}
|
}
|
|
@Override
|
public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
|
synchronized (mService.mGlobalLock) {
|
final long identity = Binder.clearCallingIdentity();
|
try {
|
mService.onRectangleOnScreenRequested(token, rectangle);
|
} finally {
|
Binder.restoreCallingIdentity(identity);
|
}
|
}
|
}
|
|
@Override
|
public IWindowId getWindowId(IBinder window) {
|
return mService.getWindowId(window);
|
}
|
|
@Override
|
public void pokeDrawLock(IBinder window) {
|
final long identity = Binder.clearCallingIdentity();
|
try {
|
mService.pokeDrawLock(this, window);
|
} finally {
|
Binder.restoreCallingIdentity(identity);
|
}
|
}
|
|
@Override
|
public void updatePointerIcon(IWindow window) {
|
final long identity = Binder.clearCallingIdentity();
|
try {
|
mService.updatePointerIcon(window);
|
} finally {
|
Binder.restoreCallingIdentity(identity);
|
}
|
}
|
|
@Override
|
public void reparentDisplayContent(IWindow window, SurfaceControl sc, int displayId) {
|
mService.reparentDisplayContent(window, sc, displayId);
|
}
|
|
@Override
|
public void updateDisplayContentLocation(IWindow window, int x, int y, int displayId) {
|
mService.updateDisplayContentLocation(window, x, y, displayId);
|
}
|
|
@Override
|
public void updateTapExcludeRegion(IWindow window, int regionId, Region region) {
|
final long identity = Binder.clearCallingIdentity();
|
try {
|
mService.updateTapExcludeRegion(window, regionId, region);
|
} finally {
|
Binder.restoreCallingIdentity(identity);
|
}
|
}
|
|
@Override
|
public void insetsModified(IWindow window, InsetsState state) {
|
synchronized (mService.mGlobalLock) {
|
final WindowState windowState = mService.windowForClientLocked(this, window,
|
false /* throwOnError */);
|
if (windowState != null) {
|
windowState.getDisplayContent().getInsetsStateController().onInsetsModified(
|
windowState, state);
|
}
|
}
|
}
|
|
void windowAddedLocked(String packageName) {
|
mPackageName = packageName;
|
mRelayoutTag = "relayoutWindow: " + mPackageName;
|
if (mSurfaceSession == null) {
|
if (WindowManagerService.localLOGV) Slog.v(
|
TAG_WM, "First window added to " + this + ", creating SurfaceSession");
|
mSurfaceSession = new SurfaceSession();
|
if (SHOW_TRANSACTIONS) Slog.i(
|
TAG_WM, " NEW SURFACE SESSION " + mSurfaceSession);
|
mService.mSessions.add(this);
|
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
|
mService.dispatchNewAnimatorScaleLocked(this);
|
}
|
}
|
mNumWindow++;
|
}
|
|
void windowRemovedLocked() {
|
mNumWindow--;
|
killSessionLocked();
|
}
|
|
|
void onWindowSurfaceVisibilityChanged(WindowSurfaceController surfaceController,
|
boolean visible, int type) {
|
|
if (!isSystemAlertWindowType(type)) {
|
return;
|
}
|
|
boolean changed;
|
|
if (!mCanAddInternalSystemWindow) {
|
// We want to track non-system signature apps adding alert windows so we can post an
|
// on-going notification for the user to control their visibility.
|
if (visible) {
|
changed = mAlertWindowSurfaces.add(surfaceController);
|
MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, true);
|
} else {
|
changed = mAlertWindowSurfaces.remove(surfaceController);
|
MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, true);
|
}
|
|
if (changed) {
|
if (mAlertWindowSurfaces.isEmpty()) {
|
cancelAlertWindowNotification();
|
} else if (mAlertWindowNotification == null){
|
mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
|
if (mShowingAlertWindowNotificationAllowed) {
|
mAlertWindowNotification.post();
|
}
|
}
|
}
|
}
|
|
if (type != TYPE_APPLICATION_OVERLAY) {
|
return;
|
}
|
|
if (visible) {
|
changed = mAppOverlaySurfaces.add(surfaceController);
|
MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, false);
|
} else {
|
changed = mAppOverlaySurfaces.remove(surfaceController);
|
MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, false);
|
}
|
|
if (changed) {
|
// Notify activity manager of changes to app overlay windows so it can adjust the
|
// importance score for the process.
|
setHasOverlayUi(!mAppOverlaySurfaces.isEmpty());
|
}
|
}
|
|
void setShowingAlertWindowNotificationAllowed(boolean allowed) {
|
mShowingAlertWindowNotificationAllowed = allowed;
|
if (mAlertWindowNotification != null) {
|
if (allowed) {
|
mAlertWindowNotification.post();
|
} else {
|
mAlertWindowNotification.cancel(false /* deleteChannel */);
|
}
|
}
|
}
|
|
private void killSessionLocked() {
|
if (mNumWindow > 0 || !mClientDead) {
|
return;
|
}
|
|
mService.mSessions.remove(this);
|
if (mSurfaceSession == null) {
|
return;
|
}
|
|
if (WindowManagerService.localLOGV) Slog.v(TAG_WM, "Last window removed from " + this
|
+ ", destroying " + mSurfaceSession);
|
if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " KILL SURFACE SESSION " + mSurfaceSession);
|
try {
|
mSurfaceSession.kill();
|
} catch (Exception e) {
|
Slog.w(TAG_WM, "Exception thrown when killing surface session " + mSurfaceSession
|
+ " in session " + this + ": " + e.toString());
|
}
|
mSurfaceSession = null;
|
mAlertWindowSurfaces.clear();
|
mAppOverlaySurfaces.clear();
|
setHasOverlayUi(false);
|
cancelAlertWindowNotification();
|
}
|
|
private void setHasOverlayUi(boolean hasOverlayUi) {
|
mService.mH.obtainMessage(H.SET_HAS_OVERLAY_UI, mPid, hasOverlayUi ? 1 : 0).sendToTarget();
|
}
|
|
private void cancelAlertWindowNotification() {
|
if (mAlertWindowNotification == null) {
|
return;
|
}
|
mAlertWindowNotification.cancel(true /* deleteChannel */);
|
mAlertWindowNotification = null;
|
}
|
|
void dump(PrintWriter pw, String prefix) {
|
pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
|
pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow);
|
pw.print(" mAppOverlaySurfaces="); pw.print(mAppOverlaySurfaces);
|
pw.print(" mAlertWindowSurfaces="); pw.print(mAlertWindowSurfaces);
|
pw.print(" mClientDead="); pw.print(mClientDead);
|
pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
|
pw.print(prefix); pw.print("mPackageName="); pw.println(mPackageName);
|
}
|
|
@Override
|
public String toString() {
|
return mStringName;
|
}
|
|
boolean hasAlertWindowSurfaces() {
|
return !mAlertWindowSurfaces.isEmpty();
|
}
|
}
|