We can now (kind-of) change screen density on the fly.

Preloaded drawables now have a density associated with them, so we
can load the correct drawable if we are using a different density.

Window manager now formally keeps track of the density for each
screen, allowing it to be overridden like you can already do with
size, and relies on this density to drive itself internally and
the configurations it reports.

There are a new set of Bitmap constructors where you provide a
DisplayMetrics so they can be constructed with the correct density.
(This will be for when you can have different windows in the same
app running at different densities.)

ActivityThread now watches for density changes, and pushes them
to the DENSITY_DEVICE and Bitmap global density values for that
process.

A new am command allows you to change the density.
diff --git a/api/current.txt b/api/current.txt
index cc67e89..ebc1fc6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8114,8 +8114,11 @@
     method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int);
     method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean);
     method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config);
+    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, int, int, android.graphics.Bitmap.Config);
+    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
+    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, boolean);
     method public int describeContents();
     method public void eraseColor(int);
@@ -19958,7 +19961,6 @@
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public boolean onSearchRequested();
     method public void onStart();
-    method public final int onStartCommand(android.content.Intent, int, int);
     method public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
     method public void onWindowFocusChanged(boolean);
     method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index f42fd2a..a79eb14 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -133,6 +133,8 @@
             runScreenCompat();
         } else if (op.equals("display-size")) {
             runDisplaySize();
+        } else if (op.equals("display-density")) {
+            runDisplayDensity();
         } else if (op.equals("to-uri")) {
             runToUri(false);
         } else if (op.equals("to-intent-uri")) {
@@ -1127,6 +1129,44 @@
         }
     }
 
+    private void runDisplayDensity() throws Exception {
+        String densityStr = nextArgRequired();
+        int density;
+        if ("reset".equals(densityStr)) {
+            density = -1;
+        } else {
+            try {
+                density = Integer.parseInt(densityStr);
+            } catch (NumberFormatException e) {
+                System.err.println("Error: bad number " + e);
+                showUsage();
+                return;
+            }
+            if (density < 72) {
+                System.err.println("Error: density must be >= 72");
+                showUsage();
+                return;
+            }
+        }
+
+        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
+                Context.WINDOW_SERVICE));
+        if (wm == null) {
+            System.err.println(NO_SYSTEM_ERROR_CODE);
+            throw new AndroidException("Can't connect to window manager; is the system running?");
+        }
+
+        try {
+            if (density > 0) {
+                // TODO(multidisplay): For now Configuration only applies to main screen.
+                wm.setForcedDisplayDensity(Display.DEFAULT_DISPLAY, density);
+            } else {
+                wm.clearForcedDisplayDensity(Display.DEFAULT_DISPLAY);
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
     private void runToUri(boolean intentScheme) throws Exception {
         Intent intent = makeIntent();
         System.out.println(intent.toUri(intentScheme ? Intent.URI_INTENT_SCHEME : 0));
@@ -1301,6 +1341,7 @@
                 "       am monitor [--gdb <port>]\n" +
                 "       am screen-compat [on|off] <PACKAGE>\n" +
                 "       am display-size [reset|MxN]\n" +
+                "       am display-density [reset|DENSITY]\n" +
                 "       am to-uri [INTENT]\n" +
                 "       am to-intent-uri [INTENT]\n" +
                 "\n" +
@@ -1355,6 +1396,8 @@
                 "\n" +
                 "am display-size: override display size.\n" +
                 "\n" +
+                "am display-density: override display density.\n" +
+                "\n" +
                 "am to-uri: print the given Intent specification as a URI.\n" +
                 "\n" +
                 "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f8d01de..bb35ddd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -167,6 +167,7 @@
     AppBindData mBoundApplication;
     Profiler mProfiler;
     int mCurDefaultDisplayDpi;
+    boolean mDensityCompatMode;
     Configuration mConfiguration;
     Configuration mCompatConfiguration;
     Configuration mResConfiguration;
@@ -2733,7 +2734,8 @@
 
                 // On platforms where we don't want thumbnails, set dims to (0,0)
                 if ((w > 0) && (h > 0)) {
-                    thumbnail = Bitmap.createBitmap(w, h, THUMBNAIL_FORMAT);
+                    thumbnail = Bitmap.createBitmap(r.activity.getResources().getDisplayMetrics(),
+                            w, h, THUMBNAIL_FORMAT);
                     thumbnail.eraseColor(0);
                 }
             }
@@ -3468,6 +3470,7 @@
         // If there was a pending configuration change, execute it first.
         if (changedConfig != null) {
             mCurDefaultDisplayDpi = changedConfig.densityDpi;
+            updateDefaultDensity();
             handleConfigurationChanged(changedConfig, null);
         }
 
@@ -3718,6 +3721,7 @@
                 if (!mPendingConfiguration.isOtherSeqNewer(config)) {
                     config = mPendingConfiguration;
                     mCurDefaultDisplayDpi = config.densityDpi;
+                    updateDefaultDensity();
                 }
                 mPendingConfiguration = null;
             }
@@ -3918,8 +3922,20 @@
         } catch (RemoteException e) {
             // Ignore
         }
