Move OVERLAY_DISPLAY_DEVICES to Global. Bug: 7127417 Change-Id: I632648ac5b01408512f59424f3bb55162431bea4
339 lines
12 KiB
Java
339 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2012 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 com.android.internal.util.DumpUtils;
|
|
import com.android.internal.util.IndentingPrintWriter;
|
|
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.database.ContentObserver;
|
|
import android.os.Handler;
|
|
import android.os.IBinder;
|
|
import android.os.UserHandle;
|
|
import android.provider.Settings;
|
|
import android.util.DisplayMetrics;
|
|
import android.util.Slog;
|
|
import android.view.Gravity;
|
|
import android.view.Surface;
|
|
|
|
import java.io.PrintWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
/**
|
|
* A display adapter that uses overlay windows to simulate secondary displays
|
|
* for development purposes. Use Development Settings to enable one or more
|
|
* overlay displays.
|
|
* <p>
|
|
* This object has two different handlers (which may be the same) which must not
|
|
* get confused. The main handler is used to posting messages to the display manager
|
|
* service as usual. The UI handler is only used by the {@link OverlayDisplayWindow}.
|
|
* </p><p>
|
|
* Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
|
|
* </p>
|
|
*/
|
|
final class OverlayDisplayAdapter extends DisplayAdapter {
|
|
static final String TAG = "OverlayDisplayAdapter";
|
|
static final boolean DEBUG = false;
|
|
|
|
private static final int MIN_WIDTH = 100;
|
|
private static final int MIN_HEIGHT = 100;
|
|
private static final int MAX_WIDTH = 4096;
|
|
private static final int MAX_HEIGHT = 4096;
|
|
|
|
private static final Pattern SETTING_PATTERN =
|
|
Pattern.compile("(\\d+)x(\\d+)/(\\d+)");
|
|
|
|
private final Handler mUiHandler;
|
|
private final ArrayList<OverlayDisplayHandle> mOverlays =
|
|
new ArrayList<OverlayDisplayHandle>();
|
|
private String mCurrentOverlaySetting = "";
|
|
|
|
public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
|
|
Context context, Handler handler, Listener listener, Handler uiHandler) {
|
|
super(syncRoot, context, handler, listener, TAG);
|
|
mUiHandler = uiHandler;
|
|
}
|
|
|
|
@Override
|
|
public void dumpLocked(PrintWriter pw) {
|
|
super.dumpLocked(pw);
|
|
|
|
pw.println("mCurrentOverlaySetting=" + mCurrentOverlaySetting);
|
|
pw.println("mOverlays: size=" + mOverlays.size());
|
|
for (OverlayDisplayHandle overlay : mOverlays) {
|
|
overlay.dumpLocked(pw);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void registerLocked() {
|
|
super.registerLocked();
|
|
|
|
getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
getContext().getContentResolver().registerContentObserver(
|
|
Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
|
|
true, new ContentObserver(getHandler()) {
|
|
@Override
|
|
public void onChange(boolean selfChange) {
|
|
updateOverlayDisplayDevices();
|
|
}
|
|
});
|
|
|
|
updateOverlayDisplayDevices();
|
|
}
|
|
});
|
|
}
|
|
|
|
private void updateOverlayDisplayDevices() {
|
|
synchronized (getSyncRoot()) {
|
|
updateOverlayDisplayDevicesLocked();
|
|
}
|
|
}
|
|
|
|
private void updateOverlayDisplayDevicesLocked() {
|
|
String value = Settings.Global.getString(getContext().getContentResolver(),
|
|
Settings.Global.OVERLAY_DISPLAY_DEVICES);
|
|
if (value == null) {
|
|
value = "";
|
|
}
|
|
|
|
if (value.equals(mCurrentOverlaySetting)) {
|
|
return;
|
|
}
|
|
mCurrentOverlaySetting = value;
|
|
|
|
if (!mOverlays.isEmpty()) {
|
|
Slog.i(TAG, "Dismissing all overlay display devices.");
|
|
for (OverlayDisplayHandle overlay : mOverlays) {
|
|
overlay.dismissLocked();
|
|
}
|
|
mOverlays.clear();
|
|
}
|
|
|
|
int count = 0;
|
|
for (String part : value.split(";")) {
|
|
Matcher matcher = SETTING_PATTERN.matcher(part);
|
|
if (matcher.matches()) {
|
|
if (count >= 4) {
|
|
Slog.w(TAG, "Too many overlay display devices specified: " + value);
|
|
break;
|
|
}
|
|
try {
|
|
int width = Integer.parseInt(matcher.group(1), 10);
|
|
int height = Integer.parseInt(matcher.group(2), 10);
|
|
int densityDpi = Integer.parseInt(matcher.group(3), 10);
|
|
if (width >= MIN_WIDTH && width <= MAX_WIDTH
|
|
&& height >= MIN_HEIGHT && height <= MAX_HEIGHT
|
|
&& densityDpi >= DisplayMetrics.DENSITY_LOW
|
|
&& densityDpi <= DisplayMetrics.DENSITY_XXHIGH) {
|
|
int number = ++count;
|
|
String name = getContext().getResources().getString(
|
|
com.android.internal.R.string.display_manager_overlay_display_name,
|
|
number);
|
|
int gravity = chooseOverlayGravity(number);
|
|
|
|
Slog.i(TAG, "Showing overlay display device #" + number
|
|
+ ": name=" + name + ", width=" + width + ", height=" + height
|
|
+ ", densityDpi=" + densityDpi);
|
|
|
|
mOverlays.add(new OverlayDisplayHandle(name,
|
|
width, height, densityDpi, gravity));
|
|
continue;
|
|
}
|
|
} catch (NumberFormatException ex) {
|
|
}
|
|
} else if (part.isEmpty()) {
|
|
continue;
|
|
}
|
|
Slog.w(TAG, "Malformed overlay display devices setting: " + value);
|
|
}
|
|
}
|
|
|
|
private static int chooseOverlayGravity(int overlayNumber) {
|
|
switch (overlayNumber) {
|
|
case 1:
|
|
return Gravity.TOP | Gravity.LEFT;
|
|
case 2:
|
|
return Gravity.BOTTOM | Gravity.RIGHT;
|
|
case 3:
|
|
return Gravity.TOP | Gravity.RIGHT;
|
|
case 4:
|
|
default:
|
|
return Gravity.BOTTOM | Gravity.LEFT;
|
|
}
|
|
}
|
|
|
|
private final class OverlayDisplayDevice extends DisplayDevice {
|
|
private final String mName;
|
|
private final int mWidth;
|
|
private final int mHeight;
|
|
private final float mRefreshRate;
|
|
private final int mDensityDpi;
|
|
|
|
private Surface mSurface;
|
|
private DisplayDeviceInfo mInfo;
|
|
|
|
public OverlayDisplayDevice(IBinder displayToken, String name,
|
|
int width, int height, float refreshRate, int densityDpi,
|
|
Surface surface) {
|
|
super(OverlayDisplayAdapter.this, displayToken);
|
|
mName = name;
|
|
mWidth = width;
|
|
mHeight = height;
|
|
mRefreshRate = refreshRate;
|
|
mDensityDpi = densityDpi;
|
|
mSurface = surface;
|
|
}
|
|
|
|
public void clearSurfaceLocked() {
|
|
mSurface = null;
|
|
sendTraversalRequestLocked();
|
|
}
|
|
|
|
@Override
|
|
public void performTraversalInTransactionLocked() {
|
|
setSurfaceInTransactionLocked(mSurface);
|
|
}
|
|
|
|
@Override
|
|
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
|
|
if (mInfo == null) {
|
|
mInfo = new DisplayDeviceInfo();
|
|
mInfo.name = mName;
|
|
mInfo.width = mWidth;
|
|
mInfo.height = mHeight;
|
|
mInfo.refreshRate = mRefreshRate;
|
|
mInfo.densityDpi = mDensityDpi;
|
|
mInfo.xDpi = mDensityDpi;
|
|
mInfo.yDpi = mDensityDpi;
|
|
mInfo.flags = DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
|
|
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
|
|
}
|
|
return mInfo;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Functions as a handle for overlay display devices which are created and
|
|
* destroyed asynchronously.
|
|
*
|
|
* Guarded by the {@link DisplayManagerService.SyncRoot} lock.
|
|
*/
|
|
private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
|
|
private final String mName;
|
|
private final int mWidth;
|
|
private final int mHeight;
|
|
private final int mDensityDpi;
|
|
private final int mGravity;
|
|
|
|
private OverlayDisplayWindow mWindow;
|
|
private OverlayDisplayDevice mDevice;
|
|
|
|
public OverlayDisplayHandle(String name,
|
|
int width, int height, int densityDpi, int gravity) {
|
|
mName = name;
|
|
mWidth = width;
|
|
mHeight = height;
|
|
mDensityDpi = densityDpi;
|
|
mGravity = gravity;
|
|
|
|
mUiHandler.post(mShowRunnable);
|
|
}
|
|
|
|
public void dismissLocked() {
|
|
mUiHandler.removeCallbacks(mShowRunnable);
|
|
mUiHandler.post(mDismissRunnable);
|
|
}
|
|
|
|
// Called on the UI thread.
|
|
@Override
|
|
public void onWindowCreated(Surface surface, float refreshRate) {
|
|
synchronized (getSyncRoot()) {
|
|
IBinder displayToken = Surface.createDisplay(mName);
|
|
mDevice = new OverlayDisplayDevice(displayToken, mName,
|
|
mWidth, mHeight, refreshRate, mDensityDpi, surface);
|
|
|
|
sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
|
|
}
|
|
}
|
|
|
|
// Called on the UI thread.
|
|
@Override
|
|
public void onWindowDestroyed() {
|
|
synchronized (getSyncRoot()) {
|
|
if (mDevice != null) {
|
|
mDevice.clearSurfaceLocked();
|
|
sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void dumpLocked(PrintWriter pw) {
|
|
pw.println(" " + mName + ":");
|
|
pw.println(" mWidth=" + mWidth);
|
|
pw.println(" mHeight=" + mHeight);
|
|
pw.println(" mDensityDpi=" + mDensityDpi);
|
|
pw.println(" mGravity=" + mGravity);
|
|
|
|
// Try to dump the window state.
|
|
if (mWindow != null) {
|
|
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
|
|
ipw.increaseIndent();
|
|
DumpUtils.dumpAsync(mUiHandler, mWindow, ipw, 200);
|
|
}
|
|
}
|
|
|
|
// Runs on the UI thread.
|
|
private final Runnable mShowRunnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
|
|
mName, mWidth, mHeight, mDensityDpi, mGravity,
|
|
OverlayDisplayHandle.this);
|
|
window.show();
|
|
|
|
synchronized (getSyncRoot()) {
|
|
mWindow = window;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Runs on the UI thread.
|
|
private final Runnable mDismissRunnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
OverlayDisplayWindow window;
|
|
synchronized (getSyncRoot()) {
|
|
window = mWindow;
|
|
mWindow = null;
|
|
}
|
|
|
|
if (window != null) {
|
|
window.dismiss();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|