resolved conflicts for merge of 05be6d6f to master

Change-Id: Ic6a6c5bb300f6f1d43f9ed550b284282b4f16212
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index da34644..ed52dd3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -75,6 +75,7 @@
 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;
@@ -147,7 +148,7 @@
         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
     static final String TAG = "ActivityManager";
     static final boolean DEBUG = false;
-    static final boolean localLOGV = false;
+    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;
@@ -544,6 +545,12 @@
     ProcessRecord mHomeProcess;
     
     /**
+     * Packages that the user has asked to have run in screen size
+     * compatibility mode instead of filling the screen.
+     */
+    final HashSet<String> mScreenCompatPackages = new HashSet<String>();
+
+    /**
      * Set of PendingResultRecord objects that are currently active.
      */
     final HashSet mPendingResultRecords = new HashSet();
@@ -2074,6 +2081,74 @@
         }
     }
     
+    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);
         
@@ -3576,12 +3651,14 @@
             }
             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();
@@ -3672,7 +3749,9 @@
             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();
@@ -7866,6 +7945,10 @@
         pw.println("  mConfiguration: " + mConfiguration);
         if (dumpAll) {
             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
@@ -9560,7 +9643,8 @@
                 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 {
@@ -10670,7 +10754,8 @@
             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
                 }
@@ -10742,7 +10827,8 @@
             // 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();
@@ -11589,6 +11675,7 @@
                     + ": " + 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 2703481..cc9e78e 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -109,6 +109,7 @@
     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 @@
                 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);
         pw.print(prefix); pw.print("thumbHolder="); pw.println(thumbHolder);
         if (launchTime != 0 || startTime != 0) {
             pw.print(prefix); pw.print("launchTime=");
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 9558895..b0400af 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -540,9 +540,11 @@
             }
             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) {
@@ -3856,7 +3858,7 @@
         // 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;
@@ -3881,6 +3883,7 @@
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                     "Configuration doesn't matter not running " + r);
             r.stopFreezingScreenLocked(false);
+            r.forceNewConfig = false;
             return true;
         }
         
@@ -3892,10 +3895,11 @@
                     + 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);
@@ -3966,6 +3970,7 @@
         
         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 e39c239..5be35ee 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.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 4f157fe..a3e8be0 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -205,12 +205,21 @@
             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 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 8b739a4..12a5000 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -19,7 +19,6 @@
 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;
@@ -589,6 +588,7 @@
 
     // 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;
@@ -1760,7 +1760,7 @@
         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) {
@@ -2889,14 +2889,14 @@
     }
 
     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");
@@ -3682,7 +3682,7 @@
     }
 
     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) {
@@ -3704,7 +3704,7 @@
                 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;
@@ -3857,7 +3857,8 @@
             }
 
             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);
         }
@@ -3983,7 +3984,8 @@
             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;
