am 13735a2: Merge branch \'readonly-p4-donut\' into donut

Merge commit '13735a255dedd2c2e3b0cff66f0be2e17671f553'

* commit '13735a255dedd2c2e3b0cff66f0be2e17671f553':
  AI 147976: Compatibility mode support. Part 2.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3bd76a6..dfa3fa8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -161,7 +161,7 @@
         return metrics;
     }
 
-    Resources getTopLevelResources(String appDir) {
+    Resources getTopLevelResources(String appDir, float applicationScale) {
         synchronized (mPackages) {
             //Log.w(TAG, "getTopLevelResources: " + appDir);
             WeakReference<Resources> wr = mActiveResources.get(appDir);
@@ -181,7 +181,27 @@
                 return null;
             }
             DisplayMetrics metrics = getDisplayMetricsLocked(false);
-            r = new Resources(assets, metrics, getConfiguration());
+            // density used to load resources
+            // scaledDensity is calculated in Resources constructor
+            //
+            boolean usePreloaded = true;
+
+            // TODO: use explicit flag to indicate the compatibility mode.
+            if (applicationScale != 1.0f) {
+                usePreloaded = false;
+                DisplayMetrics newMetrics = new DisplayMetrics();
+                newMetrics.setTo(metrics);
+                float invertedScale = 1.0f / applicationScale;
+                newMetrics.density *= invertedScale;
+                newMetrics.xdpi *= invertedScale;
+                newMetrics.ydpi *= invertedScale;
+                newMetrics.widthPixels *= invertedScale;
+                newMetrics.heightPixels *= invertedScale;
+                metrics = newMetrics;
+            }
+            //Log.i(TAG, "Resource:" + appDir + ", density " + newMetrics.density + ", orig density:" +
+            //      metrics.density);
+            r = new Resources(assets, metrics, getConfiguration(), usePreloaded);
             //Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration());
             // XXX need to remove entries when weak references go away
             mActiveResources.put(appDir, new WeakReference<Resources>(r));
@@ -209,6 +229,8 @@
         private Resources mResources;
         private ClassLoader mClassLoader;
         private Application mApplication;
+        private float mApplicationScale;
+
         private final HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
             = new HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>>();
         private final HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>> mUnregisteredReceivers
@@ -241,8 +263,8 @@
                     mSystemContext =
                         ApplicationContext.createSystemContext(mainThread);
                     mSystemContext.getResources().updateConfiguration(
-                            mainThread.getConfiguration(),
-                            mainThread.getDisplayMetricsLocked(false));
+                             mainThread.getConfiguration(),
+                             mainThread.getDisplayMetricsLocked(false));
                     //Log.i(TAG, "Created system resources "
                     //        + mSystemContext.getResources() + ": "
                     //        + mSystemContext.getResources().getConfiguration());
@@ -250,6 +272,8 @@
                 mClassLoader = mSystemContext.getClassLoader();
                 mResources = mSystemContext.getResources();
             }
+
+            mApplicationScale = -1.0f;
         }
 
         public PackageInfo(ActivityThread activityThread, String name,
@@ -268,6 +292,7 @@
             mIncludeCode = true;
             mClassLoader = systemContext.getClassLoader();
             mResources = systemContext.getResources();
+            mApplicationScale = systemContext.getApplicationScale();
         }
 
         public String getPackageName() {
@@ -278,6 +303,47 @@
             return mSecurityViolation;
         }
 
