Improve testibility of applicatin switches.
Things can be flaky, because window focus changes are
dispatched to the window on a separate path from input events,
and the window will drop events if it gets them before it sees
the focus change. I am trying to mitigate this some by noting
ASAP what the next upcoming focus state will be, so we can check
that and dispatch it before dispatching a key event if needed.
This definitely makes things better, but not perfect. ctate
suggested that maybe we should be dispatching window focus events
through the input system, which at a glance sounds like a really
really good idea to me... so maybe we can look at that later.
Also changed the wm command to just be a shell wrapper around
all of the implementation that is now in WindowManagerShellCommand.
And fixed a few places where we write debug info to streams that
would trigger strict mode violations that we really don't care
about.
Test: manual
Change-Id: I5235653bcec5522ab84c7f2e1de96d86f2f59326
diff --git a/cmds/wm/Android.mk b/cmds/wm/Android.mk
index 3f3795f..693c6e7 100644
--- a/cmds/wm/Android.mk
+++ b/cmds/wm/Android.mk
@@ -3,11 +3,6 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := wm
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
LOCAL_MODULE := wm
LOCAL_SRC_FILES := wm
LOCAL_MODULE_CLASS := EXECUTABLES
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
deleted file mode 100644
index 8defb33..0000000
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
-**
-** Copyright 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.commands.wm;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.AndroidException;
-import android.util.DisplayMetrics;
-import android.system.Os;
-import android.view.Display;
-import android.view.IWindowManager;
-import com.android.internal.os.BaseCommand;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.DataInputStream;
-import java.io.PrintStream;
-import java.lang.Runtime;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Wm extends BaseCommand {
-
- private IWindowManager mWm;
-
- /**
- * Command-line entry point.
- *
- * @param args The command-line arguments
- */
- public static void main(String[] args) {
- (new Wm()).run(args);
- }
-
- @Override
- public void onShowUsage(PrintStream out) {
- out.println(
- "usage: wm [subcommand] [options]\n" +
- " wm size [reset|WxH|WdpxHdp]\n" +
- " wm density [reset|DENSITY]\n" +
- " wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
- " wm scaling [off|auto]\n" +
- " wm screen-capture [userId] [true|false]\n" +
- "\n" +
- "wm size: return or override display size.\n" +
- " width and height in pixels unless suffixed with 'dp'.\n" +
- "\n" +
- "wm density: override display density.\n" +
- "\n" +
- "wm overscan: set overscan area for display.\n" +
- "\n" +
- "wm scaling: set display scaling mode.\n" +
- "\n" +
- "wm screen-capture: enable/disable screen capture.\n" +
- "\n" +
- "wm dismiss-keyguard: dismiss the keyguard, prompting the user for auth if " +
- "necessary.\n" +
- "\n" +
- "wm surface-trace: log surface commands to stdout in a binary format.\n"
- );
- }
-
- @Override
- public void onRun() throws Exception {
- mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
- Context.WINDOW_SERVICE));
- if (mWm == null) {
- System.err.println(NO_SYSTEM_ERROR_CODE);
- throw new AndroidException("Can't connect to window manager; is the system running?");
- }
-
- String op = nextArgRequired();
-
- if (op.equals("size")) {
- runDisplaySize();
- } else if (op.equals("density")) {
- runDisplayDensity();
- } else if (op.equals("overscan")) {
- runDisplayOverscan();
- } else if (op.equals("scaling")) {
- runDisplayScaling();
- } else if (op.equals("screen-capture")) {
- runSetScreenCapture();
- } else if (op.equals("dismiss-keyguard")) {
- runDismissKeyguard();
- } else if (op.equals("surface-trace")) {
- runSurfaceTrace();
- } else {
- showError("Error: unknown command '" + op + "'");
- return;
- }
- }
-
- private void runSurfaceTrace() throws Exception {
- ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(FileDescriptor.out);
- mWm.enableSurfaceTrace(pfd);
-
- try {
- // No one is going to wake us up, we are just waiting on SIGINT. Otherwise
- // the WM can happily continue writing to our stdout.
- synchronized (this) {
- this.wait();
- }
- } finally {
- mWm.disableSurfaceTrace();
- }
- }
-
- private void runSetScreenCapture() throws Exception {
- String userIdStr = nextArg();
- String enableStr = nextArg();
- int userId;
- boolean disable;
-
- try {
- userId = Integer.parseInt(userIdStr);
- } catch (NumberFormatException e) {
- System.err.println("Error: bad number " + e);
- return;
- }
-
- disable = !Boolean.parseBoolean(enableStr);
-
- try {
- mWm.setScreenCaptureDisabled(userId, disable);
- } catch (RemoteException e) {
- System.err.println("Error: Can't set screen capture " + e);
- }
- }
-
- private void runDisplaySize() throws Exception {
- String size = nextArg();
- int w, h;
- if (size == null) {
- Point initialSize = new Point();
- Point baseSize = new Point();
- try {
- mWm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
- mWm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
- System.out.println("Physical size: " + initialSize.x + "x" + initialSize.y);
- if (!initialSize.equals(baseSize)) {
- System.out.println("Override size: " + baseSize.x + "x" + baseSize.y);
- }
- } catch (RemoteException e) {
- }
- return;
- } else if ("reset".equals(size)) {
- w = h = -1;
- } else {
- int div = size.indexOf('x');
- if (div <= 0 || div >= (size.length()-1)) {
- System.err.println("Error: bad size " + size);
- return;
- }
- String wstr = size.substring(0, div);
- String hstr = size.substring(div+1);
- try {
- w = parseDimension(wstr);
- h = parseDimension(hstr);
- } catch (NumberFormatException e) {
- System.err.println("Error: bad number " + e);
- return;
- }
- }
-
- try {
- if (w >= 0 && h >= 0) {
- // TODO(multidisplay): For now Configuration only applies to main screen.
- mWm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
- } else {
- mWm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
- }
- } catch (RemoteException e) {
- }
- }
-
- private void runDisplayDensity() throws Exception {
- String densityStr = nextArg();
- int density;
- if (densityStr == null) {
- try {
- int initialDensity = mWm.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
- int baseDensity = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
- System.out.println("Physical density: " + initialDensity);
- if (initialDensity != baseDensity) {
- System.out.println("Override density: " + baseDensity);
- }
- } catch (RemoteException e) {
- }
- return;
- } else if ("reset".equals(densityStr)) {
- density = -1;
- } else {
- try {
- density = Integer.parseInt(densityStr);
- } catch (NumberFormatException e) {
- System.err.println("Error: bad number " + e);
- return;
- }
- if (density < 72) {
- System.err.println("Error: density must be >= 72");
- return;
- }
- }
-
- try {
- if (density > 0) {
- // TODO(multidisplay): For now Configuration only applies to main screen.
- mWm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
- UserHandle.USER_CURRENT);
- } else {
- mWm.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
- UserHandle.USER_CURRENT);
- }
- } catch (RemoteException e) {
- }
- }
-
- private void runDisplayOverscan() throws Exception {
- String overscanStr = nextArgRequired();
- Rect rect = new Rect();
- if ("reset".equals(overscanStr)) {
- rect.set(0, 0, 0, 0);
- } else {
- final Pattern FLATTENED_PATTERN = Pattern.compile(
- "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
- Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);
- if (!matcher.matches()) {
- System.err.println("Error: bad rectangle arg: " + overscanStr);
- return;
- }
- rect.left = Integer.parseInt(matcher.group(1));
- rect.top = Integer.parseInt(matcher.group(2));
- rect.right = Integer.parseInt(matcher.group(3));
- rect.bottom = Integer.parseInt(matcher.group(4));
- }
-
- try {
- mWm.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, rect.bottom);
- } catch (RemoteException e) {
- }
- }
-
- private void runDisplayScaling() throws Exception {
- String scalingStr = nextArgRequired();
- if ("auto".equals(scalingStr)) {
- mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
- } else if ("off".equals(scalingStr)) {
- mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
- } else {
- System.err.println("Error: scaling must be 'auto' or 'off'");
- }
- }
-
- private void runDismissKeyguard() throws Exception {
- mWm.dismissKeyguard(null /* callback */);
- }
-
- private int parseDimension(String s) throws NumberFormatException {
- if (s.endsWith("px")) {
- return Integer.parseInt(s.substring(0, s.length() - 2));
- }
- if (s.endsWith("dp")) {
- int density;
- try {
- density = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
- } catch (RemoteException e) {
- density = DisplayMetrics.DENSITY_DEFAULT;
- }
- return Integer.parseInt(s.substring(0, s.length() - 2)) * density /
- DisplayMetrics.DENSITY_DEFAULT;
- }
- return Integer.parseInt(s);
- }
-}
diff --git a/cmds/wm/wm b/cmds/wm/wm
index 16d6bd6..cb45be2 100755
--- a/cmds/wm/wm
+++ b/cmds/wm/wm
@@ -1,7 +1,2 @@
#!/system/bin/sh
-# Script to start "wm" on the device, which has a very rudimentary
-# shell.
-#
-base=/system
-export CLASSPATH=$base/framework/wm.jar
-exec app_process $base/bin com.android.commands.wm.Wm "$@"
+cmd window "$@"
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 129a255..adfc1ba 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -323,6 +323,13 @@
final Rect mTempRect; // used in the transaction to not thrash the heap.
final Rect mVisRect; // used to retrieve visible rect of focused view.
+ // This is used to reduce the race between window focus changes being dispatched from
+ // the window manager and input events coming through the input system.
+ @GuardedBy("this")
+ boolean mUpcomingWindowFocus;
+ @GuardedBy("this")
+ boolean mUpcomingInTouchMode;
+
public boolean mTraversalScheduled;
int mTraversalBarrier;
boolean mWillDrawSoon;
@@ -2452,6 +2459,93 @@
}
}
+ private void handleWindowFocusChanged() {
+ final boolean hasWindowFocus;
+ final boolean inTouchMode;
+ synchronized (this) {
+ hasWindowFocus = mUpcomingWindowFocus;
+ inTouchMode = mUpcomingInTouchMode;
+ }
+
+ if (mAttachInfo.mHasWindowFocus == hasWindowFocus) {
+ return;
+ }
+
+ if (mAdded) {
+ profileRendering(hasWindowFocus);
+
+ if (hasWindowFocus) {
+ ensureTouchModeLocally(inTouchMode);
+ if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
+ mFullRedrawNeeded = true;
+ try {
+ final WindowManager.LayoutParams lp = mWindowAttributes;
+ final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
+ mAttachInfo.mThreadedRenderer.initializeIfNeeded(
+ mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
+ } catch (OutOfResourcesException e) {
+ Log.e(mTag, "OutOfResourcesException locking surface", e);
+ try {
+ if (!mWindowSession.outOfMemory(mWindow)) {
+ Slog.w(mTag, "No processes killed for memory;"
+ + " killing self");
+ Process.killProcess(Process.myPid());
+ }
+ } catch (RemoteException ex) {
+ }
+ // Retry in a bit.
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_WINDOW_FOCUS_CHANGED), 500);
+ return;
+ }
+ }
+ }
+
+ mAttachInfo.mHasWindowFocus = hasWindowFocus;
+
+ mLastWasImTarget = WindowManager.LayoutParams
+ .mayUseInputMethod(mWindowAttributes.flags);
+
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+ imm.onPreWindowFocus(mView, hasWindowFocus);
+ }
+ if (mView != null) {
+ mAttachInfo.mKeyDispatchState.reset();
+ mView.dispatchWindowFocusChanged(hasWindowFocus);
+ mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
+
+ if (mAttachInfo.mTooltipHost != null) {
+ mAttachInfo.mTooltipHost.hideTooltip();
+ }
+ }
+
+ // Note: must be done after the focus change callbacks,
+ // so all of the view state is set up correctly.
+ if (hasWindowFocus) {
+ if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+ imm.onPostWindowFocus(mView, mView.findFocus(),
+ mWindowAttributes.softInputMode,
+ !mHasHadWindowFocus, mWindowAttributes.flags);
+ }
+ // Clear the forward bit. We can just do this directly, since
+ // the window manager doesn't care about it.
+ mWindowAttributes.softInputMode &=
+ ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+ ((WindowManager.LayoutParams) mView.getLayoutParams())
+ .softInputMode &=
+ ~WindowManager.LayoutParams
+ .SOFT_INPUT_IS_FORWARD_NAVIGATION;
+ mHasHadWindowFocus = true;
+ } else {
+ if (mPointerCapture) {
+ handlePointerCaptureChanged(false);
+ }
+ }
+ }
+ mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
+ }
+
private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
try {
@@ -3900,81 +3994,7 @@
}
break;
case MSG_WINDOW_FOCUS_CHANGED: {
- final boolean hasWindowFocus = msg.arg1 != 0;
- if (mAdded) {
- mAttachInfo.mHasWindowFocus = hasWindowFocus;
-
- profileRendering(hasWindowFocus);
-
- if (hasWindowFocus) {
- boolean inTouchMode = msg.arg2 != 0;
- ensureTouchModeLocally(inTouchMode);
- if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
- mFullRedrawNeeded = true;
- try {
- final WindowManager.LayoutParams lp = mWindowAttributes;
- final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
- mAttachInfo.mThreadedRenderer.initializeIfNeeded(
- mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
- } catch (OutOfResourcesException e) {
- Log.e(mTag, "OutOfResourcesException locking surface", e);
- try {
- if (!mWindowSession.outOfMemory(mWindow)) {
- Slog.w(mTag, "No processes killed for memory;"
- + " killing self");
- Process.killProcess(Process.myPid());
- }
- } catch (RemoteException ex) {
- }
- // Retry in a bit.
- sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2),
- 500);
- return;
- }
- }
- }
-
- mLastWasImTarget = WindowManager.LayoutParams
- .mayUseInputMethod(mWindowAttributes.flags);
-
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onPreWindowFocus(mView, hasWindowFocus);
- }
- if (mView != null) {
- mAttachInfo.mKeyDispatchState.reset();
- mView.dispatchWindowFocusChanged(hasWindowFocus);
- mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
-
- if (mAttachInfo.mTooltipHost != null) {
- mAttachInfo.mTooltipHost.hideTooltip();
- }
- }
-
- // Note: must be done after the focus change callbacks,
- // so all of the view state is set up correctly.
- if (hasWindowFocus) {
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onPostWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode,
- !mHasHadWindowFocus, mWindowAttributes.flags);
- }
- // Clear the forward bit. We can just do this directly, since
- // the window manager doesn't care about it.
- mWindowAttributes.softInputMode &=
- ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
- ((WindowManager.LayoutParams) mView.getLayoutParams())
- .softInputMode &=
- ~WindowManager.LayoutParams
- .SOFT_INPUT_IS_FORWARD_NAVIGATION;
- mHasHadWindowFocus = true;
- } else {
- if (mPointerCapture) {
- handlePointerCaptureChanged(false);
- }
- }
- }
- mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
+ handleWindowFocusChanged();
} break;
case MSG_DIE:
doDie();
@@ -6845,6 +6865,7 @@
}
if (stage != null) {
+ handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
@@ -7150,10 +7171,12 @@
}
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
+ synchronized (this) {
+ mUpcomingWindowFocus = hasFocus;
+ mUpcomingInTouchMode = inTouchMode;
+ }
Message msg = Message.obtain();
msg.what = MSG_WINDOW_FOCUS_CHANGED;
- msg.arg1 = hasFocus ? 1 : 0;
- msg.arg2 = inTouchMode ? 1 : 0;
mHandler.sendMessage(msg);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 979323f..51121fb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -48,6 +48,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ShellCommand;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -126,7 +127,7 @@
if (cmd == null) {
return handleDefaultCommands(cmd);
}
- PrintWriter pw = getOutPrintWriter();
+ final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {
case "start":
@@ -1326,65 +1327,95 @@
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq) throws RemoteException {
synchronized (this) {
- mPw.print(uid);
- mPw.print(" procstate ");
- mPw.print(ProcessList.makeProcStateString(procState));
- mPw.print(" seq ");
- mPw.println(procStateSeq);
- mPw.flush();
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print(uid);
+ mPw.print(" procstate ");
+ mPw.print(ProcessList.makeProcStateString(procState));
+ mPw.print(" seq ");
+ mPw.println(procStateSeq);
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
}
@Override
public void onUidGone(int uid, boolean disabled) throws RemoteException {
synchronized (this) {
- mPw.print(uid);
- mPw.print(" gone");
- if (disabled) {
- mPw.print(" disabled");
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print(uid);
+ mPw.print(" gone");
+ if (disabled) {
+ mPw.print(" disabled");
+ }
+ mPw.println();
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
- mPw.println();
- mPw.flush();
}
}
@Override
public void onUidActive(int uid) throws RemoteException {
synchronized (this) {
- mPw.print(uid);
- mPw.println(" active");
- mPw.flush();
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print(uid);
+ mPw.println(" active");
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
}
@Override
public void onUidIdle(int uid, boolean disabled) throws RemoteException {
synchronized (this) {
- mPw.print(uid);
- mPw.print(" idle");
- if (disabled) {
- mPw.print(" disabled");
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print(uid);
+ mPw.print(" idle");
+ if (disabled) {
+ mPw.print(" disabled");
+ }
+ mPw.println();
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
- mPw.println();
- mPw.flush();
}
}
@Override
public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
synchronized (this) {
- mPw.print(uid);
- mPw.println(cached ? " cached" : " uncached");
- mPw.flush();
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print(uid);
+ mPw.println(cached ? " cached" : " uncached");
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
}
@Override
public void onOomAdjMessage(String msg) {
synchronized (this) {
- mPw.print("# ");
- mPw.println(msg);
- mPw.flush();
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print("# ");
+ mPw.println(msg);
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/RemoteEventTrace.java b/services/core/java/com/android/server/wm/RemoteEventTrace.java
index 9f65ba3..b214d35 100644
--- a/services/core/java/com/android/server/wm/RemoteEventTrace.java
+++ b/services/core/java/com/android/server/wm/RemoteEventTrace.java
@@ -20,6 +20,7 @@
import java.io.FileOutputStream;
import java.io.DataOutputStream;
+import android.os.StrictMode;
import android.util.Slog;
import android.os.Debug;
@@ -40,22 +41,28 @@
}
void openSurfaceTransaction() {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
mOut.writeUTF("OpenTransaction");
writeSigil();
} catch (Exception e) {
logException(e);
mService.disableSurfaceTrace();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
}
void closeSurfaceTransaction() {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
mOut.writeUTF("CloseTransaction");
writeSigil();
} catch (Exception e) {
logException(e);
mService.disableSurfaceTrace();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
}
diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
index a12c2c4..d2cbf88 100644
--- a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
+++ b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
@@ -20,6 +20,7 @@
import android.graphics.Region;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.StrictMode;
import android.util.Slog;
import android.view.SurfaceControl;
@@ -54,67 +55,122 @@
@Override
public void setAlpha(float alpha) {
- writeFloatEvent("Alpha", alpha);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeFloatEvent("Alpha", alpha);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setAlpha(alpha);
}
@Override
public void setLayer(int zorder) {
- writeIntEvent("Layer", zorder);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeIntEvent("Layer", zorder);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setLayer(zorder);
}
@Override
public void setPosition(float x, float y) {
- writeFloatEvent("Position", x, y);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeFloatEvent("Position", x, y);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setPosition(x, y);
}
@Override
public void setGeometryAppliesWithResize() {
- writeEvent("GeometryAppliesWithResize");
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeEvent("GeometryAppliesWithResize");
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setGeometryAppliesWithResize();
}
@Override
public void setSize(int w, int h) {
- writeIntEvent("Size", w, h);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeIntEvent("Size", w, h);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setSize(w, h);
}
@Override
public void setWindowCrop(Rect crop) {
- writeRectEvent("Crop", crop);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeRectEvent("Crop", crop);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setWindowCrop(crop);
}
@Override
public void setFinalCrop(Rect crop) {
- writeRectEvent("FinalCrop", crop);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeRectEvent("FinalCrop", crop);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setFinalCrop(crop);
}
@Override
public void setLayerStack(int layerStack) {
- writeIntEvent("LayerStack", layerStack);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeIntEvent("LayerStack", layerStack);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setLayerStack(layerStack);
}
@Override
public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- writeFloatEvent("Matrix", dsdx, dtdx, dsdy, dtdy);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeFloatEvent("Matrix", dsdx, dtdx, dsdy, dtdy);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setMatrix(dsdx, dtdx, dsdy, dtdy);
}
@Override
public void hide() {
- writeEvent("Hide");
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeEvent("Hide");
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.hide();
}
@Override
public void show() {
- writeEvent("Show");
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeEvent("Show");
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.show();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 2358695..b9dc9db 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -18,9 +18,23 @@
import static android.os.Build.IS_USER;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.IWindowManager;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* ShellCommands for WindowManagerService.
@@ -29,33 +43,279 @@
*/
public class WindowManagerShellCommand extends ShellCommand {
- private final WindowManagerService mService;
+ // IPC interface to activity manager -- don't need to do additional security checks.
+ private final IWindowManager mInterface;
+
+ // Internal service impl -- must perform security checks before touching.
+ private final WindowManagerService mInternal;
public WindowManagerShellCommand(WindowManagerService service) {
- mService = service;
+ mInterface = service;
+ mInternal = service;
}
@Override
public int onCommand(String cmd) {
- switch (cmd) {
- case "tracing":
- return mService.mWindowTracing.onShellCommand(this, getNextArgRequired());
- default:
- return handleDefaultCommands(cmd);
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
}
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd) {
+ case "size":
+ return runDisplaySize(pw);
+ case "density":
+ return runDisplayDensity(pw);
+ case "overscan":
+ return runDisplayOverscan(pw);
+ case "scaling":
+ return runDisplayScaling(pw);
+ case "screen-capture":
+ return runSetScreenCapture(pw);
+ case "dismiss-keyguard":
+ return runDismissKeyguard(pw);
+ case "surface-trace":
+ return runSurfaceTrace(pw);
+ case "tracing":
+ // XXX this should probably be changed to use openFileForSystem() to create
+ // the output trace file, so the shell gets the correct semantics for where
+ // trace files can be written.
+ return mInternal.mWindowTracing.onShellCommand(this,
+ getNextArgRequired());
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int runDisplaySize(PrintWriter pw) throws RemoteException {
+ String size = getNextArg();
+ int w, h;
+ if (size == null) {
+ Point initialSize = new Point();
+ Point baseSize = new Point();
+ try {
+ mInterface.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
+ mInterface.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
+ pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
+ if (!initialSize.equals(baseSize)) {
+ pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
+ }
+ } catch (RemoteException e) {
+ }
+ return 0;
+ } else if ("reset".equals(size)) {
+ w = h = -1;
+ } else {
+ int div = size.indexOf('x');
+ if (div <= 0 || div >= (size.length()-1)) {
+ getErrPrintWriter().println("Error: bad size " + size);
+ return -1;
+ }
+ String wstr = size.substring(0, div);
+ String hstr = size.substring(div+1);
+ try {
+ w = parseDimension(wstr);
+ h = parseDimension(hstr);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad number " + e);
+ return -1;
+ }
+ }
+
+ if (w >= 0 && h >= 0) {
+ // TODO(multidisplay): For now Configuration only applies to main screen.
+ mInterface.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
+ } else {
+ mInterface.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
+ }
+ return 0;
+ }
+
+ private int runDisplayDensity(PrintWriter pw) throws RemoteException {
+ String densityStr = getNextArg();
+ int density;
+ if (densityStr == null) {
+ try {
+ int initialDensity = mInterface.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
+ int baseDensity = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+ pw.println("Physical density: " + initialDensity);
+ if (initialDensity != baseDensity) {
+ pw.println("Override density: " + baseDensity);
+ }
+ } catch (RemoteException e) {
+ }
+ return 0;
+ } else if ("reset".equals(densityStr)) {
+ density = -1;
+ } else {
+ try {
+ density = Integer.parseInt(densityStr);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad number " + e);
+ return -1;
+ }
+ if (density < 72) {
+ getErrPrintWriter().println("Error: density must be >= 72");
+ return -1;
+ }
+ }
+
+ if (density > 0) {
+ // TODO(multidisplay): For now Configuration only applies to main screen.
+ mInterface.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
+ UserHandle.USER_CURRENT);
+ } else {
+ mInterface.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
+ UserHandle.USER_CURRENT);
+ }
+ return 0;
+ }
+
+ private int runDisplayOverscan(PrintWriter pw) throws RemoteException {
+ String overscanStr = getNextArgRequired();
+ Rect rect = new Rect();
+ if ("reset".equals(overscanStr)) {
+ rect.set(0, 0, 0, 0);
+ } else {
+ final Pattern FLATTENED_PATTERN = Pattern.compile(
+ "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
+ Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);
+ if (!matcher.matches()) {
+ getErrPrintWriter().println("Error: bad rectangle arg: " + overscanStr);
+ return -1;
+ }
+ rect.left = Integer.parseInt(matcher.group(1));
+ rect.top = Integer.parseInt(matcher.group(2));
+ rect.right = Integer.parseInt(matcher.group(3));
+ rect.bottom = Integer.parseInt(matcher.group(4));
+ }
+
+ mInterface.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right,
+ rect.bottom);
+ return 0;
+ }
+
+ private int runDisplayScaling(PrintWriter pw) throws RemoteException {
+ String scalingStr = getNextArgRequired();
+ if ("auto".equals(scalingStr)) {
+ mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
+ } else if ("off".equals(scalingStr)) {
+ mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
+ } else {
+ getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'");
+ return -1;
+ }
+ return 0;
+ }
+
+ private int runSetScreenCapture(PrintWriter pw) throws RemoteException {
+ String userIdStr = getNextArg();
+ String enableStr = getNextArg();
+ int userId;
+ boolean disable;
+
+ try {
+ userId = Integer.parseInt(userIdStr);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad number " + e);
+ return -1;
+ }
+
+ disable = !Boolean.parseBoolean(enableStr);
+ mInternal.setScreenCaptureDisabled(userId, disable);
+ return 0;
+ }
+
+ private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
+ mInterface.dismissKeyguard(null /* callback */);
+ return 0;
+ }
+
+ private int runSurfaceTrace(PrintWriter pw) throws RemoteException {
+ final ParcelFileDescriptor pfd;
+ try {
+ pfd = ParcelFileDescriptor.dup(getOutFileDescriptor());
+ } catch (IOException e) {
+ getErrPrintWriter().println("Unable to dup output stream: " + e.getMessage());
+ return -1;
+ }
+ mInternal.enableSurfaceTrace(pfd);
+
+ // Read input until an explicit quit command is sent or the stream is closed (meaning
+ // the user killed the command).
+ try {
+ InputStream input = getRawInputStream();
+ InputStreamReader converter = new InputStreamReader(input);
+ BufferedReader in = new BufferedReader(converter);
+ String line;
+
+ while ((line = in.readLine()) != null) {
+ if (line.length() <= 0) {
+ // no-op
+ } else if ("q".equals(line) || "quit".equals(line)) {
+ break;
+ } else {
+ pw.println("Invalid command: " + line);
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace(pw);
+ } finally {
+ mInternal.disableSurfaceTrace();
+ try {
+ pfd.close();
+ } catch (IOException e) {
+ }
+ }
+
+ return 0;
+ }
+
+ private int parseDimension(String s) throws NumberFormatException {
+ if (s.endsWith("px")) {
+ return Integer.parseInt(s.substring(0, s.length() - 2));
+ }
+ if (s.endsWith("dp")) {
+ int density;
+ try {
+ density = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+ } catch (RemoteException e) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ }
+ return Integer.parseInt(s.substring(0, s.length() - 2)) * density /
+ DisplayMetrics.DENSITY_DEFAULT;
+ }
+ return Integer.parseInt(s);
}
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
- pw.println("Window Manager (window) commands:");
+ pw.println("Window manager (window) commands:");
pw.println(" help");
- pw.println(" Print this help text.");
- pw.println();
- if (!IS_USER){
+ pw.println(" Print this help text.");
+ pw.println(" size [reset|WxH|WdpxHdp]");
+ pw.println(" Return or override display size.");
+ pw.println(" width and height in pixels unless suffixed with 'dp'.");
+ pw.println(" density [reset|DENSITY]");
+ pw.println(" Return or override display density.");
+ pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM]");
+ pw.println(" Set overscan area for display.");
+ pw.println(" scaling [off|auto]");
+ pw.println(" Set display scaling mode.");
+ pw.println(" screen-capture [userId] [true|false]");
+ pw.println(" Enable or disable screen capture.");
+ pw.println(" dismiss-keyguard");
+ pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
+ pw.println(" surface-trace");
+ pw.println(" Log surface commands to stdout in a binary format.");
+ if (!IS_USER) {
pw.println(" tracing (start | stop)");
- pw.println(" start or stop window tracing");
- pw.println();
+ pw.println(" Start or stop window tracing.");
}
}
}