@@ -4759,8 +4761,8 @@
         synchronized(mWindowMap) {
             long ident = Binder.clearCallingIdentity();
 
-            dw = mCurDisplayWidth;
-            dh = mCurDisplayHeight;
+            dw = mPolicy.getNonDecorDisplayWidth(mCurDisplayWidth);
+            dh = mPolicy.getNonDecorDisplayHeight(mCurDisplayHeight);
 
             int aboveAppLayer = mPolicy.windowTypeToLayerLw(
                     WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER
@@ -4808,7 +4810,7 @@
                 
                 // 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;
@@ -5526,10 +5528,11 @@
 
         // Override display width and height with what we are computing,
         // to be sure they remain consistent.
-        dm.widthPixels = dw;
-        dm.heightPixels = dh;
+        dm.widthPixels = mPolicy.getNonDecorDisplayWidth(dw);
+        dm.heightPixels = mPolicy.getNonDecorDisplayHeight(dh);
 
-        CompatibilityInfo.updateCompatibleScreenFrame(dm, orientation, mCompatibleScreenFrame);
+        mCompatibleScreenScale = CompatibilityInfo.updateCompatibleScreenFrame(
+                dm, mCompatibleScreenFrame, null);
 
         config.screenWidthDp = (int)(dm.widthPixels / dm.density);
         config.screenHeightDp = (int)(dm.heightPixels / dm.density);
@@ -6680,6 +6683,9 @@
         final int dw = mCurDisplayWidth;
         final int dh = mCurDisplayHeight;
 
+        final int innerDw = mPolicy.getNonDecorDisplayWidth(dw);
+        final int innerDh = mPolicy.getNonDecorDisplayHeight(dh);
+
         final int N = mWindows.size();
         int i;
 
@@ -6732,7 +6738,9 @@
                         //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="
@@ -6767,7 +6775,9 @@
                         //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="
@@ -6798,6 +6808,9 @@
         final int dw = mCurDisplayWidth;
         final int dh = mCurDisplayHeight;
 
+        final int innerDw = mPolicy.getNonDecorDisplayWidth(dw);
+        final int innerDh = mPolicy.getNonDecorDisplayHeight(dh);
+
         int i;
 
         if (mFocusMayChange) {
@@ -6897,13 +6910,15 @@
                 boolean tokensAnimating = false;
                 final int NAT = mAppTokens.size();
                 for (i=0; i<NAT; i++) {
-                    if (mAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) {
+                    if (mAppTokens.get(i).stepAnimationLocked(currentTime,
+                            innerDw, innerDh)) {
                         tokensAnimating = true;
                     }
                 }
                 final int NEAT = mExitingAppTokens.size();
                 for (i=0; i<NEAT; i++) {
-                    if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) {
+                    if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime,
+                            innerDw, innerDh)) {
                         tokensAnimating = true;
                     }
                 }
@@ -6956,8 +6971,8 @@
 
                         final boolean wasAnimating = w.mAnimating;
 
-                        int animDw = dw;
-                        int animDh = dh;
+                        int animDw = innerDw;
+                        int animDh = innerDh;
 
                         // If the window has moved due to its containing
                         // content frame changing, then we'd like to animate
@@ -7214,6 +7229,7 @@
                         LayoutParams animLp = null;
                         int bestAnimLayer = -1;
                         boolean fullscreenAnim = false;
+                        boolean needBgFiller = false;
 
                         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                                 "New wallpaper target=" + mWallpaperTarget
@@ -7253,9 +7269,10 @@
                                 if (ws != null) {
                                     // If this is a compatibility mode
                                     // window, we will always use its anim.
-                                    if ((ws.mAttrs.flags&FLAG_COMPATIBLE_WINDOW) != 0) {
+                                    if (ws.mNeedsBackgroundFiller) {
                                         animLp = ws.mAttrs;
                                         bestAnimLayer = Integer.MAX_VALUE;
+                                        needBgFiller = true;
                                     } else if (!fullscreenAnim || ws.mLayer > bestAnimLayer) {
                                         animLp = ws.mAttrs;
                                         bestAnimLayer = ws.mLayer;
@@ -7320,7 +7337,8 @@
                             wtoken.reportedVisible = false;
                             wtoken.inPendingTransaction = false;
                             wtoken.animation = null;
-                            setTokenVisibilityLocked(wtoken, animLp, true, transit, false);
+                            setTokenVisibilityLocked(wtoken, animLp, true,
+                                    transit, false, needBgFiller);
                             wtoken.updateReportedVisibilityLocked();
                             wtoken.waitingToShow = false;
                             wtoken.showAllWindowsLocked();
@@ -7332,7 +7350,8 @@
                                     "Now closing app" + wtoken);
                             wtoken.inPendingTransaction = false;
                             wtoken.animation = null;
-                            setTokenVisibilityLocked(wtoken, animLp, false, transit, false);
+                            setTokenVisibilityLocked(wtoken, animLp, false,
+                                    transit, false, needBgFiller);
                             wtoken.updateReportedVisibilityLocked();
                             wtoken.waitingToHide = false;
                             // Force the allDrawn flag, because we want to start
@@ -7835,12 +7854,14 @@
                     }
 
                     boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
-                    if (opaqueDrawn && w.isFullscreen(dw, dh)) {
+                    if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) {
                         // This window completely covers everything behind it,
                         // so we want to leave all of them as unblurred (for
                         // performance reasons).
                         obscured = true;
-                    } else if (w.needsBackgroundFiller(dw, dh) && (canBeSeen || w.isAnimating())) {
+                    } else if (w.mNeedsBackgroundFiller && w.mHasDrawn
+                            && w.mViewVisibility == View.VISIBLE
+                            && (canBeSeen || w.isAnimating())) {
                         // This window is in compatibility mode, and needs background filler.
                         obscured = true;
                         mBackgroundFillerTarget = w;
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index c05186a..2014e9d 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -72,6 +72,7 @@
     final boolean mIsImWindow;
     final boolean mIsWallpaper;
     final boolean mIsFloatingLayer;
+    final boolean mEnforceSizeCompat;
     int mViewVisibility;
     boolean mPolicyVisibility = true;
     boolean mPolicyVisibilityAfterAnim = true;
@@ -91,6 +92,7 @@
     int mLastLayer;
     boolean mHaveFrame;
     boolean mObscured;
+    boolean mNeedsBackgroundFiller;
     boolean mTurnOnScreen;
 
     int mLayoutSeq = -1;
@@ -154,6 +156,7 @@
 
     // Current transformation being applied.
     boolean mHaveMatrix;
+    float mGlobalScale=1;
     float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
     float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
     float mHScale=1, mVScale=1;
@@ -163,6 +166,7 @@
     // "Real" frame that the application sees.
     final Rect mFrame = new Rect();
     final Rect mLastFrame = new Rect();
+    final Rect mScaledFrame = new Rect();
 
     final Rect mContainingFrame = new Rect();
     final Rect mDisplayFrame = new Rect();
@@ -273,6 +277,7 @@
         mViewVisibility = viewVisibility;
         DeathRecipient deathRecipient = new DeathRecipient();
         mAlpha = a.alpha;
+        mEnforceSizeCompat = (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0;
         if (WindowManagerService.localLOGV) Slog.v(
             WindowManagerService.TAG, "Window " + this + " client=" + c.asBinder()
             + " token=" + token + " (" + mAttrs.token + ")");
@@ -368,7 +373,7 @@
         final Rect display = mDisplayFrame;
         display.set(df);
 
-        if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
+        if (mEnforceSizeCompat) {
             container.intersect(mService.mCompatibleScreenFrame);
             if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) {
                 display.intersect(mService.mCompatibleScreenFrame);
@@ -416,6 +421,28 @@
         // Now make sure the window fits in the overall display.
         Gravity.applyDisplay(mAttrs.gravity, df, frame);
 
+        int adjRight=0, adjBottom=0;
+
+        if (mEnforceSizeCompat) {
+            // Adjust window offsets by the scaling factor.
+            int xoff = (int)((frame.left-mService.mCompatibleScreenFrame.left)*mGlobalScale)
+                    - (frame.left-mService.mCompatibleScreenFrame.left);
+            int yoff = (int)((frame.top-mService.mCompatibleScreenFrame.top)*mGlobalScale)
+                    - (frame.top-mService.mCompatibleScreenFrame.top);
+            frame.offset(xoff, yoff);
+
+            // We are temporarily going to apply the compatibility scale
+            // to the window so that we can correctly associate it with the
+            // content and visible frame.
+            adjRight = frame.right - frame.left;
+            adjRight = (int)((adjRight)*mGlobalScale + .5f) - adjRight;
+            adjBottom = frame.bottom - frame.top;
+            adjBottom = (int)((adjBottom)*mGlobalScale + .5f) - adjBottom;
+            frame.right += adjRight;
+            frame.bottom += adjBottom;
+        }
+        mScaledFrame.set(frame);
+
         // Make sure the content and visible frames are inside of the
         // final window frame.
         if (content.left < frame.left) content.left = frame.left;
@@ -439,6 +466,22 @@
         visibleInsets.right = frame.right-visible.right;
         visibleInsets.bottom = frame.bottom-visible.bottom;
 
+        if (mEnforceSizeCompat) {
+            // Scale the computed insets back to the window's compatibility
+            // coordinate space, and put frame back to correct size.
+            final float invScale = 1.0f/mGlobalScale;
+            contentInsets.left = (int)(contentInsets.left*invScale);
+            contentInsets.top = (int)(contentInsets.top*invScale);
+            contentInsets.right = (int)(contentInsets.right*invScale);
+            contentInsets.bottom = (int)(contentInsets.bottom*invScale);
+            visibleInsets.left = (int)(visibleInsets.left*invScale);
+            visibleInsets.top = (int)(visibleInsets.top*invScale);
+            visibleInsets.right = (int)(visibleInsets.right*invScale);
+            visibleInsets.bottom = (int)(visibleInsets.bottom*invScale);
+            frame.right -= adjRight;
+            frame.bottom -= adjBottom;
+        }
+
         if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
             mService.updateWallpaperOffsetLocked(this, mService.mDisplay.getRealWidth(),
                     mService.mDisplay.getRealHeight(), false);
@@ -819,9 +862,10 @@
                 if (!mLocalAnimating) {
                     if (WindowManagerService.DEBUG_ANIM) Slog.v(
                         WindowManagerService.TAG, "Starting animation in " + this +
-                        " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() +
+                        " @ " + currentTime + ": ww=" + mScaledFrame.width() +
+                        " wh=" + mScaledFrame.height() +
                         " dw=" + dw + " dh=" + dh + " scale=" + mService.mWindowAnimationScale);
-                    mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh);
+                    mAnimation.initialize(mScaledFrame.width(), mScaledFrame.height(), dw, dh);
                     mAnimation.setStartTime(currentTime);
                     mLocalAnimating = true;
                     mAnimating = true;
@@ -988,6 +1032,14 @@
         return true;
     }
 
+    void prelayout() {
+        if (mEnforceSizeCompat) {
+            mGlobalScale = mService.mCompatibleScreenScale;
+        } else {
+            mGlobalScale = 1;
+        }
+    }
+
     void computeShownFrameLocked() {
         final boolean selfTransformation = mHasLocalTransformation;
         Transformation attachedTransformation =
@@ -1031,6 +1083,7 @@
 
             // Compute the desired transformation.
             tmpMatrix.setTranslate(0, 0);
+            tmpMatrix.postScale(mGlobalScale, mGlobalScale);
             if (selfTransformation) {
                 tmpMatrix.postConcat(mTransformation.getMatrix());
             }
@@ -1105,10 +1158,10 @@
         }
         mShownAlpha = mAlpha;
         mHaveMatrix = false;
-        mDsDx = 1;
+        mDsDx = mGlobalScale;
         mDtDx = 0;
         mDsDy = 0;
-        mDtDy = 1;
+        mDtDy = mGlobalScale;
     }
 
     /**
@@ -1281,12 +1334,14 @@
                 && mService.mPolicy.isScreenOn();
     }
 
-    boolean needsBackgroundFiller(int screenWidth, int screenHeight) {
-        return
+    void evalNeedsBackgroundFiller(int screenWidth, int screenHeight) {
+        mNeedsBackgroundFiller =
              // only if the application is requesting compatible window
-             (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 &&
+             mEnforceSizeCompat &&
              // only if it's visible
              mHasDrawn && mViewVisibility == View.VISIBLE &&
+             // not needed if the compat window is actually full screen
+             !isFullscreenIgnoringCompat(screenWidth, screenHeight) &&
              // and only if the application fills the compatible screen
              mFrame.left <= mService.mCompatibleScreenFrame.left &&
              mFrame.top <= mService.mCompatibleScreenFrame.top &&
@@ -1295,8 +1350,19 @@
     }
 
     boolean isFullscreen(int screenWidth, int screenHeight) {
-        return mFrame.left <= 0 && mFrame.top <= 0 &&
-                mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
+        if (mEnforceSizeCompat) {
+            return mFrame.left <= mService.mCompatibleScreenFrame.left &&
+                    mFrame.top <= mService.mCompatibleScreenFrame.top &&
+                    mFrame.right >= mService.mCompatibleScreenFrame.right &&
+                    mFrame.bottom >= mService.mCompatibleScreenFrame.bottom;
+        } else {
+            return isFullscreenIgnoringCompat(screenWidth, screenHeight);
+        }
+    }
+
+    boolean isFullscreenIgnoringCompat(int screenWidth, int screenHeight) {
+        return mScaledFrame.left <= 0 && mScaledFrame.top <= 0 &&
+                mScaledFrame.right >= screenWidth && mScaledFrame.bottom >= screenHeight;
     }
 
     void removeLocked() {
@@ -1426,30 +1492,38 @@
         return true;
     }
 
+    private static void applyScaledInsets(Region outRegion, Rect frame, Rect inset, float scale) {
+        if (scale != 1) {
+            outRegion.set(frame.left + (int)(inset.left*scale),
+                    frame.top + (int)(inset.top*scale),
+                    frame.right - (int)(inset.right*scale),
+                    frame.bottom - (int)(inset.bottom*scale));
+        } else {
+            outRegion.set(
+                    frame.left + inset.left, frame.top + inset.top,
+                    frame.right - inset.right, frame.bottom - inset.bottom);
+        }
+    }
+
     public void getTouchableRegion(Region outRegion) {
-        final Rect frame = mFrame;
+        final Rect frame = mScaledFrame;
         switch (mTouchableInsets) {
             default:
             case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
                 outRegion.set(frame);
                 break;
-            case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
-                final Rect inset = mGivenContentInsets;
-                outRegion.set(
-                        frame.left + inset.left, frame.top + inset.top,
-                        frame.right - inset.right, frame.bottom - inset.bottom);
+            case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT:
+                applyScaledInsets(outRegion, frame, mGivenContentInsets, mGlobalScale);
                 break;
-            }
-            case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
-                final Rect inset = mGivenVisibleInsets;
-                outRegion.set(
-                        frame.left + inset.left, frame.top + inset.top,
-                        frame.right - inset.right, frame.bottom - inset.bottom);
+            case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE:
+                applyScaledInsets(outRegion, frame, mGivenVisibleInsets, mGlobalScale);
                 break;
-            }
             case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: {
                 final Region givenTouchableRegion = mGivenTouchableRegion;
                 outRegion.set(givenTouchableRegion);
+                if (mGlobalScale != 1) {
+                    outRegion.scale(mGlobalScale);
+                }
                 outRegion.translate(frame.left, frame.top);
                 break;
             }
@@ -1512,7 +1586,8 @@
         }
         pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
                 pw.print(" h="); pw.print(mRequestedHeight);
-                pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
+                pw.print(" mLayoutSeq="); pw.print(mLayoutSeq);
+                pw.print(" mNeedsBackgroundFiller="); pw.println(mNeedsBackgroundFiller);
         if (mXOffset != 0 || mYOffset != 0) {
             pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
                     pw.print(" y="); pw.println(mYOffset);
@@ -1533,6 +1608,7 @@
                 pw.println();
         pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
                 pw.print(" last="); mLastFrame.printShortString(pw);
+                pw.print(" scaled="); mScaledFrame.printShortString(pw);
                 pw.println();
         pw.print(prefix); pw.print("mContainingFrame=");
                 mContainingFrame.printShortString(pw);
@@ -1568,8 +1644,9 @@
                     pw.print(" mAlpha="); pw.print(mAlpha);
                     pw.print(" mLastAlpha="); pw.println(mLastAlpha);
         }
-        if (mHaveMatrix) {
-            pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx);
+        if (mHaveMatrix || mGlobalScale != 1) {
+            pw.print(prefix); pw.print("mGlobalScale="); pw.print(mGlobalScale);
+                    pw.print(" mDsDx="); pw.print(mDsDx);
                     pw.print(" mDtDx="); pw.print(mDtDx);
                     pw.print(" mDsDy="); pw.print(mDsDy);
                     pw.print(" mDtDy="); pw.println(mDtDy);