+        public float getApplicationScale() {
+            if (mApplicationScale > 0.0f) {
+                return mApplicationScale;
+            }
+            DisplayMetrics metrics = mActivityThread.getDisplayMetricsLocked(false);
+            // Find out the density scale (relative to 160) of the supported density  that
+            // is closest to the system's density.
+            try {
+                ApplicationInfo ai = getPackageManager().getApplicationInfo(
+                        mPackageName, PackageManager.GET_SUPPORTS_DENSITIES);
+
+                float appScale = -1.0f;
+                if (ai.supportsDensities != null) {
+                    // TODO: precompute this in DisplayMetrics
+                    float systemDensityDpi = metrics.density * DisplayMetrics.DEFAULT_DENSITY;
+                    int minDiff = Integer.MAX_VALUE;
+                    for (int density : ai.supportsDensities) {
+                        int tmpDiff = (int) Math.abs(systemDensityDpi - density);
+                        if (tmpDiff == 0) {
+                            appScale = 1.0f;
+                            break;
+                        }
+                        // prefer higher density (appScale>1.0), unless that's only option.
+                        if (tmpDiff < minDiff && appScale < 1.0f) {
+                            appScale = systemDensityDpi / density;
+                            minDiff = tmpDiff;
+                        }
+                    }
+                }
+                if (appScale < 0.0f) {
+                    mApplicationScale = metrics.density;
+                } else {
+                    mApplicationScale = appScale;
+                }
+            } catch (RemoteException e) {
+                throw new AssertionError(e);
+            }
+            if (localLOGV) Log.v(TAG, "appScale=" + mApplicationScale + ", pkg=" + mPackageName);
+            return mApplicationScale;
+        }
+
         /**
          * Gets the array of shared libraries that are listed as
          * used by the given package.
@@ -435,7 +501,7 @@
 
         public Resources getResources(ActivityThread mainThread) {
             if (mResources == null) {
-                mResources = mainThread.getTopLevelResources(mResDir);
+                mResources = mainThread.getTopLevelResources(mResDir, getApplicationScale());
             }
             return mResources;
         }
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index 94f3373..72d9e3d 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -553,6 +553,19 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public float getApplicationScale() {
+        if (mPackageInfo != null) {
+            return mPackageInfo.getApplicationScale();
+        } else {
+            // same as system density
+            return 1.0f;
+        }
+    }
+
     @Override
     public void setWallpaper(Bitmap bitmap) throws IOException  {
         try {
@@ -2024,9 +2037,11 @@
             if (app.packageName.equals("system")) {
                 return mContext.mMainThread.getSystemContext().getResources();
             }
+            ActivityThread.PackageInfo pi = mContext.mMainThread.getPackageInfoNoCheck(app);
             Resources r = mContext.mMainThread.getTopLevelResources(
                     app.uid == Process.myUid() ? app.sourceDir
-                    : app.publicSourceDir);
+                    : app.publicSourceDir,
+                    pi.getApplicationScale());
             if (r != null) {
                 return r;
             }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3d5ed3a..e8daa6e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -527,6 +527,16 @@
     public abstract int getWallpaperDesiredMinimumHeight();
 
     /**
+     * Returns the scale in which the application will be drawn on the
+     * screen. This is usually 1.0f if the application supports the device's
+     * resolution/density. This will be 1.5f, for example, if the application
+     * that supports only 160 density runs on 240 density screen.
+     *
+     * @hide
+     */
+    public abstract float getApplicationScale();
+
+    /**
      * Change the current system wallpaper to a bitmap.  The given bitmap is
      * converted to a PNG and stored as the wallpaper.  On success, the intent
      * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 36e1c34..25b2cae 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -419,4 +419,12 @@
         throws PackageManager.NameNotFoundException {
         return mBase.createPackageContext(packageName, flags);
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public float getApplicationScale() {
+        return mBase.getApplicationScale();
+    }
 }
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index e020462..665e40c 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -24,6 +24,7 @@
 
 import android.graphics.Movie;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
 import android.os.SystemProperties;
@@ -56,12 +57,14 @@
     // Information about preloaded resources.  Note that they are not
     // protected by a lock, because while preloading in zygote we are all
     // single-threaded, and after that these are immutable.
-    private static final SparseArray<Drawable.ConstantState> mPreloadedDrawables
+    private static final SparseArray<Drawable.ConstantState> sPreloadedDrawables
             = new SparseArray<Drawable.ConstantState>();
     private static final SparseArray<ColorStateList> mPreloadedColorStateLists
             = new SparseArray<ColorStateList>();
     private static boolean mPreloaded;
 
+    private final SparseArray<Drawable.ConstantState> mPreloadedDrawables;
+
     /*package*/ final TypedValue mTmpValue = new TypedValue();
 
     // These are protected by the mTmpValue lock.
@@ -82,6 +85,22 @@
     /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics();
     PluralRules mPluralRule;
 
