Merge change 692 into donut

* changes:
  location: Optimize use of mProviders ArrayList.
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 55fce49..a1f5a58 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -550,6 +550,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 {
@@ -2008,9 +2021,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 600dfa4..a301449 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 e928998..61dca4c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -135,20 +135,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;
     }
     
     /**
@@ -297,8 +305,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
@@ -325,9 +333,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;
@@ -395,15 +408,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=" +
@@ -566,6 +589,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) {
@@ -611,4 +635,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/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 6428e90..677d609 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -40,10 +40,6 @@
 import java.io.UnsupportedEncodingException;
 import java.util.Random;
 
-import static android.telephony.SmsMessage.ENCODING_7BIT;
-import static android.telephony.SmsMessage.ENCODING_8BIT;
-import static android.telephony.SmsMessage.ENCODING_16BIT;
-import static android.telephony.SmsMessage.ENCODING_UNKNOWN;
 import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
 import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
 import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
@@ -685,41 +681,22 @@
     }
 
     /**
-     * Parses the User Data of an SMS.
+     * Copy parsed user data out from internal datastructures.
      */
     private void parseUserData(UserData uData) {
-        int encodingType;
-
-        if (null == uData) {
+        if (uData == null) {
             return;
         }
 
-        encodingType = uData.msgEncoding;
-
-        // insert DCS-decoding here when type is supported by ril-library
-
         userData = uData.payload;
         userDataHeader = uData.userDataHeader;
-
-        switch (encodingType) {
-        case UserData.ENCODING_GSM_7BIT_ALPHABET:
-        case UserData.ENCODING_7BIT_ASCII:
-        case UserData.ENCODING_UNICODE_16:
-            // user data was already decoded by wmsts-library
-            messageBody = new String(userData);
-            break;
-
-        // data and unsupported encodings:
-        case UserData.ENCODING_OCTET:
-        default:
-            messageBody = null;
-            break;
-        }
-
-        if (Config.LOGV) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'");
+        messageBody = uData.payloadStr;
 
         if (messageBody != null) {
+            if (Config.LOGV) Log.v(LOG_TAG, "SMS message body: '" + messageBody + "'");
             parseMessageBody();
+        } else if ((userData != null) && (Config.LOGV)) {
+            Log.v(LOG_TAG, "SMS payload: '" + IccUtils.bytesToHexString(userData) + "'");
         }
     }
 
@@ -727,7 +704,7 @@
      * {@inheritDoc}
      */
     public MessageClass getMessageClass() {
-        if (BearerData.DISPLAY_IMMEDIATE == mBearerData.displayMode ) {
+        if (BearerData.DISPLAY_MODE_IMMEDIATE == mBearerData.displayMode ) {
             return MessageClass.CLASS_0;
         } else {
             return MessageClass.UNKNOWN;
@@ -780,9 +757,6 @@
         mBearerData.readAckReq = false;
         mBearerData.reportReq = false;
 
-        // Set the display mode (See C.S0015-B, v2.0, 4.5.16)
-        mBearerData.displayMode = BearerData.DISPLAY_DEFAULT;
-
         // number of messages: not needed for encoding!
 
         // indicate whether a user data header is available
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 8e67387..b5952a1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -46,14 +46,14 @@
     //private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE         = 0x05;
     //private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE  = 0x06;
     //private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE  = 0x07;
-    //private final static byte SUBPARAM_PRIORITY_INDICATOR               = 0x08;
-    //private final static byte SUBPARAM_PRIVACY_INDICATOR                = 0x09;
+    private final static byte SUBPARAM_PRIORITY_INDICATOR               = 0x08;
+    private final static byte SUBPARAM_PRIVACY_INDICATOR                = 0x09;
     private final static byte SUBPARAM_REPLY_OPTION                     = 0x0A;
     private final static byte SUBPARAM_NUMBER_OF_MESSAGES               = 0x0B;
-    //private final static byte SUBPARAM_ALERT_ON_MESSAGE_DELIVERY        = 0x0C;
-    //private final static byte SUBPARAM_LANGUAGE_INDICATOR               = 0x0D;
+    private final static byte SUBPARAM_ALERT_ON_MESSAGE_DELIVERY        = 0x0C;
+    private final static byte SUBPARAM_LANGUAGE_INDICATOR               = 0x0D;
     private final static byte SUBPARAM_CALLBACK_NUMBER                  = 0x0E;
-    //private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE             = 0x0F;
+    private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE             = 0x0F;
     //private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA      = 0x10;
     //private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX            = 0x11;
     //private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA    = 0x12;
@@ -63,42 +63,6 @@
     //private final static byte SUBPARAM_ENHANCED_VMN                     = 0x16;
     //private final static byte SUBPARAM_ENHANCED_VMN_ACK                 = 0x17;
 
-    // For completeness the following fields are listed, though not used yet.
-    /**
-     * Supported priority modes for CDMA SMS messages
-     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1)
-     */
-    //public static final int PRIORITY_NORMAL        = 0x0;
-    //public static final int PRIORITY_INTERACTIVE   = 0x1;
-    //public static final int PRIORITY_URGENT        = 0x2;
-    //public static final int PRIORITY_EMERGENCY     = 0x3;
-
-    /**
-     * Supported privacy modes for CDMA SMS messages
-     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.10-1)
-     */
-    //public static final int PRIVACY_NOT_RESTRICTED = 0x0;
-    //public static final int PRIVACY_RESTRICTED     = 0x1;
-    //public static final int PRIVACY_CONFIDENTIAL   = 0x2;
-    //public static final int PRIVACY_SECRET         = 0x3;
-
-    /**
-     * Supported alert modes for CDMA SMS messages
-     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.13-1)
-     */
-    //public static final int ALERT_DEFAULT          = 0x0;
-    //public static final int ALERT_LOW_PRIO         = 0x1;
-    //public static final int ALERT_MEDIUM_PRIO      = 0x2;
-    //public static final int ALERT_HIGH_PRIO        = 0x3;
-
-    /**
-     * Supported display modes for CDMA SMS messages
-     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.16-1)
-     */
-    public static final int DISPLAY_IMMEDIATE      = 0x0;
-    public static final int DISPLAY_DEFAULT        = 0x1;
-    public static final int DISPLAY_USER           = 0x2;
-
     /**
      * Supported message types for CDMA SMS messages
      * (See 3GPP2 C.S0015-B, v2.0, table 4.5.1-1)
@@ -112,9 +76,89 @@
     public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07;
     public static final int MESSAGE_TYPE_SUBMIT_REPORT  = 0x08;
 
+    public byte messageType;
+
     /**
-     * SMS Message Status Codes
-     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.21-1)
+     * 16-bit value indicating the message ID, which increments modulo 65536.
+     * (Special rules apply for WAP-messages.)
+     * (See 3GPP2 C.S0015-B, v2, 4.5.1)
+     */
+    public int messageId;
+
+    /**
+     * Supported priority modes for CDMA SMS messages
+     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1)
+     */
+    public static final int PRIORITY_NORMAL        = 0x0;
+    public static final int PRIORITY_INTERACTIVE   = 0x1;
+    public static final int PRIORITY_URGENT        = 0x2;
+    public static final int PRIORITY_EMERGENCY     = 0x3;
+
+    public boolean priorityIndicatorSet = false;
+    public byte priority = PRIORITY_NORMAL;
+
+    /**
+     * Supported privacy modes for CDMA SMS messages
+     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.10-1)
+     */
+    public static final int PRIVACY_NOT_RESTRICTED = 0x0;
+    public static final int PRIVACY_RESTRICTED     = 0x1;
+    public static final int PRIVACY_CONFIDENTIAL   = 0x2;
+    public static final int PRIVACY_SECRET         = 0x3;
+
+    public boolean privacyIndicatorSet = false;
+    public byte privacy = PRIVACY_NOT_RESTRICTED;
+
+    /**
+     * Supported alert priority modes for CDMA SMS messages
+     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.13-1)
+     */
+    public static final int ALERT_DEFAULT          = 0x0;
+    public static final int ALERT_LOW_PRIO         = 0x1;
+    public static final int ALERT_MEDIUM_PRIO      = 0x2;
+    public static final int ALERT_HIGH_PRIO        = 0x3;
+
+    public boolean alertIndicatorSet = false;
+    public int alert = ALERT_DEFAULT;
+
+    /**
+     * Supported display modes for CDMA SMS messages.  Display mode is
+     * a 2-bit value used to indicate to the mobile station when to
+     * display the received message.  (See 3GPP2 C.S0015-B, v2,
+     * 4.5.16)
+     */
+    public static final int DISPLAY_MODE_IMMEDIATE      = 0x0;
+    public static final int DISPLAY_MODE_DEFAULT        = 0x1;
+    public static final int DISPLAY_MODE_USER           = 0x2;
+
+    public boolean displayModeSet = false;
+    public byte displayMode = DISPLAY_MODE_DEFAULT;
+
+    /**
+     * Language Indicator values.  NOTE: the spec (3GPP2 C.S0015-B,
+     * v2, 4.5.14) is ambiguous as to the meaning of this field, as it
+     * refers to C.R1001-D but that reference has been crossed out.
+     * It would seem reasonable to assume the values from C.R1001-F
+     * (table 9.2-1) are to be used instead.
+     */
+    public static final int LANGUAGE_UNKNOWN  = 0x00;
+    public static final int LANGUAGE_ENGLISH  = 0x01;
+    public static final int LANGUAGE_FRENCH   = 0x02;
+    public static final int LANGUAGE_SPANISH  = 0x03;
+    public static final int LANGUAGE_JAPANESE = 0x04;
+    public static final int LANGUAGE_KOREAN   = 0x05;
+    public static final int LANGUAGE_CHINESE  = 0x06;
+    public static final int LANGUAGE_HEBREW   = 0x07;
+
+    public boolean languageIndicatorSet = false;
+    public int language = LANGUAGE_UNKNOWN;
+
+    /**
+     * SMS Message Status Codes.  The first component of the Message
+     * status indicates if an error has occurred and whether the error
+     * is considered permanent or temporary.  The second component of
+     * the Message status indicates the cause of the error (if any).
+     * (See 3GPP2 C.S0015-B, v2.0, 4.5.21)
      */
     /* no-error codes */
     public static final int ERROR_NONE                   = 0x00;
@@ -139,19 +183,9 @@
     public static final int ERROR_UNDEFINED              = 0xFF;
     public static final int STATUS_UNDEFINED             = 0xFF;
 
-    /**
-     * 4-bit value indicating the message type in accordance to
-     *      table 4.5.1-1
-     * (See 3GPP2 C.S0015-B, v2, 4.5.1)
-     */
-    public byte messageType;
-
-    /**
-     * 16-bit value indicating the message ID, which increments modulo 65536.
-     * (Special rules apply for WAP-messages.)
-     * (See 3GPP2 C.S0015-B, v2, 4.5.1)
-     */
-    public int messageId;
+    public boolean messageStatusSet = false;
+    public int errorClass = ERROR_UNDEFINED;
+    public int messageStatus = STATUS_UNDEFINED;
 
     /**
      * 1-bit value that indicates whether a User Data Header is present.
@@ -178,8 +212,6 @@
     //public SmsRelTime validityPeriodRelative;
     //public SmsTime deferredDeliveryTimeAbsolute;
     //public SmsRelTime deferredDeliveryTimeRelative;
-    //public byte priority;
-    //public byte privacy;
 
     /**
      * Reply Option
@@ -199,9 +231,6 @@
      */
     public int numberOfMessages;
 
-    //public int alert;
-    //public int language;
-
     /**
      * 4-bit or 8-bit value that indicates the number to be dialed in reply to a
      * received SMS message.
@@ -209,27 +238,6 @@
      */
     public CdmaSmsAddress callbackNumber;
 
-    /**
-     * 2-bit value that is used to indicate to the mobile station when to display
-     * the received message.
-     * (See 3GPP2 C.S0015-B, v2, 4.5.16)
-     */
-    public byte displayMode = DISPLAY_DEFAULT;
-
-    /**
-     * First component of the Message status, that indicates if an error has occurred
-     * and whether the error is considered permanent or temporary.
-     * (See 3GPP2 C.S0015-B, v2, 4.5.21)
-     */
-    public int errorClass = ERROR_UNDEFINED;
-
-    /**
-     * Second component of the Message status, that indicates if an error has occurred
-     * and the cause of the error.
-     * (See 3GPP2 C.S0015-B, v2, 4.5.21)
-     */
-    public int messageStatus = STATUS_UNDEFINED;
-
     private static class CodingException extends Exception {
         public CodingException(String s) {
             super(s);
@@ -242,6 +250,13 @@
         builder.append("BearerData:\n");
         builder.append("  messageType: " + messageType + "\n");
         builder.append("  messageId: " + (int)messageId + "\n");
+        builder.append("  priority: " + (priorityIndicatorSet ? priority : "not set") + "\n");
+        builder.append("  privacy: " + (privacyIndicatorSet ? privacy : "not set") + "\n");
+        builder.append("  alert: " + (alertIndicatorSet ? alert : "not set") + "\n");
+        builder.append("  displayMode: " + (displayModeSet ? displayMode : "not set") + "\n");
+        builder.append("  language: " + (languageIndicatorSet ? language : "not set") + "\n");
+        builder.append("  errorClass: " + (messageStatusSet ? errorClass : "not set") + "\n");
+        builder.append("  messageStatus: " + (messageStatusSet ? messageStatus : "not set") + "\n");
         builder.append("  hasUserDataHeader: " + hasUserDataHeader + "\n");
         builder.append("  timeStamp: " + timeStamp + "\n");
         builder.append("  userAckReq: " + userAckReq + "\n");
@@ -250,9 +265,6 @@
         builder.append("  reportReq: " + reportReq + "\n");
         builder.append("  numberOfMessages: " + numberOfMessages + "\n");
         builder.append("  callbackNumber: " + callbackNumber + "\n");
-        builder.append("  displayMode: " + displayMode + "\n");
-        builder.append("  errorClass: " + errorClass + "\n");
-        builder.append("  messageStatus: " + messageStatus + "\n");
         builder.append("  userData: " + userData + "\n");
         return builder.toString();
     }
@@ -387,6 +399,45 @@
         outStream.writeByteArray(6 * 8, bData.timeStamp);
     }
 
+    private static void encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream)
+        throws BitwiseOutputStream.AccessException
+    {
+        outStream.write(8, 1);
+        outStream.write(2, bData.privacy);
+        outStream.skip(6);
+    }
+
+    private static void encodeLanguageIndicator(BearerData bData, BitwiseOutputStream outStream)
+        throws BitwiseOutputStream.AccessException
+    {
+        outStream.write(8, 1);
+        outStream.write(8, bData.language);
+    }
+
+    private static void encodeDisplayMode(BearerData bData, BitwiseOutputStream outStream)
+        throws BitwiseOutputStream.AccessException
+    {
+        outStream.write(8, 1);
+        outStream.write(2, bData.displayMode);
+        outStream.skip(6);
+    }
+
+    private static void encodePriorityIndicator(BearerData bData, BitwiseOutputStream outStream)
+        throws BitwiseOutputStream.AccessException
+    {
+        outStream.write(8, 1);
+        outStream.write(2, bData.priority);
+        outStream.skip(6);
+    }
+
+    private static void encodeMsgDeliveryAlert(BearerData bData, BitwiseOutputStream outStream)
+        throws BitwiseOutputStream.AccessException
+    {
+        outStream.write(8, 1);
+        outStream.write(2, bData.alert);
+        outStream.skip(6);
+    }
+
     /**
      * Create serialized representation for BearerData object.
      * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
@@ -420,6 +471,30 @@
                 outStream.write(8, SUBPARAM_MESSAGE_CENTER_TIME_STAMP);
                 encodeMsgCenterTimeStamp(bData, outStream);
             }
+            if (bData.privacyIndicatorSet) {
+                outStream.write(8, SUBPARAM_PRIVACY_INDICATOR);
+                encodePrivacyIndicator(bData, outStream);
+            }
+            if (bData.languageIndicatorSet) {
+                outStream.write(8, SUBPARAM_LANGUAGE_INDICATOR);
+                encodeLanguageIndicator(bData, outStream);
+            }
+            if (bData.displayModeSet) {
+                outStream.write(8, SUBPARAM_MESSAGE_DISPLAY_MODE);
+                encodeDisplayMode(bData, outStream);
+            }
+            if (bData.priorityIndicatorSet) {
+                outStream.write(8, SUBPARAM_PRIORITY_INDICATOR);
+                encodePriorityIndicator(bData, outStream);
+            }
+            if (bData.alertIndicatorSet) {
+                outStream.write(8, SUBPARAM_ALERT_ON_MESSAGE_DELIVERY);
+                encodeMsgDeliveryAlert(bData, outStream);
+            }
+            if (bData.messageStatusSet) {
+                outStream.write(8, SUBPARAM_MESSAGE_STATUS);
+                encodeMsgStatus(bData, outStream);
+            }
             return outStream.toByteArray();
         } catch (BitwiseOutputStream.AccessException ex) {
             Log.e(LOG_TAG, "BearerData encode failed: " + ex);
@@ -471,6 +546,33 @@
         }
     }
 
+    private static String decodeIa5(byte[] data, int offset, int numFields) {
+        try {
+            StringBuffer strBuf = new StringBuffer(numFields);
+            BitwiseInputStream inStream = new BitwiseInputStream(data);
+            inStream.skip(offset);
+            int wantedBits = numFields * 7;
+            if (inStream.available() < wantedBits) {
+                throw new CodingException("insufficient data (wanted " + wantedBits +
+                                          " bits, but only have " + inStream.available() + ")");
+            }
+            for (int i = 0; i < numFields; i++) {
+                int charCode = inStream.read(7);
+                if ((charCode < UserData.IA5_MAP_BASE_INDEX) ||
+                        (charCode > UserData.IA5_MAP_MAX_INDEX)) {
+                    throw new CodingException("unsupported AI5 character code (" + charCode + ")");
+                }
+                strBuf.append(UserData.IA5_MAP[charCode - UserData.IA5_MAP_BASE_INDEX]);
+            }
+            return strBuf.toString();
+        } catch (BitwiseInputStream.AccessException ex) {
+            Log.e(LOG_TAG, "UserData AI5 decode failed: " + ex);
+        } catch (CodingException ex) {
+            Log.e(LOG_TAG, "UserData AI5 decode failed: " + ex);
+        }
+        return null;
+    }
+
     private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
         throws CodingException
     {
@@ -482,18 +584,23 @@
             userData.userDataHeader = SmsHeader.parse(headerData);
         }
         switch (userData.msgEncoding) {
-        case UserData.ENCODING_GSM_7BIT_ALPHABET:
-            userData.payloadStr = GsmAlphabet.gsm7BitPackedToString(userData.payload,
-                                                                    offset, userData.numFields);
+        case UserData.ENCODING_OCTET:
             break;
         case UserData.ENCODING_7BIT_ASCII:
             userData.payloadStr = decodePayloadStr(userData.payload, offset,
                                                    userData.numFields, "US-ASCII");
             break;
+        case UserData.ENCODING_IA5:
+            userData.payloadStr = decodeIa5(userData.payload, offset, userData.numFields);
+            break;
         case UserData.ENCODING_UNICODE_16:
             userData.payloadStr = decodePayloadStr(userData.payload, offset,
                                                    userData.numFields * 2, "UTF-16");
             break;
+        case UserData.ENCODING_GSM_7BIT_ALPHABET:
+            userData.payloadStr = GsmAlphabet.gsm7BitPackedToString(userData.payload,
+                                                                    offset, userData.numFields);
+            break;
         default:
             throw new CodingException("unsupported user data encoding ("
                                       + userData.msgEncoding + ")");
@@ -592,6 +699,7 @@
         }
         bData.errorClass = inStream.read(2);
         bData.messageStatus = inStream.read(6);
+        bData.messageStatusSet = true;
     }
 
     private static void decodeMsgCenterTimeStamp(BearerData bData,
@@ -604,6 +712,60 @@
         bData.timeStamp = inStream.readByteArray(6 * 8);
     }
 
+    private static void decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 1) {
+            throw new CodingException("PRIVACY_INDICATOR subparam size incorrect");
+        }
+        bData.privacy = inStream.read(2);
+        inStream.skip(6);
+        bData.privacyIndicatorSet = true;
+    }
+
+    private static void decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 1) {
+            throw new CodingException("LANGUAGE_INDICATOR subparam size incorrect");
+        }
+        bData.language = inStream.read(8);
+        bData.languageIndicatorSet = true;
+    }
+
+    private static void decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 1) {
+            throw new CodingException("DISPLAY_MODE subparam size incorrect");
+        }
+        bData.displayMode = inStream.read(2);
+        inStream.skip(6);
+        bData.displayModeSet = true;
+    }
+
+    private static void decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 1) {
+            throw new CodingException("PRIORITY_INDICATOR subparam size incorrect");
+        }
+        bData.priority = inStream.read(2);
+        inStream.skip(6);
+        bData.priorityIndicatorSet = true;
+    }
+
+    private static void decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 1) {
+            throw new CodingException("ALERT_ON_MESSAGE_DELIVERY subparam size incorrect");
+        }
+        bData.alert = inStream.read(2);
+        inStream.skip(6);
+        bData.alertIndicatorSet = true;
+    }
+
     /**
      * Create BearerData object from serialized representation.
      * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
@@ -647,6 +809,21 @@
                 case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
                     decodeMsgCenterTimeStamp(bData, inStream);
                     break;
+                case SUBPARAM_PRIVACY_INDICATOR:
+                    decodePrivacyIndicator(bData, inStream);
+                    break;
+                case SUBPARAM_LANGUAGE_INDICATOR:
+                    decodeLanguageIndicator(bData, inStream);
+                    break;
+                case SUBPARAM_MESSAGE_DISPLAY_MODE:
+                    decodeDisplayMode(bData, inStream);
+                    break;
+                case SUBPARAM_PRIORITY_INDICATOR:
+                    decodePriorityIndicator(bData, inStream);
+                    break;
+                case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
+                    decodeMsgDeliveryAlert(bData, inStream);
+                    break;
                 default:
                     throw new CodingException("unsupported bearer data subparameter ("
                                               + subparamId + ")");
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index bd6fbb4..f916089 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -22,13 +22,13 @@
 public class UserData{
 
     /**
-     * User data encoding types
+     * User data encoding types.
      * (See 3GPP2 C.R1001-F, v1.0, table 9.1-1)
      */
     public static final int ENCODING_OCTET                      = 0x00;
     public static final int ENCODING_IS91_EXTENDED_PROTOCOL     = 0x01;
     public static final int ENCODING_7BIT_ASCII                 = 0x02;
-    //public static final int ENCODING_IA5                        = 0x03;
+    public static final int ENCODING_IA5                        = 0x03;
     public static final int ENCODING_UNICODE_16                 = 0x04;
     //public static final int ENCODING_SHIFT_JIS                  = 0x05;
     //public static final int ENCODING_KOREAN                     = 0x06;
@@ -38,6 +38,25 @@
     public static final int ENCODING_GSM_DCS                    = 0x0A;
 
     /**
+     * IA5 data encoding character mappings.
+     * (See CCITT Rec. T.50 Tables 1 and 3)
+     */
+    public static final char[] IA5_MAP = {
+        ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
+        '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+        'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+        '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+        'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~'};
+
+    /**
+     * Mapping for IA5 values less than 32 are flow control signals
+     * and not used here.
+     */
+    public static final int IA5_MAP_BASE_INDEX = 0x20;
+    public static final int IA5_MAP_MAX_INDEX = IA5_MAP_BASE_INDEX + IA5_MAP.length - 1;
+
+    /**
      * Contains the data header of the user data
      */
     public SmsHeader userDataHeader;
@@ -73,8 +92,8 @@
         builder.append("  paddingBits: " + paddingBits + "\n");
         builder.append("  numFields: " + (int)numFields + "\n");
         builder.append("  userDataHeader: " + userDataHeader + "\n");
-        builder.append("  payload: " + HexDump.toHexString(payload));
-        builder.append("  payloadStr: " + payloadStr);
+        builder.append("  payload: '" + HexDump.toHexString(payload) + "'");
+        builder.append(", payloadStr: '" + payloadStr + "'");
         return builder.toString();
     }
 
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/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
index 723512c..9ff80c7 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
@@ -264,10 +264,152 @@
                      HexDump.toHexString(revBearerData.timeStamp));
     }
 
-    // XXX test messageId
+    @SmallTest
+    public void testPrivacyIndicator() throws Exception {
+        String pdu1 = "0003104090010c485f4194dfea34becf61b840090140";
+        BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
+        assertEquals(bd1.privacy, BearerData.PRIVACY_RESTRICTED);
+        String pdu2 = "0003104090010c485f4194dfea34becf61b840090180";
+        BearerData bd2 = BearerData.decode(HexDump.hexStringToByteArray(pdu2));
+        assertEquals(bd2.privacy, BearerData.PRIVACY_CONFIDENTIAL);
+        String pdu3 = "0003104090010c485f4194dfea34becf61b8400901c0";
+        BearerData bd3 = BearerData.decode(HexDump.hexStringToByteArray(pdu3));
+        assertEquals(bd3.privacy, BearerData.PRIVACY_SECRET);
+    }
 
-    // String pdu1 = "0003104090010d4866a794e07055965b91d040300c0100"; sid 12
-    // String pdu1 = "0003104090011748bea794e0731436ef3bd7c2e0352eef27a1c263fe58080d0101"; sid 13
-    // Log.d(LOG_TAG, "revBearerData -- " + revBearerData);
+    @SmallTest
+    public void testPrivacyIndicatorFeedback() throws Exception {
+        BearerData bearerData = new BearerData();
+        bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+        bearerData.messageId = 0;
+        bearerData.hasUserDataHeader = false;
+        String payloadStr = "test privacy indicator";
+        bearerData.userData = makeUserData(payloadStr);
+        bearerData.privacy = BearerData.PRIVACY_SECRET;
+        bearerData.privacyIndicatorSet = true;
+        byte []encodedSms = BearerData.encode(bearerData);
+        BearerData revBearerData = BearerData.decode(encodedSms);
+        assertEquals(revBearerData.userData.payloadStr, payloadStr);
+        assertEquals(revBearerData.privacyIndicatorSet, true);
+        assertEquals(revBearerData.privacy, BearerData.PRIVACY_SECRET);
+        bearerData.privacy = BearerData.PRIVACY_RESTRICTED;
+        encodedSms = BearerData.encode(bearerData);
+        revBearerData = BearerData.decode(encodedSms);
+        assertEquals(revBearerData.privacy, BearerData.PRIVACY_RESTRICTED);
+    }
 
+    @SmallTest
+    public void testMsgDeliveryAlert() throws Exception {
+        String pdu1 = "0003104090010d4866a794e07055965b91d040300c0100";
+        BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
+        assertEquals(bd1.alert, 0);
+        assertEquals(bd1.userData.payloadStr, "Test Alert 0");
+        String pdu2 = "0003104090010d4866a794e07055965b91d140300c0140";
+        BearerData bd2 = BearerData.decode(HexDump.hexStringToByteArray(pdu2));
+        assertEquals(bd2.alert, 1);
+        assertEquals(bd2.userData.payloadStr, "Test Alert 1");
+        String pdu3 = "0003104090010d4866a794e07055965b91d240300c0180";
+        BearerData bd3 = BearerData.decode(HexDump.hexStringToByteArray(pdu3));
+        assertEquals(bd3.alert, 2);
+        assertEquals(bd3.userData.payloadStr, "Test Alert 2");
+        String pdu4 = "0003104090010d4866a794e07055965b91d340300c01c0";
+        BearerData bd4 = BearerData.decode(HexDump.hexStringToByteArray(pdu4));
+        assertEquals(bd4.alert, 3);
+        assertEquals(bd4.userData.payloadStr, "Test Alert 3");
+    }
+
+    @SmallTest
+    public void testMsgDeliveryAlertFeedback() throws Exception {
+        BearerData bearerData = new BearerData();
+        bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+        bearerData.messageId = 0;
+        bearerData.hasUserDataHeader = false;
+        String payloadStr = "test message delivery alert";
+        bearerData.userData = makeUserData(payloadStr);
+        bearerData.alert = BearerData.ALERT_MEDIUM_PRIO;
+        bearerData.alertIndicatorSet = true;
+        byte []encodedSms = BearerData.encode(bearerData);
+        BearerData revBearerData = BearerData.decode(encodedSms);
+        assertEquals(revBearerData.userData.payloadStr, payloadStr);
+        assertEquals(revBearerData.alertIndicatorSet, true);
+        assertEquals(revBearerData.alert, bearerData.alert);
+        bearerData.alert = BearerData.ALERT_HIGH_PRIO;
+        encodedSms = BearerData.encode(bearerData);
+        revBearerData = BearerData.decode(encodedSms);
+        assertEquals(revBearerData.userData.payloadStr, payloadStr);
+        assertEquals(revBearerData.alertIndicatorSet, true);
+        assertEquals(revBearerData.alert, bearerData.alert);
+    }
+
+    @SmallTest
+    public void testLanguageIndicator() throws Exception {
+        String pdu1 = "0003104090011748bea794e0731436ef3bd7c2e0352eef27a1c263fe58080d0101";
+        BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
+        assertEquals(bd1.userData.payloadStr, "Test Language indicator");
+        assertEquals(bd1.language, BearerData.LANGUAGE_ENGLISH);
+        String pdu2 = "0003104090011748bea794e0731436ef3bd7c2e0352eef27a1c263fe58080d0106";
+        BearerData bd2 = BearerData.decode(HexDump.hexStringToByteArray(pdu2));
+        assertEquals(bd2.userData.payloadStr, "Test Language indicator");
+        assertEquals(bd2.language, BearerData.LANGUAGE_CHINESE);
+    }
+
+    @SmallTest
+    public void testLanguageIndicatorFeedback() throws Exception {
+        BearerData bearerData = new BearerData();
+        bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+        bearerData.messageId = 0;
+        bearerData.hasUserDataHeader = false;
+        String payloadStr = "test language indicator";
+        bearerData.userData = makeUserData(payloadStr);
+        bearerData.language = BearerData.LANGUAGE_ENGLISH;
+        bearerData.languageIndicatorSet = true;
+        byte []encodedSms = BearerData.encode(bearerData);
+        BearerData revBearerData = BearerData.decode(encodedSms);
+        assertEquals(revBearerData.userData.payloadStr, payloadStr);
+        assertEquals(revBearerData.languageIndicatorSet, true);
+        assertEquals(revBearerData.language, bearerData.language);
+        bearerData.language = BearerData.LANGUAGE_KOREAN;
+        encodedSms = BearerData.encode(bearerData);
+        revBearerData = BearerData.decode(encodedSms);
+        assertEquals(revBearerData.userData.payloadStr, payloadStr);
+        assertEquals(revBearerData.languageIndicatorSet, true);
+        assertEquals(revBearerData.language, bearerData.language);
+    }
+
+    @SmallTest
+    public void testDisplayMode() throws Exception {
+        String pdu1 = "0003104090010c485f4194dfea34becf61b8400f0100";
+        BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
+        //Log.d(LOG_TAG, "bd1 = " + bd1);
+        assertEquals(bd1.displayMode, BearerData.DISPLAY_MODE_IMMEDIATE);
+        String pdu2 = "0003104090010c485f4194dfea34becf61b8400f0140";
+        BearerData bd2 = BearerData.decode(HexDump.hexStringToByteArray(pdu2));
+        assertEquals(bd2.displayMode, BearerData.DISPLAY_MODE_DEFAULT);
+        String pdu3 = "0003104090010c485f4194dfea34becf61b8400f0180";
+        BearerData bd3 = BearerData.decode(HexDump.hexStringToByteArray(pdu3));
+        assertEquals(bd3.displayMode, BearerData.DISPLAY_MODE_USER);
+    }
+
+    @SmallTest
+    public void testDisplayModeFeedback() throws Exception {
+        BearerData bearerData = new BearerData();
+        bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+        bearerData.messageId = 0;
+        bearerData.hasUserDataHeader = false;
+        String payloadStr = "test display mode";
+        bearerData.userData = makeUserData(payloadStr);
+        bearerData.displayMode = BearerData.DISPLAY_MODE_IMMEDIATE;
+        bearerData.displayModeSet = true;
+        byte []encodedSms = BearerData.encode(bearerData);
+        BearerData revBearerData = BearerData.decode(encodedSms);
+        assertEquals(revBearerData.userData.payloadStr, payloadStr);
+        assertEquals(revBearerData.displayModeSet, true);
+        assertEquals(revBearerData.displayMode, bearerData.displayMode);
+        bearerData.displayMode = BearerData.DISPLAY_MODE_USER;
+        encodedSms = BearerData.encode(bearerData);
+        revBearerData = BearerData.decode(encodedSms);
+        assertEquals(revBearerData.userData.payloadStr, payloadStr);
+        assertEquals(revBearerData.displayModeSet, true);
+        assertEquals(revBearerData.displayMode, bearerData.displayMode);
+    }
 }
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();
+    }
 }