-    }    
-    
+    }
+
+    private void updateDefaultDensity() {
+        if (mCurDefaultDisplayDpi != Configuration.DENSITY_DPI_UNDEFINED
+                && mCurDefaultDisplayDpi != DisplayMetrics.DENSITY_DEVICE
+                && !mDensityCompatMode) {
+            Slog.i(TAG, "Switching default density from "
+                    + DisplayMetrics.DENSITY_DEVICE + " to "
+                    + mCurDefaultDisplayDpi);
+            DisplayMetrics.DENSITY_DEVICE = mCurDefaultDisplayDpi;
+            Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
+        }
+    }
+
     private void handleBindApplication(AppBindData data) {
         mBoundApplication = data;
         mConfiguration = new Configuration(data.config);
@@ -3980,6 +3996,16 @@
 
         data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
 
+        /**
+         * Switch this process to density compatibility mode if needed.
+         */
+        if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
+                == 0) {
+            mDensityCompatMode = true;
+            Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
+        }
+        updateDefaultDensity();
+
         final ContextImpl appContext = new ContextImpl();
         appContext.init(data.info, null, this);
         final File cacheDir = appContext.getCacheDir();
@@ -4010,14 +4036,6 @@
             StrictMode.enableDeathOnNetwork();
         }
 
-        /**
-         * Switch this process to density compatibility mode if needed.
-         */
-        if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
-                == 0) {
-            Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
-        }
-
         if (data.debugMode != IApplicationThread.DEBUG_OFF) {
             // XXX should have option to change the port.
             Debug.changeDebugPort(8100);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index c7e55ce..d2af3e9 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -89,7 +89,8 @@
             = new LongSparseArray<ColorStateList>();
     private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables
             = new LongSparseArray<Drawable.ConstantState>();
-    private static boolean mPreloaded;
+    private static boolean sPreloaded;
+    private static int sPreloadedDensity;
 
     /*package*/ final TypedValue mTmpValue = new TypedValue();
     /*package*/ final Configuration mTmpConfig = new Configuration();
@@ -1837,11 +1838,14 @@
      */
     public final void startPreloading() {
         synchronized (mSync) {
-            if (mPreloaded) {
+            if (sPreloaded) {
                 throw new IllegalStateException("Resources already preloaded");
             }
-            mPreloaded = true;
+            sPreloaded = true;
             mPreloading = true;
+            sPreloadedDensity = DisplayMetrics.DENSITY_DEVICE;
+            mConfiguration.densityDpi = sPreloadedDensity;
+            updateConfiguration(null, null);
         }
     }
     
@@ -1855,7 +1859,24 @@
             flushLayoutCache();
         }
     }
