/*
* Copyright (C) 2013 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.display;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
/**
* A display adapter that provides virtual displays on behalf of applications.
*
* Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
*
*/
final class VirtualDisplayAdapter extends DisplayAdapter {
static final String TAG = "VirtualDisplayAdapter";
static final boolean DEBUG = false;
private final ArrayMap mVirtualDisplayDevices =
new ArrayMap();
// Called with SyncRoot lock held.
public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener) {
super(syncRoot, context, handler, listener, TAG);
}
public DisplayDevice createVirtualDisplayLocked(IBinder appToken,
int ownerUid, String ownerPackageName,
String name, int width, int height, int densityDpi, Surface surface, int flags) {
boolean secure = (flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
IBinder displayToken = SurfaceControl.createDisplay(name, secure);
VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
ownerUid, ownerPackageName, name, width, height, densityDpi, surface, flags);
try {
appToken.linkToDeath(device, 0);
} catch (RemoteException ex) {
device.destroyLocked();
return null;
}
mVirtualDisplayDevices.put(appToken, device);
// Return the display device without actually sending the event indicating
// that it was added. The caller will handle it.
return device;
}
public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
if (device != null) {
device.setSurfaceLocked(surface);
}
}
public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
if (device != null) {
device.destroyLocked();
appToken.unlinkToDeath(device, 0);
}
// Return the display device that was removed without actually sending the
// event indicating that it was removed. The caller will handle it.
return device;
}
private void handleBinderDiedLocked(IBinder appToken) {
VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
if (device != null) {
Slog.i(TAG, "Virtual display device released because application token died: "
+ device.mOwnerPackageName);
device.destroyLocked();
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
}
}
private final class VirtualDisplayDevice extends DisplayDevice
implements DeathRecipient {
private final IBinder mAppToken;
private final int mOwnerUid;
final String mOwnerPackageName;
private final String mName;
private final int mWidth;
private final int mHeight;
private final int mDensityDpi;
private final int mFlags;
private Surface mSurface;
private DisplayDeviceInfo mInfo;
public VirtualDisplayDevice(IBinder displayToken,
IBinder appToken, int ownerUid, String ownerPackageName,
String name, int width, int height, int densityDpi, Surface surface, int flags) {
super(VirtualDisplayAdapter.this, displayToken);
mAppToken = appToken;
mOwnerUid = ownerUid;
mOwnerPackageName = ownerPackageName;
mName = name;
mWidth = width;
mHeight = height;
mDensityDpi = densityDpi;
mSurface = surface;
mFlags = flags;
}
@Override
public void binderDied() {
synchronized (getSyncRoot()) {
if (mSurface != null) {
handleBinderDiedLocked(mAppToken);
}
}
}
public void destroyLocked() {
if (mSurface != null) {
mSurface.release();
mSurface = null;
}
SurfaceControl.destroyDisplay(getDisplayTokenLocked());
}
@Override
public void performTraversalInTransactionLocked() {
if (mSurface != null) {
setSurfaceInTransactionLocked(mSurface);
}
}
public void setSurfaceLocked(Surface surface) {
if (mSurface != surface) {
if ((mSurface != null) != (surface != null)) {
sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
}
sendTraversalRequestLocked();
mSurface = surface;
mInfo = null;
}
}
@Override
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
if (mInfo == null) {
mInfo = new DisplayDeviceInfo();
mInfo.name = mName;
mInfo.width = mWidth;
mInfo.height = mHeight;
mInfo.refreshRate = 60;
mInfo.densityDpi = mDensityDpi;
mInfo.xDpi = mDensityDpi;
mInfo.yDpi = mDensityDpi;
mInfo.flags = 0;
if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
| DisplayDeviceInfo.FLAG_NEVER_BLANK
| DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
} else if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
}
if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
}
if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
}
mInfo.type = Display.TYPE_VIRTUAL;
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF;
mInfo.ownerUid = mOwnerUid;
mInfo.ownerPackageName = mOwnerPackageName;
}
return mInfo;
}
}
}