+    private static final SparseArray<Object> EMPTY_ARRAY = new SparseArray<Object>() {
+        @Override
+        public void put(int k, Object o) {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public void append(int k, Object o) {
+            throw new UnsupportedOperationException();
+        }
+    };
+
+    @SuppressWarnings("unchecked")
+    private static <T> SparseArray<T> emptySparseArray() {
+        return (SparseArray<T>) EMPTY_ARRAY;
+    }
+
     /**
      * This exception is thrown by the resource APIs when a requested resource
      * can not be found.
@@ -107,11 +126,27 @@
      */
     public Resources(AssetManager assets, DisplayMetrics metrics,
             Configuration config) {
+        this(assets, metrics, config, true);
+    }
+
+    /**
+     * Create a resource with an additional flag for preloaded
+     * drawable cache. Used by {@link ActivityThread}.
+     *
+     * @hide
+     */
+    public Resources(AssetManager assets, DisplayMetrics metrics,
+        Configuration config, boolean usePreloadedCache) {
         mAssets = assets;
         mConfiguration.setToDefaults();
         mMetrics.setToDefaults();
         updateConfiguration(config, metrics);
         assets.ensureStringBlocks();
+        if (usePreloadedCache) {
+            mPreloadedDrawables = sPreloadedDrawables;
+        } else {
+            mPreloadedDrawables = emptySparseArray();
+        }
     }
 
     /**
@@ -1218,6 +1253,7 @@
                 mMetrics.setTo(metrics);
             }
             mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
+
             String locale = null;
             if (mConfiguration.locale != null) {
                 locale = mConfiguration.locale.getLanguage();
@@ -1653,7 +1689,7 @@
             cs = dr.getConstantState();
             if (cs != null) {
                 if (mPreloading) {
-                    mPreloadedDrawables.put(key, cs);
+                    sPreloadedDrawables.put(key, cs);
                 } else {
                     synchronized (mTmpValue) {
                         //Log.i(TAG, "Saving cached drawable @ #" +
@@ -1883,6 +1919,6 @@
         mMetrics.setToDefaults();
         updateConfiguration(null, null);
         mAssets.ensureStringBlocks();
+        mPreloadedDrawables = sPreloadedDrawables;
     }
 }
-
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 882a079..2402660 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -102,7 +102,7 @@
     private float mYPrecision;
     private int mDeviceId;
     private int mEdgeFlags;
-    
+
     private MotionEvent mNext;
     private RuntimeException mRecycledLocation;
     private boolean mRecycled;
@@ -210,7 +210,29 @@
 
         return ev;
     }
-    
+
+    /**
+     * Scales down the cood of this event by the given scale.
+     *
+     * @hide
+     */
+    public void scale(float scale) {
+        if (scale != 1.0f) {
+            mX *= scale;
+            mY *= scale;
+            mRawX *= scale;
+            mRawY *= scale;
+            mSize *= scale;
+            mXPrecision *= scale;
+            mYPrecision *= scale;
+            float[] history = mHistory;
+            int length = history.length;
+            for (int i = 0; i < length; i++) {
+                history[i] *= scale;
+            }
+        }
+    }
+
     /**
      * Create a new MotionEvent, copying from an existing one.
      */
@@ -682,4 +704,3 @@
     }
 
 }
-
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 03ae6dc..71da9cf 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -136,20 +136,28 @@
     int mFormat = -1;
     int mType = -1;
     final Rect mSurfaceFrame = new Rect();
+    private final float mAppScale;
+    private final float mAppScaleInverted;
 
     public SurfaceView(Context context) {
         super(context);
         setWillNotDraw(true);
+        mAppScale = context.getApplicationScale();
+        mAppScaleInverted = 1.0f / mAppScale;
     }
     
     public SurfaceView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setWillNotDraw(true);
+        mAppScale = context.getApplicationScale();
+        mAppScaleInverted = 1.0f / mAppScale;
     }
 
     public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         setWillNotDraw(true);