-    
+
+    private boolean verifyPreloadConfig(TypedValue value, String name) {
+        if ((value.changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE
+                | ActivityInfo.CONFIG_DENSITY)) != 0) {
+            String resName;
+            try {
+                resName = getResourceName(value.resourceId);
+            } catch (NotFoundException e) {
+                resName = "?";
+            }
+            Log.w(TAG, "Preloaded " + name + " resource #0x"
+                    + Integer.toHexString(value.resourceId)
+                    + " (" + resName + ") that varies with configuration!!");
+            return false;
+        }
+        return true;
+    }
+
     /*package*/ Drawable loadDrawable(TypedValue value, int id)
             throws NotFoundException {
 
@@ -1879,8 +1900,10 @@
             return dr;
         }
 
-        Drawable.ConstantState cs = isColorDrawable ?
-                sPreloadedColorDrawables.get(key) : sPreloadedDrawables.get(key);
+        Drawable.ConstantState cs = isColorDrawable
+                ? sPreloadedColorDrawables.get(key)
+                : (sPreloadedDensity == mConfiguration.densityDpi
+                        ? sPreloadedDrawables.get(key) : null);
         if (cs != null) {
             dr = cs.newDrawable(this);
         } else {
@@ -1948,10 +1971,12 @@
             cs = dr.getConstantState();
             if (cs != null) {
                 if (mPreloading) {
-                    if (isColorDrawable) {
-                        sPreloadedColorDrawables.put(key, cs);
-                    } else {
-                        sPreloadedDrawables.put(key, cs);
+                    if (verifyPreloadConfig(value, "drawable")) {
+                        if (isColorDrawable) {
+                            sPreloadedColorDrawables.put(key, cs);
+                        } else {
+                            sPreloadedDrawables.put(key, cs);
+                        }
                     }
                 } else {
                     synchronized (mTmpValue) {
@@ -2016,7 +2041,9 @@
 
             csl = ColorStateList.valueOf(value.data);
             if (mPreloading) {
-                sPreloadedColorStateLists.put(key, csl);
+                if (verifyPreloadConfig(value, "color")) {
+                    sPreloadedColorStateLists.put(key, csl);
+                }
             }
 
             return csl;
@@ -2060,7 +2087,9 @@
 
         if (csl != null) {
             if (mPreloading) {
-                sPreloadedColorStateLists.put(key, csl);
+                if (verifyPreloadConfig(value, "color")) {
+                    sPreloadedColorStateLists.put(key, csl);
+                }
             } else {
                 synchronized (mTmpValue) {
                     //Log.i(TAG, "Saving cached color state list @ #" +
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 840f27d..c8bb886 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2794,6 +2794,13 @@
         public static final String DISPLAY_SIZE_FORCED = "display_size_forced";
 
         /**
+         * The saved value for WindowManagerService.setForcedDisplayDensity().
+         * One integer in dpi.  If unset, then use the real display density.
+         * @hide
+         */
+        public static final String DISPLAY_DENSITY_FORCED = "display_density_forced";
+
+        /**
          * Whether assisted GPS should be enabled or not.
          * @hide
          */
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index d58eec4..506594b 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -92,7 +92,7 @@
      * density for a display in {@link #densityDpi}.
      */
     @Deprecated
-    public static final int DENSITY_DEVICE = getDeviceDensity();
+    public static int DENSITY_DEVICE = getDeviceDensity();
 
     /**
      * The absolute width of the display in pixels.
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index c59f1bf..2b0c14d 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -16,7 +16,6 @@
 
 package android.view;
 
-import android.content.res.CompatibilityInfo;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f58cd83..5bccdd4 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -59,6 +59,8 @@
 
     void setForcedDisplaySize(int displayId, int longDimen, int shortDimen);
     void clearForcedDisplaySize(int displayId);
+    void setForcedDisplayDensity(int displayId, int density);
+    void clearForcedDisplayDensity(int displayId);
 
     // Is the device configured to have a full system bar for larger screens?
     boolean hasSystemNavBar();
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index a719a01..fe14c88 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -534,7 +534,8 @@
      */
     public Bitmap getBitmap(int width, int height) {
         if (isAvailable() && width > 0 && height > 0) {
-            return getBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888));
+            return getBitmap(Bitmap.createBitmap(getResources().getDisplayMetrics(),
+                    width, height, Bitmap.Config.ARGB_8888));
         }
         return null;
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 50c3407..cd35002 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12402,7 +12402,8 @@
                 if (bitmap != null) bitmap.recycle();
 
                 try {
-                    bitmap = Bitmap.createBitmap(width, height, quality);
+                    bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
+                            width, height, quality);
                     bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
                     if (autoScale) {
                         mDrawingCache = bitmap;
@@ -12494,7 +12495,8 @@
         width = (int) ((width * scale) + 0.5f);
         height = (int) ((height * scale) + 0.5f);
 
-        Bitmap bitmap = Bitmap.createBitmap(width > 0 ? width : 1, height > 0 ? height : 1, quality);
+        Bitmap bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
+                width > 0 ? width : 1, height > 0 ? height : 1, quality);
         if (bitmap == null) {
             throw new OutOfMemoryError();
         }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 00d4fc7..dae9265 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -546,7 +546,7 @@
                                         (view != null && view.getResources() != null) ?
                                                 view.getResources().getDisplayMetrics() : null;
                                 final Bitmap bitmap = metrics != null ?
-                                        Bitmap.createBitmap(metrics.widthPixels,
+                                        Bitmap.createBitmap(metrics, metrics.widthPixels,
                                                 metrics.heightPixels, Bitmap.Config.RGB_565) : null;
                                 final Canvas canvas = bitmap != null ? new Canvas(bitmap) : null;
                                 return new Object[] {
@@ -706,7 +706,8 @@
             Log.w("View", "Failed to create capture bitmap!");
             // Send an empty one so that it doesn't get stuck waiting for
             // something.
-            b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+            b = Bitmap.createBitmap(root.getResources().getDisplayMetrics(),
+                    1, 1, Bitmap.Config.ARGB_8888);
         }
 
         BufferedOutputStream out = null;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 9522a1b..407bae5 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -483,7 +483,7 @@
      * Called by window manager once it has the initial, default native
      * display dimensions.
      */
-    public void setInitialDisplaySize(Display display, int width, int height);
+    public void setInitialDisplaySize(Display display, int width, int height, int density);
 
     /**
      * Check permissions when adding a window.
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 293eda1..6853660 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -1412,8 +1412,8 @@
                 return null;
             }
 
-            Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
-                    Bitmap.Config.ARGB_8888);
+            Bitmap bitmap = Bitmap.createBitmap(v.getResources().getDisplayMetrics(),
+                    v.getMeasuredWidth(), v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
             mCanvas.setBitmap(bitmap);
 
             float rotationX = v.getRotationX();
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 998c037..6ad67c3 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -384,7 +384,12 @@
                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
             }
             if (id != 0) {
-                mResources.getColorStateList(id);
+                if (mResources.getColorStateList(id) == null) {
+                    throw new IllegalArgumentException(
+                            "Unable to find preloaded color resource #0x"
+                            + Integer.toHexString(id)
+                            + " (" + ar.getString(i) + ")");
+                }
             }
         }
         return N;
@@ -407,11 +412,11 @@
                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
             }
             if (id != 0) {
-                Drawable dr = mResources.getDrawable(id);
-                if ((dr.getChangingConfigurations()&~ActivityInfo.CONFIG_FONT_SCALE) != 0) {
-                    Log.w(TAG, "Preloaded drawable resource #0x"
+                if (mResources.getDrawable(id) == null) {
+                    throw new IllegalArgumentException(
+                            "Unable to find preloaded drawable resource #0x"
                             + Integer.toHexString(id)
-                            + " (" + ar.getString(i) + ") that varies with configuration!!");
+                            + " (" + ar.getString(i) + ")");
                 }
             }
         }
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index c91c2f1..128adcb 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -63,7 +63,7 @@
     private boolean mRecycled;
 
     // Package-scoped for fast access.
-    int mDensity = sDefaultDensity = getDefaultDensity();
+    int mDensity = getDefaultDensity();
 
     private static volatile Matrix sScaleMatrix;
 
@@ -85,7 +85,7 @@
         sDefaultDensity = DisplayMetrics.DENSITY_DEVICE;
         return sDefaultDensity;
     }
-    
+
     /**
      * @noinspection UnusedDeclaration
      */
@@ -625,6 +625,22 @@
 
     /**
      * Returns a mutable bitmap with the specified width and height.  Its
+     * initial density is determined from the given {@link DisplayMetrics}.
+     *
+     * @param display  Display metrics for the display this bitmap will be
+     *                 drawn on.
+     * @param width    The width of the bitmap
+     * @param height   The height of the bitmap
+     * @param config   The bitmap config to create.
+     * @throws IllegalArgumentException if the width or height are <= 0
+     */
+    public static Bitmap createBitmap(DisplayMetrics display, int width,
+            int height, Config config) {
+        return createBitmap(display, width, height, config, true);
+    }
+
+    /**
+     * Returns a mutable bitmap with the specified width and height.  Its
      * initial density is as per {@link #getDensity}.
      *
      * @param width    The width of the bitmap
@@ -637,10 +653,33 @@
      * @throws IllegalArgumentException if the width or height are <= 0
      */
     private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {
+        return createBitmap(null, width, height, config, hasAlpha);
+    }
+
+    /**
+     * Returns a mutable bitmap with the specified width and height.  Its
+     * initial density is determined from the given {@link DisplayMetrics}.
+     *
+     * @param display  Display metrics for the display this bitmap will be
+     *                 drawn on.
+     * @param width    The width of the bitmap
+     * @param height   The height of the bitmap
+     * @param config   The bitmap config to create.
+     * @param hasAlpha If the bitmap is ARGB_8888 this flag can be used to mark the
+     *                 bitmap as opaque. Doing so will clear the bitmap in black
+     *                 instead of transparent.  
+     * 
+     * @throws IllegalArgumentException if the width or height are <= 0
+     */
+    private static Bitmap createBitmap(DisplayMetrics display, int width, int height,
+            Config config, boolean hasAlpha) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("width and height must be > 0");
         }
         Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);
+        if (display != null) {
+            bm.mDensity = display.densityDpi;
+        }
         if (config == Config.ARGB_8888 && !hasAlpha) {
             nativeErase(bm.mNativeBitmap, 0xff000000);
             nativeSetHasAlpha(bm.mNativeBitmap, hasAlpha);
@@ -673,6 +712,31 @@
      */
     public static Bitmap createBitmap(int colors[], int offset, int stride,
             int width, int height, Config config) {
+        return createBitmap(null, colors, offset, stride, width, height, config);
+    }
+
+    /**
+     * Returns a immutable bitmap with the specified width and height, with each
+     * pixel value set to the corresponding value in the colors array.  Its
+     * initial density is determined from the given {@link DisplayMetrics}.
+     *
+     * @param display  Display metrics for the display this bitmap will be
+     *                 drawn on.
+     * @param colors   Array of {@link Color} used to initialize the pixels.
+     * @param offset   Number of values to skip before the first color in the
+     *                 array of colors.
+     * @param stride   Number of colors in the array between rows (must be >=
+     *                 width or <= -width).
+     * @param width    The width of the bitmap
+     * @param height   The height of the bitmap
+     * @param config   The bitmap config to create. If the config does not
+     *                 support per-pixel alpha (e.g. RGB_565), then the alpha
+     *                 bytes in the colors[] will be ignored (assumed to be FF)
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         the color array's length is less than the number of pixels.
+     */
+    public static Bitmap createBitmap(DisplayMetrics display, int colors[],
+            int offset, int stride, int width, int height, Config config) {
 
         checkWidthHeight(width, height);
         if (Math.abs(stride) < width) {
@@ -687,8 +751,12 @@
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("width and height must be > 0");
         }
-        return nativeCreate(colors, offset, stride, width, height,
+        Bitmap bm = nativeCreate(colors, offset, stride, width, height,
                             config.nativeInt, false);
+        if (display != null) {
+            bm.mDensity = display.densityDpi;
+        }
+        return bm;
     }
 
     /**
@@ -707,7 +775,29 @@
      *         the color array's length is less than the number of pixels.
      */
     public static Bitmap createBitmap(int colors[], int width, int height, Config config) {
-        return createBitmap(colors, 0, width, width, height, config);
+        return createBitmap(null, colors, 0, width, width, height, config);
+    }
+
+    /**
+     * Returns a immutable bitmap with the specified width and height, with each
+     * pixel value set to the corresponding value in the colors array.  Its
+     * initial density is determined from the given {@link DisplayMetrics}.
+     *
+     * @param display  Display metrics for the display this bitmap will be
+     *                 drawn on.
+     * @param colors   Array of {@link Color} used to initialize the pixels.
+     *                 This array must be at least as large as width * height.
+     * @param width    The width of the bitmap
+     * @param height   The height of the bitmap
+     * @param config   The bitmap config to create. If the config does not
+     *                 support per-pixel alpha (e.g. RGB_565), then the alpha
+     *                 bytes in the colors[] will be ignored (assumed to be FF)
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         the color array's length is less than the number of pixels.
+     */
+    public static Bitmap createBitmap(DisplayMetrics display, int colors[],
+            int width, int height, Config config) {
+        return createBitmap(display, colors, 0, width, width, height, config);
     }
 
     /**
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 215f597..fe459c7 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -964,7 +964,7 @@
         }
     }
 
-    public void setInitialDisplaySize(Display display, int width, int height) {
+    public void setInitialDisplaySize(Display display, int width, int height, int density) {
         mDisplay = display;
 
         int shortSize, longSize;
@@ -1018,11 +1018,7 @@
                         com.android.internal.R.dimen.navigation_bar_width);
 
         // SystemUI (status bar) layout policy
-        DisplayMetrics metrics = new DisplayMetrics();
-        mDisplay.getMetrics(metrics);
-        int shortSizeDp = shortSize
-                * DisplayMetrics.DENSITY_DEFAULT
-                / metrics.densityDpi;
+        int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
 
         if (shortSizeDp < 600) {
             // 0-599dp: "phone" UI with a separate status & navigation bar
@@ -1052,12 +1048,9 @@
             // The system bar is always at the bottom.  If you are watching
             // a video in landscape, we don't need to hide it if we can still
             // show a 16:9 aspect ratio with it.
-            int longSizeDp = longSize
-                    * DisplayMetrics.DENSITY_DEFAULT
-                    / DisplayMetrics.DENSITY_DEVICE;
+            int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / density;
             int barHeightDp = mNavigationBarHeightForRotation[mLandscapeRotation]
-                    * DisplayMetrics.DENSITY_DEFAULT
-                    / DisplayMetrics.DENSITY_DEVICE;
+                    * DisplayMetrics.DENSITY_DEFAULT / density;
             int aspect = ((shortSizeDp-barHeightDp) * 16) / longSizeDp;
             // We have computed the aspect ratio with the bar height taken
             // out to be 16:aspect.  If this is less than 9, then hiding
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 2305c88..a8854cf 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -56,8 +56,10 @@
     final Object mDisplaySizeLock = new Object();
     int mInitialDisplayWidth = 0;
     int mInitialDisplayHeight = 0;
+    int mInitialDisplayDensity = 0;
     int mBaseDisplayWidth = 0;
     int mBaseDisplayHeight = 0;
+    int mBaseDisplayDensity = 0;
     final DisplayManagerService mDisplayManager;
     final DisplayInfo mDisplayInfo = new DisplayInfo();
 
@@ -82,16 +84,14 @@
     public void dump(PrintWriter pw) {
         pw.print("  Display: mDisplayId="); pw.println(mDisplayId);
         pw.print("  init="); pw.print(mInitialDisplayWidth); pw.print("x");
-        pw.print(mInitialDisplayHeight);
+        pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
+        pw.print("dpi");
         if (mInitialDisplayWidth != mBaseDisplayWidth
-                || mInitialDisplayHeight != mBaseDisplayHeight) {
+                || mInitialDisplayHeight != mBaseDisplayHeight
+                || mInitialDisplayDensity != mBaseDisplayDensity) {
             pw.print(" base=");
             pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
-        }
-        if (mInitialDisplayWidth != mDisplayInfo.logicalWidth
-                || mInitialDisplayHeight != mDisplayInfo.logicalHeight) {
-            pw.print(" init="); pw.print(mInitialDisplayWidth);
-            pw.print("x"); pw.print(mInitialDisplayHeight);
+            pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi");
         }
         pw.print(" cur=");
         pw.print(mDisplayInfo.logicalWidth);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index e763a56..5b04810 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -6568,6 +6568,7 @@
             displayInfo.rotation = mRotation;
             displayInfo.logicalWidth = dw;
             displayInfo.logicalHeight = dh;
+            displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
             displayInfo.appWidth = appWidth;
             displayInfo.appHeight = appHeight;
             displayInfo.getLogicalMetrics(mRealDisplayMetrics, null);
@@ -6594,7 +6595,7 @@
             config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
             config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
             config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh);
-            config.densityDpi = mDisplayMetrics.densityDpi;
+            config.densityDpi = displayContent.mBaseDisplayDensity;
 
             // Update the configuration based on available input devices, lid switch,
             // and platform configuration.
@@ -6902,8 +6903,8 @@
                     info.width, info.height);
             mInputManager.setDisplayOrientation(Display.DEFAULT_DISPLAY,
                     mDisplay.getRotation(), Surface.ROTATION_0);
-            mPolicy.setInitialDisplaySize(mDisplay,
-                    displayContent.mInitialDisplayWidth, displayContent.mInitialDisplayHeight);
+            mPolicy.setInitialDisplaySize(mDisplay, displayContent.mInitialDisplayWidth,
+                    displayContent.mInitialDisplayHeight, displayContent.mInitialDisplayDensity);
         }
     }
 
@@ -6917,8 +6918,10 @@
                 mDisplayManager.getDisplayInfo(displayId, displayInfo);
                 displayContent.mInitialDisplayWidth = displayInfo.logicalWidth;
                 displayContent.mInitialDisplayHeight = displayInfo.logicalHeight;
+                displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi;
                 displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth;
                 displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight;
+                displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;
             }
         }
 
@@ -6928,7 +6931,7 @@
         }
 
         synchronized (mWindowMap) {
-            readForcedDisplaySizeLocked(getDisplayContent(displayId));
+            readForcedDisplaySizeAndDensityLocked(getDisplayContent(displayId));
         }
     }
 
@@ -7590,24 +7593,49 @@
         }
     }
 
-    private void readForcedDisplaySizeLocked(final DisplayContent displayContent) {
-        final String str = Settings.Secure.getString(mContext.getContentResolver(),
+    private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) {
+        boolean changed = false;
+        final String sizeStr = Settings.Secure.getString(mContext.getContentResolver(),
                 Settings.Secure.DISPLAY_SIZE_FORCED);
-        if (str == null || str.length() == 0) {
-            return;
+        if (sizeStr != null && sizeStr.length() > 0) {
+            final int pos = sizeStr.indexOf(',');
+            if (pos > 0 && sizeStr.lastIndexOf(',') == pos) {
+                int width, height;
+                try {
+                    width = Integer.parseInt(sizeStr.substring(0, pos));
+                    height = Integer.parseInt(sizeStr.substring(pos+1));
+                    synchronized(displayContent.mDisplaySizeLock) {
+                        if (displayContent.mBaseDisplayWidth != width
+                                || displayContent.mBaseDisplayHeight != height) {
+                            changed = true;
+                            Slog.i(TAG, "FORCED DISPLAY SIZE: " + width + "x" + height);
+                            displayContent.mBaseDisplayWidth = width;
+                            displayContent.mBaseDisplayHeight = height;
+                        }
+                    }
+                } catch (NumberFormatException ex) {
+                }
+            }
         }
-        final int pos = str.indexOf(',');
-        if (pos <= 0 || str.lastIndexOf(',') != pos) {
-            return;
+        final String densityStr = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.DISPLAY_DENSITY_FORCED);
+        if (densityStr != null && densityStr.length() > 0) {
+            int density;
+            try {
+                density = Integer.parseInt(densityStr);
+                synchronized(displayContent.mDisplaySizeLock) {
+                    if (displayContent.mBaseDisplayDensity != density) {
+                        changed = true;
+                        Slog.i(TAG, "FORCED DISPLAY DENSITY: " + density);
+                        displayContent.mBaseDisplayDensity = density;
+                    }
+                }
+            } catch (NumberFormatException ex) {
+            }
         }
-        int width, height;
-        try {
-            width = Integer.parseInt(str.substring(0, pos));
-            height = Integer.parseInt(str.substring(pos+1));
-        } catch (NumberFormatException ex) {
-            return;
+        if (changed) {
+            reconfigureDisplayLocked(displayContent);
         }
-        setForcedDisplaySizeLocked(displayContent, width, height);
     }
 
     private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) {
@@ -7617,8 +7645,49 @@
             displayContent.mBaseDisplayWidth = width;
             displayContent.mBaseDisplayHeight = height;
         }
+        reconfigureDisplayLocked(displayContent);
+    }
+
+    public void clearForcedDisplaySize(int displayId) {
+        synchronized(mWindowMap) {
+            final DisplayContent displayContent = getDisplayContent(displayId);
+            setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth,
+                    displayContent.mInitialDisplayHeight);
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.DISPLAY_SIZE_FORCED, "");
+        }
+    }
+
+    public void setForcedDisplayDensity(int displayId, int density) {
+        synchronized(mWindowMap) {
+            final DisplayContent displayContent = getDisplayContent(displayId);
+            setForcedDisplayDensityLocked(displayContent, density);
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.DISPLAY_SIZE_FORCED, Integer.toString(density));
+        }
+    }
+
+    private void setForcedDisplayDensityLocked(DisplayContent displayContent, int density) {
+        Slog.i(TAG, "Using new display density: " + density);
+
+        synchronized(displayContent.mDisplaySizeLock) {
+            displayContent.mBaseDisplayDensity = density;
+        }
+        reconfigureDisplayLocked(displayContent);
+    }
+
+    public void clearForcedDisplayDensity(int displayId) {
+        synchronized(mWindowMap) {
+            final DisplayContent displayContent = getDisplayContent(displayId);
+            setForcedDisplayDensityLocked(displayContent, displayContent.mInitialDisplayDensity);
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.DISPLAY_DENSITY_FORCED, "");
+        }
+    }
+
+    private void reconfigureDisplayLocked(DisplayContent displayContent) {
         mPolicy.setInitialDisplaySize(mDisplay, displayContent.mBaseDisplayWidth,
-                displayContent.mBaseDisplayHeight);
+                displayContent.mBaseDisplayHeight, displayContent.mBaseDisplayDensity);
 
         mLayoutNeeded = true;
 
@@ -7642,16 +7711,6 @@
         performLayoutAndPlaceSurfacesLocked();
     }
 
-    public void clearForcedDisplaySize(int displayId) {
-        synchronized(mWindowMap) {
-            final DisplayContent displayContent = getDisplayContent(displayId);
-            setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth,
-                    displayContent.mInitialDisplayHeight);
-            Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.DISPLAY_SIZE_FORCED, "");
-        }
-    }
-
     public boolean hasSystemNavBar() {
         return mPolicy.hasSystemNavBar();
     }