From e2515eebf42c763c0a2d9f873a153711778cfc17 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 27 Apr 2011 18:52:56 -0400 Subject: Better compat mode part one: start scaling windows. First step of improving app screen size compatibility mode. When running in compat mode, an application's windows are scaled up on the screen rather than being small with 1:1 pixels. Currently we scale the application to fill the entire screen, so don't use an even pixel scaling. Though this may have some negative impact on the appearance (it looks okay to me), it has a big benefit of allowing us to now treat these apps as normal full-screens apps and do the normal transition animations as you move in and out and around in them. This introduces fun stuff in the input system to take care of modifying pointer coordinates to account for the app window surface scaling. The input dispatcher is told about the scale that is being applied to each window and, when there is one, adjusts pointer events appropriately as they are being sent to the transport. Also modified is CompatibilityInfo, which has been greatly simplified to not be so insane and incomprehendible. It is now simple -- when constructed it determines if the given app is compatible with the current screen size and density, and that is that. There are new APIs on ActivityManagerService to put applications that we would traditionally consider compatible with larger screens in compatibility mode. This is the start of a facility to have a UI affordance for a user to switch apps in and out of compatibility. To test switching of modes, there is a new variation of the "am" command to do this: am screen-compat [on|off] [package] This mode switching has the fundamentals of restarting activities when it is changed, though the state still needs to be persisted and the overall mode switch cleaned up. For the few small apps I have tested, things mostly seem to be working well. I know of one problem with the text selection handles being drawn at the wrong position because at some point the window offset is being scaled incorrectly. There are probably other similar issues around the interaction between two windows because the different window coordinate spaces are done in a hacky way instead of being formally integrated into the window manager layout process. Change-Id: Ie038e3746b448135117bd860859d74e360938557 --- .../android/server/am/ActivityManagerService.java | 103 ++++++++++++++-- .../java/com/android/server/am/ActivityRecord.java | 4 +- .../java/com/android/server/am/ActivityStack.java | 11 +- .../java/com/android/server/am/ProcessRecord.java | 1 + .../java/com/android/server/wm/InputMonitor.java | 11 +- .../java/com/android/server/wm/InputWindow.java | 4 + .../android/server/wm/WindowManagerService.java | 65 +++++++---- .../java/com/android/server/wm/WindowState.java | 129 ++++++++++++++++----- 8 files changed, 268 insertions(+), 60 deletions(-) (limited to 'services/java/com/android') diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 54cc885..811221e 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -74,6 +74,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Proxy; @@ -146,7 +147,7 @@ public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { static final String TAG = "ActivityManager"; static final boolean DEBUG = false; - static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; + static final boolean localLOGV = DEBUG; static final boolean DEBUG_SWITCH = localLOGV || false; static final boolean DEBUG_TASKS = localLOGV || false; static final boolean DEBUG_PAUSE = localLOGV || false; @@ -546,6 +547,12 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord mHomeProcess; /** + * Packages that the user has asked to have run in screen size + * compatibility mode instead of filling the screen. + */ + final HashSet mScreenCompatPackages = new HashSet(); + + /** * Set of PendingResultRecord objects that are currently active. */ final HashSet mPendingResultRecords = new HashSet(); @@ -2091,6 +2098,74 @@ public final class ActivityManagerService extends ActivityManagerNative } } + CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { + return new CompatibilityInfo(ai, mConfiguration.screenLayout, + mScreenCompatPackages.contains(ai.packageName)); + } + + public void setPackageScreenCompatMode(String packageName, boolean compatEnabled) { + synchronized (this) { + ApplicationInfo ai = null; + try { + ai = AppGlobals.getPackageManager(). + getApplicationInfo(packageName, STOCK_PM_FLAGS); + } catch (RemoteException e) { + } + if (ai == null) { + Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName); + return; + } + boolean changed = false; + if (compatEnabled) { + if (!mScreenCompatPackages.contains(packageName)) { + changed = true; + mScreenCompatPackages.add(packageName); + } + } else { + if (mScreenCompatPackages.contains(packageName)) { + changed = true; + mScreenCompatPackages.remove(packageName); + } + } + if (changed) { + CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); + + // Tell all processes that loaded this package about the change. + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); + if (!app.pkgList.contains(packageName)) { + continue; + } + try { + if (app.thread != null) { + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " + + app.processName + " new compat " + ci); + app.thread.updatePackageCompatibilityInfo(packageName, ci); + } + } catch (Exception e) { + } + } + + // All activities that came from the packge must be + // restarted as if there was a config change. + for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { + ActivityRecord a = (ActivityRecord)mMainStack.mHistory.get(i); + if (a.info.packageName.equals(packageName)) { + a.forceNewConfig = true; + } + } + + ActivityRecord starting = mMainStack.topRunningActivityLocked(null); + if (starting != null) { + mMainStack.ensureActivityConfigurationLocked(starting, 0); + // And we need to make sure at this point that all other activities + // are made visible with the correct configuration. + mMainStack.ensureActivitiesVisibleLocked(starting, 0); + } + } + } + } + void reportResumedActivityLocked(ActivityRecord r) { //Slog.i(TAG, "**** REPORT RESUME: " + r); @@ -3589,12 +3664,14 @@ public final class ActivityManagerService extends ActivityManagerNative } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Binding proc " + processName + " with config " + mConfiguration); - thread.bindApplication(processName, app.instrumentationInfo != null - ? app.instrumentationInfo : app.info, providers, + ApplicationInfo appInfo = app.instrumentationInfo != null + ? app.instrumentationInfo : app.info; + thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, app.instrumentationProfileFile, app.instrumentationArguments, app.instrumentationWatcher, testMode, isRestrictedBackupMode || !normalMode, - mConfiguration, getCommonServicesLocked(), + mConfiguration, compatibilityInfoForPackageLocked(appInfo), + getCommonServicesLocked(), mCoreSettingsObserver.getCoreSettingsLocked()); updateLruProcessLocked(app, false, true); app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis(); @@ -3685,7 +3762,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app); ensurePackageDexOpt(mBackupTarget.appInfo.packageName); try { - thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode); + thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, + compatibilityInfoForPackageLocked(mBackupTarget.appInfo), + mBackupTarget.backupMode); } catch (Exception e) { Slog.w(TAG, "Exception scheduling backup agent creation: "); e.printStackTrace(); @@ -7776,6 +7855,10 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(" mConfiguration: " + mConfiguration); pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange); + if (mScreenCompatPackages.size() > 0) { + pw.print(" mScreenCompatPackages="); + pw.println(mScreenCompatPackages); + } pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown); if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient || mOrigWaitForDebugger) { @@ -9238,7 +9321,8 @@ public final class ActivityManagerService extends ActivityManagerNative r.stats.startLaunchedLocked(); } ensurePackageDexOpt(r.serviceInfo.packageName); - app.thread.scheduleCreateService(r, r.serviceInfo); + app.thread.scheduleCreateService(r, r.serviceInfo, + compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo)); r.postNotification(); created = true; } finally { @@ -10342,7 +10426,8 @@ public final class ActivityManagerService extends ActivityManagerNative if (proc.thread != null) { if (DEBUG_BACKUP) Slog.v(TAG, "Agent proc already running: " + proc); try { - proc.thread.scheduleCreateBackupAgent(app, backupMode); + proc.thread.scheduleCreateBackupAgent(app, + compatibilityInfoForPackageLocked(app), backupMode); } catch (RemoteException e) { // Will time out on the backup manager side } @@ -10414,7 +10499,8 @@ public final class ActivityManagerService extends ActivityManagerNative // If the app crashed during backup, 'thread' will be null here if (proc.thread != null) { try { - proc.thread.scheduleDestroyBackupAgent(appInfo); + proc.thread.scheduleDestroyBackupAgent(appInfo, + compatibilityInfoForPackageLocked(appInfo)); } catch (Exception e) { Slog.e(TAG, "Exception when unbinding backup agent:"); e.printStackTrace(); @@ -11261,6 +11347,7 @@ public final class ActivityManagerService extends ActivityManagerNative + ": " + r); ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, + compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo), r.resultCode, r.resultData, r.resultExtras, r.ordered); if (DEBUG_BROADCAST) Slog.v(TAG, "Process cur broadcast " + r + " DELIVERED for app " + app); diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 0fb30ff..cb0a0f0 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -111,6 +111,7 @@ class ActivityRecord extends IApplicationToken.Stub { boolean hasBeenLaunched;// has this activity ever been launched? boolean frozenBeforeDestroy;// has been frozen but not yet destroyed. boolean immersive; // immersive mode (don't interrupt if possible) + boolean forceNewConfig; // force re-create with new config next time String stringName; // for caching of toString(). @@ -174,7 +175,8 @@ class ActivityRecord extends IApplicationToken.Stub { pw.print(" immersive="); pw.print(immersive); pw.print(" launchMode="); pw.println(launchMode); pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy); - pw.print(" thumbnailNeeded="); pw.println(thumbnailNeeded); + pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded); + pw.print(" forceNewConfig="); pw.println(forceNewConfig); if (launchTime != 0 || startTime != 0) { pw.print(prefix); pw.print("launchTime="); TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime="); diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index c087aec..f385042 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -537,9 +537,11 @@ public class ActivityStack { } mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName()); r.sleeping = false; + r.forceNewConfig = false; app.thread.scheduleLaunchActivity(new Intent(r.intent), r, System.identityHashCode(r), - r.info, r.icicle, results, newIntents, !andResume, + r.info, mService.compatibilityInfoForPackageLocked(r.info.applicationInfo), + r.icicle, results, newIntents, !andResume, mService.isNextTransitionForward()); if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) { @@ -3750,7 +3752,7 @@ public class ActivityStack { // Short circuit: if the two configurations are the exact same // object (the common case), then there is nothing to do. Configuration newConfig = mService.mConfiguration; - if (r.configuration == newConfig) { + if (r.configuration == newConfig && !r.forceNewConfig) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration unchanged in " + r); return true; @@ -3775,6 +3777,7 @@ public class ActivityStack { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration doesn't matter not running " + r); r.stopFreezingScreenLocked(false); + r.forceNewConfig = false; return true; } @@ -3786,10 +3789,11 @@ public class ActivityStack { + Integer.toHexString(r.info.configChanges) + ", newConfig=" + newConfig); } - if ((changes&(~r.info.configChanges)) != 0) { + if ((changes&(~r.info.configChanges)) != 0 || r.forceNewConfig) { // Aha, the activity isn't handling the change, so DIE DIE DIE. r.configChangeFlags |= changes; r.startFreezingScreenLocked(r.app, globalChanges); + r.forceNewConfig = false; if (r.app == null || r.app.thread == null) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Switch is destroying non-running " + r); @@ -3860,6 +3864,7 @@ public class ActivityStack { try { if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r); + r.forceNewConfig = false; r.app.thread.scheduleRelaunchActivity(r, results, newIntents, changes, !andResume, mService.mConfiguration); // Note: don't need to call pauseIfSleepingLocked() here, because diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 353ff6d..a63ffae 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -24,6 +24,7 @@ import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; import android.content.ComponentName; import android.content.pm.ApplicationInfo; +import android.content.res.CompatibilityInfo; import android.os.Bundle; import android.os.IBinder; import android.os.SystemClock; diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java index 45a78af..57f0799 100644 --- a/services/java/com/android/server/wm/InputMonitor.java +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -205,12 +205,21 @@ final class InputMonitor { inputWindow.ownerPid = child.mSession.mPid; inputWindow.ownerUid = child.mSession.mUid; - final Rect frame = child.mFrame; + final Rect frame = child.mScaledFrame; inputWindow.frameLeft = frame.left; inputWindow.frameTop = frame.top; inputWindow.frameRight = frame.right; inputWindow.frameBottom = frame.bottom; + if (child.mGlobalScale != 1) { + // If we are scaling the window, input coordinates need + // to be inversely scaled to map from what is on screen + // to what is actually being touched in the UI. + inputWindow.scaleFactor = 1.0f/child.mGlobalScale; + } else { + inputWindow.scaleFactor = 1; + } + child.getTouchableRegion(inputWindow.touchableRegion); } diff --git a/services/java/com/android/server/wm/InputWindow.java b/services/java/com/android/server/wm/InputWindow.java index e3eb473..578120e 100644 --- a/services/java/com/android/server/wm/InputWindow.java +++ b/services/java/com/android/server/wm/InputWindow.java @@ -46,6 +46,10 @@ public final class InputWindow { public int frameRight; public int frameBottom; + // Global scaling factor applied to touch events when they are dispatched + // to the window + public float scaleFactor; + // Window touchable region. public final Region touchableRegion = new Region(); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 33e6a36..769e423 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -19,7 +19,6 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND; -import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; @@ -586,6 +585,7 @@ public class WindowManagerService extends IWindowManager.Stub // The frame use to limit the size of the app running in compatibility mode. Rect mCompatibleScreenFrame = new Rect(); + float mCompatibleScreenScale; // The surface used to fill the outer rim of the app running in compatibility mode. Surface mBackgroundFillerSurface = null; WindowState mBackgroundFillerTarget = null; @@ -1757,7 +1757,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean rawChanged = false; float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f; float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f; - int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw; + int availw = wallpaperWin.mScaledFrame.right-wallpaperWin.mScaledFrame.left-dw; int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0; changed = wallpaperWin.mXOffset != offset; if (changed) { @@ -2887,14 +2887,14 @@ public class WindowManagerService extends IWindowManager.Stub } private boolean applyAnimationLocked(AppWindowToken wtoken, - WindowManager.LayoutParams lp, int transit, boolean enter) { + WindowManager.LayoutParams lp, int transit, boolean enter, boolean bgFiller) { // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. if (!mDisplayFrozen && mPolicy.isScreenOn()) { Animation a; - if (lp != null && (lp.flags & FLAG_COMPATIBLE_WINDOW) != 0) { + if (bgFiller) { a = new FadeInOutAnimation(enter); if (DEBUG_ANIM) Slog.v(TAG, "applying FadeInOutAnimation for a window in compatibility mode"); @@ -3680,7 +3680,7 @@ public class WindowManagerService extends IWindowManager.Stub } boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, - boolean visible, int transit, boolean performLayout) { + boolean visible, int transit, boolean performLayout, boolean bgFiller) { boolean delayed = false; if (wtoken.clientHidden == visible) { @@ -3702,7 +3702,7 @@ public class WindowManagerService extends IWindowManager.Stub if (wtoken.animation == sDummyAnimation) { wtoken.animation = null; } - applyAnimationLocked(wtoken, lp, transit, visible); + applyAnimationLocked(wtoken, lp, transit, visible, bgFiller); changed = true; if (wtoken.animation != null) { delayed = runningAppAnimation = true; @@ -3855,7 +3855,8 @@ public class WindowManagerService extends IWindowManager.Stub } final long origId = Binder.clearCallingIdentity(); - setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET, true); + setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET, + true, false); wtoken.updateReportedVisibilityLocked(); Binder.restoreCallingIdentity(origId); } @@ -3981,7 +3982,8 @@ public class WindowManagerService extends IWindowManager.Stub WindowToken basewtoken = mTokenMap.remove(token); if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken); - delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_UNSET, true); + delayed = setTokenVisibilityLocked(wtoken, null, false, + WindowManagerPolicy.TRANSIT_UNSET, true, false); wtoken.inPendingTransaction = false; mOpeningApps.remove(wtoken); wtoken.waitingToShow = false; @@ -4753,8 +4755,8 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { long ident = Binder.clearCallingIdentity(); - dw = mDisplay.getWidth(); - dh = mDisplay.getHeight(); + dw = mPolicy.getNonDecorDisplayWidth(mDisplay.getWidth()); + dh = mPolicy.getNonDecorDisplayHeight(mDisplay.getHeight()); int aboveAppLayer = mPolicy.windowTypeToLayerLw( WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER @@ -4802,7 +4804,7 @@ public class WindowManagerService extends IWindowManager.Stub // Don't include wallpaper in bounds calculation if (!ws.mIsWallpaper) { - final Rect wf = ws.mFrame; + final Rect wf = ws.mScaledFrame; final Rect cr = ws.mContentInsets; int left = wf.left + cr.left; int top = wf.top + cr.top; @@ -5447,7 +5449,10 @@ public class WindowManagerService extends IWindowManager.Stub DisplayMetrics dm = new DisplayMetrics(); mDisplay.getMetrics(dm); - CompatibilityInfo.updateCompatibleScreenFrame(dm, orientation, mCompatibleScreenFrame); + dm.realWidthPixels = mPolicy.getNonDecorDisplayWidth(dm.realWidthPixels); + dm.realHeightPixels = mPolicy.getNonDecorDisplayHeight(dm.realHeightPixels); + mCompatibleScreenScale = CompatibilityInfo.updateCompatibleScreenFrame( + dm, mCompatibleScreenFrame, null); if (mScreenLayout == Configuration.SCREENLAYOUT_SIZE_UNDEFINED) { // Note we only do this once because at this point we don't @@ -6582,6 +6587,9 @@ public class WindowManagerService extends IWindowManager.Stub final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); + final int innerDw = mPolicy.getNonDecorDisplayWidth(dw); + final int innerDh = mPolicy.getNonDecorDisplayHeight(dh); + final int N = mWindows.size(); int i; @@ -6634,7 +6642,9 @@ public class WindowManagerService extends IWindowManager.Stub //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); win.mContentChanged = false; } + win.prelayout(); mPolicy.layoutWindowLw(win, win.mAttrs, null); + win.evalNeedsBackgroundFiller(innerDw, innerDh); win.mLayoutSeq = seq; if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame=" + win.mFrame + " mContainingFrame=" @@ -6669,7 +6679,9 @@ public class WindowManagerService extends IWindowManager.Stub //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); win.mContentChanged = false; } + win.prelayout(); mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); + win.evalNeedsBackgroundFiller(innerDw, innerDh); win.mLayoutSeq = seq; if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame=" + win.mFrame + " mContainingFrame=" @@ -6700,6 +6712,9 @@ public class WindowManagerService extends IWindowManager.Stub final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); + final int innerDw = mPolicy.getNonDecorDisplayWidth(dw); + final int innerDh = mPolicy.getNonDecorDisplayHeight(dh); + int i; if (mFocusMayChange) { @@ -6799,13 +6814,15 @@ public class WindowManagerService extends IWindowManager.Stub boolean tokensAnimating = false; final int NAT = mAppTokens.size(); for (i=0; i