+        mAppScale = context.getApplicationScale();
+        mAppScaleInverted = 1.0f / mAppScale;
     }
     
     /**
@@ -298,8 +306,8 @@
 
                 mLayout.x = mLeft;
                 mLayout.y = mTop;
-                mLayout.width = getWidth();
-                mLayout.height = getHeight();
+                mLayout.width = (int) (getWidth() * mAppScale);
+                mLayout.height = (int) (getHeight() * mAppScale);
                 mLayout.format = mRequestedFormat;
                 mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                               | WindowManager.LayoutParams.FLAG_SCALED
@@ -326,9 +334,14 @@
                 mSurfaceLock.lock();
                 mDrawingStopped = !visible;
                 final int relayoutResult = mSession.relayout(
-                        mWindow, mLayout, mWidth, mHeight,
+                    mWindow, mLayout, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale),
                         visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
                         mVisibleInsets, mSurface);
+
+                mContentInsets.scale(mAppScaleInverted);
+                mVisibleInsets.scale(mAppScaleInverted);
+                mWinFrame.scale(mAppScaleInverted);
+
                 if (localLOGV) Log.i(TAG, "New surface: " + mSurface
                         + ", vis=" + visible + ", frame=" + mWinFrame);
                 mSurfaceFrame.left = 0;
@@ -396,15 +409,25 @@
     }
 
     private static class MyWindow extends IWindow.Stub {
-        private WeakReference<SurfaceView> mSurfaceView;
+        private final WeakReference<SurfaceView> mSurfaceView;
+        private final float mAppScale;
+        private final float mAppScaleInverted;
 
         public MyWindow(SurfaceView surfaceView) {
             mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
+            mAppScale = surfaceView.getContext().getApplicationScale();
+            mAppScaleInverted = 1.0f / mAppScale;
         }
 
         public void resized(int w, int h, Rect coveredInsets,
                 Rect visibleInsets, boolean reportDraw) {
             SurfaceView surfaceView = mSurfaceView.get();
+            float scale = mAppScaleInverted;
+            w *= scale;
+            h *= scale;
+            coveredInsets.scale(scale);
+            visibleInsets.scale(scale);
+
             if (surfaceView != null) {
                 if (localLOGV) Log.v(
                         "SurfaceView", surfaceView + " got resized: w=" +
@@ -567,6 +590,7 @@
             Canvas c = null;
             if (!mDrawingStopped && mWindow != null) {
                 Rect frame = dirty != null ? dirty : mSurfaceFrame;
+                frame.scale(mAppScale);
                 try {
                     c = mSurface.lockCanvas(frame);
                 } catch (Exception e) {
@@ -612,4 +636,3 @@
         }
     };
 }
-
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 18ee9ae..0b03626 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -128,6 +128,9 @@
     int mHeight;
     Rect mDirty; // will be a graphics.Region soon
     boolean mIsAnimating;
+    // TODO: change these to scaler class.
+    float mAppScale;
+    float mAppScaleInverted; // = 1.0f / mAppScale
 
     final View.AttachInfo mAttachInfo;
 
@@ -384,10 +387,12 @@
             View panelParentView) {
         synchronized (this) {
             if (mView == null) {
+                mView = view;
+                mAppScale = mView.getContext().getApplicationScale();
+                mAppScaleInverted = 1.0f / mAppScale;
                 mWindowAttributes.copyFrom(attrs);
                 mSoftInputMode = attrs.softInputMode;
                 mWindowAttributesChanged = true;
-                mView = view;
                 mAttachInfo.mRootView = view;
                 if (panelParentView != null) {
                     mAttachInfo.mPanelParentWindowToken
@@ -400,7 +405,7 @@
                 // manager, to make sure we do the relayout before receiving
                 // any other events from the system.
                 requestLayout();
-                
+
                 try {
                     res = sWindowSession.add(mWindow, attrs,
                             getHostVisibility(), mAttachInfo.mContentInsets);
@@ -411,6 +416,7 @@
                     unscheduleTraversals();
                     throw new RuntimeException("Adding window failed", e);
                 }
+                mAttachInfo.mContentInsets.scale(mAppScaleInverted);
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
                 if (Config.LOGV) Log.v("ViewRoot", "Added window " + mWindow);
@@ -472,6 +478,8 @@
         synchronized (this) {
             int oldSoftInputMode = mWindowAttributes.softInputMode;
             mWindowAttributes.copyFrom(attrs);
+            mWindowAttributes.scale(mAppScale);
+
             if (newView) {
                 mSoftInputMode = attrs.softInputMode;
                 requestLayout();
@@ -521,9 +529,14 @@
     public void invalidateChild(View child, Rect dirty) {
         checkThread();
         if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty);
-        if (mCurScrollY != 0) {
+        if (mCurScrollY != 0 || mAppScale != 1.0f) {
             mTempRect.set(dirty);
-            mTempRect.offset(0, -mCurScrollY);
+            if (mCurScrollY != 0) {
+               mTempRect.offset(0, -mCurScrollY);
+            }
+            if (mAppScale != 1.0f) {
+                mTempRect.scale(mAppScale);
+            }
             dirty = mTempRect;
         }
         mDirty.union(dirty);
@@ -613,8 +626,8 @@
             mLayoutRequested = true;
 
             Display d = new Display(0);
-            desiredWindowWidth = d.getWidth();
-            desiredWindowHeight = d.getHeight();
+            desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted);
+            desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted);
 
             // For the very first time, tell the view hierarchy that it
             // is attached to the window.  Note that at this point the surface
@@ -683,8 +696,8 @@
                     windowResizesToFitContent = true;
 
                     Display d = new Display(0);
-                    desiredWindowWidth = d.getWidth();
-                    desiredWindowHeight = d.getHeight();
+                    desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted);
+                    desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted);
                 }
             }
 
@@ -792,10 +805,12 @@
                         params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
                     }
                 }
-                relayoutResult = sWindowSession.relayout(
-                    mWindow, params, host.mMeasuredWidth, host.mMeasuredHeight,
-                    viewVisibility, insetsPending, frame,
-                    mPendingContentInsets, mPendingVisibleInsets, mSurface);
+                if (DEBUG_LAYOUT) {
+                    Log.i(TAG, "host=w:" + host.mMeasuredWidth + ", h:" +
+                            host.mMeasuredHeight + ", params=" + params);
+                }
+                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
+
                 if (params != null) {
                     params.flags = fl;
                 }
@@ -862,7 +877,7 @@
             mHeight = frame.height();
 
             if (initialized) {
-                mGlCanvas.setViewport(mWidth, mHeight);
+                mGlCanvas.setViewport((int) (mWidth * mAppScale), (int) (mHeight * mAppScale));
             }
 
             boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
@@ -944,6 +959,11 @@
                         mTmpLocation[1] + host.mBottom - host.mTop);
 
                 host.gatherTransparentRegion(mTransparentRegion);
+
+                // TODO: scale the region, like:
+                // Region uses native methods. We probabl should have ScalableRegion class.
+
+                // Region does not have equals method ?
                 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
                     mPreviousTransparentRegion.set(mTransparentRegion);
                     // reconfigure window manager
@@ -974,6 +994,9 @@
             givenContent.left = givenContent.top = givenContent.right
                     = givenContent.bottom = givenVisible.left = givenVisible.top
                     = givenVisible.right = givenVisible.bottom = 0;
+            insets.contentInsets.scale(mAppScale);
+            insets.visibleInsets.scale(mAppScale);
+
             attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
             if (insetsPending || !mLastGivenInsets.equals(insets)) {
                 mLastGivenInsets.set(insets);
@@ -1113,7 +1136,7 @@
         
         int yoff;
         final boolean scrolling = mScroller != null
-                && mScroller.computeScrollOffset(); 
+                && mScroller.computeScrollOffset();
         if (scrolling) {
             yoff = mScroller.getCurrY();
         } else {
@@ -1135,10 +1158,19 @@
                     mGL.glEnable(GL_SCISSOR_TEST);
 
                     mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
-                    canvas.translate(0, -yoff);
                     mView.mPrivateFlags |= View.DRAWN;
-                    mView.draw(canvas);
-                    canvas.translate(0, yoff);
+
+                    float scale = mAppScale;
+                    int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+                    try {
+                        canvas.translate(0, -yoff);
+                        if (scale != 1.0f) {
+                            canvas.scale(scale, scale);
+                        }
+                        mView.draw(canvas);
+                    } finally {
+                        canvas.restoreToCount(saveCount);
+                    }
 
                     mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
                     checkEglErrors();
@@ -1160,7 +1192,7 @@
         }
 
         if (fullRedrawNeeded)
-            dirty.union(0, 0, mWidth, mHeight);
+            dirty.union(0, 0, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale));
 
         if (DEBUG_ORIENTATION || DEBUG_DRAW) {
             Log.v("ViewRoot", "Draw " + mView + "/"
@@ -1212,10 +1244,24 @@
                 dirty.setEmpty();
                 mIsAnimating = false;
                 mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
-                canvas.translate(0, -yoff);
-                mView.mPrivateFlags |= View.DRAWN;                    
-                mView.draw(canvas);
-                canvas.translate(0, yoff);
+                mView.mPrivateFlags |= View.DRAWN;
+
+                float scale = mAppScale;
+                Context cxt = mView.getContext();
+                if (DEBUG_DRAW) {
+                    Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + ", appScale=" + mAppScale);
+                }
+                int saveCount =  canvas.save(Canvas.MATRIX_SAVE_FLAG);
+                try {
+                    canvas.translate(0, -yoff);
+                    if (scale != 1.0f) {
+                        // re-scale this
+                        canvas.scale(scale, scale);
+                    }
+                    mView.draw(canvas);
+                } finally {
+                    canvas.restoreToCount(saveCount);
+                }
 
                 if (SHOW_FPS) {
                     int now = (int)SystemClock.elapsedRealtime();
@@ -1508,6 +1554,9 @@
             } else {
                 didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
             }
+            if (event != null) {
+                event.scale(mAppScaleInverted);
+            }
 
             try {
                 boolean handled;
@@ -1628,7 +1677,8 @@
                         if (mGlWanted && !mUseGL) {
                             initializeGL();
                             if (mGlCanvas != null) {
-                                mGlCanvas.setViewport(mWidth, mHeight);
+                                mGlCanvas.setViewport((int) (mWidth * mAppScale),
+                                        (int) (mHeight * mAppScale));
                             }
                         }
                     }
@@ -1828,6 +1878,9 @@
         } else {
             didFinish = false;
         }
+        if (event != null) {
+            event.scale(mAppScaleInverted);
+        }
 
         if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
 
@@ -2254,6 +2307,20 @@
         return mAudioManager;
     }
 
+    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
+            boolean insetsPending) throws RemoteException {
+        int relayoutResult = sWindowSession.relayout(
+                mWindow, params,
+                (int) (mView.mMeasuredWidth * mAppScale),
+                (int) (mView.mMeasuredHeight * mAppScale),
+                viewVisibility, insetsPending, mWinFrame,
+                mPendingContentInsets, mPendingVisibleInsets, mSurface);
+        mPendingContentInsets.scale(mAppScaleInverted);
+        mPendingVisibleInsets.scale(mAppScaleInverted);
+        mWinFrame.scale(mAppScaleInverted);
+        return relayoutResult;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -2322,12 +2389,8 @@
                     // to the window manager to make sure it has the correct
                     // animation info.
                     try {
-                        if ((sWindowSession.relayout(
-                                    mWindow, mWindowAttributes,
-                                    mView.mMeasuredWidth, mView.mMeasuredHeight,
-                                    viewVisibility, false, mWinFrame, mPendingContentInsets,
-                                    mPendingVisibleInsets, mSurface)
-                                &WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+                        if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
+                                & WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
                             sWindowSession.finishDrawing(mWindow);
                         }
                     } catch (RemoteException e) {
@@ -2361,8 +2424,11 @@
                 + " visibleInsets=" + visibleInsets.toShortString()
                 + " reportDraw=" + reportDraw);
         Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
-        msg.arg1 = w;
-        msg.arg2 = h;
+
+        coveredInsets.scale(mAppScaleInverted);
+        visibleInsets.scale(mAppScaleInverted);
+        msg.arg1 = (int) (w * mAppScaleInverted);
+        msg.arg2 = (int) (h * mAppScaleInverted);
         msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) };
         sendMessage(msg);
     }
@@ -2493,7 +2559,7 @@
                     sWindowSession.finishKey(mWindow);
                  } catch (RemoteException e) {
                  }
-            } else if (mIsPointer) {
+           } else if (mIsPointer) {
                 boolean didFinish;
                 MotionEvent event = mMotionEvent;
                 if (event == null) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 34e65ad..f6a171d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -954,7 +954,7 @@
             return sb.toString();
         }
 
-        void scaleUp(float scale) {
+        void scale(float scale) {
             if (scale != 1.0f) {
                 x *= scale;
                 y *= scale;
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 7022acf..50ab566 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -545,4 +545,17 @@
         right = in.readInt();
         bottom = in.readInt();
     }
+
+    /**
+     * Scales up the rect by the given scale.
+     * @hide
+     */
+    public void scale(float scale) {
+        if (scale != 1.0f) {
+            left *= scale;
+            top *= scale;
+            right *= scale;
+            bottom*= scale;
+        }
+    }
 }
diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java
index e733dd1..bd39a14 100644
--- a/test-runner/android/test/mock/MockContext.java
+++ b/test-runner/android/test/mock/MockContext.java
@@ -386,4 +386,12 @@
             throws PackageManager.NameNotFoundException {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public float getApplicationScale() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index baa3d53..f434e14 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -1145,4 +1145,12 @@
     public Context getApplicationContext() {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public float getApplicationScale() {
+        throw new UnsupportedOperationException();
+    }
 }